PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

MS QuickBASIC 4.5 Programming in BASIC

The following document is from the Microsoft Programmer’s Library 1.3 CD-ROM.

Microsoft QuickBASIC:  Programming in BASIC


════════════════════════════════════════════════════════════════════════════


Microsoft(R) QuickBASIC:  Programming in BASIC

For IBM(R) Personal Computers and Compatibles


════════════════════════════════════════════════════════════════════════════


    Information in this document is subject to change without notice and does
    not represent a commitment on the part of Microsoft Corporation. The
    software described in this document is furnished under a license agreement
    or nondisclosure agreement. The software may be used or copied only in
    accordance with the terms of the agreement. It is against the law to copy
    the software on any medium except as specifically allowed in the license
    or nondisclosure agreement. No part of this manual may be reproduced or
    transmitted in any form or by any means, electronic or mechanical,
    including photocopying and recording, for any purpose without the express
    written permission of Microsoft.

    (C)Copyright Microsoft Corporation, 1987, 1988. All rights reserved.

    Simultaneously published in the U.S. and Canada.

    Printed and bound in the United States of America.

    Microsoft, MS, MS-DOS, CodeView, GW-BASIC, and XENIX

    are registered trademarks of Microsoft Corporation.

    Hayes is a registered trademark of Hayes Microcomputer Products, Inc.

    IBM and PS/2 are registered trademarks of International Business Machines
    Corporation.

    Intel is a registered trademark of Intel Corporation.

    ProKey is a trademark of RoseSoft, Inc.

    SideKick and SuperKey are registered trademarks of Borland International,
    Inc.

    WordStar is a registered trademark of MicroPro International Corporation.



────────────────────────────────────────────────────────────────────────────
Contents

Introduction
            The QuickBASIC Language
            The QuickBASIC Environment
            Using This Manual
                Selected Programming Topics
                The Heart of BASIC
                Appendixes
            Document Conventions
            Programming Style in this Manual

PART 1  SELECTED PROGRAMMING TOPICS

Chapter 1  Control-Flow Structures
            1.1  Changing Statement Execution Order
            1.2  Boolean Expressions
            1.3  Decision Structures
                1.3.1  Block IF...THEN...ELSE
                1.3.2  SELECT CASE
                        1.3.2.1  Using the SELECT CASE Statement
                        1.3.2.2  SELECT CASE Versus ON...GOSUB
            1.4  Looping Structures
                1.4.1  FOR...NEXT Loops
                        1.4.1.1  Exiting a FOR...NEXT Loop with EXIT FOR
                        1.4.1.2  Pausing Program Execution with FOR...NEXT
                1.4.2  WHILE...WEND Loops
                1.4.3  DO...LOOP Loops
                        1.4.3.1  Loop Tests: One Way to Exit DO...LOOP
                        1.4.3.2  EXIT DO: An Alternative Way to Exit
                                DO...LOOP
            1.5  Sample Applications
                1.5.1  Checkbook-Balancing Program (CHECK.BAS)
                1.5.2  Carriage-Return and Line-Feed Filter (CRLF.BAS)

Chapter 2  SUB and FUNCTION Procedures
            2.1  Procedures: Building Blocks for Programming
            2.2  Comparing Procedures with Subroutines and Functions
                2.2.1  Comparing SUB with GOSUB
                        2.2.1.1  Local and Global Variables
                        2.2.1.2  Use in Multiple-Module Programs
                        2.2.1.3  Operating on Different Sets of Variables
                2.2.2  Comparing FUNCTION with DEF FN
                        2.2.2.1  Local and Global Variables
                        2.2.2.2  Changing Variables Passed to the Procedure
                        2.2.2.3  Calling the Procedure within Its
                                Definition
                        2.2.2.4  Use in Multiple-Module Programs
            2.3  Defining Procedures
            2.4  Calling Procedures
                2.4.1  Calling a FUNCTION Procedure
                2.4.2  Calling a SUB Procedure
            2.5  Passing Arguments to Procedures
                2.5.1  Parameters and Arguments
                2.5.2  Passing Constants and Expressions
                2.5.3  Passing Variables
                        2.5.3.1  Passing Simple Variables
                        2.5.3.2  Passing an Entire Array
                        2.5.3.3  Passing Individual Array Elements
                        2.5.3.4  Using Array-Bound Functions
                        2.5.3.5  Passing an Entire Record
                        2.5.3.6  Passing Individual Elements of a Record
                2.5.4  Checking Arguments with the DECLARE Statement
                        2.5.4.1  When QuickBASIC Does Not Generate a DECLARE
                                Statement
                        2.5.4.2  Developing Programs outside the QuickBASIC
                                Environment
                        2.5.4.3  Using Include Files for Declarations
                        2.5.4.4  Declaring Procedures in Quick Libraries
                2.5.5  Passing Arguments by Reference
                2.5.6  Passing Arguments by Value
            2.6  Sharing Variables with SHARED
                2.6.1  Sharing Variables with Specific Procedures in a
                        Module
                2.6.2  Sharing Variables with All Procedures in a Module
                2.6.3  Sharing Variables with Other Modules
                2.6.4  The Problem of Variable Aliasing
            2.7  Automatic and STATIC Variables
            2.8  Preserving Values of Local Variables with the STATIC
                Statement
            2.9  Recursive Procedures
                2.9.1  The Factorial Function
                2.9.2  Adjusting the Size of the Stack
            2.10  Transferring Control to Another Program with CHAIN
            2.11  Sample Application: Recursive Directory Search
                    (WHEREIS.BAS)

Chapter 3  File and Device I/O
            3.1  Printing Text on the Screen
                3.1.1  Screen Rows and Columns
                3.1.2  Displaying Text and Numbers with PRINT
                3.1.3  Displaying Formatted Output with PRINT USING
                3.1.4  Skipping Spaces and Advancing to a Specific Column
                3.1.5  Changing the Number of Columns or Rows
                3.1.6  Creating a Text Viewport
            3.2  Getting Input from the Keyboard
                3.2.1  The INPUT Statement
                3.2.2  The LINE INPUT Statement
                3.2.3  The INPUT$ Function
                3.2.4  The INKEY$ Function
            3.3  Controlling the Text Cursor
                3.3.1  Positioning the Cursor
                3.3.2  Changing the Cursor's Shape
                3.3.3  Getting Information about the Cursor's Location
            3.4  Working with Data Files
                3.4.1  How Data Files Are Organized
                3.4.2  Sequential and Random-Access Files
                3.4.3  Opening a Data File
                        3.4.3.1  File Numbers in BASIC
                        3.4.3.2  File Names in BASIC
                3.4.4  Closing a Data File
                3.4.5  Using Sequential Files
                        3.4.5.1  Records in Sequential Files
                        3.4.5.2  Putting Data in a New Sequential File
                        3.4.5.3  Reading Data from a Sequential File
                        3.4.5.4  Adding Data to a Sequential File
                        3.4.5.5  Other Ways to Write Data to a Sequential
                                File
                        3.4.5.6  Other Ways to Read Data from a Sequential
                                File
                3.4.6  Using Random-Access Files
                        3.4.6.1  Records in Random-Access Files
                        3.4.6.2  Adding Data to a Random-Access File
                        3.4.6.7  Reading Data Sequentially
                        3.4.6.8  Using Record Numbers to Retrieve Records
                3.4.7  Binary File I/O
                        3.4.7.1  Comparing Binary Access and Random Access
                        3.4.7.2  Positioning the File Pointer with SEEK
            3.5  Working with Devices
                3.5.1  Differences between Device I/O and File I/O
                3.5.2  Communications through the Serial Port
            3.6  Sample Applications
                3.6.1  Perpetual Calendar (CAL.BAS)
                3.6.2  Indexing a Random-Access File (INDEX.BAS)
                3.6.3  Terminal Emulator (TERMINAL.BAS)

Chapter 4  String Processing
            4.1  Strings Defined
            4.2  Variable- and Fixed-Length Strings
                4.2.1  Variable-Length Strings
                4.2.2  Fixed-Length Strings
            4.3  Combining Strings
            4.4  Comparing Strings
            4.5  Searching for Strings
            4.6  Retrieving Parts of Strings
                4.6.1  Retrieving Characters from the Left Side of a
                        String
                4.6.2  Retrieving Characters from the Right Side of a
                        String
                4.6.3  Retrieving Characters from Anywhere in a String
            4.7  Generating Strings
            4.8  Changing the Case of Letters
            4.9  Strings and Numbers
            4.10 Changing Strings
            4.11 Sample Application: Converting a String to a Number
                    (STRTONUM.BAS)

Chapter 5  Graphics
            5.1  What You Need for Graphics Programs
            5.2  Pixels and Screen Coordinates
            5.3  Drawing Basic Shapes: Points, Lines, Boxes, and Circles
                5.3.1  Plotting Points with PSET and PRESET
                5.3.2  Drawing Lines and Boxes with LINE
                        5.3.2.1  Using the STEP Option
                        5.3.2.2  Drawing Boxes
                        5.3.2.3  Drawing Dotted Lines
            5.4  Drawing Circles and Ellipses with CIRCLE
                5.4.1  Drawing Circles
                5.4.2  Drawing Ellipses
                5.4.3  Drawing Arcs
                5.4.4  Drawing Pie Shapes
                5.4.5  Drawing Shapes to Proportion with the Aspect Ratio
            5.5  Defining a Graphics Viewport
            5.6  Redefining Viewport Coordinates with WINDOW
                5.6.1  The Order of Coordinate Pairs
                5.6.2  Keeping Track of View and Physical Coordinates
            5.7  Using Colors
                5.7.1  Selecting a Color for Graphics Output
                5.7.2  Changing the Foreground or Background Color
                5.7.3  Changing Colors with PALETTE and PALETTE USING
            5.8  Painting Shapes
                5.8.1  Painting with Colors
                5.8.2  Painting with Patterns: Tiling
                        5.8.2.1  Pattern-Tile Size in Different Screen
                                Modes
                        5.8.2.2  Creating a Single-Color Pattern in Screen
                                Mode 2
                        5.8.2.3  Creating a Multicolor Pattern in Screen Mode
                                1
                        5.8.2.4  Creating a Multicolor Pattern in Screen Mode
                                8
            5.9  DRAW: a Graphics Macro Language
            5.10 Basic Animation Techniques
                5.10.1  Saving Images with GET
                5.10.2  Moving Images with PUT
                5.10.3  Animation with GET and PUT
                5.10.4  Animating with Screen Pages
            5.11 Sample Applications
                5.11.1  Bar-Graph Generator (BAR.BAS)
                5.11.2  Color in a Figure Generated Mathematically
                        (MANDEL.BAS)
                5.11.3  Pattern Editor (EDPAT.BAS)

Chapter 6  Error and Event Trapping
            6.1  Error Trapping
                6.1.1  Activating Error Trapping
                6.1.2  Writing an Error-Handling Routine
                        6.1.2.1  Using ERR to Identify Errors
                        6.1.2.2  Returning from an Error-Handling Routine
            6.2  Event Trapping
                6.2.1  Detecting Events by Polling
                6.2.2  Detecting Events by Trapping
                6.2.3  Specifying the Event to Trap and Activating Event
                        Trapping
                6.2.4  Events That BASIC Can Trap
                6.2.5  Suspending or Disabling Event Trapping
                6.2.6  Trapping Keystrokes
                        6.2.6.1  Trapping User-Defined Keys
                        6.2.6.2  Trapping User-Defined Shifted Keys
                6.2.7  Trapping Music Events
            6.3  Error and Event Trapping in SUB or FUNCTION Procedures
            6.4  Trapping across Multiple Modules
                6.4.1  Event Trapping across Modules
                6.4.2  Error Trapping across Modules
            6.5  Trapping Errors and Events in Programs Compiled with BC
            6.6  Sample Application: Trapping File-Access Errors
                (FILERR.BAS)

Chapter 7  Programming with Modules
            7.1  Why Use Modules?
            7.2  Main Modules
            7.3  Modules Containing Only Procedures
            7.4  Creating a Procedures-Only Module
            7.5  Loading Modules
            7.6  Using the DECLARE Statement with Multiple Modules
            7.7  Accessing Variables from Two or More Modules
            7.8  Using Modules During Program Development
            7.9  Compiling and Linking Modules
            7.10 Quick Libraries
                7.10.1  Creating Quick Libraries
            7.11 Tips for Good Programming with Modules

PART 2  HEART OF BASIC

Chapter 8  Statement and Function Summary
            ABS Function
            ASC Function
            ATN Function
            BEEP Statement
            BLOAD Statement
            BSAVE Statement
            CALL Statement (BASIC Procedures)
            CALL, CALLS Statement (Non-BASIC Procedures)
            CALL INT86OLD Statements
            CALL ABSOLUTE Statement
            CALL INTERRUPT Statements
            CDBL Function
            CHAIN Statement
            CHDIR Statement
            CHR$ Function
            CINT Function
            CIRCLE Statement
            CLEAR Statement
            CLNG Function
            CLOSE Statement
            CLS Statement
            COLOR Statement
            COM Statements
            COMMAND$ Function
            COMMON Statement
            CONST Statement
            COS Function
            CSNG Function
            CSRLIN Function
            CVI, CVS, CVL, CVD Functions
            CVSMBF, CVDMBF Functions
            DATA Statement
            DATE$ Function
            DATE$ Statement
            DECLARE Statement (BASIC Procedures)
            DECLARE Statement (Non-BASIC Procedures)
            DEF FN Statement
            DEF SEG Statement
            DEFtype Statements
            DIM Statement
            DO...LOOP Statements
            DRAW Statement
            END Statement
            ENVIRON$ Function
            ENVIRON Statement
            EOF Function
            ERASE Statement
            ERDEV, ERDEV$ Functions
            ERR, ERL Functions
            ERROR Statement
            EXIT Statement
            EXP Function
            FIELD Statement
            FILEATTR Function
            FILES Statement
            FIX Function
            FOR...NEXT Statement
            FRE Function
            FREEFILE Function
            FUNCTION Statement
            GET Statement──File I/O
            GET Statement──Graphics
            GOSUB...RETURN Statements
            GOTO Statement
            HEX$ Function
            IF...THEN...ELSE Statements
            INKEY$ Function
            INP Function
            INPUT$ Function
            INPUT Statement
            INPUT # Statement
            INSTR Function
            INT Function
            IOCTL$ Function
            IOCTL Statement
            KEY Statements
            KEY(n) Statements
            KILL Statement
            LBOUND Function
            LCASE$ Function
            LEFT$ Function
            LEN Function
            LET Statement
            LINE Statement
            LINE INPUT Statement
            LINE INPUT # Statement
            LOC Function
            LOCATE Statement
            LOCK...UNLOCK Statement
            LOF Function
            LOG Function
            LPOS Function
            LPRINT, LPRINT USING Statements
            LSET Statement
            LTRIM$ Function
            MID$ Function
            MID$ Statement
            MKD$, MKI$, MKL$, MKS$ Functions
            MKDIR Statement
            MKSMBF$, MKDMBF$ Functions
            NAME Statement
            OCT$ Function
            ON ERROR Statement
            ON event Statements
            ON UEVENT GOSUB Statement
            ON...GOSUB, ON...GOTO Statements
            OPEN Statement
            OPEN COM Statement
            OPTION BASE Statement
            OUT Statement
            PAINT Statement
            PALETTE, PALETTE USING Statements
            PCOPY Statement
            PEEK Function
            PEN Function
            PEN ON, OFF, and STOP Statements
            PLAY Function
            PLAY Statement
            PLAY ON, OFF, and STOP Statements
            PMAP Function
            POINT Function
            POKE Statement
            POS Function
            PRESET Statement
            PRINT Statement
            PRINT #, PRINT # USING Statements
            PRINT USING Statement
            PSET Statement
            PUT Statement──File I/O
            PUT Statement──Graphics
            RANDOMIZE Statement
            READ Statement
            REDIM Statement
            REM Statement
            RESET Statement
            RESTORE Statement
            RESUME Statement
            RETURN Statement
            RIGHT$ Function
            RMDIR Statement
            RND Function
            RSET Statement
            RTRIM$ Function
            RUN Statement
            SADD Function
            SCREEN Function
            SCREEN Statement
            SEEK Function
            SEEK Statement
            SELECT CASE Statement
            SETMEM Function
            SGN Function
            SHARED Statement
            SHELL Statement
            SIN Function
            SLEEP Statement
            SOUND Statement
            SPACE$ Function
            SPC Function
            SQR Function
            STATIC Statement
            STICK Function
            STOP Statement
            STR$ Function
            STRIG Function and Statement
            STRIG ON, OFF, and STOP Statements
            STRING$ Function
            SUB Statements
            SWAP Statement
            SYSTEM Statement
            TAB Function
            TAN Function
            TIME$ Function
            TIME$ Statement
            TIMER Function
            TIMER ON, OFF, and STOP Statements
            TRON/TROFF Statements
            TYPE Statement
            UBOUND Function
            UCASE$ Function
            UEVENT Statement
            UNLOCK Statement
            VAL Function
            VARPTR, VARSEG Functions
            VARPTR$ Function
            VIEW Statement
            VIEW PRINT Statement
            WAIT Statement
            WHILE...WEND Statement
            WIDTH Statement
            WINDOW Statement
            WRITE Statement
            WRITE # Statement

Chapter 9  Quick-Reference Tables
            9.1  Summary of Control-Flow Statements
            9.2  Summary of Statements Used in BASIC Procedures
            9.3  Summary of Standard I/O Statements
            9.4  Summary of File I/O Statements
            9.5  Summary of String-Processing Statements and Functions
            9.6  Summary of Graphics Statements and Functions
            9.7  Summary of Trapping Statements and Functions

Appendix A  Converting BASICA Programs to QuickBASIC

Appendix B  Differences from Previous Versions of QuickBASIC

Appendix C  Limits in QuickBASIC

Appendix D  Keyboard Scan Codes and ASCII Character Codes

Appendix E  BASIC Reserved Words

Appendix F  Metacommands

Appendix G  Compiling and Linking from DOS

Appendix H  Creating and Using Quick Libraries

Appendix I  Error Messages

Figures
Figure 1.1  Logic of FOR...NEXT Loop with Positive STEP
Figure 1.2  Logic of FOR...NEXT Loop with Negative STEP
Figure 1.3  Logic of WHILE...WEND Loop
Figure 1.4  Logic of DO WHILE...LOOP
Figure 1.5  Logic of DO UNTIL...LOOP
Figure 1.6  Logic of DO...LOOP WHILE
Figure 1.7  Logic of DO...LOOP UNTIL
Figure 2.1  Parameters and Arguments
Figure 3.1  Text Output on Screen
Figure 3.2  Records in Sequential Files
Figure 3.3  Records in a Random-Access File
Figure 5.1  Coordinates of Selected Pixels in Screen Mode 2
Figure 5.2  How Angles Are Measured for CIRCLE
Figure 5.3  The Aspect Ratio In Screen Mode 1
Figure 5.4  WINDOW Contrasted with WINDOW SCREEN
Figure 5.5  Patterned Circle
Figure 6.1  Program Flow of Control with RESUME
Figure 6.2  Program Flow of Control with RESUME NEXT
Figure 7.1  Main Module Showing Module-Level Code and Procedures
Figure H.1  Make Library Dialog Box

Tables
Table 1.1  Relational Operators Used in Basic
Table 1.2  Block IF...THEN...ELSE Syntax and Example
Table 1.3  SELECT CASE Syntax and Example
Table 1.4  FOR...NEXT Syntax and Example
Table 1.5  WHILE...WEND Syntax and Example
Table 1.6  DO...LOOP Syntax and Example: Test at the Beginning
Table 1.7  DO...LOOP Syntax and Example: Test at the End
Table 3.1  Devices Supported by BASIC for I/O
Table 5.1  Color Palettes in Screen Mode 1
Table 5.2  Background Colors in Screen Mode 1
Table 5.3  Binary to Hexadecimal Conversion
Table 5.4  The Effect of Different Action Options in Screen Mode 2
Table 5.5  The Effect of Different Action Options on Color in Screen Mode 1
(Palette 1)
Table 6.1  BC Command-Line Options for Error and Event Trapping
Table 9.1  Statements Used in Looping and Decision-Making
Table 9.2  Statements Used in Procedures
Table 9.3  Statements and Functions Used for Standard I/O
Table 9.4  Statements and Functions Used for Data-File I/O
Table 9.5  Statements and Functions Used for Processing Strings
Table 9.6  Statements and Functions Used for Graphics Output
Table 9.7  Statements andn Functions Used in Error and Event Trapping
Table A.1  Statements Requiring Modification
Table B.1  Features of Microsoft QuickBASIC Version 4.5
Table B.2  Menus with New Commands in QuickBASIC Version 4.5
Table B.3  Editing-Key Changes
Table B.4  QB and BC Options Not Used in QuickBASIC Versions 4.0 or 4.5
Table B.5  Options Introduced in Version 4.0 for the QB and BC Commands
Table B.6  Debugging-Key Changes
Table B.7  Changes to the BASIC Language
Table C.1  QuickBASIC Limits
Table G.1  Input to the BC Command
Table G.2  Input to the LINK Command
Table G.3  Input to the LIB Command
Table I.1  Run-Time Error Codes



────────────────────────────────────────────────────────────────────────────
Introduction

    Microsoft(R) QuickBASIC 4.5 is a major advance in making BASIC both more
    powerful and easier to use. It provides the most advanced BASIC yet
    offered for microcomputers, supported by an operating environment that
    allows you to focus on program creation──not the mechanics of writing or
    debugging.


The QuickBASIC Language

    If you already know how to program in BASICA (or a similar interpreted
    BASIC), you'll appreciate the enhanced language features QuickBASIC
    provides to make it easier to write and maintain your software. For
    example:

    ■ The SELECT CASE statement cleanly transfers control to any block of code
    without the use of nested IF...THEN...ELSE statements. SELECT CASE
    permits an exceptionally wide range of test expressions, so you can
    create exactly the comparison you need.

    ■ QuickBASIC's SUB and FUNCTION procedures allow you to place groups of
    program statements into subprograms that your main program can call
    repeatedly. QuickBASIC's modularity makes it easy to save these
    procedures and reuse them in other programs.

    ■ QuickBASIC procedures are fully recursive──a procedure can call itself
    repeatedly. This simplifies the programming of many numerical and
    sorting algorithms that are best expressed recursively.

    ■ You can define your own data types, made up of any combination of
    integer, real, and string variables. Related variables can be
    conveniently grouped under a single name, which simplifies passing them
    to a procedure or writing them to a file.

    ■ QuickBASIC supports binary file access. Your programs can read and
    manipulate files in any format because binary I/O can directly access
    any byte in the file.

    QuickBASIC is a powerful development tool for professional use. Yet it is
    also the ideal language for beginning and intermediate programmers──people
    who aren't professional programmers but need a language that helps them
    reach their programming goals efficiently.


The QuickBASIC Environment

    QuickBASIC isn't just an outstanding language. It is also an integrated
    programming environment that significantly simplifies writing and
    debugging software:

    ■ As you type in your program, a smart editor checks each line for syntax
    errors. When you are ready to run, press a single key to execute the
    program instantly. If something is wrong, use the full-screen editor to
    correct the problem, then run the program again.

    ■ You can debug your programs without exiting QuickBASIC. The integrated
    debugger lets you examine and alter variables, execute any part of the
    program, or halt execution when a particular condition is met. All these
    things happen within the QuickBASIC environment; you don't have to alter
    the program or add PRINT statements.

    ■ QuickBASIC 4.5 has two new commands to make the debugger even more
    powerful: the Instant Watch command and the Break on Errors command.

    ■ Also new to QuickBASIC 4.5 is the Microsoft QB Advisor, our on-line
    help. The QB Advisor is always at hand, whether you are writing,
    running, or debugging. Just place the cursor on the keyword or
    user-defined name you want to know more about, then press F1. The QB
    Advisor describes the syntax of BASIC statements and functions, explains
    how to use them, and even provides usable programming examples.


Using This Manual

    This manual is in three parts. Part 1, "Selected Programming Topics,"
    provides information on specific programming techniques and strategies.
    Part 2, "Heart of BASIC," and the appendixes contain important reference
    material.

Selected Programming Topics

    Each chapter in this first section focuses on a single programming area.
    Studying this material will help you to quickly master

    ■ Control-flow structures

    ■ SUB and FUNCTION procedures

    ■ File and device input and output

    ■ String processing

    ■ Graphics

    ■ Error and event trapping

    ■ Programming with modules

    The presentation of each topic is straightforward and easy to understand,
    with many short programming examples that demonstrate how each part of
    BASIC works. The progression is from simple to more complex topics, so you
    can work through this material at your own pace without worrying about the
    order in which to study it. The focus throughout is on utility, not
    theory──how you can solve common programming problems with QuickBASIC.

    In addition to the short examples, the chapters contain complete working
    programs that demonstrate the programming principles presented in that
    chapter. For your convenience, these programs are also included on your
    QuickBASIC release disks.

    If you're an experienced BASIC programmer, you'll probably want to browse
    through the table of contents for a topic that catches your interest. If
    you're a novice programmer, though, you should probably work through each
    chapter from beginning to end. If you have never programmed in any
    language, you should start with Chapter 4 of Learning to Use Microsoft
    QuickBASIC, "Interlude: BASIC for Beginners."

    Regardless of your interests or background, these seven chapters will help
    you learn almost everything you need to know to write sophisticated BASIC
    applications.

The Heart of BASIC

    The second part of this manual, the "Heart of BASIC," is a handy,
    two-part quick reference to BASIC statements and functions.

    Chapter 8, "Statement and Function Summary," is an alphabetically
    arranged summary of every BASIC keyword, describing its action or use and
    displaying its syntax. If your memory needs jogging on statement or
    function use, turn to this section.

    In Chapter 9, "Quick-Reference Tables," the most commonly used BASIC
    statements and functions are displayed in six sections in table form, with
    each statement given a brief description. The contents of these sections
    match the material presented in Chapters 1 through 6 of "Selected
    Programming Topics." If you are trying to figure out how to accomplish a
    particular programming task, turn to this section.

Appendixes

    The third section of this manual is a group of appendixes containing
    reference information on

    ■ Converting BASICA programs

    ■ Differences from previous versions

    ■ Limits in QuickBASIC

    ■ Keyboard scan codes and ASCII codes

    ■ BASIC reserved words

    ■ Metacommands

    ■ Compiling and linking from DOS

    ■ Creating and using Quick libraries

    ■ Error messages


Document Conventions

    This manual uses the following typographic conventions:

╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Example of Convention    Description
    ──────────────────────────────────────────────────────────────────────────
    QB.LIB, ADD.EXE, COPY,   Uppercase (capital) letters indicate file names
    LINK, /X                 and DOS-level commands. Uppercase is also used
                            for command-line options (unless the application
                            accepts only lowercase).

    SUB, IF, LOOP, PRINT,    Bold capital letters indicate language-specific
    WHILE, TIME$             keywords with special meaning to Microsoft BASIC.
    Example of Convention    Description
    ──────────────────────────────────────────────────────────────────────────
    WHILE, TIME$             keywords with special meaning to Microsoft BASIC.
                            Keywords are a required part of statement syntax,
                            unless they are enclosed in double brackets as
                            explained below. In programs you write, you must
                            enter keywords exactly as shown. However, you can
                            use uppercase letters or lowercase letters.

    CALL NewProc(arg1!,      This kind of type is used for program examples,
    var2%)                   program output, and error messages within the
                            text.

    '$INCLUDE:'BC.BI'        A column of three dots indicates that part of the
    .                      example program has been intentionally omitted.
    .
    .
    CHAIN "PROG1"
    END

    ' Make one pass          The apostrophe (single right quotation mark)
    Example of Convention    Description
    ──────────────────────────────────────────────────────────────────────────
    ' Make one pass          The apostrophe (single right quotation mark)
                            marks the beginning of a comment in sample
                            programs.

    filespec                 Italic letters indicate placeholders for
                            information you must supply, such as a file name.
                            Italics are also occasionally used in the text
                            for emphasis.

    [[optional-item]]        Items inside double square brackets are optional.

    {choice1 | choice2}      Braces and a vertical bar indicate a choice among
                            two or more items. You must choose one of the
                            items unless all of the items are also enclosed
                            in double square brackets.

    repeating elements...    Three dots following an item indicate that more
                            items having the same form may appear.

    Example of Convention    Description
    ──────────────────────────────────────────────────────────────────────────

    ALT+F1                   Capital letters are used for the names of keys
                            and key sequences, such as ENTER and CTRL+R.

                            A plus (+) indicates a combination of keys. For
                            example, CTRL+E means to hold down the CTRL key
                            while pressing the E key.

                            The carriage-return key, sometimes marked with a
                            bent arrow, is referred to as ENTER.

                            The cursor movement ("arrow") keys on the numeric
                            keypad are called DIRECTION keys. Individual
                            DIRECTION keys are referred to by the direction
                            of the arrow on the key top (LEFT, RIGHT, UP,
                            DOWN) or the name on the key top (PGUP, PGDN).

                            The key names used in this manual correspond to
                            the names on the IBM(R) Personal Computer keys.
    Example of Convention    Description
    ──────────────────────────────────────────────────────────────────────────
                            the names on the IBM(R) Personal Computer keys.
                            Other machines may use different names.

    "defined term"           Quotation marks usually indicate a new term
                            defined in the text.

    Video Graphics Array     Acronyms are usually spelled out the first time
    (VGA)                    they are used.
    ──────────────────────────────────────────────────────────────────────────


    The syntax below (for the "LOCK...UNLOCK" statements) illustrates many of
    the typographic conventions in this manual:

    LOCK[[#]]filenumber[[,{record|[[start]] TO end}]] . . .
    UNLOCK[[#]]filenumber[[,{record |[[start]] TO end}]]

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Throughout this manual, the term "DOS" refers to both the MS-DOS(R) and
    IBM Personal Computer DOS operating systems. The name of a specific
    operating system is used when it is necessary to note features that are
    unique to that system. The term "BASICA" refers to interpreted versions
    of BASIC in general.
    ──────────────────────────────────────────────────────────────────────────


Programming Style in this Manual

    The following guidelines were used in writing programs in this manual and
    on the distribution disks. These guidelines are only recommendations for
    program readability; you are not obliged to follow them when writing your
    own programs.

    ■ Keywords and symbolic constants appear in uppercase letters:

    ' PRINT, DO, LOOP, UNTIL are keywords:
    PRINT "Title Page"
    DO LOOP UNTIL Response$ = "N"

    ' FALSE and TRUE are symbolic constants
    ' equal to 0 and -1, respectively:
    CONST FALSE = 0, TRUE = NOT FALSE

    ■ Variable names are in lowercase with an initial capital letter; variable
    names with more than one syllable may contain other capital letters to
    clarify the division:

    NumRecords% = 45
    DateOfBirth$ = "11/24/54"

    ■ Line labels are used instead of line numbers. The use of line labels is
    restricted to event-trapping and error-handling routines, as well as
    DATA statements when referenced with RESTORE:

    ' TimerHandler and ScreenTwoData are line labels:
    ON TIMER GOSUB TimerHandler
    RESTORE ScreenTwoData

    ■ As noted in the preceding section, a single apostrophe (') introduces
    comments:

    ' This is a comment; these two lines
    ' are ignored when the program is running.

    ■ Control-flow blocks and statements in procedures or subroutines are
    indented from the enclosing code:

    SUB GetInput STATIC
        FOR I% = 1 TO 10
            INPUT X
            IF X > 0 THEN
                .
                .
                .
            ELSE
                .
                .
                .
            END IF
        NEXT I%
    END SUB



────────────────────────────────────────────────────────────────────────────
PART 1  SELECTED PROGRAMMING TOPICS
────────────────────────────────────────────────────────────────────────────

    Part 1 introduces the fundamentals of programming in BASIC. Simpler topics
    are covered first.

    Chapter 1 discusses the control-flow structures that direct your
    program's execution. Chapter 2 is about QuickBASIC SUB and FUNCTION
    procedures, two very powerful programming tools. Chapter 3 describes the
    ways you can use QuickBASIC to work with the data your program accepts and
    produces. Chapter 4 covers the use of text strings, and Chapter 5
    presents QuickBASIC's graphics capabilities.

    More advanced topics are covered in the last two chapters. Chapter 6
    looks at error and event trapping, and Chapter 7 tells you how to use
    programming with modules to your advantage.



────────────────────────────────────────────────────────────────────────────
Chapter 1  Control-Flow Structures

    This chapter shows you how to use control-flow structures──specifically,
    loops and decision statements──to control the flow of your program's
    execution. Loops make a program execute a sequence of statements however
    many times you want. Decision statements let the program decide which of
    several alternative paths to take.

    When you are finished with this chapter, you will know how to do the
    following tasks related to using loops and decision statements in your
    BASIC programs:

    ■ Compare expressions using relational operators

    ■ Combine string or numeric expressions with logical operators and
    determine if the resulting expression is true or false

    ■ Create branches in the flow of the program with the statements
    IF...THEN...ELSE and SELECT CASE

    ■ Write loops that repeat statements a specific number of times

    ■ Write loops that repeat statements while or until a certain condition is
    true


1.1  Changing Statement Execution Order

    Left unchecked by control-flow statements, a program's logic flows through
    statements from left to right, top to bottom. While some very simple
    programs can be written with only this unidirectional flow, most of the
    power and utility of any programming language comes from its ability to
    change statement execution order with decision structures and loops.

    With a decision structure, a program can evaluate an expression, then
    branch to one of several different groups of related statements (statement
    blocks) depending on the result of the evaluation. With a loop, a program
    can repeatedly execute statement blocks.

    If you are coming to this version of BASIC after programming in BASICA,
    you will appreciate the added versatility of these additional control-flow
    structures:

    ■ The block IF...THEN...ELSE statement

    ■ The SELECT CASE statement

    ■ The DO...LOOP and EXIT DO statements

    ■ The EXIT FOR statement, which provides an alternative way to exit
    FOR...NEXT loops


1.2  Boolean Expressions

    A Boolean expression is any expression that returns the value "true" or
    "false." BASIC uses Boolean expressions in certain kinds of decision
    structures and loops. The following IF...THEN...ELSE statement contains a
    Boolean expression, X < Y:

    IF X < Y THEN CALL Procedure1 ELSE CALL Procedure2

    In the previous example, if the Boolean expression is true (if the value
    of the variable X is in fact less than the value of the variable Y), then
    Procedure1 is executed; otherwise (if X is greater than or equal to Y),
    Procedure2 is executed.

    The preceding example also demonstrates a common use of Boolean
    expressions: comparing two other expressions (in this case, X and Y) to
    determine the relationship between them. These comparisons are made with
    the relational operators shown in Table 1.1.

    Table 1.1  Relational Operators Used in BASIC
    Operator                 Meaning
    ──────────────────────────────────────────────────────────────────────────
    =                        Equal

    < >                      Not equal

    <                        Less than

    <=                       Less than or equal to

    >                        Greater than

    >=                       Greater than or equal to
    ──────────────────────────────────────────────────────────────────────────

    You can use these relational operators to compare string expressions. In
    this case "greater than," "less than," and so on refer to alphabetical
    order. For example, the following expression is true, since the word
    "deduce" comes alphabetically before the word "deduct":

    "deduce" < "deduct"

    Logical operators

    Boolean expressions also frequently use the "logical operators" AND, OR,
    NOT, XOR, IMP, and EQV. These operators allow you to construct compound
    tests from one or more Boolean expressions. For example,

    expression1 AND expression2

    is true only if expression1 and expression2 are both true. Thus, in the
    following example, the message All sorted is printed only if both the
    Boolean expressions X <= Y and Y <= Z are true:

    IF (X <= Y) AND (Y <= Z) THEN PRINT "All sorted"

    The parentheses around the Boolean expressions in the last example are not
    really necessary, since relational operators such as <= are evaluated
    before logical operators such as AND. However, parentheses make a complex
    Boolean expression more readable and ensure that its components are
    evaluated in the order that you intend.

    BASIC uses the numeric values -1 and 0 to represent true and false,
    respectively. You can see this by asking BASIC to print a true expression
    and a false expression, as in the next example:

    x = 5
    y = 10
    PRINT x < y  ' Evaluate, print a "true"  Boolean expression.
    PRINT x > y  ' Evaluate, print a "false" Boolean expression.

    Output

    -1
    0

    The value -1 for true makes more sense when you consider how BASIC's NOT
    operator works: NOT inverts each bit in the binary representation of its
    operand, changing 1 bits to 0 bits, and 0 bits to 1 bits. Therefore, since
    the integer value 0 (false) is stored internally as a sequence of sixteen
    0 bits, NOT 0 (true) is stored internally as sixteen 1 bits, as shown
    below:

    0000000000000000

    TRUE = NOT FALSE = 1111111111111111

    In the two's-complement method that BASIC uses to store integers, sixteen
    1 bits represent the value -1.

    Note that BASIC outputs -1 when it evaluates a Boolean expression as true;
    however, BASIC considers any nonzero value to be true, as shown by the
    output from the following example:

    INPUT "Enter a value: ", x

    IF x THEN PRINT x "is true."

    Output

    Enter a value: 2
    2 is true.

    The NOT operator in BASIC is a "bitwise" operator. Some programming
    languages, such as C and Pascal, have both a bitwise NOT operator and a
    "logical" NOT operator. The distinction is as follows:

    ■ A bitwise NOT returns false (0) only for the value -1.

    ■ A logical NOT returns false (0) for any true (nonzero) value.

    In BASIC, for any true expression not equal to -1, NOT expression returns
    another true value, as shown by the following list:

    Value of expression      Value of NOT expression
    ──────────────────────────────────────────────────────────────────────────
    1                       -2

    2                       -3

    -2                        1

    -1                        0
    ──────────────────────────────────────────────────────────────────────────

    So beware: NOT expression is false only if expression evaluates to a value
    of -1. If you define Boolean constants or variables for use in your
    programs, use -1 for true.

    You can use the values 0 and -1 to define helpful mnemonic Boolean
    constants for use in loops or decisions. This technique is used in many of
    the examples in this manual, as shown in the following program fragment,
    which sorts the elements of an array named Amount in ascending order:

    ' Define symbolic constants to use in program:
    CONST FALSE = 0, TRUE = NOT FALSE
    .
    .
    .

    DO
        Swaps = FALSE
        FOR I = 1 TO TransacNum - 1
        IF Amount(I) < Amount(I+1) THEN
            SWAP Amount(I), Amount(I+1)
            Swaps = TRUE
        END IF
        NEXT I
    LOOP WHILE Swaps        ' Keep looping while Swaps is TRUE.
    .
    .
    .


1.3  Decision Structures

    Based on the value of an expression, decision structures cause a program
    to take one of the following two actions:

    1. Execute one of several alternative statements within the decision
        structure itself

    2. Branch to another part of the program outside the decision structure

    In BASICA, decision-making is handled solely by the single-line
    IF...THEN[[...ELSE]] statement. In its simplest form (IF...THEN) the
    expression following the IF keyword is evaluated. If the expression is
    true, the program executes the statements following the THEN keyword; if
    the expression is false, the program continues with the next line after
    the IF...THEN statement. Lines 50 and 70 from the following BASICA program
    fragment show examples of IF...THEN:

    30  INPUT A
    40  ' If A is greater than 100, print a message and branch
    45  ' back to line 30; otherwise, go on to line 60:
    50  IF A > 100 THEN PRINT "Too big": GOTO 30
    60  ' If A is equal to 100, branch to line 300;
    65  ' otherwise, go on to line 80:
    70  IF A = 100 THEN GOTO 300
    80  PRINT A/100: GOTO 30
    .
    .
    .

    By adding the ELSE clause to an IF...THEN statement, you can have your
    program take one set of actions (those following the THEN keyword) if an
    expression is true, and another set of actions (those following the ELSE
    keyword) if it is false. The next program fragment shows how ELSE works in
    an IF...THEN...ELSE statement:

    10 INPUT "What is your password"; Pass$
    15 ' If user enters "sword", branch to line 50;
    20 ' otherwise, print a message and branch back to line 10:
    30 IF Pass$="sword" THEN 50 ELSE PRINT "Try again": GOTO 10
    .
    .
    .

    While BASICA's single-line IF...THEN...ELSE is adequate for simple
    decisions, it can lead to virtually unreadable code in cases of more
    complicated ones. This is especially true if you write your programs so
    all alternative actions take place within the IF...THEN...ELSE statement
    itself or if you nest IF...THEN... ELSE statements (that is, if you put
    one IF...THEN...ELSE inside another, a perfectly legal construction). As
    an example of how difficult it is to follow even a simple test, consider
    the next fragment from a BASICA program:

    10 ' The following nested IF...THEN...ELSE statements print
    15 ' different output for each of the following four cases:
    20 '    1) A <= 50, B <= 50     3)  A > 50, B <= 50
    25 '    2) A <= 50, B > 50      4)  A > 50, B > 50
    30
    35 INPUT A, B
    40
    45 ' Note: even though line 70 extends over several physical
    50 ' lines on the screen, it is just one "logical line"
    55 ' (everything typed before the <ENTER> key was pressed).
    60 ' BASICA wraps long lines on the screen.
    65
    70  IF A <= 50 THEN IF B <= 50 THEN PRINT "A <= 50, B <= 50"
    ELSE PRINT "A <= 50, B > 50" ELSE IF B <= 50 THEN PRINT "A >
    50, B <= 50" ELSE PRINT "A > 50, B > 50"

    To avoid the kind of complicated statement shown above, BASIC now includes
    the block form of the IF...THEN...ELSE statement, so that a decision is no
    longer restricted to one logical line. The following shows the same BASICA
    program rewritten to use block IF...THEN...ELSE:

    INPUT A, B
    IF A <= 50 THEN
        IF B <= 50 THEN
        PRINT "A <= 50, B <= 50"
        ELSE
        PRINT "A <= 50, B > 50"
        END IF
    ELSE
        IF B <= 50 THEN
        PRINT "A > 50, B <= 50"
        ELSE
        PRINT "A > 50, B > 50"
        END IF
    END IF

    QuickBASIC also provides the SELECT CASE...END SELECT (referred to as
    SELECT CASE) statement for structured decisions.

    Both the block IF...THEN...ELSE statement and the SELECT CASE statement
    allow the appearance of your code to be based on program logic, rather
    than requiring many statements to be crowded onto one line. This gives you
    increased flexibility while you are programming, as well as improved
    program readability and ease of maintenance when you are done.

1.3.1  Block IF...THEN...ELSE

    Table 1.2 shows the syntax of the block IF...THEN...ELSE statement and
    gives an example of its use.

    Table 1.2  Block IF...THEN...ELSE Syntax and Example
    Syntax                               Example
    ──────────────────────────────────────────────────────────────────────────
    IF condition1 THEN                   IF X > 0 THEN
    [[statementblock-1]]                 PRINT "X is positive"
    [[ELSEIF condition2 THEN               PosNum = PosNum + 1
    [[statementblock-2]]]]             ELSEIF X < 0 THEN
    .                                      PRINT "X is negative"
    .                                      NegNum = NegNum + 1
    .                                    ELSE
    [[ELSE                                 PRINT "X is zero"
    [[statementblock-n]]]]             END IF
    END IF
    ──────────────────────────────────────────────────────────────────────────

    The arguments condition1, condition2, and so on are expressions. They can
    be any numeric expression──in which case true becomes any nonzero value,
    and false is zero──or they can be Boolean expressions, in which case true
    is -1 and false is zero. As explained in Section 1.2, Boolean expressions
    typically compare two numeric or string expressions using one of the
    relational operators such as < or >=.

    Each IF, ELSEIF, and ELSE clause is followed by a block of statements.
    None of the statements in the block can be on the same line as the IF,
    ELSEIF, or ELSE clause; otherwise, BASIC considers it a single-line
    IF...THEN statement.

    BASIC evaluates each of the expressions in the IF and ELSEIF clauses from
    top to bottom, skipping over statement blocks until it finds the first
    true expression. When it finds a true expression, it executes the
    statements corresponding to the expression, then branches out of the block
    to the statement following the END IF clause.

    If none of the expressions in the IF or ELSEIF clauses is true, BASIC
    skips to the ELSE clause, if there is one, and executes its statements.
    Otherwise, if there is no ELSE clause, the program continues with the next
    statement after the END IF clause.

    The ELSE and ELSEIF clauses are both optional, as shown in the following
    example:

    ' If the value of X is less than 100, do the two statements
    ' before END IF; otherwise, go to the INPUT statement
    ' following END IF:

    IF X < 100 THEN
        PRINT X
        Number = Number + 1
    END IF
    INPUT "New value"; Response$
    .
    .
    .

    A single block IF...THEN...ELSE can contain multiple ELSEIF statements, as
    shown below:

    IF C$ >= "A" AND C$ <= "Z" THEN
        PRINT "Capital letter"
    ELSEIF C$ >= "a" AND C$ <= "z" THEN
        PRINT "Lowercase letter"
    ELSEIF C$ >= "0" AND C$ <= "9" THEN
        PRINT "Number"
    ELSE
        PRINT "Not alphanumeric"
    END IF

    At most, only one block of statements is executed, even if more than one
    condition is true. For example, if you enter the word ace as input to the
    next example, it prints the message Input too short but does not print the
    message Can't start with an a:

    INPUT Check$
    IF LEN(Check$) > 6 THEN
        PRINT "Input too long"
    ELSEIF LEN(Check$) < 6 THEN
        PRINT "Input too short"
    ELSEIF LEFT$(Check$, 1) = "a" THEN
        PRINT "Can't start with an a"
    END IF

    IF...THEN...ELSE statements can be nested; in other words, you can put an
    IF...THEN...ELSE statement inside another IF...THEN...ELSE statement, as
    shown here:

    IF X > 0 THEN
        IF Y > 0 THEN
        IF Z > 0 THEN
            PRINT "All are greater than zero."
        ELSE
            PRINT "Only X and Y greater than zero."
        END IF
        END IF
    ELSEIF X = 0 THEN
        IF Y = 0 THEN
        IF Z = 0 THEN
            PRINT "All equal zero."
        ELSE
            PRINT "Only X and Y equal zero."
        END IF
        END IF
    ELSE
        PRINT "X is less than zero."
    END IF

1.3.2  SELECT CASE

    The SELECT CASE statement is a multiple-choice decision structure similar
    to the block IF...THEN...ELSE statement. Block IF...THEN...ELSE can be
    used anywhere SELECT CASE can be used.

    The major difference between the two is that SELECT CASE evaluates a
    single expression, then executes different statements or branches to
    different parts of the program based on the result. In contrast, a block
    IF...THEN...ELSE can evaluate completely different expressions.

    Examples

    The following examples illustrate the similarities and differences between
    the SELECT CASE and IF...THEN...ELSE statements. Here is an example of
    using block IF...THEN...ELSE for a multiple-choice decision:

    INPUT X
    IF X = 1 THEN
        PRINT "One"
    ELSEIF X = 2 THEN
        PRINT "Two"
    ELSEIF X = 3 THEN
        PRINT "Three"
    ELSE
        PRINT "Must be integer from 1-3."
    END IF

    The above decision is rewritten using SELECT CASE below:

    INPUT X
    SELECT CASE X
        CASE 1
        PRINT "One"
        CASE 2
        PRINT "Two"
        CASE 3
        PRINT "Three"
        CASE ELSE
        PRINT "Must be integer from 1-3."
    END SELECT

    The following decision can be made either with the SELECT CASE or the
    block IF...THEN...ELSE statement. The comparison is more efficient with
    the IF...THEN...ELSE statement because different expressions are being
    evaluated in the IF and ELSEIF clauses.

    INPUT X, Y
    IF X = 0 AND Y = 0 THEN
        PRINT "Both are zero."
    ELSEIF X = 0 THEN
        PRINT "Only X is zero."
    ELSEIF Y = 0 THEN
        PRINT "Only Y is zero."
    ELSE
        PRINT "Neither is zero."
    END IF

    1.3.2.1  Using the SELECT CASE Statement

    Table 1.3 shows the syntax of a SELECT CASE statement and an example.

    Table 1.3  SELECT CASE Syntax and Example
    Syntax                               Example
    ──────────────────────────────────────────────────────────────────────────
    SELECT CASE expression               INPUT TestValue
    CASE expressionlist1               SELECT CASE TestValue
        [[statementblock-1]]               CASE 1, 3, 5, 7, 9
    [[CASE expressionlist2                 PRINT "Odd"
        [[statementblock-2]]]]             CASE 2, 4, 6, 8
    .                                      PRINT "Even"
    .                                    CASE IS < 1
    .                                      PRINT "Too low"
    [[CASE ELSE                          CASE IS > 9
        [[statementblock-n]]]]               PRINT "Too high"
    END SELECT                             CASE ELSE
                                            PRINT "Not an integer"
                                        END SELECT

    ──────────────────────────────────────────────────────────────────────────

    The expressionlist arguments following a CASE clause can be one or more of
    the following, separated by commas:

    ■ A numeric expression or a range of numeric expressions

    ■ A string expression or a range of string expressions

    To specify a range of expressions, use the following syntax for the CASE
    statement:

    CASE expression TO expression CASE IS relational-operator expression

    The relational-operator is any of the operators shown in Table 1.1. For
    example, if you use CASE 1 TO 4, the statements associated with this case
    are executed when the expression in the SELECT CASE statement is greater
    than or equal to 1 and less than or equal to 4. If you use CASE IS < 5,
    the associated statements are executed only if expression is less than 5.

    If you are expressing a range with the TO keyword, be sure to put the
    lesser value first. For example, if you want to test for a value between
    -5 and -1, write the CASE statement as follows:

    CASE -5 TO -1

    However, the following statement is not a valid way to specify the range
    from -5 to -1, so the statements associated with this case are never
    executed:

    CASE -1 TO -5

    Similarly, a range of string constants should list the string that comes
    first alphabetically:

    CASE "aardvark" TO "bear"

    Multiple expressions or ranges of expressions can be listed for each CASE
    clause, as in the following lines:

    CASE 1 TO 4, 7 TO 9, WildCard1%, WildCard2%
    CASE IS = Test$, IS = "end of data"
    CASE IS < LowerBound, 5, 6, 12, IS > UpperBound
    CASE IS < "HAN", "MAO" TO "TAO"

    If the value of the SELECT CASE expression appears in the list following a
    CASE clause, only the statements associated with that CASE clause are
    executed. Control then jumps to the first executable statement following
    END SELECT, not the next block of statements inside the SELECT CASE
    structure, as shown by the output from the next example (where the user
    enters 1):

    INPUT x
    SELECT CASE x
        CASE 1
        PRINT "One, ";
        CASE 2
        PRINT "Two, ";
        CASE 3
        PRINT "Three, ";
    END SELECT
    PRINT "that's all"

    Output

    ? 1
    One, that's all

    If the same value or range of values appears in more than one CASE clause,
    only the statements associated with the first occurrence are executed, as
    shown by the next example's output (where the user has entered ABORIGINE):

    INPUT Test$
    SELECT CASE Test$
        CASE "A" TO "AZZZZZZZZZZZZZZZZZ"
        PRINT "An uppercase word beginning with capital A"
        CASE IS < "A"
        PRINT "Some sequence of nonalphabetic characters"
        CASE "ABORIGINE"
        ' This case is never executed since ABORIGINE
        ' falls within the range in the first CASE clause:
        PRINT "A special case"
    END SELECT

    Output

    ? ABORIGINE
    An uppercase word beginning with capital A

    If you use a CASE ELSE clause, it must be the last CASE clause listed in
    the SELECT CASE statement. The statements between a CASE ELSE clause and
    an END SELECT clause are executed only if the expression does not match
    any of the other CASE selections in the SELECT CASE statement. In fact, it
    is a good idea to have a CASE ELSE statement in your SELECT CASE block to
    handle unforeseen values for expression. However, if there is no CASE ELSE
    statement and no match is found in any CASE statement for expression, the
    program continues execution.

    Example

    The following program fragment demonstrates a common use of the SELECT
    CASE statement: it prints a menu on the screen, then branches to different
    subprograms based on the number typed by the user.

    DO                ' Start menu loop.

        CLS                  ' Clear screen.

        ' Print five menu choices on the screen:
        PRINT "MAIN MENU" : PRINT
        PRINT "1)  Add New Names"
        PRINT "2)  Delete Names"
        PRINT "3)  Change Information"
        PRINT "4)  List Names"
        PRINT
        PRINT "5)  EXIT"

        ' Print input prompt:
        PRINT : PRINT "Type your selection (1 to 5):"

        ' Wait for the user to press a key. INPUT$(1)
        ' reads one character input from the keyboard:
        Ch$ = INPUT$(1)

        ' Use SELECT CASE to process response:
        SELECT CASE Ch$

        CASE "1"
            CALL AddData
        CASE "2"
            CALL DeleteData
        CASE "3"
            CALL ChangeData
        CASE "4"
            CALL ListData
        CASE "5"
            EXIT DO         ' The only way to exit the menu loop
        CASE ELSE
            BEEP
        END SELECT

    LOOP                     ' End menu loop.

    END

    ' Subprograms AddData, DeleteData, ChangeData, and ListData:
    .
    .
    .

    1.3.2.2  SELECT CASE Versus ON...GOSUB

    You can use the more versatile SELECT CASE statement as a replacement for
    the old ON expression {GOSUB| GOTO} statement. The SELECT CASE statement
    has many advantages over the ON...GOSUB statement, as summarized below:

    ■ The expression in SELECT CASE expression can evaluate to either a string
    or numeric value. The expression given in the statement ON
    expression{GOSUB| GOTO} must evaluate to a number within the range 0 to
    255.

    ■ The SELECT CASE statement branches to a statement block immediately
    following the matching CASE clause. In contrast, ON expression GOSUB
    branches to a subroutine in another part of the program.

    ■ CASE clauses can be used to test an expression against a range of
    values. When the range is quite large, this is not easily done with ON
    expression{GOSUB| GOTO}, especially as shown in the following fragments.

    In the fragment below, the ON...GOSUB statement branches to one of the
    subroutines 50, 100, or 150, depending on the value input by the user:

    X% = -1
    WHILE X%
        INPUT "Enter choice (0 to quit): ", X%
        IF X% = 0 THEN END
        WHILE X% < 1 OR X% > 12
        PRINT "Must be value from 1 to 12"
        INPUT "Enter choice (0 to quit): ", X%
        WEND
        ON x% GOSUB 50,50,50,50,50,50,50,50,100,100,100,150
    WEND
    .
    .
    .

    Contrast the preceding example with the next one, which uses a SELECT CASE
    statement with ranges of values in each CASE clause:

    DO
        INPUT "Enter choice (0 to quit): ", X%
        SELECT CASE X%
        CASE 0
            END
        CASE 1 TO 8       ' Replaces "subroutine 50"
                            ' in preceding example
        CASE 9 TO 11      ' Replaces "subroutine 100"
                            ' in preceding example
        CASE 12           ' Replaces "subroutine 150"
                            ' in preceding example
        CASE ELSE         ' Input was out of range.
            PRINT "Must be value from 1 to 12"
        END SELECT
    LOOP


1.4  Looping Structures

    Looping structures repeat a block of statements (the loop), either for a
    specified number of times or until a certain expression (the loop
    condition) is true or false.

    Users of BASICA are familiar with two looping structures, FOR...NEXT and
    WHILE...WEND, which are discussed in Sections 1.4.1 and 1.4.2,
    respectively. QuickBASIC has extended the available loop structures with
    the DO...LOOP statement, discussed in Section 1.4.3.

1.4.1  FOR...NEXT Loops

    A FOR...NEXT loop repeats the statements enclosed in the loop a definite
    number of times, counting from a starting value to an ending value by
    increasing or decreasing a loop counter. As long as the loop counter has
    not reached the ending value, the loop continues to execute. Table 1.4
    shows the syntax of the FOR...NEXT statement and gives an example of its
    use.

    Table 1.4  FOR...NEXT Syntax and Example
    Syntax                               Example
    ──────────────────────────────────────────────────────────────────────────
    FOR counter = start TO end [[STEP    FOR I% = 1 to 10
    stepsize]]                             Array%(I%) = I%
    [[statementblock-1]]               NEXT
    [[EXIT FOR
    [[statementblock-2]]]]
    NEXT[[counter]]
    ──────────────────────────────────────────────────────────────────────────

    In a FOR...NEXT loop, the counter variable initially has the value of the
    expression start. After each repetition of the loop, the value of counter
    is adjusted. If you leave off the optional STEP keyword, the default value
    for this adjustment is one; that is, one is added to counter each time the
    loop executes. If you use STEP, then counter is adjusted by the amount
    stepsize. The stepsize argument can be any numeric value; if it is
    negative, the loop counts down from start to end. After the counter
    variable is increased or decreased, its value is compared with end. At
    this point, if either of the following is true, the loop is completed:

    ■ The loop is counting up (stepsize is positive) and counter is greater
    than end.

    ■ The loop is counting down (stepsize is negative) and counter is less
    than end.

    Figure 1.1 shows the logic of a FOR...NEXT loop when the value of
    stepsize is positive.


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 1.1 can be found on       │
    │   page 20 of the printed manual.   │
    │                                    │
    └────────────────────────────────────┘

    Figure 1.1  Logic of FOR...NEXT Loop with Positive STEP

    Figure 1.2 shows the logic of a FOR...NEXT loop when the value of
    stepsize is negative.


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 1.2 can be found on       │
    │   page 21 of the printed manual.   │
    │                                    │
    └────────────────────────────────────┘

    Figure 1.2  Logic of FOR...NEXT Loop with Negative STEP

    A FOR...NEXT statement always "tests at the top," so if one of the
    following conditions is true, the loop is never executed:

    ■ Step size is positive, and the initial value of start is greater than
    the value of end:

    ' Loop never executes, because I% starts out greater
    ' than 9:
    FOR I% = 10 TO 9
        .
        .
        .
    NEXT I%

    ■ Step size is negative, and the initial value of start is less than the
    value of end:

    ' Loop never executes, because I% starts out less than 9:
    FOR I% =  -10 TO -9 STEP -1
        .
        .
        .
    NEXT I%

    You don't have to use the counter argument in the NEXT clause; however, if
    you have several nested FOR...NEXT loops (one loop inside another),
    listing the counter arguments can be a helpful way to keep track of what
    loop you are in.

    Here are some general guidelines for nesting FOR...NEXT loops:

    ■ If you use a loop counter variable in a NEXT clause, the counter for a
    nested loop must appear before the counter for any enclosing loop. In
    other words, the following is a legal nesting:

    FOR I = 1 TO 10
        FOR J = -5 TO 0
        .
        .
        .
        NEXT J
    NEXT I

    However, the following is not a legal nesting:

    FOR I = 1 TO 10
        FOR J = -5 TO 0
        .
        .
        .
        NEXT I
    NEXT J

    ■ If you use a separate NEXT clause to end each loop, then the number of
    NEXT clauses must always be the same as the number of FOR clauses.

    ■ If you use a single NEXT clause to terminate several levels of
    FOR...NEXT loops, then the loop-counter variables must appear after the
    NEXT clause in "inside-out" order:

    NEXT innermost-loopcounter,... , outermost-loopcounter

    In this case, the number of loop-counter variables in the NEXT clause
    must be the same as the number of FOR clauses.

    Examples

    The three program fragments below illustrate different ways of nesting
    FOR... NEXT loops to produce the identical output that follows. The first
    example shows nested FOR...NEXT loops with loop counters and separate NEXT
    clauses for each loop:

    FOR I = 1 TO 2
        FOR J = 4 TO 5
        FOR K = 7 TO 8
            PRINT I, J, K
        NEXT K
        NEXT J
    NEXT I

    The following example also uses loop counters but only one NEXT clause for
    all three loops:

    FOR I = 1 TO 2
        FOR J = 4 TO 5
        FOR K = 7 TO 8
            PRINT I, J, K
    NEXT K, J, I

    The final example shows nested FOR...NEXT loops without loop counters:

    FOR I = 1 TO 2
        FOR J = 4 TO 5
        FOR K = 7 TO 8
            PRINT I, J, K
        NEXT
        NEXT
    NEXT

    Output

    1             4             7
    1             4             8
    1             5             7
    1             5             8
    2             4             7
    2             4             8
    2             5             7
    2             5             8

    1.4.1.1  Exiting a FOR...NEXT Loop with EXIT FOR

    Sometimes you may want to exit a FOR...NEXT loop before the counter
    variable reaches the ending value of the loop. You can do this with the
    EXIT FOR statement. A single FOR...NEXT loop can have any number of EXIT
    FOR statements, and the EXIT FOR statements can appear anywhere within the
    loop. The following fragment shows one use for an EXIT FOR statement:

    ' Print the square roots of the numbers from 1 to 30,000.
    ' If the user presses any key while this loop is executing,
    ' control exits from the loop:
    FOR I% = 1 TO 30000
        PRINT SQR(I%)
        IF INKEY$ <> "" THEN EXIT FOR
    NEXT
    .
    .
    .

    EXIT FOR exits only the smallest enclosing FOR...NEXT loop in which it
    appears. For example, if the user pressed a key while the following nested
    loops were executing, the program would simply exit the innermost loop. If
    the outermost loop were still active (that is if the value of I% <= 100),
    control would pass right back to the innermost loop:

    FOR I% = 1 TO 100
        FOR J% = 1 TO 100
        PRINT I% / J%
        IF INKEY$ <> "" THEN EXIT FOR
        NEXT J%
    NEXT I%

    1.4.1.2  Pausing Program Execution with FOR...NEXT

    Many BASICA programs use an empty FOR...NEXT loop such as the following to
    insert a pause in a program:

    .
    .
    .
    ' There are no statements in the body of this loop;
    ' all it does is count from 1 to 10,000
    ' using integers (whole numbers).
    FOR I% = 1 TO 10000: NEXT
    .
    .
    .

    For very short pauses or pauses that do not have to be some exact
    interval, using FOR...NEXT is fine. The problem with using an empty
    FOR...NEXT loop in this way is that different computers, different
    versions of BASIC, or different compile-time options can all affect how
    quickly the arithmetic in a FOR... NEXT loop is performed. So the length
    of a pause can vary widely. QuickBASIC's SLEEP statement now provides a
    better alternative. (See Chapter 8, "Statement and Function Summary," for
    the syntax of SLEEP.)

1.4.2  WHILE...WEND Loops

    The FOR...NEXT statement is useful when you know ahead of time exactly how
    many times a loop should be executed. When you cannot predict the precise
    number of times a loop should be executed, but do know the condition that
    will end the loop, the WHILE...WEND statement is useful. Instead of
    counting to determine if it should keep executing a loop, WHILE...WEND
    repeats the loop as long as the loop condition is true.

    Table 1.5 shows the syntax of the WHILE...WEND statement and an example.

    Table 1.5  WHILE...WEND Syntax and Example
    Syntax                               Example
    ──────────────────────────────────────────────────────────────────────────
    WHILE condition                      INPUT R$
    [[statementblock]]                 WHILE R$ <> "Y" AND R$ <> "N"
    WEND                                   PRINT "Answer must be Y or N."
                                            INPUT R$
                                        WEND
    ──────────────────────────────────────────────────────────────────────────

    Example

    The following example assigns an initial value of ten to the variable X,
    then successively halves that value and keeps halving it until the value
    of X is less than .00001:

    X = 10

    WHILE X > .00001
        PRINT X
        X = X/2
    WEND

    Figure 1.3 illustrates the logic of a WHILE...WEND loop.


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 1.3 can be found on       │
    │   page 26 of the printed manual.   │
    │                                    │
    └────────────────────────────────────┘

    Figure 1.3  Logic of WHILE...WEND Loop

1.4.3  DO...LOOP Loops

    Like the WHILE...WEND statement, the DO...LOOP statement executes a block
    of statements an indeterminate number of times; that is, exiting from the
    loop depends on the truth or falsehood of the loop condition. Unlike
    WHILE...WEND, DO...LOOP allows you to test for either a true or false
    condition. You can also put the test at either the beginning or the end of
    the loop.

    Table 1.6 shows the syntax of a loop that tests at the loop's beginning.

    Table 1.6  DO...LOOP Syntax and Example: Test at the Beginning
    Syntax                               Example
    ──────────────────────────────────────────────────────────────────────────
    DO[[{WHILE| UNTIL} condition]]       DO UNTIL INKEY$ <> ""
    [[statementblock-1]]                 ' An empty loop that
    [[EXIT DO                            ' pauses until a key
    [[statementblock-2]]]]               ' is pressed
    LOOP                                 LOOP
    ──────────────────────────────────────────────────────────────────────────

    Figures 1.4 and 1.5 illustrate the two kinds of DO...LOOP statements
    that test at the beginning of the loop.


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 1.4 can be found on       │
    │   page 27 of the printed manual.   │
    │                                    │
    └────────────────────────────────────┘

    Figure 1.4  Logic of DO WHILE...LOOP


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 1.5 can be found on       │
    │   page 28 of the printed manual.   │
    │                                    │
    └────────────────────────────────────┘

    Figure 1.5  Logic of DO UNTIL...LOOP

    Table 1.7 shows the syntax of a loop that tests for true or false at the
    end of the loop.

    Table 1.7  DO...LOOP Syntax and Example: Test at the End
    Syntax                               Example
    ──────────────────────────────────────────────────────────────────────────
    DO                                   DO
    [[statementblock-1]]                 INPUT "Change: ", Ch
    [[EXIT DO                            Total = Total + Ch
    [[statementblock-2]]]]             LOOP WHILE Ch <> 0
    LOOP[[{WHILE| UNTIL} condition]]
    ──────────────────────────────────────────────────────────────────────────

    Figures 1.6 and 1.7 illustrate the two kinds of DO...LOOP statements
    that test at the end of the loop.


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 1.6 can be found on       │
    │   page 29 of the printed manual.   │
    │                                    │
    └────────────────────────────────────┘

    Figure 1.6  Logic of DO...LOOP WHILE


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 1.7 can be found on       │
    │   page 30 of the printed manual.   │
    │                                    │
    └────────────────────────────────────┘

    Figure 1.7  Logic of DO...LOOP UNTIL

    1.4.3.1  Loop Tests: One Way to Exit DO...LOOP

    You can use a loop test at the end of a DO...LOOP statement to create a
    loop in which the statements always execute at least once. With the
    WHILE...WEND statement, you sometimes have to resort to the trick of
    presetting the loop variable to some value in order to force the first
    pass through the loop. With DO...LOOP, such tricks are no longer
    necessary. The examples below illustrate both approaches:

    ' WHILE...WEND loop tests at the top, so assigning "Y"
    ' to Response$ is necessary to force execution of the
    ' loop at least once:
    Response$ = "Y"
    WHILE UCASE$(Response$) = "Y"
        .
        .
        .
        INPUT "Do again"; Response$
    WEND

    ' The same loop using DO...LOOP to test after the
    ' body of the loop:
    DO
        .
        .
        .
        INPUT "Do again"; Response$
    LOOP WHILE UCASE$(Response$) = "Y"

    You can also rewrite a condition expressed with WHILE using UNTIL instead,
    as in the following:

    ' =======================================================
    '                  Using DO WHILE NOT...LOOP
    ' =======================================================

    ' While the end of file 1 has not been reached, read
    ' a line from the file and print it on the screen:
    DO WHILE NOT EOF(1)
        LINE INPUT #1, LineBuffer$
        PRINT LineBuffer$
    LOOP

    ' =======================================================
    '                    Using DO UNTIL...LOOP
    ' =======================================================

    ' Until the end of file 1 has been reached, read
    ' a line from the file and print it on the screen:
    DO UNTIL EOF(1)
        LINE INPUT #1, LineBuffer$
        PRINT LineBuffer$
    LOOP

    1.4.3.2  EXIT DO: An Alternative Way to Exit DO...LOOP

    Inside a DO...LOOP statement, other statements are executed that
    eventually change the loop-test condition from true to false or false to
    true, ending the loop. In the DO...LOOP examples presented so far, the
    test has occurred either at the beginning or the end of the loop. However,
    by using the EXIT DO statement to exit from the loop, you can move the
    test elsewhere inside the loop. A single DO...LOOP can contain any number
    of EXIT DO statements, and the EXIT DO statements can appear anywhere
    within the loop.

    Example

    The following example opens a file and reads it, one line at a time, until
    the end of the file is reached or until it finds the pattern input by the
    user. If it finds the pattern before getting to the end of the file, an
    EXIT DO statement exits the loop.

    INPUT "File to search: ", File$
    IF File$ = "" THEN END

    INPUT "Pattern to search for: ", Pattern$
    OPEN File$ FOR INPUT AS #1

    DO UNTIL EOF(1)    ' EOF(1) returns a true value if the
                        ' end of the file has been reached.
        LINE INPUT #1, TempLine$
        IF INSTR(TempLine$, Pattern$) > 0 THEN

        ' Print the first line containing the pattern and
        ' exit the loop:
        PRINT TempLine$
        EXIT DO
        END IF
    LOOP


1.5  Sample Applications

    The sample applications for this chapter are a checkbook-balancing program
    and a program that ensures that every line in a text file ends with a
    carriage-return and line-feed sequence.

1.5.1  Checkbook-Balancing Program (CHECK.BAS)

    This program prompts the user for the starting checking-account balance
    and all transactions──withdrawals or deposits──that have occurred. It then
    prints a sorted list of the transactions and the final balance in the
    account.

    Statements and Functions Used

    The program demonstrates the following statements discussed in this
    chapter:

    ■ DO...LOOP WHILE

    ■ FOR...NEXT

    ■ EXIT FOR

    ■ Block IF...THEN...ELSE

    Program Listing

    DIM Amount(1 TO 100)
    CONST FALSE = 0, TRUE = NOT FALSE

    CLS
    ' Get account's starting balance:
    INPUT "Type starting balance, then press <ENTER>: ", Balance

    ' Get transactions. Continue accepting input
    ' until the input is zero for a transaction,
    ' or until 100 transactions have been entered:
    FOR TransacNum% = 1 TO 100
        PRINT TransacNum%;
        PRINT ") Enter transaction amount (0 to end): ";
        INPUT "", Amount(TransacNum%)
        IF Amount(TransacNum%) = 0 THEN
        TransacNum% = TransacNum% - 1
        EXIT FOR
        END IF
    NEXT

    ' Sort transactions in ascending order,
    ' using a "bubble sort":
    Limit% = TransacNum%
    DO
        Swaps% = FALSE
        FOR I% = 1 TO (Limit% - 1)

        ' If two adjacent elements are out of order,
        ' switch those elements:
        IF Amount(I%) < Amount(I% + 1) THEN
            SWAP Amount(I%), Amount(I% + 1)
            Swaps% = I%
        END IF
        NEXT I%

        ' Sort on next pass only to where last switch was made:
        Limit% = Swaps%

    ' Sort until no elements are exchanged:
    LOOP WHILE Swaps%

    ' Print the sorted transaction array. If a transaction
    ' is greater than zero, print it as a "CREDIT"; if a
    ' transaction is less than zero, print it as a "DEBIT":
    FOR I% = 1 TO TransacNum%
        IF Amount(I%) > 0 THEN
        PRINT USING "CREDIT: $$#####.##"; Amount(I%)
        ELSEIF Amount(I%) < 0 THEN
        PRINT USING "DEBIT: $$#####.##"; Amount(I%)
        END IF

        ' Update balance:
        Balance = Balance + Amount(I%)
    NEXT I%

    ' Print the final balance:
    PRINT
    PRINT "--------------------------"
    PRINT USING "Final Balance: $$######.##"; Balance
    END

1.5.2  Carriage-Return and Line-Feed Filter (CRLF.BAS)

    Some text files are saved in a format that uses only a carriage return
    (return to the beginning of the line) or a line feed (advance to the next
    line) to signify the end of a line. Many text editors expand this single
    carriage return (CR) or line feed (LF) to a carriage-return and line-feed
    (CR-LF) sequence whenever you load the file for editing. However, if you
    use a text editor that does not expand a single CR or LF to CR-LF, you may
    have to modify the file so it has the correct sequence at the end of each
    line.

    The following program is a filter that opens a file, expands a single CR
    or LF to a CR-LF combination, then writes the adjusted lines to a new
    file. The original contents of the file are saved in a file with a .BAK
    extension.

    Statements and Functions Used

    This program demonstrates the following statements discussed in this
    chapter:

    ■ DO...LOOP WHILE

    ■ DO UNTIL...LOOP

    ■ Block IF...THEN...ELSE

    ■ SELECT CASE...END SELECT

    To make this program more useful, it contains the following constructions
    not discussed in this chapter:

    ■ A FUNCTION procedure named Backup$ that creates the file with the .BAK
    extension.

    See Chapter 2, "SUB and FUNCTION Procedures," for more information on
    defining and using procedures.

    ■ An error-handling routine named ErrorHandler to deal with errors that
    could occur when the user enters a file name. For instance, if the user
    enters the name of a nonexistent file, this routine prompts for a new
    name. Without this routine, such an error would end the program.

    See Chapter 6, "Error and Event Trapping," for more information on
    trapping errors.

    Program Listing

    DEFINT A-Z             ' Default variable type is integer.

    ' The Backup$ FUNCTION makes a backup file with
    ' the same base as FileName$ plus a .BAK extension:
    DECLARE FUNCTION Backup$ (FileName$)

    ' Initialize symbolic constants and variables:
    CONST FALSE = 0, TRUE = NOT FALSE

    CarReturn$ = CHR$(13)
    LineFeed$ = CHR$(10)

    DO
        CLS

        ' Input the name of the file to change:
        INPUT "Which file do you want to convert"; OutFile$

        InFile$ = Backup$(OutFile$)  ' Get backup file's name.

        ON ERROR GOTO ErrorHandler   ' Turn on error trapping.

        NAME OutFile$ AS InFile$     ' Rename input file as
                                    ' backup file.

        ON ERROR GOTO 0              ' Turn off error trapping.

        ' Open backup file for input and old file for output:
        OPEN InFile$ FOR INPUT AS #1
        OPEN OutFile$ FOR OUTPUT AS #2

        ' The PrevCarReturn variable is a flag set to TRUE
        ' whenever the program reads a carriage-return character:
        PrevCarReturn = FALSE

    ' Read from input file until reaching end of file:
        DO UNTIL EOF(1)

        ' This is not end of file, so read a character:
        FileChar$ = INPUT$(1, #1)

        SELECT CASE FileChar$

        CASE CarReturn$        ' The character is a CR.

            ' If the previous character was also a
            ' CR, put a LF before the character:
            IF PrevCarReturn THEN
                FileChar$ = LineFeed$ + FileChar$
            END IF

            ' In any case, set the PrevCarReturn
            ' variable to TRUE:
            PrevCarReturn = TRUE

        CASE LineFeed$         ' The character is a LF.

            ' If the previous character was not a
            ' CR, put a CR before the character:
            IF NOT PrevCarReturn THEN
                FileChar$ = CarReturn$ + FileChar$
            END IF

            ' Set the PrevCarReturn variable to FALSE:
            PrevCarReturn = FALSE

        CASE ELSE              ' Neither a CR nor a LF.

            ' If the previous character was a CR,
            ' set the PrevCarReturn variable to FALSE
            ' and put a LF before the current character:
            IF PrevCarReturn THEN
                    PrevCarReturn = FALSE
                    FileChar$ = LineFeed$ + FileChar$
            END IF

        END SELECT

        ' Write the character(s) to the new file:
        PRINT #2, FileChar$;
        LOOP

        ' Write a LF if the last character in the file was a CR:
        IF PrevCarReturn THEN PRINT #2, LineFeed$;

    CLOSE                        ' Close both files.
        PRINT "Another file (Y/N)?"  ' Prompt to continue.

        ' Change the input to uppercase (capital letter):
        More$ = UCASE$(INPUT$(1))

    ' Continue the program if the user entered a "y" or a "Y":
    LOOP WHILE More$ = "Y"
    END

    ErrorHandler:           ' Error-handling routine
        CONST NOFILE = 53, FILEEXISTS = 58

        ' The ERR function returns the error code for last error:
        SELECT CASE ERR
        CASE NOFILE       ' Program couldn't find file
                            ' with input name.

            PRINT "No such file in current directory."
            INPUT "Enter new name: ", OutFile$
            InFile$ = Backup$(OutFile$)
            RESUME
        CASE FILEEXISTS   ' There is already a file named
                            ' <filename>.BAK in this directory:
                            ' remove it, then continue.
            KILL InFile$
            RESUME
        CASE ELSE         ' An unanticipated error occurred:
                            ' stop the program.
            ON ERROR GOTO 0
        END SELECT

    ' ======================== BACKUP$ =========================
    '   This procedure returns a file name that consists of the
    '   base name of the input file (everything before the ".")
    '   plus the extension ".BAK"
    ' ==========================================================

    FUNCTION Backup$ (FileName$) STATIC

        ' Look for a period:
        Extension = INSTR(FileName$, ".")

        ' If there is a period, add .BAK to the base:
        IF Extension > 0 THEN
        Backup$ = LEFT$(FileName$, Extension - 1) + ".BAK"

        ' Otherwise, add .BAK to the whole name:
        ELSE
        Backup$ = FileName$ + ".BAK"
        END IF
    END FUNCTION



────────────────────────────────────────────────────────────────────────────
Chapter 2  SUB and FUNCTION Procedures

    This chapter explains how to simplify your programming by breaking
    programs into smaller logical components. These components──known as
    "procedures"──can then become building blocks that let you enhance and
    extend the BASIC language itself.

    When you are finished with this chapter, you will know how to perform the
    following tasks with procedures:

    ■ Define and call BASIC procedures

    ■ Use local and global variables in procedures

    ■ Use procedures instead of GOSUB subroutines and DEF FN functions

    ■ Pass arguments to procedures and return values from procedures

    ■ Write recursive procedures (procedures that can call themselves)

    Although you can create a BASIC program with any text editor, the
    QuickBASIC editor makes it especially easy to write programs that contain
    procedures. Also, in most cases QuickBASIC automatically generates a
    DECLARE statement when you save your program. (Using a DECLARE statement
    ensures that only the correct number and type of arguments are passed to a
    procedure and allows your program to call procedures defined in separate
    modules.)


2.1  Procedures: Building Blocks for Programming

    As used in this chapter, the term "procedure" covers both SUB...END SUB
    and FUNCTION...END FUNCTION constructions. Procedures are useful for
    condensing repeated tasks. For example, suppose you are writing a program
    that you eventually intend to compile as a stand-alone application and you
    want the user of this application to be able to pass several arguments to
    the application from the command line. It then makes sense to turn this
    task──breaking the string returned by the COMMAND$ function into two or
    more arguments──into a separate procedure. Once you have this procedure up
    and running, you can use it in other programs. In essence, you are
    extending BASIC to fit your individual needs when you use procedures.

    These are the two major benefits of programming with procedures:

    1. Procedures allow you to break your programs into discrete logical
        units, each of which can be more easily debugged than can an entire
        program without procedures.

    2. Procedures used in one program can be used as building blocks in other
        programs, usually with little or no modification.

    You can also put procedures in your own Quick library, which is a special
    file that you can load into memory when you start QuickBASIC. Once the
    contents of a Quick library are in memory with QuickBASIC, any program
    that you write has access to the procedures in the library. This makes it
    easier for all of your programs both to share and save code. (See Appendix
    H, "Creating and Using Quick Libraries," for more information on how to
    build Quick libraries.)


2.2  Comparing Procedures with Subroutines and Functions

    If you are familiar with earlier versions of BASIC, you might think of a
    SUB...END SUB procedure as being roughly similar to a GOSUB...RETURN
    subroutine. You will also notice some similarities between a FUNCTION...
    END FUNCTION procedure and a DEF FN...END DEF function. However,
    procedures have many advantages over these older constructions, as shown
    in Sections 2.2.1 and 2.2.2 below.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    To avoid confusion between a SUB procedure and the target of a GOSUB
    statement, SUBprocedures are referred to in this manual as
    "subprograms," while statement blocks accessed by
    GOSUB...RETURN statements are referred to as "subroutines."
    ──────────────────────────────────────────────────────────────────────────

2.2.1  Comparing SUB with GOSUB

    Although use of the GOSUB subroutine does help break programs into
    manageable units, SUB procedures have a number of advantages over
    subroutines, as discussed below.

    2.2.1.1  Local and Global Variables

    In SUB procedures, all variables are local by default; that is, they exist
    only within the scope of the SUB procedure's definition. To illustrate,
    the variable named I in the Test subprogram below is local to Test, and
    has no connection with the variable named I in the module-level code:

    I = 1
    CALL Test
    PRINT I  ' I still equals 1.
    END

    SUB Test STATIC
        I = 50
    END SUB

    A GOSUB has a major drawback as a building block in programs: it contains
    only "global variables." With global variables, if you have a variable
    named I inside your subroutine, and another variable named I outside the
    subroutine but in the same module, they are one and the same. Any changes
    to the value of I in the subroutine affect I everywhere it appears in the
    module. As a result, if you try to patch a subroutine from one module into
    another module, you may have to rename subroutine variables to avoid
    conflict with variable names in the new module.

    2.2.1.2  Use in Multiple-Module Programs

    A SUB can be defined in one module and called from another. This
    significantly reduces the amount of code required for a program and
    increases the ease with which code can be shared among a number of
    programs.

    A GOSUB subroutine, however, must be defined and used in the same module.

    2.2.1.3  Operating on Different Sets of Variables

    A SUB procedure can be called any number of times within a program, with a
    different set of variables being passed to it each time. This is done by
    calling the SUB with an argument list. (See Section 2.5, "Passing
    Arguments to Procedures," for more information on how to do this.) In the
    next example, the subprogram Compare is called twice, with different pairs
    of variables passed to it each time:

    X = 4: Y = 5

    CALL Compare (X, Y)

    Z = 7: W = 2
    CALL Compare (Z, W)
    END

    SUB Compare (A, B)
        IF A < B THEN SWAP A, B
    END SUB

    Calling a GOSUB subroutine more than once in the same program and having
    it operate on a different set of variables each time is difficult. The
    process involves copying values to and from global variables, as shown in
    the next example:

    X = 4: Y = 5
    A = X: B = Y
    GOSUB Compare
    X = A: Y = B

    Z = 7 : W = 2
    A = Z : B = W
    GOSUB Compare
    Z = A : W = B
    END

    Compare:
        IF A < B THEN SWAP A, B
    RETURN

2.2.2  Comparing FUNCTION with DEF FN

    While the multiline DEF FN function definition answers the need for
    functions more complex than can be squeezed on a single line, FUNCTION
    procedures give you this capability plus the additional advantages
    discussed below.

    2.2.2.1  Local and Global Variables

    By default, all variables within a FUNCTION procedure are local to it,
    although you do have the option of using global variables. (See Section
    2.6, "Sharing Variables with SHARED," for more information on procedures
    and global variables.)

    In a DEF FN function, variables used within the function's body are global
    to the current module by default (this is also true for GOSUB
    subroutines). However, you can make a variable in a DEF FN function local
    by putting it in a STATIC statement.

    2.2.2.2  Changing Variables Passed to the Procedure

    Variables are passed to FUNCTION procedures by reference or by value. When
    you pass a variable by reference, you can change the variable by changing
    its corresponding parameter in the FUNCTION. For example, after the call
    to GetRemainder in the next program, the value of X is 2, since the value
    of Mis 2 at the end of the FUNCTION:

    X = 89
    Y = 40
    PRINT GetRemainder(X, Y)
    PRINT X, Y                ' X is now 2.
    END

    FUNCTION GetRemainder (M, N)
        GetRemainder = M MOD N
        M = M \ N
    END FUNCTION

    Variables are passed to a DEF FN function only by value, so in the next
    example, FNRemainder changes M without affecting X :

    DEF FNRemainder (M, N)
        FNRemainder = M MOD N
        M = M \ N
    END DEF

    X = 89
    Y = 40
    PRINT FNRemainder(X, Y)

    PRINT X,Y   ' X is still
                ' 89.

    See Sections 2.5.5 and 2.5.6 for more information on the distinction
    between passing by reference and by value.

    2.2.2.3  Calling the Procedure within Its Definition

    A FUNCTION procedure can be "recursive"; in other words, it can call
    itself within its own definition. (See Section 2.9 for more information
    on how procedures can be recursive.) A DEF FN function cannot be
    recursive.

    2.2.2.4  Use in Multiple-Module Programs

    You can define a FUNCTION procedure in one module and use it in another
    module. You need to put a DECLARE statement in the module in which the
    FUNCTION is used; otherwise, your program thinks the FUNCTION name refers
    to a variable. (See Section 2.5.4, "Checking Arguments with the DECLARE
    Statement," for more information on using DECLARE this way.)

    A DEF FN function can only be used in the module in which it is defined.
    Unlike SUB or FUNCTION procedures, which can be called before they appear
    in the program, a DEF FN function must always be defined before it is used
    in a module.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    The name of a FUNCTION procedure can be any valid BASIC variable name,
    except one beginning with the letters FN. The name of a DEF FN function
    must always be preceded by FN.
    ──────────────────────────────────────────────────────────────────────────


2.3  Defining Procedures

    BASIC procedure definitions have the following general syntax:

    {SUB| FUNCTION} procedurename[[(parameterlist)]] [[STATIC]]
    [[statementblock-1]]
    [[EXIT{SUB| FUNCTION}
        [[statementblock-2]]]]
    END{SUB| FUNCTION}

    The following list describes the parts of a procedure definition:

╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Part                     Description
    ──────────────────────────────────────────────────────────────────────────
    {SUB | FUNCTION}         Marks the beginning of a SUB or FUNCTION
                            procedure, respectively.

    procedurename            Any valid variable name up to 40 characters long.
                            The same name cannot be used for both a SUB and a
                            FUNCTION.
    Part                     Description
    ──────────────────────────────────────────────────────────────────────────
                            FUNCTION.

    parameterlist            A list of variables, separated by commas, that
                            shows the number and type of arguments to be
                            passed to the procedure. (Section 2.5.1 explains
                            the difference between parameters and arguments.)

    STATIC                   If you use the STATIC attribute, local variables
                            are STATIC; that is, they retain their values
                            between calls to the procedure.
                            If you omit the STATIC attribute, local variables
                            are "automatic" by default; that is, they are
                            initialized to zeros or null strings at the start
                            of each procedure call.
                            See Section 2.7, "Automatic and STATIC
                            Variables," for more information.

    END{SUB| FUNCTION}       Ends a SUB or FUNCTION definition. To run
                            correctly, every procedure must have exactly one
    Part                     Description
    ──────────────────────────────────────────────────────────────────────────
                            correctly, every procedure must have exactly one
                            END{SUB| FUNCTION} statement.
                            When your program encounters an END SUB or END
                            FUNCTION, it exits the procedure and returns to
                            the statement immediately following the one that
                            called the procedure. You can also use one or
                            more optional EXIT{SUB| FUNCTION} statements
                            within the body of a procedure definition to exit
                            from the procedure.
    ──────────────────────────────────────────────────────────────────────────


    All valid BASIC expressions and statements are allowed within a procedure
    definition except the following:

    ■ DEF FN...END DEF, FUNCTION...END FUNCTION, or SUB...END SUB

    It is not possible to nest procedure definitions or to define a DEF FN
    function inside a procedure. However, a procedure can call another
    procedure or a DEF FN function.

    ■ COMMON

    ■ DECLARE

    ■ DIM SHARED

    ■ OPTION BASE

    ■ TYPE...END TYPE

    Example

    The following example is a FUNCTION procedure named IntegerPower :

    FUNCTION IntegerPower& (X&, Y&) STATIC
        PowerVal& = 1
        FOR I& = 1 TO Y&
        PowerVal& = PowerVal& * X&
        NEXT I&
        IntegerPower& = PowerVal&
    END FUNCTION


2.4  Calling Procedures

    Calling a FUNCTION procedure is different from calling a SUB procedure, as
    shown in the next two sections.

2.4.1  Calling a FUNCTION Procedure

    You call a FUNCTION procedure the same way you use an intrinsic BASIC
    function such as ABS, that is, by using its name in an expression, as
    shown here:

    ' Any of the following statements
    ' would call a FUNCTION named "ToDec" :
    PRINT 10 * ToDec
    X = ToDec
    IF ToDec = 10 THEN PRINT "Out of range."

    A FUNCTION can return values by changing variables passed to it as
    arguments. (See Section 2.5.5, "Passing Arguments by Reference," for an
    explanation of how this is done.) Additionally, a FUNCTION returns a
    single value in its name, so the name of a FUNCTION must agree with the
    type it returns. For example, if a FUNCTION returns a string value, either
    its name must have the dollar sign ($) type-declaration character appended
    to it or it must be declared as having the string type in a preceding
    DEFSTR statement.

    Example

    The following program shows a FUNCTION that returns a string value. Note
    that the type-declaration suffix for strings ($) is part of the procedure
    name.

    Banner$ = GetInput$       ' Call the FUNCTION and assign the
                            ' return value to a string variable.
    PRINT Banner$             ' Print the string.
    END

    ' ======================= GETINPUT$ ========================
    '    The $ type-declaration character at the end of this
    '    FUNCTION name means that it returns a string value.
    ' ==========================================================

    FUNCTION GetInput$ STATIC

        ' Return a string of 10 characters read from the
        ' keyboard, echoing each character as it is typed:
        FOR I% = 1 TO 10
        Char$ = INPUT$(1)     ' Get the character.
        PRINT Char$;          ' Echo the character on the
                                ' screen.
        Temp$ = Temp$ + Char$ ' Add the character to the
                                ' string.
        NEXT
        PRINT
        GetInput$ = Temp$    ' Assign the string to the FUNCTION.
    END FUNCTION

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    The program example above is written for use in the QB environment only.
    It cannot be compiled using the BC command from DOS.
    ──────────────────────────────────────────────────────────────────────────

2.4.2  Calling a SUB Procedure

    A SUB procedure differs from a FUNCTION procedure in that a SUB cannot be
    called by using its name within an expression. A call to a SUB is a
    stand-alone statement, like BASIC's CIRCLE statement. Also, a SUB does not
    return a value in its name as does a FUNCTION. However, like a FUNCTION, a
    SUB can modify the values of any variables passed to it. (Section 2.5.5,
    "Passing Arguments by Reference," explains how this is done.)

    There are two ways to call a SUB procedure:

    1. Put its name in a CALL statement:

        CALL PrintMessage

    2. Use its name as a statement by itself:

        PrintMessage

    If you omit the CALL keyword, don't put parentheses around arguments
    passed to the SUB:

    ' Call the ProcessInput subprogram with CALL and pass the
    ' three arguments First$, Second$, and NumArgs% to it:
    CALL ProcessInput  (First$, Second$, NumArgs%)



    ' Call the ProcessInput subprogram without CALL and pass
    ' it the same arguments (note no parentheses around the
    ' argument list):
    ProcessInput  First$, Second$, NumArgs%


    See Section 2.5 for more information on passing arguments to procedures.

    If your program calls SUB procedures without using CALL, and if you are
    not using QuickBASIC to write the program, you must put the name of the
    SUB in a DECLARE statement before it is called:

    DECLARE SUB CheckForKey
    .
    .
    .
    CheckForKey

    You need to be concerned about this only if you are developing programs
    outside QuickBASIC, since QuickBASIC automatically inserts DECLARE
    statements wherever they are needed when it saves a program.


2.5  Passing Arguments to Procedures

    Sections 2.5.1-2.5.4 explain how to tell the difference between
    parameters and arguments, how to pass arguments to procedures, and how to
    check arguments to make sure they are of the correct type and quantity.

2.5.1  Parameters and Arguments

    The first step in learning about passing arguments to procedures is
    understanding the difference between the terms "parameter" and "argument":

    Parameter                Argument
    ──────────────────────────────────────────────────────────────────────────
    A variable name that     A constant, variable, or expression passed to a
    appears in a SUB,        SUB or FUNCTION when the SUB or FUNCTION is
    FUNCTION, or DECLARE     called
    statement
    ──────────────────────────────────────────────────────────────────────────

    In a procedure definition, parameters are placeholders for arguments. As
    shown in Figure 2.1, when a procedure is called, arguments are plugged
    into the variables in the parameter list, with the first parameter
    receiving the first argument, the second parameter receiving the second
    argument, and so on.


    ┌───────────────────────────────────────────────────────────────────┐
    │                                                                   │
    │                                            Arguments              │
    │                                       ┌────────┴─────────┐        │
    │ Procedure call───────── CALL TestSub   (A%,  B!,  "text")         │
    │                         ∙               │     │      │            │
    │                         ∙               │     │      │            │
    │                         ∙               ↓     ↓      ↓            │
    │ Procedure definition ── SUB TestSub    (P1%,  P2!,   P3$)  STATIC │
    │                                       └────────┬─────────┘        │
    │                                            Parameters             │
    │                                                                   │
    └───────────────────────────────────────────────────────────────────┘

    Figure 2.1  Parameters and Arguments

    Figure 2.1 also demonstrates another important rule: although the names
    of variables in an argument list and a parameter list do not have to be
    the same, the number of parameters and the number of arguments do.
    Furthermore, the type (string, integer numeric, single-precision numeric,
    and so on) should be the same for corresponding arguments and parameters.
    (See Section 2.5.4, "Checking Arguments with the DECLARE Statement," for
    more information on how to ensure that arguments and parameters agree in
    number and type.)

    A parameter list consists of any of the following, all separated by
    commas:

    ■ Valid variable names, except for fixed-length strings

    For example, x$ and x AS STRING are both legal in a parameter list,
    since they refer to variable-length strings. However, x AS STRING * 10
    refers to a fixed-length string 10 characters long and cannot appear in
    a parameter list. (Fixed-length strings are perfectly all right as
    arguments passed to procedures. See Chapter 4, "String Processing," for
    more information on fixed-length and variable-length strings.)

    ■ Array names followed by left and right parentheses

    An argument list consists of any of the following, all separated by
    commas:

    ■ Constants

    ■ Expressions

    ■ Valid variable names

    ■ Array names followed by left and right parentheses

    Examples

    The following example shows the first line of a subprogram definition with
    a parameter list:

    SUB TestSub (A%, Array(), RecVar AS RecType, Cs$)

    The first parameter, A%, is an integer; the second parameter, Array(), is
    a single-precision array, since untyped numeric variables are single
    precision by default; the third parameter, RecVar, is a record of type
    RecType; and the fourth parameter, Cs$, is a string.

    The CALL TestSub line in the next example calls the TestSub subprogram and
    passes it four arguments of the appropriate type:

    TYPE RecType
        Rank AS STRING * 12
        SerialNum AS LONG
    END TYPE

    DIM RecVar AS RecType

    CALL TestSub (X%, A(), RecVar, "Daphne")

2.5.2  Passing Constants and Expressions

    Constants──whether string or numeric──can appear in the list of arguments
    passed to a procedure. Naturally, a string constant must be passed to a
    string parameter and a numeric constant to a numeric parameter, as shown
    in the next example:

    CONST SCREENWIDTH = 80
    CALL PrintBanner (SCREENWIDTH, "Monthly Status Report")
    .
    .
    .
    SUB PrintBanner (SW%, Title$)
        .
        .
        .
    END SUB

    If a numeric constant in an argument list does not have the same type as
    the corresponding parameter in the SUB or FUNCTION statement, then the
    constant is coerced to the type of the parameter, as you can see by the
    output from the next example:

    CALL test(4.6, 4.1)
    END

    SUB test (x%, y%)
        PRINT x%, y%
    END SUB

    Output

    5             4

    Expressions resulting from operations on variables and constants can also
    be passed to a procedure. As is the case with constants, numeric
    expressions that disagree in type with their parameters are coerced into
    agreement, as shown here:

    Checker A! + 25!, NOT BooleanVal%

    ' In the next call, putting parentheses around the
    ' long-integer variable Bval& makes it an expression.
    ' The (Bval&) expression is coerced to a short integer
    ' in the Checker SUB:
    Checker A! / 3.1, (Bval&)
    .
    .
    .
    END

    SUB Checker (Param1!, Param2%)
        .
        .
        .
    END SUB

2.5.3  Passing Variables

    This section discusses how to pass simple variables, complete arrays,
    elements of arrays, records, and elements of records to procedures.

    2.5.3.1  Passing Simple Variables

    In both argument and parameter lists, you can declare the type for a
    simple variable in one of the following three ways:

    1. Append one of the following type-declaration suffixes to the variable
        name: %, &, !, #, or $.

    2. Declare the variable in a declare variablename AS type clause, where
        the placeholder declare can be either DIM, COMMON, REDIM, SHARED, or
        STATIC, and type can be either INTEGER, LONG, SINGLE, DOUBLE, STRING,
        or STRING * n. For example:

        DIM A AS LONG

    3. Use a DEFtype statement to set the default type.

    No matter which method you choose, corresponding variables must have the
    same type in both the argument and parameter lists, as shown in the
    following example.

    Example

    In this example, two arguments are passed to the FUNCTION procedure. The
    first is an integer giving the length of the string returned by the
    FUNCTION, while the second is a character that is repeated to make the
    string.

    FUNCTION CharString$(A AS INTEGER, B$) STATIC
        CharString$ = STRING$(A%, B$)
    END FUNCTION

    DIM X AS INTEGER
    INPUT "Enter a number (1 to 80): ", X
    INPUT "Enter a character: ", Y$

    ' Print a string consisting of the Y$ character, repeated
    ' X number of times:
    PRINT CharString$(X, Y$)
    END

    Output

    Enter a number (1 to 80): 21
    Enter a character: #
    #####################

    2.5.3.2  Passing an Entire Array

    To pass all the elements of an array to a procedure, put the array's name,
    followed by left and right parentheses, in both the argument and parameter
    lists.

    Example

    This example shows how to pass all the elements of an array to a
    procedure:

    DIM Values(1 TO 5) AS INTEGER

    ' Note empty parentheses after array name when calling
    ' procedure and passing array:
    CALL ChangeArray (1, 5, Values())
    CALL PrintArray (1, 5, Values())
    END

    ' Note empty parentheses after P parameter:

    SUB ChangeArray (Min%, Max%, P() AS INTEGER) STATIC
        FOR I% = Min% TO Max%
        P(I%) = I% ^ 3
        NEXT I%
    END SUB

    SUB PrintArray (Min%, Max%, P() AS INTEGER) STATIC
        FOR I% = Min% TO Max%
        PRINT P(I%)
        NEXT I%
        PRINT
    END SUB

    2.5.3.3  Passing Individual Array Elements

    If a procedure does not require an entire array, you can pass individual
    elements of the array instead. To pass an element of an array, use the
    array name followed by the appropriate subscripts inside parentheses.

    Example

    The SqrVal Array(4,2) statement in the following example passes the
    element in row 4, column 2 of the array to the SqrVal subprogram (note how
    the subprogram actually changes the value of this array element):

    DIM Array(1 TO 5,1 TO 3)

    Array(4,2) = -36
    PRINT Array(4,2)
    SqrVal Array(4,2)
    PRINT Array(4,2)     ' The call to SqrVal has changed
                        ' the value of Array(4,2).
    END
    SUB SqrVal(A) STATIC
        A = SQR(ABS(A))
    END SUB

    Output

    -36
    6

    2.5.3.4  Using Array-Bound Functions

    The LBOUND and UBOUND functions are a useful way to determine the size of
    an array passed to a procedure. The LBOUND function finds the smallest
    index value of an array subscript, while the UBOUND function finds the
    largest one. These functions save you the trouble of having to pass the
    upper and lower bounds of each array dimension to a procedure.

    Example

    The subprogram in the following example uses the LBOUND function to
    initialize the variables Row and Col to the lowest subscript values in
    each dimension of A. It also uses the UBOUND function to limit the number
    of times the FOR loop executes to the number of elements in the array.

    SUB PrintOut(A(2)) STATIC
        FOR Row = LBOUND(A,1) TO UBOUND(A,1)
        FOR Col = LBOUND(A,2) TO UBOUND(A,2)
            PRINT A(Row,Col)
        NEXT Col
        NEXT Row
    END SUB

    2.5.3.5  Passing an Entire Record

    To pass a complete record (a variable declared as having a user-defined
    type) to a procedure, complete the following steps:

    1. Define the type (StockItem in this example):

        TYPE StockItem
        PartNumber  AS STRING *  6
        Description AS STRING * 20
        UnitPrice   AS SINGLE
        Quantity    AS INTEGER
        END TYPE

    2. Declare a variable (StockRecord) with that type:

        DIM StockRecord AS StockItem

    3. Call a procedure (FindRecord) and pass it the variable you have
        declared:

        CALL FindRecord(StockRecord)

    4. In the procedure definition, give the parameter the same type as the
        variable:

        SUB FindRecord (RecordVar AS StockItem) STATIC
        .
        .
        .
        END SUB

    2.5.3.6  Passing Individual Elements of a Record

    To pass an individual element in a record to a procedure, put the name of
    the element (recordname.elementname) in the argument list. Be sure, as
    always, that the corresponding parameter in the procedure definition
    agrees with the type of that element.

    Example

    The following example shows how to pass the two elements in the record
    variable StockItem to the PrintTag SUB procedure. Note how each parameter
    in the SUB procedure agrees with the type of the individual record
    elements.

    TYPE StockType
        PartNumber AS STRING * 6
        Descrip AS STRING * 20
        UnitPrice AS SINGLE
        Quantity AS INTEGER
    END TYPE

    DIM StockItem AS StockType

    CALL PrintTag (StockItem.Descrip, StockItem.UnitPrice)
    .
    .
    .
    END

    SUB PrintTag (Desc$, Price AS SINGLE)
        .
        .
        .
    END SUB

2.5.4  Checking Arguments with the DECLARE Statement

    If you are using QuickBASIC to write your program, you will notice that
    QuickBASIC automatically inserts a DECLARE statement for each procedure
    whenever you save the program. Each DECLARE statement consists of the word
    DECLARE, followed by the words SUB or FUNCTION, the name of the procedure,
    and a set of parentheses. If the procedure has no parameters, then the
    parentheses are empty. If the procedure has parameters, then the
    parentheses enclose a parameterlist that specifies the number and type of
    the arguments to be passed to the procedure. This parameterlist has the
    same format as the list in the definition line of the SUB or FUNCTION.

    The purpose of the parameterlist in a DECLARE statement is to turn on
    "type checking" of arguments passed to the procedure. That is, every time
    the procedure is called with variable arguments, those variables are
    checked to be sure they agree with the number and type of the parameters
    in the DECLARE statement.

    QuickBASIC puts all procedure definitions at the end of a module when it
    saves a program. Therefore, if there were no DECLARE statements, when you
    tried to compile this program with the BC command you would run into a
    problem known as "forward reference" (calling a procedure before it is
    defined). By generating a prototype of the procedure definition, DECLARE
    statements allow your program to call procedures that are defined later in
    a module, or in another module altogether.

    Examples

    The next example shows an empty parameter list in the DECLARE statement,
    since no arguments are passed to GetInput$:

    DECLARE FUNCTION GetInput$ ()
    X$ = GetInput$

    FUNCTION GetInput$ STATIC
        GetInput$ = INPUT$(10)
    END FUNCTION

    The next example shows a parameter list in the DECLARE statement, since an
    integer argument is passed to this version of GetInput$ :

    DECLARE FUNCTION GetInput$ (X%)
    X$ = GetInput$ (5)

    FUNCTION GetInput$ (X%) STATIC
        GetInput$ = INPUT$(X%)
    END FUNCTION

    2.5.4.1  When QuickBASIC Does Not Generate a DECLARE Statement

    In certain instances, QuickBASIC does not generate DECLARE statements in
    the module that calls a procedure.

    QuickBASIC cannot generate a DECLARE statement in one module for a
    FUNCTION procedure defined in another module if the module is not loaded.
    In such a case, you must type the DECLARE statement yourself at the
    beginning of the module where the FUNCTION is called; otherwise,
    QuickBASIC considers the call to the FUNCTION to be a variable name.

    QuickBASIC does not generate a DECLARE statement for a SUB procedure in
    another module, whether that module is loaded or not. The DECLARE
    statement is not needed unless you want to call the SUB procedure without
    using the keyword CALL, however.

    QuickBASIC also cannot generate a DECLARE statement for any procedure in a
    Quick library. You must add one to the program yourself.

    2.5.4.2  Developing Programs outside the QuickBASIC Environment

    If you are writing your programs with your own text editor and then
    compiling them outside the QuickBASIC environment with the BC and LINK
    commands, be sure to put DECLARE statements in the following three
    locations:

    1. At the beginning of any module that calls a FUNCTION procedure before
        it is defined:

        DECLARE FUNCTION Hypot (X!, Y!)

        INPUT X, Y
        PRINT Hypot(X, Y)
        END

        FUNCTION Hypot (A, B) STATIC
        Hypot = SQR(A ^ 2 + B ^ 2)
        END FUNCTION

    2. At the beginning of any module that calls a SUB procedure before it is
        defined and does not use CALL when calling the SUB:

        DECLARE SUB PrintString (X, Y)
        INPUT X, Y

        PrintString X, Y     ' Note: no parentheses around
                            ' arguments
        END

        SUB PrintString (A,B) STATIC

        ' Convert the numbers to strings, remove any leading
        ' blanks from the second number, and print:
        PRINT STR$(A) + LTRIM$(STR$(B))
        END SUB

        When you call a SUB procedure with CALL, you don't have to declare the
        SUB first:

        A$ = "466"
        B$ = "123"
        CALL PrintString(A$, B$)
        END

        SUB PrintString (X$, Y$) STATIC
        PRINT VAL(X$) + VAL(Y$)
        END SUB

    3. At the beginning of any module that calls a SUB or FUNCTION procedure
        defined in another module (an "external procedure")

    If your procedure has no parameters, remember to put empty parentheses
    after the name of the procedure in the DECLARE statement, as in the next
    example:

    DECLARE FUNCTION GetHour$ ()
    PRINT GetHour$
    END

    FUNCTION GetHour$ STATIC
        GetHour$ = LEFT$(TIME$,2)
    END FUNCTION

    Remember, a DECLARE statement can appear only at the module level, not the
    procedure level. A DECLARE statement affects the entire module in which it
    appears.

    2.5.4.3  Using Include Files for Declarations

    If you have created a separate procedure-definition module that defines
    one or more SUB or FUNCTION procedures, it is a good idea to make an
    include file to go along with this module. This include file should
    contain the following:

    ■ DECLARE statements for all the module's procedures.

    ■ TYPE...END TYPE record definitions for any record parameters in this
    module's SUB or FUNCTION procedures.

    ■ COMMON statements listing variables shared between this module and other
    modules in the program. (See Section 2.6.3, "Sharing Variables with
    Other Modules," for more information on using COMMON for this purpose.)

    Every time you use the definition module in one of your programs, insert a
    $INCLUDE metacommand at the beginning of any module that invokes
    procedures in the definition module. When your program is compiled, the
    actual contents of the include file are substituted for the $INCLUDE
    metacommand.

    A simple rule of thumb is to make an include file for every module and
    then use the module and the include file together as outlined above. The
    following list itemizes some of the benefits of this technique:

    ■ A module containing procedure definitions remains truly modular──that
    is, you don't have to copy all the DECLARE statements for its procedures
    every time you call them from another module; instead, you can just
    substitute one $INCLUDE metacommand.

    ■ In QuickBASIC, using an include file for procedure declarations
    suppresses automatic generation of DECLARE statements when you save a
    program.

    ■ Using an include file for declarations avoids problems with getting one
    module to recognize a FUNCTION in another module. (See Section 2.5.4.1,
    "When QuickBASIC Does Not Generate a DECLARE Statement," for more
    information.)

    You can take advantage of QuickBASIC's facility for generating DECLARE
    statements when creating your include file. The following steps show you
    how to do this:

    1. Create your module.

    2. Within that module, call any SUB or FUNCTION procedures you have
        defined.

    3. Save the module to get automatic DECLARE statements for all the
        procedures.

    4. Reedit the module, removing the procedure calls and moving the DECLARE
        statements to a separate include file.

    See Appendix F, "Metacommands," for more information on the syntax and
    usage of the $INCLUDE metacommand.

    Example

    The following fragments illustrate how to use a definition module and an
    include file together:

    ' =========================================================
    '                        MODDEF.BAS
    ' This module contains definitions for the PROMPTER and
    ' MAX! procedures.
    ' =========================================================

    FUNCTION Max! (X!, Y!) STATIC
        IF X! > Y! THEN Max! = X! ELSE Max! = Y!
    END FUNCTION

    SUB Prompter (Row%, Column%, RecVar AS RecType) STATIC
        LOCATE Row%, Column%
        INPUT "Description: ", RecVar.Description
        INPUT "Quantity:    ", RecVar.Quantity
    END SUB

    ' =========================================================
    '                        MODDEF.BI
    '  This is an include file that contains DECLARE statements
    '  for the PROMPTER and MAX! procedures (as well as a TYPE
    '  statement defining the RecType user type). Use this file
    '  whenever you use the MODDEF.BAS module.
    ' =========================================================

    TYPE RecType
        Description AS STRING * 15
        Quantity AS INTEGER
    END TYPE

    DECLARE FUNCTION Max! (X!, Y!)
    DECLARE SUB Prompter (Row%, Column%, RecVar AS RecType)

    ' ============================================================
    '                         SAMPLE.BAS
    '   This module is linked with the MODDEF.BAS module, and
    '   calls the PROMPTER and MAX! procedures in MODDEF.BAS.
    ' ============================================================

    ' The next line makes the contents of the MODDEF.BI include
    ' file part of this module as well:
    ' $INCLUDE: 'MODDEF.BI'
    .
    .
    .
    INPUT A, B
    PRINT Max!(A, B)        ' Call the Max! FUNCTION in MODDEF.BAS
    .
    .
    .
    Prompter 5, 5, RecVar   ' Call the Prompter SUB in MODDEF.BAS
    .
    .
    .

    ──────────────────────────────────────────────────────────────────────────
    IMPORTANT
    While it is good programming practice to put procedure declarations in
    an include file, do not put the procedures themselves (SUB...END SUB or
    FUNCTION...END FUNCTION blocks) in an include file. Procedure
    definitions are not allowed inside include files in QuickBASIC Version
    4.5. If you have used include files to define SUB procedures in programs
    written with QuickBASIC Versions 2.0 or 3.0, either put these
    definitions in a separate module or incorporate them into the module
    where they are called.
    ──────────────────────────────────────────────────────────────────────────

    2.5.4.4  Declaring Procedures in Quick Libraries

    A convenient programming practice is to put all the declarations for
    procedures in a Quick library into one include file. With the $INCLUDE
    metacommand you can then incorporate this include file into programs using
    the library. This saves you the trouble of copying all the relevant
    DECLARE statements every time you use the library.

2.5.5  Passing Arguments by Reference

    By default, variables──whether simple scalar variables, arrays and array
    elements, or records──are passed "by reference" to FUNCTION and SUB
    procedures. Here is what is meant by passing variables by reference:

    ■ Each program variable has an address, or a location in memory where its
    value is stored.

    ■ Calling a procedure and passing variables to it by reference calls the
    procedure and passes it the address of each variable. Thus, the address
    of the variable and the address of its corresponding parameter in the
    procedure are one and the same.

    ■ Therefore, if the procedure modifies the value of the parameter, it also
    modifies the value of the variable that is passed.

    If you do not want a procedure to change the value of a variable, pass the
    procedure the value contained in the variable, not the address. This way,
    changes are made only to a copy of the variable, not the variable itself.
    See the next section for a discussion of this alternative way of passing
    variables.

    Example

    In the following program, changes made to the parameter A$ in the Replace
    procedure also change the argument Test$:

    Test$ = "a string with all lowercase letters."
    PRINT "Before subprogram call: "; Test$
    CALL Replace (Test$, "a")
    PRINT "After subprogram call: "; Test$
    END

    SUB Replace (A$, B$) STATIC
        Start = 1
        DO

        ' Look for B$ in A$, starting at the character
        ' with position "Start" in A$:
        Found = INSTR(Start, A$, B$)
        ' Make every occurrence of B$ in A$
        ' an uppercase letter:
        IF Found > 0 THEN
            MID$(A$,Found) = UCASE$(B$)
            Start = Start + 1
        END IF
        LOOP WHILE Found > 0
    END SUB

    Output

    Before subprogram call: a string with all lowercase letters.
    After subprogram call: A string with All lowercAse letters.

2.5.6  Passing Arguments by Value

    Passing an argument "by value" means the value of the argument is passed,
    rather than its address. In BASIC procedures, passing a variable by value
    is simulated by copying the variable into a temporary location, then
    passing the address of this temporary location. Since the procedure does
    not have access to the address of the original variable, it cannot change
    the original variable; it makes all changes to the copy instead.

    You can pass expressions as arguments to procedures, as in the following:

    ' A + B is an expression; the values of A and B
    ' are not affected by this procedure call:
    CALL Mult(A + B, B)

    Expressions are always passed by value (see Section 2.5.2, "Passing
    Constants and Expressions," for more information).

    Example

    One way to pass a variable by value is to enclose it in parentheses, thus
    making it an expression. As you can see from the output that follows,
    changes to the SUB procedure's local variable Y are passed back to the
    module-level code as changes to the variable B. However, changes to X in
    the procedure do not affect the value of A, since A is passed by value.

    A = 1
    B = 1
    PRINT "Before subprogram call, A ="; A; ", B ="; B

    ' A is passed by value, and B is passed by reference:
    CALL Mult((A), B)
    PRINT "After subprogram call, A ="; A; ", B ="; B
    END

    SUB Mult (X, Y) STATIC
        X = 2 * X
        Y = 3 * Y
        PRINT "In subprogram, X ="; X; ", Y ="; Y
    END SUB

    Output

    Before subprogram call, A = 1 , B = 1
    In subprogram, X = 2 , Y = 3
    After subprogram call, A = 1 , B = 3


2.6  Sharing Variables with SHARED

    In addition to passing variables through argument and parameter lists,
    procedures can also share variables with other procedures and with code at
    the module level (that is, code within a module but outside of any
    procedure) in one of the two ways listed below:

    1. Variables listed in a SHARED statement within a procedure are shared
        only between that procedure and the module-level code. Use this method
        when different procedures in the same module need different
        combinations of module-level variables.

    2. Variables listed in a module-level COMMON SHARED, DIM SHARED, or REDIM
        SHARED statement are shared between the module-level code and all
        procedures within that module. This method is most useful when all
        procedures in a module use a common set of variables.

    You can also use the COMMON or COMMON SHARED statement to share variables
    among two or more modules. Sections 2.6.1-2.6.3 discuss these three ways
    to share variables.

2.6.1  Sharing Variables with Specific Procedures in a Module

    If different procedures within a module need to share different variables
    with the module-level code, use the SHARED statement within each
    procedure.

    Arrays in SHARED statements consist of the array name followed by a set of
    empty parentheses ( ):

    SUB JustAnotherSub STATIC
        SHARED ArrayName ()
        .
        .
        .

    If you give a variable its type in an AS type clause, then the variable
    must also be typed with the AS type clause in a SHARED statement:

    DIM Buffer AS STRING * 10
    .
    .
    .
    END

    SUB ReadRecords STATIC
        SHARED Buffer AS STRING * 10
        .
        .
        .
    END SUB

    Example

    In the next example, the SHARED statements in the GetRecords and
    InventoryTotal procedures show the format of a shared variable list:

    ' =========================================================
    '                   MODULE-LEVEL CODE
    ' =========================================================
    TYPE RecType
        Price AS SINGLE
        Desc AS STRING * 35
    END TYPE

    DIM RecVar(1 TO 100) AS RecType   ' Array of records

    INPUT "File name: ", FileSpec$
    CALL GetRecords
    PRINT InventoryTotal
    END

    ' =========================================================
    '                   PROCEDURE-LEVEL CODE
    ' =========================================================
    SUB GetRecords STATIC

    ' Both FileSpec$ and the RecVar array of records
    ' are shared with the module-level code above:
        SHARED FileSpec$, RecVar() AS RecType
        OPEN FileSpec$ FOR RANDOM AS #1
        .
        .
        .
    END SUB

    FUNCTION InventoryTotal STATIC

    ' Only the RecVar array is shared with the module-level
    ' code:
        SHARED RecVar() AS RecType
        .
        .
        .
    END FUNCTION

2.6.2  Sharing Variables with All Procedures in a Module

    If variables are declared at the module level with the SHARED attribute in
    a COMMON, DIM, or REDIM statement (for example, by using a statement of
    the form COMMON SHARED variablelist), then all procedures within that
    module have access to those variables; in other words, the SHARED
    attribute makes variables global throughout a module.

    The SHARED attribute is convenient when you need to share large numbers of
    variables among all procedures in a module.

    Examples

    These statements declare variables shared among all procedures in one
    module:

    COMMON SHARED A, B, C
    DIM SHARED Array(1 TO 10, 1 TO 10) AS UserType
    REDIM SHARED Alpha(N%)

    In the following example, the module-level code shares the string
    arrayStrArray and the integer variables Min and Max with the two SUB
    procedures FillArray and PrintArray :

    ' =========================================================
    '                    MODULE-LEVEL CODE
    ' =========================================================
    DECLARE SUB FillArray ()
    DECLARE SUB PrintArray ()

    ' The following DIM statements share the Min and Max
    ' integer variables and the StrArray string array
    ' with any SUB or FUNCTION in this module:
    DIM SHARED StrArray (33 TO 126) AS STRING * 5
    DIM SHARED Min AS INTEGER, Max AS INTEGER

    Min = LBOUND(StrArray)
    Max = UBOUND(StrArray)

    FillArray               ' Note the absence of argument lists.
    PrintArray
    END
    ' =========================================================
    '                    PROCEDURE-LEVEL CODE
    ' =========================================================
    SUB FillArray STATIC

        ' Load each element of the array from 33 to 126
        ' with a 5-character string, each character of which
        ' has the ASCII code I%:
        FOR I% = Min TO Max
        StrArray(I%) = STRING$(5, I%)
        NEXT

    END SUB

    SUB PrintArray STATIC
        FOR I% = Min TO Max
        PRINT StrArray(I%)
        NEXT
    END SUB

    Partial output

    !!!!!
    """""
    #####
    $$$$$
    %%%%%
    &&&&&
    .
    .
    .

    If you are using your own text editor to write your programs and directly
    compiling those programs outside the QuickBASIC development environment,
    note that variable declarations with the SHARED attribute must precede the
    procedure definition. Otherwise, the value of any variable declared with
    SHARED is not available to the procedure, as shown by the output from the
    next example. (If you are using QuickBASIC to create your programs, this
    sequence is not required, since QuickBASIC automatically saves programs in
    the correct order.)

    DEFINT A-Z

    FUNCTION Adder (X, Y) STATIC
        Adder = X + Y + Z
    END FUNCTION

    DIM SHARED Z
    Z = 2
    PRINT Adder (1, 3)
    END

    Output

    4

    The next example shows how you should save the module shown above, with
    the definition of Adder following the DIM SHARED Z statement:

    DEFINT A-Z

    DECLARE FUNCTION Adder (X, Y)

    ' The variable Z is now shared with Adder:
    DIM SHARED Z
    Z = 2
    PRINT Adder (1, 3)
    END

    FUNCTION Adder (X, Y) STATIC
        Adder = X + Y + Z
    END FUNCTION

    Output

    6

2.6.3  Sharing Variables with Other Modules

    If you want to share variables across modules in your program, list the
    variables in COMMON or COMMON SHARED statements at the module level in
    each module.

    Examples

    The following example shows how to share variables between modules by
    using a COMMON statement in the module that calls the SUB procedures, as
    well as a COMMON SHARED statement in the module that defines the
    procedures. With COMMON SHARED, all procedures in the second module have
    access to the common variables:

    ' =========================================================
    '                      MAIN MODULE
    ' =========================================================

    COMMON A, B
    A = 2.5
    B = 1.2
    CALL Square
    CALL Cube
    END
    ' =========================================================
    '           Module with Cube and Square Procedures
    ' =========================================================

    ' NOTE: The names of the variables (X, Y) do not have to be
    ' the same as in the other module (A, B). Only the types
    ' have to be the same.

    COMMON SHARED X, Y  ' This statement is at the module level.
                        ' Both X and Y are shared with the CUBE
                        ' and SQUARE procedures below.
    SUB Cube STATIC
        PRINT "A cubed   ="; X ^ 3
        PRINT "B cubed   ="; Y ^ 3
    END SUB

    SUB Square STATIC
        PRINT "A squared ="; X ^ 2
        PRINT "B squared ="; Y ^ 2
    END SUB

    The following example uses named COMMON blocks at the module levels and
    SHARED statements within procedures to share different sets of variables
    with each procedure:

    ' =========================================================
    '                        MAIN MODULE
    ' Prints the volume and density of a filled cylinder given
    ' the input values
    ' =========================================================

    COMMON /VolumeValues/ Height, Radius, Volume
    COMMON /DensityValues/ Weight, Density

    INPUT "Height of cylinder in centimeters: ", Height
    INPUT "Radius of cylinder in centimeters: ", Radius
    INPUT "Weight of filled cylinder in grams: ", Weight

    CALL VolumeCalc
    CALL DensityCalc

    PRINT "Volume is"; Volume; "cubic centimeters."
    PRINT "Density is"; Density; "grams/cubic centimeter."
    END

    ' =========================================================
    '     Module with DensityCalc and VolumeCalc Procedures
    ' =========================================================

    COMMON /VolumeValues/ H, R, V
    COMMON /DensityValues/ W, D

    SUB VolumeCalc STATIC

        ' Share the Height, Radius, and Volume variables
        ' with this procedure:
        SHARED H, R, V
        CONST PI = 3.141592653589#
        V = PI * H * (R ^ 2)
    END SUB

    SUB DensityCalc STATIC

        ' Share the Weight, Volume, and Density variables
        ' with this procedure:
        SHARED W, V, D
        D = W / V
    END SUB

    Output

    Height of cylinder in centimeters: 100
    Radius of cylinder in centimeters: 10
    Weight of filled cylinder in grams: 10000
    Volume is 31415.93 cubic centimeters.
    Density is .3183099 grams/cubic centimeter.

2.6.4  The Problem of Variable Aliasing

    "Variable aliasing" is sometimes a problem in long programs containing
    many variables and procedures. Variable aliasing is the situation where
    two or more names refer to the same location in memory. It occurs:

    ■ When the same variable appears more than once in the list of arguments
    passed to a procedure.

    ■ When a variable passed in an argument list is also accessed by the
    procedure by means of the SHARED statement or the SHARED attribute.

    To avoid aliasing problems, double-check variables shared with a procedure
    to make sure they don't also appear in a procedure call's argument list.
    Also, don't pass the same variable twice, as in the next statement:

    ' X is passed twice; this will lead to aliasing problems
    ' in the Test procedure:
    CALL Test(X, X, Y)

    Example

    The following example illustrates how variable aliasing can occur. Here
    the variable A is shared between the module-level code and the SUB
    procedure with the DIM SHARED statement. However, A is also passed by
    reference to the SUB as an argument. Therefore, in the subprogram, A and X
    both refer to the same location in memory. Thus, when the subprogram
    modifies X, it is also modifying A, and vice versa.

    DIM SHARED A
    A = 4
    CALL PrintHalf(A)
    END

    SUB PrintHalf (X) STATIC
        PRINT "Half of"; X; "plus half of"; A; "equals";
        X = X / 2            ' X and A now both equal 2.
        A = A / 2            ' X and A now both equal 1.
        PRINT A + X
    END SUB

    Output

    Half of 4 plus half of 4 equals 2


2.7  Automatic and STATIC Variables

    When the STATIC attribute appears on a procedure-definition line, it means
    that local variables within the procedure are STATIC; that is, their
    values are preserved between calls to the procedure.

    Leaving off the STATIC attribute makes local variables within the
    procedure "automatic" by default; that is, you get a fresh set of local
    variables each time the procedure is called.

    You can override the effect of leaving off the STATIC attribute by using
    the STATIC statement within the procedure, thus making some variables
    automatic and others STATIC (see Section 2.8 for more information).

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    The SHARED statement also overrides the default for variables in a
    procedure (local STATIC or local automatic), since any variable
    appearing in a SHARED statement is known at the module level and thus is
    not local to the procedure.
    ──────────────────────────────────────────────────────────────────────────


2.8  Preserving Values of Local Variables with the STATIC Statement

    Sometimes you may want to make some local variables in a procedure STATIC
    while keeping the rest automatic. List those variables in a STATIC
    statement within the procedure.

    Also, putting a variable name in a STATIC statement is a way of making
    absolutely sure that the variable is local, since a STATIC statement
    overrides the effect of a module-level SHARED statement.

    A STATIC statement can appear only within a procedure. An array name in a
    STATIC statement must be followed by a set of empty parentheses ( ). Also,
    you must dimension any array that appears in a STATIC statement before
    using the array, as shown in the next example:

    SUB SubProg2
        STATIC Array() AS INTEGER
        DIM Array(-5 TO 5, 1 TO 25) AS INTEGER
        .
        .
        .
    END SUB

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    If you give a variable its type in an AS type clause, then the AS type
    clause must appear along with the variable's name in both the STATIC and
    DIM statements.
    ──────────────────────────────────────────────────────────────────────────

    Example

    The following example shows how a STATIC statement preserves the value of
    the string variable Y$ throughout successive calls to TestSub:

    DECLARE SUB TestSub ()
    FOR I% = 1 TO 5
        TestSub              ' Call TestSub five times.
    NEXT I%
    END

    SUB TestSub             ' Note: no STATIC attribute

    ' Both X$ and Y$ are local variables in TestSub (that is,
    ' their values are not shared with the module-level code).
    ' However since X$ is an automatic variable, it is
    ' reinitialized to a null string every time TestSub is
    ' called. In contrast, Y$ is STATIC, so it retains the
    ' value it had from the last call:
        STATIC Y$
        X$ = X$ + "*"
        Y$ = Y$ + "*"
        PRINT X$, Y$
    END SUB

    Output

    *             *
    *             **
    *             ***
    *             ****
    *             *****


2.9  Recursive Procedures

    Procedures in BASIC can be recursive. A recursive procedure is one that
    can call itself or call other procedures that in turn call the first
    procedure.

2.9.1  The Factorial Function

    A good way to illustrate recursive procedures is to consider the factorial
    function from mathematics. One way to define n! ("n factorial") is with
    the following formula:

    n! = n * (n-1) * (n-2) * ... * 2 * 1

    For example, 5 factorial is evaluated as follows:

    5! = 5 * 4 * 3 * 2 * 1 = 120

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Do not confuse the mathematical factorial symbol (!) used in this
    discussion with the single-precision type-declaration suffix used by
    BASIC.
    ──────────────────────────────────────────────────────────────────────────

    Factorials lend themselves to a recursive definition as well:

    n! = n * (n-1)!

    This leads to the following progression:

    5! = 5 * 4!
    4! = 4 * 3!
    3! = 3 * 2!
    2! = 2 * 1!
    1! = 1 * 0!

    Recursion must always have a terminating condition. With factorials this
    terminating condition occurs when 0! is evaluated──by definition, 0! is
    equal to 1.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Although a recursive procedure can have STATIC variables by default (as
    in the next example), it is often preferable to let automatic variables
    be the default instead. In this way, recursive calls will not overwrite
    variable values from a preceding call.
    ──────────────────────────────────────────────────────────────────────────

    Example

    The following example uses a recursive FUNCTION procedure to calculate
    factorials:

    DECLARE FUNCTION Factorial# (N%)
    Format$ = "###_! = ###################"
    DO
        INPUT "Enter number from 0 - 20 (or -1 to end): ", Num%
        IF Num% >= 0 AND Num% <= 20 THEN
        PRINT USING Format$; Num%; Factorial#(Num%)
        END IF
    LOOP WHILE Num% >= 0
    END

    FUNCTION Factorial# (N%) STATIC

        IF N% > 0 THEN  ' Call Factorial# again
                        ' if N is greater than zero.
        Factorial# = N% * Factorial#(N% - 1)

        ELSE            ' Reached the end of recursive calls
                        ' (N% = 0), so "climb back up the ladder."
        Factorial# = 1
        END IF
    END FUNCTION

2.9.2  Adjusting the Size of the Stack

    Recursion can eat up a lot of memory, since each set of automatic
    variables in a SUB or FUNCTION procedure is saved on the stack. (Saving
    variables this way allows a procedure to continue with the correct
    variable values after control returns from a recursive call.)

    If you have a recursive procedure with many automatic variables, or a
    deeply nested recursive procedure, you may need to adjust the size of the
    stack with a CLEAR , , stacksize statement, where stacksize is the number
    of bytes from the stack you want to reserve. Otherwise, while your program
    is running you may get the error message Out of stack space.

    The following steps outline one way to estimate the amount of memory a
    recursive procedure needs:

    1. Insert a single quotation mark to temporarily turn the recursive call
        into a comment line so that the procedure will be invoked only once
        when the program runs.

    2. Call the FRE(-2) function (which returns the total unused stack space)
        just before you call the recursive procedure. Also call the FRE(-2)
        function right at the end of the recursive procedure. Use PRINT
        statements to display the returned values.

    3. Run the program. The difference in values is the amount of stack space
        (in bytes) used by one call to the procedure.

    4. Estimate the maximum number of times the procedure is likely to be
        invoked, then multiply this value by the stack space consumed by one
        call to the procedure. The result is totalbytes.

    5. Reserve the amount of stack space calculated in step 4:

        CLEAR , , totalbytes


2.10  Transferring Control to Another Program with CHAIN

    Unlike procedure calls, which occur within the same program, the CHAIN
    statement simply starts a new program. When a program chains to another
    program, the following sequence occurs:

    1. The first program stops running.

    2. The second program is loaded into memory.

    3. The second program starts running.

    The advantage of using CHAIN is that it enables you to split a program
    with large memory requirements into several smaller programs.

    The COMMON statement allows you to pass variables from one program to
    another program in a chain. A common programming practice is to put these
    COMMON statements in an include file, and then use the $INCLUDE
    metacommand at the beginning of each program in the chain.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Don't use a COMMON /blockname /variablelist statement (a "named COMMON
    block") to pass variables to a chained program, since variables listed
    in named COMMON blocks are not preserved when chaining. Use a blank
    COMMON block (COMMON variablelist) instead.
    ──────────────────────────────────────────────────────────────────────────

    Example

    This example, which shows a chain connecting three separate programs, uses
    an include file to declare variables passed in common among the programs:

    ' ============ CONTENTS OF INCLUDE FILE COMMONS.BI ========
    DIM Values(10)
    COMMON Values(), NumValues

    ' ======================= MAIN.BAS ========================

    ' Read in the contents of the COMMONS.BI file:
    ' $INCLUDE: 'COMMONS.BI'

        ' Input the data:
        INPUT "Enter number of data values (<=10): ", NumValues
        FOR I = 1 TO NumValues
        Prompt$ = "Value ("+LTRIM$(STR$(I))+")? "
        PRINT Prompt$;
        INPUT "", Values(I)
        NEXT I

        ' Have the user specify the calculation to do:
        INPUT "Calculation (1=st. dev., 2=mean)? ", Choice

        ' Now, chain to the correct program:
        SELECT CASE Choice

        CASE 1:             ' Standard Deviation
            CHAIN "STDEV"

        CASE 2:             ' Mean
            CHAIN "MEAN"
        END SELECT
    END

    ' ======================= STDEV.BAS =======================
    ' Calculates the standard deviation of a set of data
    ' =========================================================

    ' $INCLUDE: 'COMMONS.BI'

        Sum   = 0   ' Normal sum
        SumSq = 0   ' Sum of values squared

        FOR I = 1 TO NumValues
        Sum   = Sum   + Values(I)
        SumSq = SumSq + Values(I) ^ 2
        NEXT I

        Stdev = SQR(SumSq / NumValues - (Sum / NumValues) ^ 2)
        PRINT "The Standard Deviation of the samples is: " Stdev
    END

    ' ======================== MEAN.BAS =======================
    ' Calculates the mean (average) of a set of data
    ' =========================================================

    ' $INCLUDE: 'COMMONS.BI'

        Sum = 0

        FOR I = 1 TO NumValues
        Sum = Sum + Values(I)
        NEXT

        Mean = Sum / NumValues
        PRINT "The mean of the samples is: " Mean
    END


2.11  Sample Application: Recursive Directory Search (WHEREIS.BAS)

    The following program uses a recursive SUB procedure, ScanDir, to scan a
    disk for the file name input by the user. Each time this program finds the
    given file, it prints the complete directory path to the file.

    Statements and Functions Used

    This program demonstrates the following statements and keywords discussed
    in this chapter:

    ■ DECLARE

    ■ FUNCTION...END FUNCTION

    ■ STATIC

    ■ SUB...END SUB

Program Listing

DEFINT A-Z

' Declare symbolic constants used in program:
CONST EOFTYPE = 0, FILETYPE = 1, DIRTYPE = 2, ROOT = "TWH"

DECLARE SUB ScanDir (PathSpec$, Level, FileSpec$, Row)

DECLARE FUNCTION MakeFileName$ (Num)
DECLARE FUNCTION GetEntry$ (FileNum, EntryType)

CLS
INPUT "File to look for"; FileSpec$
PRINT
PRINT "Enter the directory where the search should start"
PRINT "(optional drive + directories). Press <ENTER> to "
PRINT "begin search in root directory of current drive."
PRINT
INPUT "Starting directory"; PathSpec$
CLS

RightCh$ = RIGHT$(PathSpec$, 1)

IF PathSpec$ = "" OR RightCh$ = ":" OR RightCh$ <> "\" THEN
    PathSpec$ = PathSpec$ + "\"
END IF

FileSpec$ = UCASE$(FileSpec$)
PathSpec$ = UCASE$(PathSpec$)
Level = 1
Row = 3

' Make the top level call (level 1) to begin the search:
ScanDir PathSpec$, Level, FileSpec$, Row

KILL ROOT + ".*"        ' Delete all temporary files created
                        ' by the program.

LOCATE Row + 1, 1: PRINT "Search complete."
END

' ======================= GETENTRY ========================
'    This procedure processes entry lines in a DIR listing
'    saved to a file.

'    This procedure returns the following values:

'       GetEntry$       A valid file or directory name
'       EntryType       If equal to 1, then GetEntry$
'                       is a file.
'                       If equal to 2, then GetEntry$
'                       is a directory.
' =========================================================

FUNCTION GetEntry$ (FileNum, EntryType) STATIC

    ' Loop until a valid entry or end-of-file (EOF) is read:
    DO UNTIL EOF(FileNum)
        LINE INPUT #FileNum, EntryLine$
        IF EntryLine$ <> "" THEN

            ' Get first character from the line for test:
            TestCh$ = LEFT$(EntryLine$, 1)
            IF TestCh$ <> " " AND TestCh$ <> "." THEN EXIT DO
        END IF
    LOOP

    ' Entry or EOF found, decide which:
    IF EOF(FileNum) THEN         ' EOF, so return EOFTYPE
        EntryType = EOFTYPE       ' in EntryType.
        GetEntry$ = ""

    ELSE                         ' Not EOF, so it must be a
                                ' file or a directory.

        ' Build and return the entry name:
        EntryName$ = RTRIM$(LEFT$(EntryLine$, 8))

        ' Test for extension and add to name if there is one:
        EntryExt$ = RTRIM$(MID$(EntryLine$, 10, 3))
        IF EntryExt$ <> "" THEN
            GetEntry$ = EntryName$ + "." + EntryExt$
        ELSE
            GetEntry$ = EntryName$
        END IF

        ' Determine the entry type, and return that value
        ' to the point where GetEntry$ was called:
        IF MID$(EntryLine$, 15, 3) = "DIR" THEN
            EntryType = DIRTYPE            ' Directory
        ELSE
            EntryType = FILETYPE           ' File
        END IF

    END IF

END FUNCTION

' ===================== MAKEFILENAME$ =====================
'    This procedure makes a file name from a root string
'    ("TWH," defined as a symbolic constant at the module
'    level) and a number passed to it as an argument (Num).
' =========================================================

FUNCTION MakeFileName$ (Num) STATIC

    MakeFileName$ = ROOT + "." + LTRIM$(STR$(Num))

END FUNCTION

' ======================= SCANDIR =========================
'   This procedure recursively scans a directory for the
'   file name entered by the user.

'   NOTE: The SUB header doesn't use the STATIC keyword
'         since this procedure needs a new set of variables
'         each time it is invoked.
' =========================================================

SUB ScanDir (PathSpec$, Level, FileSpec$, Row)

    LOCATE 1, 1: PRINT "Now searching"; SPACE$(50);
    LOCATE 1, 15: PRINT PathSpec$;

    ' Make a file specification for the temporary file:
    TempSpec$ = MakeFileName$(Level)

    ' Get a directory listing of the current directory,
    ' and save it in the temporary file:
    SHELL "DIR " + PathSpec$ + " > " + TempSpec$

    ' Get the next available file number:
    FileNum = FREEFILE

    ' Open the DIR listing file and scan it:
    OPEN TempSpec$ FOR INPUT AS #FileNu

' Process the file, one line at a time:
    DO

        ' Input an entry from the DIR listing file:
        DirEntry$ = GetEntry$(FileNum, EntryType)

        ' If entry is a file:
        IF EntryType = FILETYPE THEN

            ' If the FileSpec$ string matches,
            ' print entry and exit this loop:
            IF DirEntry$ = FileSpec$ THEN
            LOCATE Row, 1: PRINT PathSpec$; DirEntry$;
            Row = Row + 1
            EntryType = EOFTYPE
            END IF

        ' If the entry is a directory, then make a recursive
        ' call to ScanDir with the new directory:
        ELSEIF EntryType = DIRTYPE THEN
            NewPath$ = PathSpec$ + DirEntry$ + "\"
            ScanDir NewPath$, Level + 1, FileSpec$, Row
            LOCATE 1, 1: PRINT "Now searching"; SPACE$(50);
            LOCATE 1, 15: PRINT PathSpec$;
        END IF

    LOOP UNTIL EntryType = EOFTYPE

    ' Scan on this DIR listing file is finished, so close it:
    CLOSE FileNum
END SUB



────────────────────────────────────────────────────────────────────────────
Chapter 3  File and Device I/O

    This chapter shows you how to use BASIC input and output (I/O) functions
    and statements. These statements permit your programs to access data
    stored in files and to communicate with devices attached to your system.

    The chapter includes material on a variety of programming tasks related to
    retrieving, storing, and formatting information. The relationship between
    data files and physical devices such as screens and keyboards is also
    covered.

    When you are finished with this chapter, you will know how to perform the
    following programming tasks:

    ■ Print text on the screen

    ■ Get input from the keyboard for use in a program

    ■ Create data files on disk

    ■ Store records in data files

    ■ Read records from data files

    ■ Read or modify data in files that are not in American Standard Code for
    Information Interchange (ASCII) format

    ■ Communicate with other computers through the serial port


3.1  Printing Text on the Screen

    This section explains how to accomplish the following tasks:

    ■ Display text on the screen with PRINT

    ■ Display formatted text on the screen with PRINT USING

    ■ Skip spaces in a row of printed text with SPC

    ■ Skip to a given column in a row of printed text with TAB

    ■ Change the number of rows or columns appearing on the screen with WIDTH

    ■ Open a text viewport with VIEW PRINT

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Output that appears on the screen is sometimes referred to as "standard
    output." You can redirect standard output by using the DOS command-line
    symbols > or >>, thus sending output that would have gone to the screen
    to a different output device (such as a printer) or to a disk file. (See
    your DOS documentation for more information on redirecting output.)
    ──────────────────────────────────────────────────────────────────────────

3.1.1  Screen Rows and Columns

    To understand how text is printed on the screen, it helps to think of the
    screen as a grid of "rows" and "columns." The height of one row slightly
    exceeds the height of a line of printed output, while the width of one
    column is just wider than the width of one character. A standard screen
    configuration in text mode (nongraphics) is 80 columns wide by 25 rows
    high. Figure 3.1 shows how each character printed on the screen occupies a
    unique cell in the grid, a cell that can be identified by pairing a row
    argument with a column argument.

    The bottom row of the screen is not usually used for output, unless you
    use a LOCATE statement to display text there. (See Section 3.3,
    "Controlling the Text Cursor," for more information on LOCATE.)

3.1.2  Displaying Text and Numbers with PRINT


┌───────────────────────────────────────────────────────────────────┐
│                                                                   │
│                                                                   │
│        ←──────────────── Columns ──────────────→                  │
│     ┌────────────────────────                                     │
│     │     1   2   3   4   5   6   7   8   9                       │
│  ↑  │   ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬─                    │
│  │  │ 1 │   │   │   │   │   │   │   │   │   │                     │
│  │  │   ├───┼───┼───┼───┼───┼───┼───┼───┼───┼─                    │
│  │  │ 2 │   │   │   │ H │ E │ L │ L │ O │   │                     │
│Rows │   ├───┼───┼───┼──\┼───┼───┼───┼───┼───┼─                    │
│  │  │ 3 │   │   │   │   │\──┼───┼───┼───┼───┼── This output starts│
│  │  │   ├───┼───┼───┼───┼───┼───┼───┼───┼───┼─  in row 2, column 4│
│  │    4 │   │   │   │   │   │   │   │   │   │                     │
│  │      ├───┼───┼───┼───┼───┼───┼───┼───┼───┼─                    │
│  ↓      │   │   │   │   │   │   │   │   │   │                     │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

Figure 3.1  Text Output on Screen

    By far the most commonly used statement for output to the screen is the
    PRINT statement. With PRINT, you can display numeric or string values, or
    a mixture of the two. In addition, PRINT with no arguments prints a blank
    line.

    The following are some general comments about PRINT:

    ■ PRINT always prints numbers with a trailing blank space. If the number
    is positive, the number is also preceded by a space; if the number is
    negative, the number is preceded by a minus sign.

    ■ The PRINT statement can be used to print lists of expressions.
    Expressions in the list can be separated from other expressions by
    commas, semicolons, one or more blank spaces, or one or more tab
    characters. A comma causes PRINT to skip to the beginning of the next
    "print zone," or block of 14 columns, on the screen. A semicolon (or any
    combination of spaces and/or tabs) between two expressions prints the
    expressions on the screen next to each other, with no spaces in between
    (except for the built-in spaces for numbers).

    ■ Ordinarily, PRINT ends each line of output with a new-line sequence (a
    carriage-return and line-feed). However, a comma or semicolon at the end
    of the list of expressions suppresses this; the next printed output from
    the program appears on the same line unless it is too long to fit on
    that line.

    ■ PRINT wraps an output line that exceeds the width of the screen onto the
    next line. For example, if you try to print a line that is 100
    characters long on an 80-column screen, the first 80 characters of the
    line show up on one row, followed by the next 20 characters on the next
    row. If the 100-character line didn't start at the left edge of the
    screen (for example, if it followed a PRINT statement ending in a comma
    or semicolon), then the line would print until it reached the 80th
    column of one row and continue in the first column of the next row.

    Example

    The output from the following program shows some of the different ways you
    can use PRINT:

    A = 2
    B = -1
    C = 3
    X$ = "over"
    Y$ = "there"

    PRINT A, B, C
    PRINT B, A, C
    PRINT A; B; C
    PRINT X$; Y$
    PRINT X$, Y$;
    PRINT A, B
    PRINT
    FOR I = 1 TO 8
        PRINT X$,
    NEXT

    Output

    2            -1             3
    -1             2             3
    2 -1  3
    overthere
    over          there 2       -1

    over          over          over          over          over
    over          over          over

3.1.3  Displaying Formatted Output with PRINT USING

    The PRINT USING statement gives greater control than PRINT over the
    appearance of printed data, especially numeric data. Through the use of
    special characters embedded in a format string, PRINT USING allows you to
    specify information such as how many digits from a number (or how many
    characters from a string) are displayed, whether or not a plus (+) sign or
    a dollar sign ($) appears in front of a number, and so forth.

    Example

    The example that follows gives you a sample of what can be done with PRINT
    USING. You can list more than one expression after the PRINT USING format
    string. As is the case with PRINT, the expressions in the list can be
    separated from one another by commas, semicolons, spaces, or tab
    characters.

    X = 441.2318

    PRINT USING "The number with 3 decimal places ###.###";X
    PRINT USING "The number with a dollar sign $$##.##";X
    PRINT USING "The number in exponential format #.###^^^^";X
    PRINT USING "Numbers with plus signs +###  "; X; 99.9

    Output

    The number with 3 decimal places 441.232
    The number with a dollar sign $441.23
    The number in exponential format 0.441E+03
    Numbers with plus signs +441  Numbers with plus signs +100

    Consult the QB Advisor for more on PRINT USING.

3.1.4  Skipping Spaces and Advancing to a Specific Column

    By using the SPC(n) statement in a PRINT statement, you can skip n spaces
    in a row of printed output, as shown by the output from the next example:

    PRINT "         1         2         3"
    PRINT "123456789012345678901234567890"
    PRINT "First Name"; SPC(10); "Last Name"

    Output

    1         2         3
    123456789012345678901234567890
    First Name          Last Name

    By using the TAB(n) statement in a PRINT statement, you can skip to the
    nth column (counting from the left side of the screen) in a row of printed
    output. The following example uses TAB to produce the same output as that
    shown above:

    PRINT "         1         2         3"
    PRINT "123456789012345678901234567890"
    PRINT "First Name"; TAB(21); "Last Name"

    Neither SPC nor TAB can be used by itself to position printed output on
    the screen; they can only appear in PRINT statements.

3.1.5  Changing the Number of Columns or Rows

    You can control the maximum number of characters that appear in a single
    row of output by using the WIDTH columns statement. The WIDTH columns
    statement actually changes the size of characters that are printed on the
    screen, so that more or fewer characters can fit on a row. For example,
    WIDTH 40 makes characters wider, so the maximum row length is 40
    characters. WIDTH 80 makes characters narrower, so the maximum row length
    is 80 characters. The numbers 40 and 80 are the only valid values for the
    columns argument.

    On machines equipped with an Enhanced Graphics Adapter (EGA) or Video
    Graphics Adapter (VGA), the WIDTH statement can also control the number of
    rows that appear on the screen as follows:

    WIDTH[[columns]] [[,rows]]

    The value for rows may be 25, 30, 43, 50, or 60, depending on the type of
    display adapter you use and the screen mode set in a preceding SCREEN
    statement.

3.1.6  Creating a Text Viewport

    So far, the entire screen has been used for text output. However, with the
    VIEW PRINT statement, you can restrict printed output to a "text
    viewport," a horizontal slice of the screen. The syntax of the VIEW PRINT
    statement is:

    VIEW PRINT[[topline TO bottomline]]

    The values for topline and bottomline specify the locations where the
    viewport will begin and end.

    A text viewport also gives you control over on-screen scrolling. Without a
    viewport, once printed output reaches the bottom of the screen, text or
    graphics output that was at the top of the screen scrolls off and is lost.
    However, after a VIEW PRINT statement, scrolling takes place only between
    the top and bottom lines of the viewport. This means you can label the
    displayed output at the top and/or bottom of the screen without having to
    worry that the labeling will scroll it off if too many lines of data
    appear. You can also use the CLS 2 statement to clear just the text
    viewport, leaving the contents of the rest of the screen intact. See
    Section 5.5, "Defining a Graphics Viewport," to learn how to create a
    viewport for graphics output on the screen.

    Example

    You can see the effects of a VIEW PRINT statement by examining the output
    from the next example:

    CLS
    LOCATE 3, 1
    PRINT "This is above the text viewport; it doesn't scroll."

    LOCATE 4, 1
    PRINT STRING$(60, "_")       ' Print horizontal lines above
    LOCATE 11, 1
    PRINT STRING$(60, "_")       ' and below the text viewport.

    PRINT "This is below the text viewport."

    VIEW PRINT 5 TO 10             ' Text viewport extends from
                                    ' lines 5 to 10.

    FOR I = 1 TO 20                ' Print numbers and text in
        PRINT I; "a line of text"   ' the viewport.
    NEXT

    DO: LOOP WHILE INKEY$ = ""     ' Wait for a key press.
    CLS 2                          ' Clear just the viewport.
    END

    Output (Before User Presses Key)

    ┌───────────────────────────────────────────────────────────────────┐
    │                                                                   │
    │                                                                   │
    │  This is above the text viewport: it doesn't scroll.              │
    │  ───────────────────────────────────────────────────────────────  │
    │  16 a line of text                                                │
    │  17 a line of text                                                │
    │  18 a line of text                                                │
    │  19 a line of text                                                │
    │  20 a line of text                                                │
    │                                                                   │
    │                                                                   │
    │  ───────────────────────────────────────────────────────────────  │
    │  This is below the text viewport.                                 │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    └───────────────────────────────────────────────────────────────────┘

    Output (After User Presses Key)

    ┌───────────────────────────────────────────────────────────────────┐
    │                                                                   │
    │                                                                   │
    │   This is above the text viewport: it doesn't scroll.             │
    │   ───────────────────────────────────────────────────────────     │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │   ───────────────────────────────────────────────────────────     │
    │   This is below the text viewport.                                │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    └───────────────────────────────────────────────────────────────────┘


3.2  Getting Input from the Keyboard

    This section shows you how to use the following statements and functions
    to enable your BASIC programs to accept input entered from the keyboard:

    ■ INPUT

    ■ LINE INPUT

    ■ INPUT$

    ■ INKEY$

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Input typed at the keyboard is often referred to as "standard input."
    You can use the DOS symbol < to direct standard input to your program
    from a file or other input device instead of from the keyboard. (See
    your DOS documentation for more information on redirecting input.)
    ──────────────────────────────────────────────────────────────────────────

3.2.1  The INPUT Statement

    The INPUT statement takes information typed by the user and stores it in a
    list of variables, as shown in the following example:

    INPUT A%, B, C$
    INPUT D$
    PRINT A%, B, C$, D$

    Output

    ? 6.6,45,a string
    ? "two, three"
    7             45           a string      two, three

    Here are some general comments about INPUT:

    ■ An INPUT statement by itself prompts the user with a question mark (?)
    followed by a blinking cursor.

    ■ The INPUT statement is followed by one or more variable names. If there
    are more than one variable, they are separated by commas.

    ■ The number of constants entered by the user after the INPUT prompt must
    be the same as the number of variables in the INPUT statement itself.

    ■ The values the user enters must agree in type with the variables in the
    list following INPUT. In other words, enter a number if the variable is
    designated as having the type integer, long integer, single precision,
    or double precision. Enter a string if the variable is designated as
    having the type string.

    ■ Since constants in an input list must be separated by commas, an input
    string constant containing one or more commas should be enclosed in
    double quotes. The double quotes ensure that the string is treated as a
    unit and not broken into two or more parts.

    If the user breaks any of the last three rules, BASIC prints the error
    message Redo from start. This message reappears until the input agrees in
    number and type with the variable list.

    If you want your input prompt to be more informative than a simple
    question mark, you can make a prompt appear, as in the following example:

    INPUT "What is the correct time (hour, min)"; Hr$, Min$

    This prints the following prompt:

    What is the correct time (hour, min)?

    Note the semicolon between the prompt and the input variables. This
    semicolon causes a question mark to appear as part of the prompt.
    Sometimes you may want to eliminate the question mark altogether; in this
    case, put a comma between the prompt and the variable list:

    INPUT "Enter the time (hour, min): ", Hr$, Min$

    This prints the following prompt:

    Enter the time (hour, min):

3.2.2  The LINE INPUT Statement

    If you want your program to accept lines of text with embedded commas,
    leading blanks, or trailing blanks, yet you do not want to have to remind
    the user to enclose the input in double quotes, use the LINE INPUT
    statement. The LINE INPUT statement, as its name implies, accepts a line
    of input (terminated by pressing the ENTER key) from the keyboard and
    stores it in a single string variable. Unlike INPUT, the LINE INPUT
    statement does not print a question mark by default to prompt for input;
    it does, however, allow you to display a prompt string.

    The following example shows the difference between INPUT and LINE INPUT:

    ' Assign the input to three separate variables:
    INPUT "Enter three values separated by commas: ", A$, B$, C$

    ' Assign the input to one variable (commas not treated
    ' as delimiters between input):
    LINE INPUT "Enter the same three values: ", D$

    PRINT "A$ = "; A$
    PRINT "B$ = "; B$
    PRINT "C$ = "; C$
    PRINT "D$ = "; D$

    Output

    Enter 3 values separated by commas: by land, air, and sea
    Enter the same three values: by land, air, and sea
    A$ = by land
    B$ = air
    C$ = and sea
    D$ = by land, air, and sea

    With both INPUT and LINE INPUT, input is terminated when the user presses
    the ENTER key, which also advances the cursor to the next line. As the
    next example shows, a semicolon between the INPUT keyword and the prompt
    string keeps the cursor on the same line:

    INPUT "First value: ", A
    INPUT; "Second value: ", B
    INPUT "    Third value: ", C

    The following shows some sample input to the preceding program and the
    positions of the prompts:

    First value: 5
    Second value: 4     Third value: 3

3.2.3  The INPUT$ Function

    Both INPUT and LINE INPUT wait for the user to press the ENTER key before
    they store what is typed; that is, they read a line of input, then assign
    it to program variables. In contrast, the INPUT$(number) function doesn't
    wait for the enter key to be pressed; it just reads a specified number of
    characters. For example, the following line in a program reads three
    characters typed by the user, then stores the three-character string in
    the variable Test$:

    Test$ = INPUT$(3)

    Unlike the INPUT statement, the INPUT$ function does not prompt the user
    for data, nor does it echo input characters on the screen. Also, since
    INPUT$ is a function, it cannot stand by itself as a complete statement.
    INPUT$ must appear in an expression, as in the following:

    INPUT x              ' INPUT is a statement.

    PRINT INPUT$(1)      ' INPUT$ is a function, so it must
    Y$ = INPUT$(1)       ' appear in an expression.

    The INPUT$ function reads input from the keyboard as an unformatted stream
    of characters. Unlike INPUT or LINE INPUT, INPUT$ accepts any key pressed,
    including control keys like ESC or BACKSPACE. For example, pressing the
    ENTER key five times assigns five carriage-return characters to the Test$
    variable in the next line:

    Test$ = INPUT$(5)

3.2.4  The INKEY$ Function

    The INKEY$ function completes the list of BASIC's keyboard-input functions
    and statements. When BASIC encounters an expression containing the INKEY$
    function, it checks to see if the user has pressed a key since one of the
    following:

    ■ The last time it found an expression with INKEY$

    ■ The beginning of the program, if this is the first time INKEY$ appears

    If no key has been pressed since the last time the program checked, INKEY$
    returns a null string (""). If a key has been pressed, INKEY$ returns the
    character corresponding to that key.

    Example

    The most important difference between INKEY$ and the other statements and
    functions discussed in this section is that INKEY$ lets your program
    continue doing other things while it checks for input. In contrast, LINE
    INPUT, INPUT$, and INPUT suspend program execution until there is input,
    as show in this example:

    PRINT "Press any key to start. Press any key to end."

    ' Don't do anything else until the user presses a key:
    Begin$ = INPUT$(1)

    I = 1

    ' Print the numbers from one to one million.
    ' Check for a key press while the loop is executing:
    DO
        PRINT I
        I = I + 1

    ' Continue looping until the value of the variable I is
    ' greater than one million or until a key is pressed:
    LOOP UNTIL I > 1000000 OR INKEY$ <> ""


3.3  Controlling the Text Cursor

    When you display printed text on the screen, the text cursor marks the
    place on the screen where output from the program──or input typed by the
    user──will appear next. In the next example, after the INPUT statement
    displays its 12-letter prompt, First name:, the cursor waits for input in
    row 1 at column 13:

    ' Clear the screen; start printing in row one, column one:
    CLS
    INPUT "First name: ", FirstName$

    In the next example, the semicolon at the end of the second PRINT
    statement leaves the cursor in row 2 at column 27:

    CLS
    PRINT

    ' Twenty-six characters are in the next line:
    PRINT "Press any key to continue.";
    PRINT INPUT$(1)

    Sections 3.3.1-3.3.3 show how to control the location of the text cursor,
    change its shape, and get information about its location.

3.3.1  Positioning the Cursor

    The input and output statements and functions discussed so far do not
    allow much control over where output is displayed or where the cursor is
    located after the output is displayed. Input prompts or output always
    start in the far left column of the screen and descend one row at a time
    from top to bottom unless a semicolon is used in the PRINT or INPUT
    statements to suppress the carriage-return and line-feed sequence.

    The SPC and TAB statements, discussed in Section 3.1.4, "Skipping Spaces
    and Advancing to a Specific Column," give some control over the location
    of the cursor by allowing you to move it to any column within a given row.

    The LOCATE statement extends this control one step further. The syntax for
    LOCATE is

    LOCATE[[row]][[,[[column]][[,[[cursor]][[,[[start]][[,stop]]]]]]]]

    Using the LOCATE statement allows you to position the cursor in any row or
    column on the screen, as shown by the output from the next example:

    CLS
    FOR Row = 9 TO 1 STEP -2
        Column = 2 * Row
        LOCATE Row, Column
        PRINT "12345678901234567890";
    NEXT

    Output

    12345678901234567890

        12345678901234567890

            12345678901234567890

                12345678901234567890

                    12345678901234567890

3.3.2  Changing the Cursor's Shape

    The optional cursor, start, and stop arguments shown in the syntax for the
    LOCATE statement also allow you to change the shape of the cursor and make
    it visible or invisible. A value of 1 for cursor makes the cursor visible,
    while a value of 0 makes the cursor invisible. The start and stop
    arguments control the height of the cursor, if it is on, by specifying the
    top and bottom "pixel" lines, respectively, for the cursor. (Any character
    on the screen is composed of lines of pixels, which are dots of light on
    the screen.) If a cursor spans the height of one row of text, then the
    line of pixels at the top of the cursor has the value 0, while the line of
    pixels at the bottom has a value of 7 or 13, depending on the type of
    display adapter you have. (For monochrome the value is 13; for color it is
    7.)

    You can turn the cursor on and change its shape without specifying a new
    location for it. For example, the following statement keeps the cursor
    wherever it is at the completion of the next PRINT or INPUT statement,
    then makes it half a character high:

    LOCATE , , 1, 2, 5    ' Row and column arguments both optional

    The following examples show different cursor shapes produced using
    different start and stop values on a color display. Each LOCATE statement
    shown in the left column is followed by the statement

    INPUT "PROMPT:", X$

    Statement                            Input Prompt and Cursor Shape
    ──────────────────────────────────────────────────────────────────────────
    LOCATE, , 1, 0, 7                    PROMPT:█

    LOCATE, , 1, 0, 3                    PROMPT:▀

    LOCATE, , 1, 4, 7                    PROMPT:▬

    LOCATE, , 1, 6, 2                    PROMPT:(see page 94 of printed
                                        manual)
    ──────────────────────────────────────────────────────────────────────────

    In the preceding examples, note that making the start argument bigger than
    the stop argument results in a two-piece cursor.

3.3.3  Getting Information about the Cursor's Location

    You can think of the functions CSRLIN and POS(n) as the inverses of the
    LOCATE statement: whereas LOCATE tells the cursor where to go, CSRLIN and
    POS(n) tell your program where the cursor is. The CSRLIN function returns
    the current row and the POS(n) function returns the current column of the
    cursor's position.

    The argument n for POS(n) is what is known as a "dummy" argument; that is,
    n is a placeholder that can be any numeric expression. For example, POS(0)
    and POS(1) both return the same value.

    Example

    The following example uses the POS(n) function to print 50 asterisks in
    rows of 13 asterisks:

    FOR I% = 1 TO 50
        PRINT "*";                  ' Print an asterisk and keep
                                    ' the cursor on the same line.
        IF POS(1) > 13 THEN PRINT   ' If the cursor's position
                                    ' is past column 13, advance
                                    ' to the next line.
    NEXT

    Output

    *************
    *************
    *************
    ***********


3.4  Working with Data Files

    Data files are physical locations on your disk where information is
    permanently stored. The following three tasks are greatly simplified by
    using data files in your BASIC programs:

    1. Creating, manipulating, and storing large amounts of data

    2. Accessing several sets of data with one program

    3. Using the same set of data in several different programs

    The sections that follow introduce the concepts of records and fields and
    contrast different ways to access data files from BASIC. When you have
    completed Sections 3.4.1-3.4.7, you should know how to do the following:

    ■ Create new data files

    ■ Open existing files and read their contents

    ■ Add new information to an existing data file

    ■ Change the contents of an existing data file

3.4.1  How Data Files Are Organized

    A data file is a collection of related blocks of information, or
    "records." Each record in a data file is further subdivided into "fields"
    or regularly recurring items of information within each record. If you
    compare a data file with a more old-fashioned way of storing information──
    for example, a folder containing application forms filled out by job
    applicants at a particular company──then a record is analogous to one
    application form in that folder. To carry the comparison one step further,
    a field is analogous to an item of information included on every
    application form, such as a Social Security number.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    If you do not want to access a file using records but instead want to
    treat it as an unformatted sequence of bytes, then read Section 3.4.7,
    "Binary File I/O."
    ──────────────────────────────────────────────────────────────────────────

3.4.2  Sequential and Random-Access Files

    The terms "sequential file" and "random-access file" refer to two
    different ways to store and access data on disk from your BASIC programs.
    A simplified way to think of these two kinds of files is with the
    following analogy: a sequential file is like a cassette tape, while a
    random-access file is like an LP record. To find a song on a cassette
    tape, you have to start at the beginning and fast-forward through the tape
    sequentially until you find the song you are looking for──there is no way
    to jump right to the song you want. This is similar to the way you have to
    find information in a sequential file: to get to the 500th record, you
    first have to read records 1 through 499.

    In contrast, if you want to play a certain song on an LP, all you have to
    do is lift the tone arm of the record player and put the needle down right
    on the song: you can randomly access anything on the LP without having to
    play all the songs before the one you want. Similarly, you can call up any
    record in a random-access file just by specifying its number, greatly
    reducing access time.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Although there is no way to jump directly to a specific record in a
    sequential file, the SEEK statement lets you jump directly to a specific
    byte in the file (to "fast forward" or "rewind" the tape, to extend the
    preceding analogy). See Section 3.4.7, "Binary File I/O," for more
    information on how to do this.
    ──────────────────────────────────────────────────────────────────────────

3.4.3  Opening a Data File

    Before your program can read, modify, or add to a data file, it must first
    open the file. BASIC does this with the OPEN statement. The OPEN statement
    can be used to create a new file. The following list describes the various
    uses of the OPEN statement:

    ■ Create a new data file and open it so records can be added to it

    ' No file named PRICE.DAT is in the current directory:
    OPEN "Price.Dat" FOR OUTPUT AS #1

    ■ Open an existing data file so new records overwrite any data already in
    the file

    ' A file named PRICE.DAT is already in the current
    ' directory; new records can be written to it, but all
    ' old records are lost:
    OPEN "Price.Dat" FOR OUTPUT AS #1

    ■ Open an existing data file so new records are added to the end of the
    file, preserving data already in the file

    OPEN "Price.Dat" FOR APPEND AS #1

    The APPEND mode will also create a new file if a file with the given
    name does not already appear in the current directory.

    ■ Open an existing data file so old records can be read from it

    OPEN "Price.Dat" FOR INPUT AS #1

    See Section 3.4.5, "Using Sequential Files," for more information about
    the INPUT, OUTPUT, and APPEND modes.

    ■ Open an existing data file (or create a new one if a file with that name
    doesn't exist), then read or write fixed-length records to and from the
    file

    OPEN "Price.Dat" FOR RANDOM AS #1

    See Section 3.4.6, "Using Random-Access Files," for more information
    about this mode.

    ■ Open an existing data file (or create a new one if a file with that name
    doesn't exist), then read data from the file or add new data to the
    file, starting at any byte position in the file

    OPEN "Price.Dat" FOR BINARY AS #1

    See Section 3.4.7, "Binary File I/O," for more information about this
    mode.

    3.4.3.1  File Numbers in BASIC

    The OPEN statement does more than just specify a mode for data I/O for a
    particular file (OUTPUT, INPUT, APPEND, RANDOM, or BINARY); it also
    associates a unique file number with that file. This file number, which
    can be any integer from 1 to 255, is then used by subsequent file I/O
    statements in the program as a shorthand way to refer to the file. As long
    as the file is open, this number remains associated with the file. When
    the file is closed, the file number is freed for use with another file.
    (See Section 3.4.4 for information on how files are closed.) Your BASIC
    programs can open more than one file at a time.

    The FREEFILE function can help you find an unused file number. This
    function returns the next available number that can be associated with a
    file in an OPEN statement. For example, FREEFILE might return the value 3
    after the following OPEN statements:

    OPEN "Test1.Dat" FOR RANDOM AS #1
    OPEN "Test2.Dat" FOR RANDOM AS #2
    FileNum = FREEFILE
    OPEN "Test3.Dat" FOR RANDOM AS #FileNum

    The FREEFILE function is particularly useful when you create your own
    library procedures that open files. With FREEFILE, you don't have to pass
    information about the number of open files to these procedures.

    3.4.3.2  File Names in BASIC

    File names in OPEN statements can be any string expression, composed of
    any combination of the following characters:

    ■ The letters a-z and A-Z

    ■ The numbers 0-9

    ■ The following special characters:

    ( ) { } @ # $ % ^ & ! - _ ' ~

    The string expression can also contain an optional drive specification, as
    well as a complete or partial path specification. This means your BASIC
    program can work with data files on another drive or in a directory other
    than the one where the program is running. For example, the following OPEN
    statements are all valid:

    OPEN "..\Grades.Qtr" FOR INPUT AS #1

    OPEN "a:\salaries\1987.man" FOR INPUT AS #2

    FileName$ = "TempFile"
    OPEN FileName$ FOR OUTPUT AS #3

    BaseName$ = "Invent"
    OPEN BaseName$ + ".DAT" FOR OUTPUT AS #4

    DOS also imposes its own restrictions on file names: you can use no more
    than eight characters for the base name (everything to the left of an
    optional period) and no more than three characters for the extension
    (everything to the right of an optional period). Long file names in BASIC
    programs are truncated in the following fashion:

    File Name in Program     Resulting File Name in DOS
    ──────────────────────────────────────────────────────────────────────────
    Prog@Data@File           PROG@DAT.A@F
                            The BASIC name is more than 11 characters long,
                            so BASIC takes the first 8 letters for the base
                            name, inserts a period (.), and uses the next 3
                            letters as the extension. Everything else is
                            discarded.

    Mail#.Version1           MAIL#.VER
                            The base name (Mail#) is shorter than eight
                            characters, but the extension (Version1) is
                            longer than three, so the extension is shortened
                            to three characters.

    RELEASE_NOTES.BAK        Gives the run-time error message Bad file name.
                            The base name must be shorter than eight
                            characters if you are going to include an
                            explicit extension (.BAK in this case).
    ──────────────────────────────────────────────────────────────────────────

    DOS is not case sensitive, so lowercase letters in file names are
    converted to all uppercase (capital) letters. Therefore, you should not
    rely on the mixing of lowercase and uppercase to distinguish between
    files. For example, if you already had a file on the disk named
    INVESTOR.DAT, the following OPEN statement would overwrite that file,
    destroying any information already stored in it:

    OPEN "Investor.Dat" FOR OUTPUT AS #1

3.4.4  Closing a Data File

    Closing a data file has two important results: first, it writes any data
    currently in the file's buffer (a temporary holding area in memory) to the
    file; second, it frees the file number associated with that file for use
    by another OPEN statement.

    Use the CLOSE statement within a program to close a file. For example, if
    the file Price.Dat is opened with the statement

    OPEN "Price.Dat" FOR OUTPUT AS #1

    then the statement CLOSE #1 ends output to Price.Dat. If Price.Dat is
    opened with

    OPEN "Price.Dat" FOR OUTPUT AS #2

    then the appropriate statement for ending output is CLOSE #2. A CLOSE
    statement with no file-number arguments closes all open files.

    A data file is also closed when either of the following occurs:

    ■ The BASIC program performing I/O ends (program termination always closes
    all open data files).

    ■ The program performing I/O transfers control to another program with the
    RUN statement.

3.4.5  Using Sequential Files

    This section discusses how records are organized in sequential data files
    and then shows how to read data from, or write data to, an open sequential
    file.

    3.4.5.1  Records in Sequential Files

    Sequential files are ASCII (text) files. This means you can use any word
    processor to view or modify a sequential file. Records are stored in
    sequential files as a single line of text, terminated by a carriage-return
    and line-feed (CR-LF) sequence. Each record is divided into fields, or
    repeated chunks of data that occur in the same order in every record.
    Figure 3.2 shows how three records might appear in a sequential file.


    ┌──────────────────────────────────────────────────────────────────┐
    │  Record 1                                                        │
    │  ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐                     │
    │  │"│M│c│G│u│i│r│e│"│,│"│M│a│n│a│g│e│r│"│,│5│                     │
    │  └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴───┘                     │
    │  └────────┬────────┘ └────────┬────────┘ └┬┘                     │
    │        Field 1             Field 2     Field 3                   │
    │                                                                  │
    │  Record 2                                                        │
    │  ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐                             │
    │  │"│R│o│s│s│"│,│"│E│d│i│t│o│r│"│,│7│                             │
    │  └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘                             │
    │  └─────┬─────┘ └───────┬───────┘ └┬┘                             │
    │     Field 1         Field 2     Field 3                          │
    │                                                                  │
    │  Record 3                                                        │
    │  ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐           │
    │  │"│S│h│o│s│t│a│k│o│v│i│c│h│"│,│"│W│r│i│t│e│r│"│,│1│2│           │
    │  └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘           │
    │  └─────────────┬─────────────┘ └───────┬───────┘ └─┬─┘           │
    │             Field 1                 Field 2     Field 3          │
    └──────────────────────────────────────────────────────────────────┘

    Figure 3.2  Records in Sequential Files

    Note that each record in a sequential file can be a different length;
    moreover, fields can be different lengths in different records.

    The kind of variable in which a field is stored determines where that
    field begins and ends. (See Sections 3.4.5.2-3.4.5.6 for examples of
    reading and storing fields from records.) For example, if your program
    reads a field into a string variable, then any of the following can signal
    the end of that field:

    ■ Double quotes (") if the string begins with double quotes

    ■ Comma (,) if the string does not begin with double quotes

    ■ CR-LF if the field is at the end of the record

    On the other hand, if your program reads a field into a numeric variable,
    then any of the following can signal the end of that field:

    ■ Comma

    ■ One or more spaces

    ■ CR-LF

    3.4.5.2  Putting Data in a New Sequential File

    You can add data to a new sequential file after first opening it to
    receive records with an OPEN filename FOR OUTPUT statement. Use the WRITE
    # statement to write records to the file.

    You can open sequential files for reading or for writing but not for both
    at the same time. If you are writing to a sequential file and want to read
    back the data you stored, you must first close the file, then reopen it
    for input.

    Example

    The following short program creates a sequential file named Price.Dat,
    then adds data entered at the keyboard to the file. The OPEN statement in
    this program both creates the file and readies it to receive records. The
    WRITE # then writes each record to the file. Note that the number used in
    the WRITE # statement is the same number given to the file name Price.Dat
    in the OPEN statement.

    ' Create a file named Price.Dat
    ' and open it to receive new data:

    OPEN "Price.Dat" FOR OUTPUT AS #1

    DO
        ' Continue putting new records in Price.Dat until the
        ' user presses ENTER without entering a company name:
        INPUT "Company (press <ENTER> to quit): ", Company$

        IF Company$ <> "" THEN

        ' Enter the other fields of the record:
        INPUT "Style: ", Style$
        INPUT "Size: ", Size$
        INPUT "Color: ", Clr$
        INPUT "Quantity: ", Qty

        ' Put the new record in the file
        ' with the WRITE # statement:
        WRITE #1, Company$, Style$, Size$, Clr$, Qty
        END IF
    LOOP UNTIL Company$ = ""

    ' Close Price.Dat (this ends output to the file):
    CLOSE #1
    END

    ──────────────────────────────────────────────────────────────────────────
    WARNING
    If, in the preceding example, you already had a file named Price.Dat on
    the disk, the OUTPUT mode given in the OPEN statement would erase the
    existing contents of Price.Dat before writing any new data to it. If you
    want to add new data to the end of an existing file without erasing what
    is already in it, use the APPEND mode of OPEN. See Section 3.4.5.4,
    "Adding Data to a Sequential File," for more information on this mode.
    ──────────────────────────────────────────────────────────────────────────

    3.4.5.3  Reading Data from a Sequential File

    You can read data from a sequential file after first opening it with the
    statement OPEN filename FOR INPUT. Use the INPUT # statement to read
    records from the file one field at a time. (See Section 3.4.5.6, "Other
    Ways to Read Data from a Sequential File," for information on other
    file-input statements and functions you can use with a sequential file.)

    Example

    The following program opens the Price.Dat data file created in the
    previous example and reads the records from the file, displaying the
    complete record on the screen if the quantity for the item is less than
    the input amount.

    The INPUT #1 statement reads one record at a time from Price.Dat,
    assigning the fields in the record to the variables Company$, Style$,
    Size$, Clr$, and Qty. Since this is a sequential file, the records are
    read in order from the first one entered to the last one entered.

    The EOF (end-of-file) function tests whether the last record has been read
    by INPUT #. If the last record has been read, EOF returns the value -1
    (true), and the loop for getting data ends; if the last record has not
    been read, EOF returns the value 0 (false), and the next record is read
    from the file.

    OPEN "Price.Dat" FOR INPUT AS #1

    INPUT "Display all items below what level"; Reorder

    DO UNTIL EOF(1)
        INPUT #1, Company$, Style$, Size$, Clr$, Qty
        IF Qty < Reorder THEN
        PRINT  Company$, Style$, Size$, Clr$, Qty
        END IF
    LOOP
    CLOSE #1
    END

    3.4.5.4  Adding Data to a Sequential File

    As mentioned earlier, if you have a sequential file on disk and want to
    add more data to the end of it, you cannot simply open the file in output
    mode and start writing data. As soon as you open a sequential file in
    output mode, you destroy its current contents. You must use the append
    mode instead, as shown in the next example:

    OPEN "Price.Dat" FOR APPEND AS #1

    In fact, APPEND is always a safe alternative to OUTPUT, since the append
    mode creates a new file if one with the name specified doesn't already
    exist. For example, if a file named Price.Dat did not reside on disk, the
    example statement above would make a new file with that name.

    3.4.5.5  Other Ways to Write Data to a Sequential File

    The preceding examples all use the WRITE # statement to write records to a
    sequential file. There is, however, another statement you can use to write
    sequential file records: PRINT #.

    The best way to show the difference between these two data-storage
    statements is to examine the contents of a file created with both. The
    following short fragment opens a file named Test.Dat then places the same
    record in it twice, once with WRITE # and once with PRINT #. After running
    this program you can examine the contents of Test.Dat with the DOS TYPE
    commands:

    OPEN "Test.Dat" FOR OUTPUT AS #1
    Nm$ = "Penn, Will"
    Dept$ = "User Education"
    Level = 4
    Age = 25
    WRITE #1, Nm$, Dept$, Level, Age
    PRINT #1, Nm$, Dept$, Level, Age
    CLOSE #1

    Output

    "Penn, Will","User Education",4,25
    Penn, Will    User Education               4             25

    The record stored with WRITE # has commas that explicitly separate each
    field of the record, as well as quotes enclosing each string expression.
    On the other hand, PRINT # has written an image of the record to the file
    exactly as it would appear on screen with a simple PRINT statement. The
    commas in the PRINT # statement are interpreted as meaning "advance to the
    next print zone" (a new print zone occurs every 14 spaces, starting at the
    beginning of a line), and quotes are not placed around the string
    expressions.

    At this point, you may be wondering what difference these output
    statements make, except in the appearance of the data within the file. The
    answer lies in what happens when your program reads the data back from the
    file with an INPUT # statement. In the following example, the program
    reads the record stored with WRITE # and prints the values of its fields
    without any problem:

    OPEN "Test.Dat" FOR INPUT AS #1

    ' Input the first record,
    ' and display the contents of each field:
    INPUT #1, Nm$, Dept$, Level, Age
    PRINT Nm$, Dept$, Level, Age

    ' Input the second record,
    ' and display the contents of each field:
    INPUT #1, Nm$, Dept$, Level, Age
    PRINT Nm$, Dept$, Level, Age

    CLOSE #1

    Output

    Penn, Will    User Education               4             25

    However, when the program tries to input the next record stored with PRINT
    #, it produces the error message Input past end of file. Without double
    quotes enclosing the first field, the INPUT # statement sees the comma
    between Penn and Will as a field delimiter, so it assigns only the last
    name Penn to the variable Nm$. INPUT # then reads the rest of the line
    into the variable Dept$. Since all of the record has now been read, there
    is nothing left to put in the variables level and age. The result is the
    error message Input past end of file.

    If you are storing records that have string expressions and you want to
    read these records later with the INPUT # statement, follow one of these
    two rules of thumb:

    1. Use the WRITE # statement to store the records.

    2. If you want to use the PRINT # statement, remember it does not put
        commas in the record to separate fields, nor does it put quotes around
        strings. You have to put these field separators in the PRINT #
        statement yourself.

    For example, you can avoid the problems shown in the preceding example by
    using PRINT # with quotation marks surrounding each field containing a
    string expression, as in the example below.

    Example

    ' 34 is ASCII value for double-quote character:

    Q$ = CHR$(34)

    ' The next four statements all write the record to the
    ' file with double quotes around each string field:

    PRINT #1, Q$ Nm$ Q$ Q$ Dept$ Q$ Level Age
    PRINT #1, Q$ Nm$ Q$;Q$ Dept$ Q$;Level;Age
    PRINT #1, Q$ Nm$ Q$,Q$ Dept$ Q$,Level,Age
    WRITE #1, Nm$, Dept$, Level, Age

    Output to File

    "Penn, Will""User Education" 4  25
    "Penn, Will""User Education" 4  25
    "Penn, Will"  "User Education"      4             25
    "Penn, Will","User Education",4,25

    3.4.5.6  Other Ways to Read Data from a Sequential File

    In the preceding sections, INPUT # is used to read a record (one line of
    data from a file), assigning different fields in the record to the
    variables listed after INPUT #. This section explores alternative ways to
    read data from sequential files, both as records (LINE INPUT #) and as
    unformatted sequences of bytes (INPUT$).

The LINE INPUT # Statement

    With the LINE INPUT # statement, your program can read a line of text
    exactly as it appears in a file without interpreting commas or quotes as
    field delimiters. This is particularly useful in programs that work with
    ASCII text files.

    The LINE INPUT # statement reads an entire line from a sequential file (up
    to a carriage-return and line-feed sequence) into a single string
    variable.

    Examples

    The following short program reads each line from the file Chap1.Txt and
    then echoes that line to the screen:

    ' Open Chap1.Txt for sequential input:
    OPEN "Chap1.Txt" FOR INPUT AS #1

    ' Keep reading lines sequentially from the file until
    ' there are none left in the file:

    DO UNTIL EOF(1)

        ' Read a line from the file and store it
        ' in the variable LineBuffer$:
        LINE INPUT #1, LineBuffer$

        ' Print the line on the screen:
        PRINT LineBuffer$
    LOOP

    The preceding program is easily modified to a file-copying utility that
    prints each line read from the specified input file to another file,
    instead of to the screen:

    ' Input names of input and output files:

    INPUT "File to copy: ", FileName1$
    IF FileName1$ = "" THEN END
    INPUT "Name of new file: ", FileName2$
    IF FileName2$ = "" THEN END

    ' Open first file for sequential input:
    OPEN FileName1$ FOR INPUT AS #1

    ' Open second file for sequential output:
    OPEN FileName2$ FOR OUTPUT AS #2

    ' Keep reading lines sequentially from first file
    ' until there are none left in the file:
    DO UNTIL EOF(1)

        ' Read a line from first file and store it in the
        ' variable LineBuffer$:
        LINE INPUT #1, LineBuffer$

        ' Write LineBuffer$ to the second file:
        PRINT #2, LineBuffer$

    LOOP

The INPUT$ Function

    Yet another way to read data from sequential files (and, in fact, from any
    file) is to use the INPUT$ function. Whereas INPUT # and LINE INPUT # read
    a line at a time from a sequential file, INPUT$ reads a specified number
    of characters from a file, as shown in the following examples:

    Statement                Action
    ──────────────────────────────────────────────────────────────────────────
    X$ = INPUT$(100, #1)     Reads 100 characters from file number 1 and
                            assigns all of them to the string variable X$

    Test$ = INPUT$(1, #2)    Reads one character from file number 2 and
                            assigns it to the string variable Test$

    ──────────────────────────────────────────────────────────────────────────

    The INPUT$ function without a file number always reads input from standard
    input (usually the keyboard).

    The INPUT$ function does what is known as "binary input"; that is, it
    reads a file as an unformatted stream of characters. For example, it does
    not see a carriage-return and line-feed sequence as signaling the end of
    an input operation. Therefore, INPUT$ is the best choice when you want
    your program to read every single character from a file or when you want
    it to read a binary, or non-ASCII, file.

    Example

    The following program copies the named binary file to the screen, printing
    only alphanumeric and punctuation characters in the ASCII range 32 to 126,
    as well as tabs, carriage returns, and line feeds:

    ' 9 is ASCII value for horizontal tab, 10 is ASCII
    value
    ' for line feed, and 13 is ASCII value for carriage return:
    CONST LINEFEED = 10, CARRETURN = 13, TABCHAR = 9

    INPUT "Print which file: ", FileName$
    IF FileName$ = "" THEN END

    OPEN FileName$ FOR INPUT AS #1

    DO UNTIL EOF(1)
        Character$ = INPUT$(1, #1)
        CharVal = ASC(Character$)
        SELECT CASE CharVal
        CASE 32 TO 126
            PRINT Character$;
        CASE TABCHAR, CARRETURN
            PRINT Character$;
        CASE LINEFEED
            IF OldCharVal <> CARRETURN THEN PRINT Character$;
        CASE ELSE
            ' This is not one of the characters this program
            ' is interested in, so don't print anything.
        END SELECT
        OldCharVal = CharVal
    LOOP

3.4.6  Using Random-Access Files

    This section discusses how records are organized in random-access data
    files, then shows you how to read data from and write data to a file
    opened for random access.

    3.4.6.1  Records in Random-Access Files

    Random-access records are stored quite differently from sequential
    records. Each random-access record is defined with a fixed length, as is
    each field within the record. These fixed lengths determine where a record
    or field begins and ends, as there are no commas separating fields, and no
    carriage-return and line-feed sequences between records. Figure 3.3 shows
    how three records might appear in a random-access file.


                    Record 1                               Record 2
    ┌──────────────────┴──────────────────┐┌─────────────────┴─────────────────
    ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬
    │M│c│G│u│i│r│e│ │ │ │M│a│n│a│g│e│r│5│ │R│o│s│s│ │ │ │ │ │ │E│d│i│t│o│r│ │3│
    └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴
    └─────────┬─────────┘└─────┬──────┘└─┬┘└────────┬─────────┘└─────┬──────┘└─
            Field 1          Field 2   Field 3     Field 1         Field 2   Fie

    Figure 3.3  Records in a Random-Access File

    If you are storing records containing numbers, using random-access files
    saves disk space when compared with using sequential files. This is
    because sequential files save numbers as a sequence of ASCII characters
    representing each digit, whereas random-access files save numbers in a
    compressed binary format.

    For example, the number 17,000 is represented in a sequential file using
    five bytes, one for each digit. However, if 17,000 is stored in an integer
    field of a random-access record, it takes only two bytes of disk space.

    In general, integers in random-access files take two bytes, long integers
    and single-precision numbers take four bytes, and double-precision numbers
    take eight bytes.

    3.4.6.2  Adding Data to a Random-Access File

    To write a program that adds data to a random-access file, follow these
    steps:

    1. Define the fields of each record.

    2. Open the file in random-access mode and specify the length of each
        record.

    3. Get input for a new record and store the record in the file.

    Each of these steps is now considerably easier than it was in BASICA, as
    you can see from the examples that follow.

Defining Fields

    You can define your own record with a TYPE...END TYPE statement, which
    allows you to create a composite data type that mixes string and numeric
    elements. This is a big advantage over the earlier method of setting up
    records with a FIELD statement, which required that each field be defined
    as a string. By defining a record with TYPE...END TYPE, you eliminate the
    need to use the functions that convert numeric data to strings (MKtype$,
    MKSMBF$, and MKDMBF$) and strings to numeric data (CVtype, CVSMBF, and
    CVDMBF).

    The following fragments contrast these two methods of defining records:

    ■ Record defined with TYPE...END TYPE

    ' Define the RecordType structure:
    TYPE RecordType
        Name AS STRING * 30
        Age AS INTEGER
        Salary AS SINGLE
    END TYPE

    ' Declare the variable RecordVar
    ' as having the type RecordType:
    DIM RecordVar AS RecordType

    .
    .
    .

    ■ Record defined with FIELD

    ' Define the lengths of the fields
    ' in the temporary storage buffer:
    FIELD #1,30 AS Name$,2 AS Age$,4 AS Salary$
    .
    .
    .

Opening the File and Specifying Record Length

    Since the length of a random-access record is fixed, you should let your
    program know how long you want each record to be; otherwise, record length
    defaults to 128 bytes.

    To specify record length, use the LEN = clause in the OPEN statement. The
    next two fragments, which continue the contrasting examples started above,
    show how to use LEN =.

    ■ Specifying the record length for a record that is defined with the
    statement TYPE...END TYPE

    .
    .
    .
    ' Open the random-access file and specify the length
    ' of one record as being equal to the length of the
    ' RecordVar variable:
    OPEN "EMPLOYEE.DAT" FOR RANDOM AS #1 LEN = LEN(RecordVar)
    .
    .
    .

    ■ Specifying record length for a record defined with FIELD

    .
    .
    .
    ' Open the random-access file and specify the length
    ' of a record:
    OPEN "EMPLOYEE.DAT" FOR RANDOM AS #1 LEN = 31
    .
    .
    .

    As you can see, when you use FIELD, you have to add the lengths of each
    field yourself (FirstName$ is 10 bytes, LastName$ is 15 bytes, Age$ is 2
    bytes, Salary$ is 4 bytes, so the record is 10+15+2+4 or 31 bytes long).
    With TYPE...END TYPE, you no longer have to do these calculations.
    Instead, just use the LEN function to calculate the length of the variable
    you have created to hold your records (RecordVar, in this case).

Inputting Data and Storing the Record

    You can input data directly into the elements of a user-defined record
    without having to worry about left- or right-justification of input within
    a field with LSET or RSET. Compare the following two fragments, which
    continue the examples started above, to see the amount of code this
    approach saves you.

    ■ Entering data for a random-access record and storing the record using
    TYPE...END TYPE.

    .
    .
    .

    ' Enter the data:
    INPUT "First name"; RecordVar.FirstName
    INPUT "Last name"; RecordVar.LastName
    INPUT "Age"; RecordVar.Age
    INPUT "Salary"; RecordVar.Salary
    ' Store the record:
    PUT #1, , RecordVar

    .
    .
    .

    ■ Entering data for a random-access record and storing the record using
    FIELD

    .
    .
    .

    ' Enter the data:
    INPUT "First name"; FirstNm$
    INPUT "Last name"; LastNm$
    INPUT "Age"; AgeVal%
    INPUT "Salary"; SalaryVal!

    ' Left justify the data in the storage-buffer fields,
    ' using the MKI$ and MKS$ functions to convert numbers
    ' to file strings:
    LSET FirstName$ = FirstNm$
    LSET LastName$ = LastNm$
    LSET Age$ = MKI$(AgeVal%)
    LSET Salary$ = MKS$(SalaryVal!)

    ' Store the record:
    PUT #1

    .
    .
    .

Putting It All Together

    The following program puts together all the steps outlined above──defining
    fields, specifying record length, inputting data, and storing the input
    data──to open a random-access data file named STOCK.DAT and add records to
    it:

    DEFINT A-Z

    ' Define structure of a single record in the random-access
    ' file. Each record will consist of four fixed-length fields
    ' ("PartNumber", "Description", "UnitPrice", "Quantity"):
    TYPE StockItem
        PartNumber AS STRING *  6
        Description AS STRING * 20
        UnitPrice AS SINGLE
        Quantity AS INTEGER
    END TYPE

    ' Declare a variable (StockRecord) using the above type:
    DIM StockRecord AS StockItem

    ' Open the random-access file, specifying the length of one
    ' record as the length of the StockRecord variable:
    OPEN "STOCK.DAT" FOR RANDOM AS #1 LEN=LEN(StockRecord)

    ' Use LOF() to calculate the number of records already in
    ' the file, so new records will be added after them:
    RecordNumber = LOF(1) \ LEN(StockRecord)

    ' Now, add new records:
    DO

        ' Input data for a stock record from keyboard and store
        ' in the different elements of the StockRecord variable:
        INPUT "Part Number? ", StockRecord.PartNumber
        INPUT "Description? ", StockRecord.Description
        INPUT "Unit Price ? ", StockRecord.UnitPrice
        INPUT "Quantity   ? ", StockRecord.Quantity

        RecordNumber = RecordNumber + 1

        ' Write data in StockRecord to a new record in the file:
        PUT #1, RecordNumber, StockRecord



        ' Check to see if more data are to be read:
        INPUT "More (Y/N)? ", Resp$
    LOOP UNTIL UCASE$(Resp$) = "N"

    ' All done; close the file and end:
    CLOSE  #1
    END

    If the STOCK.DAT file already existed, this program would add more records
    to the file without overwriting any that were already in the file. The
    following key statement makes this work:

    RecordNumber = LOF(1) \ LEN(StockRecord)

    Here is what happens:

    1. The LOF(1) function calculates the total number of bytes in the file
        STOCK.DAT. If STOCK.DAT is new or has no records in it, LOF(1) returns
        the value 0.

    2. The LEN(StockRecord) function calculates the number of bytes in one
        record. (StockRecord is defined as having the same structure as the
        user-defined type StockItem.)

    3. Therefore, the number of records is equal to the total bytes in the
        file divided by the bytes in one record.

    This is another advantage of having a fixed-length record. Since each
    record is the same size, you can always use the above formula to calculate
    the number of records in the file. Obviously, this would not work with a
    sequential file, since sequential records can have different lengths.

    3.4.6.7  Reading Data Sequentially

    Using the technique outlined in the preceding section for calculating the
    number of records in a random-access file, you can write a program that
    reads all the records in that file.

    Example

    The following program reads records sequentially (from the first record
    stored to the last) from the STOCK.DAT file created in the previous
    section:

    ' Define a record structure (type) for random-access
    ' file records:
    TYPE StockItem
        PartNumber AS STRING *  6
        Description AS STRING * 20
        UnitPrice AS SINGLE
        Quantity AS INTEGER
    END TYPE

    ' Declare a variable (StockRecord) using the above type:
    DIM StockRecord AS StockItem

    ' Open the random-access file:
    OPEN "STOCK.DAT" FOR RANDOM AS #1 LEN = LEN(StockRecord)

    ' Calculate number of records in the file:
    NumberOfRecords = LOF(1) \ LEN(StockRecord)

    ' Read the records and write the data to the screen:
    FOR RecordNumber = 1 TO NumberOfRecords

        ' Read the data from a new record in the file:
        GET #1, RecordNumber, StockRecord



        ' Print the data to the screen:
        PRINT "Part Number: ", StockRecord.PartNumber
        PRINT "Description: ", StockRecord.Description
        PRINT "Unit Price : ", StockRecord.UnitPrice
        PRINT "Quantity   : ", StockRecord.Quantity

    NEXT

    ' All done; close the file and end:
    CLOSE #1
    END

    It is not necessary to close STOCK.DAT before reading from it. Opening a
    file for random access lets you write to or read from the file with a
    single OPEN statement.

    3.4.6.8  Using Record Numbers to Retrieve Records

    You can read any record from a random-access file by specifying the
    record's number in a GET statement. You can write to any record in a
    random-access file by specifying the record's number in a PUT statement.
    This is one of the major advantages that random-access files have over
    sequential files, since sequential files do not permit direct access to a
    specific record.

    The sample-application program INDEX.BAS, listed in Section 3.6.2, shows
    a technique that quickly finds a particular record by searching an index
    of record numbers.

    Example

    The following fragment shows how to use GET with a record number:

    DEFINT A-Z           ' Default variable type is integer.
    CONST FALSE = 0, TRUE = NOT FALSE

    TYPE StockItem
        PartNumber AS STRING *  6
        Description AS STRING * 20
        UnitPrice AS SINGLE
        Quantity AS INTEGER
    END TYPE

    DIM StockRecord AS StockItem

    OPEN "STOCK.DAT" FOR RANDOM AS #1 LEN=LEN(StockRecord)

    NumberOfRecords = LOF(1) \ LEN(StockRecord)
    GetMoreRecords = TRUE

    DO
        PRINT "Enter record number for part you want to see ";
        PRINT "(0 to end): ";
        INPUT "", RecordNumber

        IF RecordNumber > 0 AND RecordNumber < NumberOfRecords THEN

        ' Get the record whose number was entered and store
        ' it in StockRecord:
        GET #1, RecordNumber, StockRecord

        ' Display the record:
        .
        .
        .
        ELSEIF RecordNumber = 0 THEN
        GetMoreRecords = FALSE
        ELSE
        PRINT "Input value out of range."
        END IF
    LOOP WHILE GetMoreRecords
    END

3.4.7  Binary File I/O

    Binary access is a third way──in addition to random access and sequential
    access──to read or write a file's data. Use the following statement to
    open a file for binary I/O:

    OPEN file FOR BINARY AS #filenum

    Binary access is a way to get at the raw bytes of any file, not just an
    ASCII file. This makes it very useful for reading or modifying files saved
    in a non-ASCII format, such as Microsoft Word files or executable (.EXE)
    files.

    Files opened for binary access are treated as an unformatted sequence of
    bytes. Although you can read or write a record (a variable declared as
    having a user-defined type) to a file opened in the binary mode, the file
    itself does not have to be organized into fixed-length records. In fact,
    binary I/O does not have to deal with records at all, unless you consider
    each byte in a file as a separate record.

    3.4.7.1  Comparing Binary Access and Random Access

    The binary-access mode is similar to the random-access mode in that you
    can both read from and write to a file after a single OPEN statement.
    (Binary thus differs from sequential access, where you must first close a
    file and then reopen it if you want to switch between reading and
    writing.) Also, like random access, binary access lets you move backwards
    and forwards within an open file. Binary access even supports the same
    statements used for reading and writing random-access files:

    {GET| PUT} [[#]]filenumber, [[position]], variable

    Here, variable can have any type, including a variable-length string or a
    user-defined type, and position points to the place in the file where the
    next GET or PUT operation will take place. (The position is relative to
    the beginning of the file; that is, the first byte in the file has
    position one, the second byte has position two, and so on.) If you leave
    off the position argument, successive GET and PUT operations move the file
    pointer sequentially through the file from the first byte to the last.

    The GET statement reads a number of bytes from the file equal to the
    length of variable. Similarly, the PUT statement writes a number of bytes
    to the file equal to the length of variable. For example, if variable has
    integer type, then GET reads two bytes into variable; if variable has
    single-precision type, GET reads four bytes. Therefore, if you don't
    specify a position argument in a GET or PUT statement, the file pointer is
    moved a distance equal to the length of variable.

    The GET statement and INPUT$ function are the only ways to read data from
    a file opened in binary mode. The PUT statement is the only way to write
    data to a file opened in binary mode.

    Binary access, unlike random access, enables you to move to any byte
    position in a file and then read or write any number of bytes you want. In
    contrast, random access can only move to a record boundary and read a
    fixed number of bytes (the length of a record) each time.

    3.4.7.2  Positioning the File Pointer with SEEK

    If you want to move the file pointer to a certain place in a file without
    actually performing any I/O, use the SEEK statement. Its syntax is

    SEEK filenumber, position

    After a SEEK statement, the next read or write operation in the file
    opened with filenumber begins at the byte noted in position.

    The counterpart to the SEEK statement is the SEEK function, with this
    syntax:

    SEEK(filenumber)

    The SEEK function tells you the byte position where the very next read or
    write operation begins. (If you are using binary I/O to access a file, the
    LOC and SEEK functions give similar results, but LOC returns the position
    of the last byte read or written, while SEEK returns the position of the
    next byte to be read or written.)

    The SEEK statement and function also work on files opened for sequential
    or random access. With sequential access, both the statement and the
    function behave the same as they do with binary access; that is, the SEEK
    statement moves the file pointer to specific byte positions, and the SEEK
    function returns information about the next byte to read or write.

    However, if a file is opened for random access, the SEEK statement can
    move the file pointer only to the beginning of a record, not to a byte
    within a record. Also, when used with random-access files, the SEEK
    function returns the number of the next record rather than the position of
    the next byte.

    Example

    The following program opens a QuickBASIC Quick library, then reads and
    prints the names of BASIC procedures and other external symbols contained
    in the library. This program is in the file named QLBDUMP.BAS on the
    QuickBASIC distribution disks.

    ' This program prints the names of Quick library procedures.
    DECLARE SUB DumpSym (SymStart AS INTEGER, QHdrPos AS LONG)

    TYPE ExeHdr                     ' Part of DOS .EXE header
        other1    AS STRING * 8     ' Other header information
        CParHdr   AS INTEGER        ' Size of header in paragraphs
        other2    AS STRING * 10    ' Other header information
        IP          AS INTEGER      ' Initial IP value
        CS          AS INTEGER      ' Initial (relative) CS value
    END TYPE

    TYPE QBHdr                      ' QLB header
        QBHead    AS STRING * 6     ' QB specific heading
        Magic     AS INTEGER        ' Magic word: identifies file as a Quick li
        SymStart  AS INTEGER        ' Offset from header to first code symbol
        DatStart  AS INTEGER        ' Offset from header to first data symbol
    END TYPE

    TYPE QbSym                      ' QuickLib symbol entry
        Flags     AS INTEGER        ' Symbol flags
        NameStart AS INTEGER        ' Offset into name table
        other     AS STRING * 4     ' Other header information
    END TYPE

    DIM EHdr AS ExeHdr, Qhdr AS QBHdr, QHdrPos AS LONG

    INPUT "Enter Quick library file name: ", FileName$
    FileName$ = UCASE$(FileName$)
    IF INSTR(FileName$,".QLB") = 0 THEN FileName$ = FileName$ + ".QLB"
    INPUT "Enter output file name or press ENTER for screen: ", OutFile$
    OutFile$ = UCASE$(OutFile$)
    IF OutFile$ = "" THEN OutFile$ = "CON"
    OPEN FileName$ FOR BINARY AS #1
    OPEN OutFile$ FOR OUTPUT AS #2

    GET #1, , EHdr                    ' Read the EXE format header.
    QHdrPos = (EHdr.CParHdr + EHdr.CS) * 16 + EHdr.IP + 1

    GET #1, QHdrPos, Qhdr             ' Read the QuickLib format header.
    IF Qhdr.Magic <> &H6C75 THEN PRINT "Not a QB UserLibrary": END

    PRINT #2, "Code Symbols:": PRINT #2,
    DumpSym Qhdr.SymStart, QHdrPos    ' dump code symbols
    PRINT #2,
    PRINT #2, "Data Symbols:": PRINT #2, ""
    DumpSym Qhdr.DatStart, QHdrPos    ' dump data symbols
    PRINT #2,

    END

    SUB DumpSym (SymStart AS INTEGER, QHdrPos AS LONG)
        DIM QlbSym AS QbSym
        DIM NextSym AS LONG, CurrentSym AS LONG

        ' Calculate the location of the first symbol entry,
        ' then read that entry:
        NextSym = QHdrPos + Qhdr.SymStart
        GET #1, NextSym, QlbSym

    DO
        NextSym = SEEK(1)           ' Save the location of the next symbol.
        CurrentSym = QHdrPos + QlbSym.NameStart
        SEEK #1, CurrentSym         ' Use SEEK to move to the name
                                    ' for the current symbol entry.
        Prospect$ = INPUT$(40, 1)   ' Read the longest legal string,
                                    ' plus one additional byte for
                                    ' the final null character (CHR$(0)).

        ' Extract the null-terminated name:
        SName$ = LEFT$(Prospect$, INSTR(Prospect$, CHR$(0)))

        ' Print only those names that do not begin with "__", "$", or "b$"
        ' as these names are usually considered reserved:
        T$ = LEFT$(SName$, 2)
        IF T$ <> "__" AND LEFT$(SName$, 1) <> "$" AND UCASE$(T$) <> "B$" THEN
            PRINT #2, "  " + SName$
        END IF

        GET #1, NextSym, QlbSym     ' Read a symbol entry.
        LOOP WHILE QlbSym.Flags        ' Flags=0 (false) means end of table.

    END SUB


3.5  Working with Devices

    Microsoft BASIC supports device I/O. This means certain computer
    peripherals can be opened for I/O just like data files on disk. Input from
    or output to these devices can be done with the statements and functions
    listed in Table 9.4, "Statements and Functions Used for Data-File I/O,"
    with the exceptions noted in Section 3.5.1, "Differences between Device
    I/O and File I/O." Table 3.1 lists the devices supported by BASIC.

    Table 3.1  Devices Supported by BASIC for I/O
    Name                     Device                  I/O Mode Supported
    ──────────────────────────────────────────────────────────────────────────
    COM1:                    First serial port       Input and output

    COM2:                    Second serial port      Input and output

    CONS:                    Screen                  Output only

    KYBD:                    Keyboard                Input only

    LPT1:                    First printer           Output only

    LPT2:                    Second printer          Output only

    LPT3:                    Third printer           Output only

    SCRN:                    Screen                  Output only

    ──────────────────────────────────────────────────────────────────────────

3.5.1  Differences between Device I/O and File I/O

    Certain functions and statements used for file I/O are not allowed for
    device I/O, while other statements and functions behave differently. These
    are some of the differences:

    ■ The CONS:, SCRN:, and LPTn: devices cannot be opened in the input or
    append modes.

    ■ The KYBD: device cannot be opened in the output or append modes.

    ■ The EOF, LOC, and LOF functions cannot be used with the CONS:, KYBD:,
    LPTn:, or SCRN: devices.

    ■ The EOF, LOC, and LOF functions can be used with the COMn: serial
    device; however, the values these functions return have a different
    meaning than the values they return when used with data files. (See
    Section 3.5.2 for an explanation of what these functions do when used
    with COMn:.)

    Example

    The following program shows how the devices LPT1: or SCRN: can be opened
    for output using the same syntax as that for data files. This program
    reads all the lines from the file chosen by the user and then prints the
    lines on the screen or the printer according to the user's choice.

    CLS
    ' Input the name of the file to look at:
    INPUT "Name of file to display: ", FileNam$

    ' If no name is entered, end the program;
    ' otherwise, open the given file for reading (INPUT):
    IF FileNam$ = "" THEN END ELSE OPEN FileNam$ FOR INPUT AS #1

    ' Input choice for displaying file (Screen or Printer);
    ' continue prompting for input until either the "S" or "P"
    ' keys are pressed:
    DO
        ' Go to row 2, column 1 on the screen and print prompt:
        LOCATE 2, 1, 1
        PRINT "Send output to screen (S), or to printer (P): ";

        ' Print over anything after the prompt:
        PRINT SPACE$(2);

        ' Relocate cursor after the prompt, and make it visible:
        LOCATE 2, 47, 1
        Choice$ = UCASE$(INPUT$(1))     ' Get input.
        PRINT Choice$
    LOOP WHILE Choice$ <> "S" AND Choice$ <> "P"

    ' Depending on the key pressed, open either the printer
    ' or the screen for output:
    SELECT CASE Choice$
        CASE "P"
        OPEN "LPT1:" FOR OUTPUT AS #2
        PRINT "Printing file on printer."
        CASE "S"
        CLS
        OPEN "SCRN:" FOR OUTPUT AS #2
    END SELECT

    ' Set the width of the chosen output device to 80 columns:
    WIDTH #2, 80

    ' As long as there are lines in the file, read a line
    ' from the file and print it on the chosen device:
    DO UNTIL EOF(1)
        LINE INPUT #1, LineBuffer$
        PRINT #2, LineBuffer$
    LOOP

    CLOSE        ' End input from the file and output to the device.
    END

3.5.2  Communications through the Serial Port

    The OPEN "COMn:" statement (where n can be 1 or, if you have two serial
    ports, 2) allows you to open your computer's serial port(s) for serial
    (bit-by-bit) communication with other computers or with peripheral devices
    such as modems or serial printers. The following are some of the
    parameters you can specify:

    ■ Rate of data transmission, measured in "baud" (bits per second)

    ■ Whether or not to detect transmission errors and how those errors will
    be detected

    ■ How many stop bits (1, 1.5, or 2) are to be used to signal the end of a
    transmitted byte

    ■ How many bits in each byte of data transmitted or received constitute
    actual data

    When the serial port is opened for communication, an input buffer is set
    aside to hold the bytes being read from other device. This is because, at
    high baud rates, characters arrive faster than they can be processed. The
    default size for this buffer is 512 bytes, and it can be modified with the
    LEN = numbytes option of the OPEN "COMn:" statement. The values returned
    by the EOF, LOC, and LOF functions when used with a communications device
    return information about the size of this buffer, as shown in the
    following list:

    Function                 Information Returned
    ──────────────────────────────────────────────────────────────────────────
    EOF                      Whether or not any characters are waiting to be
                            read from the input buffer

    LOC                      The number of characters waiting in the input
                            buffer

    LOF                      The amount of space remaining (in bytes) in the
                            output buffer

    ──────────────────────────────────────────────────────────────────────────

    Since every character is potentially significant data, both INPUT # and
    LINE INPUT # have serious drawbacks for getting input from another device.
    This is because INPUT # stops reading data into a variable when it
    encounters a comma or new line (and, sometimes, a space or double quote),
    and LINE INPUT # stops reading data when it encounters a new line. This
    makes INPUT$ the best function to use for input from a communications
    device, since it reads all characters.

    The following line uses the LOC function to check the input buffer for the
    number of characters waiting there from the communications device opened
    as file #1; it then uses the INPUT$ function to read those characters,
    assigning them to a string variable named ModemInput$:

    ModemInput$ = INPUT$(LOC(1), #1)


3.6  Sample Applications

    The sample applications listed in this section include a screen-handling
    program that prints a calendar for any month in any year from 1899 to
    2099, a file I/O program that builds and searches an index of record
    numbers from a random-access file, and a communications program that makes
    your PC behave like a terminal when connected to a modem.

3.6.1  Perpetual Calendar (CAL.BAS)

    After prompting the user to input a month from 1 to 12 and a year from
    1899 to 2099, the following program prints the calendar for the given
    month and year. The IsLeapYear procedure makes appropriate adjustments to
    the calendar for months in a leap year.

    Statements and Functions Used

    This program demonstrates the following screen-handling functions and
    statements discussed in Sections 3.1-3.3:

    ■ INPUT

    ■ INPUT$

    ■ LOCATE

    ■ POS(0)

    ■ PRINT

    ■ PRINT USING

    ■ TAB

    Program Listing

    The perpetual calendar program CAL.BAS is listed below.

    DEFINT A-Z              ' Default variable type is integer.

    ' Define a data type for the names of the months and the
    ' number of days in each:
    TYPE MonthType
        Number AS INTEGER       ' Number of days in the month
        MName AS STRING * 9     ' Name of the month
    END TYPE

    ' Declare procedures used:
    DECLARE FUNCTION IsLeapYear% (N%)
    DECLARE FUNCTION GetInput% (Prompt$, Row%, LowVal%, HighVal%)

    DECLARE SUB PrintCalendar (Year%, Month%)
    DECLARE SUB ComputeMonth (Year%, Month%, StartDay%, TotalDays%)

    DIM MonthData(1 TO 12) AS MonthType

    ' Initialize month definitions from DATA statements below:
    FOR I = 1 TO 12
        READ MonthData(I).MName, MonthData(I).Number
    NEXT

    ' Main loop, repeat for as many months as desired:
    DO
        CLS

        ' Get year and month as input:
        Year = GetInput("Year (1899 to 2099): ", 1, 1899, 2099)
        Month = GetInput("Month (1 to 12): ", 2, 1, 12)

        ' Print the calendar:
        PrintCalendar Year, Month
    ' Another Date?
        LOCATE 13, 1           ' Locate in 13th row, 1st column.
        PRINT "New Date? ";    ' Keep cursor on same line.
        LOCATE , , 1, 0, 13    ' Turn cursor on and make it one
                            ' character high.
        Resp$ = INPUT$(1)      ' Wait for a key press.
        PRINT Resp$            ' Print the key pressed.

    LOOP WHILE UCASE$(Resp$) = "Y"
    END

    ' Data for the months of a year:
    DATA January, 31, February, 28, March, 31
    DATA April, 30, May, 31, June, 30, July, 31, August, 31
    DATA September, 30, October, 31, November, 30, December, 31

    ' ====================== COMPUTEMONTH =====================
    '  Computes the first day and the total days in a month
    ' =========================================================

    SUB ComputeMonth (Year, Month, StartDay, TotalDays) STATIC
        SHARED MonthData() AS MonthType

        CONST LEAP   = 366 MOD 7
        CONST NORMAL = 365 MOD 7

        ' Calculate total number of days (NumDays) since 1/1/1899:

        ' Start with whole years:
        NumDays = 0
        FOR I = 1899 TO Year - 1
        IF IsLeapYear(I) THEN         ' If leap year,
            NumDays = NumDays + LEAP   ' add 366 MOD 7.
        ELSE                          ' If normal year,
            NumDays = NumDays + NORMAL ' add 365 MOD 7.
        END IF
        NEXT

        ' Next, add in days from whole months:
        FOR I = 1 TO Month - 1
        NumDays = NumDays + MonthData(I).Number
        NEXT

        ' Set the number of days in the requested month:
        TotalDays = MonthData(Month).Number

        ' Compensate if requested year is a leap year:
        IF IsLeapYear(Year) THEN

        ' If after February, add one to total days:
        IF Month > 2 THEN
            NumDays = NumDays + 1

        ' If February, add one to the month's days:
        ELSEIF Month = 2 THEN
            TotalDays = TotalDays + 1
        END IF
        END IF

        ' 1/1/1899 was a Sunday, so calculating "NumDays MOD 7"
        ' gives the day of week (Sunday = 0, Monday = 1, Tuesday
        ' = 2, and so on) for the first day of the input month:
        StartDay = NumDays MOD 7
    END SUB

    ' ======================== GETINPUT =======================
    '    Prompts for input, then tests for a valid range
    ' =========================================================

    FUNCTION GetInput (Prompt$, Row, LowVal, HighVal) STATIC

        ' Locate prompt at specified row, turn cursor on and
        ' make it one character high:
        LOCATE Row, 1, 1, 0, 13
        PRINT Prompt$;

        ' Save column position:
        Column = POS(0)

        ' Input value until it's within range:
        DO
        LOCATE Row, Column   ' Locate cursor at end of prompt.
        PRINT SPACE$(10)     ' Erase anything already there.
        LOCATE Row, Column   ' Relocate cursor at end of prompt.
        INPUT "", Value      ' Input value with no prompt.
        LOOP WHILE (Value < LowVal OR Value > HighVal

        ' Return valid input as value of function:
        GetInput = Value

    END FUNCTION

    ' ====================== ISLEAPYEAR =======================
    '   Determines if a year is a leap year or not
    ' =========================================================

    FUNCTION IsLeapYear (N) STATIC

        ' If the year is evenly divisible by 4 and not divisible
        ' by 100, or if the year is evenly divisible by 400,
        ' then it's a leap year:
        IsLeapYear = (N MOD 4=0 AND N MOD 100<>0) OR (N MOD 400=0)
    END FUNCTION

    ' ===================== PRINTCALENDAR =====================
    '   Prints a formatted calendar given the year and month
    ' =========================================================

    SUB PrintCalendar (Year, Month) STATIC
    SHARED MonthData() AS MonthType

        ' Compute starting day (Su M Tu ...)
        ' and total days for the month:
        ComputeMonth Year, Month, StartDay, TotalDays
        CLS
        Header$ = RTRIM$(MonthData(Month).MName) + "," + STR$(Year)

        ' Calculate location for centering month and year:
        LeftMargin = (35 - LEN(Header$)) \ 2

    ' Print header:
        PRINT TAB(LeftMargin); Header$
        PRINT
        PRINT "Su   M   Tu   W   Th    F   Sa"
        PRINT

        ' Recalculate and print tab
        ' to the first day of the month (Su M Tu ...):
        LeftMargin = 5 * StartDay + 1
        PRINT TAB(LeftMargin);

        ' Print out the days of the month:
        FOR I = 1 TO TotalDays
        PRINT USING "##"; I;

        ' Advance to the next line
        ' when the cursor is past column 32:
        IF POS(0) > 32 THEN PRINT
        NEXT

    END SUB

    Output

    ┌───────────────────────────────────────────────────────────────────┐
    │                                                                   │
    │                June, 1988                                         │
    │                                                                   │
    │     Su    M    Tu    W   Th     F    Sa                           │
    │                                                                   │
    │                      1    2     3     4                           │
    │      5    6     7    8    9    10    11                           │
    │     12   13    14   15   16    17    18                           │
    │     19   20    21   22   23    24    25                           │
    │     26   27    28   29   30                                       │
    │                                                                   │
    │                                                                   │
    │     New Date?                                                     │
    │                                                                   │
    └───────────────────────────────────────────────────────────────────┘

3.6.2  Indexing a Random-Access File (INDEX.BAS)

    The following program uses an indexing technique to store and retrieve
    records in a random-access file. Each element of the Index array has two
    parts: a string field (PartNumber) and an integer field (RecordNumber).
    This array is sorted alphabetically on the PartNumber field, which allows
    the array to be rapidly searched for a specific part number using a binary
    search.

    The Index array functions much like the index to a book. When you want to
    find the pages in a book that deal with a particular topic, you look up an
    entry for that topic in the index. The entry then points to a page number
    in the book. Similarly, this program looks up a part number in the
    alphabetically sorted Index array. Once it finds the part number, the
    associated record number in the RecordNumber field points to the record
    containing all the information for that part.

    Statements and Functions Used

    This program demonstrates the following functions and statements used in
    accessing random-access files:

    ■ TYPE...END TYPE

    ■ OPEN...FOR RANDOM

    ■ GET #

    ■ PUT #

    ■ LOF

    Program Listing

    The random-access file indexing program INDEX.BAS is listed below.

    DEFINT A-Z

    ' Define the symbolic constants used globally in the program:
    CONST FALSE = 0, TRUE = NOT FALSE

    ' Define a record structure for random-file records:
    TYPE StockItem
        PartNumber AS STRING * 6
        Description AS STRING * 20
        UnitPrice AS SINGLE
        Quantity AS INTEGER
    END TYPE

    ' Define a record structure for each element of the index:
    TYPE IndexType
        RecordNumber AS INTEGER
        PartNumber AS STRING * 6
    END TYPE

    ' Declare procedures that will be called:
    DECLARE FUNCTION Filter$ (Prompt$)
    DECLARE FUNCTION FindRecord% (PartNumber$, RecordVar AS StockItem)

    DECLARE SUB AddRecord (RecordVar AS StockItem)
    DECLARE SUB InputRecord (RecordVar AS StockItem)
    DECLARE SUB PrintRecord (RecordVar AS StockItem)
    DECLARE SUB SortIndex ()
    DECLARE SUB ShowPartNumbers ()
    ' Define a buffer (using the StockItem type)
    ' and define and dimension the index array:
    DIM StockRecord AS StockItem, Index(1 TO 100) AS IndexType

    ' Open the random-access file:
    OPEN "STOCK.DAT" FOR RANDOM AS #1 LEN = LEN(StockRecord)

    ' Calculate number of records in the file:
    NumberOfRecords = LOF(1) \ LEN(StockRecord)

    ' If there are records, read them and build the index:
    IF NumberOfRecords <> 0 THEN
        FOR RecordNumber = 1 TO NumberOfRecords

        ' Read the data from a new record in the file:
        GET #1, RecordNumber, StockRecord

        ' Place part number and record number in index:
        Index(RecordNumber).RecordNumber = RecordNumber
        Index(RecordNumber).PartNumber = StockRecord.PartNumber
        NEXT

        SortIndex            ' Sort index in part-number order.
    END IF

    DO                      ' Main-menu loop.
        CLS
        PRINT "(A)dd records."
        PRINT "(L)ook up records."
        PRINT "(Q)uit program."
        PRINT
        LOCATE , , 1
        PRINT "Type your choice (A, L, or Q) here: ";

        ' Loop until user presses, A, L, or Q:
        DO
        Choice$ = UCASE$(INPUT$(1))
        LOOP WHILE INSTR("ALQ", Choice$) = 0

        ' Branch according to choice:
        SELECT CASE Choice$
        CASE "A"
        AddRecord StockRecord
        CASE "L"
        IF NumberOfRecords = 0 THEN
            PRINT : PRINT "No records in file yet. ";
            PRINT "Press any key to continue.";
            Pause$ = INPUT$(1)
        ELSE
            InputRecord StockRecord
        END IF
        CASE "Q"          ' End program.
        END SELECT
    LOOP UNTIL Choice$ = "Q"

    CLOSE #1                ' All done, close file and end.
    END

    ' ======================== ADDRECORD ======================
    ' Adds records to the file from input typed at the keyboard
    ' =========================================================

    SUB AddRecord (RecordVar AS StockItem) STATIC
        SHARED Index() AS IndexType, NumberOfRecords
        DO
        CLS
        INPUT "Part Number: ", RecordVar.PartNumber
        INPUT "Description: ", RecordVar.Description

        ' Call the Filter$ FUNCTION to input price & quantity:
        RecordVar.UnitPrice = VAL(Filter$("Unit Price : "))
        RecordVar.Quantity = VAL(Filter$("Quantity   : "))

        NumberOfRecords = NumberOfRecords + 1

        PUT #1, NumberOfRecords, RecordVar

        Index(NumberOfRecords).RecordNumber = NumberOfRecords
        Index(NumberOfRecords).PartNumber = RecordVar.PartNumber
        PRINT : PRINT "Add another? ";
        OK$ = UCASE$(INPUT$(1))
        LOOP WHILE OK$ = "Y"

        SortIndex            ' Sort index file again.
    END SUB

    ' ========================= FILTER ========================
    '       Filters all non-numeric characters from a string
    '       and returns the filtered string
    ' =========================================================

    FUNCTION Filter$ (Prompt$) STATIC
        ValTemp2$ = ""
        PRINT Prompt$;                    ' Print the prompt passed.
        INPUT "", ValTemp1$               ' Input a number as
                                        ' a string.
        StringLength = LEN(ValTemp1$)     ' Get the string's length.
        FOR I% = 1 TO StringLength        ' Go through the string,
        Char$ = MID$(ValTemp1$, I%, 1) ' one character at a time.

        ' Is the character a valid part of a number (i.e.,
        ' a digit or a decimal point)?  If yes, add it to
        ' the end of a new string:
        IF INSTR(".0123456789", Char$) > 0 THEN
            ValTemp2$ = ValTemp2$ + Char$

        ' Otherwise, check to see if it's a lowercase "l",
        ' since typewriter users may enter a one that way:
        ELSEIF Char$ = "l" THEN
            ValTemp2$ = ValTemp2$ + "1" ' Change the "l" to a "1".
        END IF
        NEXT I%

        Filter$ = ValTemp2$               ' Return filtered string.

    END FUNCTION

    ' ======================= FINDRECORD ===================
    '  Uses a binary search to locate a record in the index
    ' ======================================================

    FUNCTION FindRecord% (Part$, RecordVar AS StockItem) STATIC
        SHARED Index() AS IndexType, NumberOfRecords

        ' Set top and bottom bounds of search:
        TopRecord = NumberOfRecords
        BottomRecord = 1

        ' Search until top of range is less than bottom:
        DO UNTIL (TopRecord < BottomRecord)

        ' Choose midpoint:
        Midpoint = (TopRecord + BottomRecord) \ 2

        ' Test to see if it's the one wanted (RTRIM$()
        ' trims trailing blanks from a fixed string):
        Test$ = RTRIM$(Index(Midpoint).PartNumber)

        ' If it is, exit loop:
        IF Test$ = Part$ THEN
        EXIT DO

        ' Otherwise, if what we're looking for is greater,
        ' move bottom up:
        ELSEIF Part$ > Test$ THEN
        BottomRecord = Midpoint + 1

        ' Otherwise, move the top down:
        ELSE
        TopRecord = Midpoint - 1
        END IF
        LOOP

        ' If part was found, input record from file using
        ' pointer in index and set FindRecord% to TRUE:
        IF Test$ = Part$ THEN
        GET #1, Index(Midpoint).RecordNumber, RecordVar
        FindRecord% = TRUE

        ' Otherwise, if part was not found, set FindRecord%
        ' to FALSE:
        ELSE
        FindRecord% = FALSE
        END IF
    END FUNCTION

    ' ======================= INPUTRECORD =====================
    '    First, INPUTRECORD calls SHOWPARTNUMBERS, which prints
    '    a menu of part numbers on the top of the screen. Next,
    '    INPUTRECORD prompts the user to enter a part number.
    '    Finally, it calls the FINDRECORD and PRINTRECORD
    '    procedures to find and print the given record.
    ' =========================================================

    SUB InputRecord (RecordVar AS StockItem) STATIC
        CLS
        ShowPartNumbers      ' Call the ShowPartNumbers SUB.

        ' Print data from specified records
        ' on the bottom part of the screen:
        DO
        PRINT "Type a part number listed above ";
        INPUT "(or Q to quit) and press <ENTER>: ", Part$
        IF UCASE$(Part$) <> "Q" THEN
            IF FindRecord(Part$, RecordVar) THEN
                PrintRecord RecordVar
            ELSE
                PRINT "Part not found."
            END IF
        END IF
        PRINT STRING$(40, "_")
        LOOP WHILE UCASE$(Part$) <> "Q"

        VIEW PRINT   ' Restore the text viewport to entire screen.
    END SUB

    ' ======================= PRINTRECORD =====================
    '                Prints a record on the screen
    ' =========================================================

    SUB PrintRecord (RecordVar AS StockItem) STATIC
        PRINT "Part Number: "; RecordVar.PartNumber
        PRINT "Description: "; RecordVar.Description
        PRINT USING "Unit Price :$$###.##"; RecordVar.UnitPrice
        PRINT "Quantity   :"; RecordVar.Quantity
    END SUB

    ' ===================== SHOWPARTNUMBERS ===================
    ' Prints an index of all the part numbers in the upper part
    ' of the screen
    ' =========================================================

    SUB ShowPartNumbers STATIC
        SHARED Index() AS IndexType, NumberOfRecords

        CONST NUMCOLS = 8, COLWIDTH = 80 \ NUMCOLS

        ' At the top of the screen, print a menu indexing all
        ' the part numbers for records in the file. This menu is
        ' printed in columns of equal length (except possibly the
        ' last column, which may be shorter than the others):
        ColumnLength = NumberOfRecords
        DO WHILE ColumnLength MOD NUMCOLS
        ColumnLength = ColumnLength + 1
        LOOP
        ColumnLength = ColumnLength \ NUMCOLS
        Column = 1
        RecordNumber = 1

    DO UNTIL RecordNumber > NumberOfRecords
        FOR Row = 1 TO ColumnLength
            LOCATE Row, Column
            PRINT Index(RecordNumber).PartNumber
            RecordNumber = RecordNumber + 1
            IF RecordNumber > NumberOfRecords THEN EXIT FOR
        NEXT Row
        Column = Column + COLWIDTH
        LOOP

        LOCATE ColumnLength + 1, 1
        PRINT STRING$(80, "_")       ' Print separator line.

        ' Scroll information about records below the part-number
        ' menu (this way, the part numbers are not erased):
        VIEW PRINT ColumnLength + 2 TO 24
    END SUB

    ' ========================= SORTINDEX =====================
    '                Sorts the index by part number
    ' =========================================================

    SUB SortIndex STATIC


        SHARED Index() AS IndexType, NumberOfRecords

        ' Set comparison offset to half the number of records
        ' in index:
        Offset = NumberOfRecords \ 2

        ' Loop until offset gets to zero:
        DO WHILE Offset > 0
        Limit = NumberOfRecords - Offset
        DO

            ' Assume no switches at this offset:
            Switch = FALSE

            ' Compare elements and switch ones out of order:
            FOR I = 1 TO Limit
                IF Index(I).PartNumber > Index(I + Offset).PartNumber THEN
                    SWAP Index(I), Index(I + Offset)
                    Switch = I
                END IF
            NEXT I

            ' Sort on next pass only to where
            ' last switch was made:
            Limit = Switch
        LOOP WHILE Switch

        ' No switches at last offset, try one half as big:
        Offset = Offset \ 2
        LOOP
    END SUB

    Output

    ┌────────────────────────────────────────────────────────────────────────┐
    │                                                                        │
    │ 0235         0417       0583        8721                               │
    │                                                                        │
    │ ────────────────────────────────────────────────────────────────────── │
    │ Type a part number listed above <or Q to quit> and press <ENTER>: 0417 │
    │ Part Number: 0417                                                      │
    │ Description: oil filter                                                │
    │ Unit Price :   $5.99                                                   │
    │ Quantity   : 12                                                        │
    │                                                                        │
    │ ────────────────────────────────────────                               │
    │ Type a part number listed above <or Q to quit> and press <ENTER>: 8721 │
    │ Part Number: 8721                                                      │
    │ Description: chamois cloth                                             │
    │ Unit Price :   $8.99                                                   │
    │ Quantity   : 23                                                        │
    │                                                                        │
    │ ────────────────────────────────────────                               │
    │ Type a part number listed above <or Q to quit> and press <ENTER>:      │
    │                                                                        │
    └────────────────────────────────────────────────────────────────────────┘

3.6.3  Terminal Emulator (TERMINAL.BAS)

    The following simple program turns your computer into a "dumb" terminal;
    that is, it makes your computer function solely as an I/O device in tandem
    with a modem. This program uses the OPEN "COM1:" statement and associated
    device I/O functions to do the following two things:

    1. Send the characters you type to the modem

    2. Print characters returned by the modem on the screen

    Note that typed characters displayed on the screen are first sent to the
    modem and then returned to your computer through the open communications
    channel. You can verify this if you have a modem with Receive Detect (RD)
    and Send Detect (SD) lights──they will flash in sequence as you type.

    To dial a number, you would have to enter the Attention Dial Touch-Tone
    (ATDT) or Attention Dial Pulse (ATDP) commands at the keyboard (assuming
    you have a Hayes(R)-compatible modem).

    Any other commands sent to the modem would also have to be entered at the
    keyboard.

    Statements and Functions Used

    This program demonstrates the following functions and statements used in
    communicating with a modem through your computer's serial port:

    ■ OPEN "COM1:"

    ■ EOF

    ■ INPUT$

    ■ LOC

    Program Listing

    DEFINT A-Z

    DECLARE SUB Filter (InString$)

    COLOR 7, 1                    ' Set screen color.
    CLS

    Quit$ = CHR$(0) + CHR$(16)    ' Value returned by INKEY$
                                ' when ALT+q is pressed.

    ' Set up prompt on bottom line of screen and turn cursor on:
    LOCATE 24, 1, 1
    PRINT STRING$(80, "_");
    LOCATE 25, 1
    PRINT TAB(30); "Press ALT+q to quit";

    VIEW PRINT 1 TO    23           ' Print between lines 1 & 23.

    ' Open communications (1200 baud, no parity, 8-bit data,
    ' 1 stop bit, 256-byte input buffer):
    OPEN "COM1:1200,N,8,1" FOR RANDOM AS #1    LEN = 256

    DO                              ' Main communications loop.

        KeyInput$ = INKEY$           ' Check the keyboard.

        IF KeyInput$ = Quit$ THEN    ' Exit the loop if the user
        EXIT DO                   ' pressed ALT+q.

        ELSEIF KeyInput$ <> "" THEN  ' Otherwise, if the user has
        PRINT #1, KeyInput$;      ' pressed a key, send the
        END IF                       ' character typed to modem.

        ' Check the modem. If characters are waiting (EOF(1) is
        ' true), get them and print them to the screen:
        IF NOT EOF(1) THEN

        ' LOC(1) gives the number of characters waiting:
        ModemInput$ = INPUT$(LOC(1), #1)

        Filter ModemInput$        ' Filter out line feeds and
        PRINT ModemInput$;        ' backspaces, then print.
        END IF
    LOOP

    CLOSE                           ' End communications.
    CLS
    END

    '
    ' ========================= FILTER ========================
    '               Filters characters in an input string
    ' =========================================================
    '
    SUB Filter (InString$) STATIC

        ' Look for backspace characters and recode
        ' them to CHR$(29) (the LEFT cursor key):
        DO
        BackSpace = INSTR(InString$, CHR$(8))
        IF BackSpace THEN
        MID$(InString$, BackSpace) = CHR$(29)
        END IF
        LOOP WHILE BackSpace

        ' Look for line-feed characters and
        ' remove any found:
        DO
        LnFd = INSTR(InString$, CHR$(10))
        IF LnFd THEN
        InString$=LEFT$(InString$,LnFd-1)+MID$(InString$,LnFd+1)
        END IF
        LOOP WHILE LnFd

    END SUB



────────────────────────────────────────────────────────────────────────────
Chapter 4  String Processing

    This chapter shows you how to manipulate sequences of ASCII characters,
    known as strings. String manipulation is indispensable when you are
    processing text files, sorting data, or modifying string-data input.

    When you are finished with this chapter, you will know how to perform the
    following string-processing tasks:

    ■ Declare fixed-length strings

    ■ Compare strings and sort them alphabetically

    ■ Search for one string inside another

    ■ Get part of a string

    ■ Trim spaces from the beginning or end of a string

    ■ Generate a string by repeating a single character

    ■ Change uppercase letters in a string to lowercase and vice versa

    ■ Convert numeric expressions to string representations and vice versa


4.1  Strings Defined

    A string is a sequence of contiguous characters. Examples of characters
    are the letters of the alphabet (a-z and A-Z), punctuation symbols such as
    commas (,) or question marks (?), and other symbols from the fields of
    math and finance such as plus (+) or percent (%) signs.

    In this chapter, the term "string" can refer to any of the following:

    ■ A string constant

    ■ A string variable

    ■ A string expression

    String constants are declared in one of two ways:

    1. By enclosing a sequence of characters between double quotes, as in the
        following PRINT statement:

        PRINT "Processing the file. Please wait."

        This is known as a "literal string constant."

    2. By setting a name equal to a literal string in a CONST statement, as in
        the following:

        ' Define the string constant MESSAGE:
        CONST MESSAGE = "Drive door open."

        This is known as a "symbolic string constant."

    String variables can be declared in one of three ways:

    1. By appending the string-type suffix ($) to the variable name:

        LINE INPUT "Enter your name: "; Buffer$

    2. By using the DEFSTR statement:

        ' All variables beginning with the letter "B"
        ' are strings, unless they end with one of the
        ' numeric-type suffixes (%, &, !, or #):
        DEFSTR B
        .
        .
        .
        Buffer = "Your name here"  ' Buffer is a string variable

    3. By declaring the string name in an AS STRING statement:

        DIM Buffer1 AS STRING      ' Buffer1 is a variable-
                                ' length string.
        DIM Buffer2 AS STRING*10   ' Buffer2 is a fixed-length
                                ' string 10 bytes long.

    A string expression is a combination of string variables, constants,
    and/or string functions.

    Of course, the character components of strings are not stored in your
    computer's memory in a form generally recognizable to humans. Instead,
    each character is translated to a number known as the ASCII code for that
    character. For example, capital "A" is stored as decimal 65 (or
    hexadecimal 41H), while lowercase "a" is stored as decimal 97 (or
    hexadecimal 61H). See Appendix D, "Keyboard Scan Codes and ASCII
    Character Codes," for a complete list of the ASCII codes for each
    character.

    You can also use the BASIC ASC function to determine the ASCII code for a
    character; for example, ASC("A") returns the value 65. The inverse of the
    ASC function is the CHR$ function. CHR$ takes an ASCII code as its
    argument and returns the character with that code; for example, the
    statement PRINT CHR$(64) displays the character @.


4.2  Variable- and Fixed-Length Strings

    In previous versions of BASIC, strings were always variable length. BASIC
    now supports both variable-length strings and fixed-length strings.

4.2.1  Variable-Length Strings

    Variable-length strings are "elastic"; that is, they expand and contract
    to store different numbers of characters (from none to a maximum of
    32,767). For example, the length of the variable Temp$ in the following
    example varies according to the size of what is stored in Temp$:

    Temp$ = "1234567"

    ' LEN function returns length of string
    '  (number of characters it contains):



    PRINT LEN(Temp$)

    Temp$ = "1234"
    PRINT LEN(Temp$)

    Output

    7
    4

4.2.2  Fixed-Length Strings

    Fixed-length strings are commonly used as record elements in a TYPE...END
    TYPE user-defined data type. However, they can also be declared by
    themselves in COMMON, DIM, REDIM, SHARED, or STATIC statements, as in the
    following statement:

    DIM Buffer AS STRING * 10

    As their name implies, fixed-length strings have a constant length,
    regardless of the length of the string stored in them. This is shown by
    the output from the following example:

    DIM LastName AS STRING * 12
    DIM FirstName AS STRING * 10

    LastName = "Huntington-Ashley"
    FirstName = "Claire"

    PRINT "123456789012345678901234567890"
    PRINT FirstName; LastName
    PRINT LEN(FirstName)
    PRINT LEN(LastName)

    Output

    123456789012345678901234567890
    Claire    Huntington-A
    10
    12

    Note that the lengths of the string variables FirstName and LastName did
    not change, as demonstrated by the values returned by the LEN function (as
    well as the four spaces following the six-letter name, Claire).

    The output from the above example also illustrates how values assigned to
    fixed-length variables are left-justified and, if necessary, truncated on
    the right. It is not necessary to use the LSET function to left-justify
    values in fixed-length strings; this is done automatically. If you want to
    right-justify a string inside a fixed-length string, use RSET, as shown
    here:

    DIM NameBuffer AS STRING * 10
    RSET NameBuffer = "Claire"
    PRINT "1234567890"
    PRINT NameBuffer

    Output

    1234567890
        Claire


4.3  Combining Strings

    Two strings can be combined with the plus (+) operator. The string
    following the plus operator is appended to the string preceding the plus
    operator, as shown in the next example:

    A$ = "first string"
    B$ = "second string"
    C$ = A$ + B$
    PRINT C$

    Output

    first stringsecond string

    The process of combining strings in this way is called "concatenation,"
    which means linking together.

    Note that in the previous example, the two strings are combined without
    any intervening spaces. If you want a space in the combined string, you
    could pad one of the strings A$ or B$, like this:

    B$ = " second string"          ' Leading blank in B$

    Because values are left-justified in fixed-length strings, you may find
    extra spaces when you concatenate them, as in this example:

    TYPE NameType
        First AS STRING * 10
        Last  AS STRING * 12
    END TYPE

    DIM NameRecord AS NameType

    ' The constant "Ed" is left-justified in the variable
    ' NameRecord.First, so there are eight trailing blanks:
    NameRecord.First = "Ed"
    NameRecord.Last  = "Feldstein"

    ' Print a line of numbers for reference:
    PRINT "123456789012345678901234567890"

    WholeName$ = NameRecord.First + NameRecord.Last
    PRINT WholeName$

    Output

    123456789012345678901234567890
    Ed        Feldstein

    The LTRIM$ function returns a string with its leading spaces stripped
    away, while the RTRIM$ function returns a string with its trailing spaces
    stripped away. The original string is unaltered. (See Section 4.6,
    "Retrieving Parts of Strings," for more information on these functions.)


4.4  Comparing Strings

    Strings are compared with the following relational operators:

    Operator                 Meaning
    ──────────────────────────────────────────────────────────────────────────
    < >                      Not equal

    =                        Equal

    <                        Less than

    >                        Greater than

    <=                       Less than or equal to

    >=                       Greater than or equal to
    ──────────────────────────────────────────────────────────────────────────

    A single character is greater than another character if its ASCII value is
    greater. For example, the ASCII value of the letter "B" is greater than
    the ASCII value of the letter "A," so the expression "B" > "A"is true.

    When comparing two strings, BASIC looks at the ASCII values of
    corresponding characters. The first character where the two strings differ
    determines the alphabetical order of the strings. For instance, the
    strings "doorman" and "doormats" are the same up to the seventh character
    in each ("n" and "t"). Since the ASCII value of "n" is less than the ASCII
    value of "t", the expression "doorman" < "doormats"is true. Note that the
    ASCII values for letters follow alphabetical order from A to Z and from a
    to z, so you can use the relational operators listed above to alphabetize
    strings. Moreover, uppercase letters have smaller ASCII values than
    lowercase letters, so in a sorted list of strings, "ASCII" would come
    before "ascii".

    If there is no difference between corresponding characters of two strings
    and they are the same length, then the two strings are equal. If there is
    no difference between corresponding characters of two strings, but one of
    the strings is longer, then the longer string is greater than the shorter
    string. For example "abc" = "abc" and "aaaaaaa" > "aaa" are both true
    expressions.

    Leading and trailing blank spaces are significant in string comparisons.
    For example, the string " test" is less than the string "test", since a
    blank space (" ") is less than a"t"; on the other hand, the string "test "
    is greater than the string "test". Keep this in mind when comparing
    fixed-length and variable-length strings, since fixed-length strings may
    contain trailing spaces.


4.5  Searching for Strings

    One of the most common string-processing tasks is searching for a string
    inside another string. The INSTR(string1, string2) function tells you
    whether or not string2 is contained in string1 by returning the position
    of the first character in string1 (if any) where the match begins, as
    shown in this next example:

    String1$ = "A line of text with 37 letters in it."
    String2$ = "letters"

    PRINT "         1         2         3         4"
    PRINT "1234567890123456789012345678901234567890"
    PRINT String1$
    PRINT String2$
    PRINT INSTR(String1$, String2$)

    Output

    1         2         3         4
    1234567890123456789012345678901234567890
    A line of text with 37 letters in it.
    letters
    24

    If no match is found (that is, string2 is not a substring of string1),
    INSTR returns the value 0.

    A variation of the syntax shown above, INSTR(position, string1, string2),
    allows you to specify where you want the search to start in string1. To
    illustrate, making the following modification to the preceding example
    changes the output:

    ' Start looking for a match at the 30th
    ' character of String1$:
    PRINT INSTR(30, String1$, String2$)

    Output

    1         2         3         4
    1234567890123456789012345678901234567890
    A line of text with 37 letters in it.
    letters
    0

    In other words, the string "letters" does not appear after the 30th
    character of String1$.

    The INSTR(position, string1, string2) variation is useful for finding
    every occurrence of string2 in string1 instead of just the first
    occurrence, as shown in the next example:

    String1$ = "the dog jumped over the broken saxophone."
    String2$ = "the"
    PRINT String1$

    Start      = 1
    NumMatches = 0

    DO
        Match = INSTR(Start, String1$, String2$)
        IF Match > 0 THEN
        PRINT TAB(Match); String2$
        Start      = Match + 1
        NumMatches = NumMatches + 1
        END IF
    LOOP WHILE MATCH

    PRINT "Number of matches ="; NumMatches

    Output

    the dog jumped over the broken saxophone.
    the
                                    the
    Number of matches = 2


4.6  Retrieving Parts of Strings

    Section 4.3, "Combining Strings," shows how to put strings together
    (concatenate them) by using the + operator. Several functions are
    available in BASIC that take strings apart, returning pieces of a string,
    either from the left side, the right side, or the middle of a target
    string.

4.6.1  Retrieving Characters from the Left Side of a String

    The LEFT$ and RTRIM$ functions return characters from the left side of a
    string. The LEFT$(string, number) function returns the specified number of
    characters from the left side of the string. For example:

    Test$ = "Overtly"

    ' Print the leftmost 4 characters from Test$:
    PRINT LEFT$(Test$, 4)

    Output

    Over

    Note that LEFT$, like the other functions described in this chapter, does
    not change the original string Test$; it merely returns a different
    string, a copy of part of Test$.

    The RTRIM$ function returns the left part of a string after removing any
    blank spaces at the end. For example, contrast the output from the two
    PRINT statements in the following example:

    PRINT "a left-justified string      "; "next"
    PRINT RTRIM$("a left-justified string      "); "next"

    Output

    a left-justified string      next
    a left-justified stringnext

    The RTRIM$ function is useful for comparing fixed-length and
    variable-length strings, as in the next example:

    DIM NameRecord AS STRING * 10
    NameRecord = "Ed"

    NameTest$ = "Ed"

    ' Use RTRIM$ to trim all the blank spaces from the right
    ' side of the fixed-length string NameRecord, then compare
    ' it with the variable-length string NameTest$:
    IF RTRIM$(NameRecord) = NameTest$ THEN
        PRINT "They're equal"
    ELSE
        PRINT "They're not equal"
    END IF

    Output

    They're equal

4.6.2  Retrieving Characters from the Right Side of a String

    The RIGHT$ and LTRIM$ functions return characters from the right side of a
    string. The RIGHT$(string, number) function returns the specified number
    of characters from the right side of the string. For example:

    Test$ = "1234567890"

    ' Print the rightmost 5 characters from Test$:
    PRINT RIGHT$(Test$,5)

    Output

    67890

    The LTRIM$ function returns the right part of a string after removing any
    blank spaces at the beginning. For example, contrast the output from the
    next two PRINT statements:

    PRINT "first"; "      a right-justified string"
    PRINT "first"; LTRIM$("      a right-justified string")

    Output

    first      a right-justified string
    firsta right-justified string

4.6.3  Retrieving Characters from Anywhere in a String

    Use the MID$ function to retrieve any number of characters from any point
    in a string. The MID$(string, start, number) function returns the
    specified number of characters from the string, starting at the character
    with position start. For example, the statement

    MID$("**over the hill**", 12, 4)

    starts at the twelfth character (or h) of the string

    **over the hill**

    and returns that character plus the next three characters (hill).

    The following example shows how to use the MID$ function to step through a
    line of text character by character:

    .
    .
    .
    ' Get the number of characters in the string of text:
    Length = LEN(TextString$)

    FOR I = 1 TO Length

    ' Get the first character, then the second, third,
    ' and so forth, up to the end of the string:
    Char$ = MID$(TextString$, I, 1)

    ' Evaluate that character:
    .
    .
    .
    NEXT


4.7  Generating Strings

    The easiest way to create a string of one character repeated over and over
    is to use the intrinsic function STRING$. The STRING$(number, string)
    function produces a new string the specified number of characters long,
    each character of which is the first character of the string argument. For
    example, the statement

    Filler$ = STRING$(27, "*")

    generates a string of 27 asterisks. For characters that cannot be produced
    by typing, such as characters whose ASCII values are greater than 127, use
    the alternative form of this function, STRING$(number, code). This form
    creates a string the specified number of characters long, each character
    of which has the ASCII code specified by the code argument, as in the next
    example:

    ' Print a string of 10 "@" characters
    ' (64 is ASCII code for @):
    PRINT STRING$(10, 64)

    Output

    @@@@@@@@@@@

    The SPACE$(number) function generates a string consisting of the specified
    number of blank spaces.


4.8  Changing the Case of Letters

    You may want to convert uppercase (capital) letters in a string to
    lowercase or vice versa. This conversion would be useful in a
    case-insensitive search for a particular string pattern in a large file
    (in other words, help, HELP, or Help would all be considered the same).
    These functions would also be handy when you are not sure whether a user
    will input text in capital or lowercase letters.

    The UCASE$ and LCASE$ functions make the following conversions to a
    string:

    ■ UCASE$ returns a copy of the string passed to it, with all the lowercase
    letters converted to uppercase.

    ■ LCASE$ returns a copy of the string passed to it, with all the uppercase
    letters converted to lowercase.

    Example

    Sample$ = "* The ASCII Appendix: a table
    of useful codes *"
    PRINT Sample$
    PRINT UCASE$(Sample$)
    PRINT LCASE$(Sample$)

    Output

    * The ASCII Appendix: a table of useful codes *
    * THE ASCII APPENDIX: A TABLE OF USEFUL CODES *
    * the ascii appendix: a table of useful codes *

    Letters that are already uppercase, as well as characters that are not
    letters, remain unchanged by UCASE$; similarly, lowercase letters and
    characters that are not letters are unaffected by LCASE$.


4.9  Strings and Numbers

    BASIC does not allow a string to be assigned to a numeric variable, nor
    does it allow a numeric expression to be assigned to a string variable.
    For example, both of these statements result in an error message reading
    Type mismatch:

    TempBuffer$ = 45
    Counter% = "45"

    Instead, use the STR$ function to return the string representation of a
    number or the VAL function to return the numeric representation of a
    string:

    ' The following statements are both valid:
    TempBuffer$ = STR$(45)
    Counter%  = VAL("45")

    Note that STR$ includes the leading blank space that BASIC prints for
    positive numbers, as this short example shows:

    FOR I = 0 TO 9
        PRINT STR$(I);
    NEXT

    Output

    0 1 2 3 4 5 6 7 8 9

    If you want to eliminate this space, you can use the LTRIM$ function, as
    shown in the example below:

    FOR I = 0 TO 9
        PRINT LTRIM$(STR$(I));
    NEXT

    Output

    0123456789

    Another way to format numeric output is with the PRINT USING statement
    (see Section 3.1, "Printing Text on the Screen").


4.10  Changing Strings

    The functions mentioned in each of the preceding sections all leave their
    string arguments unchanged. Changes are made to a copy of the argument.

    In contrast, the MID$ statement (unlike the MID$ function discussed in
    Section 4.6.3, "Retrieving Characters from Anywhere in a String") changes
    its argument by embedding another string in it. The embedded string
    replaces part or all of the original string, as shown in the following
    example:

    Temp$ = "In the sun."
    PRINT Temp$

    ' Replace the "I" with an "O":
    MID$(Temp$,1) = "O"

    ' Replace "sun." with "road":
    MID$(Temp$,8) = "road"
    PRINT Temp$

    Output

    In the sun.
    On the road


4.11  Sample Application: Converting a String to a Number (STRTONUM.BAS)

    The following program takes a number that is input as a string, filters
    any numeric characters (such as commas) out of the string, then converts
    the string to a number with the VAL function.

    Statements and Functions Used

    This program demonstrates the following string-handling functions
    discussed in this chapter:

    ■ INSTR

    ■ LEN

    ■ MID$

    ■ VAL

    Program Listing

    DECLARE FUNCTION Filter$ (Txt$, FilterString$)

    ' Input a line:
    LINE INPUT "Enter a number with commas: ", A$

    ' Look only for valid numeric characters (0123456789.-)
    ' in the input string:
    CleanNum$ = Filter$(A$, "0123456789.-")

    ' Convert the string to a number:
    PRINT "The number's value = " VAL(CleanNum$)
    END

    ' ========================== FILTER =======================
    '         Takes unwanted characters out of a string by
    '         comparing them with a filter string containing
    '         only acceptable numeric characters
    ' =========================================================

    FUNCTION Filter$ (Txt$, FilterString$) STATIC
        Temp$ = ""
        TxtLength = LEN(Txt$)

        FOR I = 1 TO TxtLength     ' Isolate each character in
        C$ = MID$(Txt$, I, 1)   ' the string.

        ' If the character is in the filter string, save it:
        IF INSTR(FilterString$, C$) <> 0 THEN
            Temp$ = Temp$ + C$
        END IF
        NEXT I

        Filter$ = Temp$
    END FUNCTION



────────────────────────────────────────────────────────────────────────────
Chapter 5  Graphics

    This chapter shows you how to use the graphics statements and functions of
    BASIC to create a wide variety of shapes, colors, and patterns on your
    screen. With graphics, you can add a visual dimension to almost any
    program, whether it's a game, an educational tool, a scientific
    application, or a financial package.

    When you have finished studying this chapter, you will know how to perform
    the following graphics tasks:

    ■ Use the physical-coordinate system of your personal computer's screen to
    locate individual pixels, turn those pixels on or off, and change their
    colors

    ■ Draw lines

    ■ Draw and fill simple shapes, such as circles, ovals, and boxes

    ■ Restrict the area of the screen showing graphics output by using
    viewports

    ■ Adjust the coordinates used to locate a pixel by redefining screen
    coordinates

    ■ Use color in graphics output

    ■ Create patterns and use them to fill enclosed figures

    ■ Copy images and reproduce them in another location on the screen

    ■ Animate graphics output

    Section 5.1 below contains important information on what you'll need to
    run the graphics examples shown in this chapter. Read it first.


5.1  What You Need for Graphics Programs

    To run the graphics examples shown in this chapter, your computer must
    have graphics capability, either built in or in the form of a graphics
    card such as the Color Graphics Adapter (CGA), Enhanced Graphics Adapter
    (EGA), or Video Graphics Array (VGA). You also need a video display
    (either monochrome or color) that supports pixel-based graphics.

    Also, please keep in mind that these programs all require that your screen
    be in one of the "screen modes" supporting graphics output. (The screen
    mode controls the clarity of graphics images, the number of colors
    available, and the part of the video memory to display.) To select a
    graphics output mode, use the following statement in your program before
    using any of the graphics statements or functions described in this
    chapter:

    SCREEN mode

    Here, mode can be either 1, 2, 3, 4, 7, 8, 9, 10, 11, 12, or 13, depending
    on the monitor/adapter combination installed on your computer.

    If you are not sure whether or not the users of your programs have
    hardware that supports graphics, you can use the following simple test:

    CONST FALSE = 0, TRUE = NOT FALSE

    ' Test to make sure user has hardware
    ' with color/graphics capability:
    ON ERROR GOTO Message      ' Turn on error trapping.
    SCREEN 1                   ' Try graphics mode one.
    ON ERROR GOTO 0            ' Turn off error trapping.
    IF NoGraphics THEN END     ' End if no graphics hardware.
    .
    .
    .
    END

    ' Error-handling routine:
    Message:
        PRINT "This program requires graphics capability."
        NoGraphics = TRUE
        RESUME NEXT

    If the user has only a monochrome display with no graphics adapter, the
    SCREEN statement produces an error that in turn triggers a branch to the
    error-handling routine Message. (See Chapter 6, "Error and Event
    Trapping," for more information on error trapping.)


5.2  Pixels and Screen Coordinates

    Shapes and figures on a video display are composed of individual dots of
    light known as picture elements or "pixels" (or sometimes as "pels") for
    short. BASIC draws and paints on the screen by turning these pixels on or
    off and by changing their colors.

    A typical screen is composed of a grid of pixels. The exact number of
    pixels in this grid depends on the hardware you have installed and the
    screen mode you have selected in the SCREEN statement. The larger the
    number of pixels, the higher the clarity of graphics output. For example,
    a SCREEN 1 statement gives a resolution of 320 pixels horizontally by 200
    pixels vertically (320 x 200 pixels), while a SCREEN 2 statement gives a
    resolution of 640 x 200 pixels. The higher horizontal density in screen
    mode 2──640 pixels per row versus 320 pixels per row──gives images a
    sharper, less ragged appearance than they have in screen mode 1.

    Depending on the graphics capability of your system, you can use other
    screen modes that support even higher resolutions (as well as adjust other
    screen characteristics). Consult the QB Advisor for more information.

    When your screen is in one of the graphics modes, you can locate each
    pixel by means of pairs of coordinates. The first number in each
    coordinate pair tells the number of pixels from the left side of the
    screen, while the second number in each pair tells the number of pixels
    from the top of the screen. For example, in screen mode 2 the point in the
    extreme upper-left corner of the screen has coordinates (0, 0), the point
    in the center of the screen has coordinates (320, 100), and the point in
    the extreme lower-right corner of the screen has coordinates (639, 199),
    as shown in Figure 5.1.

    BASIC uses these screen coordinates to determine where to display graphics
    (for example, to locate the end points of a line or the center of a
    circle), as shown in Section 5.3, "Drawing Basic Shapes."

    Graphics coordinates differ from text-mode coordinates specified in a
    LOCATE statement. First, LOCATE is not as precise: graphics coordinates
    pinpoint individual pixels on the screen, whereas coordinates used by
    LOCATE are character positions. Second, text-mode coordinates are given in
    the form "row, column," as in the following:

    ' Move to the 13th row, 15th column,
    ' then print the message shown:
    LOCATE 13, 15
    PRINT "This should appear in the middle of the screen."

    This is the reverse of graphics coordinates, which are given in the form
    "column, row." A LOCATE statement has no effect on the positioning of
    graphics output on the screen.


    ┌──────────────────────────────────────────────────────────────────┐
    │                              x positive                          │
    │              ────────────────────────────────────────────→       │
    │            ┌──────────────────────────────────────────────────┐  │
    │          │ │°(0,0)                °(320,0)            (639,0)°│  │
    │          │ │                                                  │  │
    │          │ │                                                  │  │
    │          │ │                                                  │  │
    │          │ │                                                  │  │
    │   y      │ │                      °(320,100)                  │  │
    │ positive │ │                                                  │  │
    │          │ │                                                  │  │
    │          │ │                                                  │  │
    │          │ │                                                  │  │
    │          │ │                                                  │  │
    │          ↓ │°(0,199)              °(320,199)        (639,199)°│  │
    │            └──────────────────────────────────────────────────┘  │
    └──────────────────────────────────────────────────────────────────┘

    Figure 5.1  Coordinates of Selected Pixels in Screen Mode 2


5.3  Drawing Basic Shapes: Points, Lines, Boxes, and Circles

    You can pass coordinate values to BASIC graphics statements to produce a
    variety of simple figures, as shown in Sections 5.3.1-5.3.2.

5.3.1  Plotting Points with PSET and PRESET

    The most fundamental level of control over graphics output is simply
    turning individual pixels on and off. You do this in BASIC with the PSET
    (for pixel set) and PRESET (for pixel reset) statements. The statement
    PSET (x, y) gives the pixel at location (x, y) the current foreground
    color. The PRESET (x, y) statement gives the pixel at location (x, y) the
    current background color.

    On monochrome monitors, the foreground color is the color that is used for
    printed text and is typically white, amber, or light green; the background
    color on monochrome monitors is typically black or dark green. You can
    choose another color for PSET and PRESET to use by adding an optional
    color argument. The syntax is then:

    PSET (x, y), color

    or

    PRESET (x, y), color

    See Section 5.7 for more information on choosing colors.

    Because PSET uses the current foreground color by default and PRESET uses
    the current background color by default, PRESET without a color argument
    erases a point drawn with PSET, as shown in the next example:

    SCREEN 2          ' 640 x 200 resolution
    PRINT "Press any key to end."

    DO

        ' Draw a horizontal line from the left to the right:
        FOR X = 0 TO 640
        PSET (X, 100)
        NEXT

        ' Erase the line from the right to the left:
        FOR X = 640 TO 0 STEP -1
        PRESET (X, 100)
        NEXT

    LOOP UNTIL INKEY$ <> ""
    END

    While it is possible to draw any figure using only PSET statements to
    manipulate individual pixels, the output tends to be rather slow, since
    most pictures consist of many pixels. BASIC has several statements that
    dramatically increase the speed with which simple figures──such as lines,
    boxes, and ellipses──are drawn, as shown in Sections 5.3.2 and
    5.4.1-5.4.4.

5.3.2  Drawing Lines and Boxes with LINE

    When using PSET or PRESET, you specify only one coordinate pair since you
    are dealing with only one point on the screen. With LINE, you specify two
    pairs, one for each end of a line segment. The simplest form of the LINE
    statement is as follows:

    LINE (x1, y1) - (x2, y2)

    where (x1, y1) are the coordinates of one end of a line segment and (x2,
    y2) are the coordinates of the other. For example, the following statement
    draws a straight line from the pixel with coordinates (10, 10) to the
    pixel with coordinates (150, 200):

    SCREEN 1
    LINE (10, 10)-(150, 200)

    Note that BASIC does not care about the order of the coordinate pairs: a
    line from (x1, y1) to (x2, y2) is the same as a line from (x2, y2) to (x1,
    y1). This means you could also write the preceding statement as:

    SCREEN 1
    LINE (150, 200)-(10, 10)

    However, reversing the order of the coordinates could have an effect on
    graphics statements that follow, as shown in the next section.

    5.3.2.1  Using the STEP Option

    Up to this point, screen coordinates have been presented as absolute
    values measuring the horizontal and vertical distances from the extreme
    upper-left corner of the screen, which has coordinates (0, 0). However, by
    using the STEP option in any of the following graphics statements, you can
    make the coordinates that follow STEP relative to the last point
    referenced on the screen:

    CIRCLE
    GET
    LINE
    PAINT
    PRESET
    PSET
    PUT

    If you picture images as being drawn on the screen by a tiny paintbrush
    exactly the size of one pixel, then the last point referenced is the
    location of this paintbrush, or "graphics cursor," when it finishes
    drawing an image. For example, the following statements leave the graphics
    cursor at the pixel with coordinates (100, 150):

    SCREEN 2
    LINE (10, 10)-(100, 150)

    If the next graphics statement in the program is

    PSET STEP(20, 20)

    then the point plotted by PSET is not in the upper-left quadrant of the
    screen. Instead, the STEP option has made the coordinates (20, 20)
    relative to the last point referenced, which has coordinates (100, 150).
    This makes the absolute coordinates of the point (100 + 20, 150 + 20) or
    (120, 170).

    In the last example, the last point referenced is determined by a
    preceding graphics statement. You can also establish a reference point
    within the same statement, as shown in the next example:

    ' Set 640 x 200 pixel resolution, and make the last
    ' point referenced the center of the screen:
    SCREEN 2

    ' Draw a line from the lower-left corner of the screen
    ' to the upper-left corner:
    LINE STEP(-310, 100) -STEP(0, -200)

    ' Draw the "stair steps" down from the upper-left corner
    ' to the right side of the screen:
    FOR I% = 1 TO 20
        LINE -STEP(30, 0)     ' Draw the horizontal line.
        LINE -STEP(0, 5)      ' Draw the vertical line.
    NEXT

    ' Draw the unconnected vertical line segments from the
    ' right side to the lower-left corner:
    FOR I% = 1 TO 20
        LINE STEP(-30, 0) -STEP(0, 5)
    NEXT

    DO: LOOP WHILE INKEY$ = ""       ' Wait for a keystroke.

    Output

    ┌──────────────────────────────────────────────────────────────────┐
    │ ┌─┐                                                              │
    │ │ └──────┐                                                       │
    │ │        └──────┐                                                │
    │ │               └──────┐                                         │
    │ │                      └──────┐                                  │
    │ │                             └──────┐                           │
    │ │                                    └──────┐                    │
    │ │                                           └──────┐             │
    │ │                                                  └──────┐      │
    │ │                                                                │
    │ │                                                  │             │
    │ │                                           │                    │
    │ │                                    │                           │
    │ │                             │                                  │
    │ │                      │                                         │
    │ │               │                                                │
    │ │        │                                                       │
    │ │ │                                                              │
    └──────────────────────────────────────────────────────────────────┘

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Note the empty DO loop at the end of the last program. If you are
    running a compiled, stand-alone BASIC program that produces graphics
    output, your program needs some mechanism like this at the end to hold
    the output on the screen. Otherwise, it vanishes from the screen before
    the user has time to notice it.
    ──────────────────────────────────────────────────────────────────────────

    5.3.2.2  Drawing Boxes

    Using the forms of the LINE statement already presented, it is quite easy
    to write a short program that connects four straight lines to form a box,
    as shown here:

    SCREEN 1        ' 320 x 200 pixel resolution

    ' Draw a box measuring 120 pixels on a side:
    LINE (50, 50)-(170, 50)
    LINE -STEP(0, 120)
    LINE -STEP(-120, 0)
    LINE -STEP(0, -120)

    However, BASIC provides an even simpler way to draw a box, using a single
    LINE statement with the B (for box) option. The B option is shown in the
    next example, which produces the same output as the four LINE statements
    in the preceding program:

    SCREEN 1        ' 320 x 200 pixel resolution

    ' Draw a box with coordinates (50, 50) for the upper-left
    ' corner, and (170, 170) for the lower-right corner:
    LINE (50, 50)-(170, 170), , B

    When you add the B option, the LINE statement no longer connects the two
    points you specify with a straight line; instead, it draws a rectangle
    whose opposite corners (upper left and lower right) are at the locations
    specified.

    Two commas precede the B in the last example. The first comma functions
    here as a placeholder for an unused option (the color argument), which
    allows you to pick the color for a line or the sides of a box. (See
    Section 5.7 for more information on the use of color.)

    As before, it does not matter what order the coordinate pairs are given
    in, so the rectangle from the last example could also be drawn with this
    statement:

    LINE (170, 170)-(50, 50), , B

    Adding the F (for fill) option after B fills the interior of the box with
    the same color used to draw the sides. With a monochrome display, this is
    the same as the foreground color used for printed text. If your hardware
    has color capabilities, you can change this color with the optional color
    argument (see Section 5.7.1, "Selecting a Color for Graphics Output").

    The syntax introduced here for drawing a box is the general syntax used in
    BASIC to define a rectangular graphics region, and it also appears in the
    GET and VIEW statements:

    {GET| LINE| VIEW} (x1, y1) - (x2, y2),...

    Here, (x1, y1) and (x2, y2) are the coordinates of diagonally opposite
    corners of the rectangle (upper left and lower right). (See Section 5.5,
    "Defining a Graphics Viewport," for a discussion of VIEW, and Section
    5.10, "Basic Animation Techniques," for information on GET and PUT.)

    5.3.2.3  Drawing Dotted Lines

    The previous sections explain how to use LINE to draw solid lines and use
    them in rectangles; that is, no pixels are skipped. Using yet another
    option with LINE, you can draw dashed or dotted lines instead. This
    process is known as "line styling." The following is the syntax for
    drawing a single dashed line from point (x1, y1) to point (x2, y2) using
    the current foreground color:

    LINE (x1,y1) - (x2,y2), ,[[B]], style

    Here style is a 16-bit decimal or hexadecimal integer. The LINE statement
    uses the binary representation of the line-style argument to create dashes
    and blank spaces, with a 1 bit meaning "turn on the pixel," and a 0 bit
    meaning "leave the pixel off." For example, the hexadecimal integer &HCCCC
    is equal to the binary integer 1100110011001100, and when used as a style
    argument it draws a line alternating two pixels on, two pixels off.

    Example

    The following example shows different dashed lines produced using
    different values for style:

    SCREEN 2        ' 640 x 200 pixel resolution

    ' Style data:
    DATA &HCCCC, &HFF00, &HF0F0
    DATA &HF000, &H7777, &H8888

    Row% = 4
    Column% = 4
    XLeft% = 60
    XRight% = 600
    Y% = 28

    FOR I% = 1 TO 6
        READ Style%
        LOCATE Row%, Column%
        PRINT HEX$(Style%)
        LINE (XLeft%, Y%)-(XRight%,Y%), , , Style%
        Row% = Row% + 3
        Y% = Y% + 24
    NEXT

    Output

    ┌───────────────────────────────────────────────────────────────────┐
    │                                                                   │
    │                                                                   │
    │ CCCC····························································· │
    │                                                                   │
    │ FF00------------------------------------------------------------- │
    │                                                                   │
    │ F0F0••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• │
    │                                                                   │
    │ F000- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - │
    │                                                                   │
    │ 7777───────────────────────────────────────────────────────────── │
    │                                                                   │
    │ 8888............................................................. │
    │                                                                   │
    │                                                                   │
    │                                                                   │
    └───────────────────────────────────────────────────────────────────┘


5.4  Drawing Circles and Ellipses with CIRCLE

    The CIRCLE statement draws a variety of circular and elliptical, or oval,
    shapes. In addition, CIRCLE draws arcs (segments of circles), and
    pie-shaped wedges. In graphics mode you can produce just about any kind of
    curved line with some variation of CIRCLE.

5.4.1  Drawing Circles

    To draw a circle, you need to know only two things: the location of its
    center and the length of its radius (the distance from the center to any
    point on the circle). With this information and a reasonably steady hand
    (or better yet, a compass), you can produce an attractive circle.

    Similarly, BASIC needs only the location of a circle's center and the
    length of its radius to draw a circle. The simplest form of the CIRCLE
    syntax is

    CIRCLE[[STEP]](x, y), radius

    where x, y are the coordinates of the center, and radius is the radius of
    the circle. The next example draws a circle with center (200, 100) and
    radius 75:

    SCREEN 2
    CIRCLE (200, 100), 75

    You could rewrite the preceding example as follows, making the same circle
    but using the STEP option to make the coordinates relative to the center
    rather than to the upper-left corner:

    SCREEN 2       ' Uses center of screen (320,100) as the
                    ' reference point for the CIRCLE statement:
    CIRCLE STEP(-120, 0), 75

5.4.2  Drawing Ellipses

    The CIRCLE statement automatically adjusts the "aspect ratio" to make sure
    that circles appear round and not flattened on your screen. However, you
    may need to adjust the aspect ratio to make circles come out right on your
    monitor, or you may want to change the aspect ratio to draw the oval
    figure known as an ellipse. In either case, use the syntax

    CIRCLE[[STEP]] (x,y), radius, , , , aspect

    where aspect is a positive real number. (See Section 5.4.5 for more
    information on the aspect ratio and how to calculate it for different
    screen modes.)

    The extra commas between radius and aspect are placeholders for other
    options that tell CIRCLE which color to use (if you have a color
    monitor/adapter and are using one of the screen modes that support color),
    or whether to draw an arc or wedge. (See Sections 5.4.3, "Drawing Arcs,"
    and 5.7.1, "Selecting a Color for Graphics Output," for more information
    on these options.)

    Since the aspect argument specifies the ratio of the vertical to
    horizontal dimensions, large values for aspect produce ellipses stretched
    out along the vertical axis, while small values for aspect produce
    ellipses stretched out along the horizontal axis. Since an ellipse has two
    radii──one horizontal x-radius and one vertical y-radius──BASIC uses the
    single radius argument in a CIRCLE statement as follows: if aspect is less
    than one, then radius is the x-radius; if aspect is greater than or equal
    to one, then radius is the y-radius.

    Example

    The following example and its output show how different aspect values
    affect whether the CIRCLE statement uses the radius argument as the
    x-radius or the y-radius of an ellipse:

    SCREEN 1

    ' This draws the ellipse on the left:
    CIRCLE (60, 100), 80, , , , 3

    ' This draws the ellipse on the right:
    CIRCLE (180, 100), 80, , , , 3/10

    Output

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 160 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

5.4.3  Drawing Arcs

    An arc is a segment of a ellipse, in other words a short, curved line. To
    understand how the CIRCLE statement draws arcs, you need to know how BASIC
    measures angles.

    BASIC uses the radian as its unit of angle measure, not only in the CIRCLE
    statement, but also in the intrinsic trigonometric functions such as COS,
    SIN, or TAN. (The one exception to this use of radians is the DRAW
    statement, which expects angle measurements in degrees. See Section 5.9
    for more information about DRAW.)

    The radian is closely related to the radius of a circle. In fact, the word
    "radian" is derived from the word "radius." The circumference of a circle
    equals 2 * π * radius, where π is equal to approximately 3.14159265.
    Similarly, the number of radians in one complete angle of revolution (or
    360) equals 2 * π, or a little more than 6.28. If you are more used to
    thinking of angles in terms of degrees, here are some common equivalences:

    Angle in Degrees         Angle in Radians
    ──────────────────────────────────────────────────────────────────────────
    360                      2π    (approximately 6.283)

    180                      π    (approximately 3.142)

    90                       π/2  (approximately 1.571)

    60                       π/3  (approximately 1.047)
    ──────────────────────────────────────────────────────────────────────────

    If you picture a clock face on the screen, CIRCLE measures angles by
    starting at the "3:00" position and rotating counterclockwise, as shown in
    Figure 5.2:


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 5.2 can be found on       │
    │   page 161 of the printed manual.  │
    │                                    │
    └────────────────────────────────────┘

    Figure 5.2  How Angles Are Measured for CIRCLE

    The general formula for converting from degrees to radians is to multiply
    degrees by π/180.

    To draw an arc, give angle arguments defining the arc's limits:

    CIRCLE[[STEP]] (x, y), radius,[[color]], start, end [[, aspect]]

    Example

    The CIRCLE statements in the next example draw seven arcs, with the
    innermost arc starting at the "3:00" position (0 radians) and the
    outermost arc starting at the "6:00" position (3π/2 radians), as you can
    see from the output:

    SCREEN 2
    CLS

    CONST PI = 3.141592653589#      ' Double-precision constant

    StartAngle = 0
    FOR Radius% = 100 TO 220 STEP 20
        EndAngle = StartAngle + (PI / 2.01)
        CIRCLE (320, 100), Radius%, , StartAngle, EndAngle
        StartAngle = StartAngle + (PI / 4)
    NEXT Radius%

    Output

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 162 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

5.4.4  Drawing Pie Shapes

    By making either of CIRCLE's start or end arguments negative, you can
    connect the arc at its beginning or ending point with the center of the
    circle. By making both arguments negative, you can draw shapes ranging
    from a wedge that resembles a slice of pie to the pie itself with the
    piece missing.

    Example

    This example draws a pie shape with a piece missing:

    SCREEN 2

    CONST RADIUS = 150, PI = 3.141592653589#

    StartAngle = 2.5
    EndAngle = PI

    ' Draw the wedge:
    CIRCLE (320, 100), RADIUS, , -StartAngle, -EndAngle

    ' Swap the values for the start and end angles:
    SWAP StartAngle, EndAngle

    ' Move the center 10 pixels down and 70 pixels to the
    ' right, then draw the "pie" with the wedge missing:
    CIRCLE STEP(70, 10), RADIUS, , -StartAngle, -EndAngle

    Output

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 163 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

5.4.5  Drawing Shapes to Proportion with the Aspect Ratio

    As discussed in Section 5.4.2, "Drawing Ellipses," BASIC's CIRCLE
    statement automatically corrects the aspect ratio, which determines how
    figures are scaled on the screen. However, with other graphics statements
    you need to scale horizontal and vertical dimensions yourself to make
    shapes appear with correct proportions. For example, although the
    following statement draws a rectangle that measures 100 pixels on all
    sides, it does not look like a square:

    SCREEN 1
    LINE (0, 0)-(100, 100), , B

    In fact, this is not an optical illusion; the rectangle really is taller
    than it is wide. This is because in screen mode 1 there is more space
    between pixels vertically than horizontally. To draw a perfect square, you
    have to change the aspect ratio.

    The aspect ratio is defined as follows: in a given screen mode consider
    two lines, one vertical and one horizontal, that appear to have the same
    length. The aspect ratio is the number of pixels in the vertical line
    divided by the number of pixels in the horizontal line. This ratio depends
    on two factors:

    1. Because of the way pixels are spaced on most screens, a horizontal row
        has more pixels than a vertical column of the exact same physical
        length in all screen modes except modes 11 and 12.

    2. The standard personal computer's video-display screen is wider than it
        is high. Typically, the ratio of screen width to screen height is 4:3.

    To see how these two factors interact to produce the aspect ratio,
    consider a screen after a SCREEN 1 statement, which gives a resolution of
    320 x 200 pixels. If you draw a rectangle from the top of the screen to
    the bottom, and from the left side of the screen three-fourths of the way
    across, you have a square, as shown in Figure 5.3.


    ┌────────────────────────────────────────────────────────────────┐
    │ ┌──────────────────────────────────────────────────────┐       │
    │ │ ┌───────────────────────────────────┬─ - - - - - -   │       │
    │ │ │                                   │      ↑         │       │
    │ │ │                                   │      │         │       │
    │ │ │                                   │      │         │       │
    │ │ │                                   │      │         │       │
    │ │ │ ←────────── 240 pixels───────────→│   200 pixels   │       │
    │ │ │                                   │      │         │       │
    │ │ │                                   │      │         │       │
    │ │ │                                   │      │         │       │
    │ │ │                                   │      │         │       │
    │ │ │                                   │      │         │       │
    │ │ │                                   │      ↓         │       │
    │ │ └───────────────────────────────────┴─ - - - - - - - │       │
    │ │ ←──────────────────320 pixels───────────────────────→│       │
    │ └──────────────────────────────────────────────────────┘       │
    │                                                                │
    └────────────────────────────────────────────────────────────────┘
    Figure 5.3  The Aspect Ratio In Screen Mode 1

    As you can see from the diagram, this square has a height of 200 pixels
    and a width of 240 pixels. The ratio of the square's height to its width
    (200 / 240, or when simplified, 5 / 6) is the aspect ratio for this screen
    resolution. In other words, to draw a square in 320 x 200 resolution, make
    its height in pixels equal to 5 / 6 times its width in pixels, as shown in
    the next example:

    SCREEN 1        ' 320 x 200 pixel resolution

    ' The height of this box is 100 pixels, and the width is
    ' 120 pixels, which makes the ratio of the height to the
    ' width equal to 100/120, or 5/6. The result is a square:
    LINE (50, 50) -STEP(120, 100), , B

    The formula for calculating the aspect ratio for a given screen mode is

    (4 / 3) * (ypixels / xpixels)

    where xpixelsby ypixels is the current screen resolution. In screen mode
    1, this formula shows the aspect ratio to be (4 / 3) * (200 / 320), or 5 /
    6; in screen mode 2, the aspect ratio is (4 / 3) * (200 / 640), or 5 / 12.

    If you have a video display with a ratio of width to height that is not
    equal to 4:3, use the more general form of the formula for computing the
    aspect ratio:

    (screenwidth / screenheight) * (ypixels / xpixels)

    The CIRCLE statement can be made to draw an ellipse by varying the value
    of the aspect argument, as shown above in Section 5.4.2.


5.5  Defining a Graphics Viewport

    The graphics examples presented so far have all used the entire
    video-display screen as their drawing board, with absolute coordinate
    distances measured from the extreme upper-left corner of the screen.

    However, using the VIEW statement you can also define a kind of miniature
    screen (known as a "graphics viewport") inside the boundaries of the
    physical screen. Once it is defined, all graphics operations take place
    within this viewport. Any graphics output outside the viewport boundaries
    is "clipped"; that is, any attempt to plot a point outside the viewport is
    ignored. There are two main advantages to using a viewport:

    1. A viewport makes it easy to change the size and position of the screen
        area where graphics appear.

    2. Using CLS 1, you can clear the screen inside a viewport without
        disturbing the screen outside the viewport.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Refer to Section 3.1.6 to learn how to create a "text viewport" for
    output printed on the screen.
    ──────────────────────────────────────────────────────────────────────────

    The general syntax for VIEW (note that the STEP option is not allowed with
    VIEW) is

    VIEW[[[[SCREEN]] (x1, y1) - (x2, y2)[[,[[color]], [[border]]]]]]

    where (x1, y1) and (x2, y2) define the corners of the viewport, using the
    standard BASIC syntax for rectangles (see Section 5.3.2.2, "Drawing
    Boxes"). The optional color and border arguments allow you to choose a
    color for the interior and edges, respectively, of the viewport rectangle.
    See Section 5.7 below for more information on setting and changing
    colors.

    The VIEW statement without arguments makes the entire screen the viewport.
    Without the SCREEN option, the VIEW statement makes all pixel coordinates
    relative to the viewport, rather than the full screen. In other words,
    after the statement

    VIEW (50, 60)-(150, 175)

    the pixel plotted with

    PSET (10, 10)

    is visible, since it is 10 pixels below and 10 pixels to the right of the
    upper-left corner of the viewport. Note that this makes the pixel's
    absolute screen coordinates (50 + 10, 60 + 10) or (60, 70).

    In contrast, the VIEW statement with the SCREEN option keeps all
    coordinates absolute; that is, coordinates measure distances from the side
    of the screen, not from the sides of the viewport. Therefore, after the
    statement

    VIEW SCREEN (50, 60)-(150, 175)

    the pixel plotted with

    PSET (10, 10)

    is not visible, since it is 10 pixels below and 10 pixels to the right of
    the upper-left corner of the screen──outside the viewport.

    Examples

    The output from the next two examples should further clarify the
    distinction between VIEW and VIEW SCREEN:

    SCREEN 2

    VIEW (100, 50)-(450, 150), , 1

    ' This circle's center point has absolute coordinates
    ' (100 + 100, 50 + 50), or (200, 100):
    CIRCLE (100, 50), 50

    Output using VIEW

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 167 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

    SCREEN 2

    ' This circle's center point has absolute coordinates
    ' (100, 50), so only part of the circle appears
    '  within the view port:
    VIEW SCREEN (100, 50)-(450, 150), , 1
    CIRCLE (100, 50), 50

    Output using VIEW SCREEN

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 167 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

    Note that graphics output located outside the current viewport is clipped
    by the viewport's edges and does not appear on the screen.


5.6  Redefining Viewport Coordinates with WINDOW

    This section shows you how to use the WINDOW statement and your own
    coordinate system to redefine pixel coordinates.

    In Sections 5.2-5.5, the coordinates used to locate pixels on the screen
    represent actual physical distances from the upper-left corner of the
    screen (or the upper-left corner of the current viewport, if it has been
    defined with a VIEW statement). These are known as "physical coordinates."
    The "origin," or reference point, for physical coordinates is always the
    upper-left corner of the screen or viewport, which has coordinates (0, 0).

    As you move down the screen and to the right, x values (horizontal
    coordinates) and y values (vertical coordinates) get bigger, as shown in
    the upper diagram of Figure 5.4. While this scheme is standard for video
    displays, it may seem counterintuitive if you have used other coordinate
    systems to draw graphs. For example, on the Cartesian grid used in
    mathematics, y values get bigger toward the top of a graph and smaller
    toward the bottom.

    With BASIC's WINDOW statement, you can change the way pixels are addressed
    to use any coordinate system you choose, thus freeing you from the
    limitations of using physical coordinates.

    The general syntax for WINDOW is

    WINDOW [[[[SCREEN]] (x1, y1) - (x2, y2)]]

    where y1, y2, x1, and x2 are real numbers specifying the top, bottom,
    left, and right sides of the window, respectively. These numbers are known
    as "view coordinates." For example, the following statement remaps your
    screen so that it is bounded on the top and bottom by the lines y = 10 and
    y = -15 and on the left and right by the lines x = -25 and x = 5 :

    WINDOW (-25, -15)-(5, 10)

    After a WINDOW statement, y values get bigger toward the top of the
    screen. In contrast, after a WINDOW SCREEN statement, y values get bigger
    toward the bottom of the screen. The bottom half of Figure 5.4 shows the
    effects of both a WINDOW statement and a WINDOW SCREEN statement on a line
    drawn in screen mode 2. Note also how both of these statements change the
    coordinates of the screen corners. A WINDOW statement with no arguments
    restores the regular physical-coordinate system.

    Example


    ┌──────────────────────────────────────────────────────────────────────────
    │                               Increasing X
    │                     ─────────────────────────────→
    │                     ┌────────────────────────────┐
    │                   │ │°(0,0)              (639,0)°│
    │                   │ │                            │
    │        Increasing │ │                            │
    │             Y     │ │                            │
    │                   │ │                            │
    │                   │ │                            │
    │                   │ │°(0,199)          (639,199)°│
    │                   ↓ └────────────────────────────┘
    │                                   ^
    │                                 /   \
    │                                /      \
    │       WINDOW (-25, -15) - (5, 10)   WINDOW SCREEN (-25, -15) - (5, 10)
    │       LINE (-15, -10  - (-5, -5)    LINE (-15, -10) - (-5, -5)
    │                            /              \
    │                          /                  \
    │                        /                      \
    │                                                       Increasing X
    │                                                ─────────────────────────→
    │            ┌─────────────────────┐             ┌────────────────────────┐
    │           ↑│°(-25,10)     (5,10)°│            ││°(-25,-15)    (5,-15)°  │
    │           ││                     │            ││        °(-15,-10)      │
    │           ││           (0,0)°    │            ││             \          │
    │ Increasing││                     │  Increasing││              °(-5,-5)  │
    │      Y    ││       (-5,-5)°      │       Y    ││                        │
    │           ││(-15,-10)  /         │            ││                   °    │
    │           ││         °           │            ││               (0,0)    │
    │           ││°(-25,-15)   (5,-15)°│            ││°(-25,10)        (5,10)°│
    │            └─────────────────────┘            ↓└────────────────────────┘
    │            ──────────────────────→
    │                 Increasing X
    └──────────────────────────────────────────────────────────────────────────

    Figure 5.4  WINDOW Contrasted with WINDOW SCREEN

    The following example uses both VIEW and WINDOW to simplify writing a
    program to graph the sine-wave function for angle values from 0 radians to
    p radians (or 0° to 180°). This program is in the file named SINEWAVE.BAS
    on the QuickBASIC distribution disks.

    SCREEN 2

    ' Viewport sized to proper scale for graph:
    VIEW (20, 2)-(620, 172), , 1
    CONST PI = 3.141592653589#

    ' Make window large enough to graph sine wave from
    ' 0 radians to pi radians:
    WINDOW (0, -1.1)-(PI, 1.1)
    Style% = &HFF00                 ' Use to make dashed line.
    VIEW PRINT 23 TO 24  ' Scroll printed output in rows 23, 24.
    DO
        PRINT TAB(20);
        INPUT "Number of cycles (0 to end): ", Cycles
        CLS
        LINE (PI, 0)-(0, 0), , , Style%  ' Draw the x axis.
        IF Cycles > 0 THEN

        ' Start at (0,0) and plot the graph:
        FOR X = 0 TO PI STEP .01
            Y = SIN(Cycles * X)    ' Calculate the y coordinate.
            LINE -(X, Y)           ' Draw a line to new point.
        NEXT X
        END IF
    LOOP WHILE Cycles > 0

    Output

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 170 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

5.6.1  The Order of Coordinate Pairs

    As with the other BASIC graphics statements that define rectangular areas
    (GET, LINE, and VIEW), the order of coordinate pairs in a WINDOW statement
    is unimportant. The first pair of statements below has the same effect as
    the second pair of statements:

    VIEW (100, 20)-(300, 120)
    WINDOW (-4, -3)-(0, 0)

    VIEW (300, 120)-(100, 20)
    WINDOW (0, 0)-(-4, -3)

5.6.2  Keeping Track of View and Physical Coordinates

    The PMAP and POINT functions are useful for keeping track of physical and
    view coordinates. POINT(number) tells you the current location of the
    graphics cursor by returning either the physical or view coordinates
    (depending on the value for number) of the last point referenced in a
    graphics statement. PMAP allows you to translate physical coordinates to
    view coordinates and vice versa. The physical-coordinate values returned
    by PMAP are always relative to the current viewport.

    Examples

    The following example shows the different values that are returned by
    POINT (number) for number values of 0, 1, 2, or 3:

    SCREEN 2
    ' Define the view-coordinate window:
    WINDOW (-10, -30)-(-5, -10)
    ' Draw a line from the point with view coordinates (-9,-28)
    ' to the point with view coordinates (-6,-24):
    LINE (-9, -28)-(-6, -24)

    PRINT "Physical x-coordinate of the last point = " POINT(0)
    PRINT "Physical y-coordinate of the last point = " POINT(1)
    PRINT
    PRINT "View x-coordinate of the last point  = " POINT(2)
    PRINT "View y-coordinate of the last point  = " POINT(3)

    END

    Output

    Physical x-coordinate of the last point = 511
    Physical y-coordinate of the last point = 139

    View x-coordinate of the last point  = -6
    View y-coordinate of the last point  = -24

    Given the WINDOW statement in the preceding example, the next four PMAP
    statements would print the output that follows:

    ' Map the view x-coordinate -6 to physical x and print:
    PhysX% = PMAP(-6, 0)
    PRINT PhysX%

    ' Map the view y-coordinate -24 to physical y and print:
    PhysY% = PMAP(-24, 1)
    PRINT PhysY%

    ' Map physical x back to view x and print:
    ViewX% = PMAP(PhysX%, 2)
    PRINT ViewX%

    ' Map physical y back to view y and print:
    ViewY% = PMAP(PhysY%, 3)
    PRINT ViewY%

    Output

    511
    139
    -6
    -24


5.7  Using Colors

    If you have a Color Graphics Adapter (CGA), you can choose between the
    following two graphics modes only:

    1. Screen mode 2 has 640 x 200 high resolution, with only one foreground
        and one background color. This is known as "monochrome," since all
        graphics output has the same color.

    2. Screen mode 1 has 320 x 200 medium resolution with 4 foreground colors
        and 16 background colors.

    There is thus a tradeoff between color and clarity in the two screen modes
    supported by most color-graphics display adapter hardware. Depending on
    the graphics capability of your system, you may not have to sacrifice
    clarity to get a full range of color. However, this section focuses on
    screen modes 1 and 2.

5.7.1  Selecting a Color for Graphics Output

    The following list shows where to put the color argument in the graphics
    statements discussed in previous sections of this chapter. This list also
    shows other options (such as BF with the LINE statement or border with the
    VIEW statement) that can have a different colors. (Please note that these
    do not give the complete syntax for some of these statements. This summary
    is intended to show how to use the color option in those statements that
    accept it.)

    PSET (x, y), color
    PRESET (x, y), color
    LINE (x1, y1) - (x2, y2), color [[, B[[F]]]]
    CIRCLE (x, y), radius, color
    VIEW (x1, y1) - (x2, y2), color, border

    In screen mode 1, the color argument is a numeric expression with the
    value 0, 1, 2, or 3. Each of these values, known as an "attribute,"
    represents a different color, as demonstrated by the following program:

    ' Draw an "invisible" line (same color as background):
    LINE (10, 10)-(310, 10), 0

    ' Draw a light blue (cyan) line:
    LINE (10, 30)-(310, 30), 1

    ' Draw a purple (magenta) line:
    LINE (10, 50)-(310, 50), 2

    ' Draw a white line:
    LINE (10, 70)-(310, 70), 3
    END

    As noted in the comments for the preceding example, a color value of 0
    produces no visible output, since it is always equal to the current
    background color. At first glance, this may not seem like such a useful
    color value, but in fact it is useful for erasing a figure without having
    to clear the entire screen or viewport, as shown in the next example:

    SCREEN 1

    CIRCLE (100, 100), 80, 2, , , 3   ' Draw an ellipse.
    Pause$ = INPUT$(1)                ' Wait for a key press.
    CIRCLE (100, 100), 80, 0, , , 3   ' Erase the ellipse.

5.7.2  Changing the Foreground or Background Color

    Section 5.7.1 above describes how to use 4 different foreground colors
    for graphics output. You have a wider variety of colors in screen mode 1
    for the screen's background: 16 in all.

    In addition, you can change the foreground color by using a different
    "palette." In screen mode 1, there are two palettes, or groups of four
    colors. Each palette assigns a different color to the same attribute; so,
    for instance, in palette 1 (the default) the color associated with
    attribute 2 is magenta, while in palette 0 the color associated with
    attribute 2 is red. If you have a CGA, these colors are predetermined for
    each palette; that is, the color assigned to number 2 in palette 1 is
    always magenta, while the color assigned to number 2 in palette 0 is
    always red.

    If you have an Enhanced Graphics Adapter (EGA) or Video Graphics Adapter
    (VGA), you can use the PALETTE statement to choose the color displayed by
    any attribute. For example, by changing arguments in a PALETTE statement,
    you could make the color displayed by attribute 1 green one time and brown
    the next. (See Section 5.7.3, "Changing Colors with PALETTE and PALETTE
    USING," in this manual for more information on reassigning colors.)

    In screen mode 1, the COLOR statement allows you to control both the
    background color and the palette for the foreground colors. Here is the
    syntax for COLOR in screen mode 1:

    COLOR[[background]] [[, palette]]

    The background argument is a numeric expression from 0 to 15, and palette
    is a numeric expression equal to either 0 or 1.

    Table 5.1 shows the colors produced with the 4 different foreground
    numbers in each of the two palettes, while Table 5.2 shows the colors
    produced with the 16 different background numbers.

    Table 5.1  Color Palettes in Screen Mode 1
    Foreground Color Number  Color in Palette 0      Color in Palette 1
    ──────────────────────────────────────────────────────────────────────────
    0                        Current background      Current background color
                            color

    1                        Green                   Cyan (bluish green)

    2                        Red                     Magenta (light purple)

    3                        Brown                   White (light grey on some
                                                    monitors)
    ──────────────────────────────────────────────────────────────────────────

    Table 5.2  Background Colors in Screen Mode 1
╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Background Color Number  Color
    ──────────────────────────────────────────────────────────────────────────
    0                        Black

    1                        Blue

    Background Color Number  Color
    ──────────────────────────────────────────────────────────────────────────

    2                        Green

    3                        Cyan

    4                        Red

    5                        Magenta

    6                        Brown (dark yellow on some monitors)

    7                        White (light grey on some monitors)

    8                        Dark grey (black on some monitors)

    9                        Light blue

    10                       Light green

    Background Color Number  Color
    ──────────────────────────────────────────────────────────────────────────

    11                       Light cyan

    12                       Light red

    13                       Light magenta

    14                       Light yellow (may have greenish tinge on some
                            monitors)

    15                       Bright white or very light grey
    ──────────────────────────────────────────────────────────────────────────


    Example

    The following program shows all combinations of the two color palettes
    with the 16 different background screen colors. This program is in the
    file named COLORS.BAS on the QuickBASIC distribution disks.

    SCREEN 1

    Esc$ = CHR$(27)
    ' Draw three boxes and paint the interior
    ' of each box with a different color:
    FOR ColorVal = 1 TO 3
        LINE (X, Y) -STEP(60, 50), ColorVal, BF
        X = X + 61
        Y = Y + 51
    NEXT ColorVal

    LOCATE 21, 1
    PRINT "Press ESC to end."
    PRINT "Press any other key to continue."

    ' Restrict additional printed output to the 23rd line:
    VIEW PRINT 23 TO 23
    DO
        PaletteVal = 1
        DO

        ' PaletteVal is either 1 or 0:
        PaletteVal = 1 - PaletteVal

        ' Set the background color and choose the palette:
        COLOR BackGroundVal, PaletteVal
        PRINT "Background ="; BackGroundVal;
        PRINT "Palette ="; PaletteVal;

        Pause$ = INPUT$(1)        ' Wait for a keystroke.
        PRINT

        ' Exit the loop if both palettes have been shown,
        ' or if the user pressed the ESC key:
        LOOP UNTIL PaletteVal = 1 OR Pause$ = Esc$

        BackGroundVal = BackGroundVal + 1

    ' Exit this loop if all 16 background colors have
    ' been shown, or if the user pressed the ESC key:
    LOOP UNTIL BackGroundVal > 15 OR Pause$ = Esc$

    SCREEN 0                     ' Restore text mode and
    WIDTH 80                     ' 80-column screen width.

5.7.3  Changing Colors with PALETTE and PALETTE USING

    The preceding section showed how you can change the color displayed by an
    attribute simply by specifying a different palette in the COLOR statement.
    However, this restricts you to two fixed color palettes, with just 4
    colors in each. Furthermore, each attribute can stand for only one of two
    possible colors; for example, attribute 1 can signify only green or cyan.

    With an EGA or VGA, your choices are potentially much broader. (If you
    don't have an EGA or VGA, you may want to skip this section.) For
    instance, depending on the amount of video memory available to your
    computer, with a VGA you can choose from a palette with as many as 256K
    (that's right, over 256,000) colors and assign those colors to 256
    different attributes. Even an EGA allows you to display up to 16 different
    colors from a palette of 64 colors.

    In contrast to the COLOR statement, the PALETTE and PALETTE USING
    statements give you a lot more flexibility in manipulating the available
    color palette. Using these statements, you can assign any color from the
    palette to any attribute. For example, after the following statement, the
    output of all graphics statements using attribute 4 appears in light
    magenta (color 13):

    PALETTE 4, 13

    This color change is instantaneous and affects not only subsequent
    graphics statements but any output already on the screen. In other words,
    you can draw and paint your screen, then switch the palette to achieve an
    immediate change of color, as shown by the following example:

    SCREEN 8
    LINE (50, 50)-(150, 150), 4  ' Draws a line in red.
    SLEEP 1                      ' Pauses program.
    PALETTE 4, 13                ' Attribute 4 now means color
                                ' 13, so the line drawn in the
                                ' last statement is now light
                                ' magenta.

    With the PALETTE statement's USING option, you can change the colors
    assigned to every attribute all at once.

    Example

    The following example uses the PALETTE USING statement to give the
    illusion of movement on the screen by constantly rotating the colors
    displayed by attributes 1 through 15. This program is in the file named
    PALETTE.BAS on the QuickBASIC distribution disks.

    DECLARE SUB InitPalette ()
    DECLARE SUB ChangePalette ()
    DECLARE SUB DrawEllipses ()

    DEFINT A-Z
    DIM SHARED PaletteArray(15)

    SCREEN 8                 ' 640 x 200 resolution; 16 colors

    InitPalette              ' Initialize PaletteArray.
    DrawEllipses             ' Draw and paint concentric ellipses.

    DO                       ' Shift the palette until a key
        ChangePalette         ' is pressed.
    LOOP WHILE INKEY$ = ""

    END


    ' ====================== InitPalette ======================
    '    This procedure initializes the integer array used to
    '    change the palette.
    ' =========================================================

    SUB InitPalette STATIC
        FOR I = 0 TO 15
        PaletteArray(I) = I
        NEXT I
    END SUB
    ' ===================== DrawEllipses ======================
    '    This procedure draws 15 concentric ellipses and
    '    paints the interior of each with a different color.
    ' =========================================================

    SUB DrawEllipses STATIC
        CONST ASPECT = 1 / 3
        FOR ColorVal = 15 TO 1 STEP -1
        Radius = 20 * ColorVal
        CIRCLE (320, 100), Radius, ColorVal, , , ASPECT
        PAINT (320, 100), ColorVal
        NEXT
    END SUB


    ' ===================== ChangePalette =====================
    '  This procedure rotates the palette by one each time it
    '  is called. For example, after the first call to
    '  ChangePalette, PaletteArray(1) = 2, PaletteArray(2) = 3,
    '  . . . , PaletteArray(14) = 15, and PaletteArray(15) = 1
    ' =========================================================

    SUB ChangePalette STATIC
        FOR I = 1 TO 15
        PaletteArray(I) = (PaletteArray(I) MOD 15) + 1
        NEXT I
        PALETTE USING PaletteArray(0) ' Shift the color displayed
                                    ' by each of the attributes.
    END SUB


5.8  Painting Shapes

    Section 5.3.2.2 above shows how to draw a box with the LINE statement's B
    option, then paint the box by appending the F (for fill) option:

    SCREEN 1

    ' Draw a square, then paint the interior with color 1
    ' (cyan in the default palette):
    LINE (50, 50)-(110, 100), 1, BF

    With BASIC's PAINT statement, you can fill any enclosed figure with any
    color you choose. PAINT also allows you to fill enclosed figures with your
    own custom patterns, such as stripes or checks, as shown in Section
    5.8.2, "Painting with Patterns."

5.8.1  Painting with Colors

    To paint an enclosed shape with a solid color, use this form of the PAINT
    statement:

    PAINT[[STEP]](x, y) [[,[[interior]],[[border]]]]

    Here, x, y are the coordinates of a point in the interior of the figure
    you want to paint, interior is the number for the color you want to paint
    with, and border is the color number for the outline of the figure.

    For example, the following program lines draw a circle in cyan, then paint
    the inside of the circle magenta:

    SCREEN 1
    CIRCLE (160, 100), 50, 1
    PAINT (160, 100), 2, 1

    The following three rules apply when painting figures:

    1. The coordinates given in the PAINT statement must refer to a point
        inside the figure.

        For example, any one of the following statements would have the same
        effect as the PAINT statements shown in the two preceding examples,
        since all of the coordinates identify points in the interior of the
        circle:

        PAINT (150, 90), 2, 1
        PAINT (170, 110), 2, 1
        PAINT (180, 80), 2, 1

        In contrast, since (5, 5) identifies a point outside the circle, the
        next statement would paint all of the screen except the inside of the
        circle, leaving it colored with the current background color:

        PAINT (5, 5), 2, 1

        If the coordinates in a PAINT statement specify a point right on the
        border of the figure, then no painting occurs:

        LINE (50, 50)-(150, 150), , B  ' Draw a box.
        PAINT (50, 100)  ' The point with coordinates
                        ' (50, 100) is on the top edge of the
                        ' box, so no painting occurs.

    2. The figure must be completely enclosed; otherwise, the paint color will
        "leak out," filling the entire screen or viewport (or any larger figure
        completely enclosing the first one).

        For example, in the following program, the CIRCLE statement draws an
        ellipse that is not quite complete (there is a small gap on the right
        side); the LINE statement then encloses the partial ellipse inside a
        box. Even though painting starts in the interior of the ellipse, the
        paint color flows through the gap and fills the entire box.

        SCREEN 2
        CONST PI = 3.141592653589#
        CIRCLE (300, 100), 80, , 0, 1.9 * PI, 3
        LINE (200, 10)-(400, 190), , B
        PAINT (300, 100)

    3. If you are painting an object a different color from the one used to
        outline the object, you must use the border option to tell PAINT where
        to stop painting.

        For example, the following program draws a triangle outlined in green
        (attribute 1 in palette 0) and then tries to paint the interior of the
        triangle red (attribute 2). However, since the PAINT statement doesn't
        indicate where to stop painting, it paints the entire screen red.

        SCREEN 1
        COLOR , 0

        LINE (10, 25)-(310, 25), 1
        LINE -(160, 175), 1
        LINE -(10, 25), 1

        PAINT (160, 100), 2

        Making the following change to the PAINT statement (choose red for the
        interior and stop when a border colored green is reached) produces the
        desired effect:

        PAINT (160, 100), 2, 1

        Note that you don't have to specify a border color in the PAINT
        statement if the paint color is the same as the border color.

        LINE (10, 25)-(310, 25), 1
        LINE -(160, 175), 1
        LINE -(10, 25), 1

        PAINT (160, 100), 1

5.8.2  Painting with Patterns: Tiling

    You can use the PAINT statement to fill any enclosed figure with a
    pattern; this process is known as "tiling." A "tile" is the pattern's
    basic building block. The process is identical to laying down tiles on a
    floor. When you use tiling, the argument interior in the syntax for PAINT
    is a string expression, rather than a number. While interior can be any
    string expression, a convenient way to define tile patterns uses the
    following form for interior:

    CHR$(arg1) + CHR$(arg2) + CHR$(arg3) + ... + CHR$(argn)

    Here, arg1, arg2, and so forth are eight-bit integers. See Sections
    5.8.2.2-5.8.2.4 below for an explanation of how these eight-bit integers
    are derived.

    5.8.2.1  Pattern-Tile Size in Different Screen Modes

    Each tile for a pattern is composed of a rectangular grid of pixels. This
    tile grid can have up to 64 rows in all screen modes. However, the number
    of pixels in each row depends on the screen mode.

    Here is why the length of each tile row varies according to the screen
    mode: although the number of bits in each row is fixed at eight (the
    length of an integer), the number of pixels these eight bits can represent
    decreases as the number of color attributes in a given screen mode
    increases. For example, in screen mode 2, which has only 1 color
    attribute, the number of bits per pixel is 1; in screen mode 1, which has
    4 different attributes, the number of bits per pixel is 2; and in the EGA
    screen mode 7, which has 16 attributes, the number of bits per pixel is 4.
    The following formula allows you to compute the bits per pixel in any
    given screen mode:

    bits-per-pixel = log2(numattributes)

    Here, numattributes is the number of color attributes in that screen mode.
    (The on-line QB Advisor has this information.)

    Thus, the length of a tile row is eight pixels in screen mode 2 (eight
    bits divided by one bit per pixel), but only four pixels in screen mode 1
    (eight bits divided by two bits per pixel).

    The next three sections show the step-by-step process involved in creating
    a pattern tile. Section 5.8.2.2 shows how to make a monochrome pattern in
    screen mode 2. Next, Section 5.8.2.3 shows how to make a multicolored
    pattern in screen mode 1. Finally, if you have an EGA, read Section
    5.8.2.4 to see how to make a multicolored pattern in screen mode 8.

    5.8.2.2  Creating a Single-Color Pattern in Screen Mode 2

    The following steps show how to define and use a pattern tile that
    resembles the letter "M":

    1. Draw the pattern for a tile in a grid with 8 columns and however many
        rows you need (up to 64). In this example, the tile has 6 rows; an
        asterisk (*) in a box means the pixel is on:

        ┌──────────────────────────────────────────────────────────────────┐
        │            1 pixel                                               │
        │            ┌──┴───┐                                              │
        │          ┌ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐     │
        │ 1 pixel ─┤ │  *  │     │     │     │     │  *  │     │     │     │
        │          └ ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤     │
        │            │  *  │  *  │     │     │  *  │  *  │     │     │     │
        │            ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤     │
        │            │  *  │     │  *  │  *  │     │  *  │     │     │     │
        │            ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤     │
        │            │  *  │     │     │     │     │  *  │     │     │     │
        │            ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤     │
        │            │  *  │     │     │     │     │  *  │     │     │     │
        │            ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤     │
        │            │  *  │     │     │     │     │  *  │     │     │     │
        │            ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤     │
        │            │     │     │     │     │     │     │     │     │     │
        │            └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘     │
        │                                                                  │
        └──────────────────────────────────────────────────────────────────┘

    2. Next, translate each row of pixels to an eight-bit number, with a 1
        meaning the pixel is on, and a 0 meaning the pixel is off:

        ┌─────────────────────────────────────────────────────────────────┐
        │                                                                 │
        │        ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐        │
        │        │  1  │  0  │  0  │  0  │  0  │  1  │  0  │  0  │        │
        │        ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤        │
        │        │  1  │  1  │  0  │  0  │  0  │  1  │  0  │  0  │        │
        │        ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤        │
        │        │  1  │  0  │  1  │  1  │  0  │  1  │  0  │  0  │        │
        │        ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤        │
        │        │  1  │  0  │  0  │  0  │  0  │  1  │  0  │  0  │        │
        │        ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤        │
        │        │  1  │  0  │  0  │  0  │  0  │  1  │  0  │  0  │        │
        │        ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤        │
        │        │  1  │  0  │  0  │  0  │  0  │  1  │  0  │  0  │        │
        │        ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤        │
        │        │  0  │  0  │  0  │  0  │  0  │  0  │  0  │  0  │        │
        │        └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘        │
        │                                                                 │
        │                                                                 │
        └─────────────────────────────────────────────────────────────────┘

    3. Convert the binary numbers given in step 2 to hexadecimal integers:

        10000100 = &H84
        11001100 = &HCC
        10110100 = &HB4
        10000100 = &H84
        10000100 = &H84
        00000000 = &H00

        These integers do not have to be hexadecimal; they could be decimal or
        octal. However, binary to hexadecimal conversion easier. To convert
        from binary to hexadecimal, read the binary number from right to left.
        Each group of four digits is then converted to its hexadecimal
        equivalent, as shown here:

        ┌───────────────────────────────────────────────────────────┐
        │                                                           │
        │                Binary         1010    1001    1111        │
        │                              └─┬──┘  └─┬──┘  └─┬──┘       │
        │                                │       │       │          │
        │                Hexadecimal     A       9       F          │
        │                                                           │
        └───────────────────────────────────────────────────────────┘

        Table 5.3 lists four-bit binary sequences and their hexadecimal
        equivalents.

    4. Create a string by concatenating the characters with the ASCII values
        from step 3 (use the CHR$ function to get these characters):

        Tile$ = CHR$(&H84) + CHR$(&HCC) + CHR$(&HB4)
        Tile$ = Tile$ + CHR$(&H84) + CHR$(&H84) + CHR$(&H00)

    5. Draw a figure and paint its interior, using PAINT and the string
        argument from step 4:

        PAINT (X, Y), Tile$

    Table 5.3  Binary to Hexadecimal Conversion
╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Binary Number            Hexadecimal Number
    ──────────────────────────────────────────────────────────────────────────
    0000                     0

    0001                     1

    0010                     2

    0011                     3

    0100                     4

    0101                     5
    Binary Number            Hexadecimal Number
    ──────────────────────────────────────────────────────────────────────────
    0101                     5

    0110                     6

    0111                     7

    1000                     8

    1001                     9

    1010                     A

    1011                     B

    1100                     C

    1101                     D

    1110                     E
    Binary Number            Hexadecimal Number
    ──────────────────────────────────────────────────────────────────────────
    1110                     E

    1111                     F
    ──────────────────────────────────────────────────────────────────────────


    Example

    The following example draws a circle and then paints the circle's interior
    with the pattern created in the preceding steps:

    SCREEN 2
    CLS
    Tile$ = CHR$(&H84) + CHR$(&HCC) + CHR$(&HB4)
    Tile$ = Tile$ + CHR$(&H84) + CHR$(&H84) + CHR$(&H00)
    CIRCLE STEP(0, 0), 150
    PAINT STEP(0, 0), Tile$

    Output


    ┌────────────────────────────────────┐
    │                                    │
    │   Figure 5.5 can be found on       │
    │   page 184 of the printed manual.  │
    │                                    │
    └────────────────────────────────────┘

    Figure 5.5  Patterned Circle

    5.8.2.3  Creating a Multicolor Pattern in Screen Mode 1

    The following steps show how to create a multicolor pattern consisting of
    alternating diagonal stripes of cyan and magenta (or green and red in
    palette 0):

    1. Draw the pattern for a tile in a grid with four columns (four columns
        because each row of pixels is stored in an eight-bit integer and each
        pixel in screen mode 1 requires two bits) and however many rows you
        need (up to 64). In this example, the tile has four rows, as shown in
        the next diagram:

        ┌──────────────────────────────────────────────────────────────────┐
        │                                                                  │
        │              1 pixel                                             │
        │             ┌────┴───┐                                           │
        │           ┌─┌────────┬────────┬────────┬────────┐                │
        │   1 pixel ┤ │  cyan  │magenta │magenta │magenta │                │
        │           │ │        │        │        │        │                │
        │           └─├────────┼────────┼────────┼────────┤                │
        │             │ magenta│ cyan   │magenta │magenta │                │
        │             │        │        │        │        │                │
        │             ├────────┼────────┼────────┼────────┤                │
        │             │ magenta│ magenta│ cyan   │magenta │                │
        │             │        │        │        │        │                │
        │             ├────────┼────────┼────────┼────────┤                │
        │             │ magenta│ magenta│ magenta│ cyan   │                │
        │             │        │        │        │        │                │
        │             └────────┴────────┴────────┴────────┘                │
        │                                                                  │
        └──────────────────────────────────────────────────────────────────┘

    2. Convert the colors to their respective color numbers in binary
        notation, as shown below (be sure to use two-bit values, so 1 that =
        binary 01 and 2 = binary 10):

        ┌──────────────────────────────────────────────────────────────────┐
        │                                                                  │
        │              2 bits                                              │
        │             ┌───┴────┐                                           │
        │             ┌────────┬────────┬────────┬────────┐                │
        │             │  01    │  10    │  10    │  10    │                │
        │             │        │        │        │        │                │
        │             ├────────┼────────┼────────┼────────┤                │
        │             │  10    │  01    │  10    │  10    │                │
        │             │        │        │        │        │                │
        │             ├────────┼────────┼────────┼────────┤                │
        │             │  10    │  10    │  01    │  10    │                │
        │             │        │        │        │        │                │
        │             ├────────┼────────┼────────┼────────┤                │
        │             │  10    │  10    │  10    │  01    │                │
        │             │        │        │        │        │                │
        │             └────────┴────────┴────────┴────────┘                │
        │                                                                  │
        └──────────────────────────────────────────────────────────────────┘

    3. Convert the binary numbers from step 2 to hexadecimal integers:

        01101010 = &H6A
        10011010 = &H9A
        10100110 = &HA6
        10101001 = &HA9

    4. Create a string by concatenating the characters with the ASCII values
        from step 3 (use the CHR$ function to get these characters):

        Tile$ = CHR$(&H6A) + CHR$(&H9A) + CHR$(&HA6) + CHR$(&HA9)

    5. Draw a figure and paint its interior using PAINT and the string
        argument from step 4:

        PAINT (X, Y), Tile$

    The following program draws a triangle and then paints its interior with
    the pattern created in the preceding steps:

    SCREEN 1

    ' Define a pattern:
    Tile$ = CHR$(&H6A) + CHR$(&H9A) + CHR$(&HA6) + CHR$(&HA9)

    ' Draw a triangle in white (color 3):
    LINE (10, 25)-(310, 25)
    LINE -(160, 175)
    LINE -(10, 25)

    ' Paint the interior of the triangle with the pattern:
    PAINT (160, 100), Tile$

    Note that if the figure you want to paint is outlined in a color that is
    also contained in the pattern, then you must give the border argument with
    PAINT as shown below; otherwise, the pattern spills over the edges of the
    figure:

    SCREEN 1

    ' Define a pattern:
    Tile$ = CHR$(&H6A) + CHR$(&H9A) + CHR$(&HA6) + CHR$(&HA9)

    ' Draw a triangle in magenta (color 2):
    LINE (10, 25)-(310, 25), 2
    LINE -(160, 175), 2
    LINE -(10, 25), 2

    ' Paint the interior of the triangle with the pattern,
    ' adding the border argument (, 2) to tell PAINT
    ' where to stop:
    PAINT (160, 100), Tile$, 2

    Sometimes, after painting a figure with a solid color or pattern, you may
    want to repaint that figure, or some part of it, with a new pattern. If
    the new pattern contains two or more adjacent rows that are the same as
    the figure's current background, you will find that tiling does not work.
    Instead, the pattern starts to spread, finds itself surrounded by pixels
    that are the same as two or more of its rows, then stops.

    You can alleviate this problem by using the background argument with PAINT
    if there are at most two adjacent rows in your new pattern that are the
    same as the old background. PAINT with background has the following
    syntax:

    PAINT[[ STEP]] (x, y) [[,[[ interior]][[,[[ border]][[,background]] ]]]]

    The background argument is a string character of the form CHR$(n) that
    specifies the rows in the pattern tile that are the same as the figure's
    current background. In essence, background tells PAINT to skip over these
    rows when repainting the figure. The next example clarifies how this
    works:

    SCREEN 1

    ' Define a pattern (two rows each of cyan, magenta, white):
    Tile$ = CHR$(&H55) + CHR$(&H55) + CHR$(&HAA)
    Tile$ = Tile$ + CHR$(&HAA) + CHR$(&HFF) + CHR$(&HFF)

    ' Draw a triangle in white (color number 3):
    LINE (10, 25)-(310, 25)
    LINE -(160, 175)
    LINE -(10, 25)

    ' Paint the interior magenta:
    PAINT (160, 100), 2, 3

    ' Wait for a keystroke:
    Pause$ = INPUT$(1)

    ' Since the background is already magenta, CHR$(&HAA) tells
    ' PAINT to skip over the magenta rows in the pattern tile:
    PAINT (160, 100), Tile$, , CHR$(&HAA)

    5.8.2.4  Creating a Multicolor Pattern in Screen Mode 8

    In the EGA and VGA screen modes, it takes more than one eight-bit integer
    to define one row in a pattern tile. In these screen modes, a row is
    composed of several layers of eight-bit integers. This is because a pixel
    is represented three dimensionally in a stack of "bit planes" rather than
    sequentially in a single plane, as is the case with screen modes 1 and 2.
    For example, screen mode 8 has four of these bit planes. Each of the four
    bits per pixel in this screen mode is on a different plane.

    The following steps diagram the process for creating a multicolor pattern
    consisting of rows of alternating yellow and magenta. Note how each row in
    the pattern tile is represented by four parallel bytes:

    1. Define one row of pixels in the pattern tile. Each pixel in the row
        takes four bits, and each bit is in a different plane, as shown below:

        ┌───────────────────────────────────────────────────────────────────────
        │
        │                  Read bits for each
        │                 plane in this order.
        │                ────────────────────────────────→
        │               ↑  ─────────────────────────────────
        │              / / 1 / 1 / 0 / 0 / 0 / 0 / 1 / 1  /────→ CHR$ (&HC3) + /
        │Read bits for/ / 0 / 0 / 1 / 1 / 1 / 1 / 0 / 0  /────→ CHR$ (&H3C) + /
        │each pixel  / / 1 / 1 / 1 / 1 / 1 / 1 / 1 / 1  /────→ CHR$ (&HFF) + /AD
        │in this    / / 0 / 0 / 1 / 1 / 1 / 1 / 0 / 0  /────→ CHR$ (&H3C)  /CHR$
        │order.    / /────────────────────────────────/                   ↓ valu
        │     Magenta ─┘   │   │   │   │   │    │   │                       in t
        │         Magenta ─┘   │   │   │   │    │   │                       orde
        │              Yellow ─┘   │   │   │    │   │
        │                  Yellow ─┘   │   │    │   │   ┌───────────────────────
        │                      Yellow ─┘   │    │   │   │Color   Decimal    Bina
        │                          Yellow ─┘    │   │   │Yellow  14         1110
        │                              Magenta ─┘   │   │Magenta  5         0101
        │                                  Magenta ─┘   └───────────────────────
        │
        └───────────────────────────────────────────────────────────────────────

        Add the CHR$ values for all four bit planes to get one tile byte. This
        row is repeated in the pattern tile, so

        Row$(1) = Row$(2) =
            CHR$(&HC3) + CHR$(&H3C) + CHR$(&HFF) + CHR$(&H3C)

    2. Define another row of pixels in the pattern tile, as follows:

        ┌───────────────────────────────────────────────────────────────────────
        │
        │                  Read bits for each
        │                 plane in this order.
        │                ────────────────────────────────→
        │               ↑  ─────────────────────────────────
        │              / / 0 / 0 / 1 / 1 / 1 / 1 / 0 / 0  /────→ CHR$ (&HC3) + /
        │Read bits for/ / 1 / 1 / 0 / 0 / 0 / 0 / 1 / 1  /────→ CHR$ (&H3C) + /
        │each pixel  / / 1 / 1 / 1 / 1 / 1 / 1 / 1 / 1  /────→ CHR$ (&HFF) + /AD
        │in this    / / 1 / 1 / 0 / 0 / 0 / 0 / 1 / 1  /────→ CHR$ (&H3C)  /CHR$
        │order.    / /────────────────────────────────/                   ↓ valu
        │      Yellow ─┘   │   │   │   │   │    │   │                       in t
        │          Yellow ─┘   │   │   │   │    │   │                       orde
        │             Magenta ─┘   │   │   │    │   │
        │                 Magenta ─┘   │   │    │   │   ┌───────────────────────
        │                     Magenta ─┘   │    │   │   │Color   Decimal    Bina
        │                         Magenta ─┘    │   │   │Yellow  14         1110
        │                               Yellow ─┘   │   │Magenta  5         0101
        │                                   Yellow ─┘   └───────────────────────
        │
        └───────────────────────────────────────────────────────────────────────

        This row is also repeated in the pattern tile, so

        Row$(3) = Row$(4) =
            CHR$(&H3C) + CHR$(&HC3) + CHR$(&HFF) + CHR$(&HC3)

    Example

    The following example draws a box, then paints its interior with the
    pattern created in the preceding steps:

    SCREEN 8
    DIM Row$(1 TO 4)

    ' Two rows of alternating magenta and yellow:
    Row$(1) = CHR$(&HC3) + CHR$(&H3C) + CHR$(&HFF) + CHR$(&H3C)
    Row$(2) = Row$(1)

    ' Invert the pattern (two rows of alternating yellow
    ' and magenta):
    Row$(3) = CHR$(&H3C) + CHR$(&HC3) + CHR$(&HFF) + CHR$(&HC3)
    Row$(4) = Row$(3)

    ' Create a pattern tile from the rows defined above:
    FOR I% = 1 TO 4
        Tile$ = Tile$ + Row$(I%)
    NEXT I%

    ' Draw box and fill it with the pattern:
    LINE (50, 50)-(570, 150), , B
    PAINT (320, 100), Tile$


5.9  DRAW: a Graphics Macro Language

    The DRAW statement is a miniature language by itself. It draws and paints
    images on the screen using a set of one- or two-letter commands, known as
    "macros," embedded in a string expression.

    DRAW offers the following advantages over the other graphics statements
    discussed so far:

    ■ The macro string argument to DRAW is compact: a single, short string can
    produce the same output as several LINE statements.

    ■ Images created with DRAW can easily be scaled──that is, enlarged or
    reduced in size──by using the S macro in the macro string.

    ■ Images created with DRAW can be rotated any number of degrees by using
    the TA macro in the macro string.

    Consult the QB Advisor for more information.

    Example

    The following program gives a brief introduction to the movement macros U,
    D, L, R, E, F, G, and H; the "plot/don't plot" macro B; and the color
    macro C. This program draws horizontal, vertical, and diagonal lines in
    different colors, depending on which DIRECTION key on the numeric keypad
    (UP, DOWN, LEFT, PGUP, PGDN, and so on) is pressed.

    This program is in the file named PLOTTER.BAS on the QuickBASIC
    distribution disks.

    ' Values for keys on the numeric keypad and the spacebar:
    CONST UP = 72, DOWN = 80, LFT = 75, RGHT = 77
    CONST UPLFT = 71, UPRGHT = 73, DOWNLFT = 79, DOWNRGHT = 81
    CONST SPACEBAR = " "

    ' Null$ is the first character of the two-character INKEY$
    ' value returned for direction keys such as UP and DOWN:
    Null$ = CHR$(0)

    ' Plot$ = "" means draw lines; Plot$ = "B" means
    ' move graphics cursor, but don't draw lines:
    Plot$ = ""

    PRINT "Use the cursor movement keys to draw lines."
    PRINT "Press spacebar to toggle line drawing on and off."
    PRINT "Press <ENTER> to begin. Press q to end the program."
    DO : LOOP WHILE INKEY$ = ""

    SCREEN 1

    DO
        SELECT CASE KeyVal$
        CASE Null$ + CHR$(UP)
            DRAW Plot$ + "C1 U2"
        CASE Null$ + CHR$(DOWN)
            DRAW Plot$ + "C1 D2"
        CASE Null$ + CHR$(LFT)
            DRAW Plot$ + "C2 L2"
        CASE Null$ + CHR$(RGHT)
            DRAW Plot$ + "C2 R2"
        CASE Null$ + CHR$(UPLFT)
            DRAW Plot$ + "C3 H2"
        CASE Null$ + CHR$(UPRGHT)
            DRAW Plot$ + "C3 E2"
        CASE Null$ + CHR$(DOWNLFT)
            DRAW Plot$ + "C3 G2"
        CASE Null$ + CHR$(DOWNRGHT)
            DRAW Plot$ + "C3 F2"
        CASE SPACEBAR
            IF Plot$ = "" THEN Plot$ = "B " ELSE Plot$ = ""
        CASE ELSE
            ' The user pressed some key other than one of the
            ' direction keys, the spacebar, or "q," so
            ' don't do anything.
        END SELECT

        KeyVal$ = INKEY$

    LOOP UNTIL KeyVal$ = "q"

    SCREEN 0, 0             ' Restore the screen to 80-column
    WIDTH 80                ' text mode and end.
    END

    Output

    Here's a sample sketch created with this program.

    ┌──────────────────────────────────────────────────────────────────┐
    │                                                                  │
    │                                                                  │
    │        / ────\ │     │ │ ┌────\  │   /        │                  │
    │        │     │ │     │ │ │       │ /          │                  │
    │        │     │ │     │ │ │       /\           │                  │
    │        │     │ │     │ │ │       │  \         │                  │
    │        \────\/ \─────/ │ \─────/ │    \       ▬                  │
    │              \                                                   │
    │        ─────────────────────────────────                         │
    │                                                                  │
    │          ───────────────────────────────                         │
    │                                                                  │
    │             ────────────────────────────                         │
    │                                                                  │
    └──────────────────────────────────────────────────────────────────┘


5.10  Basic Animation Techniques

    Using only the graphics statements covered in earlier sections, you can do
    simple animation of objects on the screen. For instance, you can first
    draw a circle with CIRCLE, then redraw it with the background color to
    erase it, and finally recalculate the circle's center point and draw it in
    a new location.

    This technique works well enough for simple figures, but its disadvantages
    become apparent when animating more complex images. Even though the
    graphics statements are very fast, you can still notice the lag. Moreover,
    it is not possible to preserve the background with this method: when you
    erase the object, you also erase whatever was already on the screen.

    Two statements allow you to do high-speed animation: GET and PUT. You can
    create an image using output from statements such as PSET, LINE, CIRCLE,
    or PAINT, then take a "snapshot" of that image with GET, copying the image
    to memory. With PUT, you can then reproduce the image stored with GET
    anywhere on the screen or viewport.

5.10.1  Saving Images with GET

    After you have created the original image on the screen, you need to
    calculate the x- and y-coordinates of a rectangle large enough to hold the
    entire image. You then use GET to copy the entire rectangle to memory. The
    syntax for the graphics GET statement is

    GET[[STEP]](x1, y1) -[[STEP]](x2, y2), array-name

    where (x1, y1) and (x2, y2) give the coordinates of a rectangle's
    upper-left and lower-right corners. The argument array-name refers to any
    numeric array. The size specified in a DIM statement for array-name
    depends on the following three factors:

    1. The height and width of the rectangle enclosing the image

    2. The screen mode chosen for graphics output

    3. The type of the array (integer, long integer, single precision, or
        double precision)

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Although the array used to store images can have any numeric type, it is
    strongly recommended that you use only integer arrays. All possible
    graphics patterns on the screen can be represented by integers. This is
    not the case, however, with single-precision or double-precision real
    numbers: some graphics patterns are not valid real numbers, and it could
    lead to unforeseen results if these patterns were stored in a
    real-number array.
    ──────────────────────────────────────────────────────────────────────────

    The formula for calculating the size in bytes of arrayname is

    size-in-bytes = 4 + height * planes * INT((width * bits-per-pixel/planes +
    7)/8)

    where height and width are the dimensions, in number of pixels, of the
    rectangle to get, and the value for bits-per-pixel depends on the number
    of colors available in the given screen mode. More colors mean more bits
    are required to define each pixel. In screen mode 1, two bits define a
    pixel, while in screen mode 2, one bit is enough. (See Section 5.8.2.1
    above, "Pattern-Tile Size in Different Screen Modes," for the general
    formula for bits-per-pixel.) The following list shows the value for planes
    for each of the screen modes:

    Screen Mode                                             Number of Bit
                                                            Planes
    ──────────────────────────────────────────────────────────────────────────
    1, 2, 11, and 13                                        1

    9 (64K of video memory) and 10                          2

    7, 8, 9 (more than 64K of video memory), and 12         4
    ──────────────────────────────────────────────────────────────────────────

    To get the number of elements that should be in the array, divide the
    size-in-bytes by the number of bytes for one element in the array. This is
    where the type of the array comes into play. If it is an integer array,
    each element takes two bytes of memory (the size of an integer), so
    size-in-bytes should be divided by two to get the actual size of the
    array. Similarly, if it is a long integer array, size-in-bytes should be
    divided by four (since one long integer requires four bytes of memory),
    and so on. If it is single precision, divide by four; if it is double
    precision, divide by eight.

    The following steps show how to calculate the size of an integer array
    large enough to hold a rectangle in screen mode 1 with coordinates (10,
    40) for the upper-left corner and (90, 80) for the lower-right corner:

    1. Calculate the height and width of the rectangle:

        RectHeight = ABS(y2 - y1) + 1 = 80 - 40 + 1 = 41
        RectWidth = ABS(x2 - x1) + 1 = 90 - 10 + 1 = 81

        Remember to add one after subtracting y1 from y2 or x1 from x2. For
        example, if x1 = 10 and x2 = 20, then the width of the rectangle is 20
        - 10 + 1, or 11.

    2. Calculate the size in bytes of the integer array:

        ByteSize = 4 + RectHeight * INT((RectWidth * BitsPerPixel + 7) / 8)
                = 4 + 41 * INT((81 * 2 + 7) / 8)
                = 4 + 41 * INT(169 / 8)
                = 4 + 41 * 21
                = 865

    3. Divide the size in bytes by the bytes per element (two for integers)
        and round the result up to the nearest whole number:

        865 / 2 = 433

    Therefore, if the name of the array is Image, the following DIM statement
    ensures that Image is big enough to copy the pixel information in the
    rectangle:

    DIM Image (1 TO 433) AS INTEGER

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Although the GET statement uses view coordinates after a WINDOW
    statement, you must use physical coordinates to calculate the size of
    the array used in GET. (See Section 5.6 above, "Redefining Viewport
    Coordinates with WINDOW," for more information on WINDOW and how to
    convert view coordinates to physical coordinates.)
    ──────────────────────────────────────────────────────────────────────────

    Note that the steps outlined above give the minimum size required for the
    array; however, any larger size will do. For example, the following
    statement also works:

    DIM Image (1 TO 500) AS INTEGER

    Example

    The following program draws an ellipse and paints its interior. A GET
    statement copies the rectangular area containing the ellipse into memory.
    (Section 5.10.2 below, "Moving Images with PUT," shows how to use the PUT
    statement to reproduce the ellipse in a different location.)

    SCREEN 1

    ' Dimension an integer array large enough
    ' to hold the rectangle:
    DIM Image (1 TO 433) AS INTEGER

    ' Draw an ellipse inside the rectangle, using magenta for
    ' the outline and painting the interior white:
    CIRCLE (50, 60), 40, 2, , , .5
    PAINT (50, 60), 3, 2

    ' Store the image of the rectangle in the array:
    GET (10, 40)-(90, 80), Image

5.10.2  Moving Images with PUT

    While the GET statement allows you to take a snapshot of an image, PUT
    allows you to paste that image anywhere you want on the screen. A
    statement of the form

    PUT (x, y), array-name [[, actionverb]]

    copies the rectangular image stored in array-name back to the screen and
    places its upper-left corner at the point with coordinates (x, y). Note
    that only one coordinate pair appears in PUT.

    If a WINDOW statement appears in the program before PUT, the coordinates x
    and y refer to the lower-left corner of the rectangle. WINDOW SCREEN,
    however, does not have this effect; that is, after WINDOW SCREEN, x and y
    still refer to the upper-left corner of the rectangle.

    For example, adding the next line to the last example in Section 5.10.1
    above causes an exact duplicate of the painted ellipse to appear on the
    right side of the screen much more quickly than redrawing and repainting
    the same figure with CIRCLE and PAINT:

    PUT (200, 40), Image

    Take care not to specify coordinates that would put any part of the image
    outside the screen or active viewport, as in the following statements:

    SCREEN 2
    .
    .
    .
    ' Rectangle measures 141 pixels x 91 pixels:
    GET (10, 10)-(150, 100), Image
    PUT (510, 120), Image

    Unlike other graphics statements such as LINE or CIRCLE, PUT does not clip
    images lying outside the viewport. Instead, it produces an error message
    reading Illegal function call.

    One of the other advantages of the PUT statement is that you can control
    how the stored image interacts with what is already on the screen by using
    the argument actionverb. The actionverb argument can be one of the
    following options: PSET, PRESET, AND, OR, or XOR.

    If you do not care what happens to the existing screen background, use the
    PSET option, since it transfers an exact duplicate of the stored image to
    the screen and overwrites anything that was already there.

    Table 5.4 shows how other options affect the way the PUT statement causes
    pixels in a stored image to interact with pixels on the screen. In this
    table, a 1 means a pixel is on and a 0 means a pixel is off.

    Table 5.4  The Effect of Different Action Options in Screen Mode 2
╓┌─┌──────────────────┌─────────────────┌──────────────────┌─────────────────╖
                                        Pixel on Screen    Pixel on Screen
                        Pixel in          before PUT         after PUT
    Action Option      Stored Image      Statement          Statement
    ──────────────────────────────────────────────────────────────────────────
    PSET               0                 0                  0

                        0                 1                  0

                        1                 0                  1

                        1                 1                  1
                                        Pixel on Screen    Pixel on Screen
                        Pixel in          before PUT         after PUT
    Action Option      Stored Image      Statement          Statement
    ──────────────────────────────────────────────────────────────────────────
                        1                 1                  1

    PRESET             0                 0                  1

                        0                 1                  1

                        1                 0                  0

                        1                 1                  0

    AND                0                 0                  0

                        0                 1                  0

                        1                 0                  0

                        1                 1                  1
                                        Pixel on Screen    Pixel on Screen
                        Pixel in          before PUT         after PUT
    Action Option      Stored Image      Statement          Statement
    ──────────────────────────────────────────────────────────────────────────
                        1                 1                  1

    OR                 0                 0                  0

                        0                 1                  1

                        1                 0                  1

                        1                 1                  1

    XOR                0                 0                  0

                        0                 1                  1

                        1                 0                  1

                        1                 1                  0
                                        Pixel on Screen    Pixel on Screen
                        Pixel in          before PUT         after PUT
    Action Option      Stored Image      Statement          Statement
    ──────────────────────────────────────────────────────────────────────────
                        1                 1                  0
    ──────────────────────────────────────────────────────────────────────────


    As you can see, these options cause a PUT statement to treat pixels the
    same way logical operators treat numbers. The PRESET option is like the
    logical operator NOT in that it inverts the pixels in the stored image,
    regardless of what was on the screen. The options AND, OR, and XOR are
    identical to the logical operators with the same names; for example, the
    logical operation

    1 XOR 1

    gives 0 as its result, just as using the XOR option turns a pixel off when
    both the pixel in the image and the pixel in the background are on.

    The output from the following program shows the same image superimposed
    over a filled rectangle using each of the five options discussed above:

    SCREEN 2

    DIM CircImage (1 TO 485) AS INTEGER

    ' Draw and paint an ellipse then store its image with GET:
    CIRCLE (22, 100), 80, , , , 4
    PAINT (22, 100)
    GET (0, 20)-(44, 180), CircImage
    CLS

    ' Draw a box and fill it with a pattern:
    LINE (40, 40)-(600, 160), , B
    Pattern$ = CHR$(126) + CHR$(0) + CHR$(126) + CHR$(126)
    PAINT (50, 50), Pattern$

    ' Put the images of the ellipse over the box
    ' using the different action options:
    PUT (100, 20), CircImage, PSET
    PUT (200, 20), CircImage, PRESET
    PUT (300, 20), CircImage, AND
    PUT (400, 20), CircImage, OR
    PUT (500, 20), CircImage, XOR

    Output

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 199 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

    In screen modes supporting color, the options PRESET, AND, OR, and XOR
    produce a more complicated interaction, since color involves more than
    simply turning a pixel on or off. However, the analogy made above between
    these options and logical operators still holds in these modes. For
    example, if the current pixel on the screen is color 1 (cyan in palette 1)
    and the pixel in the overlaid image is color 2 (magenta in palette 1),
    then the color of the resulting pixel after a PUT statement depends on the
    option, as shown for just 2 of the 16 different combinations of image
    color and background color in Table 5.5.

    Table 5.5  The Effect of Different Action Options on Color in Screen Mode
    1 (Palette 1)
                                        Pixel Color on     Pixel Color on
                        Pixel Color in    Screen before      Screen after PUT
    Action Option      Stored Image      PUT Statement      Statement
    ──────────────────────────────────────────────────────────────────────────
    PSET               10 (magenta)      01 (cyan)          10 (magenta)

    PRESET             10 (magenta)      01 (cyan)          01 (cyan)

    AND                10 (magenta)      01 (cyan)          00 (black)

    OR                 10 (magenta)      01 (cyan)          11 (white)

    XOR                10 (magenta)      01 (cyan)          11 (white)
    ──────────────────────────────────────────────────────────────────────────

    In palette 1, cyan is assigned to attribute 1 (01 binary), magenta is
    assigned to attribute 2 (10 binary), and white is assigned to attribute 3
    (11 binary). If you have an EGA, you can use the PALETTE statement to
    assign different colors to the attributes 1, 2, and 3.

5.10.3  Animation with GET and PUT

    One of the most useful things that can be done with the GET and PUT
    statements is animation. The two options best suited for animation are XOR
    and PSET. Animation done with PSET is faster; but as shown by the output
    from the last program, PSET erases the screen background. In contrast, XOR
    is slower but restores the screen background after the image is moved.
    Animation with XOR is done with the following four steps:

    1. Put the object on the screen with XOR.

    2. Recalculate the new position of the object.

    3. Put the object on the screen a second time at the old location, using
        XOR again, this time to remove the old image.

    4. Go to step 1, but this time put the object at the new location.

    Movement done with these four steps leaves the background unchanged after
    step 3. Flicker can be reduced by minimizing the time between steps 4 and
    1 and by making sure that there is enough time delay between steps 1 and
    3. If more than one object is being animated, every object should be
    processed at once, one step at a time.

    If it is not important to preserve the background, use the PSET option for
    animation. The idea is to leave a border around the image when you copy it
    with the GET statement. If this border is as large or larger than the
    maximum distance the object will move, then each time the image is put in
    a new location, the border erases all traces of the image in the old
    location. This method can be somewhat faster than the method using XOR
    described above, since only one PUT statement is required to move an
    object (although you must move a larger image).

    Examples

    The following example shows how to use PUT with the PSET option to produce
    the effect of a ball bouncing off the bottom and sides of a box. Note in
    the output that follows how the rectangle containing the ball, specified
    in the GET statement, erases the filled box and the printed message.

    This program is in the file named BALLPSET.BAS on the QuickBASIC
    distribution disks.

    DECLARE FUNCTION GetArraySize (WLeft, WRight, WTop,
    WBottom)

    SCREEN 2

    ' Define a viewport and draw a border around it:
    VIEW (20, 10)-(620, 190),,1

    CONST PI = 3.141592653589#

    ' Redefine the coordinates of the viewport with view
    ' coordinates:
    WINDOW (-3.15, -.14)-(3.56, 1.01)

    ' Arrays in program are now dynamic:
    ' $DYNAMIC

    ' Calculate the view coordinates for the top and bottom of a
    ' rectangle large enough to hold the image that will be
    ' drawn with CIRCLE and PAINT:
    WLeft = -.21
    WRight = .21
    WTop = .07
    WBottom = -.07

    ' Call the GetArraySize function,
    ' passing it the rectangle's view coordinates:
    ArraySize% = GetArraySize(WLeft, WRight, WTop, WBottom)

    DIM Array (1 TO ArraySize%) AS INTEGER

    ' Draw and paint the circle:
    CIRCLE (0, 0), .18
    PAINT (0, 0)

    ' Store the rectangle in Array:
    GET (WLeft, WTop)-(WRight, WBottom), Array
    CLS
    ' Draw a box and fill it with a pattern:
    LINE (-3, .8)-(3.4, .2), , B
    Pattern$ = CHR$(126) + CHR$(0) + CHR$(126) + CHR$(126)
    PAINT (0, .5), Pattern$

    LOCATE 21, 29
    PRINT "Press any key to end."

    ' Initialize loop variables:
    StepSize = .02
    StartLoop = -PI
    Decay = 1

    DO
        EndLoop = -StartLoop
        FOR X = StartLoop TO EndLoop STEP StepSize

        ' Each time the ball "bounces" (hits the bottom of the
        ' viewport), the Decay variable gets smaller, making
        ' the height of the next bounce smaller:
        Y = ABS(COS(X)) * Decay - .14
        IF Y < -.13 THEN Decay = Decay * .9

        ' Stop if key pressed or Decay less than .01:
        Esc$ = INKEY$
        IF Esc$ <> "" OR Decay < .01 THEN EXIT FOR

        ' Put the image on the screen. The StepSize offset is
        ' smaller than the border around the circle. Thus,
        ' each time the image moves, it erases any traces
        ' left from the previous PUT (and also erases anything
        ' else on the screen):
        PUT (X, Y), Array, PSET
        NEXT X

        ' Reverse direction:
        StepSize = -StepSize
        StartLoop = -StartLoop
    LOOP UNTIL Esc$ <> "" OR Decay < .01

    END

    FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom) STATIC

        ' Map the view coordinates passed to this function to
        ' their physical-coordinate equivalents:
        VLeft = PMAP(WLeft, 0)
        VRight = PMAP(WRight, 0)
        VTop = PMAP(WTop, 1)
        VBottom = PMAP(WBottom, 1)

    ' Calculate the height and width in pixels
        ' of the enclosing rectangle:
        RectHeight = ABS(VBottom - VTop) + 1
        RectWidth = ABS(VRight - VLeft) + 1

        ' Calculate size in bytes of array:
        ByteSize = 4 + RectHeight * INT((RectWidth + 7) \ 8)

        ' Array is integer, so divide bytes by two:
        GetArraySize = ByteSize \ 2 + 1
    END FUNCTION

    Output

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 203 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

    Contrast the preceding program with the next program, which uses PUT with
    XOR to preserve the screen's background, according to the four steps
    outlined above. Note how the rectangle containing the ball is smaller than
    in the preceding program, since it is not necessary to leave a border.
    Also note that two PUT statements are required, one to make the image
    visible and another to make it disappear. Finally, observe the empty
    FOR...NEXT delay loop between the PUT statements; this loop reduces the
    flicker that results from the image appearing and disappearing too
    rapidly.

    This program is in the file named BALLXOR.BAS on the QuickBASIC
    distribution disks.

    .
    .
    .
    ' The rectangle is smaller than the one in the previous
    ' program, which means Array is also smaller:
    WLeft = -.18
    WRight = .18
    WTop = .05
    WBottom = -.05
    .
    .
    .
    DO
        EndLoop = -StartLoop
        FOR X = StartLoop TO EndLoop STEP StepSize
        Y = ABS(COS(X)) * Decay - .14

        ' The first PUT statement places the image
        ' on the screen:
        PUT (X,Y), Array, XOR

        ' Use an empty FOR...NEXT loop to delay
        ' the program and reduce image flicker:
        FOR I = 1 TO 5: NEXT I

        IF Y < -.13 THEN Decay = Decay * .9
        Esc$ = INKEY$
        IF Esc$ <> "" OR Decay < .01 THEN EXIT FOR

        ' The second PUT statement erases the image and
        ' restores the background:
        PUT (X, Y), Array, XOR
        NEXT X

        StepSize = -StepSize
        StartLoop = -StartLoop
    LOOP UNTIL Esc$ <> "" OR Decay < .01

    END
    .
    .
    .

    Output

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 205 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

5.10.4  Animating with Screen Pages

    This section describes an animation technique that utilizes multiple pages
    of your computer's video memory.

    Pages in video memory are analogous to pages in a book. Depending on the
    graphics capability of your computer, what you see displayed on the screen
    may only be part of the video memory available──just as what you see when
    you open a book is only part of the book. However, unlike a book, the
    unseen pages of your computer's video memory can be active; that is, while
    you are looking at one page on the screen, graphics output can be taking
    place on the others. It's as if the author of a book were still writing
    new pages even as you were reading the book.

    The area of video memory visible on the screen is called the "visual
    page," while the area of video memory where graphics statements put their
    output is called the "active page." The SCREEN statement allows you to
    select visual and active screen pages with the following syntax:

    SCREEN[[mode]],[[,[[apage]][[, vpage]]]]

    In this syntax, apage is the number of the active page, and vpage is the
    number of the visual page. The active page and the visual page can be one
    and the same (and are by default when the apage or vpage arguments are not
    used with SCREEN, in which case the value of both arguments is 0).

    You can animate objects on the screen by selecting a screen mode with more
    than one video memory page, then alternating the pages, sending output to
    one or more active pages while displaying finished output on the visual
    page. This technique makes an active page visible only when output to that
    page is complete. Since the viewer sees only a finished image, the display
    is instantaneous.

    Example

    The following program demonstrates the technique discussed above. It
    selects screen mode 7, which has two pages, then draws a cube with the
    DRAW statement. This cube is then rotated through successive 15° angles by
    changing the value of the TA macro in the string used by DRAW. By swapping
    the active and visual pages back and forth, this program always shows a
    completed cube while a new one is being drawn.

    This program is in the file named CUBE.BAS on the QuickBASIC distribution
    disks.

    ' Define the macro string used to draw the cube
    ' and paint its sides:
    One$ =  "BR30 BU25 C1 R54 U45 L54 D45 BE20 P1,1 G20 C2 G20"
    Two$ =  "R54 E20 L54 BD5 P2,2 U5 C4 G20 U45 E20 D45 BL5 P4,4"
    Plot$ = One$ + Two$

    APage% = 1      ' Initialize values for the active and visual
    VPage% = 0      ' pages as well as the angle of rotation.
    Angle% = 0

    DO
        SCREEN 7, , APage%, VPage% ' Draw to the active page
                                ' while showing the visual page.

        CLS 1                      ' Clear the active page.

        ' Rotate the cube "Angle%" degrees:
        DRAW "TA" + STR$(Angle%) + Plot$

        ' Angle% is some multiple of 15 degrees:
        Angle% = (Angle% + 15) MOD 360

        ' Drawing is complete, so make the cube visible in its
        ' new position by switching the active and visual pages:
        SWAP APage%, VPage%

    LOOP WHILE INKEY$ = ""        ' A keystroke ends the program.

    END


5.11  Sample Applications

    The sample applications in this chapter are a bar-graph generator, a
    program that plots points in the Mandelbrot Set using different colors,
    and a pattern editor.

5.11.1  Bar-Graph Generator (BAR.BAS)

    This program uses all the forms of the LINE statement presented above in
    Sections 5.3.2.1-5.3.2.3 to draw a filled bar chart. Each bar is filled
    with a pattern specified in a PAINT statement. The input for the program
    consists of titles for the graph, labels for the x- and y-axes, and a set
    of up to five labels (with associated values) for the bars.

    Statements and Functions Used

    This program demonstrates the use of the following graphics statements:

    ■ LINE

    ■ PAINT (with a pattern)

    ■ SCREEN

    Program Listing

    The bar-graph generator program BAR.BAS is listed below.

    ' Define type for the titles:
    TYPE TitleType
        MainTitle AS STRING * 40
        XTitle AS STRING * 40
        YTitle AS STRING * 18
    END TYPE

    DECLARE SUB InputTitles (T AS TitleType)
    DECLARE FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value!(), N%)
    DECLARE FUNCTION InputData% (Label$(), Value!())

    ' Variable declarations for titles and bar data:
    DIM Titles AS TitleType, Label$(1 TO 5), Value(1 TO 5)

    CONST FALSE = 0, TRUE = NOT FALSE

    DO
        InputTitles Titles
        N% = InputData%(Label$(), Value())
        IF N% <> FALSE THEN
        NewGraph$ = DrawGraph$(Titles, Label$(), Value(), N%)
        END IF
    LOOP WHILE NewGraph$ = "Y"

    END

    ' ======================== DRAWGRAPH ======================
    '   Draws a bar graph from the data entered in the
    '   INPUTTITLES and INPUTDATA procedures.
    ' =========================================================

    FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value(), N%) STATIC

        ' Set size of graph:
        CONST GRAPHTOP = 24, GRAPHBOTTOM = 171
        CONST GRAPHLEFT = 48, GRAPHRIGHT = 624
        CONST YLENGTH = GRAPHBOTTOM - GRAPHTOP

        ' Calculate maximum and minimum values:
        YMax = 0
        YMin = 0
        FOR I% = 1 TO N%
        IF Value(I%) < YMin THEN YMin = Value(I%)
        IF Value(I%) > YMax THEN YMax = Value(I%)
        NEXT I%

        ' Calculate width of bars and space between them:
        BarWidth = (GRAPHRIGHT - GRAPHLEFT) / N%
        BarSpace = .2 * BarWidth
        BarWidth = BarWidth - BarSpace

        SCREEN 2
        CLS

        ' Draw y-axis:
        LINE (GRAPHLEFT, GRAPHTOP)-(GRAPHLEFT, GRAPHBOTTOM), 1

        ' Draw main graph title:
        Start% = 44 - (LEN(RTRIM$(T.MainTitle)) / 2)
        LOCATE 2, Start%
        PRINT RTRIM$(T.MainTitle);

        ' Annotate y-axis:
        Start% = CINT(13 - LEN(RTRIM$(T.YTitle)) / 2)
        FOR I% = 1 TO LEN(RTRIM$(T.YTitle))
        LOCATE Start% + I% - 1, 1
        PRINT MID$(T.YTitle, I%, 1);
        NEXT I%

        ' Calculate scale factor so labels aren't bigger than four digits:
        IF ABS(YMax) > ABS(YMin) THEN
        Power = YMax
        ELSE
        Power = YMin
        END IF
        Power = CINT(LOG(ABS(Power) / 100) / LOG(10))
        IF Power < 0 THEN Power = 0

        ' Scale minimum and maximum values down:
        ScaleFactor = 10 ^ Power
        YMax = CINT(YMax / ScaleFactor)
        YMin = CINT(YMin / ScaleFactor)


        ' If power isn't zero then put scale factor on chart:
        IF Power <> 0 THEN
        LOCATE 3, 2
        PRINT "x 10^"; LTRIM$(STR$(Power))
        END IF

        ' Put tic mark and number for Max point on y-axis:
        LINE (GRAPHLEFT - 3, GRAPHTOP) -STEP(3, 0)
        LOCATE 4, 2
        PRINT USING "####"; YMax

        ' Put tic mark and number for Min point on y-axis:
        LINE (GRAPHLEFT - 3, GRAPHBOTTOM) -STEP(3, 0)
        LOCATE 22, 2
        PRINT USING "####"; YMin

        YMax = YMax * ScaleFactor ' Scale minimum and maximum back
        YMin = YMin * ScaleFactor ' up for charting calculations.

        ' Annotate x-axis:
        Start% = 44 - (LEN(RTRIM$(T.XTitle)) / 2)
        LOCATE 25, Start%
        PRINT RTRIM$(T.XTitle);

        ' Calculate the pixel range for the y-axis:
        YRange = YMax - YMin

        ' Define a diagonally striped pattern:
        Tile$ = CHR$(1)+CHR$(2)+CHR$(4)+CHR$(8)+CHR$(16)+CHR$(32)+CHR$(64)+CHR$
    (128)

        ' Draw a zero line if appropriate:
        IF YMin < 0 THEN
        Bottom = GRAPHBOTTOM - ((-YMin) / YRange * YLENGTH)
        LOCATE INT((Bottom - 1) / 8) + 1, 5
        PRINT "0";
        ELSE
        Bottom = GRAPHBOTTOM
        END IF

        ' Draw x-axis:
        LINE (GRAPHLEFT - 3, Bottom)-(GRAPHRIGHT, Bottom)
        ' Draw bars and labels:
        Start% = GRAPHLEFT + (BarSpace / 2)
        FOR I% = 1 TO N%

        ' Draw a bar label:
        BarMid = Start% + (BarWidth / 2)
        CharMid = INT((BarMid - 1) / 8) + 1
        LOCATE 23, CharMid - INT(LEN(RTRIM$(Label$(I%))) / 2)
        PRINT Label$(I%);

        ' Draw the bar and fill it with the striped pattern:
        BarHeight = (Value(I%) / YRange) * YLENGTH
        LINE (Start%, Bottom) -STEP(BarWidth, -BarHeight), , B
        PAINT (BarMid, Bottom - (BarHeight / 2)), Tile$, 1

        Start% = Start% + BarWidth + BarSpace
        NEXT I%
        LOCATE 1, 1
        PRINT "New graph? ";
        DrawGraph$ = UCASE$(INPUT$(1))

    END FUNCTION

    ' ======================== INPUTDATA ======================
    '     Gets input for the bar labels and their values
    ' =========================================================

    FUNCTION InputData% (Label$(), Value()) STATIC

        ' Initialize the number of data values:
        NumData% = 0

        ' Print data-entry instructions:
        CLS
        PRINT "Enter data for up to 5 bars:"
        PRINT "   * Enter the label and value for each bar."
        PRINT "   * Values can be negative."
        PRINT "   * Enter a blank label to stop."
        PRINT
        PRINT "After viewing the graph, press any key ";
        PRINT "to end the program."

        ' Accept data until blank label or 5 entries:
        Done% = FALSE
        DO
        NumData% = NumData% + 1
        PRINT
        PRINT "Bar("; LTRIM$(STR$(NumData%)); "):"
        INPUT ; "        Label? ", Label$(NumData%)

        ' Only input value if label isn't blank:
        IF Label$(NumData%) <> "" THEN
            LOCATE , 35
            INPUT "Value? ", Value(NumData%)

        ' If label is blank, decrement data counter
        ' and set Done flag equal to TRUE:
        ELSE
            NumData% = NumData% - 1
            Done% = TRUE
        END IF
        LOOP UNTIL (NumData% = 5) OR Done%

        ' Return the number of data values input:
        InputData% = NumData%

    END FUNCTION

    ' ====================== INPUTTITLES ======================
    '     Accepts input for the three different graph titles
    ' =========================================================

    SUB InputTitles (T AS TitleType) STATIC
        SCREEN 0, 0          ' Set text screen.
        DO                   ' Input titles.
        CLS
        INPUT "Enter main graph title: ", T.MainTitle
        INPUT "Enter x-axis title    : ", T.XTitle
        INPUT "Enter y-axis title    : ", T.YTitle

        ' Check to see if titles are OK:
        LOCATE 7, 1
        PRINT "OK (Y to continue, N to change)? ";
        LOCATE , , 1
        OK$ = UCASE$(INPUT$(1))
        LOOP UNTIL OK$ = "Y"
    END SUB

    Output

    ┌─────────────────────────────────────────────────────────────────┐
    │    New graph?                                                   │
    │                    LOW TEMPERATURE BY MONTH                     │
    │     15 ┐                                        ┌───────┐       │
    │ C      │                                        │░░░░░░░│       │
    │ e      │                                        │░░░░░░░│       │
    │ l      │                                        │░░░░░░░│       │
    │ s      │                                        │░░░░░░░│       │
    │ i      │                                        │░░░░░░░│       │
    │ u      │                                        │░░░░░░░│       │
    │ s      │                              ┌───────┐ │░░░░░░░│       │
    │        │                              │░░░░░░░│ │░░░░░░░│       │
    │ D      │                              │░░░░░░░│ │░░░░░░░│       │
    │ e    0 ┼┬───────┬─┬───────┬─┬───────┬─┴───────┴─┴───────┴─      │
    │ g      ││░░░░░░░│ │░░░░░░░│ │░░░░░░░│                           │
    │ r      ││░░░░░░░│ │░░░░░░░│ └───────┘                           │
    │ e      ││░░░░░░░│ │░░░░░░░│                                     │
    │ e      ││░░░░░░░│ │░░░░░░░│                                     │
    │ s      │└───────┘ │░░░░░░░│                                     │
    │      -9┘          └───────┘                                     │
    │          January   February    March    April     May           │
    │                              Month                              │
    └─────────────────────────────────────────────────────────────────┘

5.11.2  Color in a Figure Generated Mathematically (MANDEL.BAS)

    This program uses BASIC graphics statements to generate a figure known as
    a "fractal." A fractal is a graphic representation of what happens to
    numbers when they are subjected to a repeated sequence of mathematical
    operations. The fractal generated by this program shows a subset of the
    class of numbers known as complex numbers; this subset is called the
    "Mandelbrot Set," named after Benoit B. Mandelbrot of the IBM Thomas J.
    Watson Research Center.

    Briefly, complex numbers have two parts, a real part and a so-called
    imaginary part, some multiple of the √-1. Squaring a complex number, then
    plugging the real and imaginary parts back into a second complex number,
    squaring the new complex number, and repeating the process causes some
    complex numbers to get very large fairly fast. However, others hover
    around some stable value. The stable values are in the Mandelbrot Set and
    are represented in this program by the color black. The unstable values──
    that is, the ones that are moving away from the Mandelbrot Set──are
    represented by the other colors in the palette. The smaller the color
    attribute, the more unstable the point.

    See A.K. Dewdney's column, "Computer Recreations," in Scientific American,
    August 1985, for more background on the Mandelbrot Set.

    This program also tests for the presence of an EGA card, and if one is
    present, it draws the Mandelbrot Set in screen mode 8. After drawing each
    line, the program rotates the 16 colors in the palette with a PALETTE
    USING statement. If there is no EGA card, the program draws a four-color
    (white, magenta, cyan, and black) Mandelbrot Set in screen mode 1.

    Statements and Functions Used

    This program demonstrates the use of the following graphics statements:

    ■ LINE

    ■ PALETTE USING

    ■ PMAP

    ■ PSET

    ■ SCREEN

    ■ VIEW

    ■ WINDOW

    Program Listing

    DEFINT A-Z           ' Default variable type is integer.

    DECLARE SUB ShiftPalette ()
    DECLARE SUB WindowVals (WL%, WR%, WT%, WB%)
    DECLARE SUB ScreenTest (EM%, CR%, VL%, VR%, VT%, VB%)

    CONST FALSE = 0, TRUE = NOT FALSE ' Boolean constants

    ' Set maximum number of iterations per point:
    CONST MAXLOOP = 30, MAXSIZE = 1000000

    DIM PaletteArray(15)
    FOR I = 0 TO 15: PaletteArray(I) = I: NEXT I

    ' Call WindowVals to get coordinates of window corners:
    WindowVals WLeft, WRight, WTop, WBottom

    ' Call ScreenTest to find out if this is an EGA machine
    ' and get coordinates of viewport corners:
    ScreenTest EgaMode, ColorRange, VLeft, VRight, VTop, VBottom

    ' Define viewport and corresponding window:
    VIEW (VLeft, VTop)-(VRight, VBottom), 0, ColorRange
    WINDOW (WLeft, WTop)-(WRight, WBottom)

    LOCATE 24, 10 : PRINT "Press any key to quit.";

    XLength = VRight - VLeft
    YLength = VBottom - VTop
    ColorWidth = MAXLOOP \ ColorRange

    ' Loop through each pixel in viewport and calculate
    ' whether or not it is in the Mandelbrot Set:
    FOR Y = 0 TO YLength       ' Loop through every line
                                ' in the viewport.
        LogicY = PMAP(Y, 3)     ' Get the pixel's view
                                ' y-coordinate.
        PSET (WLeft, LogicY)    ' Plot leftmost pixel in the line.
        OldColor = 0            ' Start with background color.

        FOR X = 0 TO XLength    ' Loop through every pixel
                                ' in the line.
        LogicX = PMAP(X, 2)  ' Get the pixel's view
                                ' x-coordinate.
        MandelX& = LogicX
        MandelY& = LogicY


        ' Do the calculations to see if this point
        ' is in the Mandelbrot Set:
        FOR I = 1 TO MAXLOOP
            RealNum& = MandelX& * MandelX&
            ImagNum& = MandelY& * MandelY&
            IF (RealNum& + ImagNum&) >= MAXSIZE THEN EXIT FOR
            MandelY& = (MandelX& * MandelY&) \ 250 + LogicY
            MandelX& = (RealNum& - ImagNum&) \ 500 + LogicX
        NEXT I

        ' Assign a color to the point:
        PColor = I \ ColorWidth

        ' If color has changed, draw a line from
        ' the last point referenced to the new point,
        ' using the old color:
        IF PColor <> OldColor THEN
            LINE -(LogicX, LogicY), (ColorRange - OldColor)
            OldColor = PColor
        END IF

        IF INKEY$ <> "" THEN END
        NEXT X

        ' Draw the last line segment to the right edge
        ' of the viewport:
        LINE -(LogicX, LogicY), (ColorRange - OldColor)

        ' If this is an EGA machine, shift the palette after
        ' drawing each line:
        IF EgaMode THEN ShiftPalette
    NEXT Y

    DO
        ' Continue shifting the palette
        ' until the user presses a key:
        IF EgaMode THEN ShiftPalette
    LOOP WHILE INKEY$ = ""

    SCREEN 0, 0             ' Restore the screen to text mode,
    WIDTH 80                ' 80 columns.
    END

    BadScreen:              ' Error handler that is invoked if
        EgaMode = FALSE      ' there is no EGA graphics card
        RESUME NEXT

    ' ====================== ShiftPalette =====================
    '    Rotates the palette by one each time it is called
    ' =========================================================

    SUB ShiftPalette STATIC
        SHARED PaletteArray(), ColorRange

        FOR I = 1 TO ColorRange
        PaletteArray(I) = (PaletteArray(I) MOD ColorRange) + 1
        NEXT I
        PALETTE USING PaletteArray(0)

    END SUB

    ' ======================= ScreenTest ======================
    '    Uses a SCREEN 8 statement as a test to see if user has
    '    EGA hardware. If this causes an error, the EM flag is
    '    set to FALSE, and the screen is set with SCREEN 1.

    '    Also sets values for corners of viewport (VL = left,
    '    VR = right, VT = top, VB = bottom), scaled with the
    '    correct aspect ratio so viewport is a perfect square.
    ' =========================================================

    SUB ScreenTest (EM, CR, VL, VR, VT, VB) STATIC
        EM = TRUE
        ON ERROR GOTO BadScreen
        SCREEN 8, 1
        ON ERROR GOTO 0

        IF EM THEN                   ' No error, SCREEN 8 is OK.
        VL = 110: VR = 529
        VT = 5: VB = 179
        CR = 15                   ' 16 colors (0 - 15)

        ELSE                         ' Error, so use SCREEN 1.
        SCREEN 1, 1
        VL = 55: VR = 264
        VT = 5: VB = 179
        CR = 3                    ' 4 colors (0 - 3)
        END IF

    END SUB

    ' ======================= WindowVals ======================
    '     Gets window corners as input from the user, or sets
    '     values for the corners if there is no input
    ' =========================================================

    SUB WindowVals (WL, WR, WT, WB) STATIC
        CLS
        PRINT "This program prints the graphic representation of"
        PRINT "the complete Mandelbrot Set. The default window"
        PRINT "is from (-1000,625) to (250,-625). To zoom in on"
        PRINT "part of the figure, input coordinates inside"
        PRINT "this window."
        PRINT "Press <ENTER> to see the default window or"
        PRINT "any other key to input window coordinates: ";
        LOCATE , , 1
        Resp$ = INPUT$(1)

        ' User didn't press ENTER, so input window corners:
        IF Resp$ <> CHR$(13) THEN
        PRINT
        INPUT "x-coordinate of upper-left corner: ", WL
        DO
            INPUT "x-coordinate of lower-right corner: ", WR
            IF WR <= WL THEN
            PRINT "Right corner must be greater than left corner."
            END IF
        LOOP WHILE WR <= WL
        INPUT "y-coordinate of upper-left corner: ", WT
        DO
            INPUT "y-coordinate of lower-right corner: ", WB
            IF WB >= WT THEN
            PRINT "Bottom corner must be less than top corner."
            END IF
        LOOP WHILE WB >= WT

        ' User pressed ENTER, so set default values:
        ELSE
        WL = -1000
        WR = 250
        WT = 625
        WB = -625
        END IF
    END SUB

    Output

    The following figure shows the Mandelbrot Set in screen mode 1. This is
    the output you see if you have a CGA and you choose the default window
    coordinates.

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 217 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

    The next figure shows the Mandelbrot Set with (x, y) coordinates of (-500,
    250) for the upper-left corner and (-300, 50) for the lower-right corner.
    This figure is drawn in screen mode 8, the default for an EGA or VGA.

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 217 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘

5.11.3  Pattern Editor (EDPAT.BAS)

    This program allows you to edit a pattern tile for use with PAINT. While
    you are editing the tile on the left side of the screen, you can check the
    appearance of the finished pattern on the right side of the screen. When
    you have finished editing the pattern tile, the program prints the integer
    arguments used by the CHR$ statement to draw each row of the tile.

    Statements and Functions Used

    This program demonstrates the use of the following graphics statements:

    ■ LINE

    ■ PAINT (with pattern)

    ■ VIEW

    Program Listing

    DECLARE SUB DrawPattern ()
    DECLARE SUB EditPattern ()
    DECLARE SUB Initialize ()
    DECLARE SUB ShowPattern (OK$)

    DIM Bit%(0 TO 7), Pattern$, Esc$, PatternSize%

    DO
        Initialize
        EditPattern
        ShowPattern OK$
    LOOP WHILE OK$ = "Y"

    END

    ' ======================= DRAWPATTERN ====================
    '  Draws a patterned rectangle on the right side of screen
    ' ========================================================

    SUB DrawPattern STATIC
    SHARED Pattern$

        VIEW (320, 24)-(622, 160), 0, 1  ' Set view to rectangle.
        PAINT (1, 1), Pattern$           ' Use PAINT to fill it.
        VIEW                             ' Set view to full screen.

    END SUB

    ' ======================= EDITPATTERN =====================
    '                  Edits a tile-byte pattern
    ' =========================================================

    SUB EditPattern STATIC
    SHARED Pattern$, Esc$, Bit%(), PatternSize%

        ByteNum% = 1         ' Starting position.
        BitNum% = 7
        Null$ = CHR$(0)      ' CHR$(0) is the first byte of the
                            ' two-byte string returned when a
                            ' direction key such as UP or DOWN is
                            ' pressed.
        DO

        ' Calculate starting location on screen of this bit:
        X% = ((7 - BitNum%) * 16) + 80
        Y% = (ByteNum% + 2) * 8

        ' Wait for a key press (flash cursor each 3/10 second):
        State% = 0
        RefTime = 0
        DO

            ' Check timer and switch cursor state if 3/10 second:
            IF ABS(TIMER - RefTime) > .3 THEN
                RefTime = TIMER
                State% = 1 - State%

            ' Turn the border of bit on and off:
                LINE (X%-1, Y%-1) -STEP(15, 8), State%, B
            END IF

            Check$ = INKEY$ ' Check for keystroke.

        LOOP WHILE Check$ = "" ' Loop until a key is pressed.

        ' Erase cursor:
        LINE (X%-1, Y%-1) -STEP(15, 8), 0, B

        SELECT CASE Check$ ' Respond to keystroke.

        CASE CHR$(27)      ' ESC key pressed:
            EXIT SUB        ' exit this subprogram.

    CASE CHR$(32)      ' SPACEBAR pressed:
                            ' reset state of bit.

            ' Invert bit in pattern string:
            CurrentByte% = ASC(MID$(Pattern$, ByteNum%, 1))
            CurrentByte% = CurrentByte% XOR Bit%(BitNum%)
            MID$ (Pattern$, ByteNum%) = CHR$(CurrentByte%)

            ' Redraw bit on screen:
            IF (CurrentByte% AND Bit%(BitNum%)) <> 0 THEN
                CurrentColor% = 1
            ELSE
                CurrentColor% = 0
            END IF
            LINE (X%+1, Y%+1) -STEP(11, 4), CurrentColor%, BF

        CASE CHR$(13)              ' ENTER key pressed: draw
            DrawPattern             ' pattern in box on right.

        CASE Null$ + CHR$(75)      ' LEFT key: move cursor left.

            BitNum% = BitNum% + 1
            IF BitNum% > 7 THEN BitNum% = 0

        CASE Null$ + CHR$(77)      ' RIGHT key: move cursor right.

            BitNum% = BitNum% - 1
            IF BitNum% < 0 THEN BitNum% = 7

        CASE Null$ + CHR$(72)      ' UP key: move cursor up.

            ByteNum% = ByteNum% - 1
            IF ByteNum% < 1 THEN ByteNum% = PatternSize%

        CASE Null$ + CHR$(80)      ' DOWN key: move cursor down.

            ByteNum% = ByteNum% + 1
            IF ByteNum% > PatternSize% THEN ByteNum% = 1

        CASE ELSE
            ' User pressed a key other than ESC, SPACEBAR,
            ' ENTER, UP, DOWN, LEFT, or RIGHT, so don't
            ' do anything.
        END SELECT
        LOOP
    END SUB

    ' ======================= INITIALIZE ======================
    '             Sets up starting pattern and screen
    ' =========================================================

    SUB Initialize STATIC
    SHARED Pattern$, Esc$, Bit%(), PatternSize%

        Esc$ = CHR$(27)              ' ESC character is ASCII 27.

        ' Set up an array holding bits in positions 0 to 7:
        FOR I% = 0 TO 7
        Bit%(I%) = 2 ^ I%
        NEXT I%

        CLS

        ' Input the pattern size (in number of bytes):
        LOCATE 5, 5
        PRINT "Enter pattern size (1-16 rows):";
        DO
        LOCATE 5, 38
        PRINT "  ";
        LOCATE 5, 38
        INPUT "", PatternSize%
        LOOP WHILE PatternSize% < 1 OR PatternSize% > 16

        ' Set initial pattern to all bits set:
        Pattern$ = STRING$(PatternSize%, 255)

        SCREEN 2             ' 640 x 200 monochrome graphics mode

        ' Draw dividing lines:
        LINE (0, 10)-(635, 10), 1
        LINE (300, 0)-(300, 199)
        LINE (302, 0)-(302, 199)

        ' Print titles:
        LOCATE 1, 13: PRINT "Pattern Bytes"
        LOCATE 1, 53: PRINT "Pattern View"

    ' Draw editing screen for pattern:
        FOR I% = 1 TO PatternSize%

        ' Print label on left of each line:
        LOCATE I% + 3, 8
        PRINT USING "##:"; I%

        ' Draw "bit" boxes:
        X% = 80
        Y% = (I% + 2) * 8
        FOR J% = 1 TO 8
            LINE (X%, Y%) -STEP(13, 6), 1, BF
            X% = X% + 16
        NEXT J%
        NEXT I%

        DrawPattern          ' Draw   "Pattern View" box.

        LOCATE 21, 1
        PRINT "DIRECTION keys........Move cursor"
        PRINT "SPACEBAR............Changes point"
        PRINT "ENTER............Displays pattern"
        PRINT "ESC.........................Quits";

    END SUB

    ' ======================== SHOWPATTERN ====================
    '   Prints the CHR$ values used by PAINT to make pattern
    ' =========================================================

    SUB ShowPattern (OK$) STATIC
    SHARED Pattern$, PatternSize%

        ' Return screen to 80-column text mode:
        SCREEN 0, 0
        WIDTH 80

        PRINT "The following characters make up your pattern:"
        PRINT

        ' Print out the value for each pattern byte:
        FOR I% = 1 TO PatternSize%
        PatternByte% = ASC(MID$(Pattern$, I%, 1))
        PRINT "CHR$("; LTRIM$(STR$(PatternByte%)); ")"
        NEXT I%
        PRINT
        LOCATE , , 1
        PRINT "New pattern? ";
        OK$ = UCASE$(INPUT$(1))
    END SUB

    Output

    This is a sample pattern generated by the pattern editor.

    ┌────────────────────────────────────┐
    │                                    │
    │     The output for this example    │
    │     can be found on page 223 of    │
    │     the printed manual.            │
    │                                    │
    └────────────────────────────────────┘



────────────────────────────────────────────────────────────────────────────
Chapter 6  Error and Event Trapping

    This chapter shows how to trap errors and events that occur while a
    program is running. With error trapping, you can protect your program from
    such user errors as entering a string when a numeric value is expected or
    trying to open a file in a nonexistent directory. With event trapping,
    your program can detect and respond to real-time events such as keystrokes
    or data arriving at a COM port.

    When you have completed this chapter, you will know how to perform these
    tasks:

    ■ Activate error trapping or event trapping

    ■ Write a routine to process trapped errors or events

    ■ Return control from an error-handling routine or an event-handling
    routine

    ■ Write a program that traps any keystroke or combination of keystrokes

    ■ Trap errors or events within SUB or FUNCTION procedures

    ■ Trap errors or events in programs composed of more than one module


6.1  Error Trapping

    Error trapping lets your program intercept run-time errors before they
    force the program to halt. Without error trapping, errors during program
    execution (such as trying to open a nonexistent data file) cause BASIC to
    display the appropriate error message, then stop the program. If someone
    is running the stand-alone version of your program, they will have to
    restart the program, losing data entered or calculations performed before
    the error occurred.

    When error trapping is active, the trapped error makes the program branch
    to a user-written "error-handling routine," which corrects the error. If
    you can anticipate errors that might occur when someone else uses your
    program, error trapping will let you handle those errors in a
    "user-friendly" way.

    Sections 6.1.1 and 6.1.2 below show how to activate error trapping, how
    to write a routine to handle errors when they are trapped, and how to
    return control from the error-handling routine after it has dealt with the
    error.

6.1.1  Activating Error Trapping

    You activate error trapping with the statement ON ERROR GOTO line, where
    line is a line number or line label identifying the first line of an
    error-handling routine. Once BASIC has encountered an ON ERROR GOTO line
    statement, any run-time error within the module containing that statement
    causes a branch to the specified line. (If the number or label does not
    exist within the module, a run-time error message reading Label not
    defined appears.)

    An ON ERROR GOTO statement must be executed before error trapping takes
    effect. Therefore, you must position the ON ERROR GOTO statement so that
    program execution reaches it before it is needed. This statement would
    usually be one of the first executable statements in the main module or a
    procedure.

    You cannot use an ON ERROR GOTO 0 statement to branch to an error handler,
    because this statement has a special meaning in error trapping. The ON
    ERROR GOTO 0 statement has two effects, depending on where it appears. If
    it appears outside an error-handling routine, it turns off error trapping.
    If it appears inside an error-handling routine (as it does in the Handler
    routine in the next example), it causes BASIC to print its standard
    message for the given error and stops the program.

    Therefore, even if your program has a line with line-number 0, an ON ERROR
    GOTO 0 statement tells BASIC either to turn off error detection or to
    terminate execution.

6.1.2  Writing an Error-Handling Routine

    An error-handling routine consists of the following three parts:

    1. The line label or line number that is specified in the statement ON
        ERROR GOTO line, which is the first statement the program branches to
        after an error

    2. The body of the routine, which determines the error that caused the
        branch and takes appropriate action for each anticipated error

    3. At least one RESUME or RESUME NEXT statement to return control from the
        routine

    An error handler must be placed where it cannot be executed during the
    normal flow of program execution. For example, an error-handling routine
    in the program's main module might be placed after an END statement.
    Otherwise, a GOTO statement might be needed to skip over it during normal
    execution.

    6.1.2.1  Using ERR to Identify Errors

    Once the program has branched to an error-handling routine, it must
    determine which error caused the branch. To identify the culprit, use the
    ERR function. This function returns a numeric code for the program's most
    recent run-time error. (See Table I.1, "Run-Time Error Codes," for a
    complete list of the error codes associated with run-time errors.)

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Errors cannot be trapped within an error-handling routine. If an error
    occurs while the error-handling routine is processing another error, the
    program displays the message for the new error and then terminates.
    Event handling is also suspended, but resumes when QuickBASIC returns
    from the error handler. The first event that occurred after event
    handling was suspended is saved.
    ──────────────────────────────────────────────────────────────────────────

    Example

    The following program includes the error-handling routine Handler,
    designed to deal specifically with three different error situations. The
    ERR function, used in a SELECT CASE statement, allows this routine to take
    actions appropriate for each error.

    DATA BASIC, Pascal, FORTRAN, C, Modula, Forth
    DATA LISP, Ada, COBOL

    CONST FALSE = 0, TRUE = NOT FALSE

    EndOfData = FALSE                     ' Set end of data flag.

    ON ERROR GOTO Handler                 ' Activate error trapping.

    OPEN "LPT1:" FOR OUTPUT AS #1 ' Open the printer for output.

    DO
        ' Continue reading items from the DATA statements above,
        ' then printing them on the line printer, until there is
        ' an "Out of DATA" error message signifying no more data:
        READ Buffer$
        IF NOT EndOfData THEN
        PRINT #1, Buffer$
        ELSE
        EXIT DO
        END IF
    LOOP

    CLOSE #1
    END

    Handler:                        ' Error-handling routine

        ' Use ERR to determine which error
        ' caused the branch to "Handler":
        SELECT CASE ERR
        CASE 4

            ' 4 is the code for "Out of DATA" in DATA
            ' statements:
            EndOfData = TRUE
            RESUME NEXT
        CASE 25

            ' 25 is the code for "Device fault" error,
            ' which can be caused by trying to output
            ' to the printer when it's not on:
            PRINT "Turn printer on, then";
            PRINT "press any key to continue"
            Pause$ = INPUT$(1)
            RESUME
        CASE 27

            ' 27 is the code for "Out of paper":
            PRINT "Printer is out of paper. Insert"
            PRINT "paper, then press any key to continue."
            Pause$ = INPUT$(1)

            ' Start reading data from the beginning of first
            ' DATA statement after the paper is inserted:
            RESTORE
            RESUME
        CASE ELSE

            ' An unanticipated error has occurred; display the
            ' message for that error and stop the program:
            ON ERROR GOTO 0
        END SELECT

    6.1.2.2  Returning from an Error-Handling Routine

    The RESUME statement returns control from an error-handling routine. The
    preceding example used the following two variations of RESUME to return
    from Handler:

╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Statement                Action
    ──────────────────────────────────────────────────────────────────────────
    RESUME                   Causes the program to branch back to the exact
                            statement that caused the error.

                            If the program had to go to another module to
                            find an active error handler, control returns to
                            the last statement executed in that module.

                            The preceding program used RESUME to return to
                            the PRINT statement that attempted to send output
                            to the printer, giving it another chance to print
                            the value in Buffer$ after the printer has
                            (presumably) been turned on.

    RESUME NEXT              Causes the program to branch back to the
                            statement following the one that caused the
    Statement                Action
    ──────────────────────────────────────────────────────────────────────────
                            statement following the one that caused the
                            error.If the program had to go to another module
                            to find an active error handler, control returns
                            to the statement following the last statement
                            executed in that module.

                            The preceding program used the statement RESUME
                            NEXT when recovering from the Out of DATA error.
                            In this case, a simple RESUME would have sent the
                            program into an endless loop, since each time
                            control returned to the READ statement in the
                            main program, another Out of DATA error would
                            result, invoking the error routine over and over
                            again.

    ──────────────────────────────────────────────────────────────────────────


    Figure 6.1 outlines the flow of a program's control during error handling
    with RESUME.


    ┌──────────────────────────────────────────────────────────────────┐
    │          ┌─────────┐                                             │
    │          │  Start  │                                             │
    │          └────┬────┘                                             │
    │               │                                                  │
    │               │                                                  │
    │    ┌──────────▼───────────┐        ┌──────────────────────┐      │
    │ ┌─►│ Statement with error ├───────►│    Error handler     │      │
    │ │  └──────────┬───────────┘        └───────────┬──────────┘      │
    │ │             │                                │                 │
    │ │            ┌┼┐                   ┌───────────▼──────────┐      │
    │ └────────────┘│└───────────────────┤        RESUME        │      │
    │               │                    └──────────────────────┘      │
    │               │                                                  │
    │          ┌────▼────┐                                             │
    │          │   End   │                                             │
    │          └─────────┘                                             │
    └──────────────────────────────────────────────────────────────────┘

    Figure 6.1  Program Flow of Control with RESUME

    Contrast the preceding figure with Figure 6.2, which shows what happens
    in a program using RESUME NEXT.


    ┌──────────────────────────────────────────────────────────────────┐
    │        ┌─────────┐                                               │
    │        │  Start  │                                               │
    │        └────┬────┘                                               │
    │             │                                                    │
    │  ┌──────────▼───────────┐        ┌──────────────────────┐        │
    │  │ Statement with error ├───────►│    Error handler     │        │
    │  └──────────────────────┘        └───────────┬──────────┘        │
    │                                              │                   │
    │  ┌──────────────────────┐        ┌───────────▼──────────┐        │
    │  │ Statement following  │◄───────┤        RESUME        │        │
    │  │the one with the error│        └──────────────────────┘        │
    │  └──────────┬───────────┘                                        │
    │             │                                                    │
    │        ┌────▼────┐                                               │
    │        │   End   │                                               │
    │        └─────────┘                                               │
    └──────────────────────────────────────────────────────────────────┘

    Figure 6.2  Program Flow of Control with RESUME NEXT

    Another variant of RESUME is RESUME line, where line is a line number or
    line label outside any SUB...END SUB, FUNCTION...END FUNCTION, or DEF
    FN...END DEF block. Because line must be outside these blocks, a RESUME
    line statement can produce unexpected effects if it appears in an
    error-handling routine inside a SUB or FUNCTION procedure or a DEF FN
    function. It should generally be avoided in favor of RESUME or RESUME
    NEXT.


6.2  Event Trapping

    Section 6.2.1 below compares two methods your BASIC programs can use to
    detect events: polling and trapping. Sections 6.2.2-6.2.4 then discuss
    the different events BASIC can trap, how to set and activate a trap, and
    how to suspend or deactivate the trap. Sections 6.2.5-6.2.7 provide more
    detailed information on trapping keystrokes and music events.

6.2.1  Detecting Events by Polling

    One way to detect an event and reroute program control to the appropriate
    routine is to use "event polling." In polling, your program stops whatever
    it is currently doing and explicitly checks for an event. For example, the
    following loop keeps checking the keyboard until the user presses either
    the Q or N keys:

    DO
        Test$ = UCASE$(INKEY$)
    LOOP UNTIL Test$ = "N" OR Test$ = "Q"

    Polling works well when you know ahead of time exactly where in the flow
    of the program you want to check for an event.

6.2.2  Detecting Events by Trapping

    But suppose you don't want your program to pause, or you would like it to
    check between every statement (that is, check continuously). In these
    cases, polling is unwieldy or impossible, and trapping becomes the better
    alternative.

    Example

    The following example shows how to use event trapping:

    ' Alert the program that it should branch to "KeySub"
    ' whenever the user presses the F1 function key:
    ON KEY(1) GOSUB KeySub

    ' Turn on trapping for the F1 key. BASIC now checks in the
    ' background for this event until the end of the program,
    ' or until trapping is disabled with KEY(1) OFF
    ' or suspended with KEY(1) STOP:
    KEY(1) ON
    OPEN "Data" FOR INPUT AS #1
    OPEN "Data.Out" FOR OUTPUT AS #2

    DO UNTIL EOF(1)
        LINE INPUT #1, LineBuffer$
        PRINT #2, LineBuffer$
    LOOP

    KeySub:         ' When the user presses F1, the program
        .            ' branches to this procedure.
        .
        .
    RETURN

6.2.3  Specifying the Event to Trap and Activating Event Trapping

    As you can see from the preceding example, three steps are necessary for
    event trapping:

    1. Define a target subroutine that is known as an "event-handling
        subroutine."

    2. Tell the program to branch to the event-handling subroutine when the
        event takes place with an ON event GOSUB statement.

    3. Activate trapping of that event with an ON event GOSUB statement.

    Your program cannot trap a given event until it encounters both an event
    ON and an ON event GOSUB statement.

    An event-handling routine is an ordinary BASIC GOSUB routine: control
    passes to the first line of the routine whenever the event is detected and
    returns to the main level of the module containing the routine with a
    RETURN statement.

    Note the following important differences between the syntax of error
    trapping and the syntax of event trapping:

    Error Trapping                       Event Trapping
    ──────────────────────────────────────────────────────────────────────────
    Activated with the statement ON      Activated with an event ON statement;
    ERROR GOTO, which also specifies     a separate statement, ON event GOSUB,
    the first line of the error-         specifies the first line of the
    handling routine                     event-handling subroutine

    Returns control from the error       Returns control from the event
    routine with one or more RESUME,     subroutine with one or more RETURN
    RESUME NEXT, or RESUME line          statements
    statements
    ──────────────────────────────────────────────────────────────────────────

6.2.4  Events That BASIC Can Trap

    You can trap the following events within a BASIC program:

    BASIC Statement          Event Trapped
    ──────────────────────────────────────────────────────────────────────────
    COM(portnumber)          Data from one of the serial ports (1 or 2)
                            appearing in the communications buffer (the
                            intermediate storage area for data sent to or
                            received from the serial port)

    KEY(keynumber)           The user pressing the given key

    PEN                      The user activating the light pen

    PLAY(queuelimit)         The number of notes remaining to be played in the
                            background dropping below queuelimit

    STRIG(triggernumber)     The user squeezing the trigger of the joystick

    TIMER(interval)          The passage of interval seconds
    ──────────────────────────────────────────────────────────────────────────

6.2.5  Suspending or Disabling Event Trapping

    You can turn off detection and trapping of any event with the event OFF
    statement. Events occurring after an event OFF statement has been executed
    are ignored. If you want to suspend trapping a given event but continue
    detecting it, use the event STOP statement.

    After event STOP, if the event occurs, there is no branch to the
    event-handling routine. However, your program remembers that the event
    occurred, and as soon as trapping is turned back on with event ON,
    branching to the event routine occurs immediately.

    Event-handling routines execute an implicit event STOP statement for a
    given event whenever program control is in the routine; this is followed
    by an implicit event ON for that event when program control returns from
    the routine. For example, if a key-handling routine is processing a
    keystroke, trapping the same key is suspended until the previous keystroke
    is completely processed by the routine. If the user presses the same key
    during this time, this new keystroke is remembered and trapped after
    control returns from the key-handling routine.

    Example

    An event trap interrupts whatever is happening in the program at the
    moment the event occurs. Therefore, if you have a block of statements that
    you do not want interrupted, precede them with event STOP and follow them
    with event ON, as in this example:

    ' Once every minute (60 seconds),
    ' branch to the ShowTime subroutine:
    ON TIMER(60) GOSUB ShowTime

    ' Activate trapping of the 60-second event:
    TIMER ON
    .
    .
    .
    TIMER STOP      ' Suspend trapping.
    .       ' A sequence of lines you don't want interrupted,
    .       ' even if 60 or more seconds elapse
    .

    TIMER ON        ' Reactivate trapping.
    .
    .
    .
    END

    ShowTime:

        ' Get the current row and column position of the cursor,
        ' and store them in the variables Row and Column:
        Row    = CSRLIN
        Column = POS(0)

        ' Go to the 24th row, 20th column, and print the time:
        LOCATE 24, 20
        PRINT TIME$

        ' Restore the cursor to its former position
        ' and return to the main program:
        LOCATE Row, Column
    RETURN

6.2.6  Trapping Keystrokes

    To detect a keystroke and route program control to a key-press routine,
    you need both of the following statements in your program:

    ON KEY(keynumber) GOSUB line
    KEY(keynumber) ON

    Here, the keynumber value corresponds to the following keys:

    Value                    Key
    ──────────────────────────────────────────────────────────────────────────
    1-10                     Function keys F1-F10 (sometimes called "soft
                            keys")

    11                       The UP direction key

    12                       The LEFT direction key

    13                       The RIGHT direction key

    14                       The DOWN direction key

    15-25                    User-defined keys (see Sections 6.2.6.1-6.2.6.2)

    30                       The F11 function key (101-key keyboard only)

    31                       The F12 function key (101-key keyboard only)
    ──────────────────────────────────────────────────────────────────────────

    Examples

    The following two lines cause the program to branch to the KeySub routine
    each time the F2 function key is pressed:

    ON KEY(2) GOSUB KeySub
    KEY(2) ON
    .
    .
    .

    The following four lines cause the program to branch to the DownKey
    routine when the DOWN direction key is pressed and to the UpKey routine
    when the UP direction key is pressed:

    ON KEY(11) GOSUB UpKey
    ON KEY(14) GOSUB DownKey
    KEY(11) ON
    KEY(14) ON
    .
    .
    .

    6.2.6.1  Trapping User-Defined Keys

    In addition to providing the preassigned key numbers 1-14 (plus 30 and 31
    with the 101-key keyboard), BASIC allows you to assign the numbers 15-25
    to any of the remaining keys on the keyboard. To define your own key to
    trap, use these three statements:

    KEY keynumber, CHR$(0) + CHR$(scancode)
    ON KEY(keynumber) GOSUB line
    KEY(keynumber) ON

    Here, keynumber is a value from 15 to 25, and scancode is the scan code
    for that key. (See the first column of the "Keyboard Scan Codes" table in
    Appendix D for these codes.) For example, the following lines cause the
    program to branch to the TKey routine each time the user presses T:

    ' Define key 15 (the scan code for "t" is decimal 20):
    KEY 15, CHR$(0) + CHR$(20)

    ' Define the trap (where to go when "t" is pressed):
    ON KEY(15) GOSUB TKey

    KEY(15) ON                 ' Turn on detection of key 15.

    PRINT "Press q to end."

    DO                         ' Idle loop: wait for user to
    LOOP UNTIL INKEY$ = "q"    ' press "q".

    END

    TKey:                      ' Key-handling subroutine
        PRINT "Pressed t."
    RETURN

    6.2.6.2  Trapping User-Defined Shifted Keys

    You can also set a trap for "shifted" keys. A key is said to be shifted
    when you press it simultaneously with one or more of the special keys
    SHIFT, CTRL, or ALTor if you press it after toggling on the keys NUM LOCK
    or CAPS LOCK.

    This is how to trap the following key combinations:

    KEY keynumber, CHR$(keyboardflag) + CHR$(scancode)
    ON KEY(keynumber) GOSUB line
    KEY(keynumber) ON

    Here, keynumber is a value from 15 to 25; scancode is the scan code for
    the primary key; and keyboardflag is the sum of the individual codes for
    the special keys pressed, as shown in the following list:

    Key                                  Code for keyboardflag
    ──────────────────────────────────────────────────────────────────────────
    SHIFT                                1, 2, or 3
                                        Key trapping assumes the left and
                                        right shift keys are the same, so you
                                        can trap the SHIFT key with 1 (left),
                                        2 (right), or 3 (left + right).

    CTRL                                 4

    ALT                                  8

    NUM LOCK                             32

    CAPS LOCK                            64

    Any extended key on the 101-key      128
    keyboard (in other words, a key
    such as LEFT or DELETE that is not
    on the numeric keypad)
    ──────────────────────────────────────────────────────────────────────────

    For example, the following statements turn on trapping of CTRL+S. Note
    these statements are designed to trap both the CTRL+S (lowercase) and
    CTRL+SHIFT+S (uppercase) key combinations. To trap the uppercase S, your
    program must recognize capital letters produced by holding down the SHIFT
    key, as well as those produced when the CAPS LOCK key is active, as shown
    here:

    ' 31 = scan code for S key
    '  4 = code for CTRL key
    KEY 15, CHR$(4) + CHR$(31)     ' Trap CTRL+S.

    '  5 = code for CTRL key + code for SHIFT key
    KEY 16, CHR$(5) + CHR$(31)     ' Trap CTRL+SHIFT+S.

    ' 68 = code for CTRL key + code for CAPSLOCK
    KEY 17, CHR$(68) + CHR$(31)    ' Trap CTRL+CAPSLOCK+S.

    ON KEY (15) GOSUB CtrlSTrap    ' Tell program where to
    ON KEY (16) GOSUB CtrlSTrap    ' branch (note: same
    ON KEY (17) GOSUB CtrlSTrap    ' subroutine for each key).

    KEY (15) ON                    ' Activate key detection for
    KEY (16) ON                    ' all three combinations.
    KEY (17) ON
    .
    .
    .

    The following statements turn on trapping of CTRL+ALT+DEL:

    ' 12 = 4 + 8 = (code for CTRL key) + (code for ALT key)
    ' 83 = scan code for DEL key
    KEY 20, CHR$(12) + CHR$(83)
    ON KEY(20) GOSUB KeyHandler
    KEY(20) ON
    .
    .
    .

    Note in the preceding example that the BASIC event trap overrides the
    normal effect of CTRL+ALT+DEL (system reset). Using this trap in your
    program is a handy way to prevent the user from accidentally rebooting
    while a program is running.

    If you use a 101-key keyboard, you can trap any of the keys on the
    dedicated keypad by assigning the string

    CHR$(128) + CHR$(scancode)

    to any of the keynumber values from 15 to 25.

    The next example shows how to trap the LEFT direction keys on both the
    dedicated cursor keypad and the numeric keypad.

    ' 128 = keyboard flag for keys on the
    '       dedicated cursor keypad
    '  75 = scan code for LEFT arrow key

    KEY 15, CHR$(128) + CHR$(75)    ' Trap LEFT key on
    ON KEY(15) GOSUB CursorPad      ' the dedicated
    KEY(15) ON                      ' cursor keypad.

    ON KEY(12) GOSUB NumericPad     ' Trap LEFT key on
    KEY(12) ON                      ' the numeric keypad.

    DO: LOOP UNTIL INKEY$ = "q"     ' Start idle loop.
    END

    CursorPad:
        PRINT "Pressed LEFT key on cursor keypad."
    RETURN

    NumericPad:
        PRINT "Pressed LEFT key on numeric keypad."
    RETURN

    ──────────────────────────────────────────────────────────────────────────
    IMPORTANT
    For compatibility, QuickBASIC adopts many conventions of BASICA. One of
    these has to do with the way FUNCTION keys are trapped.
    If you initiate FUNCTION key trapping with an ON KEY (n) GOSUB
    statement, both BASICA and QuickBASIC trap Fn regardless of whether a
    shift key (CTRL, ALT, SHIFT) is also pressed. This means that
    user-defined traps for shifted versions of that FUNCTION key are
    ignored.
    Therefore, if you wish to trap a FUNCTION key in both its shifted and
    unshifted states, you should create a user definition for each state.
    Keys that are only used unshifted can continue to use the ON KEY (n)
    GOSUB statement.
    ──────────────────────────────────────────────────────────────────────────

6.2.7  Trapping Music Events

    When you use the PLAY statement to play music, you can choose whether the
    music plays in the foreground or in the background. If you choose
    foreground music (which is the default) nothing else can happen until the
    music finishes playing. However, if you use the MB (Music Background)
    option in a PLAY music string, the tune plays in the background while
    subsequent statements in your program continue executing.

    The PLAY statement plays music in the background by feeding up to 32 notes
    at a time into a buffer, then playing the notes in the buffer while the
    program does other things. A "music trap" works by checking the number of
    notes currently left to be played in the buffer. As soon as this number
    drops below the limit you set in the trap, the program branches to the
    first line of the specified routine.

    To set a music trap in your program, you need the following statements:

    ON PLAY(limit) GOSUB line
    PLAY ON
    PLAY "MB"
    .
    .
    .
    PLAY musicstring
    [[PLAY musicstring]]
    .
    .
    .

    Here, limit is a number between 1 and 32. For example, this fragment
    causes the program to branch to the MusicTrap subroutine whenever the
    number of notes remaining to be played in the music buffer goes from eight
    to seven:

    ON PLAY(8) GOSUB MusicTrap
    PLAY ON
    .
    .
    .
    PLAY "MB"       ' Play subsequent notes in the background.
    PLAY "o1 A# B# C-"
    .
    .
    .
    MusicTrap:
        ' Music-trap subroutine
        .
        .
        .
    RETURN

    ──────────────────────────────────────────────────────────────────────────
    IMPORTANT
    A music trap is triggered only when the number of notes goes from limit
    to limit-1. For example, if the music buffer in the preceding example
    never contained more than seven notes, the trap would never occur. In
    the example, the trap happens only when the number of notes drops from
    eight to seven.
    ──────────────────────────────────────────────────────────────────────────

    You can use a music-trap subroutine to play the same piece of music
    repeatedly while your program executes, as shown in the next example:

    ' Turn on trapping of background music events:
    PLAY ON

    ' Branch to the Refresh subroutine when there are fewer than
    ' two notes in the background music buffer:
    ON PLAY(2) GOSUB Refresh

    PRINT "Press any key to start, q to end."
    Pause$ = INPUT$(1)

    ' Select the background music option for PLAY:
    PLAY "MB"

    ' Start playing the music, so notes will be put in the
    ' background music buffer:
    GOSUB Refresh

    I = 0

    DO

        ' Print the numbers from 0 to 10,000 over and over until
        ' the user presses the "q" key. While this is happening,
        ' the music will repeat in the background:
        PRINT I
        I = (I + 1) MOD 10001
    LOOP UNTIL INKEY$ = "q"

    END

    Refresh:

        ' Plays the opening motive of
        ' Beethoven's Fifth Symphony:
        Listen$ = "t180 o2 p2 p8 L8 GGG L2 E-"
        Fate$   = "p24 p8 L8 FFF L2 D"
        PLAY Listen$ + Fate$
    RETURN


6.3  Error and Event Trapping in SUB or FUNCTION Procedures

    The most important thing to remember when using error or event trapping
    with BASIC procedures is that either of the following statements can
    appear within a SUB...END SUB or FUNCTION...END FUNCTION block:

    ON ERROR GOTO line ON event GOSUB line

    However, the line referred to in each case must identify a line in the
    module-level code, not another line within the SUB or FUNCTION.

    Example

    The following example shows where to put an error-handling routine that
    processes errors trapped within a SUB procedure:

    CALL ShortSub
    END

    CatchError:
        ' Put the CatchError routine at the module level,
        ' outside the subprogram:
        PRINT "Error" ERR "caught by error handler."
    RESUME NEXT

    SUB ShortSub STATIC
        ON ERROR GOTO CatchError
        ERROR 62
    END SUB

    Output

    Error 62 caught by error handler.


6.4  Trapping across Multiple Modules

    Prior to QuickBASIC 4.5, only events could be trapped across modules. Once
    an event-handling routine was defined and an event trap was activated in
    one module, an occurrence of that event during program execution in any
    other module triggered a branch to the routine.

    Errors could not be trapped across modules. If an error occurred in a
    module lacking an active error handler, program execution would halt, even
    if there were a handler in another module that could have taken care of
    the problem.

    QuickBASIC 4.5 expands the scope of error trapping. Errors, as well as
    events, can be trapped across modules. The next two sections explain how
    this works and the slight remaining differences between event and error
    trapping.

6.4.1  Event Trapping across Modules

    The output from the following program shows that a trap set for the F1
    function key in the main module is triggered even when program control is
    in another module:

    ' =========================================================
    '                      MODULE
    ' =========================================================
    ON KEY (1) GOSUB GotF1Key
    KEY (1) ON
    PRINT "In main module. Press c to continue."

    DO: LOOP UNTIL INKEY$ = "c"

    CALL SubKey

    PRINT "Back in main module. Press q to end."
    DO : LOOP UNTIL INKEY$ = "q"
    END

    GotF1Key:
        PRINT "Handled F1 keystroke in main module."
    RETURN

    ' =========================================================
    '                      SUBKEY MODULE
    ' =========================================================
    SUB SubKey STATIC
        PRINT "In module with SUBKEY. Press r to return."

        ' Pressing F1 here still invokes the GotF1Key
        ' subroutine in the MAIN module:
        DO: LOOP UNTIL INKEY$ = "r"
    END SUB

    Output

    In main module. Press c to continue.
    Handled F1 keystroke in main module.
    In module with SUBKEY. Press r to return.
    Handled F1 keystroke in main module.
    Back in main module. Press q to end.
    Handled F1 keystroke in main module.

6.4.2  Error Trapping across Modules

    Errors can be trapped across multiple modules. If there is no active error
    handler in the module where the error occurred, BASIC looks for one in the
    module from which the active module was invoked, continuing back through
    the sequence of modules called until it finds a module with an active
    error handler. If it cannot find one, an error message is displayed and
    program execution halts.

    Note that BASIC does not search through all modules──only the ones that
    were in the path of invocation leading to the code that produced the
    error.

    An ON ERROR statement must be executed before an error occurs in order for
    BASIC to look for a handler. Therefore, you must put the ON ERROR GOTO
    statement where the flow of a program's control can reach it; for example,
    place it inside a procedure called from the main module (see the examples
    that follow).

    Examples

    The following example shows how to trap errors for a procedure in a Quick
    library. The AdapterType procedure in this library tries all possible
    SCREEN statements to see which graphics screen modes your computer's
    hardware supports. (See Appendix H, "Creating and Using Quick Libraries,"
    for more information on Quick libraries.)

    ' =========== MODULE-LEVEL CODE IN QUICK LIBRARY ==========
    ' The error-handling routines for procedures in this
    ' library must be defined at this level. This level is
    ' executed only when there is an error in the procedures.
    ' =========================================================

    DEFINT A-Z

    CONST FALSE = 0, TRUE = NOT FALSE

    .
    .
    .
    CALL AdapterType
    .
    .
    .
    END

    DitchMe:                        ' Error-handling routine
        DisplayError = TRUE
        RESUME NEXT

    ' ========== PROCEDURE-LEVEL CODE IN QUICK LIBRARY ========
    '          Error trapping is activated at this level.
    ' =========================================================

    SUB AdapterType STATIC

        SHARED DisplayError          ' DisplayError variable is
                                    ' shared with DitchMe error
                                    ' handler in code above.
        DIM DisplayType(1 TO 13)     ' Dimension DisplayType
                                    ' array.

        J = 1                        ' Initialize subscript
                                    ' counter for DisplayType
                                    ' array.

    ON ERROR GOTO DitchMe   ' Set up the error trap.

    FOR Test = 13 TO 1 STEP -1
        SCREEN Test               ' Try a SCREEN statement to
                                    ' see if it causes error.
        IF NOT DisplayError THEN  ' No error, so this is a
                                    ' valid screen mode.
            DisplayType(J) = Test ' Store mode in array.
            J = J + 1             ' Increment the subscript
                                    ' counter.
        ELSE
            DisplayError = FALSE  ' Error; reset DisplayError.
        END IF
        NEXT Test

        SCREEN 0, 0                  ' Set 80-column text mode.
        WIDTH 80
        LOCATE 5, 10
        PRINT "Your computer supports these screen modes:"
        PRINT

        FOR I = 1 TO J               ' Print modes not causing
                                    ' errors.
        PRINT TAB(20); "SCREEN"; DisplayType(I)
        NEXT I

        LOCATE 20, 10
        PRINT "Press any key to continue..."

        DO: LOOP WHILE INKEY$ = ""

    END SUB

    If you want the error-handling routine in each module to do the exactly
    the same thing, you can have each routine invoke a common error-processing
    SUB procedure, as shown in the next example:

    ' =========================================================
    '                       MAIN MODULE
    ' =========================================================

    DECLARE SUB GlobalHandler (ModuleName$)
    DECLARE SUB ShortSub ()

    ON ERROR GOTO LocalHandler
    ERROR 57                ' Simulate occurrence of error 57
                            ' ("Device I/O error").
    ShortSub                ' Call the ShortSub SUB.
    END

    LocalHandler:
        ModuleName$ = "MAIN"
        CALL GlobalHandler(ModuleName$)  ' Call the
                                        ' GlobalHandler SUB.
    RESUME NEXT

    ' =========================================================
    '                     SHORTSUB MODULE
    ' =========================================================

    DECLARE SUB GlobalHandler (ModuleName$)

    LocalHandler:
        ModuleName$ = "SHORTSUB"
        GlobalHandler ModuleName$    ' Call GlobalHandler.
    RESUME NEXT

    SUB ShortSub STATIC
        ON ERROR GOTO LocalHandler
        ERROR 13                     ' Simulate a "Type mismatch" error.
    END SUB


    ' =========================================================
    '                    GLOBALHANDLER MODULE
    ' =========================================================

    SUB GlobalHandler (ModuleName$) STATIC
        PRINT "Trapped error";ERR;"in";ModuleName$;"module"
    END SUB

    Output

    Trapped error 57 in MAIN module.
    Trapped error 13 in SHORTSUB module.


6.5  Trapping Errors and Events in Programs Compiled with BC

    If both of the following statements apply to your program, then you must
    use the appropriate BC command-line option listed below when compiling
    your program:

    ■ You are developing the program outside the QuickBASIC environment──that
    is, you are using another text editor to enter your BASIC source code,
    then creating a stand-alone executable program from this source code
    with the commands BC and LINK.

    ■ Your program contains any of the statements listed in the second column
    below.

    Table 6.1  BC Command-Line Options for Error and Event Trapping
╓┌─┌──────────────┌────────────────────────────┌─────────────────────────────╖
    Command-Line   Statements                   Explanation
    Option
    ──────────────────────────────────────────────────────────────────────────
    /E             ON ERROR GOTO                The /E option tells the
                    RESUME line                  compiler your program does its
                                                own error trapping with ON
                                                ERROR GOTO and RESUME
                                                statements.

    /V             ON event GOSUB               The /V option tells the
                    event ON                     program to check between each
                                                statement to see if the given
                                                event has taken place
    Command-Line   Statements                   Explanation
    Option
    ──────────────────────────────────────────────────────────────────────────
                                                event has taken place
                                                (contrast this with the effect
                                                of /W).

    /W             ON event GOSUB               The /W option tells the
                    event ON                     program to check between each
                                                line to see if the given event
                                                has taken place (contrast this
                                                with the effect of /V).
                                                Since BASIC allows multiple
                                                statements on a single line,
                                                programs compiled with /W may
                                                check less frequently than
                                                those compiled with /V.

    /X             RESUME                       The /X option tells the
                    RESUME NEXT                  compiler you have used one of
                    RESUME 0                     the preceding forms of RESUME
    Command-Line   Statements                   Explanation
    Option
    ──────────────────────────────────────────────────────────────────────────
                    RESUME 0                     the preceding forms of RESUME
                                                in your program to return
                                                control from an error-handling
                                                routine.
    ──────────────────────────────────────────────────────────────────────────


    Example

    The following DOS command lines compile and link a BASIC module named
    RMTAB.BAS, creating a stand-alone program RMTAB.EXE. (As this program is
    compiled without the /O option, it requires the BRUN45.LIB run-time
    library in order to run. See Appendix G, "Compiling and Linking from
    DOS," for more information on compiling and linking.) Since this module
    contains ON ERROR GOTO and RESUME NEXT statements, the /E and /X options
    are required when compiling.

    BC RMTAB , , /E /X;
    LINK RMTAB;


6.6  Sample Application: Trapping File-Access Errors (FILERR.BAS)

    The following program gets as input both a file name and a string to
    search for in the file. It then lists all lines in the given file
    containing the specified string. If a file-access error occurs, it is
    trapped and dealt with in the ErrorProc routine.

    Statements and Functions Used

    This program demonstrates the use of the following error-handling
    statements and functions:

    ■ ERR

    ■ ON ERROR GOTO

    ■ RESUME

    ■ RESUME NEXT

    Program Listing

    ' Declare symbolic constants:
    CONST FALSE = 0, TRUE = NOT FALSE

    DECLARE FUNCTION GetFileName$ ()

    ' Set up the ERROR trap and specify
    ' the name of the error-handling routine:
    ON ERROR GOTO ErrorProc

    DO
        Restart = FALSE
        CLS

        FileName$ = GetFileName$     ' Input file name.

        IF FileName$ = "" THEN
        END                       ' End if <ENTER> pressed.
        ELSE

        ' Otherwise, open the file, assigning it
        ' the next available file number:
        FileNum = FREEFILE
        OPEN FileName$ FOR INPUT AS FileNum
        END IF

    IF NOT Restart THEN

        ' Input search string:
        LINE INPUT "Enter string to locate:", LocString$
        LocString$ = UCASE$(LocString$)

        ' Loop through the lines in the file,
        ' printing them if they contain the search string:
        LineNum = 1
        DO WHILE NOT EOF(FileNum)

            ' Input line from file:
            LINE INPUT #FileNum, LineBuffer$

            ' Check for string, printing the line
            ' and its number if found:
            IF INSTR(UCASE$(LineBuffer$), LocString$) <> 0 THEN
                PRINT USING "#### &"; LineNum, LineBuffer$
            END IF

            LineNum = LineNum + 1
        LOOP

        CLOSE FileNum             ' Close the file.

        END IF
    LOOP WHILE Restart = TRUE

    END

    ErrorProc:

        SELECT CASE ERR

        CASE 64:                  ' Bad file name
            PRINT "** ERROR - Invalid file name"

            ' Get a new file name and try again:
            FileName$ = GetFileName$

            ' Resume at the statement that caused the error:
            RESUME

    CASE 71:                  ' Disk not ready.
            PRINT "** ERROR - Disk drive not ready"
            PRINT "Press C to continue, R to restart, Q to quit: "
            DO
                Char$ = UCASE$(INPUT$(1))
                IF Char$ = "C" THEN
                RESUME           ' Resume where you left off.

                ELSEIF Char$ = "R" THEN
                Restart = TRUE   ' Resume at beginning.
                RESUME NEXT

                ELSEIF Char$ = "Q" THEN
                    END              ' Don't resume at all.
                    END IF
            LOOP

        CASE 53, 76:              ' File or path not found.
            PRINT "** ERROR - File or path not found"
            FileName$ = GetFileName$
            RESUME

        CASE ELSE:                ' Unforeseen error.

            ' Disable error trapping and
            ' print standard system message:
            ON ERROR GOTO 0
        END SELECT

    ' ===================== GETFILENAME$ =======================
    '           Returns a file name from user input
    ' ==========================================================

    FUNCTION GetFileName$ STATIC
        INPUT "Enter file to search (or ENTER to quit):", FTemp$
        GetFileName$ = FTemp$
    END FUNCTION



────────────────────────────────────────────────────────────────────────────
Chapter 7  Programming with Modules

    This chapter shows how you can gain more control over your programming
    projects by dividing them into "modules." Modules provide a powerful
    organizing function by letting you divide a program into logically related
    parts (rather than keeping all the code in one file).

    This chapter will show you how to use modules to:

    ■ Write and test new procedures separately from the rest of the program

    ■ Create libraries of your own SUB and FUNCTION procedures that can be
    added to any new program

    ■ Combine routines from other languages (such as C or MASM) with your
    BASIC programs


7.1  Why Use Modules?

    A module is a file that contains an executable part of your program. A
    complete program can be contained in a single module, or it can be divided
    among two or more modules.

    In dividing up a program into modules, logically related sections are
    placed in separate files. This organization can speed and simplify the
    process of writing, testing, and debugging.

    Dividing your program into modules has these advantages:

    ■ Modules allow procedures to be written separately from the rest of the
    program, then combined with it. This arrangement is especially useful
    for testing the procedures, since they can then be checked outside the
    environment of the program.

    ■ Two or more programmers can work on different parts of the same program
    without interference. This is especially helpful in managing complex
    programming projects.

    ■ As you create procedures that meet your own specific programming needs,
    you can add these procedures to their own module. They can then be
    reused in new programs simply by loading that module.

    ■ Multiple modules simplify software maintenance. A procedure used by many
    programs can be in one library module; if changes are needed, it only
    has to be modified once.


7.2  Main Modules

    The module containing the first executable statement of a program is
    called the "main module." This statement is never part of a procedure,
    because execution cannot begin within a procedure.

    Everything in a module except SUB and FUNCTION procedures is said to be
    "module-level code." In QuickBASIC, the module-level code is every
    statement that can be accessed without switching to a procedure-editing
    window. Figure 7.1 illustrates the relationship between these elements.


7.3  Modules Containing Only Procedures

    A module need not contain module-level code; a module can consist of
    nothing but SUB and FUNCTION procedures. Indeed, this is the most
    important use of modules.

    Modules are often used to "split off" the procedures from the body of the
    program. This makes it easy to divide a project among programmers, for
    example. Also, as you create general-purpose procedures that are useful in
    a variety of programs (such as procedures that evaluate matrices, send
    binary data to a COM port, alter strings, or handle errors), these can be
    stored in modules, then used in new programs simply by loading the
    appropriate module into QuickBASIC.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    If a procedure in a procedures-only module needs an error- or
    event-handling routine or a COMMON SHARED statement, these are inserted
    at module level.
    ──────────────────────────────────────────────────────────────────────────


    ┌───────────────────────────────────────────────────────────────────────┐
    │             Program execution begins here                             │
    │       ┌─ ┌────────────────────────────────────────────┐ ─┐            │
    │       │  │  declarations and definitions              │  │            │
    │       │  │  executable program statement 1            │  │            │
    │       │  │  executable program statement 2            │  │            │
    │       │  │  .                                         │  ├─ Module-   │
    │       │  │  .                                         │  │ level code │
    │       │  │  .                                         │  │            │
    │       │  │  END                                       │  │            │
    │       │  │  error-handling routines                   │  │            │
    │       │  └────────────────────────────────────────────┘ ─┘            │
    │       │  ┌────────────────────────────────────────────┐ ─┐            │
    │       │  │  SUB                                       │  │            │
    │       │  │   statements in first SUB procedure        │  │            │
    │       │  │  ENDSUB                                    │  │            │
    │       │  └────────────────────────────────────────────┘  │            │
    │       │  ┌────────────────────────────────────────────┐  │            │
    │ Main ─┤  │  SUB                                       │  │            │
    │module │  │   statements in second SUB procedure       │  │            │
    │       │  │  ENDSUB                                    │  │            │
    │       │  └────────────────────────────────────────────┘  │            │
    │       │     .                                            │            │
    │       │     .                                            ├─Procedures │
    │       │     .                                            │            │
    │       │  ┌────────────────────────────────────────────┐  │            │
    │       │  │  FUNCTION                                  │  │            │
    │       │  │   statements in first FUNCTION procedure   │  │            │
    │       │  │  ENDFUNCTION                               │  │            │
    │       │  └────────────────────────────────────────────┘  │            │
    │       │  ┌────────────────────────────────────────────┐  │            │
    │       │  │  FUNCTION                                  │  │            │
    │       │  │   statements in second FUNCTION procedure  │  │            │
    │       │  │  ENDFUNCTION                               │  │            │
    │       │  └────────────────────────────────────────────┘  │            │
    │       │     .                                            │            │
    │       │     .                                            │            │
    │       │     .                                            │            │
    │       └─                                                ─┘            │
    │                                                                       │
    └───────────────────────────────────────────────────────────────────────┘

    Figure 7.1  Main Module Showing Module-Level Code and Procedures


7.4  Creating a Procedures-Only Module

    It's easy to create a module that contains only procedures. You can enter
    new procedures in a separate file, or you can move procedures from one
    file to another.

    To create a new module:

    1. Invoke QuickBASIC without opening or loading any files.

    2. Write all the SUB and FUNCTION procedures you wish, but don't enter any
        module-level code. (Any error- or event-trapping routines and BASIC
        declarations needed are exceptions.)

    3. Use the Save As command to name and save this module.

    To move procedures from one module to another:

    1. Load the files containing the procedures you want to move.

    2. If the destination file already exists, use the Load File command from
        the File menu to load it, too. If it doesn't exist, use the Create File
        option from the File menu to make the new file.

    3. Choose the SUBs command from the View menu and use its Move feature to
        transfer the procedures from the old to the new file. This transfer is
        made final when you quit QuickBASIC and respond Yes to the dialog box
        that asks whether you want to save the modified files; otherwise, the
        procedures remain where they were when you started.


7.5  Loading Modules

    In QuickBASIC, you can load as many modules as you wish (limited only by
    the available memory) using the Load File command from the File menu. All
    the procedures in all the loaded modules can be called from any other
    procedure or from module-level code. If a module happens to contain a
    procedure that is never called, no harm occurs.

    Any or all of the loaded modules can contain module-level code. QuickBASIC
    normally begins execution with the module-level code of the first module
    loaded. If you want execution to begin in a different module, use the Set
    Main Module command from the Run menu. Execution normally halts at the end
    of the designated main module; by design, QuickBASIC does not continue
    execution with the module-level code from other modules.

    The ability to choose which module-level code gets executed is useful when
    comparing two versions of the same program. For example, you might want to
    test different user interfaces by putting each in a separate module. You
    can also place test code in a module containing only procedures, then use
    the Set Main Module command to switch between the program and the tests.

    You do not have to keep track of which modules your program uses. Whenever
    you use the Save All command, QuickBASIC creates (or updates) a .MAK file,
    which lists all the modules currently loaded. The next time the main
    module is loaded with the Open Program command, QuickBASIC consults this
    .MAK file and automatically loads the modules listed in it.


7.6  Using the DECLARE Statement with Multiple Modules

    The DECLARE statement has several important functions in QuickBASIC. Using
    a DECLARE statement will

    1. Specify the sequence and data types of a procedure's parameters

    2. Enable type-checking, which confirms, each time a procedure is called,
        that the arguments agree with the parameters in both number and data
        type

    3. Identify a FUNCTION procedure's name as a procedure name, not a
        variable name

    4. Most important of all, enable the main module to call procedures
        located in other modules (or Quick libraries)

    QuickBASIC has its own system for automatically inserting the required
    DECLARE statements into your modules. Section 2.5.4, "Checking Arguments
    with the DECLARE Statement," explains the features and limitations of this
    system.

    Despite QuickBASIC's automatic insertion of the DECLARE statement, you may
    wish to create a separate include file that contains all the DECLARE
    statements required for a program. You can update this file manually as
    you add and delete procedures or modify your argument lists.

    If you write your programs with a text editor (rather than the QuickBASIC
    programming environment) and compile with BC, you must insert DECLARE
    statements manually.


7.7  Accessing Variables from Two or More Modules

    You can use the SHARED attribute to make variables accessible both at
    module level and within that module's procedures. If these procedures are
    moved to another module, however, these variables are no longer shared.

    You could pass these variables to each procedure through its argument
    list. This may be inconvenient, though, if you need to pass a large number
    of variables.

    One solution is to use COMMON statements, which enable two or more modules
    to access the same group of variables. Section 2.6, "Sharing Variables
    with SHARED," explains how to do this.

    Another solution is to use a TYPE...END TYPE statement to combine all the
    variables you wish to pass into a single structure. The argument and
    parameter lists then have to include only one variable name, no matter how
    many variables are passed.

    If you are simply splitting up a program and its procedures into separate
    modules, either of these approaches works well. If, on the other hand, you
    are adding a procedure to a module (for use in other programs), you should
    avoid using a COMMON statement. Modules are supposed to make it easy to
    add existing procedures to new programs; COMMON statements complicate the
    process. If a procedure needs a large group of variables, it may not
    belong in a separate module.


7.8  Using Modules During Program Development

    When you start a new programming project, you should first look through
    existing modules to see if there are procedures that can be reused in your
    new software. If any of these procedures aren't already in a separate
    module, you should consider moving them to one.

    As your program takes shape, newly written procedures automatically become
    part of the program file (that is, the main module). You can move them to
    a separate module for testing or perhaps add them to one of your own
    modules along with other general-purpose procedures that are used in other
    programs.

    Your program may need procedures written in other languages. (For example,
    MASM is ideal for direct interface with the hardware, FORTRAN has almost
    any math function you could want, Pascal allows the creation of
    sophisticated data structures, and C provides both structured code and
    direct memory access.) These procedures are compiled and linked into a
    Quick library for use in your program. You can also write a separate
    module to test Quick library procedures the same way you would test other
    procedures.


7.9  Compiling and Linking Modules

    The end product of your programming efforts is usually a stand-alone .EXE
    file. You can create one in QuickBASIC by loading all of a program's
    modules, then selecting the Make EXE File command from the Run menu.

    You can also compile modules with the BC command-line compiler, then use
    LINK to combine the object code. Object files from code written in other
    languages can be linked at the same time.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    When you use the Make EXE File command, all the module-level code and
    every procedure currently loaded is included in the .EXE file, whether
    or not the program uses this code. If you want your program to be as
    compact as possible, you must unload all unneeded module-level code and
    all unneeded procedures before compiling. The same rule applies when
    using BC to compile from the command line; all unused code should be
    removed from the files.
    ──────────────────────────────────────────────────────────────────────────


7.10  Quick Libraries

    Although Microsoft Quick libraries are not modules, it is important that
    you understand their relationship with modules.

    A Quick library contains nothing but procedures. These procedures can be
    written not only in QuickBASIC, but also in other Microsoft languages as
    well (C, Pascal, FORTRAN, and MASM).

    A Quick library contains only compiled code. (Modules contain QuickBASIC
    source code.) A Quick library is created by linking compiled object code
    (.OBJ files). The code in a Quick library can come from any combination of
    Microsoft languages. Appendix H, "Creating and Using Quick Libraries,"
    explains how to create Quick libraries from object code and how to add new
    object code to existing Quick libraries.

    Quick libraries have several uses:

    ■ They provide an interface between QuickBASIC and other languages.

    ■ They allow designers to hide proprietary software. Updates and utilities
    can be distributed as Quick libraries without revealing the source code.

    ■ They load faster and are usually smaller than modules. If a large
    program with many modules loads slowly, converting the nonmain modules
    into a Quick library will improve loading performance.

    Note, however, that modules are the easiest way to work on procedures
    during development because modules are immediately ready to run after each
    edit; you don't have to recreate the Quick library. If you want to put
    your QuickBASIC procedures in a Quick library, wait until the procedures
    are complete and thoroughly debugged.

    When a Quick library is created, any module-level code in the file it was
    created from is automatically included. However, other modules cannot
    access this code, so it just wastes space. Before converting a module to a
    Quick library, be sure that all module-level statements (except any error
    or event handlers and declarations that are used by the procedures) have
    been removed.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Quick libraries are not included in .MAK files and must be loaded with
    the / L option when you run QuickBASIC. A Quick library has the file
    extension .QLB. During the process of creating the Quick library,
    another library file with the extension .LIB is created. This file
    contains the same code as the Quick library but in a form that allows it
    to be linked with the rest of the program to create a stand-alone
    application.

    If you use Quick libraries to distribute proprietary code
    (data-manipulation procedures, for example), be sure to include the .LIB
    files so that your customers can create stand-alone applications that
    use these procedures. Otherwise, they will be limited to running
    applications within the QuickBASIC environment.
    ──────────────────────────────────────────────────────────────────────────

7.10.1  Creating Quick Libraries

    You can create a Quick library of QuickBASIC procedures with the Make
    Library command from the Run menu. The Quick library created contains
    every procedure currently loaded, whether or not your program calls it.
    (It also contains all the module-level code.) If you want the Quick
    library to be compact, be sure to remove all unused procedures and all
    unnecessary module-level code first.

    You can create as many Quick libraries as you like, containing whatever
    combination of procedures you wish. However, only one Quick library can be
    loaded into QuickBASIC at a time. (You would generally create
    application-specific Quick libraries, containing only the procedures a
    particular program needs.) Large Quick libraries can be created by loading
    many modules, then using the Make Library command.

    You can also compile one or more modules with the BC command, then link
    the object code files to create a Quick library. Quick libraries of
    procedures written

    in other languages are created the same way. In linking, you are not
    limited to one language; the object-code files from any number of
    Microsoft languages can be combined in one Quick library. Appendix H,
    "Creating and Using Quick Libraries," explains how to convert object-code
    (.OBJ) files into Quick libraries.


7.11  Tips for Good Programming with Modules

    You can use modules in any way you think will improve your program or help
    organize your work. The following suggestions are offered as a guide.

    1. Think and organize first.

        When you start on a new project, make a list of the operations you want
        to be performed by procedures. Then look through your own procedure
        library to see if there are any you can use, either as-is or with
        slight modifications. Don't waste time "reinventing the wheel."

    2. Write generalized procedures with broad application.

        Try to write procedures that are useful in a wide variety of programs.
        Don't, however, make the procedure needlessly complex. A good procedure
        is a simple, finely honed tool, not a Swiss army knife.

        It is sometimes useful to alter an existing procedure to work in a new
        program. This might require modifying programs you've already written,
        but it's worth the trouble if the revised procedure is more powerful or
        has broader application.

    3. When creating your own procedure modules, keep logically separate
        procedures in separate modules.

        It makes sense to put string-manipulation procedures in one module,
        matrix-handling procedures in another, and data-communication
        procedures in a third. This arrangement avoids confusion and makes it
        easy to find the procedure you need.



────────────────────────────────────────────────────────────────────────────
PART 2  HEART OF BASIC
────────────────────────────────────────────────────────────────────────────

    Part 2 provides a quick-reference guide to all statements and functions
    used in this version of BASIC.

    Chapter 8 gives a brief summary of the action of each statement or
    function──organized alphabetically by keyword──and includes syntax lines
    that identify the statement's correct form. Use it when programming to
    remind yourself how each one works.

    Chapter 9 is made up of reference tables that organize commonly used
    statements and functions by programming topic. These tables follow the
    same organization as the first six chapters of this book. Use these tables
    to identify alternative ways to approach particular programming
    objectives.



────────────────────────────────────────────────────────────────────────────
Chapter 8  Statement and Function Summary

    This chapter summarizes QuickBASIC statements and functions. Each
    statement or function name is followed by an action description and a
    syntax line. The syntax line shows exactly what you must enter to use the
    statement.

    Complete details of the typographic conventions used in syntax lines are
    given in the introduction to this manual. In general, items you must type
    exactly as shown are in boldface type; placeholders for items of
    information you must supply are in Italic type. Square brackets indicate
    optional items.

    The QB Advisor (QuickBASIC's on-line language help) provides for each
    statement or function:

    ■ A summary of the statement's action and syntax (the QuickSCREEN)

    ■ Complete details of how to use the statement, including explanations of
    the placeholders

    ■ One of more program examples illustrating statement use

    You can access this information by putting the cursor on any BASIC keyword
    displayed in the QuickBASIC View window and pressing F1.

    The statements and functions in this chapter are grouped by topic in
    Chapter 9, "Quick-Reference Tables." Use Chapter 9 to find out what
    statements are available for a particular programming task.


ABS Function

    Returns the absolute value of a numeric expression

    SYNTAX  ABS(numeric-expression)


ASC Function

    Returns a numeric value that is the ASCII code for the first character in
    a string expression

    SYNTAX  ASC(stringexpression)


ATN Function

    Returns the arctangent of a numeric expression (the angle whose tangent is
    equal to the numeric expression)

    SYNTAX  ATN(numeric-expression)


BEEP Statement

    Sounds the speaker

    SYNTAX  BEEP


BLOAD Statement

    Loads a memory-image file, created by BSAVE, into memory from an input
    file or device

    SYNTAX  BLOAD filespec [[,offset]]


BSAVE Statement

    Transfers the contents of an area of memory to an output file or device

    SYNTAX  BSAVE filespec,offset,length


CALL Statement (BASIC Procedures)

    Transfers control to a BASIC SUB

    SYNTAX 1  CALL name[[(argumentlist )]]

    SYNTAX 2  name[[argumentlist]]


CALL, CALLS Statement (Non-BASIC Procedures)

    Transfers control to a procedure written in another language

    SYNTAX 1  CALL name [[(call-argumentlist )]]

    SYNTAX 2  name [[call-argumentlist]]

    SYNTAX 3  CALLS name [[(calls-argumentlist )]]


CALL INT86OLD Statements

    Allows programs to perform DOS system calls

    SYNTAX  CALL INT86OLD (int_no,in_array(),out_array())
            CALL INT86XOLD (int_no,in_array(),out_array())


CALL ABSOLUTE Statement

    Transfers control to a machine-language procedure

    SYNTAX  CALL ABSOLUTE ([[argumentlist,]]integervariable)


CALL INTERRUPT Statements

    Allows BASIC programs to perform DOS system calls

    SYNTAX  CALL INTERRUPT (interruptnum, inregs, outregs)
            CALL INTERRUPTX (interruptnum, inregs, outregs)


CDBL Function

    Converts a numeric expression to a double-precision number

    SYNTAX  CDBL(numeric-expression)


CHAIN Statement

    Transfers control from the current program to another program

    SYNTAX  CHAIN filespec


CHDIR Statement

    Changes the current default directory for the specified drive

    SYNTAX  CHDIR pathspec


CHR$ Function

    Returns a one-character string whose ASCII code is the argument

    SYNTAX  CHR$(code)


CINT Function

    Converts a numeric expression to an integer by rounding the fractional
    part of the expression

    SYNTAX  CINT(numeric-expression)


CIRCLE Statement

    Draws an ellipse or circle with a specified center and radius

    SYNTAX  CIRCLE[[STEP]]
            (x,y),radius[[,[[color]][[,[[start]][[,[[end]][[,aspect]]]]]]]]


CLEAR Statement

    Reinitializes all program variables, closes files, and sets the stack size

    SYNTAX  CLEAR[[,,stack]]


CLNG Function

    Converts a numeric expression to a long (4-byte) integer by rounding the
    fractional part of the expression

    SYNTAX  CLNG(numeric-expression)


CLOSE Statement

    Concludes I/O to a file or device

    SYNTAX  CLOSE[[[[#]] filenumber [[,[[#]] filenumber]]... ]]


CLS Statement

    Clears the screen

    SYNTAX  CLS[[{0| 1| 2}]]


COLOR Statement

    Selects display colors

    SYNTAX  COLOR[[foreground]][[,[[background]][[,border]]]]  Screen mode 0
            COLOR[[background]][[,palette]]                    Screen mode 1
            COLOR[[foreground]][[,background]]                 Screen modes
                                                                7-10
            COLOR[[foreground]]                                Screen modes
                                                                12-13


COM Statements

    Enables, disables, or inhibits event trapping of communications activity
    on a specified port

    SYNTAX  COM(n) ON COM(n) OFF COM(n) STOP


COMMAND$ Function

    Returns the command line used to invoke the program

    SYNTAX  COMMAND$


COMMON Statement

    Defines global variables for sharing between modules or for chaining to
    another program

    SYNTAX  COMMON[[SHARED]][[/blockname/]] variablelist


CONST Statement

    Declares symbolic constants to use in place of numeric or string values

    SYNTAX  CONST constantname = expression [[,constantname = expression]]...


COS Function

    Returns the cosine of an angle given in radians

    SYNTAX  COS(numeric-expression)


CSNG Function

    Converts a numeric expression to a single-precision value

    SYNTAX  CSNG(numeric-expression)


CSRLIN Function

    Returns the current line (row) position of the cursor

    SYNTAX  CSRLIN


CVI, CVS, CVL, CVD Functions

    Convert strings containing numeric values to numbers

    SYNTAX  CVI(2-byte-string)
            CVS(4-byte-string)
            CVL(4-byte-string)
            CVD(8-byte-string)


CVSMBF, CVDMBF Functions

    Convert strings containing Microsoft Binary format numbers to IEEE-format
    numbers

    SYNTAX  CVSMBF (4-byte-string)
            CVDMBF (8-byte-string)


DATA Statement

    Stores the numeric and string constants used by a program's READ
    statements

    SYNTAX  DATA constant1 [[,constant2]]...


DATE$ Function

    Returns a string containing the current date

    SYNTAX  DATE$


DATE$ Statement

    Sets the current date

    SYNTAX  DATE$ = stringexpression


DECLARE Statement (BASIC Procedures)

    Declares references to BASIC procedures and invokes argument type checking

    SYNTAX  DECLARE{FUNCTION | SUB } name [[([[parameterlist]])]]


DECLARE Statement (Non-BASIC Procedures)

    Declares calling sequences for external procedures written in other
    languages

    SYNTAX 1  DECLARE FUNCTION name [[CDECL]]
            [[ALIAS "aliasname"]][[([[parameterlist]])]]

    SYNTAX 2  DECLARE SUB name [[CDECL]] [[ALIAS
            "aliasname"]][[([[parameterlist]])]]


DEF FN Statement

    Defines and names a function

    SYNTAX 1  DEF FNname[[(parameterlist)]] = expression

    SYNTAX 2  DEF FNname[[(parameterlist)]]
            .
            .
            .
            FNname = expression
            .
            .
            .
            END DEF


DEF SEG Statement

    Sets the current segment address for a subsequent PEEK function or a
    BLOAD, BSAVE, CALL ABSOLUTE, or POKE statement

    SYNTAX  DEF SEG[[=address]]


DEFtype Statements

    Set the default data type for variables, DEF FN functions, and FUNCTION
    procedures

    SYNTAX  DEFINT letterrange [[,letterrange]]...
            DEFSNG letterrange [[,letterrange]]...
            DEFDBL letterrange [[,letterrange]]...
            DEFLNG letterrange [[,letterrange]]...
            DEFSTR letterrange [[,letterrange]]...


DIM Statement

    Declares a variable and allocates storage space

    SYNTAX  DIM[[SHARED]] variable[[(subscripts)]] [[AS type]]
            [[,variable[[(subscripts)]] [[AStype]]]]...


DO...LOOP Statements

    Repeats a block of statements while a condition is true or until a
    condition becomes true

    SYNTAX 1  DO     [[statementblock]]
            LOOP[[{WHILE| UNTIL} booleanexpression]]

    SYNTAX 2  DO[[{WHILE| UNTIL} booleanexpression]]
                    [[statementblock]]
            LOOP


DRAW Statement

    Draws an object defined by stringexpression

    SYNTAX  DRAW stringexpression


END Statement

    Ends a BASIC program, procedure, or block

    SYNTAX  END[[{DEF| FUNCTION | IF| SELECT| SUB | TYPE}]]


ENVIRON$ Function

    Retrieves an environment string from the DOS environment-string table

    SYNTAX  ENVIRON$ (environmentstring)
            ENVIRON$ (n)


ENVIRON Statement

    Modifies a parameter in the DOS environment-string table

    SYNTAX  ENVIRON stringexpression


EOF Function

    Tests for the end-of-file condition

    SYNTAX  EOF(filenumber)


ERASE Statement

    Reinitializes the elements of static arrays; deallocates dynamic arrays

    SYNTAX  ERASE arrayname [[,arrayname...]]


ERDEV, ERDEV$ Functions

    Provides device-specific status information after an error

    SYNTAX  ERDEV
            ERDEV$


ERR, ERL Functions

    Return error status

    SYNTAX  ERR
            ERL


ERROR Statement

    Simulates the occurrence of a BASIC error or allows the user to define
    error codes

    SYNTAX  ERROR integerexpression


EXIT Statement

    Exits a DEF FN function, DO...LOOP or FOR...NEXT loop, FUNCTION, or SUB

    SYNTAX  EXIT{DEF| DO| FOR| FUNCTION| SUB}


EXP Function

    Calculates the exponential function

    SYNTAX  EXP(x)


FIELD Statement

    Allocates space for variables in a random-access file buffer

    SYNTAX  FIELD[[#]]filenumber, fieldwidth AS stringvariable...


FILEATTR Function

    Returns information about an open file

    SYNTAX  FILEATTR(filenumber,attribute)


FILES Statement

    Prints the names of files residing on the specified disk

    SYNTAX  FILES[[filespec]]


FIX Function

    Returns the truncated integer part of x

    SYNTAX  FIX(x)


FOR...NEXT Statement

    Repeats a group of instructions a specified number of times

    SYNTAX  FOR counter = start TO end [[STEP increment]]
            .
            .
            .
            NEXT[[counter[[,counter...]]]]


FRE Function

    Returns the amount of available memory

    SYNTAX 1  FRE(numeric-expression)

    SYNTAX 2  FRE(stringexpression)


FREEFILE Function

    Returns the next free BASIC file number

    SYNTAX  FREEFILE


FUNCTION Statement

    Declares the name, the parameters, and the code that form the body of a
    FUNCTION procedure

    SYNTAX  FUNCTION name [[(parameter-list)]][[STATIC]]
            .
            .
            .
            name = expression
            .
            .
            .
            END FUNCTION


GET Statement──File I/O

    Reads from a disk file into a random-access buffer or variable

    SYNTAX  GET[[#]]filenumber[[,[[recordnumber]][[,variable]]]]


GET Statement──Graphics

    Stores graphic images from the screen

    SYNTAX  GET[[STEP]](x1,y1) -[[STEP]](x2,y2),arrayname[[(indices)]]


GOSUB...RETURN Statements

    Branches to, and returns from, a subroutine

    SYNTAX  GOSUB{linelabel1| linenumber1 }
            .
            .
            .
            RETURN[[linelabel2| linenumber2 ]]


GOTO Statement

    Branches unconditionally to the specified line

    SYNTAX  GOTO{linelabel| linenumber}


HEX$ Function

    Returns a string that represents the hexadecimal value of the decimal
    argument expression

    SYNTAX  HEX$(expression)


IF...THEN...ELSE Statements

    Allows conditional execution, based on the evaluation of a Boolean
    expression

    SYNTAX 1 (SINGLE LINE)  IF booleanexpression THEN thenpart [[ELSE
                            elsepart]]

    SYNTAX 2 (BLOCK)   IF booleanexpression1 THEN
                            [[statementblock-1]]
                        [[ELSEIF booleanexpression2 THEN
                            [[statementblock-2]]]]
                        .
                        .
                        .
                        [[ELSE
                            [[statement-blockn]]]]
                        END IF


INKEY$ Function

    Reads a character from the keyboard

    SYNTAX  INKEY$


INP Function

    Returns the byte read from an I/O port

    SYNTAX  INP(port)


INPUT$ Function

    Returns a string of characters read from the specified file

    SYNTAX  INPUT$(n[[,[[#]]filenumber]])


INPUT Statement

    Allows input from the keyboard during program execution

    SYNTAX  INPUT[[;]][["promptstring"{; | ,}]] variablelist


INPUT # Statement

    Reads data items from a sequential device or file and assigns them to
    variables

    SYNTAX  INPUT # filenumber, variablelist


INSTR Function

    Returns the character position of the first occurrence of a string in
    another string

    SYNTAX  INSTR([[start,]]stringexpression1,stringexpression2)


INT Function

    Returns the largest integer less than or equal to numeric-expression

    SYNTAX  INT(numeric-expression)


IOCTL$ Function

    Receives a control data string from a device driver

    SYNTAX  IOCTL$ ([[#]]filenumber)


IOCTL Statement

    Transmits a control data string to a device driver

    SYNTAX  IOCTL[[#]]filenumber, string


KEY Statements

    Assign soft-key string values to function keys, then display the values
    and enable or disable the FUNCTION key display line

    SYNTAX  KEY n, stringexpression
            KEY LIST
            KEY ON
            KEY OFF


KEY(n) Statements

    Start or stop trapping of specified keys

    SYNTAX  KEY(n) ON
            KEY(n) OFF
            KEY(n) STOP


KILL Statement

    Deletes a file from disk

    SYNTAX  KILL filespec


LBOUND Function

    Returns the lower bound (smallest available subscript) for the indicated
    dimension of an array

    SYNTAX  LBOUND(array[[,dimension]])


LCASE$ Function

    Returns a string expression with all letters in lower-case

    SYNTAX  LCASE$ (stringexpression)


LEFT$ Function

    Returns a string consisting of the leftmost n characters of a string

    SYNTAX  LEFT$(stringexpression,n)


LEN Function

    Returns the number of characters in a string or the number of bytes
    required by a variable

    SYNTAX  LEN(stringexpression)
            LEN(variable)


LET Statement

    Assigns the value of an expression to a variable

    SYNTAX  [[LET]]variable=expression


LINE Statement

    Draws a line or box on the screen

    SYNTAX  LINE[[[[STEP]] (x1,y1)]] -[[STEP]]
            (x2,y2)[[,[[color]][[,[[B[[F]]]][[,style]]]]]]


LINE INPUT Statement

    Inputs an entire line (up to 255 characters) to a string variable, without
    the use of delimiters

    SYNTAX  LINE INPUT[[;]] [["promptstring";]] stringvariable


LINE INPUT # Statement

    Reads an entire line without delimiters from a sequential file to a string
    variable

    SYNTAX  LINE INPUT #filenumber,stringvariable


LOC Function

    Returns the current position within the file

    SYNTAX  LOC(filenumber)


LOCATE Statement

    Moves the cursor to the specified position

    SYNTAX  LOCATE[[row]][[,[[column]][[,[[cursor]][[,[[start,stop]]]]]]]]


LOCK...UNLOCK Statement

    Controls access by other processes to all or part of an opened file

    SYNTAX  LOCK[[#]] filenumber [[,{record | [[start]] TO end}]]
            .
            .
            .
            UNLOCK[[#]] filenumber [[,{record | [[start]] TO end}]]


LOF Function

    Returns the length of the named file in bytes

    SYNTAX  LOF(filenumber)


LOG Function

    Returns the natural logarithm of a numeric expression

    SYNTAX  LOG(n)


LPOS Function

    Returns the current position of the line printer's print head within the
    printer buffer

    SYNTAX  LPOS(n)


LPRINT, LPRINT USING Statements

    Prints data on the printer LPT1:

    SYNTAX 1  LPRINT[[expressionlist]] [[{;|,}]]

    SYNTAX 2  LPRINT USING formatstring; expression-list [[{;|,}]]


LSET Statement

    Moves data from memory to a random-access file buffer (in preparation for
    a PUT statement), copies one record variable to another, or left-justifies
    the value of a string in a string variable

    SYNTAX
            LSET{stringvariable=stringexpression|stringexpression1=stringexpre
            ssion2}


LTRIM$ Function

    Returns a copy of a string with leading spaces removed

    SYNTAX  LTRIM$(stringexpression)


MID$ Function

    Returns a substring of a string

    SYNTAX  MID$(stringexpression,start[[,length]])


MID$ Statement

    Replaces a portion of a string variable with another string

    SYNTAX  MID$(stringvariable,start[[,length]])=stringexpression


MKD$, MKI$, MKL$, MKS$ Functions

    Converts numeric values to string values

    SYNTAX  MKI$(integerexpression)
            MKS$(single-precision-expression)
            MKL$(long-integer-expression)
            MKD$(double-precision-expression)


MKDIR Statement

    Creates a new directory

    SYNTAX  MKDIR pathname


MKSMBF$, MKDMBF$ Functions

    Converts an IEEE-format number to a string containing a Microsoft Binary
    format number

    SYNTAX  MKSMBF$(single-precision-expression)
            MKDMBF$(double-precision-expression)


NAME Statement

    Changes the name of a disk file or directory

    SYNTAX  NAME oldfilename AS newfilename


OCT$ Function

    Returns a string representing the octal value of the numeric argument

    SYNTAX  OCT$(numeric-expression)


ON ERROR Statement

    Enables error handling and specifies the first line of the error-handling
    routine

    SYNTAX  ON ERROR GOTO line


ON event Statements

    Indicates the first line of an event-trapping subroutine

    SYNTAX  ON event GOSUB{linenumber | linelabel }


ON UEVENT GOSUB Statement

    Defines the event-handler for a user-defined event

    SYNTAX  ON UEVENT GOSUB { linenumber| linelabel }


ON...GOSUB, ON...GOTO Statements

    Branches to one of several specified lines, depending on the value of an
    expression

    SYNTAX 1  ON expression GOSUB{line-number-list | line-label-list }

    SYNTAX 2  ON expression GOTO{line-number-list | line-label-list }


OPEN Statement

    Enables I/O to a file or device

    SYNTAX 1  OPEN file[[FOR mode1]] [[ACCESS access]] [[lock]] AS[[#]]
            filenum[[LEN=reclen]]

    SYNTAX 2  OPEN mode2,[[#]]filenum, file [[,reclen]]


OPEN COM Statement

    Opens and initializes a communications channel for I/O

    SYNTAX  OPEN "COMn: optlist1 optlist2" [[FOR mode]] AS[[#]]filenum
            [[LEN=reclen]]


OPTION BASE Statement

    Declares the default lower bound for array subscripts

    SYNTAX  OPTION BASE n


OUT Statement

    Sends a byte to a machine I/O port

    SYNTAX  OUT port, data


PAINT Statement

    Fills a graphics area with the color or pattern specified

    SYNTAX  PAINT[[STEP]] (x,y)[[,[[paint]] [[,[[bordercolor]]
            [[,background]]]]]]


PALETTE, PALETTE USING Statements

    Changes one or more of the colors in the palette

    SYNTAX  PALETTE[[attribute,color]]
            PALETTE USING array-name [[(array-index)]]


PCOPY Statement

    Copies one screen page to another

    SYNTAX  PCOPY sourcepage, destinationpage


PEEK Function

    Returns the byte stored at a specified memory location

    SYNTAX  PEEK(address)


PEN Function

    Reads the lightpen coordinates

    SYNTAX  PEN(n)


PEN ON, OFF, and STOP Statements

    Enables, disables, or suspends lightpen event trapping

    SYNTAX  PEN ON
            PEN OFF
            PEN STOP


PLAY Function

    Returns the number of notes currently in the background-music queue

    SYNTAX  PLAY (n)


PLAY Statement

    Plays music as specified by a string

    SYNTAX  PLAY commandstring


PLAY ON, OFF, and STOP Statements

    PLAY ON enables play event trapping, PLAY OFF disables play event
    trapping, and PLAY STOP suspends play event trapping.

    SYNTAX  PLAY ON
            PLAY OFF
            PLAY STOP


PMAP Function

    Maps view-coordinate expressions to physical locations, or maps physical
    expressions to a view-coordinate location

    SYNTAX  PMAP (expression, function)


POINT Function

    Reads the color number of a pixel from the screen or returns the pixel's
    coordinates

    SYNTAX  POINT (x,y)
            POINT (number)


POKE Statement

    Writes a byte into a memory location

    SYNTAX  POKE address,byte


POS Function

    Returns the current horizontal position of the cursor

    SYNTAX  POS(0)


PRESET Statement

    Draws a specified point on the screen

    SYNTAX  PRESET[[STEP]](xcoordinate,ycoordinate) [[,color]]


PRINT Statement

    Outputs data on the screen

    SYNTAX  PRINT[[expressionlist]] [[{,| ;}]]


PRINT #, PRINT # USING Statements

    Writes data to a sequential file

    SYNTAX  PRINT #filenumber,[[USING stringexpression;]] expressionlist [[{,
            | ;}]]


PRINT USING Statement

    Prints strings or numbers using a specified format

    SYNTAX  PRINT USING formatstring; expressionlist [[{,| ;}]]


PSET Statement

    Draws a point on the screen

    SYNTAX  PSET[[STEP]](xcoordinate,ycoordinate) [[,color]]


PUT Statement──File I/O

    Writes from a variable or a random-access buffer to a file

    SYNTAX  PUT[[#]]filenumber[[,[[recordnumber]][[,variable]]]]
            PUT[[#]]filenumber[[,{recordnumber|recordnumber,
            variable|,variable}]]


PUT Statement──Graphics

    Places a graphic image obtained by a GET statement onto the screen

    SYNTAX  PUT[[STEP]] (x,y), arrayname[[(indices)]] [[,actionverb]]


RANDOMIZE Statement

    Initializes (reseeds) the random-number generator

    SYNTAX  RANDOMIZE[[expression]]


READ Statement

    Reads values from a DATA statement and assigns the values to variables

    SYNTAX  READ variablelist


REDIM Statement

    Changes the space allocated to an array that has been declared $DYNAMIC

    SYNTAX  REDIM[[SHARED]] variable(subscripts)[[AS type]]
            [[,variable(subscripts)[[AS
            type]]]]...


REM Statement

    Allows explanatory remarks to be inserted in a program

    SYNTAX 1  REM remark

    SYNTAX 2  ' remark


RESET Statement

    Closes all disk files

    SYNTAX  RESET


RESTORE Statement

    Allows DATA statements to be reread from a specified line

    SYNTAX  RESTORE[[{linenumber | linelabel }]]


RESUME Statement

    Continues program execution after an error-trapping routine has been
    invoked

    SYNTAX  RESUME[[0]]
            RESUME NEXT
            RESUME { linenumber| linelabel }


RETURN Statement

    Returns control from a subroutine

    SYNTAX  RETURN[[{linenumber | linelabel }]]


RIGHT$ Function

    Returns the rightmost n characters of a string

    SYNTAX  RIGHT$(stringexpression,n)


RMDIR Statement

    Removes an existing directory

    SYNTAX  RMDIR pathname


RND Function

    Returns a single-precision random number between 0 and 1

    SYNTAX  RND[[(n)]]


RSET Statement

    Moves data from memory to a random-access file buffer (in preparation for
    a PUT statement) or right-justifies the value of a string in a string
    variable

    SYNTAX  RSET stringvariable=stringexpression


RTRIM$ Function

    Returns a string with trailing (right-hand) spaces removed

    SYNTAX  RTRIM$(stringexpression)


RUN Statement

    Restarts the program currently in memory or executes a specified program

    SYNTAX  RUN[[{ linenumber | commandline }]]


SADD Function

    Returns the address of the specified string expression

    SYNTAX  SADD(stringvariable)


SCREEN Function

    Reads a character's ASCII value or its color from a specified screen
    location

    SYNTAX  SCREEN(row,column[[,colorflag]])


SCREEN Statement

    Sets the specifications for the display screen

    SYNTAX  SCREEN[[mode]] [[,[[colorswitch]] ]][[,[[apage]] ]][[,[[vpage]]]]


SEEK Function

    Returns the current file position

    SYNTAX  SEEK(filenumber)


SEEK Statement

    Sets the position in a file for the next read or write

    SYNTAX  SEEK[[#]]filenumber,position


SELECT CASE Statement

    Executes one of several statement blocks depending on the value of an
    expression

    SYNTAX  SELECT CASE testexpression
            CASE expressionlist1
                [[statementblock-1]]
            [[CASE expressionlist2
                [[statementblock-2]]]]
            .
            .
            .
            [[CASE ELSE
                [[statementblock-n]]]]
            END SELECT


SETMEM Function

    Changes the amount of memory used by the far heap──the area where far
    objects and internal tables are stored

    SYNTAX  SETMEM(numeric-expression)


SGN Function

    Indicates the sign of a numeric expression

    SYNTAX  SGN(numeric-expression)


SHARED Statement

    Gives a SUB or FUNCTION procedure access to variables declared at the
    module level without passing them as parameters

    SYNTAX  SHARED variable [[AS type]] [[,variable [[AS type]]]]...


SHELL Statement

    Exits the BASIC program, runs a .COM, .EXE, or .BAT program or a DOS
    command, and returns to the program at the line following the SHELL
    statement

    SYNTAX  SHELL[[commandstring]]


SIN Function

    Returns the sine of the angle x, where x is in radians

    SYNTAX  SIN(x)


SLEEP Statement

    Suspends execution of the calling program

    SYNTAX  SLEEP[[ seconds ]]


SOUND Statement

    Generates sound through the speaker

    SYNTAX  SOUND frequency,duration


SPACE$ Function

    Returns a string of spaces of length n

    SYNTAX  SPACE$(n)


SPC Function

    Skips n spaces in a PRINT statement

    SYNTAX  SPC(n)


SQR Function

    Returns the square root of n

    SYNTAX  SQR(n)


STATIC Statement

    Makes simple variables or arrays local to either a DEF FN function, a
    FUNCTION, or a SUB and preserves values between calls

    SYNTAX  STATIC variablelist


STICK Function

    Returns the x and y coordinates of the two joysticks

    SYNTAX  STICK(n)


STOP Statement

    Terminates the program

    SYNTAX  STOP


STR$ Function

    Returns a string representation of the value of a numeric expression

    SYNTAX  STR$(numeric-expression)


STRIG Function and Statement

    Returns the status of a specified joystick trigger

    SYNTAX 1 (FUNCTION)  STRIG(n)

    SYNTAX 2 (STATEMENT)  STRIG{ON| OFF}


STRIG ON, OFF, and STOP Statements

    Enable, disable, or inhibit trapping of joystick activity

    SYNTAX  STRIG(n) ON
            STRIG(n) OFF
            STRIG(n) STOP


STRING$ Function

    Returns a string whose characters all have a given ASCII code or whose
    characters are all the first character of a string expression

    SYNTAX  STRING$(m,n)
            STRING$(m,stringexpression)


SUB Statements

    Marks the beginning and end of a subprogram

    SYNTAX  SUB globalname[[(parameterlist)]] [[STATIC]]
            .
            .
            .
            [[EXIT SUB]]
            .
            .
            .
            END SUB


SWAP Statement

    Exchanges the values of two variables

    SYNTAX  SWAP variable1,variable2


SYSTEM Statement

    Closes all open files and returns control to the operating system

    SYNTAX  SYSTEM


TAB Function

    Moves the print position

    SYNTAX  TAB(column)


TAN Function

    Returns the tangent of the angle x, where x is in radians

    SYNTAX  TAN(x)


TIME$ Function

    Returns the current time from the operating system

    SYNTAX  TIME$


TIME$ Statement

    Sets the time

    SYNTAX  TIME$=stringexpression


TIMER Function

    Returns the number of seconds elapsed since midnight

    SYNTAX  TIMER


TIMER ON, OFF, and STOP Statements

    Enables, disables, or inhibits timer event trapping

    SYNTAX  TIMER ON
            TIMER OFF
            TIMER STOP


TRON/TROFF Statements

    Traces the execution of program statements

    SYNTAX  TRON
            TROFF


TYPE Statement

    Defines a data type containing one or more elements

    SYNTAX  TYPE usertype
                elementname AS    typename
                elementname AS    typename
                .
                .
                .
            END TYPE


UBOUND Function

    Returns the upper bound (largest available subscript) for the indicated
    dimension of an array

    SYNTAX  UBOUND(array[[,dimension]])


UCASE$ Function

    Returns a string expression with all letters in uppercase

    SYNTAX  UCASE$ (stringexpression)


UEVENT Statement

    Enables, disables, or suspends event trapping for a user-defined event

    SYNTAX  UEVENT ON
            UEVENT OFF
            UEVENT STOP


UNLOCK Statement

    Releases locks applied to parts of a file

    SYNTAX  UNLOCK[[#]] filenumber [[,{record | [[start]] TO end}]]


VAL Function

    Returns the numeric value of a string of digits

    SYNTAX  VAL(stringexpression)


VARPTR, VARSEG Functions

    Return the address of a variable

    SYNTAX  VARPTR(variablename)
            VARSEG(variablename)


VARPTR$ Function

    Returns a string representation of a variable's address for use in DRAW
    and PLAY statements

    SYNTAX  VARPTR$(variablename)


VIEW Statement

    Defines screen limits for graphics output

    SYNTAX  VIEW[[[[SCREEN]] (x1,y1)-(x2,y2)[[,[[color]] [[,border]]]]]]


VIEW PRINT Statement

    Sets the boundaries of the screen text viewport

    SYNTAX  VIEW PRINT[[topline TO bottomline]]


WAIT Statement

    Suspends program execution while monitoring the status of a machine input
    port

    SYNTAX  WAIT portnumber,and-expression[[,xor-expression]]


WHILE...WEND Statement

    Executes a series of statements in a loop, as long as a given condition is
    true

    SYNTAX  WHILE condition
            .
            .
            .
            [[statements]]
            .
            .
            .
            WEND


WIDTH Statement

    Assigns an output-line width to a file or device or changes the number of
    columns and lines displayed on the screen

    SYNTAX  WIDTH[[columns]][[,lines]]
            WIDTH{# filenumber | device} , width
            WIDTH LPRINT width


WINDOW Statement

    Defines the logical dimensions of the current viewport

    SYNTAX  WINDOW[[[[SCREEN]] (x1,y1)-(x2,y2)]]


WRITE Statement

    Sends data to the screen

    SYNTAX  WRITE[[expressionlist]]


WRITE # Statement

    Writes data to a sequential file

    SYNTAX  WRITE #filenumber,expressionlist



────────────────────────────────────────────────────────────────────────────
Chapter 9  Quick-Reference Tables

    Each section in this chapter summarizes a group of statements or functions
    that you typically use together. Each group is presented in a table, which
    lists the type of task performed (for example, looping or searching), the
    statement or function name, and the statement action.

    The following topics are summarized in tabular form:

    ■ Control-flow statements

    ■ Statements used in BASIC procedures

    ■ Standard I/O statements

    ■ File I/O statements

    ■ String-processing statements and functions

    ■ Graphics statements and functions

    ■ Trapping statements and functions

    You can use these tables both as a reference guide to what each statement
    or function does and as a way to identify related statements.


9.1  Summary of Control-Flow Statements

    Table 9.1 lists the BASIC statements used to control the flow of a
    program's execution.

    Table 9.1  Statements Used in Looping and Decision-Making
╓┌─┌────────────────────┌────────────────────┌───────────────────────────────╖
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
    Looping              FOR...NEXT           Repeats statements between FOR
                                            and NEXT a specific number of
                                            times.

                        EXIT FOR             Provides an alternative way to
                                            exit a FOR...NEXT loop.

                        DO...LOOP            Repeats statements between DO
                                            and LOOP, either until a given
                                            condition is true (DO...LOOP
                                            UNTIL condition), or while a
                                            given condition is true
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
                                            given condition is true
                                            (DO...LOOP WHILE condition).

                        EXIT DO              Provides an alternative way to
                                            exit a DO...LOOP loop.

                        WHILE...WEND         Repeats statements between WHILE
                                            and WEND while a given condition
                                            is true (similar to DO WHILE
                                            condition...LOOP).

    Making decisions     IF...THEN...ELSE     Conditionally executes or
                                            branches to different
                                            statements.

                        SELECT CASE          Conditionally executes different
                                            statements.
    ──────────────────────────────────────────────────────────────────────────

    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────



9.2  Summary of Statements Used in BASIC Procedures

    Table 9.2 lists the statements used in BASIC to define, declare, call,
    and pass arguments to BASIC procedures. Table 9.2 also lists the
    statements used to share variables among procedures, modules, and separate
    programs in a chain.

    Table 9.2  Statements Used in Procedures
╓┌─┌────────────────────┌────────────────────┌───────────────────────────────╖
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
    Defining a procedure FUNCTION...END       Mark the beginning and end,
                        FUNCTION             respectively, of a FUNCTION
                                            procedure.

                        SUB...END SUB        Mark the beginning and end,
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
                        SUB...END SUB        Mark the beginning and end,
                                            respectively, of a SUB
                                            procedure.

    Calling a procedure  CALL                 Transfers control to a BASIC SUB
                                            procedure, or to a procedure
                                            written in another programming
                                            language and compiled
                                            separately. (The CALL keyword is
                                            optional.)

    Exiting from a       EXIT FUNCTION        Provides another way to exit a
    procedure                                 FUNCTION procedure.

                        EXIT SUB             Provides an alternative way to
                                            exit a SUB procedure.

    Referencing a        DECLARE              Declares a FUNCTION or SUB and,
    procedure before it                       optionally, specifies the number
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
    procedure before it                       optionally, specifies the number
    is defined                                and type of its parameters.

    Sharing variables    COMMON               Shares variables among separate
    among modules,                            modules. When used with the
    procedures, or                            SHARED attribute, it shares
    programs                                  variables among different
                                            procedures in the same module.
                                            Also, passes variable values
                                            from current program to new
                                            program when control is
                                            transferred with the CHAIN
                                            statement.

                        SHARED               When used with the COMMON, DIM,
                                            or REDIM statement at the module
                                            level (for example, DIM SHARED),
                                            shares variables with every SUB
                                            or FUNCTION in a single module.
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
                                            or FUNCTION in a single module.

                                            When used by itself within a
                                            procedure, shares variables
                                            between that procedure and the
                                            module-level code.

    Preserving variable  STATIC               Forces variables to be local to
    values                                    a procedure or DEF FN function
                                            and preserves the value stored
                                            in the variable if the procedure
                                            or function is exited, then
                                            called again.

    Defining a multiline DEF FN...END DEF     Mark the beginning and end,
    function                                  respectively, of a multiline DEF
                                            FN function. (This is the old
                                            style for functions in BASIC──
                                            FUNCTION procedures provide a
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
                                            FUNCTION procedures provide a
                                            powerful alternative.)

    Exiting from a       EXIT DEF             Provides an alternative way to
    multiline function                        exit a multiline DEF FN
                                            function.

    Calling a BASIC      GOSUB                Transfers control to a specific
    subroutine                                line in a module. Control is
                                            returned from the subroutine to
                                            the line following the GOSUB
                                            statement with a RETURN
                                            statement. (This is the old
                                            style for subroutines in BASIC──
                                            SUB procedures provide a
                                            powerful alternative.)

    Transferring to      CHAIN                Transfers control from current
    another program                           program in memory to another
    Task                 Statement            Action
    ──────────────────────────────────────────────────────────────────────────
    another program                           program in memory to another
                                            program; use COMMON to pass
                                            variables to the new program.
    ──────────────────────────────────────────────────────────────────────────



9.3  Summary of Standard I/O Statements

    Table 9.3 lists the statements and functions used in BASIC for standard
    I/O (typically, input from the keyboard and output to the screen).

    Table 9.3  Statements and Functions Used for Standard I/O
╓┌─┌────────────────────┌────────────────────┌───────────────────────────────╖
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    Printing text on the PRINT                Outputs text to the screen.
    screen                                    Using PRINT with no arguments
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    screen                                    Using PRINT with no arguments
                                            creates a blank line.

                        PRINT USING          Outputs formatted text to the
                                            screen.

    Changing the width   WIDTH                Changes the width of the screen
    of the output line                        to either 40 columns or 80
                                            columns and, on computers with
                                            an EGA or VGA, controls the
                                            number of lines on the screen
                                            (25 or 43).

                        WIDTH "SCRN:"        Assigns a maximum length to
                                            lines output to the screen when
                                            used before an OPEN "SCRN:"
                                            statement.

                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────

    Getting input from   INKEY$               Reads a character from the
    the keyboard                              keyboard (or a null string if no
                                            character is waiting).

                        INPUT$               Reads a specified number of
                                            characters from the keyboard and
                                            stores them in a single string
                                            variable.

                        INPUT                Reads input from the keyboard
                                            and stores it in a list of
                                            variables.

                        LINE INPUT           Reads a line of input from the
                                            keyboard and stores it in a
                                            single string variable.

                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────

    Positioning the      LOCATE               Moves the cursor to a specified
    cursor on the screen                      row and column.

                        SPC                  Skips spaces in printed output.

                        TAB                  Displays printed output in a
                                            given column.

    Getting information  CSRLIN               Tells which row or line position
    on cursor location                        the cursor is in.

                        POS(n)               Tells which column the cursor is
                                            in.

    Creating a text      VIEW PRINT           Sets the top and bottom rows for
    viewport                                  displaying text output.
    ──────────────────────────────────────────────────────────────────────────
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    ──────────────────────────────────────────────────────────────────────────



9.4  Summary of File I/O Statements

    Table 9.4 lists the statements and functions used in BASIC data-file
    programming.

    Table 9.4  Statements and Functions Used for Data-File I/O
╓┌─┌────────────────────┌────────────────────┌───────────────────────────────╖
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    Creating a new file  OPEN                 Opens a file for retrieving or
    or accessing an                           storing records (I/O).
    existing file
    Closing a file       CLOSE                Ends I/O to a file.
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    Closing a file       CLOSE                Ends I/O to a file.
    Storing data in a    PRINT #              Stores a list of variables as
    file                                      record fields in a previously
                                            opened file.☼
                        PRINT USING #        Similar to PRINT #, except PRINT
                                            USING # formats the record
                                            fields.☼
                        WRITE #              Stores a list of variables as
                                            record fields in a previously
                                            opened file.☼
                        WIDTH                Specifies a standard length for
                                            each record in a file.☼
                        PUT                  Stores the contents of a
                                            user-defined variable in a
                                            previously opened file.☼
    Retrieving data from INPUT #              Reads fields from a record and
    a file                                    assigns each field in the record
                                            to a program variable.☼
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            to a program variable.☼
                        INPUT$               Reads a string of characters
                                            from a file.
                        LINE INPUT #         Reads a record and stores it in
                                            a single string variable.☼
                        GET                  Reads data from a file and
                                            assigns the data to elements of
                                            a user-defined variable.☼
    Managing files on    FILES                Prints a listing of the files in
    disk                                      a specified directory.
                        FREEFILE             Returns the next available file
                                            number.
                        KILL                 Deletes a file from the disk.
                        NAME                 Changes a file's name.
    Getting information  EOF                  Tests whether all of the data
    about a file                              have been read from a file.
                        FILEATTR             Returns the number assigned by
                                            the operating system to an open
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            the operating system to an open
                                            file and a number that indicates
                                            the mode in which the file was
                                            opened (INPUT, OUTPUT, APPEND,
                                            BINARY, or RANDOM).
                        LOC                  Gives the current position
                                            within a file. With binary
                                            access, this is the byte
                                            position. With sequential
                                            access, this is the byte
                                            position divided by 128. With
                                            random access, this is the
                                            record number of the last record
                                            read or written.
                        LOF                  Gives the number of bytes in an
                                            open file.
                        SEEK (function)      Gives the location where the
                                            next I/O operation will take
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            next I/O operation will take
                                            place. With random access, this
                                            is the number of the next record
                                            to be read or written. With all
                                            other kinds of file access, this
                                            is the byte position of the next
                                            byte to be read or written.
    Moving around in a   SEEK (statement)     Sets the byte position for the
    file                                      next read or write operation in
                                            an open file.
    ──────────────────────────────────────────────────────────────────────────



9.5  Summary of String-Processing Statements and Functions

    Table 9.5 lists the statements and functions available in BASIC for
    working with strings.

    Table 9.5  Statements and Functions Used for Processing Strings
╓┌─┌────────────────────┌────────────────────┌───────────────────────────────╖
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    Getting part of a    LEFT$                Returns a given number of
    string                                    characters from the left side of
                                            a string.

                        RIGHT$               Returns a given number of
                                            characters from the right side
                                            of a string.

                        LTRIM$               Returns a copy of a string with
                                            leading blank spaces stripped
                                            away.

                        RTRIM$               Returns a copy of a string with
                                            trailing blank spaces stripped
                                            away.
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            away.

                        MID$ (function)      Returns a given number of
                                            characters from anywhere in a
                                            string.

    Searching strings    INSTR                Searches for a string within
                                            another string.

    Converting to        LCASE$               Returns a copy of a string with
    uppercase or                              all uppercase letters (A-Z)
    lowercase letters                         converted to lower-case letters
                                            (a-z); leaves lowercase letters
                                            and other characters unchanged.

                        UCASE$               Returns a copy of a string with
                                            all lowercase letters (a-z)
                                            converted to upper-case letters
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            converted to upper-case letters
                                            (A-Z); leaves uppercase letters
                                            and other characters unchanged.

    Changing strings     MID$ (statement)     Replaces part of a string with
                                            another string.

                        LSET                 Left justifies a string within a
                                            fixed-length string.

                        RSET                 Right justifies a string within
                                            a fixed-length string.

    Converting between   STR$                 Returns the string
    numbers and strings                       representation of the value of a
                                            numeric expression.

                        VAL                  Returns the numeric value of a
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                        VAL                  Returns the numeric value of a
                                            string expression.

    Converting numbers   CVtype               Changes numbers stored as
    to data-file                              strings back to Microsoft Binary
    strings, and                              format numbers in programs
    data-file strings to                      working with random-access files
    numbers☼                                  created with older versions of
                                            BASIC.

                        CVtypeMBF            Changes numbers stored as
                                            Microsoft Binary format strings
                                            to IEEE-format numbers.

                        MKtype$              Changes Microsoft Binary format
                                            numbers to strings suitable for
                                            storing in random-access files
                                            created with older versions of
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            created with older versions of
                                            BASIC.

                        MKtypeMBF$           Changes IEEE-format numbers to
                                            Microsoft Binary format strings.

    Creating strings of  SPACE$               Returns a string of blank
    repeating characters                      characters of a specified
                                            length.

                        STRING$              Returns a string consisting of
                                            one repeated character.

    Getting the length   LEN                  Tells how many characters are in
    of a string                               a string.

    Working with ASCII   ASC                  Returns the ASCII value of the
    values                                    given character.
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    values                                    given character.

                        CHR$                 Returns the character with the
                                            given ASCII value.
    ──────────────────────────────────────────────────────────────────────────



9.6  Summary of Graphics Statements and Functions

Table 9.6 lists the statements and functions used in BASIC for pixel-based
graphics.

    Table 9.6  Statements and Functions Used for Graphics Output
╓┌─┌────────────────────┌────────────────────┌───────────────────────────────╖
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    Setting              SCREEN               Specifies a BASIC screen mode,
    screen-display                            which determines screen
    characteristics                           characteristics such as
                                            resolution and ranges for color
                                            numbers.

    Plotting or erasing  PSET                 Gives a pixel on the screen a
    a single point                            specified color using the
                                            screen's foreground color by
                                            default.

                        PRESET               Gives a pixel on the screen a
                                            specified color using the
                                            screen's background color by
                                            default, effectively erasing the
                                            pixel.

    Drawing shapes       LINE                 Draws a straight line or a box.
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
    Drawing shapes       LINE                 Draws a straight line or a box.

                        CIRCLE               Draws a circle or ellipse.

                        DRAW                 Combines many of the features of
                                            other BASIC graphics statements
                                            (drawing lines, moving the
                                            graphics cursor, scaling images)
                                            into an all-in-one graphics
                                            macro language.

    Defining screen      VIEW                 Specifies a rectangle on the
    coordinates                               screen (or viewport) as the area
                                            for graphics output.

                        WINDOW               Allows the user to choose new
                                            view coordinates for a viewport
                                            on the screen.
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            on the screen.

                        PMAP                 Maps physical pixel coordinates
                                            to view coordinates specified by
                                            the user in the current window,
                                            or vice versa.

                        POINT(number)        Returns the current physical or
                                            view coordinates of the graphics
                                            cursor, depending on the value
                                            for number.

    Using color          COLOR                Sets the default colors used in
                                            graphics output.

                        PALETTE              Assigns different colors to
                                            color numbers. Works only on
                                            systems equipped with an EGA or
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            systems equipped with an EGA or
                                            VGA.

                        POINT(x, y)          Returns the color number of a
                                            single pixel whose screen
                                            coordinates are x and y.

    Painting enclosed    PAINT                Fills an area on the screen with
    shapes                                    a color or pattern.

    Animating            GET                  Copies a rectangular area on the
                                            screen by translating the image
                                            to numeric data and storing the
                                            data in a numeric array.

                        PUT                  Displays an image on the screen
                                            that was previously copied with
                                            GET.
                        Statement or
    Task                 Function             Action
    ──────────────────────────────────────────────────────────────────────────
                                            GET.

                        PCOPY                Copies one screen page to
                                            another.
    ──────────────────────────────────────────────────────────────────────────



9.7  Summary of Trapping Statements and Functions

    Table 9.7 lists the statements and functions used by BASIC to trap and
    process errors and events.

    Table 9.7  Statements and Functions Used in Error and Event Trapping
╓┌─┌────────────────────┌────────────────────┌───────────────────────────────╖
    Task                 Statement or         Action
                        Function
    ──────────────────────────────────────────────────────────────────────────
    Task                 Statement or         Action
                        Function
    ──────────────────────────────────────────────────────────────────────────
    Trapping errors      ON ERROR GOTO line   Causes a program to branch to
    while a program is                        the given line, where line
    running                                   refers either to a line number
                                            or line label. Branching takes
                                            place whenever an error occurs
                                            during execution.

                        RESUME               Returns control to the program
                                            after executing an
                                            error-handling routine. The
                                            program resumes at either the
                                            statement causing the error
                                            (RESUME [[0]]), the statement
                                            after the one causing the error
                                            (RESUME NEXT), or the line
                                            identified by line (RESUME
                                            line).

    Task                 Statement or         Action
                        Function
    ──────────────────────────────────────────────────────────────────────────

    Getting error-status ERR                  Returns the code for an error
    data                                      that occurs at run time.

                        ERL                  Returns the number of the line
                                            on which an error occurred (if
                                            program has line numbers).

                        ERDEV                Returns a device-specific error
                                            code for the last device (such
                                            as a printer) for which DOS
                                            detected an error.

                        ERDEV$               Returns the name of the last
                                            device for which DOS detected an
                                            error.

    Defining your own    ERROR                Simulates the occurrence of a
    Task                 Statement or         Action
                        Function
    ──────────────────────────────────────────────────────────────────────────
    Defining your own    ERROR                Simulates the occurrence of a
    error codes                               BASIC error; can also be used to
                                            define an error not trapped by
                                            BASIC.

    Trapping events      ON event GOSUB line  Causes a branch to the
    while a program is                        subroutine starting with line,
    running                                   where line refers either to a
                                            line number or line label,
                                            whenever the given event occurs
                                            during execution.

                        event ON             Enables trapping of the given
                                            event.

                        event OFF            Disables trapping of the given
                                            event.

    Task                 Statement or         Action
                        Function
    ──────────────────────────────────────────────────────────────────────────

                        event STOP           Suspends trapping of the given
                                            event.

                        RETURN               Returns control to the program
                                            after executing an
                                            event-handling subroutine. The
                                            program resumes at either the
                                            statement immediately following
                                            the place in the program where
                                            the event occurred (RETURN), or
                                            the line that is identified by
                                            line (RETURN line).
    ──────────────────────────────────────────────────────────────────────────



────────────────────────────────────────────────────────────────────────────
Appendix A  Converting BASICA Programs to QuickBASIC

    QuickBASIC generally accepts BASIC language statements written for the
    BASIC interpreters used on IBM Personal Computers and compatibles: IBM
    Advanced Personal Computer BASIC Version A3.00 (BASICA) and Microsoft
    GW-BASIC(R). However, a few changes are required because of internal
    differences between QuickBASIC and these BASIC interpreters. Since the
    changes apply equally to both interpreters, this appendix uses the term
    BASICA to refer to both GW-BASIC and BASICA.

    This appendix provides information on:

    ■ Compatible source-file format

    ■ Statements and functions prohibited or requiring modification for
    QuickBASIC use

    ■ Editor differences in handling tabs

    The following sections describe only the changes required to compile and
    run a BASICA program with QuickBASIC Version 4.5. The chapters in this
    book provide information on how to use QuickBASIC features to enhance your
    existing BASICA programs.


A.1  Source-File Format

    QuickBASIC Version 4.5 expects the source file to be in ASCII format or in
    QuickBASIC's own format. If you create a file with BASICA, it must be
    saved with the ,A option; otherwise, BASICA compresses the text of your
    program in a special format that QuickBASIC cannot read. If this happens,
    reload BASICA and resave the file in ASCII format, using the ,A option.
    For example, the following BASICA command saves the file MYPROG.BAS in
    ASCII format:

    SAVE "MYPROG.BAS",A


A.2  Statements and Functions Prohibited in QuickBASIC

    The statements and functions listed below cannot be used in a QuickBASIC
    program because they perform editing operations on the source file,
    interfere with program execution, refer to a cassette device (not
    supported by QuickBASIC), or duplicate support provided by the QuickBASIC
    environment:


    ──────────────────────────────────────────────────────────────────────────
    AUTO                     LIST                    NEW

    CONT                     LLIST                   RENUM

    DEFUSR                   LOAD                    SAVE

    DELETE                   MERGE                   USR

    EDIT                     MOTOR
    ──────────────────────────────────────────────────────────────────────────


A.3  Statements Requiring Modification

    If your BASICA program contains any of the statements listed in Table A.1,
    you probably need to modify your source code before you can run your
    program in QuickBASIC.

    Table A.1  Statements Requiring Modification
╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Statement          Modification
    ──────────────────────────────────────────────────────────────────────────
    BLOAD/BSAVE        Memory locations may be different in QuickBASIC.

    CALL               The argument is the name of the SUB procedure being
                        called.

    CHAIN              QuickBASIC does not support the ALL, MERGE, DELETE, or
                        linenumber options.

    COMMON             COMMON statements must appear before any executable
                        statements.

    DEFtype            DEFtype statements should be moved to the beginning of
                        the BASICA source file.

    Statement          Modification
    ──────────────────────────────────────────────────────────────────────────

    DIM                All DIM statements declaring static arrays must appear
                        at the beginning of QuickBASIC programs.

    DRAW, PLAY         QuickBASIC requires that the VARPTR$ function be used
                        with embedded variables.

    RESUME             If an error occurs in a single-line function,
                        QuickBASIC attempts to resume program execution at the
                        line containing the function.

    RUN                For executable files produced by QuickBASIC, the object
                        of a RUN or CHAIN statement cannot be a .BAS file; it
                        must be an executable file. The R option in BASICA is
                        not supported. While in the QuickBASIC environment, the
                        object of a RUN or CHAIN statement is still a .BAS
                        file.

                        RUN{linenumber| linelabel}, however, is supported in
    Statement          Modification
    ──────────────────────────────────────────────────────────────────────────
                        RUN{linenumber| linelabel}, however, is supported in
                        QuickBASIC; this statement restarts the program at the
                        specified line.
    ──────────────────────────────────────────────────────────────────────────



A.4  Editor Differences in Handling Tabs

    QuickBASIC uses individual spaces (rather than the literal tab character,
    ASCII 9) to represent indentation levels. The Tab Stops option in the
    Option menu's Display dialog box set the number of spaces per indentation
    level. (See Chapter 20, "The Options Menu," in Learning to Use Microsoft
    QuickBASIC for more information.)

    Some text editors use the literal tab character to represent multiple
    spaces when storing text files. The QuickBASIC editor treats literal tab
    characters in such files as follows:

    1. Literal tab characters inside quoted strings appear as the character
        shown for ASCII character 9 in the ASCII table in Appendix D,
        "Keyboard Scan Codes and ASCII Character Codes."

    2. Outside quoted strings, tab characters indent the text following them
        to the next indentation level.

    If you try to change the Tab Stops setting while such a program is loaded,
    QuickBASIC gives the following error message:

    Cannot change tab stops while file is loaded

    This is to prevent you from inadvertently reformatting old source files
    created with other editors. If you load such a file and then decide you
    prefer a different indentation, reset the indentation using the following
    procedure:

    1. Save your file to preserve any changes, then choose the New Program
        command from the File menu.

    2. Choose the Display command from the Options menu and set the Tab Stops
        option as described above.

    3. Choose the Open Program command from the File menu and reload your
        program. When your program is reloaded, the indentations will reflect
        the new setting of the Tab Stops option.

    Note that this procedure works only for old programs. Text created within
    QuickBASIC cannot be reformatted this way.



────────────────────────────────────────────────────────────────────────────
Appendix B  Differences from Previous Versions of QuickBASIC

    QuickBASIC Version 4.5 contains many new features and enhancements over
    previous versions of QuickBASIC. This appendix describes the differences
    between Versions 2.0 and 4.5 of QuickBASIC. Unless otherwise stated,
    differences between Versions 2.0 and 4.5 also apply as differences between
    Versions 3.0 and 4.5. Differences between Versions 4.0 and 4.5 are
    primarily in the QuickBASIC environment.

    This appendix provides information on the following improvements in
    QuickBASIC Version 4.5:

    ■ Product capabilities and features

    ■ Environment enhancements

    ■ Improvements in compiling and debugging capabilities

    ■ Language changes

    ■ File compatibility


B.1  QuickBASIC Features

    This section compares the features of Microsoft QuickBASIC Versions 4.5
    with those of previous versions. The features are listed in Table B.1 and
    described below.

    Table B.1  Features of Microsoft QuickBASIC Version 4.5
╓┌─┌───────────────────────────────┌─────────┌──────────┌─────────┌──────────╖
                                            QuickBASIC Version
    Feature                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    On-line QB Advisor (detailed    No        No         No        Yes
    reference)

    Selectable right mouse button   No        No         No        Yes
    function

    Instant Watches for variables   No        No         No        Yes
    and expressions

    Set default search paths        No        No         No        Yes

    User-defined types              No        No         Yes       Yes

    IEEE format, math coprocessor   No        Yes        Yes       Yes
    support

    On-line help                    No        No         Yes       Yes
                                            QuickBASIC Version
    Feature                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    On-line help                    No        No         Yes       Yes

    Symbol help                     No        No         No        Yes

    Long (32-bit) integers          No        No         Yes       Yes

    Fixed-length strings            No        No         Yes       Yes

    Syntax checking on entry        No        No         Yes       Yes

    Binary file I/O                 No        No         Yes       Yes

    FUNCTION procedures             No        No         Yes       Yes

    CodeView(R) support             No        No         Yes       Yes

    Compatibility with other        No        No         Yes       Yes
    languages
                                            QuickBASIC Version
    Feature                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    languages

    Multiple modules in memory      No        No         Yes       Yes

    ProKey TM, SideKick(R), and     No        Yes        Yes       Yes
    SuperKey(R) compatibility

    Insert/overtype modes           No        Yes        Yes       Yes

    WordStar(R)-style keyboard      No        No         Yes       Yes
    interface

    Recursion                       No        No         Yes       Yes

    Error listings during separate  No        Yes        Yes       Yes
    compilation

    Assembly-language listings      No        Yes        Yes       Yes
                                            QuickBASIC Version
    Feature                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    Assembly-language listings      No        Yes        Yes       Yes
    during separate compilation
    ──────────────────────────────────────────────────────────────────────────


B.1.1  Features New to QuickBASIC 4.5

    You can now access on-line help for QuickBASIC's keywords, commands, and
    menus, as well as general topics and your own variables. Examples shown on
    help screens can be copied and pasted directly into your own program,
    reducing development time.

    For mouse users, you can now set the function of the right mouse button
    with the Right Mouse command from the Options menu. Use the function that
    best suits your needs. For more information see Chapter 19, "The Calls
    Menu," in Learning to Use Microsoft QuickBASIC.

    For faster debugging, QuickBASIC now offers an Instant Watch command for
    immediately identifying the value of a variable or the condition (true or
    false) of an expression. For more information see Chapter 18, "The Debug
    Menu," in Learning to Use Microsoft QuickBASIC.

    Version 4.5 also lets you set default search paths to specific types of
    files. This lets you organize your files by type and keep them in separate
    directories. QuickBASIC will search the correct directory after you set
    the new default search path. You can set default paths for executable,
    include, library, and help files.

B.1.2  Features Introduced in QuickBASIC 4.0

    If you are new to QuickBASIC or unfamiliar with Version 4.0, you will want
    to review the following features introduced in QuickBASIC 4.0 and
    supported in the current version.

    B.1.2.1  User-Defined Types

    The TYPE statement allows you to create composite data types from simple
    data elements. Such data types are similar to C-language structures.
    User-defined types are discussed in Chapter 3, "File and Device I/O."

    B.1.2.2  IEEE Format and Math-Coprocessor Support

    Microsoft QuickBASIC provides IEEE-format numbers and math-coprocessor
    support. When no coprocessor is present, QuickBASIC emulates the
    coprocessor.

    Calculations done by programs compiled with QuickBASIC are generally more
    accurate and may produce results different from programs run under BASICA
    or versions of QuickBASIC prior to 4.0. Single-precision IEEE-format
    numbers provide an additional decimal digit of accuracy. When compared to
    Microsoft Binary format double-precision numbers, IEEE-format
    double-precision numbers provide an additional one to two digits in the
    mantissa and extend the range of the exponent.

    There are two ways to use QuickBASIC 4.0 and 4.5 with your old programs
    and existing data:

    1. Use the /MBF option. This way, you can use your old programs and data
        files without rewriting your programs or changing your files.

    2. Modify your data files and use the new QuickBASIC to recompile your
        programs. In the long run, this ensures compatibility with future
        versions of QuickBASIC and may produce faster programs. Only
        random-access files containing binary format real numbers need to be
        changed. Files containing only integers or string data can be used
        without modification. More information on these options is provided
        below.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    If assembly-language procedures that use real numbers are called from
    your program, they must be written so that they use IEEE-format numbers.
    This is the default for Microsoft Macro Assembler (MASM) Version 5.0 and
    later versions. With earlier versions, be sure to use the /R
    command-line option or the 8087 assembler directive.
    ──────────────────────────────────────────────────────────────────────────

    B.1.2.3  Ranges of IEEE-Format Numbers

    IEEE-format numbers have a wider range than Microsoft Binary format
    numbers, as shown in the following list:

    Type of Number           Range of Values
    ──────────────────────────────────────────────────────────────────────────
    Single precision         -3.37 * 10^38 to -8.43 * 10^-37
                            True zero

                            8.43 * 10^-37 to 3.37 * 10^38

    Double precision         -1.67 * 10^308 to -4.19 * 10^-307
                            True zero

                            4.19 * 10^-307 to 1.67 * 10^308
    ──────────────────────────────────────────────────────────────────────────

    Single-precision values are accurate to approximately seven digits.
    Double-precision values are accurate to either 15 or 16 digits.

    Note that double-precision values may have three digits in the exponent.
    This may cause problems in PRINT USING statements.

    B.1.2.4  PRINT USING and IEEE-Format Numbers

    Because double-precision IEEE-format numbers can have larger exponents
    than Microsoft Binary format double-precision numbers, you may need to use
    a special exponential format in PRINT USING statements. Use the new format
    if your program prints values with three-digit exponents. To print numbers
    with three-digit exponents, use five carets (^^^^^) instead of four carets
    to indicate exponential format:

    PRINT USING "+#.######^^^^^", Circumference#

    If an exponent is too large for a field, QuickBASIC replaces the first
    digit with a percent sign (%) to indicate the problem.

B.1.3  Recompiling Old Programs with /MBF

    Old programs and files work with QuickBASIC Version 4.5 without
    modification if you recompile the programs with the /MBF command-line
    option. For example, to compile the program named multrgrs.bas, enter the
    following at the DOS prompt:

    BC multrgrs /MBF;

    Then link the program as you usually do. To recompile using the Make EXE
    File command, be sure to use the /MBF option when you start QuickBASIC.
    Then compile as you normally would.

    The /MBF option converts Microsoft Binary format numbers to IEEE format as
    they are read from a random-access file and converts them back to
    Microsoft Binary format before writing them to a file. This lets you do
    calculations with IEEE-format numbers while keeping the numbers in
    Microsoft Binary format in your files.

B.1.4  Converting Files and Programs

    If you decide to convert your programs and data files rather than use the
    /MBF command-line option, you need to do two things:

    1. Recompile your programs.

    2. Convert your data files.

    Your old QuickBASIC programs should compile without modification. However,
    do not use the /MBF option when recompiling. Your program will not work
    with your new data files if you use the /MBF option.

    Data files containing real numbers need to be converted so that real
    numbers are stored in IEEE format. QuickBASIC Version 4.5 includes eight
    functions that help you do this.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    You do not need to convert your data files if they contain only integer
    and string data. Only files containing real numbers must be converted.
    ──────────────────────────────────────────────────────────────────────────

    Version 4.5 has the familiar CVS, CVD, MKS$, and MKD$ functions for
    reading and writing real numbers from or to random-access files. However,
    these functions now handle real numbers stored in your files in IEEE
    format, not Microsoft Binary format. To handle numbers in Microsoft Binary
    format, Version 4.5 includes the functions CVSMBF, CVDMBF, MKSMBF$, and
    MKDMBF$.

    With these functions, you can write a short program that reads records
    from the old file (using Microsoft Binary format as necessary), converts
    the real-number fields to IEEE format, places the fields in a new record,
    and writes out the new record.

    Examples

    The following program copies an old data file to a new file, making the
    necessary conversions:

    ' Define types for old and new file buffers:
    TYPE Oldbuf
        ObsId AS STRING * 20
        XPos AS STRING * 4
        YPos AS STRING * 4
        FuncVal AS STRING * 8
    END TYPE

    TYPE NewBuf
        ObsId AS STRING * 20
        XPos AS SINGLE
        YPos AS SINGLE
        FuncVal AS DOUBLE
    END TYPE

    ' Declare buffers:
    DIM OldFile AS Oldbuf, NewFile AS NewBuf

    ' Open the old and new data files:
    OPEN "OLDMBF.DAT" FOR RANDOM AS #1 LEN=LEN(OldFile)
    OPEN "NEWIEEE.DAT" FOR RANDOM AS #2 LEN=LEN(NewFile)

    I=1

    ' Read the first old record:
    GET #1,I,OldFile

    DO UNTIL EOF(1)

    ' Move the fields to the new record fields, converting
    ' the real-number fields:
        NewFile.ObsId=OldFile.ObsId
        NewFile.XPos=CVSMBF(OldFile.XPos)
        NewFile.YPos=CVSMBF(OldFile.YPos)
        NewFile.FuncVal=CVDMBF(OldFile.FuncVal)

    ' Write the converted fields to the new file:
        PUT #2,I,NewFile
        I=I+1

    ' Read next record from the old file:
        GET #1,I,OldFile
    LOOP

    CLOSE #1, #2

    Each record in the two files has four fields: an identifier field, two
    fields containing single-precision real numbers, and a final field
    containing a double-precision real number.

    Most of the conversion work is done with the functions CVDMBF and CVSMBF.
    For example, the following program line converts the double-precision
    field:

    NewFile.FuncVal=CVDMBF(OldFile.FuncVal)

    The eight bytes in the record field OldFile.FuncVal are converted from a
    Microsoft Binary format double-precision value to an IEEE-format
    double-precision value by the function CVDMBF. This value is stored in the
    corresponding field in the new record, which is later written to the new
    file by the PUT statement.

B.1.5  Other QuickBASIC Features

    QuickBASIC 4.0 introduced the following features and tools that made
    QuickBASIC a development package appreciated by the professional without
    overwhelming the novice. QuickBASIC 4.5 supports all these same features,
    along with some additional refinements.

    B.1.5.1  Long (32-Bit) Integers

    Long (32-bit) integers eliminate rounding errors in calculations with
    numbers in the range -2,147,483,648 to 2,147,483,647, and they provide
    much faster whole-number calculations in this range than do floating-point
    types.

    B.1.5.2  Fixed-Length Strings

    Fixed-length strings let you incorporate string data into user-defined
    types. See Chapter 4, "String Processing," for more information.

    B.1.5.3  Syntax Checking on Entry

    If syntax checking is turned on, QuickBASIC checks each line as you enter
    it for syntax and duplicate-definition errors. See Chapter 12, "Using the
    Editor," in Learning to Use Microsoft QuickBASIC for an explanation of
    syntax checking and other smart-editor features.

    B.1.5.4  Binary File I/O

    Versions 4.0 and 4.5 provide binary access to files. This is useful for
    reading and modifying files saved in non-ASCII format. The major benefit
    of binary access is that it does not force you to treat a file as a
    collection of records. If a file is opened in binary mode, you can move
    forward or backward to any byte position in the file, then read or write
    as many bytes as you want. Thus, different I/O operations on the same file
    can GET or PUT a varying number of bytes──unlike with random access, where
    the number of bytes is fixed by the predefined length of a single record.

    See Chapter 3, "File and Device I/O," for more information about
    accessing binary files.

    B.1.5.5  FUNCTION Procedures

    FUNCTION procedures allow you to place a function in one module and call
    it from a different module. See Chapter 2, "SUB and FUNCTION Procedures,"
    for more information about using FUNCTION procedures.

    In versions prior to 4.0, a SUB procedure and a variable could have the
    same name. Now, SUB and FUNCTION procedure names must be unique.

    B.1.5.6  Support for the CodeView(R) Debugger

    You can use the command-line tools BC.EXE and LINK.EXE to create
    executable files compatible with the Microsoft CodeView debugger, a
    powerful tool included with the Microsoft Macro Assembler (Version 5.0 or
    later) and with Microsoft C (Version 5.0 or later). Modules compiled with
    BC can be linked with modules compiled with other Microsoft languages in
    such a way that the final executable file can be debugged using the
    CodeView debugger. See Appendix G, "Compiling and Linking from DOS," for
    more information.

    B.1.5.7  Other-Language Compatibility

    QuickBASIC Version 4.5 allows you to call routines written in other
    Microsoft languages using C or Pascal calling conventions. Arguments are
    passed by NEAR or FAR reference, or by value. Other-language code may be
    placed in Quick libraries or linked into executable files.

    The PTR86 routine is not supported in QuickBASIC, Version 4.5; use the
    VARPTR and VARSEG functions instead.

    B.1.5.8  Multiple Modules in Memory

    You can load multiple program modules into memory simultaneously. Versions
    previous to 4.0 permitted only one module to be in memory at a time. Now
    you can edit, execute, and debug multiple-module programs within the
    QuickBASIC environment. See Chapter 2, "SUB and FUNCTION Procedures," and
    Chapter 7, "Programming with Modules," for more information about using
    multiple modules.

    B.1.5.9  ProKey TM, SideKick(R), and SuperKey(R) Compatibility

    You can use ProKey, SideKick, and SuperKey within the QuickBASIC
    environment. Other keyboard-reprogramming or desktop software may not work
    with QuickBASIC. Check with the suppliers or manufacturers of other
    products to find out about the product's compatibility with QuickBASIC
    Version 4.5.

    B.1.5.10  Insert and Overtype Modes

    Pressing INS toggles the editing mode between insert and overtype. When
    overtype mode is on, the cursor changes from a blinking underline to a
    block. Note that INS replaces the CTRL+O insert/overtype toggle in Version
    3.0.

    In insert mode, the editor inserts a typed character at the cursor
    position. In overtype mode, the editor replaces the character under the
    cursor with the character you type. Insert mode is the default mode.

    B.1.5.11  WordStar(R)-Style Keyboard Commands

    QuickBASIC supports many of the key sequences familiar to WordStar users.
    A complete list of WordStar-style key commands appears in Chapter 12,
    "Using the Editor," in Learning to Use Microsoft QuickBASIC.

    B.1.5.12  Recursion

    QuickBASIC Versions 4.0 and 4.5 support recursion, which is the ability of
    a procedure to call itself. Recursion is useful for solving certain
    problems, such as sorting. See Chapter 2, "SUB and FUNCTION Procedures,"
    for more information about using recursion.

    B.1.5.13  Error Listings during Separate Compilation

    QuickBASIC displays descriptive error messages when you compile programs
    using the BC command. Using BC, you can redirect the error messages to a
    file or device to get a copy of the compilation errors. See Appendix G,
    "Compiling and Linking from DOS," for more information about the BC
    command.

    Examples

    The following examples show how to use the BC command line for error
    display:

    Command Line             Action
    ──────────────────────────────────────────────────────────────────────────
    BC TEST.BAS;             Compiles the file named TEST.BAS and displays
                            errors on the screen

    BC TEST.BAS; > TEST.ERR  Compiles the file TEST.BAS and redirects error
                            messages to the file named TEST.ERR
    ──────────────────────────────────────────────────────────────────────────

    B.1.5.14  Assembly-Language Listings during Separate Compilation

    The BC command's /A option produces a listing of the assembly-language
    code produced by the compiler. See Appendix G, "Compiling and Linking
    from DOS," for more information.


B.2  Differences in the Environment

    The QuickBASIC programming environment now provides more flexible command
    selection, additional window options, and more menu commands. Sections
    B.2.1-B.2.5 describes the differences in the programming environment
    between Version 4.5 and earlier versions.

B.2.1  Choosing Commands and Options

    QuickBASIC Version 4.5 gives you flexibility in how you choose commands
    from menus and options from dialog boxes.

    Version 4.5 allows you to select any menu by pressing the ALT key followed
    by a mnemonic key. Each menu command and dialog box option also has its
    own mnemonic key, which immediately executes the command or selects the
    item. The mnemonic keys appear in intense video.

    In Version 4.5 the ENTER key functions the same way as the SPACEBAR did in
    versions 3.0 and earlier. You can press ENTER to execute a command from a
    dialog box.

B.2.2  Windows

    Version 4.5 allows up to two work-area windows (referred to as View
    windows), a Help window, and a separate Immediate window. Versions prior
    to 4.0 supported only two windows: one work area and the error-message
    window.

B.2.3  New Menu

    QuickBASIC Version 4.5 has a new menu, the Options menu. The Options menu
    accesses special QuickBASIC controls that manipulate the screen display
    attributes, the function of the right mouse button, the default path to
    specific file types, syntax checking, and Full Menus control.

B.2.4  Menu Commands

    The Debug and Help menus, which appeared in earlier versions of
    QuickBASIC, have some new commands. Table B.2 lists the new commands in
    these menus, as well as the commands from the new Options menu.

    Table B.2  Menus with New Commands in QuickBASIC Version 4.5
╓┌─┌───────────┌────────────────────────┌────────────────────────────────────╖
    Menu        Command                  Description
    ──────────────────────────────────────────────────────────────────────────
    Debug       Add Watch                Adds variable or expression to Watch
                                        window

                Instant Watch (New)      Immediately checks the value of a
                                        variable or expression

                Watchpoint               Adds a watchpoint to the Watch window

                Delete Watch             Selectively removes item from Watch
                                        window

                Delete All Watch         Removes all items from the Watch
                                        window

                Trace On                 Toggles tracing on and off

                History On               Records last 20 executed statements
    Menu        Command                  Description
    ──────────────────────────────────────────────────────────────────────────
                History On               Records last 20 executed statements

                Toggle Breakpoint        Toggles a breakpoint on the current
                                        line on and off

                Clear All Breakpoints    Removes all breakpoints

                Break on Errors (New)    Halts program execution if an error
                                        occurs, regardless of any error
                                        handling

                Set Next Statement       Sets the next statement to execute
                                        when a suspended program continues
                                        running

    Option      Display (New)            Customizes the screen elements
    (New)
                Set Paths (New)          Alters default search paths for
                                        specific types of files
    Menu        Command                  Description
    ──────────────────────────────────────────────────────────────────────────
                                        specific types of files

                Right Mouse (New)        Selects the function for the right
                                        mouse button

                Syntax Checking          Toggles automatic syntax checking on
                                        and off

                Full Menus (New)         Toggles between Full Menus and Easy
                                        Menus

    Help        Index (New)              Displays an alphabetical list of
                                        QuickBASIC keywords and a brief
                                        description of each

                Contents (New)           Displays a visual outline of contents

                Topic                    Provides information on variables,
                                        keywords
    Menu        Command                  Description
    ──────────────────────────────────────────────────────────────────────────
                                        keywords

                Help on Help (New)       Describes how to use Help and common
                                        keyword shortcuts
    ──────────────────────────────────────────────────────────────────────────


    The Version 3.0 Debug option is removed from the Run menu. In Version 4.5
    you can debug at any time, using the debugging commands in the Debug menu.

B.2.5  Editing-Key Changes

    The QuickBASIC keyboard interface has been extended to include editing key
    sequences similar to those in the WordStar editor. (See Chapter 12, "Using
    the Editor," and Chapter 13, "The Edit Menu," in Learning to Use Microsoft
    QuickBASIC for more information about editing.) The functions performed by
    the QuickBASIC Version 2.0 key sequences listed in Table B.3 are changed
    in QuickBASIC Versions 4.0 and 4.5.

    Table B.3  Editing-Key Changes
                            QuickBASIC              QuickBASIC
    Function                 2.0 Key                 4.5 Key
    ──────────────────────────────────────────────────────────────────────────
    Undo                     SHIFT+ESC               ALT+BKSP

    Cut                      DEL                     SHIFT+DEL

    Copy                     F2                      CTRL+INS

    Paste                    INS                     SHIFT+INS

    Clear                    ──                      DEL

    Overtype                 ──                      INS
    ──────────────────────────────────────────────────────────────────────────


B.3  Differences in Compiling and Debugging

    In the QuickBASIC 4.5 programming environment, compiling and debugging are
    not separate operations. Your program is always ready to run, and you can
    debug your code in several ways while you program. This section describes
    the differences in compiling and debugging features between QuickBASIC
    Versions 4.0 and 4.5 and previous versions.

B.3.1  Command-Line Differences

    QuickBASIC Versions 4.0 and 4.5 support Version 2.0 command-line options
    only for the QB command, not the BC command. To compile a program outside
    of the QuickBASIC environment, use the BC command, which is described in
    Appendix G, "Compiling and Linking from DOS."

    Versions 4.0 and 4.5 do not require any of the Compile options listed in
    Table 4.1 of the Microsoft QuickBASIC Version 2.0 manual. If you attempt
    to invoke QuickBASIC using these as command-line options, an error message
    appears. Similarly, it was necessary to choose certain compiler options
    from the Compile dialog box in Version 3.0; this is now done
    automatically. Table B.4 describes the way in which QuickBASIC now
    supports the functionality of these options.

    Table B.4  QB and BC Options Not Used in QuickBASIC Versions 4.0 or 4.5
    Version 2.0              Version 4.5
    ──────────────────────────────────────────────────────────────────────────
    On Error (/E)            Automatically set whenever an ON ERROR statement
                            is present.

    Debug (/D)               Always on when you run a program within the
                            QuickBASIC environment. When producing executable
                            programs on disk or Quick libraries, use the
                            Produce Debug Code option.

    Checking between         Automatically set whenever an ON event statement
    Statements (/V) and      is present.
    Event Trapping (/W)

    Resume Next (/X)         Automatically set whenever a RESUME NEXT
                            statement is present.

    Arrays in Row Order (/R) Available only when compiling with BC.

    Minimize String Data     The default for QB. To turn off this option, you
    (/S)                     must compile from the command line with BC.
    ──────────────────────────────────────────────────────────────────────────

    The options listed in Table B.5 are now available for the QB and BC
    commands.

    Table B.5  Options Introduced in Version 4.0 for the QB and BC Commands
╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Option                   Description
    ──────────────────────────────────────────────────────────────────────────
    /AH                      Allows dynamic arrays of records, fixed-length
                            strings, and numeric data to be larger than 64K
                            each. If this option is not specified, the
                            maximum size for each array is 64K. Note that
                            this option has no effect on the way data items
                            are passed to procedures. (This option is used
                            with the QB and BC commands.)

    /H                       Displays the highest resolution possible on your
                            hardware. For example, if you have an EGA,
                            QuickBASIC displays 43 lines and 80 columns of
                            text. (This option is used only with the QB
    Option                   Description
    ──────────────────────────────────────────────────────────────────────────
                            text. (This option is used only with the QB
                            command.)

    /MBF                     Converts Microsoft Binary format numbers to IEEE
                            format. See Section B.1.2.3 for more information
                            about this option (used with the QB and BC
                            commands).

    /RUN sourcefile          Runs sourcefile immediately, without first
                            displaying the QuickBASIC programming
                            environment. (This option is used only with the
                            QB command.)
    ──────────────────────────────────────────────────────────────────────────


B.3.2  Separate Compilation Differences

Versions 4.0 and 4.5 do not allow separate compilation with the QB command.
Use the BC command described in Appendix G, "Compiling and Linking from
DOS" to compile and link files without entering the programming environment.

B.3.3  User Libraries and BUILDLIB

    User libraries created for previous versions are not compatible with
    Versions 4.0 and 4.5. You must rebuild the library from the original
    source files.

    User libraries are now called Quick libraries. There is no change in their
    function or use. The file-name extension of these libraries is now .QLB
    instead of .EXE. The BUILDLIB utility is no longer required. Quick
    libraries are now created from within the programming environment or from
    the link command line. See Appendix H, "Creating and Using Quick
    Libraries," for more information on this topic.

B.3.4  Restrictions on Include Files

    Include files can contain SUB or FUNCTION procedure declarations but not
    definitions. If you need to use an old include file with procedure
    definitions in it, use the Merge command from the File menu to insert the
    include file into the current module. When you merge an include file
    containing a SUB procedure, the text of the procedure does not appear in
    the currently active window. To view or edit its text, choose the SUBs
    command from the View menu, then select the procedure name in the list
    box. Once the text is merged, you can run the program.

    Alternatively, you may decide to put your SUB procedure in a separate
    module. In this case, you must take one of the following two steps for any
    shared variables (variables declared in a COMMONSHARED or [[RE]]DIM SHARED
    statement outside the SUB procedure, or in a SHARED statement inside the
    SUB procedure), because variables declared this way are shared only within
    a single module:

    1. Share the variables between the modules by listing them in COMMON
        statements at the module level in both modules.

    2. Pass the variables to the SUB procedure in an argument list.

    See Chapter 2, "SUB and FUNCTION Procedures," and Chapter 7,
    "Programming with Modules," for additional information on modules and
    procedures.

B.3.5  Debugging

    QuickBASIC now helps you debug your programs faster by providing the
    following debugging features:

    1. Multiple breakpoints

    2. Watch expressions, watchpoints, and instant watches

    3. Improved program tracing

    4. Full-screen window that displays program text during single-stepping

    5. The ability to change variable values during execution, then continue

    6. The ability to edit the program, then continue execution without
        restarting

    7. History feature

    8. Calls menu

    9. Symbol help

    The debugging function-key sequences listed in Table B.6 are changed from
    QuickBASIC Version 2.0:

    Table B.6  Debugging-Key Changes
                            QuickBASIC              QuickBASIC
    Function                 Version 2.0 Key         Version 4.5 Key
    ──────────────────────────────────────────────────────────────────────────
    Trace                    ALT+F8                  F8

    Step                     ALT+F9                  F10
    ──────────────────────────────────────────────────────────────────────────

    Note that animation is turned on when you toggle on the Trace On command
    from the Debug menu, then run your program.

    See Chapter 9, "Debugging While You Program," in Learning to Use Microsoft
    QuickBASIC for more information.


B.4  Changes to the BASIC Language

    This section describes enhancements and changes to the BASIC language in
    Version 4.5 and earlier versions of QuickBASIC. Table B.7 lists the
    keywords affected by these changes and shows which version of QuickBASIC
    is affected by each change. A more detailed explanation of the changes
    appears after the table.

    Table B.7  Changes to the BASIC Language
╓┌─┌───────────────────────────────┌─────────┌──────────┌─────────┌──────────╖
                                            QuickBASIC Version
    Keyword                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    AS                              No        No         Yes       Yes

    CALL                            No        No         Yes       Yes

    CASE                            No        Yes        Yes       Yes

    CLEAR                           No        No         Yes       Yes
                                            QuickBASIC Version
    Keyword                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    CLEAR                           No        No         Yes       Yes

    CLNG                            No        No         Yes       Yes

    CLS                             No        Yes        Yes       Yes

    COLOR                           No        No         Yes       Yes

    CONST                           No        Yes        Yes       Yes

    CVL                             No        No         Yes       Yes

    CVSMBF, CVDMBF                  No        Yes        Yes       Yes

    DECLARE                         No        No         Yes       Yes

    DEFLNG                          No        No         Yes       Yes

                                            QuickBASIC Version
    Keyword                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────

    DIM                             No        No         Yes       Yes

    DO...LOOP                       No        Yes        Yes       Yes

    EXIT                            No        Yes        Yes       Yes

    FILEATTR                        No        No         Yes       Yes

    FREEFILE                        No        No         Yes       Yes

    FUNCTION                        No        No         Yes       Yes

    GET                             No        No         Yes       Yes

    LCASE$                          No        No         Yes       Yes

    LEN                             No        No         Yes       Yes
                                            QuickBASIC Version
    Keyword                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    LEN                             No        No         Yes       Yes

    LSET                            No        No         Yes       Yes

    LTRIM$                          No        No         Yes       Yes

    MKL$                            No        No         Yes       Yes

    MKSMBF$, MKDMBF$                No        Yes        Yes       Yes

    OPEN                            No        No         Yes       Yes

    ON UEVENT                       No        No         No        Yes

    PALETTE                         No        No         Yes       Yes

    PUT                             No        No         Yes       Yes

                                            QuickBASIC Version
    Keyword                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────

    RTRIM$                          No        No         Yes       Yes

    SCREEN                          No        No         Yes       Yes

    SEEK                            No        No         Yes       Yes

    SELECT CASE                     No        Yes        Yes       Yes

    SETMEM                          No        No         Yes       Yes

    SLEEP                           No        No         No        Yes

    STATIC                          No        No         Yes       Yes

    TYPE                            No        No         Yes       Yes

    UCASE$                          No        No         Yes       Yes
                                            QuickBASIC Version
    Keyword                         2.0       3.0        4.0       4.5
    ──────────────────────────────────────────────────────────────────────────
    UCASE$                          No        No         Yes       Yes

    UEVENT                          No        No         No        Yes

    VARPTR                          No        No         Yes       Yes

    VARSEG                          No        No         Yes       Yes

    WIDTH                           No        Yes        Yes       Yes
    ──────────────────────────────────────────────────────────────────────────


    The following section explains in more detail the differences in the
    keywords summarized above.

╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
    AS                 The AS clause allows the use of user-defined types in
                        DIM, COMMON, and SHARED statements and in DECLARE, SUB,
                        and FUNCTION parameter lists.

    CALL               The use of CALL is optional for calling subprograms
                        declared with the DECLARE statement.

    CLEAR              The CLEAR statement no longer sets the total size of
                        the stack; it sets only the stack size required by the
                        program. QuickBASIC sets the stack size to the amount
                        specified by the CLEAR statement plus what QuickBASIC
                        itself requires.

    CLNG               The CLNG function rounds its argument and returns a
                        long (four-byte) integer that is equal to the argument.

    CLS                The CLS statement has been modified to give you greater
                        flexibility in clearing the screen. Note that
                        QuickBASIC no longer clears the screen automatically at
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        QuickBASIC no longer clears the screen automatically at
                        the beginning of each program, as was true in earlier
                        versions.

    COLOR, SCREEN,     The COLOR, SCREEN, PALETTE, and WIDTH statements are
    PALETTE, WIDTH     extended to include screen modes available with the IBM
                        PS/2(R) VGA and Multicolor Graphics Array (MCGA) cards.

    CONST              The CONST statement lets you define symbolic constants
                        to improve program readability and ease program
                        maintenance.

    CVL                The CVL function is used to read long integers stored
                        as strings in random-access data files. CVL converts a
                        four-byte string created with the MKL$ function back to
                        a long integer for use in your BASIC program.

    CVSMBF, CVDMBF     The CVSMBF and CVDMBF functions convert strings
                        containing Microsoft Binary format numbers to
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        containing Microsoft Binary format numbers to
                        IEEE-format numbers. Although QuickBASIC Version 4.5
                        supports these statements, they are considered obsolete
                        since the IEEE-format is now the QuickBASIC standard.

    DECLARE            The DECLARE statement allows you to call procedures
                        from different modules, check the number and type of
                        arguments passed, and call procedures before they are
                        defined.

    DEFLNG             The DEFLNG statement declares all variables, DEF FN
                        functions, and FUNCTION procedures as having the
                        long-integer type. That is, unless a variable or
                        function has been declared in an AS type clause, or it
                        has an explicit type-definition suffix such as % or $,
                        it is a long integer by default.

    DIM                The DIM statement's TO clause lets you specify
                        subscripts of any integer value, giving you greater
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        subscripts of any integer value, giving you greater
                        flexibility in array declarations.

    DO...LOOP          Using DO...LOOP statements gives you more powerful
                        loops that allow you to write programs with better
                        structure.

    EXIT               The use of EXIT {DEF | DO | FOR | FUNCTION | SUB}
                        statements provides convenient exits from loops and
                        procedures.

    FREEFILE, FILEATTR The FREEFILE and FILEATTR functions help you write
                        applications that do file I/O in a multiple-module
                        environment.

    FUNCTION           The FUNCTION...END FUNCTION procedure allows you to
                        define a multiline procedure that you call from within
                        an expression. These procedures behave much like
                        intrinsic functions such as ABS or multiline DEF FN
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        intrinsic functions such as ABS or multiline DEF FN
                        functions from QuickBASIC Versions 1.0-3.0. However,
                        unlike a DEF FN function, a FUNCTION procedure can be
                        defined in one module and called from another. Also,
                        FUNCTION procedures have local variables and support
                        recursion.

    GET, PUT           For I/O operations, the syntax of the GET and PUT
                        statements is expanded to include records defined with
                        TYPE...END TYPE statements. This makes use of the FIELD
                        statement unnecessary.

    LCASE$, UCASE$,    The following string-handling functions are available
    LTRIM$, RTRIM$     in Version 4.5:

                        Function          Return Value
                        LCASE$            A copy of the string with all letters
                                        converted to lowercase
                        UCASE$            A copy of the string with all letters
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        UCASE$            A copy of the string with all letters
                                        converted to uppercase
                        LTRIM$            A copy of the string with all leading
                                        blanks removed
                        RTRIM$            A copy of the string with all
                                        trailing blanks removed
                        LEN               The LEN function has been extended to
                                        return the number of bytes required
                                        by any scalar or record variable,
                                        constant, expression, or array
                                        element.

    LSET               The LSET statement is extended to include record
                        variables as well as string variables. This allows you
                        to assign one record variable to another record
                        variable even when the records are not similar.

    MKL$               The MKL$ function is used to convert long integers to
                        strings that can be stored in random-access data files.
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        strings that can be stored in random-access data files.
                        Use the CVL function to change the string back to a
                        long integer.

    MKSMBF$, MKDMBF$   The MKSMBF$ and MKDMBF$ functions convert IEEE-format
                        numbers to strings containing a Microsoft Binary format
                        number. They are obsolete (but supported) in Version
                        4.5, which uses the IEEE-format.

    ON UEVENT          The ON UEVENT statement directs the program to a
                        specified location when a user-defined event (a UEVENT)
                        occurs. Use it in the same fashion as other
                        event-handling statements.

    OPEN               The OPEN statement now opens two files with the same
                        name for OUTPUT or APPEND as long as the path names are
                        different. For example, the following is now permitted:

                        OPEN "SAMPLE" FOR APPEND AS #1
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        OPEN "SAMPLE" FOR APPEND AS #1
                        OPEN "TMP\SAMPLE" FOR APPEND AS #2

                        A binary file mode has been added to the OPEN statement
                        syntax. See Chapter 3, "File and Device I/O," for
                        information about using this mode.

    SEEK               The SEEK statement and function allow you to position a
                        file at any byte or record. See Chapter 3, "File and
                        Device I/O," for more information.

    SELECT CASE        The use of SELECT CASE statements provides a way to
                        simplify complex condition testing. The CASE clause of
                        the SELECT CASE statement now accepts any expression
                        (including variable expressions) as an argument; in
                        previous versions, only constant expressions were
                        permitted.

    SETMEM             The SETMEM function facilitates mixed-language
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
    SETMEM             The SETMEM function facilitates mixed-language
                        programming by allowing you to decrease the amount of
                        dynamic memory allocated by BASIC so it can be used by
                        procedures in other languages.

    SLEEP              The SLEEP statement causes the program to pause for an
                        indicated period of time or until the user presses a
                        key or an enabled event occurs. The optional argument
                        indicates the length of the pause (in seconds).

    STATIC             Omitting the STATIC attribute from SUB and FUNCTION
                        statements causes variables to be allocated when the
                        procedures are called, instead of when they are
                        defined. Such variables do not retain their values
                        between procedure calls.

    TYPE               The TYPE...END TYPE statement lets you define a data
                        type containing elements of different fundamental
                        types. This simplifies defining and accessing
    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────
                        types. This simplifies defining and accessing
                        random-access file records.

    UEVENT             Enables, suspends, or disables a user-defined event.
    {ON|STOP|OFF}      The UEVENT statements are used in the same way as other
                        event-trapping statements.

    VARPTR             The VARPTR function now returns the 16-bit integer
                        offset of the BASIC variable or array element. The
                        offset is from the beginning of the segment that
                        contains the variable or array element.

    VARSEG             The VARSEG function returns the segment address of its
                        argument. This allows you to set DEF SEG appropriately
                        for use with PEEK, POKE, BLOAD, BSAVE, and CALL
                        ABSOLUTE. It also permits you to get the appropriate
                        segment for use with CALL INTERRUPT when executing
                        operating-system or BIOS interrupts.

    Keywords           Explanation
    ──────────────────────────────────────────────────────────────────────────

    WIDTH              A new argument on the WIDTH statement lets your
                        programs use the extended line modes on machines
                        equipped with EGA, VGA, and MCGA adapter cards.
    ──────────────────────────────────────────────────────────────────────────


    ──────────────────────────────────────────────────────────────────────────
    NOTE
    You can no longer conditionally execute NEXT and WEND statements using
    the single-line IF...THEN...ELSE statement.
    ──────────────────────────────────────────────────────────────────────────


B.5  File Compatibility

    All versions of QuickBASIC are source-code compatible; source code created
    for a previous version compiles under Version 4.5, except as noted in
    Section B.3.4, "Restrictions on Include Files." QuickBASIC 4.5 translates
    QuickBASIC 4.0 binary files. If you encounter difficulty translating other
    binary files, save the program as an ASCII (text) format in QuickBASIC
    4.0, then load it with QuickBASIC 4.5. You must recompile object files and
    user libraries created with previous versions of QuickBASIC.



────────────────────────────────────────────────────────────────────────────
Appendix C  Limits in QuickBASIC

    QuickBASIC and the BC compiler offer programming versatility, but both
    have limitations in order to keep file size and complexity manageable. As
    a result, you may reach these limits in some situations. This appendix
    lists the boundaries you may encounter.

    Table C.1  QuickBASIC Limits
╓┌─┌────────────────────────┌───────────────────────┌────────────────────────╖
                            Maximum                 Minimum
    ──────────────────────────────────────────────────────────────────────────
    Names and Strings
    Variable names           40 characters           1 character
                            Maximum                 Minimum
    ──────────────────────────────────────────────────────────────────────────
    Variable names           40 characters           1 character

    String length            32,767 characters       0 characters

    Integers                 32,767                  -32,768

    Long integers            2,147,483,647           -2,147,483,648

    Single-precision numbers 3.402823 E+38           1.401298 E-45
    (positive)

    Single-precision numbers -1.401298 E-45          -3.402823 E+38
    (negative)

    Double-precision numbers 1.797693134862315 D+308 4.940656458412465 D-324
    (positive)

    Double-precision numbers -4.940656458412465      -1.797693134862315 D+308
    (negative)               D-324
                            Maximum                 Minimum
    ──────────────────────────────────────────────────────────────────────────
    (negative)               D-324

    Arrays
    Array size (all
    elements)
    Static                 65,535 bytes (64K)      1
    Dynamic                Available memory

    Array dimensions         8                       1

    Array subscripts         32,767                  -32,768

    Files and Procedures
    Number of arguments      60 interpreted          0 passed to a procedure

    Nesting of include files 5 levels                0

    Procedure size           65,535 bytes (64K)      0
    (interpreted)
                            Maximum                 Minimum
    ──────────────────────────────────────────────────────────────────────────
    (interpreted)

    Module size (compiled)   65,535 bytes (64K)      0

    Data files open          255                     0 simultaneously

    Data file record number  2,147,483,647           1

    Data file record size    32,767 bytes (32K)      1 byte
    (bytes)

    Data file size           Available disk space    0

    Path names               127 characters          1 character

    Error message numbers    255                     1

    Editing in the Quick-BASIC Environment
    Text box entry           128 characters          0 characters
                            Maximum                 Minimum
    ──────────────────────────────────────────────────────────────────────────
    Text box entry           128 characters          0 characters

    "Search for" string      128                     1

    "Change to" string       40                      0

    Placemarkers             4                       0

    Watchpoints and/or watch 8                       0 expressions

    Number of lines in       10                      0 Immediate window

    Characters in View       255                     0 window on one line

    Length of COMMAND$       124                     0 string
    ──────────────────────────────────────────────────────────────────────────



────────────────────────────────────────────────────────────────────────────
Appendix D  Keyboard Scan Codes and ASCII Character Codes


D.1  Keyboard Scan Codes

    The table on the next page shows the DOS keyboard scan codes. These codes
    are returned by the INKEY$ function.

    Key combinations with NUL in the Char column return two bytes──a null byte
    (&H00) followed by the value listed in the Dec and Hex columns. For
    example, pressing ALT+F1 returns a null byte followed by a byte containing
    104 (&H68).

    ┌────────┬───────┬────────────┬─────────────┬─────────────┬──────────────┐
    │        │       │            │ ASCII or    │  ASCII or   │   ASCII or   │
    │        │ Scan  │ ASCII or   │ Extended    │  Extended   │   Extended   │
    │  Key   │ Code  │ Extended   │ with SHIFT  │  with CTRL  │   with ALT   │
    ├────────┼───────┼────────────┼─────────────┼─────────────┼──────────────┤
    │        │Dec Hex│Dec Hex Char│Dec Hex Char │Dec Hex Char │ Dec Hex Char │
    ├────────┼───────┼────────────┼─────────────┼─────────────┼──────────────┤
    │ESC     │ 1  01 │ 27  1B     │ 27  1B      │ 27  1B      │              │
    │1!      │ 2  02 │ 49  31  1  │ 33  21   !  │             │ 120  78  NUL │
    │2@      │ 3  03 │ 50  32  2  │ 64  40   @  │  3  03 NUL  │ 121  79  NUL │
    │3#      │ 4  04 │ 51  33  3  │ 35  23   #  │             │ 122  7A  NUL │
    │4$      │ 5  05 │ 52  34  4  │ 36  24   $  │             │ 123  7B  NUL │
    │5%      │ 6  06 │ 53  35  5  │ 37  25   %  │             │ 124  7C  NUL │
    │6^      │ 7  07 │ 54  36  6  │ 94  5E   ^  │ 30  IE      │ 125  7D  NUL │
    │7&      │ 8  08 │ 55  37  7  │ 38  26   &  │             │ 126  7E  NUL │
    │8*      │ 9  09 │ 56  38  8  │ 42  2A   *  │             │ 127  7F  NUL │
    │9(      │10  0A │ 57  39  9  │ 40  28   (  │             │ 128  80  NUL │
    │0)      │11  0B │ 48  30  0  │ 41  29   )  │             │ 129  81  NUL │
    │-_      │12  0C │ 45  2D  -  │ 95  5F   -  │ 31  IF      │ 130  82  NUL │
    │=+      │13  0D │ 61  3D  =  │ 43  2B   +  │             │ 131  83  NUL │
    │BKSP    │14  0E │  8  08     │  8  08      │127  7F      │              │
    │TAB     │15  0F │  9  09     │ 15  OF   NUL│             │              │
    │Q       │16  10 │113  71  q  │ 81  51   Q  │ 17  11      │  16  10  NUL │
    │W       │17  11 │119  77  w  │ 87  57   W  │ 23  17      │  17  11  NUL │
    │E       │18  12 │101  65  e  │ 69  45   E  │  5  05      │  18  12  NUL │
    │R       │19   13│114  72   r │ 82  52   R  │ 18  12      │  19  13  NUL │
    │T       │20   14│116  74   t │ 84  54   T  │ 20  14      │  20  14  NUL │
    │Y       │21   15│121  79   y │ 89  59   Y  │ 25  19      │  21  15  NUL │
    │U       │22   16│117  75   u │ 85  55   U  │ 21  15      │  22  16  NUL │
    │I       │23   17│105  69   i │ 73  49   I  │  9  09      │  23  17  NUL │
    │O       │24   18│111  6F   o │ 79  4F   O  │ 15  0F      │  24  18  NUL │
    │P       │25   19│112  70   p │ 80  50   P  │ 16  10      │  25  19  NUL │
    │[{      │26   1A│ 91  5B   [ │123  7B   {  │ 27  1B      │              │
    │]}      │27   1B│ 93  5D   ] │125  7D   }  │ 29  1D      │              │
    │ENTER   │28   1C│ 13  OD   CR│ 13  OD   CR │ 10  OA   LF │              │
    │CTRL    │29   1D│            │             │             │              │
    │A       │30   1E│ 97  61   a │ 65  41   A  │  1  01      │  30  1E  NUL │
    │S       │31   1F│115  73   s │ 83  53   S  │ 19  13      │  31  1F  NUL │
    │D       │32   20│100  64   d │ 68  44   D  │  4  04      │  32  20  NUL │
    │F       │33   21│102  66   f │ 70  46   F  │  6  06      │  33  21  NUL │
    │G       │34   22│103  67   g │ 71  47   G  │  7  07      │  34  22  NUL │
    │H       │35   23│104  68   h │ 72  48   H  │  8  08      │  35  23  NUL │
    │J       │36   24│106  6A   j │ 74  4A   J  │ 10  0A      │  36  24  NUL │
    │K       │37   25│107  6B   k │ 75  4B   K  │ 11  0B      │  37  25  NUL │
    │L       │38   26│108  6C   l │ 76  4C   L  │ 12  0C      │  38  26  NUL │
    │;:      │39   27│ 59  3B   ; │ 58  3A   :  │             │              │
    │'"      │40   28│ 39  27   ' │ 34  22   "  │             │              │
    │`~      │41   29│ 96  60   ` │126  7E   ~  │             │              │
    │L SHIFT │42   2A│            │             │             │              │
    │\|      │43   2B│ 92  5C   \ │124  7C   |  │ 28  1C      │              │
    │Z       │44  2C │122  7A  z  │ 90  5A   Z  │ 26  1A      │  44  2C  NUL │
    │X       │45  2D │120  78  x  │ 88  58   X  │ 24  18      │  45  2D  NUL │
    │C       │46  2E │ 99  63  c  │ 67  43   C  │  3  03      │  46  2E  NUL │
    │V       │47  2F │118  76  v  │ 86  56   V  │ 22  16      │  47  2F  NUL │
    │B       │48  30 │ 98  62  b  │ 66  42   B  │  2  OE      │  48  30  NUL │
    │N       │49  31 │110  6E  n  │ 78  4E   N  │ 14  OD      │  49  31  NUL │
    │M       │50  32 │109  6D  m  │ 77  4D   M  │ 13          │  50  32  NUL │
    │,<      │51  33 │ 44  2C  '  │ 60  3C   <  │             │              │
    │.>      │52  34 │ 46  2E  .  │ 62  3E   >  │             │              │
    │/?      │53  35 │ 47  2F  /  │ 63  3F   ?  │             │              │
    │R SHIFT │54  36 │            │             │             │              │
    │* PRTSC │55  37 │ 42  2A  *  │     INT  5§ │ 16  10      │              │
    │ALT     │56  38 │            │             │             │              │
    │SPACE   │57  39 │ 32  20  SPC│ 32  20   SPC│ 32  20  SPC │  32  20  SPC │
    │CAPS    │58  3A │            │             │             │              │
    │F1      │59  3B │ 59  3B  NUL│ 84  54   NUL│ 94  5E  NUL │ 104  68  NUL │
    │F2      │60  3C │ 60  3C  NUL│ 85  55   NUL│ 95  5F  NUL │ 105  69  NUL │
    │F3      │61  3D │ 61  3D  NUL│ 86  56   NUL│ 96  60  NUL │ 106  6A  NUL │
    │F4      │62  3E │ 62  3E  NUL│ 87  57   NUL│ 97  61  NUL │ 107  6B  NUL │
    │F5      │63  3F │ 63  3F  NUL│ 88  58   NUL│ 98  62  NUL │ 108  6C  NUL │
    │F6      │64  40 │ 64  40  NUL│ 89  59   NUL│ 99  63  NUL │ 109  6D  NUL │
    │F7      │65  41 │ 65  41  NUL│ 90  5A   NUL│100  64  NUL │ 110  6E  NUL │
    │F8      │66  42 │ 66  46  NUL│ 91  5B   NUL│101  65  NUL │ 111  6F  NUL │
    │F9      │67  43 │ 67  43  NUL│ 92  5C   NUL│102  66  NUL │ 112  70  NUL │
    │F10     │68  44 │ 68  44  NUL│ 93  5D   NUL│103  67  NUL │ 113  71  NUL │
    │NUM     │69  45 │            │             │             │              │
    │SCROLL  │70  46 │            │             │             │              │
    │HOME    │71  47 │ 71  47 NUL │ 55  37   7  │119  77  NUL │              │
    │UP      │72  48 │ 72  48 NUL │ 56  38   8  │             │              │
    │PGUP    │73  49 │ 73  49 NUL │ 57  39   9  │132  84  NUL │              │
    │GREY-   │74  4A │ 45  2D  -  │ 45  2D   -  │             │              │
    │LEFT    │75  4B │ 75  4B NUL │ 52  34   4  │115  73  NUL │              │
    │CENTER  │76  4C │            │ 53  35   5  │             │              │
    │RIGHT   │77  4D │ 77  4D NUL │ 54  36   6  │116  74  NUL │              │
    │GREY+   │78  4E │ 43  2B  +  │ 43  2b   +  │             │              │
    │END     │79  4F │ 79  4F NUL │ 49  31   1  │117  75  NUL │              │
    │DOWN    │80  50 │ 80  50 NUL │ 50  32   2  │             │              │
    │PGDN    │81  51 │ 81  51 NUL │ 51  33   3  │118  76  NUL │              │
    │INS     │82  52 │ 82  52 NUL │ 48  30   0  │             │              │
    │DEL     │83  53 │ 83  53 NUL │ 46  2E   .  │             │              │
    └────────┴───────┴────────────┴─────────────┴─────────────┴──────────────┘


D.2  ASCII Character Codes

    ┌─────────────────────┐  ┌────────────┐  ┌────────────┐  ┌────────────┐
    │Ctrl D   H  Char Code│  │Dec Hex Char│  │Dec Hex Char│  │Dec Hex Char│
    ├───┬───┬───┬────┬────┤  ├───┬───┬────┤  ├───┬───┬────┤  ├───┬───┬────┤
    │^@ │  0│00 │    │ NUL│  │ 32│ 20│    │  │ 64│40 │ @  │  │ 96│60 │ `  │
    │^A │  1│01 │ ☺  │ SOH│  │ 33│ 21│ !  │  │ 65│41 │ A  │  │ 97│61 │ a  │
    │^B │  2│02 │ ☻  │ STX│  │ 34│ 22│ "  │  │ 66│42 │ B  │  │ 98│62 │ b  │
    │^C │  3│03 │ ♥  │ ETX│  │ 35│ 23│ #  │  │ 67│43 │ C  │  │ 99│63 │ c  │
    │^D │  4│04 │ ♦  │ EOT│  │ 36│ 24│ $  │  │ 68│44 │ D  │  │100│64 │ d  │
    │^E │  5│05 │ ♣  │ ENQ│  │ 37│ 25│ %  │  │ 69│45 │ E  │  │101│65 │ e  │
    │^F │  6│06 │ ♠  │ ACK│  │ 38│ 26│ &  │  │ 70│46 │ F  │  │102│66 │ f  │
    │^G │  7│07 │ •  │ BEL│  │ 39│ 27│ '  │  │ 71│47 │ G  │  │103│67 │ g  │
    │^H │  8│08 │ ◘  │ BS │  │ 40│ 28│ (  │  │ 72│48 │ H  │  │104│68 │ h  │
    │^I │  9│09 │    │ HT │  │ 41│ 29│ )  │  │ 73│49 │ I  │  │105│69 │ i  │
    │^J │ 10│0A │    │ LF │  │ 42│ 2A│ *  │  │ 74│4A │ J  │  │106│6A │ j  │
    │^K │ 11│0B │    │ VT │  │ 43│ 2B│ +  │  │ 75│4B │ K  │  │107│6B │ k  │
    │^L │ 12│0C │    │ FF │  │ 44│ 2C│ ,  │  │ 76│4C │ L  │  │108│6C │ l  │
    │^M │ 13│0D │    │ CR │  │ 45│ 2D│ -  │  │ 77│4D │ M  │  │109│6D │ m  │
    │^N │ 14│0E │ ♫  │ SO │  │ 46│ 2E│ .  │  │ 78│4E │ N  │  │110│6E │ n  │
    │^O │ 15│0F │    │ SI │  │ 47│ 2F│ /  │  │ 79│4F │ O  │  │111│6F │ o  │
    │^P │ 16│10 │ ►  │ DLE│  │ 48│ 30│ 0  │  │ 80│50 │ P  │  │112│70 │ p  │
    │^Q │ 17│11 │ ◄  │ DC1│  │ 49│ 31│ 1  │  │ 81│51 │ Q  │  │113│71 │ q  │
    │^R │ 18│12 │ ↕  │ DC2│  │ 50│ 32│ 2  │  │ 82│52 │ R  │  │114│72 │ r  │
    │^S │ 19│13 │ ‼  │ DC3│  │ 51│ 33│ 3  │  │ 83│53 │ S  │  │115│73 │ s  │
    │^T │ 20│14 │ ¶  │ DC4│  │ 52│ 34│ 4  │  │ 84│54 │ T  │  │116│74 │ t  │
    │^U │ 21│15 │ §  │ NAK│  │ 53│ 35│ 5  │  │ 85│55 │ U  │  │117│75 │ u  │
    │^V │ 22│ 16│ ▬  │ SYN│  │ 54│ 36│ 6  │  │ 86│ 56│ V  │  │118│ 76│ v  │
    │^W │ 23│ 17│ ↨  │ ETB│  │ 55│ 37│ 7  │  │ 87│ 57│ W  │  │119│ 77│ w  │
    │^X │ 24│ 18│ ↑  │ CAN│  │ 56│ 38│ 8  │  │ 88│ 58│ X  │  │120│ 78│ x  │
    │^Y │ 25│ 19│ ↓  │ EM │  │ 57│ 39│ 9  │  │ 89│ 59│ Y  │  │121│ 79│ y  │
    │^Z │ 26│ 1A│ →  │ SUB│  │ 58│ 3A│ :  │  │ 90│ 5A│ Z  │  │122│ 7A│ z  │
    │^[ │ 27│ 1B│ ←  │ ESC│  │ 59│ 3B│ ;  │  │ 91│ 5B│ [  │  │123│ 7B│ {  │
    │^\ │ 28│ 1C│ ∟  │ FS │  │ 60│ 3C│ <  │  │ 92│ 5C│ \  │  │124│ 7C│ |  │
    │^} │ 29│ 1D│ ↔  │ GS │  │ 61│ 3D│ =  │  │ 93│ 5D│ ]  │  │125│ 7D│ }  │
    │^^ │ 30│ 1E│   │ RS │  │ 62│ 3E│ >  │  │ 94│ 5E│ ^  │  │126│ 7E│ ~  │
    │^_ │ 31│ 1F│ ▼  │ US │  │ 63│ 3F│ ?  │  │ 95│ 5F│ _  │  │127│ 7F│    │
    └───┴───┴───┴────┴────┘  └───┴───┴────┘  └───┴───┴────┘  └───┴───┴────┘


            ┌────────────┐  ┌────────────┐  ┌────────────┐  ┌────────────┐
            │Dec Hex Char│  │Dec Hex Char│  │Dec Hex Char│  │Dec Hex Char│
            ├───┬───┬────┤  ├───┬───┬────┤  ├───┬───┬────┤  ├───┬───┬────┤
            │128│ 80│ Ç  │  │160│ A0│ á  │  │192│ C0│ └  │  │224│ E0│ α  │
            │129│ 81│ ü  │  │161│ A1│ í  │  │193│ C1│ ┴  │  │225│ E1│ ß  │
            │130│ 82│ é  │  │162│ A2│ ó  │  │194│ C2│ ┬  │  │226│ E2│ Γ  │
            │131│ 83│ â  │  │163│ A3│ ú  │  │195│ C3│ ├  │  │227│ E3│ π  │
            │132│ 84│ ä  │  │164│ A4│ ñ  │  │196│ C4│ ─  │  │228│ E4│ Σ  │
            │133│ 85│ à  │  │165│ A5│ Ñ  │  │197│ C5│ ┼  │  │229│ E5│ σ  │
            │134│ 86│ å  │  │166│ A6│ ª  │  │198│ C6│ ╞  │  │230│ E6│ µ  │
            │135│ 87│ ç  │  │167│ A7│ º  │  │199│ C7│ ╟  │  │231│ E7│ τ  │
            │136│ 88│ ê  │  │168│ A8│ ¿  │  │200│ C8│ ╚  │  │232│ E8│ Φ  │
            │137│ 89│ ë  │  │169│ A9│ ⌐  │  │201│ C9│ ╔  │  │233│ E9│ Θ  │
            │138│ 90│ è  │  │170│ AA│ ¬  │  │202│ CA│ ╩  │  │234│ EA│ Ω  │
            │139│ 91│ ï  │  │171│ AB│ ½  │  │203│ CB│ ╦  │  │235│ EB│ δ  │
            │140│ 92│ î  │  │172│ AC│ ¼  │  │204│ CC│ ╠  │  │236│ EC│ ∞  │
            │141│ 93│ ì  │  │173│ AD│ ¡  │  │205│ CD│ ═  │  │237│ ED│ φ  │
            │142│ 94│ Ä  │  │174│ AE│ «  │  │206│ CE│ ╬  │  │238│ EE│ ε  │
            │143│ 95│ Å  │  │175│ AF│ »  │  │207│ CF│ ╧  │  │239│ EF│ ∩  │
            │144│ 96│ É  │  │176│ B0│ ░  │  │208│ D0│ ╨  │  │240│ F0│ ≡  │
            │145│ 97│ æ  │  │177│ B1│ ▒  │  │209│ D1│ ╤  │  │241│ F1│ ±  │
            │146│ 98│ Æ  │  │178│ B2│ ▓  │  │210│ D2│ ╥  │  │242│ F2│ ≥  │
            │147│ 99│ ô  │  │179│ B3│ │  │  │211│ D3│ ╙  │  │243│ F3│ ≤  │
            │148│ 9A│ ö  │  │180│ B4│ ┤  │  │212│ D4│ ╘  │  │244│ F4│ ⌠  │
            │149│ 9B│ ò  │  │181│ B5│ ╡  │  │213│ D5│ ╒  │  │245│ F5│ ⌡  │
            │150│ 96│ û  │  │182│ B6│ ╢  │  │214│ D6│ ╓  │  │246│ F6│ ÷  │
            │151│ 97│ ù  │  │183│ B7│ ╖  │  │215│ D7│ ╫  │  │247│ F7│ ≈  │
            │152│ 98│ ÿ  │  │184│ B8│ ╕  │  │216│ D8│ ╪  │  │248│ F8│ °  │
            │153│ 99│ Ö  │  │185│ B9│ ╣  │  │217│ D9│ ≈  │  │249│ F9│ ∙  │
            │154│ 9A│ Ü  │  │186│ BA│ ║  │  │218│ DA│ ┌  │  │250│ FA│ ·  │
            │155│ 9B│ ¢  │  │187│ BB│ ╗  │  │219│ DB│ █  │  │251│ FB│ √  │
            │156│ 9C│ £  │  │188│ BC│ ╝  │  │220│ DC│ ▄  │  │252│ FC│ ⁿ  │
            │157│ 9D│ ¥  │  │189│ BD│ ╜  │  │221│ DD│ ▌  │  │253│ FD│ ²  │
            │158│ 9E│ ₧  │  │190│ BE│ ╛  │  │222│ DE│ ▐  │  │254│ FE│ ■  │
            │159│ 9F│ ƒ  │  │191│ BF│ ┐  │  │223│ DF│ ▀  │  │255│ FF│    │
            └───┴───┴────┘  └───┴───┴─┴──┘  └───┴───┴────┘  └───┴───┴────┘



────────────────────────────────────────────────────────────────────────────
Appendix E  BASIC Reserved Words

    The following is a list of Microsoft BASIC reserved words:

    ──────────────────────────────────────────────────────────────────────────
    ABS               ELSE             LOOP              SEEK
    ACCESS             ELSEIF           LPOS              SEG
    ALIAS              END              LPRINT            SELECT
    AND                ENDIF            LSET              SETMEM
    ANY                ENVIRON          LTRIM$            SGN
    APPEND             ENVIRON$         MID$              SHARED
    AS                 EOF              MKD$              SHELL
    ASC               EQV               MKDIR             SIGNAL
    ATN               ERASE            MKDMBF$           SIN
    BASE               ERDEV            MKI$              SINGLE
    BEEP              ERDEV$           MKL$              SLEEP
    BINARY             ERL              MKS$              SOUND
    BLOAD             ERR              MKSMBF$           SPACE$
    BSAVE             ERROR            MOD                SPC
    BYVAL              EXIT             NAME              SQR
    CALL              EXP              NEXT              STATIC
    CALLS             FIELD            NOT                STEP
    CASE              FILEATTR         OCT$              STICK
    CDBL              FILES            OFF                STOP
    CDECL              FIX              ON                 STR$
    CHAIN             FOR              OPEN              STRIG
    CHDIR             FRE              OPTION            STRING
    CHR$              FREEFILE         OR                 STRING$
    CINT              FUNCTION         OUT               SUB
    CIRCLE            GET              OUTPUT             SWAP
    CLEAR             GOSUB            PAINT             SYSTEM
    CLNG              GOTO             PALETTE           TAB
    CLOSE             HEX$             PCOPY             TAN
    CLS               IF               PEEK              THEN
    COLOR             IMP               PEN               TIME$
    COM               INKEY$           PLAY              TIMER
    COMMAND$          INP              PMAP              TO
    COMMON            INPUT            POINT             TROFF
    CONST             INPUT$           POKE              TRON
    COS               INSTR            POS               TYPE
    CSNG              INT              PRESET            UBOUND
    CSRLIN            INTEGER           PRINT             UCASE$
    CVD               IOCTL            PSET              UEVENT
    CVDMBF            IOCTL$            PUT               UNLOCK
    CVI               IS                RANDOM             UNTIL
    CVL               KEY              RANDOMIZE         USING
    CVS               KILL             READ              VAL
    CVSMBF            LBOUND           REDIM             VARPTR
    DATA              LCASE$           REM               VARPTR$
    DATE$             LEFT$            RESET             VARSEG
    DECLARE           LEN              RESTORE           VIEW
    DEF               LET              RESUME            WAIT
    DEFDBL            LINE             RETURN            WEND
    DEFINT            LIST              RIGHT$            WHILE
    DEFLNG            LOC              RMDIR             WIDTH
    DEFSNG            LOCAL             RND               WINDOW
    DEFSTR            LOCATE           RSET              WRITE
    DIM               LOCK             RTRIM$            XOR
    DO                LOF              RUN
    DOUBLE             LOG              SADD
    DRAW              LONG              SCREEN
    ──────────────────────────────────────────────────────────────────────────



────────────────────────────────────────────────────────────────────────────
Appendix F  Metacommands

    This appendix describes the QuickBASIC metacommands──commands that direct
    QuickBASIC to handle your program in a particular way. The first section
    describes the format used for metacommands. The next two sections describe
    specific metacommands.

    By using the metacommands, you can:

    ■ Read in and compile other BASIC source files at specific points during
    compilation ($INCLUDE)

    ■ Control the allocation of dimensioned arrays ($STATIC and $DYNAMIC)


F.1  Metacommand Syntax

    Metacommands begin with a dollar sign ($) and are always enclosed in a
    program comment. More than one metacommand can be given in one comment.
    Multiple metacommands are separated by white-space characters (space or
    tab). Metacommands that take arguments have a colon between the
    metacommand and the argument:

    REM $METACOMMAND [[ : argument ]]

    String arguments must be enclosed in single quotation marks. White-space
    characters between elements of a metacommand are ignored. The following
    are all valid forms for metacommands:

    REM $STATIC $INCLUDE:  'datadefs.bi'
    REM     $STATIC    $INCLUDE :  'datadefs.bi'
    ' $STATIC $INCLUDE: 'datadefs.bi'
    '     $STATIC    $INCLUDE :  'datadefs.bi'

    Note that no spaces appear between the dollar sign and the rest of the
    metacommand.

    If you want to refer to a metacommand in a description but do not want it
    to execute, place a character that is not a tab or space before the first
    dollar sign on the line. For example, on the following line both
    metacommands are ignored:

    REM x$STATIC $INCLUDE:  'datadefs.bi'


F.2  Processing Additional Source Files: $INCLUDE

    The $INCLUDE metacommand instructs the compiler to temporarily switch from
    processing one file and instead to read program statements from the BASIC
    file named in the argument. When the end of the included file is reached,
    the compiler returns to processing the original file. Because compilation
    begins with the line immediately following the line in which $INCLUDE
    occurred, $INCLUDE should be the last statement on a line. The following
    statement is correct:

    DEFINT I-N   ' $INCLUDE: 'COMMON.BAS'

    There are two restrictions on using include files:

    1. Included files must not contain SUB or FUNCTION statements.

    2. Included files created with BASICA must be saved with the ,A option.


F.3  Dimensioned Array Allocation: $STATIC and $DYNAMIC

    The $STATIC and $DYNAMIC metacommands tell the compiler how to allocate
    memory for arrays. Neither of these metacommands takes an argument:

    'Make all arrays dynamic.

    '$DYNAMIC

    $STATIC sets aside storage for arrays during compilation. When the $STATIC
    metacommand is used, the ERASE statement reinitializes all array values to
    zero (numeric arrays) or the null string (string arrays) but does not
    remove the array. The REDIM statement has no effect on $STATIC arrays.

    $DYNAMIC allocates storage for arrays while the program is running. This
    means that the ERASE statement removes the array and frees the memory it
    took for other uses. You can also use the REDIM statement to change the
    size of a $DYNAMIC array.

    The $STATIC and $DYNAMIC metacommands affect all arrays except implicitly
    dimensioned arrays (arrays not declared in a DIM statement). Implicitly
    dimensioned arrays are always allocated as if $STATIC had been used.



────────────────────────────────────────────────────────────────────────────
Appendix G  Compiling and Linking from DOS

    This appendix explains how to compile and link outside the QuickBASIC
    environment. You might want to do this for some of the following reasons:

    ■ To use a different text editor

    ■ To create executable programs that can be debugged with the Microsoft
    CodeView debugger

    ■ To create listing files for use in debugging a stand-alone executable
    program

    ■ To use options not available within the QuickBASIC environment, such as
    storing arrays in row order

    ■ To link with NOCOM.OBJ or NOEM.OBJ (files supplied with QuickBASIC),
    which reduce the size of executable files in programs that do not use
    the COM statement or are always used with a math coprocessor

    When you finish this appendix you will understand how to

    ■ Compile from the DOS command line with the BC command

    ■ Create executable files and link program object files with the LINK
    command

    ■ Create and maintain stand-alone (.LIB) libraries with the LIB command


G.1  BC, LINK, and LIB

    The Microsoft QuickBASIC package includes BC, LINK, and LIB. The following
    list describes how these special tools are used when compiling and linking
    outside of the QuickBASIC environment:

╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Program            Function
    ──────────────────────────────────────────────────────────────────────────
    BC.EXE             When you choose the Make EXE File or Make Library
                        command from the Run menu, QuickBASIC invokes the BASIC
                        command-line compiler (BC) to produce intermediate
                        program files called object files. These object files
                        will be linked together to form your program or Quick
                        library. BC is also available any time you want to
                        compile programs outside of the QuickBASIC environment.
                        You may prefer to use BC if you want to compile
                        programs you have written with another text editor.
                        However, you only need to use BC from the command line
                        if your program is too large to compile in memory
                        within the QuickBASIC environment or if you want your
                        executable files to be compatible with the Microsoft
                        CodeView debugger.

    LINK.EXE           QuickBASIC uses the Microsoft Overlay Linker (LINK) to
                        link object files produced by BC with the appropriate
    Program            Function
    ──────────────────────────────────────────────────────────────────────────
                        link object files produced by BC with the appropriate
                        libraries to produce an executable file. You can use
                        LINK directly whenever you want to link object files or
                        make Quick libraries.

    LIB.EXE            The Microsoft Library Manager (LIB) creates stand-alone
                        libraries from the object files produced by BC.
                        QuickBASIC itself uses LIB to create such libraries and
                        then uses them when you choose the Make EXE File
                        command from the Run menu.

    ──────────────────────────────────────────────────────────────────────────



G.2  The Compiling and Linking Process

    To create a stand-alone program from a BASIC source file when you are
    outside of the QuickBASIC environment, follow these steps:

    1. Compile each source file, creating an object file.

    2. Link the object files using LINK. LINK includes one or more stand-alone
        libraries and creates an executable file. LINK makes sure that all the
        procedure calls in the source files match up with the procedures in the
        libraries or with procedures in other object files before it creates an
        executable file.

    You can use either of the following methods of compiling and linking:

    ■ Compile and link in separate steps by using the BC and LINK commands.

    ■ Create a batch file containing all the compiling and linking commands.
    This method is most useful if you use the same options whenever you
    compile and link your programs.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    When QuickBASIC compiles and links your program from within the
    environment, the /E linker option is set automatically. However, when
    you use the LINK command outside the QuickBASIC environment, you must
    explicitly specify the /E option to minimize the size of the executable
    file and maximize program-loading speed.
    ──────────────────────────────────────────────────────────────────────────

    When compiling and linking from DOS, the paths you defined in the Options
    menu are not used. To search for include and library files the way you
    specified on the Options menu, you must set the DOS environment variables
    LIB and INCLUDE to point to the appropriate directories. Otherwise the
    compiler and/or linker might generate File not found errors.

    Sections G.3 and G.4 explain how to compile and link in separate steps.


G.3  Compiling with the BC Command

    You can compile with the BC command in either of the following ways:

    ■ Type all information on a single command line, using the following
    syntax:

    BC sourcefile [[,[[objectfile]] [[,[[listingfile]]]]]]
    [[optionslist]][[;]]

    ■ Type

    BC

    and respond to the following prompts:

    Source Filename [.BAS]:
    Object Filename [basename.OBJ]:
    Source Listing: [NUL.LST]:

    Table G.1 shows the input you must give on the BC command line or in
    response to each prompt:

    Table G.1  Input to the BC Command
    Field              Prompt            Input
    ──────────────────────────────────────────────────────────────────────────
    sourcefile         "Source Filename" The name of your source file

    objectfile         "Object Filename" The name of the object file you are
                                        creating

    listingfile        "Source Listing"  The name of the file containing a
                                        source listing. The source-listing
                                        file contains the address of each
                                        line in your source file, the text of
                                        the source file, its size, and any
                                        error messages produced during
                                        compilation.

    optionslist        Gives options     Any of the compiler options described
                        after any         in Section G.3.2, "Using BC Command
                        response          Options"
    ──────────────────────────────────────────────────────────────────────────

G.3.1  Specifying File Names

    The BC command makes certain assumptions about the files you specify,
    based on the path names and extensions you use for the files. The
    following sections describe these assumptions and other rules for
    specifying file names to the BC command.

    G.3.1.1  Uppercase and Lowercase Letters

    You can use any combination of uppercase and lowercase letters for file
    names; the compiler accepts uppercase and lowercase letters
    interchangeably.

    Example

    The BC command considers the following three file names to be equivalent:

    abcde.BAS
    ABCDE.BAS
    aBcDe.Bas

    G.3.1.2  File-Name Extensions

    A DOS file name has two parts: the "base name," which includes everything
    up to (but not including) the period (.), and the "extension," which
    includes the period and up to three characters following the period. In
    general, the extension identifies the type of file (for example, whether
    the file is a BASIC source file, an object file, an executable file, or a
    stand-alone library).

    BC and LINK use the file-name extensions described in the following list:

    Extension                File Description
    ──────────────────────────────────────────────────────────────────────────
    .BAS                     BASIC source file

    .OBJ                     Object file

    .LIB                     Stand-alone library file

    .LST                     Listing file produced by BC

    .MAP                     File of symbols from the linked program

    .EXE                     Executable file

    ──────────────────────────────────────────────────────────────────────────

    G.3.1.3  Path Names

    Any file name can include a full or partial path name. A full path name
    starts with the drive name; a partial path name has one or more directory
    names preceding the file name, but does not include a drive name.

    Giving a full path name allows you to specify files in different paths as
    input to the BC command and lets you create files on different drives or
    in different directories on the current drive.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    For files that you are creating with BC, you can give a path name ending
    in a backslash (\) to create the file in that path. When it creates the
    file, BC uses the default name for the file.
    ──────────────────────────────────────────────────────────────────────────

G.3.2  Using BC Command Options

    Options to the BC command consist of either a forward-slash character (/)
    or a dash (-) followed by one or more letters. (The forward slash and the
    dash can be used interchangeably. In this manual, forward slashes are used
    for options.)

    The BC command-line options are explained in the following list:

╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Option             Description
    ──────────────────────────────────────────────────────────────────────────
    /A                 Creates a listing of the disassembled object code for
                        each source line and shows the assembly-language code
                        generated by the compiler.

    /AH                Allows dynamic arrays of records, fixed-length strings,
                        and numeric data to occupy all of available memory. If
                        this option is not specified, the maximum size is 64K
                        per array. Note that this option has no effect on the
                        way data items are passed to procedures.

    /C:buffersize      Sets the size of the buffer receiving remote data for
                        each communications port when using an asynchronous
    Option             Description
    ──────────────────────────────────────────────────────────────────────────
                        each communications port when using an asynchronous
                        communications adapter. (The transmission buffer is
                        allocated 128 bytes for each communications port and
                        cannot be changed on the BC command line.) This option
                        has no effect if the asynchronous communications card
                        is not present. The default buffer size is 512 bytes
                        total for both ports; the maximum size is 32,767 bytes.

    /D                 Generates debugging code for run-time error checking
                        and enables CTRL+BREAK. This option is the same as the
                        Produce Debug Code option from the Run menu's Make EXE
                        File command within the QuickBASIC environment.

    /E                 Indicates presence of ON ERROR with RESUME linenumber
                        statements. (See also the discussion of the /X option
                        in this list.)

    /MBF               The intrinsic functions MKS$, MKD$, CVS, and CVD are
                        converted to MKSMBF$, MKDMBF$, CVSMBF, and CVDMBF,
    Option             Description
    ──────────────────────────────────────────────────────────────────────────
                        converted to MKSMBF$, MKDMBF$, CVSMBF, and CVDMBF,
                        respectively. This allows your QuickBASIC program to
                        read and write floating-point values stored in
                        Microsoft Binary format.

    /O                 Substitutes the BCOM45.LIB run-time library for
                        BRUN45.LIB. See Chapter 16, "The Run Menu," in Learning
                        to Use Microsoft QuickBASIC for more information about
                        using these libraries.

    /R                 Stores arrays in row-major order. BASIC normally stores
                        arrays in column-major order. This option is useful if
                        you are using other-language routines that store arrays
                        in row order.

    /S                 Writes quoted strings to the object file instead of the
                        symbol table. Use this option when an Out of memory
                        error message occurs in a program that has many string
                        constants.
    Option             Description
    ──────────────────────────────────────────────────────────────────────────
                        constants.

    /V                 Enables event trapping for communications (COM),
                        lightpen (PEN), joystick (STRIG), timer (TIMER), music
                        buffer (PLAY) and function keys (KEY). Use this option
                        to check between statements for an occurrence of an
                        event.

    /W                 Enables event trapping for the same statements as /V,
                        but checks at each line number or label for occurrence
                        of an event.

    /X                 Indicates presence of ON ERROR with RESUME, RESUME
                        NEXT, or RESUME 0.

    /ZD                Produces an object file containing line-number records
                        corresponding to the line numbers of the source file.
                        This option is useful when you want to perform
                        source-level debugging using the Microsoft Symbolic
    Option             Description
    ──────────────────────────────────────────────────────────────────────────
                        source-level debugging using the Microsoft Symbolic
                        Debug Utility (SYMDEB), available with the Microsoft
                        Macro Assembler, Version 4.0.

    /ZI                Produces an object file of debugging information used
                        by the Microsoft CodeView debugger, available with
                        Microsoft C, Version 5.0 and later and Microsoft Macro
                        Assembler, Version 5.0 and later.
    ──────────────────────────────────────────────────────────────────────────


G.4  Linking

    After compiling your program, you must link the object file with the
    appropriate libraries to create an executable program. You can use the
    LINK command in any of the following ways:

    ■ Give the input on a command line of the following form:

    LINK objfile [[,[[exefile]][[,[[mapfile]][[,[[lib]]]]]]
    [[linkopts]][[;]]]]

    The command line cannot be longer than 128 characters.

    ■ Type

    LINK

    and respond to the following prompts:

    Object Modules [.OBJ]:
    Run File [basename.EXE]:
    List File [NUL.MAP]:
    Libraries [.LIB]:

    To give more files for any prompt, type a plus sign (+) at the end of
    the line. The prompt reappears on the next line, and you can continue
    typing input for the prompt.

    ■ Set up a "response file" (a file containing responses to the LINK
    command prompts), and then type a LINK command of the following form:

    LINK @filename

    Here, filename is the name of the response file. You can append linker
    options to any response or give options on one or more separate lines.
    The responses must be in the same order as the LINK command prompts
    discussed above. You can also enter the name of a response file after
    any linker prompt, or at any position in the LINK command line.

    Table G.2 shows the input you must give on the LINK command line, or in
    response to each prompt.

    Table G.2  Input to the LINK Command
╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Field                    Prompt
                            Input
    ──────────────────────────────────────────────────────────────────────────
    objfile                  "Object Modules"
                            One or more object files that you are linking.
                            The object files should be separated by either
    Field                    Prompt
                            Input
    ──────────────────────────────────────────────────────────────────────────
                            The object files should be separated by either
                            plus signs or spaces.

    exefile                  "Run File"
                            Name of the executable file you are creating, if
                            you want to give it a name or extension other
                            than the default. You should always use the .EXE
                            extension, since DOS expects executable files to
                            have this extension.

    mapfile                  "List File"
                            Name of the file containing a symbol map listing,
                            if you are creating one.☼ You can also specify
                            one of the following DOS device names to direct
                            the map file to that device: AUX for an auxiliary
                            device, CON for the console (terminal), PRN for a
                            printer device, or NUL for no device (so that no
                            map file is created). See Section G.4.6.11 for a
    Field                    Prompt
                            Input
    ──────────────────────────────────────────────────────────────────────────
                            map file is created). See Section G.4.6.11 for a
                            sample map file and information about its
                            contents.

    lib                      "Libraries"
                            One or more stand-alone libraries (or directories
                            to be searched for stand-alone libraries)
                            separated by plus signs or spaces. The
                            "Libraries" prompt allows you to specify up to 16
                            libraries; any additional libraries are ignored.
                            See Section G.4.3 for rules for specifying
                            library names to the linker.

    linkopts                 Gives options after any response
                            Any of the LINK options described in Sections
                            G.4.6.2-G.4.6.15. You can specify LINK options
                            anywhere on the command line.

    Field                    Prompt
                            Input
    ──────────────────────────────────────────────────────────────────────────

    ──────────────────────────────────────────────────────────────────────────


    If you are using a response file, each response must follow the rules
    outlined in Table G.2.

G.4.1  Defaults for LINK

    You can choose defaults for any of the information that LINK needs in any
    of the following ways:

    ■ To choose the default for any command-line entry, omit the file name or
    names before the entry and type only the required comma. The only
    exception to this is the default for the mapfile entry: if you use a
    comma as a placeholder for this entry, LINK creates a map file.

    ■ To choose the default for any prompt, press ENTER.

    ■ To choose the defaults for all remaining command-line entries or
    prompts, type a semicolon after any entry or prompt. The only required
    input is one or more object-file names.

    The following list shows the defaults that LINK uses for executable files,
    map files, and libraries:

╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    File Type                Default
    ──────────────────────────────────────────────────────────────────────────
    Executable               Base name of the first object file given, plus
                            the .EXE extension. To rename the executable
                            file, you are required to specify only the new
                            base name; if you give a file name with no
                            extension, LINK automatically appends the .EXE
                            extension.

    Map                      The special file name NUL.MAP, which tells LINK
                            not to create a map file. To create a map file,
                            you are required to specify only the base name;
    File Type                Default
    ──────────────────────────────────────────────────────────────────────────
                            you are required to specify only the base name;
                            if you give a file name with no extension, LINK
                            automatically appends the .MAP extension.

    Libraries                Libraries named in the given object files. If you
                            choose the Stand-Alone EXE File option,
                            BCOM45.LIB is the default library; otherwise, the
                            default is BRUN45.LIB. If you specify a library
                            other than a default library, you must give only
                            the base name; if you give a library name with no
                            extension, LINK automatically appends the .LIB
                            extension. See Section G.4.3 for information
                            about specifying libraries other than the default
                            libraries.
    ──────────────────────────────────────────────────────────────────────────


    ──────────────────────────────────────────────────────────────────────────
    NOTE
    When linking a stand-alone executable file, if your program does not use
    the COM statement, your program will be about 4K smaller if you link
    with NOCOM.OBJ, a file supplied with QuickBASIC.
    ──────────────────────────────────────────────────────────────────────────

    Examples

    The following example shows a response file. It tells LINK to link
    together the four object modules FRAME, TEXT, TABLE, and LINEOUT. The
    executable file FRAME.EXE and the map file named FRAMESYM.MAP are
    produced. The /PAUSE option causes LINK to pause before producing the
    executable file to permit disk swapping, if necessary. The /MAP option
    tells LINK to include public symbols and addresses in the map file. LINK
    also links any needed routines from the library file GRAF.LIB. See
    Sections G.4.6.2 and G.4.6.11 for more information on /PAUSE and /MAP
    options.

    FRAME TEXT TABLE LINEOUT
    /PAUSE /MAP
    FRAMESYM
    GRAF.LIB

    In the following example, LINK loads and links the object files FRAME.OBJ,
    TEXT.OBJ, TABLE.OBJ, and LINEOUT.OBJ, searching for unresolved references
    in the library file COBLIB.LIB. By default, the executable file is named
    FRAME.EXE. A map file called FRAMESYM.MAP is also produced.

    LINK FRAME+TEXT+TABLE+LINEOUT, ,FRAMESYM, COBLIB.LIB

    The example that follows illustrates how to continue any prompt by typing
    a plus sign (+) at the end of your response. The example links all of the
    given object files, then creates an executable file. Since a semicolon is
    typed as a response to the "Run File" prompt, the executable file is given
    the default name, which is the base name of the first object file given
    (FRAME), plus the .EXE extension. The defaults are also used for the
    remaining prompts. As a result, no map file is created, and the default
    libraries named in the object files are used for linking.

    LINK

    Object Modules [.OBJ]: FRAME TEXT TABLE LINEOUT+
    Object Modules [.OBJ]: BASELINE REVERSE COLNUM+
    Object Modules [.OBJ]: ROWNUM
    Run File [FRAME.EXE]: ;

G.4.2  Specifying Files to LINK

    The rules for specifying file names to the linker are the same as for
    specifying file names to the BC command: uppercase and lowercase letters
    can be used interchangeably, and file names can include path names to tell
    LINK to look for files or create files in the given path. See Section
    G.3.1 for more information.

G.4.3  Specifying Libraries to LINK

    Ordinarily, you do not need to give LINK a stand-alone-library name. When
    the BC command creates object files, it places in each object file the
    name of the correct stand-alone library for that object file. When the
    object file is passed to the linker, LINK looks for a library with the
    same name as the name in the object file and links the object file with
    that library automatically.

    To link object files with a stand-alone library other than the default,
    give the name of the nondefault library to LINK. You can give the library
    name in either of the following ways:

    ■ After the third comma on the LINK command line. Commas follow the list
    of object-file names, the executable-file name, and the listing-file
    name. The final name is the library name.

    ■ In response to the "Libraries" prompt of the LINK command.

    LINK searches libraries you specify to resolve external references before
    it searches default libraries.

    You might want to link with a stand-alone library other than the default
    to

    ■ Link with additional stand-alone libraries.

    ■ Link with libraries in different paths. If you specify a complete path
    name for the library, LINK only looks in that path for the library.
    Otherwise, it looks in the following three locations:

    1. The current working directory

    2. Any paths or drives you specify after the third comma on the LINK
        command line

    3. The locations given by the LIB environment variable

    ■ Ignore the library named in the object file. In this case, you must give
    the LINK option /NOD in addition to specifying the library you want to
    use for linking. See Section G.4.6.8 for more information about the
    /NOD option.

G.4.4  Memory Requirements for LINK

    LINK uses available memory for the linking session. If the files to be
    linked create an output file that exceeds available memory, LINK creates a
    temporary disk file to serve as memory. This temporary file is handled in
    one of the following ways, depending on the DOS version:

    ■ LINK uses the directory specified by the TMP environment variable from
    DOS for the purpose of creating a temporary file. For example, if the
    TMP variable was set to C:\TEMPDIR, then LINK would put the temporary
    file in C:\TEMPDIR.

    If there is no TMP environment variable, or if the directory specified
    by TMP does not exist, then LINK puts the temporary file in the current
    working directory.

    ■ If LINK is running on DOS Version 3.0 or later, it uses a DOS system
    call to create a temporary file with a unique name in the temporary-file
    directory.

    ■ If LINK is running on a version of DOS prior to 3.0, it creates a
    temporary file named VM.TMP.

    When the linker creates a temporary disk file, you see the message

    Temporary file tempfile has been created.
    Do not change diskette in drive, letter

    where tempfile is ".\" followed by either the file name VM.TMP or a name
    generated by DOS, and letter is the drive containing the temporary file.

    The message

    Do not change diskette in drive

    does not appear unless the drive named letter is a floppy-disk drive. If
    this message appears, do not remove the disk from the drive until the
    linking session ends. If you remove the disk, linker operations will be
    unpredictable, and you may see the following message:

    unexpected end-of-file on scratch file

    If you see this message, rerun the linking session.

    The temporary file that LINK creates is a working file only. LINK deletes
    it at the end of the session.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Do not give any of your own files the name VM.TMP. LINK displays an
    error message if it finds an existing file with this name.
    ──────────────────────────────────────────────────────────────────────────

G.4.5  Linking with Mixed-Language Programs

    You can link mixed-language programs with LINK. However, problems can
    result from linking .OBJ files from within the other language. Different
    assumptions by different linkers can corrupt QuickBASIC files.

    The following sections discuss linking with modules written in Pascal,
    FORTRAN, and assembly language.

    G.4.5.1  Pascal and FORTRAN Modules in QuickBASIC Programs

    Modules compiled with Microsoft Pascal or FORTRAN can be linked with BASIC
    programs, as described in the Microsoft Mixed-Language Programming Guide.
    They can also be incorporated in Quick libraries. However, QuickBASIC
    programs containing code compiled with Microsoft Pascal must allocate at
    least 2K near-heap space for Pascal. The following example does this by
    using the DIM statement to allocate a static array of 2K or greater in a
    named common block called NMALLOC:

    DIM name%(2048) : COMMON SHARED /NMALLOC/ name%()

    The Pascal run-time module assumes it always has at least 2K of near-heap
    space available. If the Pascal code cannot allocate the required space,
    QuickBASIC may crash. This applies to Pascal code in Quick libraries and
    to Pascal code linked into executable files. The situation is similar for
    FORTRAN I/O, which also requires near buffer space, and which can be
    provided by using an NMALLOC common block.

    G.4.5.2  STATIC Array Allocation in Assembly-Language Routines

    Use the SEG or CALLS keywords or far pointers to pass static array data to
    assembly-language routines. You cannot assume data is in a particular
    segment. Alternatively, you can declare all arrays dynamic (still using
    far pointers) since BC and the QuickBASIC environment handle dynamic
    arrays identically.

    G.4.5.3  References to DGROUP in Extended Run-Time Modules

    For mixed-language programs that use the CHAIN command, you should make
    sure that any code built into an extended run-time module does not contain
    any references to DGROUP. (The CHAIN command causes DGROUP to move, but
    does not update references to DGROUP.) This rule applies only to
    mixed-language programs; because BASIC routines never refer to DGROUP, you
    can ignore this caution for programs written entirely in BASIC.

    To avoid this problem, you can use the value of SS, since BASIC always
    assumes that SS coincides with DGROUP.

G.4.6  Using LINK Options

    LINK options begin with the linker's option character, which is the
    forward slash (/).

    Case is not significant in LINK options; for example, /NOI and /noi are
    equivalent.

    You can abbreviate LINK options to save space and effort. The minimum
    valid abbreviation for each option is indicated in the syntax of the
    option. For example, several options begin with the letters "NO";
    therefore, abbreviations for those options must be longer than "NO" to be
    unique. You cannot use "NO" as an abbreviation for the /NOIGNORECASE
    option, since LINK cannot tell which of the options beginning with "NO"
    you intend. The shortest valid abbreviation for this option is /NOI.

    Abbreviations must begin with the first letter of the option and must be
    continuous through the last letter typed. No gaps or transpositions are
    allowed.

    Some LINK options take numeric arguments. A numeric argument can be any of
    the following:

    ■ A decimal number from 0 to 65,535.

    ■ An octal number from 0 to 0177777. A number is interpreted as octal if
    it starts with a zero (0). For example, the number 10 is a decimal
    number, but the number 010 is an octal number, equivalent to 8 in
    decimal.

    ■ A hexadecimal number from 0 to 0xFFFF. A number is interpreted as
    hexadecimal if it starts with a zero followed by an x or an X. For
    example, 0x10 is a hexadecimal number, equivalent to 16 in decimal.

    LINK options affect all files in the linking process, regardless of where
    the options are specified.

    If you usually use the same set of LINK options, you can use the LINK
    environment variable in DOS to specify certain options each time you link.
    If you set this variable, LINK checks it for options and expects to find
    options listed exactly as you would type them on the command line. You
    cannot specify file-name arguments in the LINK environment variable.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    A command-line option overrides the effect of any environment-variable
    option with which it conflicts. For example, the command-line option
    /SE:256 cancels the effect of the environment-variable option /SE:512.
    To prevent an option in the environment variable from being used, you
    must reset the environment variable itself.
    ──────────────────────────────────────────────────────────────────────────

    Example

    In the following example, the file TEST.OBJ is linked with the options
    /SE:256 and /CO. After that, the file PROG.OBJ is linked with the option
    /NOD, as well as with the options /SE:256 and /CO.

    SET LINK=/SE:256 /CO
    LINK TEST;
    LINK /NOD PROG;

    G.4.6.1  Viewing the Options List (/HE)

    /HE[[LP]]

    The /HE option tells LINK to display a list of the available LINK options
    on the screen.

    G.4.6.2  Pausing during Linking (/PAU)

    /PAU[[SE]]

    The /PAU option tells LINK to pause in the link session and display a
    message before it writes the executable file to disk. This allows you to
    insert a new disk to hold the executable file.

    If you specify the /PAUSE option, LINK displays the following message
    before it creates the executable file:

    About to generate .EXE file
    Change diskette in drive letterand press <ENTER>

    The letter corresponds to the current drive. LINK resumes processing when
    you press the ENTER key.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Do not remove the disk on which the list file is created or the disk
    used for the temporary file. If a temporary file is created on the disk
    you plan to swap, press CTRL+C to terminate the linking session.
    Rearrange your files so that the temporary file and the executable file
    can be written to the same disk. Then try linking again.
    ──────────────────────────────────────────────────────────────────────────

    G.4.6.3  Displaying Linker Process Information (/I)

    /I[[NFORMATION]]

    The /I option displays information about the linking process, including
    the phase of linking and the names of the object files being linked.

    This option helps you determine the locations of the object files being
    linked and the order in which they are linked.

    G.4.6.4  Preventing Linker Prompting (/B)

    /B[[ATCH]]

    The /B option tells LINK not to prompt you for a new path name whenever it
    cannot find a library or object file that it needs. When this option is
    used, the linker simply continues to execute without using the file in
    question.

    This option can cause unresolved external references. It is intended
    primarily to let you use batch or MAKE files to link many executable files
    with a single command if you do not want LINK to stop processing if it
    cannot find a required file. It is also useful when you are redirecting
    the LINK command line to create a file of linker output for future
    reference. However, this option does not prevent LINK from prompting for
    arguments missing from the LINK command line.

    G.4.6.5  Creating Quick Libraries (/Q)

    /Q[[UICKLIB]]

    The /Q option tells LINK to combine the object files you specify into a
    Quick library. When you start the QuickBASIC environment, you can give the
    /L option on the QB command line to load the Quick library. If you use the
    /Q option, be sure to specify BQLB45.LIB in the library list in order to
    include QuickBASIC Quick-library support routines.

    See Appendix H, "Creating and Using Quick Libraries," for more
    information about creating and loading Quick libraries.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    You cannot use the /EXEPACK option with the /Q option.
    ──────────────────────────────────────────────────────────────────────────

    G.4.6.6  Packing Executable Files (/E)

    /E[[XEPACK]]

    The /E option removes sequences of repeated bytes (typically null
    characters) and optimizes the "load-time relocation table" before creating
    the executable file. The load-time relocation table is a table of
    references relative to the start of the program. Each reference changes
    when the executable image is loaded into memory and an actual address for
    the entry point is assigned.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Executable files linked with this option may be smaller and load faster
    than files linked without this option.
    ──────────────────────────────────────────────────────────────────────────

    G.4.6.7  Disabling Segment Packing (/NOP)

    /NOP[[ACKCODE]]

    The /NOP option is normally not necessary because code-segment packing is
    normally turned off. However, if a DOS environment variable such as LINK
    turns on code-segment packing automatically, you can use the /NOP option
    to turn segment packing back off again.

    G.4.6.8  Ignoring the Usual BASIC Libraries (/NOD)

    /NOD[[EFAULTLIBRARYSEARCH]]

    When it creates an object file, BC includes the names of the "standard"
    libraries──libraries that LINK searches to resolve external references.
    The /NOD option tells LINK not to search any library specified in an
    object file to resolve external references.

    In general, QuickBASIC programs do not work correctly without the standard
    QuickBASIC libraries (BRUN45.LIB and BCOM45.LIB). Thus, if you use the
    /NOD option, you should explicitly give the path name of the required
    standard library.

    G.4.6.9  Ignoring Dictionaries (/NOE)

    /NOE[[XTDICTIONARY]]

    If LINK suspects that a public symbol has been redefined, it prompts you
    to link again with the /NOE option. When you do so, it searches the
    individual object files, rather than "dictionaries" it has created, to
    resolve conflicts. For example, when linking a program with 87.LIB or
    NOCOM.OBJ, you must use the /NOE option.

    G.4.6.10  Setting Maximum Number of Segments (/SE)

    /SE[[GMENTS]]:number

    The /SE option controls the number of segments that LINK allows a program
    to have. The default is 128, but you can set number to any value
    (specified as decimal, octal, or hexadecimal) in the range 1-1024
    (decimal).

    For each segment, LINK must allocate space to keep track of segment
    information. When you set the segment limit higher than 128, LINK
    allocates more space for segment information. For programs with fewer than
    128 segments, you can minimize the amount of storage LINK needs by setting
    number to reflect the actual number of segments in the program. LINK
    displays an error message if this number is too high for the amount of
    memory it has available.

    G.4.6.11  Creating a Map File (/M)

    /M[[AP]]

    The /M option creates a map file. A map file lists the segments of a
    program and the program's public symbols. LINK always tries to allocate
    all of the available memory for sorting public symbols. If the number of
    symbols exceeds the memory limit, then LINK generates an unsorted list.
    The map file mapfile contains a list of symbols sorted by address;
    however, it does not contain a list sorted by name. A sample map file is
    shown below:

    Start  Stop   Length Name                   Class
    00000H 01E9FH 01EA0H _TEXT                  CODE
    01EA0H 01EA0H 00000H C_ETEXT                ENDCODE
    .
    .
    .

    The information in the columns Start and Stop shows the 20-bit address (in
    hexadecimal) of each segment, relative to the beginning of the load
    module. The load module begins at location zero. The column Length gives
    the length of the segment in bytes. The column Name gives the name of the
    segment; the column Class gives information about the segment type. See
    the Microsoft MS-DOS Programmer's Reference for information about groups,
    segments, and classes.

    The starting address and name of each group appear after the list of
    segments. A sample group listing is shown below:

    Origin   Group
    01EA:0   DGROUP

    In this example, DGROUP is the name of the data group.

    The map file shown below contains two lists of global symbols: the first
    list is sorted in ASCII-character order by symbol name; the second, by
    symbol address. The notation Abs appears next to the names of absolute
    symbols (symbols containing 16-bit constant values that are not associated
    with program addresses).

    Many of the global symbols that appear in the map file are symbols used
    internally by the compiler and linker. These symbols usually begin with
    the characters B$ or end with QQ.

    Address         Publics by Name

    01EA:0096       STKHQQ
    0000:1D86       B$Shell
    01EA:04B0       _edata
    01EA:0910       _end
    .
    .
    .
    01EA:00EC       __abrkp
    01EA:009C       __abrktb
    01EA:00EC       __abrktbe
    0000:9876  Abs  __acrtmsg
    0000:9876  Abs  __acrtused
    .
    .
    .
    01EA:0240       ___argc
    01EA:0242       ___argv

    Address         Publics by Value

    0000:0010       _main
    0000:0047       _htoi
    .
    .
    .

    The addresses of the external symbols are in the frame:offset format,
    showing the location of the symbol relative to zero (the beginning of the
    load module).

    Following the lists of symbols, the map file gives the program entry
    point, as shown in the following example:

    Program entry point at 0000:0129

    A map file can also be specified by giving a map-file name on the LINK
    command line or by giving a map-file name in response to the "List File"
    prompt.

    G.4.6.12  Including Line Numbers in a Map File (/LI)

    /LI[[NENUMBERS]]

    The /LI option creates a map file and includes the line numbers and
    associated addresses of the source program. If you are compiling and
    linking in separate steps, this option has an effect only if you are
    linking object files compiled with the /M option.

    G.4.6.13  Packing Contiguous Segments (/PAC)

    /[[NO]]PAC[[KCODE]][[:number]]

    The /PAC option tells LINK to group neighboring code segments. Code
    segments in the same group share the same segment address; all offset
    addresses are then adjusted upward as needed. As a result, many
    instructions that would otherwise have different segment addresses share
    the same segment address.

    If specified, number is the size limit of groups formed by /PAC. LINK
    stops adding segments to a particular group as soon as it cannot add a
    segment to the group without exceeding number. At that point, LINK starts
    forming a new group with the remaining code segments. If number is not
    given, the default is 65,530.

    Although LINK does not pack neighboring segments unless you explicitly ask
    for it, you can use the /NOPACKCODE option to turn off segment packing if,
    for example, you have given the /PAC option in the LINK environment
    variable in DOS.

    G.4.6.14  Using the CodeView Debugger (/CO)

    /CO[[DEVIEW]]

    The /CO option prepares an executable file for debugging using the
    CodeView debugger. If you are compiling and linking in separate steps,
    this option has an effect only if you are linking object files compiled
    with the /ZI option of the BC command. Similarly, it should not be used in
    conjunction with the LINK command's /Q option, because a Quick library
    cannot be debugged with the CodeView debugger.

    G.4.6.15  Distinguishing Case (/NOI)

    /NOI[[GNORECASE]]

    The /NOI option tells LINK to distinguish between uppercase and lowercase
    letters; for example, LINK would consider the names ABC, abc, and Abc to
    be three separate names. When you link, do not specify the /NOI option on
    the LINK command line.

G.4.7  Other LINK Command-Line Options

    Not all options of the LINK command are suitable for use with QuickBASIC
    programs. The following LINK options can be used with Microsoft QuickBASIC
    programs; however, they are never required, since they request actions
    that the BC command or QuickBASIC performs automatically:

╓┌─┌─────────────────────────────┌───────────────────────────────────────────╖
    Option                        Action
    ──────────────────────────────────────────────────────────────────────────
    /CP[[ARMAXALLOC]]:number      Sets the maximum number of 16-byte
                                paragraphs needed by the program when it is
                                loaded into memory to number, an integer
                                between 1 and 65,535, inclusive. The
                                operating system uses this value when
                                allocating space for the program before
                                loading it. Although you can use this option
                                on the LINK command line, it has no effect
                                because, while it is running, your BASIC
                                program controls memory.

    /DO[[SSEG]]                   Forces segments to be ordered using the
                                defaults for Microsoft high-level language
                                products. QuickBASIC programs always use
                                this segment order by default.

    /ST[[ACK]]:number             Specifies the size of the stack for your
    Option                        Action
    ──────────────────────────────────────────────────────────────────────────
    /ST[[ACK]]:number             Specifies the size of the stack for your
                                program, where number is any positive value
                                (decimal, octal, or hexadecimal) up to
                                65,535 (decimal) representing the size, in
                                bytes, of the stack. The standard BASIC
                                library sets the default stack size to 2K.

    /DS[[ALLOCATE]]               Loads all data starting at the high end of
                                the default data segment.

    /HI[[GH]]                     Places the executable file as high in memory
                                as possible.

    /NOG[[ROUPASSOCIATION]]       Tells LINK to ignore group associations when
                                assigning addresses to data and code items.

    /O[[VERLAYINTERRUPT]]:number  Specifies an interrupt number other than
                                0x3F for passing control to overlays.
    ──────────────────────────────────────────────────────────────────────────
    Option                        Action
    ──────────────────────────────────────────────────────────────────────────
    ──────────────────────────────────────────────────────────────────────────


    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Do not use the /DS, /HI, /NOG, or /O options when linking object files
    compiled with BC. They are suitable only for object files created by the
    Microsoft Macro Assembler (MASM).
    ──────────────────────────────────────────────────────────────────────────


G.5  Managing Stand-Alone Libraries: LIB

    The Microsoft Library Manager (LIB) manages the contents of stand-alone
    libraries. A stand-alone library is made up of "object modules"──that is,
    object files combined to form a library. Unlike an object file, an object
    module does not exist independently of its library, and does not have a
    path name or extension associated with its file name. Using LIB, you can:

    ■ Combine object files to create a new library

    ■ Add object files to an existing library

    ■ Delete or replace the object modules of an existing library

    ■ Extract object modules from an existing library and place them in
    separate object files

    ■ Combine the contents of two existing libraries into a new library

    When updating an existing library, LIB performs all of its operations on a
    copy of the library. This mechanism ensures that you have a backup copy of
    any library you update in case of problems with the updated version of the
    library.

G.5.1  Running LIB

    You can give the LIB command input in any of the following ways:

    ■ Specify the input on a command line of the following form:

    LIB oldlib [[/P[[AGESIZE]]:number]]
    [[commands]][[,[[listfile]][[,[[newlib]]]]]][[;]]

    The command line has a maximum length of 128 characters.

    ■ Type

    lib

    and respond to the following prompts:

    Library name:
    Operations:
    List file:
    Output library:

    To give more files for any prompt, type an ampersand (&) at the end of
    the line. The prompt reappears on the next line, and you can continue
    typing input for the prompt.

    ■ Set up a response file, a file with responses to LIB command prompts,
    then type a LIB command of the following form:

    LIB @filename

    Here, filename is the name of the response file. The responses must be in
    the same order as the LIB prompts discussed above. You can also enter the
    name of a response file after any LINK prompt, or at any position in the
    LIB command line.

    Table G.3 shows the input you must give on the LIB command line, or in
    response to each prompt. If you are using a response file, each response
    must follow the rules outlined in this table.

    Table G.3  Input to the LIB Command
╓┌─┌───────────┌────────────────────────┌────────────────────────────────────╖
    Field       Prompt                   Input
    ──────────────────────────────────────────────────────────────────────────
    oldlib      "Library name"           Name of the library you are changing
                                        or creating. If this library does not
                                        exist, LIB asks if you want to create
    Field       Prompt                   Input
    ──────────────────────────────────────────────────────────────────────────
                                        exist, LIB asks if you want to create
                                        it. Type the letter y to create a new
                                        library or the letter n to terminate
                                        LIB. This message is suppressed if
                                        you type command characters, a comma,
                                        or a semicolon after the library
                                        name. A semicolon tells LIB to
                                        perform a consistency check on the
                                        library; in this case, it displays a
                                        message if it finds errors in any
                                        library module.

    /P:number   /P:number after "Library The library page size. This sets the
                name" prompt             page size for the library to number,
                                        where number is an integer power of 2
                                        between 16 and 32,768, inclusive. The
                                        default page size for a new library
                                        is 16 bytes. Modules in the library
                                        are always aligned to start at a
    Field       Prompt                   Input
    ──────────────────────────────────────────────────────────────────────────
                                        are always aligned to start at a
                                        position that is a multiple of the
                                        page size (in bytes) from the
                                        beginning of the file.

    /I          None                     Tells LIB to ignore case when
                                        comparing symbols (default). Use when
                                        combining with libraries that are
                                        case sensitive.

    /NOE        None                     Tells LIB not to generate an extended
                                        dictionary.

    /NOI        None                     Tells LIB to compare case when
                                        comparing symbols (LIB remains case
                                        sensitive).

    commands    "Operations"             Command symbols and object files that
                                        tell LIB what changes to make in the
    Field       Prompt                   Input
    ──────────────────────────────────────────────────────────────────────────
                                        tell LIB what changes to make in the
                                        library.

    listfile    "List file"              Name of a cross-reference-listing
                                        file. No listing file is created if
                                        you do not give a file name.

    newlib      "Output library"         Name of the changed library that LIB
                                        creates as output. If you do not give
                                        a new library name, the original,
                                        unchanged library is saved in a
                                        library file with the same name but
                                        with a .BAK extension replacing the
                                        .LIB extension.
    ──────────────────────────────────────────────────────────────────────────


G.5.2  Usual Responses for LIB

    LIB has its own built-in (default) responses. You can choose these usual
    responses for any of the information that LIB needs, in any the following
    ways:

    ■ To choose the default for any command-line entry, omit the file name or
    names before the entry and type only the required comma. The only
    exception to this is the default for the listfile entry: if you omit
    this entry, LIB creates a cross-reference-listing file.

    ■ To choose the default for any prompt, press ENTER.

    ■ To choose the defaults for all command-line entries or prompts that
    follow an entry or prompt, type a semicolon (;) after that entry or
    prompt. The semicolon should be the last character on the command line.

    The following list shows the defaults that LIB uses for
    cross-reference-listing files and output libraries:

    File               Default
    ──────────────────────────────────────────────────────────────────────────
    Cross-reference    The special file name NUL, which tells the linker not
    listing            to create a cross-reference-listing file

    Output library     The oldlib entry or the response to the "Library name"
                        prompt
    ──────────────────────────────────────────────────────────────────────────

G.5.3  Cross-Reference-Listing Files

    A cross-reference-listing file tracks which routines are contained in a
    stand-alone library and the original object files they came from. A
    cross-reference-listing file contains the following lists:

    ■ An alphabetical list of all public symbols in the library. Each symbol
    name is followed by the name of the module in which it is defined.

    ■ A list of the modules in the library. Under each module name is an
    alphabetical listing of the public symbols defined in that module.

G.5.4  Command Symbols

    To tell LIB what changes you want to make to a library, type a command
    symbol such as +, -, -+, *, or -*, followed immediately by a module name,
    object-file name, or library name. You can specify more than one
    operation, in any order.

    The following list shows each LIB command symbol, the type of file name to
    specify with the symbol, and what the symbol does:

╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
    Command                  Meaning
    ──────────────────────────────────────────────────────────────────────────
    +{objfile | lib}         Adds the given object file to the input library
                            and makes that object file the last module in the
                            library, if given with an object-file name. You
                            can use a path name for the object file name.
                            Since LIB automatically supplies the .OBJ
                            extension, you can omit the extension from the
                            object-file name.

                            If given with a library name, the plus sign (+)
                            adds the contents of that library to the input
                            library. The library name must have the .LIB
    Command                  Meaning
    ──────────────────────────────────────────────────────────────────────────
                            library. The library name must have the .LIB
                            extension.

    -module                  Deletes the given module from the input library.
                            A module name does not have a path name or an
                            extension.

    -+module                 Replaces the given module in the input library.
                            Module names have no path names and no
                            extensions. LIB deletes the given module, then
                            appends the object file that has the same name as
                            the module. The object file is assumed to have an
                            .OBJ extension and to reside in the current
                            working directory.

    *module                  Copies the given module from the library to an
                            object file in the current working directory. The
                            module remains in the library file. When LIB
                            copies the module to an object file, it adds the
    Command                  Meaning
    ──────────────────────────────────────────────────────────────────────────
                            copies the module to an object file, it adds the
                            .OBJ extension. You cannot override the .OBJ
                            extension, drive designation, or path name given
                            to the object file. However, you can later rename
                            the file or copy it to whatever location you
                            like.

    -*module                 Moves the given object module from the library to
                            an object file. This operation is equivalent to
                            copying the module to an object file, as
                            described above, then deleting the module from
                            the library.
    ──────────────────────────────────────────────────────────────────────────


    Examples

    The example below uses the replace command symbol (-+) to instruct LIB to
    replace the HEAP module in the library LANG.LIB. LIB deletes HEAP from the
    library, then appends the object file HEAP.OBJ as a new module in the
    library. The semicolon at the end of the command line tells LIB to use the
    default responses for the remaining prompts. This means that no listing
    file is created and that the changes are written to the original library
    file instead of creating a new library file.

    LIB LANG-+HEAP;

    The examples below perform the same function as the first example in this
    section, but in two separate operations, using the add (+) and delete (-)
    command symbols. The effect is the same for these examples because delete
    operations are always carried out before add operations, regardless of the
    order of the operations in the command line. This order of execution
    prevents confusion when a new version of a module replaces an old version
    in the library file.

    LIB LANG-HEAP+HEAP;

    LIB LANG+HEAP-HEAP;

    The example below causes LIB to perform a consistency check of the library
    file FOR.LIB. No other action is performed. LIB displays any consistency
    errors it finds and returns to the operating-system level.

    LIB FOR;

    The following example tells LIB to perform a consistency check on the
    library file LANG.LIB and then create the cross-reference-listing file
    LCROSS.PUB.

    LIB LANG,LCROSS.PUB

    The next example instructs LIB to move the module STUFF from the library
    FIRST.LIB to an object file called STUFF.OBJ. The module STUFF is removed
    from the library in the process. The module MORE is copied from the
    library to an object file called MORE.OBJ; the module remains in the
    library. The revised library is called SECOND.LIB. It contains all the
    modules in the library FIRST.LIB except STUFF, which was removed by using
    the move command symbol (-*). The original library, FIRST.LIB, remains
    unchanged.

    LIB FIRST -*STUFF *MORE, ,SECOND

    The contents of the response file below cause LIB to delete the module
    HEAP from the LIBFOR.LIB library file, extract (without deleting) FOIBLES
    and place it in an object file named FOIBLES.OBJ, and append the object
    files CURSOR.OBJ and HEAP.OBJ as the last two modules in the library.
    Finally, LIB creates the cross-reference-listing file CROSSLST.

    LIBFOR
    +CURSOR+HEAP-HEAP*FOIBLES
    CROSSLST

G.5.5  LIB Options

    LIB has four options. Specify options on the command line following the
    required library-file name and preceding any commands.

    G.5.5.1  Ignoring Case for Symbols

    /I[[GNORECASE]]

    The /I option tells LIB to ignore case when comparing symbols, as LIB does
    by default. Use this option when you are combining a library that is
    marked /NOI (described below) with others that are unmarked and you want
    the new library to be unmarked.

    G.5.5.2  Ignoring Extended Dictionaries

    /NOE[[XTDICTIONARY]]

    The /NOE option tells LIB not to generate an extended dictionary. The
    extended dictionary is an extra part of the library that helps the linker
    process libraries faster.

    Use the /NOE option if you get errors U1171 or U1172, or if the extended
    dictionary causes problems with LINK. See Section G.4.6.9 for more
    information on how LINK uses the extended dictionary.

    G.5.5.3  Distinguishing Case for Symbols

    /NOI[[GNORECASE]]

    The /NOI option tells LIB not to ignore case when comparing symbols; that
    is, /NOI makes LIB case sensitive. By default, LIB ignores case. Using
    this option allows symbols that are the same except for case, such as
    SPLINE and Spline, to be put in the same library.

    Note that when you create a library with the /NOI option, LIB marks the
    library internally to indicate that /NOI is in effect. Earlier version of
    LIB did not mark libraries in this way. If you combine multiple libraries,
    and any one of them is marked /NOI, then /NOI is assumed to be in effect
    for the output library.

    G.5.5.4  Setting Page Size

    /P[[AGESIZE]]:number

    The page size of a library affects the alignment of modules stored in the
    library. Modules in the library are always aligned to start at a position
    that is a multiple of the page size (in bytes) from the beginning of the
    file. The default page size for a newly created library is 16 bytes.

    You can set a different library page size while you are creating a library
    or change the page size of an existing library by adding the following
    option after the oldlib entry on the LIB command line or after the name
    you type in response to the "Library name" prompt:

    The number specifies the new library page size. It must be an integer
    value representing a power of 2 between the values 16 and 32,768.

    The library page size determines the number of modules the library can
    hold. Thus, increasing the page size allows you to include more modules in
    the library. However, the larger the page size, the larger the amount of
    wasted storage space in the library (on the average, pagesize/2 bytes). In
    most cases you should use a small page size unless you need to put a very
    large number of modules in a library.

    The page size also determines the maximum possible size of the library.
    This limit is number * 65,536. For example, if you invoke LIB with the
    option /P:16, the library must be smaller than one megabyte (16 * 65,536
    bytes).



────────────────────────────────────────────────────────────────────────────
Appendix H  Creating and Using Quick Libraries

    This appendix describes how to create and maintain libraries from within
    the QuickBASIC programming environment. A library is a file containing the
    contents of several modules under a single file name. When you finish this
    appendix you will know how to:

    ■ Make libraries from within the QuickBASIC environment

    ■ Load a Quick library when running a QuickBASIC program

    ■ View the contents of a Quick library

    ■ Add routines written in other languages to a Quick library


H.1  Types of Libraries

    QuickBASIC provides tools for creating two different types of libraries,
    which are identified by different file-name extensions:

    Extension                Function
    ──────────────────────────────────────────────────────────────────────────
    .QLB                     The .QLB extension characterizes a Quick library,
                            a special kind of library that permits easy
                            addition of frequently used procedures to your
                            programs. A Quick library can contain procedures
                            written in QuickBASIC or other Microsoft
                            languages such as Microsoft C.

    .LIB                     The .LIB extension characterizes a stand-alone
                            (.LIB) library, one that is created with the
                            Microsoft Library Manager, LIB. When QuickBASIC
                            makes a Quick library, it simultaneously creates
                            a .LIB library containing the same procedures in
                            a somewhat different form.
    ──────────────────────────────────────────────────────────────────────────

    You can generate both types of libraries from within the programming
    environment or from the command line. You can think of a Quick library as
    a group of procedures appended to QuickBASIC when the library is loaded
    with QuickBASIC. Libraries with the .LIB extension are essentially
    independent, compiled procedures. They can either be added to a Quick
    library or linked with a main module to create a file that is executable
    from the DOS command line.

    This appendix discusses the use of command-line utilities for some common
    cases, but you should refer to Appendix G, "Compiling and Linking from
    DOS," for a full explanation of using those utilities.


H.2  Advantages of Quick Libraries

    Quick libraries facilitate program development and maintenance. As
    development progresses on a project and modules become stable components
    of your program, you can add them to a Quick library, then set aside the
    source files for the original modules until you want to improve or
    maintain those source files. Thereafter you can load the library along
    with QuickBASIC, and your program has instant access to all procedures in
    the library.

    Procedures in a Quick library behave like QuickBASIC's own statements. If
    properly declared, a SUB procedure in a Quick library can even be invoked
    without a CALL statement. See Chapter 2, "SUB and FUNCTION Procedures,"
    for more information on calling a SUB procedure with or without the CALL
    keyword.

    Procedures in a Quick library can be executed directly from the Immediate
    window, just like BASIC statements. This means that you can test their
    effects before using them in other programs.

    If you codevelop programs with others, Quick libraries make it easy to
    update a pool of common procedures. If you wish to offer a library of
    original procedures for commercial distribution, all QuickBASIC
    programmers will be able to use them immediately to enhance their own
    work. You could leave your custom Quick library on a bulletin board for
    others to try before purchasing. Because Quick libraries contain no source
    code and can only be used within the QuickBASIC programming environment,
    your proprietary interests are protected while your marketing goals are
    advanced.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Quick libraries have the same function as user libraries in QuickBASIC
    Versions 2.0 and 3.0. However, you cannot load a user library as a Quick
    library. You must recreate the library from the original source code, as
    described below.
    ──────────────────────────────────────────────────────────────────────────


H.3  Creating a Quick Library

    A Quick library automatically contains all modules, both main and nonmain,
    present in the QuickBASIC environment when you create the new library. It
    also contains the contents of any other Quick library that you loaded when
    starting QuickBASIC. If you load a whole program but only want certain
    modules to be put in the library, you must explicitly unload those you
    don't want. You can unload modules with the File menu's Unload File
    command.

    You can quickly determine which modules are loaded by checking the list
    box of the SUBs command on the View menu. However, this method does not
    show which procedures a loaded library contains. The QLBDUMP.BAS utility
    program, described in Section H.4.3, "Viewing the Contents of a Quick
    Library," allows you to list all the procedures in a library.

    Only whole modules can be put into a library. That is, you cannot select
    one procedure from among many in a module. If you want to enter only
    certain procedures from a module, put the procedures you want in a
    separate module, then put that module into a library.

    A Quick library must be self-contained. A procedure in a Quick library can
    only call other procedures within the same Quick library. Procedure names
    must be unique within the library.

    With large programs, you can reduce loading time by putting as many
    routines as possible in Quick libraries. Putting many routines in Quick
    libraries is also an advantage if you plan to make the program into a
    stand-alone executable file later, since the contents of libraries are
    simply linked without recompiling.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    Your main module may or may not contain procedures. If it does and you
    incorporate those procedures in the library, the entire main module goes
    in the library, too. This does not cause an error message, but the
    module-level code in the library can never be executed unless one of its
    procedures contains a routine (such as ON ERROR) that explicitly passes
    control to the module level. Even if that is the case, much of the
    module-level code may be extraneous. If you organize your procedures in
    modules that are frequently used together, your Quick libraries are
    likely to be less cluttered with useless code.
    ──────────────────────────────────────────────────────────────────────────

H.3.1  Files Needed to Create a Quick Library

    To create a Quick library, make sure that the proper files are available
    before you begin. If you don't have a hard disk, you should keep your
    files and the other programs on several floppy disks. QuickBASIC prompts
    you for a path name when it cannot find a file; when this happens, insert
    the correct disk and respond to the prompt.

    Make sure that the following files are in the current working directory or
    accessible to QuickBASIC through the appropriate DOS environment
    variables:

    File                     Purpose
    ──────────────────────────────────────────────────────────────────────────
    QB.EXE                   Directs the process of creating a Quick library.
                            If you are working only with QuickBASIC modules,
                            you can do everything in one step from within the
                            QuickBASIC environment.

    BC.EXE                   Creates object files from source code.

    LINK.EXE                 Links object files.

    LIB.EXE                  Manages stand-alone libraries of object modules.

    BQLB45.LIB               Supplies routines needed by your Quick library.
                            This library is a stand-alone library that is
                            linked with objects in your library to form a
                            Quick library.
    ──────────────────────────────────────────────────────────────────────────

H.3.2  Making a Quick Library

    Most of the time you create Quick libraries from within the QuickBASIC
    environment. Occasionally, you may want to update a library or include
    routines from other Microsoft languages in your Quick library. In these
    cases, begin by constructing a base library of the non-BASIC routines from
    outside the environment by invoking LINK and LIB directly. Then you can
    add the most current QuickBASIC modules from within QuickBASIC.

H.3.3  Making a Quick Library from within the Environment

    When making a library from within the QuickBASIC environment, the first
    consideration is whether the library to be made is totally new or an
    update of an existing library. If it is to be an update, you should start
    QuickBASIC with the /L command-line option, supplying the name of the
    library to be updated as a command-line argument. At the same time, you
    can also include the name of a program whose modules you want to put in
    the library. In this case QuickBASIC loads all the modules specified in
    that program's .MAK file.

    H.3.3.1  Unloading Unwanted Files

    If you load your program when starting QuickBASIC, be sure to unload any
    modules you don't want in the Quick library, including the main module
    (unless it contains procedures you want in the library).

    Follow these steps to unload modules:

    1. Choose the Unload File command from the File menu.

    2. Select the module you want to unload from the list box, then press
        ENTER.

    3. Repeat steps 1 and 2 until you have unloaded all unwanted modules.

    H.3.3.2  Loading Desired Files

    Alternatively, you can simply start QuickBASIC, with or without a library
    specification, and load the modules you want one at a time from within the
    environment. In this case, you load each module using the Load File
    command from the File menu.

    To load one module at a time with QuickBASIC:

    1. Choose the File menu's Load File command.

    2. Select the name of a module you want to load from the list box.

    3. Repeat steps 1 and 2 until all you have loaded all the modules you
        want.

    H.3.3.3  Creating a Quick Library

    Once you have loaded the previous library (if any) and all the new modules
    you want to include in the Quick library, choose the Make Library command
    from the Run menu. The dialog box shown in Figure H.1 appears.


                                Type Quick-library file name here.
                                │
    ┌────────────────────────────┼─Make Library───────────────────────────┐
    │                          ┌─┼─────────────────────────────────────┐  │
    │ Quick-Library File Name: │                                       │  │
    │                          └───────────────────────────────────────┘  │
    │ [X] Produce Debug Code                                              │
    ├─────────────────────────────────────────────────────────────────────┤
    │ < Make Library >   <Make Library and Exit >   < Cancel >   < Help > │
    └───────────────────────────────┼─────────────────────────────────────┘
                                    │
                                    │Makes a library
                                    and exits to DOS

    Figure H.1 Make Library Dialog Box

    To create a Quick library, perform the following steps:

    1. Enter the name of the library you wish to create in the Quick-Library
        File Name text box.

        If you enter only a base name (that is, a file name with no extension),
        QuickBASIC automatically appends the extension .QLB when it creates the
        library. If you want your library to have no extension, add a
        terminating period (.) to the base name. Otherwise, you may enter any
        base name and extension you like (except the name of a loaded Quick
        library), consistent with DOS file-name rules.

    2. Select the Produce Debug Code check box only if you are specifically
        trying to track a bug you believe to be in a library that you are
        updating. It makes your library larger, slows program execution and
        gives only a small amount of error control, mostly in regard to
        checking of array bounds.

    3. Create the Quick library:

        ■ Choose the Make Library command button if you want to remain in the
        environment after the Quick library is created.

        ■ Choose the Make Library and Exit command button if you want to return
        to the DOS command level after the Quick library is created.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    When you make a Quick library, be aware that if it is ever to be used
    with a nonlibrary module that needs to trap events such as keystrokes,
    then one of the modules in the library must contain at least one
    event-trapping statement. This statement can be as simple as TIMER OFF,
    but without it events are not trapped correctly in the Quick library.
    ──────────────────────────────────────────────────────────────────────────


H.4  Using Quick Libraries

    This section explains how to load a Quick library when you start
    QuickBASIC and how to view the contents of a Quick library. It also gives
    facts that you should remember when procedures within a Quick library
    perform floating-point arithmetic.

H.4.1  Loading a Quick Library

    To load a Quick library, you must specify the name of the desired library
    on the command line when you start QuickBASIC using the following syntax:

    QB[[programname]] /L [[libraryname]]

    If you start QuickBASIC with the /L option and supply the name of a
    library (libraryname), QuickBASIC loads the specified library and places
    you in the programming environment. The contents of the library are now
    available for use. If you start QuickBASIC with the /L option but don't
    specify a library, QuickBASIC loads the library QB.QLB (see Section H.5).

    You can also start QuickBASIC with the /RUN option followed by both a
    program name (programname) and the /L option. In this case, QuickBASIC
    loads both the program and the specified Quick library, then runs the
    program without stopping at the programming environment.

    ──────────────────────────────────────────────────────────────────────────
    NOTE
    When using Quick libraries to represent program modules, remember to
    update the .MAK file to keep it consistent with the modules in the
    evolving program. (This is done with the Unload File command from the
    File menu.) If the .MAK file is not up to date, it may cause QuickBASIC
    to load a module containing a procedure definition with the same name as
    one defined in the Quick library, which in turn causes the error message
    Duplicate definition.
    ──────────────────────────────────────────────────────────────────────────

    You can load only one Quick library at a time. If you specify a path,
    QuickBASIC looks where you indicate; otherwise, QuickBASIC searches for
    the Quick library in the following three locations:

    1. The current directory.

    2. The path specified for libraries by the Set Paths command.

    3. The path specified by the LIB environment variable. (See your DOS
        documentation for information about environment variables.)

    Example

    The following command starts QuickBASIC and runs the program REPORT.BAS
    using the routines in the library FIGS.QLB:

    QB /RUN REPORT.BAS /L FIGS.QLB

H.4.2  Floating-Point Arithmetic in Quick Libraries

    BASIC procedures within Quick libraries represent code compiled with the
    BC command-line compiler. These procedures share significant
    characteristics with executable files. For example, both executable files
    and Quick libraries perform floating-point arithmetic faster and with a
    higher degree of accuracy than the same calculations performed within the
    QuickBASIC environment. For more information, see Chapter 16, "The Run
    Menu," in Learning to Use Microsoft QuickBASIC.

H.4.3  Viewing the Contents of a Quick Library

    Because a Quick library is essentially a binary file, you cannot view its
    contents with a text editor to find out what it contains. Your
    distribution disk includes the QLBDUMP.BAS utility, which allows you to
    list all the procedures and data symbols in a given library. Follow these
    steps to view the contents of a Quick library:

    1. Start QuickBASIC.

    2. Load and run QLBDUMP.BAS.

    3. Enter the name of the Quick library you wish to examine in response to
        the prompt. You do not need to include the .QLB extension when you type
        the file name; however, supplying the extension does no harm.

        If the specified file exists and it is a Quick library, the program
        displays a list of all the symbol names in the library. In this
        context, symbol names correspond to the names of procedures in the
        library.

    See Chapter 3, "File and Device I/O," for a commented listing of
    QLBDUMP.BAS.


H.5  The Supplied Library (QB.QLB)

    If you invoke QuickBASIC with the /L option, but do not supply a Quick
    library name, QuickBASIC automatically loads a library named QB.QLB,
    included with the QuickBASIC package. This file contains three routines,
    INTERRUPT, INT86OLD, and ABSOLUTE, that provide software-interrupt support
    for system-service calls and support for CALL ABSOLUTE. To use the
    routines in QB.QLB, you must specify it (or another library into which
    those routines have been incorporated) on the command line when you invoke
    QuickBASIC. If you wish to use these routines along with other routines
    that you have placed in libraries, make a copy of the QB.QLB library and
    use it as a basis for building a library containing all the routines you
    need.


H.6  The .QLB File-Name Extension

    The extension .QLB is just a convenient convention. You can use any
    extension for your Quick library files, or no extension at all. However,
    in processing the /L libraryname option, QuickBASIC assumes that the
    listed libraryname has the .QLB extension if no other extension is
    specified. If your Quick library has no extension, you must put a period
    after the Quick-library name (libraryname.) or QuickBASIC searches for a
    file with your base name and the .QLB extension.


H.7  Making a Library from the Command Line

    After making a library from within the QuickBASIC environment, you will
    notice the appearance of extra files with the extensions .OBJ and .LIB. In
    creating Quick libraries, QuickBASIC actually directs the work of three
    other programs, BC, LINK, and LIB, and then combines what they produce
    into both a Quick library and a stand-alone (.LIB) library. Once the
    process is complete, there is one object (.OBJ) file for each module in
    your Quick library and a single library (.LIB) file containing an object
    module for each object file. The files with the extension .OBJ are now
    extraneous and can be deleted. However, files with the extension .LIB are
    very important and should be preserved. These parallel libraries are the
    files QuickBASIC uses to create executable files of your programs.

    You can use the programs LINK and LIB to create both Quick libraries and
    stand-alone (.LIB) libraries from the command line in batch mode. If you
    want to use routines originally written and compiled in other languages in
    QuickBASIC, you must first put the other-language routines in a Quick
    library via the command line. Once the other-language routines are in the
    library, you can incorporate your BASIC modules from the command line or
    from within the QuickBASIC environment.

    Professional software developers should be sure to deliver both the Quick
    (.QLB) and stand-alone (.LIB) versions of libraries to customers. Without
    the .LIB libraries, end users would not be able to use your library
    routines in executable files produced with QuickBASIC.

    When you create a Quick library using LINK, the library BQLB45.LIB must
    always be specified after the third comma on the LINK command line or in
    response to the "Libraries" prompt.


H.8  Using Routines from Other Languages in a Quick Library

    To place routines from other languages in a Quick library, you must start
    with precompiled or preassembled object files that contain the
    other-language routines you wish to use. Several other languages are
    suitable for this purpose, including Microsoft C, Microsoft Macro
    Assembler, Microsoft Pascal, Microsoft FORTRAN, and any other language
    that creates object files compatible with the Microsoft language family.

H.8.1  Building a Quick Library

    The following is a typical scenario for building a Quick library
    containing routines from other languages:

    1. Suppose you begin with three modules, created in FORTRAN, C, and Macro
        Assembler. First you compile or assemble each module with the proper
        language translator to produce object files called here FOR.OBJ, C.OBJ,
        and ASM.OBJ.

    2. You then link the object files with the LINK option /Q, which instructs
        the linker to produce a Quick library file, as shown in the following
        command line:

        LINK /Q FOR.OBJ C.OBJ ASM.OBJ, MIXED.QLB,,BQLB45.LIB;

        The linker interprets the entry that follows the names of the object
        files (in this case MIXED.QLB) as the file name by which the linked
        modules will be known. Thus, in this case, the Quick library file is
        named MIXED.QLB.

    3. Now create a parallel .LIB library, using the same object files you
        just used to make the Quick library. In this case the first name
        following the LIB command is the name of the .LIB library:

        LIB MIXED.LIB+FOR.OBJ+C.OBJ+ASM.OBJ;

        It is easy to overlook this step when making a library that contains
        other-language routines, but this step is crucial if you hope to use
        the library to create a stand-alone executable file. Without these
        parallel stand-alone (.LIB) libraries, QuickBASIC cannot create an
        executable file containing their routines.

    4. With the other-language routines now in a Quick library and the
        original object files in a stand-alone library having the same base
        name, you can return to the QuickBASIC environment and build as many
        BASIC modules into the library as available memory permits.

    See Appendix G, "Compiling and Linking from DOS," for a complete
    description of the features of LINK and LIB.

H.8.2  Quick Libraries with Leading Zeros in the First Code Segment

    A Quick library containing leading zeros in the first code segment is
    invalid, causing the message Error in loading file filename - Invalid
    format when you try to load it in QuickBASIC. For example, this can occur
    if an assembly-language routine puts data that is initialized to zero in
    the first code segment and it is subsequently listed first on the LINK
    command line when you make a Quick library. If you have this problem, do
    one of the following two things:

    1. Link with a BASIC module first on the LINK command line.

    2. Make sure that, in whatever module comes first on the LINK command
        line, the first code segment starts with a nonzero byte.

H.8.3  The B_OnExit Routine

    QuickBASIC provides a BASIC system-level function, B_OnExit. You can use
    B_OnExit when your other-language routines take special actions that need
    to be undone before leaving the program (intentionally or otherwise) or
    rerunning the program. For example, within the QuickBASIC environment, an
    executing program that calls other-language routines in a Quick library
    may not always run to normal termination. If such routines need to take
    special actions at termination (for example, deinstallation of previously
    installed interrupt vectors), you can guarantee that your termination
    routines will always be called if you include an invocation of B_OnExit in
    the routine. The following example illustrates such a call (for
    simplicity, the example omits error-handling code). Note that such a
    function would be compiled in C in large model.

    #include <malloc.h>

    extern pascal far B_OnExit();  /* Declare the routine   */

    int *p_IntArray;

    void InitProc()
    {
        void TermProc();     /* Declare TermProc function    */

        /* Allocate far space for 20-integer array:          */

        p_IntArray = (int *)malloc(20*sizeof(int));

        /* Log termination routine (TermProc) with BASIC:    */

        B_OnExit(TermProc);
    }

                            /* The TermProc function is      */
    void TermProc()        /* called before any restarting  */
    {                      /* or termination of program.    */

        free(p_IntArray);   /* Release far space allocated   */
    }                      /* previously by InitProc.       */

    If the InitProc function were in a Quick library, the call to B_OnExit
    would insure proper release of the space reserved in the call to malloc,
    should the program crash. The routine could be called several times, since
    the program can be executed several times from the QuickBASIC environment.
    However, the TermProc function itself would be called only once each time
    the program runs.

    The following BASIC program is an example of a call to the InitProc
    function:

    DECLARE SUB InitProc CDECL

    X = SETMEM(-2048)       ' Make room for the malloc memory
                            ' allocation in C function.
    CALL InitProc
    END

    If more than 32 routines are registered, B_OnExit returns NULL, indicating
    there is not enough space to register the current routine. (Note that
    B_OnExit has the same return values as the Microsoft C run-time-library
    routine onexit.)

    B_OnExit can be used with any other-language (including assembly-language)
    routines you place in a Quick library. With programs compiled and linked
    completely from the command line, B_OnExit is optional.


H.9  Memory Considerations with Quick Libraries

    Because a Quick library is essentially an executable file (although it
    cannot be invoked by itself from the DOS command line), it is quite large
    in comparison to the sum of the sizes of its source files. This puts an
    upper limit on the number of routines you can put in a Quick library. To
    determine how large your Quick library can be, add up the memory required
    for DOS, QB.EXE, and your program's main module. An easy way to estimate
    these factors is to boot your machine, start QuickBASIC with your program,
    and enter this command in the Immediate window:

    PRINT FRE (-1)

    This command shows you the number of bytes of free memory. This indicates
    the maximum size for any Quick library associated with this program. In
    most cases the amount of memory required for a Quick library is about the
    same as the size of its disk file. One exception to this rule of thumb is
    a library with procedures that use a lot of strings; such a program may
    require somewhat more memory.


H.10  Making Compact Executable Files

    As discussed above, when QuickBASIC creates a Quick library, it also
    creates a stand-alone (.LIB) library of object modules in which each
    object module corresponds to one of the modules in the Quick library. When
    you make an executable file, QuickBASIC searches the stand-alone (.LIB)
    library for object modules containing the procedures referenced in the
    program.

    If an object module in the library does not contain procedures referenced
    in the program, it is not included in the executable file. However, a
    single module may contain many procedures, and if even one of them is
    referenced in the program all are included in the executable file.
    Therefore, even if a program uses only one of four procedures in a certain
    module, that entire module becomes part of the final executable file.

    To make your executable files as compact as possible, you should maintain
    a library in which each module contains only closely related procedures.
    If you have any doubts about what a library contains, list its contents
    with the utility QLBDUMP.BAS, described in Section H.4.3, "Viewing the
    Contents of a Quick Library."



────────────────────────────────────────────────────────────────────────────
Appendix I  Error Messages

    During development of a BASIC program with QuickBASIC, the following types
    of errors can occur:

    ■ Invocation errors

    ■ Compile-time errors

    ■ Link-time errors

    ■ Run-time errors

    Each type of error is associated with a particular step in the program
    development process. Invocation errors occur when you invoke QuickBASIC
    with the QB or BC commands. Compile-time errors (and warnings) occur
    during compilation, and run-time errors occur when the program is
    executing. Link-time errors occur only when you use the LINK command to
    link object files created with BC or other language compilers.

    Section I.2 lists alphabetically the invocation, compile-time, and
    run-time error messages, along with any error codes that are assigned.
    Table I.1 lists the run-time error messages and error codes in numerical
    order. Section I.3 lists the Microsoft Overlay Linker error messages, and
    Section I.4, the Microsoft Library Manager error messages.


I.1  Error-Message Display

    When a run-time error occurs within the QuickBASIC environment (with
    default screen options), the error message appears in a dialog box and the
    cursor is placed on the line where the error occurred.

    In stand-alone executable programs (that is, programs that are executed by
    entering the base name of the executable file at the system prompt), the
    run-time system prints the error messages followed by an address, unless
    the /D, /E, or /W option is specified on the BC command line. In those
    cases, the error message is followed by the number of the line in which
    the error occurred. The standard forms of this type of error message are
    as follows:

    Error n in module module-name at address segment:offset

    and

    Error n in line linenumber of module module-name at
    address segment:offset

    An ERR code is listed for some errors. If an error occurs, the value
    returned by ERR is set to the appropriate code when an error-trapping
    subroutine is entered. (Error-trapping routines are entered via the ON
    ERROR statement.) The ERR value remains unchanged until a RESUME statement
    returns control to the main program. See Chapter 6, "Error and Event
    Trapping," for more information.

    Table I.1 lists the error codes in numerical order. See the alphabetical
    listing for explanations of the errors.

    Table I.1  Run-Time Error Codes
╓┌─┌────────┌───────────────────────────┌────────┌───────────────────────────╖
    Code     Description                 Code     Description
    ──────────────────────────────────────────────────────────────────────────
    2        Syntax error                53       File not found

    3        RETURN without GOSUB        54       Bad file mode

    4        Out of DATA                 55       File already open

    5        Illegal function call       56       FIELD statement active

    6        Overflow                    57       Device I/O error
    Code     Description                 Code     Description
    ──────────────────────────────────────────────────────────────────────────
    6        Overflow                    57       Device I/O error

    7        Out of memory               58       File already exists

    9        Subscript out of range      59       Bad record length

    10       Duplicate definition        61       Disk full

    11       Division by zero            62       Input past end of file

    13       Type mismatch               63       Bad record number

    14       Out of string space         64       Bad file name

    16       String formula too complex  67       Too many files

    19       No RESUME                   68       Device unavailable

    20       RESUME without error        69       Communication-buffer
    Code     Description                 Code     Description
    ──────────────────────────────────────────────────────────────────────────
    20       RESUME without error        69       Communication-buffer
                                                overflow

    24       Device timeout              70       Permission denied

    25       Device fault                71       Disk not ready

    27       Out of paper                72       Disk-media error

    39       CASE ELSE expected          73       Advanced feature unavailable

    40       Variable required           74       Rename across disks

    50       FIELD overflow              75       Path/File access error

    51       Internal error              76       Path not found

    52       Bad file name or number
    ──────────────────────────────────────────────────────────────────────────
    Code     Description                 Code     Description
    ──────────────────────────────────────────────────────────────────────────
    ──────────────────────────────────────────────────────────────────────────



I.2  Invocation, Compile-Time, and Run-Time Error Messages

Advanced feature unavailable

    You are attempting to use a feature of QuickBASIC that is available with
    another version of BASIC, or supported only under a later version of DOS.
    (Compile-time or run-time error)

    ERR code: 73

Argument-count mismatch

    You are using an incorrect number of arguments with a BASIC subprogram or
    function. (Compile-time error)

Array already dimensioned

    This error can be caused by any of the following:

    ■ More than one DIM statement for the same static array.

    ■ A DIM statement after the initial use of an array. Dynamic arrays must
    be deallocated with the ERASE statement before they can be redimensioned
    with DIM; dynamic arrays may also be redimensioned with the REDIM
    statement.

    ■ An OPTION BASE statement that occurs after an array is dimensioned.

    (Compile-time or run-time error)

Array not defined

    An array is referenced but never defined. (Compile-time error)

Array not dimensioned

    An array is referenced but not dimensioned. If you are compiling the
    program with BC, this error is not "fatal"; the program will execute,
    although program results may be incorrect. (Compile-time warning)

Array too big

    There is not enough user data space to accommodate the array declaration.
    Reduce the size of the array or use the $DYNAMIC metacommand. You may also
    get this error if the array size exceeds 64K, the array is not dynamic,
    and the /AH option is not used. Reduce the size of the array, or make the
    array dynamic and use the /AH command-line option. (Compile-time error)

AS clause required

    A variable declared with an AS clause is referenced without one. If the
    first declaration of a variable has an AS clause, every subsequent DIM,
    REDIM, SHARED, and COMMON statement that references that variable must
    have an AS clause. (Compile-time error)

AS clause required on first declaration

    A variable that has not been declared using an AS clause is being
    referenced with an AS clause. (Compile-time error)

AS missing

    The compiler expects an AS keyword, as in OPEN "FILENAME" FOR INPUT AS #1.
    (Compile-time error)

Asterisk missing

    The asterisk is missing from a string definition in a user type.
    (Compile-time error)

Bad file mode

    This error occurs in the following situations:

    ■ The program tries to use PUT or GET with a sequential file or execute an
    OPEN statement with a file mode other than I, O, or R.

    ■ The program tries to use a FIELD statement with a file not opened for
    random access.

    ■ The program tries to print to a file opened for input.

    ■ The program tries to read from a file opened for output or appending.

    ■ QuickBASIC tries to use an include file previously saved in compressed
    format. Include files must be saved in text format. Reload the include
    file, save it in text format, then try to run the program again.

    ■ You try to load a corrupt binary program.

    (Run-time error)

    ERR code: 54

Bad file name

    An illegal form is used for the file name with LOAD, SAVE, KILL, or OPEN
    (for example, the file name has too many characters). (Run-time error)

    ERR code: 64

Bad file name or number

    A statement or command references a file with a file name or number that
    is not specified in the OPEN statement or is out of the range of file
    numbers specified earlier in the program. (Run-time error)

    ERR code: 52

Bad record length

    A GET or PUT statement that specified a record variable whose length did
    not match the record length specified in the corresponding OPEN statement
    was executed. (Run-time error)

    ERR code: 59

Bad record number

    In a PUT or GET statement, the record number is less than or equal to
    zero. (Run-time error)

    ERR code: 63

BASE missing

    QuickBASIC expected the keyword BASE here, as in OPTION BASE.
    (Compile-time error)

Binary source file

    The file you have attempted to compile is not an ASCII file. All source
    files saved by BASICA should be saved with the ,A option. QuickBASIC also
    uses this message to warn you when you try to use the /ZI or /ZD CodeView
    options with binary source files. (Compile-time error)

Block IF without END IF

    There is no corresponding END IF in a block IF construct. (Compile-time
    error)

Buffer size expected after /C:

    You must specify a buffer size after the /C option. (BC invocation error)

BYVAL allowed only with numeric arguments

    BYVAL does not accept string or record arguments. (Compile-time error)

/C: buffer size too large

    The maximum size of the communications buffer is 32,767 bytes. (BC
    invocation error)

Cannot continue

    While debugging, you have made a change that prevents execution from
    continuing. (Run-time error)

Cannot find file (filename). Input path:

    This error occurs when QuickBASIC cannot find a Quick library or utility
    (BC.EXE, LINK.EXE, LIB.EXE, or QB.EXE) required by the program. Enter the
    correct path name, or press CTRL+C to return to the DOS prompt. (QB
    invocation error)

Cannot generate listing for BASIC binary source files

    You are attempting to compile a binary source file with the BC command and
    the /A option. Recompile without the /A option. (BC invocation error)

Cannot start with `FN'

    You used "FN" as the first two letters of a subprogram or variable name.
    "FN" can only be used as the first two letters when calling a DEF FN
    function. (Compile-time error)

CASE ELSE expected

    No matching case was found for an expression in a SELECT CASE statement.
    (Run-time error)

    ERR code: 39

CASE without SELECT

    The first part of a SELECT CASE statement is missing or misspelled.
    (Compile-time error)

Colon expected after /C

    A colon is required between the option and the buffer size argument. (BC
    invocation error)

Comma missing

    QuickBASIC expects a comma. (Compile-time error)

COMMON and DECLARE must precede executable statements

    A COMMON statement or a DECLARE statement is misplaced. COMMON and DECLARE
    statements must appear before any executable statements. All BASIC
    statements are executable except the following:

    ■ COMMON

    ■ DEFtype

    ■ DIM (for static arrays)

    ■ OPTION BASE

    ■ REM

    ■ TYPE

    ■ All metacommands

    (Compile-time error)

COMMON in Quick library too small

    More common variables are specified in the module than in the currently
    loaded Quick library. (Compile-time error)

COMMON name illegal

    QuickBASIC encountered an illegal /blockname/ specification (for example,
    a blockname that is a BASIC reserved word) in a named COMMON block.
    (Compile-time error).

Communication-buffer overflow

    During remote communications, the receive buffer overflowed. The size of
    the receive buffer is set by the /C command line option or the RB option
    in the OPEN COM statement. Try checking the buffer more frequently (with
    the LOC function) or emptying it more often (with the INPUT$ function).
    (Run-time error)

    ERR code: 69

CONST/DIM SHARED follows SUB/FUNCTION

    CONST and DIM SHARED statements should appear before any subprogram or
    FUNCTION procedure definitions. If you are compiling your program with BC,
    this error is not "fatal"; the program will execute, although the results
    may be incorrect. (Compile-time warning)

Control structure in IF...THEN...ELSE incomplete

    An unmatched NEXT, WEND, END IF, END SELECT, or LOOP statement appears in
    a single-line IF...THEN...ELSE statement. (Compile-time error)

Data-memory overflow

    There is too much program data to fit in memory. This error is often
    caused by too many constants, or too much static array data. If you are
    using the BC command, or the Make EXE File or Make Library commands, try
    turning off any debugging options. If memory is still exhausted, break
    your program into parts and use the CHAIN statement or use the $DYNAMIC
    metacommand. (Compile-time error)

DECLARE required

    An implicit SUB or FUNCTION procedure call appears before the procedure
    definition. (An implicit call does not use the CALL statement.) All
    procedures must be defined or declared before they are implicitly called.
    (Compile-time error)

DEF FN not allowed in control statements

    DEF FN function definitions are not permitted inside control constructs
    such as IF...THEN...ELSE and SELECT CASE. (Compile-time error)

DEF without END DEF

    There is no corresponding END DEF in a multiline function definition.
    (Compile-time error)

DEFtype character specification illegal

    A DEFtype statement is entered incorrectly. DEF can only be followed by
    LNG, DBL, INT, SNG, STR, or (for user-defined functions) a blank space.
    (Compile-time error)

Device fault

    A device has returned a hardware error. If this message occurs while data
    are being transmitted to a communications file, it indicates that the
    signals being tested with the OPEN COM statement were not found in the
    specified period of time. (Run-time error)

    ERR code: 25

Device I/O error

    An I/O error occurred on a device I/O operation. The operating system
    cannot recover from the error. (Run-time error)

    ERR code: 57

Device timeout

    The program did not receive information from an I/O device within a
    predetermined amount of time. (Run-time error)

    ERR code: 24

Device unavailable

    The device you are attempting to access is not on line or does not exist.

    (Run-time error)

    ERR code: 68

Disk full

    There is not enough room on the disk for the completion of a PRINT, WRITE,
    or CLOSE operation. This error can also occur if there is not enough room
    for QuickBASIC to write out an object or executable file. (Run-time error)

    ERR code: 61

Disk-media error

    Disk-drive hardware has detected a physical flaw on the disk. (Run-time
    error)

    ERR code: 72

Disk not ready

    The disk-drive door is open, or no disk is in the drive. (Run-time error)

    ERR code: 71

Division by zero

    A division by zero is encountered in an expression, or an exponentiation
    operation results in zero being raised to a negative power. (Compile-time
    or run-time error)

    ERR code: 11

DO without LOOP

    The terminating LOOP clause is missing from a DO...LOOP statement.
    (Compile-time error)

Document too large

    Your document exceeds QuickBASIC's internal limit. Divide the document
    into separate files.

Duplicate definition

    You are using an identifier that has already been defined. For example,
    you are attempting to use the same name in a CONST statement and as a
    variable definition, or the same name for a procedure and a variable.

    This error also occurs if you attempt to redimension an array. You must
    use DIM or REDIM when redimensioning dynamic arrays. (Compile-time or
    run-time error)

    ERR code: 10

Duplicate label

    Two program lines are assigned the same number or label. Each line number
    or label in a module must be unique. (Compile-time error)

Dynamic array element illegal

    Dynamic array elements are not allowed with VARPTR$. (Compile-time error)

Element not defined

    A user-defined type element is referenced but not defined. For example, if
    the user-defined type MYTYPE contained elements A, B, and C, then an
    attempt to use the variable D as an element of MYTYPE would cause this
    message to appear. (Compile-time error)

ELSE without IF

    An ELSE clause appears without a corresponding IF. Sometimes this error is
    caused by incorrectly nested IF statements. (Compile-time error)

ELSEIF without IF

    An ELSEIF statement appears without a corresponding IF. Sometimes this
    error is caused by incorrectly nested IF statements. (Compile-time error)

END DEF without DEF

    An END DEF statement has no corresponding DEF statement. (Compile-time
    error)

END IF without block IF

    The beginning of an IF block is missing. (Compile-time error)

END SELECT without SELECT

    The end of a SELECT CASE statement appears without a beginning SELECT
    CASE. The beginning of the SELECT CASE statement may be missing or
    misspelled. (Compile-time error)

END SUB or END FUNCTION must be last line in window

    You are attempting to add code after a procedure. You must either return
    to the main module or open another module. (Compile-time error)

END SUB/FUNCTION without SUB/FUNCTION

    You deleted the SUB or FUNCTION statement. (Compile-time error)

END TYPE without TYPE

    An END TYPE statement is used outside a TYPE declaration. (Compile-time
    error)

Equal sign missing

    QuickBASIC expects an equal sign. (Compile-time error)

Error during QuickBASIC initialization

    Several conditions can cause this error. It is most commonly caused when
    there is not enough memory in the machine to load QuickBASIC. If you are
    loading a Quick library, try reducing the size of the library.

    This error may occur when you attempt to use QuickBASIC on unsupported
    hardware. (QB invocation error)

Error in loading file (filename)──Cannot find file

    This error occurs when redirecting input to QuickBASIC from a file. The
    input file is not at the location specified on the command line. (QB
    invocation error)

Error in loading file (filename)──Disk I/O error

    This error is caused by physical problems accessing the disk, for example,
    if the drive door is left open. (QB invocation error)

Error in loading file (filename)──DOS memory-area error

    The area of memory used by DOS has been written to, either by an assembly
    language routine or with the POKE statement. (QB invocation error)

Error in loading file (filename)──Invalid format

    You are attempting to load a Quick library that is not in the correct
    format. This error can occur if you are attempting to use a Quick library
    created with a previous version of QuickBASIC, if you are trying to use a
    file that has not been processed with QuickBASIC's Make Library command or
    the /QU option from LINK, or if you are trying to load a stand-alone
    (.LIB) library with QuickBASIC. (QB invocation error)

Error in loading file (filename)──Out of memory

    More memory is required than is available. For example, there may not be
    enough memory to allocate a file buffer. Try reducing the size of your DOS
    buffers, getting rid of any terminate-and-stay resident programs, or
    eliminating some device drivers. If you have large arrays, try placing a
    $DYNAMIC metacommand at the top of your program. If you have documents
    loaded, then unloading them will free some memory. (Run-time error)

EXIT DO not within DO...LOOP

    An EXIT DO statement is used outside of a DO...LOOP statement.
    (Compile-time error)

EXIT not within FOR...NEXT

    An EXIT FOR statement is used outside of a FOR...NEXT statement.
    (Compile-time error)

Expected: item

    This is a syntax error. The cursor is positioned at the unexpected item.
    (Compile-time error)

Expression too complex

    This error is caused when certain internal limitations are exceeded. For
    example, during expression evaluation, strings that are not associated
    with variables are assigned temporary locations. A large number of such
    strings can cause this error to occur. Try simplifying expressions, and
    assigning strings to variables. (Compile-time error)

Extra file name ignored

    You specified too many files on the command line; the last file name on
    the line is ignored. (BC invocation error)

Far heap corrupt

    The far-heap memory has been corrupted by one of the following:

    ■ The QB compiler does not support a terminate-and-stay-resident program
    resident in DOS.

    ■ The POKE statement modified areas of memory used by QuickBASIC. (This
    may modify the descriptor for a dynamic array of numbers or fixed-length
    strings.)

    ■ The program called an other-language routine that modified areas of
    memory used by QuickBASIC. (This may modify the descriptor for a dynamic
    array of numbers or fixed-length strings.

    (Compile-time error)

FIELD overflow

    A FIELD statement is attempting to allocate more bytes than were specified
    for the record length of a random file. (Run-time error)

    ERR code: 50

FIELD statement active

    A GET or PUT statement referred to a record variable used in a a file with
    space previously allocated by the FIELD statement. GET or PUT with a
    record variable argument may only be used on files where no FIELD
    statements have been executed. (Run-time error)

    ERR code: 56

File already exists

    The file name specified in a NAME statement is identical to a file name
    already in use on the disk. (Run-time error)

    ERR code: 58

File already open

    A sequential-output-mode OPEN statement is issued for a file that is
    already open, or a KILL statement is given for a file that is open.
    (Run-time error)

    ERR code: 55

File not found

    A FILES, KILL, NAME, OPEN or RUN statement references a file that does not
    exist. (Run-time error)

    ERR code: 53

File not found in module module-name at address segment:offset

    A FILES, KILL, NAME, OPEN or RUN statement references a file that does not
    exist. This error message is equivalent to the File not found message, but
    it occurs during execution of compiled programs. The module-name is the
    name of the calling module. The address is the location of the error in
    the code. (Run-time error)

    ERR code: 53

File previously loaded

    You are attempting to load a file that is already in memory. (Compile-time
    error)

Fixed-length string illegal

    You are attempting to use a fixed-length string as a formal parameter.
    (Compile-time error)

FOR index variable already in use

    This error occurs when an index variable is used more than once in nested
    FOR loops. (Compile-time error)

FOR index variable illegal

    This error is usually caused when an incorrect variable type is used in a
    FOR-loop index. A FOR-loop index variable must be a simple numeric
    variable. (Compile-time error)

FOR without NEXT

    Each FOR statement must have a matching NEXT statement. (Compile-time
    error)

Formal parameter specification illegal

    There is an error in a function or subprogram parameter list.
    (Compile-time error)

Formal parameters not unique

    A FUNCTION or SUB declaration contains duplicate parameters, as in this
    example: SUB GetName(A,B,C,A) STATIC. (Compile-time error)

Function already defined

    This error occurs when a previously defined FUNCTION is redefined.
    (Compile-time error)

Function name illegal

    A BASIC reserved word is used as a user-defined FUNCTION name.
    (Compile-time error)

Function not defined

    You must declare or define a FUNCTION before using it. (Compile-time
    error)

GOSUB missing

    The GOSUB is missing from an ON event statement. (Compile-time error)

GOTO missing

    The GOTO is missing from an ON ERROR statement. (Compile-time error)

GOTO or GOSUB expected

    QuickBASIC expects a GOTO or GOSUB statement. (Compile-time error)

Help not found

    Help was requested but not found, and the program contains errors
    prohibiting QuickBASIC from building a variable table. Press F5 to view
    the line that caused the error.

Identifier cannot end with %, &, !, #, or $

    The above suffixes are not allowed in type identifiers, subprogram names,
    or names appearing in COMMON statements. (Compile-time error)

Identifier cannot include period

    User-defined type identifier and record element names cannot contain
    periods. The period should only be used as a record variable separator. In
    addition, a variable name cannot contain a period if the part of the name
    before the period has been used in an identifier AS usertype clause
    anywhere in the program. If you have programs that use the period in
    variable names, it is recommended that you change them to use mixed case
    instead. For example, variable ALPHA.BETA would become AlphaBeta.
    (Compile-time error)

Identifier expected

    You are attempting to use a number or a BASIC reserved word where an
    identifier is expected. (Compile-time error)

Identifier too long

    Identifiers must not be longer than 40 characters. (Compile-time error)

Illegal function call

    A parameter that is out of range is passed to a math or string function. A
    function-call error can also occur for the following reasons:

    ■ A negative or unreasonably large subscript is used.

    ■ A negative number is raised to a power that is not an integer.

    ■ A negative record number is given when using GET file or PUT file.

    ■ An improper or out-of-range argument is given to a function.

    ■ A BLOAD or BSAVE operation is directed to a nondisk device.

    ■ An I/O function or statement (LOC or LOF, for example) is performed on a
    device that does not support it.

    ■ Strings are concatenated to create a string greater than 32,767
    characters in length.

    (Run-time error)

    ERR code: 5

Illegal in direct mode

    The statement is valid only within a program and cannot be used in the
    Immediate window. (Compile-time error)

Illegal in procedure or DEF FN

    The statement is not allowed inside a procedure. (Compile-time error)

Illegal number

    The format of the number does not correspond to a valid number format. You
    have probably made a typographical error. For example, the number 2p3 will
    produce this error. (Compile-time error)

Illegal outside of SUB, FUNCTION, or DEF FN

    This statement is not allowed in module-level code. (Compile-time error)

Illegal outside of SUB/FUNCTION

    The statement is not allowed in module-level code or DEF FN functions.
    (Compile-time error)

Illegal outside of TYPE block

    The element AS type clause is permitted only within a TYPE...END TYPE
    block. (Compile-time error)

Illegal type character in numeric constant

    A numeric constant contains an inappropriate type-declaration character.
    (Compile-time error)

$INCLUDE-file access error

    The include file named in the $INCLUDE metacommand cannot be located.
    (Compile-time error)

Include file too large

    Your include file exceeds QuickBASIC's internal limit. Break the file into
    separate files. (Compile-time error)

Input file not found

    The source file you gave on the command line is not in the specified
    location. (BC invocation error)

INPUT missing

    The compiler expects the keyword INPUT. (Compile-time error)

Input past end of file

    An INPUT statement reads from a null (empty) file or from a file in which
    all data have already been read. To avoid this error, use the EOF function
    to detect the end of file. (Run-time error)

    ERR code: 62

Input runtime module path:

    This prompt appears if the run-time module BRUN45.EXE is not found. Enter
    the correct path specification. This error is severe and cannot be
    trapped. (Run-time error)

Integer between 1 and 32767 required

    The statement requires an integer argument. (Compile-time error)

Internal error

    An internal malfunction occurred in QuickBASIC. Use the Product Assistance
    Request form included with your documentation to report to Microsoft the
    conditions under which the message appeared. (Run-time error)

    ERR code: 51

Internal error near xxxx

    An internal malfunction occurred in QuickBASIC at location xxxx. Use the
    Product Assistance Request form included with your documentation to report
    the conditions under which the message appeared. (Compile-time error)

Invalid character

    QuickBASIC found an invalid character, such as a control character, in the
    source file. (Compile-time error)

Invalid constant

    An invalid expression is used to assign a value to a constant. Remember
    that expressions assigned to constants may contain numeric constants,
    symbolic constants, and any of the arithmetic or logical operators except
    exponentiation. A string expression assigned to a constant may consist
    only of a single literal string. (Compile-time error)

Invalid DECLARE for BASIC procedure

    You are attempting to use the DECLARE statement keywords ALIAS, CDECL, or
    BYVAL with a BASIC procedure. ALIAS, CDECL, and BYVAL can only be used
    with non-BASIC procedures. (Compile-time error)

Label not defined

    A line label is referenced (in a GOTO statement, for example), but does
    not occur in the program. (Compile-time error)

Label not defined: label

    A GOTO linelabel statement refers to a nonexistent line label.
    (Compile-time error)

Left parenthesis missing

    QuickBASIC expected a left parenthesis, or a REDIM statement tried to
    reallocate space for a scalar. (Compile-time error)

Line invalid. Start again

    An invalid file-name character was used following the path characters "\"
    (backslash) or ":" (colon). (BC invocation error)

Line number or label missing

    A line number or label is missing from a statement that requires one, for
    example, GOTO. (Compile-time error)

Line too long

    Lines are limited to 255 characters. (Compile-time error)

LOOP without DO

    The DO starting a DO...LOOP statement is missing or misspelled.
    (Compile-time error)

Lower bound exceeds upper bound

    The lower bound exceeds the upper bound defined in a DIM statement.
    (Compile-time error)

Math overflow

    The result of a calculation is too large to be represented in BASIC number
    format. (Compile-time error)

$Metacommand error

    A metacommand is incorrect. If you are compiling the program with BC this
    error is not "fatal"; the program will execute, although the results may
    be incorrect. (Compile-time warning)

Minus sign missing

    QuickBASIC expects a minus sign. (Compile-time error)

Missing Event Trapping (/W) or Checking Between Statements (/V) option

    The program contains an ON event statement requiring one of these options.
    (Compile-time error)

Missing On Error (/E) option

    When using the BC command, programs containing ON ERROR statements must be
    compiled with the On Error (/E) option. (Compile-time error)

Missing Resume Next (/X) option

    When using the BC command, programs containing RESUME, RESUME NEXT, and
    RESUME 0 statements must be compiled with the Resume Next (/X) option.
    (Compile-time error)

Module level code too large

    Your module-level code exceeds QuickBASIC's internal limit. Try moving
    some of the code into SUB or FUNCTION procedures. (Compile-time error)

Module not found. Unload module from program?

    When loading the program, QuickBASIC did not find the file containing the
    indicated module. QuickBASIC created an empty module instead. You must
    delete the empty module before you can run the program.

Must be first statement on the line

    In block IF...THEN...ELSE constructs, IF, ELSE, ELSEIF, and END IF can be
    preceded only by a line number or label. (Compile-time error)

Name of subprogram illegal

    The error is caused when a subprogram name is a BASIC reserved word, or a
    subprogram name is used twice. (Compile-time error)

Nested function definition

    A FUNCTION definition appears inside another FUNCTION definition, or
    inside an IF...THEN...ELSE clause. (Compile-time error)

NEXT missing for variable

    A FOR statement is missing a corresponding NEXT statement. The variable is
    the FOR-loop index variable. (Compile-time error)

NEXT without FOR

    Each NEXT statement must have a matching FOR statement. (Compile-time
    error)

No line number in module-name at address segment:offset

    This error occurs when the error address cannot be found in the
    line-number table during error trapping. This happens if there are no
    integer line numbers between 0 and 65,527. It may also occur if the
    line-number table has been accidentally overwritten by the user program.
    This error is severe and cannot be trapped. (Run-time error)

No main module. Choose Set Main Module from the Run menu to select one

    You are attempting to run the program after you have unloaded the main
    module. Every program must have a main module. (Compile-time error)

No RESUME

    The end of the program was encountered while the program was in an
    error-handling routine. A RESUME statement is needed to remedy this
    situation. (Run-time error)

    ERR code: 19

Not watchable

    This error occurs when you are specifying a variable in a watch
    expression. Make sure the module or procedure in the active View window
    has access to the variable you want to watch. For example, module-level
    code cannot access variables that are local to a SUB or FUNCTION.
    (Run-time error)

Numeric array illegal

    Numeric arrays are not allowed as arguments to VARPTR$. Only simple
    variables and string array elements are permitted. (Compile-time error)

Only simple variables allowed

    User-defined types and arrays are not permitted in READ and INPUT
    statements. Array elements that are not of a user-defined type are
    permitted. (Compile-time error)

Operation requires disk

    You are attempting to load from, or save to, a nondisk device such as the
    printer or keyboard. (Compile-time error)

Option unknown: option

    You have given an illegal option. (BC invocation error)

Out of DATA

    A READ statement is executed when there are no DATA statements with unread
    data remaining in the program. (Run-time error)

    ERR code: 4

Out of data space

    Try modifying your data space requirements as follows:

    ■ Use a smaller file buffer in the OPEN statement's LEN clause.

    ■ Use the $DYNAMIC metacommand to create dynamic arrays. Dynamic array
    data can usually be much larger than static array data.

    ■ Use fixed-length string arrays instead of variable-length string arrays.

    ■ Use the smallest data type that will accomplish your task. Use integers
    whenever possible.

    ■ Try not to use many small procedures. QuickBASIC must create several
    bytes of control information for each procedure.

    ■ Use CLEAR to modify the size of the stack. Use only enough stack space
    to accomplish your task.

    ■ Do not use source lines longer than 255 characters. Such lines require
    allocation of additional text buffer space.

    (Compile-time or run-time error)

Out of memory

    More memory is required than is available. For example, there may not be
    enough memory to allocate a file buffer. Try reducing the size of your DOS
    buffers, or getting rid of any terminate-and-stay-resident programs, or
    eliminating some device drivers. If you have large arrays, try placing a
    $DYNAMIC metacommand at the top of your program. If you have documents
    loaded, unloading them will free some memory. (BC invocation,
    compile-time, or run-time error)

    ERR code: 7

Out of paper

    The printer is out of paper or is not turned on. (Run-time error)

    ERR code: 27

Out of stack space

    This error can occur when a recursive FUNCTION procedure nests too deeply,
    or there are too many active subroutine, FUNCTION, and SUB calls. You can
    use the CLEAR statement to increase the program's allotted stack space.
    This error cannot be trapped. (Run-time error)

Out of string space

    String variables exceed the allocated amount of string space. (Run-time
    error)

    ERR code: 14

Overflow

    The result of a calculation is too large to be represented within the
    range allowed for either floating-point or integer numbers. (Run-time
    error)

    ERR code: 6

Overflow in numeric constant

    The numeric constant is too large. (Compile-time error)

Parameter type mismatch

    A subprogram or FUNCTION parameter type does not match the DECLARE
    statement argument or the calling argument. (Compile-time error)

Path not found

    During an OPEN, MKDIR, CHDIR, or RMDIR operation, DOS was unable to find
    the path specified. The operation is not completed. (Run-time error)

    ERR code: 76

Path/File access error

    During an OPEN, MKDIR, CHDIR, or RMDIR operation, DOS was unable to make a
    correct connection between the path and file name. The operation is not
    completed. (Compile-time or run-time error)

    ERR code: 75

Permission denied

    An attempt was made to write to a write-protected disk, or to access a
    locked file. (Run-time error)

    ERR code: 70

Procedure already defined in Quick library

    A procedure in the Quick library has the same name as a procedure in your
    program. (Compile-time error)

Procedure too large

    The procedure has exceeded QuickBASIC's internal limit. Make the procedure
    smaller by dividing it into several procedures. (Compile-time error)

Program-memory overflow

    You are attempting to compile a program whose code segment is larger than
    64K. Try splitting the program into separate modules, or use the CHAIN
    statement. (Compile-time error)

Read error on standard input

    A system error occurred while reading from the console or a redirected
    input file. (BC invocation error)

Record/string assignment required

    The string or record variable assignment is missing from the LSET
    statement. (Compile-time error)

Redo from start

    You have responded to an INPUT prompt with the wrong number or type of
    items. Retype your response in the correct form. (Run-time error)

Rename across disks

    An attempt was made to rename a file with a new drive designation. This is
    not allowed. (Run-time prompt)

    ERR code: 74

Requires DOS 2.10 or later

    You are attempting to use QuickBASIC with an incorrect version of DOS. (QB
    invocation or run-time error)

RESUME without error

    A RESUME statement is encountered before an error-trapping routine is
    entered. (Run-time error)

    ERR code: 20

RETURN without GOSUB

    A RETURN statement is encountered for which there is no previous,
    unmatched GOSUB statement. (Run-time error)

    ERR code: 3

Right parenthesis missing

    QuickBASIC expects a right (closing) parenthesis. (Compile-time error)

SEG or BYVAL not allowed in CALLS

    BYVAL and SEG are permitted only in a CALL statement. (Compile-time error)

SELECT without END SELECT

    The end of a SELECT CASE statement is missing or misspelled. (Compile-time
    error)

Semicolon missing

    QuickBASIC expects a semicolon. (Compile-time error)

Separator illegal

    There is an illegal delimiting character in a PRINT USING or WRITE
    statement. Use a semicolon or a comma as a delimiter. (Compile-time error)

Simple or array variable expected

    The compiler expects a variable argument. (Compile-time error)

Skipping forward to END TYPE statement

    An error in the TYPE statement has caused QuickBASIC to ignore everything
    between the TYPE and END TYPE statement. (Compile-time error)

Statement cannot occur within "CLUDE file

    SUB...END SUB and FUNCTION...END FUNCTION statement blocks are not
    permitted in include files. Use the Merge command from the File menu to
    insert the include file into the current module, or load the include file
    as a separate module. If you load the include file as a separate module,
    some restructuring may be necessary because shared variables are shared
    only within the scope of the module. (Compile-time error)

Statement cannot precede SUB/FUNCTION definition

    The only statements allowed before a procedure definition are the
    statements REM and DEFtype. (Compile-time error)

Statement ignored

    You are using the BC command to compile a program that contains TRON and
    TROFF statements without using the /D option. This error is not "fatal" ;
    the program will execute, although the results may be incorrect.
    (Compile-time warning)

Statement illegal in TYPE block

    The only statements allowed between the TYPE and END TYPE statements are
    REM and element AS typename. (Compile-time error)

Statement unrecognizable

    You have probably mistyped a BASIC statement. (Compile-time error)

Statements/labels illegal between SELECT CASE and CASE

    Statements and line labels are not permitted between SELECT CASE and the
    first CASE statement. Comments and statement separators are permitted.
    (Compile-time error)

STOP in module name at address segment:offset

    A STOP statement was encountered in the program. (Run-time error)

String assignment required

    The string assignment is missing from an RSET statement. (Compile-time
    error)

String constant required for ALIAS

    The DECLARE statement ALIAS keyword requires a string-constant argument.
    (Compile-time error)

String expression required

    The statement requires a string-expression argument. (Compile-time error)

String formula too complex

    Either a string formula is too long or an INPUT statement requests more
    than 15 string variables. Break the formula or INPUT statement into parts
    for correct execution. (Run-time error)

    ERR code: 16

String space corrupt

    This error occurs when an invalid string in string space is being deleted
    during heap compaction. The probable causes of this error are as follows:

    ■ A string descriptor or string back pointer has been improperly modified.
    This may occur if you use an assembly-language subroutine to modify
    strings.

    ■ Out-of-range array subscripts are used and string space is inadvertently
    modified. The Produce Debug Code option can be used at compile time to
    check for array subscripts that exceed the array bounds.

    ■ Incorrect use of the POKE and/or DEF SEG statements may modify string
    space improperly.

    ■ Mismatched COMMON declarations may occur between two chained programs.

    (Run-time error)

String variable required

    The statement requires a string-variable argument. (Compile-time error)

SUB or FUNCTION missing

    A DECLARE statement has no corresponding procedure. (Compile-time error)

SUB/FUNCTION without END SUB/FUNCTION

    The terminating statement is missing from a procedure. (Compile-time
    error)

Subprogram error

    This is a SUB or FUNCTION definition error and is usually caused by one of
    the following:

    ■ The SUB or FUNCTION is already defined.

    ■ The program contains incorrectly nested FUNCTION or SUB statements.

    ■ The SUB or FUNCTION does not terminate with an END SUB or END FUNCTION
    statement.

    (Compile-time error)

Subprogram not defined

    A subprogram is called but never defined. (Compile-time error)

Subprograms not allowed in control statements

    Subprogram FUNCTION definitions are not permitted inside control
    constructs such as IF...THEN...ELSE and SELECT CASE. (Compile-time error)

Subscript out of range

    An array element was referenced with a subscript that was outside the
    dimensions of the array, or an element of an undimensioned dynamic array
    was accessed. This message may be generated if the Debug (/D) option was
    specified at compile time. You may also get this error if the array size
    exceeds 64K, the array is not dynamic, and the /AH option was not used.
    Reduce the size of the array, or make the array dynamic and use the /AH
    command-line option. (Run-time error)

    ERR code: 9

Subscript syntax illegal

    An array subscript contains a syntax error: for example, an array
    subscript contains both string and integer data types. (Compile-time
    error)

Syntax error

    Several conditions can cause this error. The most common cause at compile
    time is a mistyped BASIC keyword or argument. At run-time, it is often
    caused by an improperly formatted DATA statement. (Compile-time or
    run-time error)

    ERR code: 2

Syntax error in numeric constant

    A numeric constant is not properly formed. (Compile-time error)

THEN missing

    QuickBASIC expects a THEN keyword. (Compile-time error)

TO missing

    QuickBASIC expects a TO keyword. (Compile-time error)

Too many arguments in function call

    Function calls are limited to 60 arguments. (Compile-time error)

Too many dimensions

    Arrays are limited to 60 dimensions. (Compile-time error)

Too many files

    At compile time, this error occurs when include files are nested more than
    five levels deep. It occurs at run time when the 255-file directory
    maximum is exceeded by an attempt to create a new file with a SAVE or OPEN
    statement. (Compile-time or run-time error)

    ERR code: 67

Too many labels

    The number of lines in the line list following an ON...GOTOor ON...GOSUB
    statement exceeds 255 (Compile-time error) or 59 (Run-time error in
    compiled applications).

Too many named COMMON blocks

    The maximum number of named COMMON blocks permitted is 126. (Compile-time
    error)

Too many TYPE definitions

    The maximum number of user-defined types permitted is 240. (Compile-time
    error)

Too many variables for INPUT

    An INPUT statement is limited to 60 variables. (Compile-time error)

Too many variables for LINE INPUT

    Only one variable is allowed in a LINE INPUT statement. (Compile-time
    error)

Type mismatch

    The variable is not of the required type. For example, you are trying to
    use the SWAP statement with a string variable and a numeric variable.
    (Compile-time or run-time error)

    ERR code: 13

TYPE missing

    The TYPE keyword is missing from an END TYPE statement. (Compile-time
    error)

Type more than 65535 bytes

    A user-defined type cannot exceed 64K. (Compile-time error)

Type not defined

    The usertype argument to the TYPE statement is not defined. (Compile-time
    error)

TYPE statement improperly nested

    User-defined type definitions are not allowed in procedures. (Compile-time
    error)

TYPE without END TYPE

    There is no END TYPE statement associated with a TYPE statement.
    (Compile-time error)

Typed variable not allowed in expression

    Variables that are user-defined types are not permitted in expressions
    such as CALL ALPHA((X)), where Xis a user-defined type. (Compile-time
    error)

Unexpected end of file in TYPE declaration

    There is an end-of-file character inside a TYPE...END TYPE block.

Unprintable error

    An error message is not available for the error condition that exists.
    This may be caused by an ERROR statement that doesn't have a defined error
    code. (Run-time error)

Unrecognized switch error: "QU"

    You are attempting to create an .EXE file or Quick library with an
    incorrect version of the Microsoft Overlay Linker. You must use the linker
    supplied on the QuickBASIC distribution disks to create an .EXE file or
    Quick library. (Compile-time error)

Valid options: [/RUN] file /AH /B /C:buf /G /NOHI /H /L [lib] /MBF /CMD
string

    This message appears when you invoke QuickBASIC with an invalid option.
    (QB invocation error)

Variable-length string required

    Only variable-length strings are permitted in a FIELD statement.
    (Compile-time error)

Variable name not unique

    You are attempting to define the variable x as a user-defined type after
    x.y has been used. (Compile-time error)

Variable required

    QuickBASIC encountered an INPUT, LET, READ, or SHARED statement without a
    variable argument. (Compile-time error)

Variable required

    A GET or PUT statement didn't specify a variable when an operation is
    performed on a file opened in BINARY mode. (Run-time error)

    ERR code: 40

WEND without WHILE

    This error is caused when a WEND statement has no corresponding WHILE
    statement. (Compile-time error)

WHILE without WEND

    This error is caused when a WHILE statement has no corresponding WEND
    statement. (Compile-time error)

Wrong number of dimensions

    An array reference contains the wrong number of dimensions. (Compile-time
    error)


I.3  LINK Error Messages

    This section lists and describes error messages generated by the Microsoft
    Overlay Linker, LINK.

    Fatal errors cause the linker to stop execution. Fatal error messages have
    the following format:

    location : fatal error L1xxx:messagetext

    Nonfatal errors indicate problems in the executable file. LINK produces
    the executable file. Nonfatal error messages have the following format:

    location : error L2xxx: messagetext

    Warnings indicate possible problems in the executable file. LINK produces
    the executable file. Warnings have the following format:

    location : warning L4xxx: messagetext

    In these messages, location is the input file associated with the error,
    or LINK if there is no input file.

    The following error messages may appear when you link object files with
    LINK:

╓┌─┌──────────────┌──────────────────────────────────────────────────────────╖
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
    L1001          option : option name ambiguous

                    A unique option name did not appear after the option
                    indicator (/). For example, the command

                    LINK /N main;
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    LINK /N main;

                    generates this error, since LINK cannot tell which of the
                    three options beginning with the letter "N" was intended.

    L1002          option : unrecognized option name

                    An unrecognized character followed the option indicator
                    (/), as in the following example:

                    LINK /ABCDEF main;

    L1003          /QUICKLIB, /EXEPACK incompatible

                    You specified two options that cannot be used together:
                    /QUICKLIB and /EXEPACK.

    L1004          option : invalid numeric value

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    An incorrect value appeared for one of the LINK options.
                    For example, a character string was given for an option
                    that requires a numeric value.

    L1006          option : stack size exceeds 65535 bytes

                    The value given as a parameter to the /STACKSIZE option
                    exceeds the maximum allowed.

    L1007          option : interrupt number exceeds 255

                    For the /OVERLAYINTERRUPT option, a number greater than 255
                    was given as a value.

    L1008          option : segment limit set too high

                    The limit on the number of segments allowed was set to
                    greater than 3072 using the /SEGMENTS option.
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    greater than 3072 using the /SEGMENTS option.

    L1009          number : CPARMAXALLOC : illegal value

                    The number specified in the /CPARMAXALLOC option was not in
                    the range 1-65,535.

    L1020          no object modules specified

                    No object-file names were specified to LINK.

    L1021          cannot nest response files

                    A response file occurred within a response file.

    L1022          response line too long

                    A line in a response file was longer than 127 characters.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

    L1023          terminated by user

                    You entered CTRL+C or CRTL+BREAK

    L1024          nested right parentheses

                    The contents of an overlay were typed incorrectly on the
                    command line.

    L1025          nested left parentheses

                    The contents of an overlay were typed incorrectly on the
                    command line.

    L1026          unmatched right parenthesis

                    A right parenthesis was missing from the contents
                    specification of an overlay on the command line.
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    specification of an overlay on the command line.

    L1027          unmatched left parenthesis

                    A left parenthesis was missing from the contents
                    specification of an overlay on the command line.

    L1043          relocation table overflow

                    More than 32,768 long calls, long jumps, or other long
                    pointers appeared in the program.

                    Try replacing long references with short references, where
                    possible, and recreate the object module.

    L1045          too many TYPDEF records

                    An object module contained more than 255 TYPDEF records.
                    These records describe communal variables. This error can
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    These records describe communal variables. This error can
                    appear only with programs produced by the Microsoft FORTRAN
                    Compiler or other compilers that support communal
                    variables. (TYPDEF is a DOS term. It is explained in the
                    Microsoft MS-DOS Programmer's Reference and in other
                    reference books on DOS.)

    L1046          too many external symbols in one module

                    An object module specified more than the limit of 1023
                    external symbols.

                    Break the module into smaller parts.

    L1047          too many group, segment, and class names in one module

                    The program contained too many group, segment, and class
                    names.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    Reduce the number of groups, segments, or classes, and
                    recreate the object file.

    L1048          too many segments in one module

                    An object module had more than 255 segments.

                    Split the module or combine segments

    L1049          too many segments

                    The program had more than the maximum number of segments.
                    Use the /SEGMENTS option, which has a default value of 128,
                    to specify the maximum legal number of segments. The
                    default is 128 in the /SEGMENTS option, which specifies the
                    maximum legal number.

                    Relink using the /SEGMENTS option with an appropriate
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    Relink using the /SEGMENTS option with an appropriate
                    number of segments.

    L1050          too many groups in one module

                    LINK encountered over 21 group definitions (GRPDEF) in a
                    single module.

                    Reduce the number of group definitions or split the module.
                    (Group definitions are explained in the Microsoft MS-DOS
                    Programmer's Reference and in other reference books on
                    DOS.)

    L1051          too many groups

                    The program defined more than 20 groups, not counting
                    DGROUP.

                    Reduce the number of groups.
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    Reduce the number of groups.

    L1052          too many libraries

                    An attempt was made to link with more than 32 libraries.

                    Combine libraries, or use modules that require fewer
                    libraries.

    L1053          out of memory for symbol table

                    There is no fixed limit to the size of the symbol table.
                    However, it is limited by the amount of available memory.

                    Combine modules or segments and recreate the object files.
                    Eliminate as many public symbols as possible.

    L1054          requested segment limit too high

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    LINK did not have enough memory to allocate tables
                    describing the number of segments requested. (The default
                    is 128 or the value specified with the /SEGMENTS option.)

                    Try linking again using the /SEGMENTS option to select a
                    smaller number of segments (for example, use 64 if the
                    default was used previously), or free some memory by
                    eliminating resident programs or shells.

    L1056          too many overlays

                    The program defined more than 63 overlays.

    L1057          data record too large

                    A LEDATA record (in an object module) contained more than
                    1024 bytes of data. This is a translator error. (LEDATA is
                    a DOS term, which is explained in the Microsoft MS-DOS
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    a DOS term, which is explained in the Microsoft MS-DOS
                    Programmer's Reference and in other DOS reference books.)

                    Note which translator (compiler or assembler) produced the
                    incorrect object module and the circumstances. Please
                    report this error to Microsoft Corporation using the
                    Product Assistance Request form included with the
                    documentation.

    L1063          out of memory for CodeView information

                    Too many linked object (".OBJ") files contain debugging
                    information. Turn off the Produce Debug Code option in the
                    Make EXE file dialog box.

    L1070          segment size exceeds 64K

                    A single segment contained more than 64K of code or data.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    Try compiling and linking using the large model.

    L1071          segment _TEXT larger than 65520 bytes

                    This error is likely to occur only in small-model C
                    programs, but it can occur when any program with a segment
                    named _TEXT is linked using the /DOSSEG option. Small-model
                    C programs must reserve code addresses 0 and 1; this range
                    is increased to 16 for alignment purposes.

    L1072          common area longer than 65536 bytes

                    The program had more than 64K of communal variables. This
                    error occurs only with programs produced by compilers that
                    support communal variables.

    L1080          cannot open list file

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    The disk or the root directory was full.

                    Delete or move files to make space.

    L1081          out of space for run file

                    The disk on which the executable file was being written was
                    full.

                    Free more space on the disk and restart LINK.

    L1083          cannot open run file

                    The disk or the root directory was full.

                    Delete or move files to make space.

    L1084          cannot create temporary file
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
    L1084          cannot create temporary file

                    The disk or root directory was full.

                    Free more space in the directory and restart LINK.

    L1085          cannot open temporary file

                    The disk or the root directory was full.

                    Delete or move files to make space.

    L1086          scratch file missing

                    An internal error has occurred.

                    Note the circumstances of the problem and contact Microsoft
                    Corporation using the Product Assistance Request form
                    included with the documentation.
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    included with the documentation.

    L1087          unexpected end-of-file on scratch file

                    The disk with the temporary output file from LINK was
                    removed.

    L1088          out of space for list file

                    The disk where the listing file is being written is full.

                    Free more space on the disk and restart LINK.

    L1089          filename : cannot open response file

                    LINK could not find the specified response file.

                    This usually indicates a typing error.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

    L1090          cannot reopen list file

                    The original disk was not replaced at the prompt.

                    Restart LINK.

    L1091          unexpected end-of-file on library

                    The disk containing the library probably was removed.

                    Replace the disk containing the library and run LINK again.

    L1093          object not found

                    One of the object files specified in the input to LINK was
                    not found.

                    Restart LINK and specify the object file.
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    Restart LINK and specify the object file.

    L1101          invalid object module

                    One of the object modules was invalid.

                    If the error persists after recompiling, please contact
                    Microsoft Corporation using the Product Assistance Request
                    form included with the documentation.

    L1102          unexpected end-of-file

                    An invalid format for a library was encountered.

    L1103          attempt to access data outside segment bounds

                    A data record in an object module specified data extending
                    beyond the end of a segment. This is a translator error.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    Note which translator (compiler or assembler) produced the
                    incorrect object module and the circumstances in which it
                    was produced. Please report this error to Microsoft
                    Corporation using the Product Assistance Request form
                    included with the documentation.

    L1104          filename : not valid library

                    The specified file was not a valid library file. This error
                    causes LINK to abort.

    L1113          unresolved COMDEF; internal error

                    Note the circumstances of the failure and contact Microsoft
                    Corporation using the Product Assistance Request form
                    included with the documentation.

    L1114          file not suitable for /EXEPACK; relink without
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
    L1114          file not suitable for /EXEPACK; relink without

                    For the linked program, the size of the packed load image
                    plus packing overhead was larger than that of the unpacked
                    load image.

                    Relink without the /EXEPACK option.

    L1115          /QUICKLIB, overlays incompatible

                    You specified overlays and used the /QUICKLIB option. These
                    cannot be used together.

    L2001          fixup(s) without data

                    A FIXUPP record occurred without a data record immediately
                    preceding it. This is probably a compiler error. (See the
                    Microsoft MS-DOS Programmer's Reference for more
                    information on FIXUPP.)
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    information on FIXUPP.)

    L2002          fixup overflow near number in frame seg segname target seg
                    segname target offset number

                    The following conditions can cause this error:

                    ■ A group is larger than 64K.

                    ■ The program contains an intersegment short jump or
                    intersegment short call.

                    ■ The name of a data item in the program conflicts with the
                    name of a library subroutine included in the link.

                    ■ An EXTRN declaration in an assembly-language source file
                    appeared inside the body of a segment, as in the following
                    example:

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    code   SEGMENT public 'CODE'
                        EXTRN   main:far
                    start  PROC    far
                        call    main
                        ret
                    start  ENDP
                    code   ENDS

                    The following construction is preferred:

                    EXTRN   main:far
                    code   SEGMENT public 'CODE'
                    start  PROC    far
                        call    main
                        ret
                    start  ENDP
                    code   ENDS

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    Revise the source file and recreate the object file. (See
                    the Microsoft MS-DOS Programmer's Reference for information
                    about frame and target segments.

    L2003          intersegment self-relative fixup at offset in segment
                    segname

                    You tried to make a near call or jump to a far entry in
                    segment segname at offset.

                    Change the call or jump to far or make the entry near.

    L2004          LOBYTE-type fixup overflow

                    A LOBYTE fixup generated an address overflow. (See the
                    Microsoft MS-DOS Programmer's Reference for more
                    information.)

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

    L2005          fixup type unsupported

                    A fixup type occurred that is not supported by the
                    Microsoft linker. This is probably a compiler error.

                    Note the circumstances of the failure and contact Microsoft
                    Corporation using the Product Assistance Request form
                    included with the documentation.

    L2011          name : NEAR/HUGE conflict

                    Conflicting NEAR and HUGE attributes were given for a
                    communal variable. This error can occur only with programs
                    produced by compilers that support communal variables.

    L2012          name : array-element size mismatch

                    A far communal array was declared with two or more
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    A far communal array was declared with two or more
                    different array-element sizes (for instance, an array was
                    declared once as an array of characters and once as an
                    array of real numbers). This error occurs only with
                    compilers that support far communal arrays.

    L2013          LIDATA record too large

                    A LIDATA record contains more than 512 bytes. This error is
                    usually caused by a compiler error.

    L2024          name : symbol already defined

                    LINK has found a public-symbol redefinition. Remove extra
                    definition(s).

    L2025          name : symbol defined more than once

                    Remove the extra symbol definition from the object file.
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    Remove the extra symbol definition from the object file.

    L2029          unresolved externals

                    One or more symbols were declared to be external in one or
                    more modules, but they were not publicly defined in any of
                    the modules or libraries. A list of the unresolved external
                    references appears after the message, as shown in the
                    following example:

                    unresolved externals
                    EXIT in file(s):
                    MAIN.OBJ (main.for)
                    OPEN in file(s):
                    MAIN.OBJ (main.for)

                    The name that comes before in file(s) is the unresolved
                    external symbol. On the next line is a list of object
                    modules that have made references to this symbol. This
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    modules that have made references to this symbol. This
                    message and the list are also written to the map file, if
                    one exists.

    L2041          stack plus data exceed 64K

                    The total size of near data and the stack exceeds 64K.
                    Reduce the stack size to control the error.

                    LINK tests for this condition only if the /DOSSEG option is
                    enabled. This option is automatically enabled by the
                    library startup module.

    L2043          Quick library support module missing

                    You did not specify, or LINK could not find, the object
                    module or library required for creating a Quick library. In
                    the case of QuickBASIC, the library provided is BQLB45.LIB.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

    L2044          name : symbol multiply defined, use /NOE

                    LINK has found a possible public-symbol redefinition. This
                    error is often caused by redefining a symbol defined in a
                    library.

                    Relink using the /NOEXTDICTIONARY option.

                    This error in combination with error L2025 for the same
                    symbol indicates a real redefinition error.

    L4011          PACKCODE value exceeding 65500 unreliable

                    Packcode segment sizes that exceed 65,500 bytes may be
                    unreliable on the Intel(R) 80286 processor.

    L4012          load-high disables EXEPACK

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

                    The /HIGH and /EXEPACK options cannot be used at the same
                    time.

    L4015          /CODEVIEW disables /DSALLOCATE

                    The /CODEVIEW and /DSALLOCATE options cannot be used at the
                    same time.

    L4016          /CODEVIEW disables /EXEPACK

                    The /CODEVIEW and /EXEPACK options cannot be used at the
                    same time.

    L4020          name : code-segment size exceeds 65500

                    Code segments of 65,501-65,536 bytes in length may be
                    unreliable on the Intel 80286 processor.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

    L4021          no stack segment

                    The program did not contain a stack segment defined with
                    STACK combine type. This message should not appear for
                    modules compiled with Microsoft QuickBASIC, but it could
                    appear for an assembly-language module.

                    Normally, every program should have a stack segment with
                    the combine type specified as STACK. You can ignore this
                    message if you have a specific reason for not defining a
                    stack or for defining one without the STACK combine type.
                    Linking with versions of LINK earlier than Version 2.40
                    might cause this message, since these linkers search
                    libraries only once.

    L4031          name : segment declared in more than one group

                    A segment was declared to be a member of two different
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
                    A segment was declared to be a member of two different
                    groups.

                    Correct the source file and recreate the object files.

    L4034          more than 239 overlay segments; extra put in root

                    The program designates more than 239 segments to go in
                    overlays. When this error occurs, segments beginning with
                    number 234 are placed in the root, the permanently resident
                    portion.

    L4045          name of output file is name

                    The prompt for the run-file field gave an inaccurate
                    default because the /QUICKLIB option was not used early
                    enough. The output will be a Quick library with the name
                    given in the error message.

    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────

    L4050          too many public symbols for sorting

                    The number of public symbols exceeds the space available
                    for sorting the symbols as requested by the /MAP option.
                    The symbols are left unsorted.

    L4051          filename : cannot find library

                    LINK could not find the specified file.

                    Enter a new file name, a new path specification, or both.

    L4053          VM.TMP : illegal file name; ignored

                    VM.TMP appeared as an object-file name. Rename the file and
                    rerun LINK.

    L4054          filename : cannot find file
    Number         LINK Error Message
    ──────────────────────────────────────────────────────────────────────────
    L4054          filename : cannot find file

                    LINK could not find the specified file. Enter a new file
                    name, a new path specification, or both.
    ──────────────────────────────────────────────────────────────────────────



I.4  LIB Error Messages

    Error messages generated by the Microsoft Library Manager, LIB, have one
    of the following formats:

    {filename| LIB} : fatal error U1xxx: messagetext
    {filename| LIB} : error U2xxx: messagetext
    {filename| LIB} : warning U4xxx: messagetext

    The message begins with the input-file name (filename), if one exists, or
    with the name of the utility. If possible, LIB prints a warning and
    continues operation. In some cases errors are fatal, and LIB terminates
    processing.

    LIB may display the following error messages:


    Number                   LIB Error Message
    ──────────────────────────────────────────────────────────────────────────
    U1150                    page size too small

                            The page size of an input library was too small,
                            which indicates an invalid input .LIB file.

    U1151                    syntax error : illegal file specification

                            A command operator such as a minus sign (-) was
                            given without a following module name.

    U1152                    syntax error : option name missing

                            A forward slash (/) was given without an option
                            after it.

    U1153                    syntax error : option value missing

                            The /PAGESIZE option was given without a value
                            following it.

    U1154                    option unknown

                            An unknown option was given. Currently, LIB
                            recognizes only the /PAGESIZE option.

    U1155                    syntax error : illegal input

                            The given command did not follow correct LIB
                            syntax as specified in Appendix G, "Compiling
                            and Linking from DOS."

    U1156                    syntax error

                            The given command did not follow correct LIB
                            syntax as specified in Appendix G, "Compiling
                            and Linking from DOS."

    U1157                    comma or new line missing

                            A comma or carriage return was expected in the
                            command line but did not appear. This may
                            indicate an inappropriately placed comma, as in
                            the line that follows:

                            LIB math.lib,-mod1+mod2;

                            The line should have been entered as follows:

                            LIB math.lib -mod1+mod2;

    U1158                    terminator missing

                            Either the response to the "Output library"
                            prompt or the last line of the response file used
                            to start LIB did not end with a carriage return.

    U1161                    cannot rename old library

                            LIB could not rename the old library to have a
                            .BAK extension because the .BAK version already
                            existed with read-only protection.

                            Change the protection on the old .BAK version.

    U1162                    cannot reopen library

                            The old library could not be reopened after it
                            was renamed to have a .BAK extension.

    U1163                    error writing to cross-reference file

                            The disk or root directory was full.

                            Delete or move files to make space.

    U1170                    too many symbols

                            More than 4609 symbols appeared in the library
                            file.

    U1171                    insufficient memory

                            LIB did not have enough memory to run.

                            Remove any shells or resident programs and try
                            again, or add more memory.

    U1172                    no more virtual memory

                            Note the circumstances of the failure and notify
                            Microsoft Corporation using the Product Assistant
                            Request form included with the documentation.

    U1173                    internal failure

                            Note the circumstances of the failure and notify
                            Microsoft Corporation using the Product Assistant
                            Request form included with the documentation.

    U1174                    mark: not allocated

                            Note the circumstances of the failure and notify
                            Microsoft Corporation using the Product
                            Assistance Request form included with the
                            documentation.

    U1175                    free: not allocated

                            Note the circumstances of the failure and notify
                            Microsoft Corporation using the Product
                            Assistance Request form included with the
                            documentation.

    U1180                    write to extract file failed

                            The disk or root directory was full.

                            Delete or move files to make space.

    U1181                    write to library file failed

                            The disk or root directory was full.

                            Delete or move files to make space.

    U1182                    filename : cannot create extract file

                            The disk or root directory was full, or the
                            specified extract file already existed with
                            read-only protection.

                            Make space on the disk or change the protection
                            of the extract file.

    U1183                    cannot open response file

                            The response file was not found.

    U1184                    unexpected end-of-file on command input

                            An end-of-file character was received prematurely
                            in response to a prompt.

    U1185                    cannot create new library

                            The disk or root directory was full, or the
                            library file already existed with read-only
                            protection.

                            Make space on the disk or change the protection
                            of the library file.

    U1186                    error writing to new library

                            The disk or root directory was full.

                            Delete or move files to make space.

    U1187                    cannot open VM.TMP

                            The disk or root directory was full.

                            Delete or move files to make space.

    U1188                    cannot write to VM

                            Note the circumstances of the failure and notify
                            Microsoft Corporation using the Product
                            Assistance Request form.

    U1189                    cannot read from VM

                            Note the circumstances of the failure and notify
                            Microsoft Corporation using the Product
                            Assistance Request form.

    U1190                    interrupted by user

                            The user pressed CTRL+C or CTRL+BREAK.

    U1200                    name : invalid library header

                            The input library file had an invalid format. It
                            was either not a library file, or it had been
                            corrupted.

    U1203                    name : invalid object module near location

                            The module specified by name was not a valid
                            object module.

    U2152                    filename : cannot create listing

                            The directory or disk was full, or the
                            cross-reference-listing file already existed with
                            read-only protection.

                            Either make space on the disk or change the
                            protection of the cross-reference-listing file.

    U2155                    modulename : module not in library; ignored

                            The specified module was not found in the input
                            library.

    U2157                    filename : cannot access file

                            LIB was unable to open the specified file.

    U2158                    libraryname : invalid library header; file
                            ignored

                            The input library had an incorrect format.

    U2159                    filename : invalid format hexnumber; file ignored

                            The signature byte or word hexnumber of the given
                            file was not one of the following recognized
                            types: Microsoft library, Intel library,
                            Microsoft object, or XENIX(R) archive.

    U4150                    modulename : module redefinition ignored

                            A module was specified to be added to a library
                            but a module with the same name was already in
                            the library. Or, a module with the same name was
                            found more than once in the library.

    U4151                    symbol : symbol redefined in module modulename,
                            redefinition ignored

                            The specified symbol was defined in more than one
                            module.

    U4153                    number : page size too small; ignored

                            The value specified in the /PAGESIZE option was
                            less than 16.

    U4155                    modulename: module not in library

                            A module to be replaced is not in the library.
                            LIB adds the module to the library.

    U4156                    libraryname : output-library specification
                            ignored

                            An output library was specified in addition to a
                            new library name. For example, specifying

                            LIB new.lib+one.obj,new.lst,new.lib

                            where new.lib does not already exist causes this
                            error.

    U4157                    Insufficient memory, extended dictionary not
                            created.

                            LIB was unable to create the extended dictionary.
                            The library is still valid, but LINK cannot take
                            advantage of extended dictionaries to link
                            faster.

    U4158                    Internal error, extended dictionary not created.

                            LIB was unable to create the extended dictionary.
                            The library is still valid, but LINK cannot take
                            advantage of extended dictionaries to link
                            faster.



────────────────────────────────────────────────────────────────────────────
Index


Symbols

#
(+)
    operator, combining strings
'
*
* (asterisk)
    AS,
    LIB command symbol
, (comma)
    fields, ending
    variable separator
$ (dollar sign), string-type suffix
" (double quotes), ending
/ (forward slash), LINK option character
(/), LINK option character
- (minus sign)
- (minus sign), LIB command symbol
-* (minus sign-asterisk, LIB
-+ (minus sign-plus sign), LIB command symbol
"" (null string)
+ (plus)
    operator, combining strings
    sign, LIB command
; (semicolon)
    LIB
; (semicolon),
; (semicolon), LIB command symbol
# USING

A

,A option
    BASICA
    #INCLUDE files
/A option (BC)
ABS function
ABSOLUTE
Absolute
ABSOLUTE statement
access, contrasted with
after input
/AH option
ALIAS, DECLARE
aliasing
Aliasing, variable
all in a module
allowable
allowed
Alphabetizing
alternative
among versions
AND
    operator
and functions. See individual statement and function names
    FIELD
    GET
    INPUT #
    PUT
    WRITE #
and GW-BASIC
AND option
    PUT
and replacing
Angle measurements
Animate mode
Animation
    GET and PUT
    graphics statements, simple
    image flicker,
    PALETTE USING
    screen pages
Apostrophe (')
    entering
    introducing comments
(apostrophe), introducing comments
Arc
Arctangent,
Arguments
    correct type and number, checking for
    LINK options
    parameters
    agreement
    distinguished from
    passing
    by
    by value
    described
    limits on
    SUB procedures,
arguments by reference
Array-bound
Array-bound functions
Arrays
    dynamic
    ERASE
    REDIM statement
    elements, passing
    format
    argument list
    FUNCTION statement
    parameter
    parameter list
    SHARED statement
    SUB statement
    LBOUND function
    limits (table)
    lower-bound function
    memory allocation
    procedures
    passing
    passing to
    sharing
    row-order option
    static, ERASE statement
    subscripts,
    subscripts, specifying
    lower
    UBOUND (upper-bound) function
    variables
arrays
    limits
Arrow keys. See DIRECTION keys
ASC function
ASCII
    character
    character codes
    CHR$
    determining
    (table)
    files, reading as sequential
Aspect ratio
    CIRCLE
    correction for
    defined
    screen size, computing for
    squares,
Assembly-language listings
Assignment statements
(asterisk)
    AS, fixed-length string with
Asterisk (*)
    fixed-length
Asterisk (*)
ATN function
attribute
    described
    DIM
    described
    sharing
    REDIM
    variables, making global
Attributes
    colors, assigning
    screen
    screen modes, EGA and VGA
    STATIC
attributes. See Attributes
automatic
Automatic
Automatic variables
AUX (device name)

B

Background color default
Background music
.BAS file, execution by QuickBASIC
BASIC
    error codes
    reserved words
    run-time errors
BASIC, compatibility
BASICA
    compatibility
    QuickBASIC, converting to
/BATCH option (LINK)
Batch file, when to use
Baud rate
BC command
    options
    /V
BC
BC command
    command line, invoking from
    file-name usage
    options
    /A
    /AH
    /C
    /D
    /E
    (list)
    /MBF
    new (table)
    not used (table)
    /O
    /R
    /S
    /V
    /W
    /X
    /ZD
    /ZI
    versions, differences among
BC.EXE
BEEP statement
Binary
    file access
    OPEN statement syntax
    random
    versions, differences among
    input
    numbers, converting to hexadecimal
Binary, differences from
Binary format
    IEEE format, differences from
Binary-access
Binary-access files
    creating
    opening
    reading
    writing to
Bit
Bitwise operators. See Logical operators
Blank COMMON block
BLOAD statement
block
Block IF...THEN...ELSE
Bold text, indicating keywords
B_OnExit routine
Boolean
    constants
    expressions
    definition
    logical operators
    other
    relational operators (table)
bound
bound for
Boxes,
Boxes, LINE statement
BRUN40.LIB, default for linker
BSAVE statement
buffer
BUILDLIB utility
by QuickBASIC
by value
    defined

C

/C option (BC)
CALL
CALL ABSOLUTE
CALL INT86OLD statement
CALL INT86XOLD statement
CALL INTERRUPT
CALL INTERRUPT statement
CALL INTERRUPTX statement
CALL statement
    DECLARE, used with
    described
    optional
    QuickBASIC/interpreter differences
    SUB procedure, calling
calls
CALLS statement
CALLS statement (non-BASIC)
Carriage-return-line-feed
Cartesian coordinates. See View coordinates
Case
    sensitivity
    significance,
CASE clause. See SELECT CASE statement
CASE$ function
CDBL function
CDECL, use in DECLARE statement
CGA
    palette, changing
    screen modes supported by
CHAIN
CHAIN statement
    described
    QuickBASIC/interpreter differences
Chaining programs,
Chaining programs, statements used. See individual
changing number of
changing with
Characters
    how stored
    limits
CHDIR statement
Checking between statements option
Choosing
    commands,
    options, differences among versions
CHR$ function
CINT function
CIRCLE
CIRCLE statement
    arcs
    circles
    described
    ellipses
    pie shapes
Circles, drawing
clause
CLEAR
CLEAR statement
CLNG function
CLOSE statement
CLS statement
/CODEVIEW option (LINK)
codes
    determining with the ASC function
    storing characters in memory
CodeView
COLOR
Color
Color Graphics Adapter. See CGA
COLOR statement
    background color, controlling
    described
    foreground color, controlling
    palette, changing with
    screen mode 1, syntax in
Colors
    background,
    CGA, using with
    clarity, tradeoff with
    foreground,
    foreground, controlling
    graphics statements, specifying in
    PALETTE
    PALETTE,
Columns
    changing
    skipping
COM
COM devices
COM statements
Comma (,)
    ending fields
    variable separator
command
    option
    /MBF
    /RUN option
COMMAND$
command, DRAW
COMMAND$ function
    limits
Command line
    BASIC program passing to
    Quick
command line
command symbol
command-line
Commands
    BC. See BC command
    Linker. See LINK
    versions,
commands
Commands QB. See QB command
Comments
    REM statement
Comments, introducing with apostrophe
COMMON
COMMON block
    blank
    named
COMMON statement
    AS clause
    chaining programs
    QuickBASIC/interpreter differences
    SHARED
    SHARED attribute
    defined
    described
    variables, sharing across
    variables,
compared with
Comparing
Comparing strings
    relational operators, with
Compatibility
    BASICA
    versions,
compatibility among
Compile options
Compile-time error messages
Compiling from DOS
Complex numbers, defined
CON (device name)
Concatenation, defined
CONS
CONST statement
Constants
    input list
    procedures, passing to
    string, literal and symbolic
    symbolic
constants
    format in program examples
    string
contrasted with
Control-flow
Control-flow statements. See individual
    CALL
    CALL ABSOLUTE
    CALLS
    CHAIN
    DO...LOOP
    FUNCTION
    GOSUB...RETURN
    GOTO
    IF...THEN...ELSE
    ON GOTO
    ON...GOTO
    RETURN
    SELECT CASE
Control-flow structures
    BASIC, used in (table)
    decision
    defined
    indenting
    new
Controlling
    linker
    segments
    stack size
controlling
conventions
    BASIC programs
Converting
    BASICA and GW-BASIC programs
    data
    IEEE format, program for
converting to QuickBASIC
Coordinates
    absolute
    STEP, specifying
    VIEW SCREEN, specifying
    physical
    GET
    pixels,
    view
    pixels, locating
    relative
    defined
    STEP
    VIEW
    view
    GET statement
    physical coordinates, translating
    WINDOW,
    viewport,
coordinates. See Coordinates, absolute
    WINDOW statement
coordinates, translating to
COS
(/CP)
/CPARMAXALLOC option (LINK)
creating outside QuickBASIC environment
Cross-reference-file
CSNG function
CSRLIN function
Cursor
    graphics
    text
    defined
    LOCATE, positioning
    location, finding
    shape, changing
cursor
CVD function
CVDMBF
CVDMBF function
CVI function
CVL
CVL function
CVS
CVSMBF function
CVtype statement

D

/D option (BC)
Data
Data files
    closing
    creating
    defined
    file numbers
    file-access errors,
    opening
    organization
    random-access
    reading
    records, adding
    sequential
data, preventing
DATA statement
Data types
    TYPE statement
Data-file buffer
DATE$
Date and time
    functions
    DATE$
    TIME$
    statements
    DATE$
    TIME$
DATE$ statement
Debug option
debugger, debugging with
debugger, LINK option for
Debugging
    /CODEVIEW (LINK) option
    statements
    versions, differences among
Decision
Decision structures
    defined
    IF...THEN...ELSE
    block
    single-line
    SELECT CASE
Declarations. See individual statement names
    CONST
    DECLARE statement (BASIC procedures)
    DECLARE statement (non-BASIC
    DEFtype
    DIM statement
DECLARE
DECLARE statement
    described
    include
    QuickBASIC,
    versions, differences among
    where required
Declaring arrays
    limits
DEF FN functions
    exit from, alternative
    FUNCTION procedure, contrasted with
    local
DEF FN statement
DEF SEG statement
DEFDBL statement
defining with
definitions
DEFINT statement
DEFLNG statement
DEFSNG statement
DEFSTR
DEFtype
    QuickBASIC/interpreter differences
DEFtype
Degrees
    compared with
    converting to radians
deleting from, including, and replacing
Device I/O, contrasted with file I/O
(device name)
Devices
    COM
    CONS
    function
    function handling
    IOCTL$
    PEN
    I/O modes, valid
    KEYBD
    LPT
    names
    SCRN
    statement handling
    IOCTL
    OPEN
    OUT
    WAIT
Device-status information
differences among
Differences among versions
    file compatibility
    (table)
differences among versions
DIM
DIM statement
    AS
    described
    QuickBASIC/interpreter differences
    SHARED attribute
    defined
    example,
    variables,
    TO clause
Dimensioning
DIRECTION
keys, trapping
Directory
Directory statements
    CHDIR
    FILES
    MKDIR
    RMDIR
Display memory, PCOPY statement
displaying
DO UNTIL statement
DO WHILE statement
Document conventions
Dollar
DO...LOOP
DO...LOOP statement
    described
    exit from,
    exit from, alternative
    keyword
    UNTIL
    WHILE
    loop test, location of
    syntax
    WHILE...WEND,
DOS
DOS-level
option (LINK)
/DOSSEG
Dotted lines, drawing
Double quotes ("), ending fields
Double-precision
Double-precision numbers
    size limits
DRAW statement
    described
    figures
    rotating
    scaling
    macro commands in
    QuickBASIC/interpreter differences
    VARPTR$
drawing
Dumb terminal, defined
Dummy arguments, POS function
#DYNAMIC metacommand
Dynamic arrays
    #DYNAMIC metacommand
    ERASE
    REDIM

E

/E option
    BC
    QB
/E option
    BC
Editing, differences
EGA, changing palette
elements
Ellipses, drawing
ELSE clause
ELSEIF clause
END
END DEF
END FUNCTION statement
END IF statement
END SELECT
END statement
END SUB statement
END TYPE statement
End-of-line markers
Enhanced Graphics Adapter. See EGA
key, equivalent to SPACEBAR
ENTER
ENVIRON$ function
ENVIRON statement
environment variable
Environment variables
    described
    LIB
    LINK
    TMP, used by LINK
Environment-string
EOF
EOF function
EQV
ERASE statement
Erasing
ERDEV$
ERDEV$ function
ERL function
ERR
    code
    function
ERR
ERRDEV function
ERRDEV$ function
Error
Error codes
Error messages
    compile-time
    described
    invocation
    LIB
    LINK
    numbers, limits (table)
    redirection
    run-time
ERROR statement
Error trapping
    ERR, identifying errors with
    ERROR statement
    error-handling routine
    file-access errors
    multiple modules
    Quick libraries
    screen modes, inappropriate
    SUB or FUNCTION procedures
    syntax,
Error-handling
Error-handling routines
    parts
    specifying
Error-handling statements
    ERDEV
    ERR, ERL
Error-message window
event
Event polling, event trapping, contrasted with
Event trapping. See specific
    activating
    background music
    described
    disabling
    keystrokes
    multiple modules
    options,
    polling, contrasted with
    routines, event-handling
    statements and functions, summary of (table)
    SUB
    suspending
    syntax, error-trapping
    trappable events
    types
    types of
    COM
    KEY
    PEN
    PLAY
    TIMER
event trapping
    PLAY ON statement
Event trapping, Quick library
Event-handling
Event-handling routine
Event-trapping
Event-trapping option
Event-trapping statements. See individual
    COM
    KEY LIST
    KEY OFF
    ON
    ON event
    PEN ON, OFF, and STOP
    PLAY ON, OFF, and STOP
    TIMER ON, OFF, and STOP
    UEVENT
event-trapping syntax, contrasted with
examples
Executable
/EXEPACK option (LINK)
EXIT
EXIT DEF statement
EXIT DO statement
EXIT FOR statement
EXIT FUNCTION statement
EXIT statement
EXIT SUB statement
Exiting, functions and procedures
EXP function
Expressions
    Boolean
    false
    lists
    procedures, passing to
    true
expressions, comparing with
Expressions, string. See Strings
Extensions, file-name
Extra files produced
extracting and deleting

F

Factorial function
False expressions
FIELD
FIELD statement
    described
    random-access
    TYPE...END
Fields
    defined
    records
    random-access
    sequential
    sequential files, delimited
fields
fields, delimiting
figures with DRAW
File
    names
    characters,
    OPEN statement
    numbers
    CLOSE, freeing with
    FREEFILE, getting with
    freeing
    OPEN statement
    values, allowable
    pointers
file
    LIB
File
File access
    LOCK statement
    UNLOCK
    UNLOCK statement
File conversion
File handling. See statements;individual statement names
    functions
    EOF
    FILEATTR
    FREEFILE
    LOC
    LOF
    SEEK
    statements
    CHDIR
    CLOSE
    FIELD
    GET
    INPUT #
    KILL
    LOCK
    NAME
    OPEN
    RESET
    SEEK
File I/O
    defined
    device I/O contrasted
File names
    case sensitivity
    described
    restrictions
    truncation
file, temporary
File-access
File-access modes
    APPEND
    BINARY
    INPUT
    OUTPUT
    RANDOM
FILEATTR function
File-name extensions
File-naming
File-naming conventions
    BC command
    DOS
    LINK
    Quick libraries
Files
    ASCII format
    attributes
    extra, produced by QuickBASIC
    #INCLUDE
    length,
    limits (table)
    map
    map (LINK)
    random-access,
    random-access, statements
    random-access, statements and functions. See individual statement and
    function names
    sequential,
    sequential, statements and functions. See individual
    INPUT
    WRITE
    versions,
files
    adding to
    advantages of
    appending
    compact
    creating
    format
    number storage
    opening
    packing
    random-access files, contrasted with
    records
    statements and functions
    FIELD
    PRINT #
    PUT
    RSET
    statements not allowed
FILES
files, closing
files, contrasted with
files (LINK)
files, placing in
FILES statement
Filling. See Painting
filling
FIX function
fixed-length string with
Fixed-length strings
    parameter lists
    record
    variable length, contrasted with
    variables, stand-alone
Floating-point
    precision, within Quick libraries
FOR statement
Foreground color
    default
    screen mode 1
form
format
    numbers
    converting from
Formatting text output
FOR...NEXT
FOR...NEXT loops
    described
    execution,
    exit from, alternative
    STEP keyword
FOR...NEXT statement
Forward reference problem
Forward reference problem, DECLARE,
Forward slash
Fractal
FRE function
FRE statement
FREEFILE
FREEFILE function
from, alternative
FUNCTION
function
function
    data, reading
    files
    described
    DRAW, use with
    PLAY, use with
FUNCTION
function, arguments to
function (cosine)
Function keys, trapping
function name
function name
function names
FUNCTION procedures
    calling
    DECLARE statements
    described
    error
    event trapping
FUNCTION statement
    AS
    described
    include files,
    include files, restrictions with
function, using
FUNCTION...END FUNCTION statements. See FUNCTION procedures
Functions. See individual
    user defined
functions
    CDBL
    CVL
    INPUT$
    INT
    SIN

G

GET
GET statement
    described
    file I/O
    binary-access
    random-access
    graphics
    array, calculating size of
    syntax
    records, user-defined
Global variables
    DEF FN function
    FUNCTION procedure
    GOSUB routine
    SHARED attribute
    variable aliasing
GOSUB
GOSUB, contrasted with
GOSUB statement
GOSUB subroutines
    SUB procedures,
GOTO statement
    described
    #INCLUDE metacommand
Graphics. See statements;individual statement names
    functions. See individual statement names
    PALETTE
    PMAP
    POINT
    VIEW
    WINDOW
    statements
    BLOAD
    BSAVE
    CIRCLE
    COLOR
    DRAW
    GET
    LINE
    PAINT
    PALETTE
    PALETTE
    PRESET
    PSET
    PUT
    VIEW
    WINDOW
graphics
Graphics Adapter), changing palette
Graphics cursor. See Cursor, graphics
Graphics screen modes. See Screen modes
graphics statement, with
Graphics statements and functions, summary (table)
Graphics viewport. See Viewport,
GW-BASIC

H

/H option (QB)
handling
    functions. See individual function names
    LPOS
    statements. See individual statement names
    UNLOCK
/HELP option (LINK)
HEX$ function
Hexadecimal
High-resolution-display

I

IEEE
IEEE format
    accuracy
    converting to
    /MBF
    numbers
    converting
    exponential,
    Microsoft
    printing
    ranges
IF...THEN...ELSE
IF...THEN...ELSE statement
    described
    SELECT CASE
IF...THEN...ELSE statement, block
IF...THEN...ELSE statement, block form
(IGN)
/IGNORECASE option (LIB)
Ignore case
    LIB
    LINK
ignoring
Images
    GET, saving to memory with
    PUT, copying to screen with
Immediate window, limits
IMP operator
in
in map files
#INCLUDE metacommand
    COMMON
    description
    procedure declarations
    restrictions
Include
Include files
    COMMON
    procedures
    declaring
    not allowed
/INFORMATION option (LINK)
INKEY$ function
INP function
INPUT
input
    reading
INPUT
INPUT #
INPUT$
Input
INPUT$ function
    data, reading
    communications device
    standard input
    described
    example
    INPUT #, contrasted with
    LINE INPUT #, contrasted with
    modem, communicating with
Input functions. See individual
    COMMAND$
    INKEY$
    INP
Input past end error
INPUT # statement
    described
    example
    INPUT$,
    LINE INPUT #, contrasted with
INPUT statement
    carriage-return-line-feed sequence, suppressing
    defined
    described
    error
    example
    LINE INPUT,
    variable
Input statements. See individual
    DATA
    INPUT
    LINE
    READ
    RESTORE
Input/Output. See I/O
Insert mode, differences
INSTR function
INT function
INT86, INT86X replacements
    CALL
    CALL INT86OLD statements
INT86OLD
Integers
    converting
    converting to
    FIX function
    limits, (table)
Interpreted
INTERRUPT
Interrupt
INTERRUPT statements
Invocation error
I/O
    binary-access
    defined
    devices, from and to
    files, from and to
    ports
    standard
IOCTL$ function
IOCTL statement
Italic text, showing placeholders
items

J

Joysticks

K

Key
KEY LIST
KEY OFF statement
KEY ON statement
KEY statement
Key trapping
    activating
    keys
    DIRECTION
    user-defined
key trapping
KEYBD
Keyboard, reading input from
Keyboard scan codes
KEY(n) statements
Keys
    debugging, differences among versions
    DIRECTION,
    editing, differences among versions
    ENTER
    SPACEBAR
Keywords, format in program examples
KILL statement

L

(QB)
/L option
Labels
Language differences among
Last
Last point referenced
LBOUND function
LCASE$
LCASE$ function
leading blank
LEFT$
LEFT$ function
LEN
LEN function
    described
    sample
    sample program application
    use of
LET statement
letters
    file names
.LIB files. See Libraries, stand-alone
LIB
    case sensitivity
    command
    command symbols
    plus
    plus sign (+)
    default responses
    described
    invoking
    libraries, combining
    library
    library modules
    listing files
    options
    case sensitivity
    dictionary, no extended
    /IGNORECASE (/P)
    /NOEXTDICTIONARY
    /NOIGNORECASE
    page size, specifying
    /PAGESIZE
    response file
    search path
LIB command symbol
LIB error messages
LIB.EXE
Libraries. See Quick libraries
    default,
    default, ignoring
    described
    LINK, specifying for
    search
    stand-alone
    combining
    defined
    described
    LIB
    listing
    modules,
    object
    object modules,
    object modules, deleting from, including,
    parallel
    types, contrasted
    user.
libraries. See Quick Libraries
    CALL INT86OLD statement
    compatibility among versions
    compilation
    contents
    described
    use of
libraries, creating
library, creating from
Library Manager. See LIB
Limits, file size and complexity (table)
/LINENUMBERS option (LINK)
Line
LINE INPUT
LINE INPUT # statement
    described
    INPUT #, contrasted
    INPUT$, contrasted
LINE INPUT statement
    described
Line printer
LINE statement
    coordinate pairs, order of
    described
    lines and boxes, drawing
    sample applications
    syntax
Lines, drawing
(LINK)
LINK. See Libraries
    command line, invoking from
    defaults
    libraries, specifying
    options
    abbreviations
    arguments, numerical
    /BATCH (/B)
    BC, unsuitable for
    case sensitivity
    CodeView
    /CODEVIEW (/CO)
    command line, order
    /CPARMAXALLOC
    default libraries, ignoring
    displaying
    /DOSSEG (/DO)
    /EXEPACK (/E)
    /HELP (/HE)
    /IGNORECASE
    information,
    /INFORMATION (/I)
    line
    /LINENUMBERS (/LI)
    LINK
    linker
    map
    /MAP (/M)
    /NODEFAULTLIBRARYSEARCH
    /NOIGNORECASE
    /NOPACKCODE (/NOP)
    /PACKCODE (/PAC)
    paragraph space, allocating
    /PAUSE
    pausing
    Quick
    /QUICKLIB (/Q)
    segments
    /SEGMENTS
    segments,
    stack
    /STACK (/ST)
    output
    output file, temporary
    Quick libraries, other language routines
    response file
LINK environment variable
LINK error messages
LINK options
Linker. See LINK
LINK.EXE
Linking. See LINK
Linking from DOS
list
list, format of
listing
Listing files, cross-reference (LIB)
Literal constants,
Loading Quick libraries
LOC function
    described
    devices, with
    modem, communicating with
    SEEK,
local
Local variables
LOCATE statement
    cursor
    changing
    positioning
    described
    example
locating
LOCK statement
LOF
LOF function
    described
    devices
    example
    files
LOG function
Logical
Logical coordinates
    physical coordinates,
Logical operators
Long integers
    advantages of
    converting
    converting to
    limits (table)
Looping
Looping structures
    defined
    DO...LOOP
    WHILE...WEND
loops
    nesting
Lowercase
Lowercase letters
    uppercase, converting to
LPOS function
LPRINT statement, SPC function
LPRINT USING statement
LPT devices
LSET
LSET statement
.LST files. See Listing files
LTRIM$ function
    blanks, leading, printing numbers without
    described
    spaces, leading, stripping
    spaces, leading, stripping

M

Macro
Main module
libraries, use with
.MAK files, Quick
Make Library command
making global
Mandelbrot set
.MAP files. See Map files
/MAP option (LINK)
Map
Map files
Map files (LINK)
mapping to
Math functions
    ABS
    ATN
    COS
    CVSMBF
    EXP
    LOG
    MKSMBF$, MKDMBF$
    SIN
    SQR
    TAN
/MBF option
    BC
    QB
option, using with old programs
Memory
    available, calculating
    requirements, Quick libraries
Memory functions
Memory management
    functions. See individual
    FRE
    SETMEM
    statements
    CLEAR
    DEF
    ERASE
    individual
    PCOPY
Menu commands, differences among versions
message, invalid-input
messages
Metacommands
    described
    #DYNAMIC
    #INCLUDE See also #INCLUDE metacommand
    #STATIC
Microsoft
Microsoft Binary format
    numbers
Microsoft Library Manager. See LIB
MID$
    function
MID$
MID$ function
MID$ statement
Minimize String Data option
Mixed-language
Mixed-language programming
    ALIAS, use of
    CALL,
    CDECL,
    DECLARE
    Quick libraries
MKD$ function
MKDIR statement
MKDMBF$ function
MKI$ function
MKL$
MKS$ function
MKSMBF$ function
MKtype statement
MKtype$ statement
MKtypeMBF$ statement
mode 1
Modem, communicating with
modes
    BINARY
    INPUT
    OUTPUT
modes, EGA and VGA
Module-level code
Modules. See Multiple
modules
    event trapping
modules
modules, deleting from, including, and replacing
monochrome
Monochrome screen mode
Moving images with PUT
Multiple
Multiple modules
    advantages
    error
    error trapping
    event trapping
    loading
    main module
    programming style
    Quick libraries
    size limits (table)
    variables,
    variables, sharing
    versions, differences among
Music
    background
    statements
Music
Music event trapping
    ON PLAY GOSUB statement
music option

N

NAME statement
Named COMMON
Named COMMON block
names
Names, devices
nclude files
    nesting limits (table)
New line. See Carriage-return-line-feed sequence
NEXT statement
No extended dictionary, library
NOCOM.OBJ file
(/NOD)
/NODEFAULTLIBRARYSEARCH option (LINK)
/NOEXTDICTIONARY option (LIB)
(/NOI)
/NOIGNORECASE option (LIB)
/NOIGNORECASE option (LINK)
non-BASIC procedures
/NOPACKCODE option (LINK)
NOT
not generated by
NOT operator
notation
    placeholders
Notational
NUL (device name)
Null
NUL.MAP file
number, displaying
number of on screen
Numbers
    positive, printing without leading blank
    random-access files, storage in
    screen,
    screen, printing on
    strings, representing as
numbers
    converting to
Numeric
Numeric conversions
    CVD
    CVI
    CVL function
    CVS function
    double-precision
    integer
    single-precision
Numeric functions. See individual function names
    CINT
    CLNG
    CSNG
    CVD
    CVI
    CVS
    FIX
    RND
    SGN

O

/O option (BC)
Object
    files
    modules
OCT$ function
Octal conversion
of
    STRIG
of, printing
on
ON
ON COM statement
ON ERROR
ON ERROR GOTO statement
    described
    example
    syntax
ON event
ON event GOSUB statements
ON event statements
ON expression statement
ON PEN statement
ON PLAY GOSUB statement
ON PLAY statement
ON statement
ON STRIG
ON TIMER statement
ON UEVENT statement
ON...GOSUB statement
ON...GOTO statement
OPEN
OPEN COM statement
OPEN statement
    described
    file names in
    file-access
    file-access modes
    APPEND
    INPUT
    OUTPUT
    LEN
opening for I/O
operator
Operators
    logical
    relational
    relational,
operators
    string comparisons
option
OPTION BASE statement
option (QB)
option required
Options, BC. See BC command
Options, LIB. See LIB
Options, LINK. See LINK
or FUNCTION procedures, within
OR operator
ordering
OUT statement
Output. See statements;individual function names
    functions. See individual function names
    LPOS
    POS
    TAB
    line
    statements
    BEEP
    CLS
    LPRINT
    OUT
    PRINT
    PRINT
    PRINT #
    PRINT USING
    PUT
    WRITE
    WRITE #
Overlay linker. See LINK
Overtype mode, differences among versions

P

(/P)
/PACKCODE option (LINK)
/PAGESIZE option (LIB)
Page size, library
Pages. See Screen
pages
pages
PAINT
PAINT statement
    argument
    background
    border
    described
    numeric expression
    shapes, filling
    colored
    patterned
    string expression
    tiling
    monochrome, defining
    multicolor, defining
Painting
    colors
    exterior
    interior
    patterns,
    patterns
    described
    multicolor
PALETTE
PALETTE statement
PALETTE USING statement
Palettes
    COLOR, changing with
    PALETTE, changing with
    screen
Paragraph space
Parameter list, defined
Parameters
    arguments
    agreement with
    distinguished from
    DECLARE, format in
    number and
Passing
Passing by reference
Passing by value
    DEF FN, used in
passing to
path
Path names, limits (table)
Pattern tiles, editing on screen
Patterns
    monochrome
    multicolor
    shapes,
    tile size
(/PAU)
/PAUSE option (LINK)
pausing
Pausing program execution, FOR...NEXT statement
PCOPY
PCOPY statement
PEEK function
PEN function
PEN OFF statement
PEN ON statement
PEN STOP
Peripheral devices
Physical coordinates
Pie shapes, drawing
Pixels
    coordinates, locating with
    defined
    PRESET,
    PSET, plotting with
    text
Place markers, limits (table)
planes
PLAY
PLAY function
PLAY OFF statement
PLAY statement
    background
    described
    QuickBASIC/interpreter differences
    VARPTR$ function, using
PLAY STOP
plotting with
Plus
Plus (+)
    sign, LIB command symbol
PMAP function
PMAP statement
POINT function
point referenced
POKE statement
Polling
POS function
Positive numbers, printing without
PRESET
PRESET option, with PUT graphics statement
PRESET statement
    color option, using with
    described
previous
PRINT
PRINT # statement
    described
    record
    WRITE #, contrasted with
PRINT statement
    described
    example
    text, wrapping
PRINT # USING statement
PRINT USING # statement
PRINT USING statement
Print zone
Printing
    numbers
    negative
    positive
    text
    printer,
    screen, to
printing
printing on
PRN
Procedures
    arguments
    passing by reference
    passing by value
    arrays
    benefits
    calling
    constants
    defining
    described
    expressions
    format
    argument-list
    parameter-list
    include files, declarations in
    libraries, user
    limits (table)
    modules, multiple
    moving
    passing
    Quick
    records
    recursive
    statements
    FUNCTION...END
    FUNCTION...END FUNCTION
    SUB...END
    SUB...END SUB
    summary (table)
    statements not allowed
    STATIC variables
    variables
    global
    local
    sharing
    variables,
    variables, automatic
procedures
    DEF FN function, contrasted with
    exit from, alternative
procedures)
procedures
Procedures-only module
Program
    suspension
    termination
program application
programming
    Quick libraries
Programming environment, differences in versions
Programming style
    multiple
Programs
    BASICA, GW-BASIC,
ProKey, using QuickBASIC with
prompting, preventing
PSET
PSET statement
    described
    example
    STEP option
PUT
PUT statement
    described
    file
    file I/O
    binary-access
    random-access
    graphics
    animation
    interaction with background, controlling
    syntax
    records,

Q

QB
QB command
    /AH
    /H option
    /L option
    /MBF
    /MBF option
    /RUN option
    versions, differences among
QB.QLB
    library
    loading, automatic
QLBDUMP.BAS
Quick
Quick libraries
    advantages
    CALL ABSOLUTE statement
    CALL INTERRUPT
    contents
    described
    listing
    reading
    creating
    command line, from
    described
    files needed
    LINK, using
    QuickBASIC, from within
    default
    described
    end user, delivery to
    errors, trapping in
    executable files, making compact
    files
    needed to create
    produced by
    floating-point precision
    include files, declaring procedures with
    loading
    .MAK file, updating
    memory
    mixed languages
    naming
    object code, linking
    routines, other languages
    search
    updating
option (LINK)
/QUICKLIB

R

/R option (BC)
Radians
radians
Random access, contrasted with binary access
Random numbers
Random-access
Random-access files
    opening
    records
    adding
    organizing
    reading
    sequential
    sequential files, contrasted with
    statements
RANDOMIZE statement
READ statement
Record numbers
    limits (table)
    random-access files, indexing in
Record numbers, random-access files
    indexing in
Records
    binary-access files, writing
    data, overwriting
    defined
    defining
    described
    fixed-length
    procedures,
    procedures, passing to
    random-access
    random-access files
    reading
    storing
    writing
    sequential
    sequential files
    appending
    reading
    storing
    variable length
records, defining
Rectangles, specifying coordinates
Recursive
Recursive procedures
REDIM
REDIM statement
    described
    SHARED
    SHARED attribute
reducing
reference
Relational
Relational operators
    Boolean expressions
    SELECT CASE statement
relative
Relative coordinates. See Coordinates,
REM statement
representing as
requirements
RESET statement
Response
Response file
    example
    LINK
RESTORE statement
restrictions with
RESUME
Resume Next option
RESUME NEXT statement
    described
    example
    RESUME,
RESUME statement
    compiler
    compiler option required
    described
    example
    QuickBASIC/interpreter differences
    RESUME NEXT, contrasted with
retrieving
    right side
RETURN statement
RIGHT$ function
RMDIR statement
RND function
Rotating
Rotating figures with DRAW
routine
Routine, B_OnExit
routines
    ERR, identifying errors with
Rows, changing number of
RSET
RSET statement
RTRIM$
RTRIM$ function
/RUN option (QB)
Run menu, Make Library command
RUN statement
    data
Run-time error messages

S

/S option (BC)
SADD function
SAVE statement (BASICA)
Saving
    images, with GET
    programs, ASCII format
Scan codes
    keyboard
    trapping keys, use
Screen
    configuration
    graphics mode
    text mode
    functions
    CSRLIN
    individual function
    POS
    SCREEN
    resolution,
    statements. See individual statement names
    CLS
    COLOR
    LOCATE
    PCOPY
    SCREEN
    VIEW PRINT
    WIDTH
Screen
SCREEN function
Screen modes
Screen pages
SCREEN statement
    aspect ratio, effect on
    described
    example
    screen
    page
    resolution, adjusting
    text mode, rows in
SCRN
Scrolling
(/SE)
Search paths
    libraries
    Quick libraries
Searching
    strings
See Quick libraries
SEEK function
SEEK statement
SEG
/SEGMENTS option (LINK)
Segments
    lists, map files
    number
    order
    packing
SELECT CASE
SELECT CASE statement
    CASE clause
    CASE ELSE clause
    described
    END SELECT
    IF...THEN...ELSE
    ON expression
    routine, error-handling
    syntax
    versions, differences among
Semicolon (;), LIB command symbol
Separate-compilation method. See BC command
sequence
Sequential
Sequential files
    creating
    fields
    opening
    random-access files, contrasted
    records
    statements
    statements and functions
    LINE INPUT #
Serial communication
    defined
    input
    input buffer
Serial ports,
Set Main Module command, differences among versions
SETMEM function
setting
SGN function
shape of
SHARED
SHARED attribute
    COMMON
    DIM
    described
    example
    prohibited
    REDIM
SHARED statement
Shared variables, between modules
sharing
Shell escape (SHELL statement)
Shifted keys, trapping
SideKick,
sign (+)
sign ($), string-type suffix
SIN function
Sine, SIN function
Sine wave, graphing
Single-precision
Single-precision numbers
    size limits (table)
size
size, setting
Skipping
    columns
    spaces
SLEEP statement
Sorting,
Sorting, examples
SOUND statement
Source
Source files
    versions, compatibility among
SPACE$
SPACE$ function
Spaces
    skipping
    trimming
SPC
SPC function
SPC statement
specifying
    maximum value
    number of
specifying values outside
SQR function
Squares, drawing
/STACK option (LINK)
Stack size,
Stack size, recursive procedures, adjusting for
Stand-alone programs,
Standard
Standard input
    defined
    reading
    redirecting
Standard I/O
    statements
Standard output
Standard places, libraries
Statement
    modification required
statement
statement
statement. See Painting
    arcs
    arguments, checking with
    AS clause
    BASIC procedures
    buffer allocation, FIELD statement
    CASE clause
    color option
    coordinates
    described
    exit from, alternative
    FIELD statement
    file numbers in
    file-access modes
    APPEND
    BINARY
    RANDOM
    flow control
    foreground color, controlling
    graphics
    animation
    images, copying to screen
    memory, copying images to
    #INCLUDE metacommand, used with
    INPUT, contrasted with INPUT
    prompting
    SHARED attribute
    prohibited
    single-line form
    SPC function
    STATIC attribute
    SUB procedure, calling
    TYPE...END TYPE, contrasted with
statement and function names
Statement block
statement, block form
statement, contrasted with
statement names
statement (non-BASIC)
statement, use in
Statements. See individual statement names
statements. See Individual statement names; individual statement names
    DEF FN
    described
    ERROR
    FOR...NEXT
    INPUT #
    KEY ON
    KEY(n)
    NAME
    ON ERROR
    ON GOSUB
    ON...GOSUB
    RESUME
    STRIG
    WAIT
    WHILE...WEND
statements and functions
    LINE INPUT #
    LSET
    PRINT #
    RSET
statements used
    CHAIN
    COMMON
#STATIC
    statement
    variables
#STATIC metacommand
    arrays, dimensioned, implicitly
    arrays, memory allocation
STATIC
    attribute
    statement
Static arrays
    ERASE statement
    #STATIC metacommand
STEP
    option
STICK function
Stop bits
STOP statement
STR$ function
STRIG function
STRIG OFF
STRIG ON statement
STRIG(n) statements
STRING
String
    expressions
    defined
    sequential files, delimiting in
    functions. See individual function names
    ASC
    CHR$
    DATE$
    HEX$
    INPUT$
    INSTR
    LCASE$
    LEFT$
    LEN
    LTRIM$
    MID$
    RIGHT$
    RTRIM$
    SADD
    SPACE$
    STR$
    STRING$
    UCASE$
    VAL
    statements. See individual statement names
    LSET
    MID$
    RSET
    variables. See Strings
string
STRING$
string ("")
STRING$ function
String, processing. See Strings
string with AS
String-handling functions, new
Strings
    alphabetizing
    characters,
    characters, retrieving
    left side
    middle
    combining
    comparing
    constants
    defined
    expressions
    fixed-length
    AS STRING
    record elements
    variable length, contrasted
    generating
    limits (table)
    numbers,
    replacing
    spaces,
    spaces, trimming
    right side
    statements and functions, summary of (table)
    substring, searching for
    variable-length
    AS
    defined
    fixed-length,
    maximum
    variables
strings
    fixed- and variable-length
structures
    FOR...NEXT
    IF...THEN...ELSE
    single-line
    looping
    SELECT CASE
styling
SUB
    procedures
    CALL
    DECLARE
    error trapping in
    event trapping in
    exit
    GOSUB, compared
    include
    #INCLUDE metacommand
    variables,
    statement
    AS clause
    described
    include files, not allowed in
    STATIC attribute
SUB procedure. See SUB procedures
SUB statement
SUB...END
Subprograms. See SUB
    CALL statement
    CALLS
    SUB statement
    variables
Subroutines. See GOSUB...RETURN subroutine
subroutines
    drawbacks in using
Subscripts
    arrays, limits (table)
    lower bound, specifying
    maximum value, specifying
    number, specifying
    upper
SuperKey,
support routines
SWAP statement
symbol
Symbol tables,
Symbolic
Symbolic constants
    CONST
    defined
    string
symbols
    asterisk (*)
    minus sign-plus sign (-+)
Syntax
Syntax Checking command, differences
syntax, contrasted with
Syntax notation
    choices
    optional
System
System calls
SYSTEM statement

T

TAB function
TAB statement
(table)
table
Text
    screen, printing on
    wrapping
Text
Text boxes, limits (table)
Tiling. See Painting
Time and date functions. See Date and time, functions
TIME$ function
TIME$ statement
TIMER
TIMER function
TIMER OFF statement
TIMER STOP statement
Timing function
TMP environment variable, LINK, used by
to
translating to
Trapping. See Error trapping, Event trapping
    errors
    events
    multiple modules
    options, command-line, required by compiler
trapping
    activating
    keys
    function
    statements and functions (table)
trapping in
Trigonometric
Trigonometric functions
    ATN
    COS
    TAN
trimming
    left side
TROFF statement
TRON statement
True expressions
TYPE command (DOS)
TYPE, contrasted with
type, declaring
TYPE statement
    described
    FIELD, contrasted with
    versions, differences among
TYPE...END
TYPE...END TYPE statement
    example
    fixed-length strings in
    random-access records, defining
Typeface
    key names
    keywords
    placeholders
    program
types
    specifying
Typographic conventions

U

UBOUND
UBOUND function
UCASE$
UCASE$ function
UEVENT
UEVENT statement
UNLOCK statement
Uppercase letters
    file names
    lowercase, converting
use of
used in BASIC (table)
User
User libraries
User-defined
    data
    events
    types
user-defined
USING
USING, changing with
using QuickBASIC with
using to circumvent
Utility, BUILDLIB

V

/V compiler option
    described
/V option (BC)
    described
    versions, differences among
VAL function
Variable aliasing
variable aliasing
Variable-length strings
    defined
    fixed-length, compared with
    GET statement
    maximum size
    PUT
Variables
    arrays
    described
    elements of, passing
    entire, passing
    automatic
    data type
    global
    described
    function
    sharing
    subprograms
    variable
    local
    described
    function definitions
    subprograms
    multiple modules, sharing
    names
    limits (table)
    program examples, format in
    procedures
    sharing
    sharing all in a module
    procedures, passing to
    programs, sharing
    simple, passing
    STATIC
    string. See Strings
    type, declaring
    values, exchanging, SWAP
variables
variables in
VARPTR$
VARPTR function
    described
    versions, differences among
VARPTR$ function
    described
VARSEG function
    described
    versions, differences among
Version differences, file compatibility
versions
VGA (Video
VIEW
View coordinates
    physical coordinates,
    WINDOW, defining with
VIEW PRINT statement
VIEW SCREEN statement
VIEW statement
Viewport
    graphics
    advantages
    VIEW,
    VIEW SCREEN, defining with
    text
viewport. See Viewport
VM.TMP file

W

/W option (BC)
    described
    versions, differences among
WAIT statement
Watch expressions, limits
Watchpoints, limits (table)
WEND statement
    described
    versions, differences among
WHILE statement
WHILE...WEND
WHILE...WEND statement
    DO...LOOP, contrasted with
    syntax
width
WIDTH
WIDTH statement
    columns, changing number of
    described
    rows,
WINDOW
WINDOW SCREEN statement, WINDOW contrasted with
WINDOW statement
    example
    GET, effect on
Windows
    error-message
    Immediate, limits (table)
    versions, differences among
with
with CLOSE
with the ASC function
WordStar keys, similarity to
Wrapping text
WRITE # statement
    described
    PRINT #, contrasted
WRITE statement

X

/X option
    described
/X option (BC)
    described
    differences among versions
XOR operator
XOR option, PUT graphics

Z

/ZD option (BC)
/ZI option (BC)