home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / basic / qbtbox.txt < prev   
Encoding:
Text File  |  2013-11-08  |  853.5 KB  |  21,340 lines

  1.  Microsoft QuickBASIC Programmer's Toolbox
  2.  
  3.  
  4.  ════════════════════════════════════════════════════════════════════════════
  5.  
  6.  
  7.  Microsoft(R) QuickBASIC Programmer's Toolbox
  8.  
  9.  By John Clark Craig
  10.  
  11.  
  12.  ════════════════════════════════════════════════════════════════════════════
  13.  
  14.  
  15.    PUBLISHED BY
  16.    Microsoft Press
  17.    A Division of Microsoft Corporation
  18.    16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717
  19.    Copyright (C) 1988 by John Clark Craig
  20.    All rights reserved. No part of the contents of this book may be
  21.    reproduced or transmitted in any form or by any means without the written
  22.    permission of the publisher.
  23.    Library of Congress Cataloging in Publication Data
  24.    Craig, John Clark.
  25.    The Microsoft QuickBASIC programmer's toolbox.
  26.    1. BASIC (Computer program language)  2. Microsoft QuickBASIC
  27.    (Computer program)  I. Title.
  28.    QA76.73.B3C7    1988    005.13'3    88-5115
  29.    ISBN 1-55615-127-6
  30.    Printed and bound in the United States of America.
  31.    1 2 3 4 5 6 7 8 9  MLML  3 2 1 0 9 8
  32.    Distributed to the book trade in the
  33.    United States by Harper & Row.
  34.    Distributed to the book trade in
  35.    Canada by General Publishing Company, Ltd.
  36.    Distributed to the book trade outside the
  37.    United States and Canada by Penguin Books Ltd.
  38.    Penguin Books Ltd., Harmondsworth, Middlesex, England
  39.    Penguin Books Australia Ltd., Ringwood, Victoria, Australia
  40.    Penguin Books N.Z. Ltd., 182─190 Wairau Road, Auckland 10, New Zealand
  41.    British Cataloging in Publication Data available
  42.    ──────────────────────────────────────────────────────────────────────────
  43.    Project Editor: Suzanne Viescas  Manuscript Editor: Michele Tomiak
  44.    Technical Editor: Jon Harshaw
  45.    ──────────────────────────────────────────────────────────────────────────
  46.  
  47.  
  48.  
  49.                          Dedication
  50.  
  51.                This book is dedicated with love to
  52.            the three most important people in my life:
  53.                     Jeanie, Jennifer, and Adam.
  54.  
  55.  
  56.  
  57.  ────────────────────────────────────────────────────────────────────────────
  58.  Contents
  59.  
  60.  
  61.  PART I:  GETTING STARTED
  62.  
  63.  QUICKBASIC AND TOOLBOXES
  64.     Advantages of Structured Programming
  65.     The Toolboxes in This Book
  66.  MINICAL.BAS──A COMPLETE PROGRAM
  67.     Modular Source-Code Editing
  68.     Building a Quick Library
  69.     Creating the Source Code for MINICAL
  70.     Compiling and Running as an Executable (.EXE) Program
  71.  
  72.  PART II:  QUICKBASIC TOOLBOXES AND PROGRAMS
  73.  USING QUICKBASIC TOOLBOXES
  74.     Special Requirements
  75.     QuickBASIC vs Executable Files
  76.  ATTRIB
  77.  BIN2HEX
  78.  BIOSCALL
  79.  BITS
  80.  CALENDAR
  81.  CARTESIA
  82.  CIPHER
  83.  COLORS
  84.  COMPLEX
  85.  DOLLARS
  86.  DOSCALLS
  87.  EDIT
  88.  ERROR
  89.  FIGETPUT
  90.  FILEINFO
  91.  FRACTION
  92.  GAMES
  93.  HEX2BIN
  94.  JUSTIFY
  95.  KEYS
  96.  LOOK
  97.  MONTH
  98.  MOUSGCRS
  99.  MOUSSUBS
  100.  MOUSTCRS
  101.  OBJECT
  102.  PARSE
  103.  PROBSTAT
  104.  QBFMT
  105.  QBTREE
  106.  QCAL
  107.  QCALMATH
  108.  RANDOMS
  109.  STDOUT
  110.  STRINGS
  111.  TRIANGLE
  112.  WINDOWS
  113.  WORDCOUN
  114.  
  115.  PART III:  MIXED-LANGUAGE TOOLBOXES
  116.  USING MIXED-LANGUAGE TOOLBOXES
  117.     Near and Far Addressing
  118.     Passing Variables
  119.     Creating Mixed-Language Toolboxes
  120.  CDEMO1.BAS AND CTOOLS1.C
  121.  CDEMO2.BAS AND CTOOLS2.C
  122.  
  123.  PART IV:  APPENDIXES
  124.  APPENDIX A  Requirements for Running Toolboxes/Programs
  125.  APPENDIX B  Functions-to-Modules Cross Reference
  126.  APPENDIX C  Subprograms-to-Modules Cross Reference
  127.  APPENDIX D  Hexadecimal Format (.OBJ) Files
  128.  APPENDIX E  Line-Drawing Characters
  129.  
  130.  
  131.  
  132.  ────────────────────────────────────────────────────────────────────────────
  133.  PART 1  GETTING STARTED
  134.  
  135.  
  136.  
  137.  ────────────────────────────────────────────────────────────────────────────
  138.  Chapter One  QuickBASIC and Toolboxes
  139.  
  140.    Thanks to Microsoft QuickBASIC 4.0, BASIC has finally grown into a
  141.    flexible, full-featured, and powerful programming language. By thumbing
  142.    through this book and glancing at the program listings, you'll see that
  143.    BASIC isn't what it used to be. Microsoft QuickBASIC is easier to read,
  144.    has a faster learning curve, and gives you the power to quickly create
  145.    sophisticated programs that would have been difficult, if not impossible,
  146.    with traditional BASIC.
  147.  
  148.    A key difference between traditional BASIC and QuickBASIC is that
  149.    QuickBASIC allows structured programming, an important feature that makes
  150.    large programs easier to create and maintain.
  151.  
  152.  
  153.  Advantages of Structured Programming
  154.  
  155.    With early versions of BASIC, a program was written and executed as a
  156.    single block of program lines. Inexperienced programmers writing large
  157.    programs could unknowingly create "spaghetti code" (programs that make
  158.    frequent and improper use of GOTO statements) making them generally
  159.    difficult to follow and maintain.
  160.  
  161.    A key feature of QuickBASIC is its ability to let you create structured
  162.    programs──large programs constructed of small, individual program modules.
  163.    Instead of having to create and work with a large (and often overwhelming)
  164.    single block of code, the QuickBASIC programmer need only construct the
  165.    program modules, which are in turn constructed from procedures called
  166.    subprograms and functions. Each of these procedures performs a specific,
  167.    well-defined task. By concentrating on the functionality of a single
  168.    procedure, the programmer is freed from having to worry about other parts
  169.    of the program and can devote full concentration to the task at hand. It's
  170.    been proven that programmers can develop complex programs more quickly and
  171.    accurately using this modular approach.
  172.  
  173.    An additional advantage to structured programming is that these modules
  174.    and procedures can be organized and saved in such a way that they can be
  175.    reused with other programs──avoiding duplication of effort from one
  176.    programming project to another. By grouping modules with complementary
  177.    functionality, a programmer can easily create "toolboxes" of useful
  178.    routines that can, over time, make large programming projects progress
  179.    quickly because major portions of the program are already written.
  180.  
  181.    After construction, a module can also be organized into a Quick Library,
  182.    which is a file saved on disk in a special format. You can then load Quick
  183.    Libraries with the QuickBASIC program, effectively adding the routines in
  184.    the Quick Library to the ones built into QuickBASIC.
  185.  
  186.  
  187.  The Toolboxes in This Book
  188.  
  189.    If you are using (or even thinking of using) QuickBASIC, this book will be
  190.    a valuable reference. If you're only starting out and learning QuickBASIC
  191.    as a first language, you'll find the book immediately useful for learning
  192.    by example. If you're a seasoned, professional programmer using QuickBASIC
  193.    as a software development system, you'll find the routines in this book to
  194.    be valuable extensions to the QuickBASIC language.
  195.  
  196.    Part 1 provides step-by-step instructions for constructing a complete,
  197.    working program called MINICAL. Beginning programmers in particular will
  198.    find this tutorial helpful. Part II contains all the QuickBASIC toolboxes
  199.    and begins with a brief section that explains how to load and run them.
  200.    Part III describes the use of mixed-language toolboxes and contains
  201.    several examples. Finally, five appendixes contain information on the
  202.    requirements for running the toolboxes, cross references to functions and
  203.    subprograms, and additional important information.
  204.  
  205.    If you're an experienced programmer, you may want to skip ahead to Part
  206.    II and start using some of the toolboxes. If you're new to QuickBASIC,
  207.    turn now to the next section. You'll create two modules containing
  208.    functions and subprograms and use them to build a Quick Library. Once
  209.    you've learned the steps needed to create your own programs and your own
  210.    Quick Library, you can use the modules in Parts II and III of this book,
  211.    as well as modules of your own, to create even more Quick Libraries.
  212.    You'll soon have powerful toolboxes that you can use to build programs
  213.    quickly.
  214.  
  215.  
  216.  
  217.  ────────────────────────────────────────────────────────────────────────────
  218.  Chapter Two  MINICAL.BAS──A Complete Program
  219.  
  220.    In this section we will start from scratch and build a complete, working
  221.    program. We'll construct the program, build a Quick Library to extend the
  222.    QuickBASIC language, and build a stand-alone, executable program.
  223.  
  224.    Before we begin, let's take a quick look at the capabilities of the
  225.    QuickBASIC programming environment and at some of the major concepts
  226.    involved. The sample program in this section is made up of two separate
  227.    modules, each consisting of several subprograms and functions. Let's look
  228.    at how QuickBASIC handles each of these.
  229.  
  230.  
  231.  Modular Source-Code Editing
  232.  
  233.    One of the new features of Microsoft QuickBASIC 4.0 is the way that you
  234.    review and edit programs from within the QuickBASIC environment. If you've
  235.    programmed in QuickBASIC, perhaps you've noticed that a program comprising
  236.    several subprograms and functions can't be shown and edited all in one
  237.    piece on the screen. If you haven't yet programmed in QuickBASIC, you need
  238.    only know at this point that you select one subprogram or function from a
  239.    list of currently loaded subprograms and functions as the one you want to
  240.    view and edit. All the other routines are hidden from view. This might
  241.    seem strange at first, but after working on a few programs, you'll begin
  242.    to appreciate the power that this modular editing provides.
  243.  
  244.    A second major advance in QuickBASIC 4.0 is its ability to load into
  245.    memory more than one source-code file at a time. This opens the door to
  246.    creating collections of subprograms and functions, stored in separate
  247.    source-code files by subject, that several different programs can load and
  248.    use independently.
  249.  
  250.    The important concepts about these new features can be summarized in this
  251.    way: A program can be made up of one or more source-code files (modules),
  252.    and each source-code file can be made up of one or more subprograms or
  253.    functions. You can load several of these source files into the QuickBASIC
  254.    environment simultaneously, and all modules can work together to make a
  255.    complete program. Although you can display and edit only one portion of a
  256.    source file at a time, it's easy to jump from one portion to another while
  257.    editing a program.
  258.  
  259.  
  260.  Building a Quick Library
  261.  
  262.    Wouldn't it be nice to create new QuickBASIC statements and functions that
  263.    you could add to the language in such a way that they'd be available for
  264.    your use every time you fired up QuickBASIC? That's what Quick Libraries
  265.    can do for you!
  266.  
  267.    For example, suppose you use hyperbolic functions in almost every program
  268.    you write. You could create these functions in a source-code file to be
  269.    loaded into memory along with each main program you write, or you could
  270.    create a Quick Library so that these functions load at the same time you
  271.    load QuickBASIC. To build a Quick Library, you would simply load the
  272.    hyperbolic function source-code file and select the appropriate menu
  273.    choices from the Run menu.
  274.  
  275.  
  276.  Creating the Source Code for MINICAL
  277.  
  278.    Let's walk through the creation of a complete programming project, step by
  279.    step, to get your feet wet. Fire up your computer and follow along to get
  280.    the maximum benefit. The MINICAL program performs five functions:
  281.    addition, subtraction, multiplication, division, and square root. The
  282.    program is simple in scope, yet it has most of the major components of
  283.    much larger software projects.
  284.  
  285.    Before we start coding, let's look at how the program should run once we
  286.    get it built. The MINICAL program uses Reverse Polish Notation (RPN) for
  287.    input. Using RPN simplifies the programming considerably because it
  288.    eliminates the coding necessary to rearrange those math commands
  289.    enshrouded in parentheses.
  290.  
  291.    Using RPN, you enter numbers first, followed by the operators. For
  292.    example, to add 3 and 4, you would enter:
  293.  
  294.  
  295.      3 4 +
  296.  
  297.    (We'll show how these numbers are actually entered later in the section.)
  298.    To add 1 and 2 and then multiply the result by 3, you would enter:
  299.  
  300.  
  301.      1 2 + 3 *
  302.  
  303.    MINICAL uses double-precision numbers, so you can enter any type of
  304.    integer or floating-point numeric values. Results are displayed using as
  305.    many as 16 digits. For example, to divide 1.2 by -3.45, you would enter:
  306.  
  307.  
  308.      1.2 -3.45 /
  309.  
  310.    and the display would read:
  311.  
  312.  
  313.      Result... -.3478260869565217
  314.  
  315.    NOTE: Because a computer keyboard does not have x and ÷ keys, an asterisk
  316.    (*) is used for multiplication and a forward slash (/) is used for
  317.    division.
  318.  
  319.    By the way, MINICAL uses a structure called a stack to hold the numbers
  320.    and the operators while performing the calculations. (Technically, the
  321.    structure used in MINICAL only mimics a traditional stack, but for
  322.    discussion purposes it can be thought of as a stack.) A stack is a
  323.    sequential series of memory locations set aside to hold a number of
  324.    separate items──in this case, the numbers and operators provided by the
  325.    user. RPN is used for specifying numbers and operators primarily because
  326.    of the existence of the stack──the RPN syntax is ideal for stack-based
  327.    calculations. The alternative method, parsing, involves "reading" the
  328.    equation entered by the user, rearranging and selecting the separate
  329.    elements, and acting on them. This latter method involves much more
  330.    coding.
  331.  
  332.    The stack in MINICAL can hold as many as 20 values plus the associated
  333.    operators. You can enter numbers as explained above, or you can enter all
  334.    numbers and then the operators. For example, to add the numbers 1 through
  335.    5, you can enter either of these two command lines:
  336.  
  337.  
  338.      1 2 + 3 + 4 + 5 +
  339.      1 2 3 4 5 + + + +
  340.  
  341.  The MINIMATH Module
  342.  
  343.    This project consists of two parts: the MINIMATH module and the MINICAL
  344.    module. Let's begin with MINIMATH.
  345.  
  346.    If you haven't done so yet, at the system prompt type QB and press Enter
  347.    to start QuickBASIC. Type in the title block on the following page.
  348.  
  349.    ──────────────────────────────────────────────────────────────────────────
  350.      ' ************************************************
  351.      ' **  Name:          MINIMATH                   **
  352.      ' **  Type:          Toolbox                    **
  353.      ' **  Module:        MINIMATH.BAS               **
  354.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  355.      ' ************************************************
  356.      '
  357.      ' Collection of math subprograms for the MINICAL
  358.      ' program.
  359.    ──────────────────────────────────────────────────────────────────────────
  360.  
  361.    At this point, it's convenient to tell QuickBASIC this module's name. Pull
  362.    down the File menu and choose Save As. Type the filename MINIMATH, and
  363.    press the Enter key or move your mouse pointer to the OK box and click the
  364.    left mouse button. The file is then saved to disk, and you're ready to
  365.    continue entering more of the program. Note that if you omit the .BAS
  366.    extension, as you did here, QuickBASIC automatically adds it for you.
  367.  
  368.    This first module will be made up of the five subprograms that perform the
  369.    math functions. First, let's create the Add subprogram. Pull down the Edit
  370.    menu and choose New SUB. When you're asked for the name of the subprogram,
  371.    respond with Add. QuickBASIC then creates the first and last lines of your
  372.    new subprogram:
  373.  
  374.  
  375.      SUB Add
  376.      END SUB
  377.  
  378.    Note that QuickBASIC also adjusts the editing window so that only the Add
  379.    subprogram is displayed, allowing you to concentrate on this subprogram
  380.    only. (You'll greatly appreciate this feature later on, when your
  381.    programming projects become larger.) The next step is to add comment
  382.    information before the first line of the subprogram and to insert the
  383.    "guts" of the subprogram between the two lines displayed by QuickBASIC.
  384.  
  385.    Start by adding comments. Move the cursor to the first character of the
  386.    first line of the subprogram and press Enter. When you do, a dialog box
  387.    appears with the message Blank lines not allowed before SUB/FUNCTION line.
  388.    Is remark OK? Since a remark (comment) is what you want, choose OK.
  389.    QuickBASIC then inserts a blank line preceded by a ' character for the
  390.    comment. After you type the first comment line (taken from the lines on
  391.    the following page) and press Enter, the dialog box appears again. Click
  392.    OK and then type the next comment line.
  393.  
  394.    Use the lines below to build the Add subprogram. Note the locations of the
  395.    SUB Add and END SUB lines, and note that you need to append items to the
  396.    SUB Add line. Also note that some lines are indented: Although this
  397.    indention is not required, it is good programming style to indent
  398.    subordinate lines so that the program is easier to read and relationships
  399.    between lines are visually apparent. When you're done, your Add subprogram
  400.    should look exactly like this:
  401.  
  402.    ──────────────────────────────────────────────────────────────────────────
  403.      ' ************************************************
  404.      ' **  Name:          Add                        **
  405.      ' **  Type:          Subprogram                 **
  406.      ' **  Module:        MINIMATH.BAS               **
  407.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  408.      ' ************************************************
  409.      '
  410.      ' Performs addition for the MINICAL program.
  411.      '
  412.        SUB Add (stack#(), ptr%) STATIC
  413.            ptr% = ptr% - 1
  414.            IF ptr% THEN
  415.                stack#(ptr%) = stack#(ptr%) + stack#(ptr% + 1)
  416.            END IF
  417.        END SUB
  418.    ──────────────────────────────────────────────────────────────────────────
  419.  
  420.    You can save your work at any point. (It's a good idea to back up your
  421.    work often to prevent losing your work in case of a power failure or other
  422.    disaster.) Do so now by pulling down the File menu and choosing Save All.
  423.  
  424.    You're well on your way now! You will create and edit the other four math
  425.    subprograms (shown on pages 11-12) in the same way.
  426.  
  427.    Here's a tip that can speed up the process. Notice that the initial
  428.    comment lines for each of the five subprograms are nearly identical. So,
  429.    instead of retyping each one, you'll copy the lines you typed in for the
  430.    Add subprogram and paste them into the other subprograms. Let's do that
  431.    now.
  432.  
  433.    First, set up the new subprograms. For the Subtract subprogram, choose
  434.    New SUB from the Edit menu, enter the name Subtract, and press Enter.
  435.    After the two lines of the Subtract subprogram are displayed, repeat the
  436.    New SUB process for the Multiply, Divide, and SquareRoot subprograms.
  437.    Be sure you type the subprogram names as shown here──each starts with a
  438.    capital letter, and no space is allowed in the subprogram name SquareRoot.
  439.  
  440.    Next, make a copy of the comment lines in the Add subprogram. Pull down
  441.    the View menu and choose SUBS. QuickBASIC displays a list of all
  442.    subprogram names you entered. Here's where the power of QuickBASIC is
  443.    really apparent: Using the SUBS command, you can jump to any subprogram
  444.    for editing by simply selecting the name of the desired subprogram. You do
  445.    this by double-clicking on the desired subprogram name with your mouse or
  446.    by using the cursor movement keys followed by the Enter key. For now,
  447.    choose Add.
  448.  
  449.    To copy the comment lines, move the cursor to the first character of the
  450.    first comment line, and then (using the keyboard) hold down either Shift
  451.    key and press the Down arrow key until all comment lines are highlighted;
  452.    or (using the mouse) move the mouse pointer to the location of the cursor,
  453.    hold down the left mouse button, and drag down until all comment lines are
  454.    highlighted. Finally, choose Copy from the Edit menu to copy the
  455.    highlighted lines to the Clipboard.
  456.  
  457.    Next, copy those lines into the four other subprograms. Choose SUBS from
  458.    the View menu to again display a list of subprograms. Select Subtract,
  459.    move the cursor to the first character of the first line, pull down the
  460.    Edit menu, and choose Paste. The lines copied from the Add subprogram
  461.    should appear. (If they don't or if you get a dialog box telling you that
  462.    blank lines can't appear before a subprogram, go back to the Add
  463.    subprogram and repeat the Edit-Copy process. Remember to copy only comment
  464.    lines──those preceded by a ' character.)
  465.  
  466.    Select the remaining subprogram names in the same way, and repeat the
  467.    Edit-Paste process. You don't have to repeat the Copy operation, because
  468.    an item copied to the Clipboard stays there until something else is copied
  469.    to the Clipboard or until you quit QuickBASIC. When you're done, go back
  470.    to each subprogram. Edit the comment lines and enter the program lines as
  471.    you did for the Add subprogram. Be sure to choose Save All from the File
  472.    menu after completing each subprogram. Also, be sure to go back and review
  473.    your work──the ability to view each subprogram separately using the SUBS
  474.    option on the View menu makes this important task easier and your program
  475.    clearer to read. Your results should match the four subprograms on the
  476.    following two pages.
  477.  
  478.    NOTE: Don't forget to edit the comments! Remember──the comments you pasted
  479.    in were the comments for the Add subprogram. You must change the name in
  480.    each comment block and the information on the line below the block to
  481.    reflect the subprogram the comments identify.
  482.  
  483.    The following is the Subtract subprogram:
  484.  
  485.    ──────────────────────────────────────────────────────────────────────────
  486.      ' ************************************************
  487.      ' **  Name:          Subtract                   **
  488.      ' **  Type:          Subprogram                 **
  489.      ' **  Module:        MINIMATH.BAS               **
  490.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  491.      ' ************************************************
  492.      '
  493.      ' Performs subtraction for the MINICAL program.
  494.      '
  495.        SUB Subtract (stack#(), ptr%) STATIC
  496.            ptr% = ptr% - 1
  497.            IF ptr% THEN
  498.                stack#(ptr%) = stack#(ptr%) - stack#(ptr% + 1)
  499.            END IF
  500.        END SUB
  501.    ──────────────────────────────────────────────────────────────────────────
  502.  
  503.    The following is the Multiply subprogram:
  504.  
  505.    ──────────────────────────────────────────────────────────────────────────
  506.      ' ************************************************
  507.      ' **  Name:          Multiply                   **
  508.      ' **  Type:          Subprogram                 **
  509.      ' **  Module:        MINIMATH.BAS               **
  510.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  511.      ' ************************************************
  512.      '
  513.      ' Performs multiplication for the MINICAL program.
  514.      '
  515.        SUB Multiply (stack#(), ptr%) STATIC
  516.            ptr% = ptr% - 1
  517.            IF ptr% THEN
  518.                stack#(ptr%) = stack#(ptr%) * stack#(ptr% + 1)
  519.            END IF
  520.        END SUB
  521.    ──────────────────────────────────────────────────────────────────────────
  522.  
  523.    The following is the Divide subprogram:
  524.  
  525.    ──────────────────────────────────────────────────────────────────────────
  526.      ' ************************************************
  527.      ' **  Name:          Divide                     **
  528.      ' **  Type:          Subprogram                 **
  529.      ' **  Module:        MINIMATH.BAS               **
  530.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  531.      ' ************************************************
  532.      '
  533.      ' Performs division for the MINICAL program.
  534.      '
  535.        SUB Divide (stack#(), ptr%) STATIC
  536.            ptr% = ptr% - 1
  537.            IF ptr% THEN
  538.                stack#(ptr%) = stack#(ptr%) / stack#(ptr% + 1)
  539.            END IF
  540.        END SUB
  541.    ──────────────────────────────────────────────────────────────────────────
  542.  
  543.    The following is the SquareRoot subprogram:
  544.  
  545.    ──────────────────────────────────────────────────────────────────────────
  546.      ' ************************************************
  547.      ' **  Name:          SquareRoot                 **
  548.      ' **  Type:          Subprogram                 **
  549.      ' **  Module:        MINIMATH.BAS               **
  550.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  551.      ' ************************************************
  552.      '
  553.      ' Determines square root for the MINICAL program.
  554.      '
  555.        SUB SquareRoot (stack#(), ptr%) STATIC
  556.            stack#(ptr%) = SQR(stack#(ptr%))
  557.        END SUB
  558.    ──────────────────────────────────────────────────────────────────────────
  559.  
  560.    This completes the first part of the MINICAL program. The subprograms you
  561.    created and saved as MINIMATH form the heart of MINICAL because they
  562.    perform the actual calculations. The next part involves creating MINICAL
  563.    itself. MINICAL performs the overhead work──taking the numbers the user
  564.    wants to calculate, passing them to the appropriate subprograms in
  565.    MINIMATH, and displaying the result.
  566.  
  567.  The MINICAL Module
  568.  
  569.    From here, you can proceed in one of two ways. Because both MINIMATH and
  570.    MINICAL are small, you can build the MINICAL program entirely in memory by
  571.    creating a second module named MINICAL. (You do this by choosing Create
  572.    File from the File menu, accepting the default choice of Module, and then
  573.    choosing OK.) Or you can turn MINIMATH into a Quick Library and load it
  574.    with the QuickBASIC system so that it becomes an extension to the
  575.    language. We'll use the second method to see how easy it is to use this
  576.    advanced QuickBASIC feature.
  577.  
  578.    To create a Quick Library, pull down the Run menu and choose Make Library.
  579.    You'll be asked to name the library. MINIMATH will be fine, because
  580.    QuickBASIC automatically appends a default extension of .QLB for the Quick
  581.    Library and .LIB for the normal library it also builds. After you type
  582.    MINIMATH, choose Make Library. (You can ignore the other options in the
  583.    dialog box for now.) When completed, QuickBASIC will have created two new
  584.    files in the current directory: MINIMATH.QLB and MINIMATH.LIB.
  585.  
  586.    Now quit QuickBASIC so that you can restart it and load the new Quick
  587.    Library with it. To do this, pull down the File menu and choose Exit. Then
  588.    start QuickBASIC from the system prompt by entering:
  589.  
  590.  
  591.      QB /L MINIMATH
  592.  
  593.    You'll see no obvious sign that anything is different, but a very exciting
  594.    event has actually taken place! Your QuickBASIC has been extended. It's
  595.    now more than it used to be. The subprograms in MINIMATH are part of the
  596.    QuickBASIC language, ready to be used like many of the other QuickBASIC
  597.    keywords. In fact, because you can optionally use the CALL keyword when
  598.    calling subprograms, these new subprograms will appear much like new
  599.    keywords in QuickBASIC.
  600.  
  601.    Proceed with the rest of the program now so that you can try out the new,
  602.    extended QuickBASIC. Be sure QuickBASIC is loaded, as described earlier,
  603.    with the MINIMATH Quick Library as part of the system. Then type in the
  604.    following program, MINICAL.BAS:
  605.  
  606.    ──────────────────────────────────────────────────────────────────────────
  607.      ' ************************************************
  608.      ' **  Name:          MINICAL                    **
  609.      ' **  Type:          Program                    **
  610.      ' **  Module:        MINICAL.BAS                **
  611.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  612.      ' ************************************************
  613.  
  614.      ' Functions
  615.        DECLARE FUNCTION NextParameter$ (cmd$)
  616.  
  617.      ' Subprograms
  618.        DECLARE SUB Process (cmd$, stack#(), ptr%)
  619.        DECLARE SUB DisplayStack (stack#(), ptr%)
  620.        DECLARE SUB Add (stack#(), ptr%)
  621.        DECLARE SUB Subtract (stack#(), ptr%)
  622.        DECLARE SUB Multiply (stack#(), ptr%)
  623.        DECLARE SUB Divide (stack#(), ptr%)
  624.        DECLARE SUB SquareRoot (stack#(), ptr%)
  625.  
  626.      ' Get the command line
  627.        cmd$ = COMMAND$
  628.  
  629.      ' Create a pseudo stack
  630.        DIM stack#(1 TO 20)
  631.        ptr% = 0
  632.  
  633.      ' Process each part of the command line
  634.        DO UNTIL cmd$ = ""
  635.            parm$ = NextParameter$(cmd$)
  636.            Process parm$, stack#(), ptr%
  637.            IF ptr% < 1 THEN
  638.                PRINT "Not enough stack values"
  639.                SYSTEM
  640.            END IF
  641.        LOOP
  642.  
  643.      ' Display results
  644.        DisplayStack stack#(), ptr%
  645.  
  646.      ' All done
  647.        END
  648.    ──────────────────────────────────────────────────────────────────────────
  649.  
  650.    This is the main part of the MINICAL program, where all the action begins.
  651.    Note the first two lines in the DO-LOOP structure, the ones that read
  652.    parm$ = NextParameter$(cmd$) and Process parm$, stack#(), ptr%. The first
  653.    line calls the user-defined function named NextParameter$, and the second
  654.    line calls the user-defined subprogram named Process. (No, you haven't
  655.    defined them yet. That's next on the list of tasks to do.) Notice that the
  656.    keyword CALL was not used to call the Process subprogram. You can use CALL
  657.    if desired, but there's no need to anymore. Because of the way QuickBASIC
  658.    deals with subprograms, the Process subprogram that you'll create shortly
  659.    will be more like part of the QuickBASIC system, rather than part of the
  660.    program, because you can't list it or modify it while this portion of the
  661.    program is on the screen. You also don't have to think about it or
  662.    recompile it! Your creative energies are free to tackle the next higher
  663.    level of the program's complexity.
  664.  
  665.    Once you've entered the main program's lines, it's again time to save this
  666.    module to disk. Select Save As from the File menu and enter the filename
  667.    MINICAL.
  668.  
  669.    You still have a few pieces of coding to do before you can try the
  670.    program. To create the one function of this program, select New Function
  671.    from the Edit menu. Type in the function name NextParameter$, and press
  672.    the Enter key. Creating and editing functions are really no different from
  673.    creating and editing subprograms. In fact, the only major difference
  674.    between a function and a subprogram is that a function returns a value to
  675.    be used in a calculation or assigned to a QuickBASIC variable. Subprograms
  676.    return values only through passed variables. Follow the same steps you
  677.    used to create the subprograms in MINIMATH to create the function
  678.    NextParameter$:
  679.  
  680.    ──────────────────────────────────────────────────────────────────────────
  681.      ' ************************************************
  682.      ' **  Name:          NextParameter$             **
  683.      ' **  Type:          Function                   **
  684.      ' **  Module:        MINICAL.BAS                **
  685.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  686.      ' ************************************************
  687.      '
  688.      ' Extracts parameters from the front of the
  689.      ' command line. Parameters are groups of any
  690.      ' characters separated by spaces.
  691.      '
  692.        FUNCTION NextParameter$ (cmd$) STATIC
  693.            parm$ = ""
  694.            DO WHILE LEFT$(cmd$, 1) <> " " AND cmd$ <> ""
  695.                parm$ = parm$ + LEFT$(cmd$, 1)
  696.                cmd$ = MID$(cmd$, 2)
  697.            LOOP
  698.            DO WHILE LEFT$(cmd$, 1) = " " AND cmd$ <> ""
  699.                cmd$ = MID$(cmd$, 2)
  700.            LOOP
  701.            NextParameter$ = parm$
  702.        END FUNCTION
  703.    ──────────────────────────────────────────────────────────────────────────
  704.  
  705.    Now create and edit the following two subprograms as part of the MINICAL
  706.    module.
  707.  
  708.    Create the subprogram DisplayStack:
  709.  
  710.    ──────────────────────────────────────────────────────────────────────────
  711.      ' ************************************************
  712.      ' **  Name:          DisplayStack               **
  713.      ' **  Type:          Subprogram                 **
  714.      ' **  Module:        MINICAL.BAS                **
  715.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  716.      ' ************************************************
  717.      '
  718.      ' Displays anything left on the stack when MINICAL
  719.      ' finishes processing the command line.
  720.      '
  721.        SUB DisplayStack (stack#(), ptr%) STATIC
  722.            PRINT
  723.            IF ptr% > 1 THEN
  724.                PRINT "Stack... ",
  725.            ELSE
  726.                PRINT "Result... ",
  727.            END IF
  728.            FOR i% = 1 TO ptr%
  729.                PRINT stack#(i%),
  730.            NEXT i%
  731.            PRINT
  732.        END SUB
  733.    ──────────────────────────────────────────────────────────────────────────
  734.  
  735.    Next create the subprogram Process:
  736.  
  737.    ──────────────────────────────────────────────────────────────────────────
  738.      ' ************************************************
  739.      ' **  Name:          Process                    **
  740.      ' **  Type:          Subprogram                 **
  741.      ' **  Module:        MINICAL.BAS                **
  742.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  743.      ' ************************************************
  744.      '
  745.      ' Processes each command parameter for the MINICAL
  746.      ' program.
  747.      '
  748.        SUB Process (parm$, stack#(), ptr%) STATIC
  749.            SELECT CASE parm$
  750.            CASE "+"
  751.                Add stack#(), ptr%
  752.            CASE "-"
  753.                Subtract stack#(), ptr%
  754.            CASE "*"
  755.                Multiply stack#(), ptr%
  756.            CASE "/"
  757.                Divide stack#(), ptr%
  758.            CASE "SQR"
  759.                SquareRoot stack#(), ptr%
  760.            CASE ELSE
  761.                ptr% = ptr% + 1
  762.                stack#(ptr%) = VAL(parm$)
  763.            END SELECT
  764.        END SUB
  765.    ──────────────────────────────────────────────────────────────────────────
  766.  
  767.    Be sure you save your efforts on disk by selecting Save All from the File
  768.    menu.
  769.  
  770.    You've done it! One last detail remains, however. This program reads the
  771.    command line and assumes that numbers and operators were typed in
  772.    following the name of the program at the system prompt. Fortunately,
  773.    QuickBASIC provides a mechanism to let you type in a command line, even
  774.    though you're currently going to be running the program in memory from the
  775.    QuickBASIC system. From the Run menu select Modify COMMAND$. You'll be
  776.    asked to enter a new command line. Enter this for the first try:
  777.  
  778.  
  779.      3 4 +
  780.  
  781.    Everything should be in place now, so try running the program. Select
  782.    Start from the Run menu. If all is well, you'll see the following:
  783.  
  784.  
  785.      Result...  7
  786.  
  787.    If all is not well, you'll probably find yourself staring at an error
  788.    message from QuickBASIC, describing an error that's probably the indirect
  789.    result of a typographical error. If so, double-check your typing, and
  790.    rebuild the library if the problem was in the MINIMATH module.
  791.  
  792.    Once you get the program working, take a little time to try different
  793.    command line parameters. See what happens if several numbers are placed on
  794.    the stack but not enough operators are given to reduce the stack to a
  795.    final result. For example, try entering:
  796.  
  797.  
  798.      3 4 5 6 7 + *
  799.  
  800.    Also, find out what happens if not enough numbers are on the stack. For
  801.    example, enter:
  802.  
  803.  
  804.      3 4 + *
  805.  
  806.  
  807.  Compiling and Running as an Executable (.EXE) Program
  808.  
  809.    Finally, to see how you can create programs that you can run from MS-DOS,
  810.    select Make EXE File from the Run menu. You can create two types of .EXE
  811.    files. The first type results in a smaller MINICAL.EXE file, but it
  812.    requires access to the QuickBASIC file named BRUN40.EXE at runtime. The
  813.    second type results in a larger MINICAL.EXE file that stands completely on
  814.    its own. When you select Make EXE File from the Run menu, you are prompted
  815.    to select the type of .EXE file you want to create. Try it both ways. Take
  816.    a look at the resulting file sizes, and note that the BRUN40.EXE file must
  817.    be accessible in the current directory or in a place defined by the MS-DOS
  818.    PATH setting. (Your QuickBASIC manual discusses this subject in more
  819.    detail.)
  820.  
  821.    Either way, running the MINICAL.EXE program from the system prompt uses
  822.    the command line in the way that COMMAND$ expects. For example, to
  823.    subtract 5 from 17, enter the following at the system prompt:
  824.  
  825.  
  826.      MINICAL 17 5 -
  827.  
  828.    While building MINICAL, you've learned how easy it is to create toolboxes
  829.    of your own or to edit existing toolboxes. Turn now to Part II of this
  830.    book. Be sure to read the first section, which explains how to use the
  831.    QuickBASIC toolboxes. Then choose a toolbox that interests you, and have
  832.    fun.
  833.  
  834.  
  835.  
  836.  ────────────────────────────────────────────────────────────────────────────
  837.  PART 2  QUICKBASIC TOOLBOXES AND PROGRAMS
  838.  
  839.  
  840.  
  841.  ────────────────────────────────────────────────────────────────────────────
  842.  Chapter Three  Using QuickBASIC Toolboxes
  843.  
  844.    The toolboxes in Part II cover a wide range of topics and are presented
  845.    alphabetically by subject. Each is designed to be loaded and called by
  846.    user-written application software. You can run the demo module that begins
  847.    each toolbox to illustrate the routines within the toolbox, you can ignore
  848.    the demo module and use the routines as they are written, or you can
  849.    restructure the routines so that they meet your application requirements.
  850.  
  851.    The toolboxes and utility programs do not require any knowledge of
  852.    previously presented toolboxes, so you can run them in any order. Try them
  853.    all at least once, and review the code as you run them. You'll find unique
  854.    techniques and programming concepts in most of the listings. You'll also
  855.    find that these toolbox routines, along with your own creations, will be
  856.    useful in your future programming projects.
  857.  
  858.    Many of the utility programs use command line input or the COMMAND$
  859.    variable from within QuickBASIC to pass values or parameters to the
  860.    program. Others (those using toolboxes from different programs) might
  861.    require an associated .MAK file. A few programs require color and graphics
  862.    capability, and others require a mouse. Check the comments at the
  863.    beginning of each listing or Appendix A to determine environmental and
  864.    running requirements for each toolbox and utility program.
  865.  
  866.  
  867.  Special Requirements
  868.  
  869.    The following toolboxes require that the MIXED.QLB Quick Library be loaded
  870.    into memory with QuickBASIC: BIOSCALL, CDEMO1, CDEMO2, COLORS,
  871.    DOSCALLS, FILEINFO, MOUSGCRS, MOUSSUBS, MOUSTCRS, QBTREE, STDOUT,
  872.    and WINDOWS. MIXED.QLB consists of a handful of subprograms and functions
  873.    written in assembly language and in Microsoft QuickC. (The
  874.    assembly-language and C source listings for MIXED.QLB are in Part III.)
  875.  
  876.    Although MIXED.QLB isn't required by all toolboxes in this book, it's a
  877.    good idea to load it each time you start QuickBASIC to use the toolboxes
  878.    in this book. Toolboxes that do not require its presence will not be
  879.    affected if MIXED.QLB is loaded.
  880.  
  881.    Below are instructions for creating MIXED.QLB, which was written in
  882.    Microsoft QuickC and assembly language to demonstrate how other languages
  883.    can be used with QuickBASIC. It is beyond the scope of this book to
  884.    explain in detail mixed-language programming concepts, so simply follow
  885.    the steps presented here to create MIXED.QLB. In Part III of the book, you
  886.    will have the opportunity to try some examples of mixed-language programs.
  887.  
  888.  Creating MIXED.QLB
  889.  
  890.    If you own Microsoft QuickC, the first step is to compile an object-code
  891.    file for CTOOLS1.C and CTOOLS2.C.
  892.  
  893.    You can load the C source-code files from the companion disk if you have
  894.    purchased it, or you can type them in yourself. You will find CTOOLS1.C
  895.    on page 445, and CTOOLS2.C on page 462. Once you have these files, enter
  896.    the following commands at the system prompt to compile the CTOOLS1.C and
  897.    CTOOLS2.C source-code files to create object-code files:
  898.  
  899.  
  900.      QCL /Ox /AM /Gs /c CTOOLS1.C
  901.      QCL /Ox /AM /Gs /c CTOOLS2.C
  902.  
  903.    NOTE: If you don't have QuickC, you can still create MIXED.QLB. Compile
  904.    the assembly-language source-code files as explained below. You will then
  905.    be able to run all toolboxes in this book except CDEMO1 and CDEMO2.
  906.  
  907.    If you have version 5.0 or later of the Microsoft Macro Assembler, load
  908.    the assembly-language source-code files from the companion disk or type
  909.    them in. MOUSE.ASM is on page 437 and CASEMAP.ASM is on page 436. The
  910.    third assembly-language file, INTRPT.ASM, is part of QuickBASIC itself and
  911.    can be found on the disk that comes with the program. Then, enter the
  912.    following commands at the system prompt to compile the source-code files
  913.    into object-code files:
  914.  
  915.  
  916.      MASM MOUSE;
  917.      MASM CASEMAP;
  918.      MASM INTRPT;
  919.  
  920.    If you have an earlier version of the Microsoft Macro Assembler, follow
  921.    the guidelines in your QuickBASIC documentation to replace the .MODEL
  922.    directives with appropriate statements.
  923.  
  924.    If you don't have the Microsoft Macro Assembler, you can use HEX2BIN (on
  925.    pages 210 and 211) to convert the MOUSE.HEX, CASEMAP.HEX, and INTRPT.HEX
  926.    files into object-code files. The hexadecimal character files are listed
  927.    in Appendix D.
  928.  
  929.    Once you've created the object-code files, you can build the MIXED library
  930.    files to use with QuickBASIC. (Note that two files will be created:
  931.    MIXED.QLB and MIXED.LIB. MIXED.QLB will be loaded with the QuickBASIC
  932.    program because it is needed by some toolboxes; MIXED.LIB will be used for
  933.    creating stand-alone programs that can be executed directly from MS-DOS.)
  934.    The following commands accomplish this task:
  935.  
  936.  
  937.      LINK /Q INTRPT+MOUSE+CASEMAP+CTOOLS1+CTOOLS2,MIXED.QLB,,BQLB40.LIB;
  938.      DEL MIXED.LIB
  939.      LIB MIXED.LIB+INTRPT+MOUSE+CASEMAP+CTOOLS1+CTOOLS2;
  940.  
  941.    NOTE: If you don't have QuickC, remember that you cannot run CDEMO1 and
  942.    CDEMO2; therefore, you must delete +CTOOLS1 and +CTOOLS2 from the above
  943.    commands.
  944.  
  945.    If you have a problem, the cause might be that the necessary files can't
  946.    be located. Try moving all the files and programs into the current
  947.    directory, including the programs LINK.EXE and LIB.EXE; the QuickBASIC
  948.    library file, BQLB40.LIB; and, if you have Quick C, MLIBCE.LIB.
  949.  
  950.    Finally, after the MIXED.QLB and MIXED.LIB files are successfully created,
  951.    enter the following lines to create a file named Q.BAT:
  952.  
  953.  
  954.      COPY CON Q.BAT
  955.      QB /L MIXED.QLB
  956.      ^Z
  957.  
  958.    NOTE: The ^Z is obtained by pressing F6 or Ctrl-Z.
  959.  
  960.    Using this batch file automates the loading process so that MIXED.QLB
  961.    loads along with QuickBASIC. To use it, type Q and press the Enter key at
  962.    the system prompt.
  963.  
  964.  Using a .MAK File
  965.  
  966.    Those toolboxes that consist of more than one module require a .MAK file.
  967.    When you save a program consisting of more than one module, QuickBASIC
  968.    automatically creates a .MAK file so that it knows where to locate each
  969.    module the next time it loads the program. If the .MAK file is not
  970.    available, or if you must create a new .MAK file, you must load each
  971.    module from within QuickBASIC by selecting Load File from the File menu,
  972.    selecting the module to load, and then repeating the process for each
  973.    additional module. After loading all the modules, choose Save All from the
  974.    File menu and QuickBASIC creates the new .MAK file. Appendix A lists all
  975.    toolboxes and required .MAK files.
  976.  
  977.  
  978.  QuickBASIC vs Executable Files
  979.  
  980.    You can run the demo modules and utility programs from within the
  981.    QuickBASIC environment by selecting the applicable source-code file, or
  982.    you can compile the code and create files to execute directly from MS-DOS.
  983.    Source-code files are those with the .BAS file extension. Executable files
  984.    are created from .BAS files from within the QuickBASIC environment and are
  985.    saved with a .EXE file extension. All toolbox and utility programs on the
  986.    companion diskettes are .BAS files. The steps necessary for loading and
  987.    running the programs and toolboxes are simple.
  988.  
  989.  Running Programs from the QuickBASIC Environment
  990.  
  991.    Check your QuickBASIC manual, "Learning and Using Microsoft QuickBASIC,"
  992.    for starting QuickBASIC on your system. When the program starts, you are
  993.    ready to load and run a demo module or utility program. Using the
  994.    keyboard, the steps are:
  995.  
  996.    1.  Press the Alt key and then F to display the File menu.
  997.  
  998.    2.  Press O to choose the Open Program command.
  999.  
  1000.    3.  Select the demo module or program from the list of .BAS files shown.
  1001.        If the file is not shown, you must set the path to the drive and
  1002.        directory where the file resides. First, type the correct path in the
  1003.        File Name box and press Enter to display the .BAS files in that path.
  1004.        Then type the name of the file you want to load, or use the Tab key to
  1005.        move to the list box and use the arrow keys to select the filename you
  1006.        want and press Enter. Filenames are displayed in all lowercase,
  1007.        directory names in all uppercase. You can also select directory names,
  1008.        including the parent(..) directory, in the list box until the .BAS
  1009.        files you want are displayed.
  1010.  
  1011.    4.  If command line parameters are required for execution, press Alt and
  1012.        then R to pull down the Run menu, and then press C to choose the
  1013.        Modify COMMAND$ option. Type the value(s) or input parameter(s)
  1014.        separated by spaces in the dialog box provided, and press the Enter
  1015.        key. You are now ready to execute the program and can do so by
  1016.        pressing Alt-R again and then S (or in this case Enter because the
  1017.        option is already highlighted) to choose the Start option.
  1018.  
  1019.    NOTE: Each listing contains a USAGE line within the comments to let you
  1020.    know when user input is expected and in what format. Parameters might be
  1021.    other filenames, numeric values, alphanumeric characters, math functions,
  1022.    drive designators, paths to directories and subdirectories, or symbols.
  1023.    You can also find parameters in Appendix A.
  1024.  
  1025.    5.  If no command line parameters are required, simply press Alt-R and
  1026.        then S to start the selected module. (You can also press Shift-F5 to
  1027.        start.)
  1028.  
  1029.    These steps are basically the same for systems using a mouse device.
  1030.    Instead of pressing the Alt key, however, you move the mouse pointer to
  1031.    the desired menu names, menu options, and dialog box fields and press the
  1032.    left mouse button to make dialog box selections and execute menu commands.
  1033.  
  1034.    If you receive no response or the program doesn't seem to work correctly,
  1035.    look up the module in Appendix A and check that .MAK files, libraries
  1036.    (including MIXED.QLB), color graphics requirements, and so on are resident
  1037.    and that the paths to them are properly set.
  1038.  
  1039.  Running Programs as Executable Files
  1040.  
  1041.    Some of the utility programs, especially those expecting command line
  1042.    variable input, are more conveniently run as stand-alone executable files
  1043.    (.EXE). If you plan to develop commercial or public domain software, the
  1044.    .EXE format is usually preferable.
  1045.  
  1046.    Before you can run the toolboxes and utility programs directly from
  1047.    MS-DOS, you must first compile a .BAS file or files using a special option
  1048.    to create a .EXE file. Two options are available for compiling programs.
  1049.    The first option is to compile the source code into a stand-alone .EXE
  1050.    file that runs by itself when executed from MS-DOS. The file, however,
  1051.    will be quite large, even if it is a simple application or module. The
  1052.    second option is to create a .EXE file that requires another file,
  1053.    BRUN40.EXE, to be in the same drive and directory at runtime (when you
  1054.    execute the program). The resulting compiled program file will be much
  1055.    smaller, but BRUN40.EXE must always accompany the executable file.
  1056.  
  1057.    To create a stand-alone executable file from within QuickBASIC, follow the
  1058.    steps below. Refer to your QuickBASIC manual for instructions on how to
  1059.    create other .EXE files from within the QuickBASIC environment or from
  1060.    MS-DOS using BC.EXE.
  1061.  
  1062.    1.  Load the file using steps 1 through 3 above in "Running Programs from
  1063.        the QuickBASIC Environment."
  1064.  
  1065.    2.  Press Alt-R to display the Run Menu.
  1066.  
  1067.    3.  Press X (Make EXE File).
  1068.  
  1069.    4.  Press Alt-A to select the Stand-Alone .EXE File option. (This method
  1070.        produces a file with the same filename as the .BAS file but appends
  1071.        the .EXE file extension. To change the filename, type over the
  1072.        displayed name before pressing Alt-A.)
  1073.  
  1074.    5.  Press E to choose the Make EXE and Exit command.
  1075.  
  1076.    QuickBASIC then creates the executable file, the QuickBASIC program is
  1077.    terminated, and you return to the system prompt. To verify that the file
  1078.    exists, type DIR and press Enter, and look for that program's filename
  1079.    with a .EXE extension.
  1080.  
  1081.    To run the program as a stand-alone .EXE file, type the filename (without
  1082.    the .EXE extension) from the system prompt. Type any command line values
  1083.    or input parameters after the filename, with spaces between the filename
  1084.    and parameters. The program or toolbox module begins executing as soon as
  1085.    you press the Enter key.
  1086.  
  1087.  
  1088.  
  1089.  ────────────────────────────────────────────────────────────────────────────
  1090.  ATTRIB
  1091.  
  1092.    The ATTRIB program generates a table showing combinations of text-mode
  1093.    character attributes, including all combinations of foreground and
  1094.    background colors. Only the blink attribute isn't demonstrated, but it is
  1095.    described at the head of the table. Use this program as a utility program.
  1096.    Compile it as a stand-alone executable program, and run it to decide what
  1097.    colors to use in your own programs.
  1098.  
  1099.    The sole purpose of the ATTRIB main program is to call the single
  1100.    subprogram, also named Attrib. Actually, the program has only enough
  1101.    supporting module-level code to demonstrate the subprogram.
  1102.  
  1103.    The Attrib subprogram may be called by other programs, either by loading
  1104.    the entire ATTRIB module into memory or by copying only the Attrib
  1105.    subprogram into another module. Refer to the MOUSTCRS program for an
  1106.    example.
  1107.  
  1108.  
  1109.  Program Module: ATTRIB
  1110.  
  1111.    ──────────────────────────────────────────────────────────────────────────
  1112.      ' ************************************************
  1113.      ' **  Name:          ATTRIB                     **
  1114.      ' **  Type:          Program                    **
  1115.      ' **  Module:        ATTRIB.BAS                 **
  1116.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1117.      ' ************************************************
  1118.      '
  1119.      ' Displays all combinations of text mode character
  1120.      ' attributes on the screen for review.
  1121.      '
  1122.      ' USAGE:           No command line parameters
  1123.      ' REQUIREMENTS:    CGA
  1124.      ' .MAK FILE:       (none)
  1125.      ' FUNCTIONS:       (none)
  1126.      ' PARAMETERS:      (none)
  1127.      ' VARIABLES:       (none)
  1128.        DECLARE SUB Attrib ()
  1129.  
  1130.      ' Call the subprogram
  1131.        Attrib
  1132.  
  1133.      ' All done
  1134.        END
  1135.    ──────────────────────────────────────────────────────────────────────────
  1136.  
  1137.  
  1138.  Subprogram: Attrib
  1139.  
  1140.    Creates the color attribute table on the screen for the ATTRIB module.
  1141.    Sixteen foreground and eight background color attributes are available in
  1142.    the default SCREEN 0 text mode, not counting the blink attribute for the
  1143.    foreground color. This subprogram displays all 128 combinations in a way
  1144.    that makes it easy to see which numbers result in which colors.
  1145.  
  1146.    ──────────────────────────────────────────────────────────────────────────
  1147.      ' ************************************************
  1148.      ' **  Name:          Attrib                     **
  1149.      ' **  Type:          Subprogram                 **
  1150.      ' **  Module:        ATTRIB.BAS                 **
  1151.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1152.      ' ************************************************
  1153.      '
  1154.      ' Displays table of color attributes for text mode.
  1155.      '
  1156.      ' EXAMPLE OF USE:  Attrib
  1157.      ' PARAMETERS:      (none)
  1158.      ' VARIABLES:       bgd%        Background number for COLOR statement
  1159.      '                  fgd%        Foreground number for COLOR statement
  1160.      ' MODULE LEVEL
  1161.      '   DECLARATIONS:              DECLARE SUB Attrib ()
  1162.      '
  1163.        SUB Attrib STATIC
  1164.            SCREEN 0
  1165.            CLS
  1166.            PRINT "Attributes for the COLOR statement in text mode (SCREEN 0)."
  1167.            PRINT "Add 16 to the foreground to cause the character to blink."
  1168.            FOR bgd% = 0 TO 7
  1169.                COLOR bgd% XOR 7, bgd%
  1170.                PRINT
  1171.                PRINT "Background%"; STR$(bgd%),
  1172.                PRINT "Foreground% ..."; SPACE$(41)
  1173.                FOR fgd% = 0 TO 15
  1174.                    COLOR fgd%, bgd%
  1175.                    PRINT STR$(fgd%); "  ";
  1176.                NEXT fgd%
  1177.            NEXT bgd%
  1178.            COLOR 7, 0
  1179.            PRINT
  1180.        END SUB
  1181.    ──────────────────────────────────────────────────────────────────────────
  1182.  
  1183.  
  1184.  
  1185.  ────────────────────────────────────────────────────────────────────────────
  1186.  BIN2HEX
  1187.  
  1188.    The BIN2HEX program is a utility that creates hexadecimal format files
  1189.    showing the contents of a given binary file. In this book, its most useful
  1190.    purpose is in displaying the contents of .OBJ files created by the
  1191.    Microsoft Macro Assembler. This enables you to create the necessary files
  1192.    for using the assembly-language routines in this book, even if you don't
  1193.    have the Macro Assembler.
  1194.  
  1195.    The program reads bytes of the input file using binary mode, converts each
  1196.    to a two-character hexadecimal string, and then formats the output by
  1197.    blocking the hexadecimal numbers into two groups of eight bytes per line.
  1198.    The output file can be listed or printed and can easily be transferred
  1199.    over a modem using conventional ASCII protocol.
  1200.  
  1201.    The HEX2BIN program performs the opposite function of this program,
  1202.    converting a hexadecimal listing back to a binary file.
  1203.  
  1204.    You will find several assembly-language subprograms in Part III of this
  1205.    book, and Microsoft provides two assembly listings with the QuickBASIC
  1206.    language. The suggested method of creating the .OBJ files from these
  1207.    listings is to use the Microsoft Macro Assembler, version 5.0. However, if
  1208.    you don't have the Macro Assembler, type in the hexadecimal files using
  1209.    the QuickBASIC Document editing capability, and then run the HEX2BIN
  1210.    program to convert each to the required .OBJ file.
  1211.  
  1212.    To use BIN2HEX, type the input filename and output filename on the command
  1213.    line when you run the program. For example, to convert MOUSE.OBJ to the
  1214.    hexadecimal listing MOUSE.HEX, enter these two command line parameters:
  1215.  
  1216.  
  1217.      MOUSE.OBJ MOUSE.HEX
  1218.  
  1219.    Be sure to use the full filename, including the extension. Separate
  1220.    filenames with spaces, as shown, or with commas if preferred.
  1221.  
  1222.    The BIN2HEX program was used to create the hexadecimal listings in
  1223.    Appendix D.
  1224.  
  1225.    Also review the HEX2BIN program for more information on using these
  1226.    routines.
  1227.  
  1228.  
  1229.  Program Module: BIN2HEX
  1230.  
  1231.    ──────────────────────────────────────────────────────────────────────────
  1232.      ' ************************************************
  1233.      ' **  Name:          BIN2HEX                    **
  1234.      ' **  Type:          Program                    **
  1235.      ' **  Module:        BIN2HEX.BAS                **
  1236.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1237.      ' ************************************************
  1238.      '
  1239.      ' Reads in any file and writes out a hexadecimal format file
  1240.      ' suitable for rebuilding the original file using the HEX2BIN
  1241.      ' program.
  1242.      '
  1243.      ' USAGE:          BIN2HEX inFileName.ext outFileName.ext
  1244.      ' .MAK FILE:      BIN2HEX.BAS
  1245.      '                 PARSE.BAS
  1246.      ' PARAMETERS:     inFileName    Name of file to be duplicated in hexadeci
  1247.      '                               format
  1248.      '                 outFileName   Name of hexadecimal format file to be cre
  1249.      ' VARIABLES:      cmd$          Working copy of the command line
  1250.      '                 inFile$       Name of input file
  1251.      '                 outFile$      Name of output file
  1252.      '                 byte$         Buffer for binary file access
  1253.      '                 i&            Index to each byte of input file
  1254.      '                 h$            Pair of hexadecimal characters representi
  1255.      '                               each byte
  1256.  
  1257.        DECLARE SUB ParseWord (a$, sep$, word$)
  1258.      ' Initialization
  1259.        CLS
  1260.        PRINT "BIN2HEX "; COMMAND$
  1261.        PRINT
  1262.  
  1263.      ' Get the input and output filenames from the command line
  1264.        cmd$ = COMMAND$
  1265.        ParseWord cmd$, " ,", inFile$
  1266.        ParseWord cmd$, " ,", outFile$
  1267.  
  1268.      ' Verify that both filenames were given
  1269.        IF outFile$ = "" THEN
  1270.            PRINT
  1271.            PRINT "Usage: BIN2HEX inFileName outFileName"
  1272.            SYSTEM
  1273.        END IF
  1274.  
  1275.      ' Open the input file
  1276.        OPEN inFile$ FOR BINARY AS #1 LEN = 1
  1277.        IF LOF(1) = 0 THEN
  1278.            CLOSE #1
  1279.            KILL inFile$
  1280.            PRINT
  1281.            PRINT "File not found - "; inFile$
  1282.            SYSTEM
  1283.        END IF
  1284.  
  1285.      ' Open the output file
  1286.        OPEN outFile$ FOR OUTPUT AS #2
  1287.  
  1288.      ' Process each byte of the file
  1289.        byte$ = SPACE$(1)
  1290.        FOR i& = 1 TO LOF(1)
  1291.            GET #1, , byte$
  1292.            h$ = RIGHT$("0" + HEX$(ASC(byte$)), 2)
  1293.            PRINT #2, h$; SPACE$(1);
  1294.            IF i& = LOF(1) THEN
  1295.                PRINT #2, ""
  1296.            ELSEIF i& MOD 16 = 0 THEN
  1297.                PRINT #2, ""
  1298.            ELSEIF i& MOD 8 = 0 THEN
  1299.                PRINT #2, "- ";
  1300.            END IF
  1301.        NEXT i&
  1302.  
  1303.      ' Clean up and quit
  1304.        CLOSE
  1305.        END
  1306.    ──────────────────────────────────────────────────────────────────────────
  1307.  
  1308.  
  1309.  
  1310.  ────────────────────────────────────────────────────────────────────────────
  1311.  BIOSCALL
  1312.  
  1313.    The BIOSCALL toolbox provides a collection of utility BIOS system calls.
  1314.  
  1315.    Several useful routines and data tables are in your computer's BIOS ROM
  1316.    (Basic Input/Output Services, Read Only Memory), ready to be tapped into
  1317.    by your QuickBASIC programs. This toolbox of routines provides a sampler
  1318.    of the most useful interrupt calls that return the information provided by
  1319.    BIOS. With QuickBASIC, it's easy to access the BIOS ROM.
  1320.  
  1321.    The module-level code provides demonstrations of the available subprograms
  1322.    when BIOSCALL is the designated main program. Later, load the BIOSCALL
  1323.    module along with any program you're developing when you need any
  1324.    information provided by the subprograms.
  1325.  
  1326.    Be aware that whenever you use the BIOSCALL toolbox, the mixed-language
  1327.    subprograms Interrupt and InterruptX must be accessible. Refer to "Using
  1328.    QuickBASIC Toolboxes" on page 21 for instructions on creating and loading
  1329.    the Quick Library MIXED.QLB with the QuickBASIC system.
  1330.  
  1331.    The Scroll subprogram, demonstrated first, prints a block of fifteen
  1332.    lines of uppercase characters on the screen. The first line is filled with
  1333.    As, the second with Bs, and so on. Each line is also printed in a
  1334.    different color scheme to make it easier to see exactly which characters
  1335.    are scrolled. The Scroll subprogram scrolls the rectangular area (from
  1336.    row 2, column 3 to row 6, column 16) up 3 lines. The attribute byte is set
  1337.    to green foreground on blue background, the same as the attribute byte at
  1338.    row 2, column 3. Study the displayed result, and notice that the lines
  1339.    moved up three rows and that the three blank lines show the blue
  1340.    background.
  1341.  
  1342.    The Equipment subprogram determines the computer equipment settings as
  1343.    maintained by BIOS. A short table is displayed, listing the availability
  1344.    or count of the printers, the game adapter, the serial I/O ports, the
  1345.    floppy disk drives, and the math coprocessor. Also displayed is the
  1346.    initial video state at boot-up time.
  1347.  
  1348.    VideoState, the next subprogram demonstrated, determines the current
  1349.    video state. The current video mode, number of text columns, and current
  1350.    active video page are displayed. Finally, GetShiftStates displays a table
  1351.    of the shift keys and shift states. This table is continuously updated
  1352.    until you press the Enter key, allowing you to try out the various shift
  1353.    keys. For example, press the left and right shift keys, singly or
  1354.    together, and notice how the state of each is monitored independently.
  1355.  
  1356.    In the demo module, two subprograms, PrintScreen and ReBoot, are
  1357.    commented out, as the actions they take are a bit extreme. To demo these
  1358.    subprograms, remove the apostrophes in front of these statements, which
  1359.    you'll find near the end of the module-level code. Don't forget that once
  1360.    you reboot, everything currently in memory is erased, and you'll be
  1361.    starting fresh.
  1362.  
  1363.    For more information on the available BIOS calls, refer to the technical
  1364.    reference manual for your computer.
  1365.  
  1366.    Name                     Type    Description
  1367.    ──────────────────────────────────────────────────────────────────────────
  1368.    BIOSCALL.BAS                    Demo module
  1369.    Equipment               Sub     Equipment/hardware information
  1370.    GetShiftStates          Sub     Shift key states
  1371.    PrintScreen             Sub     Screen dump
  1372.    ReBoot                  Sub     System reboot
  1373.    Scroll                  Sub     Moves text in designated area of screen
  1374.    VideoState              Sub     Mode, col, and page display of current
  1375.                                     state
  1376.    ──────────────────────────────────────────────────────────────────────────
  1377.  
  1378.  
  1379.  Demo Module: BIOSCALL
  1380.  
  1381.    ──────────────────────────────────────────────────────────────────────────
  1382.      ' ************************************************
  1383.      ' **  Name:          BIOSCALL                   **
  1384.      ' **  Type:          Toolbox                    **
  1385.      ' **  Module:        BIOSCALL.BAS               **
  1386.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1387.      ' ************************************************
  1388.      '
  1389.      ' Demonstrates several interrupt calls to the ROM BIOS.
  1390.      '
  1391.      ' USAGE: No command line parameters
  1392.      ' REQUIREMENTS:    MIXED.QLB/.LIB
  1393.      ' .MAK FILE:       (none)
  1394.      ' PARAMETERS:      (none)
  1395.      ' VARIABLES:       i%         Loop index for creating lines to scroll
  1396.      '                  equip      Structure of type EquipmentType
  1397.      '                  mode%      Video mode returned by VideoState
  1398.      '                  columns%   Video columns returned by VideoState
  1399.      '                  page%      Video page returned by VideoState
  1400.      '                  shift      Structure of type ShiftType
  1401.  
  1402.  
  1403.      ' Constants
  1404.        CONST FALSE = 0
  1405.        CONST TRUE = NOT FALSE
  1406.  
  1407.      ' Declare the Type structures
  1408.        TYPE RegType
  1409.            ax    AS INTEGER
  1410.            bx    AS INTEGER
  1411.            cx    AS INTEGER
  1412.            dx    AS INTEGER
  1413.            Bp    AS INTEGER
  1414.            si    AS INTEGER
  1415.            di    AS INTEGER
  1416.            flags AS INTEGER
  1417.        END TYPE
  1418.  
  1419.        TYPE RegTypeX
  1420.            ax    AS INTEGER
  1421.            bx    AS INTEGER
  1422.            cx    AS INTEGER
  1423.            dx    AS INTEGER
  1424.            Bp    AS INTEGER
  1425.            si    AS INTEGER
  1426.            di    AS INTEGER
  1427.            flags AS INTEGER
  1428.            ds    AS INTEGER
  1429.            es    AS INTEGER
  1430.        END TYPE
  1431.  
  1432.        TYPE EquipmentType
  1433.            printers     AS INTEGER
  1434.            gameAdapter  AS INTEGER
  1435.            serial       AS INTEGER
  1436.            floppies     AS INTEGER
  1437.            initialVideo AS INTEGER
  1438.            coprocessor  AS INTEGER
  1439.        END TYPE
  1440.  
  1441.        TYPE ShiftType
  1442.            right           AS INTEGER
  1443.            left            AS INTEGER
  1444.            ctrl            AS INTEGER
  1445.            alt             AS INTEGER
  1446.            scrollLockState AS INTEGER
  1447.            numLockState    AS INTEGER
  1448.            capsLockState   AS INTEGER
  1449.            insertState     AS INTEGER
  1450.        END TYPE
  1451.  
  1452.        DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  1453.        DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
  1454.        DECLARE SUB PrintScreen ()
  1455.        DECLARE SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%)
  1456.        DECLARE SUB Equipment (equip AS EquipmentType)
  1457.        DECLARE SUB VideoState (mode%, columns%, page%)
  1458.        DECLARE SUB GetShiftStates (shift AS ShiftType)
  1459.        DECLARE SUB ReBoot ()
  1460.  
  1461.      ' Demonstrate the Scroll subprogram
  1462.        CLS
  1463.        FOR i% = 1 TO 15
  1464.            COLOR i%, i% - 1
  1465.            PRINT STRING$(25, i% + 64)
  1466.        NEXT i%
  1467.        COLOR 7, 0
  1468.        PRINT
  1469.        PRINT "Press <Enter> to scroll part of the screen"
  1470.        DO
  1471.        LOOP UNTIL INKEY$ = CHR$(13)
  1472.        Scroll 2, 3, 6, 16, 3, SCREEN(2, 3, 1)
  1473.  
  1474.      ' Wait for user before continuing
  1475.        PRINT
  1476.        PRINT "Press any key to continue"
  1477.        DO
  1478.        LOOP UNTIL INKEY$ <> ""
  1479.        CLS
  1480.  
  1481.      ' Determine the equipment information
  1482.        DIM equip AS EquipmentType
  1483.        Equipment equip
  1484.        PRINT "Printers:", equip.printers
  1485.        PRINT "Game adapter:", equip.gameAdapter
  1486.        PRINT "Serial IO:", equip.serial
  1487.        PRINT "Floppies:", equip.floppies
  1488.        PRINT "Video:", equip.initialVideo
  1489.        PRINT "Coprocessor:", equip.coprocessor
  1490.  
  1491.      ' Determine the current video state
  1492.        PRINT
  1493.        VideoState mode%, columns%, page%
  1494.        PRINT "Video mode:", mode%
  1495.        PRINT "Text columns:", columns%
  1496.        PRINT "Video page:", page%
  1497.  
  1498.      ' Wait for user before continuing
  1499.        PRINT
  1500.        PRINT "Press any key to continue"
  1501.        DO
  1502.        LOOP UNTIL INKEY$ <> ""
  1503.  
  1504.      ' Demonstrate the shift key states
  1505.        CLS
  1506.        PRINT "(Press shift keys, then <Enter> to continue...)"
  1507.        DIM shift AS ShiftType
  1508.        DO
  1509.            LOCATE 4, 1
  1510.            PRINT "Shift states:"
  1511.            GetShiftStates shift
  1512.            PRINT
  1513.            PRINT "Left shift:", shift.left
  1514.            PRINT "Right shift:", shift.right
  1515.            PRINT "Ctrl:", shift.ctrl
  1516.            PRINT "Alt:", shift.alt
  1517.            PRINT "Scroll Lock:", shift.scrollLockState
  1518.            PRINT "Num Lock:", shift.numLockState
  1519.            PRINT "Caps Lock:", shift.capsLockState
  1520.            PRINT "Insert:", shift.insertState
  1521.        LOOP UNTIL INKEY$ = CHR$(13)
  1522.  
  1523.      ' Uncomment the following line to cause a screen dump to printer....
  1524.      ' PrintScreen
  1525.  
  1526.      ' Uncomment the following line only if you want to reboot....
  1527.      ' ReBoot
  1528.  
  1529.        END
  1530.    ──────────────────────────────────────────────────────────────────────────
  1531.  
  1532.  
  1533.  Subprogram: Equipment
  1534.  
  1535.    Returns information about the available computer hardware by calling the
  1536.    BIOS service at interrupt 11H, which returns bit patterns indicating the
  1537.    equipment configuration. The definition of the data structure named
  1538.    EquipmentType lists the items that this call can determine.
  1539.  
  1540.    This subprogram allows your program to decide how to handle input and
  1541.    output chores. As one example, the user can be prompted to Remove the
  1542.    first disk and insert the second or to Insert the second disk in drive B,
  1543.    depending on whether the computer has one or two floppy disk drives
  1544.    available.
  1545.  
  1546.    ──────────────────────────────────────────────────────────────────────────
  1547.      ' ************************************************
  1548.      ' **  Name:          Equipment                  **
  1549.      ' **  Type:          Subprogram                 **
  1550.      ' **  Module:        BIOSCALL.BAS               **
  1551.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1552.      ' ************************************************
  1553.      '
  1554.      ' Returns equipment configuration information from BIOS.
  1555.      '
  1556.      ' EXAMPLE OF USE:  Equipment equip
  1557.      ' PARAMETERS:      equip      Structure of type EquipmentType
  1558.      ' VARIABLES:       reg        Structure of type RegType
  1559.      ' MODULE LEVEL
  1560.      '   DECLARATIONS:  TYPE RegType
  1561.      '                     ax    AS INTEGER
  1562.      '                     bx    AS INTEGER
  1563.      '                     cx    AS INTEGER
  1564.      '                     dx    AS INTEGER
  1565.      '                     Bp    AS INTEGER
  1566.      '                     si    AS INTEGER
  1567.      '                     di    AS INTEGER
  1568.      '                     flags AS INTEGER
  1569.      '                  END TYPE
  1570.      '
  1571.      '                  TYPE EquipmentType
  1572.      '                     printers     AS INTEGER
  1573.      '                     gameAdapter  AS INTEGER
  1574.      '                     serial       AS INTEGER
  1575.      '                     floppies     AS INTEGER
  1576.      '                     initialVideo AS INTEGER
  1577.      '                     coprocessor  AS INTEGER
  1578.      '                  END TYPE
  1579.      '     DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType
  1580.      '     DECLARE SUB Equipment (equip AS EquipmentType)
  1581.      '
  1582.         SUB Equipment (equip AS EquipmentType) STATIC
  1583.            DIM reg AS RegType
  1584.            Interrupt &H11, reg, reg
  1585.            equip.printers = (reg.ax AND &HC000&) \ 16384
  1586.            equip.gameAdapter = (reg.ax AND &H1000) \ 4096
  1587.            equip.serial = (reg.ax AND &HE00) \ 512
  1588.            equip.floppies = (reg.ax AND &HC0) \ 64 + 1
  1589.            equip.initialVideo = (reg.ax AND &H30) \ 16
  1590.            equip.coprocessor = (reg.ax AND 2) \ 2
  1591.        END SUB
  1592.    ──────────────────────────────────────────────────────────────────────────
  1593.  
  1594.  
  1595.  Subprogram: GetShiftStates
  1596.  
  1597.    Returns the state of each shift key at the moment the subprogram is called
  1598.    and the current shift key states.
  1599.  
  1600.    The left Shift, right Shift, Ctrl, and Alt keys return a 1 in the
  1601.    appropriate structure variables if they are pressed at the moment this
  1602.    subprogram is called. If not pressed, a 0 is returned instead.
  1603.  
  1604.    This subprogram can also monitor the four shift states. If active, the
  1605.    Scroll Lock, Num Lock, Caps Lock, and Insert states return a value of 1 in
  1606.    the appropriate variable. If your keyboard has lights indicating the
  1607.    current states of these shift keys, this subprogram returns a 1 whenever a
  1608.    light is on and a 0 when the light is off.
  1609.  
  1610.    ──────────────────────────────────────────────────────────────────────────
  1611.      ' ************************************************
  1612.      ' **  Name:          GetShiftStates             **
  1613.      ' **  Type:          Subprogram                 **
  1614.      ' **  Module:        BIOSCALL.BAS               **
  1615.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1616.      ' ************************************************
  1617.      '
  1618.      ' Returns state of the various shift keys.
  1619.      '
  1620.      ' EXAMPLE OF USE:  GetShiftStates shift
  1621.      ' PARAMETERS:      shift      Structure of type ShiftType
  1622.      ' VARIABLES:       reg        Structure of type RegType
  1623.      ' MODULE LEVEL
  1624.      '   DECLARATIONS:  TYPE RegType
  1625.      '                     ax    AS INTEGER
  1626.      '                     bx    AS INTEGER
  1627.      '                     cx    AS INTEGER
  1628.      '                     dx    AS INTEGER
  1629.      '                     Bp    AS INTEGER
  1630.      '                     si    AS INTEGER
  1631.      '                     di    AS INTEGER
  1632.      '                     flags AS INTEGER
  1633.      '                  END TYPE
  1634.      '
  1635.      '                  TYPE ShiftType
  1636.      '                     right           AS INTEGER
  1637.      '                     left            AS INTEGER
  1638.      '                     ctrl            AS INTEGER
  1639.      '                     alt             AS INTEGER
  1640.      '                     scrollLockState AS INTEGER
  1641.      '                     numLockState    AS INTEGER
  1642.      '                     capsLockState   AS INTEGER
  1643.      '                     insertState     AS INTEGER
  1644.      '                  END TYPE
  1645.      '
  1646.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  1647.      '      DECLARE SUB GetShiftStates (shift AS ShiftType)
  1648.      '
  1649.        SUB GetShiftStates (shift AS ShiftType) STATIC
  1650.            DIM reg AS RegType
  1651.            reg.ax = &H200
  1652.            Interrupt &H16, reg, reg
  1653.            shift.right = reg.ax AND 1
  1654.            shift.left = (reg.ax AND 2) \ 2
  1655.            shift.ctrl = (reg.ax AND 4) \ 4
  1656.            shift.alt = (reg.ax AND 8) \ 8
  1657.            shift.scrollLockState = (reg.ax AND 16) \ 16
  1658.            shift.numLockState = (reg.ax AND 32) \ 32
  1659.            shift.capsLockState = (reg.ax AND 64) \ 64
  1660.            shift.insertState = (reg.ax AND 128) \ 128
  1661.        END SUB
  1662.    ──────────────────────────────────────────────────────────────────────────
  1663.  
  1664.  
  1665.  Subprogram: PrintScreen
  1666.  
  1667.    Performs exactly the same screen-to-printer dump that occurs when the
  1668.    Shift-Print Screen keys are pressed.
  1669.  
  1670.    Whenever you press the Shift-Print Screen keys, the operating system
  1671.    performs an interrupt 5 to activate the BIOS-level code for performing the
  1672.    screen dump. With the PrintScreen subprogram, you can program such a
  1673.    screen dump at any point in the operation of a running program without
  1674.    requiring user intervention.
  1675.  
  1676.    Because the screen dump BIOS routine is interrupt driven, any changes to
  1677.    the screen dump code are automatically taken into account. For example, if
  1678.    your computer loads and patches in an improved version of the screen dump
  1679.    at boot-up time, this subprogram activates the new routine with no
  1680.    problem. That's one of the nice features of the interrupt mechanism
  1681.    provided by the 8086 family of computers.
  1682.  
  1683.    ──────────────────────────────────────────────────────────────────────────
  1684.      ' ************************************************
  1685.      ' **  Name:          PrintScreen                **
  1686.      ' **  Type:          Subprogram                 **
  1687.      ' **  Module:        BIOSCALL.BAS               **
  1688.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1689.      ' ************************************************
  1690.      '
  1691.      ' Activates interrupt 5 to cause a dump of the
  1692.      ' screen's contents to the printer.
  1693.      '
  1694.      ' EXAMPLE OF USE:  PrintScreen
  1695.      ' PARAMETERS:      (none)
  1696.      ' VARIABLES:       reg        Structure of type RegType
  1697.      ' MODULE LEVEL
  1698.      '   DECLARATIONS:  TYPE RegType
  1699.      '                     ax    AS INTEGER
  1700.      '                     bx    AS INTEGER
  1701.      '                     cx    AS INTEGER
  1702.      '                     dx    AS INTEGER
  1703.      '                     Bp    AS INTEGER
  1704.      '                     si    AS INTEGER
  1705.      '                     di    AS INTEGER
  1706.      '                     flags AS INTEGER
  1707.      '                  END TYPE
  1708.      '
  1709.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  1710.      '      DECLARE SUB PrintScreen ()
  1711.      '
  1712.        SUB PrintScreen STATIC
  1713.            DIM reg AS RegType
  1714.            Interrupt 5, reg, reg
  1715.        END SUB
  1716.    ──────────────────────────────────────────────────────────────────────────
  1717.  
  1718.  
  1719.  Subprogram: ReBoot
  1720.  
  1721.    Causes the system to reboot. Depending on the computer and its
  1722.    configuration, this reboot won't always work perfectly. Be sure to test
  1723.    the subprogram carefully for your specific circumstances if you plan to
  1724.    use it on a routine basis.
  1725.  
  1726.    Perhaps the best and safest use for this subprogram is as an escape route
  1727.    for unauthorized access to software, because rebooting can frustrate
  1728.    attempts to overcome copy protection schemes. For example, try rebooting
  1729.    after a user fails a password check for the third time or if an
  1730.    unauthorized copy of a program is detected.
  1731.  
  1732.    ──────────────────────────────────────────────────────────────────────────
  1733.      ' ************************************************
  1734.      ' **  Name:          ReBoot                     **
  1735.      ' **  Type:          Subprogram                 **
  1736.      ' **  Module:        BIOSCALL.BAS               **
  1737.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1738.      ' ************************************************
  1739.      '
  1740.      ' Causes the computer to reboot.
  1741.      '
  1742.      ' EXAMPLE OF USE:  ReBoot
  1743.      ' PARAMETERS:      (none)
  1744.      ' VARIABLES:       reg        Structure of type RegType
  1745.      ' MODULE LEVEL
  1746.      '   DECLARATIONS:  TYPE RegType
  1747.      '                     ax    AS INTEGER
  1748.      '                     bx    AS INTEGER
  1749.      '                     cx    AS INTEGER
  1750.      '                     dx    AS INTEGER
  1751.      '                     Bp    AS INTEGER
  1752.      '                     si    AS INTEGER
  1753.      '                     di    AS INTEGER
  1754.      '                     flags AS INTEGER
  1755.      '                  END TYPE
  1756.      '
  1757.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  1758.      '      DECLARE SUB ReBoot ()
  1759.      '
  1760.        SUB ReBoot STATIC
  1761.            DIM reg AS RegType
  1762.            Interrupt &H19, reg, reg
  1763.        END SUB
  1764.    ──────────────────────────────────────────────────────────────────────────
  1765.  
  1766.  
  1767.  Subprogram: Scroll
  1768.  
  1769.    Provides a quick scroll of text lines in a rectangular area of the
  1770.    display. The BIOS video interrupt 10H is set up to scroll. You place the
  1771.    correct parameters in the processor registers, and the BIOS code does the
  1772.    rest.
  1773.  
  1774.    Six parameters are passed to this subprogram. The first four define the
  1775.    upper left and lower right corners of the area to be scrolled. These
  1776.    coordinates refer to text-mode character locations, with the upper left
  1777.    corner of the screen defined as row 1, column 1. The lower right corner of
  1778.    the screen is defined as row 25, column 80 for 80-column text mode, or row
  1779.    25, column 40 for 40-column text mode.
  1780.  
  1781.    The last two parameters provide the line count and the color attribute. If
  1782.    the line count is a positive number, the lines scroll up by the indicated
  1783.    number of rows, leaving blank lines at the bottom of the scrolled area. If
  1784.    the line count is negative, the lines scroll down. The blank lines are
  1785.    filled with space characters, and the color attribute is set by the
  1786.    attribute byte passed in the sixth parameter.
  1787.  
  1788.    Usually this subprogram is used to scroll text one line at a time, such as
  1789.    when displaying the contents of a long file using the MS-DOS TYPE command.
  1790.    A handy feature is the subprogram's ability to completely clear any or all
  1791.    of the screen, setting the background color at the same time. To do this,
  1792.    pass a line count of 0. The BIOS routine will fill the entire rectangular
  1793.    area with spaces, much faster than if you were to PRINT the same number of
  1794.    space strings.
  1795.  
  1796.    ──────────────────────────────────────────────────────────────────────────
  1797.      ' ************************************************
  1798.      ' **  Name:          Scroll                     **
  1799.      ' **  Type:          Subprogram                 **
  1800.      ' **  Module:        BIOSCALL.BAS               **
  1801.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1802.      ' ************************************************
  1803.      '
  1804.      ' Scrolls the screen in the rectangular area defined
  1805.      ' by the row and col parameters.  Positive line count
  1806.      ' moves the lines up, leaving blank lines at bottom;
  1807.      ' negative line count moves the lines down.
  1808.      '
  1809.      ' EXAMPLE OF USE:  Scroll row1%, col1%, row2%, col2%, lines%, attr%
  1810.      ' PARAMETERS:      row1%    Upper left character row defining rectangular
  1811.      '                           scroll area
  1812.      '                  col1     Upper left character column defining rectangu
  1813.      '                           scroll area
  1814.      '                  row2%    Lower right character row defining rectangula
  1815.      '                           scroll area
  1816.      '                  col2%    Lower right character column defining
  1817.      '                           rectangular scroll area
  1818.      '                  lines%   Number of character lines to scroll
  1819.      '                  attr%    Color attribute byte to be used in new text
  1820.      '                           lines scrolled onto the screen
  1821.      ' VARIABLES:       reg      Structure of type RegType
  1822.      ' MODULE LEVEL
  1823.      '   DECLARATIONS:  TYPE RegType
  1824.      '                     ax    AS INTEGER
  1825.      '                     bx    AS INTEGER
  1826.      '                     cx    AS INTEGER
  1827.      '                     dx    AS INTEGER
  1828.      '                     Bp    AS INTEGER
  1829.      '                     si    AS INTEGER
  1830.      '                     di    AS INTEGER
  1831.      '                     flags AS INTEGER
  1832.      '                  END TYPE
  1833.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  1834.      '      DECLARE SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%
  1835.      '
  1836.        SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%) STATIC
  1837.            DIM reg AS RegType
  1838.            IF lines% > 0 THEN
  1839.                reg.ax = &H600 + lines% MOD 256
  1840.            ELSE
  1841.                reg.ax = &H700 + ABS(lines%) MOD 256
  1842.            END IF
  1843.            reg.bx = (attribute% * 256&) AND &HFF00
  1844.            reg.cx = (row1% - 1) * 256 + col1% - 1
  1845.            reg.dx = (row2% - 1) * 256 + col2% - 1
  1846.            Interrupt &H10, reg, reg
  1847.        END SUB
  1848.    ──────────────────────────────────────────────────────────────────────────
  1849.  
  1850.  
  1851.  Subprogram: VideoState
  1852.  
  1853.    Returns the current mode, the number of columns, and the page of the
  1854.    display.
  1855.  
  1856.    This subprogram returns information about the current video mode. The
  1857.    mode% parameter returned by the ROM BIOS is different from the number used
  1858.    in the SCREEN statement to set a video mode. The two parameters do
  1859.    correlate, however, and the following table provides a useful comparison:
  1860.  
  1861.    SCREEN Mode    WIDTH          Mode% (from VideoState)
  1862.    ──────────────────────────────────────────────────────────────────────────
  1863.     0             40              1
  1864.     0             80              3
  1865.     1             40              4
  1866.     2             80              6
  1867.     7             40             13
  1868.     8             80             14
  1869.     9             80             16
  1870.    10             80             15
  1871.    11             80             17
  1872.    12             80             18
  1873.    13             40             19
  1874.    ──────────────────────────────────────────────────────────────────────────
  1875.  
  1876.    The column% parameter is always 40 or 80, depending on the current SCREEN
  1877.    and WIDTH settings.
  1878.  
  1879.    The page% parameter is the currently active page number as set by the
  1880.    SCREEN statement. The default is page 0, and the maximum active page
  1881.    number is a function of the current screen mode. See the SCREEN statement
  1882.    in your QuickBASIC documentation for more information about active and
  1883.    virtual pages as set by the SCREEN statement.
  1884.  
  1885.    ──────────────────────────────────────────────────────────────────────────
  1886.      ' ************************************************
  1887.      ' **  Name:          VideoState                 **
  1888.      ' **  Type:          Subprogram                 **
  1889.      ' **  Module:        BIOSCALL.BAS               **
  1890.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1891.      ' ************************************************
  1892.      '
  1893.      ' Determines the current video mode parameters.
  1894.      '
  1895.      ' EXAMPLE OF USE:  VideoState mode%, columns%, page%
  1896.      ' PARAMETERS:      mode%      Current video mode
  1897.      '                  columns%   Current number of text columns
  1898.      '                  page%      Current active display page
  1899.      ' VARIABLES:       reg        Structure of type RegType
  1900.      ' MODULE LEVEL
  1901.      '   DECLARATIONS:  TYPE RegType
  1902.      '                     ax    AS INTEGER
  1903.      '                     bx    AS INTEGER
  1904.      '                     cx    AS INTEGER
  1905.      '                     dx    AS INTEGER
  1906.      '                     Bp    AS INTEGER
  1907.      '                     si    AS INTEGER
  1908.      '                     di    AS INTEGER
  1909.      '                     flags AS INTEGER
  1910.      '                  END TYPE
  1911.      '
  1912.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  1913.      '      DECLARE SUB VideoState (mode%, columns%, page%)
  1914.      '
  1915.        SUB VideoState (mode%, columns%, page%) STATIC
  1916.            DIM reg AS RegType
  1917.            reg.ax = &HF00
  1918.            Interrupt &H10, reg, reg
  1919.            mode% = reg.ax AND &HFF
  1920.            columns% = (CLNG(reg.ax) AND &HFF00) \ 256
  1921.            page% = (CLNG(reg.bx) AND &HFF00) \ 256
  1922.        END SUB
  1923.    ──────────────────────────────────────────────────────────────────────────
  1924.  
  1925.  
  1926.  
  1927.  ────────────────────────────────────────────────────────────────────────────
  1928.  BITS
  1929.  
  1930.    The BITS toolbox provides four bit manipulation routines. The
  1931.    Bin2BinStr$ and BinStr2Bin% functions convert integer numbers to and
  1932.    from binary string representations. This action is similar to that of the
  1933.    QuickBASIC HEX$, OCT$, and VAL functions, except that the conversions deal
  1934.    with base 2 representations.
  1935.  
  1936.    The BitGet and BitPut subprograms let you store and retrieve single bits
  1937.    from any location in any string. Up to 32767 bits can be accessed in a
  1938.    single string, which results in a string of 4096 bytes. These subprograms
  1939.    would be useful for data acquisition and process control applications
  1940.    involving a large number of contact closures. The famous sieve of
  1941.    Eratosthenes for finding prime numbers is used to demonstrate these two
  1942.    subprograms. Prime numbers from 1 through 1000 are found and printed by
  1943.    keeping track of a string of bits, each representing an integer from 1
  1944.    through 1000.
  1945.  
  1946.    You can change the value of max% in this demonstration to find prime
  1947.    numbers up to about 10937. Larger values of max% will cause overflow, but
  1948.    by reprogramming the variables involved, you can probably find even bigger
  1949.    primes.
  1950.  
  1951.    Name                 Type       Description
  1952.    ──────────────────────────────────────────────────────────────────────────
  1953.    BITS.BAS                       Demo module
  1954.    Bin2BinStr$         Func       Integer to 16-character binary string
  1955.    BinStr2Bin%         Func       16-character binary string to integer
  1956.    BitGet              Sub        Value from any bit position in a string
  1957.    BitPut              Sub        Sets or clears bit at location in a string
  1958.    ──────────────────────────────────────────────────────────────────────────
  1959.  
  1960.  
  1961.  Demo Module: BITS
  1962.  
  1963.    ──────────────────────────────────────────────────────────────────────────
  1964.      ' ************************************************
  1965.      ' **  Name:          BITS                       **
  1966.      ' **  Type:          Toolbox                    **
  1967.      ' **  Module:        BITS.BAS                   **
  1968.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  1969.      ' ************************************************
  1970.      '
  1971.      ' Demonstrates the bit manipulation functions
  1972.      ' and subprograms.
  1973.      '
  1974.      ' USAGE: No command line parameters
  1975.      ' .MAK FILE:       (none)
  1976.      ' PARAMETERS:      (none)
  1977.      ' VARIABLES:       max%       Upper limit for the prime number generator
  1978.      '                  b$         Bit string for finding prime numbers
  1979.      '                  n%         Loop index for sieve of Eratosthenes
  1980.      '                  bit%       Bit retrieved from b$
  1981.      '                  i%         Bit loop index
  1982.      '                  q$         The double quote character
  1983.  
  1984.        DECLARE FUNCTION BinStr2Bin% (b$)
  1985.        DECLARE FUNCTION Bin2BinStr$ (b%)
  1986.  
  1987.      ' Subprograms
  1988.        DECLARE SUB BitGet (a$, bitIndex%, bit%)
  1989.        DECLARE SUB BitPut (b$, bitIndex%, bit%)
  1990.  
  1991.      ' Prime numbers less than max%, using bit fields in B$
  1992.        CLS
  1993.        max% = 1000
  1994.        PRINT "Primes up to"; max%; "using BitGet and BitPut for sieve..."
  1995.        PRINT
  1996.        PRINT 1; 2;
  1997.        b$ = STRING$(max% \ 8 + 1, 0)
  1998.        FOR n% = 3 TO max% STEP 2
  1999.            BitGet b$, n%, bit%
  2000.            IF bit% = 0 THEN
  2001.                PRINT n%;
  2002.                FOR i% = 3 * n% TO max% STEP n% + n%
  2003.                    BitPut b$, i%, 1
  2004.                NEXT i%
  2005.            END IF
  2006.        NEXT n%
  2007.        PRINT
  2008.  
  2009.      ' Demonstration of the Bin2BinStr$ function
  2010.        PRINT
  2011.        PRINT "Bin2BinStr$(12345) = "; Bin2BinStr$(12345)
  2012.  
  2013.      ' Demonstration of the BinStr2Bin% function
  2014.        PRINT
  2015.        q$ = CHR$(34)
  2016.        PRINT "BinStr2Bin%("; q$; "1001011"; q$; ") = ";
  2017.        PRINT BinStr2Bin%("1001011")
  2018.  
  2019.      ' That's all
  2020.        END
  2021.    ──────────────────────────────────────────────────────────────────────────
  2022.  
  2023.  
  2024.  Function: Bin2BinStr$
  2025.  
  2026.    Returns a 16-character binary representation of an integer value. This
  2027.    function is similar to QuickBASIC's HEX$ and OCT$ functions, except that
  2028.    the conversion base is 2 instead of 16 or 8, and that 16 characters are
  2029.    always returned. For example, Bin2BinStr$(7) returns 0000000000000111, and
  2030.    Bin2BinStr$(-1) returns 1111111111111111.
  2031.  
  2032.    You can easily remove leading zeros in the 16-character string by using
  2033.    the LtrimSet$ function, as shown in the STRINGS module:
  2034.  
  2035.  
  2036.      bin$ = LtrimSet$(bin$, "0")
  2037.  
  2038.    ──────────────────────────────────────────────────────────────────────────
  2039.      ' ************************************************
  2040.      ' **  Name:          Bin2BinStr$                **
  2041.      ' **  Type:          Function                   **
  2042.      ' **  Module:        BITS.BAS                   **
  2043.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2044.      ' ************************************************
  2045.      '
  2046.      ' Returns a string of sixteen "0" and "1" characters
  2047.      ' that represent the binary value of b%.
  2048.      '
  2049.      ' EXAMPLE OF USE:  PRINT Bin2BinStr$(b%)
  2050.      ' PARAMETERS:      b%         Integer number
  2051.      ' VARIABLES:       t$         Working string space for forming
  2052.                                                  binary string
  2053.      '                  b%         Integer number
  2054.      '                  mask%      Bit isolation mask
  2055.      '                  i%         Looping index
  2056.      ' MODULE LEVEL
  2057.      '   DECLARATIONS:  DECLARE FUNCTION Bin2BinStr$ (b%)
  2058.      '
  2059.        FUNCTION Bin2BinStr$ (b%) STATIC
  2060.            t$ = STRING$(16, "0")
  2061.            IF b% THEN
  2062.                IF b% < 0 THEN
  2063.                    MID$(t$, 1, 1) = "1"
  2064.                END IF
  2065.                mask% = &H4000
  2066.                FOR i% = 2 TO 16
  2067.                    IF b% AND mask% THEN
  2068.                        MID$(t$, i%, 1) = "1"
  2069.                    END IF
  2070.                    mask% = mask% \ 2
  2071.                NEXT i%
  2072.            END IF
  2073.            Bin2BinStr$ = t$
  2074.        END FUNCTION
  2075.    ──────────────────────────────────────────────────────────────────────────
  2076.  
  2077.  
  2078.  Function: BinStr2Bin%
  2079.  
  2080.    Returns the integer represented by a string of up to 16 0s and 1s. For
  2081.    example, BinStr2Bin%("111") returns 7; BinStr2Bin%("000101") returns 5.
  2082.  
  2083.    If the string has more than 16 characters, only the rightmost 16 are used.
  2084.    Any character other than 1 is treated as 0.
  2085.  
  2086.    ──────────────────────────────────────────────────────────────────────────
  2087.      ' ************************************************
  2088.      ' **  Name:          BinStr2Bin%                **
  2089.      ' **  Type:          Function                   **
  2090.      ' **  Module:        BITS.BAS                   **
  2091.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2092.      ' ************************************************
  2093.      '
  2094.      ' Returns the integer represented by a string of up
  2095.      ' to 16 "0" and "1" characters.
  2096.      '
  2097.      ' EXAMPLE OF USE:  PRINT BinStr2Bin%(b$)
  2098.      ' PARAMETERS:      b$         Binary representation string
  2099.      ' VARIABLES:       bin%       Working variable for finding value
  2100.      '                  t$         Working copy of b$
  2101.      '                  mask%      Bit mask for forming value
  2102.      '                  i%         Looping index
  2103.      ' MODULE LEVEL
  2104.      '   DECLARATIONS:  DECLARE FUNCTION BinStr2Bin% (b$)
  2105.      '
  2106.        FUNCTION BinStr2Bin% (b$) STATIC
  2107.            bin% = 0
  2108.            t$ = RIGHT$(STRING$(16, "0") + b$, 16)
  2109.            IF LEFT$(t$, 1) = "1" THEN
  2110.                bin% = &H8000
  2111.            END IF
  2112.            mask% = &H4000
  2113.            FOR i% = 2 TO 16
  2114.                IF MID$(t$, i%, 1) = "1" THEN
  2115.                    bin% = bin% OR mask%
  2116.                END IF
  2117.                mask% = mask% \ 2
  2118.            NEXT i%
  2119.            BinStr2Bin% = bin%
  2120.        END FUNCTION
  2121.    ──────────────────────────────────────────────────────────────────────────
  2122.  
  2123.  
  2124.  Subprogram: BitGet
  2125.  
  2126.    Returns a bit value extracted from any bit position in a string. The bits
  2127.    are numbered consecutively, starting with bit 1 in the most significant
  2128.    bit position of the first byte of the string. Bit 8 is the least
  2129.    significant bit of this same byte, bit 9 is the most significant bit of
  2130.    the second byte, and so on. This subprogram can access up to 32767 bits,
  2131.    in which case the string must be 4096 bytes in length. For example:
  2132.  
  2133.  
  2134.  
  2135.      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
  2136.  
  2137.      BitGet (a$, 17, bit%) ... bit% = 0
  2138.      BitGet (a$, 18, bit%) ... bit% = 1
  2139.  
  2140.    The BitPut subprogram lets you set the bits in a string as desired.
  2141.  
  2142.    ──────────────────────────────────────────────────────────────────────────
  2143.      ' ************************************************
  2144.      ' **  Name:          BitGet                     **
  2145.      ' **  Type:          Subprogram                 **
  2146.      ' **  Module:        BITS.BAS                   **
  2147.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2148.      ' ************************************************
  2149.      '
  2150.      ' Extracts the bit at bitIndex% into a$ and returns
  2151.      ' either 0 or 1 in bit%.  The value of bitIndex%
  2152.      ' can range from 1 to 8 * LEN(a$).
  2153.      '
  2154.      ' EXAMPLE OF USE:  BitGet a$, bitIndex%, bit%
  2155.      ' PARAMETERS:      a$         String where bit is stored
  2156.      '                  bitIndex%  Bit position in string
  2157.      '                  bit%       Extracted bit value, 0 or 1
  2158.      ' VARIABLES:       byte%      Byte location in string of the bit
  2159.      '                  mask%      Bit isolation mask for given bit
  2160.      ' MODULE LEVEL
  2161.      '   DECLARATIONS:  DECLARE SUB BitGet (a$, bitIndex%, bit%)
  2162.      '
  2163.         SUB BitGet (a$, bitIndex%, bit%) STATIC
  2164.            byte% = (bitIndex% - 1) \ 8 + 1
  2165.            SELECT CASE bitIndex% MOD 8
  2166.            CASE 1
  2167.                mask% = 128
  2168.            CASE 2
  2169.                mask% = 64
  2170.            CASE 3
  2171.                mask% = 32
  2172.            CASE 4
  2173.                mask% = 16
  2174.            CASE 5
  2175.                mask% = 8
  2176.            CASE 6
  2177.                mask% = 4
  2178.            CASE 7
  2179.                mask% = 2
  2180.            CASE 0
  2181.                mask% = 1
  2182.            END SELECT
  2183.            IF ASC(MID$(a$, byte%, 1)) AND mask% THEN
  2184.                bit% = 1
  2185.            ELSE
  2186.                bit% = 0
  2187.            END IF
  2188.        END SUB
  2189.    ──────────────────────────────────────────────────────────────────────────
  2190.  
  2191.  
  2192.  Subprogram: BitPut
  2193.  
  2194.    Sets or clears a single bit at any bit location in a string. The string
  2195.    can be up to 4096 bytes in length, allowing access of up to 32767 bits.
  2196.    Bits are numbered from left to right; the most significant bit of the
  2197.    first byte is bit 1, the least significant bit of the first byte is bit 8,
  2198.    the most significant bit of the second byte is bit 9, and so on. You can
  2199.    use the BitGet subprogram to get the bit values from the string as
  2200.    necessary. To initialize a string to all zeros or ones, use the STRING$
  2201.    function. For example, STRING$(4096, 0) returns a string of 32767 cleared
  2202.    bits, and STRING$(4096, 255) returns a string of 32767 set bits.
  2203.  
  2204.    ──────────────────────────────────────────────────────────────────────────
  2205.      ' ************************************************
  2206.      ' **  Name:          BitPut                     **
  2207.      ' **  Type:          Subprogram                 **
  2208.      ' **  Module:        BITS.BAS                   **
  2209.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2210.      ' ************************************************
  2211.      '
  2212.      ' If bit% is non-zero, then the bit at bitIndex% into
  2213.      ' a$ is set to 1; otherwise, it's set to 0. The value
  2214.      ' of bitIndex% can range from 1 to 8 * LEN(a$).
  2215.      '
  2216.      ' EXAMPLE OF USE:  BitPut a$, bitIndex%, bit%
  2217.      ' PARAMETERS:      a$         String containing the bits
  2218.      '                  bitIndex%  Index to the bit of concern
  2219.      '                  bit%       Value of bit (1 to set, 0 to clear)
  2220.      ' VARIABLES:       bytePtr%   Pointer to the byte position in the string
  2221.      '                  mask%      Bit isolation mask
  2222.      '                  byteNow%   Current numeric value of string byte
  2223.      ' MODULE LEVEL
  2224.      '   DECLARATIONS:  DECLARE SUB BitPut (b$, bitIndex%, bit%)
  2225.      '
  2226.        SUB BitPut (a$, bitIndex%, bit%) STATIC
  2227.            bytePtr% = bitIndex% \ 8 + 1
  2228.            SELECT CASE bitIndex% MOD 8
  2229.            CASE 1
  2230.                mask% = 128
  2231.            CASE 2
  2232.                mask% = 64
  2233.            CASE 3
  2234.                mask% = 32
  2235.            CASE 4
  2236.                mask% = 16
  2237.            CASE 5
  2238.                mask% = 8
  2239.            CASE 6
  2240.                mask% = 4
  2241.            CASE 7
  2242.                mask% = 2
  2243.            CASE 0
  2244.                mask% = 1
  2245.                bytePtr% = bytePtr% - 1
  2246.            END SELECT
  2247.            byteNow% = ASC(MID$(a$, bytePtr%, 1))
  2248.            IF byteNow% AND mask% THEN
  2249.                IF bit% = 0 THEN
  2250.                    MID$(a$, bytePtr%, 1) = CHR$(byteNow% XOR mask%)
  2251.                END IF
  2252.            ELSE
  2253.                IF bit% THEN
  2254.                    MID$(a$, bytePtr%, 1) = CHR$(byteNow% XOR mask%)
  2255.                END IF
  2256.            END IF
  2257.        END SUB
  2258.    ──────────────────────────────────────────────────────────────────────────
  2259.  
  2260.  
  2261.  
  2262.  ────────────────────────────────────────────────────────────────────────────
  2263.  CALENDAR
  2264.  
  2265.    The CALENDAR toolbox is a collection of easy-to-use functions and
  2266.    subprograms for date and time conversions and calculations. See the
  2267.    MONTH program for an example of how this module can be loaded as a
  2268.    toolbox for use by another main program.
  2269.  
  2270.    Wherever possible, dates and times are passed in a string format identical
  2271.    to that used by the QuickBASIC DATE$ and TIME$ functions. This makes the
  2272.    required parameters easier to remember and makes it possible to define
  2273.    many of the routines as functions that would otherwise have to be defined
  2274.    as subprograms. For example, the Julian2Date$ function returns a date in
  2275.    the string format mentioned. Alternative approaches would require defining
  2276.    three functions (one for returning the year, one for the month, and one
  2277.    for the day) or defining a subprogram that returned the three numbers in
  2278.    the parameter list. Returning dates in this string format also eliminates
  2279.    output numeric formatting because the string is ready to be printed as is.
  2280.  
  2281.    The Julian day number is an astronomical convention that allows dates to
  2282.    be cataloged by a single, large integer. A useful feature of the Julian
  2283.    day number is that a simple subtraction can calculate the number of days
  2284.    between any two dates. Leap years and the strange pattern of days in the
  2285.    various months make this calculation difficult when dealing with the usual
  2286.    month, day, and year numbers. The Date2Julian& and Julian2Date$
  2287.    conversion functions take care of all the details for you, making calendar
  2288.    calculations a breeze. Other functions return the day of the week, day of
  2289.    the year, day of the century, name of each month, and other related
  2290.    details──just about everything you ever wanted to know, but were afraid to
  2291.    ask, about dates and time.
  2292.  
  2293.    The calculations are usually accurate for dates from 1583 to the
  2294.    indefinite future, although some functions generate errors for dates
  2295.    between 1583 and 1599 if the calculations involve earlier dates. For
  2296.    example, consider how the DayOfTheCentury& function would attempt to
  2297.    calculate the day of the century for July 4, 1599. First, the function
  2298.    calculates the Julian day number for 07-04-1599 and then it attempts to
  2299.    subtract from that the Julian day number for the last day of the previous
  2300.    century. Because 12-31-1499 is earlier than 1583, the function will not
  2301.    work correctly.
  2302.  
  2303. ╓┌─┌─────────────────────────────┌─────────────┌─────────────────────────────╖
  2304.    Name                          Type          Description
  2305.    ──────────────────────────────────────────────────────────────────────────
  2306.    CALENDAR.BAS                               Demo module
  2307.    CheckDate%                   Func          Validates date with return of
  2308.                                                TRUE/FALSE
  2309.    Date2Day%                    Func          Day of month number from date
  2310.                                                string
  2311.    Date2Julian&                 Func          Julian day number for a given
  2312.    Name                          Type          Description
  2313.    ──────────────────────────────────────────────────────────────────────────
  2314.   Date2Julian&                 Func          Julian day number for a given
  2315.                                                date
  2316.    Date2Month%                  Func          Month number from date string
  2317.    Date2Year%                   Func          Year number from date string
  2318.    DayOfTheCentury&             Func          Day of the given century
  2319.    DayOfTheWeek$                Func          Name of day of the week for
  2320.                                                given date
  2321.    DayOfTheYear%                Func          Day of the year (1 through
  2322.                                                366) for given date
  2323.    DaysBetweenDates&            Func          Number of days between two
  2324.                                                dates
  2325.    HMS2Time$                    Func          Time string for given hour,
  2326.                                                minute, and second
  2327.    Julian2Date$                 Func          Date string from given Julian
  2328.                                                day number
  2329.    MDY2Date$                    Func          Date string from given month,
  2330.                                                day, and year
  2331.    MonthName$                   Func          Name of month for a given date
  2332.    OneMonthCalendar             Sub           One-month calendar for given
  2333.    Name                          Type          Description
  2334.    ──────────────────────────────────────────────────────────────────────────
  2335.   OneMonthCalendar             Sub           One-month calendar for given
  2336.                                                date
  2337.    Second2Date$                 Func          Seconds from last of 1979 to
  2338.                                                date given
  2339.    Second2Time$                 Func          Time of day from seconds since
  2340.                                                last of 1979
  2341.    Time2Hour%                   Func          Hour number from time string
  2342.    Time2Minute%                 Func          Minute number from time string
  2343.    Time2Second%                 Func          Seconds number from time
  2344.                                                string
  2345.    TimeDate2Second&             Func          Seconds from last of 1979 from
  2346.                                                date/time
  2347.    ──────────────────────────────────────────────────────────────────────────
  2348.  
  2349.  
  2350.  
  2351.  Demo Module: CALENDAR
  2352.  
  2353.    ──────────────────────────────────────────────────────────────────────────
  2354.      ' ************************************************
  2355.      ' **  Name:          CALENDAR                   **
  2356.      ' **  Type:          Toolbox                    **
  2357.      ' **  Module:        CALENDAR.BAS               **
  2358.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2359.      ' ************************************************
  2360.      '
  2361.      ' USAGE: No command line parameters
  2362.      ' .MAK FILE:       (none)
  2363.      ' PARAMETERS:      (none)
  2364.      ' VARIABLES:       month%     Month for demonstration
  2365.      '                  day%       Day for demonstration
  2366.      '                  year%      Year for demonstration
  2367.      '                  dat$       Date for demonstration
  2368.      '                  j&         Julian day number
  2369.      '                  tim$       System time right now
  2370.      '                  hour%      Hour right now
  2371.      '                  minute%    Minute right now
  2372.      '                  second%    Second right now
  2373.      '                  sec&       Seconds since last second of 1979
  2374.  
  2375.  
  2376.        CONST FALSE = 0
  2377.        CONST TRUE = NOT FALSE
  2378.  
  2379.      ' Functions
  2380.        DECLARE FUNCTION CheckDate% (dat$)
  2381.        DECLARE FUNCTION Date2Day% (dat$)
  2382.        DECLARE FUNCTION Date2Julian& (dat$)
  2383.        DECLARE FUNCTION Date2Month% (dat$)
  2384.        DECLARE FUNCTION Date2Year% (dat$)
  2385.        DECLARE FUNCTION DayOfTheCentury& (dat$)
  2386.        DECLARE FUNCTION DayOfTheWeek$ (dat$)
  2387.        DECLARE FUNCTION DayOfTheYear% (dat$)
  2388.        DECLARE FUNCTION DaysBetweenDates& (dat1$, dat2$)
  2389.        DECLARE FUNCTION HMS2Time$ (hour%, minute%, second%)
  2390.        DECLARE FUNCTION Julian2Date$ (julian&)
  2391.        DECLARE FUNCTION MDY2Date$ (month%, day%, year%)
  2392.        DECLARE FUNCTION MonthName$ (dat$)
  2393.        DECLARE FUNCTION Second2Date$ (second&)
  2394.        DECLARE FUNCTION Second2Time$ (second&)
  2395.        DECLARE FUNCTION Time2Hour% (tim$)
  2396.        DECLARE FUNCTION Time2Minute% (tim$)
  2397.        DECLARE FUNCTION Time2Second% (tim$)
  2398.        DECLARE FUNCTION TimeDate2Second& (tim$, dat$)
  2399.  
  2400.      ' Subprograms
  2401.        DECLARE SUB OneMonthCalendar (dat$, row%, col%)
  2402.  
  2403.      ' Let's choose the fourth of July for the demonstration
  2404.        CLS
  2405.        PRINT "All about the fourth of July for this year..."
  2406.        month% = 7
  2407.        day% = 4
  2408.        year% = Date2Year%(DATE$)
  2409.  
  2410.      ' Demonstrate the conversion to dat$
  2411.        PRINT
  2412.        dat$ = MDY2Date$(month%, day%, year%)
  2413.        PRINT "QuickBASIC string format for this date is "; dat$
  2414.  
  2415.      ' Check the validity of this date
  2416.        IF CheckDate%(dat$) = FALSE THEN
  2417.            PRINT "The date you entered is faulty... " + dat$
  2418.            SYSTEM
  2419.        END IF
  2420.  
  2421.      ' Day of the week and name of the month
  2422.        PRINT "The day of the week is "; DayOfTheWeek$(dat$); "."
  2423.  
  2424.      ' Astronomical Julian day number
  2425.        j& = Date2Julian&(dat$)
  2426.        PRINT "The Julian day number is"; j&
  2427.  
  2428.      ' Conversion of Julian number to date
  2429.        PRINT "Date for the given Julian number is "; Julian2Date$(j&); "."
  2430.  
  2431.      ' Convert the date string to numbers
  2432.        PRINT "The month, day, and year numbers are ";
  2433.        PRINT Date2Month%(dat$); ","; Date2Day%(dat$); ","; Date2Year%(dat$)
  2434.  
  2435.      ' The month name
  2436.        PRINT "The month name is "; MonthName$(dat$)
  2437.  
  2438.      ' Day of the year
  2439.        PRINT "The day of the year is"; DayOfTheYear%(dat$)
  2440.  
  2441.      ' Day of the century
  2442.        PRINT "The day of the century is"; DayOfTheCentury&(dat$)
  2443.  
  2444.      ' Days from right now
  2445.        IF Date2Julian&(dat$) < Date2Julian&(DATE$) THEN
  2446.            PRINT "That was"; DaysBetweenDates&(dat$, DATE$); "days ago."
  2447.        ELSEIF Date2Julian&(dat$) > Date2Julian&(DATE$) THEN
  2448.            PRINT "That is"; DaysBetweenDates&(dat$, DATE$); "days from now."
  2449.        ELSE
  2450.            PRINT "The date you entered is today's date."
  2451.        END IF
  2452.  
  2453.      ' Print a one-month calendar
  2454.        OneMonthCalendar dat$, 14, 25
  2455.  
  2456.      ' Wait for user
  2457.        LOCATE 23, 1
  2458.        PRINT "Press any key to continue"
  2459.        DO
  2460.        LOOP UNTIL INKEY$ <> ""
  2461.        CLS
  2462.  
  2463.      ' Demonstrate extracting hour, minute, and second from tim$
  2464.        dat$ = DATE$
  2465.        tim$ = TIME$
  2466.        hour% = Time2Hour%(tim$)
  2467.        minute% = Time2Minute%(tim$)
  2468.        second% = Time2Second%(tim$)
  2469.        PRINT "The date today... "; dat$
  2470.        PRINT "The time now  ... "; tim$
  2471.        PRINT "The hour, minute, and second numbers are ";
  2472.        PRINT hour%; ","; minute%; ","; second%
  2473.  
  2474.      ' Now put it all back together again
  2475.        PRINT "Time string created from hour, minute, and second is ";
  2476.        PRINT HMS2Time$(hour%, minute%, second%)
  2477.  
  2478.      ' Seconds since end of 1979
  2479.        dat$ = DATE$
  2480.        PRINT "The number of seconds since the last second of 1979 is";
  2481.        sec& = TimeDate2Second&(tim$, dat$)
  2482.        PRINT sec&
  2483.        PRINT "From this number we can extract the date and time..."
  2484.        PRINT Second2Date$(sec&); " and "; Second2Time$(sec&); "."
  2485.    ──────────────────────────────────────────────────────────────────────────
  2486.  
  2487.  
  2488.  Function: CheckDate%
  2489.  
  2490.    Returns TRUE if date is valid or FALSE if date is faulty.
  2491.  
  2492.    Was February 29, 1726, a real date? The CheckDate% function quickly finds
  2493.    the answer to this question. If the date checks out as valid, a value of
  2494.    TRUE (non-zero) is returned. If the date is faulty, a value of FALSE (0)
  2495.    is returned.
  2496.  
  2497.    This function is useful in any program that prompts the user to enter a
  2498.    date. A quick check can be made of the entered date, and the user can be
  2499.    asked to repeat the input if the entered date is faulty.
  2500.  
  2501.    ──────────────────────────────────────────────────────────────────────────
  2502.      ' ************************************************
  2503.      ' **  Name:          CheckDate%                 **
  2504.      ' **  Type:          Function                   **
  2505.      ' **  Module:        CALENDAR.BAS               **
  2506.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2507.      ' ************************************************
  2508.      '
  2509.      ' Returns TRUE if the given date represents a real
  2510.      ' date or FALSE if the date is in error.
  2511.      '
  2512.      ' EXAMPLE OF USE:  test% = CheckDate%(dat$)
  2513.      ' PARAMETERS:      dat$       Date to be checked
  2514.      ' VARIABLES:       julian&    Julian day number for the date
  2515.      '                  test$      Date string for given Julian day number
  2516.      ' MODULE LEVEL
  2517.      '   DECLARATIONS:  CONST FALSE = 0
  2518.      '                  CONST TRUE = NOT FALSE
  2519.      '
  2520.      '                  DECLARE FUNCTION CheckDate% (dat$)
  2521.      '                  DECLARE FUNCTION Date2Julian& (dat$)
  2522.      '                  DECLARE FUNCTION Julian2Date$ (julian&)
  2523.      '
  2524.        FUNCTION CheckDate% (dat$) STATIC
  2525.            julian& = Date2Julian&(dat$)
  2526.            test$ = Julian2Date$(julian&)
  2527.            IF dat$ = test$ THEN
  2528.                CheckDate% = TRUE
  2529.            ELSE
  2530.                CheckDate% = FALSE
  2531.            END IF
  2532.        END FUNCTION
  2533.    ──────────────────────────────────────────────────────────────────────────
  2534.  
  2535.  
  2536.  Function: Date2Day%
  2537.  
  2538.    Extracts the day number from a date string that is in the standard format
  2539.    MM-DD-YYYY.
  2540.  
  2541.    ──────────────────────────────────────────────────────────────────────────
  2542.      ' ************************************************
  2543.      ' **  Name:          Date2Day%                  **
  2544.      ' **  Type:          Function                   **
  2545.      ' **  Module:        CALENDAR.BAS               **
  2546.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2547.      ' ************************************************
  2548.      '
  2549.      ' Returns the day number given a date in the
  2550.      ' QuickBASIC string format MM-DD-YYYY.
  2551.      '
  2552.      ' EXAMPLE OF USE:  day% = Date2Day%(dat$)
  2553.      ' PARAMETERS:      dat$       Date of concern
  2554.      ' VARIABLES:       (none)
  2555.      ' MODULE LEVEL
  2556.      '   DECLARATIONS:  DECLARE FUNCTION Date2Day% (dat$)
  2557.      '
  2558.        FUNCTION Date2Day% (dat$) STATIC
  2559.            Date2Day% = VAL(MID$(dat$, 4, 2))
  2560.        END FUNCTION
  2561.    ──────────────────────────────────────────────────────────────────────────
  2562.  
  2563.  
  2564.  Function: Date2Julian&
  2565.  
  2566.    Returns the Julian day number for a given date. This function and the
  2567.    related function Julian2Date$ are at the heart of many of the other
  2568.    functions in this toolbox. This function calculates the astronomical
  2569.    Julian day number for any date from January 1, 1583, into the indefinite
  2570.    future, accounting for leap years and century adjustments.
  2571.  
  2572.    The main advantage of converting dates to long integer numbers is in being
  2573.    able to easily calculate the number of days between dates and the day of
  2574.    the week for any date. Further, if you need to store a large number of
  2575.    dates in a disk file, storing them as four-byte, long integers is more
  2576.    efficient than storing them in the longer string format or as separate
  2577.    integers representing the month, day, and year.
  2578.  
  2579.    ──────────────────────────────────────────────────────────────────────────
  2580.      ' ************************************************
  2581.      ' **  Name:          Date2Julian&               **
  2582.      ' **  Type:          Function                   **
  2583.      ' **  Module:        CALENDAR.BAS               **
  2584.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2585.      ' ************************************************
  2586.      '
  2587.      ' Returns the astronomical Julian day number given a
  2588.      ' date in the QuickBASIC string format MM-DD-YYYY.
  2589.      '
  2590.      ' EXAMPLE OF USE:  j& = Date2Julian&(dat$)
  2591.      ' PARAMETERS:      dat$       Date of concern
  2592.      ' VARIABLES:       month%     Month number for given date
  2593.      '                  day%       Day number for given date
  2594.      '                  year%      Year number for given date
  2595.      '                  ta&        First term of the Julian day number calcula
  2596.      '                  tb&        Second term of the Julian day number calcul
  2597.      '                  tc&        Third term of the Julian day number calcula
  2598.      ' MODULE LEVEL
  2599.      '   DECLARATIONS:  DECLARE FUNCTION Date2Day% (dat$)
  2600.      '                  DECLARE FUNCTION Date2Julian& (dat$)
  2601.      '                  DECLARE FUNCTION Date2Month% (dat$)
  2602.      '                  DECLARE FUNCTION Date2Year% (dat$)
  2603.      '
  2604.        FUNCTION Date2Julian& (dat$) STATIC
  2605.            month% = Date2Month%(dat$)
  2606.            day% = Date2Day%(dat$)
  2607.            year% = Date2Year%(dat$)
  2608.            IF year% < 1583 THEN
  2609.                PRINT "Date2Julian: Year is less than 1583"
  2610.                SYSTEM
  2611.            END IF
  2612.            IF month% > 2 THEN
  2613.                month% = month% - 3
  2614.            ELSE
  2615.                month% = month% + 9
  2616.                year% = year% - 1
  2617.            END IF
  2618.            ta& = 146097 * (year% \ 100) \ 4
  2619.            tb& = 1461& * (year% MOD 100) \ 4
  2620.            tc& = (153 * month% + 2) \ 5 + day% + 1721119
  2621.            Date2Julian& = ta& + tb& + tc&
  2622.        END FUNCTION
  2623.    ──────────────────────────────────────────────────────────────────────────
  2624.  
  2625.  
  2626.  Function: Date2Month%
  2627.  
  2628.    Extracts the month number from a date string that is in the standard
  2629.    format MM-DD-YYYY.
  2630.  
  2631.    ──────────────────────────────────────────────────────────────────────────
  2632.      ' ************************************************
  2633.      ' **  Name:          Date2Month%                **
  2634.      ' **  Type:          Function                   **
  2635.      ' **  Module:        CALENDAR.BAS               **
  2636.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2637.      ' ************************************************
  2638.      '
  2639.      ' Returns the month number given a date in the
  2640.      ' QuickBASIC string format MM-DD-YYYY.
  2641.      '
  2642.      ' EXAMPLE OF USE:  month% = Date2Month%(dat$)
  2643.      ' PARAMETERS:      dat$       Date of concern
  2644.      ' VARIABLES:       (none)
  2645.      ' MODULE LEVEL
  2646.      '   DECLARATIONS:  DECLARE FUNCTION Date2Month% (dat$)
  2647.      '
  2648.        FUNCTION Date2Month% (dat$) STATIC
  2649.            Date2Month% = VAL(MID$(dat$, 1, 2))
  2650.        END FUNCTION
  2651.    ──────────────────────────────────────────────────────────────────────────
  2652.  
  2653.  
  2654.  Function: Date2Year%
  2655.  
  2656.    Extracts the year number from a date string that is in the standard format
  2657.    MM-DD-YYYY.
  2658.  
  2659.    ──────────────────────────────────────────────────────────────────────────
  2660.      ' ************************************************
  2661.      ' **  Name:          Date2Year%                 **
  2662.      ' **  Type:          Function                   **
  2663.      ' **  Module:        CALENDAR.BAS               **
  2664.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2665.      ' ************************************************
  2666.      '
  2667.      ' Returns the year number given a date in the
  2668.      ' QuickBASIC string format MM-DD-YYYY.
  2669.      '
  2670.      ' EXAMPLE OF USE:  year% = Date2Year%(dat$)
  2671.      ' PARAMETERS:      dat$       Date of concern
  2672.      ' VARIABLES:       (none)
  2673.      ' MODULE LEVEL
  2674.      '   DECLARATIONS:  DECLARE FUNCTION Date2Year% (dat$)
  2675.      '
  2676.        FUNCTION Date2Year% (dat$) STATIC
  2677.            Date2Year% = VAL(MID$(dat$, 7))
  2678.        END FUNCTION
  2679.    ──────────────────────────────────────────────────────────────────────────
  2680.  
  2681.  
  2682.  Function: DayOfTheCentury&
  2683.  
  2684.    Returns the day of the given century. Each century has more than 32767
  2685.    days, requiring this function to be declared as returning a long integer
  2686.    result.
  2687.  
  2688.    Dates before 01-01-1600 generate an error. See page 55 for an
  2689.    explanation.
  2690.  
  2691.    ──────────────────────────────────────────────────────────────────────────
  2692.      ' ************************************************
  2693.      ' **  Name:          DayOfTheCentury%           **
  2694.      ' **  Type:          Function                   **
  2695.      ' **  Module:        CALENDAR.BAS               **
  2696.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2697.      ' ************************************************
  2698.      '
  2699.      ' Returns the number of the day of the century.
  2700.      '
  2701.      ' EXAMPLE OF USE:  cDay& = DayOfTheCentury&(dat$)
  2702.      ' PARAMETERS:      dat$       Date of concern
  2703.      ' VARIABLES:       year%      Year for given date
  2704.      '                  dat1$      Date for last day of previous century
  2705.      ' MODULE LEVEL
  2706.      '   DECLARATIONS:  DECLARE FUNCTION DayOfTheCentury& (dat$)
  2707.      '
  2708.        FUNCTION DayOfTheCentury& (dat$)
  2709.            year% = Date2Year%(dat$)
  2710.            dat1$ = MDY2Date$(12, 31, year% - (year% MOD 100) - 1)
  2711.            DayOfTheCentury& = DaysBetweenDates&(dat1$, dat$)
  2712.        END FUNCTION
  2713.    ──────────────────────────────────────────────────────────────────────────
  2714.  
  2715.  
  2716.  Function: DayOfTheWeek$
  2717.  
  2718.    Finds the name of the day of the week for any date. In displaying calendar
  2719.    calculation results, it's often desirable to be able to print the name of
  2720.    the day of the week. This function lets you conveniently do so.
  2721.  
  2722.    ──────────────────────────────────────────────────────────────────────────
  2723.      ' ************************************************
  2724.      ' **  Name:          DayOfTheWeek$              **
  2725.      ' **  Type:          Function                   **
  2726.      ' **  Module:        CALENDAR.BAS               **
  2727.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2728.      ' ************************************************
  2729.      '
  2730.      ' Returns a string stating the day of the week.
  2731.      ' Input is a date expressed in the QuickBASIC string
  2732.      ' format MM-DD-YYYY.
  2733.      '
  2734.      ' EXAMPLE OF USE:  PRINT "The day of the week is "; DayOfTheWeek$(dat$)
  2735.      ' PARAMETERS:      dat$       Date of concern
  2736.      ' VARIABLES:       (none)
  2737.      ' MODULE LEVEL
  2738.      '   DECLARATIONS:  DECLARE FUNCTION DayOfTheWeek$ (dat$)
  2739.      '
  2740.        FUNCTION DayOfTheWeek$ (dat$) STATIC
  2741.            SELECT CASE Date2Julian&(dat$) MOD 7
  2742.            CASE 0
  2743.                DayOfTheWeek$ = "Monday"
  2744.            CASE 1
  2745.                DayOfTheWeek$ = "Tuesday"
  2746.            CASE 2
  2747.                DayOfTheWeek$ = "Wednesday"
  2748.            CASE 3
  2749.                DayOfTheWeek$ = "Thursday"
  2750.            CASE 4
  2751.                DayOfTheWeek$ = "Friday"
  2752.            CASE 5
  2753.                DayOfTheWeek$ = "Saturday"
  2754.            CASE 6
  2755.                DayOfTheWeek$ = "Sunday"
  2756.            END SELECT
  2757.        END FUNCTION
  2758.    ──────────────────────────────────────────────────────────────────────────
  2759.  
  2760.  
  2761.  Function: DayOfTheYear%
  2762.  
  2763.    Returns a number in the range 1 through 366, indicating the day of the
  2764.    year for the given date, by subtracting the Julian day number for the last
  2765.    day of the previous year from that of the given date. This calculation
  2766.    generates an error if the date is before January 1, 1584.
  2767.  
  2768.    ──────────────────────────────────────────────────────────────────────────
  2769.      ' ************************************************
  2770.      ' **  Name:          DayOfTheYear%              **
  2771.      ' **  Type:          Function                   **
  2772.      ' **  Module:        CALENDAR.BAS               **
  2773.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2774.      ' ************************************************
  2775.      '
  2776.      ' Returns the number of the day of the year (1-366).
  2777.      '
  2778.      ' EXAMPLE OF USE:  PRINT "The day of the year is"; DayOfTheYear%(dat$)
  2779.      ' PARAMETERS:      dat$       Date of concern
  2780.      ' VARIABLES:       dat1$      Date of last day of previous year
  2781.      ' MODULE LEVEL
  2782.      '   DECLARATIONS:  DECLARE FUNCTION DayOfTheYear% (dat$)
  2783.      '
  2784.        FUNCTION DayOfTheYear% (dat$) STATIC
  2785.            dat1$ = MDY2Date$(12, 31, Date2Year%(dat$) - 1)
  2786.            DayOfTheYear% = DaysBetweenDates&(dat1$, dat$)
  2787.        END FUNCTION
  2788.    ──────────────────────────────────────────────────────────────────────────
  2789.  
  2790.  
  2791.  Function: DaysBetweenDates&
  2792.  
  2793.    Returns the number of days between two dates by subtracting the Julian day
  2794.    numbers of the dates. The absolute value of the difference is returned, so
  2795.    the first date can be earlier or later than the second. The number of days
  2796.    returned will always be a positive value.
  2797.  
  2798.    ──────────────────────────────────────────────────────────────────────────
  2799.      ' ************************************************
  2800.      ' **  Name:          DaysBetweenDates&          **
  2801.      ' **  Type:          Function                   **
  2802.      ' **  Module:        CALENDAR.BAS               **
  2803.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2804.      ' ************************************************
  2805.      '
  2806.      ' Returns the number of days between any two dates.
  2807.      '
  2808.      ' EXAMPLE OF USE:  days& = DaysBetweenDates&(dat1$, dat2$)
  2809.      ' PARAMETERS:      dat1$      First date
  2810.      '                  dat2$      Second date
  2811.      ' VARIABLES:       (none)
  2812.      ' MODULE LEVEL
  2813.      '   DECLARATIONS:  DECLARE FUNCTION DaysBetweenDates& (dat1$, dat2$)
  2814.      '
  2815.        FUNCTION DaysBetweenDates& (dat1$, dat2$) STATIC
  2816.            DaysBetweenDates& = ABS(Date2Julian&(dat1$) - Date2Julian&(dat2$))
  2817.        END FUNCTION
  2818.    ──────────────────────────────────────────────────────────────────────────
  2819.  
  2820.  
  2821.  Function: HMS2Time$
  2822.  
  2823.    Given hour, minute, and second numbers, returns a time string, in the same
  2824.    format as the string returned by QuickBASIC's TIME$ function. For example,
  2825.    HMS2Time$(23, 59, 59) returns 23:59:59.
  2826.  
  2827.    ──────────────────────────────────────────────────────────────────────────
  2828.      ' ************************************************
  2829.      ' **  Name:          HMS2Time$                  **
  2830.      ' **  Type:          Function                   **
  2831.      ' **  Module:        CALENDAR.BAS               **
  2832.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2833.      ' ************************************************
  2834.      '
  2835.      ' Returns the time in the QuickBASIC string format
  2836.      ' HH:MM:SS given hour%, minute%, and second%.
  2837.      '
  2838.      ' EXAMPLE OF USE:  PRINT HMS2Time$(hour%, minute%, second%)
  2839.      ' PARAMETERS:      hour%      Hour number
  2840.      '                  minute%    Minutes number
  2841.      '                  second%    Seconds number
  2842.      ' VARIABLES:       t$         Workspace for building the time string
  2843.      ' MODULE LEVEL
  2844.      '   DECLARATIONS:  DECLARE FUNCTION HMS2Time$ (hour%, minute%, second%)
  2845.      '
  2846.        FUNCTION HMS2Time$ (hour%, minute%, second%) STATIC
  2847.            t$ = RIGHT$("0" + MID$(STR$(hour%), 2), 2) + ":"
  2848.            t$ = t$ + RIGHT$("0" + MID$(STR$(minute%), 2), 2) + ":"
  2849.            HMS2Time$ = t$ + RIGHT$("0" + MID$(STR$(second%), 2), 2)
  2850.        END FUNCTION
  2851.    ──────────────────────────────────────────────────────────────────────────
  2852.  
  2853.  
  2854.  Function: Julian2Date$
  2855.  
  2856.    Converts a Julian day number to a date. The smallest long integer number
  2857.    that can be passed to this function without generating an error is
  2858.    2299239, the Julian number for the date 01-01-1583.
  2859.  
  2860.    ──────────────────────────────────────────────────────────────────────────
  2861.      ' ************************************************
  2862.      ' **  Name:          Julian2Date$               **
  2863.      ' **  Type:          Function                   **
  2864.      ' **  Module:        CALENDAR.BAS               **
  2865.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2866.      ' ************************************************
  2867.      '
  2868.      ' Returns a date in the QuickBASIC string format
  2869.      ' MM-DD-YYYY as calculated from a Julian day number.
  2870.      '
  2871.      ' EXAMPLE OF USE:
  2872.      '        PRINT "Date for the given Julian number is ";Julian2Date$(j&)
  2873.      ' PARAMETERS:      j&         Julian day number
  2874.      ' VARIABLES:       x&         Temporary calculation variable
  2875.      '                  y&         Temporary calculation variable
  2876.      '                  d&         Day number in long integer form
  2877.      '                  m&         Month number before adjustment
  2878.      '                  month%     Month number
  2879.      '                  year%      Year number
  2880.      '                  day%       Day number
  2881.      ' MODULE LEVEL
  2882.      '   DECLARATIONS:  DECLARE FUNCTION Julian2Date$ (julian&)
  2883.      '
  2884.        FUNCTION Julian2Date$ (julian&) STATIC
  2885.  
  2886.            x& = 4 * julian& - 6884477
  2887.            y& = (x& \ 146097) * 100
  2888.            d& = (x& MOD 146097) \ 4
  2889.  
  2890.            x& = 4 * d& + 3
  2891.            y& = (x& \ 1461) + y&
  2892.            d& = (x& MOD 1461) \ 4 + 1
  2893.  
  2894.            x& = 5 * d& - 3
  2895.            m& = x& \ 153 + 1
  2896.            d& = (x& MOD 153) \ 5 + 1
  2897.  
  2898.            IF m& < 11 THEN
  2899.                month% = m& + 2
  2900.            ELSE
  2901.                month% = m& - 10
  2902.            END IF
  2903.            day% = d&
  2904.            year% = y& + m& \ 11
  2905.  
  2906.            dat$ = MDY2Date$(month%, day%, year%)
  2907.            Julian2Date$ = dat$
  2908.        END FUNCTION
  2909.    ──────────────────────────────────────────────────────────────────────────
  2910.  
  2911.  
  2912.  Function: MDY2Date$
  2913.  
  2914.    Creates a date string from the numeric values of month, day, and year for
  2915.    a given date. The string format is the same as that returned by the
  2916.    QuickBASIC DATE$ function, MM-DD-YYYY.
  2917.  
  2918.    ──────────────────────────────────────────────────────────────────────────
  2919.      ' ************************************************
  2920.      ' **  Name:          MDY2Date$                  **
  2921.      ' **  Type:          Function                   **
  2922.      ' **  Module:        CALENDAR.BAS               **
  2923.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2924.      ' ************************************************
  2925.      '
  2926.      ' Converts month%, day%, and year% to a date string
  2927.      ' in the QuickBASIC string format MM-DD-YYYY.
  2928.      '
  2929.      ' EXAMPLE OF USE:  dat$ = MDY2Date$(month%, day%, year%)
  2930.      ' PARAMETERS:      month%     Month for the date
  2931.      '                  day%       Day of the month
  2932.      '                  year%      Year number
  2933.      ' VARIABLES:       y$         Temporary year string
  2934.      '                  m$         Temporary month string
  2935.      '                  d$         Temporary day string
  2936.      ' MODULE LEVEL
  2937.      '   DECLARATIONS:  DECLARE FUNCTION MDY2Date$ (month%, day%, year%)
  2938.      '
  2939.        FUNCTION MDY2Date$ (month%, day%, year%) STATIC
  2940.            y$ = RIGHT$("000" + MID$(STR$(year%), 2), 4)
  2941.            m$ = RIGHT$("0" + MID$(STR$(month%), 2), 2)
  2942.            d$ = RIGHT$("0" + MID$(STR$(day%), 2), 2)
  2943.            MDY2Date$ = m$ + "-" + d$ + "-" + y$
  2944.        END FUNCTION
  2945.    ──────────────────────────────────────────────────────────────────────────
  2946.  
  2947.  
  2948.  Function: MonthName$
  2949.  
  2950.    Returns the name of the month for a given date. If the passed date string
  2951.    has the wrong number of characters, the returned name defaults to
  2952.    MM-DD-YYYY to remind you of the required format for date strings. If the
  2953.    string is the right length but the first two characters don't represent a
  2954.    valid month number, ?MonthName? is returned.
  2955.  
  2956.    ──────────────────────────────────────────────────────────────────────────
  2957.      ' ************************************************
  2958.      ' **  Name:          MonthName$                 **
  2959.      ' **  Type:          Function                   **
  2960.      ' **  Module:        CALENDAR.BAS               **
  2961.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  2962.      ' ************************************************
  2963.      '
  2964.      ' Returns a string stating the month as indicated
  2965.      ' in dat$ (QuickBASIC string format MM-DD-YYYY).
  2966.      '
  2967.      ' EXAMPLE OF USE:  PRINT MonthName$(dat$)
  2968.      ' PARAMETERS:      dat$       Date of concern
  2969.      ' VARIABLES:       (none)
  2970.      ' MODULE LEVEL
  2971.      '   DECLARATIONS:  DECLARE FUNCTION MonthName$ (dat$)
  2972.      '
  2973.        FUNCTION MonthName$ (dat$) STATIC
  2974.  
  2975.            IF LEN(dat$) <> 10 THEN
  2976.                dat$ = "MM-DD-YYYY"
  2977.            END IF
  2978.  
  2979.            SELECT CASE LEFT$(dat$, 2)
  2980.            CASE "01"
  2981.                MonthName$ = "January"
  2982.            CASE "02"
  2983.                MonthName$ = "February"
  2984.            CASE "03"
  2985.                MonthName$ = "March"
  2986.            CASE "04"
  2987.                MonthName$ = "April"
  2988.            CASE "05"
  2989.                MonthName$ = "May"
  2990.            CASE "06"
  2991.                MonthName$ = "June"
  2992.            CASE "07"
  2993.                MonthName$ = "July"
  2994.            CASE "08"
  2995.                MonthName$ = "August"
  2996.            CASE "09"
  2997.                MonthName$ = "September"
  2998.            CASE "10"
  2999.                MonthName$ = "October"
  3000.            CASE "11"
  3001.                MonthName$ = "November"
  3002.            CASE "12"
  3003.                MonthName$ = "December"
  3004.            CASE ELSE
  3005.                MonthName$ = "?MonthName?"
  3006.            END SELECT
  3007.  
  3008.        END FUNCTION
  3009.    ──────────────────────────────────────────────────────────────────────────
  3010.  
  3011.  
  3012.  Subprogram: OneMonthCalendar
  3013.  
  3014.    Uses several functions from the CALENDAR toolbox to print a small,
  3015.    one-month calendar at any location on the screen. The stand-alone program
  3016.    named MONTH provides a good demonstration of this subprogram at work.
  3017.  
  3018.    ──────────────────────────────────────────────────────────────────────────
  3019.      ' ************************************************
  3020.      ' **  Name:          OneMonthCalendar           **
  3021.      ' **  Type:          Subprogram                 **
  3022.      ' **  Module:        CALENDAR.BAS               **
  3023.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3024.      ' ************************************************
  3025.      '
  3026.      ' Prints a small, one-month calendar at the row%
  3027.      ' and col% indicated.
  3028.      '
  3029.      ' EXAMPLE OF USE:  OneMonthCalendar dat$, row%, col%
  3030.      ' PARAMETERS:      dat$       Date of concern
  3031.      '                  row%       Screen row for upper left corner of calenda
  3032.      '                  col%       Screen column for upper left corner of cale
  3033.      ' VARIABLES:       mname$     Name of given month
  3034.      '                  month%     Month number
  3035.      '                  day%       Day number
  3036.      '                  year%      Year number
  3037.      '                  dat1$      Date for first of the given month
  3038.      '                  j&         Julian day number for each day of the month
  3039.      '                  heading$   Title line for calendar
  3040.      '                  wa%        Day of the week for each day of the month
  3041.      '                  rowloc%    Row for printing each day number
  3042.      ' MODULE LEVEL
  3043.      '   DECLARATIONS:  DECLARE SUB OneMonthCalendar (dat$, row%, col%)
  3044.      '
  3045.        SUB OneMonthCalendar (dat$, row%, col%) STATIC
  3046.            mname$ = MonthName$(dat$)
  3047.            LOCATE row%, col% + 12 - LEN(mname$) \ 2
  3048.            PRINT mname$; ","; Date2Year%(dat$)
  3049.            month% = Date2Month%(dat$)
  3050.            day% = 1
  3051.            year% = Date2Year%(dat$)
  3052.            dat1$ = MDY2Date$(month%, day%, year%)
  3053.            j& = Date2Julian&(dat1$)
  3054.            heading$ = " Sun Mon Tue Wed Thu Fri Sat"
  3055.            wa% = INSTR(heading$, LEFT$(DayOfTheWeek$(dat1$), 3)) \ 4
  3056.            LOCATE row% + 1, col%
  3057.            PRINT heading$
  3058.            rowloc% = row% + 2
  3059.            LOCATE rowloc%, col% + 4 * wa%
  3060.            DO
  3061.                PRINT USING "####"; day%;
  3062.                IF wa% = 6 THEN
  3063.                    rowloc% = rowloc% + 1
  3064.                    LOCATE rowloc%, col%
  3065.                END IF
  3066.                wa% = (wa% + 1) MOD 7
  3067.                j& = j& + 1
  3068.                day% = Date2Day%(Julian2Date$(j&))
  3069.            LOOP UNTIL day% = 1
  3070.            PRINT
  3071.        END SUB
  3072.    ──────────────────────────────────────────────────────────────────────────
  3073.  
  3074.  
  3075.  Function: Second2Date$
  3076.  
  3077.    Returns a date string given the number of seconds since the last second of
  3078.    1979. The number of seconds is limited to the range of positive long
  3079.    integers (1 to 2147483647). Given the largest possible long integer, the
  3080.    function returns the date 01-19-2048.
  3081.  
  3082.    Related functions are Second2Time$ and TimeDate2Second&. The
  3083.    Second2Time$ function finds the time string for a given second, and the
  3084.    TimeDate2Second$ function finds the seconds since 1979 for a given date
  3085.    and time.
  3086.  
  3087.    ──────────────────────────────────────────────────────────────────────────
  3088.      ' ************************************************
  3089.      ' **  Name:          Second2Date$               **
  3090.      ' **  Type:          Function                   **
  3091.      ' **  Module:        CALENDAR.BAS               **
  3092.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3093.      ' ************************************************
  3094.      '
  3095.      ' Returns the date in the QuickBASIC string format
  3096.      ' MM-DD-YYYY given a number of seconds since the
  3097.      ' last second of 1979.  Use Second2Time$ to find
  3098.      ' the time of day at the indicated second.
  3099.      '
  3100.      ' EXAMPLE OF USE:  dat$ = Second2Date$(second&)
  3101.      ' PARAMETERS:      second&    Number of seconds since the last second of
  3102.      ' VARIABLES:       days&      Julian day number of the date
  3103.      ' MODULE LEVEL
  3104.      '   DECLARATIONS:  DECLARE FUNCTION Second2Date$ (second&)
  3105.      '
  3106.        FUNCTION Second2Date$ (second&) STATIC
  3107.            days& = second& \ 86400 + 2444240
  3108.            Second2Date$ = Julian2Date$(days&)
  3109.        END FUNCTION
  3110.    ──────────────────────────────────────────────────────────────────────────
  3111.  
  3112.  
  3113.  Function: Second2Time$
  3114.  
  3115.    Returns a time string given the number of seconds since the last second of
  3116.    1979.
  3117.  
  3118.    Related functions are Second2Date$ and TimeDate2Second&. The
  3119.    Second2Date$ function finds the date string for a given second, and the
  3120.    TimeDate2Second$ function finds the seconds since 1979 for a given date
  3121.    and time.
  3122.  
  3123.    ──────────────────────────────────────────────────────────────────────────
  3124.      ' ************************************************
  3125.      ' **  Name:          Second2Time$               **
  3126.      ' **  Type:          Function                   **
  3127.      ' **  Module:        CALENDAR.BAS               **
  3128.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3129.      ' ************************************************
  3130.      '
  3131.      ' Returns the time in the QuickBASIC string format
  3132.      ' HH:MM:SS given the number of seconds since the
  3133.      ' last second of 1979.  Use Second2Date$ to find
  3134.      ' the date at the indicated second.
  3135.      '
  3136.      ' EXAMPLE OF USE:  tim$ = Second2Time$(second&)
  3137.      ' PARAMETERS:      second&    Number of seconds since the last second of
  3138.      ' VARIABLES:       time&      Number of seconds in current day
  3139.      '                  second%    Current second of the minute
  3140.      '                  minute%    Current minute of the hour
  3141.      '                  hour%      Current hour of the day
  3142.      ' MODULE LEVEL
  3143.      '   DECLARATIONS:  DECLARE FUNCTION Second2Time$ (second&)
  3144.      '
  3145.        FUNCTION Second2Time$ (second&) STATIC
  3146.            IF second& > 0 THEN
  3147.                time& = second& MOD 86400
  3148.                second% = time& MOD 60
  3149.                time& = time& \ 60
  3150.                minute% = time& MOD 60
  3151.                hour% = time& \ 60
  3152.                Second2Time$ = HMS2Time$(hour%, minute%, second%)
  3153.            ELSE
  3154.                Second2Time$ = "HH:MM:SS"
  3155.            END IF
  3156.        END FUNCTION
  3157.    ──────────────────────────────────────────────────────────────────────────
  3158.  
  3159.  
  3160.  Function: Time2Hour%
  3161.  
  3162.    Extracts the numeric value of the hour from a time string if in the
  3163.    standard TIME$ format HH:MM:SS.
  3164.  
  3165.    ──────────────────────────────────────────────────────────────────────────
  3166.      ' ************************************************
  3167.      ' **  Name:          Time2Hour%                 **
  3168.      ' **  Type:          Function                   **
  3169.      ' **  Module:        CALENDAR.BAS               **
  3170.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3171.      ' ************************************************
  3172.      '
  3173.      ' Returns the hour number as indicated in a time
  3174.      ' string in the format HH:MM:SS.
  3175.      '
  3176.      ' EXAMPLE OF USE:  hour% = Time2Hour%(tim$)
  3177.      ' PARAMETERS:      tim$       Time of concern
  3178.      ' VARIABLES:       (none)
  3179.      ' MODULE LEVEL
  3180.      '   DECLARATIONS:  DECLARE FUNCTION Time2Hour% (tim$)
  3181.      '
  3182.        FUNCTION Time2Hour% (tim$) STATIC
  3183.            Time2Hour% = VAL(LEFT$(tim$, 2))
  3184.        END FUNCTION
  3185.    ──────────────────────────────────────────────────────────────────────────
  3186.  
  3187.  
  3188.  Function: Time2Minute%
  3189.  
  3190.    Extracts the numeric value of the hour from a time string that is in the
  3191.    standard TIME$ format HH:MM:SS.
  3192.  
  3193.    ──────────────────────────────────────────────────────────────────────────
  3194.      ' ************************************************
  3195.      ' **  Name:          Time2Minute%               **
  3196.      ' **  Type:          Function                   **
  3197.      ' **  Module:        CALENDAR.BAS               **
  3198.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3199.      ' ************************************************
  3200.      '
  3201.      ' Returns the minute number as indicated in a time
  3202.      ' string in the format HH:MM:SS.
  3203.      '
  3204.      ' EXAMPLE OF USE:  minute% = Time2Minute%(tim$)
  3205.      ' PARAMETERS:      tim$       Time of concern
  3206.      ' VARIABLES:       (none)
  3207.      ' MODULE LEVEL
  3208.      '   DECLARATIONS:  DECLARE FUNCTION Time2Minute% (tim$)
  3209.      '
  3210.        FUNCTION Time2Minute% (tim$) STATIC
  3211.            Time2Minute% = VAL(MID$(tim$, 4, 2))
  3212.        END FUNCTION
  3213.    ──────────────────────────────────────────────────────────────────────────
  3214.  
  3215.  
  3216.  Function: Time2Second%
  3217.  
  3218.    Extracts the numeric value of the seconds from a time string that is in
  3219.    the standard TIME$ format HH:MM:SS.
  3220.  
  3221.    ──────────────────────────────────────────────────────────────────────────
  3222.      ' ************************************************
  3223.      ' **  Name:          Time2Second%               **
  3224.      ' **  Type:          Function                   **
  3225.      ' **  Module:        CALENDAR.BAS               **
  3226.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3227.      ' ************************************************
  3228.      '
  3229.      ' Returns the second number as indicated in a time
  3230.      ' string in the format HH:MM:SS.
  3231.      '
  3232.      ' EXAMPLE OF USE:  second% = Time2Second%(tim$)
  3233.      ' PARAMETERS:      tim$       Time of concern
  3234.      ' VARIABLES:       (none)
  3235.      ' MODULE LEVEL
  3236.      '   DECLARATIONS:  DECLARE FUNCTION Time2Second% (tim$)
  3237.      '
  3238.        FUNCTION Time2Second% (tim$) STATIC
  3239.            Time2Second% = VAL(MID$(tim$, 7))
  3240.        END FUNCTION
  3241.    ──────────────────────────────────────────────────────────────────────────
  3242.  
  3243.  
  3244.  Function: TimeDate2Second&
  3245.  
  3246.    Returns the number of seconds since the last second of 1979 given any date
  3247.    and time between the first second of 1980 and a moment in the year 2048.
  3248.  
  3249.    The largest positive long integer that can be stored in four bytes using
  3250.    two's complement notation is 2147483647. From the arbitrary point in time
  3251.    at the start of 1980, counting in seconds reaches this largest possible
  3252.    positive integer at 03:14:07 on 01-19-2048.
  3253.  
  3254.    One advantage of converting date and time to a number of seconds is that
  3255.    this long integer is more compact; this is an advantage, for example, when
  3256.    a large number of dates and times must be recorded in a disk file. Event
  3257.    logging, data acquisition, and business transaction time stamping are
  3258.    examples of the use of this type of subprogram.
  3259.  
  3260.    ──────────────────────────────────────────────────────────────────────────
  3261.      ' ************************************************
  3262.      ' **  Name:          TimeDate2Second&           **
  3263.      ' **  Type:          Function                   **
  3264.      ' **  Module:        CALENDAR.BAS               **
  3265.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3266.      ' ************************************************
  3267.      '
  3268.      ' Returns the number of seconds since the last
  3269.      ' second of 1979.  If the date is not in the years
  3270.      ' 1980 to 2047, an error message is output.
  3271.      '
  3272.      ' EXAMPLE OF USE:  sec& = TimeDate2Second&(tim$, dat$)
  3273.      ' PARAMETERS:      tim$       Time of concern
  3274.      '                  dat$       Date of concern
  3275.      ' VARIABLES:       days&      Days since 12-31-1979
  3276.      '                  hour%      Hour of the day
  3277.      '                  minute%    Minute of the hour
  3278.      '                  second%    Second of the minute
  3279.      '                  secs&      Working number of total seconds
  3280.      ' MODULE LEVEL
  3281.      '   DECLARATIONS:  DECLARE FUNCTION TimeDate2Second& (tim$, dat$)
  3282.      '
  3283.        FUNCTION TimeDate2Second& (tim$, dat$) STATIC
  3284.            days& = Date2Julian&(dat$) - 2444240
  3285.            hour% = VAL(LEFT$(tim$, 2))
  3286.            minute% = VAL(MID$(tim$, 4, 2))
  3287.            second% = VAL(RIGHT$(tim$, 2))
  3288.            secs& = CLNG(hour%) * 3600 + minute% * 60 + second%
  3289.            IF days& >= 0 AND days& < 24857 THEN
  3290.                TimeDate2Second& = days& * 86400 + secs&
  3291.            ELSE
  3292.                PRINT "TimeDate2Second: Not in range 1980 to 2047"
  3293.                SYSTEM
  3294.            END IF
  3295.        END FUNCTION
  3296.    ──────────────────────────────────────────────────────────────────────────
  3297.  
  3298.  
  3299.  
  3300.  ────────────────────────────────────────────────────────────────────────────
  3301.  CARTESIA
  3302.  
  3303.    The CARTESIA toolbox contains two subprograms and two functions that
  3304.    convert between Cartesian and polar coordinates. The program first prompts
  3305.    you to enter x and y values defining a point on the Cartesian plane and
  3306.    then prints the equivalent coordinate in polar notation.
  3307.  
  3308.    ┌────────────────────────────────────────────────────────────────────────┐
  3309.    │ This figure can be found on p.78 of the printed version of the book.   │
  3310.    └────────────────────────────────────────────────────────────────────────┘
  3311.  
  3312.    All of the variables in this module are defined as single-precision,
  3313.    floating-point values. If you need greater precision, globally change all
  3314.    exclamation point characters to pound sign characters. You'll also want to
  3315.    change the CONST PI statement in the Angle! function to provide a
  3316.    double-precision value for PI.
  3317.  
  3318.    Name                     Type    Description
  3319.    ──────────────────────────────────────────────────────────────────────────
  3320.    CARTESIA.BAS                    Demo module
  3321.    Angle!                  Func    Angle between X axis and line to x, y
  3322.                                     point
  3323.    Magnitude!              Func    Distance from origin to x, y point
  3324.    Pol2Rec                 Sub     Polar to Cartesian conversion
  3325.    Rec2Pol                 Sub     Cartesian to polar conversion
  3326.    ──────────────────────────────────────────────────────────────────────────
  3327.  
  3328.  
  3329.  Demo Module: CARTESIA
  3330.  
  3331.    ──────────────────────────────────────────────────────────────────────────
  3332.      ' ************************************************
  3333.      ' **  Name:          CARTESIA                   **
  3334.      ' **  Type:          Toolbox                    **
  3335.      ' **  Module:        CARTESIA.BAS               **
  3336.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3337.      ' ************************************************
  3338.      '
  3339.      ' Demonstrates a set of functions and subprograms
  3340.      ' dealing with Cartesian coordinates.
  3341.      '
  3342.      ' USAGE:           No command line parameters
  3343.      ' .MAK FILE:       (none)
  3344.      ' PARAMETERS:      (none)
  3345.      ' VARIABLES:       x!     X value of Cartesian coordinate
  3346.      '                  y!     Y value of Cartesian coordinate
  3347.      '                  r!     Polar notation distance from origin
  3348.      '                  theta! Polar notation angle from X axis
  3349.  
  3350.        DECLARE FUNCTION Angle! (x!, y!)
  3351.        DECLARE FUNCTION Magnitude! (x!, y!)
  3352.  
  3353.        DECLARE SUB Pol2Rec (r!, theta!, x!, y!)
  3354.        DECLARE SUB Rec2Pol (x!, y!, r!, theta!)
  3355.  
  3356.        CLS
  3357.        INPUT "Enter X  ", x!
  3358.        INPUT "Enter Y  ", y!
  3359.        PRINT
  3360.        PRINT "Magnitude!(x!, y!)", Magnitude!(x!, y!)
  3361.        PRINT "Angle!(x!, y!)", Angle!(x!, y!)
  3362.        PRINT
  3363.        Rec2Pol x!, y!, r!, theta!
  3364.        PRINT "Rec2Pol", , r!; theta!
  3365.        Pol2Rec r!, theta!, x!, y!
  3366.        PRINT "Pol2Rec", , x!; y!
  3367.    ──────────────────────────────────────────────────────────────────────────
  3368.  
  3369.  
  3370.  Function: Angle!
  3371.  
  3372.    Returns the angle from the origin to a given Cartesian coordinate,
  3373.    measured from the positive X axis. The angle, expressed in radians, is
  3374.    returned in the range -PI < Angle! <= +PI.
  3375.  
  3376.    This function has a good example of an IF-ELSEIF-ELSE-ENDIF structured
  3377.    statement. Notice that even though this function contains quite a few
  3378.    statements, the routine quickly skips over the unnecessary instructions.
  3379.    These tests let the function cover all the special case situations, such
  3380.    as when the coordinate falls on one or both axes.
  3381.  
  3382.    ──────────────────────────────────────────────────────────────────────────
  3383.      ' ************************************************
  3384.      ' **  Name:          Angle!                     **
  3385.      ' **  Type:          Function                   **
  3386.      ' **  Module:        CARTESIA.BAS               **
  3387.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3388.      ' ************************************************
  3389.      '
  3390.      ' Returns the angle (in radians) between the X axis
  3391.      ' and the line from the origin to the point x!,y!
  3392.      '
  3393.      ' EXAMPLE OF USE:  a! = Angle!(x!, y!)
  3394.      ' PARAMETERS:      x!         X part of the Cartesian coordinate
  3395.      '                  y!         Y part of the Cartesian coordinate
  3396.      ' VARIABLES:       (none)
  3397.      ' MODULE LEVEL
  3398.      '   DECLARATIONS:  DECLARE FUNCTION Angle! (x!, y!)
  3399.      '
  3400.        FUNCTION Angle! (x!, y!) STATIC
  3401.  
  3402.         CONST PI = 3.141593
  3403.         CONST HALFPI = PI / 2
  3404.  
  3405.         IF x! = 0! THEN
  3406.             IF y! > 0! THEN
  3407.              Angle! = HALFPI
  3408.             ELSEIF y! < 0! THEN
  3409.              Angle! = -HALFPI
  3410.             ELSE
  3411.              Angle! = 0!
  3412.             END IF
  3413.         ELSEIF y! = 0! THEN
  3414.             IF x! < 0! THEN
  3415.              Angle! = PI
  3416.             ELSE
  3417.              Angle! = 0!
  3418.             END IF
  3419.         ELSE
  3420.             IF x! < 0! THEN
  3421.              IF y! > 0! THEN
  3422.                  Angle! = ATN(y! / x!) + PI
  3423.              ELSE
  3424.                  Angle! = ATN(y! / x!) - PI
  3425.              END IF
  3426.             ELSE
  3427.              Angle! = ATN(y! / x!)
  3428.             END IF
  3429.         END IF
  3430.  
  3431.        END FUNCTION
  3432.    ──────────────────────────────────────────────────────────────────────────
  3433.  
  3434.  
  3435.  Function: Magnitude!
  3436.  
  3437.    Returns the distance from the origin to a given Cartesian coordinate.
  3438.  
  3439.    This function, together with the Angle! function, provides the
  3440.    calculations that perform rectangular to polar coordinate conversions.
  3441.    They are both called by the Rec2Pol subprogram.
  3442.  
  3443.    ──────────────────────────────────────────────────────────────────────────
  3444.      ' ************************************************
  3445.      ' **  Name:          Magnitude!                 **
  3446.      ' **  Type:          Function                   **
  3447.      ' **  Module:        CARTESIA.BAS               **
  3448.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3449.      ' ************************************************
  3450.      '
  3451.      ' Returns the distance from the origin to the
  3452.      ' point x!,y!
  3453.      '
  3454.      ' EXAMPLE OF USE:  r! =  Magnitude!(x!, y!)
  3455.      ' PARAMETERS:      x!         X part of the Cartesian coordinate
  3456.      '                  y!         Y part of the Cartesian coordinate
  3457.      ' VARIABLES:       (none)
  3458.      ' MODULE LEVEL
  3459.      '   DECLARATIONS:  DECLARE FUNCTION Magnitude! (x!, y!)
  3460.      '
  3461.        FUNCTION Magnitude! (x!, y!) STATIC
  3462.         Magnitude! = SQR(x! * x! + y! * y!)
  3463.        END FUNCTION
  3464.    ──────────────────────────────────────────────────────────────────────────
  3465.  
  3466.  
  3467.  Subprogram: Pol2Rec
  3468.  
  3469.    Converts a polar notation point (magnitude, angle) to its equivalent (x,
  3470.    y) Cartesian coordinates. The conversion assumes that theta! is expressed
  3471.    in radians and uses the built-in QuickBASIC functions for finding the sine
  3472.    and cosine of this angle.
  3473.  
  3474.    ──────────────────────────────────────────────────────────────────────────
  3475.      ' ************************************************
  3476.      ' **  Name:          Pol2rec                    **
  3477.      ' **  Type:          Subprogram                 **
  3478.      ' **  Module:        CARTESIA.BAS               **
  3479.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3480.      ' ************************************************
  3481.      '
  3482.      ' Converts polar coordinates to Cartesian notation.
  3483.      '
  3484.      ' EXAMPLE OF USE:  Pol2Rec r!, theta!, x!, y!
  3485.      ' PARAMETERS:      r!         Distance of point from the origin
  3486.      '                  theta!     Angle of point from the X axis
  3487.      '                  x!         X coordinate of the point
  3488.      '                  y!         Y coordinate of the point
  3489.      ' VARIABLES:       (none)
  3490.      ' MODULE LEVEL
  3491.      '   DECLARATIONS:  DECLARE SUB Pol2Rec (r!, theta!, x!, y!)
  3492.      '
  3493.        SUB Pol2Rec (r!, theta!, x!, y!) STATIC
  3494.         x! = r! * COS(theta!)
  3495.         y! = r! * SIN(theta!)
  3496.        END SUB
  3497.    ──────────────────────────────────────────────────────────────────────────
  3498.  
  3499.  
  3500.  Subprogram: Rec2Pol
  3501.  
  3502.    Converts a point expressed as a Cartesian coordinate pair (x, y) to the
  3503.    equivalent polar notation (magnitude, angle). The Angle! and Magnitude!
  3504.    functions within this toolbox perform the calculations for this
  3505.    conversion.
  3506.  
  3507.    ──────────────────────────────────────────────────────────────────────────
  3508.      ' ************************************************
  3509.      ' **  Name:          Rec2pol                    **
  3510.      ' **  Type:          Subprogram                 **
  3511.      ' **  Module:        CARTESIA.BAS               **
  3512.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3513.      ' ************************************************
  3514.      '
  3515.      ' Converts Cartesian coordinates to polar notation.
  3516.      '
  3517.      ' EXAMPLE OF USE:  Rec2Pol x!, y!, r!, theta!
  3518.      ' PARAMETERS:      x!         X coordinate of the point
  3519.      '                  y!         Y coordinate of the point
  3520.      '                  r!         Distance of point from the origin
  3521.      '                  theta!     Angle of point from the X axis
  3522.      ' VARIABLES:       (none)
  3523.      ' MODULE LEVEL
  3524.      '   DECLARATIONS:  DECLARE FUNCTION Angle! (x!, y!)
  3525.      '                  DECLARE FUNCTION Magnitude! (x!, y!)
  3526.      '                  DECLARE SUB Rec2Pol (x!, y!, r!, theta!)
  3527.      '
  3528.        SUB Rec2Pol (x!, y!, r!, theta!) STATIC
  3529.         r! = Magnitude!(x!, y!)
  3530.         theta! = Angle!(x!, y!)
  3531.        END SUB
  3532.    ──────────────────────────────────────────────────────────────────────────
  3533.  
  3534.  
  3535.  
  3536.  ────────────────────────────────────────────────────────────────────────────
  3537.  CIPHER
  3538.  
  3539.    The CIPHER program securely ciphers and deciphers any file. You probably
  3540.    have some files or data that you'd prefer to keep secret, such as personal
  3541.    financial information or proprietary business matters. Several packages on
  3542.    the market let you keep your files secure from prying eyes, but they do it
  3543.    at some expense. This program does it quickly and simply.
  3544.  
  3545.    For each byte in the file to be ciphered, the program generates a
  3546.    pseudorandom byte in the range 0 through 255. The pseudorandom byte and
  3547.    the file byte are combined using the QuickBASIC XOR function, and the byte
  3548.    in the file is replaced with this result. To decipher the file, use the
  3549.    CIPHER program to process the file a second time, using exactly the same
  3550.    key. XOR then returns the file to its original state.
  3551.  
  3552.    The bytes in the ciphered file will appear to be as random as the sequence
  3553.    of pseudorandom bytes generated for this process. The RandInteger%
  3554.    function generates the bytes, which makes the number of possible
  3555.    pseudorandom sequences astronomical. Without knowing the key string used
  3556.    to initialize this sequence, a person could probably not break the cipher.
  3557.    This points out an important fact about the security of this technique:
  3558.    The ciphered file is only as secure as the key you select. Let's see how
  3559.    you can choose a secure key.
  3560.  
  3561.    First, the "don'ts": Don't use simple, obvious keys such as your name,
  3562.    initials, names of family members or pets, addresses, phone numbers, and
  3563.    the like. Don't use the same key repeatedly. Don't record the keys in an
  3564.    easy-to-find place, such as in a batch file containing CIPHER commands.
  3565.    And don't forget to keep track of your key in some safe way. You won't be
  3566.    able to get your file back if you forget the key!
  3567.  
  3568.    There are several ways to generate your own keys in a safe, secure manner.
  3569.    Be creative! For example, you might choose your own private "magic number"
  3570.    that you can easily remember and use it to define a key. If your number is
  3571.    17, you could use the first 17 characters of line 17, page 17, in your
  3572.    favorite novel. Another technique is to create a common phrase that's easy
  3573.    to remember yet contains deliberately misspelled words or an odd
  3574.    combination of upper- and lowercase characters──3 blined MiSe, for
  3575.    example. Don't get too carried away with your creativity, though, and end
  3576.    up with something you can't remember. Changing even one character in the
  3577.    key will generate an entirely different sequence of pseudorandom bytes.
  3578.  
  3579.    The CIPHER program has a unique feature built into it to help generate new
  3580.    words that you can use as keys. Instead of typing the filename and key
  3581.    string on the command line that invokes the CIPHER program, type CIPHER
  3582.    /NEWKEY and press the Enter key. The program will generate nine
  3583.    pseudorandom words, created by randomly selecting characters from sets of
  3584.    consonants and vowels in a way that makes most of them readable.
  3585.  
  3586.    When you use the /NEWKEY command line option, a unique set of new words is
  3587.    generated for every possible clock tick in the life of your computer. In
  3588.    the module-level code of CIPHER.BAS is the statement RandShuffle DATE$ +
  3589.    TIME$ + STR$(TIMER), which initializes the random number generator when
  3590.    you give the /NEWKEY option. The key string for the initialization is
  3591.    formed by combining the date, time, and timer information into a string of
  3592.    about 27 characters.
  3593.  
  3594.    Eighteen times each second your computer updates its internal clock. The
  3595.    TIMER function returns a different value each time this happens, and the
  3596.    date and time are unique for every possible second of each day. As a
  3597.    result, the key string that initializes the random number generator will
  3598.    always be unique, and it's safe to say you'll never see the same group of
  3599.    nine words repeated.
  3600.  
  3601.    If you have a large number of files that you'd like to cipher, you can
  3602.    automate the process. Create a batch file containing CIPHER command lines,
  3603.    complete with filenames and keys. Keep this batch file ciphered at all
  3604.    times, except when you want to use it to cipher or decipher the group of
  3605.    files listed in the commands. This way, the only key you must remember is
  3606.    the one for unlocking the batch command file.
  3607.  
  3608.    To try out the CIPHER program, follow these steps. Create a small,
  3609.    readable file using the Document mode of the QuickBASIC editor, and save
  3610.    it as TEST.TXT. Verify the file contents by typing TYPE TEST.TXT. Now, to
  3611.    cipher the file, run CIPHER with the command line TEST.TXT ABC. When
  3612.    CIPHER is finished, the file will be unreadable, and entering TYPE
  3613.    TEST.TXT will result in strange characters on your screen. To decipher the
  3614.    file, once again run CIPHER with the same command line: TEST.TXT ABC. Be
  3615.    sure to enter the key string ("ABC" in this case) exactly the same as you
  3616.    did when the file was ciphered. Finally, type out the file to verify that
  3617.    it was correctly deciphered.
  3618.  
  3619.    Name                        Type     Description
  3620.    ──────────────────────────────────────────────────────────────────────────
  3621.    CIPHER.BAS                          Program module
  3622.    NewWord$                   Func     Creates pseudorandom new word
  3623.    ProcesX                    Sub      Enciphers string by XORing bytes
  3624.    ──────────────────────────────────────────────────────────────────────────
  3625.  
  3626.  
  3627.  Program Module: CIPHER
  3628.  
  3629.    ──────────────────────────────────────────────────────────────────────────
  3630.      ' ************************************************
  3631.      ' **  Name:          CIPHER                     **
  3632.      ' **  Type:          Program                    **
  3633.      ' **  Module:        CIPHER.BAS                 **
  3634.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3635.      ' ************************************************
  3636.      '
  3637.      ' USAGE:   CIPHER  filename.ext key    or    CIPHER /NEWKEY
  3638.      ' .MAK FILE:       CIPHER.BAS
  3639.      '                  RANDOMS.BAS
  3640.      ' PARAMETERS:      filename      Name of file to be ciphered or deciphere
  3641.      '                  key           String of one or more words used as the
  3642.      '                                cipher key
  3643.      ' VARIABLES:       cmd$          Working copy of COMMAND$
  3644.      '                  i%            Loop index
  3645.      '                  firstSpace%   Location in command line of first charac
  3646.      '                  fileName$     Name of file to be processed
  3647.      '                  key$          String to be used as cipher key
  3648.      '                  fileLength&   Length of file to be processed
  3649.      '                  a$            Workspace for groups of bytes from the f
  3650.      '                  count%        Number of groups of bytes to be processe
  3651.      '                  j&            Location in file of each group of bytes
  3652.  
  3653.      ' Constants
  3654.        CONST BYTES = 1000&
  3655.  
  3656.      ' Functions
  3657.        DECLARE FUNCTION NewWord$ ()
  3658.        DECLARE FUNCTION Rand& ()
  3659.        DECLARE FUNCTION RandInteger% (a%, b%)
  3660.  
  3661.      ' Subprograms
  3662.        DECLARE SUB RandShuffle (key$)
  3663.        DECLARE SUB ProcesX (a$)
  3664.  
  3665.      ' Initialization
  3666.        CLS
  3667.        PRINT "CIPHER "; COMMAND$
  3668.        PRINT
  3669.  
  3670.      ' Grab the command line parameters
  3671.        cmd$ = COMMAND$
  3672.  
  3673.      ' If no command line parameters, then tell user what's needed
  3674.        IF cmd$ = "" THEN
  3675.            PRINT
  3676.            PRINT "Usage:  CIPHER /NEWKEY"
  3677.            PRINT "(or)    CIPHER filename key-string"
  3678.            PRINT
  3679.            SYSTEM
  3680.        END IF
  3681.  
  3682.      ' If /NEWKEY option, generate a few new words, and then quit
  3683.        IF INSTR(cmd$, "/NEWKEY") THEN
  3684.  
  3685.          ' Clear the screen and describe the output
  3686.            CLS
  3687.            PRINT "Randomly created words that can be used as cipher keys..."
  3688.            PRINT
  3689.            RandShuffle DATE$ + TIME$ + STR$(TIMER)
  3690.            FOR i% = 1 TO 9
  3691.                PRINT NewWord$; " ";
  3692.            NEXT i%
  3693.            PRINT
  3694.            SYSTEM
  3695.        END IF
  3696.  
  3697.      ' Get the filename from the command line
  3698.        cmd$ = cmd$ + " "
  3699.        firstSpace% = INSTR(cmd$, " ")
  3700.        fileName$ = LEFT$(cmd$, firstSpace% - 1)
  3701.  
  3702.      ' Grab the rest of the command line as the cipher key
  3703.        key$ = LTRIM$(MID$(cmd$, firstSpace% + 1))
  3704.  
  3705.      ' Prepare the pseudorandom numbers using the key for shuffling
  3706.        RandShuffle key$
  3707.  
  3708.      ' Open up the file
  3709.        OPEN fileName$ FOR BINARY AS #1
  3710.        fileLength& = LOF(1)
  3711.  
  3712.      ' Process the file in manageable pieces
  3713.        a$ = SPACE$(BYTES)
  3714.        count% = fileLength& \ BYTES
  3715.  
  3716.      ' Loop through the file
  3717.        FOR i% = 0 TO count%
  3718.            j& = i% * BYTES + 1
  3719.            IF i% = count% THEN
  3720.                a$ = SPACE$(fileLength& - BYTES * count%)
  3721.            END IF
  3722.            GET #1, j&, a$
  3723.            ProcesX a$
  3724.            PUT #1, j&, a$
  3725.        NEXT i%
  3726.  
  3727.      ' All done
  3728.        SYSTEM
  3729.    ──────────────────────────────────────────────────────────────────────────
  3730.  
  3731.  
  3732.  Function: NewWord$
  3733.  
  3734.    Creates a pseudorandom, new "word" by randomly selecting appropriate
  3735.    consonants and vowels to form one to three syllables. These words can be
  3736.    useful as passwords, cipher keys, or new product names.
  3737.  
  3738.    ──────────────────────────────────────────────────────────────────────────
  3739.      ' ************************************************
  3740.      ' **  Name:          NewWord$                   **
  3741.      ' **  Type:          Function                   **
  3742.      ' **  Module:        CIPHER.BAS                 **
  3743.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3744.      ' ************************************************
  3745.      '
  3746.      ' Returns a pseudorandom word of a possibly
  3747.      ' speakable form.
  3748.      '
  3749.      ' EXAMPLE OF USE: PRINT NewWord$
  3750.      ' PARAMETERS:     (none)
  3751.      ' VARIABLES:      vowel$     String constant listing the set of vowels
  3752.      '                 consonant$ String constant listing the set of consonant
  3753.      '                 syllables% Random number of syllables for the new word
  3754.      '                 i%         Loop index for creating each syllable
  3755.      '                 t$         Temporary work string for forming the new wo
  3756.      ' MODULE LEVEL
  3757.      '   DECLARATIONS: DECLARE FUNCTION NewWord$ ()
  3758.      '
  3759.        FUNCTION NewWord$ STATIC
  3760.            CONST vowel$ = "aeiou"
  3761.            CONST consonant$ = "bcdfghjklmnpqrstvwxyz"
  3762.            syllables% = Rand& MOD 3 + 1
  3763.            FOR i% = 1 TO syllables%
  3764.                t$ = t$ + MID$(consonant$, RandInteger%(1, 21), 1)
  3765.                IF i% = 1 THEN
  3766.                    t$ = UCASE$(t$)
  3767.                END IF
  3768.                t$ = t$ + MID$(vowel$, RandInteger%(1, 5), 1)
  3769.            NEXT i%
  3770.            IF Rand& MOD 2 THEN
  3771.                t$ = t$ + MID$(consonant$, RandInteger%(1, 21), 1)
  3772.            END IF
  3773.            NewWord$ = t$
  3774.            t$ = ""
  3775.        END FUNCTION
  3776.    ──────────────────────────────────────────────────────────────────────────
  3777.  
  3778.  
  3779.  Subprogram: ProcesX
  3780.  
  3781.    Enciphers a string by XORing the bytes with a sequence of pseudorandom
  3782.    bytes. The bytes are generated as pseudorandom integers in the range 0
  3783.    through 255 by the RandInteger% function.
  3784.  
  3785.    If you initialize the random number generator with the same sequence and
  3786.    process the ciphered string a second time with this subprogram, the
  3787.    original string will result. The CIPHER program allows deciphering in this
  3788.    way by simply requiring that the ciphered file be "ciphered" a second time
  3789.    with the same key.
  3790.  
  3791.    ──────────────────────────────────────────────────────────────────────────
  3792.      ' ************************************************
  3793.      ' **  Name:          ProcesX                    **
  3794.      ' **  Type:          Subprogram                 **
  3795.      ' **  Module:        CIPHER.BAS                 **
  3796.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3797.      ' ************************************************
  3798.      '
  3799.      ' Enciphers a string by XORing with pseudorandom bytes.
  3800.      '
  3801.      ' EXAMPLE OF USE:  ProcesX a$
  3802.      ' PARAMETERS:      a$         String to be ciphered
  3803.      ' VARIABLES:       i%         Index into the string
  3804.      '                  byte%      Numeric value of each string character
  3805.      ' MODULE LEVEL
  3806.      '   DECLARATIONS:  DECLARE SUB ProcesX (a$)
  3807.      '
  3808.        SUB ProcesX (a$) STATIC
  3809.            FOR i% = 1 TO LEN(a$)
  3810.                byte% = ASC(MID$(a$, i%, 1)) XOR RandInteger%(0, 255)
  3811.                MID$(a$, i%, 1) = CHR$(byte%)
  3812.            NEXT i%
  3813.        END SUB
  3814.    ──────────────────────────────────────────────────────────────────────────
  3815.  
  3816.  
  3817.  
  3818.  ────────────────────────────────────────────────────────────────────────────
  3819.  COLORS
  3820.  
  3821.    The COLORS program provides a handy utility for interactively selecting
  3822.    colors from the 262,144 available in the VGA and MCGA graphics modes. To
  3823.    run this program, you must have a mouse and VGA or MCGA graphics
  3824.    capability.
  3825.  
  3826.    The program is easy to use. Simply click on any of the three color bars to
  3827.    set the intensity of that color. The ellipse on the left side of the
  3828.    screen shows the color shade you selected, and the long integer value at
  3829.    the top of the screen shows the numeric value to use with the PALETTE
  3830.    statement for setting this same color in other programs. When you're ready
  3831.    to quit, click on the X at the lower left corner of the screen.
  3832.  
  3833.    You can run this program from the QuickBASIC environment, but to make it
  3834.    an easily accessible utility, it's probably better to compile it and
  3835.    create a stand-alone .EXE program module.
  3836.  
  3837.    Name                     Type    Description
  3838.    ──────────────────────────────────────────────────────────────────────────
  3839.    COLORS.BAS                      Program module
  3840.    Shade&                  Func    Color value from given red, green, and
  3841.                                     blue
  3842.    ──────────────────────────────────────────────────────────────────────────
  3843.  
  3844.  
  3845.  Program Module: COLORS
  3846.  
  3847.    ──────────────────────────────────────────────────────────────────────────
  3848.      ' ************************************************
  3849.      ' **  Name:          COLORS                     **
  3850.      ' **  Type:          Program                    **
  3851.      ' **  Module:        COLORS.BAS                 **
  3852.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  3853.      ' ************************************************
  3854.      '
  3855.      ' Provides interactive selection of a color shade.
  3856.      '
  3857.      ' USAGE:           No command line parameters
  3858.      ' REQUIREMENTS:    VGA or MCGA
  3859.      '                  MIXED.QLB/.LIB
  3860.      '                  Mouse
  3861.      ' .MAK FILE:       COLORS.BAS
  3862.      '                  BITS.BAS
  3863.      '                  MOUSSUBS.BAS
  3864.      ' PARAMETERS:      (none)
  3865.      ' VARIABLES:       red!            Intensity of red, from 0 to 1
  3866.      '                  green!          Intensity of green, from 0 to 1
  3867.      '                  blue!           Intensity of blue, from 0 to 1
  3868.      '                  mask$           Mouse graphics cursor definition strin
  3869.      '                  xHot%           Mouse cursor hot spot X location
  3870.      '                  yHot%           Mouse cursor hot spot Y location
  3871.      '                  cursor$         Mouse cursor binary definition string
  3872.      '                  fill%           Color bar height calculation
  3873.      '                  x%              Color bar horizontal left edge
  3874.      '                  x2%             Color bar horizontal right edge
  3875.      '                  y%              Color bar vertical top edge
  3876.      '                  y2%             Color bar vertical bottom edge
  3877.      '                  leftButton%     State of left mouse button
  3878.      '                  rightButton%    State of right mouse button
  3879.      '                  xMouse%         Horizontal mouse location
  3880.      '                  yMouse%         Vertical mouse location
  3881.      '                  clickFlag%      Toggle for left mouse button state
  3882.      '                  xM%             Modified mouse horizontal location
  3883.      '                  quitFlag%       Signal to end program
  3884.  
  3885.  
  3886.      ' Logical constants
  3887.        CONST FALSE = 0
  3888.        CONST TRUE = NOT FALSE
  3889.  
  3890.      ' Constants
  3891.        CONST REDPAL = 1
  3892.        CONST BLUEPAL = 2
  3893.        CONST GREENPAL = 3
  3894.        CONST TESTPAL = 4
  3895.        CONST WHITEPAL = 5
  3896.        CONST BARPAL = 6
  3897.        CONST DX = 15
  3898.        CONST DY = 150
  3899.        CONST RX = 180
  3900.        CONST RY = 30
  3901.        CONST GX = RX + DX + DX
  3902.        CONST GY = RY
  3903.        CONST BX = GX + DX + DX
  3904.        CONST BY = RY
  3905.  
  3906.      ' Functions
  3907.        DECLARE FUNCTION Shade& (red!, green!, blue!)
  3908.  
  3909.      ' Subprograms
  3910.        DECLARE SUB MouseHide ()
  3911.        DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$)
  3912.        DECLARE SUB MouseSetGcursor (cursor$)
  3913.        DECLARE SUB MouseShow ()
  3914.        DECLARE SUB Cursleft (mask$, xHot%, yHot%)
  3915.        DECLARE SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%)
  3916.  
  3917.      ' Set 256 color mode
  3918.        SCREEN 13
  3919.  
  3920.      ' Set first three colors as pure red, green, blue
  3921.        PALETTE REDPAL, Shade&(1!, 0!, 0!)
  3922.        PALETTE GREENPAL, Shade&(0!, 1!, 0!)
  3923.        PALETTE BLUEPAL, Shade&(0!, 0!, 1!)
  3924.  
  3925.      ' Set a pure white color choice
  3926.        PALETTE WHITEPAL, Shade&(1!, 1!, 1!)
  3927.  
  3928.      ' Set bar background color
  3929.        PALETTE BARPAL, Shade&(0!, 0!, 0!)
  3930.  
  3931.      ' Set background to light gray
  3932.        PALETTE 0, Shade&(.4, .4, .4)
  3933.  
  3934.      ' Start each intensity at midscale
  3935.        red! = .5
  3936.        green! = .5
  3937.        blue! = .5
  3938.  
  3939.      ' Set starting shade
  3940.        PALETTE TESTPAL, Shade&(red!, green!, blue!)
  3941.  
  3942.      ' Create ellipse of circle to show current shade selected
  3943.        CIRCLE (70, 100), 80, TESTPAL, , , 1.4
  3944.        PAINT (70, 100), TESTPAL
  3945.  
  3946.      ' Create the three color bars
  3947.        LINE (RX, RY)-(RX + DX, RY + DY), WHITEPAL, B
  3948.        LINE (GX, GY)-(GX + DX, GY + DY), WHITEPAL, B
  3949.        LINE (BX, BY)-(BX + DX, BY + DY), WHITEPAL, B
  3950.  
  3951.      ' Mark place to quit by clicking
  3952.        LOCATE 25, 1
  3953.        PRINT "(X) "; CHR$(27); " Quit";
  3954.  
  3955.      ' Make the left arrow mouse cursor
  3956.        Cursleft mask$, xHot%, yHot%
  3957.        MouseMaskTranslate mask$, xHot%, yHot%, cursor$
  3958.        MouseSetGcursor cursor$
  3959.  
  3960.      ' Main loop
  3961.        DO
  3962.  
  3963.          ' Put title and current shade number at top
  3964.            LOCATE 1, 1
  3965.            PRINT "COLOR CHOOSER"; TAB(22);
  3966.            PRINT USING "##########"; Shade&(red!, green!, blue!)
  3967.  
  3968.          ' Fill in the red color bar
  3969.            fill% = red! * (DY - 3) + 1
  3970.            x% = RX + 1
  3971.            x2% = RX + DX
  3972.            y% = RY + 1
  3973.            y2% = RY + DY
  3974.            LINE (x%, y%)-(x2% - 1, y2% - fill% - 1), BARPAL, BF
  3975.            LINE (x%, y2% - fill%)-(x2% - 1, y2% - 1), REDPAL, BF
  3976.  
  3977.          ' Fill in the green color bar
  3978.            fill% = green! * (DY - 3) + 1
  3979.            x% = GX + 1
  3980.            x2% = GX + DX
  3981.            y% = GY + 1
  3982.            y2% = GY + DY
  3983.            LINE (x%, y%)-(x2% - 1, y2% - fill% - 1), BARPAL, BF
  3984.            LINE (x%, y2% - fill%)-(x2% - 1, y2% - 1), GREENPAL, BF
  3985.  
  3986.          ' Fill in the blue color bar
  3987.            fill% = blue! * (DY - 3) + 1
  3988.            x% = BX + 1
  3989.            x2% = BX + DX
  3990.            y% = BY + 1
  3991.            y2% = BY + DY
  3992.            LINE (x%, y%)-(x2% - 1, y2% - fill% - 1), BARPAL, BF
  3993.            LINE (x%, y2% - fill%)-(x2% - 1, y2% - 1), BLUEPAL, BF
  3994.  
  3995.          ' Change the shade of the ellipse
  3996.            PALETTE TESTPAL, Shade&(red!, green!, blue!)
  3997.  
  3998.          ' Refresh mouse cursor
  3999.            MouseShow
  4000.  
  4001.          ' Wait for fresh mouse left button click
  4002.            DO
  4003.                MouseNow leftButton%, rightButton%, xMouse%, yMouse%
  4004.                IF leftButton% = FALSE THEN
  4005.                    clickFlag% = FALSE
  4006.                END IF
  4007.                IF clickFlag% THEN
  4008.                    leftButton% = 0
  4009.                END IF
  4010.            LOOP UNTIL leftButton%
  4011.  
  4012.          ' Hide mouse and set parameters
  4013.            MouseHide
  4014.            clickFlag% = TRUE
  4015.            xM% = xMouse% \ 2
  4016.  
  4017.          ' Is mouse in the "Quit" area?
  4018.            IF xMouse% < 45 AND yMouse% > 190 THEN
  4019.                quitFlag% = TRUE
  4020.            END IF
  4021.  
  4022.          ' Is mouse at the right height to be in a bar?
  4023.            IF yMouse% > RY - 2 AND yMouse% < RY + DY + 2 THEN
  4024.  
  4025.              ' Is mouse in the red bar?
  4026.                IF xM% > RX AND xM% < RX + DX THEN
  4027.                    red! = 1! - (yMouse% - RY) / DY
  4028.                    IF red! < 0 THEN
  4029.                        red! = 0
  4030.                    ELSEIF red! > 1 THEN
  4031.                        red! = 1
  4032.                    END IF
  4033.                END IF
  4034.  
  4035.              ' Is mouse in the green bar?
  4036.                IF xM% > GX AND xM% < GX + DX THEN
  4037.                    green! = 1! - (yMouse% - RY) / DY
  4038.                    IF green! < 0 THEN
  4039.                        green! = 0
  4040.                    ELSEIF green! > 1 THEN
  4041.                        green! = 1
  4042.                    END IF
  4043.                END IF
  4044.  
  4045.              ' Is mouse in the blue bar?
  4046.                IF xM% > BX AND xM% < BX + DX THEN
  4047.                    blue! = 1! - (yMouse% - RY) / DY
  4048.                    IF blue! < 0 THEN
  4049.                        blue! = 0
  4050.                    ELSEIF blue! > 1 THEN
  4051.                        blue! = 1
  4052.                    END IF
  4053.                END IF
  4054.  
  4055.            END IF
  4056.  
  4057.        LOOP UNTIL quitFlag%
  4058.  
  4059.        SCREEN 0
  4060.        WIDTH 80
  4061.        CLS
  4062.        END
  4063.    ──────────────────────────────────────────────────────────────────────────
  4064.  
  4065.  
  4066.  Function: Shade&
  4067.  
  4068.    Returns the long integer number for a given shade of color.
  4069.  
  4070.    This is the only function the COLORS utility provides, but it's useful
  4071.    when programming the VGA and MCGA SCREEN modes 11, 12, and 13. You can use
  4072.    the long integer value returned in a PALETTE statement for setting a color
  4073.    attribute to one of 262,144 color choices.
  4074.  
  4075.    Three single-precision numbers are passed to this routine, representing
  4076.    the desired intensities of the red, green, and blue colors. These numbers
  4077.    must be in the range 0.0 through 1.0. Shade&(1!, 0!, 0!), for example,
  4078.    returns the long integer value for bright red; Shade&(.5, .5, .5) returns
  4079.    a number for medium gray.
  4080.  
  4081.    The best way to see the results of setting the three colors to various
  4082.    intensity levels is by running the COLORS program.
  4083.  
  4084.    ──────────────────────────────────────────────────────────────────────────
  4085.      ' ************************************************
  4086.      ' **  Name:          Shade&                     **
  4087.      ' **  Type:          Function                   **
  4088.      ' **  Module:        COLORS.BAS                 **
  4089.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4090.      ' ************************************************
  4091.      '
  4092.      ' Returns the long integer color number given red,
  4093.      ' green, and blue intensity numbers in the range
  4094.      ' 0 through 1.
  4095.      '
  4096.      ' EXAMPLE OF USE:  PALETTE 1, Shade&(red!, green!, blue!)
  4097.      ' PARAMETERS:      red!       Intensity of red, from 0 to 1
  4098.      '                  green!     Intensity of green, from 0 to 1
  4099.      '                  blue!      Intensity of blue, from 0 to 1
  4100.      ' VARIABLES:       r&         Red amount
  4101.      '                  g&         Green amount
  4102.      '                  b&         Blue amount
  4103.      ' MODULE LEVEL
  4104.      '   DECLARATIONS:  DECLARE FUNCTION Shade& (red!, green!, blue!)
  4105.      '
  4106.        FUNCTION Shade& (red!, green!, blue!) STATIC
  4107.            r& = red! * 63!
  4108.            g& = green! * 63!
  4109.            b& = blue! * 63!
  4110.            Shade& = r& + g& * 256& + b& * 65536
  4111.        END FUNCTION
  4112.    ──────────────────────────────────────────────────────────────────────────
  4113.  
  4114.  
  4115.  
  4116.  ────────────────────────────────────────────────────────────────────────────
  4117.  COMPLEX
  4118.  
  4119.    The COMPLEX toolbox provides a collection of subprograms for working with
  4120.    complex numbers. The QuickBASIC TYPE definition statement is ideal for
  4121.    declaring variables to be of type Complex, as shown. The variables a, b,
  4122.    and c each comprise a pair of single-precision numbers representing the
  4123.    real and imaginary parts of a complex number. These variables are passed
  4124.    to and from the subprograms as easily as if they were simple numeric
  4125.    values.
  4126.  
  4127.    Complex numbers are expressed as the sum of a real and an imaginary
  4128.    number. Usually you show a complex number by writing the real number,
  4129.    followed immediately by a plus or minus sign, the imaginary number, and a
  4130.    small letter "i" or "j," which represents the square root of -1 and
  4131.    indicates the imaginary numeric component of the complex number.
  4132.  
  4133.    In this program, complex numbers are entered and displayed in a similar
  4134.    format. You use parentheses to surround each complex number. This sample
  4135.    run of COMPLEX shows typical input and output complex-number formats:
  4136.  
  4137.  
  4138.      Enter first complex number  ? (4-5i)
  4139.      (4-5i)
  4140.  
  4141.      ComplexExp                     (15.48743+52.35549i)
  4142.      ComplexLog                     1.856786-.8960554i)
  4143.      ComplexReciprocal              9.756097E-02+.1219512i)
  4144.      ComplexSqr                     2.280693-1.096158i)
  4145.  
  4146.      Enter second complex number    ? (3+4i)
  4147.      (3+4i)
  4148.  
  4149.      ComplexAdd                     (7-1i)
  4150.      ComplexSub                     (1-9i)
  4151.      ComplexMul                     (32+1i)
  4152.      ComplexDiv                     (-.32-1.24i)
  4153.      ComplexPower                   (251.4394-9454.315i)
  4154.      ComplexRoot                    (.9952651-.4262131i)
  4155.  
  4156.  
  4157.  
  4158.      Press any key to continue
  4159.  
  4160.    Name                     Type    Description
  4161.    ──────────────────────────────────────────────────────────────────────────
  4162.    COMPLEX.BAS                     Demo module
  4163.    Complex2String          Sub     String representation of a complex number
  4164.    ComplexAdd              Sub     Adds two complex numbers
  4165.    ComplexDiv              Sub     Divides two complex numbers
  4166.    ComplexExp              Sub     Exponential function of a complex number
  4167.    ComplexLog              Sub     Natural log of a complex number
  4168.    ComplexMul              Sub     Multiplies two complex numbers
  4169.    ComplexPower            Sub     Complex number raised to a complex number
  4170.    ComplexReciprocal       Sub     Reciprocal of a complex number
  4171.    ComplexRoot             Sub     Complex root of a complex number
  4172.    ComplexSqr              Sub     Square root of a complex number
  4173.    ComplexSub              Sub     Subtracts two complex numbers
  4174.    String2Complex          Sub     Converts string to complex variable
  4175.    ──────────────────────────────────────────────────────────────────────────
  4176.  
  4177.  
  4178.  Demo Module: COMPLEX
  4179.  
  4180.    ──────────────────────────────────────────────────────────────────────────
  4181.      ' ************************************************
  4182.      ' **  Name:          COMPLEX                    **
  4183.      ' **  Type:          Toolbox                    **
  4184.      ' **  Module:        COMPLEX.BAS                **
  4185.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4186.      ' ************************************************
  4187.      '
  4188.      ' Demonstrates a set of complex number functions and
  4189.      ' subprograms.
  4190.      '
  4191.      ' USAGE:         No command line parameters
  4192.      ' .MAK FILE:     COMPLEX.BAS
  4193.      '                CARTESIA.BAS
  4194.      ' PARAMETERS:    (none)
  4195.      ' VARIABLES:     a          Variable of type Complex
  4196.      '                b          Variable of type Complex
  4197.      '                c          Variable of type Complex
  4198.      '                x$         String representation of a complex number
  4199.      '                y$         String representation of a complex number
  4200.      '                z$         String representation of a complex number
  4201.  
  4202.        TYPE Complex
  4203.            r AS SINGLE
  4204.            i AS SINGLE
  4205.        END TYPE
  4206.  
  4207.      ' Subprograms
  4208.        DECLARE SUB ComplexSub (a AS Complex, b AS Complex, c AS Complex)
  4209.        DECLARE SUB ComplexSqr (a AS Complex, c AS Complex)
  4210.        DECLARE SUB ComplexRoot (a AS Complex, b AS Complex, c AS Complex)
  4211.        DECLARE SUB ComplexReciprocal (a AS Complex, c AS Complex)
  4212.        DECLARE SUB ComplexAdd (a AS Complex, b AS Complex, c AS Complex)
  4213.        DECLARE SUB ComplexLog (a AS Complex, c AS Complex)
  4214.        DECLARE SUB ComplexPower (a AS Complex, b AS Complex, c AS Complex)
  4215.        DECLARE SUB Complex2String (a AS Complex, x$)
  4216.        DECLARE SUB String2Complex (x$, a AS Complex)
  4217.        DECLARE SUB ComplexDiv (a AS Complex, b AS Complex, c AS Complex)
  4218.        DECLARE SUB ComplexExp (a AS Complex, c AS Complex)
  4219.        DECLARE SUB ComplexMul (a AS Complex, b AS Complex, c AS Complex)
  4220.        DECLARE SUB Rec2pol (x!, y!, r!, theta!)
  4221.  
  4222.        DIM a AS Complex, b AS Complex, c AS Complex
  4223.  
  4224.        CLS
  4225.        INPUT "Enter first complex number  "; x$
  4226.        String2Complex x$, a
  4227.        Complex2String a, x$
  4228.        PRINT x$
  4229.        PRINT
  4230.  
  4231.        ComplexExp a, c
  4232.        Complex2String c, z$
  4233.        PRINT "ComplexExp", , z$
  4234.  
  4235.        ComplexLog a, c
  4236.        Complex2String c, z$
  4237.        PRINT "ComplexLog", , z$
  4238.  
  4239.        ComplexReciprocal a, c
  4240.        Complex2String c, z$
  4241.        PRINT "ComplexReciprocal", z$
  4242.  
  4243.        ComplexSqr a, c
  4244.        Complex2String c, z$
  4245.        PRINT "ComplexSqr", , z$
  4246.  
  4247.        PRINT
  4248.        INPUT "Enter second complex number "; y$
  4249.        String2Complex y$, b
  4250.        Complex2String b, y$
  4251.        PRINT y$
  4252.        PRINT
  4253.  
  4254.        ComplexAdd a, b, c
  4255.        Complex2String c, z$
  4256.        PRINT "ComplexAdd", , z$
  4257.  
  4258.        ComplexSub a, b, c
  4259.        Complex2String c, z$
  4260.        PRINT "ComplexSub", , z$
  4261.  
  4262.        ComplexMul a, b, c
  4263.        Complex2String c, z$
  4264.        PRINT "ComplexMul", , z$
  4265.  
  4266.        ComplexDiv a, b, c
  4267.        Complex2String c, z$
  4268.        PRINT "ComplexDiv", , z$
  4269.  
  4270.        ComplexPower a, b, c
  4271.        Complex2String c, z$
  4272.        PRINT "ComplexPower", , z$
  4273.  
  4274.        ComplexRoot a, b, c
  4275.        Complex2String c, z$
  4276.        PRINT "ComplexRoot", , z$
  4277.    ──────────────────────────────────────────────────────────────────────────
  4278.  
  4279.  
  4280.  Subprogram: Complex2String
  4281.  
  4282.    Creates a string representation of a complex number suitable for printing
  4283.    or displaying the results of complex number calculations. The string
  4284.    consists of two numbers enclosed in parentheses and separated by either a
  4285.    plus or minus sign, with the second number followed by a lowercase "i" to
  4286.    indicate the imaginary component. The length of this string result will
  4287.    vary, depending on the numeric values of the real and imaginary parts.
  4288.  
  4289.    All results displayed by the demonstrations are formatted using this
  4290.    subprogram.
  4291.  
  4292.    ──────────────────────────────────────────────────────────────────────────
  4293.      ' ************************************************
  4294.      ' **  Name:          Complex2String             **
  4295.      ' **  Type:          Subprogram                 **
  4296.      ' **  Module:        COMPLEX.BAS                **
  4297.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4298.      ' ************************************************
  4299.      '
  4300.      ' Makes a string representation of a complex number.
  4301.      '
  4302.      ' EXAMPLE OF USE:  Complex2String a, x$
  4303.      ' PARAMETERS:      a          Complex number variable (type Complex)
  4304.      '                  x$         String representation of the complex number
  4305.      ' VARIABLES:       r$         Working string, real part
  4306.      '                  i$         Working string, imaginary part
  4307.      ' MODULE LEVEL
  4308.      '   DECLARATIONS:  TYPE Complex
  4309.      '                     r AS SINGLE
  4310.      '                     i AS SINGLE
  4311.      '                  END TYPE
  4312.      '
  4313.      '                  DECLARE SUB Complex2String (a AS Complex, x$)
  4314.      '
  4315.        SUB Complex2String (a AS Complex, x$) STATIC
  4316.  
  4317.          ' Form the left part of the string
  4318.            IF a.r < 0 THEN
  4319.                r$ = "(" + STR$(a.r)
  4320.            ELSE
  4321.                r$ = "(" + MID$(STR$(a.r), 2)
  4322.            END IF
  4323.  
  4324.          ' Form the right part of the string
  4325.            IF a.i < 0 THEN
  4326.                i$ = STR$(a.i)
  4327.            ELSE
  4328.                i$ = "+" + MID$(STR$(a.i), 2)
  4329.            END IF
  4330.  
  4331.          ' The whole is more complex than the sum of the parts
  4332.            x$ = r$ + i$ + "i)"
  4333.  
  4334.        END SUB
  4335.    ──────────────────────────────────────────────────────────────────────────
  4336.  
  4337.  
  4338.  Subprogram: ComplexAdd
  4339.  
  4340.    Calculates the sum of two complex numbers. Complex number a is added to
  4341.    complex number b, and the result is placed in the variable c.
  4342.  
  4343.    ──────────────────────────────────────────────────────────────────────────
  4344.      ' ************************************************
  4345.      ' **  Name:          ComplexAdd                 **
  4346.      ' **  Type:          Subprogram                 **
  4347.      ' **  Module:        COMPLEX.BAS                **
  4348.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4349.      ' ************************************************
  4350.      '
  4351.      ' Adds two complex numbers.
  4352.      '
  4353.      ' EXAMPLE OF USE:  ComplexAdd a, b, c
  4354.      ' PARAMETERS:      a          First complex number for the addition
  4355.      '                  b          Second complex number for the addition
  4356.      '                  c          Result of the complex number addition
  4357.      ' VARIABLES:       (none)
  4358.      ' MODULE LEVEL
  4359.      '   DECLARATIONS:  TYPE Complex
  4360.      '                     r AS SINGLE
  4361.      '                     i AS SINGLE
  4362.      '                  END TYPE
  4363.      '
  4364.      '         DECLARE SUB ComplexAdd (a AS Complex, b AS Complex, c AS Comple
  4365.      '
  4366.        SUB ComplexAdd (a AS Complex, b AS Complex, c AS Complex) STATIC
  4367.            c.r = a.r + b.r
  4368.            c.i = a.i + b.i
  4369.        END SUB
  4370.    ──────────────────────────────────────────────────────────────────────────
  4371.  
  4372.  
  4373.  Subprogram: ComplexDiv
  4374.  
  4375.    Calculates the result of dividing one complex number by another. The
  4376.    result of a/b is placed in the variable c.
  4377.  
  4378.    ──────────────────────────────────────────────────────────────────────────
  4379.      ' ************************************************
  4380.      ' **  Name:          ComplexDiv                 **
  4381.      ' **  Type:          Subprogram                 **
  4382.      ' **  Module:        COMPLEX.BAS                **
  4383.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4384.      ' ************************************************
  4385.      '
  4386.      ' Divides two complex numbers.
  4387.      '
  4388.      ' EXAMPLE OF USE:  ComplexDiv a, b, c
  4389.      ' PARAMETERS:      a          First complex number for the division
  4390.      '                  b          Second complex number for the division
  4391.      '                  c          Result of the complex number division a/b
  4392.      ' VARIABLES:       (none)
  4393.      ' MODULE LEVEL
  4394.      '   DECLARATIONS:  TYPE Complex
  4395.      '                     r AS SINGLE
  4396.      '                     i AS SINGLE
  4397.      '                  END TYPE
  4398.      '
  4399.      '        DECLARE SUB ComplexDiv (a AS Complex, b AS Complex, c AS Complex
  4400.      '
  4401.        SUB ComplexDiv (a AS Complex, b AS Complex, c AS Complex) STATIC
  4402.            t! = b.r * b.r + b.i * b.i
  4403.            c.r = (a.r * b.r + a.i * b.i) / t!
  4404.            c.i = (a.i * b.r - a.r * b.i) / t!
  4405.        END SUB
  4406.    ──────────────────────────────────────────────────────────────────────────
  4407.  
  4408.  
  4409.  Subprogram: ComplexExp
  4410.  
  4411.    Calculates the exponential function of a complex number a. The result is
  4412.    placed in the variable c.
  4413.  
  4414.    ──────────────────────────────────────────────────────────────────────────
  4415.      ' ************************************************
  4416.      ' **  Name:          ComplexExp                 **
  4417.      ' **  Type:          Subprogram                 **
  4418.      ' **  Module:        COMPLEX.BAS                **
  4419.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4420.      ' ************************************************
  4421.      '
  4422.      ' Calculates the exponential function of a complex number.
  4423.      '
  4424.      ' EXAMPLE OF USE:  ComplexExp a, c
  4425.      ' PARAMETERS:      a          Complex number argument
  4426.      '                  c          Complex result of the calculations
  4427.      ' VARIABLES:       t!         Temporary working value
  4428.      ' MODULE LEVEL
  4429.      '   DECLARATIONS:  TYPE Complex
  4430.      '                     r AS SINGLE
  4431.      '                     i AS SINGLE
  4432.      '                  END TYPE
  4433.      '
  4434.      '                  DECLARE SUB ComplexExp (a AS Complex, c AS Complex)
  4435.      '
  4436.        SUB ComplexExp (a AS Complex, c AS Complex) STATIC
  4437.            t! = EXP(a.r)
  4438.            c.r = t! * COS(a.i)
  4439.            c.i = t! * SIN(a.i)
  4440.        END SUB
  4441.    ──────────────────────────────────────────────────────────────────────────
  4442.  
  4443.  
  4444.  Subprogram: ComplexLog
  4445.  
  4446.    Calculates the complex logarithm of a complex number a. The result is
  4447.    placed in the variable c.
  4448.  
  4449.    ──────────────────────────────────────────────────────────────────────────
  4450.      ' ************************************************
  4451.      ' **  Name:          ComplexLog                 **
  4452.      ' **  Type:          Subprogram                 **
  4453.      ' **  Module:        COMPLEX.BAS                **
  4454.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4455.      ' ************************************************
  4456.      '
  4457.      ' Calculates the log of a complex number.
  4458.      '
  4459.      ' EXAMPLE OF USE:  ComplexLog a, c
  4460.      ' PARAMETERS:      a          Complex number argument
  4461.      '                  c          Complex result of the calculations
  4462.      ' VARIABLES:       r!         Magnitude of complex number a
  4463.      '                  theta!     Angle of complex number a
  4464.      ' MODULE LEVEL
  4465.      '   DECLARATIONS:  TYPE Complex
  4466.      '                     r AS SINGLE
  4467.      '                     i AS SINGLE
  4468.      '                  END TYPE
  4469.      '
  4470.      '                  DECLARE SUB ComplexLog (a AS Complex, c AS Complex)
  4471.      '                  DECLARE SUB Rec2pol (x!, y!, r!, theta!)
  4472.      '
  4473.        SUB ComplexLog (a AS Complex, c AS Complex) STATIC
  4474.            CALL Rec2pol(a.r, a.i, r!, theta!)
  4475.            IF r! <> 0! THEN
  4476.                c.r = LOG(r!)
  4477.                c.i = theta!
  4478.            ELSE
  4479.                ERROR 5
  4480.            END IF
  4481.        END SUB
  4482.    ──────────────────────────────────────────────────────────────────────────
  4483.  
  4484.  
  4485.  Subprogram: ComplexMul
  4486.  
  4487.    Calculates the product of two complex numbers. Complex variables a and b
  4488.    are multiplied, and the result is placed in the variable c.
  4489.  
  4490.    ──────────────────────────────────────────────────────────────────────────
  4491.      ' ************************************************
  4492.      ' **  Name:          ComplexMul                 **
  4493.      ' **  Type:          Subprogram                 **
  4494.      ' **  Module:        COMPLEX.BAS                **
  4495.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4496.      ' ************************************************
  4497.      '
  4498.      ' Multiplies two complex numbers.
  4499.      '
  4500.      ' EXAMPLE OF USE:  ComplexMul a, b, c
  4501.      ' PARAMETERS:      a          First complex number for the multiplication
  4502.      '                  b          Second complex number for the multiplicatio
  4503.      '                  c          Result of the complex number multiplication
  4504.      ' VARIABLES:       (none)
  4505.      ' MODULE LEVEL
  4506.      '   DECLARATIONS:  TYPE Complex
  4507.      '                     r AS SINGLE
  4508.      '                     i AS SINGLE
  4509.      '                  END TYPE
  4510.      '
  4511.      '         DECLARE SUB ComplexMul (a AS Complex, b AS Complex, c AS Comple
  4512.      '
  4513.        SUB ComplexMul (a AS Complex, b AS Complex, c AS Complex) STATIC
  4514.            c.r = a.r * b.r - a.i * b.i
  4515.            c.i = a.r * b.i + a.i * b.r
  4516.        END SUB
  4517.    ──────────────────────────────────────────────────────────────────────────
  4518.  
  4519.  
  4520.  Subprogram: ComplexPower
  4521.  
  4522.    Calculates the result of raising one complex number to the power of
  4523.    another. The result of raising a to the power of b is then placed in the
  4524.    variable c.
  4525.  
  4526.    Notice that this subprogram calls several others. If you extract this
  4527.    routine for use in another program module, be sure to extract the other
  4528.    subprograms as well.
  4529.  
  4530.    ──────────────────────────────────────────────────────────────────────────
  4531.      ' ************************************************
  4532.      ' **  Name:          ComplexPower               **
  4533.      ' **  Type:          Subprogram                 **
  4534.      ' **  Module:        COMPLEX.BAS                **
  4535.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4536.      ' ************************************************
  4537.      '
  4538.      ' Calculates a complex number raised to a complex number.
  4539.      '
  4540.      ' EXAMPLE OF USE:  ComplexPower a, b, c
  4541.      ' PARAMETERS:      a          Complex number to be raised to a power
  4542.      '                  b          Complex number to raise a to
  4543.      '                  c          Result of a raised to the power of b
  4544.      ' VARIABLES:       t1         Structure of type Complex
  4545.      '                  t2         Structure of type Complex
  4546.      ' MODULE LEVEL
  4547.      '   DECLARATIONS:  TYPE Complex
  4548.      '                     r AS SINGLE
  4549.      '                     i AS SINGLE
  4550.      '                  END TYPE
  4551.      '
  4552.      '      DECLARE SUB ComplexPower (a AS Complex, b AS Complex, c AS Complex
  4553.      '      DECLARE SUB ComplexExp (a AS Complex, c AS Complex)
  4554.      '      DECLARE SUB ComplexLog (a AS Complex, c AS Complex)
  4555.      '      DECLARE SUB ComplexMul (a AS Complex, b AS Complex, c AS Complex)
  4556.      '
  4557.        SUB ComplexPower (a AS Complex, b AS Complex, c AS Complex) STATIC
  4558.            DIM t1 AS Complex, t2 AS Complex
  4559.            IF a.r <> 0! OR a.i <> 0! THEN
  4560.                CALL ComplexLog(a, t1)
  4561.                CALL ComplexMul(t1, b, t2)
  4562.                CALL ComplexExp(t2, c)
  4563.            ELSE
  4564.                ERROR 5
  4565.            END IF
  4566.        END SUB
  4567.    ──────────────────────────────────────────────────────────────────────────
  4568.  
  4569.  
  4570.  Subprogram: ComplexReciprocal
  4571.  
  4572.    Calculates the reciprocal of a complex number by dividing the complex
  4573.    number (1+0i) by the complex number a. The result is placed in the
  4574.    variable c.
  4575.  
  4576.    ──────────────────────────────────────────────────────────────────────────
  4577.      ' ************************************************
  4578.      ' **  Name:          ComplexReciprocal          **
  4579.      ' **  Type:          Subprogram                 **
  4580.      ' **  Module:        COMPLEX.BAS                **
  4581.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4582.      ' ************************************************
  4583.      '
  4584.      ' Calculates the reciprocal of a complex number.
  4585.      '
  4586.      ' EXAMPLE OF USE:  ComplexReciprocal a, c
  4587.      ' PARAMETERS:      a          Complex number to be processed
  4588.      '                  c          Result of calculating 1/a
  4589.      ' VARIABLES:       t          Structure of type Complex
  4590.      ' MODULE LEVEL
  4591.      '   DECLARATIONS:  TYPE Complex
  4592.      '                     r AS SINGLE
  4593.      '                     i AS SINGLE
  4594.      '                  END TYPE
  4595.      '
  4596.      '         DECLARE SUB ComplexReciprocal (a AS Complex, c AS Complex)
  4597.      '         DECLARE SUB ComplexDiv (a AS Complex, b AS Complex, c AS Comple
  4598.      '
  4599.        SUB ComplexReciprocal (a AS Complex, c AS Complex) STATIC
  4600.            DIM t AS Complex
  4601.            t.r = 1!
  4602.            t.i = 0
  4603.            ComplexDiv t, a, c
  4604.        END SUB
  4605.    ──────────────────────────────────────────────────────────────────────────
  4606.  
  4607.  
  4608.  Subprogram: ComplexRoot
  4609.  
  4610.    Calculates the complex root of a complex number. The ComplexReciprocal
  4611.    and ComplexPower subprograms are called by this subprogram. These
  4612.    routines allow the root to be found by raising a to the power of 1/b.
  4613.  
  4614.    ──────────────────────────────────────────────────────────────────────────
  4615.      ' ************************************************
  4616.      ' **  Name:          ComplexRoot                **
  4617.      ' **  Type:          Subprogram                 **
  4618.      ' **  Module:        COMPLEX.BAS                **
  4619.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4620.      ' ************************************************
  4621.      '
  4622.      ' Calculates the complex root of a complex number.
  4623.      '
  4624.      ' EXAMPLE OF USE:  ComplexRoot a, b, c
  4625.      ' PARAMETERS:      a          First complex number
  4626.      '                  b          Complex number root
  4627.      '                  c          Result of finding the bth root of a
  4628.      ' VARIABLES:       t          Structure of type Complex
  4629.      ' MODULE LEVEL
  4630.      '   DECLARATIONS:  TYPE Complex
  4631.      '                     r AS SINGLE
  4632.      '                     i AS SINGLE
  4633.      '                  END TYPE
  4634.      '
  4635.      '       DECLARE SUB ComplexRoot (a AS Complex, b AS Complex, c AS Complex
  4636.      '       DECLARE SUB ComplexReciprocal (a AS Complex, c AS Complex)
  4637.      '       DECLARE SUB ComplexPower (a AS Complex, b AS Complex, c AS Comple
  4638.      '
  4639.        SUB ComplexRoot (a AS Complex, b AS Complex, c AS Complex) STATIC
  4640.            DIM t AS Complex
  4641.            IF b.r <> 0! OR b.i <> 0! THEN
  4642.                CALL ComplexReciprocal(b, t)
  4643.                CALL ComplexPower(a, t, c)
  4644.            ELSE
  4645.                ERROR 5
  4646.            END IF
  4647.        END SUB
  4648.    ──────────────────────────────────────────────────────────────────────────
  4649.  
  4650.  
  4651.  Subprogram: ComplexSqr
  4652.  
  4653.    Calculates the complex square root of a complex number. The square root of
  4654.    a is placed in the variable c.
  4655.  
  4656.    ──────────────────────────────────────────────────────────────────────────
  4657.      ' ************************************************
  4658.      ' **  Name:          ComplexSqr                 **
  4659.      ' **  Type:          Subprogram                 **
  4660.      ' **  Module:        COMPLEX.BAS                **
  4661.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4662.      ' ************************************************
  4663.      '
  4664.      ' Calculates the square root of a complex number.
  4665.      '
  4666.      ' EXAMPLE OF USE:  ComplexSqr a, c
  4667.      ' PARAMETERS:      a          Complex number argument
  4668.      '                  c          Result of finding the square root of a
  4669.      ' VARIABLES:       r!         Magnitude of complex number a
  4670.      '                  theta!     Angle of complex number a
  4671.      '                  rs!        Square root of r!
  4672.      '                  h!         One half of theta!
  4673.      ' MODULE LEVEL
  4674.      '   DECLARATIONS:  TYPE Complex
  4675.      '                     r AS SINGLE
  4676.      '                     i AS SINGLE
  4677.      '                  END TYPE
  4678.      '
  4679.      '                  DECLARE SUB ComplexSqr (a AS Complex, c AS Complex)
  4680.      '
  4681.        SUB ComplexSqr (a AS Complex, c AS Complex) STATIC
  4682.            CALL Rec2pol(a.r, a.i, r!, theta!)
  4683.            rs! = SQR(r!)
  4684.            h! = theta! / 2!
  4685.            c.r = rs! * COS(h!)
  4686.            c.i = rs! * SIN(h!)
  4687.        END SUB
  4688.    ──────────────────────────────────────────────────────────────────────────
  4689.  
  4690.  
  4691.  Subprogram: ComplexSub
  4692.  
  4693.    Calculates the difference between two complex numbers. The result of
  4694.    subtracting b from a is returned in the variable c.
  4695.  
  4696.    ──────────────────────────────────────────────────────────────────────────
  4697.      ' ************************************************
  4698.      ' **  Name:          ComplexSub                 **
  4699.      ' **  Type:          Subprogram                 **
  4700.      ' **  Module:        COMPLEX.BAS                **
  4701.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4702.      ' ************************************************
  4703.      '
  4704.      ' Subtracts two complex numbers.
  4705.      '
  4706.      ' EXAMPLE OF USE:  ComplexSub a, b, c
  4707.      ' PARAMETERS:      a          First complex number
  4708.      '                  b          Second Complex number
  4709.      '                  c          Result of subtracting b from a
  4710.      ' VARIABLES:       (none)
  4711.      ' MODULE LEVEL
  4712.      '   DECLARATIONS:  TYPE Complex
  4713.      '                     r AS SINGLE
  4714.      '                     i AS SINGLE
  4715.      '                  END TYPE
  4716.      '
  4717.      '         DECLARE SUB ComplexSub (a AS Complex, b AS Complex, c AS Comple
  4718.      '
  4719.        SUB ComplexSub (a AS Complex, b AS Complex, c AS Complex) STATIC
  4720.            c.r = a.r - b.r
  4721.            c.i = a.i - b.i
  4722.        END SUB
  4723.    ──────────────────────────────────────────────────────────────────────────
  4724.  
  4725.  
  4726.  Subprogram: String2Complex
  4727.  
  4728.    Converts a string representation of a complex number to a complex number
  4729.    variable of type Complex. This routine is useful for converting user input
  4730.    of a complex number to a complex variable.
  4731.  
  4732.    In general, the string should be in the same format as that produced by
  4733.    the Complex2String function. However, there is some flexibility to allow
  4734.    for variations in the way a user might type in complex numbers. For
  4735.    example, the "i" character indicates the imaginary part of a complex
  4736.    number, but a "j" will also be recognized. Also, parentheses around the
  4737.    numbers are optional.
  4738.  
  4739.    ──────────────────────────────────────────────────────────────────────────
  4740.      ' ************************************************
  4741.      ' **  Name:          String2Complex             **
  4742.      ' **  Type:          Subprogram                 **
  4743.      ' **  Module:        COMPLEX.BAS                **
  4744.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4745.      ' ************************************************
  4746.      '
  4747.      ' Converts a string representation of a complex
  4748.      ' number to a type Complex variable.
  4749.      '
  4750.      ' EXAMPLE OF USE:  String2Complex x$, a
  4751.      ' PARAMETERS:      x$         String representation of a complex number
  4752.      '                  a          Complex number structure of type Complex
  4753.      ' VARIABLES:       j%         Index to first numerical character
  4754.      '                  i%         Pointer to the "i" or "j" character
  4755.      '                  k%         Pointer to start of imaginary part
  4756.      ' MODULE LEVEL
  4757.      '   DECLARATIONS:  TYPE Complex
  4758.      '                     r AS SINGLE
  4759.      '                     i AS SINGLE
  4760.      '                  END TYPE
  4761.      '
  4762.      '                  DECLARE SUB Complex2String (a AS Complex, x$)
  4763.      '
  4764.        SUB String2Complex (x$, a AS Complex) STATIC
  4765.  
  4766.          ' Real part starts just after left parenthesis
  4767.            j% = INSTR(x$, "(") + 1
  4768.  
  4769.          ' Step forward to find start of number
  4770.            DO UNTIL INSTR("+-0123456789", MID$(x$, j%, 1)) OR j% > LEN(x$)
  4771.                j% = j% + 1
  4772.            LOOP
  4773.  
  4774.          ' Imaginary part ends at the "i" or "j"
  4775.            i% = INSTR(LCASE$(x$), "i")
  4776.            IF INSTR(LCASE$(x$), "j") > i% THEN
  4777.                i% = INSTR(LCASE$(x$), "j")
  4778.            END IF
  4779.  
  4780.          ' Step back to find start of imaginary part
  4781.            FOR k% = i% TO 1 STEP -1
  4782.                IF INSTR("+-", MID$(x$, k%, 1)) THEN
  4783.                    EXIT FOR
  4784.                END IF
  4785.            NEXT k%
  4786.  
  4787.          ' Error if pointers don't make sense
  4788.            IF j% = 0 OR j% > LEN(x$) THEN
  4789.                PRINT "Error: String2Complex - unrecognizable string format"
  4790.                SYSTEM
  4791.            END IF
  4792.  
  4793.          ' Grab the real part
  4794.            a.r = VAL(MID$(x$, j%))
  4795.  
  4796.          ' Grab the imaginary part
  4797.            IF k% > j% THEN
  4798.                a.i = VAL(MID$(x$, k%))
  4799.            ELSEIF k% = j% THEN
  4800.                a.r = 0
  4801.                a.i = VAL(MID$(x$, j%))
  4802.            ELSE
  4803.                a.i = 0
  4804.            END IF
  4805.  
  4806.        END SUB
  4807.    ──────────────────────────────────────────────────────────────────────────
  4808.  
  4809.  
  4810.  
  4811.  ────────────────────────────────────────────────────────────────────────────
  4812.  DOLLARS
  4813.  
  4814.    The DOLLARS toolbox contains three functions for working with monetary
  4815.    amounts.
  4816.  
  4817.    QuickBASIC provides a way for you to put commas between groups of three
  4818.    digits in numbers as they're printed or displayed. However, the Comma$
  4819.    and DollarString$ functions have the added advantage of allowing you to
  4820.    manipulate the string results further before outputting the results.
  4821.  
  4822.    The Round# function is presented here as a means of rounding dollar
  4823.    amounts to the nearest cent, but you can also use it for scientific and
  4824.    engineering calculations when you want to round numbers to a given number
  4825.    of decimal places.
  4826.  
  4827.    Name                     Type    Description
  4828.    ──────────────────────────────────────────────────────────────────────────
  4829.    DOLLARS.BAS                     Demo module
  4830.    Comma$                  Func    Double-precision with commas inserted
  4831.    DollarString$           Func    Dollar representation rounded with commas
  4832.    Round#                  Func    Rounding at specified decimal place
  4833.    ──────────────────────────────────────────────────────────────────────────
  4834.  
  4835.  
  4836.  Demo Module: DOLLARS
  4837.  
  4838.    ──────────────────────────────────────────────────────────────────────────
  4839.      ' ************************************************
  4840.      ' **  Name:          DOLLARS                    **
  4841.      ' **  Type:          Toolbox                    **
  4842.      ' **  Module:        DOLLARS.BAS                **
  4843.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4844.      ' ************************************************
  4845.      '
  4846.      ' USAGE:         No command line parameters
  4847.      ' .MAK FILE:     (none)
  4848.      ' PARAMETERS:    (none)
  4849.      ' VARIABLES:     n#         Number for demonstration of the functions
  4850.  
  4851.        DECLARE FUNCTION Comma$ (n#)
  4852.        DECLARE FUNCTION DollarString$ (amount#, length%)
  4853.        DECLARE FUNCTION Round# (n#, place%)
  4854.  
  4855.        CLS
  4856.        n# = 1234567.76543#
  4857.        PRINT "Number n#:", , n#
  4858.        PRINT "Comma$(n#)", , Comma$(n#)
  4859.        PRINT "Comma$(Round#(n#, -2))", Comma$(Round#(n#, -2))
  4860.        PRINT
  4861.        PRINT "DollarString$(n#, 20)", ":"; DollarString$(n#, 20); ":"
  4862.        PRINT , , " 12345678901234567890"
  4863.        PRINT
  4864.  
  4865.        PRINT "Round#(n#, -3)", Round#(n#, -3)
  4866.        PRINT "Round#(n#, -2)", Round#(n#, -2)
  4867.        PRINT "Round#(n#, -1)", Round#(n#, -1)
  4868.        PRINT "Round#(n#, 0)", , Round#(n#, 0)
  4869.        PRINT "Round#(n#, 1)", , Round#(n#, 1)
  4870.        PRINT "Round#(n#, 2)", , Round#(n#, 2)
  4871.    ──────────────────────────────────────────────────────────────────────────
  4872.  
  4873.  
  4874.  Function: Comma$
  4875.  
  4876.    Returns a string representation of a given double-precision number, with
  4877.    commas separating groups of three digits to the left of the decimal point.
  4878.    The returned string is the same as that returned by the QuickBASIC STR$
  4879.    function, except for the addition of the commas.
  4880.  
  4881.    ──────────────────────────────────────────────────────────────────────────
  4882.      ' ************************************************
  4883.      ' **  Name:          Comma$                     **
  4884.      ' **  Type:          Function                   **
  4885.      ' **  Module:        DOLLARS.BAS                **
  4886.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4887.      ' ************************************************
  4888.      '
  4889.      ' Creates a string representing a double-precision
  4890.      ' number, with commas inserted every three digits.
  4891.      '
  4892.      ' EXAMPLE OF USE:    n$  =  Comma$(n#)
  4893.      ' PARAMETERS:        n#     Number to be formatted
  4894.      ' VARIABLES:         tn$    Temporary string of the number
  4895.      '                    dp%    Position of the decimal point
  4896.      '                    i%     Index into tn$
  4897.      ' MODULE LEVEL
  4898.      '   DECLARATIONS:           DECLARE FUNCTION Comma$ (n#)
  4899.      '
  4900.        FUNCTION Comma$ (n#) STATIC
  4901.            tn$ = STR$(n#)
  4902.            dp% = INSTR(tn$, ".")
  4903.            IF dp% = 0 THEN
  4904.                dp% = LEN(tn$) + 1
  4905.            END IF
  4906.            IF dp% > 4 THEN
  4907.                FOR i% = dp% - 3 TO 3 STEP -3
  4908.                    tn$ = LEFT$(tn$, i% - 1) + "," + MID$(tn$, i%)
  4909.                NEXT i%
  4910.            END IF
  4911.            Comma$ = LTRIM$(tn$)
  4912.        END FUNCTION
  4913.    ──────────────────────────────────────────────────────────────────────────
  4914.  
  4915.  
  4916.  Function: DollarString$
  4917.  
  4918.    Returns a string representation of a dollar amount, as passed in a
  4919.    double-precision variable. The Round# function rounds the number to the
  4920.    nearest penny, and the Comma$ function separates each group of three
  4921.    digits to the left of the decimal point with commas. The string is then
  4922.    padded on the left with spaces until the desired string length is
  4923.    achieved, and a dollar sign is placed to the left of the spaces. Thus, you
  4924.    can conveniently display the dollar amounts in columns.
  4925.  
  4926.    ──────────────────────────────────────────────────────────────────────────
  4927.      ' ************************************************
  4928.      ' **  Name:          DollarString$              **
  4929.      ' **  Type:          Function                   **
  4930.      ' **  Module:        DOLLARS.BAS                **
  4931.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4932.      ' ************************************************
  4933.      '
  4934.      ' Returns a string representation of a dollar amount,
  4935.      ' rounded to the nearest cent, with commas separating
  4936.      ' groups of three digits, and with a preceding dollar sign.
  4937.      '
  4938.      ' EXAMPLE OF USE:    d$ = DollarString$(dollars#)
  4939.      ' PARAMETERS:        dollars#   Amount of money
  4940.      ' VARIABLES:         tmp$       Temporary working string
  4941.      ' MODULE LEVEL
  4942.      '   DECLARATIONS:    DECLARE FUNCTION Comma$ (n#)
  4943.      '                    DECLARE FUNCTION DollarString$ (amount#, length%)
  4944.      '                    DECLARE FUNCTION Round# (n#, place%)
  4945.      '
  4946.        FUNCTION DollarString$ (amount#, length%) STATIC
  4947.            tmp$ = SPACE$(length%) + "$" + Comma$(Round#(amount#, -2))
  4948.            DollarString$ = RIGHT$(tmp$, length%)
  4949.            tmp$ = ""
  4950.        END FUNCTION
  4951.    ──────────────────────────────────────────────────────────────────────────
  4952.  
  4953.  
  4954.  Function: Round#
  4955.  
  4956.    Rounds numbers to any decimal position, as specified by the passed power
  4957.    of ten rounding value. For example, to round pi to the nearest integer
  4958.    value, you would use Round#(3.1416#, 0); to round 2/3 of ten dollars to
  4959.    the nearest cent, Round#(6.6666667#, -2); and finally, to round the
  4960.    distance to the moon to the nearest thousand miles, Round#(238857#, 3).
  4961.  
  4962.    ──────────────────────────────────────────────────────────────────────────
  4963.      ' ************************************************
  4964.      ' **  Name:          Round#                     **
  4965.      ' **  Type:          Function                   **
  4966.      ' **  Module:        DOLLARS.BAS                **
  4967.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  4968.      ' ************************************************
  4969.      '
  4970.      ' Rounds a number at the power of 10 decimal place.
  4971.      '
  4972.      ' EXAMPLE OF USE:  x# = Round#(n#, place%)
  4973.      ' EXAMPLES:        Round#(12.3456#, -2) = 12.35#
  4974.      '                  Round#(12.3456#, -1) = 12.3#
  4975.      '                  Round#(12.3456#, 0)  = 12#
  4976.      '                  Round#(12.3456#, 1)  = 10#
  4977.      ' PARAMETERS:      n#         Number to be rounded
  4978.      '                  place%     Power of 10 for rounding the number
  4979.      ' VARIABLES:       pTen#      10 raised to the indicated power of 10
  4980.      ' MODULE LEVEL
  4981.      '   DECLARATIONS:             DECLARE FUNCTION Round# (n#, place%)
  4982.      '
  4983.        FUNCTION Round# (n#, powerOfTen%) STATIC
  4984.            pTen# = 10# ^ powerOfTen%
  4985.            Round# = INT(n# / pTen# + .5#) * pTen#
  4986.        END FUNCTION
  4987.    ──────────────────────────────────────────────────────────────────────────
  4988.  
  4989.  
  4990.  
  4991.  ────────────────────────────────────────────────────────────────────────────
  4992.  DOSCALLS
  4993.  
  4994.    These routines use the Interrupt and InterruptX subprograms, provided as
  4995.    part of the QuickBASIC package, to access the operating system through
  4996.    software interrupts. The information returned by these functions and
  4997.    subprograms is extensive and useful.
  4998.  
  4999.    The DOSCALLS demo module proceeds in this way:
  5000.  
  5001.    The BufferedKeyInput$ function prompts you to enter up to nine
  5002.    characters. The appearance of the prompt and the input action from the
  5003.    keyboard seem very similar to the QuickBASIC INPUT statement, but there
  5004.    are some fundamental differences.
  5005.  
  5006.    Next, the DOSVersion! function returns the MS-DOS version number.
  5007.  
  5008.    The SetDrive subprogram temporarily switches the current drive, and the
  5009.    GetDrive$ function displays the results.
  5010.  
  5011.    The GetMediaDescriptor subprogram returns several useful pieces of
  5012.    information about the current disk drive.
  5013.  
  5014.    The Verify state is normally set using the MS-DOS VERIFY command, but with
  5015.    the GetVerifyState% function and the SetVerifyState subprogram, your
  5016.    programs can now control this setting directly.
  5017.  
  5018.    The GetDiskFreeSpace subprogram returns five useful details about the
  5019.    structure of the data and free space on any disk drive in your system.
  5020.  
  5021.    The GetCountry subprogram returns a data structure filled with details
  5022.    that enable you to modify your program outputs for use in other countries.
  5023.    Also returned is the address of the MS-DOS character translation
  5024.    subroutine called CaseMap, which translates certain characters for some
  5025.    foreign languages.
  5026.  
  5027.    The TranslateCountry$ function uses the address returned by the
  5028.    GetCountry subprogram to translate a string of characters for the
  5029.    currently set country.
  5030.  
  5031.    The GetDirectory$ function and SetDirectory subprogram let you determine
  5032.    or set the current directory-path string.
  5033.  
  5034.    The WriteToDevice subprogram is useful for outputting strings directly to
  5035.    the indicated device, using the MS-DOS output handler rather than
  5036.    QuickBASIC's. One advantage of this approach is in being able to use the
  5037.    ANSI.SYS escape-code sequences to control cursor movement, screen mode,
  5038.    and color attributes.
  5039.  
  5040.    Finally, the GetFileAttributes and SetFileAttributes subprograms let you
  5041.    determine or change the file attribute bits as desired. With these
  5042.    routines, it's easy to hide or unhide files and to set or clear the
  5043.    archive bit for use by the MS-DOS XCOPY command.
  5044.  
  5045.    Name                     Type    Description
  5046.    ──────────────────────────────────────────────────────────────────────────
  5047.    DOSCALLS.BAS                    Demo module
  5048.    BufferedKeyInput$       Func    ASCII string of specified length
  5049.    DOSVersion!             Func    Version number of MS-DOS returned
  5050.    GetCountry              Sub     Current country setting
  5051.    GetDirectory$           Func    Path to disk directory specified
  5052.    GetDiskFreeSpace        Sub     Disk space format and usage for input
  5053.                                     drive
  5054.    GetDrive$               Func    Current drive string
  5055.    GetFileAttributes       Sub     Attribute bits for given file
  5056.    GetMediaDescriptor      Sub     Drive information for system
  5057.    GetVerifyState%         Func    Verify setting (state)
  5058.    SetDirectory            Sub     Sets current directory
  5059.    SetDrive                Sub     Sets current disk drive
  5060.    SetFileAttributes       Sub     Sets the attribute bits for a given file
  5061.    SetVerifyState          Sub     Sets or clears verify state (writing to
  5062.                                     file)
  5063.    TranslateCountry$       Func    Translates string──current country
  5064.                                     setting
  5065.    WriteToDevice           Sub     Outputs a string to a device
  5066.    ──────────────────────────────────────────────────────────────────────────
  5067.  
  5068.  
  5069.  Demo Module: DOSCALLS
  5070.  
  5071.    ──────────────────────────────────────────────────────────────────────────
  5072.      ' ************************************************
  5073.      ' **  Name:          DOSCALLS                   **
  5074.      ' **  Type:          Toolbox                    **
  5075.      ' **  Module:        DOSCALLS.BAS               **
  5076.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5077.      ' ************************************************
  5078.      '
  5079.      ' Demonstrates several interrupt calls to MS-DOS.
  5080.      '
  5081.      ' USAGE:          No command line parameters
  5082.      ' REQUIREMENTS:   MS-DOS 3.0 or later
  5083.      '                 MIXED.QLB/.LIB
  5084.      '.MAK FILE:       (none)
  5085.      ' PARAMETERS:     (none)
  5086.      ' VARIABLES:      buffer$    String for buffered input demonstration
  5087.      '                 x$         Buffered input string
  5088.      '                 drive$     Current disk drive name
  5089.      '                 desc       Structure of type MediaDescriptorType
  5090.      '                 state%     Current status of the Verify state
  5091.      '                 oppositeState%   Opposite state for Verify
  5092.      '                 disk       Structure of type DiskFreeSpaceType
  5093.      '                 country    Structure of type CountryType
  5094.      '                 i%         Loop index for creating translation characte
  5095.      '                 a$         Characters to be translated
  5096.      '                 path$      Current directory
  5097.      '                 result%    Result code from call to SetDirectory
  5098.      '                 t$         Temporary copy of TIME$
  5099.      '                 attr       Structure of type FileAttributesType
  5100.      '                 fileName$  Name of file for determining file attributes
  5101.  
  5102.  
  5103.         TYPE RegType
  5104.            ax    AS INTEGER
  5105.            bx    AS INTEGER
  5106.            cx    AS INTEGER
  5107.            dx    AS INTEGER
  5108.            bp    AS INTEGER
  5109.            si    AS INTEGER
  5110.            di    AS INTEGER
  5111.            flags AS INTEGER
  5112.        END TYPE
  5113.  
  5114.        TYPE RegTypeX
  5115.            ax    AS INTEGER
  5116.            bx    AS INTEGER
  5117.            cx    AS INTEGER
  5118.            dx    AS INTEGER
  5119.            bp    AS INTEGER
  5120.            si    AS INTEGER
  5121.            di    AS INTEGER
  5122.            flags AS INTEGER
  5123.            ds    AS INTEGER
  5124.            es    AS INTEGER
  5125.        END TYPE
  5126.  
  5127.        TYPE MediaDescriptorType
  5128.            sectorsPerAllocationUnit AS INTEGER
  5129.            bytesPerSector AS INTEGER
  5130.            FATIdentificationByte AS INTEGER
  5131.        END TYPE
  5132.  
  5133.        TYPE DiskFreeSpaceType
  5134.            sectorsPerCluster AS INTEGER
  5135.            bytesPerSector AS INTEGER
  5136.            clustersPerDrive AS LONG
  5137.            availableClusters AS LONG
  5138.            availableBytes AS LONG
  5139.        END TYPE
  5140.  
  5141.        TYPE CountryType
  5142.            dateTimeFormat AS STRING * 11
  5143.            currencySymbol AS STRING * 4
  5144.            thousandsSeparator AS STRING * 1
  5145.            decimalSeparator AS STRING * 1
  5146.            dateSeparator AS STRING * 1
  5147.            timeSeparator AS STRING * 1
  5148.            currencyThenSymbol AS INTEGER
  5149.            currencySymbolSpace AS INTEGER
  5150.            currencyPlaces AS INTEGER
  5151.            hours24 AS INTEGER
  5152.            caseMapSegment AS INTEGER
  5153.            caseMapOffset AS INTEGER
  5154.            dataListSeparator AS STRING * 1
  5155.        END TYPE
  5156.  
  5157.        TYPE FileAttributesType
  5158.            readOnly AS INTEGER
  5159.            hidden AS INTEGER
  5160.            systemFile AS INTEGER
  5161.            archive AS INTEGER
  5162.            result AS INTEGER
  5163.        END TYPE
  5164.  
  5165.      ' Subprograms
  5166.        DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  5167.        DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
  5168.        DECLARE SUB SetDrive (drive$)
  5169.        DECLARE SUB GetMediaDescriptor (drive$, desc AS MediaDescriptorType)
  5170.        DECLARE SUB SetVerifyState (state%)
  5171.        DECLARE SUB GetDiskFreeSpace (drive$, disk AS DiskFreeSpaceType)
  5172.        DECLARE SUB GetCountry (country AS CountryType)
  5173.        DECLARE SUB CaseMap (character%, BYVAL Segment%, BYVAL Offset%)
  5174.        DECLARE SUB SetDirectory (path$, result%)
  5175.        DECLARE SUB WriteToDevice (handle%, a$, result%)
  5176.        DECLARE SUB GetFileAttributes (fileName$, attr AS FileAttributesType)
  5177.        DECLARE SUB SetFileAttributes (fileName$, attr AS FileAttributesType)
  5178.  
  5179.      ' Functions
  5180.        DECLARE FUNCTION DOSVersion! ()
  5181.        DECLARE FUNCTION BufferedKeyInput$ (n%)
  5182.        DECLARE FUNCTION GetDrive$ ()
  5183.        DECLARE FUNCTION GetVerifyState% ()
  5184.        DECLARE FUNCTION TranslateCountry$ (a$, country AS CountryType)
  5185.        DECLARE FUNCTION GetDirectory$ (drive$)
  5186.  
  5187.      ' Try the Buffered Keyboard Input call
  5188.        CLS
  5189.        PRINT "BufferedKeyInput$:"
  5190.        PRINT "Enter a string of up to nine characters...  ";
  5191.        x$ = BufferedKeyInput$(9)
  5192.        PRINT
  5193.        PRINT "Here's the nine-character string result... ";
  5194.        PRINT CHR$(34); x$; CHR$(34)
  5195.  
  5196.      ' Get the MS-DOS version number
  5197.        PRINT
  5198.        PRINT "DosVersion!:"
  5199.        PRINT "DOS Version number is "; DOSVersion!
  5200.  
  5201.      ' Demonstrate the GetDrive and SetDrive routines
  5202.        PRINT
  5203.        PRINT "GetDrive$ and SetDrive:"
  5204.        drive$ = GetDrive$
  5205.        PRINT "The current drive is "; drive$
  5206.        PRINT "Setting the current drive to A:"
  5207.        SetDrive "A:"
  5208.        PRINT "Now the current drive is "; GetDrive$
  5209.        PRINT "Setting the current drive back to "; drive$
  5210.        SetDrive drive$
  5211.        PRINT "Now the current drive is "; GetDrive$
  5212.  
  5213.      ' Call the MS-DOS "Media Descriptor" function for the current drive
  5214.        PRINT
  5215.        PRINT "GetMediaDescriptor"
  5216.        DIM desc AS MediaDescriptorType
  5217.        GetMediaDescriptor drive$, desc
  5218.        PRINT "Drive                        "; drive$
  5219.        PRINT "Sectors per allocation unit "; desc.sectorsPerAllocationUnit
  5220.        PRINT "Bytes per sector            "; desc.bytesPerSector
  5221.        PRINT "FAT identification byte      &H"; HEX$(desc.FATIdentificationByt
  5222.  
  5223.      ' Wait for user
  5224.        PRINT
  5225.        PRINT
  5226.        PRINT "Press any key to continue"
  5227.        DO
  5228.        LOOP UNTIL INKEY$ <> ""
  5229.        CLS
  5230.  
  5231.      ' Demonstrate the GetVerifyState and SetVerifyState routines
  5232.        PRINT
  5233.        PRINT "GetVerifyState% and SetVerifyState:"
  5234.        state% = GetVerifyState%
  5235.        PRINT "Current verify state is"; state%
  5236.        oppositeState% = 1 AND NOT state%
  5237.        SetVerifyState oppositeState%
  5238.        PRINT "Now the verify state is"; GetVerifyState%
  5239.        SetVerifyState state%
  5240.        PRINT "Now the verify state is"; GetVerifyState%
  5241.  
  5242.      ' Determine free space on the current drive
  5243.        PRINT
  5244.        PRINT "GetDiskFreeSpace:"
  5245.        DIM disk AS DiskFreeSpaceType
  5246.        GetDiskFreeSpace drive$, disk
  5247.        PRINT "Sectors per cluster     "; disk.sectorsPerCluster
  5248.        PRINT "Bytes per sector        "; disk.bytesPerSector
  5249.        PRINT "Total clusters on drive "; disk.clustersPerDrive
  5250.        PRINT "Available clusters      "; disk.availableClusters
  5251.        PRINT "Available bytes         "; disk.availableBytes
  5252.  
  5253.      ' Wait for user
  5254.        PRINT
  5255.        PRINT
  5256.        PRINT "Press any key to continue"
  5257.        DO
  5258.        LOOP UNTIL INKEY$ <> ""
  5259.        CLS
  5260.  
  5261.      ' Get country-dependent information
  5262.        PRINT
  5263.        PRINT "GetCountry:"
  5264.        DIM country AS CountryType
  5265.        GetCountry country
  5266.        PRINT "Date and time format    "; country.dateTimeFormat
  5267.        PRINT "Currency symbol         "; country.currencySymbol
  5268.        PRINT "Thousands separator     "; country.thousandsSeparator
  5269.        PRINT "Decimal separator       "; country.decimalSeparator
  5270.        PRINT "Date separator          "; country.dateSeparator
  5271.        PRINT "Time separator          "; country.timeSeparator
  5272.        PRINT "Currency before symbol "; country.currencyThenSymbol
  5273.        PRINT "Currency symbol space  "; country.currencySymbolSpace
  5274.        PRINT "Currency decimal places"; country.currencyPlaces
  5275.        PRINT "24-hour time           "; country.hours24
  5276.        PRINT "Case map segment       "; country.caseMapSegment
  5277.        PRINT "Case map offset        "; country.caseMapOffset
  5278.        PRINT "Data list separator     "; country.dataListSeparator
  5279.  
  5280.      ' Let's translate lowercase characters for the current country
  5281.        PRINT
  5282.        PRINT "TranslateCountry$:"
  5283.        FOR i% = 128 TO 175
  5284.            a$ = a$ + CHR$(i%)
  5285.        NEXT i%
  5286.        PRINT "Character codes 128 to 175, before and after translation... "
  5287.        PRINT a$
  5288.        PRINT TranslateCountry$(a$, country)
  5289.  
  5290.      ' Wait for user
  5291.        PRINT
  5292.        PRINT
  5293.        PRINT "Press any key to continue"
  5294.        DO
  5295.        LOOP UNTIL INKEY$ <> ""
  5296.        CLS
  5297.  
  5298.      ' Demonstrate the SetDirectory and GetDirectory routines
  5299.        PRINT
  5300.        PRINT "GetDirectory$ and SetDirectory:"
  5301.        path$ = GetDirectory$(drive$)
  5302.        PRINT "Current directory is "; path$
  5303.        SetDirectory GetDrive$ + "\", result%
  5304.        PRINT "Now the directory is "; GetDirectory$(drive$)
  5305.        SetDirectory path$, result%
  5306.        PRINT "Now the directory is "; GetDirectory$(drive$)
  5307.  
  5308.      ' Write to a file or device
  5309.        PRINT
  5310.        PRINT "WriteToDevice:"
  5311.        PRINT "Writing a 'bell' character to the CRT"
  5312.        WriteToDevice 1, CHR$(7), result%
  5313.        t$ = TIME$
  5314.        DO
  5315.        LOOP UNTIL t$ <> TIME$
  5316.        PRINT "Writing a 'bell' character to the printer"
  5317.        WriteToDevice 4, CHR$(7), result%
  5318.  
  5319.      ' Wait for user
  5320.        PRINT
  5321.        PRINT
  5322.        PRINT "Press any key to continue"
  5323.        DO
  5324.        LOOP UNTIL INKEY$ <> ""
  5325.        CLS
  5326.  
  5327.      ' Demonstrate the GetFileAttributes and SetFileAttributes routines
  5328.        PRINT
  5329.        PRINT "GetFileAttributes and SetFileAttributes:"
  5330.        DIM attr AS FileAttributesType
  5331.        fileName$ = "C:\IBMDOS.COM"
  5332.        GetFileAttributes fileName$, attr
  5333.        PRINT "File attributes for "; fileName$
  5334.        PRINT "Result of call "; attr.result
  5335.        PRINT "Read only      "; attr.readOnly
  5336.        PRINT "Hidden         "; attr.hidden
  5337.        PRINT "System         "; attr.systemFile
  5338.        PRINT "Archive        "; attr.archive
  5339.        PRINT
  5340.        attr.hidden = 0
  5341.        SetFileAttributes fileName$, attr
  5342.        GetFileAttributes fileName$, attr
  5343.        PRINT "File attributes for "; fileName$
  5344.        PRINT "Result of call "; attr.result
  5345.        PRINT "Read only      "; attr.readOnly
  5346.        PRINT "Hidden         "; attr.hidden
  5347.        PRINT "System         "; attr.systemFile
  5348.        PRINT "Archive        "; attr.archive
  5349.        PRINT
  5350.        attr.hidden = 1
  5351.        SetFileAttributes fileName$, attr
  5352.        GetFileAttributes fileName$, attr
  5353.        PRINT "File attributes for "; fileName$
  5354.        PRINT "Result of call "; attr.result
  5355.        PRINT "Read only      "; attr.readOnly
  5356.        PRINT "Hidden         "; attr.hidden
  5357.        PRINT "System         "; attr.systemFile
  5358.        PRINT "Archive        "; attr.archive
  5359.        PRINT
  5360.    ──────────────────────────────────────────────────────────────────────────
  5361.  
  5362.  
  5363.  Function: BufferedKeyInput$
  5364.  
  5365.    Calls the MS-DOS Buffered Keyboard Input routine, which is similar in
  5366.    concept to QuickBASIC's LINE INPUT statement but contains some useful
  5367.    differences.
  5368.  
  5369.    When you call the BufferedKeyInput$ function, you pass an integer that
  5370.    tells the MS-DOS routine the maximum number of characters to be input. If
  5371.    extra characters are typed, the computer beeps, and the keystrokes are
  5372.    ignored. The Backspace and Left arrow keys allow editing of the input, and
  5373.    the screen is constantly updated to display the input buffer at the
  5374.    current cursor location.
  5375.  
  5376.    The returned string is always n% characters in length, even if the user
  5377.    entered fewer than n% characters. If necessary, the string is padded on
  5378.    the right with spaces to bring the length up to n%.
  5379.  
  5380.    ──────────────────────────────────────────────────────────────────────────
  5381.      ' ************************************************
  5382.      ' **  Name:          BufferedKeyInput$          **
  5383.      ' **  Type:          Function                   **
  5384.      ' **  Module:        DOSCALLS.BAS               **
  5385.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5386.      ' ************************************************
  5387.      '
  5388.      ' Calls the "Buffered Keyboard Input" MS-DOS function
  5389.      ' and returns the entered string of characters.
  5390.      '
  5391.      ' EXAMPLE OF USE:  x$ = BufferedKeyInput$(n%)
  5392.      ' PARAMETERS:      buffer$    Buffer for keyboard input
  5393.      ' VARIABLES:       regX       Structure of type RegTypeX
  5394.      '                  bufSize%   Length of buffer$
  5395.      '                  b$         Working copy of buffer$
  5396.      '                  count%     Count of characters entered
  5397.      ' MODULE LEVEL
  5398.      '   DECLARATIONS:  TYPE RegTypeX
  5399.      '                     ax    AS INTEGER
  5400.      '                     bx    AS INTEGER
  5401.      '                     cx    AS INTEGER
  5402.      '                     dx    AS INTEGER
  5403.      '                     bp    AS INTEGER
  5404.      '                     si    AS INTEGER
  5405.      '                     di    AS INTEGER
  5406.      '                     flags AS INTEGER
  5407.      '                     ds    AS INTEGER
  5408.      '                     es    AS INTEGER
  5409.      '                  END TYPE
  5410.      '
  5411.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  5412.      '   DECLARE FUNCTION BufferedKeyInput$ (n%)
  5413.      '
  5414.        FUNCTION BufferedKeyInput$ (n%) STATIC
  5415.            DIM regX AS RegTypeX
  5416.            b$ = CHR$(n% + 1) + SPACE$(n% + 1)
  5417.            regX.ax = &HA00
  5418.            regX.ds = VARSEG(b$)
  5419.            regX.dx = SADD(b$)
  5420.            InterruptX &H21, regX, regX
  5421.            count% = ASC(MID$(b$, 2, 1))
  5422.            BufferedKeyInput$ = MID$(b$, 3, count%) + SPACE$(n% - count%)
  5423.        END FUNCTION
  5424.    ──────────────────────────────────────────────────────────────────────────
  5425.  
  5426.  
  5427.  Function: DOSVersion!
  5428.  
  5429.    Returns the version number of MS-DOS. Sometimes it's necessary to know the
  5430.    current version of MS-DOS before proceeding with certain MS-DOS functions.
  5431.  
  5432.    ──────────────────────────────────────────────────────────────────────────
  5433.      ' ************************************************
  5434.      ' **  Name:          DOSVersion!                **
  5435.      ' **  Type:          Function                   **
  5436.      ' **  Module:        DOSCALLS.BAS               **
  5437.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5438.      ' ************************************************
  5439.      '
  5440.      ' Returns the version number of MS-DOS.
  5441.      '
  5442.      ' EXAMPLE OF USE:  PRINT "MS-DOS Version number is "; DOSVersion!
  5443.      ' PARAMETERS:      (none)
  5444.      ' VARIABLES:       reg        Structure of type RegType
  5445.      '                  major%     Integer part of the MS-DOS version number
  5446.      '                  minor%     Fractional part of the MS-DOS version numbe
  5447.      ' MODULE LEVEL
  5448.      '   DECLARATIONS:  TYPE RegType
  5449.      '                     ax    AS INTEGER
  5450.      '                     bx    AS INTEGER
  5451.      '                     cx    AS INTEGER
  5452.      '                     dx    AS INTEGER
  5453.      '                     bp    AS INTEGER
  5454.      '                     si    AS INTEGER
  5455.      '                     di    AS INTEGER
  5456.      '                     flags AS INTEGER
  5457.      '                  END TYPE
  5458.      '
  5459.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  5460.      '      DECLARE FUNCTION DOSVersion! ()
  5461.      '
  5462.        FUNCTION DOSVersion! STATIC
  5463.            DIM reg AS RegType
  5464.            reg.ax = &H3000
  5465.            Interrupt &H21, reg, reg
  5466.            major% = reg.ax MOD 256
  5467.            minor% = reg.ax \ 256
  5468.            DOSVersion! = major% + minor% / 100!
  5469.        END FUNCTION
  5470.    ──────────────────────────────────────────────────────────────────────────
  5471.  
  5472.  
  5473.  Subprogram: GetCountry
  5474.  
  5475.    Returns information from MS-DOS about the current country settings. This
  5476.    information can be invaluable for programs slated to be marketed in more
  5477.    than one country. A program's data output can be modified to conform to
  5478.    the standards of each country. For example, the date 3-4-88 can refer to
  5479.    March 4th or April 3rd, depending on which part of the world you are in.
  5480.  
  5481.    The date and time format string indicates the order of the six numeric
  5482.    values that make up a given date and time.
  5483.  
  5484.    Several variables are returned to indicate the desirable way to format
  5485.    monetary values. These determine whether the currency symbol is before or
  5486.    after the monetary value, whether a space separates the two, and the
  5487.    number of decimal places to use.
  5488.  
  5489.    The currency symbol is a four-character string such as "Lira". For
  5490.    dollars, the string contains three spaces followed by a $.
  5491.  
  5492.    The thousands separator is a one-character string, usually a decimal point
  5493.    or a comma.
  5494.  
  5495.    The decimal separator is also a one-character string, usually a decimal
  5496.    point or a comma.
  5497.  
  5498.    The date separator is a one-character string such as "-" or "/".
  5499.  
  5500.    The time separator is a one-character string such as ":".
  5501.  
  5502.    The hours designation indicates whether a 24-hour format or an A.M. and
  5503.    P.M. 12-hour format is more commonly used. The CaseMap address is the
  5504.    segment and offset address of the MS-DOS character translation function.
  5505.    Refer to the TranslateCountry$ function to see how this address is used.
  5506.    (The CaseMap subprogram is discussed in Part III of this book.)
  5507.  
  5508.    The data list separator is a one-character string such as ",".
  5509.  
  5510.    ──────────────────────────────────────────────────────────────────────────
  5511.      ' ************************************************
  5512.      ' **  Name:          GetCountry                 **
  5513.      ' **  Type:          Subprogram                 **
  5514.      ' **  Module:        DOSCALLS.BAS               **
  5515.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5516.      ' ************************************************
  5517.      '
  5518.      ' Returns country-dependent information as defined
  5519.      ' by MS-DOS.
  5520.      '
  5521.      ' EXAMPLE OF USE:  GetCountry country
  5522.      ' PARAMETERS:      country    Structure of type CountryType
  5523.      ' VARIABLES:       regX       Structure of type RegTypeX
  5524.      '                  c$         Buffer for data returned from interrupt
  5525.      ' MODULE LEVEL
  5526.      '   DECLARATIONS:  TYPE RegTypeX
  5527.      '                     ax    AS INTEGER
  5528.      '                     bx    AS INTEGER
  5529.      '                     cx    AS INTEGER
  5530.      '                     dx    AS INTEGER
  5531.      '                     bp    AS INTEGER
  5532.      '                     si    AS INTEGER
  5533.      '                     di    AS INTEGER
  5534.      '                     flags AS INTEGER
  5535.      '                     ds    AS INTEGER
  5536.      '                     es    AS INTEGER
  5537.      '                  END TYPE
  5538.      '
  5539.      '                  TYPE CountryType
  5540.      '                     DateTimeFormat AS STRING * 11
  5541.      '                     CurrencySymbol AS STRING * 4
  5542.      '                     ThousandsSeparator AS STRING * 1
  5543.      '                     DecimalSeparator AS STRING * 1
  5544.      '                     DateSeparator AS STRING * 1
  5545.      '                     TimeSeparator AS STRING * 1
  5546.      '                     CurrencyThenSymbol AS INTEGER
  5547.      '                     CurrencySymbolSpace AS INTEGER
  5548.      '                     CurrencyPlaces AS INTEGER
  5549.      '                     Hours24 AS INTEGER
  5550.      '                     caseMapSegment AS INTEGER
  5551.      '                     caseMapOffset AS INTEGER
  5552.      '                     DataListSeparator AS STRING * 1
  5553.      '                  END TYPE
  5554.      '
  5555.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  5556.      '   DECLARE SUB GetCountry (country AS CountryType)
  5557.      '
  5558.        SUB GetCountry (country AS CountryType)
  5559.            DIM regX AS RegTypeX
  5560.            regX.ax = &H3800
  5561.            c$ = SPACE$(32)
  5562.            regX.ds = VARSEG(c$)
  5563.            regX.dx = SADD(c$)
  5564.            InterruptX &H21, regX, regX
  5565.            SELECT CASE CVI(LEFT$(c$, 2))
  5566.            CASE 0
  5567.                country.dateTimeFormat = "h:m:s m/d/y"
  5568.            CASE 1
  5569.                country.dateTimeFormat = "h:m:s d/m/y"
  5570.            CASE 2
  5571.                country.dateTimeFormat = "y/m/d h:m:s"
  5572.            CASE ELSE
  5573.                country.dateTimeFormat = "h:m:s m/d/y"
  5574.            END SELECT
  5575.            country.currencySymbol = MID$(c$, 3, 4)
  5576.            country.thousandsSeparator = MID$(c$, 8, 1)
  5577.            country.decimalSeparator = MID$(c$, 10, 1)
  5578.            country.dateSeparator = MID$(c$, 12, 1)
  5579.            country.timeSeparator = MID$(c$, 14, 1)
  5580.            country.currencyThenSymbol = ASC(MID$(c$, 16)) AND 1
  5581.            country.currencySymbolSpace = (ASC(MID$(c$, 16)) AND 2) \ 2
  5582.            country.currencyPlaces = ASC(MID$(c$, 17))
  5583.            country.hours24 = ASC(MID$(c$, 18))
  5584.            country.caseMapSegment = CVI(MID$(c$, 21, 2))
  5585.            country.caseMapOffset = CVI(MID$(c$, 19, 2))
  5586.            country.dataListSeparator = MID$(c$, 23, 1)
  5587.        END SUB
  5588.    ──────────────────────────────────────────────────────────────────────────
  5589.  
  5590.  
  5591.  Function: GetDirectory$
  5592.  
  5593.    Returns the complete path for any drive on your system. The called MS-DOS
  5594.    function doesn't return the drive designation or the first slash,
  5595.    representing the root directory, but the GetDirectory$ function adds these
  5596.    parts to the returned string for you.
  5597.  
  5598.    For the current directory of the current, default drive, pass a null
  5599.    string. For a specific drive, pass a string containing the letter of the
  5600.    drive in the first character position. For example, GetDirectory$("A:")
  5601.    might return A:\QB4\SOURCE.
  5602.  
  5603.    ──────────────────────────────────────────────────────────────────────────
  5604.      ' ************************************************
  5605.      ' **  Name:          GetDirectory$              **
  5606.      ' **  Type:          Function                   **
  5607.      ' **  Module:        DOSCALLS.BAS               **
  5608.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5609.      ' ************************************************
  5610.      '
  5611.      ' Returns the name of the current directory for any drive.
  5612.      '
  5613.      ' EXAMPLE OF USE:  path$ = GetDirectory$(drive$)
  5614.      ' PARAMETERS:      drive$     Drive of concern, or null string for defaul
  5615.      '                             drive
  5616.      ' VARIABLES:       regX       Structure of type RegTypeX
  5617.      '                  d$         Working copy of drive$
  5618.      '                  p$         Buffer space for returned path
  5619.      ' MODULE LEVEL
  5620.      '   DECLARATIONS:  TYPE RegTypeX
  5621.      '                     ax    AS INTEGER
  5622.      '                     bx    AS INTEGER
  5623.      '                     cx    AS INTEGER
  5624.      '                     dx    AS INTEGER
  5625.      '                     bp    AS INTEGER
  5626.      '                     si    AS INTEGER
  5627.      '                     di    AS INTEGER
  5628.      '                     flags AS INTEGER
  5629.      '                     ds    AS INTEGER
  5630.      '                     es    AS INTEGER
  5631.      '                  END TYPE
  5632.      '
  5633.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  5634.      '   DECLARE FUNCTION GetDirectory$ (drive$)
  5635.      '
  5636.        FUNCTION GetDirectory$ (drive$) STATIC
  5637.            DIM regX AS RegTypeX
  5638.            IF drive$ = "" THEN
  5639.                d$ = GetDrive$
  5640.            ELSE
  5641.                d$ = UCASE$(drive$)
  5642.            END IF
  5643.            drive% = ASC(d$) - 64
  5644.            regX.dx = drive%
  5645.            regX.ax = &H4700
  5646.            p$ = SPACE$(64)
  5647.            regX.ds = VARSEG(p$)
  5648.            regX.si = SADD(p$)
  5649.            InterruptX &H21, regX, regX
  5650.            p$ = LEFT$(p$, INSTR(p$, CHR$(0)) - 1)
  5651.            GetDirectory$ = LEFT$(d$, 1) + ":\" + p$
  5652.            IF regX.flags AND 1 THEN
  5653.                GetDirectory$ = ""
  5654.            END IF
  5655.        END FUNCTION
  5656.    ──────────────────────────────────────────────────────────────────────────
  5657.  
  5658.  
  5659.  Subprogram: GetDiskFreeSpace
  5660.  
  5661.    Returns information about the current usage and format of a given disk
  5662.    drive's contents. The data structure of type DiskFreeSpaceType lists the
  5663.    various information returned by this subprogram. Some information, such as
  5664.    the sectors per cluster, bytes per sector, and total clusters information,
  5665.    is constant in nature, so the subprogram always returns the same value for
  5666.    a given drive. The available clusters and available bytes are the variable
  5667.    information that this subprogram returns.
  5668.  
  5669.    Probably the most important information this subprogram returns is the
  5670.    total bytes available. Call this subprogram before creating a large file
  5671.    on a disk to prevent the program from being interrupted by a "disk full"
  5672.    message. This lets you prompt the user to insert a different disk or take
  5673.    other action before any data is lost.
  5674.  
  5675.    ──────────────────────────────────────────────────────────────────────────
  5676.      ' ************************************************
  5677.      ' **  Name:          GetDiskFreeSpace           **
  5678.      ' **  Type:          Subprogram                 **
  5679.      ' **  Module:        DOSCALLS.BAS               **
  5680.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5681.      ' ************************************************
  5682.      '
  5683.      ' Get information about a drive's organization, including
  5684.      ' total number of bytes available.
  5685.      '
  5686.      ' EXAMPLE OF USE:  GetDiskFreeSpace drive$, disk
  5687.      ' PARAMETERS:      drive$     Disk drive designation
  5688.      '                  disk       Structure of type DiskFreeSpaceType
  5689.      ' VARIABLES:       reg        Structure of type RegType
  5690.      '                  drive%     Numeric drive designation
  5691.      ' MODULE LEVEL
  5692.      '   DECLARATIONS:  TYPE RegType
  5693.      '                     ax    AS INTEGER
  5694.      '                     bx    AS INTEGER
  5695.      '                     cx    AS INTEGER
  5696.      '                     dx    AS INTEGER
  5697.      '                     bp    AS INTEGER
  5698.      '                     si    AS INTEGER
  5699.      '                     di    AS INTEGER
  5700.      '                     flags AS INTEGER
  5701.      '                  END TYPE
  5702.      '
  5703.      '                  TYPE DiskFreeSpaceType
  5704.      '                     sectorsPerCluster AS INTEGER
  5705.      '                     bytesPerSector AS INTEGER
  5706.      '                     clustersPerDrive AS LONG
  5707.      '                     availableClusters AS LONG
  5708.      '                     availableBytes AS LONG
  5709.      '                  END TYPE
  5710.      '
  5711.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  5712.      '      DECLARE SUB GetDiskFreeSpace (drive$, disk AS DiskFreeSpaceType)
  5713.      '
  5714.        SUB GetDiskFreeSpace (drive$, disk AS DiskFreeSpaceType)
  5715.            DIM reg AS RegType
  5716.            IF drive$ <> "" THEN
  5717.                drive% = ASC(UCASE$(drive$)) - 64
  5718.            ELSE
  5719.                drive% = 0
  5720.            END IF
  5721.            IF drive% >= 0 THEN
  5722.                reg.dx = drive%
  5723.            ELSE
  5724.                reg.dx = 0
  5725.            END IF
  5726.            reg.ax = &H3600
  5727.            Interrupt &H21, reg, reg
  5728.            disk.sectorsPerCluster = reg.ax
  5729.            disk.bytesPerSector = reg.cx
  5730.            IF reg.dx >= 0 THEN
  5731.                disk.clustersPerDrive = reg.dx
  5732.            ELSE
  5733.                disk.clustersPerDrive = reg.dx + 65536
  5734.            END IF
  5735.            IF reg.bx >= 0 THEN
  5736.                disk.availableClusters = reg.bx
  5737.            ELSE
  5738.                disk.availableClusters = reg.bx + 65536
  5739.            END IF
  5740.            disk.availableBytes = disk.availableClusters * reg.ax * reg.cx
  5741.        END SUB
  5742.    ──────────────────────────────────────────────────────────────────────────
  5743.  
  5744.  
  5745.  Function: GetDrive$
  5746.  
  5747.    Returns a two-character string designation for the current disk drive. The
  5748.    first character is always an uppercase letter, and the second character is
  5749.    always a colon.
  5750.  
  5751.    ──────────────────────────────────────────────────────────────────────────
  5752.      ' ************************************************
  5753.      ' **  Name:          GetDrive$                  **
  5754.      ' **  Type:          Function                   **
  5755.      ' **  Module:        DOSCALLS.BAS               **
  5756.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5757.      ' ************************************************
  5758.      '
  5759.      ' Returns the current disk drive name, such as "A:".
  5760.      '
  5761.      ' EXAMPLE OF USE:  drive$ = GetDrive$
  5762.      ' PARAMETERS:      (none)
  5763.      ' VARIABLES:       reg        Structure of type RegType
  5764.      ' MODULE LEVEL
  5765.      '   DECLARATIONS:  TYPE RegType
  5766.      '                     ax    AS INTEGER
  5767.      '                     bx    AS INTEGER
  5768.      '                     cx    AS INTEGER
  5769.      '                     dx    AS INTEGER
  5770.      '                     bp    AS INTEGER
  5771.      '                     si    AS INTEGER
  5772.      '                     di    AS INTEGER
  5773.      '                     flags AS INTEGER
  5774.      '                  END TYPE
  5775.      '
  5776.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  5777.      '      DECLARE FUNCTION GetDrive$ ()
  5778.      '
  5779.        FUNCTION GetDrive$ STATIC
  5780.            DIM reg AS RegType
  5781.            reg.ax = &H1900
  5782.            Interrupt &H21, reg, reg
  5783.            GetDrive$ = CHR$((reg.ax AND &HFF) + 65) + ":"
  5784.        END FUNCTION
  5785.    ──────────────────────────────────────────────────────────────────────────
  5786.  
  5787.  
  5788.  Subprogram: GetFileAttributes
  5789.  
  5790.    Returns current attribute bits for a given file. Each file has several
  5791.    attribute bits that serve useful purposes in MS-DOS. For example, whenever
  5792.    a change is made to a file, the "archive" bit is set. The MS-DOS XCOPY
  5793.    utility can check the setting of this bit and copy only those files that
  5794.    have been modified since the last XCOPY command was given for the same set
  5795.    of files. XCOPY clears this bit when a file is copied.
  5796.  
  5797.    The "read only" attribute bit protects a file by preventing you from
  5798.    changing or deleting its contents. You can read the file, list it, or
  5799.    access it in any normal way, but the operating system will generate an
  5800.    error if you try to edit or delete it.
  5801.  
  5802.    The "hidden" attribute bit makes a file invisible to the user. A good
  5803.    example of this bit's action is shown by the module-level code that
  5804.    demonstrates this subprogram. The hidden file IBMDOS.COM has its "hidden"
  5805.    bit cleared and then reset. If you leave this bit cleared, the IBMDOS.COM
  5806.    file will show up in your root directory whenever you give the DIR
  5807.    command.
  5808.  
  5809.    DOSCALLS
  5810.  
  5811.    The "system" attribute bit marks files such as IBMBIO.COM and IBMDOS.COM
  5812.    as special system files. These two files are in the root directory of all
  5813.    your bootable disks and are necessary for MS-DOS to be able to
  5814.    successfully boot from a given disk.
  5815.  
  5816.    The variable attr.result returns a 0 if the attempt to read the file
  5817.    attribute bits was successful.
  5818.  
  5819.    ──────────────────────────────────────────────────────────────────────────
  5820.      ' ************************************************
  5821.      ' **  Name:          GetFileAttributes          **
  5822.      ' **  Type:          Subprogram                 **
  5823.      ' **  Module:        DOSCALLS.BAS               **
  5824.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5825.      ' ************************************************
  5826.      '
  5827.      ' Returns the file attribute settings for a file.
  5828.      '
  5829.      ' EXAMPLE OF USE:  GetFileAttributes fileName$, attr
  5830.      ' PARAMETERS:      fileName$  Name of file
  5831.      '                  attr       Structure of type FileAttributesType
  5832.      ' VARIABLES:       regX       Structure of type RegTypeX
  5833.      '                  f$         Null terminated copy of fileName$
  5834.      ' MODULE LEVEL
  5835.      '   DECLARATIONS:  TYPE RegTypeX
  5836.      '                     ax    AS INTEGER
  5837.      '                     bx    AS INTEGER
  5838.      '                     cx    AS INTEGER
  5839.      '                     dx    AS INTEGER
  5840.      '                     bp    AS INTEGER
  5841.      '                     si    AS INTEGER
  5842.      '                     di    AS INTEGER
  5843.      '                     flags AS INTEGER
  5844.      '                     ds    AS INTEGER
  5845.      '                     es    AS INTEGER
  5846.      '                  END TYPE
  5847.      '
  5848.      '                  TYPE FileAttributesType
  5849.      '                     readOnly AS INTEGER
  5850.      '                     hidden AS INTEGER
  5851.      '                     systemFile AS INTEGER
  5852.      '                     archive AS INTEGER
  5853.      '                     result AS INTEGER
  5854.      '                  END TYPE
  5855.      '
  5856.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  5857.      '   DECLARE SUB GetFileAttributes (fileName$, attr AS FileAttributesType)
  5858.      '
  5859.        SUB GetFileAttributes (fileName$, attr AS FileAttributesType) STATIC
  5860.            DIM regX AS RegTypeX
  5861.            regX.ax = &H4300
  5862.            f$ = fileName$ + CHR$(0)
  5863.            regX.ds = VARSEG(f$)
  5864.            regX.dx = SADD(f$)
  5865.            InterruptX &H21, regX, regX
  5866.            IF regX.flags AND 1 THEN
  5867.                attr.result = regX.ax
  5868.            ELSE
  5869.                attr.result = 0
  5870.            END IF
  5871.            attr.readOnly = regX.cx AND 1
  5872.            attr.hidden = (regX.cx \ 2) AND 1
  5873.            attr.systemFile = (regX.cx \ 4) AND 1
  5874.            attr.archive = (regX.cx \ 32) AND 1
  5875.        END SUB
  5876.    ──────────────────────────────────────────────────────────────────────────
  5877.  
  5878.  
  5879.  Subprogram: GetMediaDescriptor
  5880.  
  5881.    Returns media information about any disk drive currently defined by
  5882.    MS-DOS. For any given drive, you can determine the number of sectors per
  5883.    allocation unit, the number of bytes per sector, and the FAT
  5884.    identification byte MS-DOS uses to determine how to treat the drive. This
  5885.    information is returned by the MS-DOS function 21H.
  5886.  
  5887.    The GetDiskFreeSpace subprogram returns related information.
  5888.  
  5889.    ──────────────────────────────────────────────────────────────────────────
  5890.      ' ************************************************
  5891.      ' **  Name:          GetMediaDescriptor         **
  5892.      ' **  Type:          Subprogram                 **
  5893.      ' **  Module:        DOSCALLS.BAS               **
  5894.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5895.      ' ************************************************
  5896.      '
  5897.      ' Calls the MS-DOS "Get Media Descriptor" function for
  5898.      ' the indicated drive.  Results are returned in a
  5899.      ' structure of type MediaDescriptorType.
  5900.      '
  5901.      ' EXAMPLE OF USE:  GetMediaDescriptor drive$, desc
  5902.      ' PARAMETERS:      drive$     Drive designation, such as "A:"
  5903.      '                  desc       Structure of type MediaDescriptorType
  5904.      ' VARIABLES:       regX       Structure of type RegTypeX
  5905.      '                  drive%     Numeric drive designation
  5906.      ' MODULE LEVEL
  5907.      '   DECLARATIONS:  TYPE RegTypeX
  5908.      '                     ax    AS INTEGER
  5909.      '                     bx    AS INTEGER
  5910.      '                     cx    AS INTEGER
  5911.      '                     dx    AS INTEGER
  5912.      '                     bp    AS INTEGER
  5913.      '                     si    AS INTEGER
  5914.      '                     di    AS INTEGER
  5915.      '                     flags AS INTEGER
  5916.      '                     ds    AS INTEGER
  5917.      '                     es    AS INTEGER
  5918.      '                  END TYPE
  5919.      '
  5920.      '                  TYPE MediaDescriptorType
  5921.      '                     sectorsPerAllocationUnit AS INTEGER
  5922.      '                     bytesPerSector AS INTEGER
  5923.      '                     FATIdentificationByte AS INTEGER
  5924.      '                  END TYPE
  5925.      '
  5926.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  5927.      '   DECLARE SUB GetMediaDescriptor (drive$, desc AS MediaDescriptorType)
  5928.      '
  5929.        SUB GetMediaDescriptor (drive$, desc AS MediaDescriptorType) STATIC
  5930.            DIM regX AS RegTypeX
  5931.            IF drive$ <> "" THEN
  5932.                drive% = ASC(UCASE$(drive$)) - 64
  5933.            ELSE
  5934.                drive% = 0
  5935.            END IF
  5936.            IF drive% >= 0 THEN
  5937.                regX.dx = drive%
  5938.            ELSE
  5939.                regX.dx = 0
  5940.            END IF
  5941.            regX.ax = &H1C00
  5942.            InterruptX &H21, regX, regX
  5943.            desc.sectorsPerAllocationUnit = regX.ax AND &HFF
  5944.            desc.bytesPerSector = regX.cx
  5945.            DEF SEG = regX.ds
  5946.            desc.FATIdentificationByte = PEEK(regX.bx)
  5947.            DEF SEG
  5948.        END SUB
  5949.    ──────────────────────────────────────────────────────────────────────────
  5950.  
  5951.  
  5952.  Function: GetVerifyState%
  5953.  
  5954.    Returns the current setting of the MS-DOS Verify flag. If Verify is on,
  5955.    this function returns a 1. If Verify is off, a 0 is returned.
  5956.  
  5957.    See the SetVerifyState subprogram to see how to set the Verify on or off
  5958.    as desired.
  5959.  
  5960.    ──────────────────────────────────────────────────────────────────────────
  5961.      ' ************************************************
  5962.      ' **  Name:          GetVerifyState%            **
  5963.      ' **  Type:          Function                   **
  5964.      ' **  Module:        DOSCALLS.BAS               **
  5965.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  5966.      ' ************************************************
  5967.      '
  5968.      ' Returns the current state of the MS-DOS "Verify After
  5969.      ' Write" flag.
  5970.      '
  5971.      ' EXAMPLE OF USE:  state% = GetVerifyState%
  5972.      ' PARAMETERS:      (none)
  5973.      ' VARIABLES:       reg        Structure of type RegType
  5974.      ' MODULE LEVEL
  5975.      '   DECLARATIONS:  TYPE RegTypeX
  5976.      '                     ax    AS INTEGER
  5977.      '                     bx    AS INTEGER
  5978.      '                     cx    AS INTEGER
  5979.      '                     dx    AS INTEGER
  5980.      '                     bp    AS INTEGER
  5981.      '                     si    AS INTEGER
  5982.      '                     di    AS INTEGER
  5983.      '                     flags AS INTEGER
  5984.      '                  END TYPE
  5985.      '
  5986.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  5987.      '      DECLARE FUNCTION GetVerifyState% ()
  5988.      '
  5989.        FUNCTION GetVerifyState% STATIC
  5990.            DIM reg AS RegType
  5991.            reg.ax = &H5400
  5992.            Interrupt &H21, reg, reg
  5993.            GetVerifyState% = reg.ax AND &HFF
  5994.        END FUNCTION
  5995.    ──────────────────────────────────────────────────────────────────────────
  5996.  
  5997.  
  5998.  Subprogram: SetDirectory
  5999.  
  6000.    Sets the current directory for the default drive in the same way as the
  6001.    MS-DOS CHDIR command.
  6002.  
  6003.    For example, to cause a program to change to the directory C:\TXT, use
  6004.    this program statement:
  6005.  
  6006.  
  6007.      SetDirectory "C:\TXT", result%
  6008.  
  6009.    The returned value of result% indicates whether the attempt to change the
  6010.    directory was successful. If result% is 0, the directory change was
  6011.    successful.
  6012.  
  6013.    ──────────────────────────────────────────────────────────────────────────
  6014.      ' ************************************************
  6015.      ' **  Name:          SetDirectory               **
  6016.      ' **  Type:          Subprogram                 **
  6017.      ' **  Module:        DOSCALLS.BAS               **
  6018.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6019.      ' ************************************************
  6020.      '
  6021.      ' Sets the current directory.
  6022.      '
  6023.      ' EXAMPLE OF USE:  SetDirectory path$, result%
  6024.      ' PARAMETERS:      path$      The path to the directory
  6025.      '                  result%    Returned error code, zero if successful
  6026.      ' VARIABLES:       regX       Structure of type RegTypeX
  6027.      '                  p$         Null terminated copy of path$
  6028.      ' MODULE LEVEL
  6029.      '   DECLARATIONS:  TYPE RegTypeX
  6030.      '                     ax    AS INTEGER
  6031.      '                     bx    AS INTEGER
  6032.      '                     cx    AS INTEGER
  6033.      '                     dx    AS INTEGER
  6034.      '                     bp    AS INTEGER
  6035.      '                     si    AS INTEGER
  6036.      '                     di    AS INTEGER
  6037.      '                     flags AS INTEGER
  6038.      '                     ds    AS INTEGER
  6039.      '                     es    AS INTEGER
  6040.      '                  END TYPE
  6041.      '
  6042.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  6043.      '   DECLARE SUB SetDirectory (path$, result%)
  6044.      '
  6045.        SUB SetDirectory (path$, result%) STATIC
  6046.            DIM regX AS RegTypeX
  6047.            regX.ax = &H3B00
  6048.            p$ = path$ + CHR$(0)
  6049.            regX.ds = VARSEG(p$)
  6050.            regX.dx = SADD(p$)
  6051.            InterruptX &H21, regX, regX
  6052.            IF regX.flags AND 1 THEN
  6053.                result% = regX.ax
  6054.            ELSE
  6055.                result% = 0
  6056.            END IF
  6057.        END SUB
  6058.    ──────────────────────────────────────────────────────────────────────────
  6059.  
  6060.  
  6061.  Subprogram: SetDrive
  6062.  
  6063.    Lets a QuickBASIC program change the current disk drive. Another way of
  6064.    doing the same thing would be to use the SHELL statement:
  6065.  
  6066.  
  6067.      SHELL "CD " + d$
  6068.  
  6069.    However, this subprogram is much more efficient and much faster.
  6070.  
  6071.    ──────────────────────────────────────────────────────────────────────────
  6072.      ' ************************************************
  6073.      ' **  Name:          SetDrive                   **
  6074.      ' **  Type:          Subprogram                 **
  6075.      ' **  Module:        DOSCALLS.BAS               **
  6076.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6077.      ' ************************************************
  6078.      '
  6079.      ' Calls MS-DOS to set the current drive.
  6080.      '
  6081.      ' EXAMPLE OF USE:  SetDrive d$
  6082.      ' PARAMETERS:      d$         Drive designation, such as "A:"
  6083.      ' VARIABLES:       reg        Structure of type RegType
  6084.      '                  drive%     Numeric value of drive
  6085.      ' MODULE LEVEL
  6086.      '   DECLARATIONS:  TYPE RegTypeX
  6087.      '                     ax    AS INTEGER
  6088.      '                     bx    AS INTEGER
  6089.      '                     cx    AS INTEGER
  6090.      '                     dx    AS INTEGER
  6091.      '                     bp    AS INTEGER
  6092.      '                     si    AS INTEGER
  6093.      '                     di    AS INTEGER
  6094.      '                     flags AS INTEGER
  6095.      '                  END TYPE
  6096.      '
  6097.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  6098.      '      DECLARE SUB SetDrive (drive$)
  6099.      '
  6100.        SUB SetDrive (drive$) STATIC
  6101.            DIM reg AS RegType
  6102.            IF drive$ <> "" THEN
  6103.                drive% = ASC(UCASE$(drive$)) - 65
  6104.            ELSE
  6105.                drive% = 0
  6106.            END IF
  6107.            IF drive% >= 0 THEN
  6108.                reg.dx = drive%
  6109.            ELSE
  6110.                reg.dx = 0
  6111.            END IF
  6112.            reg.ax = &HE00
  6113.            Interrupt &H21, reg, reg
  6114.        END SUB
  6115.    ──────────────────────────────────────────────────────────────────────────
  6116.  
  6117.  
  6118.  Subprogram: SetFileAttributes
  6119.  
  6120.    Sets the file attribute bits for a file as desired. For example, to make a
  6121.    file invisible to the user, set the "hidden" attribute bit. To protect a
  6122.    file from accidentally being modified or deleted, set the "read only"
  6123.    attribute bit.
  6124.  
  6125.    The GetFileAttributes subprogram describes these file attribute bits in
  6126.    more detail.
  6127.  
  6128.    ──────────────────────────────────────────────────────────────────────────
  6129.      ' ************************************************
  6130.      ' **  Name:          SetFileAttributes          **
  6131.      ' **  Type:          Subprogram                 **
  6132.      ' **  Module:        DOSCALLS.BAS               **
  6133.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6134.      ' ************************************************
  6135.      '
  6136.      ' Sets attribute bits for a file.
  6137.      '
  6138.      ' EXAMPLE OF USE:  SetFileAttributes fileName$, attr
  6139.      ' PARAMETERS:      fileName$  Name of file
  6140.      '                  attr       Structure of type FileAttributesType
  6141.      ' VARIABLES:       regX       Structure of type RegTypeX
  6142.      '                  f$         Null terminated copy of fileName$
  6143.      ' MODULE LEVEL
  6144.      '   DECLARATIONS:  TYPE RegTypeX
  6145.      '                     ax    AS INTEGER
  6146.      '                     bx    AS INTEGER
  6147.      '                     cx    AS INTEGER
  6148.      '                     dx    AS INTEGER
  6149.      '                     bp    AS INTEGER
  6150.      '                     si    AS INTEGER
  6151.      '                     di    AS INTEGER
  6152.      '                     flags AS INTEGER
  6153.      '                     ds    AS INTEGER
  6154.      '                     es    AS INTEGER
  6155.      '                  END TYPE
  6156.      '
  6157.      '                 TYPE FileAttributesType
  6158.      '                    readOnly AS INTEGER
  6159.      '                    hidden AS INTEGER
  6160.      '                    systemFile AS INTEGER
  6161.      '                    archive AS INTEGER
  6162.      '                    result AS INTEGER
  6163.      '                 END TYPE
  6164.      '
  6165.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  6166.      '   DECLARE SUB SetFileAttributes (fileName$, attr AS FileAttributesType)
  6167.      '
  6168.        SUB SetFileAttributes (fileName$, attr AS FileAttributesType)
  6169.            DIM regX AS RegTypeX
  6170.            regX.ax = &H4301
  6171.            IF attr.readOnly THEN
  6172.                regX.cx = 1
  6173.            ELSE
  6174.                regX.cx = 0
  6175.            END IF
  6176.            IF attr.hidden THEN
  6177.                regX.cx = regX.cx + 2
  6178.            END IF
  6179.            IF attr.systemFile THEN
  6180.                regX.cx = regX.cx + 4
  6181.            END IF
  6182.            IF attr.archive THEN
  6183.                regX.cx = regX.cx + 32
  6184.            END IF
  6185.            f$ = fileName$ + CHR$(0)
  6186.            regX.ds = VARSEG(f$)
  6187.            regX.dx = SADD(f$)
  6188.            InterruptX &H21, regX, regX
  6189.            IF regX.flags AND 1 THEN
  6190.                attr.result = regX.ax
  6191.            ELSE
  6192.                attr.result = 0
  6193.            END IF
  6194.        END SUB
  6195.    ──────────────────────────────────────────────────────────────────────────
  6196.  
  6197.  
  6198.  Subprogram: SetVerifyState
  6199.  
  6200.    Sets or clears the write verify flag MS-DOS uses during disk file writing,
  6201.    duplicating the actions of the MS-DOS commands VERIFY ON and VERIFY OFF.
  6202.    If a parameter (state%) of 0 is passed to the routine, the subprogram sets
  6203.    the Verify state to off. If non-zero, it sets it to on.
  6204.  
  6205.    To determine the current setting of the Verify flag, use the
  6206.    GetVerifyState% function.
  6207.  
  6208.    ──────────────────────────────────────────────────────────────────────────
  6209.      ' ************************************************
  6210.      ' **  Name:          SetVerifyState             **
  6211.      ' **  Type:          Subprogram                 **
  6212.      ' **  Module:        DOSCALLS.BAS               **
  6213.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6214.      ' ************************************************
  6215.      '
  6216.      ' Sets or clears the "Verify After Write" MS-DOS flag.
  6217.      '
  6218.      ' EXAMPLE OF USE:  SetVerifyState state%
  6219.      ' PARAMETERS:      state%     If 0, resets Verify;  If non-zero,
  6220.      '                             then sets Verify on
  6221.      ' VARIABLES:       reg        Structure of type RegType
  6222.      ' MODULE LEVEL
  6223.      '   DECLARATIONS:  TYPE RegTypeX
  6224.      '                     ax    AS INTEGER
  6225.      '                     bx    AS INTEGER
  6226.      '                     cx    AS INTEGER
  6227.      '                     dx    AS INTEGER
  6228.      '                     bp    AS INTEGER
  6229.      '                     si    AS INTEGER
  6230.      '                     di    AS INTEGER
  6231.      '                     flags AS INTEGER
  6232.      '                  END TYPE
  6233.      '
  6234.      '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
  6235.      '      DECLARE SUB SetVerifyState (state%)
  6236.      '
  6237.        SUB SetVerifyState (state%) STATIC
  6238.            DIM reg AS RegType
  6239.            IF state% THEN
  6240.                reg.ax = &H2E01
  6241.            ELSE
  6242.                reg.ax = &H2E00
  6243.            END IF
  6244.            Interrupt &H21, reg, reg
  6245.        END SUB
  6246.    ──────────────────────────────────────────────────────────────────────────
  6247.  
  6248.  
  6249.  Function: TranslateCountry$
  6250.  
  6251.    Returns the translated version of the string passed to it, according to
  6252.    the current MS-DOS country setting. Only characters with byte values in
  6253.    the range 128 through 255 are candidates for translation.
  6254.  
  6255.    Before calling this function, you must call the GetCountry subprogram to
  6256.    fill in the structure of type GetCountryType with the address of the
  6257.    translation routine in the operating system. This housekeeping is all
  6258.    taken care of automatically if you only remember to call GetCountry
  6259.    before calling TranslateCountry$.
  6260.  
  6261.    The TranslateCountry$ function calls an assembly-language subprogram named
  6262.    CaseMap to translate each character of the passed string. CaseMap
  6263.    demonstrates the powerful DECLARE statement of QuickBASIC. ( CaseMap is
  6264.    discussed in Part III of this book.) Notice that the segment% and offset%
  6265.    variables representing the address of the MS-DOS translation routine are
  6266.    passed by value rather than by address, the default.
  6267.  
  6268.    ──────────────────────────────────────────────────────────────────────────
  6269.      ' ************************************************
  6270.      ' **  Name:          TranslateCountry$          **
  6271.      ' **  Type:          Function                   **
  6272.      ' **  Module:        DOSCALLS.BAS               **
  6273.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6274.      ' ************************************************
  6275.      '
  6276.      ' Returns a string of characters translated according to
  6277.      ' the current country setting of MS-DOS.
  6278.      '
  6279.      ' EXAMPLE OF USE:  b$ = TranslateCountry$(a$, country)
  6280.      ' PARAMETERS:      a$         String to be translated
  6281.      '                  country    Structure of type CountryType
  6282.      ' VARIABLES:       i%         Index to each character of a$
  6283.      '                  c%         Byte value of each character in a$
  6284.      ' MODULE LEVEL
  6285.      '   DECLARATIONS:  TYPE CountryType
  6286.      '                     DateTimeFormat AS STRING * 11
  6287.      '                     CurrencySymbol AS STRING * 4
  6288.      '                     ThousandsSeparator AS STRING * 1
  6289.      '                     DecimalSeparator AS STRING * 1
  6290.      '                     DateSeparator AS STRING * 1
  6291.      '                     TimeSeparator AS STRING * 1
  6292.      '                     CurrencyThenSymbol AS INTEGER
  6293.      '                     CurrencySymbolSpace AS INTEGER
  6294.      '                     CurrencyPlaces AS INTEGER
  6295.      '                     Hours24 AS INTEGER
  6296.      '                     caseMapSegment AS INTEGER
  6297.      '                     caseMapOffset AS INTEGER
  6298.      '                     DataListSeparator AS STRING * 1
  6299.      '                  END TYPE
  6300.      '
  6301.      '           DECLARE SUB CaseMap (character%, BYVAL Segment%, BYVAL Offset
  6302.      '           DECLARE FUNCTION TranslateCountry$ (a$, country AS CountryTyp
  6303.      '
  6304.        FUNCTION TranslateCountry$ (a$, country AS CountryType) STATIC
  6305.            FOR i% = 1 TO LEN(a$)
  6306.                c% = ASC(MID$(a$, i%))
  6307.                CaseMap c%, country.caseMapSegment, country.caseMapOffset
  6308.                MID$(a$, i%, 1) = CHR$(c%)
  6309.            NEXT i%
  6310.            TranslateCountry$ = a$
  6311.        END FUNCTION
  6312.    ──────────────────────────────────────────────────────────────────────────
  6313.  
  6314.  
  6315.  Subprogram: WriteToDevice
  6316.  
  6317.    Outputs a string of bytes or characters to any device or file. QuickBASIC
  6318.    provides comprehensive input and output capabilities and should be used
  6319.    whenever possible. This routine is for those rare instances when accessing
  6320.    the MS-DOS output routines is of benefit. For example, the STDOUT toolbox
  6321.    is a good example of the use of the MS-DOS level code for output.
  6322.    QuickBASIC PRINT statements bypass the extended screen and keyboard
  6323.    control device named ANSI.SYS. Using this WriteToDevice subprogram (or the
  6324.    routines in the STDOUT toolbox) lets you use the ANSI.SYS driver's
  6325.    capabilities.
  6326.  
  6327.    ──────────────────────────────────────────────────────────────────────────
  6328.      ' ************************************************
  6329.      ' **  Name:          WriteToDevice              **
  6330.      ' **  Type:          Subprogram                 **
  6331.      ' **  Module:        DOSCALLS.BAS               **
  6332.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6333.      ' ************************************************
  6334.      '
  6335.      ' Writes bytes to a file or device.
  6336.      '
  6337.      ' EXAMPLE OF USE:  WriteToDevice handle%, a$, result%
  6338.      ' PARAMETERS:      handle%    File or device handle
  6339.      '                  a$         String to be output
  6340.      '                  result%    Error code returned from MS-DOS
  6341.      ' VARIABLES:       regX       Structure of type RegTypeX
  6342.      ' MODULE LEVEL
  6343.      '   DECLARATIONS:  TYPE RegTypeX
  6344.      '                     ax    AS INTEGER
  6345.      '                     bx    AS INTEGER
  6346.      '                     cx    AS INTEGER
  6347.      '                     dx    AS INTEGER
  6348.      '                     bp    AS INTEGER
  6349.      '                     si    AS INTEGER
  6350.      '                     di    AS INTEGER
  6351.      '                     flags AS INTEGER
  6352.      '                     ds    AS INTEGER
  6353.      '                     es    AS INTEGER
  6354.      '                  END TYPE
  6355.      '
  6356.      '   DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
  6357.      '   DECLARE SUB WriteToDevice (handle%, a$, result%)
  6358.      '
  6359.        SUB WriteToDevice (handle%, a$, result%) STATIC
  6360.            DIM regX AS RegTypeX
  6361.            regX.ax = &H4000
  6362.            regX.cx = LEN(a$)
  6363.            regX.bx = handle%
  6364.            regX.ds = VARSEG(a$)
  6365.            regX.dx = SADD(a$)
  6366.            InterruptX &H21, regX, regX
  6367.            IF regX.flags AND 1 THEN
  6368.                result% = regX.ax
  6369.            ELSEIF regX.ax <> LEN(a$) THEN
  6370.                result% = -1
  6371.            ELSE
  6372.                result% = 0
  6373.            END IF
  6374.        END SUB
  6375.    ──────────────────────────────────────────────────────────────────────────
  6376.  
  6377.  
  6378.  
  6379.  ────────────────────────────────────────────────────────────────────────────
  6380.  EDIT
  6381.  
  6382.    The EDIT toolbox is a collection of subprograms for line and screen input
  6383.    of strings. The EditLine subprogram allows full input editing on a single
  6384.    line, and the EditBox subprogram allows user input and editing inside a
  6385.    rectangular area of the screen. The DrawBox, FormatTwo, and
  6386.    InsertCharacter subprograms enhance the capabilities of the EditBox
  6387.    routine and provide capabilities that can be useful in themselves.
  6388.  
  6389.    Name                     Type    Description
  6390.    ──────────────────────────────────────────────────────────────────────────
  6391.    EDIT.BAS                        Demo module
  6392.    DrawBox                 Sub     Creates a double-lined box on the display
  6393.    EditBox                 Sub     Allows editing in a boxed area of the
  6394.                                     screen
  6395.    EditLine                Sub     Allows editing of string at cursor
  6396.                                     position
  6397.    FormatTwo               Sub     Splits string into two strings
  6398.    InsertCharacter         Sub     Inserts a character
  6399.    ──────────────────────────────────────────────────────────────────────────
  6400.  
  6401.  
  6402.  Demo Module: EDIT
  6403.  
  6404.    ──────────────────────────────────────────────────────────────────────────
  6405.      ' ************************************************
  6406.      ' **  Name:          EDIT                       **
  6407.      ' **  Type:          Toolbox                    **
  6408.      ' **  Module:        EDIT.BAS                   **
  6409.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6410.      ' ************************************************
  6411.      '
  6412.      ' USAGE:           No command line parameters
  6413.      ' .MAK FILE:       EDIT.BAS
  6414.      '                  KEYS.BAS
  6415.      ' PARAMETERS:      (none)
  6416.      ' VARIABLES:       a$         String to be edited by the user
  6417.  
  6418.        CONST FALSE = 0
  6419.        CONST TRUE = NOT FALSE
  6420.  
  6421.      ' Key code numbers
  6422.        CONST BACKSPACE = 8
  6423.        CONST CTRLLEFTARROW = 29440
  6424.        CONST CTRLRIGHTARROW = 29696
  6425.        CONST CTRLY = 25
  6426.        CONST CTRLQ = 17
  6427.        CONST DELETE = 21248
  6428.        CONST DOWNARROW = 20480
  6429.        CONST ENDKEY = 20224
  6430.        CONST ENTER = 13
  6431.        CONST ESCAPE = 27
  6432.        CONST HOME = 18176
  6433.        CONST INSERTKEY = 20992
  6434.        CONST LEFTARROW = 19200
  6435.        CONST RIGHTARROW = 19712
  6436.        CONST TABKEY = 9
  6437.        CONST UPARROW = 18432
  6438.  
  6439.      ' Functions
  6440.        DECLARE FUNCTION KeyCode% ()
  6441.  
  6442.      ' Subprograms
  6443.        DECLARE SUB EditLine (a$, exitCode%)
  6444.        DECLARE SUB DrawBox (row1%, col1%, row2%, col2%)
  6445.        DECLARE SUB EditBox (a$, row1%, col1%, row2%, col2%)
  6446.        DECLARE SUB FormatTwo (a$, b$, col%)
  6447.        DECLARE SUB InsertCharacter (x$(), kee$, cp%, rp%, wide%, high%)
  6448.  
  6449.      ' Demonstrate the EditLine subprogram
  6450.        a$ = " Edit this line, and then press Up arrow, Down arrow, or Enter "
  6451.        CLS
  6452.        COLOR 14, 1
  6453.        EditLine a$, exitCode%
  6454.        COLOR 7, 0
  6455.        PRINT
  6456.        PRINT
  6457.        PRINT "Result of edit ..."
  6458.        COLOR 14, 0
  6459.        PRINT a$
  6460.        COLOR 7, 0
  6461.        PRINT
  6462.        SELECT CASE exitCode%
  6463.        CASE 0
  6464.            PRINT "Enter";
  6465.        CASE -1
  6466.            PRINT "Down arrow";
  6467.        CASE 1
  6468.            PRINT "Up arrow";
  6469.        CASE ELSE
  6470.        END SELECT
  6471.        PRINT " key pressed."
  6472.  
  6473.      ' Demonstrate the EditBox subprogram
  6474.        a$ = "Now, edit text inside this box.  Press "
  6475.        a$ = a$ + "Esc to end the editing..."
  6476.        COLOR 12, 1
  6477.        DrawBox 8, 17, 19, 57
  6478.        COLOR 11, 1
  6479.        EditBox a$, 8, 17, 19, 57
  6480.        LOCATE 21, 1
  6481.        COLOR 7, 0
  6482.        PRINT "Result..."
  6483.        COLOR 14, 0
  6484.        PRINT a$
  6485.        COLOR 7, 0
  6486.    ──────────────────────────────────────────────────────────────────────────
  6487.  
  6488.  
  6489.  Subprogram: DrawBox
  6490.  
  6491.    Draws a rectangular, double-lined box on the screen. No attempt is made to
  6492.    save the screen contents under the box area, and no control of the
  6493.    character colors is provided. The DrawBox subprogram simply provides a
  6494.    fast, flexible way to get a rectangular area of the screen cleared and
  6495.    outlined using the current foreground and background color settings. Use
  6496.    the COLOR statement before calling this subprogram if you want to change
  6497.    the foreground and background colors.
  6498.  
  6499.    The WINDOWS.BAS module provides a more comprehensive method of creating
  6500.    and removing windows for information and menuing tasks.
  6501.  
  6502.    ──────────────────────────────────────────────────────────────────────────
  6503.      ' ************************************************
  6504.      ' **  Name:          DrawBox                    **
  6505.      ' **  Type:          Subprogram                 **
  6506.      ' **  Module:        EDIT.BAS                   **
  6507.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6508.      ' ************************************************
  6509.      '
  6510.      ' Draws a double-lined box.
  6511.      '
  6512.      ' EXAMPLE OF USE:  DrawBox row1%, col1%, row2%, col2%
  6513.      ' PARAMETERS:      row1%    Screen text row at upper left corner of the b
  6514.      '                  col1%    Screen text column at upper left corner of th
  6515.      '                  row2%    Screen text row at lower right corner of the
  6516.      '                  col2%    Screen text column at lower right corner of t
  6517.      '                           box
  6518.      ' VARIABLES:       wide%    Inside width of box
  6519.      '                  row3%    Loop row number for creating sides of box
  6520.      ' MODULE LEVEL
  6521.      '   DECLARATIONS:  DECLARE SUB DrawBox (row1%, col1%, row2%, col2%)
  6522.      '
  6523.        SUB DrawBox (row1%, col1%, row2%, col2%) STATIC
  6524.  
  6525.          ' Determine inside width of box
  6526.            wide% = col2% - col1% - 1
  6527.  
  6528.          ' Across the top
  6529.            LOCATE row1%, col1%, 0
  6530.            PRINT CHR$(201);
  6531.            PRINT STRING$(wide%, 205);
  6532.            PRINT CHR$(187);
  6533.  
  6534.          ' Down the sides
  6535.            FOR row3% = row1% + 1 TO row2% - 1
  6536.                LOCATE row3%, col1%, 0
  6537.                PRINT CHR$(186);
  6538.                PRINT SPACE$(wide%);
  6539.                PRINT CHR$(186);
  6540.            NEXT row3%
  6541.  
  6542.          ' Across the bottom
  6543.            LOCATE row2%, col1%, 0
  6544.            PRINT CHR$(200);
  6545.            PRINT STRING$(wide%, 205);
  6546.            PRINT CHR$(188);
  6547.  
  6548.        END SUB
  6549.    ──────────────────────────────────────────────────────────────────────────
  6550.  
  6551.  
  6552.  Subprogram: EditBox
  6553.  
  6554.    Lets a user input and edit string characters in a rectangular area of the
  6555.    screen. This routine doesn't draw a box around the area on the display,
  6556.    but you can easily create one by calling the DrawBox subprogram before
  6557.    calling EditBox.
  6558.  
  6559.    This subprogram is a simple text editor. Features include automatic
  6560.    wordwrap and reformatting, line insert and delete, and support of many of
  6561.    the same editing keys used in the QuickBASIC editing environment. The keys
  6562.    acted upon are Left arrow, Right arrow, Up arrow, Down arrow, Home, End,
  6563.    Insert, Backspace, Delete, Ctrl-Y, Ctrl-Q-Y, Ctrl-Right arrow, Ctrl-Left
  6564.    arrow, Enter, and Escape.
  6565.  
  6566.    You can force a reformat of the entire rectangular area by moving the
  6567.    cursor to the upper left corner of the rectangular area and then pressing
  6568.    the Backspace key. The cursor won't move anywhere, but all text in the
  6569.    area will be reformatted.
  6570.  
  6571.    To escape from the editing mode, press the Escape key. The string result
  6572.    of the edit is returned in a$ to the calling program. Note that linefeeds
  6573.    and all double spaces are removed from a$ and that a$ is trimmed of spaces
  6574.    from each end before being returned.
  6575.  
  6576.    ──────────────────────────────────────────────────────────────────────────
  6577.      ' ************************************************
  6578.      ' **  Name:          EditBox                    **
  6579.      ' **  Type:          Subprogram                 **
  6580.      ' **  Module:        EDIT.BAS                   **
  6581.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6582.      ' ************************************************
  6583.      '
  6584.      ' Allows the user to edit text inside a rectangular area.
  6585.      '
  6586.      ' EXAMPLE OF USE:  EditBox a$, row1%, col1%, row2%, col2%
  6587.      ' PARAMETERS:      a$     String to be edited
  6588.      '                  row1%  Screen text row at upper left corner of the are
  6589.      '                  col1%  Screen text column at upper left corner of the
  6590.      '                  row2%  Screen text row at lower right corner of the ar
  6591.      '                  col2%  Screen text column at lower right corner of the
  6592.      ' VARIABLES:       r1%    Upper inside row of rectangular area
  6593.      '                  r2%    Lower inside row of rectangular area
  6594.      '                  c1%    Left inside column of rectangular area
  6595.      '                  c2%    Right inside column of rectangular area
  6596.      '                  wide%  Width of area
  6597.      '                  high%  Height of area
  6598.      '                  rp%    Index to current working row
  6599.      '                  cp%    Index to current working column
  6600.      '                  insert%  Flag for insert/replace mode
  6601.      '                  quit%  Flag for quitting the subprogram
  6602.      '                  across%  Saved current cursor column
  6603.      '                  down%  Saved current cursor row
  6604.      '                  x$()  Workspace string array
  6605.      '                  i%  Looping index
  6606.      '                  b$  Works with a$ to format a$ into x$()
  6607.      '                  keyNumber%  Integer code for any key press
  6608.      '                  c$  Temporary string workspace
  6609.      '                  ds%  Index to double-space groupings
  6610.      '                  sp%  Index to character where split of string is to oc
  6611.      '                  ctrlQflag%  Indicates Ctrl-Q has been pressed
  6612.      '                  kee$  Character entered from keyboard
  6613.      ' MODULE LEVEL
  6614.      '   DECLARATIONS:  DECLARE FUNCTION KeyCode% ()
  6615.      '                  DECLARE SUB EditBox ($, row1%, col1%, row2%, col2%)
  6616.      '                  DECLARE SUB FormatTwo (a$, b$, col%)
  6617.      '                  DECLARE SUB InsertCharacter (x$(), kee$, cp%, rp%,
  6618.      '                                               wide%, high%)
  6619.      '
  6620.        SUB EditBox (a$, row1%, col1%, row2%, col2%) STATIC
  6621.  
  6622.          ' Set up some working variables
  6623.            r1% = row1% + 1
  6624.            r2% = row2% - 1
  6625.            c1% = col1% + 2
  6626.            c2% = col2% - 2
  6627.            wide% = c2% - c1% + 1
  6628.            high% = r2% - r1% + 1
  6629.            rp% = 1
  6630.            cp% = 1
  6631.            insert% = TRUE
  6632.            quit% = FALSE
  6633.  
  6634.          ' Record the current cursor location
  6635.            across% = POS(0)
  6636.            down% = CSRLIN
  6637.  
  6638.          ' Dimension a workspace array
  6639.            REDIM x$(1 TO high%)
  6640.  
  6641.          ' Format a$ into array space
  6642.            FOR i% = 1 TO high%
  6643.                FormatTwo a$, b$, wide%
  6644.                x$(i%) = a$
  6645.                a$ = b$
  6646.            NEXT i%
  6647.  
  6648.          ' Display the strings
  6649.            FOR i% = 1 TO high%
  6650.                LOCATE r1% + i% - 1, c1%, 0
  6651.                PRINT x$(i%);
  6652.            NEXT i%
  6653.  
  6654.          ' Process each keystroke
  6655.            DO
  6656.  
  6657.              ' Update the current line
  6658.                LOCATE r1% + rp% - 1, c1%, 0
  6659.                PRINT x$(rp%);
  6660.  
  6661.              ' Place the cursor
  6662.                IF insert% THEN
  6663.                    LOCATE r1% + rp% - 1, c1% + cp% - 1, 1, 6, 7
  6664.                ELSE
  6665.                    LOCATE r1% + rp% - 1, c1% + cp% - 1, 1, 1, 7
  6666.                END IF
  6667.  
  6668.              ' Grab next keystroke
  6669.                keyNumber% = KeyCode%
  6670.  
  6671.              ' Process the key
  6672.                SELECT CASE keyNumber%
  6673.  
  6674.                CASE INSERTKEY
  6675.                    IF insert% THEN
  6676.                        insert% = FALSE
  6677.                    ELSE
  6678.                        insert% = TRUE
  6679.                    END IF
  6680.  
  6681.                CASE BACKSPACE
  6682.  
  6683.                  ' Rub out character to the left
  6684.                    IF cp% > 1 THEN
  6685.                        x$(rp%) = x$(rp%) + " "
  6686.                        b$ = LEFT$(x$(rp%), cp% - 2)
  6687.                        c$ = MID$(x$(rp%), cp%)
  6688.                        x$(rp%) = b$ + c$
  6689.                        cp% = cp% - 1
  6690.  
  6691.                      ' Upper left corner, so reformat the whole box
  6692.                    ELSEIF rp% = 1 THEN
  6693.  
  6694.                      ' Pull all the strings together
  6695.                        a$ = ""
  6696.                        FOR i% = 1 TO high%
  6697.                            a$ = a$ + LTRIM$(RTRIM$(x$(i%))) + " "
  6698.                        NEXT i%
  6699.  
  6700.                      ' Remove double spaces
  6701.                        ds% = INSTR(a$, "  ")
  6702.                        DO WHILE ds%
  6703.                            a$ = LEFT$(a$, ds% - 1) + MID$(a$, ds% + 1)
  6704.                            ds% = INSTR(a$, "  ")
  6705.                        LOOP
  6706.  
  6707.                      ' Format into the array and display lines
  6708.                        FOR i% = 1 TO high%
  6709.                            FormatTwo a$, b$, wide%
  6710.                            x$(i%) = a$
  6711.                            a$ = b$
  6712.                            LOCATE r1% + i% - 1, c1%, 0
  6713.                            PRINT x$(i%);
  6714.                        NEXT i%
  6715.  
  6716.                      ' Concatenate to the preceding line
  6717.                    ELSE
  6718.  
  6719.                      ' Use the InsertCharacter sub to insert a space
  6720.                        rp% = rp% - 1
  6721.                        cp% = wide% + 1
  6722.                        InsertCharacter x$(), " ", rp%, cp%, wide%, high%
  6723.  
  6724.                      ' Remove the extra spaces introduced
  6725.                        IF cp% > 2 THEN
  6726.                            b$ = LEFT$(x$(rp%), cp% - 3)
  6727.                            c$ = MID$(x$(rp%), cp%)
  6728.                        ELSE
  6729.                            b$ = ""
  6730.                            c$ = MID$(x$(rp%), cp% + 1)
  6731.                        END IF
  6732.  
  6733.                      ' Pull the line pieces together
  6734.                        x$(rp%) = LEFT$(b$ + c$ + SPACE$(3), wide%)
  6735.  
  6736.                      ' Adjust the cursor position
  6737.                        cp% = cp% - 1
  6738.  
  6739.                      ' Display the lines
  6740.                        FOR i% = 1 TO high%
  6741.                            LOCATE r1% + i% - 1, c1%, 0
  6742.                            PRINT x$(i%);
  6743.                        NEXT i%
  6744.                    END IF
  6745.  
  6746.                CASE DELETE
  6747.                    x$(rp%) = x$(rp%) + " "
  6748.                    b$ = LEFT$(x$(rp%), cp% - 1)
  6749.                    c$ = MID$(x$(rp%), cp% + 1)
  6750.                    x$(rp%) = b$ + c$
  6751.  
  6752.                CASE UPARROW
  6753.                    IF rp% > 1 THEN
  6754.                        rp% = rp% - 1
  6755.                    END IF
  6756.  
  6757.                CASE DOWNARROW
  6758.                    IF rp% < high% THEN
  6759.                        rp% = rp% + 1
  6760.                    END IF
  6761.  
  6762.                CASE LEFTARROW
  6763.                    IF cp% > 1 THEN
  6764.                        cp% = cp% - 1
  6765.                    END IF
  6766.  
  6767.                CASE RIGHTARROW
  6768.                    IF cp% < wide% THEN
  6769.                        cp% = cp% + 1
  6770.                    END IF
  6771.  
  6772.                CASE ENTER
  6773.                    IF rp% < high% AND x$(high%) = SPACE$(wide%) THEN
  6774.  
  6775.                      ' Shuffle lines down
  6776.                        FOR i% = high% TO rp% + 1 STEP -1
  6777.                            x$(i%) = x$(i% - 1)
  6778.                        NEXT i%
  6779.  
  6780.                      ' Split current line at cursor
  6781.                        sp% = wide% - cp% + 1
  6782.                        IF sp% THEN
  6783.                            MID$(x$(rp%), cp%, sp%) = SPACE$(sp%)
  6784.                        END IF
  6785.  
  6786.                      ' Move to next line
  6787.                        rp% = rp% + 1
  6788.                        x$(rp%) = MID$(x$(rp%), cp%) + SPACE$(cp% - 1)
  6789.                        cp% = 1
  6790.  
  6791.                      ' Display the modified lines
  6792.                        FOR i% = rp% - 1 TO high%
  6793.                            LOCATE r1% + i% - 1, c1%, 0
  6794.                            PRINT x$(i%);
  6795.                        NEXT i%
  6796.  
  6797.                    ELSE
  6798.  
  6799.                      ' Nowhere to push things down
  6800.                        BEEP
  6801.  
  6802.                    END IF
  6803.  
  6804.                CASE HOME
  6805.                    cp% = 1
  6806.  
  6807.                CASE ENDKEY
  6808.                    cp% = wide% + 1
  6809.  
  6810.                  ' Move back to just after last character
  6811.                    IF x$(rp%) <> SPACE$(wide%) THEN
  6812.                        DO UNTIL MID$(x$(rp%), cp% - 1, 1) <> " "
  6813.                            cp% = cp% - 1
  6814.                        LOOP
  6815.                    ELSE
  6816.                        cp% = 1
  6817.                    END IF
  6818.  
  6819.                CASE CTRLRIGHTARROW
  6820.  
  6821.                  ' Find next space
  6822.                    DO UNTIL MID$(x$(rp%), cp%, 1) = " " OR cp% = wide%
  6823.                        cp% = cp% + 1
  6824.                    LOOP
  6825.  
  6826.                  ' Find first non-space character
  6827.                    DO UNTIL MID$(x$(rp%), cp%, 1) <> " " OR cp% = wide%
  6828.                        cp% = cp% + 1
  6829.                    LOOP
  6830.  
  6831.                CASE CTRLLEFTARROW
  6832.  
  6833.                  ' Find first space to the left
  6834.                    DO UNTIL MID$(x$(rp%), cp%, 1) = " " OR cp% = 1
  6835.                        cp% = cp% - 1
  6836.                    LOOP
  6837.  
  6838.                  ' Find first non-space character to the left
  6839.                    DO UNTIL MID$(x$(rp%), cp%, 1) <> " " OR cp% = 1
  6840.                        cp% = cp% - 1
  6841.                    LOOP
  6842.  
  6843.                  ' Find next space to the left
  6844.                    DO UNTIL MID$(x$(rp%), cp%, 1) = " " OR cp% = 1
  6845.                        cp% = cp% - 1
  6846.                    LOOP
  6847.  
  6848.                  ' Adjust cursor position to first non-space character
  6849.                    IF cp% > 1 THEN
  6850.                        cp% = cp% + 1
  6851.                    END IF
  6852.  
  6853.                CASE CTRLY
  6854.                    IF rp% < high% THEN
  6855.                      ' Shuffle lines up, spacing out the last
  6856.                        FOR i% = rp% TO high%
  6857.                            IF i% < high% THEN
  6858.                                x$(i%) = x$(i% + 1)
  6859.                            ELSE
  6860.                                x$(i%) = SPACE$(wide%)
  6861.                            END IF
  6862.                            LOCATE r1% + i% - 1, c1%, 0
  6863.                            PRINT x$(i%);
  6864.                        NEXT i%
  6865.                    END IF
  6866.  
  6867.                  ' Move cursor to far left
  6868.                    cp% = 1
  6869.  
  6870.                CASE CTRLQ
  6871.                    ctrlQflag% = TRUE
  6872.  
  6873.                CASE ESCAPE
  6874.                    quit% = TRUE
  6875.  
  6876.                CASE IS > 255
  6877.                    SOUND 999, 1
  6878.  
  6879.                CASE IS < 32
  6880.                    SOUND 999, 1
  6881.  
  6882.                CASE ELSE
  6883.                    kee$ = CHR$(keyNumber%)
  6884.  
  6885.                  ' Insert mode
  6886.                    IF insert% THEN
  6887.                        InsertCharacter x$(), kee$, rp%, cp%, wide%, high%
  6888.                        FOR i% = 1 TO high%
  6889.                            LOCATE r1% + i% - 1, c1%, 0
  6890.                            PRINT x$(i%);
  6891.                        NEXT i%
  6892.  
  6893.                      ' Must be overstrike mode
  6894.                    ELSE
  6895.                        MID$(x$(rp%), cp%, 1) = kee$
  6896.                        IF cp% < wide% + 1 THEN
  6897.                            cp% = cp% + 1
  6898.                        ELSE
  6899.                            IF rp% < high% THEN
  6900.                                LOCATE r1% + rp% - 1, c1%, 0
  6901.                                PRINT x$(rp%);
  6902.                                rp% = rp% + 1
  6903.                                cp% = 1
  6904.                            END IF
  6905.                        END IF
  6906.                    END IF
  6907.  
  6908.                  ' Correct for bottom right corner problem
  6909.                    IF rp% > high% THEN
  6910.                        cp% = wide%
  6911.                        rp% = high%
  6912.                    END IF
  6913.  
  6914.                  ' Check for Ctrl-Q-Y combination (del to end of line)
  6915.                    IF kee$ = "y" AND ctrlQflag% THEN
  6916.                        cp% = cp% - 1
  6917.                        IF cp% = 0 THEN
  6918.                            cp% = wide%
  6919.                            rp% = rp% - 1
  6920.                        END IF
  6921.                        sp% = wide% - cp% + 1
  6922.                        MID$(x$(rp%), cp%, sp%) = SPACE$(sp%)
  6923.                    END IF
  6924.  
  6925.                  ' Clear out the possible Ctrl-Q signal
  6926.                    ctrlQflag% = FALSE
  6927.  
  6928.                END SELECT
  6929.  
  6930.            LOOP UNTIL quit%
  6931.  
  6932.          ' Concatenate the array strings to form the result
  6933.            a$ = ""
  6934.            FOR i% = 1 TO high%
  6935.                a$ = a$ + " " + LTRIM$(RTRIM$(x$(i%)))
  6936.            NEXT i%
  6937.  
  6938.          ' Remove double spaces
  6939.            ds% = INSTR(a$, "  ")
  6940.            DO WHILE ds%
  6941.                a$ = LEFT$(a$, ds% - 1) + MID$(a$, ds% + 1)
  6942.                ds% = INSTR(a$, "  ")
  6943.            LOOP
  6944.  
  6945.          ' Trim both ends of spaces
  6946.            a$ = LTRIM$(RTRIM$(a$))
  6947.  
  6948.          ' Restore original cursor position
  6949.            LOCATE down%, across%, 1
  6950.  
  6951.        END SUB
  6952.    ──────────────────────────────────────────────────────────────────────────
  6953.  
  6954.  
  6955.  Subprogram: EditLine
  6956.  
  6957.    Allows the user to edit a single line of text. The string is displayed at
  6958.    the current cursor location using the current foreground and background
  6959.    colors. Many of the same editing keys from the QuickBASIC editing
  6960.    environment are supported in the expected manner. For example, pressing
  6961.    Ctrl-Right arrow moves the cursor to the start of the next word, and
  6962.    pressing Ctrl-Q-Y deletes to the end of the line. Insert and overstrike
  6963.    modes are both supported, and you can delete characters by pressing the
  6964.    Delete or Backspace key.
  6965.  
  6966.    To exit the editing, press the Enter, Up arrow, or Down arrow key. The
  6967.    exitCode% value is set to 0, 1, or -1 respectively, allowing your calling
  6968.    program to determine which key terminated the editing.
  6969.  
  6970.    ──────────────────────────────────────────────────────────────────────────
  6971.      ' ************************************************
  6972.      ' **  Name:          EditLine                   **
  6973.      ' **  Type:          Subprogram                 **
  6974.      ' **  Module:        EDIT.BAS                   **
  6975.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  6976.      ' ************************************************
  6977.      '
  6978.      ' Allows the user to edit a string at the current cursor position
  6979.      ' on the screen.  Keys acted upon are Ctrl-Y, Ctrl-Q-Y, Right arrow,
  6980.      ' Left arrow, Ctrl-Left arrow, Ctrl-Right arrow, Home, End,
  6981.      ' Insert, Escape, Backspace, and Delete.
  6982.      ' Pressing Enter, Up arrow, or Down arrow terminates
  6983.      ' the subprogram and returns exitCode% of 0, +1, or -1.
  6984.      '
  6985.      ' EXAMPLE OF USE:  EditLine a$, exitCode%
  6986.      ' PARAMETERS:      a$         String to be edited
  6987.      '                  exitCode%  Returned code indicating the terminating
  6988.      '                             key press
  6989.      ' VARIABLES:       row%       Saved current cursor row
  6990.      '                  col%       Saved current cursor column
  6991.      '                  length%    Length of a$
  6992.      '                  ptr%       Location of cursor during the editing
  6993.      '                  insert%    Insert mode toggle
  6994.      '                  quit%      Flag for quitting the editing
  6995.      '                  original$  Saved copy of starting a$
  6996.      '                  keyNumber% Integer code for any key press
  6997.      '                  ctrlQflag% Indicates Ctrl-Q key press
  6998.      '                  kee$       Character of key just pressed
  6999.      '                  sp%        Length of space string
  7000.      ' MODULE LEVEL
  7001.      '   DECLARATIONS:  DECLARE FUNCTION KeyCode% ()
  7002.      '                  DECLARE SUB EditLine (a$, exitCode%)
  7003.      '
  7004.          SUB EditLine (a$, exitCode%) STATIC
  7005.  
  7006.          ' Set up some variables
  7007.            row% = CSRLIN
  7008.            col% = POS(0)
  7009.            length% = LEN(a$)
  7010.            ptr% = 0
  7011.            insert% = TRUE
  7012.            quit% = FALSE
  7013.            original$ = a$
  7014.  
  7015.          ' Main processing loop
  7016.            DO
  7017.  
  7018.              ' Display the line
  7019.                LOCATE row%, col%, 0
  7020.                PRINT a$;
  7021.  
  7022.              ' Show appropriate cursor type
  7023.                IF insert% THEN
  7024.                    LOCATE row%, col% + ptr%, 1, 6, 7
  7025.                ELSE
  7026.                    LOCATE row%, col% + ptr%, 1, 1, 7
  7027.                END IF
  7028.  
  7029.              ' Get next keystroke
  7030.                keyNumber% = KeyCode%
  7031.  
  7032.              ' Process the key
  7033.                SELECT CASE keyNumber%
  7034.  
  7035.                CASE INSERTKEY
  7036.                    IF insert% THEN
  7037.                        insert% = FALSE
  7038.                    ELSE
  7039.                        insert% = TRUE
  7040.                    END IF
  7041.  
  7042.                CASE BACKSPACE
  7043.                    IF ptr% THEN
  7044.                        a$ = a$ + " "
  7045.                        a$ = LEFT$(a$, ptr% - 1) + MID$(a$, ptr% + 1)
  7046.                        ptr% = ptr% - 1
  7047.                    END IF
  7048.  
  7049.                CASE DELETE
  7050.                    a$ = a$ + " "
  7051.                    a$ = LEFT$(a$, ptr%) + MID$(a$, ptr% + 2)
  7052.  
  7053.                CASE UPARROW
  7054.                    exitCode% = 1
  7055.                    quit% = TRUE
  7056.  
  7057.                CASE DOWNARROW
  7058.                    exitCode% = -1
  7059.                    quit% = TRUE
  7060.  
  7061.                CASE LEFTARROW
  7062.                    IF ptr% THEN
  7063.                        ptr% = ptr% - 1
  7064.                    END IF
  7065.  
  7066.                CASE RIGHTARROW
  7067.                    IF ptr% < length% - 1 THEN
  7068.                        ptr% = ptr% + 1
  7069.                    END IF
  7070.  
  7071.                CASE ENTER
  7072.                    exitCode% = 0
  7073.                    quit% = TRUE
  7074.  
  7075.                CASE HOME
  7076.                    ptr% = 0
  7077.  
  7078.                CASE ENDKEY
  7079.                    ptr% = length% - 1
  7080.  
  7081.                CASE CTRLRIGHTARROW
  7082.                    DO UNTIL MID$(a$, ptr% + 1, 1) = " " OR ptr% = length% - 1
  7083.                        ptr% = ptr% + 1
  7084.                    LOOP
  7085.                    DO UNTIL MID$(a$, ptr% + 1, 1) <> " " OR ptr% = length% - 1
  7086.                        ptr% = ptr% + 1
  7087.                    LOOP
  7088.  
  7089.                CASE CTRLLEFTARROW
  7090.                    DO UNTIL MID$(a$, ptr% + 1, 1) = " " OR ptr% = 0
  7091.                        ptr% = ptr% - 1
  7092.                    LOOP
  7093.                    DO UNTIL MID$(a$, ptr% + 1, 1) <> " " OR ptr% = 0
  7094.                        ptr% = ptr% - 1
  7095.                    LOOP
  7096.                    DO UNTIL MID$(a$, ptr% + 1, 1) = " " OR ptr% = 0
  7097.                        ptr% = ptr% - 1
  7098.                    LOOP
  7099.                    IF ptr% THEN
  7100.                        ptr% = ptr% + 1
  7101.                    END IF
  7102.  
  7103.                CASE CTRLY
  7104.                    a$ = SPACE$(length%)
  7105.                    ptr% = 0
  7106.  
  7107.                CASE CTRLQ
  7108.                    ctrlQflag% = TRUE
  7109.  
  7110.                CASE ESCAPE
  7111.                    a$ = original$
  7112.                    ptr% = 0
  7113.                    insert% = TRUE
  7114.  
  7115.                CASE IS > 255
  7116.                    SOUND 999, 1
  7117.  
  7118.                CASE IS < 32
  7119.                    SOUND 999, 1
  7120.  
  7121.                CASE ELSE
  7122.  
  7123.                  ' Convert key code to character string
  7124.                    kee$ = CHR$(keyNumber%)
  7125.  
  7126.                  ' Insert or overstrike
  7127.                    IF insert% THEN
  7128.                        a$ = LEFT$(a$, ptr%) + kee$ + MID$(a$, ptr% + 1)
  7129.                        a$ = LEFT$(a$, length%)
  7130.                    ELSE
  7131.                        IF ptr% < length% THEN
  7132.                            MID$(a$, ptr% + 1, 1) = kee$
  7133.                        END IF
  7134.                    END IF
  7135.  
  7136.                  ' Are we up against the wall?
  7137.                    IF ptr% < length% THEN
  7138.                        ptr% = ptr% + 1
  7139.                    ELSE
  7140.                        SOUND 999, 1
  7141.                    END IF
  7142.  
  7143.                  ' Special check for Ctrl-Q-Y (del to end of line)
  7144.                    IF kee$ = "y" AND ctrlQflag% THEN
  7145.                        IF ptr% <= length% THEN
  7146.                            sp% = length% - ptr% + 1
  7147.                            MID$(a$, ptr%, sp%) = SPACE$(sp%)
  7148.                            ptr% = ptr% - 1
  7149.                        END IF
  7150.                    END IF
  7151.  
  7152.                  ' Clear out the Ctrl-Q signal
  7153.                    ctrlQflag% = FALSE
  7154.  
  7155.                END SELECT
  7156.  
  7157.            LOOP UNTIL quit%
  7158.  
  7159.        END SUB
  7160.    ──────────────────────────────────────────────────────────────────────────
  7161.  
  7162.  
  7163.  Subprogram: FormatTwo
  7164.  
  7165.    Formats text lines to a given maximum length. The value of col% is used to
  7166.    find a point in a$ where a$ can be split into two strings between words.
  7167.    The length of the returned a$ will be less than or equal to col%, and the
  7168.    rest of the original a$ will be returned in b$.
  7169.  
  7170.    Notice that repeated calls to this subprogram can format an entire
  7171.    paragraph of text. An example of this is shown in the subprogram EditBox.
  7172.  
  7173.    ──────────────────────────────────────────────────────────────────────────
  7174.      ' ************************************************
  7175.      ' **  Name:          FormatTwo                  **
  7176.      ' **  Type:          Subprogram                 **
  7177.      ' **  Module:        EDIT.BAS                   **
  7178.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7179.      ' ************************************************
  7180.      '
  7181.      ' Splits a string into two strings between words,
  7182.      ' and with spaces padded to the first string to bring it to
  7183.      ' length, col%.
  7184.      '
  7185.      ' EXAMPLE OF USE:  FormatTwo a$, b$, col%
  7186.      ' PARAMETERS:      a$         String to be split
  7187.      '                  b$         Returns the tail of the string
  7188.      '                  col%       Maximum length of a$ after being split
  7189.      ' VARIABLES:       ptr%       Pointer to split point in a$
  7190.      ' MODULE LEVEL
  7191.      '   DECLARATIONS:  DECLARE SUB FormatTwo (a$, b$, col%)
  7192.      '
  7193.        SUB FormatTwo (a$, b$, col%) STATIC
  7194.  
  7195.          ' Be sure string is long enough
  7196.            a$ = a$ + SPACE$(col%)
  7197.  
  7198.          ' Look for rightmost space
  7199.            ptr% = col% + 1
  7200.            DO WHILE MID$(a$, ptr%, 1) <> " " AND ptr% > 1
  7201.                ptr% = ptr% - 1
  7202.            LOOP
  7203.  
  7204.          ' Do the split
  7205.            IF ptr% = 1 THEN
  7206.                b$ = MID$(a$, col% + 1)
  7207.                a$ = LEFT$(a$, col%)
  7208.            ELSE
  7209.                b$ = MID$(a$, ptr% + 1)
  7210.                a$ = LEFT$(a$, ptr% - 1)
  7211.            END IF
  7212.  
  7213.          ' Pad the first string with spaces to length col%
  7214.            a$ = LEFT$(a$ + SPACE$(col%), col%)
  7215.  
  7216.          ' Trim extra spaces from end of second string
  7217.            b$ = RTRIM$(b$)
  7218.  
  7219.        END SUB
  7220.    ──────────────────────────────────────────────────────────────────────────
  7221.  
  7222.  
  7223.  Subprogram: InsertCharacter
  7224.  
  7225.    Inserts a character into the array of text being maintained by the
  7226.    EditBox subprogram. While in Insert mode, the EditBox subprogram calls
  7227.    InsertCharacter. The character insertion is simple enough, but this
  7228.    subprogram also handles the chore of performing automatic wordwrap and
  7229.    formatting.
  7230.  
  7231.    This task of character insertion could have been performed in the
  7232.    EditBox subprogram, but breaking the code out into a separate subprogram
  7233.    makes it much easier to isolate this task from the others. One great
  7234.    advantage of QuickBASIC is the ability to break complex programming tasks
  7235.    into smaller, more manageable tasks.
  7236.  
  7237.    ──────────────────────────────────────────────────────────────────────────
  7238.      ' ************************************************
  7239.      ' **  Name:          InsertCharacter            **
  7240.      ' **  Type:          Subprogram                 **
  7241.      ' **  Module:        EDIT.BAS                   **
  7242.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7243.      ' ************************************************
  7244.      '
  7245.      ' Handles the task of inserting a character
  7246.      ' for the EditBox subprogram.
  7247.      '
  7248.      ' EXAMPLE OF USE:  InsertCharacter x$(), kee$, rp%, cp%, wide%, high%
  7249.      ' PARAMETERS:      x$()       Array in EditBox where character is to be
  7250.      '                             inserted
  7251.      '                  kee$       Character to be inserted
  7252.      '                  rp%        Row location of insert
  7253.      '                  cp%        Column location of insert
  7254.      '                  wide%      Width of rectangular area being edited
  7255.      '                  high%      Height of rectangular area being edited
  7256.      ' VARIABLES:       dum$       Marker character
  7257.      '                  b$         String from array at insertion point
  7258.      '                  c$         Right part of string at insertion point
  7259.      '                  i%         Looping index
  7260.      '                  ds%        Position in string of double spaces
  7261.      ' MODULE LEVEL
  7262.      '   DECLARATIONS:  DECLARE SUB InsertCharacter (x$(), kee$, cp%, rp%,
  7263.      '                                               wide%, high%)
  7264.      '
  7265.        SUB InsertCharacter (x$(), kee$, rp%, cp%, wide%, high%) STATIC
  7266.  
  7267.          ' First, insert a dummy character as a marker
  7268.            dum$ = CHR$(255)
  7269.            b$ = LEFT$(x$(rp%), cp% - 1)
  7270.            c$ = MID$(x$(rp%), cp%)
  7271.            b$ = b$ + dum$ + c$
  7272.  
  7273.          ' If end of string is a space, then drop it
  7274.            IF RIGHT$(b$, 1) = " " THEN
  7275.                x$(rp%) = LEFT$(b$, wide%)
  7276.  
  7277.              ' Otherwise, need to adjust the lines
  7278.            ELSE
  7279.  
  7280.              ' If not in the last line, then tack them all together
  7281.                IF rp% < high% THEN
  7282.                    FOR i% = rp% + 1 TO high%
  7283.                        b$ = b$ + " " + x$(i%)
  7284.                    NEXT i%
  7285.                END IF
  7286.  
  7287.              ' Trim both ends
  7288.                b$ = LTRIM$(RTRIM$(b$))
  7289.  
  7290.              ' Remove all double spaces
  7291.                ds% = INSTR(b$, "  ")
  7292.                DO WHILE ds%
  7293.                    b$ = LEFT$(b$, ds% - 1) + MID$(b$, ds% + 1)
  7294.                    ds% = INSTR(b$, "  ")
  7295.                LOOP
  7296.  
  7297.              ' Reformat the lines
  7298.                FOR i% = rp% TO high%
  7299.                    FormatTwo b$, c$, wide%
  7300.                    x$(i%) = b$
  7301.                    b$ = c$
  7302.                NEXT i%
  7303.  
  7304.            END IF
  7305.  
  7306.          ' Find out where that dummy character is now
  7307.            FOR rp% = 1 TO high%
  7308.                cp% = INSTR(x$(rp%), dum$)
  7309.                IF cp% THEN
  7310.                    EXIT FOR
  7311.                END IF
  7312.            NEXT rp%
  7313.  
  7314.          ' Replace the dummy character with the keystroke character
  7315.            IF cp% THEN
  7316.                MID$(x$(rp%), cp%, 1) = kee$
  7317.            END IF
  7318.  
  7319.          ' Increment the cursor location
  7320.            IF cp% < wide% + 1 THEN
  7321.                cp% = cp% + 1
  7322.            ELSE
  7323.                IF rp% < high% THEN
  7324.                    cp% = 1
  7325.                    rp% = rp% + 1
  7326.                END IF
  7327.            END IF
  7328.  
  7329.        END SUB
  7330.    ──────────────────────────────────────────────────────────────────────────
  7331.  
  7332.  
  7333.  
  7334.  ────────────────────────────────────────────────────────────────────────────
  7335.  ERROR
  7336.  
  7337.    The ERROR toolbox contains a single subprogram that displays an error
  7338.    message in a box. If you have a color monitor, you can make the display
  7339.    quite eye-catching. In this example, the message is yellow on a red
  7340.    background.
  7341.  
  7342.    Name                     Type    Description
  7343.    ──────────────────────────────────────────────────────────────────────────
  7344.    ERROR.BAS                       Demo module
  7345.    ErrorMessage            Sub     Error message display
  7346.    ──────────────────────────────────────────────────────────────────────────
  7347.  
  7348.  
  7349.  Demo Module: ERROR
  7350.  
  7351.    ──────────────────────────────────────────────────────────────────────────
  7352.      ' ************************************************
  7353.      ' **  Name:          ERROR                      **
  7354.      ' **  Type:          Toolbox                    **
  7355.      ' **  Module:        ERROR.BAS                  **
  7356.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7357.      ' ************************************************
  7358.      ' USAGE:           No command line parameters
  7359.      ' .MAK FILE:       (none)
  7360.      ' PARAMETERS:      (none)
  7361.      ' VARIABLES:       (none)
  7362.  
  7363.      ' Subprogram
  7364.        DECLARE SUB ErrorMessage (message$)
  7365.  
  7366.      ' Demonstrate the subprogram
  7367.  
  7368.        ErrorMessage "This is a sample message for ErrorMessage"
  7369.    ──────────────────────────────────────────────────────────────────────────
  7370.  
  7371.  
  7372.  Subprogram: ErrorMessage
  7373.  
  7374.    Provides a convenient, noticeable way to display error messages.
  7375.  
  7376.    QuickBASIC has a built-in mechanism for terminating a program when a
  7377.    serious error occurs. For example, if you try to divide by 0, the program
  7378.    immediately halts and displays the message Division by zero.
  7379.  
  7380.    In other situations, you might want to terminate a program because of a
  7381.    serious error that QuickBASIC would otherwise let pass. One approach in
  7382.    such a situation would be to use QuickBASIC's ERROR n% statement. This
  7383.    works fine, but unless one of the built-in error messages happens to fit
  7384.    the given situation, you're stuck with the default message Unprintable
  7385.    error, which sounds ghastly.
  7386.  
  7387.    A second approach to terminating a program in a controlled way would be to
  7388.    print your own descriptive error message and then follow with the SYSTEM
  7389.    statement. In many cases this technique is sufficient, but it's preferable
  7390.    to present a more polished, eye-catching display.
  7391.  
  7392.    This subprogram lets you systematically display your own error messages in
  7393.    a unique error-message window, just before terminating and returning to
  7394.    MS-DOS. The display──in this example, a red background and bright yellow
  7395.    message──immediately lets you know that a serious error has been detected.
  7396.  
  7397.    The table of color-defining constants in this subprogram can be useful in
  7398.    any program where you use the COLOR statement. A statement such as COLOR
  7399.    YELLOW, RED is much more descriptive than the equivalent COLOR 23, 4. It
  7400.    also makes programming easier because you don't have to remember or look
  7401.    up the numbers for the various colors.
  7402.  
  7403.    ──────────────────────────────────────────────────────────────────────────
  7404.      ' ************************************************
  7405.      ' **  Name:          ErrorMessage               **
  7406.      ' **  Type:          Subprogram                 **
  7407.      ' **  Module:        ERROR.BAS                  **
  7408.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7409.      ' ************************************************
  7410.      '
  7411.      ' Displays an error message and then exits to the system.
  7412.      '
  7413.      ' EXAMPLE OF USE: ErrorMessage "This is a sample message for ErrorMessage
  7414.      ' PARAMETERS:     message$         String to be displayed in the error bo
  7415.      ' VARIABLES:      lm%              Length of message$ during processing
  7416.      '                 col%             Screen character column for left edge
  7417.      '                                  of error box
  7418.      ' MODULE LEVEL
  7419.      '  DECLARATIONS:  DECLARE SUB ErrorMessage (message$)
  7420.      '
  7421.        SUB ErrorMessage (message$) STATIC
  7422.  
  7423.          ' Define color constants
  7424.            CONST BLACK = 0
  7425.            CONST BLUE = 1
  7426.            CONST GREEN = 2
  7427.            CONST CYAN = 3
  7428.            CONST RED = 4
  7429.            CONST MAGENTA = 5
  7430.            CONST BROWN = 6
  7431.            CONST WHITE = 7
  7432.            CONST BRIGHT = 8
  7433.            CONST BLINK = 16
  7434.            CONST YELLOW = BROWN + BRIGHT
  7435.  
  7436.          ' Trim off spaces on each end of message
  7437.            message$ = LTRIM$(RTRIM$(message$))
  7438.  
  7439.          ' Make message length an odd number
  7440.            IF LEN(message$) MOD 2 = 0 THEN
  7441.                message$ = message$ + " "
  7442.            END IF
  7443.  
  7444.          ' Minimum length of message is 9 characters
  7445.            DO WHILE LEN(message$) < 9
  7446.                message$ = " " + message$ + " "
  7447.            LOOP
  7448.  
  7449.          ' Maximum length of message is 75
  7450.            message$ = LEFT$(message$, 75)
  7451.  
  7452.          ' Initialization of display
  7453.            SCREEN 0
  7454.            WIDTH 80
  7455.            CLS
  7456.  
  7457.          ' Calculate screen locations
  7458.            lm% = LEN(message$)
  7459.            col% = 38 - lm% \ 2
  7460.  
  7461.          ' Create the error box
  7462.            COLOR RED + BRIGHT, RED
  7463.            LOCATE 9, col%
  7464.            PRINT CHR$(201); STRING$(lm% + 2, 205); CHR$(187)
  7465.            LOCATE 10, col%
  7466.            PRINT CHR$(186); SPACE$(lm% + 2); CHR$(186)
  7467.            LOCATE 11, col%
  7468.            PRINT CHR$(186); SPACE$(lm% + 2); CHR$(186)
  7469.            LOCATE 12, col%
  7470.            PRINT CHR$(200); STRING$(lm% + 2, 205); CHR$(188)
  7471.  
  7472.          ' The title
  7473.            COLOR CYAN + BRIGHT, RED
  7474.            LOCATE 10, 36
  7475.            PRINT "* ERROR *";
  7476.  
  7477.          ' The message$
  7478.            COLOR YELLOW, RED
  7479.            LOCATE 11, col% + 2
  7480.            PRINT message$;
  7481.  
  7482.          ' System will prompt for "any key"
  7483.            COLOR WHITE, BLACK
  7484.            LOCATE 22, 1
  7485.            SYSTEM
  7486.  
  7487.        END SUB
  7488.    ──────────────────────────────────────────────────────────────────────────
  7489.  
  7490.  
  7491.  
  7492.  ────────────────────────────────────────────────────────────────────────────
  7493.  FIGETPUT
  7494.  
  7495.    The FIGETPUT toolbox demonstrates the FileGet$ function and FilePut
  7496.    subprogram, routines that allow efficient binary-mode access to files up
  7497.    to 32767 bytes in length. Because each of these routines uses binary-file
  7498.    mode, an entire file can be read by one GET statement or written by one
  7499.    PUT statement. Any type of file containing no more than 32767 bytes can be
  7500.    read into or written from a QuickBASIC string variable by these routines.
  7501.    When reading an ASCII file, the FileGet$ function returns all lines of
  7502.    the file in one string. A carriage return/line feed pair of characters
  7503.    marks the separation of each line in the file.
  7504.  
  7505.    These routines can be useful for file-processing utility programs, such as
  7506.    byte-for-byte file comparisons, text searches, and file ciphering.
  7507.  
  7508.    To demonstrate the routines, the module-level code reads a copy of itself
  7509.    into a single string, converts all characters to uppercase, counts the
  7510.    occurrences of each letter of the alphabet, and saves the resulting string
  7511.    in a file named FIGETPUT.TST. For a meaningful character count save
  7512.    FIGETPUT.BAS in ASCII format.
  7513.  
  7514.    Name                     Type    Description
  7515.    ──────────────────────────────────────────────────────────────────────────
  7516.    FIGETPUT.BAS                    Demo module
  7517.    FileGet$                Func    Returns a string with contents of file
  7518.    FilePut                 Sub     Writes contents of string into binary
  7519.                                     file
  7520.    ──────────────────────────────────────────────────────────────────────────
  7521.  
  7522.  
  7523.  Demo Module: FIGETPUT
  7524.  
  7525.    ──────────────────────────────────────────────────────────────────────────
  7526.      ' ************************************************
  7527.      ' **  Name:          FIGETPUT                   **
  7528.      ' **  Type:          Toolbox                    **
  7529.      ' **  Module:        FIGETPUT.BAS               **
  7530.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7531.      ' ************************************************
  7532.      '
  7533.      ' Reads itself (FIGETPUT.BAS) into a string,
  7534.      ' converts characters to uppercase, counts occurrences of
  7535.      ' the characters "A" through "Z," and saves the
  7536.      ' result in a file named FIGETPUT.TST.
  7537.      '
  7538.      ' USAGE:           No command line parameters
  7539.      ' .MAK FILE:       (none)
  7540.      ' PARAMETERS:      filename
  7541.      ' VARIABLES:       count%()   Tally array for the 26 alpha characters
  7542.      '                  fileName$  Name of file to be processed
  7543.      '                  a$         Contents of the file
  7544.      '                  i%         Looping index
  7545.      '                  c%         ASCII value of each file byte
  7546.  
  7547.      ' Functions
  7548.        DECLARE FUNCTION FileGet$ (fileName$)
  7549.  
  7550.      ' Subprograms
  7551.        DECLARE SUB FilePut (fileName$, a$)
  7552.  
  7553.      ' Dimension array of counts for each ASCII code "A" to "Z"
  7554.        DIM count%(65 TO 90)
  7555.  
  7556.      ' Read in the file (must be no greater than 32767 bytes long)
  7557.        a$ = FileGet$("FIGETPUT.BAS")
  7558.  
  7559.      ' Convert to uppercase
  7560.        a$ = UCASE$(a$)
  7561.  
  7562.      ' Count the letters
  7563.        FOR i% = 1 TO LEN(a$)
  7564.            c% = ASC(MID$(a$, i%, 1))
  7565.            IF c% >= 65 AND c% <= 90 THEN
  7566.                count%(c%) = count%(c%) + 1
  7567.            END IF
  7568.        NEXT i%
  7569.  
  7570.      ' Output the results
  7571.        CLS
  7572.        PRINT "Alphabetic character count for FIGETPUT.BAS"
  7573.        PRINT
  7574.        FOR i% = 65 TO 90
  7575.            PRINT CHR$(i%); " -"; count%(i%),
  7576.        NEXT i%
  7577.  
  7578.      ' Write out the new file
  7579.        FilePut "FIGETPUT.TST", a$
  7580.  
  7581.      ' All done
  7582.        END
  7583.    ──────────────────────────────────────────────────────────────────────────
  7584.  
  7585.  
  7586.  Function: FileGet$
  7587.  
  7588.    Uses the binary file mode to read the contents of any MS-DOS file into a
  7589.    string variable. The file length must be fewer than 32768 bytes to fit in
  7590.    one string. If you try to read a larger file, an error message is
  7591.    displayed, and the program halts.
  7592.  
  7593.    ──────────────────────────────────────────────────────────────────────────
  7594.      ' ************************************************
  7595.      ' **  Name:          FileGet$                   **
  7596.      ' **  Type:          Function                   **
  7597.      ' **  Module:        FIGETPUT.BAS               **
  7598.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7599.      ' ************************************************
  7600.      '
  7601.      ' Returns a string containing the contents of a file.
  7602.      ' Maximum file length is 32767 bytes.
  7603.      '
  7604.      ' EXAMPLE OF USE:  a$ = FileGet$(fileName$)
  7605.      ' PARAMETERS:      fileName$     Name of file to be accessed
  7606.      ' VARIABLES:       fileNumber    Next available free file number
  7607.      '                  length&       Length of file
  7608.      '                  a$            String for binary read of file
  7609.      ' MODULE LEVEL
  7610.      '   DECLARATIONS:  DECLARE FUNCTION FileGet$ (fileName$)
  7611.      '
  7612.         FUNCTION FileGet$ (fileName$) STATIC
  7613.            fileNumber = FREEFILE
  7614.            OPEN fileName$ FOR BINARY AS #fileNumber
  7615.            length& = LOF(fileNumber)
  7616.            IF length& <= 32767 THEN
  7617.                a$ = SPACE$(length&)
  7618.                GET #fileNumber, , a$
  7619.                FileGet$ = a$
  7620.                a$ = ""
  7621.            ELSE
  7622.                PRINT "FileGet$()... file too large"
  7623.                SYSTEM
  7624.            END IF
  7625.            CLOSE #fileNumber
  7626.        END FUNCTION
  7627.    ──────────────────────────────────────────────────────────────────────────
  7628.  
  7629.  
  7630.  Subprogram: FilePut
  7631.  
  7632.    Writes the contents of any string variable to a file using binary file
  7633.    mode. The biggest file that you can create in this way is 32767 bytes
  7634.    because that's the longest string you can build.
  7635.  
  7636.    ──────────────────────────────────────────────────────────────────────────
  7637.      ' ************************************************
  7638.      ' **  Name:          FilePut                    **
  7639.      ' **  Type:          Subprogram                 **
  7640.      ' **  Module:        FIGETPUT.BAS               **
  7641.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7642.      ' ************************************************
  7643.      '
  7644.      ' Writes contents of a$ into a binary file named fileName$.
  7645.      '
  7646.      ' EXAMPLE OF USE:  FilePut fileName$, a$
  7647.      ' PARAMETERS:      fileName$  Name of file to be written
  7648.      '                  a$         Bytes to be placed in the file
  7649.      ' VARIABLES:       fileNumber Next available free file number
  7650.      ' MODULE LEVEL
  7651.      '   DECLARATIONS:  DECLARE SUB FilePut (fileName$, a$)
  7652.      '
  7653.        SUB FilePut (fileName$, a$) STATIC
  7654.  
  7655.          ' Find available file number
  7656.            fileNumber = FREEFILE
  7657.  
  7658.          ' Truncate any previous contents
  7659.            OPEN fileName$ FOR OUTPUT AS #fileNumber
  7660.            CLOSE #fileNumber
  7661.  
  7662.          ' Write string to file
  7663.            OPEN fileName$ FOR BINARY AS #fileNumber
  7664.            PUT #fileNumber, , a$
  7665.  
  7666.          ' All done
  7667.            CLOSE #fileNumber
  7668.  
  7669.        END SUB
  7670.    ──────────────────────────────────────────────────────────────────────────
  7671.  
  7672.  
  7673.  
  7674.  ────────────────────────────────────────────────────────────────────────────
  7675.  FILEINFO
  7676.  
  7677.    The FILEINFO toolbox contains subprograms that obtain directory
  7678.    information about files. Basically, this program mimics the MS-DOS DIR
  7679.    command or the QuickBASIC FILES command.
  7680.  
  7681.    As set up, this program finds normal file entries. You can change the
  7682.    FILEATTRIBUTE constant to access other types of files. Refer to the CONST
  7683.    statements that define the various file attribute bits.
  7684.  
  7685.    The starting path$ for the search is set to the current directory, but you
  7686.    can change the path$ assignment to search any desired drive or directory.
  7687.  
  7688.    Name                     Type    Description
  7689.    ──────────────────────────────────────────────────────────────────────────
  7690.    FILEINFO.BAS                    Demo module
  7691.    FindFirstFile           Sub     Finds first file that matches parameter
  7692.    FindNextFile            Sub     Locates next file that matches parameter
  7693.    GetFileData             Sub     Extracts file directory information
  7694.    ──────────────────────────────────────────────────────────────────────────
  7695.  
  7696.  
  7697.  Demo Module: FILEINFO
  7698.  
  7699.    ──────────────────────────────────────────────────────────────────────────
  7700.      ' ************************************************
  7701.      ' **  Name:          FILEINFO                   **
  7702.      ' **  Type:          Toolbox                    **
  7703.      ' **  Module:        FILEINFO.BAS               **
  7704.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7705.      ' ************************************************
  7706.      '
  7707.      ' Collection of subprograms and functions for accessing
  7708.      ' directory information about files.
  7709.      '
  7710.      ' USAGE:           No command line parameters
  7711.      ' REQUIREMENTS:    MIXED.QLB/.LIB
  7712.      ' .MAK FILE:       (none)
  7713.      ' PARAMETERS:      (none)
  7714.      ' VARIABLES:       path$      Path to files for gathering directory
  7715.      '                             information; wildcard characters accepted
  7716.      '                  dta$       Disk transfer area buffer string
  7717.      '                  result%    Code returned as result of directory search
  7718.      '                  file       Structure of type FileDataType
  7719.      '                  n%         File count
  7720.  
  7721.      ' File search attribute bits
  7722.        CONST ISNORMAL = 0
  7723.        CONST ISREADONLY = 1
  7724.        CONST ISHIDDEN = 2
  7725.        CONST ISSYSTEM = 4
  7726.        CONST ISVOLUMELABEL = 8
  7727.        CONST ISSUBDIRECTORY = 16
  7728.        CONST ISARCHIVED = 32
  7729.  
  7730.      ' Here we'll search for normal files and subdirectories
  7731.        CONST FILEATTRIBUTE = ISNORMAL + ISSUBDIRECTORY
  7732.  
  7733.        TYPE RegTypeX
  7734.            ax    AS INTEGER
  7735.            bx    AS INTEGER
  7736.            cx    AS INTEGER
  7737.            dx    AS INTEGER
  7738.            bp    AS INTEGER
  7739.            si    AS INTEGER
  7740.            di    AS INTEGER
  7741.            flags AS INTEGER
  7742.            ds    AS INTEGER
  7743.            es    AS INTEGER
  7744.        END TYPE
  7745.  
  7746.        TYPE FileDataType
  7747.            finame    AS STRING * 12
  7748.            year      AS INTEGER
  7749.            month     AS INTEGER
  7750.            day       AS INTEGER
  7751.            hour      AS INTEGER
  7752.            minute    AS INTEGER
  7753.            second    AS INTEGER
  7754.            attribute AS INTEGER
  7755.            size      AS LONG
  7756.        END TYPE
  7757.  
  7758.      ' Subprograms
  7759.        DECLARE SUB INTERRUPTX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
  7760.        DECLARE SUB FindFirstFile (path$, dta$, result%)
  7761.        DECLARE SUB FindNextFile (dta$, result%)
  7762.        DECLARE SUB GetFileData (dta$, file AS FileDataType)
  7763.  
  7764.      ' Data structures
  7765.        DIM file AS FileDataType
  7766.  
  7767.      ' For demonstration purposes, list current directory
  7768.        CLS
  7769.        path$ = "*.*"
  7770.  
  7771.      ' Always start by finding the first match
  7772.        FindFirstFile path$, dta$, result%
  7773.  
  7774.      ' Check that the path$ got us off to a good start
  7775.        IF result% THEN
  7776.            PRINT "Error: FindFirstFile - found no match for path$"
  7777.            SYSTEM
  7778.        END IF
  7779.  
  7780.      ' List all the files in this directory
  7781.        DO
  7782.            IF n% MOD 19 = 0 THEN
  7783.                CLS
  7784.                PRINT TAB(4); "File"; TAB(18); "Date"; TAB(29); "Time";
  7785.                PRINT TAB(39); "Size"; TAB(48); "Attributes"
  7786.                PRINT
  7787.            END IF
  7788.            GetFileData dta$, file
  7789.            PRINT file.finame;
  7790.            PRINT USING "  ##/##/####"; file.month, file.day, file.year;
  7791.            PRINT USING "  ##:##:##"; file.hour, file.minute, file.second;
  7792.            PRINT USING "  ########"; file.size;
  7793.            PRINT USING "  &"; RIGHT$("0" + HEX$(file.attribute), 2)
  7794.            PRINT "     &H";
  7795.            PRINT USING "&"; RIGHT$("0" + HEX$(file.attribute), 2)
  7796.            n% = n% + 1
  7797.            FindNextFile dta$, result%
  7798.            IF n% MOD 19 = 0 THEN
  7799.                PRINT
  7800.                PRINT "Press any key to continue"
  7801.                DO
  7802.                LOOP WHILE INKEY$ = ""
  7803.            END IF
  7804.        LOOP UNTIL result%
  7805.        PRINT
  7806.        PRINT n%; "files found"
  7807.    ──────────────────────────────────────────────────────────────────────────
  7808.  
  7809.  
  7810.  Subprogram: FindFirstFile
  7811.  
  7812.    Finds the first directory entry that matches a given path$. This
  7813.    subprogram is called once before FindNextFile is called numerous times to
  7814.    find the rest of the entries.
  7815.  
  7816.    The result of this file search is returned in the dta$ variable. Call the
  7817.    GetFileData subprogram to extract the information from the string.
  7818.  
  7819.    ──────────────────────────────────────────────────────────────────────────
  7820.      ' ************************************************
  7821.      ' **  Name:          FindFirstFile              **
  7822.      ' **  Type:          Subprogram                 **
  7823.      ' **  Module:        FILEINFO.BAS               **
  7824.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7825.      ' ************************************************
  7826.      '
  7827.      ' Finds first file that matches the path$.
  7828.      '
  7829.      ' EXAMPLE OF USE:  FindFirstFile path$, dta$, result%
  7830.      ' PARAMETERS:      path$      Complete path, including wildcard character
  7831.      '                             desired, for the directory search
  7832.      '                  dta$       Disk transfer area buffer space
  7833.      '                  result%    Returned result code for the search
  7834.      ' VARIABLES:       reg        Structure of type RegTypeX
  7835.      '                  thePath$   Null terminated version of path$
  7836.      '                  sgmt%      Current DTA address segment
  7837.      '                  ofst%      Current DTA address offset
  7838.      ' MODULE LEVEL
  7839.      '   DECLARATIONS:  File search attribute bits
  7840.      '                     CONST ISNORMAL = 0
  7841.      '                     CONST ISREADONLY = 1
  7842.      '                     CONST ISHIDDEN = 2
  7843.      '                     CONST ISSYSTEM = 4
  7844.      '                     CONST ISVOLUMELABEL = 8
  7845.      '                     CONST ISSUBDIRECTORY = 16
  7846.      '                     CONST ISARCHIVED = 32
  7847.      '
  7848.      '                     CONST FILEATTRIBUTE = ISNORMAL + ISSUBDIRECTORY
  7849.      '
  7850.      '                     TYPE RegTypeX
  7851.      '                        ax    AS INTEGER
  7852.      '                        bx    AS INTEGER
  7853.      '                        cx    AS INTEGER
  7854.      '                        dx    AS INTEGER
  7855.      '                        bp    AS INTEGER
  7856.      '                        si    AS INTEGER
  7857.      '                        di    AS INTEGER
  7858.      '                        flags AS INTEGER
  7859.      '                        ds    AS INTEGER
  7860.      '                        es    AS INTEGER
  7861.      '                     END TYPE
  7862.      '
  7863.      '   DECLARE SUB INTERRUPTX (intnum%, inreg AS RegTypeX, outreg AS RegType
  7864.      '   DECLARE SUB FindFirstFile (path$, dta$, result%)
  7865.      '
  7866.        SUB FindFirstFile (path$, dta$, result%) STATIC
  7867.  
  7868.          ' Initialization
  7869.            DIM reg AS RegTypeX
  7870.  
  7871.          ' The path must be a null terminated string
  7872.            thePath$ = path$ + CHR$(0)
  7873.  
  7874.          ' Get current DTA address
  7875.            reg.ax = &H2F00
  7876.            INTERRUPTX &H21, reg, reg
  7877.            sgmt% = reg.es
  7878.            ofst% = reg.bx
  7879.  
  7880.          ' Set dta address
  7881.            dta$ = SPACE$(43)
  7882.            reg.ax = &H1A00
  7883.            reg.ds = VARSEG(dta$)
  7884.            reg.dx = SADD(dta$)
  7885.            INTERRUPTX &H21, reg, reg
  7886.  
  7887.          ' Find first file match
  7888.            reg.ax = &H4E00
  7889.            reg.cx = FILEATTRIBUTE
  7890.            reg.ds = VARSEG(thePath$)
  7891.            reg.dx = SADD(thePath$)
  7892.            INTERRUPTX &H21, reg, reg
  7893.  
  7894.          ' The carry flag tells if a file was found or not
  7895.            result% = reg.flags AND 1
  7896.  
  7897.          ' Reset the original DTA
  7898.            reg.ax = &H1A00
  7899.            reg.ds = sgmt%
  7900.            reg.dx = ofst%
  7901.            INTERRUPTX &H21, reg, reg
  7902.  
  7903.        END SUB
  7904.    ──────────────────────────────────────────────────────────────────────────
  7905.  
  7906.  
  7907.  Subprogram: FindNextFile
  7908.  
  7909.    Continues the search for file directory entries after the FindFirstFile
  7910.    subprogram was called once. This subprogram is usually called repeatedly
  7911.    until all files that match the original path$ are found. The value of
  7912.    result% is 0 until the last file is found.
  7913.  
  7914.    The dta$ variable carries the important information about the search from
  7915.    call to call of this subprogram. Be careful not to alter its contents
  7916.    between calls to this routine. To extract details about each file's
  7917.    directory entry, pass dta$ to the subprogram GetFileData.
  7918.  
  7919.    ──────────────────────────────────────────────────────────────────────────
  7920.      ' ************************************************
  7921.      ' **  Name:          FindNextFile               **
  7922.      ' **  Type:          Subprogram                 **
  7923.      ' **  Module:        FILEINFO.BAS               **
  7924.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  7925.      ' ************************************************
  7926.      '
  7927.      ' Locates next file. FindFirstFile must be called
  7928.      ' before this subprogram is called.
  7929.      '
  7930.      ' EXAMPLE OF USE: FindNextFile dta$, result%
  7931.      ' PARAMETERS:      dta$       Previously filled-in Disk Transfer Area
  7932.      '                             buffer string
  7933.      '                  result%    Result code for the search
  7934.      ' VARIABLES:       reg        Structure of type RegTypeX
  7935.      '                  thePath$   Null terminated version of path$
  7936.      '                  sgmt%      Current DTA address segment
  7937.      '                  ofst%      Current DTA address offset
  7938.      ' MODULE LEVEL
  7939.      '   DECLARATIONS:  CONST ISNORMAL = 0
  7940.      '                  CONST ISREADONLY = 1
  7941.      '                  CONST ISHIDDEN = 2
  7942.      '                  CONST ISSYSTEM = 4
  7943.      '                  CONST ISVOLUMELABEL = 8
  7944.      '                  CONST ISSUBDIRECTORY = 16
  7945.      '                  CONST ISARCHIVED = 32
  7946.      '
  7947.      '                  CONST FILEATTRIBUTE = ISNORMAL + ISSUBDIRECTORY
  7948.      '
  7949.      '                     TYPE RegTypeX
  7950.      '                        ax    AS INTEGER
  7951.      '                        bx    AS INTEGER
  7952.      '                        cx    AS INTEGER
  7953.      '                        dx    AS INTEGER
  7954.      '                        bp    AS INTEGER
  7955.      '                        si    AS INTEGER
  7956.      '                        di    AS INTEGER
  7957.      '                        flags AS INTEGER
  7958.      '                        ds    AS INTEGER
  7959.      '                        es    AS INTEGER
  7960.      '                     END TYPE
  7961.      '
  7962.      '   DECLARE SUB INTERRUPTX (intnum%, inreg AS RegTypeX, outreg AS RegType
  7963.      '   DECLARE SUB FindNextFile (dta$, result%)
  7964.      '
  7965.        SUB FindNextFile (dta$, result%) STATIC
  7966.  
  7967.          ' Initialization
  7968.            DIM reg AS RegTypeX
  7969.  
  7970.          ' Be sure dta$ was built (FindFirstFile should have been called)
  7971.            IF LEN(dta$) <> 43 THEN
  7972.                result% = 2
  7973.                EXIT SUB
  7974.            END IF
  7975.  
  7976.          ' Get current DTA address
  7977.            reg.ax = &H2F00
  7978.            INTERRUPTX &H21, reg, reg
  7979.            sgmt% = reg.es
  7980.            ofst% = reg.bx
  7981.  
  7982.          ' Set dta address
  7983.            reg.ax = &H1A00
  7984.            reg.ds = VARSEG(dta$)
  7985.            reg.dx = SADD(dta$)
  7986.            INTERRUPTX &H21, reg, reg
  7987.  
  7988.          ' Find next file match
  7989.            reg.ax = &H4F00
  7990.            reg.cx = FILEATTRIBUTE
  7991.            reg.ds = VARSEG(thePath$)
  7992.            reg.dx = SADD(thePath$)
  7993.            INTERRUPTX &H21, reg, reg
  7994.  
  7995.          ' The carry flag tells whether a file was found or not
  7996.            result% = reg.flags AND 1
  7997.  
  7998.          ' Reset the original DTA
  7999.            reg.ax = &H1A00
  8000.            reg.ds = sgmt%
  8001.            reg.dx = ofst%
  8002.            INTERRUPTX &H21, reg, reg
  8003.  
  8004.        END SUB
  8005.    ──────────────────────────────────────────────────────────────────────────
  8006.  
  8007.  
  8008.  Subprogram: GetFileData
  8009.  
  8010.    Extracts the information about a file's directory entry from the variable
  8011.    dta$ passed back from calls to FindFirstFile and FindNextFile. The
  8012.    information is returned in the data structure of type FileDataType and
  8013.    includes the date and time of the last file update, the filename, the file
  8014.    size, and the file attribute byte.
  8015.  
  8016.    ──────────────────────────────────────────────────────────────────────────
  8017.      ' ************************************************
  8018.      ' **  Name:          GetFileData                **
  8019.      ' **  Type:          Subprogram                 **
  8020.      ' **  Module:        FILEINFO.BAS               **
  8021.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8022.      ' ************************************************
  8023.      '
  8024.      ' Extracts the file directory information from a Disk
  8025.      ' Transfer Area (dta$) that has been filled in by a
  8026.      ' call to either FindFirstFile or FindNextFile.
  8027.      '
  8028.      ' EXAMPLE OF USE:  GetFileData dta$, file
  8029.      ' PARAMETERS:      dta$       Disk Transfer Area buffer string passed bac
  8030.      '                             either FindFirstFile or FindNextFile
  8031.      ' VARIABLES:       tim&       Time stamp of the file
  8032.      '                  dat&       Date stamp of the file
  8033.      '                  f$         Filename during extraction
  8034.      ' MODULE LEVEL
  8035.      '   DECLARATIONS:  TYPE FileDataType
  8036.      '                     finame    AS STRING * 12
  8037.      '                     year      AS INTEGER
  8038.      '                     month     AS INTEGER
  8039.      '                     day       AS INTEGER
  8040.      '                     hour      AS INTEGER
  8041.      '                     minute    AS INTEGER
  8042.      '                     second    AS INTEGER
  8043.      '                     attribute AS INTEGER
  8044.      '                     size      AS LONG
  8045.      '                  END TYPE
  8046.      '
  8047.      '                  DECLARE SUB GetFileData (dta$, file AS FileDataType)
  8048.      '
  8049.        SUB GetFileData (dta$, file AS FileDataType) STATIC
  8050.  
  8051.            file.attribute = ASC(MID$(dta$, 22, 1))
  8052.            tim& = CVI(MID$(dta$, 23, 2))
  8053.            IF tim& < 0 THEN
  8054.                tim& = tim& + 65536
  8055.            END IF
  8056.            file.second = tim& AND &H1F
  8057.            file.minute = (tim& \ 32) AND &H3F
  8058.            file.hour = (tim& \ 2048) AND &H1F
  8059.            dat& = CVI(MID$(dta$, 25, 2))
  8060.            file.day = dat& AND &H1F
  8061.            file.month = (dat& \ 32) AND &HF
  8062.            file.year = ((dat& \ 512) AND &H1F) + 1980
  8063.            file.size = CVL(MID$(dta$, 27, 4))
  8064.            f$ = MID$(dta$, 31) + CHR$(0)
  8065.            file.finame = LEFT$(f$, INSTR(f$, CHR$(0)) - 1)
  8066.  
  8067.        END SUB
  8068.    ──────────────────────────────────────────────────────────────────────────
  8069.  
  8070.  
  8071.  
  8072.  ────────────────────────────────────────────────────────────────────────────
  8073.  FRACTION
  8074.  
  8075.    The FRACTION toolbox is a set of subprograms and functions for working
  8076.    with fractions. The fractions are handled as data structures, defined by
  8077.    the TYPE statement in the module-level code. This effectively allows
  8078.    fractions, comprising a pair of long integer numerator and denominator
  8079.    numbers, to be referenced as a new type of variable.
  8080.  
  8081.    The demo module displays examples of the LeastComMul& and
  8082.    GreatestComDiv& functions and then prompts you to enter fraction problems
  8083.    involving the addition, subtraction, multiplication, or division of two
  8084.    fractions. Enter the problems using the format displayed on screen. The
  8085.    results, reduced to lowest terms, will be displayed.
  8086.  
  8087.    Name                     Type    Description
  8088.    ──────────────────────────────────────────────────────────────────────────
  8089.    FRACTION.BAS                    Demo module
  8090.    Fraction2String$        Func    Converts type Fraction variable to a
  8091.                                     string
  8092.    FractionAdd             Sub     Adds two fractions and reduces
  8093.    FractionDiv             Sub     Divides two fractions and reduces
  8094.    FractionMul             Sub     Multiplies two fractions and reduces
  8095.    FractionReduce          Sub     Reduces fraction to lowest terms
  8096.    FractionSub             Sub     Subtracts two fractions and reduces
  8097.    GreatestComDiv&         Func    Returns greatest common divisor
  8098.    LeastComMul&            Func    Returns least common multiple
  8099.    SplitFractions          Sub     Parses fraction problem string
  8100.    String2Fraction         Sub     Converts a string to Fraction variable
  8101.    ──────────────────────────────────────────────────────────────────────────
  8102.  
  8103.  
  8104.  Demo Module: FRACTION
  8105.  
  8106.    ──────────────────────────────────────────────────────────────────────────
  8107.      ' ************************************************
  8108.      ' **  Name:          FRACTION                   **
  8109.      ' **  Type:          Toolbox                    **
  8110.      ' **  Module:        FRACTION.BAS               **
  8111.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8112.      ' ************************************************
  8113.      '
  8114.      ' Demonstrates a collection of functions and subprograms
  8115.      ' for working with fractions.
  8116.      '
  8117.      ' USAGE:           No command line parameters
  8118.      ' .MAK FILE:       (none)
  8119.      ' PARAMETERS:      (none)
  8120.      ' VARIABLES:       a          Structure of type Fraction
  8121.      '                  b          Structure of type Fraction
  8122.      '                  c          Structure of type Fraction
  8123.      '                  f$         Input string for fraction problems
  8124.      '                  fa$        First fraction in string format
  8125.      '                  fb$        Second fraction in string format
  8126.      '                  operator$  Function indicator
  8127.      '                  fc$        Resultant fraction in string output form
  8128.  
  8129.      ' Data structure definitions
  8130.        TYPE Fraction
  8131.            Num AS LONG
  8132.            Den AS LONG
  8133.        END TYPE
  8134.  
  8135.      ' Subprograms
  8136.        DECLARE SUB FractionReduce (a AS Fraction)
  8137.        DECLARE SUB String2Fraction (f$, a AS Fraction)
  8138.        DECLARE SUB FractionAdd (a AS Fraction, b AS Fraction, c AS Fraction)
  8139.        DECLARE SUB FractionDiv (a AS Fraction, b AS Fraction, c AS Fraction)
  8140.        DECLARE SUB FractionMul (a AS Fraction, b AS Fraction, c AS Fraction)
  8141.        DECLARE SUB FractionSub (a AS Fraction, b AS Fraction, c AS Fraction)
  8142.        DECLARE SUB SplitFractions (f$, fa$, operator$, fb$)
  8143.  
  8144.      ' Functions
  8145.        DECLARE FUNCTION Fraction2String$ (a AS Fraction)
  8146.        DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
  8147.        DECLARE FUNCTION LeastComMul& (n1&, n2&)
  8148.  
  8149.      ' Data structures
  8150.        DIM a AS Fraction
  8151.        DIM b AS Fraction
  8152.        DIM c AS Fraction
  8153.  
  8154.      ' Demonstrate the LeastComMul& function
  8155.        CLS
  8156.        PRINT "LeastComMul&(21&, 49&)    =", LeastComMul&(21&, 49&)
  8157.        PRINT
  8158.  
  8159.      ' Demonstrate the GreatestComDiv& function
  8160.        PRINT "GreatestComDiv&(21&, 49&) =", GreatestComDiv&(21&, 49&)
  8161.        PRINT
  8162.  
  8163.      ' Demonstrate the fraction routines
  8164.        DO
  8165.            PRINT
  8166.            PRINT "Enter a fraction problem, or simply press Enter"
  8167.            PRINT "Example: 2/3 + 4/5"
  8168.            PRINT
  8169.            LINE INPUT f$
  8170.            IF INSTR(f$, "/") = 0 THEN
  8171.                EXIT DO
  8172.            END IF
  8173.            SplitFractions f$, fa$, operator$, fb$
  8174.            String2Fraction fa$, a
  8175.            String2Fraction fb$, b
  8176.            SELECT CASE operator$
  8177.            CASE "+"
  8178.                FractionAdd a, b, c
  8179.            CASE "-"
  8180.                FractionSub a, b, c
  8181.            CASE "*"
  8182.                FractionMul a, b, c
  8183.            CASE "/"
  8184.                FractionDiv a, b, c
  8185.            CASE ELSE
  8186.                BEEP
  8187.            END SELECT
  8188.            fc$ = Fraction2String$(c)
  8189.            PRINT "Result (reduced to lowest terms) is "; fc$
  8190.        LOOP
  8191.    ──────────────────────────────────────────────────────────────────────────
  8192.  
  8193.  
  8194.  Function: Fraction2String$
  8195.  
  8196.    Returns a string representation of a fraction. The numerator and
  8197.    denominator values are converted to strings by the QuickBASIC STR$
  8198.    function, and a slash (/) is concatenated between them to form the
  8199.    resultant string.
  8200.  
  8201.    ──────────────────────────────────────────────────────────────────────────
  8202.      ' ************************************************
  8203.      ' **  Name:          Fraction2String$           **
  8204.      ' **  Type:          Function                   **
  8205.      ' **  Module:        FRACTION.BAS               **
  8206.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8207.      ' ************************************************
  8208.      '
  8209.      ' Converts a type Fraction variable to a string.
  8210.      '
  8211.      ' EXAMPLE OF USE:  fa$ = Fraction2String$(a)
  8212.      ' PARAMETERS:      a          Structure of type Fraction
  8213.      ' VARIABLES:       (none)
  8214.      ' MODULE LEVEL
  8215.      '   DECLARATIONS:  TYPE Fraction
  8216.      '                     Num AS LONG
  8217.      '                     Den AS LONG
  8218.      '                  END TYPE
  8219.      '
  8220.      '                  DECLARE FUNCTION Fraction2String$ (a AS Fraction)
  8221.      '
  8222.        FUNCTION Fraction2String$ (a AS Fraction) STATIC
  8223.            Fraction2String$ = STR$(a.Num) + "/" + STR$(a.Den)
  8224.        END FUNCTION
  8225.    ──────────────────────────────────────────────────────────────────────────
  8226.  
  8227.  
  8228.  Subprogram: FractionAdd
  8229.  
  8230.    Adds fraction a to fraction b, reduces the result to lowest terms, and
  8231.    returns the result in fraction c.
  8232.  
  8233.    ──────────────────────────────────────────────────────────────────────────
  8234.      ' ************************************************
  8235.      ' **  Name:          FractionAdd                **
  8236.      ' **  Type:          Subprogram                 **
  8237.      ' **  Module:        FRACTION.BAS               **
  8238.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8239.      ' ************************************************
  8240.      '
  8241.      ' Adds two fractions and reduces the result to lowest terms.
  8242.      '
  8243.      ' EXAMPLE OF USE:  FractionAdd a, b, c
  8244.      ' PARAMETERS:      a          First fraction to add
  8245.      '                  b          Second fraction to add
  8246.      '                  c          Resulting fraction
  8247.      ' VARIABLES:       (none)
  8248.      ' MODULE LEVEL
  8249.      '   DECLARATIONS:  TYPE Fraction
  8250.      '                     Num AS LONG
  8251.      '                     Den AS LONG
  8252.      '                  END TYPE
  8253.      '
  8254.      '     DECLARE SUB FractionReduce (a AS Fraction)
  8255.      '     DECLARE SUB FractionAdd (a AS Fraction, b AS Fraction, c AS Fractio
  8256.      '     DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
  8257.      '
  8258.        SUB FractionAdd (a AS Fraction, b AS Fraction, c AS Fraction)
  8259.            c.Num = a.Num * b.Den + a.Den * b.Num
  8260.            c.Den = a.Den * b.Den
  8261.            FractionReduce c
  8262.        END SUB
  8263.    ──────────────────────────────────────────────────────────────────────────
  8264.  
  8265.  
  8266.  Subprogram: FractionDiv
  8267.  
  8268.    Divides fraction b into fraction a, reduces the result to lowest terms,
  8269.    and returns the result in fraction c.
  8270.  
  8271.    ──────────────────────────────────────────────────────────────────────────
  8272.      ' ************************************************
  8273.      ' **  Name:          FractionDiv                **
  8274.      ' **  Type:          Subprogram                 **
  8275.      ' **  Module:        FRACTION.BAS               **
  8276.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8277.      ' ************************************************
  8278.      '
  8279.      ' Divides two fractions and reduces the result to
  8280.      ' lowest terms.
  8281.      '
  8282.      ' EXAMPLE OF USE:  FractionDiv a, b, c
  8283.      ' PARAMETERS:      a          First fraction
  8284.      '                  b          Fraction to divide into first
  8285.      '                  c          Resulting fraction
  8286.      ' VARIABLES:       (none)
  8287.      ' MODULE LEVEL
  8288.      '   DECLARATIONS:  TYPE Fraction
  8289.      '                     Num AS LONG
  8290.      '                     Den AS LONG
  8291.      '                  END TYPE
  8292.      '     DECLARE SUB FractionReduce (a AS Fraction)
  8293.      '     DECLARE SUB FractionDiv (a AS Fraction, b AS Fraction, c AS Fractio
  8294.      '     DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
  8295.      '
  8296.        SUB FractionDiv (a AS Fraction, b AS Fraction, c AS Fraction)
  8297.            c.Num = a.Num * b.Den
  8298.            c.Den = a.Den * b.Num
  8299.            FractionReduce c
  8300.        END SUB
  8301.    ──────────────────────────────────────────────────────────────────────────
  8302.  
  8303.  
  8304.  Subprogram: FractionMul
  8305.  
  8306.    Multiplies fraction a times fraction b, reduces the result to lowest
  8307.    terms, and returns the result in fraction c.
  8308.  
  8309.    ──────────────────────────────────────────────────────────────────────────
  8310.      ' ************************************************
  8311.      ' **  Name:          FractionMul                **
  8312.      ' **  Type:          Subprogram                 **
  8313.      ' **  Module:        FRACTION.BAS               **
  8314.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8315.      ' ************************************************
  8316.      '
  8317.      ' Multiplies two fractions and reduces the result to
  8318.      ' lowest terms.
  8319.      '
  8320.      ' EXAMPLE OF USE:  FractionMul a, b, c
  8321.      ' PARAMETERS:      a          First fraction to multiply
  8322.      '                  b          Second fraction to multiply
  8323.      '                  c          Resulting fraction
  8324.      ' VARIABLES:       (none)
  8325.      ' MODULE LEVEL
  8326.      '   DECLARATIONS:  TYPE Fraction
  8327.      '                     Num AS LONG
  8328.      '                     Den AS LONG
  8329.      '                  END TYPE
  8330.      '
  8331.      '     DECLARE SUB FractionReduce (a AS Fraction)
  8332.      '     DECLARE SUB FractionMul (a AS Fraction, b AS Fraction, c AS Fractio
  8333.      '     DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
  8334.      '
  8335.        SUB FractionMul (a AS Fraction, b AS Fraction, c AS Fraction)
  8336.            c.Num = a.Num * b.Num
  8337.            c.Den = a.Den * b.Den
  8338.            FractionReduce c
  8339.        END SUB
  8340.    ──────────────────────────────────────────────────────────────────────────
  8341.  
  8342.  
  8343.  Subprogram: FractionReduce
  8344.  
  8345.    Reduces a fraction to its lowest terms by dividing the numerator and
  8346.    denominator by their greatest common divisor.
  8347.  
  8348.    ──────────────────────────────────────────────────────────────────────────
  8349.      ' ************************************************
  8350.      ' **  Name:          FractionReduce             **
  8351.      ' **  Type:          Subprogram                 **
  8352.      ' **  Module:        FRACTION.BAS               **
  8353.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8354.      ' ************************************************
  8355.      '
  8356.      ' Reduces a fraction to its lowest terms.
  8357.      '
  8358.      ' EXAMPLE OF USE:  FractionReduce a
  8359.      ' PARAMETERS:      a          Fraction to reduce
  8360.      ' VARIABLES:       d&         Greatest common divisor of the numerator an
  8361.      '                             denominator
  8362.      ' MODULE LEVEL
  8363.      '   DECLARATIONS:  TYPE Fraction
  8364.      '                     Num AS LONG
  8365.      '                     Den AS LONG
  8366.      '                  END TYPE
  8367.      '
  8368.      '                  DECLARE SUB FractionReduce (a AS Fraction)
  8369.      '                  DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
  8370.      '
  8371.        SUB FractionReduce (a AS Fraction)
  8372.            d& = GreatestComDiv&(a.Num, a.Den)
  8373.            a.Num = a.Num / d&
  8374.            a.Den = a.Den / d&
  8375.        END SUB
  8376.    ──────────────────────────────────────────────────────────────────────────
  8377.  
  8378.  
  8379.  Subprogram: FractionSub
  8380.  
  8381.    Subtracts fraction b from fraction a, reduces the result to lowest terms,
  8382.    and returns the result in fraction c.
  8383.  
  8384.    ──────────────────────────────────────────────────────────────────────────
  8385.      ' ************************************************
  8386.      ' **  Name:          FractionSub                **
  8387.      ' **  Type:          Subprogram                 **
  8388.      ' **  Module:        FRACTION.BAS               **
  8389.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8390.      ' ************************************************
  8391.      '
  8392.      ' Subtracts two fractions and reduces the result to
  8393.      ' lowest terms.
  8394.      '
  8395.      ' EXAMPLE OF USE:  FractionSub a, b, c
  8396.      ' PARAMETERS:      a          First fraction
  8397.      '                  b          Fraction to subtract from the first
  8398.      '                  c          Resulting fraction
  8399.      ' VARIABLES:       (none)
  8400.      ' MODULE LEVEL
  8401.      '   DECLARATIONS:  TYPE Fraction
  8402.      '                     Num AS LONG
  8403.      '                     Den AS LONG
  8404.      '                  END TYPE
  8405.      '
  8406.      '     DECLARE SUB FractionReduce (a AS Fraction)
  8407.      '     DECLARE SUB FractionSub (a AS Fraction, b AS Fraction, c AS Fractio
  8408.      '     DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
  8409.      '
  8410.        SUB FractionSub (a AS Fraction, b AS Fraction, c AS Fraction)
  8411.            c.Num = a.Num * b.Den - a.Den * b.Num
  8412.            c.Den = a.Den * b.Den
  8413.            FractionReduce c
  8414.        END SUB
  8415.    ──────────────────────────────────────────────────────────────────────────
  8416.  
  8417.  
  8418.  Function: GreatestComDiv&
  8419.  
  8420.    Returns the greatest common divisor of two long integers.
  8421.  
  8422.    The greatest common divisor of the numerator and denominator of a fraction
  8423.    is efficient for reducing the fraction to its lowest terms, as
  8424.    demonstrated by the FractionReduce subprogram.
  8425.  
  8426.    ──────────────────────────────────────────────────────────────────────────
  8427.      ' ************************************************
  8428.      ' **  Name:          GreatestComDiv&            **
  8429.      ' **  Type:          Function                   **
  8430.      ' **  Module:        FRACTION.BAS               **
  8431.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8432.      ' ************************************************
  8433.      '
  8434.      ' Returns the greatest common divisor of two long integers.
  8435.      '
  8436.      ' EXAMPLE OF USE:  gcd& = GreatestComDiv& (n1&, n2&)
  8437.      ' PARAMETERS:      n1&        First long integer
  8438.      '                  n2&        Second long integer
  8439.      ' VARIABLES:       ta&        Working copy of n1&
  8440.      '                  tb&        Working copy of n2&
  8441.      '                  tc&        Working variable
  8442.      ' MODULE LEVEL
  8443.      '   DECLARATIONS:  DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
  8444.      '
  8445.        FUNCTION GreatestComDiv& (n1&, n2&)
  8446.            ta& = n1&
  8447.            tb& = n2&
  8448.            DO
  8449.                tc& = ta& MOD tb&
  8450.                ta& = tb&
  8451.                tb& = tc&
  8452.            LOOP WHILE tc&
  8453.            GreatestComDiv& = ta&
  8454.        END FUNCTION
  8455.    ──────────────────────────────────────────────────────────────────────────
  8456.  
  8457.  
  8458.  Function: LeastComMul&
  8459.  
  8460.    Returns the least common multiple of two long integers.
  8461.  
  8462.    Although this function is not used by any other routine in the
  8463.    FRACTION.BAS module, it is included because of its close ties to the
  8464.    GreatestComDiv& function. In fact, by using the GreatestComDiv& function
  8465.    in the calculations, the LeastComMul& function is shortened to one program
  8466.    line, as shown.
  8467.  
  8468.    ──────────────────────────────────────────────────────────────────────────
  8469.      ' ************************************************
  8470.      ' **  Name:          LeastComMul&               **
  8471.      ' **  Type:          Function                   **
  8472.      ' **  Module:        FRACTION.BAS               **
  8473.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8474.      ' ************************************************
  8475.      '
  8476.      ' Returns the least common multiple of two long integers.
  8477.      '
  8478.      ' EXAMPLE OF USE:  lcm& = LeastComMul& (n1&, n2&)
  8479.      ' PARAMETERS:      n1&         First long integer
  8480.      '                  n2&         Second long integer
  8481.      ' VARIABLES:       (none)
  8482.      ' MODULE LEVEL
  8483.      '   DECLARATIONS:  DECLARE FUNCTION LeastComMul& (n1&, n2&)
  8484.      '
  8485.        FUNCTION LeastComMul& (n1&, n2&)
  8486.            LeastComMul& = ABS(n1& * n2& / GreatestComDiv&(n1&, n2&))
  8487.        END FUNCTION
  8488.    ──────────────────────────────────────────────────────────────────────────
  8489.  
  8490.  
  8491.  Subprogram: SplitFractions
  8492.  
  8493.    Splits the input fraction problem string into two fraction strings and one
  8494.    operator string.
  8495.  
  8496.    This subprogram has a special purpose in the FRACTION.BAS program. After
  8497.    you enter a fraction problem, this subprogram splits your input into three
  8498.    strings: a string representation of the first fraction, a one-character
  8499.    symbol representing the desired mathematical operation, and a string
  8500.    representation of the second fraction.
  8501.  
  8502.    The results of this subprogram are passed to the String2Fraction
  8503.    subprogram before the indicated calculations are performed.
  8504.  
  8505.    ──────────────────────────────────────────────────────────────────────────
  8506.      ' ************************************************
  8507.      ' **  Name:          SplitFractions             **
  8508.      ' **  Type:          Subprogram                 **
  8509.      ' **  Module:        FRACTION.BAS               **
  8510.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8511.      ' ************************************************
  8512.      '
  8513.      ' Splits an input fraction problem string into
  8514.      ' three strings representing each of the two
  8515.      ' fractions and a one-character string of the
  8516.      ' operation given.
  8517.      '
  8518.      ' EXAMPLE OF USE: SplitFractions f$, fa$, operator$, fb$
  8519.      ' PARAMETERS:     f$         Input string from the FRACTIONS demonstratio
  8520.      '                            program
  8521.      '                 fa$        First fraction, extracted from f$
  8522.      '                 operator$  Mathematical operation symbol, from f$
  8523.      '                 fb$        Second fraction, extracted from f$
  8524.      ' VARIABLES:      i%         Looping index
  8525.      '                 ndx%       Index to mathematical operation symbol
  8526.      ' MODULE LEVEL
  8527.      '   DECLARATIONS: DECLARE SUB SplitFractions (f$, fa$, operator$, fb$)
  8528.      '
  8529.        SUB SplitFractions (f$, fa$, operator$, fb$)
  8530.            fa$ = ""
  8531.            fb$ = ""
  8532.            operator$ = ""
  8533.            FOR i% = 1 TO 4
  8534.                ndx% = INSTR(f$, MID$("+-*/", i%, 1))
  8535.                IF ndx% THEN
  8536.                    IF i% = 4 THEN
  8537.                        ndx% = INSTR(ndx% + 1, f$, "/")
  8538.                    END IF
  8539.                    fa$ = LEFT$(f$, ndx% - 1)
  8540.                    fb$ = MID$(f$, ndx% + 1)
  8541.                    operator$ = MID$(f$, ndx%, 1)
  8542.                    EXIT FOR
  8543.                END IF
  8544.            NEXT i%
  8545.        END SUB
  8546.    ──────────────────────────────────────────────────────────────────────────
  8547.  
  8548.  
  8549.  Subprogram: String2Fraction
  8550.  
  8551.    Converts a string representation of a fraction to a data structure of type
  8552.    Fraction. This routine is useful for converting user input of fractional
  8553.    values to type Fraction variables.
  8554.  
  8555.    This subprogram extracts numerator and denominator values from a string
  8556.    representation of a fraction and fills in a data structure of type
  8557.    Fraction with the results.
  8558.  
  8559.    ──────────────────────────────────────────────────────────────────────────
  8560.      ' ************************************************
  8561.      ' **  Name:          String2Fraction            **
  8562.      ' **  Type:          Subprogram                 **
  8563.      ' **  Module:        FRACTION.BAS               **
  8564.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8565.      ' ************************************************
  8566.      '
  8567.      ' Converts a string to a type Fraction variable.
  8568.      '
  8569.      ' EXAMPLE OF USE: String2Fraction f$, a
  8570.      ' PARAMETERS:     f$         String representation of a fraction
  8571.      '                 a          Structure of type Fraction
  8572.      ' VARIABLES:      (none)
  8573.      ' MODULE LEVEL
  8574.      '   DECLARATIONS:  DECLARE SUB String2Fraction (f$, a AS Fraction)
  8575.      '
  8576.        SUB String2Fraction (f$, a AS Fraction)
  8577.            a.Num = VAL(f$)
  8578.            a.Den = VAL(MID$(f$, INSTR(f$, "/") + 1))
  8579.        END SUB
  8580.    ──────────────────────────────────────────────────────────────────────────
  8581.  
  8582.  
  8583.  
  8584.  ────────────────────────────────────────────────────────────────────────────
  8585.  GAMES
  8586.  
  8587.    The GAMES toolbox is a collection of subprograms and functions that
  8588.    provide some common tasks for programming games with QuickBASIC.
  8589.    QuickBASIC is an ideal language for developing many graphics- and
  8590.    text-oriented games, partly because of the interactive nature of the
  8591.    development process, and partly because of the excellent set of graphics
  8592.    functions and subprograms provided by the language.
  8593.  
  8594.    Name                     Type    Description
  8595.    ──────────────────────────────────────────────────────────────────────────
  8596.    GAMES.BAS                       Demo module
  8597.    Card$                   Func    Returns name of card given a number from
  8598.                                     1 through 52
  8599.    Collision%              Func    Returns TRUE or FALSE collision condition
  8600.    Dice%                   Func    Returns total showing for throwing N dice
  8601.    FillArray               Sub     Fills an integer array with a sequence of
  8602.                                     numbers defined by the bounds
  8603.    Shuffle$                Func    Randomizes character bytes in a string
  8604.    ShuffleArray            Sub     Randomizes integers in an array
  8605.    ──────────────────────────────────────────────────────────────────────────
  8606.  
  8607.  
  8608.  Demo Module: GAMES
  8609.  
  8610.    ──────────────────────────────────────────────────────────────────────────
  8611.      ' ************************************************
  8612.      ' **  Name:          GAMES                      **
  8613.      ' **  Type:          Toolbox                    **
  8614.      ' **  Module:        GAMES.BAS                  **
  8615.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8616.      ' ************************************************
  8617.      '
  8618.      ' USAGE:          No command line parameters
  8619.      ' REQUIREMENTS:   CGA
  8620.      ' .MAK FILE:      (none)
  8621.      ' PARAMETERS:     (none)
  8622.      ' VARIABLES:      a$             String containing the 26 letters of the
  8623.      '                                alphabet
  8624.      '                 x%             Lower bound for array a%()
  8625.      '                 y%             Upper bound for array a%()
  8626.      '                 a%()           Array of numbers to be shuffled
  8627.      '                 i%             Looping index
  8628.      '                 size%          Dimension of bouncing ball array
  8629.      '                 object%()      Array for GET and PUT of bouncing ball
  8630.      '                 backGround%()  Array for GET and PUT of background
  8631.      '                 dx%            X velocity of bouncing ball
  8632.      '                 dy%            Y velocity of bouncing ball
  8633.      '                 px%            X coordinate of bouncing ball
  8634.      '                 py%            Y coordinate of bouncing ball
  8635.      '                 testNumber%    One of four bounce direction tests
  8636.      '                 test%          Result of the Collision% test
  8637.  
  8638.      ' Constants
  8639.        CONST FALSE = 0
  8640.        CONST TRUE = NOT FALSE
  8641.  
  8642.      ' Functions
  8643.        DECLARE FUNCTION Shuffle$ (a$)
  8644.        DECLARE FUNCTION Dice% (numberOfDice%)
  8645.        DECLARE FUNCTION Card$ (cardNumber%)
  8646.        DECLARE FUNCTION Collision% (object%(), backGround%())
  8647.  
  8648.      ' Subprograms
  8649.        DECLARE SUB FillArray (a%())
  8650.        DECLARE SUB ShuffleArray (a%())
  8651.  
  8652.      ' Demonstration of the Shuffle$ function
  8653.        CLS
  8654.        RANDOMIZE TIMER
  8655.        a$ = "abcdefghijklmnopqrstuvwxyz"
  8656.        PRINT "a$           = "; a$
  8657.        PRINT "Shuffle$(a$) = "; Shuffle$(a$)
  8658.        PRINT
  8659.  
  8660.      ' Demonstration of the FillArray subprogram
  8661.        x% = -7
  8662.        y% = 12
  8663.        DIM a%(x% TO y%)
  8664.        PRINT "FillArray a%()   where DIM a%( -7 TO 12) ..."
  8665.        FillArray a%()
  8666.        FOR i% = x% TO y%
  8667.            PRINT a%(i%);
  8668.        NEXT i%
  8669.        PRINT
  8670.  
  8671.      ' Demonstration of the ShuffleArray subprogram
  8672.        PRINT
  8673.        PRINT "ShuffleArray a%() ..."
  8674.        ShuffleArray a%()
  8675.        FOR i% = x% TO y%
  8676.            PRINT a%(i%);
  8677.        NEXT i%
  8678.        PRINT
  8679.  
  8680.      ' Demonstration of the Dice% function
  8681.        PRINT
  8682.        PRINT "Dice%(2)..."
  8683.        FOR i% = 1 TO 20
  8684.            PRINT Dice%(2);
  8685.        NEXT i%
  8686.        PRINT
  8687.  
  8688.      ' Deal a hand of seven cards
  8689.        PRINT
  8690.        PRINT "Seven random cards, without replacement..."
  8691.        REDIM a%(1 TO 54)
  8692.        FillArray a%()
  8693.        ShuffleArray a%()
  8694.        FOR i% = 1 TO 7
  8695.            PRINT Card$(a%(i%))
  8696.        NEXT i%
  8697.        PRINT
  8698.  
  8699.      ' Wait for user to press a key
  8700.        PRINT
  8701.        PRINT "Press any key to continue"
  8702.        DO
  8703.        LOOP WHILE INKEY$ = ""
  8704.  
  8705.      ' Demonstration of the Collision% function
  8706.        size% = 6
  8707.        DIM object%(size%), backGround%(size%)
  8708.  
  8709.      ' Set medium resolution graphics mode
  8710.        SCREEN 1
  8711.  
  8712.      ' Create the bouncing ball
  8713.        CIRCLE (2, 2), 2, 3
  8714.        PAINT (2, 2), 3
  8715.        GET (0, 0)-(4, 4), object%
  8716.  
  8717.      ' Make solid border around screen
  8718.        LINE (14, 18)-(305, 187), 1, B
  8719.        PAINT (0, 0), 1
  8720.  
  8721.        PRINT " Collision% function... Press any key to quit "
  8722.  
  8723.      ' Make three obstacles
  8724.        CIRCLE (115, 78), 33, 2, , , .6
  8725.        PAINT (115, 78), 2
  8726.        CIRCLE (205, 78), 33, 2, , , .6
  8727.        PAINT (205, 78), 2
  8728.        LINE (90, 145)-(230, 155), 2, BF
  8729.  
  8730.      ' Initialize position and velocity of the object
  8731.        dx% = 1
  8732.        dy% = 1
  8733.        px% = 160
  8734.        py% = 44
  8735.        PUT (px%, py%), object%
  8736.  
  8737.      ' Move the object around the screen, avoiding collisions,
  8738.      ' until any key is pressed
  8739.        DO
  8740.            testNumber% = 0
  8741.            DO
  8742.                PUT (px%, py%), object%
  8743.                px% = px% + dx%
  8744.                py% = py% + dy%
  8745.                GET (px%, py%)-(px% + 4, py% + 4), backGround%
  8746.                PUT (px%, py%), object%
  8747.                test% = Collision%(object%(), backGround%())
  8748.                IF test% THEN
  8749.                    testNumber% = testNumber% + 1
  8750.                    PUT (px%, py%), object%
  8751.                    px% = px% - dx%
  8752.                    py% = py% - dy%
  8753.                    SELECT CASE testNumber%
  8754.                    CASE 1
  8755.                        dx% = -dx%
  8756.                    CASE 2
  8757.                        dx% = -dx%
  8758.                        dy% = -dy%
  8759.                    CASE 3
  8760.                        dy% = -dy%
  8761.                    CASE ELSE
  8762.                    END SELECT
  8763.                    PUT (px%, py%), object%
  8764.                END IF
  8765.            LOOP UNTIL test% = 0
  8766.        LOOP UNTIL INKEY$ <> ""
  8767.  
  8768.      ' Clean up a little
  8769.        SCREEN 0
  8770.        WIDTH 80
  8771.        CLS
  8772.        SYSTEM
  8773.    ──────────────────────────────────────────────────────────────────────────
  8774.  
  8775.  
  8776.  Function: Card$
  8777.  
  8778.    Returns the name of a card from a standard, 52-card deck, given a number
  8779.    from 1 through 52.
  8780.  
  8781.    The passed number is first checked to determine the suit. Numbers from 1
  8782.    through 13 indicate a Spade, 14 through 26 a Club, and so on. The face
  8783.    name or the number of the card is then determined using the MOD function.
  8784.  
  8785.    If the number is less than 1 or greater than 52, this function returns
  8786.    Joker, making it convenient to deal a 54-card deck if desired.
  8787.  
  8788.    ──────────────────────────────────────────────────────────────────────────
  8789.      ' ************************************************
  8790.      ' **  Name:          Card$                      **
  8791.      ' **  Type:          Function                   **
  8792.      ' **  Module:        GAMES.BAS                  **
  8793.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8794.      ' ************************************************
  8795.      '
  8796.      ' Returns the name of a playing card given a number
  8797.      ' from 1 to 52.  Any other number returns "Joker."
  8798.      '
  8799.      ' EXAMPLE OF USE:  PRINT Card$(n%)
  8800.      ' PARAMETERS:      n%         Number from 1 to 52 representing a card (an
  8801.      '                             other number returns a Joker)
  8802.      ' VARIABLES:       suit$      Name of one of the four card suits
  8803.      ' MODULE LEVEL
  8804.      '   DECLARATIONS:  DECLARE FUNCTION Card$ (cardNumber%)
  8805.      '
  8806.         FUNCTION Card$ (cardNumber%)
  8807.  
  8808.            SELECT CASE (cardNumber% - 1) \ 13      ' Which suit?
  8809.            CASE 0
  8810.                suit$ = " of Spades"
  8811.            CASE 1
  8812.                suit$ = " of Clubs"
  8813.            CASE 2
  8814.                suit$ = " of Hearts"
  8815.            CASE 3
  8816.                suit$ = " of Diamonds"
  8817.            CASE ELSE
  8818.                Card$ = "Joker"
  8819.                EXIT FUNCTION
  8820.            END SELECT
  8821.  
  8822.            SELECT CASE (cardNumber% - 1) MOD 13    ' Which card?
  8823.            CASE 0
  8824.                Card$ = "Ace" + suit$
  8825.            CASE 1 TO 9
  8826.                Card$ = MID$(STR$(cardNumber% MOD 13), 2) + suit$
  8827.            CASE 10
  8828.                Card$ = "Jack" + suit$
  8829.            CASE 11
  8830.                Card$ = "Queen" + suit$
  8831.            CASE 12
  8832.                Card$ = "King" + suit$
  8833.            END SELECT
  8834.  
  8835.        END FUNCTION
  8836.    ──────────────────────────────────────────────────────────────────────────
  8837.  
  8838.  
  8839.  Function: Collision%
  8840.  
  8841.    Returns -1 or 0 (TRUE or FALSE), indicating whether a collision or near
  8842.    collision has occurred between two graphics objects stored in integer
  8843.    arrays.
  8844.  
  8845.    The graphics images are copied into the arrays by using the QuickBASIC GET
  8846.    statement. If you're not familiar with using the GET and PUT statements to
  8847.    create graphics animation, refer to your QuickBASIC documentation. These
  8848.    two statements provide a powerful method for quickly moving or duplicating
  8849.    graphics objects on your screen.
  8850.  
  8851.    To use the Collision function, you must pass two integer arrays of the
  8852.    same dimension. Normally, the background is copied into one array (using
  8853.    the GET statement) just before the object stored in the second is PUT on
  8854.    the screen at that same location. These two arrays are passed to the
  8855.    Collision% function, and the returned result determines whether the object
  8856.    overlaps (or very nearly overlaps) any pixel already on the screen.
  8857.  
  8858.    The check for near collision of pixels proceeds as follows. The first
  8859.    three integers in each array are skipped, as these integers contain
  8860.    object-dimensioning information and don't represent any pixel. The
  8861.    remaining integers from each array are compared to the corresponding
  8862.    integers from the other. Pixels having color attribute 0 represent the
  8863.    background and are stored in the integer array as one or more 0 bits. If
  8864.    an integer is 0, then all the pixels it represents are of the background
  8865.    color. If an integer is non-zero, then one or more of the pixels stored in
  8866.    it have a non-zero color attribute. To make the collision check fast and
  8867.    efficient, this function simply checks for non-zero bits in any
  8868.    corresponding integers from the two arrays. The pixels might not actually
  8869.    be overlapping, but they'll be very close neighbors. If a near collision
  8870.    is detected, the remaining integers are not checked, and the function
  8871.    returns a value of TRUE. If all the integers are checked and no collisions
  8872.    are detected, the function returns FALSE.
  8873.  
  8874.    The demonstration module shows one way to use the collision function to
  8875.    allow objects to bounce off each other. The bouncing ball moves around
  8876.    quickly in the "square face," bouncing off the mouth, eyes, and screen
  8877.    edges. Try experimenting by changing the size of the mouth or eyes or by
  8878.    drawing additional objects on the screen. The ball should bounce off any
  8879.    non-zero pixel objects.
  8880.  
  8881.    ──────────────────────────────────────────────────────────────────────────
  8882.      ' ************************************************
  8883.      ' **  Name:          Collision%                 **
  8884.      ' **  Type:          Function                   **
  8885.      ' **  Module:        GAMES.BAS                  **
  8886.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8887.      ' ************************************************
  8888.      '
  8889.      ' Returns TRUE if any non-zero pixels occur in the
  8890.      ' same byte of video memory, as saved in the object%()
  8891.      ' and backGround%() arrays.  The arrays must be the
  8892.      ' same size.
  8893.      '
  8894.      ' EXAMPLE OF USE:  test% = Collision%(object%(), backGround%())
  8895.      ' PARAMETERS:      object%()       First array, filled in with the GET
  8896.      '                                  statement
  8897.      '                  backGround%()   Second array, filled in with the GET
  8898.      '                                  statement
  8899.      ' VARIABLES:       lo%             Lower bound of first array
  8900.      '                  up%             Upper bound of first array
  8901.      '                  lb%             Lower bound of second array
  8902.      '                  ub%             Upper bound of second array
  8903.      '                  i%              Index to integers in each array
  8904.      ' MODULE LEVEL
  8905.      '   DECLARATIONS:  CONST FALSE = 0
  8906.      '                  CONST TRUE = NOT FALSE
  8907.      '                  DECLARE FUNCTION Collision% (object%(), backGround%())
  8908.      '
  8909.        FUNCTION Collision% (object%(), backGround%()) STATIC
  8910.            lo% = LBOUND(object%)
  8911.            uo% = UBOUND(object%)
  8912.            lb% = LBOUND(backGround%)
  8913.            ub% = UBOUND(backGround%)
  8914.            IF lo% <> lb% OR uo% <> ub% THEN
  8915.                PRINT "Error: Collision - The object and background"
  8916.                PRINT "graphics arrays have different dimensions."
  8917.                SYSTEM
  8918.            END IF
  8919.            FOR i% = lo% + 2 TO uo%
  8920.                IF object%(i%) THEN
  8921.                    IF backGround%(i%) THEN
  8922.                        Collision% = TRUE
  8923.                        EXIT FUNCTION
  8924.                    END IF
  8925.                END IF
  8926.            NEXT i%
  8927.            Collision% = FALSE
  8928.        END FUNCTION
  8929.    ──────────────────────────────────────────────────────────────────────────
  8930.  
  8931.  
  8932.  Function: Dice%
  8933.  
  8934.    Returns a total for all dots that are showing when n% pseudorandom dice
  8935.    are thrown.
  8936.  
  8937.    The QuickBASIC RND function creates the pseudorandom sequence of
  8938.    unpredictable numbers to simulate the dice. Unless you want the same
  8939.    scores to show up every time a program is run, you should randomize the
  8940.    QuickBASIC random number generator by using the RANDOMIZE statement.
  8941.  
  8942.    ──────────────────────────────────────────────────────────────────────────
  8943.      ' ************************************************
  8944.      ' **  Name:          Dice%                      **
  8945.      ' **  Type:          Function                   **
  8946.      ' **  Module:        GAMES.BAS                  **
  8947.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8948.      ' ************************************************
  8949.      '
  8950.      ' Returns the total of the dots showing when any
  8951.      ' desired number of dice are rolled.
  8952.      '
  8953.      ' EXAMPLE OF USE:  total% = Dice%(n%)
  8954.      ' PARAMETERS:      n%         Number of dice
  8955.      ' VARIABLES:       toss%      Loop index for throwing the n% dice
  8956.      '                  total%     Total of the dots showing
  8957.      ' MODULE LEVEL
  8958.      '   DECLARATIONS:  DECLARE FUNCTION Dice% (numberOfDice%)
  8959.      '
  8960.        FUNCTION Dice% (numberOfDice%)
  8961.            IF numberOfDice% < 1 THEN
  8962.                PRINT "Error: Dice%() - Can't throw fewer than one die"
  8963.                SYSTEM
  8964.            END IF
  8965.            FOR toss% = 1 TO numberOfDice%
  8966.                total% = total% + INT(RND * 6) + 1
  8967.            NEXT toss%
  8968.            Dice% = total%
  8969.        END FUNCTION
  8970.    ──────────────────────────────────────────────────────────────────────────
  8971.  
  8972.    GAMES
  8973.  
  8974.  
  8975.  Subprogram: FillArray
  8976.  
  8977.    Fills an integer array with a sequence of numbers defined by the bounds.
  8978.    For example, consider these two statements:
  8979.  
  8980.  
  8981.      DIM year%(1900 TO 1999)
  8982.      FillArray year%
  8983.  
  8984.    The array will be filled with year numbers from 1900 through 1999.
  8985.  
  8986.    As a second example, consider an array dimensioned from 1 through 52.
  8987.    After filling this array with the numbers 1 through 52, the array contents
  8988.    can be shuffled efficiently with the ShuffleArray subprogram. The result
  8989.    is a freshly shuffled deck of 52 cards. Pulling these random "cards"
  8990.    sequentially from the array prevents duplication of a card.
  8991.  
  8992.    ──────────────────────────────────────────────────────────────────────────
  8993.      ' ************************************************
  8994.      ' **  Name:          FillArray                  **
  8995.      ' **  Type:          Subprogram                 **
  8996.      ' **  Module:        GAMES.BAS                  **
  8997.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  8998.      ' ************************************************
  8999.      '
  9000.      ' Initializes an integer array by putting i% into
  9001.      ' each i%th element.
  9002.      '
  9003.      ' EXAMPLE OF USE:  FillArray a%()
  9004.      ' PARAMETERS:      a%()       Array to be filled with a sequence of numbe
  9005.      ' VARIABLES:       i%         Looping index
  9006.      ' MODULE LEVEL
  9007.      '   DECLARATIONS:  DECLARE SUB FillArray (a%())
  9008.      '
  9009.        SUB FillArray (a%()) STATIC
  9010.            FOR i% = LBOUND(a%) TO UBOUND(a%)
  9011.                a%(i%) = i%
  9012.            NEXT i%
  9013.        END SUB
  9014.    ──────────────────────────────────────────────────────────────────────────
  9015.  
  9016.  
  9017.  Function: Shuffle$
  9018.  
  9019.    Shuffles the contents of a string by randomly swapping bytes throughout
  9020.    the string.
  9021.  
  9022.    ──────────────────────────────────────────────────────────────────────────
  9023.      ' ************************************************
  9024.      ' **  Name:          Shuffle$                   **
  9025.      ' **  Type:          Function                   **
  9026.      ' **  Module:        GAMES.BAS                  **
  9027.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9028.      ' ************************************************
  9029.      '
  9030.      ' Randomizes the order of the character bytes in a$.
  9031.      '
  9032.      ' EXAMPLE OF USE:  b$ = Shuffle$(a$)
  9033.      ' PARAMETERS:      a$         String to be shuffled
  9034.      ' VARIABLES:       x$         Working string space
  9035.      '                  lenx%      Number of bytes in the string
  9036.      '                  i%         Pointer to each byte
  9037.      '                  j%         Pointer to randomly selected byte
  9038.      '                  t$         Temporary byte-swapping string
  9039.      ' MODULE LEVEL
  9040.      '   DECLARATIONS:  DECLARE FUNCTION Shuffle$ (a$)
  9041.      '
  9042.        FUNCTION Shuffle$ (a$) STATIC
  9043.            x$ = a$
  9044.            lenx% = LEN(x$)
  9045.            FOR i% = 1 TO lenx%
  9046.                j% = INT(RND * lenx% + 1)
  9047.                t$ = MID$(x$, i%, 1)
  9048.                MID$(x$, i%, 1) = MID$(x$, j%, 1)
  9049.                MID$(x$, j%, 1) = t$
  9050.            NEXT i%
  9051.            Shuffle$ = x$
  9052.            x$ = ""
  9053.        END FUNCTION
  9054.    ──────────────────────────────────────────────────────────────────────────
  9055.  
  9056.  
  9057.  Subprogram: ShuffleArray
  9058.  
  9059.    Shuffles the contents of an integer array. The array dimensions are
  9060.    automatically determined, and each integer entry is swapped with a
  9061.    randomly selected second entry.
  9062.  
  9063.    ──────────────────────────────────────────────────────────────────────────
  9064.      ' ************************************************
  9065.      ' **  Name:          ShuffleArray               **
  9066.      ' **  Type:          Subprogram                 **
  9067.      ' **  Module:        GAMES.BAS                  **
  9068.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9069.      ' ************************************************
  9070.      '
  9071.      ' Randomizes the order of the integers in a%()
  9072.      ' by swapping contents in a pseudorandom order.
  9073.      '
  9074.      ' EXAMPLE OF USE:  ShuffleArray a%()
  9075.      ' PARAMETERS:      a%()       Array to be shuffled
  9076.      ' VARIABLES:       lb%        Lower bound of the array
  9077.      '                  ub%        Upper bound of the array
  9078.      '                  range%     Number of array entries
  9079.      '                  i%         Looping index
  9080.      '
  9081.      ' MODULE LEVEL
  9082.      '   DECLARATIONS:  DECLARE SUB ShuffleArray (a%())
  9083.      '
  9084.        SUB ShuffleArray (a%()) STATIC
  9085.            lb% = LBOUND(a%)
  9086.            ub% = UBOUND(a%)
  9087.            range% = ub% - lb% + 1
  9088.            FOR i% = lb% TO ub%
  9089.                SWAP a%(i%), a%(INT(RND * range% + lb%))
  9090.            NEXT i%
  9091.        END SUB
  9092.    ──────────────────────────────────────────────────────────────────────────
  9093.  
  9094.  
  9095.  
  9096.  ────────────────────────────────────────────────────────────────────────────
  9097.  HEX2BIN
  9098.  
  9099.    The HEX2BIN program reads in a file containing hexadecimal notation and
  9100.    creates a file containing the bytes that are indicated. Characters that
  9101.    are not in the set of hexadecimal characters are ignored, and each byte is
  9102.    assumed to be indicated by a pair of hexadecimal characters.
  9103.  
  9104.    This program converts the hexadecimal format files created by the
  9105.    BIN2HEX program into the object code files they represent. For example,
  9106.    you can create the MOUSE.OBJ file from the MOUSE.HEX file if you don't
  9107.    have the Microsoft Macro Assembler. (If you do have the Macro Assembler,
  9108.    you should create MOUSE.OBJ directly from the MOUSE.ASM listing.)
  9109.  
  9110.    The command line for performing this conversion (assuming you've compiled
  9111.    HEX2BIN to an executable program to be run from the MS-DOS prompt) is:
  9112.  
  9113.  
  9114.      HEX2BIN MOUSE.HEX MOUSE.OBJ
  9115.  
  9116.    Refer to the BIN2HEX program for information about creating this and the
  9117.    other .HEX files.
  9118.  
  9119.  
  9120.  Program Module: HEX2BIN
  9121.  
  9122.    ──────────────────────────────────────────────────────────────────────────
  9123.      ' ************************************************
  9124.      ' **  Name:          HEX2BIN                    **
  9125.      ' **  Type:          Program                    **
  9126.      ' **  Module:        HEX2BIN.BAS                **
  9127.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9128.      ' ************************************************
  9129.      '
  9130.      ' Reads in a hexadecimal format file and writes out a binary
  9131.      ' file created from the hexadecimal byte numbers.
  9132.      '
  9133.      ' USAGE:           HEX2BIN inFileName.ext outFileName.ext
  9134.      ' .MAK FILE:       HEX2BIN.BAS
  9135.      '                  PARSE.BAS
  9136.      '                  STRINGS.BAS
  9137.      ' PARAMETERS:      inFileName.ext    Name of hexadecimal format file to b
  9138.      '                  outFileName.ext   Name of file to be created
  9139.      ' VARIABLES:       cmd$       Working copy of the command line
  9140.      '                  inFile$    Name of input file
  9141.      '                  outFile$   Name of output file
  9142.      '                  h$         Pair of hexadecimal characters representing
  9143.      '                             each byte
  9144.      '                  i%         Index into list of hexadecimal character pa
  9145.      '                  byte$      Buffer for binary file access
  9146.  
  9147.        DECLARE SUB ParseWord (a$, sep$, word$)
  9148.        DECLARE FUNCTION FilterIn$ (a$, set$)
  9149.  
  9150.      ' Get the input and output filenames from the command line
  9151.        cmd$ = COMMAND$
  9152.        ParseWord cmd$, " ,", inFile$
  9153.        ParseWord cmd$, " ,", outFile$
  9154.  
  9155.      ' Verify both filenames were given
  9156.        IF outFile$ = "" THEN
  9157.            PRINT
  9158.            PRINT "Usage: HEX2BIN inFileName.ext outFileName.ext"
  9159.            SYSTEM
  9160.        END IF
  9161.  
  9162.      ' Open the input file
  9163.        OPEN inFile$ FOR INPUT AS #1
  9164.  
  9165.      ' Truncate the output file if it already exists
  9166.        OPEN outFile$ FOR OUTPUT AS #2
  9167.        CLOSE #2
  9168.  
  9169.      ' Now open it for binary output
  9170.        OPEN outFile$ FOR BINARY AS #2 LEN = 1
  9171.  
  9172.      ' Process each line of the hexadecimal file
  9173.        DO
  9174.            LINE INPUT #1, h$
  9175.            h$ = FilterIn$(UCASE$(h$), "0123456789ABCDEF")
  9176.            FOR i% = 1 TO LEN(h$) STEP 2
  9177.                byte$ = CHR$(VAL("&H" + MID$(h$, i%, 2)))
  9178.                PUT #2, , byte$
  9179.            NEXT i%
  9180.        LOOP WHILE NOT EOF(1)
  9181.  
  9182.      ' Clean up and quit
  9183.        CLOSE
  9184.        END
  9185.    ──────────────────────────────────────────────────────────────────────────
  9186.  
  9187.  
  9188.  
  9189.  ────────────────────────────────────────────────────────────────────────────
  9190.  JUSTIFY
  9191.  
  9192.    The JUSTIFY toolbox contains the subprogram, Justify, which pads a string
  9193.    with spaces between words in a pseudorandom manner until the string is a
  9194.    desired number of characters. This sounds simple, but the process is
  9195.    surprisingly complicated. For example, the inserted spaces must fall
  9196.    randomly between words, but it's desirable to keep the density of spaces
  9197.    as even as possible. You wouldn't want five spaces between the first two
  9198.    words and two spaces between the next two words.
  9199.  
  9200.    The demo module prints a paragraph justified to three different widths. As
  9201.    shown in the demo, the FormatTwo subprogram works hand in hand with the
  9202.    Justify subprogram to format a long string into several smaller strings.
  9203.    By padding the resulting shorter strings with a fixed number of spaces on
  9204.    the left, you're able to format paragraphs of text between arbitrary
  9205.    margins. Refer to .MAK FILE in the comment lines of the listing to see the
  9206.    other modules you must load for this program to run correctly.
  9207.  
  9208.    Name               Type     Description
  9209.    ──────────────────────────────────────────────────────────────────────────
  9210.    JUSTIFY.BAS                Demo module
  9211.    Justify           Sub      Adjusts strings to specified widths
  9212.    ──────────────────────────────────────────────────────────────────────────
  9213.  
  9214.  
  9215.  Demo Module: JUSTIFY
  9216.  
  9217.    ──────────────────────────────────────────────────────────────────────────
  9218.      ' ************************************************
  9219.      ' **  Name:          JUSTIFY                    **
  9220.      ' **  Type:          Toolbox                    **
  9221.      ' **  Module:        JUSTIFY.BAS                **
  9222.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9223.      ' ************************************************
  9224.      '
  9225.      ' Demonstrates the Justify subprogram.
  9226.      '
  9227.      ' USAGE:           No command line parameters
  9228.      ' .MAK FILE:     JUSTIFY.BAS
  9229.      '                EDIT.BAS
  9230.      '                PARSE.BAS
  9231.      '                KEYS.BAS
  9232.      ' PARAMETERS:    (none)
  9233.      ' VARIABLES:     a$         String to be justified
  9234.      '                col%       Number of columns for each example of Justify
  9235.      '                x$         Working copy of a$
  9236.      '                y$         Working string space
  9237.  
  9238.        DECLARE SUB Justify (a$, n%)
  9239.        DECLARE SUB ParseLine (x$, sep$, a$())
  9240.        DECLARE SUB FormatTwo (a$, b$, col%)
  9241.  
  9242.        CLS
  9243.        a$ = ""
  9244.        a$ = a$ + "This paragraph is used to demonstrate the Justify "
  9245.        a$ = a$ + "subprogram.  First, the entire paragraph is "
  9246.        a$ = a$ + "placed in a single string variable.  This string "
  9247.        a$ = a$ + "is then split between words into shorter strings, "
  9248.        a$ = a$ + "and these shorter strings are then justified in "
  9249.        a$ = a$ + "order to align both the left and right edges of "
  9250.        a$ = a$ + "the text."
  9251.  
  9252.        FOR col% = 50 TO 70 STEP 10
  9253.            x$ = a$
  9254.            DO
  9255.                FormatTwo x$, y$, col%
  9256.                IF y$ <> "" THEN
  9257.                    Justify x$, col%
  9258.                END IF
  9259.                PRINT x$
  9260.                x$ = y$
  9261.            LOOP WHILE y$ <> ""
  9262.            PRINT
  9263.        NEXT col%
  9264.  
  9265.        END
  9266.    ──────────────────────────────────────────────────────────────────────────
  9267.  
  9268.  
  9269.  Subprogram: Justify
  9270.  
  9271.    Inserts spaces between words until the given string is the desired length.
  9272.    Spaces are not added before the first word or after the last word,
  9273.    resulting in a string that is left- and right-justified to the length
  9274.    indicated.
  9275.  
  9276.    ──────────────────────────────────────────────────────────────────────────
  9277.      ' ************************************************
  9278.      ' **  Name:          Justify                    **
  9279.      ' **  Type:          Subprogram                 **
  9280.      ' **  Module:        JUSTIFY.BAS                **
  9281.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9282.      ' ************************************************
  9283.      '
  9284.      ' Spaces words with extra spaces until line
  9285.      ' is n% characters long.
  9286.      '
  9287.      ' EXAMPLE OF USE:  Justify a$, n%
  9288.      ' PARAMETERS:      a$         String to be justified
  9289.      '                  n%         Desired string length
  9290.      ' VARIABLES:       ary$()     Array to store individual words from the st
  9291.      '                  cnt%       Count of non-space characters
  9292.      '                  i%         Looping index
  9293.      '                  j%         Count of words
  9294.      '                  each%      Minimum space count to insert between words
  9295.      ' MODULE LEVEL
  9296.      '   DECLARATIONS:  DECLARE SUB Justify (a$, n%)
  9297.      '                  DECLARE SUB ParseLine (x$, sep$, a$())
  9298.      '                  DECLARE SUB FormatTwo (a$, b$, col%)
  9299.      '
  9300.        SUB Justify (a$, n%) STATIC
  9301.  
  9302.          ' If string is shorter than n%, don't bother
  9303.            IF LEN(a$) < n% THEN
  9304.                EXIT SUB
  9305.            END IF
  9306.  
  9307.          ' Array for list of words from original string
  9308.            REDIM ary$(1 TO n%)
  9309.  
  9310.          ' Split line up into individual words
  9311.            ParseLine a$, " ", ary$()
  9312.  
  9313.          ' Count the words and total of non-space characters
  9314.            cnt% = 0
  9315.            FOR i% = n% TO 1 STEP -1
  9316.                cnt% = cnt% + LEN(ary$(i%))
  9317.                IF ary$(i%) = "" THEN
  9318.                    j% = i% - 1
  9319.                END IF
  9320.            NEXT i%
  9321.  
  9322.          ' If only one or zero words, there's not much we can do
  9323.            IF j% < 2 THEN
  9324.                a$ = LEFT$(ary$(1) + SPACE$(n%), n%)
  9325.                EXIT SUB
  9326.            END IF
  9327.  
  9328.          ' We want an extra space at the ends of sentences, questions, etc.
  9329.            FOR i% = 1 TO j% - 1
  9330.                IF INSTR(".!?", RIGHT$(ary$(i%), 1)) THEN
  9331.                    ary$(i%) = ary$(i%) + " "
  9332.                    cnt% = cnt% + 1
  9333.                END IF
  9334.            NEXT i%
  9335.  
  9336.          ' How many spaces minimum to add to each word?
  9337.            each% = (n% - cnt%) \ (j% - 1)
  9338.  
  9339.          ' Tack on the minimum spaces to each word
  9340.            FOR i% = 1 TO j% - 1
  9341.                ary$(i%) = ary$(i%) + SPACE$(each%)
  9342.                cnt% = cnt% + each%
  9343.            NEXT i%
  9344.  
  9345.          ' Which is quicker, adding remaining spaces, or
  9346.          ' adding spaces to all and removing a few of them?
  9347.            IF (n% - cnt%) < j% \ 2 THEN
  9348.  
  9349.              ' We'll add a few spaces at random
  9350.                DO UNTIL cnt% = n%
  9351.                    DO
  9352.                        i% = INT(RND * (j% - 1) + 2)
  9353.                    LOOP UNTIL LEFT$(ary$(i%), 1) <> " "
  9354.                    ary$(i%) = " " + ary$(i%)
  9355.                    cnt% = cnt% + 1
  9356.                LOOP
  9357.  
  9358.            ELSE
  9359.  
  9360.              ' We'll add a space to each, and then remove some at random
  9361.                FOR i% = 2 TO j%
  9362.                    ary$(i%) = " " + ary$(i%)
  9363.                    cnt% = cnt% + 1
  9364.                NEXT i%
  9365.  
  9366.              ' Now we'll take a few away at random
  9367.                DO UNTIL cnt% = n%
  9368.                    DO
  9369.                        i% = INT(RND * (j% - 1) + 2)
  9370.                    LOOP UNTIL LEFT$(ary$(i%), 1) = " "
  9371.                    ary$(i%) = MID$(ary$(i%), 2)
  9372.                    cnt% = cnt% - 1
  9373.                LOOP
  9374.  
  9375.            END IF
  9376.  
  9377.          ' Glue it all back together
  9378.            a$ = ary$(1)
  9379.            FOR i% = 2 TO j%
  9380.                a$ = a$ + ary$(i%)
  9381.            NEXT i%
  9382.  
  9383.        END SUB
  9384.    ──────────────────────────────────────────────────────────────────────────
  9385.  
  9386.  
  9387.  
  9388.  ────────────────────────────────────────────────────────────────────────────
  9389.  KEYS
  9390.  
  9391.    The KEYS toolbox performs two enhanced keyboard input functions. It prints
  9392.    the unique integer number returned by the KeyCode% or InKeyCode%
  9393.    function for any key pressed. Run the program and press a few keys to see
  9394.    the numbers. To try the InKeyCode% function for one second at a time,
  9395.    press the Escape key followed immediately by other keys.
  9396.  
  9397.    The QuickBASIC INKEY$ function returns a string of zero, one, or two
  9398.    characters, depending on whether a key was pressed and whether the key has
  9399.    an extended key code. The two functions presented here always return a
  9400.    unique integer for any key pressed, even if the key normally returns an
  9401.    extended key code. For example, pressing the letter "a" returns 97, F1
  9402.    returns 15104, Alt-F1 returns 26624, and the Home key returns 18176. Run
  9403.    the program to determine other returned values.
  9404.  
  9405.    The EDIT.BAS module uses these functions and presents a table of CONST
  9406.    statements that define several common editing keys. Note that most
  9407.    standard alphanumeric keys return the expected ASCII code number.
  9408.  
  9409.    Name               Type     Description
  9410.    ──────────────────────────────────────────────────────────────────────────
  9411.    KEYS.BAS                   Demo module
  9412.    InKeyCode%        Func     Returns unique integer for any key pressed
  9413.    KeyCode%          Func     Waits and returns integer value for key
  9414.    ──────────────────────────────────────────────────────────────────────────
  9415.  
  9416.  
  9417.  Demo Module: KEYS
  9418.  
  9419.    ──────────────────────────────────────────────────────────────────────────
  9420.      ' ************************************************
  9421.      ' **  Name:          KEYS                       **
  9422.      ' **  Type:          Toolbox                    **
  9423.      ' **  Module:        KEYS.BAS                   **
  9424.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9425.      ' ************************************************
  9426.      '
  9427.      ' Demonstrates keyboard access functions.
  9428.      ' USAGE:           No command line parameters
  9429.      ' .MAK FILE:       (none)
  9430.      ' PARAMETERS:      (none)
  9431.      ' VARIABLES:       kee%       Unique integer returned by KeyCode% and
  9432.      '                             InKeyCode%
  9433.  
  9434.  
  9435.        DECLARE FUNCTION KeyCode% ()
  9436.        DECLARE FUNCTION InKeyCode% ()
  9437.  
  9438.        CLS
  9439.        PRINT "Press any key to see the unique number returned by KeyCode%."
  9440.        PRINT "Press Esc to see InKeyCode% results for 1 second."
  9441.        PRINT "Press Esc twice in a row to quit."
  9442.        PRINT
  9443.  
  9444.        DO
  9445.            kee% = KeyCode%
  9446.            PRINT kee%
  9447.            IF kee% = 27 THEN
  9448.                t0 = TIMER
  9449.                DO
  9450.                    kee% = InKeyCode%
  9451.                    PRINT kee%;
  9452.                    IF kee% THEN
  9453.                        PRINT
  9454.                    END IF
  9455.                    IF kee% = 27 THEN
  9456.                        quitFlag% = -1
  9457.                        t0 = t0 - 1
  9458.                    END IF
  9459.                LOOP UNTIL TIMER - t0 > 1
  9460.                PRINT
  9461.            END IF
  9462.        LOOP UNTIL quitFlag%
  9463.  
  9464.        END
  9465.    ──────────────────────────────────────────────────────────────────────────
  9466.  
  9467.  
  9468.  Function: InKeyCode%
  9469.  
  9470.    Immediately returns a unique integer for any key pressed or 0 if no key
  9471.    was pressed.
  9472.  
  9473.    ──────────────────────────────────────────────────────────────────────────
  9474.      ' ************************************************
  9475.      ' **  Name:          InKeyCode%                 **
  9476.      ' **  Type:          Function                   **
  9477.      ' **  Module:        KEYS.BAS                   **
  9478.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9479.      ' ************************************************
  9480.      '
  9481.      ' Returns a unique integer for any key pressed or
  9482.      ' a zero if no key was pressed.
  9483.      '
  9484.      ' EXAMPLE OF USE:  k% = InKeyCode%
  9485.      ' PARAMETERS:      (none)
  9486.      ' VARIABLES:       (none)
  9487.      ' MODULE LEVEL
  9488.      '   DECLARATIONS:  DECLARE FUNCTION KeyCode% ()
  9489.      '
  9490.        FUNCTION InKeyCode% STATIC
  9491.            InKeyCode% = CVI(INKEY$ + STRING$(2, 0))
  9492.        END FUNCTION
  9493.    ──────────────────────────────────────────────────────────────────────────
  9494.  
  9495.  
  9496.  Function: KeyCode%
  9497.  
  9498.    Waits until a key is pressed, and then returns the unique key-code integer
  9499.    for each key on the keyboard.
  9500.  
  9501.    ──────────────────────────────────────────────────────────────────────────
  9502.      ' ************************************************
  9503.      ' **  Name:          KeyCode%                   **
  9504.      ' **  Type:          Function                   **
  9505.      ' **  Module:        KEYS.BAS                   **
  9506.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9507.      ' ************************************************
  9508.      '
  9509.      ' Returns a unique integer for any key pressed.
  9510.      '
  9511.      ' EXAMPLE OF USE:  k% = KeyCode%
  9512.      ' PARAMETERS:      (none)
  9513.      ' VARIABLES:       (none)
  9514.      ' MODULE LEVEL
  9515.      '   DECLARATIONS:  DECLARE FUNCTION KeyCode% ()
  9516.      '
  9517.        FUNCTION KeyCode% STATIC
  9518.            DO
  9519.                k$ = INKEY$
  9520.            LOOP UNTIL k$ <> ""
  9521.            KeyCode% = CVI(k$ + CHR$(0))
  9522.        END FUNCTION
  9523.    ──────────────────────────────────────────────────────────────────────────
  9524.  
  9525.  
  9526.  
  9527.  ────────────────────────────────────────────────────────────────────────────
  9528.  LOOK
  9529.  
  9530.    The LOOK program is a utility for viewing text-file contents. The program
  9531.    displays ASCII text-file contents and provides limited keyboard control to
  9532.    allow scrolling or paging through files.
  9533.  
  9534.    This program presents the FileRead subprogram for reading ASCII files
  9535.    into an array of strings and also demonstrates the VIEW PRINT statement
  9536.    for limiting printing and scrolling of text to only those display lines
  9537.    desired.
  9538.  
  9539.    Name                     Type    Description
  9540.    ──────────────────────────────────────────────────────────────────────────
  9541.    LOOK.BAS                        Program module
  9542.    FileRead                Sub     Reads lines of ASCII files into an array
  9543.    ──────────────────────────────────────────────────────────────────────────
  9544.  
  9545.  
  9546.  Program Module: LOOK
  9547.  
  9548.    ──────────────────────────────────────────────────────────────────────────
  9549.      ' ************************************************
  9550.      ' **  Name:          LOOK                       **
  9551.      ' **  Type:          Program                    **
  9552.      ' **  Module:        LOOK.BAS                   **
  9553.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9554.      ' ************************************************
  9555.      '
  9556.      ' USAGE:           LOOK filename.ext
  9557.      ' .MAK FILE:       LOOK.BAS
  9558.      '                  KEYS.BAS
  9559.      ' PARAMETERS:      filename.ext  Name of file to view
  9560.      ' VARIABLES:       a$()          Array of lines from the file
  9561.      '                  fileName$     Name of file, from the command line
  9562.      '                  lineCount%    Count of lines read from the file
  9563.      '                  linePtr%      First file line currently on the display
  9564.      '                  i%            Loop index for printing 24 lines
  9565.      '                  quitFlag%     Indicates Escape key press
  9566.      '                  updateFlag%   Indicates if update of screen is necessa
  9567.  
  9568.      ' Constants
  9569.        CONST FALSE = 0
  9570.        CONST TRUE = NOT FALSE
  9571.  
  9572.      ' Key code numbers
  9573.        CONST UPARROW = 18432
  9574.        CONST DOWNARROW = 20480
  9575.        CONST PGUP = 18688
  9576.        CONST PGDN = 20736
  9577.        CONST HOME = 18176
  9578.        CONST ENDKEY = 20224
  9579.        CONST ESCAPE = 27
  9580.  
  9581.      ' Functions
  9582.        DECLARE FUNCTION KeyCode% ()
  9583.  
  9584.      ' Subprograms
  9585.        DECLARE SUB FileRead (fileName$, lineCount%, a$())
  9586.  
  9587.      ' Dimension string array
  9588.      ' NOTE:
  9589.      ' Must be dimensioned big enough to read in all lines from the file
  9590.        DIM a$(1 TO 2000)
  9591.  
  9592.      ' Get the command line parameters
  9593.        fileName$ = COMMAND$
  9594.  
  9595.      ' Read in the file
  9596.        ON ERROR GOTO FileError
  9597.        FileRead fileName$, lineCount%, a$()
  9598.        ON ERROR GOTO 0
  9599.  
  9600.      ' Prepare the screen
  9601.        SCREEN 0, 0, 0, 0
  9602.        CLS
  9603.  
  9604.      ' Set line pointer
  9605.        linePtr% = 1
  9606.  
  9607.      ' Main loop
  9608.        DO
  9609.  
  9610.          ' Print information bar at top
  9611.            VIEW PRINT 1 TO 1
  9612.            COLOR 0, 3
  9613.            LOCATE 1, 1
  9614.            PRINT " Line:"; LEFT$(STR$(linePtr%) + SPACE$(7), 8);
  9615.            PRINT "File: "; LEFT$(fileName$ + SPACE$(19), 19);
  9616.            PRINT "Quit: ESC"; SPACE$(3);
  9617.            PRINT "Move: "; CHR$(24); " "; CHR$(25); " PGUP PGDN HOME END ";
  9618.  
  9619.          ' Update the 24 lines of text
  9620.            VIEW PRINT 2 TO 25
  9621.            COLOR 7, 1
  9622.            FOR i% = 0 TO 23
  9623.                LOCATE i% + 2, 1
  9624.                PRINT LEFT$(a$(i% + linePtr%) + SPACE$(80), 80);
  9625.            NEXT i%
  9626.  
  9627.          ' Wait for a meaningful key to be pressed
  9628.            SELECT CASE KeyCode%
  9629.            CASE UPARROW
  9630.                IF linePtr% > 1 THEN
  9631.                    linePtr% = linePtr% - 1
  9632.                END IF
  9633.            CASE DOWNARROW
  9634.                IF linePtr% < lineCount% THEN
  9635.                    linePtr% = linePtr% + 1
  9636.                END IF
  9637.            CASE PGUP
  9638.                IF linePtr% > 1 THEN
  9639.                    linePtr% = linePtr% - 24
  9640.                    IF linePtr% < 1 THEN
  9641.                        linePtr% = 1
  9642.                    END IF
  9643.                END IF
  9644.            CASE PGDN
  9645.                IF linePtr% < lineCount% - 24 THEN
  9646.                    linePtr% = linePtr% + 24
  9647.                    IF linePtr% > lineCount% THEN
  9648.                        linePtr% = lineCount%
  9649.                    END IF
  9650.                END IF
  9651.            CASE HOME
  9652.                IF linePtr% > 1 THEN
  9653.                    linePtr% = 1
  9654.                END IF
  9655.            CASE ENDKEY
  9656.                IF linePtr% < lineCount% - 24 THEN
  9657.                    linePtr% = lineCount% - 24
  9658.                END IF
  9659.            CASE ESCAPE
  9660.                quitFlag% = TRUE
  9661.            CASE ELSE
  9662.                updateFlag% = FALSE
  9663.            END SELECT
  9664.  
  9665.        LOOP UNTIL quitFlag%
  9666.  
  9667.      ' Set color back to normal
  9668.        COLOR 7, 0
  9669.        END
  9670.  
  9671.    FileError:
  9672.        PRINT
  9673.        PRINT "Usage: LOOK filename.ext"
  9674.        SYSTEM
  9675.        RESUME NEXT
  9676.    ──────────────────────────────────────────────────────────────────────────
  9677.  
  9678.  
  9679.  Subprogram: FileRead
  9680.  
  9681.    Reads all lines of a text (ASCII) file into a string array and returns the
  9682.    array of lines from the file and the count of the read lines.
  9683.  
  9684.    The string array must be large enough to hold all the lines from the file.
  9685.    The file to read must be readable using the LINE INPUT statement.
  9686.  
  9687.    ──────────────────────────────────────────────────────────────────────────
  9688.      ' ************************************************
  9689.      ' **  Name:          FileRead                   **
  9690.      ' **  Type:          Subprogram                 **
  9691.      ' **  Module:        LOOK.BAS                   **
  9692.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9693.      ' ************************************************
  9694.      '
  9695.      ' Reads lines of an ASCII file into a$().  The
  9696.      ' lineCount% is set to the number of lines read
  9697.      ' in.  If a$() wasn't dimensioned large enough,
  9698.      ' then lineCount% will be set to -1.
  9699.      '
  9700.      ' EXAMPLE OF USE:  FileRead fileName$, lineCount%, a$()
  9701.      ' PARAMETERS:      fileName$     Name of file to be read into the array
  9702.      '                  lineCount%    Returned count of lines read from the fi
  9703.      '                  a$()          String array of file contents
  9704.      ' VARIABLES:       FileNumber%   Next available free file number
  9705.      '                  i%            Index for string array
  9706.      ' MODULE LEVEL
  9707.      '   DECLARATIONS:    DECLARE SUB FileRead (fileName$, lineCount%, a$())
  9708.      '
  9709.        SUB FileRead (fileName$, lineCount%, a$()) STATIC
  9710.            FileNumber% = FREEFILE
  9711.            OPEN fileName$ FOR INPUT AS FileNumber%
  9712.            FOR i% = LBOUND(a$) TO UBOUND(a$)
  9713.                LINE INPUT #FileNumber%, a$(i%)
  9714.                lineCount% = i%
  9715.                IF EOF(FileNumber%) THEN
  9716.                    EXIT FOR
  9717.                END IF
  9718.            NEXT i%
  9719.            IF NOT EOF(FileNumber%) THEN
  9720.                lineCount% = -1
  9721.            END IF
  9722.        END SUB
  9723.    ──────────────────────────────────────────────────────────────────────────
  9724.  
  9725.  
  9726.  
  9727.  ────────────────────────────────────────────────────────────────────────────
  9728.  MONTH
  9729.  
  9730.    The MONTH program demonstrates how to use the CALENDAR.BAS toolbox to
  9731.    perform calendar-related calculations.
  9732.  
  9733.    When MONTH is run, a display of three one-month calendars is created. The
  9734.    current system date determines the second month displayed; the previous
  9735.    and next month are also shown.
  9736.  
  9737.    Included with the display are instructions on how to increment or
  9738.    decrement the years or months. Press the lowercase y key to display the
  9739.    same three months of the previous year. Press a shifted (uppercase) Y to
  9740.    increment the year. In the same way, press M to increment or m to
  9741.    decrement the range of months displayed.
  9742.  
  9743.  
  9744.  Program Module: MONTH
  9745.  
  9746.    ──────────────────────────────────────────────────────────────────────────
  9747.      ' ************************************************
  9748.      ' **  Name:          MONTH                      **
  9749.      ' **  Type:          Program                    **
  9750.      ' **  Module:        MONTH.BAS                  **
  9751.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9752.      ' ************************************************
  9753.      '
  9754.      ' Creates and displays a three-month calendar.
  9755.      ' USAGE:           No command line parameters
  9756.      ' .MAK FILE:       MONTH.BAS
  9757.      '                  CALENDAR.BAS
  9758.      ' PARAMETERS:      (none)
  9759.      ' VARIABLES:       year%      Year of concern
  9760.      '                  month%     Month of concern
  9761.      '                  quitFlag%  Indicates that program is to terminate
  9762.      '                  day%       Day near middle of the month
  9763.      '                  d2$        Date for second calendar month
  9764.      '                  j2&        Julian day number for second calendar month
  9765.      '                  d1$        Date for first calendar month
  9766.      '                  j1&        Julian day number for first calendar month
  9767.      '                  d3$        Date for third calendar month
  9768.      '                  j3&        Julian day number for third calendar month
  9769.      '                  k$         Key press character
  9770.  
  9771.      ' Constants
  9772.        CONST FALSE = 0
  9773.        CONST TRUE = NOT FALSE
  9774.  
  9775.      ' Functions
  9776.        DECLARE FUNCTION Date2Julian& (dat$)
  9777.        DECLARE FUNCTION MDY2Date$ (month%, day%, year%)
  9778.        DECLARE FUNCTION Date2Year% (dat$)
  9779.        DECLARE FUNCTION Date2Month% (dat$)
  9780.        DECLARE FUNCTION Julian2Date$ (julian&)
  9781.  
  9782.      ' Subprograms
  9783.        DECLARE SUB OneMonthCalendar (dat$, row%, col%)
  9784.  
  9785.      ' Get today's month and year
  9786.        year% = Date2Year%(DATE$)
  9787.        month% = Date2Month%(DATE$)
  9788.  
  9789.      ' Make calendars until the Esc key is pressed
  9790.        DO UNTIL quitFlag%
  9791.  
  9792.          ' Get Julian day number for about the middle of the month
  9793.            day% = 15
  9794.            d2$ = MDY2Date$(month%, day%, year%)
  9795.            j2& = Date2Julian&(d2$)
  9796.  
  9797.          ' Get last month's date
  9798.            j1& = j2& - 30
  9799.            d1$ = Julian2Date$(j1&)
  9800.  
  9801.          ' Get next month's date
  9802.            j3& = j2& + 30
  9803.            d3$ = Julian2Date$(j3&)
  9804.  
  9805.          ' Display the heading
  9806.            CLS
  9807.            LOCATE 1, 57
  9808.            PRINT "THREE-MONTH CALENDAR"
  9809.            LOCATE 2, 57
  9810.            PRINT "QuickBASIC 4.0"
  9811.  
  9812.          ' Create the three calendar sheets
  9813.            OneMonthCalendar d1$, 1, 1
  9814.            OneMonthCalendar d2$, 8, 25
  9815.            OneMonthCalendar d3$, 15, 49
  9816.  
  9817.          ' Display the instructions
  9818.            LOCATE 17, 1
  9819.            PRINT "Press <Y> to increment the year"
  9820.            LOCATE 18, 1
  9821.            PRINT "Press <y> to decrement the year"
  9822.            LOCATE 19, 1
  9823.            PRINT "Press <M> to increment the months"
  9824.            LOCATE 20, 1
  9825.            PRINT "Press <m> to decrement the months"
  9826.            LOCATE 22, 1
  9827.            PRINT "Press the Esc key to quit"
  9828.  
  9829.          ' Wait for a keystroke
  9830.            DO
  9831.                k$ = INKEY$
  9832.            LOOP UNTIL k$ <> ""
  9833.  
  9834.          ' Check for appropriate keystroke
  9835.            SELECT CASE k$
  9836.            CASE "y"
  9837.                year% = year% - 1
  9838.            CASE "Y"
  9839.                year% = year% + 1
  9840.            CASE "m"
  9841.                month% = month% - 3
  9842.            CASE "M"
  9843.                month% = month% + 3
  9844.            CASE CHR$(27)
  9845.                quitFlag% = TRUE
  9846.            CASE ELSE
  9847.            END SELECT
  9848.  
  9849.          ' Adjust month for proper range
  9850.            IF month% < 1 THEN
  9851.                month% = month% + 12
  9852.                year% = year% - 1
  9853.            ELSEIF month% > 12 THEN
  9854.                month% = month% - 12
  9855.                year% = year% + 1
  9856.            END IF
  9857.  
  9858.        LOOP
  9859.  
  9860.      ' All done
  9861.        END
  9862.    ──────────────────────────────────────────────────────────────────────────
  9863.  
  9864.  
  9865.  
  9866.  ────────────────────────────────────────────────────────────────────────────
  9867.  MOUSGCRS
  9868.  
  9869.    The MOUSGCRS program is a utility for designing graphics-mode mouse
  9870.    cursors.
  9871.  
  9872.    This program lets you create new graphics-mode cursors for programs that
  9873.    use the Microsoft Mouse. The program's output is a QuickBASIC subprogram
  9874.    file that other programs can load and use. To run MOUSGCRS, your computer
  9875.    must have CGA graphics capability and a mouse.
  9876.  
  9877.    This module can also be used as a toolbox for choosing any of the
  9878.    predefined cursors. For an example of a program using this module as a
  9879.    toolbox, see the OBJECT.BAS utility program.
  9880.  
  9881.    The MOUSE.ASM subprogram must be assembled and loaded with the QuickBASIC
  9882.    environment for this program to run correctly. See the MOUSE.ASM
  9883.    subprogram in Part III of this book for more information on loading this
  9884.    routine.
  9885.  
  9886.    Two masks are displayed while this program is running. The memory-resident
  9887.    mouse driver uses the screen mask to define areas of the cursor where the
  9888.    background pixels are to be left alone (0s) or blanked out (1s) before the
  9889.    cursor mask is displayed. Often, the screen-mask pixels define an area of
  9890.    the same shape but slightly larger than the cursor mask, creating an
  9891.    outline around the cursor when it's located on a pure white background.
  9892.  
  9893.    To edit a cursor, click with either the left or right mouse button on any
  9894.    of the small squares that make up the two masks. The left button sets
  9895.    pixel locations on, and the right button sets them off. To change the hot
  9896.    spot to a new location, press both mouse buttons simultaneously.
  9897.  
  9898.    When you're ready to try out your cursor creation, click on the "Try new
  9899.    cursor" box. A solid white area at the right side of the screen lets you
  9900.    view your new cursor against a white background.
  9901.  
  9902.    Click on the "Try standard cursors" box to select one of the predefined
  9903.    cursors. Each time you click on this box, the cursor changes to the next
  9904.    available predefined cursor type, allowing you to preview them all.
  9905.  
  9906.    When you click on the "Create cursor subroutine" box, the currently
  9907.    defined cursor masks are written to a QuickBASIC subprogram source file
  9908.    named GCURSOR.BAS. This file can be loaded by or merged with any program
  9909.    in which you want to use the new cursor. To create more than one cursor,
  9910.    be sure to rename the GCURSOR.BAS file after creating each cursor
  9911.    subprogram.
  9912.  
  9913.  
  9914.  Program Module: MOUSGCRS
  9915.  
  9916.    ──────────────────────────────────────────────────────────────────────────
  9917.      ' ************************************************
  9918.      ' **  Name:          MOUSGCRS                   **
  9919.      ' **  Type:          Program                    **
  9920.      ' **  Module:        MOUSGCRS.BAS               **
  9921.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  9922.      ' ************************************************
  9923.      '
  9924.      ' Program for the interactive design of graphics-
  9925.      ' mode mouse cursor subroutines.
  9926.      '
  9927.      ' USAGE:          No command line parameters
  9928.      ' REQUIREMENTS:   CGA
  9929.      '                 MIXED.QLB/.LIB
  9930.      '                 Mouse
  9931.      ' .MAK FILE:      MOUSGCRS.BAS
  9932.      '                 BITS.BAS
  9933.      '                 MOUSSUBS.BAS
  9934.      ' PARAMETERS:     (none)
  9935.      ' VARIABLES:      curs$()       Array of binary cursor string data
  9936.      '                 defaultMask$  Pattern mask for the default cursor
  9937.      '                 xdef%         Default hot spot X value
  9938.      '                 ydef%         Default hot spot Y value
  9939.      '                 mask$         Pattern mask for a cursor
  9940.      '                 xHot%         Hot spot X value
  9941.      '                 yHot%         Hot spot Y value
  9942.      '                 maskChr%      Index into the pattern mask
  9943.      '                 maskPtr%      Index to the background or foreground mas
  9944.      '                               pattern
  9945.      '                 y%            Cursor bit pointer, vertical
  9946.      '                 x%            Cursor bit pointer, horizontal
  9947.      '                 xbox%         X location on screen for cursor bit box
  9948.      '                 ybox%         Y location on screen for cursor bit box
  9949.      '                 xh%           Screen X location for hot spot
  9950.      '                 yh%           Screen Y location for hot spot
  9951.      '                 click$        DRAW string for creating the click boxes
  9952.      '                 quitFlag%     Indication that user wants to quit
  9953.      '                 t$            Copy of TIME$
  9954.      '                 toggle%       Once per second toggle for hot spot visib
  9955.      '                 pxl%          Pixel value at the hot spot
  9956.      '                 leftButton%   Current state of the left mouse button
  9957.      '                 rightButton%  Current state of the right mouse button
  9958.      '                 resetBox%     Indicates cursor is in the "Try standard
  9959.      '                               cursors" box
  9960.      '                 tryBox%       Indicates cursor is in the "Try new curso
  9961.      '                               box
  9962.      '                 subBox%       Indicates cursor is in the "Create cursor
  9963.      '                               subroutine" box
  9964.      '                 quitBox%      Indicates cursor is in the "Quit" box
  9965.      '                 xold%         X location of just-modified pixel box
  9966.      '                 yold%         Y location of just-modified pixel box
  9967.      '                 ix%           X bit pointer for pixel change
  9968.      '                 iy%           Y bit pointer for pixel change
  9969.      '                 q$            Double-quote character
  9970.  
  9971.      ' Define constants
  9972.        CONST FALSE = 0
  9973.        CONST TRUE = NOT FALSE
  9974.  
  9975.      ' Subprograms
  9976.        DECLARE SUB Cursdflt (mask$, xHot%, yHot%)
  9977.        DECLARE SUB Curschek (mask$, xHot%, yHot%)
  9978.        DECLARE SUB Curshand (mask$, xHot%, yHot%)
  9979.        DECLARE SUB Curshour (mask$, xHot%, yHot%)
  9980.        DECLARE SUB Cursjet (mask$, xHot%, yHot%)
  9981.        DECLARE SUB Cursleft (mask$, xHot%, yHot%)
  9982.        DECLARE SUB Cursplus (mask$, xHot%, yHot%)
  9983.        DECLARE SUB Cursup (mask$, xHot%, yHot%)
  9984.        DECLARE SUB Cursx (mask$, xHot%, yHot%)
  9985.        DECLARE SUB MouseShow ()
  9986.        DECLARE SUB MouseNow (lbutton%, rbutton%, xMouse%, yMouse%)
  9987.        DECLARE SUB MouseHide ()
  9988.        DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$)
  9989.        DECLARE SUB MouseSetGcursor (cursor$)
  9990.  
  9991.      ' Arrays
  9992.        DIM curs$(0 TO 8)
  9993.  
  9994.      ' Initialization
  9995.        SCREEN 2
  9996.        CLS
  9997.  
  9998.      ' Create set of cursors
  9999.        Cursdflt defaultMask$, xdef%, ydef%
  10000.        MouseMaskTranslate defaultMask$, xdef%, ydef%, curs$(0)
  10001.  
  10002.        Curschek mask$, xHot%, yHot%
  10003.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(1)
  10004.  
  10005.        Curshand mask$, xHot%, yHot%
  10006.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(2)
  10007.  
  10008.        Curshour mask$, xHot%, yHot%
  10009.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(3)
  10010.  
  10011.        Cursjet mask$, xHot%, yHot%
  10012.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(4)
  10013.  
  10014.        Cursleft mask$, xHot%, yHot%
  10015.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(5)
  10016.  
  10017.        Cursplus mask$, xHot%, yHot%
  10018.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(6)
  10019.  
  10020.        Cursup mask$, xHot%, yHot%
  10021.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(7)
  10022.  
  10023.        Cursx mask$, xHot%, yHot%
  10024.        MouseMaskTranslate mask$, xHot%, yHot%, curs$(8)
  10025.  
  10026.      ' Set the default cursor
  10027.        MouseSetGcursor curs$(0)
  10028.  
  10029.      ' Make the default cursor the starting point for editing
  10030.        mask$ = defaultMask$
  10031.        xHot% = xdef%
  10032.        yHot% = ydef%
  10033.  
  10034.      ' Place titles above pixel boxes
  10035.        LOCATE 2, 22, 0
  10036.        PRINT "Screen mask";
  10037.        LOCATE 2, 50, 0
  10038.        PRINT "Cursor mask";
  10039.  
  10040.      ' Outline the pixel boxes, filling the "ones" using the Mask$
  10041.        maskChr% = 0
  10042.        FOR maskPtr% = 0 TO 1
  10043.            FOR y% = 1 TO 16
  10044.                FOR x% = 1 TO 16
  10045.                    xbox% = x% * 12 + maskPtr% * 222 + 107
  10046.                    ybox% = y% * 9 + 10
  10047.                    maskChr% = maskChr% + 1
  10048.                    LINE (xbox%, ybox%)-(xbox% + 12, ybox% + 9), 1, B
  10049.                    IF MID$(mask$, maskChr%, 1) = "1" THEN
  10050.                        LINE (xbox% + 3, ybox% + 2)-(xbox% + 9, ybox% + 7), 1,
  10051.                    END IF
  10052.                    IF maskPtr% = 0 THEN
  10053.                        IF x% = xHot% + 1 AND y% = yHot% + 1 THEN
  10054.                            xh% = xbox%
  10055.                            yh% = ybox%
  10056.                        END IF
  10057.                    END IF
  10058.                NEXT x%
  10059.            NEXT y%
  10060.        NEXT maskPtr%
  10061.  
  10062.      ' Instruction text at bottom of display
  10063.        LOCATE 23, 1
  10064.        PRINT TAB(16); "Left button       Right button         Both buttons"
  10065.        PRINT TAB(16); "to set pixel      to clear pixel       for hot spot";
  10066.  
  10067.      ' Print menu items
  10068.        LOCATE 3, 2, 0
  10069.        PRINT "Try";
  10070.        LOCATE 4, 2, 0
  10071.        PRINT "standard";
  10072.        LOCATE 5, 2, 0
  10073.        PRINT "cursors";
  10074.        LOCATE 9, 2, 0
  10075.        PRINT "Try new";
  10076.        LOCATE 10, 2, 0
  10077.        PRINT "cursor";
  10078.        LOCATE 14, 2, 0
  10079.        PRINT "Create"
  10080.        LOCATE 15, 2, 0
  10081.        PRINT "cursor";
  10082.        LOCATE 16, 2, 0
  10083.        PRINT "subroutine";
  10084.        LOCATE 16, 74, 0
  10085.        PRINT "Quit";
  10086.  
  10087.      ' Make click box draw string
  10088.        click$ = "R20D10L20U10BF5BR1F3E6"
  10089.  
  10090.      ' Draw the click boxes
  10091.        DRAW "BM20,45" + click$
  10092.        DRAW "BM20,85" + click$
  10093.        DRAW "BM20,132" + click$
  10094.        DRAW "BM592,132" + click$
  10095.  
  10096.      ' Make a white cursor testing area
  10097.        LOCATE 5, 71
  10098.        PRINT "Cursor";
  10099.        LOCATE 6, 71
  10100.        PRINT "viewing";
  10101.        LOCATE 7, 71
  10102.        PRINT "area";
  10103.        LINE (560, 60)-(610, 100), 1, BF
  10104.  
  10105.      ' Turn on the mouse
  10106.        MouseShow
  10107.  
  10108.      ' Main processing loop control
  10109.        DO
  10110.            GOSUB MainLoop
  10111.        LOOP UNTIL quitFlag%
  10112.  
  10113.      ' Exit the loop and end program because Quitflag% has been set
  10114.        CLS
  10115.        SYSTEM
  10116.  
  10117.  
  10118.      ' Main processing loop
  10119.    MainLoop:
  10120.  
  10121.      ' Toggle the hot spot once per second
  10122.        IF t$ <> TIME$ THEN
  10123.            t$ = TIME$
  10124.            IF toggle% = 1 THEN
  10125.                toggle% = 0
  10126.            ELSE
  10127.                toggle% = 1
  10128.            END IF
  10129.            pxl% = POINT(xh% + 3, yh% + 2) XOR toggle%
  10130.            LINE (xh% + 5, yh% + 3)-(xh% + 7, yh% + 6), pxl%, BF
  10131.            pxl% = POINT(xh% + 3 + 222, yh% + 2) XOR toggle%
  10132.            LINE (xh% + 5 + 222, yh% + 3)-(xh% + 7 + 222, yh% + 6), pxl%, BF
  10133.        END IF
  10134.  
  10135.      ' What is the mouse location and button state right now?
  10136.        MouseNow leftButton%, rightButton%, x%, y%
  10137.  
  10138.      ' Are both buttons being pressed right now?
  10139.        IF leftButton% AND rightButton% THEN
  10140.            GOSUB WhichBox
  10141.            IF xbox% THEN
  10142.                GOSUB SetHotSpot
  10143.            END IF
  10144.        END IF
  10145.  
  10146.      ' Are we traversing the "Try standard cursors" click box?
  10147.        IF x% > 20 AND x% < 40 AND y% > 45 AND y% < 55 THEN
  10148.            IF resetBox% = 0 THEN
  10149.                MouseHide
  10150.                resetBox% = 1
  10151.                LINE (17, 43)-(43, 57), 1, B
  10152.                MouseShow
  10153.            END IF
  10154.        ELSE
  10155.            IF resetBox% = 1 THEN
  10156.                MouseHide
  10157.                resetBox% = 0
  10158.                LINE (17, 43)-(43, 57), 0, B
  10159.                MouseShow
  10160.            END IF
  10161.        END IF
  10162.  
  10163.      ' Are we traversing the "Try new cursor" click box?
  10164.        IF x% > 20 AND x% < 40 AND y% > 85 AND y% < 95 THEN
  10165.            IF tryBox% = 0 THEN
  10166.                MouseHide
  10167.                tryBox% = 1
  10168.                LINE (17, 83)-(43, 97), 1, B
  10169.                MouseShow
  10170.            END IF
  10171.        ELSE
  10172.            IF tryBox% = 1 THEN
  10173.                MouseHide
  10174.                tryBox% = 0
  10175.                LINE (17, 83)-(43, 97), 0, B
  10176.                MouseShow
  10177.            END IF
  10178.        END IF
  10179.  
  10180.      ' Are we traversing the "Create cursor subroutine" click box?
  10181.        IF x% > 20 AND x% < 40 AND y% > 132 AND y% < 142 THEN
  10182.            IF subBox% = 0 THEN
  10183.                MouseHide
  10184.                subBox% = 1
  10185.                LINE (17, 130)-(43, 144), 1, B
  10186.                MouseShow
  10187.            END IF
  10188.        ELSE
  10189.            IF subBox% = 1 THEN
  10190.                MouseHide
  10191.                subBox% = 0
  10192.                LINE (17, 130)-(43, 144), 0, B
  10193.                MouseShow
  10194.            END IF
  10195.        END IF
  10196.  
  10197.      ' Are we traversing the "Quit" click box?
  10198.        IF x% > 592 AND x% < 612 AND y% > 132 AND y% < 142 THEN
  10199.            IF quitBox% = 0 THEN
  10200.                MouseHide
  10201.                quitBox% = 1
  10202.                LINE (589, 130)-(615, 144), 1, B
  10203.                MouseShow
  10204.            END IF
  10205.        ELSE
  10206.            IF quitBox% = 1 THEN
  10207.                MouseHide
  10208.                quitBox% = 0
  10209.                LINE (589, 130)-(615, 144), 0, B
  10210.                MouseShow
  10211.            END IF
  10212.        END IF
  10213.  
  10214.      ' If just one button or the other is pressed, then check further
  10215.        IF leftButton% XOR rightButton% THEN
  10216.            GOSUB ButtonWasPressed
  10217.        ELSE
  10218.            xold% = 0
  10219.            yold% = 0
  10220.        END IF
  10221.  
  10222.      ' End of main loop
  10223.        RETURN
  10224.  
  10225.      ' Is the mouse currently pointing at a pixel box?
  10226.    WhichBox:
  10227.        IF x% > 320 THEN
  10228.            maskPtr% = 1
  10229.            x% = x% - 222
  10230.        ELSE
  10231.            maskPtr% = 0
  10232.        END IF
  10233.        ix% = (x% - 107) \ 12
  10234.        iy% = (y% - 10) \ 9
  10235.        xbox% = 0
  10236.        ybox% = 0
  10237.        IF ix% >= 1 AND ix% <= 16 THEN
  10238.            IF iy% >= 1 AND iy% <= 16 THEN
  10239.                xbox% = ix% * 12 + maskPtr% * 222 + 107
  10240.                ybox% = iy% * 9 + 10
  10241.            END IF
  10242.        END IF
  10243.        RETURN
  10244.  
  10245.      ' Move the hot spot to the current pixel box
  10246.    SetHotSpot:
  10247.        IF (xbox% <> xh% AND xbox% - 222 <> xh%) OR ybox% <> yh% THEN
  10248.            MouseHide
  10249.            pxl% = POINT(xh% + 3, yh% + 2)
  10250.            LINE (xh% + 5, yh% + 3)-(xh% + 7, yh% + 6), pxl%, BF
  10251.            pxl% = POINT(xh% + 3 + 222, yh% + 2)
  10252.            LINE (xh% + 5 + 222, yh% + 3)-(xh% + 7 + 222, yh% + 6), pxl%, BF
  10253.            MouseShow
  10254.            IF xbox% > 320 THEN
  10255.                xh% = xbox% - 222
  10256.            ELSE
  10257.                xh% = xbox%
  10258.            END IF
  10259.            yh% = ybox%
  10260.        END IF
  10261.        RETURN
  10262.  
  10263.      ' Process the button press depending on mouse location
  10264.    ButtonWasPressed:
  10265.        IF quitBox% THEN
  10266.            GOSUB DoQuitBox
  10267.        ELSEIF resetBox% THEN
  10268.            GOSUB DoResetCursor
  10269.        ELSEIF tryBox% THEN
  10270.            GOSUB DoSetNewCursor
  10271.        ELSEIF subBox% THEN
  10272.            GOSUB DoSetNewCursor
  10273.            GOSUB DoCreateSub
  10274.        ELSE
  10275.            GOSUB DoPixelControl
  10276.        END IF
  10277.        RETURN
  10278.  
  10279.      ' Button was pressed while mouse was in the "Quit" box
  10280.    DoQuitBox:
  10281.        MouseHide
  10282.        quitFlag% = TRUE
  10283.        RETURN
  10284.  
  10285.      ' Button was pressed while mouse was in the "Try new cursor" box
  10286.    DoSetNewCursor:
  10287.        MouseHide
  10288.        maskChr% = 0
  10289.        FOR maskPtr% = 0 TO 1
  10290.            FOR y% = 1 TO 16
  10291.                FOR x% = 1 TO 16
  10292.                    xbox% = x% * 12 + maskPtr% * 222 + 107
  10293.                    ybox% = y% * 9 + 10
  10294.                    maskChr% = maskChr% + 1
  10295.                    IF POINT(xbox% + 3, ybox% + 2) THEN
  10296.                        MID$(mask$, maskChr%, 1) = "1"
  10297.                    ELSE
  10298.                        MID$(mask$, maskChr%, 1) = "0"
  10299.                    END IF
  10300.                    IF xbox% = xh% AND ybox% = yh% THEN
  10301.                        xHot% = x% - 1
  10302.                        yHot% = y% - 1
  10303.                    END IF
  10304.                NEXT x%
  10305.            NEXT y%
  10306.        NEXT maskPtr%
  10307.        MouseMaskTranslate mask$, xHot%, yHot%, cursor$
  10308.        MouseSetGcursor cursor$
  10309.        MouseShow
  10310.        RETURN
  10311.  
  10312.      ' Button was pressed while mouse was in the "Try standard cursors" box
  10313.    DoResetCursor:
  10314.        MouseHide
  10315.        cursorIndex% = (cursorIndex% + 1) MOD 9
  10316.        MouseSetGcursor curs$(cursorIndex%)
  10317.        MouseShow
  10318.        DO
  10319.            MouseNow leftButton%, rightButton%, xMouse%, yMouse%
  10320.        LOOP UNTIL leftButton% = 0 AND rightButton% = 0
  10321.        RETURN
  10322.  
  10323.      ' Button was pressed while mouse was in the "Create cursor subroutine" bo
  10324.    DoCreateSub:
  10325.        q$ = CHR$(34)
  10326.        OPEN "GCURSOR.BAS" FOR OUTPUT AS #1
  10327.        PRINT #1, "   ' ************************************************"
  10328.        PRINT #1, "   ' **  Name:          Gcursor                    **"
  10329.        PRINT #1, "   ' **  Type:          Subprogram                 **"
  10330.        PRINT #1, "   ' **  Module:        GCURSOR.BAS                **"
  10331.        PRINT #1, "   ' **  Language:      Microsoft QuickBASIC 4.00  **"
  10332.        PRINT #1, "   ' ************************************************"
  10333.        PRINT #1, "   '"
  10334.        PRINT #1, "   SUB Gcursor (mask$, xHot%, yHot%) STATIC"
  10335.        PRINT #1, ""
  10336.        PRINT #1, "       mask$ = "; q$; q$
  10337.        FOR i% = 0 TO 31
  10338.            PRINT #1, "       mask$ = mask$ + ";
  10339.            PRINT #1, q$; MID$(mask$, 16 * i% + 1, 16); q$
  10340.            IF i% = 15 THEN
  10341.                PRINT #1, ""
  10342.            END IF
  10343.        NEXT i%
  10344.        PRINT #1, ""
  10345.        PRINT #1, "       xHot% ="; STR$(xHot%)
  10346.        PRINT #1, "       yHot% ="; STR$(yHot%)
  10347.        PRINT #1, ""
  10348.        PRINT #1, "   END SUB"
  10349.        RETURN
  10350.  
  10351.      ' Set or clear pixel box if mouse is on one
  10352.    DoPixelControl:
  10353.        GOSUB WhichBox
  10354.        IF xbox% THEN
  10355.            IF xold% <> xbox% OR yold% <> ybox% THEN
  10356.                xold% = xbox%
  10357.                yold% = ybox%
  10358.                MouseHide
  10359.                IF leftButton% THEN
  10360.                    LINE (xbox% + 3, ybox% + 2)-(xbox% + 9, ybox% + 7), 1, BF
  10361.                ELSE
  10362.                    LINE (xbox% + 3, ybox% + 2)-(xbox% + 9, ybox% + 7), 0, BF
  10363.                END IF
  10364.                MouseShow
  10365.            END IF
  10366.        END IF
  10367.        RETURN
  10368.    ──────────────────────────────────────────────────────────────────────────
  10369.  
  10370.  
  10371.  
  10372.  ────────────────────────────────────────────────────────────────────────────
  10373.  MOUSSUBS
  10374.  
  10375.    The MOUSSUBS toolbox presents a collection of subprograms for accessing
  10376.    and using your mouse. Your computer must have CGA graphics capability and
  10377.    a mouse for this program to be useful. If you have a mouse but are limited
  10378.    to monochrome text modes, see the MOUSTCRS.BAS module.
  10379.  
  10380.    The assembly-language subroutine named MOUSE.ASM must be assembled and
  10381.    linked with these routines or included in the user library loaded with
  10382.    QuickBASIC. See the MOUSE.ASM subprogram description in Part III of this
  10383.    book for more information on doing this.
  10384.  
  10385.    To use these subprograms in your own programs, load this module (along
  10386.    with the MOUSE.ASM routine), and be sure to declare the subprograms used
  10387.    by your main program module. For examples of programs that use this module
  10388.    as a toolbox, see the OBJECT.BAS, MOUSGCRS.BAS, MOUSTCRS.BAS, and
  10389.    WINDOWS.BAS program modules.
  10390.  
  10391.    Each subprogram that creates cursors defines a graphics-mode mouse cursor
  10392.    by filling in the pattern mask string and hot spot location variables.
  10393.    After this subprogram is called, call MouseMaskTranslate to translate the
  10394.    variables to a binary format string, which should then be passed to the
  10395.    MouseSetGcursor subprogram to quickly set the indicated cursor.
  10396.  
  10397.    You might find it helpful to follow the program listing as the interactive
  10398.    demonstration progresses.
  10399.  
  10400. ╓┌─┌─────────────────────────────┌──────┌────────────────────────────────────╖
  10401.    Name                          Type   Description
  10402.    ──────────────────────────────────────────────────────────────────────────
  10403.    MOUSSUBS.BAS                        Demo module
  10404.    Curschek                     Sub    Check mark mouse cursor
  10405.    Cursdflt                     Sub    Arrow mouse cursor pointing up and
  10406.                                         left
  10407.    Curshand                     Sub    Pointing hand mouse cursor
  10408.    Curshour                     Sub    Hourglass mouse cursor
  10409.    Cursjet                      Sub    Jet-shaped mouse cursor
  10410.    Cursleft                     Sub    Left arrow mouse cursor
  10411.    Cursplus                     Sub    Plus sign mouse cursor
  10412.    Cursup                       Sub    Up arrow mouse cursor
  10413.    Cursx                        Sub    X-mark mouse cursor
  10414.    MouseHide                    Sub    Turns off mouse visibility
  10415.    MouseInches                  Sub    Sets mouse-to-cursor motion ratio
  10416.    MouseInstall                 Sub    Checks mouse availability; resets
  10417.                                         mouse parameters
  10418.    Name                          Type   Description
  10419.    ──────────────────────────────────────────────────────────────────────────
  10420.                                        mouse parameters
  10421.    MouseLightPen                Sub    Mouse emulation of a lightpen
  10422.    MouseMaskTranslate           Sub    Translates pattern/hot spot to binary
  10423.    MouseMickey                  Sub    Returns motion increments since last
  10424.                                         call
  10425.    MouseNow                     Sub    Current state/location of the mouse
  10426.    MousePressLeft               Sub    Location of mouse──left button press
  10427.    MousePressRight              Sub    Location of mouse──right button press
  10428.    MousePut                     Sub    Moves cursor to the given position
  10429.    MouseRange                   Sub    Limits mouse cursor motion to
  10430.                                         rectangle
  10431.    MouseReleaseLeft             Sub    Location of mouse──left button
  10432.                                         release
  10433.    MouseReleaseRight            Sub    Location of mouse──right button
  10434.                                         release
  10435.    MouseSetGcursor              Sub    Sets graphics-mode mouse cursor
  10436.    MouseShow                    Sub    Activates and displays mouse cursor
  10437.    MouseSoftCursor              Sub    Sets text-mode attributes (mouse
  10438.                                         cursor)
  10439.    Name                          Type   Description
  10440.    ──────────────────────────────────────────────────────────────────────────
  10441.                                        cursor)
  10442.    MouseWarp                    Sub    Sets mouse double-speed threshold
  10443.    ──────────────────────────────────────────────────────────────────────────
  10444.  
  10445.  
  10446.  
  10447.  Demo Module: MOUSSUBS
  10448.  
  10449.    ──────────────────────────────────────────────────────────────────────────
  10450.      ' ************************************************
  10451.      ' **  Name:          MOUSSUBS                   **
  10452.      ' **  Type:          Toolbox                    **
  10453.      ' **  Module:        MOUSSUBS.BAS               **
  10454.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  10455.      ' ************************************************
  10456.      '
  10457.      ' Collection of subprograms for using the Microsoft Mouse.
  10458.      '
  10459.      ' Note:         The assembly-language subroutine named MOUSE.ASM
  10460.      '               must be assembled and linked with these routines
  10461.      '               or included in the user library loaded with
  10462.      '               QuickBASIC.
  10463.      ' USAGE:        No command line parameters
  10464.      ' REQUIREMENTS: CGA
  10465.      '               MIXED.QLB/.LIB
  10466.      '               Mouse
  10467.      ' .MAK FILE:   MOUSSUBS.BAS
  10468.      '              BITS.BAS
  10469.      ' PARAMETERS:  (none)
  10470.      ' VARIABLES:   i%            Looping index
  10471.      '              mask$         Pattern mask for each graphics mouse cursor
  10472.      '              xHot%         X hot spot location
  10473.      '              yHot%         Y hot spot location
  10474.      '              curs$         Binary bit pattern for defining mouse cursor
  10475.      '              j%            Test for left mouse button press and release
  10476.      '              leftButton%   State of left mouse button
  10477.      '              rightButton%  State of right mouse button
  10478.      '              xMouse%       X location of mouse
  10479.      '              yMouse%       Y location of mouse
  10480.      '              mflag%        Indicates mouse is available
  10481.      '              horizontal%   Horizontal mouse mickies
  10482.      '              vertical%     Vertical mouse mickies
  10483.      '              xpLeft%       X location of last left button press
  10484.      '              ypLeft%       Y location of last left button press
  10485.      '              xrLeft%       X location of last left button release
  10486.      '              yrLeft%       Y location of last left button release
  10487.      '              xpRight%      X location of last right button press
  10488.      '              ypRight%      Y location of last right button press
  10489.      '              xrRight%      X location of last right button release
  10490.      '              yrRight%      Y location of last right button release
  10491.      '              t0            Timer value
  10492.      '
  10493.      ' Functions
  10494.        DECLARE FUNCTION BinStr2Bin% (b$)
  10495.  
  10496.      ' Subprograms
  10497.        DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  10498.        DECLARE SUB MouseRange (x1%, y1%, x2%, y2%)
  10499.        DECLARE SUB MousePut (xMouse%, yMouse%)
  10500.        DECLARE SUB MouseHide ()
  10501.        DECLARE SUB MouseInches (horizontal%, vertical%)
  10502.        DECLARE SUB MouseInstall (mflag%)
  10503.        DECLARE SUB MouseMickey (horizontal%, vertical%)
  10504.        DECLARE SUB MousePressLeft (leftCount%, xMouse%, yMouse%)
  10505.        DECLARE SUB MousePressRight (rightCount%, xMouse%, yMouse%)
  10506.        DECLARE SUB MouseReleaseLeft (leftCount%, xMouse%, yMouse%)
  10507.        DECLARE SUB MouseReleaseRight (rightCount%, xMouse%, yMouse%)
  10508.        DECLARE SUB MouseWarp (threshold%)
  10509.        DECLARE SUB Cursdflt (mask$, xHot%, yHot%)
  10510.        DECLARE SUB Curschek (mask$, xHot%, yHot%)
  10511.        DECLARE SUB Curshand (mask$, xHot%, yHot%)
  10512.        DECLARE SUB Curshour (mask$, xHot%, yHot%)
  10513.        DECLARE SUB Cursjet (mask$, xHot%, yHot%)
  10514.        DECLARE SUB Cursleft (mask$, xHot%, yHot%)
  10515.        DECLARE SUB Cursplus (mask$, xHot%, yHot%)
  10516.        DECLARE SUB Cursup (mask$, xHot%, yHot%)
  10517.        DECLARE SUB Cursx (mask$, xHot%, yHot%)
  10518.        DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$)
  10519.        DECLARE SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%)
  10520.        DECLARE SUB MouseSetGcursor (cursor$)
  10521.        DECLARE SUB MouseShow ()
  10522.  
  10523.      ' Check for mouse
  10524.        SCREEN 2
  10525.        CLS
  10526.        MouseInstall mflag%
  10527.        PRINT "MouseInstall ... "; mflag%
  10528.  
  10529.      ' Demonstrate the available graphics-mode cursors
  10530.        PRINT
  10531.        PRINT "Click left mouse button to see the graphics-mode cursors..."
  10532.        MouseShow
  10533.  
  10534.        FOR i% = 1 TO 9
  10535.            SELECT CASE i%
  10536.            CASE 1
  10537.                Curschek mask$, xHot%, yHot%
  10538.            CASE 2
  10539.                Curshand mask$, xHot%, yHot%
  10540.            CASE 3
  10541.                Curshour mask$, xHot%, yHot%
  10542.            CASE 4
  10543.                Cursjet mask$, xHot%, yHot%
  10544.            CASE 5
  10545.                Cursleft mask$, xHot%, yHot%
  10546.            CASE 6
  10547.                Cursplus mask$, xHot%, yHot%
  10548.            CASE 7
  10549.                Cursup mask$, xHot%, yHot%
  10550.            CASE 8
  10551.                Cursx mask$, xHot%, yHot%
  10552.            CASE ELSE
  10553.                Cursdflt mask$, xHot%, yHot%
  10554.            END SELECT
  10555.            MouseMaskTranslate mask$, xHot%, yHot%, curs$
  10556.            FOR j% = -1 TO 0
  10557.                DO
  10558.                    MouseNow leftButton%, rightButton%, xMouse%, yMouse%
  10559.                LOOP UNTIL leftButton% = j%
  10560.            NEXT j%
  10561.            MouseSetGcursor curs$
  10562.        NEXT i%
  10563.  
  10564.      ' Mouse hide and show
  10565.        PRINT "MouseHide ... (Press any key to continue)"
  10566.        MouseHide
  10567.        DO
  10568.        LOOP UNTIL INKEY$ <> ""
  10569.        PRINT "MouseShow ... (Press any key to continue)"
  10570.        MouseShow
  10571.        DO
  10572.        LOOP UNTIL INKEY$ <> ""
  10573.  
  10574.      ' Mouse inches per screen
  10575.        MouseHide
  10576.        PRINT
  10577.        PRINT "Setting MouseWarp to 9999 to prevent doubling of speed."
  10578.        MouseWarp 9999
  10579.        PRINT
  10580.        PRINT "Setting MouseInches to 8 by 11. (8 inches of mouse motion"
  10581.        PRINT "across desk to move across screen, and 11 inches vertical"
  10582.        PRINT "mouse motion from top to bottom of screen) ..."
  10583.        PRINT
  10584.        PRINT "Press any key to continue"
  10585.        MouseInches 8, 11
  10586.        MouseShow
  10587.        DO
  10588.        LOOP UNTIL INKEY$ <> ""
  10589.  
  10590.      ' Resetting the mouse
  10591.        MouseHide
  10592.        PRINT
  10593.        PRINT "Resetting the mouse"
  10594.        MouseInstall mflag%
  10595.  
  10596.      ' Show realtime mouse data
  10597.        CLS
  10598.        PRINT "Instantaneous mouse information (Press any key to continue)"
  10599.        MouseShow
  10600.        DO
  10601.            MouseMickey horizontal%, vertical%
  10602.            MouseNow leftButton%, rightButton%, xMouse%, yMouse%
  10603.            MousePressLeft leftCount%, xpLeft%, ypLeft%
  10604.            MouseReleaseLeft leftCount%, xrLeft%, yrLeft%
  10605.            MousePressRight rightCount%, xpRight%, ypRight%
  10606.            MouseReleaseRight rightCount%, xrRight%, yrRight%
  10607.            LOCATE 3, 1
  10608.            PRINT "Mickies       ";
  10609.            PRINT USING "#######, #######"; horizontal%, vertical%
  10610.            PRINT "Position      ";
  10611.            PRINT USING "#######, #######"; xMouse%, yMouse%
  10612.            PRINT
  10613.            PRINT "Buttons       ";
  10614.            PRINT USING "#######, #######"; leftButton%, rightButton%
  10615.            PRINT
  10616.            PRINT "Left Press    ";
  10617.            PRINT USING "#######, #######"; xpLeft%, ypLeft%
  10618.            PRINT "Left Release  ";
  10619.            PRINT USING "#######, #######"; xrLeft%, yrLeft%
  10620.            PRINT
  10621.            PRINT "Right Press   ";
  10622.            PRINT USING "#######, #######"; xpRight%, ypRight%
  10623.            PRINT "Right Release ";
  10624.            PRINT USING "#######, #######"; xrRight%, yrRight%
  10625.        LOOP UNTIL INKEY$ <> ""
  10626.  
  10627.      ' Mouse placement
  10628.        CLS
  10629.        MouseHide
  10630.        PRINT "MousePut..."
  10631.        MouseShow
  10632.        FOR i% = 1 TO 20
  10633.            xMouse% = RND * 639
  10634.            yMouse% = RND * 199
  10635.            MousePut xMouse%, yMouse%
  10636.            t0 = TIMER
  10637.            DO
  10638.            LOOP UNTIL TIMER - t0 > .2
  10639.        NEXT i%
  10640.  
  10641.      ' Range limiting
  10642.        CLS
  10643.        MouseHide
  10644.        PRINT "Range limited to a rectangular area ..."
  10645.        PRINT "Press any key to continue"
  10646.        MouseShow
  10647.        MouseRange 200, 50, 400, 100
  10648.        DO
  10649.        LOOP UNTIL INKEY$ <> ""
  10650.  
  10651.      ' All done
  10652.        SCREEN 0
  10653.        CLS
  10654.    ──────────────────────────────────────────────────────────────────────────
  10655.  
  10656.  
  10657.  Subprogram: Curschek
  10658.  
  10659.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  10660.    values for defining a check mark cursor.
  10661.  
  10662.    ──────────────────────────────────────────────────────────────────────────
  10663.      ' ************************************************
  10664.      ' **  Name:          Curschek                   **
  10665.      ' **  Type:          Subprogram                 **
  10666.      ' **  Module:        MOUSSUBS.BAS               **
  10667.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  10668.      ' ************************************************
  10669.      '
  10670.      ' Defines a graphics-mode mouse cursor (check mark).
  10671.      '
  10672.      ' EXAMPLE OF USE:  Curschek mask$, xHot%, yHot%
  10673.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  10674.      '                  xHot%      X location for cursor hot spot
  10675.      '                  yHot%      Y location for cursor hot spot
  10676.      ' VARIABLES:       (none)
  10677.      ' MODULE LEVEL
  10678.      '   DECLARATIONS:  DECLARE SUB Curschek (mask$, xHot%, yHot%)
  10679.      '
  10680.        SUB Curschek (mask$, xHot%, yHot%) STATIC
  10681.  
  10682.            mask$ = ""
  10683.            mask$ = mask$ + "1111111111110000"
  10684.            mask$ = mask$ + "1111111111100000"
  10685.            mask$ = mask$ + "1111111111000000"
  10686.            mask$ = mask$ + "1111111110000001"
  10687.            mask$ = mask$ + "1111111100000011"
  10688.            mask$ = mask$ + "0000011000000111"
  10689.            mask$ = mask$ + "0000000000001111"
  10690.            mask$ = mask$ + "0000000000011111"
  10691.            mask$ = mask$ + "1100000000111111"
  10692.            mask$ = mask$ + "1111000001111111"
  10693.            mask$ = mask$ + "1111111111111111"
  10694.            mask$ = mask$ + "1111111111111111"
  10695.            mask$ = mask$ + "1111111111111111"
  10696.            mask$ = mask$ + "1111111111111111"
  10697.            mask$ = mask$ + "1111111111111111"
  10698.            mask$ = mask$ + "1111111111111111"
  10699.  
  10700.            mask$ = mask$ + "0000000000000000"
  10701.            mask$ = mask$ + "0000000000000110"
  10702.            mask$ = mask$ + "0000000000001100"
  10703.            mask$ = mask$ + "0000000000011000"
  10704.            mask$ = mask$ + "0000000000110000"
  10705.            mask$ = mask$ + "0000000001100000"
  10706.            mask$ = mask$ + "0111000011000000"
  10707.            mask$ = mask$ + "0001110110000000"
  10708.            mask$ = mask$ + "0000011100000000"
  10709.            mask$ = mask$ + "0000000000000000"
  10710.            mask$ = mask$ + "0000000000000000"
  10711.            mask$ = mask$ + "0000000000000000"
  10712.            mask$ = mask$ + "0000000000000000"
  10713.            mask$ = mask$ + "0000000000000000"
  10714.            mask$ = mask$ + "0000000000000000"
  10715.            mask$ = mask$ + "0000000000000000"
  10716.  
  10717.            xHot% = 6
  10718.            yHot% = 7
  10719.  
  10720.        END SUB
  10721.    ──────────────────────────────────────────────────────────────────────────
  10722.  
  10723.  
  10724.  Subprogram: Cursdflt
  10725.  
  10726.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  10727.    values for defining the default cursor (an arrow pointing up and left).
  10728.  
  10729.    ──────────────────────────────────────────────────────────────────────────
  10730.      ' ************************************************
  10731.      ' **  Name:          Cursdflt                   **
  10732.      ' **  Type:          Subprogram                 **
  10733.      ' **  Module:        MOUSSUBS.BAS               **
  10734.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  10735.      ' ************************************************
  10736.      '
  10737.      ' Defines a default graphics-mode mouse cursor (arrow pointing up and lef
  10738.      '
  10739.      ' EXAMPLE OF USE:  Cursdflt mask$, xHot%, yHot%
  10740.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  10741.      '                  xHot%      X location for cursor hot spot
  10742.      '                  yHot%      Y location for cursor hot spot
  10743.      ' VARIABLES:       (none)
  10744.      ' MODULE LEVEL
  10745.      '   DECLARATION:   DECLARE SUB Cursdflt (mask$, xHot%, yHot%)
  10746.      '
  10747.        SUB Cursdflt (mask$, xHot%, yHot%) STATIC
  10748.  
  10749.            mask$ = ""
  10750.            mask$ = mask$ + "1111111111111111"
  10751.            mask$ = mask$ + "1001111111111111"
  10752.            mask$ = mask$ + "1000111111111111"
  10753.            mask$ = mask$ + "1000011111111111"
  10754.            mask$ = mask$ + "1000001111111111"
  10755.            mask$ = mask$ + "1000000111111111"
  10756.            mask$ = mask$ + "1000000011111111"
  10757.            mask$ = mask$ + "1000000001111111"
  10758.            mask$ = mask$ + "1000000000111111"
  10759.            mask$ = mask$ + "1000000000011111"
  10760.            mask$ = mask$ + "1000000000001111"
  10761.            mask$ = mask$ + "1000000000000111"
  10762.            mask$ = mask$ + "1000100001111111"
  10763.            mask$ = mask$ + "1001100001111111"
  10764.            mask$ = mask$ + "1111110000111111"
  10765.            mask$ = mask$ + "1111110000111111"
  10766.  
  10767.            mask$ = mask$ + "0000000000000000"
  10768.            mask$ = mask$ + "0000000000000000"
  10769.            mask$ = mask$ + "0010000000000000"
  10770.            mask$ = mask$ + "0011000000000000"
  10771.            mask$ = mask$ + "0011100000000000"
  10772.            mask$ = mask$ + "0011110000000000"
  10773.            mask$ = mask$ + "0011111000000000"
  10774.            mask$ = mask$ + "0011111100000000"
  10775.            mask$ = mask$ + "0011111110000000"
  10776.            mask$ = mask$ + "0011111111000000"
  10777.            mask$ = mask$ + "0011111111100000"
  10778.            mask$ = mask$ + "0011111000000000"
  10779.            mask$ = mask$ + "0010001100000000"
  10780.    ──────────────────────────────────────────────────────────────────────────
  10781.  
  10782.            mask$ = mask$ + "0000001100000000"
  10783.  
  10784.            mask$ = mask$ + "0000000110000000"
  10785.  
  10786.            mask$ = mask$ + "0000000110000000"
  10787.  
  10788.  
  10789.  
  10790.            xHot% = 1
  10791.  
  10792.            yHot% = 1
  10793.  
  10794.  
  10795.  
  10796.        END SUB
  10797.  
  10798.  
  10799.  Subprogram: Curshand
  10800.  
  10801.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  10802.    values for defining a pointing hand cursor.
  10803.  
  10804.    ──────────────────────────────────────────────────────────────────────────
  10805.      ' ************************************************
  10806.      ' **  Name:          Curshand                   **
  10807.      ' **  Type:          Subprogram                 **
  10808.      ' **  Module:        MOUSSUBS.BAS               **
  10809.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  10810.      ' ************************************************
  10811.      '
  10812.      ' Defines a graphics-mode mouse cursor (pointing hand).
  10813.      '
  10814.      ' EXAMPLE OF USE:  Curshand mask$, xHot%, yHot%
  10815.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  10816.      '                  xHot%      X location for cursor hot spot
  10817.      '                  yHot%      Y location for cursor hot spot
  10818.      ' VARIABLES:       (none)
  10819.      ' MODULE LEVEL
  10820.      '   DECLARATIONS:  DECLARE SUB Curshand (mask$, xHot%, yHot%)
  10821.      '
  10822.        SUB Curshand (mask$, xHot%, yHot%) STATIC
  10823.  
  10824.            mask$ = ""
  10825.            mask$ = mask$ + "1110000111111111"
  10826.            mask$ = mask$ + "1110000111111111"
  10827.            mask$ = mask$ + "1110000111111111"
  10828.            mask$ = mask$ + "1110000111111111"
  10829.            mask$ = mask$ + "1110000111111111"
  10830.            mask$ = mask$ + "1110000000000000"
  10831.            mask$ = mask$ + "1110000000000000"
  10832.            mask$ = mask$ + "1110000000000000"
  10833.            mask$ = mask$ + "0000000000000000"
  10834.            mask$ = mask$ + "0000000000000000"
  10835.            mask$ = mask$ + "0000000000000000"
  10836.            mask$ = mask$ + "0000000000000000"
  10837.            mask$ = mask$ + "0000000000000000"
  10838.            mask$ = mask$ + "0000000000000000"
  10839.            mask$ = mask$ + "0000000000000000"
  10840.            mask$ = mask$ + "0000000000000000"
  10841.  
  10842.            mask$ = mask$ + "0001111000000000"
  10843.            mask$ = mask$ + "0001001000000000"
  10844.            mask$ = mask$ + "0001001000000000"
  10845.            mask$ = mask$ + "0001001000000000"
  10846.            mask$ = mask$ + "0001001000000000"
  10847.            mask$ = mask$ + "0001001111111111"
  10848.            mask$ = mask$ + "0001001001001001"
  10849.            mask$ = mask$ + "0001001001001001"
  10850.            mask$ = mask$ + "1111001001001001"
  10851.            mask$ = mask$ + "1001000000000001"
  10852.            mask$ = mask$ + "1001000000000001"
  10853.            mask$ = mask$ + "1001000000000001"
  10854.            mask$ = mask$ + "1000000000000001"
  10855.            mask$ = mask$ + "1000000000000001"
  10856.            mask$ = mask$ + "1000000000000001"
  10857.            mask$ = mask$ + "1111111111111111"
  10858.  
  10859.            xHot% = 5
  10860.            yHot% = 0
  10861.  
  10862.        END SUB
  10863.    ──────────────────────────────────────────────────────────────────────────
  10864.  
  10865.  
  10866.  Subprogram: Curshour
  10867.  
  10868.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  10869.    values for defining an hourglass cursor.
  10870.  
  10871.    ──────────────────────────────────────────────────────────────────────────
  10872.      ' ************************************************
  10873.      ' **  Name:          Curshour                   **
  10874.      ' **  Type:          Subprogram                 **
  10875.      ' **  Module:        MOUSSUBS.BAS               **
  10876.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  10877.      ' ************************************************
  10878.      '
  10879.      ' Defines a graphics-mode mouse cursor (hourglass).
  10880.      '
  10881.      ' EXAMPLE OF USE:  Curshour mask$, xHot%, yHot%
  10882.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  10883.      '                  xHot%      X location for cursor hot spot
  10884.      '                  yHot%      Y location for cursor hot spot
  10885.      ' VARIABLES:       (none)
  10886.      ' MODULE LEVEL
  10887.      '   DECLARATIONS:    DECLARE SUB Curshour (mask$, xHot%, yHot%)
  10888.      '
  10889.        SUB Curshour (mask$, xHot%, yHot%) STATIC
  10890.  
  10891.            mask$ = ""
  10892.            mask$ = mask$ + "0000000000000000"
  10893.            mask$ = mask$ + "0000000000000000"
  10894.            mask$ = mask$ + "0000000000000000"
  10895.            mask$ = mask$ + "1000000000000001"
  10896.            mask$ = mask$ + "1100000000000011"
  10897.            mask$ = mask$ + "1110000000000111"
  10898.            mask$ = mask$ + "1111000000001111"
  10899.            mask$ = mask$ + "1110000000000111"
  10900.            mask$ = mask$ + "1100000000000011"
  10901.            mask$ = mask$ + "1000000000000001"
  10902.            mask$ = mask$ + "0000000000000000"
  10903.            mask$ = mask$ + "0000000000000000"
  10904.            mask$ = mask$ + "0000000000000000"
  10905.            mask$ = mask$ + "0000000000000000"
  10906.            mask$ = mask$ + "0000000000000000"
  10907.            mask$ = mask$ + "0000000000000000"
  10908.  
  10909.            mask$ = mask$ + "0000000000000000"
  10910.            mask$ = mask$ + "0111111111111110"
  10911.            mask$ = mask$ + "0110000000000110"
  10912.            mask$ = mask$ + "0011000000001100"
  10913.            mask$ = mask$ + "0001100000011000"
  10914.            mask$ = mask$ + "0000110000110000"
  10915.            mask$ = mask$ + "0000011001100000"
  10916.            mask$ = mask$ + "0000001111000000"
  10917.            mask$ = mask$ + "0000011001100000"
  10918.            mask$ = mask$ + "0000110000110000"
  10919.            mask$ = mask$ + "0001100110011000"
  10920.            mask$ = mask$ + "0011001111001100"
  10921.            mask$ = mask$ + "0110011111100110"
  10922.            mask$ = mask$ + "0111111111111110"
  10923.            mask$ = mask$ + "0000000000000000"
  10924.            mask$ = mask$ + "0000000000000000"
  10925.  
  10926.            xHot% = 7
  10927.            yHot% = 7
  10928.  
  10929.        END SUB
  10930.    ──────────────────────────────────────────────────────────────────────────
  10931.  
  10932.  
  10933.  Subprogram: Cursjet
  10934.  
  10935.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  10936.    values for defining a jet aircraft cursor.
  10937.  
  10938.    ──────────────────────────────────────────────────────────────────────────
  10939.      ' ************************************************
  10940.      ' **  Name:          Cursjet                    **
  10941.      ' **  Type:          Subprogram                 **
  10942.      ' **  Module:        MOUSSUBS.BAS               **
  10943.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  10944.      ' ************************************************
  10945.      '
  10946.      ' Defines a graphics-mode mouse cursor (jet aircraft).
  10947.      '
  10948.      ' EXAMPLE OF USE:  Cursjet mask$, xHot%, yHot%
  10949.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  10950.      '                  xHot%      X location for cursor hot spot
  10951.      '                  yHot%      Y location for cursor hot spot
  10952.      ' VARIABLES:       (none)
  10953.      ' MODULE LEVEL
  10954.      '   DECLARATIONS:  DECLARE SUB Cursjet (mask$, xHot%, yHot%)
  10955.      '
  10956.        SUB Cursjet (mask$, xHot%, yHot%) STATIC
  10957.  
  10958.            mask$ = ""
  10959.            mask$ = mask$ + "1111111111111111"
  10960.            mask$ = mask$ + "1111111011111111"
  10961.            mask$ = mask$ + "1111110001111111"
  10962.            mask$ = mask$ + "1111100000111111"
  10963.            mask$ = mask$ + "1111100000111111"
  10964.            mask$ = mask$ + "1111100000111111"
  10965.            mask$ = mask$ + "1111000000011111"
  10966.            mask$ = mask$ + "1110000000001111"
  10967.            mask$ = mask$ + "1100000000000111"
  10968.            mask$ = mask$ + "1000000000000011"
  10969.            mask$ = mask$ + "1000000000000011"
  10970.            mask$ = mask$ + "1111100000111111"
  10971.            mask$ = mask$ + "1111100000111111"
  10972.            mask$ = mask$ + "1111000000011111"
  10973.            mask$ = mask$ + "1110000000001111"
  10974.            mask$ = mask$ + "1111111111111111"
  10975.  
  10976.            mask$ = mask$ + "0000000000000000"
  10977.            mask$ = mask$ + "0000000000000000"
  10978.            mask$ = mask$ + "0000000100000000"
  10979.            mask$ = mask$ + "0000001110000000"
  10980.            mask$ = mask$ + "0000001110000000"
  10981.            mask$ = mask$ + "0000001110000000"
  10982.            mask$ = mask$ + "0000011111000000"
  10983.            mask$ = mask$ + "0000111111100000"
  10984.            mask$ = mask$ + "0001111111110000"
  10985.            mask$ = mask$ + "0011111111111000"
  10986.            mask$ = mask$ + "0110001110001100"
  10987.            mask$ = mask$ + "0000001110000000"
  10988.            mask$ = mask$ + "0000001110000000"
  10989.            mask$ = mask$ + "0000011111000000"
  10990.            mask$ = mask$ + "0000110001100000"
  10991.            mask$ = mask$ + "0000000000000000"
  10992.  
  10993.            xHot% = 7
  10994.            yHot% = 1
  10995.  
  10996.        END SUB
  10997.    ──────────────────────────────────────────────────────────────────────────
  10998.  
  10999.  
  11000.  Subprogram: Cursleft
  11001.  
  11002.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  11003.    values for defining a left arrow cursor.
  11004.  
  11005.    ──────────────────────────────────────────────────────────────────────────
  11006.      ' ************************************************
  11007.      ' **  Name:          Cursleft                   **
  11008.      ' **  Type:          Subprogram                 **
  11009.      ' **  Module:        MOUSSUBS.BAS               **
  11010.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11011.      ' ************************************************
  11012.      '
  11013.      ' Defines a graphics-mode mouse cursor (left arrow).
  11014.      '
  11015.      ' EXAMPLE OF USE:  Cursleft mask$, xHot%, yHot%
  11016.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  11017.      '                  xHot%      X location for cursor hot spot
  11018.      '                  yHot%      Y location for cursor hot spot
  11019.      ' VARIABLES:       (none)
  11020.      ' MODULE LEVEL
  11021.      '   DECLARATIONS:    DECLARE SUB Cursleft (mask$, xHot%, yHot%)
  11022.      '
  11023.        SUB Cursleft (mask$, xHot%, yHot%) STATIC
  11024.  
  11025.            mask$ = ""
  11026.            mask$ = mask$ + "1111111000011111"
  11027.            mask$ = mask$ + "1111000000011111"
  11028.            mask$ = mask$ + "0000000000000000"
  11029.            mask$ = mask$ + "0000000000000000"
  11030.            mask$ = mask$ + "0000000000000000"
  11031.            mask$ = mask$ + "1111000000011111"
  11032.            mask$ = mask$ + "1111111000011111"
  11033.            mask$ = mask$ + "1111111111111111"
  11034.            mask$ = mask$ + "1111111111111111"
  11035.            mask$ = mask$ + "1111111111111111"
  11036.            mask$ = mask$ + "1111111111111111"
  11037.            mask$ = mask$ + "1111111111111111"
  11038.            mask$ = mask$ + "1111111111111111"
  11039.            mask$ = mask$ + "1111111111111111"
  11040.            mask$ = mask$ + "1111111111111111"
  11041.            mask$ = mask$ + "1111111111111111"
  11042.  
  11043.            mask$ = mask$ + "0000000000000000"
  11044.            mask$ = mask$ + "0000000011000000"
  11045.            mask$ = mask$ + "0000011111000000"
  11046.            mask$ = mask$ + "0111111111111110"
  11047.            mask$ = mask$ + "0000011111000000"
  11048.            mask$ = mask$ + "0000000011000000"
  11049.            mask$ = mask$ + "0000000000000000"
  11050.            mask$ = mask$ + "0000000000000000"
  11051.            mask$ = mask$ + "0000000000000000"
  11052.            mask$ = mask$ + "0000000000000000"
  11053.            mask$ = mask$ + "0000000000000000"
  11054.            mask$ = mask$ + "0000000000000000"
  11055.            mask$ = mask$ + "0000000000000000"
  11056.            mask$ = mask$ + "0000000000000000"
  11057.            mask$ = mask$ + "0000000000000000"
  11058.            mask$ = mask$ + "0000000000000000"
  11059.  
  11060.            xHot% = 0
  11061.            yHot% = 3
  11062.  
  11063.        END SUB
  11064.    ──────────────────────────────────────────────────────────────────────────
  11065.  
  11066.  
  11067.  Subprogram: Cursplus
  11068.  
  11069.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  11070.    values for defining a plus sign cursor.
  11071.  
  11072.    ──────────────────────────────────────────────────────────────────────────
  11073.      ' ************************************************
  11074.      ' **  Name:          Cursplus                   **
  11075.      ' **  Type:          Subprogram                 **
  11076.      ' **  Module:        MOUSSUBS.BAS               **
  11077.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11078.      ' ************************************************
  11079.      '
  11080.      ' Defines a graphics-mode mouse cursor (plus sign).
  11081.      '
  11082.      ' EXAMPLE OF USE:  Cursplus mask$, xHot%, yHot%
  11083.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  11084.      '                  xHot%      X location for cursor hot spot
  11085.      '                  yHot%      Y location for cursor hot spot
  11086.      ' VARIABLES:       (none)
  11087.      ' MODULE LEVEL
  11088.      '   DECLARATIONS:  DECLARE SUB Cursplus (mask$, xHot%, yHot%)
  11089.      '
  11090.        SUB Cursplus (mask$, xHot%, yHot%) STATIC
  11091.  
  11092.            mask$ = ""
  11093.            mask$ = mask$ + "1111110000111111"
  11094.            mask$ = mask$ + "1111110000111111"
  11095.            mask$ = mask$ + "1111110000111111"
  11096.            mask$ = mask$ + "0000000000000000"
  11097.            mask$ = mask$ + "0000000000000000"
  11098.            mask$ = mask$ + "0000000000000000"
  11099.            mask$ = mask$ + "1111110000111111"
  11100.            mask$ = mask$ + "1111110000111111"
  11101.            mask$ = mask$ + "1111110000111111"
  11102.            mask$ = mask$ + "1111111111111111"
  11103.            mask$ = mask$ + "1111111111111111"
  11104.            mask$ = mask$ + "1111111111111111"
  11105.            mask$ = mask$ + "1111111111111111"
  11106.            mask$ = mask$ + "1111111111111111"
  11107.            mask$ = mask$ + "1111111111111111"
  11108.            mask$ = mask$ + "1111111111111111"
  11109.  
  11110.            mask$ = mask$ + "0000000000000000"
  11111.            mask$ = mask$ + "0000000110000000"
  11112.            mask$ = mask$ + "0000000110000000"
  11113.            mask$ = mask$ + "0000000110000000"
  11114.            mask$ = mask$ + "0111111111111110"
  11115.            mask$ = mask$ + "0000000110000000"
  11116.            mask$ = mask$ + "0000000110000000"
  11117.            mask$ = mask$ + "0000000110000000"
  11118.            mask$ = mask$ + "0000000000000000"
  11119.            mask$ = mask$ + "0000000000000000"
  11120.            mask$ = mask$ + "0000000000000000"
  11121.            mask$ = mask$ + "0000000000000000"
  11122.            mask$ = mask$ + "0000000000000000"
  11123.            mask$ = mask$ + "0000000000000000"
  11124.            mask$ = mask$ + "0000000000000000"
  11125.            mask$ = mask$ + "0000000000000000"
  11126.  
  11127.            xHot% = 7
  11128.            yHot% = 4
  11129.  
  11130.        END SUB
  11131.    ──────────────────────────────────────────────────────────────────────────
  11132.  
  11133.  
  11134.  Subprogram: Cursup
  11135.  
  11136.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  11137.    values for defining an up arrow cursor.
  11138.  
  11139.    ──────────────────────────────────────────────────────────────────────────
  11140.      ' ************************************************
  11141.      ' **  Name:          Cursup                     **
  11142.      ' **  Type:          Subprogram                 **
  11143.      ' **  Module:        MOUSSUBS.BAS               **
  11144.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11145.      ' ************************************************
  11146.      '
  11147.      ' Defines a graphics-mode mouse cursor (up arrow).
  11148.      '
  11149.      ' EXAMPLE OF USE:  Cursup mask$, xHot%, yHot%
  11150.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  11151.      '                  xHot%      X location for cursor hot spot
  11152.      '                  yHot%      Y location for cursor hot spot
  11153.      ' VARIABLES:       (none)
  11154.      ' MODULE LEVEL
  11155.      '   DECLARATIONS:  DECLARE SUB Cursup (mask$, xHot%, yHot%)
  11156.      '
  11157.        SUB Cursup (mask$, xHot%, yHot%) STATIC
  11158.  
  11159.            mask$ = ""
  11160.            mask$ = mask$ + "1111100111111111"
  11161.            mask$ = mask$ + "1111000011111111"
  11162.            mask$ = mask$ + "1110000001111111"
  11163.            mask$ = mask$ + "1110000001111111"
  11164.            mask$ = mask$ + "1100000000111111"
  11165.            mask$ = mask$ + "1100000000111111"
  11166.            mask$ = mask$ + "1000000000011111"
  11167.            mask$ = mask$ + "1000000000011111"
  11168.            mask$ = mask$ + "0000000000001111"
  11169.            mask$ = mask$ + "0000000000001111"
  11170.            mask$ = mask$ + "1111000011111111"
  11171.            mask$ = mask$ + "1111000011111111"
  11172.            mask$ = mask$ + "1111000011111111"
  11173.            mask$ = mask$ + "1111000011111111"
  11174.            mask$ = mask$ + "1111000011111111"
  11175.            mask$ = mask$ + "1111000011111111"
  11176.  
  11177.            mask$ = mask$ + "0000000000000000"
  11178.            mask$ = mask$ + "0000011000000000"
  11179.            mask$ = mask$ + "0000111100000000"
  11180.            mask$ = mask$ + "0000111100000000"
  11181.            mask$ = mask$ + "0001111110000000"
  11182.            mask$ = mask$ + "0001111110000000"
  11183.            mask$ = mask$ + "0011111111000000"
  11184.            mask$ = mask$ + "0011111111000000"
  11185.            mask$ = mask$ + "0111111111100000"
  11186.            mask$ = mask$ + "0000011000000000"
  11187.            mask$ = mask$ + "0000011000000000"
  11188.            mask$ = mask$ + "0000011000000000"
  11189.            mask$ = mask$ + "0000011000000000"
  11190.            mask$ = mask$ + "0000011000000000"
  11191.            mask$ = mask$ + "0000011000000000"
  11192.            mask$ = mask$ + "0000000000000000"
  11193.  
  11194.            xHot% = 5
  11195.            yHot% = 0
  11196.  
  11197.        END SUB
  11198.    ──────────────────────────────────────────────────────────────────────────
  11199.  
  11200.  
  11201.  Subprogram: Cursx
  11202.  
  11203.    Creates a graphics mouse cursor; fills in the pattern mask and hot spot
  11204.    values for defining an X-mark cursor.
  11205.  
  11206.    ──────────────────────────────────────────────────────────────────────────
  11207.      ' ************************************************
  11208.      ' **  Name:          Cursx                      **
  11209.      ' **  Type:          Subprogram                 **
  11210.      ' **  Module:        MOUSSUBS.BAS               **
  11211.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11212.      ' ************************************************
  11213.      '
  11214.      ' Defines a graphics-mode mouse cursor (X mark).
  11215.      '
  11216.      ' EXAMPLE OF USE:  Cursx mask$, xHot%, yHot%
  11217.      ' PARAMETERS:      mask$      Pattern mask for creating cursor
  11218.      '                  xHot%      X location for cursor hot spot
  11219.      '                  yHot%      Y location for cursor hot spot
  11220.      ' VARIABLES:       (none)
  11221.      ' MODULE LEVEL
  11222.      '   DECLARATIONS:  DECLARE SUB Cursx (mask$, xHot%, yHot%)
  11223.      '
  11224.        SUB Cursx (mask$, xHot%, yHot%) STATIC
  11225.  
  11226.            mask$ = ""
  11227.            mask$ = mask$ + "0000011111100000"
  11228.            mask$ = mask$ + "0000000110000000"
  11229.            mask$ = mask$ + "0000000000000000"
  11230.            mask$ = mask$ + "1100000000000011"
  11231.            mask$ = mask$ + "1111000000001111"
  11232.            mask$ = mask$ + "1100000000000011"
  11233.            mask$ = mask$ + "0000000000000000"
  11234.            mask$ = mask$ + "0000000110000000"
  11235.            mask$ = mask$ + "0000001111000000"
  11236.            mask$ = mask$ + "1111111111111111"
  11237.            mask$ = mask$ + "1111111111111111"
  11238.            mask$ = mask$ + "1111111111111111"
  11239.            mask$ = mask$ + "1111111111111111"
  11240.            mask$ = mask$ + "1111111111111111"
  11241.            mask$ = mask$ + "1111111111111111"
  11242.            mask$ = mask$ + "1111111111111111"
  11243.  
  11244.            mask$ = mask$ + "0000000000000000"
  11245.            mask$ = mask$ + "0111000000001110"
  11246.            mask$ = mask$ + "0001110000111000"
  11247.            mask$ = mask$ + "0000011001100000"
  11248.            mask$ = mask$ + "0000001111000000"
  11249.            mask$ = mask$ + "0000011001100000"
  11250.            mask$ = mask$ + "0001110000111000"
  11251.            mask$ = mask$ + "0111000000001110"
  11252.            mask$ = mask$ + "0000000000000000"
  11253.            mask$ = mask$ + "0000000000000000"
  11254.            mask$ = mask$ + "0000000000000000"
  11255.            mask$ = mask$ + "0000000000000000"
  11256.            mask$ = mask$ + "0000000000000000"
  11257.            mask$ = mask$ + "0000000000000000"
  11258.            mask$ = mask$ + "0000000000000000"
  11259.            mask$ = mask$ + "0000000000000000"
  11260.  
  11261.            xHot% = 7
  11262.            yHot% = 4
  11263.  
  11264.        END SUB
  11265.    ──────────────────────────────────────────────────────────────────────────
  11266.  
  11267.  
  11268.  Subprogram: MouseHide
  11269.  
  11270.    Deactivates the mouse cursor, making it invisible and inaccessible. Use
  11271.    the MouseShow subprogram to reactivate the cursor.
  11272.  
  11273.    ──────────────────────────────────────────────────────────────────────────
  11274.      ' ************************************************
  11275.      ' **  Name:          MouseHide                  **
  11276.      ' **  Type:          Subprogram                 **
  11277.      ' **  Module:        MOUSSUBS.BAS               **
  11278.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11279.      ' ************************************************
  11280.      '
  11281.      ' Hides the mouse cursor.
  11282.      '
  11283.      ' EXAMPLE OF USE:  MouseHide
  11284.      '
  11285.      ' PARAMETERS:      (none)
  11286.      ' VARIABLES:       (none)
  11287.      ' MODULE LEVEL
  11288.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11289.      '                  DECLARE SUB MouseHide ()
  11290.      '
  11291.        SUB MouseHide STATIC
  11292.            Mouse 2, 0, 0, 0
  11293.        END SUB
  11294.    ──────────────────────────────────────────────────────────────────────────
  11295.  
  11296.  
  11297.  Subprogram: MouseInches
  11298.  
  11299.    Sets the ratio of mouse motion to cursor motion. The horizontal% and
  11300.    vertical% parameters indicate the number of inches of desktop motion that
  11301.    your mouse must move to move the mouse cursor from one edge of the screen
  11302.    to the other. Note that the vertical and horizontal values are independent
  11303.    of each other.
  11304.  
  11305.    Before calling this subprogram, set the double-speed threshold to a large
  11306.    value by calling the MouseWarp subprogram. This prevents fast mouse
  11307.    motion from doubling the cursor velocity, keeping the motion ratios
  11308.    constant.
  11309.  
  11310.    ──────────────────────────────────────────────────────────────────────────
  11311.      ' ************************************************
  11312.      ' **  Name:          MouseInches                **
  11313.      ' **  Type:          Subprogram                 **
  11314.      ' **  Module:        MOUSSUBS.BAS               **
  11315.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11316.      ' ************************************************
  11317.      '
  11318.      ' Sets mouse motion ratio in inches per screen.
  11319.      '
  11320.      ' EXAMPLE OF USE:  MouseInches horizontal%, vertical%
  11321.      ' PARAMETERS:      horizontal%   Inches of horizontal mouse motion per
  11322.      '                                screen width
  11323.      '                  vertical%     Inches of vertical% mouse motion per
  11324.      '                                screen height
  11325.      ' VARIABLES:       h%            Calculated value to pass to mouse driver
  11326.      '                  v%            Calculated value to pass to mouse driver
  11327.      ' MODULE LEVEL
  11328.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11329.      '                  DECLARE SUB MouseInches (horizontal%, vertical%)
  11330.      '
  11331.        SUB MouseInches (horizontal%, vertical%) STATIC
  11332.            IF horizontal% > 100 THEN
  11333.                horizontal% = 100
  11334.            END IF
  11335.            IF vertical% > 100 THEN
  11336.                vertical% = 100
  11337.            END IF
  11338.            h% = horizontal% * 5 \ 2
  11339.            v% = vertical% * 8
  11340.            Mouse 15, 0, h%, v%
  11341.        END SUB
  11342.    ──────────────────────────────────────────────────────────────────────────
  11343.  
  11344.  
  11345.  Subprogram: MouseInstall
  11346.  
  11347.    Checks the memory-resident mouse driver to determine whether a mouse is
  11348.    available. The value of mflag% is returned as 0 if no mouse is available
  11349.    and as -1 if one is.
  11350.  
  11351.    This subprogram also initializes the mouse driver to the default state.
  11352.    The original cursor is set, and the mouse velocity, threshold, and other
  11353.    parameters are all set to their original states.
  11354.  
  11355.    Normally, this subprogram is called immediately when a program is run.
  11356.  
  11357.    ──────────────────────────────────────────────────────────────────────────
  11358.      ' ************************************************
  11359.      ' **  Name:          MouseInstall               **
  11360.      ' **  Type:          Subprogram                 **
  11361.      ' **  Module:        MOUSSUBS.BAS               **
  11362.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11363.      ' ************************************************
  11364.      '
  11365.      ' Determines whether mouse is available and resets all mouse parameters.
  11366.      '
  11367.      ' EXAMPLE OF USE:  MouseInstall mflag%
  11368.      ' PARAMETERS:      mflag%     Returned indication of mouse availability
  11369.      ' VARIABLES:       (none)
  11370.      ' MODULE LEVEL
  11371.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11372.      '                  DECLARE SUB MouseInstall (mflag%)
  11373.      '
  11374.        SUB MouseInstall (mflag%) STATIC
  11375.            mflag% = 0
  11376.            Mouse mflag%, 0, 0, 0
  11377.        END SUB
  11378.    ──────────────────────────────────────────────────────────────────────────
  11379.  
  11380.  
  11381.  Subprogram: MouseLightPen
  11382.  
  11383.    Activates or deactivates lightpen emulation by the mouse.
  11384.  
  11385.    The QuickBASIC PEN function provides ten unique functions for accessing
  11386.    information on the lightpen, depending on the parameter you pass to it.
  11387.    This complete set of lightpen functions can be emulated using the mouse
  11388.    rather than the lightpen. To activate lightpen emulation, call
  11389.    MouseLightPen with a non-zero parameter. To deactivate lightpen emulation,
  11390.    use a zero parameter.
  11391.  
  11392.    ──────────────────────────────────────────────────────────────────────────
  11393.      ' ************************************************
  11394.      ' **  Name:          MouseLightPen              **
  11395.      ' **  Type:          Subprogram                 **
  11396.      ' **  Module:        MOUSSUBS.BAS               **
  11397.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11398.      ' ************************************************
  11399.  
  11400.      ' Activates and deactivates lightpen emulation mode.
  11401.      '
  11402.      ' EXAMPLE OF USE:  MouseLightPen switch%
  11403.      ' PARAMETERS:      switch%    non-zero to activate lightpen emulation,
  11404.      '                             zero to deactivate
  11405.      ' VARIABLES:       (none)
  11406.      ' MODULE LEVEL
  11407.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11408.      '                  DECLARE SUB MouseLightPen (switch%)
  11409.      '
  11410.        SUB MouseLightPen (switch%) STATIC
  11411.            IF switch% THEN
  11412.                Mouse 13, 0, 0, 0
  11413.            ELSE
  11414.                Mouse 14, 0, 0, 0
  11415.            END IF
  11416.        END SUB
  11417.    ──────────────────────────────────────────────────────────────────────────
  11418.  
  11419.  
  11420.  Subprogram: MouseMaskTranslate
  11421.  
  11422.    Translates the pattern mask and hot spot values for a given graphics-mode
  11423.    mouse cursor to a binary format string suitable for passing to the
  11424.    memory-resident mouse driver for setting the cursor.
  11425.  
  11426.    This translation process is relatively time-consuming and should normally
  11427.    only be performed once, when a program first starts up. To save multiple
  11428.    cursors for quick switching between cursor types, save only the cursor$
  11429.    result from this subprogram. The call to MouseSetCursor, using this binary
  11430.    format cursor$, is very fast.
  11431.  
  11432.    ──────────────────────────────────────────────────────────────────────────
  11433.      ' ************************************************
  11434.      ' **  Name:          MouseMaskTranslate         **
  11435.      ' **  Type:          Subprogram                 **
  11436.      ' **  Module:        MOUSSUBS.BAS               **
  11437.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11438.      ' ************************************************
  11439.      '
  11440.      ' Translates mouse graphics cursor Mask$ to Cursor$.
  11441.      '
  11442.      ' EXAMPLE OF USE:  MouseMaskTranslate mask$, xHot%, yHot%, cursor$
  11443.      ' PARAMETERS:      mask$      Pattern mask that defines a mouse
  11444.      '                             graphics-mode cursor
  11445.      '                  xHot%      X location of the hot spot
  11446.      '                  yHot%      Y location of the hot spot
  11447.      '                  cursor$    The returned binary buffer string
  11448.      '                             for the cursor
  11449.      ' VARIABLES:       i%         Looping index
  11450.      '                  b%         Integer formed from string bit representati
  11451.      ' MODULE LEVEL
  11452.      '   DECLARATIONS:  DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%,
  11453.      '                              cursor$)
  11454.      '
  11455.        SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$) STATIC
  11456.            cursor$ = CHR$(xHot%) + CHR$(yHot%) + STRING$(64, 0)
  11457.            IF LEN(mask$) = 512 THEN
  11458.                FOR i% = 1 TO 32
  11459.                    b% = BinStr2Bin%(MID$(mask$, i% * 16 - 15, 16))
  11460.                    MID$(cursor$, i% + i% + 1, 2) = MKI$(b%)
  11461.                NEXT i%
  11462.            END IF
  11463.        END SUB
  11464.    ──────────────────────────────────────────────────────────────────────────
  11465.  
  11466.  
  11467.  Subprogram: MouseMickey
  11468.  
  11469.    Returns the mouse "mickies," or relative motion counts, since the last
  11470.    call to this routine. If the mouse has not been moved since the last call,
  11471.    zeros are returned.
  11472.  
  11473.    ──────────────────────────────────────────────────────────────────────────
  11474.      ' ************************************************
  11475.      ' **  Name:          MouseMickey                **
  11476.      ' **  Type:          Subprogram                 **
  11477.      ' **  Module:        MOUSSUBS.BAS               **
  11478.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11479.      ' ************************************************
  11480.      '
  11481.      ' Reads mouse mickey counts.
  11482.      '
  11483.      ' EXAMPLE OF USE:  MouseMickey horizontal%, vertical%
  11484.      ' PARAMETERS:      horizontal%   Horizontal motion mickey counts
  11485.      '                  vertical%     Vertical motion mickey counts
  11486.      ' VARIABLES:       (none)
  11487.      ' MODULE LEVEL
  11488.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11489.      '                  DECLARE SUB MouseMickey (horizontal, vertical%)
  11490.      '
  11491.        SUB MouseMickey (horizontal%, vertical%) STATIC
  11492.            Mouse 11, 0, horizontal%, vertical%
  11493.        END SUB
  11494.    ──────────────────────────────────────────────────────────────────────────
  11495.  
  11496.  
  11497.  Subprogram: MouseNow
  11498.  
  11499.    Returns the state of the mouse buttons and the mouse location. This
  11500.    subprogram is one of the most useful routines presented. Four parameters
  11501.    are passed back: the states of the two mouse buttons and the horizontal
  11502.    and vertical location of the mouse.
  11503.  
  11504.    The horizontal position is scaled according to the current video mode. In
  11505.    most cases, the X position at the right edge of the screen is 639, no
  11506.    matter what the screen pixel range is. Check the returned values for the
  11507.    mode you want to use.
  11508.  
  11509.    ──────────────────────────────────────────────────────────────────────────
  11510.      ' ************************************************
  11511.      ' **  Name:          MouseNow                   **
  11512.      ' **  Type:          Subprogram                 **
  11513.      ' **  Module:        MOUSSUBS.BAS               **
  11514.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11515.      ' ************************************************
  11516.      '
  11517.      ' Returns the state of the mouse.
  11518.      '
  11519.      ' EXAMPLE OF USE:  MouseNow leftButton%, rightButton%, xMouse%, yMouse%
  11520.      ' PARAMETERS:      leftButton%   Indicates left mouse button state
  11521.      '                  rightButton%  Indicates right mouse button state
  11522.      '                  xMouse%       X location of mouse
  11523.      '                  yMouse%       Y location of mouse
  11524.      ' VARIABLES:       m2%           Mouse driver parameter containing button
  11525.      '                                press information
  11526.      ' MODULE LEVEL
  11527.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11528.      '                  DECLARE SUB MouseNow (leftButton%, rightButton%,
  11529.      '                                        xMouse%, yMouse%)
  11530.      '
  11531.        SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%) STATIC
  11532.            Mouse 3, m2%, xMouse%, yMouse%
  11533.            leftButton% = ((m2% AND 1) <> 0)
  11534.            rightButton% = ((m2% AND 2) <> 0)
  11535.        END SUB
  11536.    ──────────────────────────────────────────────────────────────────────────
  11537.  
  11538.  
  11539.  Subprogram: MousePressLeft
  11540.  
  11541.    Returns the position of the mouse at the time the left button was last
  11542.    pressed. Also returned is the number of left button presses since the last
  11543.    call to this subprogram.
  11544.  
  11545.    ──────────────────────────────────────────────────────────────────────────
  11546.      ' ************************************************
  11547.      ' **  Name:          MousePressLeft             **
  11548.      ' **  Type:          Subprogram                 **
  11549.      ' **  Module:        MOUSSUBS.BAS               **
  11550.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11551.      ' ************************************************
  11552.      '
  11553.      ' Returns the mouse state at last press of left button.
  11554.      '
  11555.      ' EXAMPLE OF USE:  MousePressLeft leftCount%, xMouse%, yMouse%
  11556.      ' PARAMETERS:      leftCount%    Number of times the left button has been
  11557.      '                                pressed since the last call to this
  11558.      '                                subprogram
  11559.      '                  xMouse%       X location of the mouse at the last pres
  11560.      '                                of the left button
  11561.      '                  yMouse%       Y location of the mouse at the last pres
  11562.      '                                of the left button
  11563.      ' VARIABLES:       m1%           Parameter for call to mouse driver
  11564.      ' MODULE LEVEL
  11565.      '   DECLARATIONS:    DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11566.      '                    DECLARE SUB MousePressLeft (leftCount%, xMouse%, yMo
  11567.      '
  11568.        SUB MousePressLeft (leftCount%, xMouse%, yMouse%) STATIC
  11569.            m1% = 5
  11570.            leftCount% = 0
  11571.            Mouse m1%, leftCount%, xMouse%, yMouse%
  11572.        END SUB
  11573.    ──────────────────────────────────────────────────────────────────────────
  11574.  
  11575.  
  11576.  Subprogram: MousePressRight
  11577.  
  11578.    Returns the position of the mouse at the time the right button was last
  11579.    pressed. Also returned is the number of right button presses since the
  11580.    last call to this subprogram.
  11581.  
  11582.    ──────────────────────────────────────────────────────────────────────────
  11583.      ' ************************************************
  11584.      ' **  Name:          MousePressRight            **
  11585.      ' **  Type:          Subprogram                 **
  11586.      ' **  Module:        MOUSSUBS.BAS               **
  11587.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11588.      ' ************************************************
  11589.      '
  11590.      ' Returns the mouse state at last press of right button.
  11591.      '
  11592.        SUB MousePressRight (rightCount%, xMouse%, yMouse%) STATIC
  11593.            m1% = 5
  11594.            rightCount% = 1
  11595.            Mouse m1%, rightCount%, xMouse%, yMouse%
  11596.        END SUB
  11597.    ──────────────────────────────────────────────────────────────────────────
  11598.  
  11599.  
  11600.  Subprogram: MousePut
  11601.  
  11602.    Allows you to move the mouse to any desired location.
  11603.  
  11604.    ──────────────────────────────────────────────────────────────────────────
  11605.      ' ************************************************
  11606.      ' **  Name:          MousePut                   **
  11607.      ' **  Type:          Subprogram                 **
  11608.      ' **  Module:        MOUSSUBS.BAS               **
  11609.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11610.      ' ************************************************
  11611.      '
  11612.      ' Sets the mouse position.
  11613.      '
  11614.      ' EXAMPLE OF USE:  MousePut xMouse%, yMouse%
  11615.      ' PARAMETERS:      xMouse%    Horizontal location to place cursor
  11616.      '                  yMouse%    Vertical location to place cursor
  11617.      ' VARIABLES:       (none)
  11618.      ' MODULE LEVEL
  11619.      '   DECLARATIONS:   DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11620.      '                   DECLARE SUB MousePut (xMouse%, yMouse%)
  11621.      '
  11622.        SUB MousePut (xMouse%, yMouse%) STATIC
  11623.            Mouse 4, 0, xMouse%, yMouse%
  11624.        END SUB
  11625.    ──────────────────────────────────────────────────────────────────────────
  11626.  
  11627.  
  11628.  Subprogram: MouseRange
  11629.  
  11630.    Sets a rectangular area of the screen to which the mouse cursor will be
  11631.    limited. The mouse cursor will stay in the bounds defined, no matter which
  11632.    way the mouse is moved.
  11633.  
  11634.    ──────────────────────────────────────────────────────────────────────────
  11635.      ' ************************************************
  11636.      ' **  Name:          MouseRange                 **
  11637.      ' **  Type:          Subprogram                 **
  11638.      ' **  Module:        MOUSSUBS.BAS               **
  11639.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11640.      ' ************************************************
  11641.      '
  11642.      ' Sets mouse range of motion.
  11643.      '
  11644.      ' EXAMPLE OF USE:  MouseRange x1%, y1%, x2%, y2%
  11645.      ' PARAMETERS:      x1%        Upper left corner X coordinate
  11646.      '                  y1%        Upper left corner Y coordinate
  11647.      '                  x2%        Lower right corner X coordinate
  11648.      '                  y2%        Lower right corner Y coordinate
  11649.      ' VARIABLES:       (none)
  11650.      ' MODULE LEVEL
  11651.      '   DECLARATIONS:   DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11652.      '                   DECLARE SUB MouseRange (x1%, y1%, x2%, y2%)
  11653.      '
  11654.         SUB MouseRange (x1%, y1%, x2%, y2%) STATIC
  11655.            Mouse 7, 0, x1%, x2%
  11656.            Mouse 8, 0, y1%, y2%
  11657.        END SUB
  11658.    ──────────────────────────────────────────────────────────────────────────
  11659.  
  11660.  
  11661.  Subprogram: MouseReleaseLeft
  11662.  
  11663.    Returns the position of the mouse at the time the left button was last
  11664.    released. Also returned is the number of left button releases since the
  11665.    last call to this subprogram.
  11666.  
  11667.    ──────────────────────────────────────────────────────────────────────────
  11668.      ' ************************************************
  11669.      ' **  Name:          MouseReleaseLeft           **
  11670.      ' **  Type:          Subprogram                 **
  11671.      ' **  Module:        MOUSSUBS.BAS               **
  11672.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11673.      ' ************************************************
  11674.      '
  11675.      ' Returns the mouse state at last release of left button.
  11676.      '
  11677.      ' EXAMPLE OF USE:  MouseReleaseLeft leftCount%, xMouse%, yMouse%
  11678.      ' PARAMETERS:      leftCount%    Number of times the left button has been
  11679.      '                                released since the last call to this
  11680.      '                                subprogram
  11681.      '                  xMouse%       X location of the mouse at the last
  11682.      '                                release of the left button
  11683.      '                  yMouse%       Y location of the mouse at the last
  11684.      '                                release of the left button
  11685.      ' VARIABLES:       m1%           Parameter for call to mouse driver
  11686.      ' MODULE LEVEL
  11687.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11688.      '                  DECLARE SUB MouseReleaseLeft (leftCount%, xMouse%,
  11689.      '                                               yMouse%)
  11690.      '
  11691.        SUB MouseReleaseLeft (leftCount%, xMouse%, yMouse%) STATIC
  11692.            m1% = 6
  11693.            leftCount% = 0
  11694.            Mouse m1%, leftCount%, xMouse%, yMouse%
  11695.        END SUB
  11696.    ──────────────────────────────────────────────────────────────────────────
  11697.  
  11698.  
  11699.  Subprogram: MouseReleaseRight
  11700.  
  11701.    Returns the position of the mouse at the time the right button was last
  11702.    released. Also returned is the number of right button releases since the
  11703.    last call to this subprogram.
  11704.  
  11705.    ──────────────────────────────────────────────────────────────────────────
  11706.      ' ************************************************
  11707.      ' **  Name:          MouseReleaseRight          **
  11708.      ' **  Type:          Subprogram                 **
  11709.      ' **  Module:        MOUSSUBS.BAS               **
  11710.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11711.      ' ************************************************
  11712.      '
  11713.      ' Returns the mouse state at last release of right button.
  11714.      '
  11715.      ' EXAMPLE OF USE:  MouseReleaseRight rightCount%, xMouse%, yMouse%
  11716.      ' PARAMETERS:      rightCount%   Number of times the right button has bee
  11717.      '                                released since the last call to this
  11718.      '                                subprogram
  11719.      '                  xMouse%       X location of the mouse at the last
  11720.      '                                release of the right button
  11721.      '                  yMouse%       Y location of the mouse at the last
  11722.      '                                release of the right button
  11723.      ' VARIABLES:       m1%           Parameter for call to mouse driver
  11724.      ' MODULE LEVEL
  11725.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11726.      '                  DECLARE SUB MouseReleaseRight (rightCount%, xMouse%,
  11727.      '                                                 yMouse%)
  11728.      '
  11729.        SUB MouseReleaseRight (rightCount%, xMouse%, yMouse%) STATIC
  11730.            m1% = 6
  11731.            rightCount% = 1
  11732.            Mouse m1%, rightCount%, xMouse%, yMouse%
  11733.        END SUB
  11734.    ──────────────────────────────────────────────────────────────────────────
  11735.  
  11736.  
  11737.  Subprogram: MouseSetGcursor
  11738.  
  11739.    Sets the mouse cursor using the binary-format cursor string created by an
  11740.    earlier call to the subprogram MouseMaskTranslate.
  11741.  
  11742.    To quickly switch among a selection of mouse cursors, keep the
  11743.    binary-format cursor strings available, and call this subprogram to change
  11744.    cursors.
  11745.  
  11746.    ──────────────────────────────────────────────────────────────────────────
  11747.      ' ************************************************
  11748.      ' **  Name:          MouseSetGcursor            **
  11749.      ' **  Type:          Subprogram                 **
  11750.      ' **  Module:        MOUSSUBS.BAS               **
  11751.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11752.      ' ************************************************
  11753.      '
  11754.      ' Sets mouse graphics cursor using cursor$.
  11755.      '
  11756.      ' EXAMPLE OF USE:  MouseSetGcursor cursor$
  11757.      ' PARAMETERS:      cursor$    Binary format cursor string
  11758.      ' VARIABLES:       xHot%      X hot spot location
  11759.      '                  yHot%      Y hot spot location
  11760.      ' MODULE LEVEL
  11761.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11762.      '                  DECLARE SUB MouseSetGcursor (cursor$)
  11763.      '
  11764.        SUB MouseSetGcursor (cursor$) STATIC
  11765.            xHot% = ASC(LEFT$(cursor$, 1))
  11766.            yHot% = ASC(MID$(cursor$, 2, 1))
  11767.            Mouse 9, xHot%, yHot%, SADD(cursor$) + 2
  11768.        END SUB
  11769.    ──────────────────────────────────────────────────────────────────────────
  11770.  
  11771.  
  11772.  Subprogram: MouseShow
  11773.  
  11774.    Activates the mouse cursor, making it visible and movable by the mouse. To
  11775.    turn the cursor off, use the MouseHide subprogram.
  11776.  
  11777.    When you are updating the screen, such as when printing text in a graphics
  11778.    mode, it's a good idea to hide the mouse just before printing and then
  11779.    show it after the printing is done. This helps prevent glitches or blank
  11780.    spots from appearing due to overlapping and unsynchronized pixel mapping
  11781.    between your program and the mouse driver.
  11782.  
  11783.    ──────────────────────────────────────────────────────────────────────────
  11784.      ' ************************************************
  11785.      ' **  Name:          MouseShow                  **
  11786.      ' **  Type:          Subprogram                 **
  11787.      ' **  Module:        MOUSSUBS.BAS               **
  11788.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11789.      ' ************************************************
  11790.      '
  11791.      ' Shows the mouse cursor.
  11792.      '
  11793.      ' EXAMPLE OF USE:  MouseShow
  11794.      ' PARAMETERS:      (none)
  11795.      ' VARIABLES:       (none)
  11796.      ' MODULE LEVEL
  11797.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11798.      '                  DECLARE SUB MouseShow ()
  11799.      '
  11800.        SUB MouseShow STATIC
  11801.            Mouse 1, 0, 0, 0
  11802.        END SUB
  11803.    ──────────────────────────────────────────────────────────────────────────
  11804.  
  11805.  
  11806.  Subprogram: MouseSoftCursor
  11807.  
  11808.    Sets the software mouse cursor for text mode. This cursor changes the
  11809.    attributes of screen characters (foreground/background color, intensity,
  11810.    or underscoring) when the display adapter is in text mode. The easiest way
  11811.    to get a feel for how these masks work is by running the MOUSTCRS.BAS
  11812.    program.
  11813.  
  11814.    ──────────────────────────────────────────────────────────────────────────
  11815.      ' ************************************************
  11816.      ' **  Name:          MouseSoftCursor            **
  11817.      ' **  Type:          Subprogram                 **
  11818.      ' **  Module:        MOUSSUBS.BAS               **
  11819.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11820.      ' ************************************************
  11821.      '
  11822.      ' Sets text-mode software cursor.
  11823.      '
  11824.      ' EXAMPLE OF USE:  MouseSoftCursor screenMask%, cursorMask%
  11825.      ' PARAMETERS:      screenMask%   Integer bit pattern for the screen mask
  11826.      '                  cursorMask%   Integer bit pattern for the cursor mask
  11827.      ' VARIABLES:       (none)
  11828.      ' MODULE LEVEL
  11829.      '   DECLARATIONS:  DECLARE SUB MouseSoftCursor (screenMaks%, cursorMask%)
  11830.      '                  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11831.      '
  11832.        SUB MouseSoftCursor (screenMask%, cursorMask%) STATIC
  11833.            Mouse 10, 0, screenMask%, cursorMask%
  11834.        END SUB
  11835.    ──────────────────────────────────────────────────────────────────────────
  11836.  
  11837.  
  11838.  Subprogram: MouseWarp
  11839.  
  11840.    Sets the double-speed threshold for the mouse in units of mickies per
  11841.    second. The default setting is 64 mickies per second.
  11842.  
  11843.    Whenever the mouse is moved at a rate greater than the threshold value,
  11844.    the cursor motion is doubled. This helps zip the cursor across the screen
  11845.    during quick moves but allows slower, more accurate motion at slower
  11846.    speeds.
  11847.  
  11848.    To use the MouseInches subprogram to approximate the action of an
  11849.    absolute-motion pointing device, set the threshold to a large, unreachable
  11850.    value. For example, MouseWarp 9999 effectively turns off the threshold
  11851.    checking.
  11852.  
  11853.    ──────────────────────────────────────────────────────────────────────────
  11854.      ' ************************************************
  11855.      ' **  Name:          MouseWarp                  **
  11856.      ' **  Type:          Subprogram                 **
  11857.      ' **  Module:        MOUSSUBS.BAS               **
  11858.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11859.      ' ************************************************
  11860.  
  11861.      ' Sets double-speed threshold.
  11862.      '
  11863.      ' EXAMPLE OF USE:  MouseWarp threshold%
  11864.      ' PARAMETERS:      threshold%    Mickies per second rate of threshold
  11865.      ' VARIABLES:       (none)
  11866.      ' MODULE LEVEL
  11867.      '   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  11868.      '                  DECLARE SUB MouseWarp (threshold%)
  11869.      '
  11870.        SUB MouseWarp (threshold%) STATIC
  11871.            Mouse 19, 0, 0, threshold%
  11872.        END SUB
  11873.    ──────────────────────────────────────────────────────────────────────────
  11874.  
  11875.  
  11876.  
  11877.  ────────────────────────────────────────────────────────────────────────────
  11878.  MOUSTCRS
  11879.  
  11880.    The MOUSTCRS program lets you experiment with the screen and cursor masks
  11881.    that define the action of the software mouse cursor in text modes.
  11882.  
  11883.    Run the program and move the mouse cursor to any of the mask bits
  11884.    displayed near the bottom of the screen. Click with the left mouse button
  11885.    on any bit to toggle that bit, and move the cursor around the screen to
  11886.    see how the cursor's appearance is affected.
  11887.  
  11888.    To set any screen and cursor mask combination in your own programs, record
  11889.    the hexadecimal numbers for the two masks, and pass these two numbers to
  11890.    the MouseSoftCursor subprogram in the MOUSSUBS.BAS toolbox.
  11891.  
  11892.  
  11893.  Program Module: MOUSTCRS
  11894.  
  11895.    ──────────────────────────────────────────────────────────────────────────
  11896.      ' ************************************************
  11897.      ' **  Name:          MOUSTCRS                   **
  11898.      ' **  Type:          Program                    **
  11899.      ' **  Module:        MOUSTCRS.BAS               **
  11900.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  11901.      ' ************************************************
  11902.      '
  11903.      ' USAGE:           No command line parameters
  11904.      ' REQUIREMENTS:    MIXED.QLB/.LIB
  11905.      '                  Mouse
  11906.      ' .MAK FILE:       MOUSTCRS.BAS
  11907.      '                  MOUSSUBS.BAS
  11908.      '                  BITS.BAS
  11909.      '                  ATTRIB.BAS
  11910.      ' PARAMETERS:      (none)
  11911.      ' VARIABLES:       screenMask%   Integer bit mask for screen mask
  11912.      '                  cursorMask%   Integer bit mask for cursor mask
  11913.      '                  leftCount%    Count of left mouse button presses
  11914.      '                  xm%           Mouse X position at last left button pre
  11915.      '                  ym%           Mouse Y position at last left button pre
  11916.      '                  row%          Code for which screen bit row was select
  11917.      '                  bit%          Bit pattern determined by screen column
  11918.      '                                click on
  11919.      '                  screenMask$   String of 0s and 1s for bit pattern disp
  11920.      '                  cursorMask$   String of 0s and 1s for bit pattern disp
  11921.      '                  i%            Looping index
  11922.      '                  Shex$         Hexadecimal representation of the screen
  11923.      '                  Chex$         Hexadecimal representation of the cursor
  11924.  
  11925.      ' Define constants
  11926.        CONST FALSE = 0
  11927.        CONST TRUE = NOT FALSE
  11928.  
  11929.      ' Functions
  11930.        DECLARE FUNCTION Bin2BinStr$ (b%)
  11931.  
  11932.      ' Subprograms
  11933.        DECLARE SUB Attrib ()
  11934.        DECLARE SUB MouseHide ()
  11935.        DECLARE SUB MouseInstall (mouseFlag%)
  11936.        DECLARE SUB MousePressLeft (leftCount%, xMouse%, yMouse%)
  11937.        DECLARE SUB MouseShow ()
  11938.        DECLARE SUB MouseSoftCursor (screenMask%, cursorMask%)
  11939.  
  11940.      ' Is the mouse out there?
  11941.        MouseInstall mouseFlag%
  11942.        IF mouseFlag% = 0 THEN
  11943.            PRINT "Mouse does not appear to be installed.  Check"
  11944.            PRINT "your mouse documentation for proper installation."
  11945.            PRINT
  11946.            SYSTEM
  11947.        END IF
  11948.  
  11949.      ' Put all attributes on the screen
  11950.        Attrib
  11951.  
  11952.      ' Set masks to initial state
  11953.        screenMask% = &H77FF
  11954.        cursorMask% = &H7700
  11955.  
  11956.      ' Create the outlined boxes
  11957.        COLOR 14, 0
  11958.        PRINT "                  +---+-------+---+--------+----------+--------+
  11959.        PRINT "                  | b | bckgd | i | foregd |   char   |   =    |
  11960.        PRINT "    +-------------+---+-------+---+--------+----------+--------+
  11961.        PRINT "    | screen mask | 0 |  111  | 0 |  111   | 11111111 | &H77FF |
  11962.        PRINT "    | cursor mask | 0 |  111  | 0 |  111   | 00000000 | &H7700 |
  11963.        PRINT "    +-------------+---+-------+---+--------+----------+--------+
  11964.  
  11965.      ' Print the instructions
  11966.        COLOR 11, 0
  11967.        PRINT "Click the mouse on any of the mask bits shown.  Then, try the"
  11968.        PRINT "new cursor by moving across the attribute fields above.";
  11969.  
  11970.      ' Special indication for quitting
  11971.        COLOR 15, 0
  11972.        LOCATE 17, 1, 0
  11973.        PRINT "Click here";
  11974.        LOCATE 18, 1, 0
  11975.        PRINT "to Quit - ";
  11976.        COLOR 10, 0
  11977.        PRINT "X";
  11978.  
  11979.      ' Put mask bits into boxes on screen
  11980.        GOSUB PrintScreenMask
  11981.        GOSUB PrintCursorMask
  11982.  
  11983.      ' Activate the mouse
  11984.        MouseShow
  11985.  
  11986.      ' Do the main processing loop until the quit flag is set
  11987.        DO
  11988.            GOSUB MainLoop
  11989.        LOOP UNTIL quitFlag%
  11990.  
  11991.      ' All done
  11992.        MouseHide
  11993.        CLS
  11994.        SYSTEM
  11995.  
  11996.      ' Main processing loop
  11997.    MainLoop:
  11998.  
  11999.      ' Where was mouse when left button was last pressed?
  12000.        MousePressLeft leftCount%, xm%, ym%
  12001.  
  12002.      ' Was it on one of the two important rows of the screen?
  12003.        SELECT CASE ym%
  12004.        CASE 152
  12005.            row% = 1
  12006.        CASE 160
  12007.            row% = 2
  12008.        CASE ELSE
  12009.            row% = 0
  12010.        END SELECT
  12011.  
  12012.      ' Was it on an important column?
  12013.        SELECT CASE xm%
  12014.        CASE 80
  12015.            IF ym% = 136 THEN
  12016.                quitFlag% = TRUE
  12017.            END IF
  12018.        CASE 160
  12019.            bit% = &H8000
  12020.        CASE 200
  12021.            bit% = &H4000
  12022.        CASE 208
  12023.            bit% = &H2000
  12024.        CASE 216
  12025.            bit% = &H1000
  12026.        CASE 256
  12027.            bit% = &H800
  12028.        CASE 296
  12029.            bit% = &H400
  12030.        CASE 304
  12031.            bit% = &H200
  12032.        CASE 312
  12033.            bit% = &H100
  12034.        CASE 360
  12035.            bit% = &H80
  12036.        CASE 368
  12037.            bit% = &H40
  12038.        CASE 376
  12039.            bit% = &H20
  12040.        CASE 384
  12041.            bit% = &H10
  12042.        CASE 392
  12043.            bit% = &H8
  12044.        CASE 400
  12045.            bit% = &H4
  12046.        CASE 408
  12047.            bit% = &H2
  12048.        CASE 416
  12049.            bit% = &H1
  12050.        CASE ELSE
  12051.            bit% = 0
  12052.        END SELECT
  12053.  
  12054.      ' Modify the masks and update the cursor
  12055.        IF leftCount% THEN
  12056.            SELECT CASE row%
  12057.            CASE 1
  12058.                screenMask% = screenMask% XOR bit%
  12059.            CASE 2
  12060.                cursorMask% = cursorMask% XOR bit%
  12061.            CASE ELSE
  12062.            END SELECT
  12063.            MouseSoftCursor screenMask%, cursorMask%
  12064.            GOSUB PrintScreenMask
  12065.            GOSUB PrintCursorMask
  12066.        END IF
  12067.  
  12068.      ' End of main processing loop
  12069.        RETURN
  12070.  
  12071.      ' Put screen mask bits on the screen
  12072.    PrintScreenMask:
  12073.        COLOR 12, 0
  12074.        screenMask$ = ""
  12075.        screenMask$ = Bin2BinStr$(screenMask%)
  12076.        MouseHide
  12077.        FOR i% = 0 TO 15
  12078.            SELECT CASE i%
  12079.            CASE 0 TO 7
  12080.                LOCATE 20, 53 - i%, 0
  12081.                PRINT MID$(screenMask$, 16 - i%, 1);
  12082.            CASE 8 TO 10
  12083.                LOCATE 20, 48 - i%, 0
  12084.                PRINT MID$(screenMask$, 16 - i%, 1);
  12085.            CASE 11
  12086.                LOCATE 20, 44 - i%, 0
  12087.                PRINT MID$(screenMask$, 16 - i%, 1);
  12088.            CASE 12 TO 14
  12089.                LOCATE 20, 40 - i%, 0
  12090.                PRINT MID$(screenMask$, 16 - i%, 1);
  12091.            CASE 15
  12092.                LOCATE 20, 36 - i%, 0
  12093.                PRINT MID$(screenMask$, 16 - i%, 1);
  12094.            CASE ELSE
  12095.            END SELECT
  12096.        NEXT i%
  12097.        shex$ = "&H" + RIGHT$("000" + HEX$(screenMask%), 4)
  12098.        LOCATE 20, 57, 0
  12099.        COLOR 10, 0
  12100.        PRINT shex$;
  12101.        MouseShow
  12102.        RETURN
  12103.  
  12104.      ' Put cursor mask bits on the screen
  12105.    PrintCursorMask:
  12106.        COLOR 12, 0
  12107.        cursorMask$ = ""
  12108.        cursorMask$ = Bin2BinStr$(cursorMask%)
  12109.        MouseHide
  12110.        FOR i% = 0 TO 15
  12111.            SELECT CASE i%
  12112.            CASE 0 TO 7
  12113.                LOCATE 21, 53 - i%, 0
  12114.                PRINT MID$(cursorMask$, 16 - i%, 1);
  12115.            CASE 8 TO 10
  12116.                LOCATE 21, 48 - i%, 0
  12117.                PRINT MID$(cursorMask$, 16 - i%, 1);
  12118.            CASE 11
  12119.                LOCATE 21, 44 - i%, 0
  12120.                PRINT MID$(cursorMask$, 16 - i%, 1);
  12121.            CASE 12 TO 14
  12122.                LOCATE 21, 40 - i%, 0
  12123.                PRINT MID$(cursorMask$, 16 - i%, 1);
  12124.            CASE 15
  12125.                LOCATE 21, 36 - i%, 0
  12126.                PRINT MID$(cursorMask$, 16 - i%, 1);
  12127.            CASE ELSE
  12128.            END SELECT
  12129.        NEXT i%
  12130.        chex$ = "&H" + RIGHT$("000" + HEX$(cursorMask%), 4)
  12131.        LOCATE 21, 57, 0
  12132.        COLOR 10, 0
  12133.        PRINT chex$;
  12134.        MouseShow
  12135.        RETURN
  12136.    ──────────────────────────────────────────────────────────────────────────
  12137.  
  12138.  
  12139.  
  12140.  ────────────────────────────────────────────────────────────────────────────
  12141.  OBJECT
  12142.  
  12143.    The OBJECT program lets you interactively create subprograms that produce
  12144.    graphics objects for your programs.
  12145.  
  12146.    When a QuickBASIC program uses the GET and PUT statements for graphics
  12147.    animation purposes, you'll often notice the objects being created on the
  12148.    screen as the program first starts up. The normal procedure is to create
  12149.    the graphics objects using the LINE and DRAW statements and then to save
  12150.    the objects in integer arrays using the GET statement. The creation of the
  12151.    objects the first time is relatively slow, compared with the very fast PUT
  12152.    statement.
  12153.  
  12154.    The OBJECT program lets you create these objects interactively and
  12155.    "off-line" until you're satisfied with their appearance. Then, this
  12156.    program automatically writes a subprogram source file that, when loaded or
  12157.    merged with your main program, quickly creates the integer arrays by
  12158.    simply reading the appropriate integers into the arrays.
  12159.  
  12160.    The best way to get a feel for this program is to give it a try. Run it,
  12161.    and follow the directions. You can edit a DRAW string, try it to see what
  12162.    the new object looks like, and then re-edit the string until you like the
  12163.    results. When you select the "Save" option, the program automatically
  12164.    determines the smallest integer array that will hold the object you've
  12165.    created and writes a source code subprogram file that creates and fills an
  12166.    integer array with the bit pattern for your object. Later on, you can load
  12167.    this source code file and re-edit the object to make other changes.
  12168.  
  12169.    To use the new object source code in your own program, merge the file into
  12170.    the program where you want to use the object. The program should run
  12171.    through the statements once, but you can use PUT statements repeatedly to
  12172.    animate or duplicate the image.
  12173.  
  12174.    Name                     Type    Description
  12175.    ──────────────────────────────────────────────────────────────────────────
  12176.    OBJECT.BAS                      Program module
  12177.    SaveObject              Sub     Creates graphics "PUT" file source code
  12178.    ──────────────────────────────────────────────────────────────────────────
  12179.  
  12180.  
  12181.  Program Module: OBJECT
  12182.  
  12183.    ──────────────────────────────────────────────────────────────────────────
  12184.      ' ************************************************
  12185.      ' **  Name:          OBJECT                     **
  12186.      ' **  Type:          Program                    **
  12187.      ' **  Module:        OBJECT.BAS                 **
  12188.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12189.      ' ************************************************
  12190.      '
  12191.      ' Allows interactive graphics object creation.
  12192.      ' Dumps code for another program to be able to create
  12193.      ' the graphics object "PUT array" directly.
  12194.      '
  12195.      ' USAGE:           No command line parameters
  12196.      ' REQUIREMENTS:    CGA
  12197.      ' .MAK FILE:       OBJECT.BAS
  12198.      '                  KEYS.BAS
  12199.      '                  EDIT.BAS
  12200.      ' PARAMETERS:      (none)
  12201.      ' VARIABLES:       quitFlag%     Indicates user is ready to quit
  12202.      '                  modeFlag%     Indicates a valid graphics mode was sele
  12203.      '                  mode%         Graphics mode
  12204.      '                  xMax%         Maximum screen X coordinate
  12205.      '                  yMax%         Maximum screen Y coordinate
  12206.      '                  fileName$     Name of object creation subprogram file
  12207.      '                  exitCode%     Return code from EditLine subprogram
  12208.      '                  t$            Temporary work string while reading file
  12209.      '                                contents
  12210.      '                  a$            The DRAW string
  12211.      '                  editFlag%     Indicates an edit of the string is desir
  12212.      '               drawErrorFlag%   Indicates an error occurred during the D
  12213.      '                  keyNumber%    Integer key code returned by KeyCode%
  12214.      '                                function
  12215.      '                  okayFlag%     Shared flag for determining array dimens
  12216.  
  12217.      ' Logical constants
  12218.        CONST FALSE = 0
  12219.        CONST TRUE = NOT FALSE
  12220.  
  12221.      ' Key code constants
  12222.        CONST SKEYLC = 115
  12223.        CONST SKEYUC = SKEYLC - 32
  12224.        CONST QKEYLC = 113
  12225.        CONST QKEYUC = QKEYLC - 32
  12226.        CONST ESC = 27
  12227.  
  12228.      ' Color constants
  12229.        CONST BLACK = 0
  12230.        CONST BLUE = 1
  12231.        CONST GREEN = 2
  12232.        CONST CYAN = 3
  12233.        CONST RED = 4
  12234.        CONST MAGENTA = 5
  12235.        CONST BROWN = 6
  12236.        CONST WHITE = 7
  12237.        CONST BRIGHT = 8
  12238.        CONST BLINK = 16
  12239.        CONST YELLOW = BROWN + BRIGHT
  12240.  
  12241.      ' Functions
  12242.        DECLARE FUNCTION KeyCode% ()
  12243.  
  12244.      ' Subprograms
  12245.        DECLARE SUB DrawBox (row1%, col1%, row2%, col2%)
  12246.        DECLARE SUB EditBox (a$, row1%, col1%, row2%, col2%)
  12247.        DECLARE SUB EditLine (a$, exitCode%)
  12248.        DECLARE SUB SaveObject (mode%, xMax%, yMax%, fileName$, a$)
  12249.  
  12250.      ' Initialization
  12251.        SCREEN 0
  12252.        CLS
  12253.        quitFlag% = FALSE
  12254.  
  12255.      ' Title
  12256.        PRINT "OBJECT - Interactive graphics object editor"
  12257.        PRINT
  12258.        PRINT
  12259.  
  12260.      ' Display screen mode table
  12261.        PRINT "Adapter       SCREEN modes allowed"
  12262.        PRINT "----------    --------------------"
  12263.        PRINT "Monochrome    (none)"
  12264.        PRINT "Hercules      3"
  12265.        PRINT "CGA           1,2"
  12266.        PRINT "EGA           1,2,7,8,9"
  12267.        PRINT "MCGA          1,2,11,13"
  12268.        PRINT "VGA           1,2,7,8,9,11,12,13"
  12269.        PRINT
  12270.  
  12271.      ' Ask user for the graphics screen mode
  12272.        DO
  12273.            PRINT "Enter a SCREEN mode number, ";
  12274.            INPUT "based on your graphics adapter "; mode%
  12275.            modeFlag% = TRUE
  12276.            SELECT CASE mode%
  12277.            CASE 1, 7, 13
  12278.                xMax% = 319
  12279.                yMax% = 199
  12280.            CASE 2, 8
  12281.                xMax% = 639
  12282.                yMax% = 199
  12283.            CASE 9, 10
  12284.                xMax% = 639
  12285.                yMax% = 349
  12286.            CASE 11, 12
  12287.                xMax% = 639
  12288.                yMax% = 479
  12289.            CASE 3
  12290.                xMax% = 719
  12291.                yMax% = 347
  12292.            CASE ELSE
  12293.                modeFlag% = FALSE
  12294.            END SELECT
  12295.        LOOP UNTIL modeFlag% = TRUE
  12296.  
  12297.      ' Ask user for the filename
  12298.        fileName$ = "IMAGEARY.BAS" + SPACE$(20)
  12299.        SCREEN 0
  12300.        WIDTH 80
  12301.        CLS
  12302.        COLOR WHITE, BLACK
  12303.        PRINT "Name of the file where source code will be written:"
  12304.        PRINT
  12305.        PRINT "Edit the default filename IMAGEARY.BAS ";
  12306.        PRINT "if desired, and then press Enter..."
  12307.        PRINT
  12308.        PRINT SPACE$(12);
  12309.        COLOR YELLOW, BLUE
  12310.        EditLine fileName$, exitCode%
  12311.        COLOR WHITE, BLACK
  12312.  
  12313.      ' Try to read in previous contents of the file
  12314.        ON ERROR GOTO FileError
  12315.        OPEN fileName$ FOR INPUT AS #1
  12316.        ON ERROR GOTO 0
  12317.        DO UNTIL EOF(1)
  12318.            LINE INPUT #1, t$
  12319.            IF INSTR(t$, "(DRAW$)") THEN
  12320.                t$ = MID$(t$, INSTR(t$, CHR$(34)) + 1)
  12321.                t$ = LEFT$(t$, INSTR(t$, CHR$(34)) - 1)
  12322.                a$ = a$ + t$
  12323.            END IF
  12324.        LOOP
  12325.        CLOSE #1
  12326.  
  12327.      ' Main loop
  12328.        DO
  12329.  
  12330.          ' Prepare for DRAW string editing by the user
  12331.            SCREEN 0
  12332.            WIDTH 80
  12333.            CLS
  12334.            editFlag% = FALSE
  12335.  
  12336.          ' Display useful information
  12337.            PRINT "OBJECT - Screen mode"; mode%
  12338.            PRINT
  12339.            PRINT "Edit the DRAW string workspace; then press"
  12340.            PRINT "the Esc key to try out your creation..."
  12341.            PRINT
  12342.            PRINT , "                Cn      Color"
  12343.            PRINT , " H   U   E      Mx,y    Move absolute"
  12344.            PRINT , "   \ | /        M+|/-x,y Move relative"
  12345.            PRINT , " L -   - R      An      Angle (1=90,2=180...)"
  12346.            PRINT , "   / | \        TAn     Turn angle (-360 to 360)"
  12347.            PRINT , " G   D   F      Sn      Scale factor"
  12348.            PRINT , "                Pc,b    Paint (color, border)"
  12349.        PRINT "(These commands are described in detail in the ";
  12350.        PRINT "Microsoft QuickBASIC Language Reference)"
  12351.  
  12352.          ' Input DRAW string via EditBox subprogram
  12353.            COLOR GREEN + BRIGHT, BLUE
  12354.            DrawBox 15, 1, 24, 80
  12355.            COLOR YELLOW, BLUE
  12356.            EditBox a$, 15, 1, 24, 80
  12357.  
  12358.          ' Try out the DRAW string
  12359.            SCREEN mode%
  12360.            drawErrorFlag% = FALSE
  12361.            ON ERROR GOTO DrawError
  12362.            DRAW a$
  12363.            ON ERROR GOTO 0
  12364.  
  12365.          ' Give user idea of what to do
  12366.            LOCATE 1, 1
  12367.            PRINT "<S>ave, <Esc> to edit, or <Q>uit"
  12368.  
  12369.          ' Get next valid keystroke
  12370.            DO UNTIL editFlag% OR drawErrorFlag% OR quitFlag%
  12371.  
  12372.              ' Grab key code
  12373.                keyNumber% = KeyCode%
  12374.  
  12375.              ' Process the keystroke
  12376.                SELECT CASE keyNumber%
  12377.  
  12378.                CASE ESC
  12379.                    editFlag% = TRUE
  12380.  
  12381.                CASE QKEYLC, QKEYUC
  12382.                    quitFlag% = TRUE
  12383.  
  12384.                CASE SKEYLC, SKEYUC
  12385.                    SaveObject mode%, xMax%, yMax%, fileName$, a$
  12386.  
  12387.                CASE ELSE
  12388.                END SELECT
  12389.  
  12390.            LOOP
  12391.  
  12392.        LOOP UNTIL quitFlag%
  12393.  
  12394.      ' All done
  12395.        CLS
  12396.        SCREEN 0
  12397.        WIDTH 80
  12398.        END
  12399.  
  12400.    FileError:
  12401.      ' Create the new file
  12402.        OPEN fileName$ FOR OUTPUT AS #1
  12403.        CLOSE #1
  12404.        OPEN fileName$ FOR INPUT AS #1
  12405.        RESUME NEXT
  12406.  
  12407.    DrawError:
  12408.        drawErrorFlag% = TRUE
  12409.        SCREEN 0
  12410.        CLS
  12411.        PRINT "Your DRAW string caused an error"
  12412.        PRINT
  12413.        PRINT "Press any key to continue"
  12414.        DO
  12415.        LOOP UNTIL INKEY$ <> ""
  12416.        RESUME NEXT
  12417.  
  12418.    ArrayError:
  12419.        okayFlag% = FALSE
  12420.        RESUME NEXT
  12421.    ──────────────────────────────────────────────────────────────────────────
  12422.  
  12423.  
  12424.  Subprogram: SaveObject
  12425.  
  12426.    Creates a source code subprogram module file for the OBJECT program.
  12427.  
  12428.    This subprogram performs the tricky task of finding the boundaries of the
  12429.    graphics object, dimensioning an integer array of exactly the right size,
  12430.    getting the object from the screen and into the array, and writing a
  12431.    source code subprogram file that will recreate the array when merged into
  12432.    a different program.
  12433.  
  12434.    ──────────────────────────────────────────────────────────────────────────
  12435.      ' ************************************************
  12436.      ' **  Name:          SaveObject                 **
  12437.      ' **  Type:          Subprogram                 **
  12438.      ' **  Module:        OBJECT.BAS                 **
  12439.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12440.      ' ************************************************
  12441.      '
  12442.      ' Creates source code file for creating graphics mode
  12443.      ' objects for efficient "PUT" graphics.
  12444.      '
  12445.      ' EXAMPLE OF USE:   SaveObject mode%, xMax%, yMax%, fileName$, a$
  12446.      ' PARAMETERS:       mode%      Graphics mode
  12447.      '                   xMax%      Maximum X screen coordinate for given
  12448.      '                              graphics mode
  12449.      '                   yMax%      Maximum Y screen coordinate for given
  12450.      '                              graphics mode
  12451.      '                   fileName$  Name of source code file to edit and/or
  12452.      '                              create
  12453.      '                   a$         The DRAW string that creates the object
  12454.      '                              initially
  12455.      ' VARIABLES:        okayFlag%  Shared flag used to determine array size
  12456.      '                   size%      Array sizing
  12457.      '                   edge%      Array for efficiently finding edges of obj
  12458.      '                   stepSize%  Scanning step for search for object edges
  12459.      '                   yTop%      Y coordinate at top edge of object
  12460.      '                   yBot%      Y coordinate at bottom edge of object
  12461.      '                   y1%        Starting edge search Y coordinate
  12462.      '                   y2%        Ending edge search Y coordinate
  12463.      '                   i%         Looping index
  12464.      '                   xLeft%     X coordinate at left edge of object
  12465.      '                   xRight%    X coordinate at right edge of object
  12466.      '                   x1%        Starting edge search X coordinate
  12467.      '                   x2%        Ending edge search X coordinate
  12468.      '                   object%()  Array to hold GET object from screen
  12469.      '                   objName$   Name of object, derived from filename
  12470.      '                   ndx%       Index to any special characters in objName
  12471.      '                   ary$       Name of array, derived from filename
  12472.      '                   d$         Works string for building lines for file
  12473.      ' MODULE LEVEL
  12474.      '   DECLARATIONS: DECLARE FUNCTION SaveObject (mode%, xMax%, yMax%,
  12475.      '                                              fileName$, a$)
  12476.      '
  12477.        SUB SaveObject (mode%, xMax%, yMax%, fileName$, a$) STATIC
  12478.  
  12479.          ' Shared error trap variable
  12480.            SHARED okayFlag%
  12481.  
  12482.          ' Select the right array size for the mode
  12483.            SELECT CASE mode%
  12484.            CASE 1, 2
  12485.                size% = 93
  12486.            CASE 7, 8
  12487.                size% = 367
  12488.            CASE 9
  12489.                size% = 667
  12490.            CASE 10
  12491.                size% = 334
  12492.            CASE 11
  12493.                size% = 233
  12494.            CASE 12
  12495.                size% = 927
  12496.            CASE 13
  12497.                size% = 161
  12498.            CASE ELSE
  12499.            END SELECT
  12500.  
  12501.          ' Build the array space
  12502.            DIM edge%(size%)
  12503.  
  12504.          ' Scan to find top and bottom edges of the object
  12505.            stepSize% = 32
  12506.            yTop% = yMax%
  12507.            yBot% = 0
  12508.            y1% = 17
  12509.            y2% = yMax%
  12510.            DO
  12511.                FOR y% = y1% TO y2% STEP stepSize%
  12512.                    IF y% < yTop% OR y% > yBot% THEN
  12513.                        GET (0, y%)-(xMax%, y%), edge%
  12514.                        LINE (0, y%)-(xMax%, y%)
  12515.                        FOR i% = 2 TO size%
  12516.                            IF edge%(i%) THEN
  12517.                                IF y% < yTop% THEN
  12518.                                    yTop% = y%
  12519.                                END IF
  12520.                                IF y% > yBot% THEN
  12521.                                    yBot% = y%
  12522.                                END IF
  12523.                                i% = size%
  12524.                            END IF
  12525.                        NEXT i%
  12526.                        PUT (0, y%), edge%, PSET
  12527.                    END IF
  12528.                NEXT y%
  12529.                IF yTop% <= yBot% THEN
  12530.                    y1% = yTop% - stepSize% * 2
  12531.                    y2% = yBot% + stepSize% * 2
  12532.                    IF y1% < 17 THEN
  12533.                        y1% = 17
  12534.                    END IF
  12535.                    IF y2% > yMax% THEN
  12536.                        y2% = yMax%
  12537.                    END IF
  12538.                END IF
  12539.                stepSize% = stepSize% \ 2
  12540.            LOOP UNTIL stepSize% = 0
  12541.  
  12542.          ' Scan to find left and right edges of the object
  12543.            stepSize% = 32
  12544.            xLeft% = xMax%
  12545.            xRight% = 0
  12546.            x1% = 0
  12547.            x2% = xMax%
  12548.            DO
  12549.                FOR x% = x1% TO x2% STEP stepSize%
  12550.                    IF x% < xLeft% OR x% > xRight% THEN
  12551.                        GET (x%, yTop%)-(x%, yBot%), edge%
  12552.                        LINE (x%, yTop%)-(x%, yBot%)
  12553.                        FOR i% = 2 TO size%
  12554.                            IF edge%(i%) THEN
  12555.                                IF x% < xLeft% THEN
  12556.                                    xLeft% = x%
  12557.                                END IF
  12558.                                IF x% > xRight% THEN
  12559.                                    xRight% = x%
  12560.                                END IF
  12561.                                i% = size%
  12562.                            END IF
  12563.                        NEXT i%
  12564.                        PUT (x%, yTop%), edge%, PSET
  12565.                    END IF
  12566.                NEXT x%
  12567.                IF xLeft% <= xRight% THEN
  12568.                    x1% = xLeft% - stepSize% * 2
  12569.                    x2% = xRight% + stepSize% * 2
  12570.                    IF x1% < 0 THEN
  12571.                        x1% = 0
  12572.                    END IF
  12573.                    IF x2% > xMax% THEN
  12574.                        x2% = xMax%
  12575.                    END IF
  12576.                END IF
  12577.                stepSize% = stepSize% \ 2
  12578.            LOOP UNTIL stepSize% = 0
  12579.  
  12580.          ' Draw border around the object
  12581.            LINE (xLeft% - 1, yTop% - 1)-(xRight% + 1, yBot% + 1), , B
  12582.  
  12583.          ' Build the right size integer array
  12584.            stepSize% = 256
  12585.            size% = 3
  12586.            DO
  12587.                DO
  12588.                    IF size% < 3 THEN
  12589.                        size% = 3
  12590.                    END IF
  12591.                    REDIM object%(size%)
  12592.                    okayFlag% = TRUE
  12593.                    ON ERROR GOTO ArrayError
  12594.                    GET (xLeft%, yTop%)-(xRight%, yBot%), object%
  12595.                    ON ERROR GOTO 0
  12596.                    IF okayFlag% = FALSE THEN
  12597.                        size% = size% + stepSize%
  12598.                    ELSE
  12599.                        IF stepSize% > 1 THEN
  12600.                            size% = size% - stepSize%
  12601.                        END IF
  12602.                    END IF
  12603.                LOOP UNTIL okayFlag%
  12604.                stepSize% = stepSize% \ 2
  12605.            LOOP UNTIL stepSize% = 0
  12606.  
  12607.          ' Make the name of the object
  12608.            objName$ = LTRIM$(RTRIM$(fileName$)) + "."
  12609.            ndx% = INSTR(objName$, "\")
  12610.            DO WHILE ndx%
  12611.                objName$ = MID$(objName$, ndx% + 1)
  12612.                ndx% = INSTR(objName$, "\")
  12613.            LOOP
  12614.            ndx% = INSTR(objName$, ":")
  12615.            DO WHILE ndx%
  12616.                objName$ = MID$(objName$, ndx% + 1)
  12617.                ndx% = INSTR(objName$, ":")
  12618.            LOOP
  12619.            ndx% = INSTR(objName$, ".")
  12620.            objName$ = LCASE$(LEFT$(objName$, ndx% - 1))
  12621.            IF objName$ = "" THEN
  12622.                objName$ = "xxxxxx"
  12623.            END IF
  12624.  
  12625.          ' Make array name
  12626.            ary$ = objName$ + "%("
  12627.  
  12628.          ' Open the file for the new source lines
  12629.            OPEN fileName$ FOR OUTPUT AS #1
  12630.  
  12631.          ' Print the lines
  12632.            PRINT #1, " "
  12633.            PRINT #1, "  ' " + objName$
  12634.            FOR i% = 1 TO LEN(a$) STEP 50
  12635.                PRINT #1, "  ' (DRAW$) "; CHR$(34);
  12636.                PRINT #1, MID$(a$, i%, 50); CHR$(34)
  12637.            NEXT i%
  12638.            PRINT #1, "    DIM " + ary$; "0 TO";
  12639.            PRINT #1, STR$(size%) + ")"
  12640.            PRINT #1, "    FOR i% = 0 TO"; size%
  12641.            PRINT #1, "        READ h$"
  12642.            PRINT #1, "        " + ary$ + "i%) = VAL(";
  12643.            PRINT #1, CHR$(34) + "&H" + CHR$(34);
  12644.            PRINT #1, " + h$)"
  12645.            PRINT #1, "    NEXT i%"
  12646.            FOR i% = 0 TO size%
  12647.                IF d$ = "" THEN
  12648.                    d$ = "    DATA "
  12649.                ELSE
  12650.                    d$ = d$ + ","
  12651.                END IF
  12652.                d$ = d$ + HEX$(object%(i%))
  12653.                IF LEN(d$) > 60 OR i% = size% THEN
  12654.                    PRINT #1, d$
  12655.                    d$ = ""
  12656.                END IF
  12657.            NEXT i%
  12658.            PRINT #1, " "
  12659.  
  12660.          ' Close the file
  12661.            CLOSE
  12662.  
  12663.          ' Erase the border around the object
  12664.            LINE (xLeft% - 1, yTop% - 1)-(xRight% + 1, yBot% + 1), 0, B
  12665.  
  12666.        END SUB
  12667.    ──────────────────────────────────────────────────────────────────────────
  12668.  
  12669.  
  12670.  
  12671.  ────────────────────────────────────────────────────────────────────────────
  12672.  PARSE
  12673.  
  12674.    The PARSE toolbox demonstrates the ParseLine and ParseWord subprograms.
  12675.    A sample string of text (x$) is parsed by each of these subprograms, and
  12676.    the results are displayed for review.
  12677.  
  12678.    The purpose of these subprograms is to split a string into substrings,
  12679.    where each substring is delineated by any of a given set of characters
  12680.    that you define. For example, a string can be parsed into individual words
  12681.    by splitting the string wherever spaces or commas occur.
  12682.  
  12683.    A common use for these subprograms would be the processing of a list of
  12684.    commands passed to a QuickBASIC program from the MS-DOS command line,
  12685.    available in the special variable COMMAND$. The HEX2BIN, BIN2HEX, and
  12686.    QBFMT programs use the PARSE toolbox in this way.
  12687.  
  12688.    Name                     Type    Description
  12689.    ──────────────────────────────────────────────────────────────────────────
  12690.    PARSE.BAS                       Demo module
  12691.    ParseLine                Sub     Breaks a string into individual words
  12692.    ParseWord                Sub     Parses and removes first word from string
  12693.    ──────────────────────────────────────────────────────────────────────────
  12694.  
  12695.  
  12696.  Demo Module: PARSE
  12697.  
  12698.    ──────────────────────────────────────────────────────────────────────────
  12699.      ' ************************************************
  12700.      ' **  Name:          PARSE                      **
  12701.      ' **  Type:          Toolbox                    **
  12702.      ' **  Module:        PARSE.BAS                  **
  12703.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12704.      ' ************************************************
  12705.      '
  12706.      ' USAGE:           No command line parameters
  12707.      ' .MAK FILE:       (none)
  12708.      ' PARAMETERS:      (none)
  12709.      ' VARIABLES:       a$()       Array of words parsed from x$
  12710.      '                  x$         String to be parsed
  12711.      '                  sep$       Characters defining word separation
  12712.      '                  word$      Each word from the string
  12713.      '                  n%         Index to each word in array
  12714.  
  12715.        DECLARE SUB ParseLine (x$, sep$, a$())
  12716.        DECLARE SUB ParseWord (a$, sep$, word$)
  12717.  
  12718.      ' Initialization
  12719.        CLS
  12720.        DIM a$(1 TO 99)
  12721.  
  12722.      ' Demonstrate ParseWord
  12723.        x$ = "This is a test line. A,B,C, etc."
  12724.        sep$ = " ,"
  12725.        PRINT "x$:", x$
  12726.        PRINT "sep$:", CHR$(34); sep$; CHR$(34)
  12727.        ParseWord x$, sep$, word$
  12728.        PRINT "ParseWord x$, sep$, word$"
  12729.        PRINT "x$:", x$
  12730.        PRINT "word$:", word$
  12731.  
  12732.      ' Demonstrate ParseLine
  12733.        PRINT
  12734.        x$ = "This is a test line. A,B,C, etc."
  12735.        sep$ = " ,"
  12736.        PRINT "x$:", x$
  12737.        PRINT "sep$:", CHR$(34); sep$; CHR$(34)
  12738.        ParseLine x$, sep$, a$()
  12739.        PRINT "ParseLine x$, sep$, a$()"
  12740.        PRINT "a$()..."
  12741.        DO
  12742.            n% = n% + 1
  12743.            PRINT n%, a$(n%)
  12744.        LOOP UNTIL a$(n% + 1) = ""
  12745.  
  12746.      ' All done
  12747.        END
  12748.    ──────────────────────────────────────────────────────────────────────────
  12749.  
  12750.  
  12751.  Subprogram: ParseLine
  12752.  
  12753.    Parses a string into individual words, returning the list of words in a
  12754.    string array. You can list any characters in sep$ to define the division
  12755.    between words, but the most commonly used characters are space, comma, and
  12756.    tab. The string array will contain a null string after the last word
  12757.    parsed from the string.
  12758.  
  12759.    ──────────────────────────────────────────────────────────────────────────
  12760.      ' ************************************************
  12761.      ' **  Name:          ParseLine                  **
  12762.      ' **  Type:          Subprogram                 **
  12763.      ' **  Module:        PARSE.BAS                  **
  12764.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12765.      ' ************************************************
  12766.      '
  12767.      ' Breaks a string into an array of words, as defined
  12768.      ' by any characters listed in sep$.
  12769.      '
  12770.      ' EXAMPLE OF USE:  ParseLine x$, sep$, a$()
  12771.      ' PARAMETERS:      x$      String to be parsed
  12772.      '                  sep$    List of characters defined as word separators
  12773.      '                  a$()    Returned array of words
  12774.      ' VARIABLES:       t$      Temporary work string
  12775.      '                  i%      Index to array entries
  12776.      ' MODULE LEVEL
  12777.      '   DECLARATIONS:  DECLARE SUB ParseLine (x$, sep$, a$())
  12778.      '
  12779.        SUB ParseLine (x$, sep$, a$()) STATIC
  12780.            t$ = x$
  12781.            FOR i% = LBOUND(a$) TO UBOUND(a$)
  12782.                ParseWord t$, sep$, a$(i%)
  12783.                IF a$(i%) = "" THEN
  12784.                    EXIT FOR
  12785.                END IF
  12786.            NEXT i%
  12787.            t$ = ""
  12788.        END SUB
  12789.    ──────────────────────────────────────────────────────────────────────────
  12790.  
  12791.  
  12792.  Subprogram: ParseWord
  12793.  
  12794.    Extracts the first word from the front of a string, returning the word and
  12795.    the original string minus the leading word. You can call this routine
  12796.    repeatedly to parse out each word, one at a time. You set the characters
  12797.    that separate words in sep$. For example, to parse words separated by
  12798.    either spaces or commas, set as follows:
  12799.  
  12800.  
  12801.      sep$ = " ,"
  12802.  
  12803.    ──────────────────────────────────────────────────────────────────────────
  12804.      ' ************************************************
  12805.      ' **  Name:          ParseWord                  **
  12806.      ' **  Type:          Subprogram                 **
  12807.      ' **  Module:        PARSE.BAS                  **
  12808.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12809.      ' ************************************************
  12810.      '
  12811.      ' Breaks off the first word in a$, as delimited by
  12812.      ' any characters listed in sep$.
  12813.      '
  12814.      ' EXAMPLE OF USE:  ParseWord a$, sep$, word$
  12815.      ' PARAMETERS:      a$         String to be parsed
  12816.      '                  sep$       List of characters defined as word separato
  12817.      '                  word$      Returned first word parsed from a$
  12818.      ' VARIABLES:       lena%      Length of a$
  12819.      '                  i%         Looping index
  12820.      '                  j%         Looping index
  12821.      '                  k%         Looping index
  12822.      ' MODULE LEVEL
  12823.      '   DECLARATIONS:  DECLARE SUB ParseWord (a$, sep$, word$)
  12824.      '
  12825.        SUB ParseWord (a$, sep$, word$) STATIC
  12826.            word$ = ""
  12827.            lena% = LEN(a$)
  12828.            IF a$ = "" THEN
  12829.                EXIT SUB
  12830.            END IF
  12831.            FOR i% = 1 TO lena%
  12832.                IF INSTR(sep$, MID$(a$, i%, 1)) = 0 THEN
  12833.                    EXIT FOR
  12834.                END IF
  12835.            NEXT i%
  12836.            FOR j% = i% TO lena%
  12837.                IF INSTR(sep$, MID$(a$, j%, 1)) THEN
  12838.                    EXIT FOR
  12839.                END IF
  12840.            NEXT j%
  12841.            FOR k% = j% TO lena%
  12842.                IF INSTR(sep$, MID$(a$, k%, 1)) = 0 THEN
  12843.                    EXIT FOR
  12844.                END IF
  12845.            NEXT k%
  12846.            IF i% > lena% THEN
  12847.                a$ = ""
  12848.                EXIT SUB
  12849.            END IF
  12850.            IF j% > lena% THEN
  12851.                word$ = MID$(a$, i%)
  12852.                a$ = ""
  12853.                EXIT SUB
  12854.            END IF
  12855.            word$ = MID$(a$, i%, j% - i%)
  12856.            IF k% > lena% THEN
  12857.                a$ = ""
  12858.            ELSE
  12859.                a$ = MID$(a$, k%)
  12860.            END IF
  12861.        END SUB
  12862.    ──────────────────────────────────────────────────────────────────────────
  12863.  
  12864.  
  12865.  
  12866.  ────────────────────────────────────────────────────────────────────────────
  12867.  PROBSTAT
  12868.  
  12869.    The PROBSTAT toolbox is a collection of functions for probability and
  12870.    statistics calculations.
  12871.  
  12872.    Name                          Type   Description
  12873.    ──────────────────────────────────────────────────────────────────────────
  12874.    PROBSTAT.BAS                        Demo module
  12875.    ArithmeticMean#              Func   Arithmetic mean of an array of
  12876.                                         numbers
  12877.    Combinations#                Func   Combinations of n items, r at a time
  12878.    Factorial#                   Func   Factorial of a number
  12879.    GeometricMean#               Func   Geometric mean of an array of numbers
  12880.    HarmonicMean#                Func   Harmonic mean of an array of numbers
  12881.    Permutations#                Func   Permutations of n items, r at a time
  12882.    QuadraticMean#               Func   Quadratic mean of an array of numbers
  12883.    ──────────────────────────────────────────────────────────────────────────
  12884.  
  12885.  
  12886.  Demo Module: PROBSTAT
  12887.  
  12888.    ──────────────────────────────────────────────────────────────────────────
  12889.      ' ************************************************
  12890.      ' **  Name:          PROBSTAT                   **
  12891.      ' **  Type:          Toolbox                    **
  12892.      ' **  Module:        PROBSTAT.BAS               **
  12893.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12894.      ' ************************************************
  12895.      '
  12896.      ' Demonstrates several probability- and statistics-
  12897.      ' related mathematical functions.
  12898.      '
  12899.      ' USAGE:          No command line parameters
  12900.      ' .MAK FILE:      (none)
  12901.      ' PARAMETERS:     (none)
  12902.      ' VARIABLES:      a#()       Array of numbers to be processed
  12903.      '                 i%         Index into array
  12904.      '                 n&         Number of items for combinations and permuta
  12905.      '                 r&         Quantity for combinations and permutations
  12906.      '
  12907.        DECLARE FUNCTION Combinations# (n&, r&)
  12908.        DECLARE FUNCTION Factorial# (n&)
  12909.        DECLARE FUNCTION Permutations# (n&, r&)
  12910.        DECLARE FUNCTION GeometricMean# (a#())
  12911.        DECLARE FUNCTION HarmonicMean# (a#())
  12912.        DECLARE FUNCTION ArithmeticMean# (a#())
  12913.        DECLARE FUNCTION QuadraticMean# (a#())
  12914.  
  12915.      ' Demonstrations
  12916.        CLS
  12917.        PRINT "PROBSTAT"
  12918.        PRINT
  12919.        PRINT "Array of numbers..."
  12920.        DIM a#(-3 TO 6)
  12921.        FOR i% = -3 TO 6
  12922.            READ a#(i%)
  12923.            PRINT a#(i%),
  12924.        NEXT i%
  12925.        PRINT
  12926.        DATA  1.2,3.4,5.6,7.8,9.1,2.3,4.5,6.7,8.9,1.2
  12927.  
  12928.        PRINT
  12929.        PRINT "Arithmetic mean = "; ArithmeticMean#(a#())
  12930.        PRINT "Geometric mean  = "; GeometricMean#(a#())
  12931.        PRINT "Harmonic mean   = "; HarmonicMean#(a#())
  12932.        PRINT "Quadratic mean  = "; QuadraticMean#(a#())
  12933.        PRINT
  12934.  
  12935.        n& = 17
  12936.        r& = 5
  12937.        PRINT "Combinations of"; n&; "objects taken";
  12938.        PRINT r&; "at a time = "; Combinations#(n&, r&)
  12939.  
  12940.        PRINT "Permutations of"; n&; "objects taken";
  12941.        PRINT r&; "at a time = "; Permutations#(n&, r&)
  12942.  
  12943.        PRINT
  12944.        PRINT "Factorial of 17 = "; Factorial#(17&)
  12945.  
  12946.      ' All done
  12947.        END
  12948.    ──────────────────────────────────────────────────────────────────────────
  12949.  
  12950.  
  12951.  Function: ArithmeticMean#
  12952.  
  12953.    Returns the arithmetic mean of an array of double-precision numbers.
  12954.  
  12955.    ──────────────────────────────────────────────────────────────────────────
  12956.      ' ************************************************
  12957.      ' **  Name:          ArithmeticMean#            **
  12958.      ' **  Type:          Function                   **
  12959.      ' **  Module:        PROBSTAT.BAS               **
  12960.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12961.      ' ************************************************
  12962.      '
  12963.      ' Returns the arithmetic mean of an array of numbers.
  12964.      '
  12965.      ' EXAMPLE OF USE:  ArithmeticMean# a#()
  12966.      ' PARAMETERS:      a#()       Array of double-precision numbers to be
  12967.      '                             processed
  12968.      ' VARIABLES:       n%         Count of array entries
  12969.      '                  sum#       Sum of the array entries
  12970.      '                  i%         Looping index
  12971.      ' MODULE LEVEL
  12972.      '   DECLARATIONS:  DECLARE FUNCTION ArithmeticMean# (a#())
  12973.      '
  12974.        FUNCTION ArithmeticMean# (a#()) STATIC
  12975.            n% = 0
  12976.            sum# = 0
  12977.            FOR i% = LBOUND(a#) TO UBOUND(a#)
  12978.                n% = n% + 1
  12979.                sum# = sum# + a#(i%)
  12980.            NEXT i%
  12981.            ArithmeticMean# = sum# / n%
  12982.        END FUNCTION
  12983.    ──────────────────────────────────────────────────────────────────────────
  12984.  
  12985.  
  12986.  Function: Combinations#
  12987.  
  12988.    Calculates the number of combinations of n& items taken r& at a time. This
  12989.    function returns a double-precision result to allow for larger answers.
  12990.  
  12991.    ──────────────────────────────────────────────────────────────────────────
  12992.      ' ************************************************
  12993.      ' **  Name:          Combinations#              **
  12994.      ' **  Type:          Function                   **
  12995.      ' **  Module:        PROBSTAT.BAS               **
  12996.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  12997.      ' ************************************************
  12998.      '
  12999.      ' Returns the number of combinations of n& items
  13000.      ' taken r& at a time.
  13001.      '
  13002.      ' EXAMPLE OF USE:  c# = Combinations#(n&, r&)
  13003.      ' PARAMETERS:      n&         Number of items
  13004.      '                  r&         Taken r& at a time
  13005.      ' VARIABLES:       result#    Working result variable
  13006.      '                  j&         Working copy of r&
  13007.      '                  k&         Difference between n& and r&
  13008.      '                  h&         Values from r& through n&
  13009.      '                  i&         Values from 1 through j&
  13010.      ' MODULE LEVEL
  13011.      '   DECLARATIONS:  DECLARE FUNCTION Combinations# (n&, r&)
  13012.      '
  13013.        FUNCTION Combinations# (n&, r&) STATIC
  13014.            result# = 1
  13015.            j& = r&
  13016.            k& = n& - r&
  13017.            h& = n&
  13018.            IF j& > k& THEN
  13019.                SWAP j&, k&
  13020.            END IF
  13021.            FOR i& = 1 TO j&
  13022.                result# = (result# * h&) / i&
  13023.                h& = h& - 1
  13024.            NEXT i&
  13025.            Combinations# = result#
  13026.        END FUNCTION
  13027.    ──────────────────────────────────────────────────────────────────────────
  13028.  
  13029.  
  13030.  Function: Factorial#
  13031.  
  13032.    Returns the factorial of a long integer. The returned value is
  13033.    double-precision, allowing for larger arguments. This is a recursive
  13034.    function. If the argument n& is greater than 1, n& is multiplied by the
  13035.    result of finding the factorial of n& - 1. The result is that this
  13036.    function will call itself n& times.
  13037.  
  13038.    Notice that the STATIC keyword is missing from the end of the FUNCTION
  13039.    statement because recursive functions must not be defined as STATIC.
  13040.  
  13041.    ──────────────────────────────────────────────────────────────────────────
  13042.      ' ************************************************
  13043.      ' **  Name:          Factorial#                 **
  13044.      ' **  Type:          Function                   **
  13045.      ' **  Module:        PROBSTAT.BAS               **
  13046.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13047.      ' ************************************************
  13048.      '
  13049.      ' Returns the factorial of n& (recursive).
  13050.      '
  13051.      ' EXAMPLE OF USE:  f# = Factorial#(n&)
  13052.      ' PARAMETERS:      n&         Number to be evaluated
  13053.      ' VARIABLES:       (none)
  13054.      ' MODULE LEVEL
  13055.      '   DECLARATIONS:  DECLARE FUNCTION Factorial# (n&)
  13056.      '
  13057.        FUNCTION Factorial# (n&)
  13058.            IF n& > 1 THEN
  13059.                Factorial# = n& * Factorial#(n& - 1)
  13060.            ELSE
  13061.                Factorial# = 1
  13062.            END IF
  13063.        END FUNCTION
  13064.    ──────────────────────────────────────────────────────────────────────────
  13065.  
  13066.  
  13067.  Function: GeometricMean#
  13068.  
  13069.    Returns the geometric mean of an array of double-precision numbers.
  13070.  
  13071.    ──────────────────────────────────────────────────────────────────────────
  13072.      ' ************************************************
  13073.      ' **  Name:          GeometricMean#             **
  13074.      ' **  Type:          Function                   **
  13075.      ' **  Module:        PROBSTAT.BAS               **
  13076.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13077.      ' ************************************************
  13078.      '
  13079.      ' Returns the geometric mean of an array of numbers.
  13080.      '
  13081.      ' EXAMPLE OF USE:  gm# = GeometricMean#(a#())
  13082.      ' PARAMETERS:      a#()       Array of numbers to be processed
  13083.      ' VARIABLES:       n%         Count of numbers
  13084.      '                  product#   Product of all the numbers
  13085.      '                  i%         Index to array entries
  13086.      ' MODULE LEVEL
  13087.      '   DECLARATIONS:  DECLARE FUNCTION GeometricMean# (a#())
  13088.      '
  13089.        FUNCTION GeometricMean# (a#()) STATIC
  13090.            n% = 0
  13091.            product# = 1
  13092.            FOR i% = LBOUND(a#) TO UBOUND(a#)
  13093.                n% = n% + 1
  13094.                product# = product# * a#(i%)
  13095.            NEXT i%
  13096.            GeometricMean# = product# ^ (1 / n%)
  13097.        END FUNCTION
  13098.    ──────────────────────────────────────────────────────────────────────────
  13099.  
  13100.  
  13101.  Function: HarmonicMean#
  13102.  
  13103.    Returns the harmonic mean of an array of double-precision numbers.
  13104.  
  13105.    ──────────────────────────────────────────────────────────────────────────
  13106.      ' ************************************************
  13107.      ' **  Name:          HarmonicMean#              **
  13108.      ' **  Type:          Function                   **
  13109.      ' **  Module:        PROBSTAT.BAS               **
  13110.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13111.      ' ************************************************
  13112.      '
  13113.      ' Returns the harmonic mean of an array of numbers.
  13114.      '
  13115.      ' EXAMPLE OF USE:  hm# = HarmonicMean#(a#())
  13116.      ' PARAMETERS:      a#()       Array of numbers to be processed
  13117.      ' VARIABLES:       n%         Number of array entries
  13118.      '                  sum#       Sum of the reciprocal of each number
  13119.      '                  i%         Index to each array entry
  13120.      ' MODULE LEVEL
  13121.      '   DECLARATIONS:  DECLARE FUNCTION HarmonicMean# (a#())
  13122.      '
  13123.        FUNCTION HarmonicMean# (a#()) STATIC
  13124.            n% = 0
  13125.            sum# = 0
  13126.            FOR i% = LBOUND(a#) TO UBOUND(a#)
  13127.                n% = n% + 1
  13128.                sum# = sum# + 1# / a#(i%)
  13129.            NEXT i%
  13130.            HarmonicMean# = n% / sum#
  13131.        END FUNCTION
  13132.    ──────────────────────────────────────────────────────────────────────────
  13133.  
  13134.  
  13135.  Function: Permutations#
  13136.  
  13137.    Returns the number of permutations of n& items taken r& at a time.
  13138.  
  13139.    ──────────────────────────────────────────────────────────────────────────
  13140.      ' ************************************************
  13141.      ' **  Name:          Permutations#              **
  13142.      ' **  Type:          Function                   **
  13143.      ' **  Module:        PROBSTAT.BAS               **
  13144.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13145.      ' ************************************************
  13146.      '
  13147.      ' Returns the permutations of n& items taken r& at a time.
  13148.      '
  13149.      ' EXAMPLE OF USE:  perm# = Permutations#(n&, r&)
  13150.      ' PARAMETERS:      n&         Number of items
  13151.      '                  r&         Taken r& at a time
  13152.      ' VARIABLES:       p#         Working variable for permutations
  13153.      '                  i&         Loop index
  13154.      ' MODULE LEVEL
  13155.      '   DECLARATIONS:  DECLARE FUNCTION Permutations# (n&, r&)
  13156.      '
  13157.        FUNCTION Permutations# (n&, r&) STATIC
  13158.            p# = 1
  13159.            FOR i& = n& - r& + 1 TO n&
  13160.                p# = p# * i&
  13161.            NEXT i&
  13162.            Permutations# = p#
  13163.        END FUNCTION
  13164.    ──────────────────────────────────────────────────────────────────────────
  13165.  
  13166.  
  13167.  Function: QuadraticMean#
  13168.  
  13169.    Returns the quadratic mean of an array of double-precision numbers.
  13170.  
  13171.    ──────────────────────────────────────────────────────────────────────────
  13172.      ' ************************************************
  13173.      ' **  Name:          QuadraticMean#             **
  13174.      ' **  Type:          Function                   **
  13175.      ' **  Module:        PROBSTAT.BAS               **
  13176.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13177.      ' ************************************************
  13178.      '
  13179.      ' Returns the quadratic mean of an array of numbers.
  13180.      '
  13181.      ' EXAMPLE OF USE:  qm# = QuadraticMean#(a#())
  13182.      ' PARAMETERS:      a#()       Array of numbers to be processed
  13183.      ' VARIABLES:       n%         Count of array entries
  13184.      '                  sum#       Sum of the square of each number
  13185.      ' MODULE LEVEL
  13186.      '   DECLARATIONS:  DECLARE FUNCTION QuadraticMean# (a#())
  13187.      '
  13188.         FUNCTION QuadraticMean# (a#()) STATIC
  13189.            n% = 0
  13190.            sum# = 0
  13191.            FOR i% = LBOUND(a#) TO UBOUND(a#)
  13192.                n% = n% + 1
  13193.                sum# = sum# + a#(i%) ^ 2
  13194.            NEXT i%
  13195.            QuadraticMean# = SQR(sum# / n%)
  13196.        END FUNCTION
  13197.    ──────────────────────────────────────────────────────────────────────────
  13198.  
  13199.  
  13200.  
  13201.  ────────────────────────────────────────────────────────────────────────────
  13202.  QBFMT
  13203.  
  13204.    The QBFMT program reformats QuickBASIC modules by indenting the lines
  13205.    according to the structure of the statements. For example, all lines found
  13206.    between matching DO and LOOP statements are indented four character
  13207.    columns more than the DO and LOOP statements. Of course, nested structures
  13208.    are indented even farther.
  13209.  
  13210.    One advantage of processing a file with this program is that improperly
  13211.    matched statements are detected. Improper matching can happen if, for
  13212.    example, you forget to type an END IF statement to match an IF. A special
  13213.    comment line is placed in the file at the point where each error is
  13214.    detected.
  13215.  
  13216.    This utility program was an immense help throughout the creation of this
  13217.    book. Each module was formatted with this program, resulting in a
  13218.    consistent structure, style, and general appearance to the listings.
  13219.  
  13220.    Notice that QuickBASIC programs to be processed by the QBFMT program must
  13221.    be saved in text format and have the extension .BAS.
  13222.  
  13223.    Name                     Type    Description
  13224.    ──────────────────────────────────────────────────────────────────────────
  13225.    QBFMT.BAS                       Program module
  13226.    Indent                  Sub     Performs line indention
  13227.    SetCode                 Sub     Determines indention code by keyword
  13228.    SplitUp                 Sub     Splits line into major components
  13229.    ──────────────────────────────────────────────────────────────────────────
  13230.  
  13231.  
  13232.  Program Module: QBFMT
  13233.  
  13234.    ──────────────────────────────────────────────────────────────────────────
  13235.      ' ************************************************
  13236.      ' **  Name:          QBFMT                      **
  13237.      ' **  Type:          Program                    **
  13238.      ' **  Module:        QBFMT.BAS                  **
  13239.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13240.      ' ************************************************
  13241.      '
  13242.      ' Reformats a QuickBASIC program by indenting
  13243.      ' lines according to the structure of the statements.  The
  13244.      ' default amount is 4 spaces if no indention parameter
  13245.      ' is given on the command line.
  13246.      '
  13247.      ' USAGE:  QBFMT filename [indention]
  13248.      '         Command$ = filename [indention]
  13249.      ' .MAK FILE:   QBFMT.BAS
  13250.      '              PARSE.BAS
  13251.      '              STRINGS.BAS
  13252.      ' PARAMETERS:  filename(.BAS)   Name of QuickBASIC module to be formatted
  13253.      '                               the module must be saved in "Text" format
  13254.      ' VARIABLES:   md$              Working copy of COMMAND$ contents
  13255.      '              fileName$        Name of QuickBASIC module to be formatted
  13256.      '              dpoint%          Position of the decimal point character
  13257.      '                               in cmd$
  13258.      '              ndent$           Part of cmd$ dealing with optional
  13259.      '                               indention amount
  13260.      '              indention%       Number of character columns per
  13261.      '                               indention level
  13262.      '              progline$        Each line of the file being processed
  13263.      '              indentLevel%     Keeps track of current indention amount
  13264.      '              nest$            Message placed in file if faulty structur
  13265.      '                               detected
  13266.  
  13267.        DECLARE FUNCTION LtrimSet$ (a$, set$)
  13268.        DECLARE FUNCTION RtrimSet$ (a$, set$)
  13269.        DECLARE SUB Indent (a$, indention%, indentLevel%)
  13270.        DECLARE SUB ParseWord (a$, sep$, word$)
  13271.        DECLARE SUB SetCode (a$, keyWord$, code%)
  13272.        DECLARE SUB SplitUp (a$, comment$, keyWord$)
  13273.  
  13274.      ' Decipher the user command line
  13275.        cmd$ = COMMAND$
  13276.        IF cmd$ = "" THEN
  13277.            PRINT
  13278.            PRINT "Usage:  QBFMT filename(.BAS) [indention]"
  13279.            SYSTEM
  13280.        ELSE
  13281.            ParseWord cmd$, " ,", fileName$
  13282.            dpoint% = INSTR(fileName$, ".")
  13283.            IF dpoint% THEN
  13284.                fileName$ = LEFT$(fileName$, dpoint% - 1)
  13285.            END IF
  13286.            ParseWord cmd$, " ,", ndent$
  13287.            indention% = VAL(ndent$)
  13288.            IF indention% < 1 THEN
  13289.                indention% = 4
  13290.            END IF
  13291.        END IF
  13292.  
  13293.      ' Try to open the indicated files
  13294.        PRINT
  13295.        ON ERROR GOTO ErrorTrapOne
  13296.        OPEN fileName$ + ".BAS" FOR INPUT AS #1
  13297.        OPEN fileName$ + ".@$@" FOR OUTPUT AS #2
  13298.        ON ERROR GOTO 0
  13299.  
  13300.      ' Process each line of the file
  13301.        DO
  13302.            LINE INPUT #1, progLine$
  13303.            Indent progLine$, indention%, indentLevel%
  13304.            PRINT progLine$
  13305.            PRINT #2, progLine$
  13306.            IF indentLevel% < 0 OR (EOF(1) AND indentLevel% <> 0) THEN
  13307.                SOUND 555, 5
  13308.                SOUND 333, 9
  13309.                nest$ = "'<<<<<<<<<<<<<<<<<<<<< Nesting error detected!"
  13310.                PRINT nest$
  13311.                PRINT #2, nest$
  13312.                indentLevel% = 0
  13313.            END IF
  13314.        LOOP UNTIL EOF(1)
  13315.  
  13316.      ' Close all files
  13317.        CLOSE
  13318.  
  13319.      ' Delete any old .BAK file
  13320.        ON ERROR GOTO ErrorTrapTwo
  13321.        KILL fileName$ + ".BAK"
  13322.        ON ERROR GOTO 0
  13323.  
  13324.      ' Rename the files
  13325.        NAME fileName$ + ".BAS" AS fileName$ + ".BAK"
  13326.        NAME fileName$ + ".@$@" AS fileName$ + ".BAS"
  13327.  
  13328.      ' We're done
  13329.        END
  13330.  
  13331.      '----------- Error trapping routines
  13332.  
  13333.    ErrorTrapOne:
  13334.        PRINT "Error while opening files"
  13335.        SYSTEM
  13336.  
  13337.    ErrorTrapTwo:
  13338.        RESUME NEXT
  13339.    ──────────────────────────────────────────────────────────────────────────
  13340.  
  13341.  
  13342.  Subprogram: Indent
  13343.  
  13344.    Performs the task of indenting each line of a program for the QBFMT
  13345.    program. The indention amount is determined by the first word of each
  13346.    line, and spaces are added to the front end of each line accordingly.
  13347.  
  13348.    ──────────────────────────────────────────────────────────────────────────
  13349.      ' ************************************************
  13350.      ' **  Name:          Indent                     **
  13351.      ' **  Type:          Subprogram                 **
  13352.      ' **  Module:        QBFMT.BAS                  **
  13353.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13354.      ' ************************************************
  13355.      '
  13356.      ' Determines the indention for each line.
  13357.      '
  13358.      ' EXAMPLE OF USE:  Indent a$, indention%, indentLevel%
  13359.      ' PARAMETERS:      a$             Program line to be indented
  13360.      '                  indention%     Spaces to add for each indention level
  13361.      '                  indentLevel%   Level of indention
  13362.      ' VARIABLES:       comment$       Part of program line that represents a
  13363.      '                                 REMARK
  13364.      '                  keyWord$       First word of the program line
  13365.      '                  code%          Indention control code determined by
  13366.      '                                 keyWord$
  13367.      ' MODULE LEVEL
  13368.      '   DECLARATIONS:  DECLARE SUB Indent (a$, indention%, indentLevel%)
  13369.      '
  13370.      SUB Indent (a$, indention%, indentLevel%) STATIC
  13371.  
  13372.          ' Break line into manageable parts
  13373.            SplitUp a$, comment$, keyWord$
  13374.  
  13375.            IF keyWord$ <> "" THEN
  13376.  
  13377.              ' Set indention code according to type of keyword
  13378.                SetCode a$, keyWord$, code%
  13379.  
  13380.              ' Build a string of spaces for the indicated indention
  13381.                SELECT CASE code%
  13382.                CASE -2
  13383.                    a$ = SPACE$(indention% * indentLevel%) + a$
  13384.                CASE -1
  13385.                    a$ = SPACE$(indention% * indentLevel%) + a$
  13386.                    indentLevel% = indentLevel% - 1
  13387.                CASE 0
  13388.                    a$ = SPACE$(indention% * (indentLevel% + 1)) + a$
  13389.                CASE 1
  13390.                    indentLevel% = indentLevel% + 1
  13391.                    a$ = SPACE$(indention% * indentLevel%) + a$
  13392.                CASE ELSE
  13393.                END SELECT
  13394.            ELSE
  13395.                a$ = SPACE$(indention% * indentLevel% + 2)
  13396.            END IF
  13397.  
  13398.          ' Round out the position of trailing comments
  13399.            IF comment$ <> "" THEN
  13400.                IF a$ <> SPACE$(LEN(a$)) AND a$ <> "" THEN
  13401.                    a$ = a$ + SPACE$(16 - (LEN(a$) MOD 16))
  13402.                END IF
  13403.            END IF
  13404.  
  13405.          ' Tack the comment back onto the end of the line
  13406.            a$ = a$ + comment$
  13407.  
  13408.        END SUB
  13409.    ──────────────────────────────────────────────────────────────────────────
  13410.  
  13411.  
  13412.  Subprogram: SetCode
  13413.  
  13414.    Determines the indention code for the QBFMT program based on the first
  13415.    word of each program line. For example, if the first word of a program
  13416.    line is FOR, a code number is returned that signals the QBFMT program to
  13417.    indent the following lines one more level. When NEXT is encountered, the
  13418.    indention level decreases by one.
  13419.  
  13420.    ──────────────────────────────────────────────────────────────────────────
  13421.      ' ************************************************
  13422.      ' **  Name:          SetCode                    **
  13423.      ' **  Type:          Subprogram                 **
  13424.      ' **  Module:        QBFMT.BAS                  **
  13425.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13426.      ' ************************************************
  13427.      '
  13428.      ' Determines a code number for the type of indention
  13429.      ' implied by the various types of keywords that begin
  13430.      ' each line of QuickBASIC programs.
  13431.      '
  13432.      ' EXAMPLE OF USE:   SetCode a$, keyWord$, code%
  13433.      ' PARAMETERS:       a$         Program line to indent
  13434.      '                   keyWord$   First word of the program line
  13435.      '                   code%      Returned code indicating the action to be
  13436.      ' VARIABLES:        (none)
  13437.      ' MODULE LEVEL
  13438.      '   DECLARATIONS:   DECLARE SUB SetCode (a$, keyWord$, code%)
  13439.      '
  13440.        SUB SetCode (a$, keyWord$, code%) STATIC
  13441.            SELECT CASE keyWord$
  13442.            CASE "DEF"
  13443.                IF INSTR(a$, "=") THEN
  13444.                    code% = 0
  13445.                ELSE
  13446.                    IF INSTR(a$, " SEG") = 0 THEN
  13447.                        code% = 1
  13448.                    END IF
  13449.                END IF
  13450.            CASE "ELSE"
  13451.                code% = -2
  13452.            CASE "ELSEIF"
  13453.                code% = -2
  13454.            CASE "CASE"
  13455.                code% = -2
  13456.            CASE "END"
  13457.                IF a$ <> "END" THEN
  13458.                    code% = -1
  13459.                ELSE
  13460.                    code% = 0
  13461.                END IF
  13462.            CASE "FOR"
  13463.                code% = 1
  13464.            CASE "DO"
  13465.                code% = 1
  13466.            CASE "SELECT"
  13467.                code% = 1
  13468.            CASE "IF"
  13469.                IF RIGHT$(a$, 4) = "THEN" THEN
  13470.                    code% = 1
  13471.                ELSE
  13472.                    code% = 0
  13473.                END IF
  13474.            CASE "NEXT"
  13475.                code% = -1
  13476.            CASE "LOOP"
  13477.                code% = -1
  13478.            CASE "SUB"
  13479.                code% = 1
  13480.            CASE "FUNCTION"
  13481.                code% = 1
  13482.            CASE "TYPE"
  13483.                code% = 1
  13484.            CASE "WHILE"
  13485.                code% = 1
  13486.            CASE "WEND"
  13487.                code% = -1
  13488.            CASE ELSE
  13489.                code% = 0
  13490.            END SELECT
  13491.        END SUB
  13492.    ──────────────────────────────────────────────────────────────────────────
  13493.  
  13494.  
  13495.  Subprogram: SplitUp
  13496.  
  13497.    Breaks each program line into its major components for the QBFMT program.
  13498.    Leading spaces and tabs are removed, and the first word and any REMARK
  13499.    part are returned. Later, after the line is indented the proper amount,
  13500.    the parts of the line are patched back together and output to the program
  13501.    listing file.
  13502.  
  13503.    ──────────────────────────────────────────────────────────────────────────
  13504.      ' ************************************************
  13505.      ' **  Name:          SplitUp                    **
  13506.      ' **  Type:          Subprogram                 **
  13507.      ' **  Module:        QBFMT.BAS                  **
  13508.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13509.      ' ************************************************
  13510.      '
  13511.      ' Splits the line into statement, comment, and keyword.
  13512.      '
  13513.      ' EXAMPLE OF USE:  SplitUp a$, comment$, keyWord$
  13514.      ' PARAMETERS:      a$         Program line to be split up
  13515.      '                  comment$   Part of line following "REM" or "'"
  13516.      '                  keyWord$   First word of program line
  13517.      ' VARIABLES:       set$       Characters to be trimmed, space and tab
  13518.      '                  strFlag%   Indication of a quoted string
  13519.      '                  k%         Index to start of REMARK
  13520.      '                  i%         Looping index
  13521.      '                  m%         Pointer to REMARK
  13522.      '                  sptr%      Pointer to first space following the
  13523.      '                             first word in a$
  13524.      ' MODULE LEVEL
  13525.      '   DECLARATIONS:  DECLARE SUB SplitUp (a$, comment$, keyWord$)
  13526.      '
  13527.        SUB SplitUp (a$, comment$, keyWord$) STATIC
  13528.            set$ = " " + CHR$(9)
  13529.            strFlag% = 0
  13530.            k% = 0
  13531.            FOR i% = LEN(a$) TO 1 STEP -1
  13532.                IF MID$(a$, i%, 1) = CHR$(34) THEN
  13533.                    IF strFlag% = 0 THEN
  13534.                        strFlag% = 1
  13535.                    ELSE
  13536.                        strFlag% = 0
  13537.                    END IF
  13538.                END IF
  13539.                IF MID$(a$, i%, 1) = "'" OR MID$(a$, i%, 3) = "REM" THEN
  13540.                    IF strFlag% = 0 THEN
  13541.                        k% = i%
  13542.                    END IF
  13543.                END IF
  13544.            NEXT i%
  13545.            IF k% > 0 THEN
  13546.                m% = 0
  13547.                FOR j% = k% - 1 TO 1 STEP -1
  13548.                    IF INSTR(set$, MID$(a$, j%, 1)) = 0 THEN
  13549.                        IF m% = 0 THEN m% = j%
  13550.                    END IF
  13551.                NEXT j%
  13552.                IF m% THEN
  13553.                    comment$ = MID$(a$, m% + 1)
  13554.                    a$ = LEFT$(a$, m%)
  13555.                ELSE
  13556.                    comment$ = a$
  13557.                    a$ = ""
  13558.                END IF
  13559.            ELSE
  13560.                comment$ = ""
  13561.            END IF
  13562.            a$ = LtrimSet$(a$, set$)
  13563.            a$ = RtrimSet$(a$, set$)
  13564.            comment$ = LtrimSet$(comment$, set$)
  13565.            comment$ = RtrimSet$(comment$, set$)
  13566.            sptr% = INSTR(a$, " ")
  13567.            IF sptr% THEN
  13568.                keyWord$ = UCASE$(LEFT$(a$, sptr% - 1))
  13569.            ELSE
  13570.                keyWord$ = UCASE$(a$)
  13571.            END IF
  13572.        END SUB
  13573.    ──────────────────────────────────────────────────────────────────────────
  13574.  
  13575.  
  13576.  
  13577.  ────────────────────────────────────────────────────────────────────────────
  13578.  QBTREE
  13579.  
  13580.    The QBTREE program performs a recursive directory search and then displays
  13581.    all file entries indented for each level of subdirectory encountered. If a
  13582.    command line parameter is given, the search starts at the indicated path.
  13583.    If no command line parameter is given, the search begins with the current
  13584.    directory.
  13585.  
  13586.    Name                          Type   Description
  13587.    ──────────────────────────────────────────────────────────────────────────
  13588.    QBTREE.BAS                          Program module
  13589.    FileTreeSearch               Sub    Recursive directory search routine
  13590.    ──────────────────────────────────────────────────────────────────────────
  13591.  
  13592.  
  13593.  Program Module: QBTREE
  13594.  
  13595.    ──────────────────────────────────────────────────────────────────────────
  13596.      ' ************************************************
  13597.      ' **  Name:          QBTREE                     **
  13598.      ' **  Type:          Program                    **
  13599.      ' **  Module:        QBTREE.BAS                 **
  13600.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13601.      ' ************************************************
  13602.      '
  13603.      ' This program creates a list of directories and
  13604.      ' subdirectories, and all files in them.  If no
  13605.      ' command line path is given, the search
  13606.      ' begins with the current directory.
  13607.      '
  13608.      ' USAGE:          QBTREE [path]
  13609.      ' REQUIREMENTS:   MIXED.QLB/.LIB
  13610.      ' .MAK FILE:      QBTREE.BAS
  13611.      '                 FILEINFO.BAS
  13612.      ' PARAMETERS:     path       Path for starting directory search
  13613.      ' VARIABLES:      path$      Path string, from the command line, or set
  13614.      '                            to "*.*"
  13615.      '                 indent%    Indention amount for printing
  13616.  
  13617.  
  13618.         TYPE RegTypeX
  13619.            ax    AS INTEGER
  13620.            bx    AS INTEGER
  13621.            cx    AS INTEGER
  13622.            dx    AS INTEGER
  13623.            bp    AS INTEGER
  13624.            si    AS INTEGER
  13625.            di    AS INTEGER
  13626.            flags AS INTEGER
  13627.            ds    AS INTEGER
  13628.            es    AS INTEGER
  13629.        END TYPE
  13630.  
  13631.        TYPE FileDataType
  13632.            finame    AS STRING * 12
  13633.            year      AS INTEGER
  13634.            month     AS INTEGER
  13635.            day       AS INTEGER
  13636.            hour      AS INTEGER
  13637.            minute    AS INTEGER
  13638.            second    AS INTEGER
  13639.            attribute AS INTEGER
  13640.            size      AS LONG
  13641.        END TYPE
  13642.  
  13643.      ' Subprograms
  13644.        DECLARE SUB Interruptx (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
  13645.        DECLARE SUB FindFirstFile (path$, dta$, result%)
  13646.        DECLARE SUB FindNextFile (dta$, result%)
  13647.        DECLARE SUB GetFileData (dta$, file AS FileDataType)
  13648.        DECLARE SUB FileTreeSearch (path$, indent%)
  13649.  
  13650.      ' Create structure for deciphering the DTA file search results
  13651.        DIM file AS FileDataType
  13652.  
  13653.      ' Get the command line path for starting the file search
  13654.        path$ = COMMAND$
  13655.  
  13656.      ' If no path was given, then use "*.*" to search the current directory
  13657.        IF path$ = "" THEN
  13658.            path$ = "*.*"
  13659.        END IF
  13660.  
  13661.      ' If only a drive was given, then add "*.*"
  13662.        IF LEN(path$) = 2 AND RIGHT$(path$, 1) = ":" THEN
  13663.            path$ = path$ + "*.*"
  13664.        END IF
  13665.  
  13666.      ' Adjust the given path if necessary
  13667.        IF INSTR(path$, "*") = 0 AND INSTR(path$, "?") = 0 THEN
  13668.            FindFirstFile path$, dta$, result%
  13669.            IF result% = 0 OR RIGHT$(path$, 1) = "\" THEN
  13670.                IF RIGHT$(path$, 1) <> "\" THEN
  13671.                    path$ = path$ + "\"
  13672.                END IF
  13673.                path$ = path$ + "*.*"
  13674.            END IF
  13675.        END IF
  13676.  
  13677.      ' Start with a clean slate
  13678.        CLS
  13679.  
  13680.      ' Call the recursive search subprogram
  13681.        FileTreeSearch path$, indent%
  13682.  
  13683.      ' That's all there is to it
  13684.        END
  13685.    ──────────────────────────────────────────────────────────────────────────
  13686.  
  13687.  
  13688.  Subprogram: FileTreeSearch
  13689.  
  13690.    Performs a recursive search for filenames in directories. Whenever a
  13691.    subdirectory is encountered, the subprogram builds a modified search path
  13692.    string (by adding the subdirectory name to the end of the current search
  13693.    path) and calls itself again. In this way, all files in all subdirectories
  13694.    are located, starting with the initial search path given.
  13695.  
  13696.    The filenames are printed with an indention amount that is a function of
  13697.    the level of recursion. This means that each subdirectory entry is
  13698.    indented four spaces more than its parent directory.
  13699.  
  13700.    ──────────────────────────────────────────────────────────────────────────
  13701.      ' ************************************************
  13702.      ' **  Name:          FileTreeSearch             **
  13703.      ' **  Type:          Subprogram                 **
  13704.      ' **  Module:        QBTREE.BAS                 **
  13705.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13706.      ' ************************************************
  13707.      '
  13708.      ' Directory searching and listing subprogram for
  13709.      ' the QBTREE program.  (recursive)
  13710.      '
  13711.      ' EXAMPLE OF USE:  FileTreeSearch path$, indent%
  13712.      ' PARAMETERS:      path$      Path for search of files
  13713.      '                  indent%    Level of indention, function of recursion
  13714.      '                             level
  13715.      ' VARIABLES:       file       Structure of type FileDataType
  13716.      '                  path$      Path for search of files
  13717.      '                  dta$       Disk Transfer Area buffer string
  13718.      '                  result%    Returned result code from FindFirstFile or
  13719.      '                             FindNextFile
  13720.      '                  newPath$   Path with added subdirectory for recursive
  13721.      '                             search
  13722.      ' MODULE LEVEL
  13723.      '   DECLARATIONS: TYPE FileDataType
  13724.      '                     finame    AS STRING * 12
  13725.      '                     year      AS INTEGER
  13726.      '                     month     AS INTEGER
  13727.      '                     day       AS INTEGER
  13728.      '                     hour      AS INTEGER
  13729.      '                     minute    AS INTEGER
  13730.      '                     second    AS INTEGER
  13731.      '                     attribute AS INTEGER
  13732.      '                     size      AS LONG
  13733.      '                  END TYPE
  13734.      '
  13735.      '                  DECLARE SUB FindFirstFile (path$, dta$, result%)
  13736.      '                  DECLARE SUB FindNextFile (dta$, result%)
  13737.      '                  DECLARE SUB GetFileData (dta$, file AS FileDataType)
  13738.      '                  DECLARE SUB FileTreeSearch (path$, indent%)
  13739.      '
  13740.        SUB FileTreeSearch (path$, indent%)
  13741.  
  13742.          ' Create structure for deciphering the DTA file search results
  13743.            DIM file AS FileDataType
  13744.  
  13745.          ' Find the first file given the current search path
  13746.            FindFirstFile path$, dta$, result%
  13747.  
  13748.          ' Search through the directory for all files
  13749.            DO UNTIL result%
  13750.  
  13751.              ' Unpack the Disk Transfer Area for file information
  13752.                GetFileData dta$, file
  13753.  
  13754.              ' Skip the "." and ".." files
  13755.                IF LEFT$(file.finame, 1) <> "." THEN
  13756.  
  13757.                  ' Print the filename, indented to show tree structure
  13758.                    PRINT SPACE$(indent% * 4); file.finame;
  13759.  
  13760.                  ' Print any other desired file information here
  13761.                    PRINT TAB(50); file.size;
  13762.                    PRINT TAB(58); file.attribute
  13763.  
  13764.                  ' If we found a directory, then recursively search through it
  13765.                    IF file.attribute AND &H10 THEN
  13766.  
  13767.                      ' Modify path$ to add this new directory to the search pa
  13768.                        newPath$ = path$
  13769.                        IF INSTR(newPath$, "\") = 0 THEN
  13770.                            newPath$ = "\" + newPath$
  13771.                        END IF
  13772.                        DO WHILE RIGHT$(newPath$, 1) <> "\"
  13773.                            newPath$ = LEFT$(newPath$, LEN(newPath$) - 1)
  13774.                        LOOP
  13775.                        newPath$ = newPath$ + file.finame + "\*.*"
  13776.  
  13777.                      ' Example of recursion here
  13778.                        FileTreeSearch newPath$, indent% + 1
  13779.  
  13780.                    END IF
  13781.  
  13782.                END IF
  13783.  
  13784.              ' Try to find the next file in this directory
  13785.                FindNextFile dta$, result%
  13786.  
  13787.            LOOP
  13788.  
  13789.        END SUB
  13790.    ──────────────────────────────────────────────────────────────────────────
  13791.  
  13792.  
  13793.  
  13794.  ────────────────────────────────────────────────────────────────────────────
  13795.  QCAL
  13796.  
  13797.    The QCAL program provides scientific calculator functions from the MS-DOS
  13798.    command line. This program is a modified and expanded version of
  13799.    MINICAL.BAS, presented earlier in this book. The original version's goal
  13800.    was to demonstrate the methods used to create a small, modular program.
  13801.    The functionality of the program wasn't the important issue. Here, the
  13802.    program has been enhanced, and several new functions and capabilities make
  13803.    this program more useful as a utility. Run the program by typing QCAL
  13804.    HELP, QCAL ?, or QCAL, and a list of the available functions will be
  13805.    displayed. In addition to the original five functions, several new
  13806.    trigonometric, hyperbolic, and logarithmic functions have been added.
  13807.  
  13808.    You might want to review the original MINICAL program, which is on pages
  13809.    5 through 18. You'll find an explanation of how the numeric values are
  13810.    placed on the stack and how the functions operate on those values.
  13811.  
  13812.    Because of the modular, structured organization of QuickBASIC programs,
  13813.    you can easily modify this program to include other functions. To add a
  13814.    new function, modify the Process and QcalHelp subprograms where
  13815.    applicable, and follow the same pattern of stack and variable
  13816.    manipulations exhibited by the other routines when writing your own.
  13817.  
  13818.    Name                          Type   Description
  13819.    ──────────────────────────────────────────────────────────────────────────
  13820.    QCAL.BAS                            Program module
  13821.    DisplayStack                  Sub    Displays final results of the program
  13822.    NextParameter$                Func   Extracts number or command from
  13823.                                         COMMAND$
  13824.    Process                       Sub    Controls action for command line
  13825.                                         parameters
  13826.    QcalHelp                      Sub    Provides a "Help" display for program
  13827.    ──────────────────────────────────────────────────────────────────────────
  13828.  
  13829.  
  13830.  Program Module: QCAL
  13831.  
  13832.    ──────────────────────────────────────────────────────────────────────────
  13833.      ' ************************************************
  13834.      ' **  Name:          QCAL                       **
  13835.      ' **  Type:          Program                    **
  13836.      ' **  Module:        QCAL.BAS                   **
  13837.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13838.      ' ************************************************
  13839.      '
  13840.      ' USAGE:           QCAL [number] [function] [...]
  13841.      ' .MAK FILE:       QCAL.BAS
  13842.      '                  QCALMATH.BAS
  13843.      ' PARAMETERS:      [number]      Numbers to be placed on the stack
  13844.      '                  [function]    Operations to be performed on the stack
  13845.      '                                contents
  13846.      ' VARIABLES:       cmd$          Working copy of COMMAND$
  13847.      '                  stack#()      Array representing the numeric stack
  13848.      '                  ptr%          Index into the stack
  13849.      '                  parm$         Each number of command extracted from cm
  13850.  
  13851.      ' Constants
  13852.        CONST PI = 3.141592653589793#
  13853.  
  13854.      ' Functions
  13855.        DECLARE FUNCTION AbsoluteX# (x#)
  13856.        DECLARE FUNCTION Add# (y#, x#)
  13857.        DECLARE FUNCTION ArcCosine# (x#)
  13858.        DECLARE FUNCTION ArcHypCosine# (x#)
  13859.        DECLARE FUNCTION ArcHypSine# (x#)
  13860.        DECLARE FUNCTION ArcHypTangent# (x#)
  13861.        DECLARE FUNCTION ArcSine# (x#)
  13862.        DECLARE FUNCTION ArcTangent# (x#)
  13863.        DECLARE FUNCTION Ceil# (x#)
  13864.        DECLARE FUNCTION ChangeSign# (x#)
  13865.        DECLARE FUNCTION Cosine# (x#)
  13866.        DECLARE FUNCTION Divide# (y#, x#)
  13867.        DECLARE FUNCTION Exponential# (x#)
  13868.        DECLARE FUNCTION FractionalPart# (x#)
  13869.        DECLARE FUNCTION HypCosine# (x#)
  13870.        DECLARE FUNCTION HypSine# (x#)
  13871.        DECLARE FUNCTION HypTangent# (x#)
  13872.        DECLARE FUNCTION IntegerPart# (x#)
  13873.        DECLARE FUNCTION LogBase10# (x#)
  13874.        DECLARE FUNCTION LogBaseN# (y#, x#)
  13875.        DECLARE FUNCTION LogE# (x#)
  13876.        DECLARE FUNCTION Modulus# (y#, x#)
  13877.        DECLARE FUNCTION Multiply# (y#, x#)
  13878.        DECLARE FUNCTION NextParameter$ (cmd$)
  13879.        DECLARE FUNCTION OneOverX# (x#)
  13880.        DECLARE FUNCTION Sign# (x#)
  13881.        DECLARE FUNCTION Sine# (x#)
  13882.        DECLARE FUNCTION SquareRoot# (x#)
  13883.        DECLARE FUNCTION Subtract# (y#, x#)
  13884.        DECLARE FUNCTION Tangent# (x#)
  13885.        DECLARE FUNCTION Xsquared# (x#)
  13886.        DECLARE FUNCTION YRaisedToX# (y#, x#)
  13887.  
  13888.      ' Subprograms
  13889.        DECLARE SUB QcalHelp ()
  13890.        DECLARE SUB Process (parm$, stack#(), ptr%)
  13891.        DECLARE SUB DisplayStack (stack#(), ptr%)
  13892.        DECLARE SUB SwapXY (stack#(), ptr%)
  13893.  
  13894.      ' Get the command line
  13895.        cmd$ = COMMAND$
  13896.  
  13897.      ' First check if user is asking for help
  13898.        IF cmd$ = "" OR cmd$ = "HELP" OR cmd$ = "?" THEN
  13899.            QcalHelp
  13900.            SYSTEM
  13901.        END IF
  13902.  
  13903.      ' Create a pseudo stack
  13904.        DIM stack#(1 TO 20)
  13905.        ptr% = 0
  13906.  
  13907.      ' Process each part of the command line
  13908.        DO UNTIL cmd$ = ""
  13909.            parm$ = NextParameter$(cmd$)
  13910.            Process parm$, stack#(), ptr%
  13911.            IF ptr% < 1 THEN
  13912.                PRINT "Not enough stack values"
  13913.                SYSTEM
  13914.            END IF
  13915.        LOOP
  13916.  
  13917.      ' Display results
  13918.        DisplayStack stack#(), ptr%
  13919.  
  13920.      ' All done
  13921.        END
  13922.    ──────────────────────────────────────────────────────────────────────────
  13923.  
  13924.  
  13925.  Subprogram: DisplayStack
  13926.  
  13927.    Displays the final results of the QCAL program. When the QCAL program is
  13928.    finished, one or more numeric values are left on the stack, representing
  13929.    the final values of the calculations. If the stack has a single value
  13930.    remaining on it, this number is displayed with the label Result.... If,
  13931.    however, two or more values are left on the stack after QCAL has acted on
  13932.    all functions, the values are displayed with the label Stack ...,
  13933.    indicating to the user that more than a single result was left on the
  13934.    stack.
  13935.  
  13936.    ──────────────────────────────────────────────────────────────────────────
  13937.      ' ************************************************
  13938.      ' **  Name:          DisplayStack               **
  13939.      ' **  Type:          Subprogram                 **
  13940.      ' **  Module:        QCAL.BAS                   **
  13941.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13942.      ' ************************************************
  13943.      '
  13944.      ' Displays the value(s) left on the stack when QCAL
  13945.      ' is finished processing the command line.
  13946.      '
  13947.      ' EXAMPLE OF USE:  DisplayStack stack#(), ptr%
  13948.      ' PARAMETERS:      stack#()   Array of numbers representing the stack
  13949.      '                  ptr%       Index into the stack
  13950.      ' VARIABLES:       i%         Looping index
  13951.      ' MODULE LEVEL
  13952.      '   DECLARATIONS:  DECLARE SUB DisplayStack (stack#(), ptr%)
  13953.      '
  13954.        SUB DisplayStack (stack#(), ptr%) STATIC
  13955.            PRINT
  13956.            IF ptr% > 1 THEN
  13957.                PRINT "Stack ... ",
  13958.            ELSE
  13959.                PRINT "Result... ",
  13960.            END IF
  13961.            FOR i% = 1 TO ptr%
  13962.                PRINT stack#(i%),
  13963.            NEXT i%
  13964.            PRINT
  13965.        END SUB
  13966.    ──────────────────────────────────────────────────────────────────────────
  13967.  
  13968.  
  13969.  Function: NextParameter$
  13970.  
  13971.    Returns the first group of nonspace characters found at the left of the
  13972.    passed string. The passed string is then trimmed of these characters,
  13973.    along with any extra spaces.
  13974.  
  13975.    The PARSE.BAS module contains alternative routines that perform the same
  13976.    function in a slightly different way. Take a look at the ParseWord and
  13977.    ParseLine routines found there. The NextParameter$ subprogram
  13978.    demonstrates how the code from a module can be copied and modified for a
  13979.    specific purpose, with any extra code removed. This results in a smaller
  13980.    program but has the disadvantage that any future changes to the
  13981.    PARSE.BAS module will probably not show up here in the QCAL.BAS module.
  13982.  
  13983.    ──────────────────────────────────────────────────────────────────────────
  13984.      ' ************************************************
  13985.      ' **  Name:          NextParameter$             **
  13986.      ' **  Type:          Function                   **
  13987.      ' **  Module:        QCAL.BAS                   **
  13988.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  13989.      ' ************************************************
  13990.      '
  13991.      ' Extracts parameters from the front of the
  13992.      ' command line.  Parameters are groups of any
  13993.      ' characters separated by spaces.
  13994.      '
  13995.      ' EXAMPLE OF USE:  parm$ = NextParameter$(cmd$)
  13996.      ' PARAMETERS:      cmd$       The working copy of COMMAND$
  13997.      ' VARIABLES:       parm$      Each number or command from cmd$
  13998.      ' MODULE LEVEL
  13999.      '   DECLARATIONS:  DECLARE FUNCTION NextParameter$ (cmd$)
  14000.      '
  14001.        FUNCTION NextParameter$ (cmd$) STATIC
  14002.            parm$ = ""
  14003.            DO WHILE LEFT$(cmd$, 1) <> " " AND cmd$ <> ""
  14004.                parm$ = parm$ + LEFT$(cmd$, 1)
  14005.                cmd$ = MID$(cmd$, 2)
  14006.            LOOP
  14007.            DO WHILE LEFT$(cmd$, 1) = " " AND cmd$ <> ""
  14008.                cmd$ = MID$(cmd$, 2)
  14009.            LOOP
  14010.            NextParameter$ = parm$
  14011.        END FUNCTION
  14012.    ──────────────────────────────────────────────────────────────────────────
  14013.  
  14014.  
  14015.  Subprogram: Process
  14016.  
  14017.    Acts upon each command line parameter. If the parameter is a valid
  14018.    function, the function is called, and the stack is adjusted appropriately.
  14019.    If the parameter isn't a recognizable function, it is assumed to be a
  14020.    numeric quantity. The VAL function converts the parameter to its numeric
  14021.    equivalent, and the result is pushed on the stack, ready for the next
  14022.    operation.
  14023.  
  14024.    This subprogram demonstrates a fairly long CASE statement. The same logic
  14025.    could be developed using IF THEN, ELSE IF, ELSE, and END IF statements,
  14026.    but the CASE statement is ideal for making selections from a large number
  14027.    of choices in this way.
  14028.  
  14029.    ──────────────────────────────────────────────────────────────────────────
  14030.      ' ************************************************
  14031.      ' **  Name:          Process                    **
  14032.      ' **  Type:          Subprogram                 **
  14033.      ' **  Module:        QCAL.BAS                   **
  14034.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14035.      ' ************************************************
  14036.      '
  14037.      ' Processes each command parameter for the QCAL
  14038.      ' program.
  14039.      '
  14040.      ' EXAMPLE OF USE:  Process parm$, stack#(), ptr%
  14041.      ' PARAMETERS:      parm$      The command line parameter to be processed
  14042.      '                  stack#()   Array of numbers representing the stack
  14043.      '                  ptr%       Index pointing to last stack entry
  14044.      ' VARIABLES:       (none)
  14045.      ' MODULE LEVEL
  14046.      '   DECLARATIONS:  DECLARE SUB Process (parm$, stack#(), ptr%)
  14047.      '
  14048.        SUB Process (parm$, stack#(), ptr%) STATIC
  14049.            SELECT CASE parm$
  14050.            CASE "+"
  14051.                ptr% = ptr% - 1
  14052.                IF ptr% > 0 THEN
  14053.                    stack#(ptr%) = Add#(stack#(ptr%), stack#(ptr% + 1))
  14054.                END IF
  14055.            CASE "-"
  14056.                ptr% = ptr% - 1
  14057.                IF ptr% > 0 THEN
  14058.                    stack#(ptr%) = Subtract#(stack#(ptr%), stack#(ptr% + 1))
  14059.                END IF
  14060.            CASE "*"
  14061.                ptr% = ptr% - 1
  14062.                IF ptr% > 0 THEN
  14063.                    stack#(ptr%) = Multiply#(stack#(ptr%), stack#(ptr% + 1))
  14064.                END IF
  14065.            CASE "/"
  14066.                ptr% = ptr% - 1
  14067.                IF ptr% > 0 THEN
  14068.                    stack#(ptr%) = Divide#(stack#(ptr%), stack#(ptr% + 1))
  14069.                END IF
  14070.            CASE "CHS"
  14071.                IF ptr% > 0 THEN
  14072.                    stack#(ptr%) = ChangeSign#(stack#(ptr%))
  14073.                END IF
  14074.            CASE "ABS"
  14075.                IF ptr% > 0 THEN
  14076.                    stack#(ptr%) = AbsoluteX#(stack#(ptr%))
  14077.                END IF
  14078.            CASE "SGN"
  14079.                IF ptr% > 0 THEN
  14080.                    stack#(ptr%) = Sign#(stack#(ptr%))
  14081.                END IF
  14082.            CASE "INT"
  14083.                IF ptr% > 0 THEN
  14084.                    stack#(ptr%) = IntegerPart#(stack#(ptr%))
  14085.                END IF
  14086.            CASE "MOD"
  14087.                ptr% = ptr% - 1
  14088.                IF ptr% > 0 THEN
  14089.                    stack#(ptr%) = Modulus#(stack#(ptr%), stack#(ptr% + 1))
  14090.                END IF
  14091.            CASE "FRC"
  14092.                IF ptr% > 0 THEN
  14093.                    stack#(ptr%) = FractionalPart#(stack#(ptr%))
  14094.                END IF
  14095.            CASE "1/X"
  14096.                IF ptr% > 0 THEN
  14097.                    stack#(ptr%) = OneOverX#(stack#(ptr%))
  14098.                END IF
  14099.            CASE "SQR"
  14100.                IF ptr% > 0 THEN
  14101.                    stack#(ptr%) = SquareRoot#(stack#(ptr%))
  14102.                END IF
  14103.            CASE "X2"
  14104.                IF ptr% > 0 THEN
  14105.                    stack#(ptr%) = Xsquared#(stack#(ptr%))
  14106.                END IF
  14107.            CASE "SIN"
  14108.                IF ptr% > 0 THEN
  14109.                    stack#(ptr%) = Sine#(stack#(ptr%))
  14110.                END IF
  14111.            CASE "COS"
  14112.                IF ptr% > 0 THEN
  14113.                    stack#(ptr%) = Cosine#(stack#(ptr%))
  14114.                END IF
  14115.            CASE "TAN"
  14116.                IF ptr% > 0 THEN
  14117.                    stack#(ptr%) = Tangent#(stack#(ptr%))
  14118.                END IF
  14119.            CASE "ASN"
  14120.                IF ptr% > 0 THEN
  14121.                    stack#(ptr%) = ArcSine#(stack#(ptr%))
  14122.                END IF
  14123.            CASE "ACS"
  14124.                IF ptr% > 0 THEN
  14125.                    stack#(ptr%) = ArcCosine#(stack#(ptr%))
  14126.                END IF
  14127.            CASE "ATN"
  14128.                IF ptr% > 0 THEN
  14129.                    stack#(ptr%) = ArcTangent#(stack#(ptr%))
  14130.                END IF
  14131.            CASE "HSN"
  14132.                IF ptr% > 0 THEN
  14133.                    stack#(ptr%) = HypSine#(stack#(ptr%))
  14134.                END IF
  14135.            CASE "HCS"
  14136.                IF ptr% > 0 THEN
  14137.                    stack#(ptr%) = HypCosine#(stack#(ptr%))
  14138.                END IF
  14139.            CASE "HTN"
  14140.                IF ptr% > 0 THEN
  14141.                    stack#(ptr%) = HypTangent#(stack#(ptr%))
  14142.                END IF
  14143.            CASE "AHS"
  14144.                IF ptr% > 0 THEN
  14145.                    stack#(ptr%) = ArcHypSine#(stack#(ptr%))
  14146.                END IF
  14147.            CASE "AHC"
  14148.                IF ptr% > 0 THEN
  14149.                    stack#(ptr%) = ArcHypCosine#(stack#(ptr%))
  14150.                END IF
  14151.            CASE "AHT"
  14152.                IF ptr% > 0 THEN
  14153.                    stack#(ptr%) = ArcHypTangent#(stack#(ptr%))
  14154.                END IF
  14155.            CASE "LOG"
  14156.                IF ptr% > 0 THEN
  14157.                    stack#(ptr%) = LogE#(stack#(ptr%))
  14158.                END IF
  14159.            CASE "LOG10"
  14160.                IF ptr% > 0 THEN
  14161.                    stack#(ptr%) = LogBase10#(stack#(ptr%))
  14162.                END IF
  14163.            CASE "LOGN"
  14164.                ptr% = ptr% - 1
  14165.                IF ptr% > 0 THEN
  14166.                    stack#(ptr%) = LogBaseN#(stack#(ptr%), stack#(ptr% + 1))
  14167.                END IF
  14168.            CASE "EXP"
  14169.                IF ptr% > 0 THEN
  14170.                    stack#(ptr%) = Exponential#(stack#(ptr%))
  14171.                END IF
  14172.            CASE "CEIL"
  14173.                IF ptr% > 0 THEN
  14174.                    stack#(ptr%) = Ceil#(stack#(ptr%))
  14175.                END IF
  14176.            CASE "Y^X"
  14177.                ptr% = ptr% - 1
  14178.                IF ptr% > 0 THEN
  14179.                    stack#(ptr%) = YRaisedToX#(stack#(ptr%), stack#(ptr% + 1))
  14180.                END IF
  14181.            CASE "PI"
  14182.                ptr% = ptr% + 1
  14183.                stack#(ptr%) = PI
  14184.            CASE "SWAP"
  14185.                SwapXY stack#(), ptr%
  14186.            CASE "DUP"
  14187.                IF ptr% > 0 THEN
  14188.                    stack#(ptr% + 1) = stack#(ptr%)
  14189.                    ptr% = ptr% + 1
  14190.                END IF
  14191.            CASE ELSE
  14192.                ptr% = ptr% + 1
  14193.                stack#(ptr%) = VAL(parm$)
  14194.            END SELECT
  14195.        END SUB
  14196.    ──────────────────────────────────────────────────────────────────────────
  14197.  
  14198.  
  14199.  Subprogram: QcalHelp
  14200.  
  14201.    Provides a Help display for the QCAL program.
  14202.  
  14203.    One feature that sets good software apart from mediocre software is the
  14204.    ability to provide on-line help for the user. Nothing is more frustrating
  14205.    than a program that terminates suddenly, without any explanation of the
  14206.    problem or suggestion for solving it.
  14207.  
  14208.    The QcalHelp subprogram demonstrates one approach to helping the user with
  14209.    a program. Entering any of the following command lines will cause the QCAL
  14210.    program to call QcalHelp:
  14211.  
  14212.  
  14213.      QCAL HELP
  14214.      QCAL ?
  14215.      QCAL
  14216.  
  14217.    ──────────────────────────────────────────────────────────────────────────
  14218.      ' ************************************************
  14219.      ' **  Name:          QcalHelp                   **
  14220.      ' **  Type:          Subprogram                 **
  14221.      ' **  Module:        QCAL.BAS                   **
  14222.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14223.      ' ************************************************
  14224.      '
  14225.      ' Displays a help screen when QCAL is run with no
  14226.      ' parameters or with a parameter of ? or HELP.
  14227.      '
  14228.      ' EXAMPLE OF USE:  QcalHelp
  14229.      ' PARAMETERS:      (none)
  14230.      ' VARIABLES:       (none)
  14231.      ' MODULE LEVEL
  14232.      '   DECLARATIONS:  DECLARE SUB QcalHelp ()
  14233.      '
  14234.        SUB QcalHelp STATIC
  14235.            PRINT
  14236.            PRINT "Usage:  QCAL [number] [function] [...] <Enter>"
  14237.            PRINT
  14238.            PRINT "Numbers are placed on an RPN stack, and functions operate"
  14239.            PRINT "on the stacked quantities.  When the program is finished,"
  14240.            PRINT "whatever is left on the stack is displayed."
  14241.            PRINT
  14242.            PRINT "List of available functions..."
  14243.            PRINT
  14244.            PRINT "Two numbers:     +  -  *  /"
  14245.            PRINT "One number:      CHS ABS SGN INT MOD FRC CHS 1/X SQR X2 CEIL
  14246.            PRINT "Trigonometric:   SIN COS TAN ASN ACS ATN"
  14247.            PRINT "Hyperbolic:      HSN HCS HTN AHS AHC AHT"
  14248.            PRINT "Logarithmic:     LOG LOG10 LOGN EXP Y^X"
  14249.            PRINT "Constants:       PI"
  14250.            PRINT "Stack:           SWAP DUP"
  14251.        END SUB
  14252.    ──────────────────────────────────────────────────────────────────────────
  14253.  
  14254.  
  14255.  
  14256.  ────────────────────────────────────────────────────────────────────────────
  14257.  QCALMATH
  14258.  
  14259.    QCALMATH is a toolbox of scientific functions for the QCAL program.
  14260.    Several functions included in QCALMATH are similar to functions provided
  14261.    by QuickBASIC. You could shorten QCALMATH by deleting these functions here
  14262.    and coding the QuickBASIC routines directly in the Process subprogram,
  14263.    located in the QCAL.BAS module. However, there's something to be said for
  14264.    keeping the functions as shown here: QCALMATH checks for additional error
  14265.    conditions and generates clear messages if errors exist. For example,
  14266.    although the SquareRoot# function duplicates a QuickBASIC function,
  14267.    SquareRoot# checks for values less than 0 before trying to find the
  14268.    square root and prints a clear message if such an attempt is made.
  14269.  
  14270.    It's easy to add your own functions to the QCAL program. Simply create
  14271.    the function in the same format and style as shown in this module. You'll
  14272.    also need to modify the Process subprogram in the QCAL module to let the
  14273.    program call the new function. For the final touch, be sure to add the new
  14274.    function to the list displayed by the QcalHelp subprogram.
  14275.  
  14276.    QCALMATH is the only toolbox in this book that doesn't have any
  14277.    module-level code to demonstrate the subprograms and functions. The QCAL
  14278.    program loads this toolbox and provides the demonstration code.
  14279.  
  14280. ╓┌─┌─────────────────────────────┌──────┌────────────────────────────────────╖
  14281.    Name                          Type   Description
  14282.    Name                          Type   Description
  14283.    ──────────────────────────────────────────────────────────────────────────
  14284.    QCALMATH.BAS                        Toolbox
  14285.    AbsoluteX#                   Func   Absolute value of a number
  14286.    Add#                         Func   Sum of two numbers
  14287.    ArcCosine#                   Func   Arc cosine function of a number
  14288.    ArcHypCosine#                Func   Inverse hyperbolic cosine of a number
  14289.    ArcHypSine#                  Func   Inverse hyperbolic sine of a number
  14290.    ArcHypTangent#               Func   Inverse hyperbolic tangent of a
  14291.                                         number
  14292.    ArcSine#                     Func   Inverse sine of a number
  14293.    ArcTangent#                  Func   Inverse tangent of a number
  14294.    Ceil#                        Func   Smallest whole number greater than a
  14295.                                         number
  14296.    ChangeSign#                  Func   Reverses sign of a number
  14297.    Cosine#                      Func   Cosine of a number
  14298.    Divide#                      Func   Result of dividing two numbers
  14299.    Dup                          Sub    Duplicates top entry on the stack
  14300.    Exponential#                 Func   Exponential function of a number
  14301.    FractionalPart#              Func   Fractional part of a number
  14302.    HypCosine#                   Func   Hyperbolic cosine of a number
  14303.    Name                          Type   Description
  14304.    ──────────────────────────────────────────────────────────────────────────
  14305.   HypCosine#                   Func   Hyperbolic cosine of a number
  14306.    HypSine#                     Func   Hyperbolic sine of a number
  14307.    HypTangent#                  Func   Hyperbolic tangent of a number
  14308.    IntegerPart#                 Func   Integer part of a number
  14309.    LogBase10#                   Func   Log base 10 of a number
  14310.    LogBaseN#                    Func   Log base N of a number
  14311.    LogE#                        Func   Natural logarithm of a number
  14312.    Modulus#                     Func   Remainder of the division of two
  14313.                                         numbers
  14314.    Multiply#                    Func   Product of two numbers
  14315.    OneOverX#                    Func   Result of dividing 1 by a number
  14316.    Sign#                        Func   Sign of a number
  14317.    Sine#                        Func   Sine of a number
  14318.    SquareRoot#                  Func   Square root of a number
  14319.    Subtract#                    Func   Difference between two numbers
  14320.    SwapXY                       Sub    Swaps top two entries on the stack
  14321.    Tangent#                     Func   Tangent of a number
  14322.    Xsquared#                    Func   Square of a number
  14323.    YRaisedToX#                  Func   Number raised to the power of a
  14324.    Name                          Type   Description
  14325.    ──────────────────────────────────────────────────────────────────────────
  14326.   YRaisedToX#                  Func   Number raised to the power of a
  14327.                                         second
  14328.    ──────────────────────────────────────────────────────────────────────────
  14329.  
  14330.  
  14331.  
  14332.  Toolbox: QCALMATH
  14333.  
  14334.    ──────────────────────────────────────────────────────────────────────────
  14335.      ' ************************************************
  14336.      ' **  Name:          QCALMATH                   **
  14337.      ' **  Type:          Toolbox                    **
  14338.      ' **  Module:        QCALMATH.BAS               **
  14339.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14340.      ' ************************************************
  14341.      '
  14342.      ' Collection of math functions and subprograms for
  14343.      ' the QCAL program.
  14344.      '
  14345.      ' USAGE:         (loaded by the QCAL program)
  14346.      '.MAK FILE:      (none)
  14347.      ' PARAMETERS:    (none)
  14348.      ' VARIABLES:     (none)
  14349.      ' Constants
  14350.        CONST PI = 3.141592653589793#
  14351.        CONST L10 = 2.302585092994046#
  14352.  
  14353.      ' Functions
  14354.        DECLARE FUNCTION AbsoluteX# (x#)
  14355.        DECLARE FUNCTION Add# (y#, x#)
  14356.        DECLARE FUNCTION ArcCosine# (x#)
  14357.        DECLARE FUNCTION ArcHypCosine# (x#)
  14358.        DECLARE FUNCTION ArcHypSine# (x#)
  14359.        DECLARE FUNCTION ArcHypTangent# (x#)
  14360.        DECLARE FUNCTION ArcSine# (x#)
  14361.        DECLARE FUNCTION ArcTangent# (x#)
  14362.        DECLARE FUNCTION Ceil# (x#)
  14363.        DECLARE FUNCTION ChangeSign# (x#)
  14364.        DECLARE FUNCTION Cosine# (x#)
  14365.        DECLARE FUNCTION Divide# (y#, x#)
  14366.        DECLARE FUNCTION Exponential# (x#)
  14367.        DECLARE FUNCTION FractionalPart# (x#)
  14368.        DECLARE FUNCTION HypCosine# (x#)
  14369.        DECLARE FUNCTION HypSine# (x#)
  14370.        DECLARE FUNCTION HypTangent# (x#)
  14371.        DECLARE FUNCTION IntegerPart# (x#)
  14372.        DECLARE FUNCTION LogBase10# (x#)
  14373.        DECLARE FUNCTION LogBaseN# (y#, x#)
  14374.        DECLARE FUNCTION LogE# (x#)
  14375.        DECLARE FUNCTION Modulus# (y#, x#)
  14376.        DECLARE FUNCTION Multiply# (y#, x#)
  14377.        DECLARE FUNCTION OneOverX# (x#)
  14378.        DECLARE FUNCTION Sign# (x#)
  14379.        DECLARE FUNCTION Sine# (x#)
  14380.        DECLARE FUNCTION SquareRoot# (x#)
  14381.        DECLARE FUNCTION Subtract# (y#, x#)
  14382.        DECLARE FUNCTION Tangent# (x#)
  14383.        DECLARE FUNCTION Xsquared# (x#)
  14384.        DECLARE FUNCTION YRaisedToX# (y#, x#)
  14385.    ──────────────────────────────────────────────────────────────────────────
  14386.  
  14387.  
  14388.  Function: AbsoluteX#
  14389.  
  14390.    Returns the absolute value of the passed value. The absolute value of a
  14391.    number is that number's positive value.
  14392.  
  14393.    ──────────────────────────────────────────────────────────────────────────
  14394.      ' ************************************************
  14395.      ' **  Name:          AbsoluteX#                 **
  14396.      ' **  Type:          Function                   **
  14397.      ' **  Module:        QCALMATH.BAS               **
  14398.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14399.      ' ************************************************
  14400.      '
  14401.      ' EXAMPLE OF USE:  y# = AbsoluteX#(x#)
  14402.      ' PARAMETERS:      x#         Double-precision value to be evaluated
  14403.      ' VARIABLES:       (none)
  14404.      ' MODULE LEVEL
  14405.      '   DECLARATIONS:  DECLARE FUNCTION AbsoluteX# (x#)
  14406.      '
  14407.        FUNCTION AbsoluteX# (x#) STATIC
  14408.            AbsoluteX# = ABS(x#)
  14409.        END FUNCTION
  14410.    ──────────────────────────────────────────────────────────────────────────
  14411.  
  14412.  
  14413.  Function: Add#
  14414.  
  14415.    Returns the sum of two double-precision numbers.
  14416.  
  14417.    ──────────────────────────────────────────────────────────────────────────
  14418.      ' ************************************************
  14419.      ' **  Name:          Add#                       **
  14420.      ' **  Type:          Function                   **
  14421.      ' **  Module:        QCALMATH.BAS               **
  14422.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14423.      ' ************************************************
  14424.      '
  14425.      ' EXAMPLE OF USE:  z# = Add#(y#, x#)
  14426.      ' PARAMETERS:      y#         First number
  14427.      '                  x#         Second number
  14428.      ' VARIABLES:       (none)
  14429.      ' MODULE LEVEL
  14430.      '   DECLARATIONS:  DECLARE FUNCTION Add# (y#, x#)
  14431.      '
  14432.        FUNCTION Add# (y#, x#) STATIC
  14433.            Add# = y# + x#
  14434.        END FUNCTION
  14435.    ──────────────────────────────────────────────────────────────────────────
  14436.  
  14437.  
  14438.  Function: ArcCosine#
  14439.  
  14440.    Returns the arc cosine of a number; the returned angle is expressed in
  14441.    radians. If the number passed is less than 1, an error message is
  14442.    displayed, and the program terminates.
  14443.  
  14444.    ──────────────────────────────────────────────────────────────────────────
  14445.      ' ************************************************
  14446.      ' **  Name:          ArcCosine#                 **
  14447.      ' **  Type:          Function                   **
  14448.      ' **  Module:        QCALMATH.BAS               **
  14449.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14450.      ' ************************************************
  14451.      '
  14452.      ' EXAMPLE OF USE:  y# = ArcCosine#(x#)
  14453.      ' PARAMETERS:      x#         Number to be evaluated
  14454.      ' VARIABLES:       (none)
  14455.      ' MODULE LEVEL
  14456.      '   DECLARATIONS:  DECLARE FUNCTION ArcCosine# (x#)
  14457.      '
  14458.        FUNCTION ArcCosine# (x#) STATIC
  14459.            x2# = x# * x#
  14460.            IF x2# < 1# THEN
  14461.                ArcCosine# = PI / 2# - ATN(x# / SQR(1# - x# * x#))
  14462.            ELSE
  14463.                PRINT "Error: ACS(x#) where x# < 1"
  14464.                SYSTEM
  14465.            END IF
  14466.        END FUNCTION
  14467.    ──────────────────────────────────────────────────────────────────────────
  14468.  
  14469.  
  14470.  Function: ArcHypCosine#
  14471.  
  14472.    Returns the inverse hyperbolic cosine of a number. If the number passed is
  14473.    less than or equal to 1, an error message is displayed, and the program
  14474.    terminates.
  14475.  
  14476.    ──────────────────────────────────────────────────────────────────────────
  14477.      ' ************************************************
  14478.      ' **  Name:          ArcHypCosine#              **
  14479.      ' **  Type:          Function                   **
  14480.      ' **  Module:        QCALMATH.BAS               **
  14481.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14482.      ' ************************************************
  14483.      '
  14484.      ' EXAMPLE OF USE:  y# = ArcHypCosine#(x#)
  14485.      ' PARAMETERS:      x#         Number to be evaluated
  14486.      ' VARIABLES:       (none)
  14487.      ' MODULE LEVEL
  14488.      '   DECLARATIONS:  DECLARE FUNCTION ArcHypCosine# (x#)
  14489.      '
  14490.        FUNCTION ArcHypCosine# (x#) STATIC
  14491.            IF ABS(x#) > 1# THEN
  14492.                ArcHypCosine# = LOG(x# + SQR(x# * x# - 1#))
  14493.            ELSE
  14494.                PRINT "Error: AHS(x#) where -1 <= x# <= +1"
  14495.                SYSTEM
  14496.            END IF
  14497.        END FUNCTION
  14498.    ──────────────────────────────────────────────────────────────────────────
  14499.  
  14500.  
  14501.  Function: ArcHypSine#
  14502.  
  14503.    Returns the inverse hyperbolic sine of a number.
  14504.  
  14505.    ──────────────────────────────────────────────────────────────────────────
  14506.      ' ************************************************
  14507.      ' **  Name:          ArcHypSine#                **
  14508.      ' **  Type:          Function                   **
  14509.      ' **  Module:        QCALMATH.BAS               **
  14510.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14511.      ' ************************************************
  14512.      '
  14513.      ' EXAMPLE OF USE:  y# = ArcHypSine#(x#)
  14514.      ' PARAMETERS:      x#    Number to be evaluated
  14515.      ' VARIABLES:       (none)
  14516.      ' MODULE LEVEL
  14517.      '   DECLARATIONS:  DECLARE FUNCTION AryHypSine# (x#)
  14518.      '
  14519.        FUNCTION ArcHypSine# (x#) STATIC
  14520.            ArcHypSine# = LOG(x# + SQR(1# + x# * x#))
  14521.        END FUNCTION
  14522.    ──────────────────────────────────────────────────────────────────────────
  14523.  
  14524.  
  14525.  Function: ArcHypTangent#
  14526.  
  14527.    Returns the inverse hyperbolic tangent of a number. If the number passed
  14528.    is less than -1 or greater than 1, an error message is displayed, and the
  14529.    program terminates.
  14530.  
  14531.    ──────────────────────────────────────────────────────────────────────────
  14532.      ' ************************************************
  14533.      ' **  Name:          ArcHypTangent#             **
  14534.      ' **  Type:          Function                   **
  14535.      ' **  Module:        QCALMATH.BAS               **
  14536.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14537.      ' ************************************************
  14538.      '
  14539.      ' EXAMPLE OF USE:  y# = ArcHypTangent#(x#)
  14540.      ' PARAMETERS:      x#         Number to be evaluated
  14541.      ' VARIABLES:       (none)
  14542.      ' MODULE LEVEL
  14543.      '   DECLARATIONS:  DECLARE FUNCTION ArcHypTangent# (x#)
  14544.      '
  14545.        FUNCTION ArcHypTangent# (x#) STATIC
  14546.            IF ABS(x#) < 1 THEN
  14547.                ArcHypTangent# = LOG((1# + x#) / (1# - x#)) / 2#
  14548.            ELSE
  14549.                PRINT "Error: AHT(x#) where x# <= -1 or x# >= +1"
  14550.                SYSTEM
  14551.            END IF
  14552.        END FUNCTION
  14553.    ──────────────────────────────────────────────────────────────────────────
  14554.  
  14555.  
  14556.  Function: ArcSine#
  14557.  
  14558.    Returns the inverse sine of a number. If the number passed is greater than
  14559.    or equal to 1, the function displays an error message, and the program
  14560.    terminates.
  14561.  
  14562.    ──────────────────────────────────────────────────────────────────────────
  14563.      ' ************************************************
  14564.      ' **  Name:          ArcSine#                   **
  14565.      ' **  Type:          Function                   **
  14566.      ' **  Module:        QCALMATH.BAS               **
  14567.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14568.      ' ************************************************
  14569.      '
  14570.      ' EXAMPLE OF USE:  y# = ArcSine#(x#)
  14571.      ' PARAMETERS:      x#         Number to be evaluated
  14572.      ' VARIABLES:       (none)
  14573.      ' MODULE LEVEL
  14574.      '   DECLARATIONS:  DECLARE FUNCTION ArcSine# (x#)
  14575.      '
  14576.        FUNCTION ArcSine# (x#) STATIC
  14577.            x2# = x# * x#
  14578.            IF x2# < 1# THEN
  14579.                ArcSine# = ATN(x# / SQR(1# - x# * x#))
  14580.            ELSE
  14581.                PRINT "Error: ASN(x#) where x# >= 1"
  14582.                SYSTEM
  14583.            END IF
  14584.        END FUNCTION
  14585.    ──────────────────────────────────────────────────────────────────────────
  14586.  
  14587.  
  14588.  Function: ArcTangent#
  14589.  
  14590.    Returns the inverse tangent of a number.
  14591.  
  14592.    ──────────────────────────────────────────────────────────────────────────
  14593.      ' ************************************************
  14594.      ' **  Name:          ArcTangent#                **
  14595.      ' **  Type:          Function                   **
  14596.      ' **  Module:        QCALMATH.BAS               **
  14597.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14598.      ' ************************************************
  14599.      '
  14600.      ' EXAMPLE OF USE:  y# = ArcTangent#(x#)
  14601.      ' PARAMETERS:      x#         Number to be evaluated
  14602.      ' VARIABLES:       (none)
  14603.      ' MODULE LEVEL
  14604.      '   DECLARATIONS:  DECLARE FUNCTION ArcTangent# (x#)
  14605.      '
  14606.        FUNCTION ArcTangent# (x#) STATIC
  14607.            ArcTangent# = ATN(x#)
  14608.        END FUNCTION
  14609.    ──────────────────────────────────────────────────────────────────────────
  14610.  
  14611.  
  14612.  Function: Ceil#
  14613.  
  14614.    Returns the smallest whole number that is greater than a number. For
  14615.    example, Ceil#(3.14) returns 4, Ceil#(-3.14) returns -3, and Ceil#(17)
  14616.    returns 17.
  14617.  
  14618.    ──────────────────────────────────────────────────────────────────────────
  14619.      ' ************************************************
  14620.      ' **  Name:          Ceil#                      **
  14621.      ' **  Type:          Function                   **
  14622.      ' **  Module:        QCALMATH.BAS               **
  14623.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14624.      ' ************************************************
  14625.      '
  14626.      ' EXAMPLE OF USE:  y# = Ceil#(x#)
  14627.      ' PARAMETERS:      x#         Number to be evaluated
  14628.      ' VARIABLES:       (none)
  14629.      ' MODULE LEVEL
  14630.      '   DECLARATIONS:  DECLARE FUNCTION Ceil# (x#)
  14631.      '
  14632.        FUNCTION Ceil# (x#) STATIC
  14633.            Ceil# = -INT(-x#)
  14634.        END FUNCTION
  14635.    ──────────────────────────────────────────────────────────────────────────
  14636.  
  14637.  
  14638.  Function: ChangeSign#
  14639.  
  14640.    Returns a number with its sign changed. This function could easily be
  14641.    deleted from QCAL by changing the Process subprogram so that it directly
  14642.    performs negation in the CASE statement in which the CHS command is acted
  14643.    upon. I decided to provide a consistent interface between the functions in
  14644.    the QCALMATH module and the Process subprogram, however, making it
  14645.    easier to add, delete, or modify functions as desired.
  14646.  
  14647.    ──────────────────────────────────────────────────────────────────────────
  14648.      ' ************************************************
  14649.      ' **  Name:          ChangeSign#                **
  14650.      ' **  Type:          Function                   **
  14651.      ' **  Module:        QCALMATH.BAS               **
  14652.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14653.      ' ************************************************
  14654.      '
  14655.      ' EXAMPLE OF USE:  y# = ChangeSign#(x#)
  14656.      ' PARAMETERS:      x#         Number to be evaluated
  14657.      ' VARIABLES:       (none)
  14658.      ' MODULE LEVEL
  14659.      '   DECLARATIONS:  DECLARE FUNCTION ChangeSign# (x#)
  14660.      '
  14661.        FUNCTION ChangeSign# (x#) STATIC
  14662.            ChangeSign# = -x#
  14663.        END FUNCTION
  14664.    ──────────────────────────────────────────────────────────────────────────
  14665.  
  14666.  
  14667.  Function: Cosine#
  14668.  
  14669.    Returns the cosine of an angle.
  14670.  
  14671.    ──────────────────────────────────────────────────────────────────────────
  14672.      ' ************************************************
  14673.      ' **  Name:          Cosine#                    **
  14674.      ' **  Type:          Function                   **
  14675.      ' **  Module:        QCALMATH.BAS               **
  14676.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14677.      ' ************************************************
  14678.      '
  14679.      ' EXAMPLE OF USE:  y# = Cosine#(x#)
  14680.      ' PARAMETERS:      x#         Angle to be evaluated
  14681.      ' VARIABLES:       (none)
  14682.      ' MODULE LEVEL
  14683.      '   DECLARATIONS:  DECLARE FUNCTION Cosine# (x#)
  14684.      '
  14685.        FUNCTION Cosine# (x#) STATIC
  14686.            Cosine# = COS(x#)
  14687.        END FUNCTION
  14688.    ──────────────────────────────────────────────────────────────────────────
  14689.  
  14690.  
  14691.  Function: Divide#
  14692.  
  14693.    Returns the result of dividing two numbers. If a division by 0 is
  14694.    attempted, the function displays an error message, and the program
  14695.    terminates.
  14696.  
  14697.    ──────────────────────────────────────────────────────────────────────────
  14698.      ' ************************************************
  14699.      ' **  Name:          Divide#                    **
  14700.      ' **  Type:          Function                   **
  14701.      ' **  Module:        QCALMATH.BAS               **
  14702.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14703.      ' ************************************************
  14704.      '
  14705.      ' EXAMPLE OF USE:  y# = Divide#(y#, x#)
  14706.      ' PARAMETERS:      y#         Number to be processed
  14707.      '                  x#         Number to be processed
  14708.      ' VARIABLES:       (none)
  14709.      ' MODULE LEVEL
  14710.      '   DECLARATIONS:  DECLARE FUNCTION Divide# (y#, x#)
  14711.      '
  14712.        FUNCTION Divide# (y#, x#) STATIC
  14713.            IF x# <> 0 THEN
  14714.                Divide# = y# / x#
  14715.            ELSE
  14716.                PRINT "Error: Division by zero"
  14717.                SYSTEM
  14718.            END IF
  14719.        END FUNCTION
  14720.    ──────────────────────────────────────────────────────────────────────────
  14721.  
  14722.  
  14723.  Subprogram: Dup
  14724.  
  14725.    Duplicates the top entry on the stack for the QCAL program.
  14726.  
  14727.    ──────────────────────────────────────────────────────────────────────────
  14728.      ' ************************************************
  14729.      ' **  Name:          Dup                        **
  14730.      ' **  Type:          Subprogram                 **
  14731.      ' **  Module:        QCALMATH.BAS               **
  14732.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14733.      ' ************************************************
  14734.      '
  14735.      ' EXAMPLE OF USE:  Dup stack#(), ptr%
  14736.      ' PARAMETERS:      stack#()   Numeric stack
  14737.      '                  ptr%       Index to last entry on stack
  14738.      ' VARIABLES:       (none)
  14739.      ' MODULE LEVEL
  14740.      '   DECLARATIONS:  DECLARE SUB Dup (Stack#(), ptr%)
  14741.      '
  14742.        SUB Dup (stack#(), ptr%) STATIC
  14743.            IF ptr% THEN
  14744.                ptr% = ptr% + 1
  14745.                stack#(ptr%) = stack#(ptr% - 1)
  14746.            END IF
  14747.        END SUB
  14748.    ──────────────────────────────────────────────────────────────────────────
  14749.  
  14750.  
  14751.  Function: Exponential#
  14752.  
  14753.    Returns the exponential function of a number.
  14754.  
  14755.    ──────────────────────────────────────────────────────────────────────────
  14756.      ' ************************************************
  14757.      ' **  Name:          Exponential#               **
  14758.      ' **  Type:          Function                   **
  14759.      ' **  Module:        QCALMATH.BAS               **
  14760.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14761.      ' ************************************************
  14762.      '
  14763.      ' EXAMPLE OF USE:  y# = Exponential#(x#)
  14764.      ' PARAMETERS:      x#         Number to be processed
  14765.      ' VARIABLES:       (none)
  14766.      ' MODULE LEVEL
  14767.      '   DECLARATIONS:  DECLARE FUNCTION Exponential# (x#)
  14768.      '
  14769.        FUNCTION Exponential# (x#) STATIC
  14770.            Exponential# = EXP(x#)
  14771.        END FUNCTION
  14772.    ──────────────────────────────────────────────────────────────────────────
  14773.  
  14774.  
  14775.  Function: FractionalPart#
  14776.  
  14777.    Returns the fractional part of a number. For example, the fractional part
  14778.    of 3.14 is .14, of -3.14 is -.14, and of 17 is 0.
  14779.  
  14780.    ──────────────────────────────────────────────────────────────────────────
  14781.      ' ************************************************
  14782.      ' **  Name:          FractionalPart#            **
  14783.      ' **  Type:          Function                   **
  14784.      ' **  Module:        QCALMATH.BAS               **
  14785.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14786.      ' ************************************************
  14787.      '
  14788.      ' EXAMPLE OF USE:  y# = FractionalPart#(x#)
  14789.      ' PARAMETERS:      x#         Number to be processed
  14790.      ' VARIABLES:       (none)
  14791.      ' MODULE LEVEL
  14792.      '   DECLARATIONS:  DECLARE FUNCTION FractionalPart# (x#)
  14793.      '
  14794.        FUNCTION FractionalPart# (x#) STATIC
  14795.            IF x# >= 0 THEN
  14796.                FractionalPart# = x# - INT(x#)
  14797.            ELSE
  14798.                FractionalPart# = x# - INT(x#) - 1#
  14799.            END IF
  14800.        END FUNCTION
  14801.    ──────────────────────────────────────────────────────────────────────────
  14802.  
  14803.  
  14804.  Function: HypCosine#
  14805.  
  14806.    Returns the hyperbolic cosine of a number.
  14807.  
  14808.    ──────────────────────────────────────────────────────────────────────────
  14809.      ' ************************************************
  14810.      ' **  Name:          HypCosine#                 **
  14811.      ' **  Type:          Function                   **
  14812.      ' **  Module:        QCALMATH.BAS               **
  14813.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14814.      ' ************************************************
  14815.      '
  14816.      ' EXAMPLE OF USE:  y# = HypCosine#(x#)
  14817.      ' PARAMETERS:      x#         Number to be processed
  14818.      ' VARIABLES:       (none)
  14819.      ' MODULE LEVEL
  14820.      '   DECLARATIONS:  DECLARE FUNCTION HypCosine# (x#)
  14821.      '
  14822.        FUNCTION HypCosine# (x#) STATIC
  14823.            HypCosine# = (EXP(x#) + EXP(-x#)) / 2#
  14824.        END FUNCTION
  14825.    ──────────────────────────────────────────────────────────────────────────
  14826.  
  14827.  
  14828.  Function: HypSine#
  14829.  
  14830.    Returns the hyperbolic sine of a number.
  14831.  
  14832.    ──────────────────────────────────────────────────────────────────────────
  14833.      ' ************************************************
  14834.      ' **  Name:          HypSine#                   **
  14835.      ' **  Type:          Function                   **
  14836.      ' **  Module:        QCALMATH.BAS               **
  14837.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14838.      ' ************************************************
  14839.      '
  14840.      ' EXAMPLE OF USE:  y# = HypSine#(x#)
  14841.      ' PARAMETERS:      x#         Number to be processed
  14842.      ' VARIABLES:       (none)
  14843.      ' MODULE LEVEL
  14844.      '   DECLARATIONS:  DECLARE FUNCTION HypSine# (x#)
  14845.      '
  14846.        FUNCTION HypSine# (x#) STATIC
  14847.            HypSine# = (EXP(x#) - EXP(-x#)) / 2#
  14848.        END FUNCTION
  14849.    ──────────────────────────────────────────────────────────────────────────
  14850.  
  14851.  
  14852.  Function: HypTangent#
  14853.  
  14854.    Returns the hyperbolic tangent of a number.
  14855.  
  14856.    ──────────────────────────────────────────────────────────────────────────
  14857.      ' ************************************************
  14858.      ' **  Name:          HypTangent#                **
  14859.      ' **  Type:          Function                   **
  14860.      ' **  Module:        QCALMATH.BAS               **
  14861.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14862.      ' ************************************************
  14863.      '
  14864.      ' EXAMPLE OF USE:  y# = HypTangent#(x#)
  14865.      ' PARAMETERS:      x#         Number to be processed
  14866.      ' VARIABLES:       (none)
  14867.      ' MODULE LEVEL
  14868.      '   DECLARATIONS:  DECLARE FUNCTION HypTangent# (x#)
  14869.      '
  14870.        FUNCTION HypTangent# (x#) STATIC
  14871.            HypTangent# = (EXP(x#) - EXP(-x#)) / (EXP(x#) + EXP(-x#))
  14872.        END FUNCTION
  14873.    ──────────────────────────────────────────────────────────────────────────
  14874.  
  14875.  
  14876.  Function: IntegerPart#
  14877.  
  14878.    Returns the integer part of a number. For example, the integer part of
  14879.    3.14 is 3, of -3.14 is -4, and of 17 is 17.
  14880.  
  14881.    ──────────────────────────────────────────────────────────────────────────
  14882.      ' ************************************************
  14883.      ' **  Name:          IntegerPart#               **
  14884.      ' **  Type:          Function                   **
  14885.      ' **  Module:        QCALMATH.BAS               **
  14886.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14887.      ' ************************************************
  14888.      '
  14889.      ' EXAMPLE OF USE:  y# = IntegerPart#(x#)
  14890.      ' PARAMETERS:      x#         Number to be processed
  14891.      ' VARIABLES:       (none)
  14892.      ' MODULE LEVEL
  14893.      '   DECLARATIONS:  DECLARE FUNCTION IntegerPart# (x#)
  14894.      '
  14895.        FUNCTION IntegerPart# (x#) STATIC
  14896.            IntegerPart# = INT(x#)
  14897.        END FUNCTION
  14898.    ──────────────────────────────────────────────────────────────────────────
  14899.  
  14900.  
  14901.  Function: LogBase10#
  14902.  
  14903.    Returns the logarithm, base 10, of a number. If the number is not greater
  14904.    than 0, the function displays an error message, and the program
  14905.    terminates.
  14906.  
  14907.    Look in the listing at the constant L10, defined in the module-level code
  14908.    of QCALMATH. This constant is the double-precision natural logarithm of
  14909.    10. The constant can be replaced with LOG(10), its mathematic equivalent,
  14910.    but using a constant makes the program faster and the compiled program
  14911.    shorter.
  14912.  
  14913.    ──────────────────────────────────────────────────────────────────────────
  14914.      ' ************************************************
  14915.      ' **  Name:          LogBase10#                 **
  14916.      ' **  Type:          Function                   **
  14917.      ' **  Module:        QCALMATH.BAS               **
  14918.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14919.      ' ************************************************
  14920.      '
  14921.      ' EXAMPLE OF USE:  y# = Log10#(x#)
  14922.      ' PARAMETERS:      x#         Number to be processed
  14923.      ' VARIABLES:       (none)
  14924.      ' MODULE LEVEL
  14925.      '   DECLARATIONS:  DECLARE FUNCTION LogBase10# (x#)
  14926.      '
  14927.        FUNCTION LogBase10# (x#) STATIC
  14928.            IF x# > 0 THEN
  14929.                LogBase10# = LOG(x#) / L10
  14930.            ELSE
  14931.                PRINT "Error: LOG10(x#) where x# <= 0"
  14932.                SYSTEM
  14933.            END IF
  14934.        END FUNCTION
  14935.    ──────────────────────────────────────────────────────────────────────────
  14936.  
  14937.  
  14938.  Function: LogBaseN#
  14939.  
  14940.    Returns the logarithm, base N, of a number. This function checks for
  14941.    several possible error conditions. The number to be processed must be
  14942.    greater than 0, and the base for finding the logarithm must be greater
  14943.    than 0 and must not be exactly 1. If one of these checks fails, a message
  14944.    is displayed, and the program terminates.
  14945.  
  14946.    ──────────────────────────────────────────────────────────────────────────
  14947.      ' ************************************************
  14948.      ' **  Name:          LogBaseN#                  **
  14949.      ' **  Type:          Function                   **
  14950.      ' **  Module:        QCALMATH.BAS               **
  14951.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14952.      ' ************************************************
  14953.      '
  14954.      ' EXAMPLE OF USE:  y# = LogBaseN#(y#, x#)
  14955.      ' PARAMETERS:      y#         Number to be processed
  14956.      '                  x#         The base for finding the logarithm
  14957.      ' VARIABLES:       (none)
  14958.      ' MODULE LEVEL
  14959.      '   DECLARATIONS:  DECLARE FUNCTION LogBaseN# (y#, x#)
  14960.      '
  14961.        FUNCTION LogBaseN# (y#, x#) STATIC
  14962.            IF x# <= 0 THEN
  14963.                PRINT "Error: LOGN(y#, x#) where x# <= 0"
  14964.                SYSTEM
  14965.            ELSEIF x# = 1# THEN
  14966.                PRINT "Error: LOGN(y#, x#) where x# = 1"
  14967.                SYSTEM
  14968.            ELSEIF y# <= 0 THEN
  14969.                PRINT "Error: LOGN(y#, x#) where y# is <= 0"
  14970.                SYSTEM
  14971.            ELSE
  14972.                LogBaseN# = LOG(y#) / LOG(x#)
  14973.            END IF
  14974.        END FUNCTION
  14975.    ──────────────────────────────────────────────────────────────────────────
  14976.  
  14977.  
  14978.  Function: LogE#
  14979.  
  14980.    Returns the natural logarithm of a number. The QuickBASIC function LOG()
  14981.    is used to calculate the logarithm, but this function first checks that
  14982.    the number is greater than 0. If the number is equal to or less than 0, an
  14983.    error message is displayed, and the program terminates.
  14984.  
  14985.    ──────────────────────────────────────────────────────────────────────────
  14986.      ' ************************************************
  14987.      ' **  Name:          LogE#                      **
  14988.      ' **  Type:          Function                   **
  14989.      ' **  Module:        QCALMATH.BAS               **
  14990.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  14991.      ' ************************************************
  14992.      '
  14993.      ' EXAMPLE OF USE:  y# = LogE#(x#)
  14994.      ' PARAMETERS:      x#         Number to be processed
  14995.      ' VARIABLES:       (none)
  14996.      ' MODULE LEVEL
  14997.      '   DECLARATIONS:  DECLARE FUNCTION LogE# (x#)
  14998.      '
  14999.        FUNCTION LogE# (x#) STATIC
  15000.            IF x# > 0 THEN
  15001.                LogE# = LOG(x#)
  15002.            ELSE
  15003.                PRINT "Error: LOGE(x#) where x# <= 0"
  15004.                SYSTEM
  15005.            END IF
  15006.        END FUNCTION
  15007.    ──────────────────────────────────────────────────────────────────────────
  15008.  
  15009.  
  15010.  Function: Modulus#
  15011.  
  15012.    Returns the remainder of the division of two numbers. If a division by 0
  15013.    is attempted, the function displays an error message, and the program
  15014.    terminates. The function is valid for non-integer quantities.
  15015.  
  15016.    ──────────────────────────────────────────────────────────────────────────
  15017.      ' ************************************************
  15018.      ' **  Name:          Modulus#                   **
  15019.      ' **  Type:          Function                   **
  15020.      ' **  Module:        QCALMATH.BAS               **
  15021.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15022.      ' ************************************************
  15023.      '
  15024.      ' EXAMPLE OF USE:  y# = Modulus#(y#, x#)
  15025.      ' PARAMETERS:      y#         Number to be divided
  15026.      '                  x#         Number for dividing by
  15027.      ' VARIABLES:       (none)
  15028.      ' MODULE LEVEL
  15029.      '   DECLARATIONS:  DECLARE FUNCTION Modulus# (y#, x#)
  15030.      '
  15031.        FUNCTION Modulus# (y#, x#) STATIC
  15032.            IF x# <> 0 THEN
  15033.                Modulus# = y# - INT(y# / x#) * x#
  15034.            ELSE
  15035.                PRINT "Error: MOD(y#, x#) where x# = 0"
  15036.                SYSTEM
  15037.            END IF
  15038.        END FUNCTION
  15039.    ──────────────────────────────────────────────────────────────────────────
  15040.  
  15041.  
  15042.  Function: Multiply#
  15043.  
  15044.    Returns the product of two numbers.
  15045.  
  15046.    ──────────────────────────────────────────────────────────────────────────
  15047.      ' ************************************************
  15048.      ' **  Name:          Multiply#                  **
  15049.      ' **  Type:          Function                   **
  15050.      ' **  Module:        QCALMATH.BAS               **
  15051.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15052.      ' ************************************************
  15053.      '
  15054.      ' EXAMPLE OF USE:  y# = Multiply#(y#, x#)
  15055.      ' PARAMETERS:      y#         First number to be processed
  15056.      '                  x#         Second number to be processed
  15057.      ' VARIABLES:       (none)
  15058.      ' MODULE LEVEL
  15059.      '   DECLARATIONS:  DECLARE FUNCTION Multiply# (y#, x#)
  15060.      '
  15061.        FUNCTION Multiply# (y#, x#) STATIC
  15062.            Multiply# = y# * x#
  15063.        END FUNCTION
  15064.    ──────────────────────────────────────────────────────────────────────────
  15065.  
  15066.  
  15067.  Function: OneOverX#
  15068.  
  15069.    Returns the result of dividing 1 by a number. If a division by 0 is
  15070.    attempted, the function displays an error message, and the program
  15071.    terminates.
  15072.  
  15073.    ──────────────────────────────────────────────────────────────────────────
  15074.      ' ************************************************
  15075.      ' **  Name:          OneOverX#                  **
  15076.      ' **  Type:          Function                   **
  15077.      ' **  Module:        QCALMATH.BAS               **
  15078.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15079.      ' ************************************************
  15080.      '
  15081.      ' EXAMPLE OF USE:  y# = OneOverX#(x#)
  15082.      ' PARAMETERS:      x#         Number to be processed
  15083.      ' VARIABLES:       (none)
  15084.      ' MODULE LEVEL
  15085.      '   DECLARATIONS:  DECLARE FUNCTION OneOverX# (x#)
  15086.      '
  15087.        FUNCTION OneOverX# (x#) STATIC
  15088.            IF x# <> 0 THEN
  15089.                OneOverX# = 1# / x#
  15090.            ELSE
  15091.                PRINT "Error: 1/x where x = 0"
  15092.                SYSTEM
  15093.            END IF
  15094.        END FUNCTION
  15095.    ──────────────────────────────────────────────────────────────────────────
  15096.  
  15097.  
  15098.  Function: Sign#
  15099.  
  15100.    Returns -1 for all negative numbers, 1 for positive numbers, and 0 for
  15101.    zero.
  15102.  
  15103.    ──────────────────────────────────────────────────────────────────────────
  15104.      ' ************************************************
  15105.      ' **  Name:          Sign#                      **
  15106.      ' **  Type:          Function                   **
  15107.      ' **  Module:        QCALMATH.BAS               **
  15108.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15109.      ' ************************************************
  15110.      '
  15111.      ' EXAMPLE OF USE:  y# = Sign#(x#)
  15112.      ' PARAMETERS:      x#         Number to be processed
  15113.      ' VARIABLES:       (none)
  15114.      ' MODULE LEVEL
  15115.      '   DECLARATIONS:  DECLARE FUNCTION Sign# (x#)
  15116.      '
  15117.        FUNCTION Sign# (x#) STATIC
  15118.            Sign# = SGN(x#)
  15119.        END FUNCTION
  15120.    ──────────────────────────────────────────────────────────────────────────
  15121.  
  15122.  
  15123.  Function: Sine#
  15124.  
  15125.    Returns the sine of an angle; assumes the angle is expressed in radians.
  15126.  
  15127.    ──────────────────────────────────────────────────────────────────────────
  15128.      ' ************************************************
  15129.      ' **  Name:          Sine#                      **
  15130.      ' **  Type:          Function                   **
  15131.      ' **  Module:        QCALMATH.BAS               **
  15132.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15133.      ' ************************************************
  15134.      '
  15135.      ' EXAMPLE OF USE:  y# = Sine#(x#)
  15136.      ' PARAMETERS:      x#         Angle, expressed in radians
  15137.      ' VARIABLES:       (none)
  15138.      ' MODULE LEVEL
  15139.      '   DECLARATIONS:  DECLARE FUNCTION Sine# (x#)
  15140.      '
  15141.        FUNCTION Sine# (x#) STATIC
  15142.            Sine# = SIN(x#)
  15143.        END FUNCTION
  15144.    ──────────────────────────────────────────────────────────────────────────
  15145.  
  15146.  
  15147.  Function: SquareRoot#
  15148.  
  15149.    Returns the square root of a number. Before the QuickBASIC SQR function is
  15150.    used to actually find the square root, the number is checked to be sure it
  15151.    isn't negative. If it is, an error message is displayed, and the program
  15152.    terminates.
  15153.  
  15154.    ──────────────────────────────────────────────────────────────────────────
  15155.      ' ************************************************
  15156.      ' **  Name:          SquareRoot#                **
  15157.      ' **  Type:          Function                   **
  15158.      ' **  Module:        QCALMATH.BAS               **
  15159.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15160.      ' ************************************************
  15161.      '
  15162.      ' EXAMPLE OF USE:  y# = SquareRoot#(x#)
  15163.      ' PARAMETERS:      x#         Number to be processed
  15164.      ' VARIABLES:       (none)
  15165.      ' MODULE LEVEL
  15166.      '   DECLARATIONS:  DECLARE FUNCTION SquareRoot# (x#)
  15167.      '
  15168.        FUNCTION SquareRoot# (x#) STATIC
  15169.            IF x# >= 0 THEN
  15170.                SquareRoot# = SQR(x#)
  15171.            ELSE
  15172.                PRINT "Error: SQR(x#) where x# < 0"
  15173.                SYSTEM
  15174.            END IF
  15175.        END FUNCTION
  15176.    ──────────────────────────────────────────────────────────────────────────
  15177.  
  15178.  
  15179.  Function: Subtract#
  15180.  
  15181.    Returns the difference of two numbers.
  15182.  
  15183.    ──────────────────────────────────────────────────────────────────────────
  15184.      ' ************************************************
  15185.      ' **  Name:          Subtract#                  **
  15186.      ' **  Type:          Function                   **
  15187.      ' **  Module:        QCALMATH.BAS               **
  15188.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15189.      ' ************************************************
  15190.      '
  15191.      ' EXAMPLE OF USE:   y# = Subtract#(y#, x#)
  15192.      ' PARAMETERS:       y#         Number to be processed
  15193.      '                   x#         Number to be processed
  15194.      ' VARIABLES:        (none)
  15195.      ' MODULE LEVEL
  15196.      '   DECLARATIONS:   DECLARE FUNCTION Subtract# (y#, x#)
  15197.      '
  15198.        FUNCTION Subtract# (y#, x#) STATIC
  15199.            Subtract# = y# - x#
  15200.        END FUNCTION
  15201.    ──────────────────────────────────────────────────────────────────────────
  15202.  
  15203.  
  15204.  Subprogram: SwapXY
  15205.  
  15206.    Swaps the top two entries on the stack.
  15207.  
  15208.    ──────────────────────────────────────────────────────────────────────────
  15209.      ' ************************************************
  15210.      ' **  Name:          SwapXY                     **
  15211.      ' **  Type:          Subprogram                 **
  15212.      ' **  Module:        QCALMATH.BAS               **
  15213.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15214.      ' ************************************************
  15215.      '
  15216.      ' EXAMPLE OF USE:  SwapXY stack#(), ptr%
  15217.      ' PARAMETERS:      stack#()   Numeric stack
  15218.      '                  ptr%       Pointer to top of stack
  15219.      ' VARIABLES:       (none)
  15220.      ' MODULE LEVEL
  15221.      '   DECLARATIONS:  DECLARE SUB SwapXY (stack#(), ptr%
  15222.      '
  15223.        SUB SwapXY (stack#(), ptr%) STATIC
  15224.            IF ptr% > 1 THEN
  15225.                SWAP stack#(ptr%), stack#(ptr% - 1)
  15226.            END IF
  15227.        END SUB
  15228.    ──────────────────────────────────────────────────────────────────────────
  15229.  
  15230.  
  15231.  Function: Tangent#
  15232.  
  15233.    Returns the tangent of an angle; assumes the angle is in radians.
  15234.  
  15235.    ──────────────────────────────────────────────────────────────────────────
  15236.      ' ************************************************
  15237.      ' **  Name:          Tangent#                   **
  15238.      ' **  Type:          Function                   **
  15239.      ' **  Module:        QCALMATH.BAS               **
  15240.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15241.      ' ************************************************
  15242.      '
  15243.      ' EXAMPLE OF USE:  y# = Tangent#(x#)
  15244.      ' PARAMETERS:      x#         Angle, expressed in radians
  15245.      ' VARIABLES:       (none)
  15246.      ' MODULE LEVEL
  15247.      '   DECLARATIONS:  DECLARE FUNCTION Tangent# (x#)
  15248.      '
  15249.        FUNCTION Tangent# (x#) STATIC
  15250.            Tangent# = TAN(x#)
  15251.        END FUNCTION
  15252.    ──────────────────────────────────────────────────────────────────────────
  15253.  
  15254.  
  15255.  Function: Xsquared#
  15256.  
  15257.    Returns the square of a number.
  15258.  
  15259.    ──────────────────────────────────────────────────────────────────────────
  15260.      ' ************************************************
  15261.      ' **  Name:          Xsquared#                  **
  15262.      ' **  Type:          Function                   **
  15263.      ' **  Module:        QCALMATH.BAS               **
  15264.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15265.      ' ************************************************
  15266.      '
  15267.      ' EXAMPLE OF USE:  y# = Xsquared#(x#)
  15268.      ' PARAMETERS:      x#         Number to be processed
  15269.      ' VARIABLES:       (none)
  15270.      ' MODULE LEVEL
  15271.      '   DECLARATIONS:  DECLARE FUNCTION Xsquared# (x#)
  15272.      '
  15273.        FUNCTION Xsquared# (x#) STATIC
  15274.            Xsquared# = x# * x#
  15275.        END FUNCTION
  15276.    ──────────────────────────────────────────────────────────────────────────
  15277.  
  15278.  
  15279.  Function: YRaisedToX#
  15280.  
  15281.    Returns a number raised to the power of a second number.
  15282.  
  15283.    ──────────────────────────────────────────────────────────────────────────
  15284.      ' ************************************************
  15285.      ' **  Name:          YRaisedToX#                **
  15286.      ' **  Type:          Function                   **
  15287.      ' **  Module:        QCALMATH.BAS               **
  15288.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15289.      ' ************************************************
  15290.      '
  15291.      ' EXAMPLE OF USE:  z# = YRaisedToX#(y#, x#)
  15292.      ' PARAMETERS:      y#         Number to be raised to a power
  15293.      '                  x#         Power to raise the other number to
  15294.      ' VARIABLES:       (none)
  15295.      ' MODULE LEVEL
  15296.      '   DECLARATIONS:  DECLARE FUNCTION YRaisedToX# (y#, x#)
  15297.      '
  15298.        FUNCTION YRaisedToX# (y#, x#) STATIC
  15299.            YRaisedToX# = y# ^ x#
  15300.        END FUNCTION
  15301.    ──────────────────────────────────────────────────────────────────────────
  15302.  
  15303.  
  15304.  
  15305.  ────────────────────────────────────────────────────────────────────────────
  15306.  RANDOMS
  15307.  
  15308.    The RANDOMS toolbox provides a collection of random number generators.
  15309.  
  15310.    At the heart of these routines are two techniques, which are described in
  15311.    The Art of Computer Programming, Vol. 2, Seminumerical Algorithms, by
  15312.    Donald Knuth and which are combined to form the method in the Rand&
  15313.    function. The Rand& function returns pseudorandom integers in the range 0
  15314.    through 999999999. No multiplication or division is used, the algorithm is
  15315.    easily translated to any language that supports 32-bit integers, and all
  15316.    digits in the returned numbers are equally random. A table-shuffling
  15317.    technique further increases the randomness of the sequence.
  15318.  
  15319.    Several other functions use the random long integers returned by the
  15320.    Rand& function to create other random number distributions. For example,
  15321.    the RandReal!(x!, y!) function returns random real numbers in the range x!
  15322.    through y!. One common example of this function, RandReal!(0!, 1!),
  15323.    returns a pseudorandom, single-precision, floating-point value in the
  15324.    range 0 through 1.
  15325.  
  15326.    The RandShuffle subprogram and the RandInteger% function are used by
  15327.    CIPHER to generate a repeatable but secure sequence of random byte values
  15328.    in the range 0 through 255. See the CIPHER program for more information
  15329.    on using this file-ciphering technique.
  15330.  
  15331.    Name                          Type   Description
  15332.    ──────────────────────────────────────────────────────────────────────────
  15333.    RANDOMS.BAS                          Demo module
  15334.    Rand&                        Func   Long integers
  15335.    RandExponential!             Func   Real value with exponential
  15336.                                         distribution from mean
  15337.    RandFrac!                    Func   Single-precision positive value < 1.0
  15338.    RandInteger%                 Func   Integers within desired range
  15339.    RandNormal!                  Func   Single-precision value from mean
  15340.                                         and standard deviation
  15341.    RandReal!                    Func   Single-precision value in desired
  15342.                                         range
  15343.    RandShuffle                  Sub    Initializes random number generator
  15344.    ──────────────────────────────────────────────────────────────────────────
  15345.  
  15346.  
  15347.  Demo Module: RANDOMS
  15348.  
  15349.    ──────────────────────────────────────────────────────────────────────────
  15350.      ' ************************************************
  15351.      ' **  Name:          RANDOMS                    **
  15352.      ' **  Type:          Toolbox                    **
  15353.      ' **  Module:        RANDOMS.BAS                **
  15354.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15355.      ' ************************************************
  15356.      ' USAGE:           No command line parameters
  15357.      ' .MAK FILE:       (none)
  15358.      ' PARAMETERS:      (none)
  15359.      ' VARIABLES:       i%      Loop index for generating pseudorandom numbers
  15360.  
  15361.        DECLARE FUNCTION Rand& ()
  15362.        DECLARE FUNCTION RandExponential! (mean!)
  15363.        DECLARE FUNCTION RandFrac! ()
  15364.        DECLARE FUNCTION RandInteger% (a%, b%)
  15365.        DECLARE FUNCTION RandNormal! (mean!, stddev!)
  15366.        DECLARE FUNCTION RandReal! (x!, y!)
  15367.  
  15368.        DECLARE SUB RandShuffle (key$)
  15369.  
  15370.      ' Array of long integers for generating all randoms
  15371.        DIM SHARED r&(1 TO 100)
  15372.  
  15373.      ' Clear the screen
  15374.        CLS
  15375.  
  15376.      ' Shuffle the random number generator, creating a
  15377.      ' unique sequence for every possible second
  15378.        RandShuffle DATE$ + TIME$
  15379.  
  15380.        PRINT "Rand&"
  15381.        FOR i% = 1 TO 5
  15382.            PRINT Rand&,
  15383.        NEXT i%
  15384.        PRINT
  15385.  
  15386.        PRINT "RandInteger%(0, 9)"
  15387.        FOR i% = 1 TO 5
  15388.            PRINT RandInteger%(0, 9),
  15389.        NEXT i%
  15390.        PRINT
  15391.  
  15392.        PRINT "RandReal!(-10!, 10!)"
  15393.        FOR i% = 1 TO 5
  15394.            PRINT RandReal!(-10!, 10!),
  15395.        NEXT i%
  15396.        PRINT
  15397.  
  15398.        PRINT "RandExponential!(100!)"
  15399.        FOR i% = 1 TO 5
  15400.            PRINT RandExponential!(100!),
  15401.        NEXT i%
  15402.        PRINT
  15403.  
  15404.        PRINT "RandNormal!(100!, 10!)"
  15405.        FOR i% = 1 TO 5
  15406.            PRINT RandNormal!(100!, 10!),
  15407.        NEXT i%
  15408.        PRINT
  15409.  
  15410.        PRINT "RandFrac!"
  15411.        FOR i% = 1 TO 5
  15412.            PRINT RandFrac!,
  15413.        NEXT i%
  15414.        PRINT
  15415.    ──────────────────────────────────────────────────────────────────────────
  15416.  
  15417.  
  15418.  Function: Rand&
  15419.  
  15420.    Returns a pseudorandom long integer in the range 0 through 999999999,
  15421.    inclusive. Using the Rand& function provides you several advantages: It is
  15422.    fast because a minimal number of mathematical manipulations are performed;
  15423.    the sequence length is long, much greater than 2^55; and all digits in the
  15424.    returned random integer are equally random.
  15425.  
  15426.    The array of long integers, r&(1 TO 100), is shared by this function and
  15427.    the RandShuffle subprogram. This array contains a table of 55 random
  15428.    integers, a table of 42 values for shuffling the order of the random
  15429.    numbers upon output, two index pointers into the first 55 values, and the
  15430.    last generated random integer.
  15431.  
  15432.    You must call the RandShuffle subprogram once before you use the Rand&
  15433.    function. This initializes the tables and presets the two index numbers
  15434.    used to access table entries. If you don't call RandShuffle
  15435.  
  15436.    first, the Rand& function stops, you receive a Subscript out of range
  15437.    error message, and the program halts.
  15438.  
  15439.    Here's how Rand& works. The index numbers stored in r&(98) and r&(99) are
  15440.    always in the range 1 through 55 and are used to access two numbers stored
  15441.    in the first 55 entries of r&(). The first of these values is subtracted
  15442.    from the second, and if the result is less than zero, 1000000000 is added
  15443.    to bring the result somewhere into the range 0 through 999999999. This
  15444.    result replaces the number at the first location accessed. Finally, the
  15445.    two index numbers are decremented by 1, adjusted if necessary so that they
  15446.    remain in the range 1 through 55, and stored back in r&(98) and r&(99) for
  15447.    the next call to this routine.
  15448.  
  15449.    This table subtraction algorithm results in a good-quality random long
  15450.    integer, but an additional technique is used within Rand& to generate a
  15451.    significantly more random sequence of numbers. The generated number is
  15452.    used to point to one of the 42 entries in the locations r&(56) through
  15453.    r&(97). The previously generated number stored at that location is
  15454.    extracted, saved in r&(100), and replaced with the number just generated.
  15455.    Finally, the value saved in r&(100) is returned as the result. This
  15456.    randomly shuffles the order of the output values and effectively
  15457.    obliterates any subtle patterns that the sequence might have.
  15458.  
  15459.    ──────────────────────────────────────────────────────────────────────────
  15460.      ' ************************************************
  15461.      ' **  Name:          Rand&                      **
  15462.      ' **  Type:          Function                   **
  15463.      ' **  Module:        RANDOMS.BAS                **
  15464.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15465.      ' ************************************************
  15466.      '
  15467.      ' Returns a pseudorandom long integer in the range
  15468.      ' 0 through 999999999.
  15469.      '
  15470.      ' EXAMPLE OF USE:  n& = Rand&
  15471.      ' PARAMETERS:      (none)
  15472.      ' VARIABLES:       i%         First index into random number table
  15473.      '                  j%         Second index into random number table
  15474.      '                  t&         Working variable
  15475.      ' MODULE LEVEL
  15476.      '   DECLARATIONS:  DECLARE FUNCTION Rand& ()
  15477.      '                  DIM SHARED r&(1 TO 100)
  15478.      '
  15479.        FUNCTION Rand& STATIC
  15480.  
  15481.          ' Get the pointers into the table
  15482.            i% = r&(98)
  15483.            j% = r&(99)
  15484.  
  15485.          ' Subtract the two table values
  15486.            t& = r&(i%) - r&(j%)
  15487.  
  15488.          ' Adjust result if less than zero
  15489.            IF t& < 0 THEN
  15490.                t& = t& + 1000000000
  15491.            END IF
  15492.  
  15493.          ' Replace table entry with new random number
  15494.            r&(i%) = t&
  15495.  
  15496.          ' Decrement first index, keeping in range 1 through 55
  15497.            IF i% > 1 THEN
  15498.                r&(98) = i% - 1
  15499.            ELSE
  15500.                r&(98) = 55
  15501.            END IF
  15502.  
  15503.          ' Decrement second index, keeping in range 1 through 55
  15504.            IF j% > 1 THEN
  15505.                r&(99) = j% - 1
  15506.            ELSE
  15507.                r&(99) = 55
  15508.            END IF
  15509.  
  15510.          ' Use last random number to index into shuffle table
  15511.            i% = r&(100) MOD 42 + 56
  15512.  
  15513.          ' Grab random from table as current random number
  15514.            r&(100) = r&(i%)
  15515.  
  15516.          ' Put new calculated random into table
  15517.            r&(i%) = t&
  15518.  
  15519.          ' Return the random number grabbed from the table
  15520.            Rand& = r&(100)
  15521.  
  15522.        END FUNCTION
  15523.    ──────────────────────────────────────────────────────────────────────────
  15524.  
  15525.  
  15526.  Function: RandExponential!
  15527.  
  15528.    Returns a pseudorandom real value with an exponential distribution, which
  15529.    is defined by the passed value of the mean.
  15530.  
  15531.    Be sure to call the RandShuffle subprogram before using this function.
  15532.  
  15533.    ──────────────────────────────────────────────────────────────────────────
  15534.      ' ************************************************
  15535.      ' **  Name:          RandExponential!           **
  15536.      ' **  Type:          Function                   **
  15537.      ' **  Module:        RANDOMS.BAS                **
  15538.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15539.      ' ************************************************
  15540.      '
  15541.      ' Returns an exponentially distributed pseudorandom,
  15542.      ' single-precision number given the mean of the
  15543.      ' distribution.
  15544.      '
  15545.      ' EXAMPLE OF USE:  x! = RandExponential!(mean!)
  15546.      ' PARAMETERS:      mean!   The mean of the exponential distribution
  15547.      ' VARIABLES:       (none)
  15548.      ' MODULE LEVEL
  15549.      '   DECLARATIONS:  DECLARE FUNCTION RandExponential! (mean!)
  15550.      '
  15551.        FUNCTION RandExponential! (mean!) STATIC
  15552.            RandExponential! = -mean! * LOG(RandFrac!)
  15553.        END FUNCTION
  15554.    ──────────────────────────────────────────────────────────────────────────
  15555.  
  15556.  
  15557.  Function: RandFrac!
  15558.  
  15559.    Returns a pseudorandom real value in the range 0 through 1. This function
  15560.    is similar to the QuickBASIC function RND, but has a much longer sequence
  15561.    and a more random distribution.
  15562.  
  15563.    Be sure to call the RandShuffle subprogram before using this function.
  15564.  
  15565.    ──────────────────────────────────────────────────────────────────────────
  15566.      ' ************************************************
  15567.      ' **  Name:          RandFrac!                  **
  15568.      ' **  Type:          Function                   **
  15569.      ' **  Module:        RANDOMS.BAS                **
  15570.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15571.      ' ************************************************
  15572.      '
  15573.      ' Returns a pseudorandom, single-precision number
  15574.      ' in the range 0 through 1.
  15575.      '
  15576.      ' EXAMPLE OF USE:  x! = RandFrac!
  15577.      ' PARAMETERS:      (none)
  15578.      ' VARIABLES:       (none)
  15579.      ' MODULE LEVEL
  15580.      '   DECLARATIONS:  DECLARE FUNCTION RandFrac! ()
  15581.      '
  15582.        FUNCTION RandFrac! STATIC
  15583.            RandFrac! = Rand& / 1E+09
  15584.        END FUNCTION
  15585.    ──────────────────────────────────────────────────────────────────────────
  15586.  
  15587.  
  15588.  Function: RandInteger%
  15589.  
  15590.    Returns a pseudorandom integer in the range a% through b%, inclusive. For
  15591.    example, RandInteger%(0, 9) returns a random digit from 0 through 9.
  15592.  
  15593.    The passed value of a% must be less than b%; if it is not, this function
  15594.    generates incorrect random numbers. These parameters must be in the legal
  15595.    range of 16-bit signed integers, not less than -32768 nor greater than
  15596.    32767.
  15597.  
  15598.    Be sure to call the RandShuffle subprogram before using this function.
  15599.  
  15600.    ──────────────────────────────────────────────────────────────────────────
  15601.      ' ************************************************
  15602.      ' **  Name:          RandInteger%               **
  15603.      ' **  Type:          Function                   **
  15604.      ' **  Module:        RANDOMS.BAS                **
  15605.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15606.      ' ************************************************
  15607.      '
  15608.      ' Returns a pseudorandom integer in the range
  15609.      ' a% to b% inclusive.
  15610.      '
  15611.      ' EXAMPLE OF USE:  n% = RandInteger%(a%, b%)
  15612.      ' PARAMETERS:      a%    Minimum value for returned integer
  15613.      '                  b%    Maximum value for returned integer
  15614.      ' VARIABLES:       (none)
  15615.      ' MODULE LEVEL
  15616.      '   DECLARATIONS:  DECLARE FUNCTION RandInteger% (a%, b%)
  15617.      '
  15618.        FUNCTION RandInteger% (a%, b%) STATIC
  15619.            RandInteger% = a% + (Rand& MOD (b% - a% + 1))
  15620.        END FUNCTION
  15621.    ──────────────────────────────────────────────────────────────────────────
  15622.  
  15623.  
  15624.  Function: RandNormal!
  15625.  
  15626.    Returns pseudorandom real values with a normal distribution, which is
  15627.    defined by the passed mean and standard deviation.
  15628.  
  15629.    Be sure to call the RandShuffle subprogram before using this function.
  15630.  
  15631.    ──────────────────────────────────────────────────────────────────────────
  15632.      ' ************************************************
  15633.      ' **  Name:          RandNormal!                **
  15634.      ' **  Type:          Function                   **
  15635.      ' **  Module:        RANDOMS.BAS                **
  15636.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15637.      ' ************************************************
  15638.      '
  15639.      ' Returns a normally distributed single-precision,
  15640.      ' pseudorandom number given the mean and standard deviation.
  15641.      '
  15642.      ' EXAMPLE OF USE:  x! = RandNormal!(mean!, stddev!)
  15643.      ' PARAMETERS:      mean!    Mean of the distribution of returned
  15644.      '                           values
  15645.      '                  stddev!  Standard deviation of the distribution
  15646.      ' VARIABLES:       u1!      Pseudorandom positive real value
  15647.      '                           less than 1
  15648.      '                  u2!      Pseudorandom positive real value
  15649.      '                           less than 1
  15650.      '                  x!       Working value
  15651.      ' MODULE LEVEL
  15652.      '   DECLARATIONS:  DECLARE FUNCTION RandNormal! (mean!, stddev!)
  15653.      '
  15654.        FUNCTION RandNormal! (mean!, stddev!) STATIC
  15655.            u1! = RandFrac!
  15656.            u2! = RandFrac!
  15657.            x! = SQR(-2! * LOG(u1!)) * COS(6.283185 * u2)
  15658.            RandNormal! = mean! + stddev! * x!
  15659.        END FUNCTION
  15660.    ──────────────────────────────────────────────────────────────────────────
  15661.  
  15662.  
  15663.  Function: RandReal!
  15664.  
  15665.    Returns a pseudorandom real value in the range x! through y! For example,
  15666.    RandReal!(-10!, 10!) returns a floating-point, single-precision value in
  15667.    the range -10 through +10.
  15668.  
  15669.    Be sure to call the RandShuffle subprogram before using this function.
  15670.  
  15671.    ──────────────────────────────────────────────────────────────────────────
  15672.      ' ************************************************
  15673.      ' **  Name:          RandReal!                  **
  15674.      ' **  Type:          Function                   **
  15675.      ' **  Module:        RANDOMS.BAS                **
  15676.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15677.      ' ************************************************
  15678.      '
  15679.      ' Returns a pseudorandom, single-precision real
  15680.      ' number in the range x! to y!.
  15681.      ' EXAMPLE OF USE:  z! = RandReal!(x!, y!)
  15682.      ' PARAMETERS:      x!    Minimum for returned value
  15683.      '                  y!    Maximum for returned value
  15684.      ' VARIABLES:       (none)
  15685.      ' MODULE LEVEL
  15686.      '   DECLARATIONS:  DECLARE FUNCTION RandReal! (x!, y!)
  15687.      '
  15688.        FUNCTION RandReal! (x!, y!) STATIC
  15689.            RandReal! = x! + (y! - x!) * (Rand& / 1E+09)
  15690.        END FUNCTION
  15691.    ──────────────────────────────────────────────────────────────────────────
  15692.  
  15693.  
  15694.  Subprogram: RandShuffle
  15695.  
  15696.    Initializes the sequence of random numbers that the Rand& function
  15697.    returns. The r&() array contains all the values necessary for the Rand&
  15698.    function. This subprogram initializes all values in r&() based on the
  15699.    characters passed in key$. Refer to the Rand& function for a description
  15700.    of the contents of the shared array r&().
  15701.  
  15702.    The passed string key$ is first modified to a length of 97 characters.
  15703.    Notice that an arbitrary string (in this subprogram, Abra Ca Da Bra) is
  15704.    concatenated to the front end of key$. Any string can be used, but at
  15705.    least one character must have an odd byte number. This guarantees that at
  15706.    least one initial table entry will be odd, a necessity of this
  15707.    random-number-generation algorithm.
  15708.  
  15709.    Each character of the new key string (k$) is used to generate a
  15710.    pseudorandom long integer to be entered in the first 97 entries of r&().
  15711.    To "warm up" the sequence, 997 iterations of the Rand& algorithm, slightly
  15712.    modified, are performed on the table.
  15713.  
  15714.    Finally, starting values for the index values necessary for the Rand&
  15715.    function are stored in r&(98) and r&(99), and an initial value for the
  15716.    last generated number is stored in r&(100).
  15717.  
  15718.    All the other random number generators in this toolbox call the Rand&
  15719.    function, which generates an error and quits if RandShuffle isn't run
  15720.    first to initialize r&(). Therefore, you must be sure to call RandShuffle
  15721.    once during a program run before calling any of these functions.
  15722.  
  15723.    To generate the same sequence every time the program is run, pass the same
  15724.    key$ each time. To generate a unique sequence each time, pass a unique
  15725.    string. For example, to generate a unique sequence for every clock tick of
  15726.    your computer's existence, you could enter RandShuffle(DATE$ + TIME$ +
  15727.    STR$(TIMER)).
  15728.  
  15729.    The key$ can be any reasonable length, but only the first 83 characters
  15730.    are used to seed the generator. Because there are 256 possible characters
  15731.    for each of the 83, there are 256^83 possible unique sequences. It's safe
  15732.    to say you'll never run out!
  15733.  
  15734.    ──────────────────────────────────────────────────────────────────────────
  15735.      ' ************************************************
  15736.      ' **  Name:          RandShuffle                **
  15737.      ' **  Type:          Subprogram                 **
  15738.      ' **  Module:        RANDOMS.BAS                **
  15739.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15740.      ' ************************************************
  15741.      '
  15742.      ' Creates original table of pseudorandom long integers
  15743.      ' for use by the function Rand&.  The contents of key$
  15744.      ' are used to seed the table.
  15745.      '
  15746.      ' EXAMPLE OF USE:  RandShuffle(key$)
  15747.      ' PARAMETERS:      key$            String used to seed the generator
  15748.      '          r&(1 TO 100) (shared)   Array of long integers for
  15749.      '                                  generating pseudorandom numbers
  15750.      ' VARIABLES:       k$              Modified key string
  15751.      '                  i%              Index into k$, index into table
  15752.      '                  j%              Index into table
  15753.      '                  k%              Loop count for warming up generator
  15754.      ' MODULE LEVEL
  15755.      '   DECLARATIONS:  DECLARE SUB RandShuffle (key$)
  15756.      '
  15757.        SUB RandShuffle (key$) STATIC
  15758.  
  15759.          ' Form 97-character string, with key$ as part of it
  15760.            k$ = LEFT$("Abra Ca Da Bra" + key$ + SPACE$(83), 97)
  15761.  
  15762.          ' Use each character to seed table
  15763.            FOR i% = 1 TO 97
  15764.                r&(i%) = ASC(MID$(k$, i%, 1)) * 8171717 + i% * 997&
  15765.            NEXT i%
  15766.  
  15767.          ' Preserve string space
  15768.            k$ = ""
  15769.  
  15770.          ' Initialize pointers into table
  15771.            i% = 97
  15772.            j% = 12
  15773.  
  15774.          ' Randomize the table to get it warmed up
  15775.            FOR k% = 1 TO 997
  15776.  
  15777.              ' Subtract entries pointed to by i% and j%
  15778.                r&(i%) = r&(i%) - r&(j%)
  15779.  
  15780.              ' Adjust result if less than zero
  15781.                IF r&(i%) < 0 THEN
  15782.                    r&(i%) = r&(i%) + 1000000000
  15783.                END IF
  15784.  
  15785.              ' Decrement first index, keeping in range of 1 through 97
  15786.                IF i% > 1 THEN
  15787.                    i% = i% - 1
  15788.                ELSE
  15789.                    i% = 97
  15790.                END IF
  15791.  
  15792.              ' Decrement second index, keeping in range of 1 through 97
  15793.                IF j% > 1 THEN
  15794.                    j% = j% - 1
  15795.                ELSE
  15796.                    j% = 97
  15797.                END IF
  15798.  
  15799.            NEXT k%
  15800.  
  15801.          ' Initialize pointers for use by Rand& function
  15802.            r&(98) = 55
  15803.            r&(99) = 24
  15804.  
  15805.          ' Initialize pointer for shuffle table lookup by Rand& function
  15806.            r&(100) = 77
  15807.  
  15808.        END SUB
  15809.    ──────────────────────────────────────────────────────────────────────────
  15810.  
  15811.  
  15812.  
  15813.  ────────────────────────────────────────────────────────────────────────────
  15814.  STDOUT
  15815.  
  15816.    The STDOUT toolbox is a collection of subprograms for outputting
  15817.    characters through the MS-DOS standard output channel rather than through
  15818.    the QuickBASIC PRINT statement.
  15819.  
  15820.    QuickBASIC bypasses the ANSI.SYS driver. However, some nice features are
  15821.    built into this driver, and this toolbox lets you access them from
  15822.    QuickBASIC. For example, the AssignKey subprogram lets you redefine keys
  15823.    on the keyboard to any character or string of characters you want.
  15824.  
  15825.    Be sure you load the ANSI.SYS driver before trying this program. Several
  15826.    of the escape code sequences create meaningless output if the ANSI.SYS
  15827.    driver is not resident. In most cases, a statement similar to the
  15828.    following in your CONFIG.SYS file will load the ANSI.SYS driver at boot-up
  15829.    time:
  15830.  
  15831.  
  15832.      DEVICE = \DOS\ANSI.SYS
  15833.  
  15834.    When you run the STDOUT demo module, pay close attention to the prompts
  15835.    that appear. In one case you are prompted to press the "a" and "b" keys,
  15836.    immediately before the program exits to MS-DOS via the SHELL statement. Be
  15837.    sure you press "a" and then "b" to prevent the program from getting lost.
  15838.  
  15839.    Name                     Type    Description
  15840.    ──────────────────────────────────────────────────────────────────────────
  15841.    STDOUT.BAS                      Demo module
  15842.    AssignKey               Sub     Reassigns a string to a key
  15843.    Attribute               Sub     Sets screen color (ANSI driver
  15844.                                     definition)
  15845.    ClearLine               Sub     Clears current line from cursor to end of
  15846.                                     line
  15847.    ClearScreen             Sub     Clears screen
  15848.    CrLf                    Sub     Sends carriage return and line feed
  15849.    CursorDown              Sub     Moves cursor down specified number of
  15850.                                     lines
  15851.    CursorHome              Sub     Moves cursor to upper left corner of
  15852.                                     screen
  15853.    CursorLeft              Sub     Moves cursor left specified number of
  15854.                                     spaces
  15855.    CursorPosition          Sub     Moves cursor to specified row and column
  15856.    CursorRight             Sub     Moves cursor right specified number of
  15857.                                     spaces
  15858.    CursorUp                Sub     Moves cursor up specified number of lines
  15859.    StdOut                  Sub     Sends a string to standard output channel
  15860.    ──────────────────────────────────────────────────────────────────────────
  15861.  
  15862.  
  15863.  Demo Module: STDOUT
  15864.  
  15865.    ──────────────────────────────────────────────────────────────────────────
  15866.      ' ************************************************
  15867.      ' **  Name:          STDOUT                     **
  15868.      ' **  Type:          Toolbox                    **
  15869.      ' **  Module:        STDOUT.BAS                 **
  15870.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  15871.      ' ************************************************
  15872.      '
  15873.      ' USAGE:            No command line parameters
  15874.      ' REQUIREMENTS:     MIXED.QLB/.LIB
  15875.      '                   ANSI.SYS
  15876.      ' .MAK FILE:        (none)
  15877.      ' PARAMETERS:       (none)
  15878.      ' VARIABLES:        t0         Timer variable
  15879.      '                   bell$      ASCII character 7 (bell)
  15880.  
  15881.      ' Attribute definitions
  15882.        CONST NORMAL = 0
  15883.        CONST BRIGHT = 1
  15884.        CONST UNDERSCORE = 4
  15885.        CONST BLINK = 5
  15886.        CONST REVERSE = 7
  15887.        CONST INVISIBLE = 8
  15888.        CONST BLACKFOREGROUND = 30
  15889.        CONST REDFOREGROUND = 31
  15890.        CONST GREENFOREGROUND = 32
  15891.        CONST YELLOWFOREGROUND = 33
  15892.        CONST BLUEFOREGROUND = 34
  15893.        CONST MAGENTAFOREGROUND = 35
  15894.        CONST CYANFOREGROUND = 36
  15895.        CONST WHITEFOREGROUND = 37
  15896.        CONST BLACKBACKGROUND = 40
  15897.        CONST REDBACKGROUND = 41
  15898.        CONST GREENBACKGROUND = 42
  15899.        CONST YELLOWBACKGROUND = 43
  15900.        CONST BLUEBACKGROUND = 44
  15901.        CONST MAGENTABACKGROUND = 45
  15902.        CONST CYANBACKGROUND = 46
  15903.        CONST WHITEBACKGROUND = 47
  15904.  
  15905.        TYPE RegTypeX
  15906.            ax    AS INTEGER
  15907.            bx    AS INTEGER
  15908.            cx    AS INTEGER
  15909.            dx    AS INTEGER
  15910.            Bp    AS INTEGER
  15911.            si    AS INTEGER
  15912.            di    AS INTEGER
  15913.            flags AS INTEGER
  15914.            ds    AS INTEGER
  15915.            es    AS INTEGER
  15916.        END TYPE
  15917.  
  15918.      ' Subprograms
  15919.        DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
  15920.        DECLARE SUB ClearLine ()
  15921.        DECLARE SUB ClearScreen ()
  15922.        DECLARE SUB StdOut (a$)
  15923.        DECLARE SUB CrLf ()
  15924.        DECLARE SUB CursorPosition (row%, col%)
  15925.        DECLARE SUB CursorDown (n%)
  15926.        DECLARE SUB CursorLeft (n%)
  15927.        DECLARE SUB CursorRight (n%)
  15928.        DECLARE SUB CursorUp (n%)
  15929.        DECLARE SUB AssignKey (keyCode%, assign$)
  15930.        DECLARE SUB Attribute (attr%)
  15931.  
  15932.      ' Demonstrate the ClearLine and ClearScreen routines
  15933.        CLS
  15934.        PRINT "This will be erased quickly, in two steps..."
  15935.        t0 = TIMER
  15936.        DO
  15937.        LOOP UNTIL TIMER - t0 > 2
  15938.        LOCATE 1, 27
  15939.        ClearLine
  15940.        t0 = TIMER
  15941.        DO
  15942.        LOOP UNTIL TIMER - t0 > 2
  15943.        LOCATE 15, 1
  15944.        ClearScreen
  15945.  
  15946.      ' Demonstrate the StdOut routine
  15947.        bell$ = CHR$(7)
  15948.        StdOut "Sending a 'Bell' to StdOut" + bell$
  15949.        CrLf
  15950.  
  15951.      ' Set cursor position
  15952.        CursorPosition 3, 20
  15953.        StdOut "* CursorPosition 3, 20"
  15954.        CrLf
  15955.  
  15956.      ' Move the cursor around the screen
  15957.        StdOut "Cursor movements..."
  15958.        CrLf
  15959.        CursorDown 1
  15960.        StdOut "Down 1"
  15961.        CursorRight 12
  15962.        StdOut "Right 12"
  15963.        CursorDown 2
  15964.        StdOut "Down 2"
  15965.        CursorLeft 99
  15966.        StdOut "Left 99"
  15967.        CrLf
  15968.  
  15969.      ' Character attributes
  15970.        CrLf
  15971.        Attribute YELLOWFOREGROUND
  15972.        Attribute BRIGHT
  15973.        Attribute BLUEBACKGROUND
  15974.        StdOut "Bright yellow on blue"
  15975.        CrLf
  15976.        Attribute NORMAL
  15977.        StdOut "Back to normal attributes"
  15978.        CrLf
  15979.      '
  15980.      ' Key reassignment
  15981.        AssignKey 97, "REM The 'a' and 'b' keys have been redefined" + CHR$(13)
  15982.        AssignKey 98, "EXIT" + CHR$(13)
  15983.        CursorDown 1
  15984.        Attribute BRIGHT
  15985.        Attribute YELLOWFOREGROUND
  15986.        StdOut "NOTE:"
  15987.        CrLf
  15988.        StdOut "Press the 'a' key and then the 'b' key ... "
  15989.        CrLf
  15990.        StdOut "The program will then continue ........ "
  15991.        Attribute NORMAL
  15992.        CrLf
  15993.        SHELL
  15994.        AssignKey 97, ""
  15995.        AssignKey 98, ""
  15996.    ──────────────────────────────────────────────────────────────────────────
  15997.  
  15998.  
  15999.  Subprogram: AssignKey
  16000.  
  16001.    Assigns a string to any key on the keyboard. The first parameter is the
  16002.    key code number returned by the ASC(INKEY$) statement for a given key
  16003.    press. The second parameter is a string of characters assigned to the
  16004.    indicated key. The string can be a maximum of 63 characters in length. If
  16005.    the string is null, the original key definition is returned to the key.
  16006.  
  16007.    One complication arises if the key normally returns an extended key code.
  16008.    Recall that such keys return CHR$(0), followed by a second character that
  16009.    identifies the key. The AssignKey subprogram recognizes negative key
  16010.    numbers as extended key codes. Pass the negative of the second byte of an
  16011.    extended key code to indicate the key.
  16012.  
  16013.    ──────────────────────────────────────────────────────────────────────────
  16014.      ' ************************************************
  16015.      ' **  Name:          AssignKey                  **
  16016.      ' **  Type:          Subprogram                 **
  16017.      ' **  Module:        STDOUT.BAS                 **
  16018.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16019.      ' ************************************************
  16020.      '
  16021.      ' Assigns a string to any key using ANSI.SYS driver.
  16022.      '
  16023.      ' EXAMPLE OF USE:  AssignKey keyCode%, assign$
  16024.      ' PARAMETERS:      keyCode%   ASCII number for key to be reassigned
  16025.      '                  assign$    String to assign to key
  16026.      ' VARIABLES:       k$         Command string for ANSI.SYS driver
  16027.      '                  i%         Index to each character of assign$
  16028.      ' MODULE LEVEL
  16029.      '   DECLARATIONS:  DECLARE SUB AssignKey (keyCode%, assign$)
  16030.      '
  16031.        SUB AssignKey (keyCode%, assign$) STATIC
  16032.            IF keyCode% <= 0 THEN
  16033.                k$ = "[0;"
  16034.            ELSE
  16035.                k$ = "["
  16036.            END IF
  16037.            k$ = k$ + MID$(STR$(keyCode%), 2)
  16038.            IF assign$ <> "" THEN
  16039.                FOR i% = 1 TO LEN(assign$)
  16040.                    k$ = k$ + ";" + MID$(STR$(ASC(MID$(assign$, i%))), 2)
  16041.                NEXT i%
  16042.            END IF
  16043.            StdOut CHR$(27) + k$ + "p"
  16044.        END SUB
  16045.    ──────────────────────────────────────────────────────────────────────────
  16046.  
  16047.  
  16048.  Subprogram: Attribute
  16049.  
  16050.    Sets screen color attributes as defined by the ANSI.SYS driver.
  16051.  
  16052.    ──────────────────────────────────────────────────────────────────────────
  16053.      ' ************************************************
  16054.      ' **  Name:          Attribute                  **
  16055.      ' **  Type:          Subprogram                 **
  16056.      ' **  Module:        STDOUT.BAS                 **
  16057.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16058.      ' ************************************************
  16059.      '
  16060.      ' Sets the foreground, background, and other color
  16061.      ' attributes.
  16062.      '
  16063.      ' EXAMPLE OF USE:  Attribute attr%
  16064.      ' PARAMETERS:      attr%      Number for attribute to be set
  16065.      ' VARIABLES:       (none)
  16066.      ' MODULE LEVEL
  16067.      '   DECLARATIONS:  DECLARE SUB StdOut (a$)
  16068.      '                  DECLARE SUB Attribute (attr%)
  16069.      '
  16070.        SUB Attribute (attr%) STATIC
  16071.            StdOut CHR$(27) + "[" + MID$(STR$(attr%), 2) + "m"
  16072.        END SUB
  16073.    ──────────────────────────────────────────────────────────────────────────
  16074.  
  16075.  
  16076.  Subprogram: ClearLine
  16077.  
  16078.    Sends to standard output the ANSI.SYS escape-code sequence that erases the
  16079.    current line from the cursor to the end of the line. The current cursor
  16080.    position is maintained.
  16081.  
  16082.    ──────────────────────────────────────────────────────────────────────────
  16083.      ' ************************************************
  16084.      ' **  Name:          ClearLine                  **
  16085.      ' **  Type:          Subprogram                 **
  16086.      ' **  Module:        STDOUT.BAS                 **
  16087.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16088.      ' ************************************************
  16089.      '
  16090.      ' Clears the display line from the current cursor
  16091.      ' position to the end of the line.
  16092.      '
  16093.      ' EXAMPLE OF USE:  ClearLine
  16094.      ' PARAMETERS:      (none)
  16095.      ' VARIABLES:       (none)
  16096.      ' MODULE LEVEL
  16097.      '   DECLARATIONS:  DECLARE SUB ClearLine ()
  16098.      '                  DECLARE SUB StdOut (a$)
  16099.      '
  16100.        SUB ClearLine STATIC
  16101.            StdOut CHR$(27) + "[K"
  16102.        END SUB
  16103.    ──────────────────────────────────────────────────────────────────────────
  16104.  
  16105.  
  16106.  Subprogram: ClearScreen
  16107.  
  16108.    Sends to standard output the ANSI.SYS escape-code sequence that clears the
  16109.    screen; positions the cursor at the top left of the screen.
  16110.  
  16111.    ──────────────────────────────────────────────────────────────────────────
  16112.      ' ************************************************
  16113.      ' **  Name:          ClearScreen                **
  16114.      ' **  Type:          Subprogram                 **
  16115.      ' **  Module:        STDOUT.BAS                 **
  16116.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16117.      ' ************************************************
  16118.      '
  16119.      ' Clears the screen and moves the cursor to the
  16120.      ' home position.
  16121.      '
  16122.      ' EXAMPLE OF USE:  ClearScreen
  16123.      ' PARAMETERS:      (none)
  16124.      ' VARIABLES:       (none)
  16125.      ' MODULE LEVEL
  16126.      '   DECLARATIONS:  DECLARE SUB ClearScreen ()
  16127.      '                  DECLARE SUB StdOut (a$)
  16128.      '
  16129.          SUB ClearScreen STATIC
  16130.            StdOut CHR$(27) + "[2J"
  16131.        END SUB
  16132.    ──────────────────────────────────────────────────────────────────────────
  16133.  
  16134.  
  16135.  Subprogram: CrLf
  16136.  
  16137.    Sends carriage return and line feed to a standard output.
  16138.  
  16139.    ──────────────────────────────────────────────────────────────────────────
  16140.      ' ************************************************
  16141.      ' **  Name:          CrLf                       **
  16142.      ' **  Type:          Subprogram                 **
  16143.      ' **  Module:        STDOUT.BAS                 **
  16144.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16145.      ' ************************************************
  16146.      '
  16147.      ' Sends line feed and carriage return characters
  16148.      ' to standard output.
  16149.      '
  16150.      ' EXAMPLE OF USE:  CrLf
  16151.      ' PARAMETERS:      (none)
  16152.      ' VARIABLES:       (none)
  16153.      ' MODULE LEVEL
  16154.      '   DECLARATIONS:  DECLARE SUB StdOut (a$)
  16155.      '                  DECLARE SUB CrLf ()
  16156.      '
  16157.        SUB CrLf STATIC
  16158.            StdOut CHR$(13) + CHR$(10)
  16159.        END SUB
  16160.    ──────────────────────────────────────────────────────────────────────────
  16161.  
  16162.  
  16163.  Subprogram: CursorDown
  16164.  
  16165.    Sends to standard output the ANSI.SYS escape-code sequence that moves the
  16166.    cursor down the screen n% lines. The cursor stays in the same column and
  16167.    stops at the bottom line of the screen.
  16168.  
  16169.    ──────────────────────────────────────────────────────────────────────────
  16170.      ' ************************************************
  16171.      ' **  Name:          CursorDown                 **
  16172.      ' **  Type:          Subprogram                 **
  16173.      ' **  Module:        STDOUT.BAS                 **
  16174.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16175.      ' ************************************************
  16176.      '
  16177.      ' Moves the cursor n% lines down the screen.
  16178.      '
  16179.      ' EXAMPLE OF USE:   CursorDown n%
  16180.      ' PARAMETERS:       n%         Number of lines to move the cursor down
  16181.      ' VARIABLES:        (none)
  16182.      ' MODULE LEVEL
  16183.      '   DECLARATIONS:   DECLARE SUB StdOut (a$)
  16184.      '                   DECLARE SUB CursorDown (n%)
  16185.      '
  16186.        SUB CursorDown (n%) STATIC
  16187.            StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "B"
  16188.        END SUB
  16189.    ──────────────────────────────────────────────────────────────────────────
  16190.  
  16191.  
  16192.  Subprogram: CursorHome
  16193.  
  16194.    Sends to standard output the ANSI.SYS escape-code sequence that moves the
  16195.    cursor to the home position; does not erase the display.
  16196.  
  16197.    ──────────────────────────────────────────────────────────────────────────
  16198.      ' ************************************************
  16199.      ' **  Name:          CursorHome                 **
  16200.      ' **  Type:          Subprogram                 **
  16201.      ' **  Module:        STDOUT.BAS                 **
  16202.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16203.      ' ************************************************
  16204.      '
  16205.      ' Moves the cursor to the top left of the
  16206.      ' screen.
  16207.      '
  16208.      ' EXAMPLE OF USE:  CursorHome
  16209.      ' PARAMETERS:      (none)
  16210.      ' VARIABLES:       (none)
  16211.      ' MODULE LEVEL
  16212.      '   DECLARATIONS:  DECLARE SUB CursorHome
  16213.      '
  16214.        SUB CursorHome STATIC
  16215.            StdOut CHR$(27) + "[H"
  16216.        END SUB
  16217.    ──────────────────────────────────────────────────────────────────────────
  16218.  
  16219.  
  16220.  Subprogram: CursorLeft
  16221.  
  16222.    Sends to standard output the ANSI.SYS escape-code sequence that moves the
  16223.    cursor to the left n% columns. The cursor stays in the same row and stops
  16224.    at the left column of the screen.
  16225.  
  16226.    ──────────────────────────────────────────────────────────────────────────
  16227.      ' ************************************************
  16228.      ' **  Name:          CursorLeft                 **
  16229.      ' **  Type:          Subprogram                 **
  16230.      ' **  Module:        STDOUT.BAS                 **
  16231.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16232.      ' ************************************************
  16233.      '
  16234.      ' Moves the cursor n% columns left on the screen.
  16235.      '
  16236.      ' EXAMPLE OF USE:  CursorLeft n%
  16237.      ' PARAMETERS:      n%      Number of columns to move the cursor left
  16238.      ' VARIABLES:       (none)
  16239.      ' MODULE LEVEL
  16240.      '   DECLARATIONS:  DECLARE SUB CursorLeft (n%)
  16241.      '
  16242.        SUB CursorLeft (n%) STATIC
  16243.            StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "D"
  16244.        END SUB
  16245.    ──────────────────────────────────────────────────────────────────────────
  16246.  
  16247.  
  16248.  Subprogram: CursorPosition
  16249.  
  16250.    Sends to standard output the ANSI.SYS escape-code sequence that moves the
  16251.    cursor to a given row and column.
  16252.  
  16253.    ──────────────────────────────────────────────────────────────────────────
  16254.      ' ************************************************
  16255.      ' **  Name:          CursorPosition             **
  16256.      ' **  Type:          Subprogram                 **
  16257.      ' **  Module:        STDOUT.BAS                 **
  16258.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16259.      ' ************************************************
  16260.      '
  16261.      ' Moves the cursor to the indicated row and column.
  16262.      '
  16263.      ' EXAMPLE OF USE:  CursorPosition row%, col%
  16264.      ' PARAMETERS:      row%       Row to move the cursor to
  16265.      '                  col%       Column to move the cursor to
  16266.      ' VARIABLES:       row$       String representation of row%
  16267.      '                  col$       String representation of col%
  16268.      ' MODULE LEVEL
  16269.      '   DECLARATIONS:  DECLARE SUB CursorPosition (row%, col%)
  16270.      '
  16271.        SUB CursorPosition (row%, col%) STATIC
  16272.            row$ = MID$(STR$(row%), 2)
  16273.            col$ = MID$(STR$(col%), 2)
  16274.            StdOut CHR$(27) + "[" + row$ + ";" + col$ + "H"
  16275.        END SUB
  16276.    ──────────────────────────────────────────────────────────────────────────
  16277.  
  16278.  
  16279.  Subprogram: CursorRight
  16280.  
  16281.    Sends to standard output the ANSI.SYS escape-code sequence that moves the
  16282.    cursor to the right n% columns. The cursor stays in the same row and stops
  16283.    at the right column of the screen.
  16284.  
  16285.    ──────────────────────────────────────────────────────────────────────────
  16286.      ' ************************************************
  16287.      ' **  Name:          CursorRight                **
  16288.      ' **  Type:          Subprogram                 **
  16289.      ' **  Module:        STDOUT.BAS                 **
  16290.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16291.      ' ************************************************
  16292.      '
  16293.      ' Moves the cursor n% columns right on the screen.
  16294.      '
  16295.      ' EXAMPLE OF USE:  CursorRight n%
  16296.      ' PARAMETERS:      n%     Number of columns to move the cursor right
  16297.      ' VARIABLES:       (none)
  16298.      ' MODULE LEVEL
  16299.      '   DECLARATIONS:  DECLARE SUB CursorRight (n%)
  16300.      '
  16301.        SUB CursorRight (n%) STATIC
  16302.            StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "C"
  16303.        END SUB
  16304.    ──────────────────────────────────────────────────────────────────────────
  16305.  
  16306.  
  16307.  Subprogram: CursorUp
  16308.  
  16309.    Sends to standard output the ANSI.SYS escape-code sequence that moves the
  16310.    cursor up the screen n% lines. The cursor stays in the same column and
  16311.    stops at the top line of the screen.
  16312.  
  16313.    ──────────────────────────────────────────────────────────────────────────
  16314.      ' ************************************************
  16315.      ' **  Name:          CursorUp                   **
  16316.      ' **  Type:          Subprogram                 **
  16317.      ' **  Module:        STDOUT.BAS                 **
  16318.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16319.      ' ************************************************
  16320.      '
  16321.      ' Moves the cursor n% lines up the screen.
  16322.      '
  16323.      ' EXAMPLE OF USE:  CursorUp n%
  16324.      ' PARAMETERS:      n%         Number of lines to move the cursor up
  16325.      ' VARIABLES:       (none)
  16326.      ' MODULE LEVEL
  16327.      '   DECLARATIONS:  DECLARE SUB CursorUp (n%)
  16328.      '
  16329.        SUB CursorUp (n%) STATIC
  16330.            StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "A"
  16331.        END SUB
  16332.    ──────────────────────────────────────────────────────────────────────────
  16333.  
  16334.  
  16335.  Subprogram: StdOut
  16336.  
  16337.    Sends a string of bytes to the standard output device. The string is
  16338.    output through the MS-DOS function for string output, bypassing the
  16339.    QuickBASIC PRINT statement.
  16340.  
  16341.    ──────────────────────────────────────────────────────────────────────────
  16342.      ' ************************************************
  16343.      ' **  Name:          StdOut                     **
  16344.      ' **  Type:          Subprogram                 **
  16345.      ' **  Module:        STDOUT.BAS               **
  16346.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16347.      ' ************************************************
  16348.      '
  16349.      ' Writes string to the MS-DOS standard output.
  16350.      '
  16351.      ' EXAMPLE OF USE:  StdOut a$
  16352.      ' PARAMETERS:      a$         String to be output
  16353.      ' VARIABLES:       regX       Structure of type RegTypeX
  16354.      ' MODULE LEVEL
  16355.      '   DECLARATIONS:    DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX,
  16356.      '                                          outreg AS RegTypeX)
  16357.      '                  DECLARE SUB StdOut (a$)
  16358.      '
  16359.        SUB StdOut (a$) STATIC
  16360.            DIM regX AS RegTypeX
  16361.            regX.ax = &H4000
  16362.            regX.cx = LEN(a$)
  16363.            regX.bx = 1
  16364.            regX.ds = VARSEG(a$)
  16365.            regX.dx = SADD(a$)
  16366.            InterruptX &H21, regX, regX
  16367.            IF regX.flags AND 1 THEN
  16368.                PRINT "Error while calling StdOut:"; regX.ax
  16369.                SYSTEM
  16370.            END IF
  16371.        END SUB
  16372.    ──────────────────────────────────────────────────────────────────────────
  16373.  
  16374.  
  16375.  
  16376.  ────────────────────────────────────────────────────────────────────────────
  16377.  STRINGS
  16378.  
  16379.    The STRINGS toolbox provides several common (and not so common)
  16380.    string-manipulation functions and subprograms.
  16381.  
  16382. ╓┌─┌────────────────────────┌───────┌────────────────────────────────────────╖
  16383.    Name                     Type    Description
  16384.    ──────────────────────────────────────────────────────────────────────────
  16385.    STRINGS.BAS                     Demo module
  16386.    Ascii2Ebcdic$           Func    Converts string from ASCII to EBCDIC
  16387.    BestMatch$              Func    Returns best match to input string
  16388.    BuildAEStrings          Sub     Builds ASCII and EBCDIC character
  16389.                                     translation tables
  16390.    Center$                 Func    Centers string by padding with spaces
  16391.    Detab$                  Func    Replaces tab characters with spaces
  16392.    Ebcdic2Ascii$           Func    Converts a string from EBCDIC to ASCII
  16393.    Entab$                  Func    Replaces spaces with tab characters
  16394.    FilterIn$               Func    Retains only specified characters in
  16395.                                     string
  16396.    FilterOut$              Func    Deletes specified characters from string
  16397.    Lpad$                   Func    Returns left-justified input string
  16398.    LtrimSet$               Func    Deletes specified characters from left
  16399.    Ord%                    Func    Returns byte number for ANSI mnemonic
  16400.    Repeat$                 Func    Combines multiple copies into one string
  16401.    Replace$                Func    Replaces specified characters in string
  16402.    Reverse$                Func    Reverses order of characters in a string
  16403.    Name                     Type    Description
  16404.    ──────────────────────────────────────────────────────────────────────────
  16405.   Reverse$                Func    Reverses order of characters in a string
  16406.    ReverseCase$            Func    Reverses case for each character in a
  16407.                                     string
  16408.    Rpad$                   Func    Returns right-justified input string
  16409.    RtrimSet$               Func    Deletes specified characters from right
  16410.    Translate$              Func    Exchanges characters in string from table
  16411.    ──────────────────────────────────────────────────────────────────────────
  16412.  
  16413.  
  16414.  
  16415.  Demo Module: STRINGS
  16416.  
  16417.    ──────────────────────────────────────────────────────────────────────────
  16418.      ' ************************************************
  16419.      ' **  Name:          STRINGS                    **
  16420.      ' **  Type:          Toolbox                    **
  16421.      ' **  Module:        STRINGS.BAS                **
  16422.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16423.      ' ************************************************
  16424.      ' USAGE:           No command line parameters
  16425.      ' .MAK FILE:       (none)
  16426.      ' PARAMETERS:      (none)
  16427.      ' VARIABLES:       a$       Working string for demonstrations
  16428.      '                  b$       Working string for demonstrations
  16429.      '                  c$       Working string for demonstrations
  16430.      '                  x$       Working string for demonstrations
  16431.      '                  y$       Working string for demonstrations
  16432.      '                  set$     Set of characters that define word separation
  16433.  
  16434.        DECLARE FUNCTION Ascii2Ebcdic$ (a$)
  16435.        DECLARE FUNCTION BestMatch$ (a$, x$, y$)
  16436.        DECLARE FUNCTION Center$ (a$, n%)
  16437.        DECLARE FUNCTION Detab$ (a$, tabs%)
  16438.        DECLARE FUNCTION Ebcdic2Ascii$ (e$)
  16439.        DECLARE FUNCTION Entab$ (a$, tabs%)
  16440.        DECLARE FUNCTION FilterIn$ (a$, set$)
  16441.        DECLARE FUNCTION FilterOut$ (a$, set$)
  16442.        DECLARE FUNCTION Lpad$ (a$, n%)
  16443.        DECLARE FUNCTION LtrimSet$ (a$, set$)
  16444.        DECLARE FUNCTION Ord% (a$)
  16445.        DECLARE FUNCTION Repeat$ (a$, n%)
  16446.        DECLARE FUNCTION Replace$ (a$, find$, substitute$)
  16447.        DECLARE FUNCTION Reverse$ (a$)
  16448.        DECLARE FUNCTION ReverseCase$ (a$)
  16449.        DECLARE FUNCTION Rpad$ (a$, n%)
  16450.        DECLARE FUNCTION RtrimSet$ (a$, set$)
  16451.        DECLARE FUNCTION Translate$ (a$, f$, t$)
  16452.  
  16453.      ' Subprograms
  16454.        DECLARE SUB BuildAEStrings ()
  16455.  
  16456.      ' Quick demonstrations
  16457.        CLS
  16458.        a$ = "This is a test"
  16459.        PRINT "a$", , a$
  16460.        PRINT "ReverseCase$(a$)", ReverseCase$(a$)
  16461.        PRINT "Reverse$(a$)", , Reverse$(a$)
  16462.        PRINT "Repeat$(a$, 3)", Repeat$(a$, 3)
  16463.        PRINT
  16464.  
  16465.        set$ = "T this"
  16466.        PRINT "set$", , set$
  16467.        PRINT "LtrimSet$(a$, set$)", LtrimSet$(a$, set$)
  16468.        PRINT "RtrimSet$(a$, set$)", RtrimSet$(a$, set$)
  16469.        PRINT "FilterOut$(a$, set$)", FilterOut$(a$, set$)
  16470.        PRINT "FilterIn$(a$, set$)", FilterIn$(a$, set$)
  16471.        PRINT
  16472.  
  16473.        a$ = "elephant"
  16474.        x$ = "alpha"
  16475.        y$ = "omega"
  16476.        PRINT "a$", , a$
  16477.        PRINT "x$", , x$
  16478.        PRINT "y$", , y$
  16479.        PRINT "BestMatch$(a$, x$, y$)", BestMatch$(a$, x$, y$)
  16480.        PRINT
  16481.  
  16482.        PRINT "Press any key to continue"
  16483.        DO
  16484.        LOOP UNTIL INKEY$ <> ""
  16485.  
  16486.        CLS
  16487.        a$ = "BEL"
  16488.        PRINT "a$", , a$
  16489.        PRINT "Ord%(a$)", , Ord%(a$)
  16490.        PRINT
  16491.  
  16492.        a$ = "This is a test"
  16493.        find$ = "s"
  16494.        substitute$ = "<s>"
  16495.        PRINT "a$", , , a$
  16496.        PRINT "find$", , , find$
  16497.        PRINT "substitute$", , , substitute$
  16498.        PRINT "Replace$(a$, find$, substitute$)", Replace$(a$, find$, substitut
  16499.        PRINT
  16500.  
  16501.        PRINT "a$", , a$
  16502.        PRINT "Lpad$(a$, 40)", , ":"; Lpad$(a$, 40); ":"
  16503.        PRINT "Rpad$(a$, 40)", , ":"; Rpad$(a$, 40); ":"
  16504.        PRINT "Center$(a$, 40)", ":"; Center$(a$, 40); ":"
  16505.        PRINT
  16506.  
  16507.        a$ = "a$ character" + STRING$(2, 9) + "count" + CHR$(9) + "is"
  16508.        PRINT a$; LEN(a$)
  16509.        PRINT "a$ = Detab$(a$, 8)"
  16510.        a$ = Detab$(a$, 8)
  16511.        PRINT a$; LEN(a$)
  16512.        PRINT "a$ = Entab$(a$, 8)"
  16513.        a$ = Entab$(a$, 8)
  16514.        PRINT a$; LEN(a$)
  16515.        PRINT
  16516.  
  16517.        PRINT "Press any key to continue"
  16518.        DO
  16519.        LOOP UNTIL INKEY$ <> ""
  16520.  
  16521.        CLS
  16522.        a$ = "You know this test string has vowels."
  16523.        x$ = "aeiou"
  16524.        y$ = "eioua"
  16525.        PRINT "a$", , a$
  16526.        PRINT "x$", , x$
  16527.        PRINT "y$", , y$
  16528.        PRINT "Translate$(a$, x$, y$)", Translate$(a$, x$, y$)
  16529.        PRINT
  16530.  
  16531.        a$ = "This is a test."
  16532.        b$ = Ascii2Ebcdic$(a$)
  16533.        c$ = Ebcdic2Ascii$(b$)
  16534.        PRINT "a$", , a$
  16535.        PRINT "b$ = Ascii2Ebcdic$(a$)", b$
  16536.        PRINT "c$ = Ebcdic2Ascii$(b$)", c$
  16537.        PRINT
  16538.  
  16539.        END
  16540.    ──────────────────────────────────────────────────────────────────────────
  16541.  
  16542.  
  16543.  Function: Ascii2Ebcdic$
  16544.  
  16545.    Converts a string of ASCII characters to EBCDIC equivalents.
  16546.  
  16547.    Almost all computers use the ASCII character set to define which byte
  16548.    represents which character. This standard makes it possible for computers,
  16549.    printers, plotters, and other equipment to communicate effectively.
  16550.    However, IBM's larger computers have long used the EBCDIC character set,
  16551.    an alternative way for computers and peripherals to communicate. If files
  16552.    are to be transferred to or from an IBM mainframe, it's necessary to
  16553.    translate character bytes between the two methods. This function, along
  16554.    with its counterpart Ebcdic2Ascii$, translates strings of characters
  16555.    between the ASCII character set and the EBCDIC character set.
  16556.  
  16557.    These functions and the BuildAEStrings subprogram share a pair of string
  16558.    variables, ascii$ and ebcdic$. The SHARED statement lets these two strings
  16559.    be accessed by each of these three routines while remaining invisible and
  16560.    unalterable to all other parts of a program.
  16561.  
  16562.    The BuildAEStrings subprogram is called only once, to build both the
  16563.    ascii$ and ebcdic$ translation strings the first time that the
  16564.    Ascii2Ebcdic$ or Ebcdic2Ascii$ function is called. All subsequent calls
  16565.    to these functions use these strings immediately, as the contents of the
  16566.    strings are preserved between calls.
  16567.  
  16568.    Refer to the BuildAEStrings subprogram for more information about how
  16569.    these two strings are built. Refer to the Translate$ function for more
  16570.    information about the character-by-character translation.
  16571.  
  16572.    ──────────────────────────────────────────────────────────────────────────
  16573.      ' ************************************************
  16574.      ' **  Name:          Ascii2Ebcdic$              **
  16575.      ' **  Type:          Function                   **
  16576.      ' **  Module:        STRINGS.BAS                **
  16577.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16578.      ' ************************************************
  16579.      '
  16580.      ' Returns a$ with each character translated from ASCII to EBCDIC.
  16581.      '
  16582.      ' EXAMPLE OF USE:  e$ = Ascii2Ebcdic$(a$)
  16583.      ' PARAMETERS:      a$         String of ASCII characters to be
  16584.      '                             converted
  16585.      ' VARIABLES:       ebcdic$    Table of translation characters
  16586.      '                  ascii$     Table of translation characters
  16587.      ' MODULE LEVEL
  16588.      '   DECLARATIONS:  DECLARE FUNCTION Ascii2Ebcdic$ (a$)
  16589.      '
  16590.        FUNCTION Ascii2Ebcdic$ (a$) STATIC
  16591.            SHARED ebcdic$, ascii$
  16592.            IF ebcdic$ = "" THEN
  16593.                BuildAEStrings
  16594.            END IF
  16595.            Ascii2Ebcdic$ = Translate$(a$, ascii$, ebcdic$)
  16596.        END FUNCTION
  16597.    ──────────────────────────────────────────────────────────────────────────
  16598.  
  16599.  
  16600.  Function: BestMatch$
  16601.  
  16602.    Compares two strings with a third and returns the string that most closely
  16603.    matches the third.
  16604.  
  16605.    Everybody's talking "artificial intelligence" these days. Programs capable
  16606.    of making decisions based on "fuzzy" facts are already being used for
  16607.    voice analysis, pattern matching, and other similar tasks. This function
  16608.    provides a way to make an educated guess as to the best pattern match when
  16609.    comparing two strings.
  16610.  
  16611.    The method of comparison used here scans substrings of the target string
  16612.    and checks for occurrences of these substrings in each of the other two
  16613.    strings. A score is kept for the number of substring matches found for
  16614.    each string. The score is weighted heavier for longer substring matches.
  16615.    For example, finding an occurrence of the substring "ABC" is worth 6
  16616.    points, while finding separate occurrences of "E," "F," and "G" is worth a
  16617.    total of only 3 points.
  16618.  
  16619.    When all substrings of the target string have been checked, the points are
  16620.    compared for each test string. The highest score wins, and that string is
  16621.    returned as the result.
  16622.  
  16623.    ──────────────────────────────────────────────────────────────────────────
  16624.      ' ************************************************
  16625.      ' **  Name:          BestMatch$                 **
  16626.      ' **  Type:          Function                   **
  16627.      ' **  Module:        STRINGS.BAS                **
  16628.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16629.      ' ************************************************
  16630.      '
  16631.      ' Returns either x$ or y$, whichever is a best match to a$.
  16632.      '
  16633.      ' EXAMPLE OF USE:  b$ = BestMatch$(a$, x$, y$)
  16634.      ' PARAMETERS:      a$          The string to be matched
  16635.      '                  x$          The first string to compare with a$
  16636.      '                  y$          The second string to compare with a$
  16637.      ' VARIABLES:       ua$         Uppercase working copy of a$
  16638.      '                  ux$         Uppercase working copy of x$
  16639.      '                  uy$         Uppercase working copy of y$
  16640.      '                  lena%       Length of a$
  16641.      '                  i%          Length of substrings of ua$
  16642.      '                  j%          Index into ua$
  16643.      '                  t$          Substrings of ua$
  16644.      '                  xscore%     Accumulated score for substring matches
  16645.      '                              found in ux$
  16646.      '                  yscore%     Accumulated score for substring matches
  16647.      '                              found in uy$
  16648.      ' MODULE LEVEL
  16649.      '   DECLARATIONS:  DECLARE FUNCTION BestMatch$ (a$, x$, y$)
  16650.      '
  16651.        FUNCTION BestMatch$ (a$, x$, y$) STATIC
  16652.            ua$ = UCASE$(a$)
  16653.            ux$ = UCASE$(x$)
  16654.            uy$ = UCASE$(y$)
  16655.            lena% = LEN(ua$)
  16656.            FOR i% = 1 TO lena%
  16657.                FOR j% = 1 TO lena% - i% + 1
  16658.                    t$ = MID$(ua$, j%, i%)
  16659.                    IF INSTR(ux$, t$) THEN
  16660.                        xscore% = xscore% + i% + i%
  16661.                    END IF
  16662.                    IF INSTR(uy$, t$) THEN
  16663.                        yscore% = yscore% + i% + i%
  16664.                    END IF
  16665.                NEXT j%
  16666.            NEXT i%
  16667.            IF xscore% > yscore% THEN
  16668.                BestMatch$ = x$
  16669.            ELSE
  16670.                BestMatch$ = y$
  16671.            END IF
  16672.        END FUNCTION
  16673.    ──────────────────────────────────────────────────────────────────────────
  16674.  
  16675.  
  16676.  Subprogram: BuildAEStrings
  16677.  
  16678.    Initializes the ASCII-EBCDIC translation table strings. This subprogram is
  16679.    called once per program run, by either the Ascii2Ebcdic$ or
  16680.    Ebcdic2Ascii$ function, when one is called first. Each function checks to
  16681.    see whether the shared strings, ascii$ and ebcdic$, are filled in or
  16682.    whether they are still null (empty) strings. If they are null, this
  16683.    subprogram is called to fill them in before they are used as character
  16684.    translation tables.
  16685.  
  16686.    The method used to fill in the strings can easily create strings
  16687.    containing any binary bytes. First, ebcdic$ is created as a string of
  16688.    hexadecimal characters, each pair of which represents a single byte. At
  16689.    this point, ebcdic$ is twice the desired length. The processing loop near
  16690.    the end of the function converts each pair of hexadecimal characters to
  16691.    the byte it represents and replaces the hexadecimal characters with these
  16692.    bytes. After all hexadecimal character pairs are converted, the STRINGS
  16693.  
  16694.    first half of ebcdic$ contains the desired string of bytes. The second
  16695.    half of ebcdic$ is deleted.
  16696.  
  16697.    The string variable ascii$ is filled in with binary byte values 0 through
  16698.    127. This string is built to be passed to the Translate$ function, which
  16699.    requires a string table for both lookup as well as replacement.
  16700.  
  16701.    ──────────────────────────────────────────────────────────────────────────
  16702.      ' ************************************************
  16703.      ' **  Name:          BuildAEStrings             **
  16704.      ' **  Type:          Subprogram                 **
  16705.      ' **  Module:        STRINGS.BAS                **
  16706.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16707.      ' ************************************************
  16708.      '
  16709.      ' Called by the Ascii2Ebcdic$ and Ebcdic2Ascii$
  16710.      ' functions to build the translation strings.
  16711.      ' This subprogram is called only once.
  16712.      '
  16713.      ' EXAMPLE OF USE:  Called automatically by either the Ascii2Ebcdic$ or
  16714.      '                  Ebcdic2Ascii$ function
  16715.      ' PARAMETERS:      ascii$     Shared by Ascii2Ebcdic$, Ebcdic2Ascii$, and
  16716.      '                             BuildAEStrings
  16717.      '                  ebcdic$    Shared by Ascii2Ebcdic$, Ebcdic2Ascii$, and
  16718.      '                             BuildAEStrings
  16719.      ' VARIABLES:       i%         Index into strings
  16720.      '                  byte%      Binary value of character byte
  16721.      ' MODULE LEVEL
  16722.      '   DECLARATIONS:  DECLARE SUB BuildAEStrings ()
  16723.      '
  16724.        SUB BuildAEStrings STATIC
  16725.            SHARED ebcdic$, ascii$
  16726.            ascii$ = SPACE$(128)
  16727.            ebcdic$ = ebcdic$ + "00010203372D2E2F1605250B0C0D0E0F"
  16728.            ebcdic$ = ebcdic$ + "101112133C3D322618193F271C1D1E1F"
  16729.            ebcdic$ = ebcdic$ + "404F7F7B5B6C507D4D5D5C4E6B604B61"
  16730.            ebcdic$ = ebcdic$ + "F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F"
  16731.            ebcdic$ = ebcdic$ + "7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6"
  16732.            ebcdic$ = ebcdic$ + "D7D8D9E2E3E4E5E6E7E8E94AE05A5F6D"
  16733.            ebcdic$ = ebcdic$ + "79818283848586878889919293949596"
  16734.            ebcdic$ = ebcdic$ + "979899A2A3A4A5A6A7A8A9C06AD0A107"
  16735.            FOR i% = 0 TO 127
  16736.                MID$(ascii$, i% + 1, 1) = CHR$(i%)
  16737.                byte% = VAL("&H" + MID$(ebcdic$, i% + i% + 1, 2))
  16738.                MID$(ebcdic$, i% + 1, 1) = CHR$(byte%)
  16739.            NEXT i%
  16740.            ebcdic$ = LEFT$(ebcdic$, 128)
  16741.        END SUB
  16742.    ──────────────────────────────────────────────────────────────────────────
  16743.  
  16744.  
  16745.  Function: Center$
  16746.  
  16747.    Returns a string of length n% by padding a$ with spaces on both ends.
  16748.  
  16749.    The original string is centered in the new string. If n% is less than the
  16750.    length of a$ (after any spaces are stripped from the ends), the string is
  16751.    returned with no spaces tacked on and with a length greater than n%.
  16752.  
  16753.    One obvious use for this function is centering titles and labels on a
  16754.    printed or displayed page.
  16755.  
  16756.    ──────────────────────────────────────────────────────────────────────────
  16757.      ' ************************************************
  16758.      ' **  Name:          Center$                    **
  16759.      ' **  Type:          Function                   **
  16760.      ' **  Module:        STRINGS.BAS                **
  16761.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16762.      ' ************************************************
  16763.      '
  16764.      ' Pads a$ with spaces on both ends until text is
  16765.      ' centered and the string length is n%.
  16766.      '
  16767.      ' EXAMPLE OF USE:  b$ = Center$(a$, n%)
  16768.      ' PARAMETERS:      a$         String of characters to be padded with spac
  16769.      '                  n%         Desired length of resulting string
  16770.      ' VARIABLES:       pad%       Number of spaces to pad at ends of string
  16771.      ' MODULE LEVEL
  16772.      '   DECLARATIONS:  DECLARE FUNCTION Center$ (a$, n%)
  16773.      '
  16774.        FUNCTION Center$ (a$, n%) STATIC
  16775.            a$ = LTRIM$(RTRIM$(a$))
  16776.            pad% = n% - LEN(a$)
  16777.            IF pad% > 0 THEN
  16778.                Center$ = SPACE$(pad% \ 2) + a$ + SPACE$(pad% - pad% \ 2)
  16779.            ELSE
  16780.                Center$ = a$
  16781.            END IF
  16782.        END FUNCTION
  16783.    ──────────────────────────────────────────────────────────────────────────
  16784.  
  16785.  
  16786.  Function: Detab$
  16787.  
  16788.    Replaces tab characters with the appropriate number of spaces.
  16789.  
  16790.    Tab characters are useful for forcing text alignment into predictable
  16791.    columns and for conserving space in text files. If you then need to
  16792.    exchange the tab characters for the equivalent number of spaces, this
  16793.    function lets you do so.
  16794.  
  16795.    Your computer display and (probably) your printer use a tab spacing
  16796.    constant of 8. For this reason, the most common value passed to this
  16797.    function for tabs% is 8. Spaces are inserted in a$ in place of tab
  16798.    characters to align the following characters into columns that are
  16799.    multiples of 8. Displaying or printing the string before and after it's
  16800.    processed by this function should result in exactly the same output.
  16801.  
  16802.    Also see Entab$, which performs exactly the opposite function.
  16803.  
  16804.    ──────────────────────────────────────────────────────────────────────────
  16805.      ' ************************************************
  16806.      ' **  Name           Detab$                     **
  16807.      ' **  Type:          Function                   **
  16808.      ' **  Module:        STRINGS.BAS                **
  16809.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16810.      ' ************************************************
  16811.      '
  16812.      ' Replaces all tab characters with spaces, using
  16813.      ' tabs% to determine proper alignment.
  16814.      '
  16815.      ' EXAMPLE OF USE:  b$ = Detab$(a$, tabs%)
  16816.      ' PARAMETERS:      a$           String with possible tab characters
  16817.      '                  tabs%        Tab spacing
  16818.      ' VARIABLES:       t$           Working copy of a$
  16819.      '                  tb$          Tab character
  16820.      '                  tp%          Pointer to position in t$ of a tab charac
  16821.      '                  sp$          Spaces to replace a given tab character
  16822.      ' MODULE LEVEL
  16823.      '   DECLARATIONS:  DECLARE FUNCTION Detab$ (a$, tabs%)
  16824.      '
  16825.        FUNCTION Detab$ (a$, tabs%) STATIC
  16826.            t$ = a$
  16827.            tb$ = CHR$(9)
  16828.            DO
  16829.                tp% = INSTR(t$, tb$)
  16830.                IF tp% THEN
  16831.                    Sp$ = SPACE$(tabs% - ((tp% - 1) MOD tabs%))
  16832.                    t$ = LEFT$(t$, tp% - 1) + Sp$ + MID$(t$, tp% + 1)
  16833.                END IF
  16834.            LOOP UNTIL tp% = 0
  16835.            Detab$ = t$
  16836.            t$ = ""
  16837.        END FUNCTION
  16838.    ──────────────────────────────────────────────────────────────────────────
  16839.  
  16840.  
  16841.  Function: Ebcdic2Ascii$
  16842.  
  16843.    Converts a string of EBCDIC characters to ASCII equivalents. This function
  16844.    performs the exact opposite of the Ascii2Ebcdic$ function.
  16845.  
  16846.    Refer to the Ascii2Ebcdic$ function for more information.
  16847.  
  16848.    ──────────────────────────────────────────────────────────────────────────
  16849.      ' ************************************************
  16850.      ' **  Name:          Ebcdic2Ascii$              **
  16851.      ' **  Type:          Function                   **
  16852.      ' **  Module:        STRINGS.BAS                **
  16853.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16854.      ' ************************************************
  16855.      '
  16856.      ' Returns a$ with each character translated from
  16857.      ' EBCDIC to ASCII.
  16858.      '
  16859.      ' EXAMPLE OF USE:  b$ = Ascii2Ebcdic$(a$)
  16860.      ' PARAMETERS:      a$          String of EBCDIC characters to be converte
  16861.      ' VARIABLES:       ebcdic$     Table of translation characters
  16862.      '                  ascii$      Table of translation characters
  16863.      ' MODULE LEVEL
  16864.      '   DECLARATIONS:  DECLARE FUNCTION Ebcdic2Ascii$ (e$)
  16865.      '
  16866.        FUNCTION Ebcdic2Ascii$ (e$) STATIC
  16867.            SHARED ebcdic$, ascii$
  16868.            IF ebcdic$ = "" THEN
  16869.                BuildAEStrings
  16870.            END IF
  16871.            Ebcdic2Ascii$ = Translate$(e$, ebcdic$, ascii$)
  16872.        END FUNCTION
  16873.    ──────────────────────────────────────────────────────────────────────────
  16874.  
  16875.  
  16876.  Function: Entab$
  16877.  
  16878.    Replaces spaces with tab characters wherever possible, providing a way to
  16879.    compress the size of a text file.
  16880.  
  16881.    For the opposite function, replacing tabs with appropriate numbers of
  16882.    spaces, see Detab$.
  16883.  
  16884.    ──────────────────────────────────────────────────────────────────────────
  16885.      ' ************************************************
  16886.      ' **  Name:          Entab$                     **
  16887.      ' **  Type:          Function                   **
  16888.      ' **  Module:        STRINGS.BAS                **
  16889.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16890.      ' ************************************************
  16891.      '
  16892.      ' Replaces groups of spaces, where possible, with
  16893.      ' tab characters, keeping the alignment indicated
  16894.      ' by the value of tabs%.
  16895.      '
  16896.      ' EXAMPLE OF USE:  b$ = Entab$(a$, tabs%)
  16897.      ' PARAMETERS:      a$            String with possible tab characters
  16898.      '                  tabs%         Tab spacing
  16899.      ' VARIABLES:       t$            Working copy of a$
  16900.      '                  tb$           Tab character
  16901.      '                  i%            Index into t$
  16902.      '                  k%            Count of spaces being replaced
  16903.      '                  j%            Index into t$
  16904.      ' MODULE LEVEL
  16905.      '   DECLARATIONS:  DECLARE FUNCTION Entab$ (a$, tabs%)
  16906.      '
  16907.        FUNCTION Entab$ (a$, tabs%) STATIC
  16908.            t$ = a$
  16909.            tb$ = CHR$(9)
  16910.            FOR i% = (LEN(t$) \ tabs%) * tabs% + 1 TO tabs% STEP -tabs%
  16911.                IF MID$(t$, i% - 1, 1) = " " THEN
  16912.                    k% = 0
  16913.                    FOR j% = 1 TO tabs%
  16914.                        IF MID$(t$, i% - j%, 1) <> " " THEN
  16915.                            k% = i% - j%
  16916.                            EXIT FOR
  16917.                        END IF
  16918.                    NEXT j%
  16919.                    IF k% = 0 THEN
  16920.                        k% = i% - tabs% - 1
  16921.                    END IF
  16922.                    t$ = LEFT$(t$, k%) + tb$ + MID$(t$, i%)
  16923.                END IF
  16924.            NEXT i%
  16925.            Entab$ = t$
  16926.            t$ = ""
  16927.        END FUNCTION
  16928.    ──────────────────────────────────────────────────────────────────────────
  16929.  
  16930.  
  16931.  Function: FilterIn$
  16932.  
  16933.    Filters a string, character by character, and removes any characters that
  16934.    are not in the designated set. FilterIn$("EXAMPLE", "AEIOU"), for example,
  16935.    returns the string EAE, because all characters, except uppercase vowels,
  16936.    are removed from EXAMPLE.
  16937.  
  16938.    To filter a string by removing characters listed in set$ (as opposed to
  16939.    removing all characters not in set$), see the FilterOut$ function.
  16940.  
  16941.    ──────────────────────────────────────────────────────────────────────────
  16942.      ' ************************************************
  16943.      ' **  Name:          FilterIn$                  **
  16944.      ' **  Type:          Function                   **
  16945.      ' **  Module:        STRINGS.BAS                **
  16946.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16947.      ' ************************************************
  16948.      '
  16949.      ' Returns a$ with all occurrences of any characters
  16950.      ' that are not in set$ deleted.
  16951.      '
  16952.      ' EXAMPLE OF USE:  b$ = FilterIn$(a$, set$)
  16953.      ' PARAMETERS:      a$          String to be processed
  16954.      '                  set$        Set of characters to be retained
  16955.      ' VARIABLES:       i%          Index into a$
  16956.      '                  j%          Count of characters retained
  16957.      '                  lena%       Length of a$
  16958.      '                  t$          Working string space
  16959.      '                  c$          Each character of a$
  16960.      ' MODULE LEVEL
  16961.      '   DECLARATIONS:  DECLARE FUNCTION FilterIn$ (a$, set$)
  16962.      '
  16963.        FUNCTION FilterIn$ (a$, set$) STATIC
  16964.            i% = 1
  16965.            j% = 0
  16966.            lena% = LEN(a$)
  16967.            t$ = a$
  16968.            DO UNTIL i% > lena%
  16969.                c$ = MID$(a$, i%, 1)
  16970.                IF INSTR(set$, c$) THEN
  16971.                    j% = j% + 1
  16972.                    MID$(t$, j%, 1) = c$
  16973.                END IF
  16974.                i% = i% + 1
  16975.            LOOP
  16976.            FilterIn$ = LEFT$(t$, j%)
  16977.            t$ = ""
  16978.        END FUNCTION
  16979.    ──────────────────────────────────────────────────────────────────────────
  16980.  
  16981.  
  16982.  Function: FilterOut$
  16983.  
  16984.    Filters a string, character by character, and removes any characters that
  16985.    are listed in the designated set. FilterOut$("EXAMPLE", "AEIOU"),
  16986.    STRINGSfor example, returns the string XMPL, because all uppercase vowels
  16987.    are removed from EXAMPLE.
  16988.  
  16989.    To filter a string by removing characters not listed in set$ (as opposed
  16990.    to removing all characters found in set$), see the FilterIn$ function.
  16991.  
  16992.    ──────────────────────────────────────────────────────────────────────────
  16993.      ' ************************************************
  16994.      ' **  Name:          FilterOut$                 **
  16995.      ' **  Type:          Function                   **
  16996.      ' **  Module:        STRINGS.BAS                **
  16997.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  16998.      ' ************************************************
  16999.      '
  17000.      ' Returns a$ with all occurrences of any characters
  17001.      ' from set$ deleted.
  17002.      '
  17003.      ' EXAMPLE OF USE:  b$ = FilterOut$(a$, set$)
  17004.      ' PARAMETERS:      a$           String to be processed
  17005.      '                  set$         Set of characters to be retained
  17006.      ' VARIABLES:       i%           Index into a$
  17007.      '                  j%           Count of characters retained
  17008.      '                  lena%        Length of a$
  17009.      '                  t$           Working string space
  17010.      '                  c$           Each character of a$
  17011.      ' MODULE LEVEL
  17012.      '   DECLARATIONS:  DECLARE FUNCTION FilterOut$ (a$, set$)
  17013.      '
  17014.        FUNCTION FilterOut$ (a$, set$) STATIC
  17015.            i% = 1
  17016.            j% = 0
  17017.            lena% = LEN(a$)
  17018.            t$ = a$
  17019.            DO UNTIL i% > lena%
  17020.                c$ = MID$(a$, i%, 1)
  17021.                IF INSTR(set$, c$) = 0 THEN
  17022.                    j% = j% + 1
  17023.                    MID$(t$, j%, 1) = c$
  17024.                END IF
  17025.                i% = i% + 1
  17026.            LOOP
  17027.            FilterOut$ = LEFT$(t$, j%)
  17028.            t$ = ""
  17029.        END FUNCTION
  17030.    ──────────────────────────────────────────────────────────────────────────
  17031.  
  17032.  
  17033.  Function: Lpad$
  17034.  
  17035.    Returns a left-justified string of n% characters by shifting a$ to the
  17036.    left and adding space characters on the right.
  17037.  
  17038.    This function actually does an amazing amount of work for only one program
  17039.    line. First, the string passed as parameter a$ has all spaces removed from
  17040.    its left, the final goal being to left justify the string.
  17041.  
  17042.    The desired string length is n%. To guarantee that you have at least n%
  17043.    characters to work with, n% space characters are added to the right of the
  17044.    string. Most likely, the string is now longer than desired. So, the LEFT$
  17045.    function returns the first n% characters from the string, finishing the
  17046.    desired processing of a$ and assigning the result to Lpad$, the name of
  17047.    the function.
  17048.  
  17049.    See Rpad$ for a similar function.
  17050.  
  17051.    ──────────────────────────────────────────────────────────────────────────
  17052.      ' ************************************************
  17053.      ' **  Name:          Lpad$                      **
  17054.      ' **  Type:          Function                   **
  17055.      ' **  Module:        STRINGS.BAS                **
  17056.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17057.      ' ************************************************
  17058.      '
  17059.      ' Returns a string of length n%, with a$ left justified
  17060.      ' and padded on the right with spaces.
  17061.      '
  17062.      ' EXAMPLE OF USE:  b$ = Lpad$(a$, n%)
  17063.      ' PARAMETERS:      a$          String to be left justified and padded
  17064.      '                  n%          Length of string result
  17065.      ' VARIABLES:       (none)
  17066.      ' MODULE LEVEL
  17067.      '   DECLARATIONS:  DECLARE FUNCTION Lpad$ (a$, n%)
  17068.      '
  17069.        FUNCTION Lpad$ (a$, n%) STATIC
  17070.            Lpad$ = LEFT$(LTRIM$(a$) + SPACE$(n%), n%)
  17071.        END FUNCTION
  17072.    ──────────────────────────────────────────────────────────────────────────
  17073.  
  17074.  
  17075.  Function: LtrimSet$
  17076.  
  17077.    Trims characters in set$ from the left of a$ until a character is found
  17078.    that is not in set$.
  17079.  
  17080.    STRINGS
  17081.  
  17082.    The QuickBASIC LTRIM$() function removes space characters from the end of
  17083.    a string. This function goes a step further and lets you remove any of
  17084.    several characters from the left of a string. For example,
  17085.    LtrimSet$("EXAMPLE", "AXE") returns MPLE.
  17086.  
  17087.    One use for this function is to remove tabs and spaces from the left of a
  17088.    string.
  17089.  
  17090.    See RtrimSet$ for a similar function.
  17091.  
  17092.    ──────────────────────────────────────────────────────────────────────────
  17093.      ' ************************************************
  17094.      ' **  Name:          LtrimSet$                  **
  17095.      ' **  Type:          Function                   **
  17096.      ' **  Module:        STRINGS.BAS                **
  17097.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17098.      ' ************************************************
  17099.      '
  17100.      ' Trims occurrences of any characters in set$
  17101.      ' from the left of a$.
  17102.      '
  17103.      ' EXAMPLE OF USE:  b$ = LtrimSet$(a$, set$)
  17104.      ' PARAMETERS:      a$           String to be trimmed
  17105.      '                  set$         Set of characters to be trimmed
  17106.      ' VARIABLES:       i%           Index into a$
  17107.      ' MODULE LEVEL
  17108.      '   DECLARATIONS:  DECLARE FUNCTION LtrimSet$ (a$, set$)
  17109.      '
  17110.        FUNCTION LtrimSet$ (a$, set$) STATIC
  17111.            IF a$ <> "" THEN
  17112.                FOR i% = 1 TO LEN(a$)
  17113.                    IF INSTR(set$, MID$(a$, i%, 1)) = 0 THEN
  17114.                        LtrimSet$ = MID$(a$, i%)
  17115.                        EXIT FUNCTION
  17116.                    END IF
  17117.                NEXT i%
  17118.            END IF
  17119.            LtrimSet$ = ""
  17120.        END FUNCTION
  17121.    ──────────────────────────────────────────────────────────────────────────
  17122.  
  17123.  
  17124.  Function: Ord%
  17125.  
  17126.    Returns the byte number defined by ANSI standard mnemonics.
  17127.  
  17128.    This function interprets ANSI standard mnemonics for control characters
  17129.    and returns the numeric value of the byte the mnemonics represent (the
  17130.    ordinal of the mnemonic). Ord%("BEL"), for example, returns 7, the byte
  17131.    number for the bell character. (Recall that PRINT CHR$(7) causes your
  17132.    computer to beep.)
  17133.  
  17134.    Other common control-character mnemonics include CR (carriage return), LF
  17135.    (line feed), FF (form feed), and NUL (the zero byte value). Many others
  17136.    are available, however, including mnemonics for the lowercase alphabetic
  17137.    characters.
  17138.  
  17139.    ──────────────────────────────────────────────────────────────────────────
  17140.      ' ************************************************
  17141.      ' **  Name:          Ord%                       **
  17142.      ' **  Type:          Function                   **
  17143.      ' **  Module:        STRINGS.BAS                **
  17144.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17145.      ' ************************************************
  17146.      '
  17147.      ' Similar to ASC() function; returns
  17148.      ' numeric byte values for the ANSI standard
  17149.      ' mnemonics for control characters.
  17150.      '
  17151.      ' EXAMPLE OF USE:  byte% = Ord%(a$)
  17152.      ' PARAMETERS:      a$          ANSI standard character mnemonic string
  17153.      ' VARIABLES:      (none)
  17154.      ' MODULE LEVEL
  17155.      '   DECLARATIONS:  DECLARE FUNCTION Ord% (a$)
  17156.      '
  17157.        FUNCTION Ord% (a$) STATIC
  17158.            SELECT CASE UCASE$(a$)
  17159.            CASE "NUL"              'Null
  17160.                Ord% = 0
  17161.            CASE "SOH"              'Start of heading
  17162.                Ord% = 1
  17163.            CASE "STX"              'Start of text
  17164.                Ord% = 2
  17165.            CASE "ETX"              'End of text
  17166.                Ord% = 3
  17167.            CASE "EOT"              'End of transmission
  17168.                Ord% = 4
  17169.            CASE "ENQ"              'Enquiry
  17170.                Ord% = 5
  17171.            CASE "ACK"              'Acknowledge
  17172.                Ord% = 6
  17173.            CASE "BEL"              'Bell
  17174.                Ord% = 7
  17175.            CASE "BS"               'Backspace
  17176.                Ord% = 8
  17177.            CASE "HT"               'Horizontal tab
  17178.                Ord% = 9
  17179.            CASE "LF"               'Line feed
  17180.                Ord% = 10
  17181.            CASE "VT"               'Vertical tab
  17182.                Ord% = 11
  17183.            CASE "FF"               'Form feed
  17184.                Ord% = 12
  17185.            CASE "CR"               'Carriage return
  17186.                Ord% = 13
  17187.            CASE "SO"               'Shift out
  17188.                Ord% = 14
  17189.            CASE "SI"               'Shift in
  17190.                Ord% = 15
  17191.            CASE "DLE"              'Data link escape
  17192.                Ord% = 16
  17193.            CASE "DC1"              'Device control 1
  17194.                Ord% = 17
  17195.            CASE "DC2"              'Device control 2
  17196.                Ord% = 18
  17197.            CASE "DC3"              'Device control 3
  17198.                Ord% = 19
  17199.            CASE "DC4"              'Device control 4
  17200.                Ord% = 20
  17201.            CASE "NAK"              'Negative acknowledge
  17202.                Ord% = 21
  17203.            CASE "SYN"              'Synchronous idle
  17204.                Ord% = 22
  17205.            CASE "ETB"              'End of transmission block
  17206.                Ord% = 23
  17207.            CASE "CAN"              'Cancel
  17208.                Ord% = 24
  17209.            CASE "EM"               'End of medium
  17210.                Ord% = 25
  17211.            CASE "SUB"              'Substitute
  17212.                Ord% = 26
  17213.            CASE "ESC"              'Escape
  17214.                Ord% = 27
  17215.            CASE "FS"               'File separator
  17216.                Ord% = 28
  17217.            CASE "GS"               'Group separator
  17218.                Ord% = 29
  17219.            CASE "RS"               'Record separator
  17220.                Ord% = 30
  17221.            CASE "US"               'Unit separator
  17222.                Ord% = 31
  17223.            CASE "SP"               'Space
  17224.                Ord% = 32
  17225.            CASE "UND"              'Underline
  17226.                Ord% = 95
  17227.            CASE "GRA"              'Grave accent
  17228.                Ord% = 96
  17229.            CASE "LCA"              'Lowercase a
  17230.                Ord% = 97
  17231.            CASE "LCB"              'Lowercase b
  17232.                Ord% = 98
  17233.            CASE "LCC"              'Lowercase c
  17234.                Ord% = 99
  17235.            CASE "LCD"              'Lowercase d
  17236.                Ord% = 100
  17237.            CASE "LCE"              'Lowercase e
  17238.                Ord% = 101
  17239.            CASE "LCF"              'Lowercase f
  17240.                Ord% = 102
  17241.            CASE "LCG"              'Lowercase g
  17242.                Ord% = 103
  17243.            CASE "LCH"              'Lowercase h
  17244.                Ord% = 104
  17245.            CASE "LCI"              'Lowercase i
  17246.                Ord% = 105
  17247.            CASE "LCJ"              'Lowercase j
  17248.                Ord% = 106
  17249.            CASE "LCK"              'Lowercase k
  17250.                Ord% = 107
  17251.            CASE "LCL"              'Lowercase l
  17252.                Ord% = 108
  17253.            CASE "LCM"              'Lowercase m
  17254.                Ord% = 109
  17255.            CASE "LCN"              'Lowercase n
  17256.                Ord% = 110
  17257.            CASE "LCO"              'Lowercase o
  17258.                Ord% = 111
  17259.            CASE "LCP"              'Lowercase p
  17260.                Ord% = 112
  17261.            CASE "LCQ"              'Lowercase q
  17262.                Ord% = 113
  17263.            CASE "LCR"              'Lowercase r
  17264.                Ord% = 114
  17265.            CASE "LCS"              'Lowercase s
  17266.                Ord% = 115
  17267.            CASE "LCT"              'Lowercase t
  17268.                Ord% = 116
  17269.            CASE "LCU"              'Lowercase u
  17270.                Ord% = 117
  17271.            CASE "LCV"              'Lowercase v
  17272.                Ord% = 118
  17273.            CASE "LCW"              'Lowercase w
  17274.                Ord% = 119
  17275.            CASE "LCX"              'Lowercase x
  17276.                Ord% = 120
  17277.            CASE "LCY"              'Lowercase y
  17278.                Ord% = 121
  17279.            CASE "LCZ"              'Lowercase z
  17280.                Ord% = 122
  17281.            CASE "LBR"              'Left brace
  17282.                Ord% = 123
  17283.            CASE "VLN"              'Vertical line
  17284.                Ord% = 124
  17285.            CASE "RBR"              'Right brace
  17286.                Ord% = 125
  17287.            CASE "TIL"              'Tilde
  17288.                Ord% = 126
  17289.            CASE "DEL"              'Delete
  17290.                Ord% = 127
  17291.            CASE ELSE               'Not ANSI Standard ORD mnemonic
  17292.                Ord% = -1
  17293.            END SELECT
  17294.        END FUNCTION
  17295.    ──────────────────────────────────────────────────────────────────────────
  17296.  
  17297.  
  17298.  Function: Repeat$
  17299.  
  17300.    Returns the string result of concatenating n% copies of a$. If the length
  17301.    of the result is less than 0 or greater than 32767, an error message is
  17302.    displayed, and the program terminates.
  17303.  
  17304.    To create a string of 80 spaces, you can use the QuickBASIC function
  17305.    SPACE$(80). To create a string of 80 equal signs, you can use STRING$(80,
  17306.    "="). But how can you create an 80-character string made up of 40
  17307.    repetitions of "+-"? The Repeat$ function lets you do so. Repeat$("+-",
  17308.    40) would do the trick.
  17309.  
  17310.    At first glance, this function looks like more code than is needed.
  17311.    Consider the short function on the following page that returns the same
  17312.    result.
  17313.  
  17314.  
  17315.      FUNCTION SlowRepeat$ (a$, n%) STATIC
  17316.          x$ = ""
  17317.          FOR i% = 1 to n%
  17318.             x$ = x$ + a$
  17319.          NEXT i%
  17320.          SlowRepeat$ = x$
  17321.      END FUNCTION
  17322.  
  17323.    In tests of operating speed, this shorter function often ran about 10
  17324.    times slower than did the Repeat$ function! The difference is in how the
  17325.    string space is handled.
  17326.  
  17327.    SlowRepeat$ performs much string-manipulation overhead for each n%
  17328.    repetition of a$. In particular, the statement x$ = x$ + a$ creates
  17329.    working copies of x$ and a$ in the string workspace for each iteration. As
  17330.    x$ becomes larger, this shuffling of strings begins to bog down the
  17331.    function, even though QuickBASIC performs these functions efficiently.
  17332.  
  17333.    The Repeat$ function avoids much of this string-manipulation overhead by
  17334.    assigning string results to the MID$ of a large string (t$) that was
  17335.    created only once. First, t$ is created as a string of spaces long enough
  17336.    to hold all n% copies of a$. Each copy of a$ is then assigned to the
  17337.    appropriate substring location in t$ by use of the MID$ statement.
  17338.  
  17339.    This technique can often be used to speed up other string manipulations.
  17340.    The difference in speed is often insignificant, except in cases where a
  17341.    large number of string operations are performed.
  17342.  
  17343.    ──────────────────────────────────────────────────────────────────────────
  17344.      ' ************************************************
  17345.      ' **  Name:          Repeat$                    **
  17346.      ' **  Type:          Function                   **
  17347.      ' **  Module:        STRINGS.BAS                **
  17348.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17349.      ' ************************************************
  17350.      '
  17351.      ' Returns a string formed by concatenating n%
  17352.      ' copies of a$ together.
  17353.      '
  17354.      ' EXAMPLE OF USE:  b$ = Repeat$(a$, n%)
  17355.      ' PARAMETERS:      a$           String to be repeated
  17356.      '                  n%           Number of copies of a$ to concatenate
  17357.      ' VARIABLES:       lena%        Length of a$
  17358.      '                  lent&        Length of result
  17359.      '                  t$           Work space for building result
  17360.      '                  ndx%         Index into t$
  17361.      ' MODULE LEVEL
  17362.      '   DECLARATIONS:  DECLARE FUNCTION Repeat$ (a$, n%)
  17363.      '
  17364.        FUNCTION Repeat$ (a$, n%) STATIC
  17365.            lena% = LEN(a$)
  17366.            lent& = n% * lena%
  17367.            IF lent& < 0 OR lent& > 32767 THEN
  17368.                PRINT "ERROR: Repeat$ - Negative repetition, or result too long
  17369.                SYSTEM
  17370.            ELSEIF lent& = 0 THEN
  17371.                Repeat$ = ""
  17372.            ELSE
  17373.                t$ = SPACE$(lent&)
  17374.                ndx% = 1
  17375.                DO
  17376.                    MID$(t$, ndx%, lena%) = a$
  17377.                    ndx% = ndx% + lena%
  17378.                LOOP UNTIL ndx% > lent&
  17379.                Repeat$ = t$
  17380.                t$ = ""
  17381.            END IF
  17382.        END FUNCTION
  17383.    ──────────────────────────────────────────────────────────────────────────
  17384.  
  17385.  
  17386.  Function: Replace$
  17387.  
  17388.    Replaces all occurrences of find$ in a$ with substitute$.
  17389.  
  17390.    One common function provided by text editors and word processors is the
  17391.    ability to globally replace occurrences of character strings with other
  17392.    character strings. This function performs such a global replacement in a
  17393.    single string. By using this function repeatedly, you can globally edit
  17394.    entire files of strings.
  17395.  
  17396.    For example, Replace$ ("This is a test", "i", "ii") returns the string
  17397.    Thiis iis a test.
  17398.  
  17399.    ──────────────────────────────────────────────────────────────────────────
  17400.      ' ************************************************
  17401.      ' **  Name:          Replace$                   **
  17402.      ' **  Type:          Function                   **
  17403.      ' **  Module:        STRINGS.BAS                **
  17404.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17405.      ' ************************************************
  17406.      '
  17407.      ' Replaces all occurrences of find$ in a$ with substitute$.
  17408.      '
  17409.      ' EXAMPLE OF USE:  b$ = Replace$(a$, find$, substitute$)
  17410.      ' PARAMETERS:      a$            String to make substring replacements in
  17411.      '                  find$         Substring to be searched for
  17412.      '                  substitutes$  String for replacing the found
  17413.      '                                substrings
  17414.      ' VARIABLES:       t$            Working copy of a$
  17415.      '                  lenf%         Length of find$
  17416.      '                  lens%         Length of substitute$
  17417.      '                  i%            Index into a$, pointing at substrings
  17418.      ' MODULE LEVEL
  17419.      '   DECLARATIONS:  DECLARE FUNCTION Replace$ (a$, find$, substitute$)
  17420.      '
  17421.        FUNCTION Replace$ (a$, find$, substitute$) STATIC
  17422.            t$ = a$
  17423.            lenf% = LEN(find$)
  17424.            lens% = LEN(substitute$)
  17425.            i% = 1
  17426.            DO
  17427.                i% = INSTR(i%, t$, find$)
  17428.                IF i% = 0 THEN
  17429.                    EXIT DO
  17430.                END IF
  17431.                t$ = LEFT$(t$, i% - 1) + substitute$ + MID$(t$, i% + lenf%)
  17432.                i% = i% + lens%
  17433.            LOOP
  17434.            Replace$ = t$
  17435.            t$ = ""
  17436.        END FUNCTION
  17437.    ──────────────────────────────────────────────────────────────────────────
  17438.  
  17439.  
  17440.  Function: Reverse$
  17441.  
  17442.    Quickly reverses the order of all characters in a string. For example,
  17443.    Reverse$("QuickBASIC") returns CISABkciuQ.
  17444.  
  17445.    ──────────────────────────────────────────────────────────────────────────
  17446.      ' ************************************************
  17447.      ' **  Name:          Reverse$                   **
  17448.      ' **  Type:          Function                   **
  17449.      ' **  Module:        STRINGS.BAS                **
  17450.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17451.      ' ************************************************
  17452.      '
  17453.      ' Reverses the order of all characters in a$.
  17454.      '
  17455.      ' EXAMPLE OF USE:  b$ = Reverse$(a$)
  17456.      ' PARAMETERS:      a$         String to be processed
  17457.      ' VARIABLES:       n%         Length of the string
  17458.      '                  r$         Working string space
  17459.      '                  i%         Index into the string
  17460.      ' MODULE LEVEL
  17461.      '   DECLARATIONS:  DECLARE FUNCTION Reverse$ (a$)
  17462.      '
  17463.        FUNCTION Reverse$ (a$) STATIC
  17464.            n% = LEN(a$)
  17465.            r$ = a$
  17466.            FOR i% = 1 TO n%
  17467.                MID$(r$, i%, 1) = MID$(a$, n% - i% + 1, 1)
  17468.            NEXT i%
  17469.            Reverse$ = r$
  17470.            r$ = ""
  17471.        END FUNCTION
  17472.    ──────────────────────────────────────────────────────────────────────────
  17473.  
  17474.  
  17475.  Function: ReverseCase$
  17476.  
  17477.    Changes the case of all alphabetical characters in a passed string.
  17478.    Nonalphabetic characters are left undisturbed.
  17479.  
  17480.    Some text editors can change the case of characters from the current
  17481.    cursor location to the end of the line. This function was designed with
  17482.    that concept in mind. ReverseCase$("Testing 1,2,3"), for example, returns
  17483.    tESTING 1,2,3.
  17484.  
  17485.    ──────────────────────────────────────────────────────────────────────────
  17486.      ' ************************************************
  17487.      ' **  Name:          ReverseCase$               **
  17488.      ' **  Type:          Function                   **
  17489.      ' **  Module:        STRINGS.BAS                **
  17490.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17491.      ' ************************************************
  17492.      '
  17493.      ' Changes all lowercase characters to uppercase
  17494.      ' and all uppercase characters to lowercase.
  17495.      '
  17496.      ' EXAMPLE OF USE:  b$ = ReverseCase$(a$)
  17497.      ' PARAMETERS:      a$         String to be processed
  17498.      ' VARIABLES:       r$         Working copy of a$
  17499.      '                  i%         Index into r$
  17500.      '                  t$         Character from middle of a$
  17501.      ' MODULE LEVEL
  17502.      '   DECLARATIONS:  DECLARE FUNCTION ReverseCase$ (a$)
  17503.      '
  17504.        FUNCTION ReverseCase$ (a$) STATIC
  17505.            r$ = a$
  17506.            FOR i% = 1 TO LEN(a$)
  17507.                t$ = MID$(a$, i%, 1)
  17508.                IF LCASE$(t$) <> t$ THEN
  17509.                    MID$(r$, i%, 1) = LCASE$(t$)
  17510.                ELSE
  17511.                    MID$(r$, i%, 1) = UCASE$(t$)
  17512.                END IF
  17513.            NEXT i%
  17514.            ReverseCase$ = r$
  17515.            r$ = ""
  17516.        END FUNCTION
  17517.    ──────────────────────────────────────────────────────────────────────────
  17518.  
  17519.  
  17520.  Function: Rpad$
  17521.  
  17522.    Returns a right-justified string of n% characters by shifting a$ to the
  17523.    right as far as possible and adding space characters on the left.
  17524.  
  17525.    See Lpad$ for a similar function.
  17526.  
  17527.    ──────────────────────────────────────────────────────────────────────────
  17528.      ' ************************************************
  17529.      ' **  Name:          Rpad$                      **
  17530.      ' **  Type:          Function                   **
  17531.      ' **  Module:        STRINGS.BAS                **
  17532.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17533.      ' ************************************************
  17534.      '
  17535.      ' Returns string of length n%, with a$ right justified
  17536.      ' and padded on the left with spaces.
  17537.      '
  17538.      ' EXAMPLE OF USE:  b$ = Rpad$(a$, n%)
  17539.      ' PARAMETERS:      a$           String to be right justified and padded
  17540.      '                  n%           Length of string result
  17541.      ' VARIABLES:       (none)
  17542.      ' MODULE LEVEL
  17543.      '   DECLARATIONS:  DECLARE FUNCTION Rpad$ (a$, n%)
  17544.      '
  17545.        FUNCTION Rpad$ (a$, n%) STATIC
  17546.            Rpad$ = RIGHT$(SPACE$(n%) + RTRIM$(a$), n%)
  17547.        END FUNCTION
  17548.    ──────────────────────────────────────────────────────────────────────────
  17549.  
  17550.  
  17551.  Function: RtrimSet$
  17552.  
  17553.    Trims characters in set$ from the right of a$ until a character is found
  17554.    that is not in set$.
  17555.  
  17556.    The QuickBASIC RTRIM$() function removes space characters from the right
  17557.    of a string. This function goes a step further and lets you remove any of
  17558.    several characters from the right of a string. For example,
  17559.    RtrimSet$("EXAMPLE", "LEAVE") returns EXAMP.
  17560.  
  17561.    One use for this function is to remove tabs and spaces from the right of a
  17562.    string.
  17563.  
  17564.    See LtrimSet$ for a similar function.
  17565.  
  17566.    ──────────────────────────────────────────────────────────────────────────
  17567.      ' ************************************************
  17568.      ' **  Name:          RtrimSet$                  **
  17569.      ' **  Type:          Function                   **
  17570.      ' **  Module:        STRINGS.BAS                **
  17571.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17572.      ' ************************************************
  17573.      '
  17574.      ' Trims occurrences of any characters in set$
  17575.      ' from the right of a$.
  17576.      '
  17577.      ' EXAMPLE OF USE:  b$ = LtrimSet$(a$, set$)
  17578.      ' PARAMETERS:      a$           String to be trimmed
  17579.      '                  set$         Set of characters to be trimmed
  17580.      ' VARIABLES:       i%           Index into a$
  17581.      ' MODULE LEVEL
  17582.      '   DECLARATIONS:  DECLARE FUNCTION RtrimSet$ (a$, set$)
  17583.      '
  17584.        FUNCTION RtrimSet$ (a$, set$) STATIC
  17585.            IF a$ <> "" THEN
  17586.                FOR i% = LEN(a$) TO 1 STEP -1
  17587.                    IF INSTR(set$, MID$(a$, i%, 1)) = 0 THEN
  17588.                        RtrimSet$ = LEFT$(a$, i%)
  17589.                        EXIT FUNCTION
  17590.                    END IF
  17591.                NEXT i%
  17592.            END IF
  17593.            RtrimSet$ = ""
  17594.        END FUNCTION
  17595.    ──────────────────────────────────────────────────────────────────────────
  17596.  
  17597.  
  17598.  Function: Translate$
  17599.  
  17600.    Performs a table-lookup translation of the characters in a$. Each
  17601.    character in a$ is searched for in f$. If found, the character is replaced
  17602.    by the character located in the same position in t$. Take a look at a
  17603.    simple example to help clarify the explanation.
  17604.  
  17605.    Translate$("EXAMPLE", "ABCDE", "vwxyz") returns zXvMPLz. The first
  17606.    character of "EXAMPLE" is found in the fifth character position of
  17607.    "ABCDE," so it is replaced with the fifth character of "vwxyz." (The "E"
  17608.    is replaced with a "z.") Then each remaining character in "EXAMPLE" is
  17609.    searched for and replaced in the same way.
  17610.  
  17611.    The Ascii2Ebcdic$ and Ebcdic2Ascii$ functions call this function to
  17612.    translate characters from one standard set to the other.
  17613.  
  17614.    ──────────────────────────────────────────────────────────────────────────
  17615.      ' ************************************************
  17616.      ' **  Name:          Translate$                 **
  17617.      ' **  Type:          Function                   **
  17618.      ' **  Module:        STRINGS.BAS                **
  17619.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17620.      ' ************************************************
  17621.      '
  17622.      ' Returns a$ with each character translated from
  17623.      ' f$ to t$.  If a character from a$ is found in f$,
  17624.      ' it is replaced with the character located
  17625.      ' in the same position in t$.
  17626.      '
  17627.      ' EXAMPLE OF USE:  b$ = Translate$ (a$, f$, t$)
  17628.      ' PARAMETERS:      a$         String to be translated
  17629.      '                  f$         Table of lookup characters
  17630.      '                  t$         Table of replacement characters
  17631.      ' VARIABLES:       ta$        Working copy of a$
  17632.      '                  lena%      Length of a$
  17633.      '                  lenf%      Length of f$
  17634.      '                  lent%      Length of t$
  17635.      '                  i%         Index into ta$
  17636.      '                  ptr%       Pointer into f$
  17637.      ' MODULE LEVEL
  17638.      '   DECLARATIONS:  DECLARE FUNCTION Translate$ (a$, f$, t$)
  17639.      '
  17640.        FUNCTION Translate$ (a$, f$, t$) STATIC
  17641.            ta$ = a$
  17642.            lena% = LEN(ta$)
  17643.            lenf% = LEN(f$)
  17644.            lent% = LEN(t$)
  17645.            IF lena% > 0 AND lenf% > 0 AND lent% > 0 THEN
  17646.                FOR i% = 1 TO lena%
  17647.                    ptr% = INSTR(f$, MID$(ta$, i%, 1))
  17648.                    IF ptr% THEN
  17649.                        MID$(ta$, i%, 1) = MID$(t$, ptr%, 1)
  17650.                    END IF
  17651.                NEXT i%
  17652.            END IF
  17653.            Translate$ = ta$
  17654.            ta$ = ""
  17655.        END FUNCTION
  17656.    ──────────────────────────────────────────────────────────────────────────
  17657.  
  17658.  
  17659.  
  17660.  ────────────────────────────────────────────────────────────────────────────
  17661.  TRIANGLE
  17662.  
  17663.    The TRIANGLE toolbox is a collection of analytical geometry functions and
  17664.    subprograms for calculating parts of triangles.
  17665.  
  17666.    The demonstration module-level code of this toolbox provides a useful
  17667.    triangle calculator utility.
  17668.  
  17669.    Run the program, and enter the known sides and/or angles of a triangle
  17670.    when prompted. If it's possible to calculate the remaining sides and
  17671.    angles, the program does so and then displays the results.
  17672.  
  17673.    Name                          Type   Description
  17674.    ──────────────────────────────────────────────────────────────────────────
  17675.    TRIANGLE.BAS                        Demo module
  17676.    Deg2Rad#                     Func   Converts degree angular units to
  17677.                                         radians
  17678.    Rad2Deg#                     Func   Converts radian angular units to
  17679.                                         degrees
  17680.    Triangle                     Sub    Calculates sides and angles of
  17681.                                         triangle
  17682.    TriangleArea#                Func   Calculates area of triangle from 3
  17683.                                         sides
  17684.    ──────────────────────────────────────────────────────────────────────────
  17685.  
  17686.  
  17687.  Demo Module: TRIANGLE
  17688.  
  17689.    ──────────────────────────────────────────────────────────────────────────
  17690.      ' ************************************************
  17691.      ' **  Name:          TRIANGLE                   **
  17692.      ' **  Type:          Toolbox                    **
  17693.      ' **  Module:        TRIANGLE.BAS               **
  17694.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17695.      ' ************************************************
  17696.      '
  17697.      ' USAGE:          No command line parameters
  17698.      ' REQUIREMENTS:   CGA
  17699.      ' .MAK FILE:      TRIANGLE.BAS
  17700.      '                 QCALMATH.BAS
  17701.      ' PARAMETERS:     (none)
  17702.      ' VARIABLES:      sA$        User input of side a
  17703.      '                 sB$        User input of side b
  17704.      '                 sC$        User input of side c
  17705.      '                 aA$        User input of angle A
  17706.      '                 aB$        User input of angle B
  17707.      '                 aC$        User input of angle C
  17708.      '                 sA#        Side A
  17709.      '                 sB#        Side B
  17710.      '                 sC#        Side C
  17711.      '                 aA#        Angle A
  17712.      '                 aB#        Angle B
  17713.      '                 aC#        Angle C
  17714.  
  17715.      ' Functions
  17716.        DECLARE FUNCTION Deg2Rad# (deg#)
  17717.        DECLARE FUNCTION Rad2Deg# (rad#)
  17718.        DECLARE FUNCTION ArcCosine# (x#)
  17719.        DECLARE FUNCTION ArcSine# (x#)
  17720.        DECLARE FUNCTION TriangleArea# (sA#, sB#, sC#)
  17721.  
  17722.      ' Subprograms
  17723.        DECLARE SUB Triangle (sA#, sB#, sC#, aA#, aB#, aC#)
  17724.  
  17725.      ' Initialization
  17726.        SCREEN 2
  17727.        CLS
  17728.        PRINT "TRIANGLE"
  17729.  
  17730.      ' Draw a representative triangle
  17731.        WINDOW (0, 0)-(1, 1)
  17732.        LINE (.3, .7)-(.8, .7)
  17733.        LINE -(.4, 1)
  17734.        LINE -(.3, .7)
  17735.  
  17736.      ' Label the triangle sides
  17737.        LOCATE 4, 26
  17738.        PRINT "a"
  17739.        LOCATE 3, 48
  17740.        PRINT "b"
  17741.        LOCATE 9, 42
  17742.        PRINT "c"
  17743.  
  17744.      ' Label the triangle angles
  17745.        LOCATE 7, 55
  17746.        PRINT "A"
  17747.        LOCATE 7, 28
  17748.        PRINT "B"
  17749.        LOCATE 2, 33
  17750.        PRINT "C"
  17751.  
  17752.      ' Ask user for the known data
  17753.        LOCATE 12, 1
  17754.        PRINT "Enter known sides and angles (deg),"
  17755.        PRINT "and press Enter for unknowns..."
  17756.        LOCATE 16, 1
  17757.        LINE INPUT "Side  a  "; sA$
  17758.        LINE INPUT "Side  b  "; sB$
  17759.        LINE INPUT "Side  c  "; sC$
  17760.        PRINT
  17761.        LINE INPUT "Angle A  "; aA$
  17762.        LINE INPUT "Angle B  "; aB$
  17763.        LINE INPUT "Angle C  "; aC$
  17764.        PRINT
  17765.  
  17766.      ' Convert to numeric values
  17767.        sA# = VAL(sA$)
  17768.        sB# = VAL(sB$)
  17769.        sC# = VAL(sC$)
  17770.        aA# = Deg2Rad#(VAL(aA$))
  17771.        aB# = Deg2Rad#(VAL(aB$))
  17772.        aC# = Deg2Rad#(VAL(aC$))
  17773.  
  17774.      ' Solve for the unknowns
  17775.        Triangle sA#, sB#, sC#, aA#, aB#, aC#
  17776.  
  17777.      ' Output the results
  17778.        LOCATE 16, 1
  17779.        PRINT "Side  a  "; sA#
  17780.        PRINT "Side  b  "; sB#
  17781.        PRINT "Side  c  "; sC#
  17782.        PRINT
  17783.        PRINT "Angle A  "; Rad2Deg#(aA#); "Deg"
  17784.        PRINT "Angle B  "; Rad2Deg#(aB#); "Deg"
  17785.        PRINT "Angle C  "; Rad2Deg#(aC#); "Deg"
  17786.        LOCATE 20, 40
  17787.        PRINT "Area = "; TriangleArea#(sA#, sB#, sC#)
  17788.  
  17789.      ' All done
  17790.        LOCATE 24, 1
  17791.        PRINT "Press any key to continue";
  17792.        DO
  17793.        LOOP WHILE INKEY = ""
  17794.        SCREEN 0
  17795.        END
  17796.    ──────────────────────────────────────────────────────────────────────────
  17797.  
  17798.  
  17799.  Function: Deg2Rad#
  17800.  
  17801.    Converts degrees to radians.
  17802.  
  17803.    ──────────────────────────────────────────────────────────────────────────
  17804.      ' ************************************************
  17805.      ' **  Name:          Deg2Rad#                   **
  17806.      ' **  Type:          Function                   **
  17807.      ' **  Module:        TRIANGLE.BAS               **
  17808.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17809.      ' ************************************************
  17810.      '
  17811.      ' Converts degree angular units to radians.
  17812.      '
  17813.      ' EXAMPLE OF USE:  r# = Deg2Rad#(deg#)
  17814.      ' PARAMETERS:      deg#       Degrees
  17815.      ' VARIABLES:       (none)
  17816.      ' MODULE LEVEL
  17817.      '   DECLARATIONS:  DECLARE FUNCTION Deg2Rad# (deg#)
  17818.      '
  17819.        FUNCTION Deg2Rad# (deg#) STATIC
  17820.            Deg2Rad# = deg# / 57.29577951308232#
  17821.        END FUNCTION
  17822.    ──────────────────────────────────────────────────────────────────────────
  17823.  
  17824.  
  17825.  Function: Rad2Deg#
  17826.  
  17827.    Converts radians to degrees.
  17828.  
  17829.    ──────────────────────────────────────────────────────────────────────────
  17830.      ' ************************************************
  17831.      ' **  Name:          Rad2Deg#                   **
  17832.      ' **  Type:          Function                   **
  17833.      ' **  Module:        TRIANGLE.BAS               **
  17834.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17835.      ' ************************************************
  17836.      '
  17837.      ' Converts radian angular units to degrees.
  17838.      '
  17839.      ' EXAMPLE OF USE:  d# = Rad2Deg#(rad#)
  17840.      ' PARAMETERS:      rad#       Radians
  17841.      ' VARIABLES:       (none)
  17842.      ' MODULE LEVEL
  17843.      '   DECLARATIONS:  DECLARE FUNCTION Rad2Deg# (rad#)
  17844.      '
  17845.        FUNCTION Rad2Deg# (rad#) STATIC
  17846.            Rad2Deg# = rad# * 57.29577951308232#
  17847.        END FUNCTION
  17848.    ──────────────────────────────────────────────────────────────────────────
  17849.  
  17850.  
  17851.  Subprogram: Triangle
  17852.  
  17853.    Calculates sides and angles of a triangle if enough sides and/or angles
  17854.    are given to be able to deduce the rest. Any combination of sides and
  17855.    angles can be given, although illegal combinations will produce
  17856.    unpredictable results.
  17857.  
  17858.    Double-precision numbers are used throughout this subprogram to maintain
  17859.    high accuracy. Change all the pound signs to exclamation points if you
  17860.    prefer to work with single-precision numbers.
  17861.  
  17862.    ──────────────────────────────────────────────────────────────────────────
  17863.      ' ************************************************
  17864.      ' **  Name:          Triangle                   **
  17865.      ' **  Type:          Subprogram                 **
  17866.      ' **  Module:        TRIANGLE.BAS               **
  17867.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17868.      ' ************************************************
  17869.      '
  17870.      ' Calculates all sides and angles of a triangle,
  17871.      ' assuming enough sides and angles are given.
  17872.      '
  17873.      ' EXAMPLE OF USE:  Triangle sA#, sB#, sC#, aA#, aB#, aC#
  17874.      ' PARAMETERS:      sA#        Side A
  17875.      '                  sB#        Side B
  17876.      '                  sC#        Side C
  17877.      '                  aA#        Angle A
  17878.      '                  aB#        Angle B
  17879.      '                  aC#        Angle C
  17880.      ' VARIABLES:       i%         Looping index
  17881.      ' MODULE LEVEL
  17882.      '   DECLARATIONS:  DECLARE SUB Triangle (sA#, sB#, sC#, aA#, aB#, aC#)
  17883.      '
  17884.        SUB Triangle (sA#, sB#, sC#, aA#, aB#, aC#) STATIC
  17885.  
  17886.            FOR i% = 1 TO 18
  17887.  
  17888.                IF aA# = 0# THEN
  17889.                    IF sA# <> 0# AND sB# <> 0# AND sC# <> 0# THEN
  17890.                        t# = sB# * sB# + sC# * sC# - sA# * sA#
  17891.                        aA# = ArcCosine#(t# / 2# / sB# / sC#)
  17892.                    END IF
  17893.                END IF
  17894.  
  17895.                IF aB# = 0# THEN
  17896.                    IF sA# <> 0# AND sB# <> 0# AND aA# <> 0# THEN
  17897.                        aB# = ArcSine#(sB# * SIN(aA#) / sA#)
  17898.                    END IF
  17899.                END IF
  17900.  
  17901.                IF aC# = 0# THEN
  17902.                    IF aA# <> 0# AND aB# <> 0# THEN
  17903.                        aC# = 3.141592653589793# - aA# - aB#
  17904.                    END IF
  17905.                END IF
  17906.  
  17907.                IF sB# = 0# THEN
  17908.                    IF sA# <> 0# AND aB# <> 0# AND aA# <> 0# THEN
  17909.                        sB# = sA# * SIN(aB#) / SIN(aA#)
  17910.                    END IF
  17911.                END IF
  17912.  
  17913.                IF sC# = 0# THEN
  17914.                    IF sA# <> 0# AND sB# <> 0# AND aC# <> 0# THEN
  17915.                        t# = sA# * sA# + sB# * sB#
  17916.                        sC# = SQR(t# - 2# * sA# * sB# * COS(aC#))
  17917.                    END IF
  17918.                END IF
  17919.  
  17920.                IF i% MOD 2 THEN
  17921.                    SWAP sB#, sC#
  17922.                    SWAP aB#, aC#
  17923.                ELSE
  17924.                    SWAP sA#, sB#
  17925.                    SWAP aA#, aB#
  17926.                END IF
  17927.  
  17928.            NEXT i%
  17929.  
  17930.        END SUB
  17931.    ──────────────────────────────────────────────────────────────────────────
  17932.  
  17933.  
  17934.  Function: TriangleArea#
  17935.  
  17936.    Calculates the area of a triangle given the three sides of the triangle.
  17937.  
  17938.    ──────────────────────────────────────────────────────────────────────────
  17939.      ' ************************************************
  17940.      ' **  Name:          TriangleArea#              **
  17941.      ' **  Type:          Function                   **
  17942.      ' **  Module:        TRIANGLE.BAS               **
  17943.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  17944.      ' ************************************************
  17945.      '
  17946.      ' Returns the area of a triangle given the three sides.
  17947.      '
  17948.      ' EXAMPLE OF USE:  TriangleArea# sA#, sB#, sC#
  17949.      ' PARAMETERS:      sA#        Side A
  17950.      '                  sB#        Side B
  17951.      '                  sC#        Side C
  17952.      ' VARIABLES:       s#         Sum of the three sides of the triangle
  17953.      '                             divided by two
  17954.      '                  t1#        Temporary variable
  17955.      '                  t2#        Temporary variable
  17956.      '                  t3#        Temporary variable
  17957.      ' MODULE LEVEL
  17958.      '   DECLARATIONS:  DECLARE FUNCTION TriangleArea# (sA#, sB#, sC#)
  17959.      '
  17960.        FUNCTION TriangleArea# (sA#, sB#, sC#) STATIC
  17961.            s# = (sA# + sB# + sC#) / 2#
  17962.            t1# = s# - sA#
  17963.            t2# = s# - sB#
  17964.            t3# = s# - sC#
  17965.            TriangleArea# = SQR(s# * t1# * t2# * t3#)
  17966.        END FUNCTION
  17967.    ──────────────────────────────────────────────────────────────────────────
  17968.  
  17969.  
  17970.  
  17971.  ────────────────────────────────────────────────────────────────────────────
  17972.  WINDOWS
  17973.  
  17974.    The WINDOWS demo module demonstrates the windows subprograms. One lets
  17975.    you create several types of windows for displaying information and menu
  17976.    selections. A second subprogram removes the most recently created window.
  17977.  
  17978.    The WindowsType data structure completely defines the action and
  17979.    appearance of the windows that you can create. Although the list of
  17980.    variables in this structure is fairly long, you have the advantage of
  17981.    complete control over the windows.
  17982.  
  17983.    Set the action code to 0, 1, or 2. An action code of 0 indicates a return
  17984.    to the calling program immediately after the window is created, leaving
  17985.    the window on the screen. You could use this type of window for simply
  17986.    displaying information.
  17987.  
  17988.    An action code of 1 creates the window and then waits for the user to
  17989.    press any key before continuing. The code for the key pressed is returned
  17990.    to the calling program, making it possible to use a type 1 window to ask
  17991.    yes-or-no, multiple choice, or other questions.
  17992.  
  17993.    An action code of 2 creates the most sophisticated type of window. In this
  17994.    case, a menu window is created, providing several methods for the user to
  17995.    select from among the available menu choices. One line of the menu window
  17996.    is highlighted to indicate the currently selected choice. You can use the
  17997.    up and down arrow keys or the mouse to move this highlight to the desired
  17998.    line. Clicking with the mouse or pressing the Enter key selects the
  17999.    currently highlighted line. You can also press the key for the first
  18000.    unique character of a line, which immediately selects that line. The
  18001.    Windows subprogram then returns the line number for a type 2 action code
  18002.    menu.
  18003.  
  18004.    The edgeLine variable in the WindowsType data structure should also be set
  18005.    to 0, 1, or 2. This parameter tells the Windows subprogram to draw a
  18006.    border around the window with 0-, 1-, or 2-line graphics characters.
  18007.  
  18008.    You can select and control the foreground and background colors for each
  18009.    part of a window individually. The color definition constants used in the
  18010.    demonstration program can be very useful for setting these parameters.
  18011.  
  18012.    The row and column variables define the placement of the upper left corner
  18013.    of the window. The Windows subprogram automatically sizes the window for
  18014.    both the number of lines and the length of the longest line. Be sure to
  18015.    place the window where it won't hit the edges of the screen.
  18016.  
  18017.    The title string appears in the center of the top border of the window,
  18018.    and the prompt string appears in the center of the bottom border of the
  18019.    display. If these strings are null, nothing is displayed in the window
  18020.    borders.
  18021.  
  18022.    The PCOPY statement copies screen pages for saving and restoring the
  18023.    background information under the windows. This technique results in very
  18024.    quick window appearance and disappearance without using complicated
  18025.    assembly-language routines. In 40-column SCREEN mode 0, you can display up
  18026.    to seven windows at once, one for each available screen page. Depending on
  18027.    the graphics adapter you have, other video modes let you display two to
  18028.    four windows simultaneously.
  18029.  
  18030.    Name                          Type   Description
  18031.    ──────────────────────────────────────────────────────────────────────────
  18032.    WINDOWS.BAS                         Demo module
  18033.    Windows                      Sub    Creates a pop-up window
  18034.    WindowsPop                   Sub    Removes last displayed window
  18035.    ──────────────────────────────────────────────────────────────────────────
  18036.  
  18037.  
  18038.  Demo Module: WINDOWS
  18039.  
  18040.    ──────────────────────────────────────────────────────────────────────────
  18041.      ' ************************************************
  18042.      ' **  Name:          WINDOWS                    **
  18043.      ' **  Type:          Toolbox                    **
  18044.      ' **  Module:        WINDOWS.BAS                **
  18045.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  18046.      ' ************************************************
  18047.      ' USAGE:           No command line parameters
  18048.      ' REQUIREMENTS:    MIXED.QLB/.LIB
  18049.      '                  Mouse (optional)
  18050.      ' .MAK FILE:       WINDOWS.BAS
  18051.      '                  BITS.BAS
  18052.      '                  BIOSCALL.BAS
  18053.      '                  MOUSSUBS.BAS
  18054.      '                  KEYS.BAS
  18055.      ' PARAMETERS:      (none)
  18056.      ' VARIABLES:       w1         Structure of type WindowsType
  18057.      '                  w2         Structure of type WindowsType
  18058.      '                  w3         Structure of type WindowsType
  18059.      '                  w1Text$()  Strings to display in first window
  18060.      '                  w2Text$()  Strings to display in second window
  18061.      '                  w3Text$()  Strings to display in third window
  18062.      '                  w1Title$   Title string for first window
  18063.      '                  w1Prompt$  Prompt string for first window
  18064.      '                  w2Title$   Title string for second window
  18065.      '                  w2Prompt$  Prompt string for second window
  18066.      '                  w3Title$   Title string for third window
  18067.      '                  arrow$     String showing up and down arrows
  18068.      '                  entSymbol$ String showing the Enter key symbol
  18069.      '                  w3Prompt$  Prompt string for third window
  18070.      '                  i%         Looping index
  18071.      '                  t0         Timer value
  18072.  
  18073.  
  18074.      ' Define color constants
  18075.        CONST BLACK = 0
  18076.        CONST BLUE = 1
  18077.        CONST GREEN = 2
  18078.        CONST CYAN = 3
  18079.        CONST RED = 4
  18080.        CONST MAGENTA = 5
  18081.        CONST BROWN = 6
  18082.        CONST WHITE = 7
  18083.        CONST BRIGHT = 8
  18084.        CONST BLINK = 16
  18085.        CONST YELLOW = BROWN + BRIGHT
  18086.  
  18087.        TYPE WindowsType
  18088.            action       AS INTEGER
  18089.            edgeLine     AS INTEGER
  18090.            row          AS INTEGER
  18091.            col          AS INTEGER
  18092.            fgdEdge      AS INTEGER
  18093.            bgdEdge      AS INTEGER
  18094.            fgdBody      AS INTEGER
  18095.            bgdBody      AS INTEGER
  18096.            fgdHighlight AS INTEGER
  18097.            bgdHighlight AS INTEGER
  18098.            fgdTitle     AS INTEGER
  18099.            bgdTitle     AS INTEGER
  18100.            fgdPrompt    AS INTEGER
  18101.            bgdPrompt    AS INTEGER
  18102.            returnCode   AS INTEGER
  18103.        END TYPE
  18104.  
  18105.      ' Functions
  18106.        DECLARE FUNCTION InKeyCode% ()
  18107.  
  18108.      ' Subprograms
  18109.        DECLARE SUB Windows (w AS WindowsType, wText$(), wTitle$, wPrompt$)
  18110.        DECLARE SUB WindowsPop ()
  18111.        DECLARE SUB VideoState (mode%, columns%, page%)
  18112.        DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  18113.        DECLARE SUB MouseMickey (horizontal%, vertical%)
  18114.        DECLARE SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%)
  18115.  
  18116.      ' Data structures
  18117.        DIM w1 AS WindowsType
  18118.        DIM w2 AS WindowsType
  18119.        DIM w3 AS WindowsType
  18120.  
  18121.      ' Arrays
  18122.        DIM w1Text$(1 TO 5)
  18123.        DIM w2Text$(1 TO 3)
  18124.        DIM w3Text$(1 TO 9)
  18125.  
  18126.      ' Define first window
  18127.        w1.action = 0
  18128.        w1.edgeLine = 1
  18129.        w1.row = 2
  18130.        w1.col = 3
  18131.        w1.fgdEdge = YELLOW
  18132.        w1.bgdEdge = BLUE
  18133.        w1.fgdBody = BRIGHT + WHITE
  18134.        w1.bgdBody = BLUE
  18135.        w1.fgdHighlight = 0
  18136.        w1.bgdHighlight = 0
  18137.        w1.fgdTitle = YELLOW
  18138.        w1.bgdTitle = BLUE
  18139.        w1.fgdPrompt = YELLOW
  18140.        w1.bgdPrompt = BLUE
  18141.        w1Title$ = " First Window "
  18142.        w1Text$(1) = "This window demonstrates how information"
  18143.        w1Text$(2) = "can be displayed without requesting any"
  18144.        w1Text$(3) = "response from the user.  The action code"
  18145.        w1Text$(4) = "is 0, causing an immediate return to the"
  18146.        w1Text$(5) = "program after the window is displayed."
  18147.        w1Prompt$ = ""
  18148.  
  18149.      ' Define second window
  18150.        w2.action = 1
  18151.        w2.edgeLine = 2
  18152.        w2.row = 10
  18153.        w2.col = 12
  18154.        w2.fgdEdge = CYAN + BRIGHT
  18155.        w2.bgdEdge = BLACK
  18156.        w2.fgdBody = YELLOW
  18157.        w2.bgdBody = BLACK
  18158.        w2.fgdHighlight = 0
  18159.        w2.bgdHighlight = 0
  18160.        w2.fgdTitle = CYAN + BRIGHT
  18161.        w2.bgdTitle = BLUE
  18162.        w2.fgdPrompt = CYAN + BRIGHT
  18163.        w2.bgdPrompt = BLUE
  18164.        w2Title$ = " Second window, action code is 1 "
  18165.        w2Text$(1) = "This window waits for the user to press"
  18166.        w2Text$(2) = "any key before continuing.  The key code"
  18167.        w2Text$(3) = "is passed back to the calling program."
  18168.        w2Prompt$ = " Press any key to continue. "
  18169.  
  18170.      ' Define third window
  18171.        w3.action = 2
  18172.        w3.edgeLine = 2
  18173.        w3.row = 7
  18174.        w3.col = 15
  18175.        w3.fgdEdge = YELLOW
  18176.        w3.bgdEdge = WHITE
  18177.        w3.fgdBody = BLACK
  18178.        w3.bgdBody = WHITE
  18179.        w3.fgdHighlight = WHITE + BRIGHT
  18180.        w3.bgdHighlight = BLACK
  18181.        w3.fgdTitle = YELLOW
  18182.        w3.bgdTitle = WHITE
  18183.        w3.fgdPrompt = YELLOW
  18184.        w3.bgdPrompt = WHITE
  18185.        w3Title$ = " Third window, action is 2 (menu selection) "
  18186.        arrows$ = CHR$(24) + " " + CHR$(25) + " "
  18187.        entSymbol$ = CHR$(17) + CHR$(196) + CHR$(217)
  18188.        w3Prompt$ = " <Character> " + arrows$ + entSymbol$ + " or use mouse "
  18189.        w3Text$(1) = "1. This is the first line in the window."
  18190.        w3Text$(2) = "2. This is the second."
  18191.        w3Text$(3) = "3. This is the third line."
  18192.        w3Text$(4) = "4. The fourth."
  18193.        w3Text$(5) = "5. The fifth."
  18194.        w3Text$(6) = "A. You can press <A> or <a> to select this line."
  18195.        w3Text$(7) = "B. You can press <1> to <5> for one of the first 5 lines.
  18196.        w3Text$(8) = "C. Try moving the cursor up or down and pressing Enter."
  18197.        w3Text$(9) = "D. Also, try the mouse. Click with left button."
  18198.  
  18199.      ' Initialize the display
  18200.        SCREEN 0, , 0, 0
  18201.        WIDTH 80
  18202.        CLS
  18203.        FOR i% = 1 TO 20
  18204.            PRINT STRING$(80, 178)
  18205.        NEXT i%
  18206.        LOCATE 6, 24
  18207.        PRINT " * Windows toolbox demonstration * "
  18208.  
  18209.      ' Wait for any key to be pressed
  18210.        LOCATE 22, 1
  18211.        PRINT "Press any key to continue"
  18212.        DO
  18213.        LOOP UNTIL INKEY$ <> ""
  18214.  
  18215.      ' Clear the "press any key" prompt
  18216.        LOCATE 22, 1
  18217.        PRINT SPACE$(25)
  18218.  
  18219.      ' Create the three windows
  18220.        Windows w1, w1Text$(), w1Title$, w1Prompt$
  18221.        Windows w2, w2Text$(), w2Title$, w2Prompt$
  18222.        Windows w3, w3Text$(), w3Title$, w3Prompt$
  18223.  
  18224.      ' Display the result codes, and erase each window
  18225.        FOR i% = 1 TO 4
  18226.            LOCATE 21, 1
  18227.            COLOR WHITE, BLACK
  18228.            PRINT "The three return codes...";
  18229.            PRINT w1.returnCode; w2.returnCode; w3.returnCode
  18230.            COLOR YELLOW
  18231.            PRINT "Every five seconds another window will disappear..."
  18232.            COLOR WHITE, BLACK
  18233.            t0 = TIMER
  18234.            DO
  18235.            LOOP UNTIL TIMER - t0 > 5
  18236.            WindowsPop
  18237.        NEXT i%
  18238.  
  18239.      ' All done
  18240.        CLS
  18241.        END
  18242.    ──────────────────────────────────────────────────────────────────────────
  18243.  
  18244.  
  18245.  Subprogram: Windows
  18246.  
  18247.    Creates a pop-up window for displaying string data or menu selections. The
  18248.    data structure of type WindowsType defines the action, colors, borders,
  18249.    and other attributes of the windows. If you provide invalid parameters,
  18250.    you receive an appropriate error message, and the program terminates.
  18251.  
  18252.    ──────────────────────────────────────────────────────────────────────────
  18253.      ' ************************************************
  18254.      ' **  Name:          Windows                    **
  18255.      ' **  Type:          Subprogram                 **
  18256.      ' **  Module:        WINDOWS.BAS                **
  18257.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  18258.      ' ************************************************
  18259.      '
  18260.      ' Displays a rectangular window for information display
  18261.      ' or menu selection.
  18262.      '
  18263.      ' EXAMPLE OF USE:  Windows w1, wText$(), wTitle$, wPrompt$
  18264.      ' PARAMETERS:      w1            Structure of type WindowsType
  18265.      '                  wTest$()      Array of strings to be displayed
  18266.      '                  wTitle$       Title string
  18267.      '                  wPrompt$      Prompt string
  18268.      ' VARIABLES:       mode%         Current video mode
  18269.      '                  columns%      Current number of character columns
  18270.      '                  page%         Current video page
  18271.      '                  cursorRow%    Saved cursor row position
  18272.      '                  cursorCol%    Saved cursor column position
  18273.      '                  newpage%      Next video page
  18274.      '                  lbText%       Lower boundary of array of text lines
  18275.      '                  ubText%       Upper boundary of array of text lines
  18276.      '                  i%            Looping index
  18277.      '                  maxlen%       Length of longest string to display
  18278.      '                  length%       Length of each array string
  18279.      '                  row2%         Row number at bottom right corner of win
  18280.      '                  col2%         Column number at bottom right corner of
  18281.      '                                window
  18282.      '                  ul%           Upper left corner border character code
  18283.      '                  ur%           Upper right corner border character code
  18284.      '                  ll%           Lower left corner border character code
  18285.      '                  lr%           Lower right corner border character code
  18286.      '                  vl%           Vertical border character code
  18287.      '                  hl%           Horizontal border character code
  18288.      '                  r%            Index to each line of text
  18289.      '                  ptr%          Highlighted line pointer
  18290.      '                  lastPtr%      Last highlighted line
  18291.      '                  horizontal%   Horizontal mouse mickies
  18292.      '                  vertical%     Vertical mouse mickies
  18293.      '                  mickies       Accumulated vertical mickies
  18294.      '                  choice$       Set of unique characters for each menu l
  18295.      '                  tmp$          Work string
  18296.      '                  kee%          Key code returned by InKeyCode% function
  18297.      '                  leftButton%   Mouse left button state
  18298.      '                  rightButton%  Mouse right button state
  18299.      '                  xMouse%       Mouse X position
  18300.      '                  yMouse%       Mouse Y position
  18301.      ' MODULE LEVEL
  18302.      '   DECLARATIONS:  SUB Windows (w AS WindowsType, wText$(), wTitle$,
  18303.      '                               wPrompt$) STATIC
  18304.      '
  18305.        SUB Windows (w AS WindowsType, wText$(), wTitle$, wPrompt$) STATIC
  18306.          ' Key code numbers
  18307.            CONST DOWNARROW = 20480
  18308.            CONST ENTER = 13
  18309.            CONST ESCAPE = 27
  18310.            CONST UPARROW = 18432
  18311.  
  18312.          ' Determine current video page
  18313.            VideoState mode%, columns%, page%
  18314.  
  18315.          ' Record current cursor location
  18316.            cursorRow% = CSRLIN
  18317.            cursorCol% = POS(0)
  18318.  
  18319.          ' Window will be on the next page, if available
  18320.            newpage% = page% + 1
  18321.            IF newpage% > 7 THEN
  18322.                SCREEN , , 0, 0
  18323.                PRINT "Error: Windows - not enough video pages"
  18324.                SYSTEM
  18325.            END IF
  18326.  
  18327.          ' Copy current page to new page
  18328.            PCOPY page%, newpage%
  18329.  
  18330.          ' Show the current page while building window on new page
  18331.            SCREEN , , newpage%, page%
  18332.  
  18333.          ' Determine array bounds
  18334.            lbText% = LBOUND(wText$)
  18335.            ubText% = UBOUND(wText$)
  18336.  
  18337.          ' Check the text array bounds, lower always 1, upper > 0
  18338.            IF lbText% <> 1 OR ubText% < 1 THEN
  18339.                SCREEN , , 0, 0
  18340.                PRINT "Error: Windows - text array dimensioned incorrectly"
  18341.                SYSTEM
  18342.            END IF
  18343.  
  18344.          ' Determine longest string in the text array
  18345.            maxLen% = 0
  18346.            FOR i% = lbText% TO ubText%
  18347.                length% = LEN(wText$(i%))
  18348.                IF length% > maxLen% THEN
  18349.                    maxLen% = length%
  18350.                END IF
  18351.            NEXT i%
  18352.  
  18353.          ' Determine the bottom right corner of window
  18354.            row2% = w.row + ubText% + 1
  18355.            col2% = w.col + maxLen% + 3
  18356.  
  18357.          ' Check that window is on screen
  18358.            IF w.row < 1 OR w.col < 1 OR row2% > 25 OR col2% > columns% THEN
  18359.                SCREEN , , 0, 0
  18360.                PRINT "Error: Windows - part of window is off screen"
  18361.                SYSTEM
  18362.            END IF
  18363.  
  18364.          ' Set the edge characters
  18365.            SELECT CASE w.edgeLine
  18366.            CASE 0
  18367.                ul% = 32
  18368.                ur% = 32
  18369.                ll% = 32
  18370.                lr% = 32
  18371.                vl% = 32
  18372.                hl% = 32
  18373.            CASE 1
  18374.                ul% = 218
  18375.                ur% = 191
  18376.                ll% = 192
  18377.                lr% = 217
  18378.                vl% = 179
  18379.                hl% = 196
  18380.            CASE 2
  18381.                ul% = 201
  18382.                ur% = 187
  18383.                ll% = 200
  18384.                lr% = 188
  18385.                vl% = 186
  18386.                hl% = 205
  18387.            CASE ELSE
  18388.                SCREEN , , 0, 0
  18389.                PRINT "Error: Windows - Edge line type incorrect"
  18390.                SYSTEM
  18391.            END SELECT
  18392.  
  18393.          ' Draw top edge of the box
  18394.            LOCATE w.row, w.col, 0
  18395.            COLOR w.fgdEdge, w.bgdEdge
  18396.            PRINT CHR$(ul%); STRING$(maxLen% + 2, hl%); CHR$(ur%);
  18397.  
  18398.          ' Draw the body of the window
  18399.            FOR r% = w.row + 1 TO row2% - 1
  18400.                LOCATE r%, w.col, 0
  18401.                COLOR w.fgdEdge, w.bgdEdge
  18402.                PRINT CHR$(vl%);
  18403.                COLOR w.fgdBody, w.bgdBody
  18404.                tmp$ = LEFT$(wText$(r% - w.row) + SPACE$(maxLen%), maxLen%)
  18405.                PRINT " "; tmp$; " ";
  18406.                COLOR w.fgdEdge, w.bgdEdge
  18407.                PRINT CHR$(vl%);
  18408.            NEXT r%
  18409.  
  18410.          ' Draw bottom edge of the box
  18411.            LOCATE row2%, w.col, 0
  18412.            COLOR w.fgdEdge, w.bgdEdge
  18413.            PRINT CHR$(ll%); STRING$(maxLen% + 2, hl%); CHR$(lr%);
  18414.  
  18415.          ' Center and print top title if present
  18416.            IF wTitle$ <> "" THEN
  18417.                LOCATE w.row, (w.col + col2% - LEN(wTitle$) + 1) \ 2, 0
  18418.                COLOR w.fgdTitle, w.bgdTitle
  18419.                PRINT wTitle$;
  18420.            END IF
  18421.  
  18422.          ' Center and print prompt if present
  18423.            IF wPrompt$ <> "" THEN
  18424.                LOCATE row2%, (w.col + col2% - LEN(wPrompt$) + 1) \ 2, 0
  18425.                COLOR w.fgdPrompt, w.bgdPrompt
  18426.                PRINT wPrompt$;
  18427.            END IF
  18428.  
  18429.          ' Now make the new page visible and active
  18430.            SCREEN , , newpage%, newpage%
  18431.  
  18432.          ' Take next action based on action code
  18433.            SELECT CASE w.action
  18434.            CASE 1
  18435.  
  18436.              ' Get a key code number and return it
  18437.                DO
  18438.                    w.returnCode = InKeyCode%
  18439.                LOOP UNTIL w.returnCode
  18440.  
  18441.            CASE 2
  18442.  
  18443.              ' Set choice pointer to last selection if known
  18444.                IF w.returnCode > 0 AND w.returnCode < ubText% THEN
  18445.                    ptr% = w.returnCode
  18446.                ELSE
  18447.                    ptr% = 1
  18448.                END IF
  18449.  
  18450.              ' Start with last pointer different, to update highlighting
  18451.                IF ptr% > 1 THEN
  18452.                    lastPtr% = 1
  18453.                ELSE
  18454.                    lastPtr% = 2
  18455.                END IF
  18456.  
  18457.              ' Clear any mouse mickey counts
  18458.                MouseMickey horizontal%, vertical%
  18459.                mickies% = 0
  18460.  
  18461.              ' Create unique key selection string
  18462.                choice$ = ""
  18463.                FOR i% = 1 TO ubText%
  18464.                    tmp$ = UCASE$(LTRIM$(wText$(i%)))
  18465.                    DO
  18466.                        IF tmp$ <> "" THEN
  18467.                            t$ = LEFT$(tmp$, 1)
  18468.                            tmp$ = MID$(tmp$, 2)
  18469.                            IF INSTR(choice$, t$) = 0 THEN
  18470.                                choice$ = choice$ + t$
  18471.                            END IF
  18472.                        ELSE
  18473.                            SCREEN 0, , 0
  18474.                            PRINT "Error: Windows - No unique character"
  18475.                            SYSTEM
  18476.                        END IF
  18477.                    LOOP UNTIL LEN(choice$) = i%
  18478.                NEXT i%
  18479.  
  18480.              ' Main loop, monitor mouse and keyboard
  18481.                DO
  18482.  
  18483.                  ' Add the mouse mickies
  18484.                    MouseMickey horizontal%, vertical%
  18485.                    mickies% = mickies% + vertical%
  18486.  
  18487.                  ' Check for enough mickies
  18488.                    IF mickies% < -17 THEN
  18489.                        mickies% = 0
  18490.                        IF ptr% > 1 THEN
  18491.                            ptr% = ptr% - 1
  18492.                        END IF
  18493.                    ELSEIF mickies% > 17 THEN
  18494.                        mickies% = 0
  18495.                        IF ptr% < ubText% THEN
  18496.                            ptr% = ptr% + 1
  18497.                        END IF
  18498.                    END IF
  18499.  
  18500.                  ' Check keyboard
  18501.                    kee% = InKeyCode%
  18502.                    IF kee% >= ASC("a") AND kee% <= ASC("z") THEN
  18503.                        kee% = ASC(UCASE$(CHR$(kee%)))
  18504.                    END IF
  18505.                    SELECT CASE kee%
  18506.                    CASE UPARROW
  18507.                        IF ptr% > 1 THEN
  18508.                            ptr% = ptr% - 1
  18509.                        END IF
  18510.                    CASE DOWNARROW
  18511.                        IF ptr% < ubText% THEN
  18512.                            ptr% = ptr% + 1
  18513.                        END IF
  18514.                    CASE ENTER
  18515.                        w.returnCode = ptr%
  18516.                    CASE ESCAPE
  18517.                        w.returnCode = -1
  18518.                    CASE ELSE
  18519.                        w.returnCode = INSTR(choice$, CHR$(kee%))
  18520.                        IF w.returnCode THEN
  18521.                            ptr% = w.returnCode
  18522.                        END IF
  18523.                    END SELECT
  18524.  
  18525.                  ' Check the left mouse button
  18526.                    MouseNow leftButton%, rightButton%, xMouse%, yMouse%
  18527.                    IF leftButton% THEN
  18528.                        w.returnCode = ptr%
  18529.                    END IF
  18530.  
  18531.                  ' Update the highlight if line has changed
  18532.                    IF ptr% <> lastPtr% THEN
  18533.                        LOCATE lastPtr% + w.row, w.col + 2, 0
  18534.                        COLOR w.fgdBody, w.bgdBody
  18535.                        tmp$ = LEFT$(wText$(lastPtr%) + SPACE$(maxLen%), maxLen
  18536.                        PRINT tmp$;
  18537.                        LOCATE ptr% + w.row, w.col + 2, 0
  18538.                        COLOR w.fgdHighlight, w.bgdHighlight
  18539.                        tmp$ = LEFT$(wText$(ptr%) + SPACE$(maxLen%), maxLen%)
  18540.                        PRINT tmp$;
  18541.                        lastPtr% = ptr%
  18542.                    END IF
  18543.  
  18544.                LOOP WHILE w.returnCode = 0
  18545.  
  18546.            CASE ELSE
  18547.                w.returnCode = 0
  18548.            END SELECT
  18549.  
  18550.          ' Reset the cursor position
  18551.            LOCATE cursorRow%, cursorCol%
  18552.  
  18553.        END SUB
  18554.    ──────────────────────────────────────────────────────────────────────────
  18555.  
  18556.  
  18557.  Subprogram: WindowsPop
  18558.  
  18559.    Removes the most recently created window from the screen. The SCREEN
  18560.    statement is used to change the apage and vpage parameters simultaneously,
  18561.    resulting in nearly instant removal of the window.
  18562.  
  18563.    ──────────────────────────────────────────────────────────────────────────
  18564.      ' ************************************************
  18565.      ' **  Name:          WindowsPop                 **
  18566.      ' **  Type:          Subprogram                 **
  18567.      ' **  Module:        WINDOWS.BAS                **
  18568.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  18569.      ' ************************************************
  18570.      '
  18571.      ' Removes last displayed window.
  18572.      '
  18573.      ' EXAMPLE OF USE:  WindowsPop
  18574.      ' PARAMETERS:      (none)
  18575.      ' VARIABLES:       mode%      Current video mode
  18576.      '                  columns%   Current number of display columns
  18577.      '                  page%      Current display page
  18578.      ' MODULE LEVEL
  18579.      '   DECLARATIONS:  DECLARE SUB WindowsPop ()
  18580.      '
  18581.        SUB WindowsPop STATIC
  18582.            VideoState mode%, columns%, page%
  18583.            IF page% THEN
  18584.                SCREEN 0, , page% - 1, page% - 1
  18585.            END IF
  18586.        END SUB
  18587.    ──────────────────────────────────────────────────────────────────────────
  18588.  
  18589.  
  18590.  
  18591.  ────────────────────────────────────────────────────────────────────────────
  18592.  WORDCOUN
  18593.  
  18594.    The WORDCOUN toolbox counts words in a file and contains a function that
  18595.    counts words in a string. Enter a filename on the command line when you
  18596.    run this program.
  18597.  
  18598.    Name                          Type   Description
  18599.    ──────────────────────────────────────────────────────────────────────────
  18600.    WORDCOUN.BAS                        Demo module
  18601.    WordCount%                   Func   Returns number of words in a string
  18602.    ──────────────────────────────────────────────────────────────────────────
  18603.  
  18604.  
  18605.  Demo Module: WORDCOUN
  18606.  
  18607.    ──────────────────────────────────────────────────────────────────────────
  18608.      ' ************************************************
  18609.      ' **  Name:          WORDCOUN                   **
  18610.      ' **  Type:          Toolbox                    **
  18611.      ' **  Module:        WORDCOUN.BAS               **
  18612.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  18613.      ' ************************************************
  18614.      '
  18615.      ' USAGE:           WORDCOUN filename
  18616.      ' .MAK FILE:       (none)
  18617.      ' PARAMETERS:      filename      Name of file to be processed
  18618.      ' VARIABLES:       fileName$     Name of file from the command line
  18619.      '                  sep$          List of characters defined as word separ
  18620.      '                  a$            Each line from the file
  18621.      '                  totalCount&   Total count of words
  18622.  
  18623.        DECLARE FUNCTION WordCount% (a$, sep$)
  18624.  
  18625.      ' Assume a filename has been given on the command line
  18626.        fileName$ = COMMAND$
  18627.  
  18628.      ' Open the file
  18629.        OPEN fileName$ FOR INPUT AS #1
  18630.  
  18631.      ' Define the word-separating characters as space, tab, and comma
  18632.        sep$ = " " + CHR$(9) + ","
  18633.  
  18634.      ' Read in and process each line
  18635.        DO
  18636.            LINE INPUT #1, a$
  18637.            totalCount& = totalCount& + WordCount%(a$, sep$)
  18638.        LOOP UNTIL EOF(1)
  18639.  
  18640.      ' Print the results
  18641.        PRINT "There are"; totalCount&; "words in "; fileName$
  18642.  
  18643.      ' That's all
  18644.        END
  18645.    ──────────────────────────────────────────────────────────────────────────
  18646.  
  18647.  
  18648.  Function: WordCount%
  18649.  
  18650.    Returns the number of words in a string. Words are defined as groups of
  18651.    characters separated by one or more of the characters in sep$. The
  18652.    WORDCOUN toolbox passes sep$ with a space, a tab, and a comma in it, but
  18653.    you can place any characters in sep$ that you want to use to define the
  18654.    separation of words.
  18655.  
  18656.    ──────────────────────────────────────────────────────────────────────────
  18657.      ' ************************************************
  18658.      ' **  Name:          Wordcount%                 **
  18659.      ' **  Type:          Function                   **
  18660.      ' **  Module:        WORDCOUN.BAS               **
  18661.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  18662.      ' ************************************************
  18663.      '
  18664.      ' Returns the number of words in a string.
  18665.      '
  18666.      ' EXAMPLE OF USE:  WordCount% a$, sep$
  18667.      ' PARAMETERS:      a$         String containing words to be counted
  18668.      '                  sep$       List of word separation characters
  18669.      ' VARIABLES:       count%     Count of words
  18670.      '                  flag%      Indicates if scanning is currently inside o
  18671.      '                             word
  18672.      '                  la%        length of a$
  18673.      '                  i%         Index to each character of a$
  18674.      ' MODULE LEVEL
  18675.      '   DECLARATIONS:  DECLARE FUNCTION WordCount% (a$, sep$)
  18676.      '
  18677.          FUNCTION WordCount% (a$, sep$) STATIC
  18678.            count% = 0
  18679.            flag% = 0
  18680.            la% = LEN(a$)
  18681.            IF la% > 0 AND sep$ <> "" THEN
  18682.                FOR i% = 1 TO la%
  18683.                    IF INSTR(sep$, MID$(a$, i%, 1)) THEN
  18684.                        IF flag% THEN
  18685.                            flag% = 0
  18686.                            count% = count% + 1
  18687.                        END IF
  18688.                    ELSE
  18689.                        flag% = 1
  18690.                    END IF
  18691.                NEXT i%
  18692.            END IF
  18693.            WordCount% = count% + flag%
  18694.  
  18695.        END FUNCTION
  18696.    ──────────────────────────────────────────────────────────────────────────
  18697.  
  18698.  
  18699.  
  18700.  ────────────────────────────────────────────────────────────────────────────
  18701.  PART 3  MIXED-LANGUAGE TOOLBOXES
  18702.  ────────────────────────────────────────────────────────────────────────────
  18703.  
  18704.  
  18705.  
  18706.  ────────────────────────────────────────────────────────────────────────────
  18707.  Chapter Four  Using Mixed-Language Toolboxes
  18708.  
  18709.    Although Microsoft QuickBASIC is a sophisticated and powerful
  18710.    software-development tool, other languages, such as C, FORTRAN, Pascal,
  18711.    and assembly language, have unique strengths. The ability to mix
  18712.    subprograms and functions written in any of these languages lets you use
  18713.    the best features of each. FORTRAN, for example, has extensive mathematics
  18714.    and engineering libraries, and C is a powerful development language. For
  18715.    the fastest running programs, assembly language can't be beat. Once you
  18716.    understand a few concepts, you'll be able to easily combine routines from
  18717.    these languages.
  18718.  
  18719.  
  18720.  Near and Far Addressing
  18721.  
  18722.    MS-DOS runs on the 8088, 8086, 80286, and 80386 family of microprocessors
  18723.    found at the heart of IBM Personal Computers and compatibles. These chips
  18724.    use special hardware registers that allow quicker access and shorter
  18725.    instruction when referring to data or procedures located in the same block
  18726.    of 65536 (64 KB) bytes of memory.
  18727.  
  18728.    From a software point of view, microprocessor instructions can address
  18729.    memory locations by referring to either locations in a currently defined,
  18730.    single block of 64 KB memory addresses or to any possible locations in
  18731.    memory space. These references are called "near" and "far," respectively.
  18732.    Near references require one word, and far references require two words.
  18733.    Normally, high-level languages such as QuickBASIC take care of all these
  18734.    details for you, but when you link subprograms from other languages with
  18735.    QuickBASIC, you need to be sure that references to variables and calls to
  18736.    subprograms and functions all use the same type of near or far addressing.
  18737.  
  18738.    You can adjust Microsoft QuickC and Microsoft Macro Assembler 5.0 to
  18739.    create programs using a variety of memory models that indicate whether
  18740.    memory locations are referred to by near or far addressing. To be
  18741.    compatible with QuickBASIC's method, you should use Medium Model settings
  18742.    for both compilers. In fact, this is the default setting for QuickC,
  18743.    making it easy for you to write QuickC routines to be called from
  18744.    QuickBASIC.
  18745.  
  18746.  
  18747.  Passing Variables
  18748.  
  18749.    Most programming languages, including QuickBASIC and QuickC, let you pass
  18750.    variables to called subprograms, functions, and subroutines by listing
  18751.    them in parentheses as part of the calling statement. This list matches
  18752.    one for one the parameters defined and used in the called routine.
  18753.  
  18754.    You can pass these parameters to and from a subprogram or function by
  18755.    reference or by value. Some languages pass the address of the referenced
  18756.    variable, and changes made to the variable by the routine modify the
  18757.    contents of memory that this address points to. Other languages pass
  18758.    copies of the values of the variables. Changes to the passed variables
  18759.    don't affect the originals because the changes are made only to the
  18760.    copies.
  18761.  
  18762.    The important concept is that both the calling routine and the called
  18763.    routine must agree as to how they pass parameters back and forth. For
  18764.    example, QuickBASIC usually passes parameters by reference, and QuickC by
  18765.    value. Fortunately, both languages let you control whether parameters are
  18766.    passed by value or by reference. See the CDECL modifier of the QuickBASIC
  18767.    DECLARE statement for an example of how you can control parameter passing.
  18768.  
  18769.    Parameter passing can also vary from language to language in the order in
  18770.    which the parameters are pushed onto the stack as well as in the method
  18771.    used to remove these parameters from the stack when the called routine is
  18772.    finished. For example, consider a QuickBASIC program that passes
  18773.    parameters (A, B, C) to a QuickC subprogram. The QuickC routine processes
  18774.    the parameters as (C, B, A). The CDECL modifier in the QuickBASIC DECLARE
  18775.    statement can tell QuickBASIC to reverse the normal order of parameter
  18776.    pushing to be compatible with QuickC.
  18777.  
  18778.    In most languages, when a subprogram or function is finished, the
  18779.    parameters are removed from the stack before the routine returns to the
  18780.    calling program. QuickC routines expect the calling program to clean up
  18781.    the stack after the return. (This allows passing a variable number of
  18782.    parameters in C.) The CDECL modifier instructs QuickBASIC to clean up the
  18783.    stack after calling a subprogram or function.
  18784.  
  18785.    By using standard parameter-passing techniques and by following the
  18786.    examples in this book and in your Microsoft QuickBASIC and Microsoft Macro
  18787.    Assembler manuals, you'll find mixed-language programming easy and
  18788.    convenient.
  18789.  
  18790.    The routines in Part III of this book demonstrate passing integers, arrays
  18791.    of integers, and string variables. The Microsoft Macro Assembler was used
  18792.    to develop the mouse interface subprogram, and Microsoft QuickC was used
  18793.    to create some bit manipulation and byte-movement routines.
  18794.  
  18795.  
  18796.  Creating Mixed-Language Toolboxes
  18797.  
  18798.    In the section "Using QuickBASIC Toolboxes," you will find the steps to
  18799.    follow for using the QuickC and Macro Assembler routines presented in Part
  18800.    III of this book. Please see "Creating MIXED.QLB," beginning on page 22.
  18801.  
  18802.    In addition to compiling from the system prompt, you can also compile each
  18803.    QuickC toolbox, CTOOLS1.C and CTOOLS2.C, from within the QuickC
  18804.    environment. Run QuickC by typing QC, and notice that the environment is
  18805.    very similar in appearance and feel to that of QuickBASIC. Pull down the
  18806.    Files menu and choose Open. Select CTOOLS1.C to load the first toolbox
  18807.    into QuickC. Then pull down the Run menu and choose Compile. A dialog box
  18808.    opens, providing several compiling options. In the Output Options section,
  18809.    select Obj, rather than the default Memory, and then select Compile File,
  18810.    rather than the default Build Program option. You can experiment with the
  18811.    options listed under Miscellaneous, but selecting Optimizations and
  18812.    deselecting the Stack Checking and Debug options generally results in a
  18813.    smaller .OBJ file. QuickC then compiles the CTOOLS1.C source code
  18814.    currently in memory and writes the resulting CTOOLS1.OBJ file to disk.
  18815.    Repeat the process for the CTOOLS2.C file.
  18816.  
  18817.    Once you have created the object-code files and the MIXED.QLB Quick
  18818.    Library and have loaded MIXED.QLB with QuickBASIC, you can then call any
  18819.    or all of the QuickC and Macro Assembler functions and subprograms from
  18820.    within programs running in the QuickBASIC environment. Be sure to declare
  18821.    the subprogram or function in the module-level code, and then call the
  18822.    routines freely, as though they were part of the standard set of
  18823.    QuickBASIC functions and commands.
  18824.  
  18825.    Once a program is running as desired in the QuickBASIC environment you may
  18826.    want to compile it into a stand-alone .EXE format file. Simply compile the
  18827.    program from within QuickBASIC and the appropriate .LIB file will be
  18828.    searched. The MIXED.LIB file will automatically pull in the necessary code
  18829.    during the LINK process.
  18830.  
  18831.    NOTE: You must have QuickC installed to compile CDEMO1 and CDEMO2 into
  18832.    executable files.
  18833.  
  18834.  Assembly Source-Code Files
  18835.  
  18836.    The CASEMAP.ASM and MOUSE.ASM source-code files are listed here for your
  18837.    convenience in building both the MIXED.QLB and MIXED.LIB libraries.
  18838.  
  18839.    The CASEMAP.ASM subprogram is called by TranslateCountry$, a function
  18840.    found in the DOSCALLS module, to translate each character, one at a time.
  18841.  
  18842.    This routine demonstrates how you can use QuickBASIC's DECLARE statement
  18843.    to pass parameters by value rather than by reference. In this case, the
  18844.    segment- and offset-address parameters for the MS-DOS translation routine
  18845.    are passed by value, resulting in a very efficient branch to the MS-DOS
  18846.    character-translation routine from CaseMap.
  18847.  
  18848.    ──────────────────────────────────────────────────────────────────────────
  18849.     ; **********************************************
  18850.     ; **  CASEMAP.ASM                   MASM 5.0  **
  18851.     ; **                                          **
  18852.     ; **  Assembly subprogram for translating     **
  18853.     ; **  some characters according to the        **
  18854.     ; **  currently loaded MS-DOS country-        **
  18855.     ; **  dependent information.                  **
  18856.     ; **                                          **
  18857.     ; **  Use:  CALL CASEMAP (CHAR%, SEG%, OFS%)  **
  18858.     ; **  Note: CHAR% is passed by reference      **
  18859.     ; **        SEG% and OFS% are passed by value **
  18860.     ; **********************************************
  18861.     ;
  18862.     ; EXAMPLE OF USE:  CALL CaseMap (char%, seg%, ofs%)
  18863.     ; PARAMETERS:      char%      Character byte to be translated
  18864.     ;                  seg%       Segment of address of MS-DOS translate routi
  18865.     ;                  ofs%       Offset of address of MS-DOS translate routin
  18866.     ; VARIABLES:       (none)
  18867.     ; MODULE LEVEL
  18868.     ;   DECLARATIONS:  DECLARE SUB GetCountry (country AS CountryType)
  18869.     ;                  DECLARE SUB CaseMap (character%, BYVAL Segment%,
  18870.     ;                                       BYVAL Offset%)
  18871.     ;                  DECLARE FUNCTION TranslateCountry$ (a$, country AS Coun
  18872.  
  18873.  
  18874.    .MODEL  MEDIUM
  18875.    .CODE
  18876.            public  casemap
  18877.  
  18878.    casemap proc
  18879.  
  18880.    ; Standard entry
  18881.            push    bp
  18882.            mov     bp,sp
  18883.  
  18884.    ; Get CHAR% into AX register
  18885.            mov     bx,[bp+10]
  18886.            mov     ax,[bx]
  18887.  
  18888.    ; Call the translate function in MS-DOS
  18889.            call    dword ptr [bp+6]
  18890.  
  18891.    ; Return translated character to CHAR%
  18892.            mov     bx,[bp+10]
  18893.            mov     [bx],ax
  18894.  
  18895.    ; Standard exit, assumes three variables passed
  18896.            pop     bp
  18897.            ret     6
  18898.  
  18899.    ; End of the procedure
  18900.    casemap endp
  18901.            end
  18902.    ──────────────────────────────────────────────────────────────────────────
  18903.  
  18904.    The MOUSE.ASM subprogram provides a fast and efficient method of
  18905.    interfacing QuickBASIC with the memory-resident mouse-driver software.
  18906.    (See your mouse documentation for information on loading this driver into
  18907.    memory.)
  18908.  
  18909.    ──────────────────────────────────────────────────────────────────────────
  18910.     ; **********************************************
  18911.     ; **  MOUSE.ASM              Macro Assembler  **
  18912.     ; **                                          **
  18913.     ; **  Assembly subprogram for accessing the   **
  18914.     ; **  Microsoft Mouse from QuickBASIC 4.00    **
  18915.     ; **                                          **
  18916.     ; **  Use:  CALL MOUSE (M1%, M2%, M3%, M4%)   **
  18917.     ; **********************************************
  18918.      ;
  18919.     ; EXAMPLE OF USE:  CALL Mouse (m1%, m2%, m3%, m4%)
  18920.     ; PARAMETERS:      m1%        Passed in AX to the mouse driver
  18921.     ;                  m2%        Passed in BX to the mouse driver
  18922.     ;                  m3%        Passed in CX to the mouse driver
  18923.     ;                  m4%        Passed in DX to the mouse driver
  18924.     ; VARIABLES:       (none)
  18925.     ; MODULE LEVEL
  18926.     ;   DECLARATIONS:  DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
  18927.  
  18928.    .MODEL  MEDIUM
  18929.    .CODE
  18930.            public  mouse
  18931.  
  18932.    mouse   proc
  18933.  
  18934.    ; Standard entry
  18935.            push    bp
  18936.            mov     bp,sp
  18937.  
  18938.    ; Get M1% and store it on the stack
  18939.            mov     bx,[bp+12]
  18940.            mov     ax,[bx]
  18941.            push    ax
  18942.  
  18943.    ; Get M2% and store it on the stack
  18944.            mov     bx,[bp+10]
  18945.            mov     ax,[bx]
  18946.            push    ax
  18947.  
  18948.    ; Get M3% into CX register
  18949.            mov     bx,[bp+8]
  18950.            mov     cx,[bx]
  18951.  
  18952.    ; Get M4% into DX register
  18953.            mov     bx,[bp+6]
  18954.            mov     dx,[bx]
  18955.  
  18956.    ; Move M2% from stack into BX register
  18957.            pop     bx
  18958.  
  18959.    ; Move M1% from stack into AX register
  18960.            pop     ax
  18961.  
  18962.    ; Set ES to same as DS (for mouse function 9)
  18963.            push    ds
  18964.            pop     es
  18965.  
  18966.    ; Do the mouse interrupt
  18967.            int     33h
  18968.  
  18969.    ; Save BX (M2%) on stack to free register
  18970.            push    bx
  18971.  
  18972.    ; Return M1% from AX
  18973.            mov     bx,[bp+12]
  18974.            mov     [bx],ax
  18975.  
  18976.    ; Return M2% from stack (was BX)
  18977.            pop     ax
  18978.            mov     bx,[bp+10]
  18979.            mov     [bx],ax
  18980.  
  18981.    ; Return M3% from CX
  18982.            mov     bx,[bp+8]
  18983.            mov     [bx],cx
  18984.  
  18985.    ; Return M4% from DX
  18986.            mov     bx,[bp+6]
  18987.            mov     [bx],dx
  18988.  
  18989.    ; Standard exit, assumes four variables passed
  18990.            pop     bp
  18991.            ret     8
  18992.  
  18993.    ; End of this procedure
  18994.    mouse   endp
  18995.            end
  18996.    ──────────────────────────────────────────────────────────────────────────
  18997.  
  18998.  
  18999.  
  19000.  ────────────────────────────────────────────────────────────────────────────
  19001.  CDEMO1.BAS AND CTOOLS1.C
  19002.  
  19003.    The CDEMO1.BAS program is a QuickBASIC program that demonstrates the
  19004.    proper declaration and calling of the QuickC routines presented in the
  19005.    CTOOLS1.C toolbox.
  19006.  
  19007.    The IsIt[Type]% functions can efficiently determine the classification of
  19008.    any character, given its ASCII numeric value. For example, given c% =
  19009.    ASC("A"), the IsItAlnum%, IsItAlpha%, IsItAscii%, IsItGraph%,
  19010.    IsItPrint%, IsItUpper%, and IsItXDigit% functions all return a true
  19011.    (non-zero) value, and all other functions return zero.
  19012.  
  19013.    The MovBytes and MovWords subprograms allow movement of bytes or words
  19014.    from any location in memory to any other, using the QuickC movedata
  19015.    function. You can use these subprograms to copy the contents of variables
  19016.    into variables of a different type. The eight bytes of a double-precision
  19017.    number, for example, can be easily extracted from an eight-character
  19018.    string after the data has been moved from the number into the string.
  19019.    Large arrays of data can efficiently be moved into arrays of a different
  19020.    type, and video memory can be stored in a string, as demonstrated by the
  19021.    module-level code of CDEMO1.
  19022.  
  19023.    Name                     Type    Description
  19024.    ──────────────────────────────────────────────────────────────────────────
  19025.    CDEMO1.BAS                      QuickBASIC program module
  19026.    CTOOLS1.C                       C-language toolbox containing
  19027.                                     functions/subprograms
  19028.    IsItAlnum%              Func    Alphanumeric character determination
  19029.    IsItAlpha%              Func    Alphabetic character determination
  19030.    IsItAscii%              Func    Standard ASCII character determination
  19031.    IsItCntrl%              Func    Control character determination
  19032.    IsItDigit%              Func    Decimal digit (0─9) determination
  19033.    IsItGraph%              Func    Graphics character determination
  19034.    IsItLower%              Func    Lowercase character determination
  19035.    IsItPrint%              Func    Printable character determination
  19036.    IsItPunct%              Func    Punctuation character determination
  19037.    IsItSpace%              Func    Space character determination
  19038.    IsItUpper%              Func    Uppercase character determination
  19039.    IsItXDigit%             Func    Hexadecimal character determination
  19040.    MovBytes                Sub     Moves bytes from one location to another
  19041.    MovWords                Sub     Moves blocks of words in memory
  19042.    ──────────────────────────────────────────────────────────────────────────
  19043.  
  19044.  
  19045.  Program Module: CDEMO1
  19046.  
  19047.    ──────────────────────────────────────────────────────────────────────────
  19048.      ' ************************************************
  19049.      ' **  Name:          CDEMO1                     **
  19050.      ' **  Type:          Program                    **
  19051.      ' **  Module:        CDEMO1.BAS                 **
  19052.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  19053.      ' ************************************************
  19054.      '
  19055.      ' Demonstrates the QuickC routines presented in
  19056.      ' the file CTOOLS1.C.
  19057.      '
  19058.      ' USAGE:           No command line parameters
  19059.      ' REQUIREMENTS:    CGA
  19060.      '                  MIXED.QLB/.LIB
  19061.      ' .MAK FILE:       (none)
  19062.      ' PARAMETERS:      (none)
  19063.      ' VARIABLES:       a%(0 TO 1999) Storage space for first text screen
  19064.      '                  b%(0 TO 1999) Storage space for second text screen
  19065.      '                  i%            Looping index
  19066.      '                  sseg%         Word and byte move source segment
  19067.      '                                part of address
  19068.      '                  soff%         Word and byte move source offset
  19069.      '                                part of address
  19070.      '                  dseg%         Word and byte move destination segment
  19071.      '                                part of address
  19072.      '                  doff%         Word and byte move destination offset
  19073.      '                                part of address
  19074.      '                  nwords%       Number of words to move
  19075.      '                  nbytes%       Number of bytes to move
  19076.      '                  t$            Copy of TIME$
  19077.      '                  quitflag%     Signal to end first demonstration
  19078.  
  19079.      ' Functions
  19080.        DECLARE FUNCTION IsItAlnum% CDECL (BYVAL c AS INTEGER)
  19081.        DECLARE FUNCTION IsItAlpha% CDECL (BYVAL c AS INTEGER)
  19082.        DECLARE FUNCTION IsItAscii% CDECL (BYVAL c AS INTEGER)
  19083.        DECLARE FUNCTION IsItCntrl% CDECL (BYVAL c AS INTEGER)
  19084.        DECLARE FUNCTION IsItDigit% CDECL (BYVAL c AS INTEGER)
  19085.        DECLARE FUNCTION IsItGraph% CDECL (BYVAL c AS INTEGER)
  19086.        DECLARE FUNCTION IsItLower% CDECL (BYVAL c AS INTEGER)
  19087.        DECLARE FUNCTION IsItPrint% CDECL (BYVAL c AS INTEGER)
  19088.        DECLARE FUNCTION IsItPunct% CDECL (BYVAL c AS INTEGER)
  19089.        DECLARE FUNCTION IsItSpace% CDECL (BYVAL c AS INTEGER)
  19090.        DECLARE FUNCTION IsItUpper% CDECL (BYVAL c AS INTEGER)
  19091.        DECLARE FUNCTION IsItXDigit% CDECL (BYVAL c AS INTEGER)
  19092.  
  19093.      ' Subprograms
  19094.        DECLARE SUB MovBytes CDECL (sseg%, soff%, dseg%, doff%, nbytes%)
  19095.        DECLARE SUB MovWords CDECL (sseg%, soff%, dseg%, doff%, nwords%)
  19096.  
  19097.      ' Make two buffers for the first page of video memory
  19098.        DIM a%(0 TO 1999), b%(0 TO 1999)
  19099.  
  19100.      ' Prevent scrolling when printing in row 25, column 80
  19101.        VIEW PRINT 1 TO 25
  19102.  
  19103.      ' Create the first page of text
  19104.        CLS
  19105.        COLOR 14, 4
  19106.        FOR i% = 1 TO 25
  19107.            PRINT STRING$(80, 179);
  19108.        NEXT i%
  19109.        COLOR 15, 1
  19110.        LOCATE 11, 25
  19111.        PRINT STRING$(30, 32);
  19112.        LOCATE 12, 25
  19113.        PRINT "    -  Calling MovWords  -    "
  19114.        LOCATE 13, 25
  19115.        PRINT STRING$(30, 32);
  19116.  
  19117.      ' Move the screen memory into the first array
  19118.        sseg% = &HB800
  19119.        soff% = 0
  19120.        dseg% = VARSEG(a%(0))
  19121.        doff% = VARPTR(a%(0))
  19122.        nwords% = 2000
  19123.        MovWords sseg%, soff%, dseg%, doff%, nwords%
  19124.  
  19125.      ' Create the second page of text
  19126.        CLS
  19127.        COLOR 14, 4
  19128.        FOR i% = 1 TO 25
  19129.            PRINT STRING$(80, 196);
  19130.        NEXT i%
  19131.        COLOR 15, 1
  19132.        LOCATE 11, 25
  19133.        PRINT STRING$(30, 32);
  19134.        LOCATE 12, 25
  19135.        PRINT "    -  Calling MovBytes  -    "
  19136.        LOCATE 13, 25
  19137.        PRINT STRING$(30, 32);
  19138.  
  19139.      ' Move the screen memory into the second array
  19140.        sseg% = &HB800
  19141.        soff% = 0
  19142.        dseg% = VARSEG(b%(0))
  19143.        doff% = VARPTR(b%(0))
  19144.        nwords% = 2000
  19145.        MovWords sseg%, soff%, dseg%, doff%, nwords%
  19146.  
  19147.      ' Set destination to the video screen memory
  19148.        dseg% = &HB800
  19149.        doff% = 0
  19150.  
  19151.      ' Do the following until a key is pressed
  19152.        DO
  19153.  
  19154.          ' Move 2000 words from first array to screen memory
  19155.            sseg% = VARSEG(a%(0))
  19156.            soff% = VARPTR(a%(0))
  19157.            nwords% = 2000
  19158.            MovWords sseg%, soff%, dseg%, doff%, nwords%
  19159.  
  19160.          ' Wait one second
  19161.            t$ = TIME$
  19162.            DO
  19163.                IF INKEY$ <> "" THEN
  19164.                    t$ = ""
  19165.                    quitFlag% = 1
  19166.                END IF
  19167.            LOOP UNTIL TIME$ <> t$
  19168.  
  19169.          ' Move 4000 bytes from second array to screen memory
  19170.            sseg% = VARSEG(b%(0))
  19171.            soff% = VARPTR(b%(0))
  19172.            nbytes% = 4000
  19173.            MovBytes sseg%, soff%, dseg%, doff%, nbytes%
  19174.  
  19175.          ' Wait one second
  19176.            t$ = TIME$
  19177.            DO
  19178.                IF INKEY$ <> "" THEN
  19179.                    t$ = ""
  19180.                    quitFlag% = 1
  19181.                END IF
  19182.            LOOP UNTIL TIME$ <> t$
  19183.  
  19184.        LOOP UNTIL quitFlag%
  19185.  
  19186.      ' Create a table of all 256 characters and their type designations
  19187.        FOR i% = 0 TO 255
  19188.  
  19189.          ' After each screenful, display a heading
  19190.            IF i% MOD 19 = 0 THEN
  19191.  
  19192.              ' If not the first heading, prompt user before continuing
  19193.                IF i% THEN
  19194.                    PRINT
  19195.                    PRINT "Press any key to continue"
  19196.                    DO WHILE INKEY$ = ""
  19197.                    LOOP
  19198.                END IF
  19199.  
  19200.              ' Print the heading
  19201.                CLS
  19202.                PRINT "Char   Alnum Alpha Ascii Cntrl Digit Graph ";
  19203.                PRINT "Lower Print Punct Space Upper XDigit"
  19204.                PRINT
  19205.            END IF
  19206.  
  19207.          ' Some characters we don't want to display
  19208.            SELECT CASE i%
  19209.            CASE 7, 8, 9, 10, 11, 12, 13, 29, 30, 31
  19210.                PRINT USING "###    "; i%;
  19211.            CASE ELSE
  19212.                PRINT USING "### \ \"; i%, CHR$(i%);
  19213.            END SELECT
  19214.  
  19215.          ' Display "1" if test is true, "0" otherwise
  19216.            PRINT USING "  #   "; 1 + (0 = IsItAlnum%(i%));
  19217.            PRINT USING "  #   "; 1 + (0 = IsItAlpha%(i%));
  19218.            PRINT USING "  #   "; 1 + (0 = IsItAscii%(i%));
  19219.            PRINT USING "  #   "; 1 + (0 = IsItCntrl%(i%));
  19220.            PRINT USING "  #   "; 1 + (0 = IsItDigit%(i%));
  19221.            PRINT USING "  #   "; 1 + (0 = IsItGraph%(i%));
  19222.            PRINT USING "  #   "; 1 + (0 = IsItLower%(i%));
  19223.            PRINT USING "  #   "; 1 + (0 = IsItPrint%(i%));
  19224.            PRINT USING "  #   "; 1 + (0 = IsItPunct%(i%));
  19225.            PRINT USING "  #   "; 1 + (0 = IsItSpace%(i%));
  19226.            PRINT USING "  #   "; 1 + (0 = IsItUpper%(i%));
  19227.            PRINT USING "  #   "; 1 + (0 = IsItXDigit%(i%))
  19228.  
  19229.        NEXT i%
  19230.        END
  19231.    ──────────────────────────────────────────────────────────────────────────
  19232.  
  19233.  
  19234.  Toolbox: CTOOLS1.C
  19235.  
  19236.    The CTOOLS1.C toolbox provides access to the efficient QuickC functions
  19237.    for classifying characters and to QuickC's fast memory move functions for
  19238.    copying blocks of bytes or words from any location in memory to any other.
  19239.  
  19240.    You can determine character types using QuickBASIC code, but the QuickC
  19241.    routines are optimized for speed. Also, adhering to the definitions
  19242.    provided by QuickC guarantees that character classifications will be the
  19243.    same for both languages.
  19244.  
  19245.    You must add both of the following #include statements at the top of the
  19246.    CTOOLS1.C source-code file, before the function definitions are given.
  19247.    These two statements pull in the contents of header files necessary for
  19248.    correct compilation by QuickC:
  19249.  
  19250.  
  19251.      #include <ctype.h>
  19252.      #include <memory.h>
  19253.  
  19254.  
  19255.  Function: IsItAlnum%
  19256.  
  19257.    Determines whether a character is alphanumeric. This function returns a
  19258.    non-zero value if the integer value represents an alphanumeric ASCII
  19259.    character or a zero if the value does not. Alphanumeric characters are in
  19260.    the ranges A through Z, a through z, and 0 through 9.
  19261.  
  19262.    ──────────────────────────────────────────────────────────────────────────
  19263.    /***********************************************
  19264.    **  Name:         IsItAlnum%                  **
  19265.    **  Type:         Function                    **
  19266.    **  Module:       CTOOLS1.C                   **
  19267.    **  Language:     Microsoft QuickC/QuickBASIC **
  19268.    ************************************************
  19269.    *
  19270.    * EXAMPLE OF USE:   result% = IsItAlnum%(c%)
  19271.    * PARAMETERS:       c%         ASCII character code
  19272.    * VARIABLES:        (none)
  19273.    * MODULE LEVEL
  19274.    *   DECLARATIONS:   #include <ctype.h>        */
  19275.  
  19276.  
  19277.    int isitalnum (c)
  19278.    int c;
  19279.        {
  19280.        return (isalnum(c));
  19281.        }
  19282.    ──────────────────────────────────────────────────────────────────────────
  19283.  
  19284.  
  19285.  Function: IsItAlpha%
  19286.  
  19287.    Determines whether a character is alphabetic. This function returns a
  19288.    non-zero value if the integer value represents an alphabetic ASCII
  19289.    character or a zero if the value does not. The alphabetic characters are
  19290.    in the ranges A through Z and a through z.
  19291.  
  19292.    ──────────────────────────────────────────────────────────────────────────
  19293.    /***********************************************
  19294.    **  Name:         IsItAlpha%                  **
  19295.    **  Type:         Function                    **
  19296.    **  Module:       CTOOLS1.C                   **
  19297.    **  Language:     Microsoft QuickC/QuickBASIC **
  19298.    ************************************************
  19299.    *
  19300.    * EXAMPLE OF USE:   result% = IsItAlpha%(c%)
  19301.    * PARAMETERS:       c%         ASCII character code
  19302.    * VARIABLES:        (none)
  19303.    * MODULE LEVEL
  19304.    *   DECLARATIONS:   #include <ctype.h>        */
  19305.  
  19306.  
  19307.    int isitalpha (c)
  19308.    int c;
  19309.        {
  19310.        return (isalpha(c));
  19311.        }
  19312.    ──────────────────────────────────────────────────────────────────────────
  19313.  
  19314.  
  19315.  Function: IsItAscii%
  19316.  
  19317.    Determines whether a character is standard ASCII. This function returns a
  19318.    non-zero value if the integer value represents an ASCII character or a
  19319.    zero if the value does not. The ASCII character values are in the range 0
  19320.    through 127.
  19321.  
  19322.    ──────────────────────────────────────────────────────────────────────────
  19323.    /***********************************************
  19324.    **  Name:         IsItAscii%                  **
  19325.    **  Type:         Function                    **
  19326.    **  Module:       CTOOLS1.C                   **
  19327.    **  Language:     Microsoft QuickC/QuickBASIC **
  19328.    ************************************************
  19329.    *
  19330.    * EXAMPLE OF USE:  result% = IsItAscii%(c%)
  19331.    * PARAMETERS:      c%         ASCII character code
  19332.    * VARIABLES:       (none)
  19333.    * MODULE LEVEL
  19334.    *   DECLARATIONS:  #include <ctype.h>         */
  19335.  
  19336.  
  19337.    int isitascii (c)
  19338.    int c;
  19339.        {
  19340.        return (isascii(c));
  19341.        }
  19342.    ──────────────────────────────────────────────────────────────────────────
  19343.  
  19344.  
  19345.  Function: IsItCntrl%
  19346.  
  19347.    Determines whether a character is a control character. This function
  19348.    returns a non-zero value if the integer value represents a control
  19349.    character or a zero if the value does not. The control characters are in
  19350.    the range 0 through 31, and 127.
  19351.  
  19352.    ──────────────────────────────────────────────────────────────────────────
  19353.    /***********************************************
  19354.    **  Name:         IsItCntrl%                  **
  19355.    **  Type:         Function                    **
  19356.    **  Module:       CTOOLS1.C                   **
  19357.    **  Language:     Microsoft QuickC/QuickBASIC **
  19358.    ************************************************
  19359.    *
  19360.    * EXAMPLE OF USE:   result% = IsItCntrl%(c%)
  19361.    * PARAMETERS:       c%         ASCII character code
  19362.    * VARIABLES:        (none)
  19363.    * MODULE LEVEL
  19364.    *   DECLARATIONS:   #include <ctype.h>        */
  19365.  
  19366.  
  19367.    int isitcntrl (c)
  19368.    int c;
  19369.        {
  19370.        return (iscntrl(c));
  19371.        }
  19372.    ──────────────────────────────────────────────────────────────────────────
  19373.  
  19374.  
  19375.  Function: IsItDigit%
  19376.  
  19377.    Determines whether a character is a numeric digit. This function returns a
  19378.    non-zero value if the integer value represents a decimal digit or a zero
  19379.    if the value does not. The digit characters are in the range 0 through 9.
  19380.  
  19381.    ──────────────────────────────────────────────────────────────────────────
  19382.    /***********************************************
  19383.    **  Name:         IsItDigit%                  **
  19384.    **  Type:         Function                    **
  19385.    **  Module:       CTOOLS1.C                   **
  19386.    **  Language:     Microsoft QuickC/QuickBASIC **
  19387.    ************************************************
  19388.    *
  19389.    ──────────────────────────────────────────────────────────────────────────
  19390.  
  19391.    * EXAMPLE OF USE:  result% = IsItDigit%(c%)
  19392.  
  19393.    * PARAMETERS:      c%         ASCII character code
  19394.  
  19395.    * VARIABLES:       (none)
  19396.  
  19397.    * MODULE LEVEL
  19398.  
  19399.    *   DECLARATIONS:  #include <ctype.h>         */
  19400.  
  19401.  
  19402.  
  19403.  
  19404.  
  19405.    int isitdigit (c)
  19406.  
  19407.    int c;
  19408.  
  19409.        {
  19410.  
  19411.        return (isdigit(c));
  19412.  
  19413.        }
  19414.  
  19415.  
  19416.  Function: IsItGraph%
  19417.  
  19418.    Determines whether a character is graphic. This function returns a
  19419.    non-zero value if the integer value represents a printable character, not
  19420.    including the space character. These character values are in the range 33
  19421.    through 126.
  19422.  
  19423.    ──────────────────────────────────────────────────────────────────────────
  19424.    /***********************************************
  19425.    **  Name:         IsItGraph%                  **
  19426.    **  Type:         Function                    **
  19427.    **  Module:       CTOOLS1.C                   **
  19428.    **  Language:     Microsoft QuickC/QuickBASIC **
  19429.    ************************************************
  19430.    *
  19431.    * EXAMPLE OF USE:  result% = IsItGraph%(c%)
  19432.    * PARAMETERS:      c%         ASCII character code
  19433.    * VARIABLES:       (none)
  19434.    * MODULE LEVEL
  19435.    *   DECLARATIONS:  #include <ctype.h>         */
  19436.  
  19437.  
  19438.    int isitgraph (c)
  19439.    int c;
  19440.        {
  19441.        return (isgraph(c));
  19442.        }
  19443.    ──────────────────────────────────────────────────────────────────────────
  19444.  
  19445.  
  19446.  Function: IsItLower%
  19447.  
  19448.    Determines whether a character is lowercase. This function returns a
  19449.    non-zero value if the integer value represents a lowercase character or a
  19450.    zero if the value does not. The lowercase characters are in the range a
  19451.    through z.
  19452.  
  19453.    ──────────────────────────────────────────────────────────────────────────
  19454.    /***********************************************
  19455.    **  Name:         IsItLower%                  **
  19456.    **  Type:         Function                    **
  19457.    **  Module:       CTOOLS1.C                   **
  19458.    **  Language:     Microsoft QuickC/QuickBASIC **
  19459.    ************************************************
  19460.    *
  19461.    * EXAMPLE OF USE:  result% = IsItLower%(c%)
  19462.    * PARAMETERS:      c%         ASCII character code
  19463.    * VARIABLES:       (none)
  19464.    * MODULE LEVEL
  19465.    *   DECLARATIONS:  #include <ctype.h>         */
  19466.  
  19467.  
  19468.    int isitlower (c)
  19469.    int c;
  19470.        {
  19471.        return (islower(c));
  19472.        }
  19473.    ──────────────────────────────────────────────────────────────────────────
  19474.  
  19475.  
  19476.  Function: IsItPrint%
  19477.  
  19478.    Determines whether a character is printable. This function returns a
  19479.    non-zero value if the integer value represents a printable character or a
  19480.    zero if the value does not. The printable characters are in the range 32
  19481.    through 126.
  19482.  
  19483.    ──────────────────────────────────────────────────────────────────────────
  19484.    /***********************************************
  19485.    **  Name:         IsItPrint%                  **
  19486.    **  Type:         Function                    **
  19487.    **  Module:       CTOOLS1.C                   **
  19488.    **  Language:     Microsoft QuickC/QuickBASIC **
  19489.    ************************************************
  19490.    *
  19491.    * EXAMPLE OF USE:  result% = IsItPrint%(c%)
  19492.    * PARAMETERS:      c%         ASCII character code
  19493.    * VARIABLES:       (none)
  19494.    * MODULE LEVEL
  19495.    *   DECLARATIONS:  #include <ctype.h>         */
  19496.  
  19497.  
  19498.    int isitprint (c)
  19499.    int c;
  19500.        {
  19501.        return (isprint(c));
  19502.        }
  19503.    ──────────────────────────────────────────────────────────────────────────
  19504.  
  19505.  
  19506.  Function: IsItPunct%
  19507.  
  19508.    Determines whether a character is punctuation. This function returns a
  19509.    non-zero value if the integer value represents a punctuation character or
  19510.    a zero if the value does not. The punctuation characters are in the ranges
  19511.    33 through 47, 59 through 64, 91 through 96, or 123 through 126.
  19512.  
  19513.    ──────────────────────────────────────────────────────────────────────────
  19514.    /***********************************************
  19515.    **  Name:         IsItPunct%                  **
  19516.    **  Type:         Function                    **
  19517.    **  Module:       CTOOLS1.C                   **
  19518.    **  Language:     Microsoft QuickC/QuickBASIC **
  19519.    ************************************************
  19520.    *
  19521.    * EXAMPLE OF USE:  result% = IsItPunct%(c%)
  19522.    * PARAMETERS:      c%         ASCII character code
  19523.    * VARIABLES:       (none)
  19524.    * MODULE LEVEL
  19525.    *   DECLARATIONS:  #include <ctype.h>         */
  19526.  
  19527.  
  19528.    int isitpunct (c)
  19529.    int c;
  19530.        {
  19531.        return (ispunct(c));
  19532.        }
  19533.    ──────────────────────────────────────────────────────────────────────────
  19534.  
  19535.  
  19536.  Function: IsItSpace%
  19537.  
  19538.    Determines whether a character is white space. This function returns a
  19539.    non-zero value if the integer value represents a white-space character or
  19540.    a zero if the value does not. The white-space character values are in the
  19541.    range 9 through 13, and 32.
  19542.  
  19543.    ──────────────────────────────────────────────────────────────────────────
  19544.    /***********************************************
  19545.    **  Name:         IsItSpace%                  **
  19546.    **  Type:         Function                    **
  19547.    **  Module:       CTOOLS1.C                   **
  19548.    **  Language:     Microsoft QuickC/QuickBASIC **
  19549.    ************************************************
  19550.    *
  19551.    * EXAMPLE OF USE:  result% = IsItSpace%(c%)
  19552.    * PARAMETERS:      c%         ASCII character code
  19553.    * VARIABLES:       (none)
  19554.    * MODULE LEVEL
  19555.    *   DECLARATIONS:  #include <ctype.h>         */
  19556.  
  19557.  
  19558.    int isitspace (c)
  19559.    int c;
  19560.        {
  19561.        return (isspace(c));
  19562.        }
  19563.    ──────────────────────────────────────────────────────────────────────────
  19564.  
  19565.  
  19566.  Function: IsItUpper%
  19567.  
  19568.    Determines whether a character is uppercase. This function returns a
  19569.    non-zero value if the integer value represents an uppercase character or a
  19570.    zero if the value does not. The uppercase characters are in the range A
  19571.    through Z.
  19572.  
  19573.    ──────────────────────────────────────────────────────────────────────────
  19574.    /***********************************************
  19575.    **  Name:         IsItUpper%                  **
  19576.    **  Type:         Function                    **
  19577.    **  Module:       CTOOLS1.C                   **
  19578.    **  Language:     Microsoft QuickC/QuickBASIC **
  19579.    ************************************************
  19580.    *
  19581.    * EXAMPLE OF USE:   result% = IsItUpper%(c%)
  19582.    * PARAMETERS:       c%         ASCII character code
  19583.    * VARIABLES:        (none)
  19584.    * MODULE LEVEL
  19585.    *   DECLARATIONS:   #include <ctype.h>         */
  19586.  
  19587.    int isitupper (c)
  19588.    int c;
  19589.        {
  19590.        return (isupper(c));
  19591.        }
  19592.    ──────────────────────────────────────────────────────────────────────────
  19593.  
  19594.  
  19595.  Function: IsItXDigit%
  19596.  
  19597.    Determines whether a character is a hexadecimal digit. This function
  19598.    returns a non-zero value if the integer value represents a hexadecimal
  19599.    character or a zero if the value does not. The hexadecimal characters are
  19600.    in the ranges 0 through 9, a through f, or A through F.
  19601.  
  19602.    ──────────────────────────────────────────────────────────────────────────
  19603.    /***********************************************
  19604.    **  Name:         IsItXDigit%                 **
  19605.    **  Type:         Function                    **
  19606.    **  Module:       CTOOLS1.C                   **
  19607.    **  Language:     Microsoft QuickC/QuickBASIC **
  19608.    ************************************************
  19609.    *
  19610.    * EXAMPLE OF USE:  result% = IsItXDigit%(c%)
  19611.    * PARAMETERS:      c%         ASCII character code
  19612.    * VARIABLES:       (none)
  19613.    * MODULE LEVEL
  19614.    *   DECLARATIONS:  #include <ctype.h>         */
  19615.  
  19616.  
  19617.    int isitxdigit (c)
  19618.    int c;
  19619.        {
  19620.        return (isxdigit(c));
  19621.        }
  19622.    ──────────────────────────────────────────────────────────────────────────
  19623.  
  19624.  
  19625.  Subprogram: MovBytes
  19626.  
  19627.    Calls the QuickC movedata function to quickly copy a block of bytes from
  19628.    any address in memory to any other.
  19629.  
  19630.    ──────────────────────────────────────────────────────────────────────────
  19631.    /***********************************************
  19632.    **  Name:         MovBytes                    **
  19633.    **  Type:         Subprogram                  **
  19634.    **  Module:       CTOOLS1.C                   **
  19635.    **  Language:     Microsoft QuickC/QuickBASIC **
  19636.    ************************************************
  19637.    *
  19638.    *  Moves bytes from a source segment and offset
  19639.    *  location in memory to a destination segment and
  19640.    *  offset location.
  19641.    *
  19642.    *  EXAMPLE OF USE:  MovBytes sseg%, soff%, dseg%, doff%, nbytes%
  19643.    *  PARAMETERS:      sseg%      Source segment address of bytes to be moved
  19644.    *                   soff%      Source offset address of bytes to be moved
  19645.    *                   dseg%      Destination segment address of bytes to be m
  19646.    *                   doff%      Destination offset address of bytes to be mo
  19647.    *                   nbytes%    Number of bytes to be moved
  19648.    * VARIABLES:        (none)
  19649.    * MODULE LEVEL
  19650.    *   DECLARATIONS:   #include <memory.h>     */
  19651.  
  19652.  
  19653.    void movbytes (srcseg, srcoff, destseg, destoff, nbytes)
  19654.    unsigned int *srcseg, *srcoff, *destseg, *destoff, *nbytes;
  19655.        {
  19656.        movedata(*srcseg, *srcoff, *destseg, *destoff, *nbytes);
  19657.        }
  19658.    ──────────────────────────────────────────────────────────────────────────
  19659.  
  19660.  
  19661.  Subprogram: MovWords
  19662.  
  19663.    Moves a block of words from any memory location to any other. This
  19664.    subprogram calls the QuickC movedata function to quickly copy a block of
  19665.    words from any address in memory to any other.
  19666.  
  19667.    ──────────────────────────────────────────────────────────────────────────
  19668.    /***********************************************
  19669.    **  Name:         MovWords                    **
  19670.    **  Type:         Subprogram                  **
  19671.    **  Module:       CTOOLS1.C                   **
  19672.    **  Language:     Microsoft QuickC/QuickBASIC **
  19673.    ************************************************
  19674.    *
  19675.    *  Moves words from a source segment and offset
  19676.    *  location in memory to a destination segment and
  19677.    *  offset location.
  19678.    *
  19679.    * EXAMPLE OF USE:  MovWords sseg%, soff%, dseg%, doff%, nbytes%
  19680.    * PARAMETERS:      sseg%      Source segment address of words to be moved
  19681.    *                  soff%      Source offset address of words to be moved
  19682.    *                  dseg%      Destination segment address of words to be mo
  19683.    *                  doff%      Destination offset address of words to be mov
  19684.    *                  nwords%    Number of words to be moved
  19685.    * VARIABLES:       (none)
  19686.    * MODULE LEVEL
  19687.    *   DECLARATIONS:  #include <memory.h>        */
  19688.  
  19689.  
  19690.    void movwords (srcseg, srcoff, destseg, destoff, nwords)
  19691.    unsigned int *srcseg, *srcoff, *destseg, *destoff, *nwords;
  19692.        {
  19693.        unsigned int nbytes;
  19694.  
  19695.        nbytes = *nwords + *nwords;
  19696.        movedata(*srcseg, *srcoff, *destseg, *destoff, nbytes);
  19697.        }
  19698.    ──────────────────────────────────────────────────────────────────────────
  19699.  
  19700.  
  19701.  
  19702.  ────────────────────────────────────────────────────────────────────────────
  19703.  CDEMO2.BAS AND CTOOLS2.C
  19704.  
  19705.    The CDEMO2.BAS program is a QuickBASIC program that demonstrates the
  19706.    proper declaration and calling of the QuickC routines presented in the
  19707.    CTOOLS2.C toolbox.
  19708.  
  19709.    The MenuString% function creates a horizontal bar menu, similar to the
  19710.    menu line at the top of the display in the QuickBASIC environment. The
  19711.    function call returns the number of the word selected. You can place the
  19712.    menu bar anywhere on the screen, and it can contain any number of one-word
  19713.    choices. The first letter of each word must be uppercase, and no more than
  19714.    two words can have the same first letter.
  19715.  
  19716.    The BitShiftLeft% and BitShiftRight% functions let you shift all the
  19717.    bits in a string of bytes one position to the left or right. Each function
  19718.    returns the bit shifted off the end of the string and shifts in a zero at
  19719.    the other end.
  19720.  
  19721.    The NumberOfBits& function returns the number of bits in all the bytes of
  19722.    a string. You could do this by using the bit-shifting functions and adding
  19723.    up the returned values, but the NumberOfBits& function is much faster.
  19724.    The string contents are unchanged by the function.
  19725.  
  19726.    The PackWord and UnPackWord subprograms let you pack and unpack two byte
  19727.    values (integers in the range 0 through 255) into an integer variable.
  19728.    This can be accomplished using the QuickBASIC math functions (although it
  19729.    gets complicated when dealing with negative numbers), but QuickC has
  19730.    features ideal for performing these types of data manipulations. By
  19731.    declaring a union of a two-byte structure with an integer, the bytes can
  19732.    simply be moved into place instead of calculated.
  19733.  
  19734.    The TextGet and TextPut subprograms let you quickly save and restore
  19735.    rectangular areas of the text-mode screen. These routines are similar in
  19736.    concept to the QuickBASIC GET and PUT statements that save and restore
  19737.    rectangular areas of graphics-mode screens. Unlike the GET and PUT
  19738.    statements, though, the rectangular area restored can be of a different
  19739.    shape than the area that was saved. The total number of bytes must be
  19740.    identical, but the width and height of the area can differ. The program
  19741.    module first prints a line of text that is saved into a string using
  19742.    TextGet and is then restored in a vertical (one column wide) rectangular
  19743.    area.
  19744.  
  19745.    Name                     Type    Description
  19746.    ──────────────────────────────────────────────────────────────────────────
  19747.    CDEMO2.BAS                      QuickBASIC program module
  19748.    CTOOLS2.C                       C-language toolbox containing
  19749.                                     functions/subprograms
  19750.    BitShiftLeft%           Func    Shifts all bits in a string left one bit
  19751.    BitShiftRight%          Func    Shifts all bits in a string right one bit
  19752.    MenuString%             Func    Bar menu and user response function
  19753.    NumberOfBits&           Func    Determines number of 1 bits in a string
  19754.    PackWord                Sub     Packs two bytes into an integer value
  19755.    TextGet                 Sub     Saves characters and attributes from area
  19756.                                     of screen
  19757.    TextPut                 Sub     Restores text from TextGet to screen
  19758.    UnPackWord              Sub     Unpacks values from high and low bytes
  19759.    ──────────────────────────────────────────────────────────────────────────
  19760.  
  19761.  
  19762.  Program Module: CDEMO2
  19763.  
  19764.    ──────────────────────────────────────────────────────────────────────────
  19765.      ' ************************************************
  19766.      ' **  Name:          CDEMO2                     **
  19767.      ' **  Type:          Program                    **
  19768.      ' **  Module:        CDEMO2.BAS                 **
  19769.      ' **  Language:      Microsoft QuickBASIC 4.00  **
  19770.      ' ************************************************
  19771.      '
  19772.      ' USAGE:           No command line parameters
  19773.      ' REQUIREMENTS:    CGA
  19774.      '                  MIXED.QLB/.LIB
  19775.      ' .MAK FILE:       (none)
  19776.      ' PARAMETERS:      (none)
  19777.      ' VARIABLES:       m$            Menu string
  19778.      '                  word%         Integer to be packed with two bytes
  19779.      '                  hi%           Most significant byte unpacked from an
  19780.      '                                integer
  19781.      '                  lo%           Least significant byte unpacked from an
  19782.      '                                integer
  19783.      '                  a$            Workspace for TextGet and TextPut
  19784.      '                  b$            Workspace for TextGet and TextPut
  19785.      '                  n%            Timing constant for TextPut demonstratio
  19786.      '                  row%          Row location to put small "window" using
  19787.      '                                TextPut
  19788.      '                  col%          Column location to put small "window" us
  19789.      '                                TextPut
  19790.      '                  t0            Timer variable
  19791.      '                  x$            String variable for bit shifting
  19792.      '                  i%            Looping index
  19793.  
  19794.      ' Functions
  19795.        DECLARE FUNCTION MenuString% CDECL (row%, col%, a$)
  19796.        DECLARE FUNCTION BitShiftleft% CDECL (a$)
  19797.        DECLARE FUNCTION BitShiftRight% CDECL (a$)
  19798.        DECLARE FUNCTION NumberOfBits& CDECL (a$)
  19799.  
  19800.      ' Subprograms
  19801.        DECLARE SUB PackWord CDECL (word%, hi%, lo%)
  19802.        DECLARE SUB UnPackWord CDECL (word%, hi%, lo%)
  19803.        DECLARE SUB TextGet CDECL (r1%, c1%, r2%, c2%, a$)
  19804.        DECLARE SUB TextPut CDECL (r1%, c1%, r2%, c2%, a$)
  19805.  
  19806.      ' Build menu string
  19807.        m$ = "Packword Unpackword Textget Textput "
  19808.        m$ = m$ + "Bitshiftleft Bitshiftright Numberofbits Quit"
  19809.  
  19810.      ' Let user repeatedly select the demonstrations
  19811.        DO
  19812.            COLOR 15, 1
  19813.            CLS
  19814.            PRINT
  19815.            PRINT
  19816.            PRINT "MenuString function..."
  19817.            PRINT
  19818.            PRINT "Select one of the CTOOLS2 demonstrations by ";
  19819.            PRINT "pressing the Left arrow,"
  19820.            PRINT "Right arrow, first letter of the choice, or Enter keys."
  19821.  
  19822.          ' Use MenuString to choose demonstrations
  19823.            SELECT CASE MenuString%(1, 1, m$)
  19824.  
  19825.          ' PackWord demonstration
  19826.            CASE 1
  19827.  
  19828.                CLS
  19829.                PRINT "PackWord word%, 255, 255  ...  word% = ";
  19830.                PackWord word%, 255, 255
  19831.                PRINT word%
  19832.                PRINT "PackWord word%,   0,   1  ...  word% = ";
  19833.                PackWord word%, 0, 1
  19834.                PRINT word%
  19835.                PRINT "PackWord word%,   1,   0  ...  word% = ";
  19836.                PackWord word%, 1, 0
  19837.                PRINT word%
  19838.  
  19839.                PRINT
  19840.                PRINT "Press any key to continue..."
  19841.  
  19842.                DO
  19843.                LOOP UNTIL INKEY$ <> ""
  19844.  
  19845.          ' UnPackWord demonstration
  19846.            CASE 2
  19847.  
  19848.                CLS
  19849.                PRINT "UnPackWord  -1, hi%, lo%  ...  hi%, lo% =";
  19850.                UnPackWord -1, hi%, lo%
  19851.                PRINT hi%; lo%
  19852.                PRINT "UnPackWord   1, hi%, lo%  ...  hi%, lo% =";
  19853.                UnPackWord 1, hi%, lo%
  19854.                PRINT hi%; lo%
  19855.                PRINT "UnPackWord 256, hi%, lo%  ...  hi%, lo% =";
  19856.                UnPackWord 256, hi%, lo%
  19857.                PRINT hi%; lo%
  19858.  
  19859.                PRINT
  19860.                PRINT "Press any key to continue..."
  19861.  
  19862.                DO
  19863.                LOOP UNTIL INKEY$ <> ""
  19864.  
  19865.          ' TextGet and TextPut demonstration
  19866.            CASE 3, 4
  19867.  
  19868.              ' TextGet a line of text
  19869.                CLS
  19870.                PRINT "A Vertical Message"
  19871.                a$ = SPACE$(36)
  19872.                TextGet 1, 1, 1, 18, a$
  19873.  
  19874.              ' TextPut it back, but stretch it vertically
  19875.                TextPut 6, 1, 23, 1, a$
  19876.  
  19877.              ' Now just a normal line of text at top
  19878.                LOCATE 1, 1
  19879.                PRINT "TextGet and TextPut - Press any key to stop"
  19880.  
  19881.              ' Create first of two colorful text patterns
  19882.                COLOR 14, 4
  19883.                LOCATE 13, 13, 0
  19884.                PRINT CHR$(201); CHR$(205); CHR$(209); CHR$(205); CHR$(187)
  19885.                LOCATE 14, 13, 0
  19886.                PRINT CHR$(199); CHR$(196); CHR$(197); CHR$(196); CHR$(182)
  19887.                LOCATE 15, 13, 0
  19888.                PRINT CHR$(200); CHR$(205); CHR$(207); CHR$(205); CHR$(188)
  19889.                a$ = SPACE$(30)
  19890.                TextGet 13, 13, 15, 17, a$
  19891.  
  19892.              ' Create second of two colorful text patterns
  19893.                COLOR 10, 1
  19894.                LOCATE 13, 13, 0
  19895.                PRINT CHR$(218); CHR$(196); CHR$(210); CHR$(196); CHR$(191)
  19896.                LOCATE 14, 13, 0
  19897.                PRINT CHR$(198); CHR$(205); CHR$(206); CHR$(205); CHR$(181)
  19898.                LOCATE 15, 13, 0
  19899.                PRINT CHR$(192); CHR$(196); CHR$(208); CHR$(196); CHR$(217)
  19900.                b$ = SPACE$(30)
  19901.                TextGet 13, 13, 15, 17, b$
  19902.  
  19903.              ' Randomly pop up little "windows"
  19904.                n% = 0
  19905.                DO
  19906.                    row% = INT(RND * 21 + 3)
  19907.                    col% = INT(RND * 73 + 4)
  19908.                    TextPut row%, col%, row% + 2, col% + 4, a$
  19909.                    row% = INT(RND * 21 + 3)
  19910.                    col% = INT(RND * 73 + 4)
  19911.                    TextPut row%, col%, row% + 2, col% + 4, b$
  19912.                    IF n% < 10 THEN
  19913.                        n% = n% + 1
  19914.                        t0 = TIMER
  19915.                        DO
  19916.                        LOOP UNTIL TIMER > t0 + (10 - n%) / 10
  19917.                    END IF
  19918.                LOOP UNTIL INKEY$ <> ""
  19919.  
  19920.          ' BitShiftLeft demonstration
  19921.            CASE 5
  19922.  
  19923.                CLS
  19924.                x$ = "This string will be shifted left 8 bits"
  19925.                PRINT x$
  19926.                FOR i% = 1 TO 8
  19927.                    PRINT "bit ="; BitShiftleft%(x$)
  19928.                NEXT i%
  19929.                PRINT x$
  19930.  
  19931.                PRINT
  19932.                PRINT "Press any key to continue..."
  19933.  
  19934.                DO
  19935.                LOOP UNTIL INKEY$ <> ""
  19936.  
  19937.          ' BitShiftRight demonstration
  19938.            CASE 6
  19939.  
  19940.                CLS
  19941.                x$ = "This string will be shifted right 8 bits"
  19942.                PRINT x$
  19943.                FOR i% = 1 TO 8
  19944.                    PRINT "bit ="; BitShiftRight%(x$)
  19945.                NEXT i%
  19946.                PRINT x$
  19947.  
  19948.                PRINT
  19949.                PRINT "Press any key to continue..."
  19950.  
  19951.                DO
  19952.                LOOP UNTIL INKEY$ <> ""
  19953.  
  19954.          ' BitShiftRight demonstration
  19955.            CASE 7
  19956.  
  19957.                CLS
  19958.                x$ = "The number of bits in this string is ..."
  19959.                PRINT x$
  19960.                PRINT NumberOfBits&(x$)
  19961.  
  19962.                PRINT
  19963.                PRINT "Press any key to continue..."
  19964.  
  19965.                DO
  19966.                LOOP UNTIL INKEY$ <> ""
  19967.  
  19968.          ' Must be time to quit
  19969.            CASE ELSE
  19970.                COLOR 7, 0
  19971.                CLS
  19972.                END
  19973.            END SELECT
  19974.        LOOP
  19975.    ──────────────────────────────────────────────────────────────────────────
  19976.  
  19977.  
  19978.  Toolbox: CTOOLS2.C
  19979.  
  19980.    The CTOOLS2.C toolbox provides a collection of functions and subprograms
  19981.    that perform tasks that QuickC is well suited for.
  19982.  
  19983.    You must enter the following block of lines into the first lines of the
  19984.    CTOOLS2.C source-code file, immediately before the functions and
  19985.    subprograms. Note that the definition for VIDEO_START should be changed to
  19986.    0xb0000000 for monochrome operation.
  19987.  
  19988.  
  19989.      #include <ctype.h>
  19990.      #include <conio.h>
  19991.  
  19992.      #define VIDEO_START         0xb8000000
  19993.  
  19994.      #define BLACK_ON_CYAN       48
  19995.      #define RED_ON_CYAN         52
  19996.      #define BRIGHT_WHITE_ON_RED 79
  19997.  
  19998.      #define ENTER               13
  19999.      #define RIGHT_ARROW         77
  20000.      #define LEFT_ARROW          75
  20001.  
  20002.      /* Definition of the QuickBASIC string descriptor structure */
  20003.      struct bas_str
  20004.          {
  20005.          int  sd_len;
  20006.          char *sd_addr;
  20007.          };
  20008.  
  20009.  
  20010.  Function: BitShiftLeft%
  20011.  
  20012.    Shifts all bits in a QuickBASIC string variable to the left one bit
  20013.    position. The number of bits in a string is eight times the length of the
  20014.    string. The function returns the leftmost bit of the first character of
  20015.    the string.
  20016.  
  20017.    ──────────────────────────────────────────────────────────────────────────
  20018.    /***********************************************
  20019.    **  Name:         BitShiftLeft%               **
  20020.    **  Type:         Function                    **
  20021.    **  Module:       CTOOLS2.C                   **
  20022.    **  Language:     Microsoft QuickC/QuickBASIC **
  20023.    ************************************************
  20024.    *
  20025.    * Shifts all bits in a QuickBASIC string one bit
  20026.    * to the left.  The leftmost bit is returned, and
  20027.    * the rightmost bit is set to zero.
  20028.    *
  20029.    * EXAMPLE OF USE:  bit% = BitShiftLeft%(bit$)
  20030.    * PARAMETERS:      bit$       String containing a bit pattern
  20031.    * VARIABLES:       len        Length of the string (number of bytes)
  20032.    *                  str        Pointer to string contents
  20033.    *                  i          Looping index to each byte of the string
  20034.    *                  carry      Bit carried over from byte to byte
  20035.    *                  the_byte   Working copy of each byte of the string
  20036.    *
  20037.    * Definition of the QuickBASIC string descriptor structure
  20038.    *    struct bas_str
  20039.    *        {
  20040.    *        int  sd_len;
  20041.    *        char *sd_addr;
  20042.    *        };                                    */
  20043.  
  20044.  
  20045.    int bitshiftleft (basic_string)
  20046.    struct bas_str *basic_string;
  20047.        {
  20048.        int len = basic_string->sd_len;
  20049.        unsigned char *str = basic_string->sd_addr;
  20050.        int i, carry;
  20051.        unsigned int the_byte;
  20052.  
  20053.        for (i=len-1, carry=0; i>=0; i--)
  20054.            {
  20055.            the_byte = *(str + i);
  20056.            *(str + i) = (the_byte << 1) + carry;
  20057.            carry = the_byte >> 7;
  20058.            }
  20059.  
  20060.        return (carry);
  20061.        }
  20062.    ──────────────────────────────────────────────────────────────────────────
  20063.  
  20064.  
  20065.  Function: BitShiftRight%
  20066.  
  20067.    Shifts all bits in a QuickBASIC string variable to the right one bit
  20068.    position. The number of bits in a string is eight times the length of the
  20069.    string. The function returns the rightmost bit of the last character of
  20070.    the string.
  20071.  
  20072.    ──────────────────────────────────────────────────────────────────────────
  20073.    /***********************************************
  20074.    **  Name:         BitShiftRight%              **
  20075.    **  Type:         Function                    **
  20076.    **  Module:       CTOOLS2.C                   **
  20077.    **  Language:     Microsoft QuickC/QuickBASIC **
  20078.    ************************************************
  20079.    *
  20080.    * Shifts all bits in a QuickBASIC string one bit to
  20081.    * the right.  The rightmost bit is returned, and the
  20082.    * leftmost bit is set to zero.
  20083.    *
  20084.    * EXAMPLE OF USE:  bit% = BitShiftRight%(bit$)
  20085.    * PARAMETERS:      bit$       String containing a bit pattern
  20086.    * VARIABLES:       len        Length of the string (number of bytes)
  20087.    *                  str        Pointer to string contents
  20088.    *                  i          Looping index to each byte of the string
  20089.    *                  carry      Bit carried over from byte to byte
  20090.    *                  the_byte   Working copy of each byte of the string
  20091.    *
  20092.    * Definition of the QuickBASIC string descriptor structure
  20093.    *    struct bas_str
  20094.    *        {
  20095.    *        int  sd_len;
  20096.    *        char *sd_addr;
  20097.    *        };                                             */
  20098.  
  20099.    int bitshiftright (basic_string)
  20100.    struct bas_str *basic_string;
  20101.        {
  20102.        int len = basic_string->sd_len;
  20103.        unsigned char *str = basic_string->sd_addr;
  20104.        int i, carry;
  20105.        unsigned int the_byte;
  20106.  
  20107.        for (i=0, carry=0; i<len; i++)
  20108.            {
  20109.            the_byte = *(str + i);
  20110.            *(str + i) = (the_byte >> 1) + carry;
  20111.            carry = (the_byte & 1) << 7;
  20112.            }
  20113.  
  20114.        if (carry)
  20115.            return(1);
  20116.        else
  20117.            return(0);
  20118.        }
  20119.    ──────────────────────────────────────────────────────────────────────────
  20120.  
  20121.  
  20122.  Function: MenuString%
  20123.  
  20124.    Creates a horizontal menu bar, highlights the one-word choices, and
  20125.    returns the number of the choice selected when the Enter key is pressed.
  20126.    The menu is highlighted using the same color scheme as the main pull-down
  20127.    menu bars of both the QuickBASIC and QuickC environments.
  20128.  
  20129.    ──────────────────────────────────────────────────────────────────────────
  20130.    /***********************************************
  20131.    **  Name:         MenuString%                 **
  20132.    **  Type:         Function                    **
  20133.    **  Module:       CTOOLS2.C                   **
  20134.    **  Language:     Microsoft QuickC/QuickBASIC **
  20135.    ************************************************
  20136.    *
  20137.    *  Displays a horizontal bar menu and waits for a
  20138.    *  response from the user.  Returns the number of
  20139.    *  the word selected from the string.
  20140.    * EXAMPLE OF USE:  choice% = MenuString%(row%, col%, menu$)
  20141.    * PARAMETERS:      row%       Row location to display the menu string
  20142.    *                  col%       Column location to display the menu string
  20143.    *                  menu$      String containing list of words representing
  20144.    *                             choices
  20145.    * VARIABLES:       len        Length of the menu string
  20146.    *                  str        Pointer to string contents
  20147.    *                  vidptr     Pointer to video memory
  20148.    *                  attribute  Index into string
  20149.    *                  character  Character from keyboard press
  20150.    *                  both       Combination of a character and its attribute
  20151.    *                  i          Looping index
  20152.    *                  j          Looping index
  20153.    *                  k          Looping index
  20154.    *                  c          Looping index
  20155.    *                  choice     Menu selection number
  20156.    *                  wordnum    Sequential count of each word in the menu str
  20157.    *                  refresh    Signals to redraw the menu string
  20158.    * #include <ctype.h>
  20159.    * #include <conio.h>
  20160.    * #define VIDEO_START         0xb8000000
  20161.    * #define BLACK_ON_CYAN       48
  20162.    * #define RED_ON_CYAN         52
  20163.    * #define BRIGHT_WHITE_ON_RED 79
  20164.    * #define ENTER               13
  20165.    * #define RIGHT_ARROW         77
  20166.    * #define LEFT_ARROW          75
  20167.    *
  20168.    * Definition of the QuickBASIC string descriptor structure
  20169.    *    struct bas_str
  20170.    *        {
  20171.    *        int  sd_len;
  20172.    *        char *sd_addr;
  20173.    *        };                                        */
  20174.  
  20175.  
  20176.    int menustring (row, col, basic_string)
  20177.    int *row, *col;
  20178.    struct bas_str *basic_string;
  20179.        {
  20180.        int len;
  20181.        char * str;
  20182.        int far * vidptr;
  20183.        int attribute, character, both;
  20184.        int i, j, k, c;
  20185.        int choice, wordnum;
  20186.        int refresh;
  20187.        void packword();
  20188.  
  20189.        /* Initialize variables */
  20190.        len = basic_string->sd_len;
  20191.        str = basic_string->sd_addr;
  20192.        vidptr = (int far *) VIDEO_START + (*row - 1) * 80 + (*col - 1);
  20193.        choice = 1;
  20194.        refresh = 1;
  20195.  
  20196.        /* Loop until return() statement */
  20197.        while (1)
  20198.            {
  20199.  
  20200.            /* Display the string only if refresh is non-zero */
  20201.            if (refresh)
  20202.                {
  20203.                refresh = 0;
  20204.  
  20205.                /* Loop through each character of the string */
  20206.                for (wordnum = 0, i=0; i<len; i++)
  20207.                    {
  20208.  
  20209.                    /* Set the character and default attribute */
  20210.                    character = str[i];
  20211.                    attribute = BLACK_ON_CYAN;
  20212.  
  20213.                    /* Uppercase? */
  20214.                    if (isupper(character))
  20215.                        {
  20216.                        wordnum++;
  20217.                        attribute = RED_ON_CYAN;
  20218.                        }
  20219.  
  20220.                    /* In the middle of the current selection? */
  20221.                    if (wordnum == choice && character != ' ')
  20222.                        attribute = BRIGHT_WHITE_ON_RED;
  20223.  
  20224.                    /* Move data to video */
  20225.                    packword(&both, &attribute, &character);
  20226.                    vidptr[i] = both;
  20227.                    }
  20228.                }
  20229.  
  20230.            /* Check for any key presses */
  20231.            if (kbhit())
  20232.                {
  20233.  
  20234.                /* Get the key code and process it */
  20235.                switch (c = getch())
  20236.                    {
  20237.  
  20238.                    /* Return the choice when Enter is pressed */
  20239.                    case ENTER:
  20240.                        return (choice);
  20241.  
  20242.                    /* Highlight next choice if Right arrow is pressed */
  20243.                    case RIGHT_ARROW:
  20244.                        if (choice < wordnum)
  20245.                            {
  20246.                            choice++;
  20247.                            refresh = 1;
  20248.                            }
  20249.                        break;
  20250.  
  20251.                    /* Highlight previous choice if Left arrow is pressed */
  20252.                    case LEFT_ARROW:
  20253.                        if (choice > 1)
  20254.                            {
  20255.                            choice--;
  20256.                            refresh = 1;
  20257.                            }
  20258.                        break;
  20259.  
  20260.                    /* Check for match on first character of each word */
  20261.                    default:
  20262.                        c = _toupper(c);
  20263.                        for (k=0, j=0; j<len; j++)
  20264.                            {
  20265.  
  20266.                            /* Each choice starts at an uppercase char */
  20267.                            if (isupper(str[j]))
  20268.                                k++;
  20269.  
  20270.                            /* Match if same char and not current choice */
  20271.                            if (str[j] == c && k != choice)
  20272.                                {
  20273.                                choice = k;
  20274.                                refresh = 1;
  20275.                                break;
  20276.                                }
  20277.                            }
  20278.                        break;
  20279.                    }
  20280.                }
  20281.            }
  20282.        }
  20283.    ──────────────────────────────────────────────────────────────────────────
  20284.  
  20285.  
  20286.  Function: NumberOfBits&
  20287.  
  20288.    Returns the number of bits in a string without altering its contents.
  20289.  
  20290.    ──────────────────────────────────────────────────────────────────────────
  20291.    /***********************************************
  20292.    **  Name:         NumberOfBits&               **
  20293.    **  Type:         Function                    **
  20294.    **  Module:       CTOOLS2.C                   **
  20295.    **  Language:     Microsoft QuickC/QuickBASIC **
  20296.    ************************************************
  20297.    *
  20298.    * Counts the 1 bits in a QuickBASIC string.
  20299.    *
  20300.    * EXAMPLE OF USE:  count& = NumberOfBits&(a$)
  20301.    * PARAMETERS:      a$         String containing bits to be counted
  20302.    * VARIABLES:       len        Length of the string
  20303.    *                  str        Pointer to string contents
  20304.    *                  i          Looping index to each byte
  20305.    *                  the_byte   Working copy of each byte of the string
  20306.    *                  count      Count of the bits
  20307.    *
  20308.    * Definition of the QuickBASIC string descriptor structure
  20309.    *    struct bas_str
  20310.    *        {
  20311.    *        int  sd_len;
  20312.    *        char *sd_addr;
  20313.    *        };                                           */
  20314.  
  20315.  
  20316.    long numberofbits (basic_string)
  20317.    struct bas_str *basic_string;
  20318.        {
  20319.        int len = basic_string->sd_len;
  20320.        unsigned char *str = basic_string->sd_addr;
  20321.        int i,the_byte;
  20322.        long count = 0;
  20323.  
  20324.        for (i=0; i<len; i++)
  20325.            {
  20326.            the_byte = *(str+i);
  20327.            while (the_byte)
  20328.                {
  20329.                count += (the_byte & 1);
  20330.                the_byte >>= 1;
  20331.                }
  20332.            }
  20333.        return (count);
  20334.        }
  20335.    ──────────────────────────────────────────────────────────────────────────
  20336.  
  20337.  
  20338.  Subprogram: PackWord
  20339.  
  20340.    Packs two bytes into an integer value. For example, the high and low (most
  20341.    significant and least significant) bytes of the integer value 258 are 1
  20342.    and 2. QuickBASIC can pack two values into an integer by multiplying the
  20343.    first value by 256 and adding the second. This works well for small byte
  20344.    values but becomes awkward when the high byte is 128 or greater. In such
  20345.    cases, the resulting integer is a negative number.
  20346.  
  20347.    PackWord uses the QuickC union and structure data definition features to
  20348.    pack the byte values using simple data moves in memory.
  20349.  
  20350.    ──────────────────────────────────────────────────────────────────────────
  20351.    /***********************************************
  20352.    **  Name:         PackWord                    **
  20353.    **  Type:         Subprogram                  **
  20354.    **  Module:       CTOOLS2.C                   **
  20355.    **  Language:     Microsoft QuickC/QuickBASIC **
  20356.    ************************************************
  20357.    *
  20358.    *  Packs two byte values into the high and low
  20359.    *  bytes of an integer (word).
  20360.    *
  20361.    * EXAMPLE OF USE:  PackWord hiloword%, hibyte%, lobyte%
  20362.    * PARAMETERS:      hiloword%  Integer word to pack the two bytes into
  20363.    *                  hibyte%    Integer value of the most significant byte
  20364.    *                  lobyte%    Integer value of the least significant byte
  20365.    * VARIABLES:       both       A union of a two-byte structure and an intege
  20366.    *                             variable                                */
  20367.  
  20368.  
  20369.    void packword (hiloword, hibyte, lobyte)
  20370.    int *hiloword, *hibyte, *lobyte;
  20371.        {
  20372.        union
  20373.            {
  20374.            struct
  20375.                {
  20376.                unsigned char lo;
  20377.                unsigned char hi;
  20378.                } bytes;
  20379.            int hilo;
  20380.            } both;
  20381.  
  20382.        both.bytes.hi = *hibyte;
  20383.        both.bytes.lo = *lobyte;
  20384.        *hiloword = both.hilo;
  20385.        }
  20386.    ──────────────────────────────────────────────────────────────────────────
  20387.  
  20388.  
  20389.  Subprogram: TextGet
  20390.  
  20391.    Copies a rectangular area of the text screen into a string variable. This
  20392.    is similar in concept to the graphics-oriented GET statement, except that
  20393.    this subprogram copies text-mode screen data. To redisplay the text
  20394.    anywhere on the screen, use the TextPut subprogram.
  20395.  
  20396.    The string variable must be exactly the right length for the amount of
  20397.    data to be copied, or the call will be ignored. There are two bytes of
  20398.    screen memory for each character displayed (the character and its color
  20399.    attribute), so the string must contain width * height * 2 bytes. For
  20400.    example, to save the area from row 3, column 4, to row 5, column 9, the
  20401.    string length must be 3 * 6 * 2, or 36 characters. The SPACE$ statement is
  20402.    ideal for preparing strings for this call. For example, a$ = SPACE$(36)
  20403.    makes the previous string the correct length.
  20404.  
  20405.    ──────────────────────────────────────────────────────────────────────────
  20406.    /***********************************************
  20407.    **  Name:         TextGet                     **
  20408.    **  Type:         Subprogram                  **
  20409.    **  Module:       CTOOLS2.C                   **
  20410.    **  Language:     Microsoft QuickC/QuickBASIC **
  20411.    ************************************************
  20412.    *
  20413.    * Saves characters and attributes from a rectangular
  20414.    * area of the screen.
  20415.    *
  20416.    * EXAMPLE OF USE:  TextGet r1%, c1%, r2%, c2%, a$
  20417.    * PARAMETERS:      r1%        Pointer to row at upper left corner
  20418.    *                  c1%        Pointer to column at upper left corner
  20419.    *                  r2%        Pointer to row at lower right corner
  20420.    *                  c2%        Pointer to column at lower right corner
  20421.    *                  a$         String descriptor, where screen contents
  20422.    *                             will be stored
  20423.    * VARIABLES:       len        Length of string
  20424.    *                  str        Pointer to string contents
  20425.    *                  video      Pointer to video memory
  20426.    *                  i          Index into string
  20427.    *                  row        Looping index
  20428.    *                  col        Looping index
  20429.    * #define VIDEO_START         0xb8000000
  20430.    *
  20431.    * Definition of the QuickBASIC string descriptor structure
  20432.    *
  20433.    *  struct bas_str
  20434.    *      {
  20435.    *      int  sd_len;
  20436.    *      char *sd_addr;
  20437.    *      };                                           */
  20438.  
  20439.  
  20440.    void textget (r1,c1,r2,c2,basic_string)
  20441.    int *r1,*c1,*r2,*c2;
  20442.    struct bas_str *basic_string;
  20443.        {
  20444.        int len;
  20445.        int * str;
  20446.        int far * video;
  20447.        int i,row,col;
  20448.  
  20449.        len = basic_string->sd_len;
  20450.        str = (int *) basic_string->sd_addr;
  20451.        video = (int far *) VIDEO_START;
  20452.  
  20453.        if (len == (*r2 - *r1 + 1) * (*c2 - *c1 + 1) * 2)
  20454.            for (row = *r1 - 1, i = 0; row < *r2; row++)
  20455.                for (col = *c1 - 1; col < *c2; col++)
  20456.                    str[i++] = video[row * 80 + col];
  20457.        }
  20458.    ──────────────────────────────────────────────────────────────────────────
  20459.  
  20460.  
  20461.  Subprogram: TextPut
  20462.  
  20463.    Restores a rectangular area of a text screen from a string variable
  20464.    previously used to copy screen contents via the TextGet subprogram. This
  20465.    is similar to the graphics-oriented PUT statement, except that this
  20466.    subprogram copies text-mode screen data.
  20467.  
  20468.    The string variable must be exactly the right length for the amount of
  20469.    data to be copied onto the screen, or the call will be ignored. There are
  20470.    two bytes of screen memory for each character displayed (the character and
  20471.    its color attribute), so the string must contain width * height * 2 bytes.
  20472.    See the TextGet subprogram for more details on string length
  20473.    requirements.
  20474.  
  20475.    The shape of the restored area can differ from the original area as long
  20476.    as the total number of bytes is the same. For example, an area three
  20477.    characters wide by four characters high can be copied using TextGet and
  20478.    then placed back on the screen in an area two wide by six high. As long as
  20479.    the width times the height remains constant, TextPut will move the data
  20480.    onto the screen.
  20481.  
  20482.    ──────────────────────────────────────────────────────────────────────────
  20483.    /***********************************************
  20484.    **  Name:         TextPut                     **
  20485.    **  Type:         Subprogram                  **
  20486.    **  Module:       CTOOLS2.C                   **
  20487.    **  Language:     Microsoft QuickC/QuickBASIC **
  20488.    ************************************************
  20489.    *
  20490.    * Restores characters and attributes to a rectangular
  20491.    * area of the screen.
  20492.    *
  20493.    * EXAMPLE OF USE:  TextPut r1%, c1%, r2%, c2%, a$
  20494.    * PARAMETERS:      r1%        Pointer to row at upper left corner
  20495.    *                  c1%        Pointer to column at upper left corner
  20496.    *                  r2%        Pointer to row at lower right corner
  20497.    *                  c2%        Pointer to column at lower right corner
  20498.    *                  a$         String descriptor where screen contents are s
  20499.    * VARIABLES:       len        Length of string
  20500.    *                  str        Pointer to string contents
  20501.    *                  video      Pointer to video memory
  20502.    *                  i          Index into string
  20503.    *                  row        Looping index
  20504.    *                  col        Looping index
  20505.    * #define VIDEO_START         0xb8000000
  20506.    *
  20507.    * Definition of the QuickBASIC string descriptor structure
  20508.    *    struct bas_str
  20509.    *        {
  20510.    *        int  sd_len;
  20511.    *        char *sd_addr;
  20512.    *        };                                        */
  20513.  
  20514.  
  20515.    void textput (r1,c1,r2,c2,basic_string)
  20516.    int *r1,*c1,*r2,*c2;
  20517.    struct bas_str *basic_string;
  20518.        {
  20519.        int len;
  20520.        int * str;
  20521.        int far * video;
  20522.        int i,row,col;
  20523.  
  20524.        len = basic_string->sd_len;
  20525.        str = (int *) basic_string->sd_addr;
  20526.        video = (int far *) VIDEO_START;
  20527.  
  20528.        if (len == (*r2 - *r1 + 1) * (*c2 - *c1 + 1) * 2)
  20529.            for (row = *r1 - 1, i = 0; row < *r2; row++)
  20530.                for (col = *c1 - 1; col < *c2; col++)
  20531.                    video[row * 80 + col] = str[i++];
  20532.        }
  20533.    ──────────────────────────────────────────────────────────────────────────
  20534.  
  20535.  
  20536.  Subprogram: UnPackWord
  20537.  
  20538.    Extracts two bytes from a QuickBASIC integer value. For example, the high
  20539.    and low (most significant and least significant) bytes of the integer
  20540.    value 258 are 1 and 2, and the two bytes of -1 are 255 and 255.
  20541.  
  20542.    CDEMO2.BAS AND CTOOLS2.C
  20543.  
  20544.    This subprogram uses the QuickC union and structure data definition
  20545.    features to unpack the byte values using simple data moves in memory.
  20546.  
  20547.    ──────────────────────────────────────────────────────────────────────────
  20548.    /***********************************************
  20549.    **  Name:         UnPackWord                  **
  20550.    **  Type:         Subprogram                  **
  20551.    **  Module:       CTOOLS2.C                   **
  20552.    **  Language:     Microsoft QuickC/QuickBASIC **
  20553.    ************************************************
  20554.    *
  20555.    *  Unpacks two byte values from the high and low
  20556.    *  bytes of an integer (word).
  20557.    *
  20558.    * EXAMPLE OF USE:  UnPackWord hiloword%, hibyte%, lobyte%
  20559.    * PARAMETERS:      hiloword%  Integer word containing the two bytes
  20560.    *                  hibyte%    Integer value of the most significant byte
  20561.    *                  lobyte%    Integer value of the least significant byte
  20562.    * VARIABLES:       both       A union of a two-byte structure and an intege
  20563.    *                             variable                               */
  20564.  
  20565.  
  20566.    void unpackword (hiloword, hibyte, lobyte)
  20567.    int *hiloword, *hibyte, *lobyte;
  20568.        {
  20569.        union
  20570.            {
  20571.            struct
  20572.                {
  20573.                unsigned char lo;
  20574.                unsigned char hi;
  20575.                } bytes;
  20576.            int hilo;
  20577.            } both;
  20578.  
  20579.        both.hilo = *hiloword;
  20580.        *hibyte = both.bytes.hi;
  20581.        *lobyte = both.bytes.lo;
  20582.        }
  20583.    ──────────────────────────────────────────────────────────────────────────
  20584.  
  20585.  
  20586.  
  20587.  ────────────────────────────────────────────────────────────────────────────
  20588.  PART 4  APPENDIXES
  20589.  
  20590.  
  20591.  
  20592.  ────────────────────────────────────────────────────────────────────────────
  20593.  Appendix A  Requirements for Running Toolboxes/Programs
  20594.  
  20595.    In the following table, the Usage line assumes execution from the system
  20596.    prompt after you compile the .BAS program. From QuickBASIC, modify
  20597.    COMMAND$ and enter any parameters before selecting Run and Start.
  20598.  
  20599.    CGA─Color Graphics Adapter/Monitor
  20600.  
  20601.    EGA─Enhanced Graphics Adapter/Monitor
  20602.  
  20603.    VGA─Video Graphics Adapter/Monitor
  20604.  
  20605.    Sub─Subprogram
  20606.  
  20607.    Func─Function
  20608.  
  20609. ╓┌─┌────────────────────┌────────────────────────────────────────────────────╖
  20610.    Name                 Description
  20611.    ──────────────────────────────────────────────────────────────────────────
  20612.    ATTRIB.BAS          Screen/Text Attribute Display
  20613.                         Utility Program: 1 Sub
  20614.                         Usage: ATTRIB
  20615.                         Requirements: CGA
  20616.    BIN2HEX.BAS         Binary-to-Hex Conversion
  20617.                         Utility Program
  20618.                         Usage: BIN2HEX inFileName.ext outFileName.ext
  20619.                         .MAK File: BIN2HEX.BAS
  20620.                                    PARSE.BAS
  20621.    BIOSCALL.BAS        ROM BIOS Interrupt Calls
  20622.                         Toolbox: 6 Sub with Demo Module
  20623.                         Usage: BIOSCALL
  20624.    Name                 Description
  20625.    ──────────────────────────────────────────────────────────────────────────
  20626.                        Usage: BIOSCALL
  20627.                         Requirements: MIXED.QLB/.LIB
  20628.    BITS.BAS            Bit Manipulation
  20629.                         Toolbox: 2 Func/2 Sub with Demo Module
  20630.                         Usage: BITS
  20631.    CALENDAR.BAS        Calendar and Time Routines
  20632.                         Toolbox: 19 Func/1 Sub with Demo Module
  20633.                         Usage: CALENDAR
  20634.    CARTESIA.BAS        Cartesian Coordinate Routines
  20635.                         Toolbox: 2 Func/2 Sub with Demo Module
  20636.                         Usage: CARTESIA
  20637.    CDEMO1.BAS          Demo 1 of C-Language Routines
  20638.                         Program
  20639.                         Usage: CDEMO1
  20640.                         Requirements: CGA
  20641.                                       MIXED.QLB/.LIB
  20642.                                       Microsoft QuickC
  20643.    CDEMO2.BAS          Demo 2 of C-Language Routines
  20644.                         Program
  20645.    Name                 Description
  20646.    ──────────────────────────────────────────────────────────────────────────
  20647.                        Program
  20648.                         Usage: CDEMO2
  20649.                         Requirements: CGA
  20650.                                       MIXED.QLB/.LIB
  20651.                                       Microsoft QuickC
  20652.    CIPHER.BAS          Cipher File Security
  20653.                         Utility Program: 1 Func/1 Sub
  20654.                         Usage: CIPHER filename.ext key or CIPHER /NEWKEY
  20655.                         .MAK File: CIPHER.BAS
  20656.                                    RANDOMS.BAS
  20657.    COLORS.BAS          VGA Color Selection
  20658.                         Utility Program: 1 Func
  20659.                         Usage: COLORS
  20660.                         Requirements: VGA or MCGA
  20661.                                       MIXED.QLB/.LIB
  20662.                                       Mouse
  20663.                         .MAK File: COLORS.BAS
  20664.                                    BITS.BAS
  20665.                                    MOUSSUBS.BAS
  20666.    Name                 Description
  20667.    ──────────────────────────────────────────────────────────────────────────
  20668.                                   MOUSSUBS.BAS
  20669.    COMPLEX.BAS         Complex Numbers
  20670.                         Toolbox: 12 Sub with Demo Module
  20671.                         Usage: COMPLEX
  20672.                         .MAK File: COMPLEX.BAS
  20673.                                    CARTESIA.BAS
  20674.    CTOOLS1.C           C Functions──Characters
  20675.                         C-Language Toolbox: 12 Func/2 Sub
  20676.                         Usage: Place in MIXED.QLB and MIXED.LIB
  20677.                                after compiling into object files and then run
  20678.                                CDEMO1.BAS from QuickBASIC
  20679.    CTOOLS2.C           C Functions──Text
  20680.                         C-Language Toolbox: 4 Func/4 Sub
  20681.                         Usage: Place in MIXED.QLB and MIXED.LIB
  20682.                                after compiling into object files and then run
  20683.                                CDEMO2.BAS from QuickBASIC
  20684.    DOLLARS.BAS         Dollar Formatting
  20685.                         Toolbox: 3 Func with Demo Module
  20686.                         Usage: DOLLARS
  20687.    Name                 Description
  20688.    ──────────────────────────────────────────────────────────────────────────
  20689.                        Usage: DOLLARS
  20690.    DOSCALLS.BAS        MS-DOS System Calls
  20691.                         Toolbox: 6 Func/9 Sub with Demo Module
  20692.                         Usage: DOSCALLS
  20693.                         Requirements: MIXED.QLB/.LIB
  20694.                                       MS-DOS 3.3 or later
  20695.    EDIT.BAS            Editing
  20696.                         Toolbox: 5 Sub with Demo Module
  20697.                         Usage: EDIT
  20698.                         .MAK File: EDIT.BAS
  20699.                                    KEYS.BAS
  20700.    ERROR.BAS           Error Message
  20701.                         Toolbox: 1 Sub with Demo Module
  20702.                         Usage: ERROR
  20703.    FIGETPUT.BAS        FILEGET and FILEPUT Routines
  20704.                         Toolbox: 1 Func/1 Sub with Demo Module
  20705.                         Usage: FIGETPUT
  20706.    FILEINFO.BAS        Directory/File Listing Information
  20707.                         Toolbox: 3 Sub with Demo Module
  20708.    Name                 Description
  20709.    ──────────────────────────────────────────────────────────────────────────
  20710.                        Toolbox: 3 Sub with Demo Module
  20711.                         Usage: FILEINFO
  20712.                         Requirements: MIXED.QLB/.LIB
  20713.    FRACTION.BAS        Fractions
  20714.                         Toolbox: 3 Func/7 Sub with Demo Module
  20715.                         Usage: FRACTION
  20716.    GAMES.BAS           Games
  20717.                         Toolbox: 4 Func/2 Sub with Demo Module
  20718.                         Usage: GAMES
  20719.                         Requirements: CGA
  20720.    HEX2BIN.BAS         Hex-to-Binary Conversion
  20721.                         Utility Program
  20722.                         Usage: HEX2BIN inFileName.ext outFileName.ext
  20723.                         .MAK File: HEX2BIN.BAS
  20724.                                    PARSE.BAS
  20725.                                    STRINGS.BAS
  20726.    JUSTIFY.BAS         Paragraph Justification
  20727.                         Toolbox: 1 Sub with Demo Module
  20728.                         Usage: JUSTIFY
  20729.    Name                 Description
  20730.    ──────────────────────────────────────────────────────────────────────────
  20731.                        Usage: JUSTIFY
  20732.                         .MAK File: JUSTIFY.BAS
  20733.                                    EDIT.BAS
  20734.                                    KEYS.BAS
  20735.                                    PARSE.BAS
  20736.    KEYS.BAS            Enhanced Keyboard Input Functions
  20737.                         Toolbox: 2 Func with Demo Module
  20738.                         Usage: KEYS
  20739.    LOOK.BAS            Text File Display Utility
  20740.                         Utility Program
  20741.                         Usage: LOOK filename.ext
  20742.                         .MAK File: LOOK.BAS
  20743.                                    KEYS.BAS
  20744.    MONTH.BAS           Three-Month Calendar
  20745.                         Utility Program
  20746.                         Usage: MONTH
  20747.                         .MAK File: MONTH.BAS
  20748.                                    CALENDAR.BAS
  20749.    MOUSGCRS.BAS        Custom Graphics Mouse Cursors
  20750.    Name                 Description
  20751.    ──────────────────────────────────────────────────────────────────────────
  20752.   MOUSGCRS.BAS        Custom Graphics Mouse Cursors
  20753.                         Utility Program
  20754.                         Usage: MOUSGCRS
  20755.                         Requirements: CGA
  20756.                                       MIXED.QLB/.LIB
  20757.                                       Mouse
  20758.                         .MAK File: MOUSGCRS.BAS
  20759.                                    BITS.BAS
  20760.                                    MOUSSUBS.BAS
  20761.    MOUSSUBS.BAS        Mouse Subroutines
  20762.                         Toolbox: 26 Subs with Demo Module
  20763.                         Usage: MOUSSUBS
  20764.                         Requirements: CGA
  20765.                                       MIXED.QLB/.LIB
  20766.                                       Mouse
  20767.                         .MAK File: MOUSSUBS.BAS
  20768.                                    BITS.BAS
  20769.    MOUSTCRS.BAS        Text-Mode Mouse Cursor
  20770.                         Utility Program
  20771.    Name                 Description
  20772.    ──────────────────────────────────────────────────────────────────────────
  20773.                        Utility Program
  20774.                         Usage: MOUSTCRS
  20775.                         Requirements: MIXED.QLB/.LIB
  20776.                                       Mouse
  20777.                         .MAK File: MOUSTCRS.BAS
  20778.                                    MOUSSUBS.BAS
  20779.                                    BITS.BAS
  20780.                                    ATTRIB.BAS
  20781.    OBJECT.BAS          Interactive Graphics Creation
  20782.                         Toolbox and Utility Program: 1 Sub
  20783.                         Usage: OBJECT
  20784.                         Requirements: CGA
  20785.                         .MAK File: OBJECT.BAS
  20786.                                    KEYS.BAS
  20787.                                    EDIT.BAS
  20788.    PARSE.BAS           Command Line Parsing
  20789.                         Toolbox: 2 Subs with Demo Module
  20790.                         Usage: PARSE
  20791.    PROBSTAT.BAS        Probability and Statistical Routines
  20792.    Name                 Description
  20793.    ──────────────────────────────────────────────────────────────────────────
  20794.   PROBSTAT.BAS        Probability and Statistical Routines
  20795.                         Toolbox: 7 Func with Demo Module
  20796.                         Usage: PROBSTAT
  20797.    QBFMT.BAS           Formatting Utility
  20798.                         Utility Program: 3 Subs
  20799.                         Usage: QBFMT filename [indention]
  20800.                         .MAK File: QBFMT.BAS
  20801.                                    PARSE.BAS
  20802.                                    STRINGS.BAS
  20803.    QBTREE.BAS          Directory/Subdirectory/Files Listing
  20804.                         Utility Program
  20805.                         Usage: QBTREE [path]
  20806.                         Requirements: MIXED.QLB/.LIB
  20807.                         .MAK File: QBTREE.BAS
  20808.                                    FILEINFO.BAS
  20809.    QCAL.BAS            Command Line Scientific Calculator
  20810.                         Utility Program: 1 Func/3 Subs
  20811.                         Usage: QCAL [number] [function] [...]
  20812.                         .MAK File: QCAL.BAS
  20813.    Name                 Description
  20814.    ──────────────────────────────────────────────────────────────────────────
  20815.                        .MAK File: QCAL.BAS
  20816.                                    QCALMATH.BAS
  20817.    QCALMATH.BAS        Math Functions
  20818.                         Toolbox: 31 Func/2 Sub
  20819.                         Usage: loaded by the QCAL program
  20820.    RANDOMS.BAS         Pseudorandom Numbers
  20821.                         Toolbox: 6 Func/1 Sub with Demo Module
  20822.                         Usage: RANDOMS
  20823.    STDOUT.BAS          MS-DOS Standard (ANSI) Output
  20824.                         Toolbox: 12 Sub with Demo Module
  20825.                         Usage: STDOUT
  20826.                         Requirements: MIXED.QLB/.LIB
  20827.                                       ANSI.SYS
  20828.    STRINGS.BAS         String Manipulation
  20829.                         Toolbox: 18 Func/1 Sub with Demo Module
  20830.                         Usage: STRINGS
  20831.    TRIANGLE.BAS        Triangles
  20832.                         Toolbox: 3 Func/1 Sub with Demo Module
  20833.                         Usage: TRIANGLE
  20834.    Name                 Description
  20835.    ──────────────────────────────────────────────────────────────────────────
  20836.                        Usage: TRIANGLE
  20837.                         Requirements: CGA
  20838.                         .MAK File: TRIANGLE.BAS
  20839.                                    QCALMATH.BAS
  20840.    WINDOWS.BAS         Windows
  20841.                         Toolbox: 2 Sub with Demo Module
  20842.                         Usage: WINDOWS
  20843.                         Requirements: MIXED.QLB/.LIB
  20844.                                       Mouse (optional)
  20845.                         .MAK File: WINDOWS.BAS
  20846.                                    BIOSCALL.BAS
  20847.                                    BITS.BAS
  20848.                                    KEYS.BAS
  20849.                                    MOUSSUBS.BAS
  20850.    WORDCOUN.BAS        Word Counting
  20851.                         Toolbox: 1 Func with Demo Module
  20852.                         Usage: WORDCOUN filename
  20853.    ──────────────────────────────────────────────────────────────────────────
  20854.  
  20855.    Name                 Description
  20856.    ──────────────────────────────────────────────────────────────────────────
  20857. 
  20858.  
  20859.  
  20860.  
  20861.  
  20862.  ────────────────────────────────────────────────────────────────────────────
  20863.  Appendix B  Functions-to-Modules Cross Reference
  20864.  
  20865. ╓┌─┌───────────────────┌─────────────┌───────────────────────────────────────╖
  20866.    Function            Module        Description
  20867.    ──────────────────────────────────────────────────────────────────────────
  20868.    AbsoluteX#         QCALMATH      Absolute value of a number
  20869.    Add#               QCALMATH      Sum of two numbers
  20870.    Angle!             CARTESIA      Angle between X axis and line to x, y
  20871.                                      point
  20872.    ArcCosine#         QCALMATH      Arc cosine function of a number
  20873.    ArcHypCosine#      QCALMATH      Inverse hyperbolic cosine of a number
  20874.    ArcHypSine#        QCALMATH      Inverse hyperbolic sine of a number
  20875.    ArcHypTangent#     QCALMATH      Inverse hyperbolic tangent of a number
  20876.    Function            Module        Description
  20877.    ──────────────────────────────────────────────────────────────────────────
  20878.   ArcHypTangent#     QCALMATH      Inverse hyperbolic tangent of a number
  20879.    ArcSine#           QCALMATH      Inverse sine of a number
  20880.    ArcTangent#        QCALMATH      Inverse tangent of a number
  20881.    ArithmeticMean#    PROBSTAT      Arithmetic mean of an array of numbers
  20882.    Ascii2Ebcdic$      STRINGS       Converts string from ASCII to EBCDIC
  20883.    BestMatch$         STRINGS       Returns best match to input string
  20884.    Bin2BinStr$        BITS          Integer to 16-character binary string
  20885.    BinStr2Bin%        BITS          16-character binary string to integer
  20886.    BitShiftLeft%      CTOOLS2       Shifts all bits in a string left one bit
  20887.    BitShiftRight%     CTOOLS2       Shifts all bits in a string right one
  20888.                                      1 through 52
  20889.    BufferedKeyInput$  DOSCALLS      numberstring of specified length
  20890.    Card$              GAMES         Returns name of card given a number from
  20891.    Ceil#              QCALMATH      Smallest whole number greater than a
  20892.    Center$            STRINGS       Centers string by padding with spaces
  20893.    ChangeSign#        QCALMATH      Reverses sign of a number
  20894.    CheckDate%         CALENDAR      Validates date with return of TRUE/FALSE
  20895.    Collision%         GAMES         Returns TRUE or FALSE collision
  20896.                                      condition
  20897.    Function            Module        Description
  20898.    ──────────────────────────────────────────────────────────────────────────
  20899.                                     condition
  20900.    Combinations#      PROBSTAT      Combinations of n items, r at a time
  20901.    Comma$             DOLLARS       Double-precision with commas inserted
  20902.    Cosine#            QCALMATH      Cosine of a number
  20903.    Date2Day%          CALENDAR      Day of month number from date string
  20904.    Date2Julian&       CALENDAR      Julian day number for a given date
  20905.    Date2Month%        CALENDAR      Month number from date string
  20906.    Date2Year%         CALENDAR      Year number from date string
  20907.    DayOfTheCentury&   CALENDAR      Day of the given century
  20908.    DayOfTheWeek$      CALENDAR      Name of day of the week for given date
  20909.    DayOfTheYear%      CALENDAR      Day of the year (1 through 366) for
  20910.                                      given date
  20911.    DaysBetweenDates&  CALENDAR      Number of days between two dates
  20912.    Deg2Rad#           TRIANGLE      Converts degree angular units to radians
  20913.    Detab$             STRINGS       Replaces tab characters with spaces
  20914.    Dice%              GAMES         Returns total showing for throwing n
  20915.                                      dice
  20916.    Divide#            QCALMATH      Result of dividing two numbers
  20917.    DollarString$      DOLLARS       Dollar representation rounded with
  20918.    Function            Module        Description
  20919.    ──────────────────────────────────────────────────────────────────────────
  20920.   DollarString$      DOLLARS       Dollar representation rounded with
  20921.                                      commas
  20922.    DOSVersion!        DOSCALLS      Version number of MS-DOS returned
  20923.    Ebcdic2Ascii$      STRINGS       Converts a string from EBCDIC to ASCII
  20924.    Entab$             STRINGS       Replaces spaces with tab characters
  20925.    Exponential#       QCALMATH      Exponential function of a number
  20926.    Factorial#         PROBSTAT      Factorial of a number
  20927.    FileGet$           FIGETPUT      Returns a string with contents of file
  20928.    FilterIn$          STRINGS       Retains only specified characters in
  20929.                                      string
  20930.    FilterOut$         STRINGS       Deletes specified characters from string
  20931.    Fraction2String$   FRACTION      Converts type Fraction variable to a
  20932.                                      string
  20933.    FractionalPart#    QCALMATH      Fractional part of a number
  20934.    GeometricMean#     PROBSTAT      Geometric mean of an array of numbers
  20935.    GetDirectory$      DOSCALLS      Path to disk directory specified
  20936.    GetDrive$          DOSCALLS      Current drive string
  20937.    GetVerifyState%    DOSCALLS      Verify setting (state)
  20938.    GreatestComDiv&    FRACTION      seconds greatest common divisor
  20939.    Function            Module        Description
  20940.    ──────────────────────────────────────────────────────────────────────────
  20941.   GreatestComDiv&    FRACTION      seconds greatest common divisor
  20942.    HarmonicMean#      PROBSTAT      Harmonic mean of an array of numbers
  20943.    HMS2Time$          CALENDAR      Time string for given hour, minute, and
  20944.    HypCosine#         QCALMATH      Hyperbolic cosine of a number
  20945.    HypSine#           QCALMATH      Hyperbolic sine of a number
  20946.    HypTangent#        QCALMATH      Hyperbolic tangent of a number
  20947.    InKeyCode%         KEYS          Returns unique integer for any key
  20948.                                      pressed
  20949.    IntegerPart#       QCALMATH      Integer part of a number
  20950.    IsItAlnum%         CTOOLS1       Alphanumeric character determination
  20951.    IsItAlpha%         CTOOLS1       Alphabetic character determination
  20952.    IsItAscii%         CTOOLS1       Standard ASCII character determination
  20953.    IsItCntrl%         CTOOLS1       Control character determination
  20954.    IsItDigit%         CTOOLS1       Decimal digit (0─9) determination
  20955.    IsItGraph%         CTOOLS1       Graphics character determination
  20956.    IsItLower%         CTOOLS1       Lowercase character determination
  20957.    IsItPrint%         CTOOLS1       Printable character determination
  20958.    IsItPunct%         CTOOLS1       Punctuation character determination
  20959.    IsItSpace%         CTOOLS1       Space character determination
  20960.    Function            Module        Description
  20961.    ──────────────────────────────────────────────────────────────────────────
  20962.   IsItSpace%         CTOOLS1       Space character determination
  20963.    IsItUpper%         CTOOLS1       Uppercase character determination
  20964.    IsItXDigit%        CTOOLS1       Hexadecimal character determination
  20965.    Julian2Date$       CALENDAR      Date string from given Julian day number
  20966.    KeyCode%           KEYS          Waits and returns integer value for key
  20967.    LeastComMul&       FRACTION      Returns least common multiple
  20968.    LogBase10#         QCALMATH      Log base 10 of a number
  20969.    LogBaseN#          QCALMATH      Log base N of a number
  20970.    LogE#              QCALMATH      Natural logarithm of a number
  20971.    Lpad$              STRINGS       Returns left-justified input string
  20972.    LtrimSet$          STRINGS       Deletes specified characters from left
  20973.    Magnitude!         CARTESIA      Distance from origin to x, y point
  20974.    MDY2Date$          CALENDAR      Date string from given month, day, and
  20975.                                      year
  20976.    MenuString%        CTOOLS2       Bar menu and user response function
  20977.    Modulus#           QCALMATH      Remainder of the division of two numbers
  20978.    MonthName$         CALENDAR      Name of month for a given date
  20979.    Multiply#          QCALMATH      COMMAND$of two numbers
  20980.    NewWord$           CIPHER        Creates pseudorandom new word
  20981.    Function            Module        Description
  20982.    ──────────────────────────────────────────────────────────────────────────
  20983.   NewWord$           CIPHER        Creates pseudorandom new word
  20984.    NextParameter$     QCAL          Extracts number or command from
  20985.    NumberOfBits&      CTOOLS2       Determines number of 1 bits in a string
  20986.    OneOverX#          QCALMATH      Result of dividing 1 by a number
  20987.    Ord%               STRINGS       Returns byte number for ANSI mnemonic
  20988.    Permutations#      PROBSTAT      Permutations of n items, r at a time
  20989.    QuadraticMean#     PROBSTAT      Quadratic mean of an array of numbers
  20990.    Rad2Deg#           TRIANGLE      from meanradian angular units to degrees
  20991.    Rand&              RANDOMS       Long integers
  20992.    RandExponential!   RANDOMS       Real value with exponential distribution
  20993.    RandFrac!          RANDOMS       deviationecision positive value < 1.0
  20994.    RandInteger%       RANDOMS       Integers within desired range
  20995.    RandNormal!        RANDOMS       Single-precision from mean and standard
  20996.    RandReal!          RANDOMS       Single-precision value in desired range
  20997.    Repeat$            STRINGS       Combines multiple copies into one string
  20998.    Replace$           STRINGS       Replaces specified characters in string
  20999.    Reverse$           STRINGS       Reverses order of characters in a string
  21000.    ReverseCase$       STRINGS       Reverses case for each charater in a
  21001.                                      string
  21002.    Function            Module        Description
  21003.    ──────────────────────────────────────────────────────────────────────────
  21004.                                     string
  21005.    Round#             DOLLARS       Rounding at specified decimal place
  21006.    Rpad$              STRINGS       Returns right-justified input string
  21007.    RtrimSet$          STRINGS       Deletes specified characters from right
  21008.    Second2Date$       CALENDAR      Seconds from last of 1979 to date given
  21009.    Second2Time$       CALENDAR      Time of day from seconds since last of
  21010.                                      1979
  21011.    Shade&             COLORS        Color value from given red, green, and
  21012.                                      blue
  21013.    Shuffle$           GAMES         Randomizes character bytes in string
  21014.    Sign#              QCALMATH      Sign of a number
  21015.    Sine#              QCALMATH      Sine of a number
  21016.    SquareRoot#        QCALMATH      Square root of a number
  21017.    Subtract#          QCALMATH      Difference between two numbers
  21018.    Tangent#           QCALMATH      Tangent of a number
  21019.    Time2Hour%         CALENDAR      Hour number from time string
  21020.    Time2Minute%       CALENDAR      Minute number from time string
  21021.    Time2Second%       CALENDAR      Seconds number from time string
  21022.    TimeDate2Second&   CALENDAR      Seconds from last of 1979 from date/time
  21023.    Function            Module        Description
  21024.    ──────────────────────────────────────────────────────────────────────────
  21025.   TimeDate2Second&   CALENDAR      Seconds from last of 1979 from date/time
  21026.    Translate$         STRINGS       Exchanges characters in string from
  21027.                                      table
  21028.    TranslateCountry$  DOSCALLS      Translates string──current country
  21029.                                      setting
  21030.    TriangleArea#      TRIANGLE      Calculates area of triangle from three
  21031.                                      sides
  21032.    WordCount%         WORDCOUN      Returns number of words in a string
  21033.    Xsquared#          QCALMATH      Square of a number
  21034.    YRaisedToX#        QCALMATH      Number raised to the power of a second
  21035.    ──────────────────────────────────────────────────────────────────────────
  21036.  
  21037.  
  21038.  
  21039.  
  21040.  
  21041.  ────────────────────────────────────────────────────────────────────────────
  21042.  Appendix C  Subprograms-to-Modules Cross Reference
  21043.  
  21044. ╓┌─┌────────────────────────┌───────────┌────────────────────────────────────╖
  21045.    Subprogram               Module      Description
  21046.    ──────────────────────────────────────────────────────────────────────────
  21047.    AssignKey               STDOUT      Reassigns a string to a key
  21048.    Attrib                  ATTRIB      Table of color attributes (text mode)
  21049.    Attribute               STDOUT      Sets screen color (ANSI driver
  21050.                                         definition)
  21051.    BitGet                  BITS        Value from any bit position in a
  21052.                                         string
  21053.    BitPut                  BITS        translation tables at location in a
  21054.                                         string
  21055.    BuildAEStrings          STRINGS     Builds ASCII and EBCDIC character
  21056.    ClearLine               STDOUT      Clears line from cursor to end of
  21057.                                         line
  21058.    ClearScreen             STDOUT      Clears screen
  21059.    Complex2String          COMPLEX     String representation of a complex
  21060.                                         number
  21061.    ComplexAdd              COMPLEX     Adds two complex numbers
  21062.    ComplexDiv              COMPLEX     Divides two complex numbers
  21063.    ComplexExp              COMPLEX     Exponential function of a complex
  21064.                                         number
  21065.    Subprogram               Module      Description
  21066.    ──────────────────────────────────────────────────────────────────────────
  21067.                                        number
  21068.    ComplexLog              COMPLEX     numberl log of a complex number
  21069.    ComplexMul              COMPLEX     Multiplies two complex numbers
  21070.    ComplexPower            COMPLEX     Complex number raised to a complex
  21071.    ComplexReciprocal       COMPLEX     Reciprocal of a complex number
  21072.    ComplexRoot             COMPLEX     Complex root of a complex number
  21073.    ComplexSqr              COMPLEX     Square root of a complex number
  21074.    ComplexSub              COMPLEX     Subtracts two complex numbers
  21075.    CrLf                    STDOUT      Sends carriage return and line feed
  21076.    Curschek                MOUSSUBS    Check-mark mouse cursor pattern mask
  21077.    Cursdflt                MOUSSUBS    Arrow mouse cursor pointing up and
  21078.                                         left
  21079.    Curshand                MOUSSUBS    Pointing hand mouse cursor
  21080.    Curshour                MOUSSUBS    Hourglass mouse cursor
  21081.    Cursjet                 MOUSSUBS    Jet-shaped mouse cursor
  21082.    Cursleft                MOUSSUBS    Left arrow mouse cursor
  21083.    CursorDown              STDOUT      Moves cursor down specified number of
  21084.                                         lines
  21085.    CursorHome              STDOUT      Moves cursor to upper left corner of
  21086.    Subprogram               Module      Description
  21087.    ──────────────────────────────────────────────────────────────────────────
  21088.   CursorHome              STDOUT      Moves cursor to upper left corner of
  21089.                                         screen
  21090.    CursorLeft              STDOUT      Moves cursor left specified number of
  21091.                                         spaces
  21092.    CursorPosition          STDOUT      Moves cursor to specified row and
  21093.                                         column
  21094.    CursorRight             STDOUT      Moves cursor right specified number
  21095.                                         of spaces
  21096.    CursorUp                STDOUT      Moves cursor up specified number of
  21097.                                         lines
  21098.    Cursplus                MOUSSUBS    Plus sign mouse cursor
  21099.    Cursup                  MOUSSUBS    Up arrow mouse cursor
  21100.    Cursx                   MOUSSUBS    X-mark mouse cursor
  21101.    DisplayStack            QCAL        Displays final results of the program
  21102.    DrawBox                 EDIT        Creates a double-lined box on the
  21103.                                         display
  21104.    Dup                     QCALMATH    Duplicates top entry on the stack
  21105.    EditBox                 EDIT        Allows editing in a boxed area of the
  21106.                                         screen
  21107.    Subprogram               Module      Description
  21108.    ──────────────────────────────────────────────────────────────────────────
  21109.                                        screen
  21110.    EditLine                EDIT        Allows editing of string at cursor
  21111.                                         position
  21112.    Equipment               BIOSCALL    Equipment/hardware information
  21113.    ErrorMessage            ERROR       Error message display
  21114.    FilePut                 FIGETPUT    Writes contents of string into binary
  21115.                                         file
  21116.    FileRead                LOOK        Reads lines of ASCII files into an
  21117.                                         array
  21118.    FileTreeSearch          QBTREE      numbers defined by the boundsutine
  21119.    FillArray               GAMES       Fills an integer array with a
  21120.    FindFirstFile           FILEINFO    Finds first file that matches
  21121.                                         parameter
  21122.    FindNextFile            FILEINFO    Finds next file that matches
  21123.                                         parameter
  21124.    FormatTwo               EDIT        Splits string into two strings
  21125.    FractionAdd             FRACTION    Adds two fractions and reduces
  21126.    FractionDiv             FRACTION    Divides two fractions and reduces
  21127.    FractionMul             FRACTION    Multiplies two fractions and reduces
  21128.    Subprogram               Module      Description
  21129.    ──────────────────────────────────────────────────────────────────────────
  21130.   FractionMul             FRACTION    Multiplies two fractions and reduces
  21131.    FractionReduce          FRACTION    Reduces fraction to lowest terms
  21132.    FractionSub             FRACTION    Subtracts two fractions and reduces
  21133.    GetCountry              DOSCALLS    Current country setting
  21134.    GetDiskFreeSpace        DOSCALLS    Disk space format and usage for input
  21135.                                         drive
  21136.    GetFileAttributes       DOSCALLS    Attribute bits for given file
  21137.    GetFileData             FILEINFO    Extracts file directory information
  21138.    GetMediaDescriptor      DOSCALLS    Drive information for system
  21139.    GetShiftStates          BIOSCALL    Shift key states
  21140.    Indent                  QBFMT       Performs line indention
  21141.    InsertCharacter         EDIT        Inserts a character
  21142.    Justify                 JUSTIFY     Adjusts strings to specified widths
  21143.    MouseHide               MOUSSUBS    Turns off mouse visibility
  21144.    MouseInches             MOUSSUBS    parameters-to-cursor motion ratio
  21145.    MouseInstall            MOUSSUBS    Checks mouse availability; resets
  21146.    MouseLightPen           MOUSSUBS    Mouse emulation of a lightpen
  21147.    MouseMaskTranslate      MOUSSUBS    Translates pattern/hot spot to binary
  21148.    MouseMickey             MOUSSUBS    Returns motion increments since last
  21149.    Subprogram               Module      Description
  21150.    ──────────────────────────────────────────────────────────────────────────
  21151.   MouseMickey             MOUSSUBS    Returns motion increments since last
  21152.                                         call
  21153.    MouseNow                MOUSSUBS    Current state/location of the mouse
  21154.    MousePressLeft          MOUSSUBS    Location of mouse──left button press
  21155.    MousePressRight         MOUSSUBS    Location of mouse──right button press
  21156.    MousePut                MOUSSUBS    Moves cursor to the given position
  21157.    MouseRange              MOUSSUBS    Limits mouse cursor motion to
  21158.                                         rectangle
  21159.    MouseReleaseLeft        MOUSSUBS    Location of mouse──left button
  21160.                                         release
  21161.    MouseReleaseRight       MOUSSUBS    Location of mouse──right button
  21162.                                         release
  21163.    MouseSetGcursor         MOUSSUBS    Sets graphics-mode mouse cursor
  21164.    MouseShow               MOUSSUBS    Activates and displays mouse cursor
  21165.    MouseSoftCursor         MOUSSUBS    Sets text-mode attributes (mouse
  21166.                                         cursor)
  21167.    MouseWarp               MOUSSUBS    Sets mouse double-speed threshold
  21168.    MovBytes                CTOOLS1     Moves bytes from one location to
  21169.                                         another
  21170.    Subprogram               Module      Description
  21171.    ──────────────────────────────────────────────────────────────────────────
  21172.                                        another
  21173.    MovWords                CTOOLS1     Moves blocks of words in memory
  21174.    OneMonthCalendar        CALENDAR    One-month calendar for given date
  21175.    PackWord                CTOOLS2     Packs two bytes into an integer value
  21176.    ParseLine               PARSE       Breaks a string into individual words
  21177.    ParseWord               PARSE       Parses and removes first word from
  21178.                                         string
  21179.    Pol2Rec                 CARTESIA    Polar to Cartesian conversion
  21180.    PrintScreen             BIOSCALL    Screen dump
  21181.    Process                 QCAL        Controls action for command line
  21182.                                         parameters
  21183.    ProcesX                 CIPHER      Enciphers string by XORing bytes
  21184.    QcalHelp                QCAL        Provides a "Help" display for program
  21185.    RandShuffle             RANDOMS     Initializes random number generator
  21186.    ReBoot                  BIOSCALL    System reboot
  21187.    Rec2Pol                 CARTESIA    Cartesian to polar conversion
  21188.    SaveObject              OBJECT      Creates graphics "PUT" file source
  21189.                                         code
  21190.    Scroll                  BIOSCALL    Moves text in designated area of
  21191.    Subprogram               Module      Description
  21192.    ──────────────────────────────────────────────────────────────────────────
  21193.   Scroll                  BIOSCALL    Moves text in designated area of
  21194.                                         screen
  21195.    SetCode                 QBFMT       Determines indention code by keyword
  21196.    SetDirectory            DOSCALLS    Sets current directory
  21197.    SetDrive                DOSCALLS    Sets current disk drive
  21198.    SetFileAttributes       DOSCALLS    Sets the attribute bits for a given
  21199.                                         file
  21200.    SetVerifyState          DOSCALLS    Sets or clears verify state (writing
  21201.                                         to file)
  21202.    ShuffleArray            GAMES       Randomizes integers in an array
  21203.    SplitFractions          FRACTION    Parses fraction problem string
  21204.    SplitUp                 QBFMT       Splits line into major components
  21205.    StdOut                  STDOUT      Sends a string to standard output
  21206.                                         channel
  21207.    String2Complex          COMPLEX     Converts string to complex variable
  21208.    String2Fraction         FRACTION    Converts a string to Fraction
  21209.                                         variable
  21210.    SwapXY                  QCALMATH    of screen two entries on the stack
  21211.    TextGet                 CTOOLS2     Saves characters and attributes from
  21212.    Subprogram               Module      Description
  21213.    ──────────────────────────────────────────────────────────────────────────
  21214.   TextGet                 CTOOLS2     Saves characters and attributes from
  21215.    TextPut                 CTOOLS2     Restores text from TextGet to screen
  21216.    Triangle                TRIANGLE    Calculates sides and angles of
  21217.                                         triangle
  21218.    UnPackWord              CTOOLS2     Unpacks values from high and low
  21219.                                         state
  21220.    VideoState              BIOSCALL    Mode, column, and page display of
  21221.    Windows                 WINDOWS     Creates a pop-up window
  21222.    WindowsPop              WINDOWS     Removes last displayed window
  21223.    WriteToDevice           DOSCALLS    Outputs a string to a device
  21224.    ──────────────────────────────────────────────────────────────────────────
  21225.  
  21226.  
  21227.  
  21228.  
  21229.  
  21230.  ────────────────────────────────────────────────────────────────────────────
  21231.  Appendix D  Hexadecimal Format (.obj) Files
  21232.  
  21233.    Three assembly-language modules are discussed in this book. The suggested
  21234.    method for creating object-code files is to use the Microsoft Macro
  21235.    Assembler 5.0 on the source-code files. If necessary, however, you can
  21236.    process these files using the HEX2BIN.BAS program to create the desired
  21237.    object-code files. (See the HEX2BIN.BAS program for information about how
  21238.    to make the conversions.)
  21239.  
  21240.  
  21241.      MOUSE.HEX (MOUSE.OBJ)
  21242.      80 0B 00 09 4D 4F 55 53 - 45 2E 41 53 4D D4 96 24
  21243.      00 00 06 44 47 52 4F 55 - 50 04 44 41 54 41 04 43
  21244.      4F 44 45 0A 4D 4F 55 53 - 45 5F 54 45 58 54 05 5F
  21245.      44 41 54 41 7D 98 07 00 - 48 39 00 05 04 01 D6 98
  21246.      07 00 48 00 00 06 03 01 - 0F 9A 04 00 02 FF 02 5F
  21247.      90 0C 00 00 01 05 4D 4F - 55 53 45 00 00 00 D5 88
  21248.      04 00 00 A2 01 D1 A0 3D - 00 01 00 00 55 8B EC 8B
  21249.      5E 0C 8B 07 50 8B 5E 0A - 8B 07 50 8B 5E 08 8B 0F
  21250.      8B 5E 06 8B 17 5B 58 1E - 07 CD 33 53 8B 5E 0C 89
  21251.      07 58 8B 5E 0A 89 07 8B - 5E 08 89 0F 8B 5E 06 89
  21252.      17 5D CA 08 00 BC 8A 02 - 00 00 74
  21253.  
  21254.      INTRPT.HEX (INTRPT.OBJ)
  21255.      80 0C 00 0A 49 4E 54 52 - 50 54 2E 41 53 4D 7A 88
  21256.      03 00 80 9E 57 96 25 00 - 00 06 44 47 52 4F 55 50
  21257.      04 44 41 54 41 04 43 4F - 44 45 05 5F 44 41 54 41
  21258.      0B 49 4E 54 52 50 54 5F - 54 45 58 54 23 98 07 00
  21259.      48 07 01 06 04 01 06 98 - 07 00 48 00 00 05 03 01
  21260.      10 9A 04 00 02 FF 02 5F - 90 1E 00 00 01 09 49 4E
  21261.      54 45 52 52 55 50 54 00 - 00 00 0A 49 4E 54 45 52
  21262.      52 55 50 54 58 0D 00 00 - 3F 88 04 00 00 A2 01 D1
  21263.      A0 0B 01 01 00 00 55 8B - EC 83 C4 E2 C7 46 FA 08
  21264.      00 EB 0B 55 8B EC 83 C4 - E2 C7 46 FA 0A 00 89 76
  21265.      E4 89 7E E2 8C 5E FC 9C - 8F 46 FE 8B 76 08 8D 7E
  21266.      E6 8B 4E FA FC 16 07 F3 - A5 55 8B 76 0A 8B 1C 0A
  21267.      FF 74 03 E9 00 00 80 FB - 25 74 05 80 FB 26 75 0E
  21268.      B8 08 00 50 B8 02 CA 50 - B8 83 C4 50 EB 07 33 C0
  21269.      50 B8 CA 06 50 8A E3 B0 - CD 50 0E B8 00 00 50 16
  21270.      8B C4 05 06 00 50 8B 46 - F4 25 D5 0F 50 8B 46 E6
  21271.      8B 5E E8 8B 4E EA 8B 56 - EC 8B 76 F0 8B 7E E2 83
  21272.      7E FA 08 74 14 81 7E F6 - FF FF 74 03 8E 5E F6 81
  21273.      7E F8 FF FF 74 03 8E 46 - F8 8B 6E EE 9D CB 55 8B
  21274.      EC 8B 6E 02 9C 8F 46 F4 - FF 76 FE 9D 89 46 E6 89
  21275.      5E E8 89 4E EA 89 56 EC - 8B 46 DE 89 46 EE 89 76
  21276.      F0 89 7E E2 8C 5E F6 8C - 46 F8 8E 5E FC 8D 76 E6
  21277.      1E 07 8B 7E 06 8B 4E FA - FC F3 A5 8B 76 E4 8B 7E
  21278.      E2 8B E5 5D CA 06 00 8B - 76 0A C7 04 FF FF 8B 76
  21279.      E4 8B 7E E2 8E 5E FC 8B - E5 5D CA 06 00 0B 9C 0F
  21280.      00 84 3E 00 01 01 F1 00 - C4 66 00 01 01 A8 00 CC
  21281.      8A 02 00 00 74
  21282.  
  21283.      CASEMAP.HEX (CASEMAP.OBJ)
  21284.      80 0D 00 0B 43 41 53 45 - 4D 41 50 2E 41 53 4D 5F
  21285.      96 26 00 00 06 44 47 52 - 4F 55 50 0C 43 41 53 45
  21286.      4D 41 50 5F 54 45 58 54 - 04 44 41 54 41 04 43 4F
  21287.      44 45 05 5F 44 41 54 41 - 08 98 07 00 48 14 00 03
  21288.      05 01 FC 98 07 00 48 00 - 00 06 04 01 0E 9A 04 00
  21289.      02 FF 02 5F 90 0E 00 00 - 01 07 43 41 53 45 4D 41
  21290.      50 00 00 00 60 88 04 00 - 00 A2 01 D1 A0 18 00 01
  21291.      00 00 55 8B EC 8B 5E 0A - 8B 07 FF 5E 06 8B 5E 0A
  21292.      89 07 5D CA 06 00 E3 8A - 02 00 00 74
  21293.  
  21294.  
  21295.  
  21296.  ────────────────────────────────────────────────────────────────────────────
  21297.  Appendix E  Line-Drawing Characters
  21298.  
  21299.    You can enter line-drawing characters into QuickBASIC programs by pressing
  21300.    and holding the Alt key while typing up to three decimal digits on the
  21301.    numeric keypad. You can then use QuickBASIC strings to outline screen
  21302.    areas or to draw boxes around text. This chart organizes the line-drawing
  21303.    characters by type rather than by ASCII value.
  21304.  
  21305.          201  203  187                   218  194  191
  21306.          ╔     ╦     ╗                   ┌     ┬     ┐
  21307.  
  21308.      204 ╠           ╣ 185           195 ├           ┤ 180
  21309.  
  21310.          ╚     ╩     ╝                   └     ┴     ┘
  21311.          200  202  188                   192  193  217
  21312.  
  21313.  
  21314.                          206  205  197
  21315.                          ╬     ═     ┼
  21316.  
  21317.                      186 ║           │ 179
  21318.  
  21319.                          ╪     ─     ╫
  21320.                          216  196  215
  21321.  
  21322.  
  21323.          213  209  184                   214  210  183
  21324.          ╒     ╤     ╕                   ╓     ╥     ╖
  21325.  
  21326.      198 ╞           ╡ 181           199 ╟           ╢ 182
  21327.  
  21328.          ╘     ╧     ╛                   ╙     ╨     ╜
  21329.          212  207  190                   211  208  189
  21330.  
  21331.  
  21332.  
  21333.  
  21334.  
  21335.    John Clark Craig has written several books on computer programming since
  21336.    1980, including True BASIC Programs and Subroutines. He lives with his
  21337.    family in Eagle River, Alaska, where he is a programmer for ARCO Alaska,
  21338.    Inc.
  21339.  
  21340.