home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / pas / qpbyex.txt < prev   
Encoding:
Text File  |  2013-11-08  |  465.9 KB  |  12,536 lines

  1.  Microsoft QuickPascal - Pascal by Example
  2.  
  3.  
  4.  
  5.  ───────────────────────────────────────────────────────────────────────────
  6.  
  7.  
  8.  
  9.  MICROSOFT(R) QUICKPASCAL
  10.  PASCAL BY EXAMPLE
  11.  
  12.  
  13.  
  14.  ───────────────────────────────────────────────────────────────────────────
  15.  
  16.  
  17.  
  18.  Information in this document is subject to change without notice and does
  19.  not represent a commitment on the part of Microsoft Corporation. The
  20.  software described in this document is furnished under a license agreement
  21.  or nondisclosure agreement. The software may be used or copied only in
  22.  accordance with the terms of the agreement. It is against the law to copy
  23.  the software on any medium except as specifically allowed in the license or
  24.  nondisclosure agreement. No part of this manual may be reproduced or
  25.  transmitted in any form or by any means, electronic or mechanical, including
  26.  photocopying and recording, for any purpose without the express written
  27.  permission of Microsoft.
  28.  
  29.  (c)Copyright Microsoft Corporation, 1989. All rights reserved.
  30.  Simultaneously published in the U.S. and Canada.
  31.  
  32.  Printed and bound in the United States of America.
  33.  
  34.  Microsoft, MS, MS-DOS, CodeView, GW-BASIC, QuickC, and XENIX are registered
  35.  trademarks of Microsoft Corporation.
  36.  
  37.  IBM is a registered trademark of International Business Machines
  38.  Corporation.
  39.  
  40.  Intel is a registered trademark of Intel Corporation.
  41.  
  42.  Document No. LN0114-200-R00-0689 Part No. 06792 10 9 8 7 6 5 4 3 2 1
  43.  
  44.  
  45.  Table of Contents
  46.  
  47.  ───────────────────────────────────────────────────────────────────────────
  48.  
  49.  Introduction
  50.  About This Book
  51.  Using the Example Programs
  52.  Key to Document Conventions
  53.  Other Books on Pascal Programming
  54.  Getting Assistance or Reporting Problems
  55.  
  56.  Part 1  Pascal Basics
  57.  ───────────────────────────────────────────────────────────────────────────
  58.  
  59.  Chapter 1  Your First Pascal Program
  60.  1.1  Sample Pascal Program
  61.  1.2  Parts of a Pascal Program
  62.  1.3  Some Terms You Should Know
  63.        1.3.1  Keywords
  64.        1.3.2  Identifiers
  65.        1.3.3  Constants and Variables
  66.        1.3.4  Data Types
  67.        1.3.5  Operators and Expressions
  68.  1.4  Input and Output
  69.  1.5  Moving On
  70.  
  71.  Chapter 2  Programming Basics
  72.  2.1  Data Types
  73.        2.1.1  Integer Types
  74.        2.1.2  Floating-Point Types
  75.        2.1.3  Character Type
  76.        2.1.4  String Types
  77.        2.1.5  Boolean Type
  78.  2.2  Constants
  79.        2.2.1  Simple Constants
  80.        2.2.2  Typed Constants
  81.  2.3  Simple Variables
  82.  2.4  Pascal Operators
  83.        2.4.1  Kinds of Operators
  84.        2.4.2  Operator Precedence
  85.  2.5  Simple Pascal Expressions
  86.        2.5.1  Arithmetic Expressions
  87.        2.5.2  String Expressions
  88.  
  89.  Chapter 3  Procedures and Functions
  90.  3.1  Overview
  91.  3.2  Procedures
  92.        3.2.1  Calling Procedures
  93.        3.2.2  Declaring Procedures
  94.        3.2.3  Declaring Local Variables
  95.        3.2.4  Passing Arguments
  96.  3.3  Functions
  97.        3.3.1  Calling Functions
  98.        3.3.2  Returning Values from Functions
  99.        3.3.3  Declaring Functions
  100.  3.4  Nested Procedures
  101.  3.5  Recursion
  102.  
  103.  Chapter 4  Controlling Program Flow
  104.  4.1  Relational and Boolean Operators
  105.  4.2  Looping Statements
  106.        4.2.1  WHILE Loops
  107.        4.2.2  REPEAT Loops
  108.        4.2.3  FOR Loops
  109.  4.3  Decision-Making Statements
  110.        4.3.1  IF Statements
  111.        4.3.2  ELSE Clauses
  112.        4.3.3  CASE Statements
  113.  
  114.  Chapter 5  User-Defined Data Types
  115.  5.1  Enumerated Data Types
  116.        5.1.1  The First Function
  117.        5.1.2  The Last Function
  118.        5.1.3  The Succ Function
  119.        5.1.4  The Pred Function
  120.        5.1.5  The Inc Procedure
  121.        5.1.6  The Dec Procedure
  122.        5.1.7  The Ord Function
  123.  5.2  Subrange Types
  124.        5.2.1  Integer Subranges
  125.        5.2.2  Character Subranges
  126.        5.2.3  Enumerated Subranges
  127.  5.3  Sets
  128.        5.3.1  Declaring Set Types
  129.        5.3.2  Assigning Set Elements to Variables
  130.        5.3.3  Set Operators
  131.  
  132.  Chapter 6  Arrays and Records
  133.  6.1  Arrays
  134.        6.1.1  Declaring Arrays
  135.        6.1.2  Accessing Array Elements
  136.        6.1.3  Declaring Constant Arrays
  137.        6.1.4  Passing Arrays as Parameters
  138.        6.1.5  Using the Debugger with Arrays
  139.  6.2  Records
  140.        6.2.1  Declaring Records
  141.        6.2.2  Accessing Record Fields
  142.        6.2.3  Using the WITH Statement to Access Fields
  143.        6.2.4  Constant Records
  144.        6.2.5  Assigning Records to Record Variables
  145.        6.2.6  Using the Debugger with Records
  146.  6.3  Variant Records
  147.        6.3.1  Declaring Variant Records
  148.        6.3.2  Accessing Variant Record Fields
  149.  
  150.  Chapter 7  Units
  151.  7.1  Understanding Units
  152.  7.2  Using Units in a Program
  153.  7.3  Standard QuickPascal Units
  154.  7.4  Creating Your Own Units
  155.        7.4.1  Writing a New Unit
  156.        7.4.2  Compiling a Unit
  157.        7.4.3  Tips for Programming with Units
  158.  
  159.  Part 2  Programming Topics
  160.  ───────────────────────────────────────────────────────────────────────────
  161.  
  162.  Chapter 8  The Keyboard and Screen
  163.  8.1  Basic Input and Output
  164.        8.1.1  Read and Readln Procedures
  165.        8.1.2  Write and Writeln Procedures
  166.        8.1.3  Formatted Output with Write and Writeln
  167.        8.1.4  DOS Redirection: Input and Output Files
  168.  8.2  The Crt Unit
  169.        8.2.1  Using the Crt Unit
  170.        8.2.2  Character Input
  171.        8.2.3  Cursor and Screen Control
  172.        8.2.4  Using Windows
  173.  
  174.  Chapter 9  Text Files
  175.  9.1  Working with Text Files
  176.        9.1.1  Declaring a File Variable and File Name
  177.        9.1.2  Opening a Text File
  178.        9.1.3  Writing Text to a File
  179.        9.1.4  Reading Text from a File
  180.        9.1.5  Closing a Text File
  181.  9.2  Increasing the Speed for Input and Output
  182.  9.3  Redirecting Text Output
  183.  9.4  Standard Procedures and Functions for Input and Output
  184.  
  185.  Chapter 10  Binary Files
  186.  10.1  Typed Files
  187.         10.1.1  Declaring Typed Files
  188.         10.1.2  Accessing Data in a Typed File
  189.         10.1.3  Using Random Access
  190.  10.2  Untyped Files
  191.  
  192.  Chapter 11  Pointers and Dynamic Memory
  193.  11.1  Declaring and Accessing Pointers
  194.         11.1.1  Declaring Pointers
  195.         11.1.2  Initializing Pointers
  196.         11.1.3  Manipulating Pointers
  197.  11.2  Dynamic-Memory Allocation
  198.         11.2.1  Allocating a Single Object
  199.         11.2.2  Allocating a Memory Block
  200.  11.3  Linked Lists
  201.  11.4  Binary Trees
  202.  
  203.  Chapter 12  Advanced Topics
  204.  12.1  The Bitwise Operators
  205.  12.2  QuickPascal Memory Map
  206.  12.3  Managing the Heap
  207.         12.3.1  Using Mark and Release to Free Memory
  208.         12.3.2  Determining Free Memory and Size of the Free List
  209.         12.3.3  Preventing Deadlock with the Free List
  210.         12.3.4  Writing a Heap-Error Function
  211.  12.4  Internal Data Formats
  212.         12.4.1  Non-Floating-Point Data Types
  213.         12.4.2  Floating-Point Data Types
  214.  12.5  Linking to Assembly Language
  215.         12.5.1  Setting Up a Link to Assembly Language
  216.         12.5.2  Segment and Data Conventions
  217.         12.5.3  Entering the Procedure
  218.         12.5.4  Accessing Parameters
  219.         12.5.5  Returning a Value
  220.         12.5.6  Exiting the Procedure
  221.         12.5.7  A Complete Example
  222.  
  223.  Part 3  Graphics and Objects
  224.  ───────────────────────────────────────────────────────────────────────────
  225.  
  226.  Chapter 13  Using Graphics
  227.  13.1  Getting Started with Graphics
  228.         13.1.1  Graphics Terms
  229.         13.1.2  For More Information
  230.  13.2  Writing Your First Graphics Program
  231.  13.3  Using Video Modes
  232.         13.3.1  Selecting a Video Mode
  233.         13.3.2  CGA Color Graphics Modes
  234.         13.3.3  EGA, MCGA, and VGA Palettes
  235.         13.3.4  EGA Color Graphics Modes
  236.         13.3.5  VGA Color Graphics Modes
  237.         13.3.6  Using the Color Video Text Modes
  238.  13.4  Understanding Coordinate Systems
  239.         13.4.1  Text Coordinates
  240.         13.4.2  Physical Screen Coordinates
  241.         13.4.3  Viewport Coordinates
  242.         13.4.4  Real Coordinates in a Window
  243.  13.5  Animation
  244.         13.5.1  Video-Page Animation
  245.         13.5.2  Bit-Mapped Animation
  246.  
  247.  Chapter 14  Using Fonts
  248.  14.1  Overview of QuickPascal Fonts
  249.  14.2  Using Fonts in QuickPascal
  250.         14.2.1  Registering Fonts
  251.         14.2.2  Setting the Current Font
  252.         14.2.3  Displaying Text Using the Current Font
  253.  14.3  A Few Hints on Using Fonts
  254.  14.4  Example Program
  255.  
  256.  Chapter 15  Object-Oriented Programming
  257.  15.1  Overview
  258.  15.2  Object Programming Concepts
  259.  15.3  Using Objects
  260.         15.3.1  Setting the Method Compiler Directive
  261.         15.3.2  Creating Classes
  262.         15.3.3  Creating Subclasses
  263.         15.3.4  Defining Methods
  264.         15.3.5  Using INHERITED
  265.         15.3.6  Declaring Objects
  266.         15.3.7  Allocating Memory
  267.         15.3.8  Calling Methods
  268.         15.3.9  Testing Membership
  269.         15.3.10  Disposing of Objects
  270.  15.4  Object Programming Strategies
  271.         15.4.1  Object Style Conventions
  272.         15.4.2  Object Reusability
  273.         15.4.3  Modularity
  274.         15.4.4  Methods
  275.         15.4.5  Data Fields
  276.  15.5  Example Program
  277.  
  278.  Appendixes
  279.  ───────────────────────────────────────────────────────────────────────────
  280.  
  281.  Appendix A  ASCII Character Codes and Extended Key Codes
  282.  A.1  ASCII Character Codes
  283.  A.2  Extended-Key Codes
  284.  
  285.  Appendix B  Compiler Directives
  286.  B.1  Switch Directives
  287.  B.2  Parameter Directives
  288.  B.3  Conditional Directives
  289.  
  290.  Appendix C  Summary of Standard Units
  291.  
  292.  Appendix D  Quick Reference Guide
  293.  D.1  Keywords, Procedures, and Functions
  294.  D.2  Crt Procedures and Functions
  295.  D.3  Dos Procedures and Functions
  296.  D.4  Printer Unit Interface
  297.  D.5  MSGraph Procedures and Functions
  298.  
  299.  Tables
  300.  ───────────────────────────────────────────────────────────────────────────
  301.  
  302.  Table 2.1    Integer Data Types
  303.  Table 2.2    Floating-Point Data Types
  304.  Table 2.3    Arithmetic Operators
  305.  Table 2.4    Relational Operators
  306.  Table 2.5    Operator Precedence
  307.  Table 4.1    Relational Operators
  308.  Table 4.2    Boolean Operators
  309.  Table 5.1    Relational Operators
  310.  Table 8.1    Crt Text-Mode Constants
  311.  Table 8.2    Crt Color Constants
  312.  Table 8.3    Variables Provided by the Crt Unit
  313.  Table 8.4    Procedures and Functions Provided by the Crt Unit
  314.  Table 8.6    Statement Effects
  315.  Table 9.1    Standard Procedures and Functions for All File Types
  316.  Table 9.2    Standard Procedures and Functions for Text Files
  317.  Table 11.1   Pointer Procedures
  318.  Table 13.1   Constants Set by _SetVideoMode
  319.  Table 13.2   Constants for Graphics Adapters
  320.  Table 13.3   Constants for Monitors
  321.  Table 13.4   Available CGA Colors
  322.  Table 13.5   CGA Colors:  _MResNoColor Mode
  323.  Table 13.6   Text Colors
  324.  Table 14.1   Typefaces and Type Sizes in QuickPascal
  325.  Table B.1    Minimum and Maximum Memory Allocation Parameters
  326.  Table B.2    QuickPascal Predefined Conditional Identifiers
  327.  
  328.  
  329.  
  330.  Introduction
  331.  ───────────────────────────────────────────────────────────────────────────
  332.  
  333.  Congratulations on your purchase of Microsoft(R) QuickPascal. By now you
  334.  should have read the other book in this package, Up and Running, and
  335.  installed the software. And you're getting a feel for the many features of
  336.  the product and the power that QuickPascal places at your fingertips. Now
  337.  you're ready to use that power as you learn how to program in Pascal.
  338.  QuickPascal makes learning easy with
  339.  
  340.    ■  An integrated programming environment. You can write and edit code with
  341.       the built-in editor, and then compile, run, and save your programs with
  342.       easy-to-use menus.
  343.  
  344.    ■  A powerful on-line help system, the QuickPascal Advisor. By pressing
  345.       one key, the F1 key, you can access everything you need to know about
  346.       the QuickPascal environment and about the Pascal language itself.
  347.  
  348.    ■  An excellent debugger. You can find out exactly where a syntax error
  349.       occurred, or trace through a program to find errors in logic.
  350.  
  351.    ■  Object-oriented extensions. You can use QuickPascal to learn and use
  352.       the newest concept in programming: object-oriented programming.
  353.  
  354.  
  355.  About This Book
  356.  
  357.  This book, Pascal by Example, complements the on-line features of
  358.  QuickPascal to teach you how to program in this language. In discussing
  359.  programming terms and processes, this book refers frequently to example
  360.  programs that are located on-line. Fragments of the example code are often
  361.  quoted in text, thus teaching you as you read along. You can also load the
  362.  whole example, so you can compile and experiment with working code.
  363.  
  364.  As you read the book and run the example programs, you'll learn about these
  365.  QuickPascal programming features:
  366.  
  367.    ■  Programming basics such as variables, operators, and expressions
  368.  
  369.    ■  Specific programming features such as strings, loops, units,
  370.       procedures, data types, and input/output
  371.  
  372.    ■  Powerful graphics capabilities and object-oriented programming
  373.  
  374.  Pascal by Example makes certain assumptions about your knowledge. It does
  375.  not explain programming concepts and terms in their simplest form. This book
  376.  assumes you have programmed in other programming languages but are not
  377.  familiar with Pascal. The following list summarizes the book's contents:
  378.  
  379.    ■  Part 1, "Pascal Basics," covers basic Pascal language fundamentals such
  380.       as procedures and data types. These chapters are designed to be read in
  381.       order, from beginning to end.
  382.  
  383.    ■  Part 2, "Programming Topics," covers practical programming topics such
  384.       as using pointers and files. An advanced topics chapter discusses
  385.       bitwise operators, calling assembly language routines, and similar
  386.       problems.
  387.  
  388.    ■  Part 3, "Graphics and Objects," covers QuickPascal graphics, which
  389.       include graphics primitives and font support. The final chapter
  390.       introduces you to the object extensions in QuickPascal. If you've never
  391.       seen object-oriented programming before, this will get you started.
  392.  
  393.    ■  Appendix A, "ASCII Character Codes and Extended Key Codes," provides
  394.       valuable reference information on ASCII codes and the extended key
  395.       codes returned by the Crt unit function ReadKey.
  396.  
  397.    ■  Appendix B, "Compiler Directives," lists all QuickPascal directives.
  398.  
  399.    ■  Appendix C, "Summary of Standard Units," briefly describes each unit
  400.       provided with QuickPascal.
  401.  
  402.    ■  Appendix D, "Quick Reference Guide," lists all QuickPascal keywords and
  403.       the standard units' procedures and functions.
  404.  
  405.  ───────────────────────────────────────────────────────────────────────────
  406.  NOTE
  407.     Microsoft documentation uses the term "DOS" to refer to both the
  408.     MS-DOS(R) and IBM Personal Computer DOS operating systems. The name of a
  409.     specific operating system is used when it is necessary to note features
  410.     that are unique to that system.
  411.  ───────────────────────────────────────────────────────────────────────────
  412.  
  413.  
  414.  Using the Example Programs
  415.  
  416.  The QuickPascal Advisor includes all of the significant program examples in
  417.  this book (although it doesn't include all of the code fragments). This
  418.  feature allows you to load, run, and experiment with example programs as you
  419.  read.
  420.  
  421.  Every complete program in this book starts with a comment of this general
  422.  form:
  423.  
  424.       { %FTOC.PAS: converts temperatures }
  425.  
  426.  The comment contains the program's name and a brief description of what it
  427.  does. This program can be found in the QP Advisor as FTOC.PAS.
  428.  
  429.  You must be using the QuickPascal environment to load an example. To load
  430.  the example program, open the Help menu and choose the Contents command.
  431.  Choose the title of this book from within the Contents screen. Then find the
  432.  desired program in the list of Pascal by Example programs, and copy it into
  433.  the source window using the Copy and Paste commands from the Edit menu.
  434.  
  435.  After you copy an example program into the QuickPascal source window, treat
  436.  it as you would any Pascal source program. You can compile or edit the
  437.  program, save it on disk, and so on.
  438.  
  439.  Note that you can easily print out an example program, in whole or in part,
  440.  from Help or from your file, by choosing the Print command from the File
  441.  menu.
  442.  
  443.  
  444.  Key to Document Conventions
  445.  
  446.  This book uses the following document conventions:
  447.  
  448. ╓┌──────────────────────┌────────────────────────────────────────────────────╖
  449.  Example                Description
  450.  ────────────────────────────────────────────────────────────────────────────
  451.  COPY TEST.OBJ C:       UPPERCASE type represents DOS commands and filenames.
  452.  
  453.  BEGIN                  Boldface type (whether in all uppercase or in both
  454.                         upper- and lowercase letters) indicates standard
  455.                         features of the QuickPascal language: keywords,
  456.                         operators, and standard procdures and functions.
  457.  
  458.  PROGRAM FtoC           This typeface is used for example programs, program
  459.  CONST                  fragments, and the names of user-defined procedures,
  460.   Factor = 32.0         functions and variables. It also indicates user input
  461.                         and screen output.
  462.  
  463.  ArrayName              Words in italics indicate placeholders for
  464.  Example                Description
  465.  ────────────────────────────────────────────────────────────────────────────
  466. ArrayName              Words in italics indicate placeholders for
  467.                         information that you must supply. A file name is an
  468.                         example of this kind of information.
  469.  
  470.  «Option»               Items inside chevrons are optional.
  471.  
  472.  {Choice1 | Choice2}    Braces and a vertical bar indicate a choice among two
  473.                         or more items. You must choose one of the items
  474.                         unless all of the items are also enclosed in
  475.                         chevrons.
  476.  
  477.  Repeating elements...  Three dots following an item indicate that more items
  478.                         having the same form may be entered.
  479.  
  480.  REPEAT                 A column of three dots indicates that part of the
  481.  .                      example program has intentionally been omitted.
  482.  .
  483.  .
  484.  UNTIL
  485.  Example                Description
  486.  ────────────────────────────────────────────────────────────────────────────
  487. UNTIL
  488.  
  489.  F1                     Small capital letters denote names of keys on the
  490.                         keyboard. A plus (+) indicates a combination of keys.
  491.                         For example, SHIFT+F5 tells you to hold down the
  492.                         SHIFT key while pressing the F5 key.
  493.  
  494.  "array pointer"        The first time a new term is defined, it is enclosed
  495.                         in quotation marks. Since some knowledge of
  496.                         programming is assumed, common terms such as memory
  497.                         or branch are not defined.
  498.  
  499.  American National      An acronym is spelled out the first time it appears.
  500.  Standards Institute
  501.  (ANSI)
  502.  ────────────────────────────────────────────────────────────────────────────
  503.  
  504.  
  505.  
  506.  
  507.  Other Books on Pascal Programming
  508.  
  509.  This book introduces the Pascal language and some practical programming
  510.  topics. It does not attempt to teach you basic computer programming or
  511.  advanced Pascal programming techniques. The following books cover a variety
  512.  of topics that you may find useful. They are listed only for your
  513.  convenience. With the exception of its own publications, Microsoft does not
  514.  endorse these books or recommend them over others on the same subject.
  515.  
  516.  Cooper, Doug. Standard Pascal: User Reference Manual. New York, NY: W.W.
  517.  Norton & Company, 1983.
  518.  
  519.    ■  A complete and concise reference manual to standard Pascal.
  520.  
  521.  Cooper, Doug and Michael Clancy. Oh! Pascal! 2d ed. New York, NY: W. W.
  522.  Norton & Company, 1985.
  523.  
  524.    ■  A beginner's introduction to standard Pascal.
  525.  
  526.  Craig, John Clark. Microsoft QuickPascal Programmer's Toolbox. Redmond, WA:
  527.  Microsoft Press. In press.
  528.  
  529.    ■  A sourcebook that beginners will find useful for learning by example.
  530.       Seasoned professionals using QuickPascal as a development system will
  531.       find the routines to be valuable and immediately useful extensions to
  532.       the Pascal language.
  533.  
  534.  Grogono, Peter. Programming in Pascal. rev. ed. Menlo Park, CA:
  535.  Addison-Wesley, 1980.
  536.  
  537.    ■  A good teaching source, but one that also covers sophisticated topics.
  538.  
  539.  Jamsa, Kris. Microsoft QuickPascal Programming. Redmond, WA: Microsoft
  540.  Press. In press.
  541.  
  542.    ■  A comprehensive introduction to mastering QuickPascal programming for
  543.       the beginning- to intermediate- level programmer complete with hands-on
  544.       examples.
  545.  
  546.  Jamsa, Kris. Microsoft QuickPascal: Programmer's Quick  Reference. Redmond,
  547.  WA: Microsoft Press. In press.
  548.  
  549.    ■  This alphabetical reference provides concise descriptions of all
  550.       QuickPascal procedures and functions.
  551.  
  552.  Kernighan, Brian W., and P.J. Plauger. Software Tools in Pascal. Menlo Park,
  553.  CA: Addison-Wesley, 1981.
  554.  
  555.    ■  A beginning- to intermediate-level guide to software tools and
  556.       programming techniques.
  557.  
  558.  Ladd, Robert Scott. Learning Object-Oriented Programming with Microsoft
  559.  QuickPascal. Redmond, WA: Microsoft Press. In press.
  560.  
  561.    ■  Provides an example-rich introduction to the philosophy and procedures
  562.       of object-oriented programming for the beginning- to intermediate-level
  563.       QuickPascal user.
  564.  
  565.  Leestma, Sanford, and Larry Nyhoff. Pascal: Programming and Problem Solving.
  566.  New York, NY: Macmillan Publishing Company, 1987.
  567.  
  568.    ■  A beginner's guide to Pascal programming. The book also covers problem
  569.       analysis, algorithm development, algorithm translation to Pascal, and
  570.       program validation.
  571.  
  572.  Wirth, Niklaus, and Kathleen Jensen. Pascal User Manual and Report. 3d ed.
  573.  New York, NY: Springer-Verlag, 1985.
  574.  
  575.    ■  The definitive source.
  576.  
  577.  
  578.  Getting Assistance or Reporting Problems
  579.  
  580.  If you feel you have discovered a problem in the software, please report the
  581.  problem using the Product Assistance Request form at the back of this book.
  582.  
  583.  If you have comments or suggestions reqarding any of the books accompanying
  584.  this product, please use the Documentation Feedback Card, also at the back
  585.  of this book.
  586.  
  587.  
  588.  
  589.  ───────────────────────────────────────────────────────────────────────────
  590.  Part 1  Pascal Basics
  591.  
  592.  Part 1 of Pascal by Example introduces you to the Pascal programming
  593.  language and the basic elements of Pascal programs. The chapters in this
  594.  part assume that you know some programming concepts but are not familiar
  595.  with Pascal. Experienced Pascal programmers may want to skim these chapters.
  596.  
  597.  The information in Part 1 helps you start writing Pascal programs
  598.  immediately. Chapters progress through such major topics as procedures,
  599.  program flow, user-defined data types, and units. If you're new to Pascal,
  600.  reading the chapters in this part sequentially will give you a thorough
  601.  introduction to the fundamentals of programming in Pascal.
  602.  
  603.  
  604.  Chapter 1  Your First Pascal Program
  605.  ───────────────────────────────────────────────────────────────────────────
  606.  
  607.  You're probably eager to use QuickPascal and begin writing programs in
  608.  Pascal. In this chapter, you'll compile and run your first Pascal program,
  609.  FTOC.PAS, which is shown in Figure 1.1. As with all of the complete
  610.  programs in this book, we've done the work of typing it in for you. You
  611.  just need to use the Copy and Paste commands on the Edit menu to copy the
  612.  program from the QuickPascal Advisor into a blank source window. Then you
  613.  can press F5 to compile and run the program.
  614.  
  615.  This chapter uses the sample program to introduce the parts of a Pascal
  616.  program, some terms you should know, and input and output. Most of these
  617.  terms are discussed in more detail in Chapter 2.
  618.  
  619.  If you're an experienced programmer in another structured language such as
  620.  C, you might want to skip this chapter and begin with Chapter 2,
  621.  "Programming Basics," or Chapter 3, "Procedures and Functions."
  622.  
  623.  
  624.  1.1  Sample Pascal Program
  625.  
  626.  FTOC.PAS is a simple program that converts temperatures from Fahrenheit to
  627.  Celsius. Like all of the sample programs in this book, you'll find it in the
  628.  QuickPascal Advisor. Figure 1.1 contains the FTOC.PAS program code and
  629.  illustrates the program parts.
  630.  
  631.  
  632.  Figure 1.1  Parts of a Pascal Program
  633.              ┌─────
  634.             │ PROGRAM FtoC;
  635.             │ { FTOC.PAS: converts temperatures }
  636.              │
  637.             │ USES
  638.              │     Crt;
  639.              │
  640.  Declara-    │ CONST
  641.    tions     │     factor = 32.0;
  642.              │
  643.              │ VAR
  644.             │     degrees_fahr, degrees_cel, answer : Real;
  645.              │
  646.             │ FUNCTION    ConvertToCel( degrees : Real ) : Real;
  647.              │     BEGIN
  648.              │         ConvertToCel := ((degrees - factor) * 5.0) / 9.0;
  649.              │     END;
  650.              └─────
  651.              ┌─────
  652.              │ BEGIN
  653.              │     ClrScr;
  654.              │     Writeln( 'Temperature Converter' );
  655.              │     Writeln( 'Fahrenheit to Celsius' );
  656.    Main      │     Writeln;
  657.  Program     │     Write( 'Temperature in degrees Fahrenheit? ' );
  658.              │     Readln( degrees_fahr );
  659.              │     degrees_cel := ConvertToCel( degrees_fahr );
  660.              │     Writeln( 'Equals ', degrees_cel:4:1, ' degrees Celsius' );
  661.              │     Writeln;
  662.              │ END.
  663.              └─────
  664.  
  665.  
  666.  1.2  Parts of a Pascal Program
  667.  
  668.  Pascal is a highly structured language. Programs have parts, and the parts
  669.  have a required order. This section explains the parts of a Pascal program.
  670.  
  671.  The Program Declaration
  672.  
  673.  Traditionally, the first line of every Pascal program contains the program
  674.  declaration. This declaration consists of the keyword PROGRAM, the name of
  675.  the program, and a semicolon. With Microsoft QuickPascal, such a declaration
  676.  is not required, but it is still helpful as a way of labeling a program.
  677.  Also, a program declaration is necessary if you want your program to compile
  678.  on compilers other than those developed by Microsoft.
  679.  
  680.  The USES Statement
  681.  
  682.  The USES statement always follows the program declaration. It specifies
  683.  which units are called from the program. Units are explained in detail in
  684.  Chapter 7.
  685.  
  686.  The USES statement in the FTOC.PAS program calls the Crt unit. This unit
  687.  contains procedures and functions that allow you to control your computer
  688.  screen.
  689.  
  690.  Constant and Variable Declarations
  691.  
  692.  Pascal requires that you declare constants, types, and variables before
  693.  using them. The keywords CONST, TYPE, and VAR are used for this purpose.
  694.  Constant declarations are made with the CONST keyword and include both the
  695.  constant identifier (a name) and its value. For example, the constant shown
  696.  in the FTOC.PAS program is named factor and has the value 32.0.
  697.  
  698.  The VAR keyword is used for variable declarations. Variable declarations
  699.  include the variable identifier (a name) and its type. Multiple variables of
  700.  the same type can be declared in the same statement.
  701.  
  702.  Procedures and Functions
  703.  
  704.  Procedure and function declarations come after the constant and variable
  705.  declarations. Each procedure or function has its own BEGIN...END block.
  706.  FTOC.PAS uses the function converttocel to convert the temperature from
  707.  Fahrenheit to Celsius.
  708.  
  709.  The Program Body
  710.  
  711.  The body of the program, as shown in Figure 1.1, is enclosed by the keywords
  712.  BEGIN and END. Pascal executes the instructions between BEGIN and END. The
  713.  period after the END keyword tells Pascal that the program is over; anything
  714.  that follows the period is ignored.
  715.  
  716.  Comments
  717.  
  718.  Comments are notes to yourself or others that make your programs easier to
  719.  understand and maintain. Braces tell QuickPascal to ignore comment text.
  720.  Instead of braces, comments can also be enclosed in parentheses and
  721.  asterisks, as shown in this example:
  722.  
  723.       (* This is a comment between the special symbols *)
  724.  
  725.  The text on line 2 of FTOC.PAS is a comment.
  726.  
  727.  
  728.  1.3  Some Terms You Should Know
  729.  
  730.  If you've never programmed before, the terms "keyword" and "identifier" may
  731.  be confusing to you. These and other terms are discussed in this section. If
  732.  you are an experienced programmer, you can skip this section and go on to
  733.  Chapter 2, "Programming Basics."
  734.  
  735.  
  736.  1.3.1  Keywords
  737.  
  738.  Pascal contains a set of "keywords," or words that it reserves for its own
  739.  use. Whenever the Pascal compiler encounters a keyword, a specific action is
  740.  performed. In the sample program FTOC.PAS, the words PROGRAM, VAR, BEGIN,
  741.  and END are all keywords. A keyword always means the same thing to a
  742.  program; for example, the keyword BEGIN always signals the beginning of a
  743.  statement block, and the keyword END always signals the end of a statement
  744.  block.
  745.  
  746.  The QuickPascal environment gives you the option of displaying keywords in a
  747.  special color. This makes your programs easier to read on screen. See Up and
  748.  Running for more information on how to display in color.
  749.  
  750.  Always remember that you cannot use a keyword as a variable name or for
  751.  anything other than its intended purpose. Keywords are shown in all
  752.  uppercase letters in this book, and in the sample programs. A list of
  753.  QuickPascal keywords is shown below:
  754.  
  755.       ABSOLUTE              IF                    RECORD
  756.       AND                   IMPLEMENTATION        REPEAT
  757.       ARRAY                 IN                    SET
  758.       BEGIN                 INHERITED             SHL
  759.       CASE                  INLINE                SHR
  760.       CONST                 INTERFACE             STRING
  761.       CSTRING               INTERRUPT             THEN
  762.       DIV                   LABEL                 TO
  763.       DO                    MOD                   TYPE
  764.       DOWNTO                NIL                   UNIT
  765.       ELSE                  NOT                   UNTIL
  766.       END                   OBJECT                USES
  767.       EXTERNAL              OF                    VAR
  768.       FILE                  OR                    WHILE
  769.       FOR                   OVERRIDE              WITH
  770.       FORWARD               PACKED                XOR
  771.       FUNCTION              PROCEDURE
  772.       GOTO                  PROGRAM
  773.  
  774.  
  775.  1.3.2  Identifiers
  776.  
  777.  "Identifiers" are names that you use in your program. In the example
  778.  program, the word degrees_cel is the identifier for a variable. You could
  779.  call it celsiusdegrees or cd or fred if you wanted to; however, for the sake
  780.  of good programming practice, the name should provide information about the
  781.  purpose of the variable. Thus degrees_cel is preferable to cd or fred.
  782.  QuickPascal requires that you follow three rules when creating identifiers:
  783.  
  784.    1. The first character of an identifier must be a letter or underscore
  785.       character (_).
  786.  
  787.    2. Digits can be used within an identifier.
  788.  
  789.    3. Identifier names can be of any length, but only the first 63 characters
  790.       are significant.
  791.  
  792.  Thus, the following are examples of valid identifiers:
  793.  
  794.       FirstTime
  795.       first_time
  796.       index0
  797.       area233
  798.       area_555
  799.       A4_9RT4_98NNS
  800.  
  801.  By contrast, the following are examples of invalid identifiers:
  802.  
  803.       Identifier      Reason It's Invalid
  804.  
  805.       First Time      There is a space between First and Time.
  806.       index0$         The $ sign is not a valid identifier character.
  807.       area!233        The ! mark is not a valid identifier character.
  808.       555area         The first character is a digit.
  809.  
  810.  Identifiers in Pascal are not case sensitive. This means that the
  811.  identifiers First_Time, first_Time, first_time, and FIRST_TIME all refer to
  812.  the same identifier. If these identifiers were typed in a program, the
  813.  compiler would not generate an error. Instead, the compiler would treat them
  814.  as the same identifier.
  815.  
  816.  Some identifiers are defined by Microsoft. These identifiers name certain
  817.  data types, standard procedures and functions, units, and variables and
  818.  constants defined in standard units. In this manual and throughout the
  819.  sample programs, these "standard identifiers" are shown with initial capital
  820.  letters.
  821.  
  822.  
  823.  1.3.3  Constants and Variables
  824.  
  825.  "Constants" and "variables" are two different ways for your program to use
  826.  data. A constant is defined at the beginning of your program and always has
  827.  the same value. In the sample program FTOC.PAS, the constant factor is
  828.  assigned the value 32.0. After such an assignment is made (using the CONST
  829.  keyword), QuickPascal replaces the identifier factor with the value 32.0
  830.  whenever the identifier is encountered. Constants can make your program more
  831.  readable and understandable.
  832.  
  833.  Programs operate on data. In Pascal, as in other programming languages, data
  834.  are stored in variables. Variables must be declared in a Pascal program
  835.  before being used. Variable declarations follow the keyword VAR and have two
  836.  parts: a variable name (identifier) and type. Data types are discussed
  837.  below. In our sample program, both degrees_fahr and degrees_cel are
  838.  variables of type Real.
  839.  
  840.  
  841.  1.3.4  Data Types
  842.  
  843.  Pascal classifies data according to "type." Roughly speaking, data types
  844.  allow the computer to determine how much memory a variable requires, and
  845.  what level of precision should be maintained for a number. Type declarations
  846.  keep you from making mistakes such as storing your zip code where your
  847.  salary should be.
  848.  
  849.  When you declare a variable, you must declare its type as well as its name.
  850.  Data naturally falls into two general categories: text and numbers. Textual
  851.  data items (which may include numbers, as in a street address) are referred
  852.  to as "strings." Numbers can be integers (whole numbers such as 1 and 34) or
  853.  real numbers (decimal numbers such as 29.5 and 3.14159). Four simple data
  854.  types are illustrated below:
  855.  
  856.        Data                  Type
  857.  
  858.        Mary Smith            String
  859.  
  860.        1413 Oak Lane         String
  861.  
  862.        76                    Integer
  863.  
  864.        21.0987               Real
  865.  
  866.        Y                     Character
  867.  
  868.  Data types are discussed in more detail in Chapter 2, "Programming Basics."
  869.  
  870.  
  871.  1.3.5  Operators and Expressions
  872.  
  873.  Operators are used in expressions to manipulate data within your program.
  874.  Pascal includes many operators, but the simplest are already familiar to
  875.  you: addition, subtraction, multiplication, and division. Another class of
  876.  common operators are relational operators, such as "greater than," "less
  877.  than," and "equal to." You'll use operators in your programs to do tasks
  878.  such as counting how many lines you've printed on a page, or seeing if the
  879.  number of hours someone worked in a week exceeds 40. More sophisticated uses
  880.  for operators also exist, and those are discussed in Chapter 2,
  881.  "Programming Basics."
  882.  
  883.  Expressions combine operators with data, as shown in the example below:
  884.  
  885.         { Multiply the number of hours worked by the rate of
  886.           pay. Store the answer in the variable 'Salary'
  887.         }
  888.         salary := hours * pay_rate;
  889.  
  890.  
  891.  1.4  Input and Output
  892.  
  893.  At the risk of oversimplification, you could say that most programs get some
  894.  data, manipulate that data in some fashion, and display some final data to a
  895.  user. So far in this chapter you've learned a bit about data and data
  896.  manipulation. "Input" and "output" refer to the processes of getting and
  897.  displaying data.
  898.  
  899.  Input
  900.  
  901.  Pascal programs frequently make use of the Read and Readln procedures for
  902.  data input. Read is useful for getting a single keystroke──for instance,
  903.  when you want the user to press a key to stop a process. Readln is useful
  904.  when you want the user to be able to type and correct a complete line of
  905.  text before the program accepts it. Readln doesn't do anything until the
  906.  user presses ENTER, thus providing the user with the ability to edit the
  907.  input line before sending it on to the program for processing.
  908.  
  909.  In the example below, the first line that the user types is stored as the
  910.  variable name and the next line is stored as address.
  911.  
  912.       Readln( name );
  913.       Readln( address );
  914.  
  915.  Output
  916.  
  917.  Just as Read and Readln handle data input, Write and Writeln handle output.
  918.  Writeln differs from Write in that Writeln generates a carriage return at
  919.  the end of the string (the line of text). Thus, Writeln moves the cursor to
  920.  a new line on your output screen.
  921.  
  922.  
  923.  1.5  Moving On
  924.  
  925.  So far you have looked at and compiled your first program, and you have been
  926.  exposed to some basic Pascal terms. The next chapter elaborates on the
  927.  concepts introduced here and shows you how to create some more complex
  928.  programs.
  929.  
  930.  
  931.  Chapter 2  Programming Basics
  932.  ───────────────────────────────────────────────────────────────────────────
  933.  
  934.  This chapter introduces you to some Pascal programming fundamentals, such as
  935.  data types, constants, variables, operators, and expressions. If you're
  936.  already an experienced programmer in another language, you may find most of
  937.  this material to be familiar. In that case, you might want to skim this
  938.  chapter quickly or just skip ahead to Chapter 3.
  939.  
  940.  
  941.  2.1  Data Types
  942.  
  943.  All data in your program is either a constant or a variable; each has an
  944.  associated data type. Two kinds of data types exist in QuickPascal:
  945.  predefined data types and user-defined data types. Predefined data types,
  946.  such as Real and STRING, are a built-in part of the language and are
  947.  discussed below. User-defined data types expand your programming power
  948.  considerably. They are complex enough that Chapter 5 of this book is
  949.  devoted to them.
  950.  
  951.  The predefined data types supported by QuickPascal and explained in the
  952.  following sections are:
  953.  
  954.    ■  Integers
  955.    ■  Floating-point numbers
  956.    ■  Characters
  957.    ■  Strings
  958.    ■  Booleans
  959.  
  960.  
  961.  2.1.1  Integer Types
  962.  
  963.  Integers are whole numbers like the numbers you use to count with; that is,
  964.  they have no fractional parts. The number 12 is an integer; 12.0 and 12.5
  965.  are not. Table 2.1 lists the five integer types that QuickPascal supports.
  966.  Programs discussed later in this chapter show how these integers are used.
  967.  
  968.  Table 2.1  Integer Data Types
  969.  
  970.  Integer Type       Range of Values       Byte Size    Examples
  971.  
  972.  ShortInt           -128 to 127           1            -7, 55, 123, 0, $F
  973.  
  974.  Byte               0 to 255              1            55, 123, $F, 0
  975.  
  976.  Integer            -32768 to 32767       2            -555, 30000, 0, $FF
  977.  
  978.  Word               0 to 65535            2            30000, 60000, $FFFF
  979.  
  980.  LongInt            -2147483648 to        4            -100000, 100000, $FFFF
  981.                      2147483647
  982.  
  983.  
  984.  The integer types Byte and Word are called "unsigned" integers, while the
  985.  other integer types are called "signed" integers. The word "unsigned"
  986.  indicates that the integer includes values from zero to the upper positive
  987.  limit. Signed integers include negative numbers.
  988.  
  989.  You can see that the main difference between the different integer types is
  990.  the range of the values that can be stored. In the VAR section of your
  991.  program, you declare the variable identifier and the specific type.
  992.  
  993.  As the examples in Table 2.1 show, QuickPascal allows you to write integers
  994.  in either decimal (base 10) or hexadecimal notation (base 16). Hexadecimal
  995.  numbers begin with the dollar sign ($) and use the characters 0-9 and A-F.
  996.  
  997.  The INTTYPES.PAS program below demonstrates different types of integers.
  998.  
  999.       PROGRAM Inttypes;
  1000.       { INTTYPES.PAS demonstrates integer data types. }
  1001.  
  1002.       USES
  1003.           Crt;
  1004.  
  1005.       VAR
  1006.           short_int_val    :   ShortInt;
  1007.           byte_val         :   Byte;
  1008.           integer_val      :   Integer;
  1009.           word_val         :   Word;
  1010.           long_int_val     :   LongInt;
  1011.  
  1012.  
  1013.       BEGIN
  1014.           short_int_val := -31;
  1015.           byte_val      := 255;
  1016.           integer_val   := -21212;
  1017.           word_val      := $FFFF;
  1018.           long_int_val  := 12918656;
  1019.           ClrScr;
  1020.           Writeln( 'short_int_val = ', short_int_val );
  1021.           Writeln( 'byte_val      = ', byte_val );
  1022.           Writeln( 'integer_val   = ', integer_val );
  1023.           Writeln( 'word_val      = ', word_val );
  1024.           Writeln( 'long_int_val  = ', long_int_val );
  1025.       END.
  1026.  
  1027.  Here is the output from INTTYPES.PAS:
  1028.  
  1029.       short_int_val  = -31
  1030.       byte_val  = 255
  1031.       integer_val    = -21212
  1032.       word_val  = 65535
  1033.       long_int_val   = 12918656
  1034.  
  1035.  
  1036.  2.1.2  Floating-Point Types
  1037.  
  1038.  Floating-point numbers are not integers; that is, they are written with a
  1039.  decimal point. Thus the number 12.5 is a floating-point number, as is
  1040.  .00021459862. Also, QuickPascal treats constants larger than its maximum as
  1041.  long-integer size floating-point numbers, even if they do not contain a
  1042.  decimal point. Floating-point numbers are also referred to as "real"
  1043.  numbers.
  1044.  
  1045.  QuickPascal supports a group of floating-point types that vary in their
  1046.  precision, range of values, and storage requirements. They are shown in
  1047.  Table 2.2.
  1048.  
  1049.  Table 2.2  Floating-Point Data Types
  1050.  
  1051.  Type              Range of Value            Byte Size     Significant Digits
  1052.  
  1053.  Single            1.5E-45 to 3.4E+38         4            7-8
  1054.  
  1055.  Real              2.9E-39 to 1.7E+38         6            11-12
  1056.  
  1057.  Double            5.0E-324 to 1.7E+308       8            15-16
  1058.  
  1059.  Extended          3.4E-4951 to 1.1E+4932    10            15-16
  1060.  
  1061.  Comp              -9.2E+18 to 9.2E+18        8            15-16
  1062.  
  1063.  
  1064.  By default, QuickPascal assumes that your computer does not use a math
  1065.  coprocessor, resulting in fewer significant digits than if it did use a
  1066.  coprocessor. However, if you have a math coprocessor available (any member
  1067.  of the 8087 family of processors), you can increase the precision of Comp
  1068.  and Extended data types by 4 significant digits by including the {$N+}
  1069.  compiler directive in your program.
  1070.  
  1071.  Unlike some other Pascal compilers, QuickPascal makes all real number types
  1072.  available for your use, whether or not your computer has a numeric
  1073.  coprocessor.
  1074.  
  1075.  The Comp type is a little different from the other floating-point types.
  1076.  Comp is designed to count very large numbers, and so it stores only the
  1077.  integer part of a number between -(2^63)+1 and (2^63)-1.
  1078.  
  1079.  
  1080.  2.1.3  Character Type
  1081.  
  1082.  The character data type Char stores only one character. Each character
  1083.  occupies one byte of storage. You can represent characters using the
  1084.  following formats:
  1085.  
  1086.  Characters                  Representation
  1087.  
  1088.  Readable characters         Can be represented by using same letters,
  1089.  (that is, the alphabet,     digits, and punctuation.
  1090.  digits, and punctuation
  1091.  characters)
  1092.  
  1093.  Control characters          Can be represented using the carat symbol (^)
  1094.  (ASCII characters 0         followed by the control letter. For example, the
  1095.  through 31)                 form feed is represented by ^L, since form feed
  1096.                              is ASCII 12 and L is the 12th letter in the
  1097.                              alphabet. Appendix A contains a chart of all
  1098.                              ASCII codes.
  1099.  
  1100.  All characters,             Can be represented by using the number sign (#)
  1101.  including those in the      followed by the ASCII code number. Thus, #65 is
  1102.  extended ASCII table        the letter A, #12 is the form feed, and so on.
  1103.  
  1104.  
  1105.  2.1.4  String Types
  1106.  
  1107.  This data type stores a string of characters such as a name or an address.
  1108.  QuickPascal strings can hold up to 255 characters. Many of the examples in
  1109.  this book use strings to display messages and store input. The STRINGS.PAS
  1110.  program demonstrates how to create, read, and write simple strings.
  1111.  
  1112.       PROGRAM Strings;
  1113.  
  1114.       { STRINGS.PAS demonstrates basic string operations. }
  1115.  
  1116.       USES
  1117.           Crt;
  1118.  
  1119.       CONST
  1120.           str_constant = 'Type something and press Enter: ';
  1121.  
  1122.       VAR
  1123.           prompt_one : STRING;
  1124.           prompt_two : STRING;
  1125.           input_str  : STRING;
  1126.  
  1127.       BEGIN
  1128.           prompt_one := str_constant;
  1129.           prompt_two := 'You typed: ';
  1130.  
  1131.           ClrScr;
  1132.           Write( prompt_one );
  1133.           Readln( input_str );
  1134.           Write( prompt_two );
  1135.           Writeln( input_str );
  1136.       END.
  1137.  
  1138.  Here is typical output from STRINGS.PAS:
  1139.  
  1140.       Type something and press Enter: QuickPascal!
  1141.       You typed: QuickPascal!
  1142.  
  1143.  2.1.4.1  Declaring Strings
  1144.  
  1145.  Pascal strings are usually of the STRING type. In STRINGS.PAS, the
  1146.  statements
  1147.  
  1148.       VAR
  1149.           prompt_one : STRING;
  1150.           prompt_two : STRING;
  1151.           input_str  : STRING;
  1152.  
  1153.  declare three string variables named prompt_one, prompt_two, and input_str.
  1154.  
  1155.  2.1.4.2  Initializing Strings
  1156.  
  1157.  You can initialize string variables by assigning string literals or
  1158.  constants to them:
  1159.  
  1160.       CONST
  1161.           str_constant = 'Type something and press Enter: ';
  1162.           .
  1163.           .
  1164.           .
  1165.           prompt_one := str_constant;  { Assign string constant }
  1166.           prompt_two := 'You typed: '; { Assign string literal }
  1167.  
  1168.  Pascal strings can contain as many as 255 characters. The first character of
  1169.  a string is a "length byte" that indicates the number of characters in the
  1170.  string. Procedures such as Writeln look at this byte to determine the
  1171.  string's size.
  1172.  
  1173.  Thus, if you assign Hello to a string variable, the variable actually
  1174.  contains six characters: a length byte that contains the number 5, followed
  1175.  by the 5 characters of Hello.
  1176.  
  1177.  2.1.4.3  Reading and Writing Strings
  1178.  
  1179.  You can use the Read and Readln procedures to read a string:
  1180.  
  1181.       Readln( input_str );
  1182.  
  1183.  and the Write and Writeln procedures to write a string:
  1184.  
  1185.       Write( prompt_two );
  1186.       Writeln( input_str );
  1187.  
  1188.  These procedures read input from the keyboard and write output to the
  1189.  screen. See Chapter 8, "The Keyboard and Screen," for more information on
  1190.  input and output.
  1191.  
  1192.  
  1193.  2.1.5  Boolean Type
  1194.  
  1195.  A variable of type Boolean can be assigned a value of true or false.
  1196.  Variables of this type are often used as "flags" when a condition in your
  1197.  program becomes true (or false). Suppose, for example, that you wanted to
  1198.  check whether a user had finished entering data. If the variable all_done
  1199.  had been declared as type Boolean, you may use it like this:
  1200.  
  1201.       IF (x = 0) THEN all_done := true;
  1202.  
  1203.  Later you may need to check the all_done variable:
  1204.  
  1205.       IF (all_done = True) THEN ...
  1206.  
  1207.  A shorthand way of writing this statement is
  1208.  
  1209.       IF (all_done) THEN ...
  1210.  
  1211.  It is understood that IF all_done means the same as IF (all_done  = True)
  1212.  and by the same understanding, IF (NOT all_done) means IF (all_done  =
  1213.  False).
  1214.  
  1215.  For more information about Boolean types and what you can do with them, see
  1216.  Chapter 12, "Advanced Topics."
  1217.  
  1218.  
  1219.  2.2  Constants
  1220.  
  1221.  Fixed data, or "constants," are assigned a value which is never changed (an
  1222.  exception to this, called "typed constants," is discussed below). When you
  1223.  declare constants in Pascal, you assign them a name and value. This value,
  1224.  once assigned, cannot be changed in your program. The assignment of name and
  1225.  value to a constant is called a constant declaration.
  1226.  
  1227.  QuickPascal supports two kinds of constants: simple and typed.
  1228.  
  1229.  
  1230.  2.2.1  Simple Constants
  1231.  
  1232.  Simple constants can be used for two purposes. First, they can store fixed
  1233.  values, like the number of inches in a foot. Second, they can store values
  1234.  you wish to use within your program, like the text of a title screen or your
  1235.  assumption for the rate of inflation. Since constants are all declared at
  1236.  the beginning of your program, it is easy to change any of those values and
  1237.  recompile your program. Remember, changing the value of a constant in its
  1238.  initial declaration gives that new value to the constant wherever it is
  1239.  used. In fact, any other constants that depend on the one you alter will
  1240.  also receive their updated values when you recompile the program.
  1241.  
  1242.  The general syntax for declaring and initializing a constant is:
  1243.  
  1244.  CONST ConstantIdentifier = {ConstantValue|Expression}
  1245.  
  1246.  For example:
  1247.  
  1248.       CONST
  1249.  
  1250.           ft_per_mile = 5280;
  1251.  
  1252.  In this example, the ConstantIdentifier is ft_per_mile and the
  1253.  ConstantValue is 5280.
  1254.  
  1255.  In QuickPascal, you can use an expression that contains a previously
  1256.  declared constant. You can also set a constant equal to an expression, as in
  1257.  
  1258.       CONST
  1259.  
  1260.           days_per_year = 365;
  1261.           seconds_per_day = 60 * 60 * 24;
  1262.           light_speed = 186282;
  1263.           light_year = light_speed * seconds_per_day
  1264.                        * days_per_year;
  1265.  
  1266.  However, if you declare a constant equal to an expression, that expression
  1267.  can only contain simple operations like addition, subtraction,
  1268.  multiplication, and division. More advanced Pascal functions like square
  1269.  root cannot be used.
  1270.  
  1271.  Here are some more examples of constants:
  1272.  
  1273.       CONST
  1274.  
  1275.           max_row = 10;
  1276.           max_col = 10;
  1277.  
  1278.         { Uses previously declared constants  }
  1279.           table_size = max_row * max_col;
  1280.  
  1281.           prompt = 'Press any key to resume...';
  1282.           byebye = 'Thank you for using the program...';
  1283.  
  1284.           euler_const = 0.577215664901;
  1285.  
  1286.         { An approximation of the Euler constant }
  1287.           euler_const2 = 228.0 / 395.0;
  1288.  
  1289.  
  1290.  2.2.2  Typed Constants
  1291.  
  1292.  QuickPascal lets you use another kind of constant called a "typed constant."
  1293.  In standard Pascal, you can't specify the data type of a constant. The
  1294.  compiler assigns the data a type according to how it is presented. For
  1295.  example, a constant called number_of_people with a value of 35 would be
  1296.  assigned an integer type by QuickPascal, which makes sense. However, if you
  1297.  declared number_of_people equal to 35.0, then the compiler would assign a
  1298.  real number type to number_of_people, which doesn't make much sense.
  1299.  
  1300.  Fortunately, QuickPascal allows you to specifically state the data type
  1301.  associated with a constant, which is the typed constant. You may also see
  1302.  the terms "variable constant" or "static variable" used to describe such a
  1303.  constant. Declaring data with a typed constant actually changes the constant
  1304.  into an initialized variable. That is, the value associated with the
  1305.  typed-constant identifier can be changed in the program, unlike regular
  1306.  constants whose value cannot be changed within the program.
  1307.  
  1308.  A typed constant can be thought of as a cross between a constant and a
  1309.  variable. A constant's value is declared but can never be changed. A
  1310.  variable, on the other hand, is not declared with a predefined value, and
  1311.  its value can change within the program. A typed constant is like a variable
  1312.  whose predefined value is declared but which can be redefined within the
  1313.  program.
  1314.  
  1315.  The general syntax is shown below:
  1316.  
  1317.  CONST ConstantIdentifier : TypeName = {ConstantValue|Expression}
  1318.  
  1319.  Examples of typed constant declarations are given below. Note that the
  1320.  ConstantValue or Expression assigned to the typed constant is much like a
  1321.  value or expression assigned to a regular variable. Note that you use the
  1322.  :TypeName to specify the type of the constant, as you would in a VAR
  1323.  declaration.
  1324.  
  1325.       CONST
  1326.  
  1327.           max_row : Word = 10;
  1328.           max_col : Word = 10;
  1329.           table_size : Word = max_row * max_col;
  1330.  
  1331.           prompt : STRING = 'Press any key to resume...';
  1332.           byebye : STRING = 'Thank you for using the program...';
  1333.  
  1334.           euler_const : Real = 0.577215664901;
  1335.           { An approximation to the Euler const }
  1336.           euler_const2 : Real = 228.0 / 395.0;
  1337.  
  1338.  
  1339.  2.3  Simple Variables
  1340.  
  1341.  While constants are identifiers that are declared and assigned a fixed
  1342.  value, variables are identifiers that are merely declared. They are assigned
  1343.  values in the main program or a subprogram. The general syntax for declaring
  1344.  a variable and identifying its type is as follows:
  1345.  
  1346.       VAR VariableName «, VariableName...» : DataType
  1347.          .
  1348.          .
  1349.          .
  1350.  
  1351.  Some examples of declaring variables:
  1352.  
  1353.       VAR
  1354.           { Variables declared with predefined type }
  1355.           column_index : Word;
  1356.           area, circumference : Real;
  1357.           message : STRING;
  1358.           input_character : Char;
  1359.  
  1360.  Once a variable is declared, QuickPascal sets aside the required amount of
  1361.  memory to store data associated with the variable. The amount of memory set
  1362.  aside is based on the variable's data type. For example, a variable declared
  1363.  with type STRING[40] gets 41 bytes of memory of which 40 bytes are available
  1364.  for data (the remaining byte holds the string length); a variable with type
  1365.  Integer gets 2 bytes of memory.
  1366.  
  1367.  Once a variable is declared, you may assign it a value. Often you will use
  1368.  the assignment operator (:=), which should not be confused with the equality
  1369.  operator (=). The assigned value can be a constant, another variable, or an
  1370.  expression.
  1371.  
  1372.  The VARS.PAS program shows a simple assignment of variables. The variable
  1373.  radius is assigned a value through user input (the Readln procedure). The
  1374.  variable area is assigned the value of an expression using the assignment
  1375.  operator.
  1376.  
  1377.       PROGRAM Vars;
  1378.           { VARS.PAS: variable declaration and use }
  1379.  
  1380.       VAR
  1381.           radius, area   :   Real;
  1382.  
  1383.       BEGIN
  1384.           Writeln( 'Enter radius' );
  1385.           Readln( radius );
  1386.           Area := (Pi * (radius * radius));
  1387.           Writeln( 'Area of the circle = ', area:8:2 );
  1388.           Writeln;
  1389.       END.
  1390.  
  1391.  Typical output from VARS.PAS looks like this:
  1392.  
  1393.       Enter radius
  1394.       12
  1395.       Area of the circle =   452.39
  1396.  
  1397.  
  1398.  2.4  Pascal Operators
  1399.  
  1400.  Operators let you manipulate data. Pascal operators are used to build
  1401.  expressions. This section describes some of the operators available in
  1402.  Microsoft QuickPascal. In addition to the ones discussed here, QuickPascal
  1403.  supports some operators for advanced use, including bitwise and set
  1404.  operators. The bitwise operators are discussed in detail in Chapter 12,
  1405.  "Advanced Topics." Set operators are discussed in Chapter 5, "User-Defined
  1406.  Data Types."
  1407.  
  1408.  
  1409.  2.4.1  Kinds of Operators
  1410.  
  1411.  QuickPascal supports the following categories of operators:
  1412.  
  1413.    ■  Arithmetic
  1414.    ■  Relational
  1415.    ■  String
  1416.    ■  Address-of
  1417.  
  1418.  2.4.1.1  Arithmetic Operators
  1419.  
  1420.  Table 2.3 lists the arithmetic operators, which perform numeric
  1421.  manipulation of integer and floating-point types. While the addition (+),
  1422.  subtraction (-), and multiplication (*) operators work with both integers
  1423.  and floating-point types, the division (/) operator performs floating-point
  1424.  division (even if the operands are integers), and the DIV and MOD operators
  1425.  work with integers only.
  1426.  
  1427.  While most of the operators take two operands, the unary plus and unary
  1428.  minus operators work with one operand only.
  1429.  
  1430.  Table 2.3  Arithmetic Operators
  1431.  
  1432.        Operator       Purpose
  1433.  
  1434.        +              Unary plus sign
  1435.  
  1436.        -              Unary minus sign
  1437.  
  1438.        +              Adds two numbers
  1439.  
  1440.        -              Subtracts two numbers
  1441.  
  1442.        *              Multiplies two numbers
  1443.  
  1444.        /              Divides two floating-point numbers
  1445.  
  1446.        DIV            Divides two integer numbers
  1447.  
  1448.        MOD            Returns the remainder of integer division
  1449.  
  1450.  
  1451.  2.4.1.2  Relational Operators
  1452.  
  1453.  Relational operators are used to compare two operands, which may be
  1454.  constants, variables, functions, or expressions. The operands must be of the
  1455.  same or compatible types. Some useful relational operators are shown in
  1456.  Table 2.4.
  1457.  
  1458.  Table 2.4  Relational Operators
  1459.  
  1460.        Operator        Purpose
  1461.  
  1462.        =               Equal to
  1463.        <>              Not equal to
  1464.        >               Greater than
  1465.        >=              Greater than or equal to
  1466.        <               Less than
  1467.        <=              Less than or equal to
  1468.  
  1469.  
  1470.  2.4.1.3  String Operators
  1471.  
  1472.  QuickPascal has one string operator, the (+) operator, for string and
  1473.  character concatenation. Other aspects of string manipulation are performed
  1474.  by predefined procedures and functions.
  1475.  
  1476.  2.4.1.4  Address-Of Operator
  1477.  
  1478.  The address-of operator (@) is used to return the address of variables,
  1479.  routines, parameters, and so forth. The topic of pointers is covered fully
  1480.  in Chapter 11.
  1481.  
  1482.  
  1483.  2.4.2  Operator Precedence
  1484.  
  1485.  Operators in expressions are evaluated in order of their precedence. Table
  1486.  2.5 lists the QuickPascal operators according to their precedence level.
  1487.  Although not all of the operators shown in the table are discussed in this
  1488.  section, they are in the table for the sake of completeness. For information
  1489.  on operators such as SHL or XOR, see Chapter 12, "Advanced Topics," or use
  1490.  QP Advisor.
  1491.  
  1492.  Table 2.5  Operator Precedence
  1493.  
  1494.  Precedence Level      Operators                            Operator Class
  1495.  
  1496.  1 (highest)           @, NOT                               Unary
  1497.  2                     *, /, DIV, MOD, AND, SHL, SHR        Multiplying
  1498.  3                     +, -, OR, XOR                        Adding
  1499.  4 (lowest)            =, <>, <, <=, >, >=, IN              Relational
  1500.  
  1501.  
  1502.  The following list interprets this table and gives examples of the use of
  1503.  operators.
  1504.  
  1505.    ■  The operation defined by the operator with highest precedence and its
  1506.       related operand is performed first. In the example
  1507.  
  1508.          2 * 4 + 3
  1509.  
  1510.       the number 4 is placed between two operators of different precedence.
  1511.       Since the (*) operator has a higher precedence, multiplication of 2 and
  1512.       4 proceeds first. The addition is performed afterward.
  1513.  
  1514.    ■  Operations of the same precedence level are performed from left to
  1515.       right. In the example
  1516.  
  1517.          2 * 4 / 3
  1518.  
  1519.       the number 4 is placed between two operators of the same precedence.
  1520.       Since the (*) operator appears to the left of number 4, it is applied
  1521.       before the (/) operator.
  1522.  
  1523.    ■  Parentheses are used to group operations. Expressions enclosed in
  1524.       parentheses are evaluated first. The most deeply nested expression is
  1525.       evaluated before any other. (Nested expressions are expressions within
  1526.       expressions.) Parentheses alter the "effective" or "working" precedence
  1527.       of an operator. In the example
  1528.  
  1529.          2 / ((3 + 4) * 5)
  1530.  
  1531.       the expression in the parentheses (3 + 4) is evaluated first, since it
  1532.       is the most nested expression in parentheses. The (*) operator is
  1533.       applied next, since it is enclosed in parentheses, giving it a higher
  1534.       effect precedence. Finally, the (/) operator is applied.
  1535.  
  1536.  
  1537.  2.5  Simple Pascal Expressions
  1538.  
  1539.  Expressions are a special and very important part of any programming
  1540.  language. They are created to evaluate data in your program. Expressions use
  1541.  the operators described in Section 2.4 and operands, which are the data
  1542.  types in your program, as their components. QuickPascal supports simple and
  1543.  advanced expressions.
  1544.  
  1545.  This chapter briefly presents the simple expressions. Each type of
  1546.  expression follows a syntax rule which gives its components and syntax. The
  1547.  simple expressions explained in this chapter are
  1548.  
  1549.    ■  Arithmetic
  1550.  
  1551.    ■  String
  1552.  
  1553.  
  1554.  2.5.1  Arithmetic Expressions
  1555.  
  1556.  Arithmetic expressions combine constants, numbers, variables, and functions
  1557.  with arithmetic operators.
  1558.  
  1559.  The syntax of an expression is
  1560.  
  1561.  { Constant | Expression } |
  1562.  
  1563.  « Constant | Expression » { Operator Constant | Expression }
  1564.  
  1565.  For example
  1566.  
  1567.       (length * width)
  1568.  
  1569.  Expressions combine with constants or variables and relational or assignment
  1570.  operators to make Pascal statements:
  1571.  
  1572.       area := (length * width)
  1573.  
  1574.  In the example above, the expression length * width is evaluated, then the
  1575.  result is stored in the variable area.
  1576.  
  1577.  Expressions are evaluated according to operator precedence. Using
  1578.  parentheses to force the correct evaluation of an expression is always a
  1579.  good idea.
  1580.  
  1581.  An expression may contain nested expressions, which are expressions within
  1582.  expressions. Examples are shown below:
  1583.  
  1584.       K := (1 + 6) DIV (55 - J);
  1585.       Z := (2 * X) + (Y / 4);
  1586.       T := (((2 * X + 2) * X - 5) * X + 1) * X - 10;
  1587.  
  1588.  
  1589.  2.5.2  String Expressions
  1590.  
  1591.  String expressions use the (+) operator to concatenate strings and
  1592.  characters. The general syntax rule is shown below:
  1593.  
  1594.  {Character | String} + {Character | String}...
  1595.  
  1596.  For example
  1597.  
  1598.       'dog' + 'house';   { creates the word "doghouse" }
  1599.  
  1600.  The (+) operator works identically to the Concat function. String and
  1601.  character concatenation cannot build a string longer than 255 characters.
  1602.  
  1603.  
  1604.  Chapter 3  Procedures and Functions
  1605.  ───────────────────────────────────────────────────────────────────────────
  1606.  
  1607.  Procedures and functions allow you to write well-organized Pascal programs,
  1608.  in which discrete tasks are done in separate, logically contained modules.
  1609.  Once you understand procedures and functions, you are well on your way to
  1610.  becoming a true Pascal programmer.
  1611.  
  1612.  This chapter begins by discussing procedures, which are simpler and more
  1613.  commonly used than functions. The discussion covers many topics, such as
  1614.  argument passing, that are common to both procedures and functions. The
  1615.  chapter ends with an explanation of functions, nested procedures, and
  1616.  recursion.
  1617.  
  1618.  
  1619.  3.1  Overview
  1620.  
  1621.  Procedures and functions let you program using a "divide and conquer"
  1622.  approach. Instead of trying to solve every aspect of a large problem at
  1623.  once, you divide it into several small problems and solve each one
  1624.  separately. This strategy allows you to write clear, reliable programs that
  1625.  perform different tasks in distinct, logically contained modules. In Pascal,
  1626.  these modules are called procedures or functions.
  1627.  
  1628.  Dividing a program into task-based modules offers several advantages:
  1629.  
  1630.    ■  Makes programs easier to write and read. All of the statements related
  1631.       to a task are located in one place.
  1632.  
  1633.    ■  Prevents unexpected side effects because you can use private ("local")
  1634.       variables that are not visible to the main program or other sections.
  1635.  
  1636.    ■  Eliminates unnecessary repetition of code for frequently performed
  1637.       tasks.
  1638.  
  1639.    ■  Simplifies debugging. Once you have debugged a procedure or function,
  1640.       you can use it with confidence in many different situations.
  1641.  
  1642.  The distinction between procedures and functions can be summarized in a few
  1643.  words. A procedure performs a specific task; a function performs a specific
  1644.  task and also returns a value. We will return to this topic in Section 3.3,
  1645.  "Functions."
  1646.  
  1647.  If you are familiar with Microsoft QuickBASIC, you will notice many
  1648.  similarities in Pascal. A Pascal procedure resembles a QuickBASIC SUB
  1649.  procedure, and a Pascal function is like a QuickBASIC FUNCTION procedure. If
  1650.  you know the C language, you will notice that a C function combines the
  1651.  qualities of Pascal procedures and functions; a C function can return a
  1652.  value, or return nothing.
  1653.  
  1654.  
  1655.  3.2  Procedures
  1656.  
  1657.  Procedures and functions are very similar──so similar, in fact, that
  1658.  everything explained in this section applies to functions as well as
  1659.  procedures. To avoid repeating the phrase "procedures and functions" with
  1660.  every sentence, this section refers only to procedures. You should read it
  1661.  with the understanding that these ideas also apply to functions. Section
  1662.  3.3, "Functions," explains how functions differ from procedures.
  1663.  
  1664.  A "procedure" is a collection of declarations and statements that performs a
  1665.  certain task. You have already seen a few of the QuickPascal standard
  1666.  procedures, such as Writeln, which writes a line. This section explains
  1667.  procedures using several programs. The first example, CENTER.PAS, contains a
  1668.  procedure that centers a line on the screen:
  1669.  
  1670.       PROGRAM center;
  1671.  
  1672.       { CENTER.PAS: Demonstrate simple procedure }
  1673.  
  1674.       USES
  1675.           Crt;
  1676.  
  1677.       VAR
  1678.           row   : Byte;
  1679.           title : STRING;
  1680.  
  1681.       PROCEDURE center_line( message : STRING; line : Byte );
  1682.           BEGIN
  1683.               GotoXY( 40 - Length( message ) DIV 2, line );
  1684.               Writeln( message );
  1685.           END;
  1686.  
  1687.       BEGIN
  1688.           row   := 2;
  1689.           title := 'Each line of text is centered.';
  1690.           ClrScr;
  1691.           center_line( title, row );
  1692.           center_line( '--------', row+1 );
  1693.           center_line( 'Microsoft QuickPascal!', row+2 );
  1694.       END.
  1695.  
  1696.  The CENTER.PAS program displays these lines on the screen:
  1697.  
  1698.       Each line of text is centered.
  1699.                  --------
  1700.           Microsoft QuickPascal!
  1701.  
  1702.  The rest of this section refers frequently to the CENTER.PAS example.
  1703.  
  1704.  
  1705.  3.2.1  Calling Procedures
  1706.  
  1707.  The CENTER.PAS program uses a procedure named center_line to center a line.
  1708.  You "call," or execute, a procedure by stating its name and by supplying any
  1709.  "arguments," or data items that it might require. The center_line procedure
  1710.  expects you to supply two arguments: a piece of text to print, and the
  1711.  screen line on which to print it. To print the message
  1712.  
  1713.       Vite!
  1714.  
  1715.  on the second screen line, you would call center_line with this statement:
  1716.  
  1717.       center_line( 'Vite!', 2 );
  1718.  
  1719.  You list the arguments in parentheses after the procedure name, placing a
  1720.  comma between each two arguments. Here, the first argument is the string
  1721.  Vite! and the second is the number 2. Later in this chapter, you will learn
  1722.  how a procedure handles the arguments it receives.
  1723.  
  1724.  
  1725.  3.2.2  Declaring Procedures
  1726.  
  1727.  A procedure "declaration" contains the complete code for the procedure. Here
  1728.  is the procedure declaration for center_line:
  1729.  
  1730.       PROCEDURE center_line( message : STRING; line : Byte );
  1731.           BEGIN
  1732.               GotoXY( 40 - Length( message ) DIV 2, line );
  1733.               Write( message );
  1734.           END;
  1735.  
  1736.  A procedure declaration has two parts, called the "head" and the "body." A
  1737.  procedure head consists of the PROCEDURE keyword, followed by the
  1738.  procedure's name and a list of its arguments in parentheses. It ends with a
  1739.  semicolon. Here is the head of the center_line procedure:
  1740.  
  1741.       PROCEDURE center_line( message : STRING; line : Byte );
  1742.  
  1743.  The list of arguments states the name and type of each argument. This
  1744.  example states that center_line requires two arguments, one each of the data
  1745.  types STRING and BYTE. The first argument is named message, and the second
  1746.  argument is named line.
  1747.  
  1748.  The procedure body is a statement block that contains the procedure's
  1749.  executable statements. Like other blocks, the procedure body is enclosed in
  1750.  BEGIN and END, and it ends with a semicolon. Here is the body of the
  1751.  center_line procedure:
  1752.  
  1753.       BEGIN
  1754.           GotoXY( 40 - Length( message ) DIV 2, line );
  1755.           Write( message );
  1756.       END;
  1757.  
  1758.  The body of the center_line procedure contains two statements, each of which
  1759.  calls a standard Pascal procedure. The first calls the GotoXY procedure and
  1760.  the second calls Write.
  1761.  
  1762.  Forward Declarations
  1763.  
  1764.  Pascal requires that you declare an entity before using it. Before using a
  1765.  variable, for instance, you must declare its name and type. The same rule
  1766.  applies to a procedure. Before calling a procedure, you must declare it as
  1767.  explained in the previous section.
  1768.  
  1769.  In simple programs, such as CENTER.PAS, it's easy to satisfy the "declare
  1770.  before calling" rule. Simply place all of your procedure declarations before
  1771.  the main program body. In CENTER.PAS, the center_line procedure declaration
  1772.  appears before the main program.
  1773.  
  1774.  Occasionally, however, you may need to call a procedure before it has been
  1775.  declared. This can be done by providing a "forward declaration" of the
  1776.  procedure prior to the procedure call.
  1777.  
  1778.  A forward declaration is identical to a procedure head, except that it
  1779.  contains both the keyword FORWARD and a semicolon after the argument list.
  1780.  For instance, the center_line procedure head looks like this
  1781.  
  1782.       PROCEDURE center_line( message : STRING; line : Byte );
  1783.  
  1784.  and its forward declaration looks like this
  1785.  
  1786.       PROCEDURE center_line( message : STRING; line : Byte );
  1787.       FORWARD;
  1788.  
  1789.  The forward declaration must appear before the first reference to the
  1790.  procedure. Most programmers put forward references at or very near the
  1791.  beginning of the program.
  1792.  
  1793.  
  1794.  3.2.3  Declaring Local Variables
  1795.  
  1796.  In addition to statements, the procedure body can contain variable
  1797.  declarations. Variables declared in a procedure are said to be "local,"
  1798.  meaning they can be seen only inside the procedure. Because their visibility
  1799.  is limited, local variables are less likely to be changed accidentally than
  1800.  global variables.
  1801.  
  1802.  The LOCAL.PAS program demonstrates local variables. It prompts you to enter
  1803.  a number and then displays the factorial of that number. (A factorial is the
  1804.  product of all the integers from 1 to a number. For instance, the factorial
  1805.  of 4 is 24, the product of 1 *) 2 * 3 * 4.)
  1806.  
  1807.       PROGRAM local_variables;
  1808.  
  1809.       { LOCAL.PAS: Demonstrate local variables. }
  1810.  
  1811.       VAR
  1812.           num : Byte;
  1813.  
  1814.       PROCEDURE factor( value : Byte );
  1815.           VAR
  1816.               factorial : Real;
  1817.               count : Byte;
  1818.           BEGIN
  1819.               factorial := 1.0;
  1820.               FOR count := 1 TO value DO
  1821.               factorial := factorial * count;
  1822.               Write( 'Factorial of ', value, ' is ' );
  1823.               Writeln( factorial );
  1824.           END; { procedure factor }
  1825.  
  1826.       BEGIN { main program }
  1827.           Write( 'Enter a number smaller than 34: ' );
  1828.           Readln( num );
  1829.           Factor( num );
  1830.       END.
  1831.  
  1832.  Here is typical output from LOCAL.PAS:
  1833.  
  1834.       Enter a number smaller than 34: 5
  1835.       Factorial of 5 is 1.20000000000000E+0002
  1836.  
  1837.  The factor procedure in LOCAL.PAS appears below. It declares two local
  1838.  variables named factorial and count:
  1839.  
  1840.       PROCEDURE factor( value : Byte );
  1841.           VAR
  1842.               factorial : Real;
  1843.               count : Byte;
  1844.           BEGIN
  1845.               factorial := 1.0;
  1846.               FOR count := 1 TO value DO
  1847.                   factorial := factorial * count;
  1848.               Write( 'Factorial of ', value,' is ' );
  1849.               Writeln( factorial );
  1850.           END; { procedure factor }
  1851.  
  1852.  Notice where local variables are declared: between the procedure's head and
  1853.  the BEGIN keyword.
  1854.  
  1855.  Variable Scope
  1856.  
  1857.  Unlike global variables, which are declared outside any procedure and are
  1858.  therefore visible everywhere in the program, local variables are declared
  1859.  inside a procedure and are hidden from the rest of the program. If you refer
  1860.  to a local variable outside the "scope," or range, where it is visible,
  1861.  QuickPascal issues an error message:
  1862.  
  1863.       Error P0032: Unknown identifier
  1864.  
  1865.  The same message appears if you refer to a variable that has never been
  1866.  declared. In both cases, it means the variable is not visible──and cannot be
  1867.  used──in the place where the reference appears.
  1868.  
  1869.  The ability to limit a variable's visibility makes it easier to write
  1870.  reliable programs. If a variable is local, it can't be changed accidentally
  1871.  by some other part of the program. Such haphazard side effects are common in
  1872.  older interpreted BASIC programs, in which all variables are global.
  1873.  
  1874.  
  1875.  3.2.4  Passing Arguments
  1876.  
  1877.  The Factor procedure in LOCAL.PAS has another local variable that hasn't
  1878.  been mentioned yet. In addition to factorial and count, which it declares,
  1879.  the procedure uses a third variable named value:
  1880.  
  1881.       PROCEDURE Factor( value: Byte );
  1882.           VAR
  1883.               factorial : Real;
  1884.               count : Byte;
  1885.           BEGIN
  1886.               factorial := 1.0;
  1887.               FOR count := 1 TO value DO
  1888.                   factorial := factorial * count;
  1889.               Write( 'Factorial of ', value,' is ' )
  1890.               Writeln( factorial );
  1891.           END; { procedure factor }
  1892.  
  1893.  The value argument, in the procedure head, is "passed" when you call the
  1894.  factor procedure. The argument comes from a number you type in at the
  1895.  keyboard. The main procedure in LOCAL.PAS stores your input in a variable
  1896.  named num:
  1897.  
  1898.       Writeln( 'Enter a number smaller than 34: ' );
  1899.       Readln( num );
  1900.  
  1901.  The main program passes the value of num as an argument when it calls the
  1902.  factor procedure:
  1903.  
  1904.       factor( num );
  1905.  
  1906.  When you list an argument in the procedure head, it becomes a local variable
  1907.  in the procedure. Thus, the head of the factor procedure
  1908.  
  1909.       PROCEDURE Factor( value : Byte );
  1910.  
  1911.  creates a local variable named value. Inside the factor procedure, value
  1912.  can be treated like any other local variable.
  1913.  
  1914.  3.2.4.1  Passing by Value
  1915.  
  1916.  The type of argument passing in LOCAL.PAS is called "passing by value"
  1917.  because the procedure receives the value of the original variable, not the
  1918.  variable itself. The BYVALUE.PAS program demonstrates this idea, which has
  1919.  important consequences for managing variables:
  1920.  
  1921.       PROGRAM byvalue;
  1922.  
  1923.       { BYVALUE.PAS: Demonstrate passing by value. }
  1924.  
  1925.       VAR
  1926.           global_var : Integer;
  1927.  
  1928.       PROCEDURE proc( local_var : Integer );
  1929.           BEGIN
  1930.               Writeln( 'local_var = ', local_var );
  1931.               local_var := 333;
  1932.               Writeln( 'local_var = ', local_var );
  1933.           END; { procedure proc }
  1934.  
  1935.       BEGIN { main program }
  1936.           global_var := 5;
  1937.           proc( global_var );
  1938.           Writeln( 'global_var = ', global_var );
  1939.       END.
  1940.  
  1941.  Here is the output from BYVALUE.PAS:
  1942.  
  1943.       local_var  = 5
  1944.       local_var  = 333
  1945.       global_var = 5
  1946.  
  1947.  The program declares a global variable named global_var and assigns the
  1948.  value 5 to global_var. The proc procedure expects you to pass one argument,
  1949.  which it names local_var. The procedure prints the value of local_var
  1950.  (initially, 5), then changes its value to 333 and prints it again. After
  1951.  control returns to the main program, BYVALUE.PAS prints the value of
  1952.  global_var, which remains at 5.
  1953.  
  1954.  The proc procedure alters the value of local_var. But this change has no
  1955.  effect on the original variable,global_var, which is not affected by
  1956.  anything that happens in proc. The same is true even if both variables have
  1957.  the same name (if you name both of them my_val, for instance).
  1958.  
  1959.  Passing an argument by value creates a local copy of the variable in the
  1960.  procedure. Because the procedure receives only a local copy, it can give the
  1961.  argument any name, and change its value, without affecting variables
  1962.  outside the procedure.
  1963.  
  1964.  3.2.4.2  Passing by Reference
  1965.  
  1966.  Sometimes, you may want a procedure to change the value of an argument. For
  1967.  instance, say you need a procedure that swaps two variables. If you pass the
  1968.  variables by value, their values change inside the swap procedure, but
  1969.  remain unchanged in the rest of the program. You need a way to tell the
  1970.  procedure to change the original variables, not its local copies of them.
  1971.  
  1972.  Pascal offers a second passing method, called "passing by reference," for
  1973.  just such cases. The BYREF.PAS program demonstrates this method:
  1974.  
  1975.       PROGRAM byref;
  1976.  
  1977.       { BYREF.PAS: Demonstrate passing by reference. }
  1978.  
  1979.       VAR
  1980.           var1, var2 : Integer;
  1981.  
  1982.       PROCEDURE swap_vars(VAR var1 : Integer; VAR var2 : Integer);
  1983.           VAR
  1984.               temp : Integer;
  1985.           BEGIN
  1986.               temp := var1;
  1987.               var1 := var2;
  1988.               var2 := temp;
  1989.           END; { procedure swap_vars }
  1990.  
  1991.       BEGIN
  1992.           var1 := 55;
  1993.           var2 := 99;
  1994.           Writeln( 'var1 = ', var1, '  var2 = ', var2 );
  1995.           swap_vars( var1, var2 );
  1996.           Writeln( 'var1 = ', var1, '  var2 = ', var2 );
  1997.       END.
  1998.  
  1999.  Here is the output from BYREF.PAS:
  2000.  
  2001.       var1 = 55  var2 = 99
  2002.       var1 = 99  var2 = 55
  2003.  
  2004.  The program declares two global variables named var1 and var2, assigning
  2005.  them the values 55 and 99, respectively. It prints their values, calls the
  2006.  swap_vars procedure, then prints their values again. The output proves that
  2007.  swap_vars changes the original variables.
  2008.  
  2009.  The important difference between this program and the previous example
  2010.  appears in the swap_vars procedure head:
  2011.  
  2012.       PROCEDURE swap_vars(VAR var1 : Integer; VAR var2: Integer);
  2013.  
  2014.  Notice the VAR keyword in front of each name in the argument list. It tells
  2015.  the swap_vars procedure to treat the argument as a variable (located
  2016.  elsewhere) rather than as a value. Instead of creating a local copy of the
  2017.  passed value, the procedure acts upon the variable itself.
  2018.  
  2019.  The swap_vars procedure head happens to use the same names for these
  2020.  arguments (var1 and var2) in its argument list. But the result would be the
  2021.  same if swap_vars used different names. Because the arguments are declared
  2022.  with VAR, their names in swap_vars are synonyms for the original variables.
  2023.  
  2024.  You can underscore this point by making a simple change to the previous
  2025.  example, BYVALUE.PAS. Load the program and add VAR to the proc procedure
  2026.  head:
  2027.  
  2028.       PROCEDURE proc( VAR local_var : Integer );
  2029.  
  2030.  After you make this change, the proc procedure changes the global variable
  2031.  global_var, giving this output:
  2032.  
  2033.       local_var = 5
  2034.       local_var = 333
  2035.       global_var = 333
  2036.  
  2037.  In the original BYVALUE.PAS program, the global variable global_var
  2038.  retained the value 5 even though proc changed the value of local_var.
  2039.  Passing global_var by reference gives proc the ability to modify global_var.
  2040.  
  2041.  When you pass an argument by reference, you must pass a variable, not a
  2042.  value. That is, the argument must be a variable name,
  2043.  
  2044.       swap_vars( global_1, global_2 ); { correct }
  2045.  
  2046.  not a constant,
  2047.  
  2048.       swap_vars( 55, 99 ); { error! }
  2049.  
  2050.  or an expression:
  2051.  
  2052.       swap_vars( 5 * 11, 93 + 6 ); { error! }
  2053.  
  2054.  It's best to pass arguments by reference only when you want the procedure to
  2055.  change the argument. Unnecessary passing by reference creates the same
  2056.  problems as the overuse of global variables.
  2057.  
  2058.  
  2059.  3.3  Functions
  2060.  
  2061.  A function is a procedure that returns a value. Like most languages, Pascal
  2062.  has many standard functions, such as Sqrt, which returns a square root.
  2063.  
  2064.  You can think of a function as a special kind of procedure. All of the
  2065.  concepts explained in Section 3.2, "Procedures," also apply to functions.
  2066.  Rather than restate everything that procedures and functions share in
  2067.  common, this section explains the features that make functions different
  2068.  from procedures.
  2069.  
  2070.  The FUNCT.PAS program contains a simple function:
  2071.  
  2072.       PROGRAM FUNCT;
  2073.  
  2074.       { FUNCT.PAS: Demonstrate function basics. }
  2075.  
  2076.       VAR
  2077.           num, expo, powr : Real;
  2078.  
  2079.       FUNCTION power( base, exponent : Real ) : Real;
  2080.  
  2081.           BEGIN
  2082.               IF (base > 0) THEN
  2083.                   Power := Exp( exponent * Ln( base ) )
  2084.               ELSE
  2085.                   Power := -1.0;
  2086.       END;
  2087.  
  2088.       BEGIN
  2089.           Write( 'Enter a number: ' );
  2090.           Readln( num );
  2091.           Write( 'Enter an exponent: ' );
  2092.           Readln( expo );
  2093.           powr := Power( num, expo );
  2094.           Writeln( num, ' ^ ', expo, ' = ', powr );
  2095.       END.
  2096.  
  2097.  The FUNCT.PAS program prompts you to enter two numbers, a base and an
  2098.  exponent. Then it calls the power function to raise the base to the
  2099.  exponent. Typical output appears below:
  2100.  
  2101.      Enter a number: 2
  2102.      Enter an exponent: 8
  2103.  
  2104.      2.00000000000000E+0000 ^ 8.00000000000000E+0000 = 2.56000000000000E+0002
  2105.  
  2106.  FUNCT.PAS raises 2 to the eighth power, giving a result of 256.
  2107.  
  2108.  
  2109.  3.3.1  Calling Functions
  2110.  
  2111.  Function calls are identical to procedure calls. You state the function's
  2112.  name, listing in parentheses any arguments that the function requires. The
  2113.  only difference is in where the call can appear. A procedure call can stand
  2114.  alone as a statement, but a function call, because it returns a value, must
  2115.  appear in an assignment or expression.
  2116.  
  2117.  The following statement from FUNCT.PAS calls the power function, assigning
  2118.  its return value to the variable powr:
  2119.  
  2120.       powr := power( num, expo );
  2121.  
  2122.  Notice the similarity to a procedure call. The power function takes two
  2123.  arguments, which are listed in parentheses after the function name.
  2124.  
  2125.  The previous example uses the function call in an assignment. Function calls
  2126.  can also appear in expressions:
  2127.  
  2128.       dazzle := 12 * surprise( 730, 88 ) / 2;
  2129.  
  2130.  Here, the surprise function appears as part of the expression to the right
  2131.  of the assignment symbol.
  2132.  
  2133.  
  2134.  3.3.2  Returning Values from Functions
  2135.  
  2136.  A function returns a value by assigning the value to its own name. In the
  2137.  power function, for instance, this statement causes the function to return
  2138.  the value -1.0:
  2139.  
  2140.       power := -1.0;
  2141.  
  2142.  The return value must match the type declared in the function head. Since
  2143.  the power function returns a value of type Real, the above statement uses
  2144.  the value -1.0 (with a decimal point).
  2145.  
  2146.  
  2147.  3.3.3  Declaring Functions
  2148.  
  2149.  Function declarations are identical to procedure declarations except that
  2150.  you substitute FUNCTION for PROCEDURE and declare the function's return type
  2151.  after the argument list. Below is the function head from FUNCT.PAS:
  2152.  
  2153.       FUNCTION power( base, exponent : Real ) : Real;
  2154.  
  2155.  Following the argument list, separated by a colon, is the identifier Real,
  2156.  which indicates that the power function returns a value of type Real. If
  2157.  power returned an integer value, you would replace the Real withInteger, and
  2158.  so on.
  2159.  
  2160.  Again, except for the differences noted in this section, functions are
  2161.  identical to procedures. They can handle arguments and local variables
  2162.  exactly as described in Section 3.2, "Procedures."
  2163.  
  2164.  
  2165.  3.4  Nested Procedures
  2166.  
  2167.  In addition to local variables, a procedure can declare other procedures.
  2168.  You can "hide" one procedure declaration inside another. Like a local
  2169.  variable, the hidden procedure is visible only in the procedure where it is
  2170.  declared. This feature, which is unique to Pascal, allows you to limit the
  2171.  visibility of a procedure (and that procedure's local variables) in the same
  2172.  way you limit the visibility of local variables. Nesting applies to both
  2173.  procedures and functions.
  2174.  
  2175.  The HIDEPROC.PAS program demonstrates procedure nesting:
  2176.  
  2177.       PROGRAM hideproc;
  2178.  
  2179.       { HIDEPROC.PAS: Demonstrate procedure nesting. }
  2180.  
  2181.       VAR
  2182.           globl : Integer;
  2183.  
  2184.       PROCEDURE proc( p_parm : Integer );
  2185.  
  2186.           VAR
  2187.               p_locl: Integer;
  2188.  
  2189.           PROCEDURE hidden( hidn_parm : Integer );
  2190.  
  2191.                VAR
  2192.                    hidn_locl: Integer;
  2193.  
  2194.                BEGIN
  2195.                    Writeln( 'hidden can see: globl, p_parm, '+
  2196.                             'p_locl, hidn_parm, hidn_locl' );
  2197.                END; { hidden procedure }
  2198.  
  2199.            BEGIN
  2200.                Writeln( 'Proc can see: globl, p_parm, p_locl' );
  2201.                hidden( 44 ); { Pass argument to hidden }
  2202.            END; { proc }
  2203.  
  2204.       BEGIN { main program }
  2205.           Writeln( 'Main program can see: globl' );
  2206.           proc( 99 ); { Pass argument to proc  }
  2207.       END.
  2208.  
  2209.  HIDEPROC.PAS produces this output:
  2210.  
  2211.       Main program can see: globl
  2212.       Proc can see: globl, p_parm, p_locl
  2213.       Hidden can see: globl, p_parm, p_locl, hidn_parm, hidn_locl
  2214.  
  2215.  The program has two procedures named proc and hidden. Because the hidden
  2216.  procedure declaration appears in the proc declaration, hidden is visible
  2217.  only inside proc.
  2218.  
  2219.  The program's output shows how nesting affects variable visibility. At the
  2220.  deepest level in HIDEPROC.PAS──inside hidden──all of the program's variables
  2221.  are visible. The hidden procedure can see its own local variables, plus the
  2222.  variables local to the proc procedure, plus all of the global variables. At
  2223.  the next level──inside proc──visibility is more restricted. The proc
  2224.  procedure can see its own local variables and the global variables, but not
  2225.  the variables local to hidden. The main program has the most restricted
  2226.  visibility. It can see only global variables.
  2227.  
  2228.  Nesting also affects the visibility of a procedure itself. The hidden
  2229.  procedure can be called only from the proc procedure, where it is declared.
  2230.  If you call hidden from the main program, QuickPascal issues an error, just
  2231.  as it would if the main program referred to one of the local variables in
  2232.  proc.
  2233.  
  2234.  
  2235.  3.5  Recursion
  2236.  
  2237.  "Recursion" is the ability of a procedure or function to call itself. The
  2238.  primary use of recursion is in solving certain mathematical problems that
  2239.  require repetitive operations.
  2240.  
  2241.  The RECURSE.PAS program demonstrates recursion. It is a revision of the
  2242.  LOCAL.PAS program that demonstrated local variables. Like its predecessor,
  2243.  RECURSE.PAS computes a factorial. But instead of a loop, it uses a recursive
  2244.  function named factor:
  2245.  
  2246.       PROGRAM recurse;
  2247.  
  2248.       { RECURSE.PAS: Demonstrate recursion. }
  2249.  
  2250.       USES
  2251.           Crt;
  2252.  
  2253.       VAR
  2254.           num : Byte;
  2255.           result : Real;
  2256.  
  2257.       FUNCTION factor( value : Byte ) : Real;
  2258.           BEGIN
  2259.               IF (value > 1) THEN
  2260.                   factor := value * factor( value - 1 )
  2261.               ELSE
  2262.                   factor := 1.0;
  2263.           END; { factor }
  2264.  
  2265.       BEGIN
  2266.           Write( 'Enter a number smaller than 34: ' );
  2267.           Readln( num );
  2268.           result := factor( num );
  2269.           Write( 'Factorial of ', num, ' is ' );
  2270.           Writeln( result );
  2271.       END.
  2272.  
  2273.  The output from RECURSE.PAS and LOCAL.PAS is identical:
  2274.  
  2275.       Enter a number smaller than 34: 5
  2276.       Factorial of 5 is 1.20000000000000E+0002
  2277.  
  2278.  The only difference between normal functions and recursive functions is that
  2279.  a recursive function contains a statement that calls itself. Here is the
  2280.  recursive statement in RECURSE.PAS:
  2281.  
  2282.       Factor := value * factor( value - 1 )
  2283.  
  2284.  The expression on the right side of the assignment operator contains a call
  2285.  to the factor function. The first call to factor can trigger a second call,
  2286.  which can trigger a third call, and so on.
  2287.  
  2288.  The IF statement at the beginning of the factor function prevents the
  2289.  function from calling itself endlessly. Every recursive procedure and
  2290.  function must include such an exit mechanism.
  2291.  
  2292.  Most recursive procedures and functions exploit the fact that each
  2293.  invocation of a procedure or function creates a new set of local variables.
  2294.  Recursion can be very efficient in terms of programming time. You may be
  2295.  able to solve a complex math problem with only a few lines of code. But
  2296.  deeply recursive procedures can also be memory inefficient, consuming huge
  2297.  amounts of memory to store local variables.
  2298.  
  2299.  
  2300.  Chapter 4  Controlling Program Flow
  2301.  ───────────────────────────────────────────────────────────────────────────
  2302.  
  2303.  Like other high-level languages, Pascal offers a wide variety of ways to
  2304.  control a program's flow of execution. This chapter discusses looping
  2305.  statements, which perform repetitive actions, and decision-making
  2306.  statements, which transfer control based on logical tests. Before examining
  2307.  those statements in detail, this chapter briefly summarizes the operators
  2308.  used in logical tests.
  2309.  
  2310.  
  2311.  4.1  Relational and Boolean Operators
  2312.  
  2313.  All of the looping and branching statements in Pascal depend on the outcome
  2314.  of a Boolean (true or false) test. Such tests use relational and Boolean
  2315.  operators, which look familiar to anyone who knows BASIC or C.
  2316.  
  2317.  Even if you have never seen a line of Pascal code, you may be able to guess
  2318.  that the statement
  2319.  
  2320.       IF my_val = 20 THEN Writeln( 'my_val equals 20' );
  2321.  
  2322.  prints the message
  2323.  
  2324.       my_val equals 20
  2325.  
  2326.  if the value of the variable my_val equals 20. (The Pascal IF statement, as
  2327.  you'll read later in this chapter, works very much like IF in BASIC and C.)
  2328.  
  2329.  The example uses the equality operator (=) to compare the variable my_val to
  2330.  the constant 20. It produces a True result when my_val equals 20, and a
  2331.  False result in every other case.
  2332.  
  2333.  ───────────────────────────────────────────────────────────────────────────
  2334.  NOTE
  2335.     True and False are symbolic values in Pascal. Although they are
  2336.     represented by actual numbers internally, you don't need to worry about
  2337.     what those numbers are.
  2338.  ───────────────────────────────────────────────────────────────────────────
  2339.  
  2340.  Relational operators, including the equality operator, compare two values
  2341.  and produce a True or False result. Table 4.1 lists all of the Pascal
  2342.  relational operators.
  2343.  
  2344.  Table 4.1  Relational Operators
  2345.  
  2346.        Operator            Description
  2347.  
  2348.        =                   Equal
  2349.  
  2350.        <>                  Not equal
  2351.  
  2352.        <                   Less than
  2353.  
  2354.        >                   Greater than
  2355.  
  2356.        <=                  Less than or equal
  2357.  
  2358.        >=                  Greater than or equal
  2359.  
  2360.  
  2361.  A second group of operators allows you to perform Boolean logical
  2362.  operations. They are listed in Table 4.2.
  2363.  
  2364.  Table 4.2  Boolean Operators
  2365.  
  2366.        Operator            Description
  2367.  
  2368.        NOT                 Negation
  2369.  
  2370.        AND                 Logical AND
  2371.  
  2372.        OR                  Logical OR
  2373.  
  2374.        XOR                 Exclusive OR
  2375.  
  2376.  
  2377.  Boolean operators (except for NOT) can act on one or two values, allowing
  2378.  more complex logical tests. For instance, the statement
  2379.  
  2380.       IF ((my_val > 3) AND (my_val < 20)) THEN Writeln( 'Wahoo!');
  2381.  
  2382.  tests two conditions instead of one. It prints the message
  2383.  
  2384.       Wahoo!
  2385.  
  2386.  if the value of my_val is greater than 3 and less than 20.
  2387.  
  2388.  Pascal provides many more operators, but these are the important ones for
  2389.  controlling program flow. The QP Advisor contains information about all of
  2390.  the QuickPascal operators.
  2391.  
  2392.  
  2393.  4.2  Looping Statements
  2394.  
  2395.  A loop performs one of the most basic computer operations: repeating an
  2396.  action. This section discusses the Pascal looping statements: WHILE, REPEAT,
  2397.  and FOR.
  2398.  
  2399.  
  2400.  4.2.1  WHILE Loops
  2401.  
  2402.  A WHILE loop is the simplest kind of loop. It repeats 0 or more times, as
  2403.  long as a given condition remains true. The QWHILE.PAS program contains a
  2404.  simple WHILE loop.
  2405.  
  2406.       PROGRAM qwhile;
  2407.  
  2408.       { QWHILE.PAS: Demonstrate WHILE loop. }
  2409.  
  2410.       VAR
  2411.           count : Integer;
  2412.  
  2413.       BEGIN
  2414.  
  2415.           count := 0;
  2416.  
  2417.           WHILE count < 10 DO
  2418.               BEGIN
  2419.               Writeln( 'count = ', count );
  2420.               count := count + 2;
  2421.               END;
  2422.  
  2423.       END.
  2424.  
  2425.  Here is the output from QWHILE.PAS:
  2426.  
  2427.       count = 0
  2428.       count = 2
  2429.       count = 4
  2430.       count = 6
  2431.       count = 8
  2432.  
  2433.  A WHILE loop begins with the WHILE keyword followed by a condition. The loop
  2434.  repeats as long as the condition remains true. In QWHILE.PAS, the condition
  2435.  is
  2436.  
  2437.       count < 10
  2438.  
  2439.  so the loop continues as long as the value of the variable count is less
  2440.  than 10. After the condition is the DO keyword followed by a "loop body,"
  2441.  which can be a single statement or a statement block. In QWHILE.PAS, the
  2442.  loop body is a statement block:
  2443.  
  2444.       BEGIN
  2445.           Writeln( 'count = ', count );
  2446.           count := count + 2;
  2447.       END;
  2448.  
  2449.  You should enclose the body of a WHILE loop with BEGIN and END, even if the
  2450.  loop is only one statement. This convention prevents any confusion about
  2451.  where the loop body ends.
  2452.  
  2453.  It's important to know that a WHILE loop tests its condition before it
  2454.  executes the loop body. Unlike some other kinds of loops, it's possible for
  2455.  a WHILE loop to skip everything in its loop body. If the test condition is
  2456.  false when a WHILE loop begins, the loop body does not execute at all. For
  2457.  instance, if count has the value 10 when the above loop begins, QWHILE.PAS
  2458.  doesn't print anything.
  2459.  
  2460.  
  2461.  4.2.2  REPEAT Loops
  2462.  
  2463.  A REPEAT loop is an inverted WHILE loop. It tests the condition after it
  2464.  executes the loop body, and the loop repeats until the test condition
  2465.  becomes true.
  2466.  
  2467.  The QREPEAT.PAS program performs the same task as QWHILE.PAS, but it uses a
  2468.  REPEAT loop instead of a WHILE loop.
  2469.  
  2470.       PROGRAM qrepeat;
  2471.  
  2472.       { QREPEAT.PAS: Demonstrate REPEAT loop. }
  2473.  
  2474.       VAR
  2475.           count : Integer;
  2476.  
  2477.       BEGIN
  2478.  
  2479.           count := 0;
  2480.  
  2481.           REPEAT
  2482.               Writeln( 'count = ', count );
  2483.               count := count + 2;
  2484.           UNTIL ( count > 8 );
  2485.  
  2486.       END.
  2487.  
  2488.  The output from QREPEAT.PAS and QWHILE.PAS is identical:
  2489.  
  2490.       count = 0
  2491.       count = 2
  2492.       count = 4
  2493.       count = 6
  2494.       count = 8
  2495.  
  2496.  The REPEAT loop in QREPEAT.PAS contains the same loop body as the WHILE loop
  2497.  in QWHILE.PAS:
  2498.  
  2499.       REPEAT
  2500.           Writeln( 'count = ', count );
  2501.           count := count + 2;
  2502.       UNTIL ( count > 8 );
  2503.  
  2504.  You don't need to enclose the loop body of a REPEAT loop with the BEGIN and
  2505.  END keywords (although adding them does no harm). Since the loop body is
  2506.  already enclosed between two keywords (REPEAT and UNTIL), there can be no
  2507.  confusion about where the block begins and ends.
  2508.  
  2509.  Remember that a REPEAT loop always executes the loop body at least once. If
  2510.  count has the value 10 when the loop begins, QREPEAT.PAS prints
  2511.  
  2512.       count = 10
  2513.  
  2514.  even though 10 is clearly greater than 8, the cutoff value in the test
  2515.  condition. The value of count is not tested until after the loop body has
  2516.  executed.
  2517.  
  2518.  Notice that WHILE and REPEAT loops use opposite logical tests. A WHILE loop
  2519.  continues as long as the test condition is true, but a REPEAT loop continues
  2520.  until the test condition becomes true (or, to put it differently, as long as
  2521.  the test condition is false). To illustrate, the WHILE loop in QWHILE.PAS
  2522.  continues as long as count is less than 10:
  2523.  
  2524.       count < 10
  2525.  
  2526.  However, the REPEAT loop in QREPEAT.PAS continues until count becomes
  2527.  greater than 8:
  2528.  
  2529.       count > 8
  2530.  
  2531.  
  2532.  4.2.3  FOR Loops
  2533.  
  2534.  WHILE and REPEAT loops are ideal for cases in which you cannot predict how
  2535.  many repetitions are needed. A program that gets keyboard input, for
  2536.  instance, might use REPEAT to repeat an action until you press a certain
  2537.  key. Sometimes, however, you know in advance exactly how many repetitions
  2538.  are required.
  2539.  
  2540.  The FOR loop repeats a statement, or statement block, a set number of times.
  2541.  The QFOR.PAS program contains a simple FOR loop:
  2542.  
  2543.       PROGRAM qfor;
  2544.  
  2545.       { QFOR.PAS: Demonstrate FOR loop. }
  2546.  
  2547.       VAR
  2548.           count : Integer;
  2549.  
  2550.       BEGIN
  2551.  
  2552.           FOR count := 0 TO 10 DO
  2553.               Writeln( 'count = ', count );
  2554.  
  2555.       END.
  2556.  
  2557.  QFOR.PAS produces this output:
  2558.  
  2559.       count = 0
  2560.       count = 1
  2561.       count = 2
  2562.       count = 3
  2563.       count = 4
  2564.       count = 5
  2565.       count = 6
  2566.       count = 7
  2567.       count = 8
  2568.       count = 9
  2569.       count = 10
  2570.  
  2571.  The FOR loop in QFOR.PAS counts from 0 to 10 in increments of 1:
  2572.  
  2573.       FOR count := 0 TO 10 DO
  2574.           Writeln( 'count = ', count );
  2575.  
  2576.  In this example the control variable count is first set to 0. Each
  2577.  repetition executes the loop body once and adds 1 to count until count
  2578.  reaches 10, the terminating value.
  2579.  
  2580.  FOR loops can count down as well as up. The TO keyword makes the loop count
  2581.  up in increments of 1, and DOWNTO has the opposite effect. If you substitute
  2582.  this loop in QFOR.PAS, the loop counts down from 10 to0 in increments of 1:
  2583.  
  2584.       FOR count := 10 DOWNTO 0 DO
  2585.           Writeln( 'count = ', count );
  2586.  
  2587.  The loop body in QFOR.PAS happens to be a single statement. If the loop body
  2588.  is a statement block, you must enclose the block with BEGIN and END
  2589.  statements:
  2590.  
  2591.       FOR count := 0 TO 10 DO
  2592.           BEGIN
  2593.           Writeln( 'count = ', count );
  2594.           Writeln( 'Another statement' );
  2595.           END;
  2596.  
  2597.  
  2598.  4.3  Decision-Making Statements
  2599.  
  2600.  Decision-making statements allow your program to perform different actions
  2601.  based on the outcome of a logical test. This section examines the Pascal
  2602.  decision-making statements: IF and CASE.
  2603.  
  2604.  
  2605.  4.3.1  IF Statements
  2606.  
  2607.  An IF statement consists of the IF keyword followed by a test expression and
  2608.  the THEN keyword. After THEN is a statement or statement block. The
  2609.  statement is executed if the test expression is true, or skipped if it is
  2610.  false.
  2611.  
  2612.  The QIF.PAS program contains a simple IF statement:
  2613.  
  2614.       PROGRAM qif;
  2615.  
  2616.       { QIF.PAS: Demonstrate IF statement.  }
  2617.  
  2618.       VAR
  2619.           my_val : Integer;
  2620.  
  2621.       BEGIN
  2622.  
  2623.           my_val := 3;
  2624.  
  2625.           IF (my_val = 3) THEN Writeln( 'my_val equals 3' );
  2626.  
  2627.       END.
  2628.  
  2629.  Here is the IF statement from QIF.PAS:
  2630.  
  2631.       IF (my_val = 3) THEN Writeln( 'my_val equals 3' );
  2632.  
  2633.  In this statement the test condition
  2634.  
  2635.       (my_val = 3)
  2636.  
  2637.  compares the variable my_val to the constant 3. Since the comparison is
  2638.  true (my_val does equal 3), QIF.PAS prints:
  2639.  
  2640.       my_val equals 3
  2641.  
  2642.  The statement following THEN can be a single statement or a statement block.
  2643.  A block must be enclosed with BEGIN and END statements:
  2644.  
  2645.       IF (my_val = 3) THEN
  2646.           BEGIN
  2647.           Writeln( 'my_val equals 3' );
  2648.           Writeln( 'Another statement' );
  2649.           END;
  2650.  
  2651.  
  2652.  4.3.2  ELSE Clauses
  2653.  
  2654.  The ELSE keyword allows an IF statement to perform more complex branching.
  2655.  The QELSE.PAS program adds an ELSE clause to QIF.PAS:
  2656.  
  2657.       PROGRAM qelse;
  2658.  
  2659.       { QELSE.PAS: Demonstrate ELSE clause. }
  2660.  
  2661.  
  2662.       VAR
  2663.           my_val : Integer;
  2664.  
  2665.       BEGIN
  2666.  
  2667.           my_val := 555;
  2668.  
  2669.           IF (my_val = 3) THEN
  2670.                Writeln( 'my_val equals 3' )
  2671.           ELSE
  2672.                Writeln( 'my_val does not equal 3' )
  2673.  
  2674.       END.
  2675.  
  2676.  The QELSE.PAS program contains the following IF...ELSE statement:
  2677.  
  2678.       IF (my_val = 3) THEN
  2679.            Writeln( 'my_val equals 3' )
  2680.       ELSE
  2681.            Writeln( 'my_val does not equal 3' );
  2682.  
  2683.  The ELSE clause allows the IF statement to take two alternate actions. The
  2684.  IF statement prints
  2685.  
  2686.       my_val equals 3
  2687.  
  2688.  if my_val equals 3, and it prints
  2689.  
  2690.       my_val does not equal 3
  2691.  
  2692.  in all other cases. Note that a semicolon does not precede the ELSE, because
  2693.  ELSE is considered part of the IF statement.
  2694.  
  2695.  You can nest and combine IF statements and ELSE clauses as needed. Each ELSE
  2696.  is associated with the closest preceding IF that does not have an ELSE
  2697.  already. Consider this example:
  2698.  
  2699.       IF (my_val > 9) THEN
  2700.           IF (my_val = 10) THEN
  2701.               Writeln( 'Ten' )
  2702.           ELSE
  2703.               Writeln( 'Greater than nine, but not ten ')
  2704.       ELSE
  2705.           Writeln( 'Less than ten' );
  2706.  
  2707.  The example can take three different actions. If my_val is greater then 9
  2708.  and equal to 10, it prints Ten. If my_val is greater then 9 but not equal to
  2709.  10, it prints Greater than nine, but not ten. If my_val is less than or
  2710.  equal to 9, it prints Less than ten.
  2711.  
  2712.  Use a BEGIN...END block to enclose the nested IF statement when the ELSE
  2713.  applies to the surrounding IF, as shown below:
  2714.  
  2715.       IF (my_val > 9) THEN
  2716.            BEGIN
  2717.            IF (my_val = 10) THEN
  2718.                  Writeln( 'Ten' );
  2719.            END
  2720.       ELSE
  2721.            Writeln( 'Less than ten ');
  2722.  
  2723.  This example prints Ten if my_val equals 10, and Less than  ten otherwise.
  2724.  Without the BEGIN...END block, the ELSE clause would apply to the IF (my_val
  2725.  = 10) condition, and not to IF (my_val> 9).
  2726.  
  2727.  
  2728.  4.3.3  CASE Statements
  2729.  
  2730.  As the previous example demonstrates, complex IF...ELSE statements can be
  2731.  difficult to read. If all of the branches test the same value (as in the
  2732.  previous example), the CASE statement provides a cleaner solution.
  2733.  
  2734.  The Pascal CASE statement is similar to SELECT CASE in BASIC or the switch
  2735.  statement in C. It can branch to several different alternatives based on the
  2736.  value of a single ordinal expression test. The QCASE.PAS program contains a
  2737.  simple CASE statement:
  2738.  
  2739.       PROGRAM qcase;
  2740.  
  2741.       { QCASE.PAS: Demonstrate CASE statement. }
  2742.  
  2743.       VAR
  2744.           my_val : Integer;
  2745.  
  2746.       BEGIN
  2747.  
  2748.           my_val := 33;
  2749.  
  2750.           CASE my_val OF
  2751.               10 : Writeln( 'Ten' );
  2752.               20 : Writeln( 'Twenty' )
  2753.               ELSE
  2754.                   Writeln( 'Not ten or twenty' );
  2755.               END;
  2756.  
  2757.       END.
  2758.  
  2759.  A CASE statement begins with the CASE keyword, followed by an ordinal
  2760.  expression and the OF keyword. The CASE statement in QCASE.PAS tests the
  2761.  value of my_val:
  2762.  
  2763.       CASE my_val OF
  2764.  
  2765.  Next comes a list of alternatives, each labeled with a constant followed by
  2766.  a colon (a "case constant"):
  2767.  
  2768.       10 : Writeln( 'Ten' );
  2769.       20 : Writeln( 'Twenty' );
  2770.  
  2771.  The alternatives to execute can be single statements or statement blocks.
  2772.  Each case constant in the list acts as a target. When my_val equals 10,
  2773.  QCASE.PAS executes the statement after the case constant
  2774.  
  2775.       10:
  2776.  
  2777.  When my_val equals 20, control is transferred to the case constant
  2778.  
  2779.       20:
  2780.  
  2781.  When my_val doesn't match any case constant, QCASE.PAS executes the
  2782.  statement following ELSE:
  2783.  
  2784.       ELSE
  2785.           Writeln( 'Not ten or twenty' );
  2786.  
  2787.  The ELSE clause is optional. If you omit it, and the value of the expression
  2788.  does not match any of the CASE constants, none of the alternatives are
  2789.  executed. Instead, execution proceeds with the first statement following the
  2790.  CASE statements.
  2791.  
  2792.  The CASE statement can use as many case constants as needed. The case
  2793.  constant can be a single constant (as shown above), a group, or a range of
  2794.  constants:
  2795.  
  2796.       CASE my_val OF
  2797.          1600,2000 : Writeln( 'Leap century' );
  2798.          1601..1999: Writeln( 'Non-leap century' );
  2799.       END;
  2800.  
  2801.  The first case constant includes two values: 1600 and 2000. The second
  2802.  includes a range of 399 values: 1601 through 1999.
  2803.  
  2804.  
  2805.  Chapter 5  User-Defined Data Types
  2806.  ───────────────────────────────────────────────────────────────────────────
  2807.  
  2808.  Instead of limiting a program to using only predefined data types,
  2809.  QuickPascal allows you to create your own custom data types that are
  2810.  relevant to the program at hand.
  2811.  
  2812.  An ordinal data type is a collection of values where each value (except the
  2813.  first) has a unique value that precedes it and (except for the last) a
  2814.  unique value that follows it. Examples of ordinal data types are Boolean,
  2815.  Char, Integer, and LongInt. With QuickPascal, you can create your own
  2816.  ordinal types through the use of enumerated types and subrange types.
  2817.  
  2818.  An enumerated data type has a series of unique, ordinal values defined in
  2819.  it. You can think of it in terms of an Integer, but instead of a range of
  2820.  numbers, you designate a range of your own values. A subrange data type is
  2821.  created by specifying the first and last elements of an existing ordinal
  2822.  type. The type may be one of the standard Pascal ordinal types, or an
  2823.  enumerated type of your own creation.
  2824.  
  2825.  In addition to enumerated and subrange types, you can also create a set
  2826.  type. A set holds up to 255 unique values from an existing ordinal type.
  2827.  
  2828.  
  2829.  5.1  Enumerated Data Types
  2830.  
  2831.  Enumerated data types consist of an ordered list of unique identifiers. The
  2832.  identifiers can be anything from cars to days of the week. An enumerated
  2833.  data type is defined simply by listing the identifiers that make up the
  2834.  type.
  2835.  
  2836.  The syntax for an enumerated type is
  2837.  
  2838.       TYPE ListName = (Identifier «, Identifier...»)
  2839.  
  2840.  QuickPascal assigns numbers to the identifiers in the list. The first list
  2841.  element is assigned 0, the second is 1, and so on, up to a maximum of 65,535
  2842.  values. This is the QuickPascal internal representation. You normally refer
  2843.  to a value by its name.
  2844.  
  2845.  An example of an enumerated data type is
  2846.  
  2847.       TYPE
  2848.           japanese_cars = ( honda, isuzu, nissan, toyota );
  2849.  
  2850.  The use of the values assigned to elements in enumerated lists is shown in
  2851.  this statement:
  2852.  
  2853.       VAR
  2854.           rental_car : japanese_cars;
  2855.       BEGIN
  2856.           rental_car := nissan;
  2857.  
  2858.  This puts nissan, with a value of 2, into the variable rental_car.
  2859.  
  2860.  There may be cases where you want the first element of a list to have a
  2861.  non-zero value. You can accomplish this by declaring a fake identifier as
  2862.  the first element:
  2863.  
  2864.       TYPE
  2865.           place = (null, first, second, third, fourth);
  2866.  
  2867.  This provides a more natural numeric ordering for the list elements.
  2868.  
  2869.  QuickPascal provides two procedures and five functions to manipulate
  2870.  enumerated data types. The predefined routines are First, Last, Succ, Pred,
  2871.  Inc, Dec, and Ord.
  2872.  
  2873.  
  2874.  5.1.1  The First Function
  2875.  
  2876.  The First function returns the first element of any ordinal type. The
  2877.  function is passed an ordinal type, and returns a value of the same type.
  2878.  
  2879.  The following example shows you how to use First:
  2880.  
  2881.     PROGRAM enums;
  2882.  
  2883.  
  2884.     TYPE
  2885.         my_type = -5..5;
  2886.         greek   = (alpha, beta, gamma, delta, epsilon);
  2887.         subgreek = gamma..epsilon;
  2888.  
  2889.  
  2890.     BEGIN
  2891.       Writeln( 'Type ':20, 'First(Type)':20, 'Last(Type)':20);
  2892.       Writeln;
  2893.       Writeln( 'Integer':20, First( Integer ):20, Last( Integer ):20 );
  2894.       Writeln( 'LongInt':20, First( LongInt ):20, Last( LongInt ):20 );
  2895.       Writeln( 'ShortInt':20, First( ShortInt ):20, Last( ShortInt ):20 );
  2896.       Writeln( 'Word':20, First( Word ):20, Last( Word ):20 );
  2897.       Writeln( 'Char(ord)':20, Ord( First( Char ) ):20,
  2898.               Ord( Last( Char ) ):20 );
  2899.       Writeln( 'Boolean':20, First( Boolean ):20, Last( Boolean ):20 );
  2900.       Writeln( 'my_type':20, First( my_type ):20, Last( my_type ):20 );
  2901.       Writeln( 'greek(ord)':20, Ord( First( greek ) ):20,
  2902.               Ord( Last( greek ) ):20 );
  2903.       Writeln( 'subgreek(ord)':20, Ord( First( subgreek ) ):20,
  2904.               Ord( Last( subgreek ) ):20 );
  2905.  
  2906.     END.
  2907.  
  2908.  
  2909.  5.1.2  The Last Function
  2910.  
  2911.  The Last function returns the last element of any ordinal type. The function
  2912.  is  passed an ordinal type, and returns a value of the same type.
  2913.  
  2914.  The previous example shows how to use Last.
  2915.  
  2916.  
  2917.  5.1.3  The Succ Function
  2918.  
  2919.  The Succ function returns the successor, or following element, of an
  2920.  enumerated value.
  2921.  
  2922.  Sample Function Call                Ordinal Type               Result
  2923.  
  2924.  Succ(second)                        place                      third
  2925.  
  2926.  Succ(isuzu)                         japanese_cars              nissan
  2927.  
  2928.  Succ(15)                            Integer                    16
  2929.  
  2930.  Succ('a')                           Char                       'b'
  2931.  
  2932.  Succ(False)                         Boolean                    True
  2933.  
  2934.  Both standard and user-defined ordinal types can be used with this function.
  2935.  
  2936.  If range checking {$R+} is set, a run-time error occurs if you try to assign
  2937.  an element beyond the last in the list. You can avoid this problem by using
  2938.  the Last function to check if the element is last in the list.
  2939.  
  2940.  
  2941.  5.1.4  The Pred Function
  2942.  
  2943.  The Pred function returns the predecessor, or preceding element, of an
  2944.  enumerated value.
  2945.  
  2946.  Sample Function Call                Ordinal Type               Result
  2947.  
  2948.  Pred(isuzu)                         japanese_cars              honda
  2949.  
  2950.  Pred(second)                        place                      first
  2951.  
  2952.  Pred(12)                            Integer                    11
  2953.  
  2954.  Pred('b')                           Char                       'a'
  2955.  
  2956.  Pred(True)                          Boolean                    False
  2957.  
  2958.  Both standard and user-defined enumerated data types can be used with this
  2959.  function.
  2960.  
  2961.  If range checking {$R+} is set, a run-time error occurs if you try to assign
  2962.  an element preceding the first in the list. You can avoid an error by using
  2963.  the First function to check if the element is first in the list.
  2964.  
  2965.  
  2966.  5.1.5  The Inc Procedure
  2967.  
  2968.  The Inc procedure provides a shorthand form of the Succ function. Upon
  2969.  calling the procedure, the variable it is passed is incremented by the
  2970.  number of elements specified.
  2971.  
  2972.  For example, instead of using Succ as follows:
  2973.  
  2974.       status := Succ( status );
  2975.       status := Succ( status );
  2976.  
  2977.  the Inc procedure could be used as
  2978.  
  2979.       Inc( status, 2 );
  2980.  
  2981.  If no increment parameter is specified, the variable is incremented by one:
  2982.  
  2983.       Inc( status );
  2984.  
  2985.  The Last function should be used to avoid unpredictable results when
  2986.  incrementing elements.
  2987.  
  2988.  
  2989.  5.1.6  The Dec Procedure
  2990.  
  2991.  The Dec procedure is an alternative to the Pred function. Upon calling the
  2992.  procedure, the variable it is passed is decremented by the number of
  2993.  elements specified.
  2994.  
  2995.  For example, instead of using Pred as follows:
  2996.  
  2997.       status := Pred( status );
  2998.       status := Pred( status );
  2999.  
  3000.  the Dec procedure could be used as
  3001.  
  3002.       Dec( status, 2 );
  3003.  
  3004.  If no decrement parameter is specified, the variable is decremented by one:
  3005.  
  3006.       Dec( status );
  3007.  
  3008.  The First function should be used to avoid unpredictable results when
  3009.  decrementing elements.
  3010.  
  3011.  
  3012.  5.1.7  The Ord Function
  3013.  
  3014.  The Ord function returns the ordinal number of an enumerated element. Since
  3015.  each enumerated element is unique, you need not mention the enumerated data
  3016.  type that the element belongs to. Ordinal values start at zero.
  3017.  
  3018.  Sample Function Call                Ordinal Type               Result
  3019.  
  3020.  Ord(first)                          place                      1
  3021.  
  3022.  Ord(toyota)                         japanese_cars              3
  3023.  
  3024.  Ord('a')                            Char                       97
  3025.  
  3026.  Ord(False)                          Boolean                    0
  3027.  
  3028.  The Ord function accepts both enumeration constants and variables. Both
  3029.  standard and user-defined enumerated data types can be used with the
  3030.  function.
  3031.  
  3032.  
  3033.  5.2  Subrange Types
  3034.  
  3035.  Sometimes, only a few of the elements of an existing data type are needed.
  3036.  Instead of declaring a new data type, Pascal allows you to define a subrange
  3037.  of either a standard or enumerated data type.
  3038.  
  3039.  For example, in a grading program, the variable test_score records grades
  3040.  between 0 and 100. Instead of creating a new data type for the variable, an
  3041.  Integer subrange is declared with a minimum value of 0 and a maximum value
  3042.  of 100.
  3043.  
  3044.  You can define subranges of the following ordinal types:
  3045.  
  3046.    ■  Integers
  3047.  
  3048.    ■  Characters
  3049.  
  3050.    ■  Enumerations
  3051.  
  3052.  To declare a subrange type, identify the SubrangeName and define the first
  3053.  and last value in the range, connected with two dots, as shown in the syntax
  3054.  below:
  3055.  
  3056.       SubrangeName = FirstValue..LastValue
  3057.  
  3058.  The FirstValue and LastValue must be constants of the same type, with the
  3059.  FirstValue as a predecessor of the LastValue. That is,
  3060.  
  3061.       Ord( FirstValue ) <= Ord( LastValue)
  3062.  
  3063.  Subrange types serve two useful purposes:
  3064.  
  3065.    1. If the range of a type needs to be changed, only one change in the
  3066.       declaration is necessary.
  3067.  
  3068.    2. QuickPascal automatically checks the range assigned to variables of the
  3069.       subrange type, with the following limitations:
  3070.  
  3071.         ■  Range checking must be turned on with the compiler directive
  3072.            {$R+}. This can be done at the beginning of the program, or
  3073.            turned on ({$R+}) and off ({$R-}) around a statement where a
  3074.            subrange variable is used. When range checking is on, QuickPascal
  3075.            generates a run-time error if a value outside of the subrange is
  3076.            assigned to a variable of the subrange type. See Appendix B,
  3077.            "Compiler Directives," for more information.
  3078.  
  3079.         ■  QuickPascal does range checking only in direct assignment
  3080.            statements. It does not check for out-of-range values in
  3081.            loop-control variables or Read/Write statements.
  3082.  
  3083.  
  3084.  5.2.1  Integer Subranges
  3085.  
  3086.  Integer subranges define a range of valid integer values. Some examples of
  3087.  simple integer subranges include
  3088.  
  3089.       TYPE
  3090.          screen_columns = 1..80;
  3091.          die_faces      = 1..6;  { values on dice faces }
  3092.          days           = 1..31; { max. 31 in a month }
  3093.          months         = 1..12; { 12 months/year }
  3094.          years          = 1900..2099; { the years DOS knows }
  3095.          seconds        = 0..59;
  3096.          minutes        = 0..59;
  3097.          hours          = 0..23;
  3098.  
  3099.  Constant identifiers can define the subranges. Examples include
  3100.  
  3101.       CONST
  3102.          max_col            = 80;
  3103.          max_row            = 25; { or 43 for EGA screen }
  3104.          max_days_per_month = 31;
  3105.          months_per_year    = 12;
  3106.          min_year           = 1900;
  3107.          max_year           = 2099;
  3108.  
  3109.       TYPE
  3110.          screen_columns = 1..max_col;
  3111.          screen_rows    = 1..max_row;
  3112.          days           = 1..max_days_per_month;
  3113.          months         = 1..months_per_year;
  3114.          years          = min_year..max_year;
  3115.  
  3116.  Adding constants enhances readability and simplifies the subrange limits.
  3117.  For example, if you are developing a program that employs EGA video, you
  3118.  need to display 43 lines per screen. Variables of the screen_rows type
  3119.  should fall within the 1..43 range. All that is required is to change the
  3120.  constant max_row to 43 at one location.
  3121.  
  3122.  QuickPascal permits expressions in defining ranges. The following are
  3123.  examples of declarations that include constant expressions:
  3124.  
  3125.       CONST
  3126.          sec_per_minute  = 60;
  3127.          minute_per_hour = 60;
  3128.          hour_per_day    = 24;
  3129.  
  3130.       TYPE
  3131.          seconds = 0..sec_per_minute - 1;
  3132.          minutes = 0..minute_per_hour - 1;
  3133.          hours   = 0..hour_per_day - 1;
  3134.  
  3135.  
  3136.  5.2.2  Character Subranges
  3137.  
  3138.  Character subranges define a range of acceptable Char values. Examples
  3139.  include
  3140.  
  3141.       TYPE
  3142.          up_case_char = 'A'..'Z';
  3143.          lo_case_char = 'a'..'z';
  3144.          digit_char   = '0'..'9';
  3145.          ctrl_char    = #0..#31;
  3146.          { range of characters after 'Z' and before 'a' }
  3147.          between_Z_a  = Succ('Z') .. Pred('a');
  3148.  
  3149.  Any range of ASCII characters can be defined in a subrange. Note the use of
  3150.  the Succ and Pred functions in the last example to define the six ASCII
  3151.  characters between 'Z' and 'a'. (See the ASCII character chart in Appendix
  3152.  A.)
  3153.  
  3154.  
  3155.  5.2.3  Enumerated Subranges
  3156.  
  3157.  Enumerated subranges limit the range of permissible enumerated values. An
  3158.  example is
  3159.  
  3160.       TYPE
  3161.          vehicles = (volkswagen, honda, toyota, corvette,
  3162.                      porsche, ferrari, suburban, blazer,
  3163.                      bronco);
  3164.  
  3165.          economy_cars = volkswagen..toyota;
  3166.          sports_cars  = corvette..ferrari;
  3167.  
  3168.  The type economy_cars is a subrange with legal values of volkswagen, honda,
  3169.  and toyota. Similarly, the sports_cars type has only the enumerated
  3170.  corvette, porsche,  and ferrari values.
  3171.  
  3172.  
  3173.  5.3  Sets
  3174.  
  3175.  Sets are structured, user-defined data types. In mathematics, a set is an
  3176.  unordered collection of elements. The concept of a set is the same in
  3177.  QuickPascal. A set holds only unique values. For example, if A, B, and C
  3178.  were contained in a set, and you added B to it, the set would still only
  3179.  contain A, B, and C, not A, B, B, and C. Sets are useful for holding a
  3180.  collection of attributes or determining if an element is a member of a
  3181.  particular group.
  3182.  
  3183.  The syntax for declaring a set is
  3184.  
  3185.       SetName = SET OF OrdinalType
  3186.  
  3187.  The argument OrdinalType is an ordered range of values. The members of the
  3188.  OrdinalType must be all of the same type, and can be single elements or a
  3189.  subrange. The OrdinalType cannot have more than 256 possible values. This
  3190.  limits sets to the predefined Boolean, Char, and Byte types, and restricts
  3191.  the use of Word, Integer, and LongInt types.
  3192.  
  3193.  As an example, a program might declare a set of uppercase letters and vowels
  3194.  to be
  3195.  
  3196.       CONST
  3197.           vowels     = ['A', 'E', 'I', 'O', 'U'];
  3198.           upper_case = SET OF 'A'..'Z' = ['A'..'Z'];
  3199.  
  3200.  Once you have declared a set, the operator IN can be used to test for the
  3201.  presence or absence of a specified element. For example, in these statements
  3202.  
  3203.       IF ch IN upper_case THEN...
  3204.       IF ch IN vowels THEN...
  3205.  
  3206.  IN returns a true result if ch is a member of the set, and false if it is
  3207.  not.
  3208.  
  3209.  
  3210.  5.3.1  Declaring Set Types
  3211.  
  3212.  Sets can be declared with a variety of types. Included are
  3213.  
  3214.    ■  Predefined ordinal types of Boolean, Char, and Byte
  3215.  
  3216.          TYPE
  3217.          boolean_set = SET OF Boolean;
  3218.          char_set    = SET OF Char;
  3219.          byte_set    = SET OF Byte;
  3220.  
  3221.    ■  Subranges of predefined types (either directly as the first set or
  3222.       indirectly as the last)
  3223.  
  3224.          TYPE
  3225.          bits       = 1..7;
  3226.          byte_bits  = SET OF bits;
  3227.          up_case    = SET OF 'A'..'Z';
  3228.          lo_case    = SET OF 'a'..'z';
  3229.  
  3230.    ■  Enumerated types
  3231.  
  3232.          TYPE
  3233.          transportation  = (bicycle, motorcycle, car, truck,
  3234.          bus);
  3235.          four_wheels    = car..bus;
  3236.          trans_set      = SET OF transportation;
  3237.          four_wheel_set  = SET OF four_wheels;
  3238.  
  3239.    ■  Variables
  3240.  
  3241.          VAR
  3242.          fast_trans    : four_wheels;
  3243.          lower_letters : lo_case;
  3244.          num1, num2    : byte_bits;
  3245.  
  3246.    ■  Constants and typed constants
  3247.  
  3248.          CONST
  3249.          math_op     : SET OF Char = ['=', '-', '^(*)', '/'];
  3250.          vowels      : SET OF Char = ['A','E','I','O','U'];
  3251.          up_chars    : SET OF Char = ['A'..'Z'];
  3252.          lo_chars    : SET OF Char = ['a'..'z'];
  3253.          cheap_trans : SET OF transportation =
  3254.          [bicycle, motorcycle, bus];
  3255.  
  3256.  Character set constants are useful in representing fixed sets of characters
  3257.  used in menu selections. For example, if you have a menu with the following
  3258.  selections:
  3259.  
  3260.       Add    Change    Delete    Print    View    Store    Recall
  3261.  
  3262.  where the capital letters are "hot" keys used to quickly select a menu
  3263.  option, the corresponding typed constant would look like this:
  3264.  
  3265.       CONST
  3266.        menu_char : SET OF Char = ['A','C','D','P','V','S','R'];
  3267.  
  3268.  
  3269.  5.3.2  Assigning Set Elements to Variables
  3270.  
  3271.  To assign set elements to a set variable, use the square brackets:
  3272.  
  3273.       SetVariable := [SetElements]
  3274.  
  3275.  If there are no set elements present, the set variable is assigned an empty
  3276.  set (SetVariable: = []). Set variables may be initialized in this way.
  3277.  
  3278.  A set may be constructed from a list of single elements, a subrange, or a
  3279.  combination of both. Examples of assigning set elements to variables are
  3280.  
  3281.       set1 := [1, 3, 5, 7, 9];{ single elements }
  3282.       set2 := [0..7]; { subrange }
  3283.       set3 := [0..7, 14, 15, 16]; { subrange & single elements }
  3284.       char_list := ['A'..'Z','a'..'z','0'..'9'];  { subranges }
  3285.  
  3286.  
  3287.  5.3.3  Set Operators
  3288.  
  3289.  Although individual elements of a set cannot be directly accessed, a variety
  3290.  of operators is available to test membership and manipulate the set as a
  3291.  whole.
  3292.  
  3293.  These operators offer powerful and flexible methods of creating new sets
  3294.  with elements from existing sets. Set operators supported by QuickPascal
  3295.  include
  3296.  
  3297.    ■  Relational operators
  3298.  
  3299.    ■  IN operator
  3300.  
  3301.    ■  Set-union operator
  3302.  
  3303.    ■  Set-difference operator
  3304.  
  3305.    ■  Set-intersection operator
  3306.  
  3307.  5.3.3.1  Relational Operators
  3308.  
  3309.  A variety of relational operators is available to test set membership. Based
  3310.  on the condition of an expression, the operator will return True or False.
  3311.  
  3312.  Table 5.1 lists the relational operators that work on sets, with A and B as
  3313.  example sets.
  3314.  
  3315.  Table 5.1  Relational Operators
  3316.  
  3317.  Expression      Returns True if
  3318.  
  3319.  A = B           A and B are identical.
  3320.  
  3321.  A <> B          At least one element in A is
  3322.                  not in B, or at least one element
  3323.                  in B is not in  A.
  3324.  
  3325.  A <= B          All elements in A are in B.
  3326.  
  3327.  A >= B          All elements in B are in A.
  3328.  
  3329.  
  3330.  5.3.3.2  IN Operator
  3331.  
  3332.  As previously discussed, the IN operator tests for set membership. This
  3333.  operator returns a Boolean result indicating whether a value is a member of
  3334.  the set. The tested value must be the same or of a compatible type with the
  3335.  tested set's base type. The syntax is
  3336.  
  3337.       Value IN Set
  3338.  
  3339.  For example,
  3340.  
  3341.       ch IN vowels
  3342.       'i' IN consonants
  3343.       operator IN ['+', '-', '/', '*']
  3344.  
  3345.  Figure 5.1 displays the action of set operators on sets A and B. The shaded
  3346.  area represents the result of the operations on the sets.
  3347.  
  3348.  Figure 5.1  Set Operators
  3349.  
  3350.          │        A + B         │         A - B         │         A * B
  3351.          │       (Union)        │      (Difference)     │    (Intersection)
  3352.  ────────┼──────────────────────┼───────────────────────┼─────────────────────
  3353.          │┌───────────┐         │ ┌───────────┐         │ ┌───────────┐
  3354.  A and B ││▒A▒▒▒▒▒▒▒▒▒│ ┌──────┐│ │▒A▒▒▒▒▒▒▒▒▒│ ┌──────┐│ │ A         │ ┌─────
  3355.    are   ││▒▒▒▒▒▒▒▒▒▒▒│ │▒▒▒▒B▒││ │▒▒▒▒▒▒▒▒▒▒▒│ │    B ││ │           │ │    B
  3356.  disjoint││▒▒▒▒▒▒▒▒▒▒▒│ │▒▒▒▒▒▒││ │▒▒▒▒▒▒▒▒▒▒▒│ │      ││ │           │ │
  3357.   sets.  ││▒▒▒▒▒▒▒▒▒▒▒│ └──────┘│ │▒▒▒▒▒▒▒▒▒▒▒│ └──────┘│ │           │ └─────
  3358.          │└───────────┘         │ └───────────┘         │ └───────────┘
  3359.  ────────┼──────────────────────┼───────────────────────┼─────────────────────
  3360.          │  ┌───────────┐       │   ┌───────────┐       │   ┌───────────┐
  3361.     A    │  │▒A▒▒▒▒▒▒┌──┼───┐   │   │▒A▒▒▒▒▒▒┌──┼───┐   │   │ A      ┌──┼───┐
  3362.   inter- │  │▒▒▒▒▒▒▒▒│▒▒│▒B▒│   │   │▒▒▒▒▒▒▒▒│  │ B │   │   │        │▒▒│ B │
  3363.   sects  │  │▒▒▒▒▒▒▒▒│▒▒│▒▒▒│   │   │▒▒▒▒▒▒▒▒│  │   │   │   │        │▒▒│   │
  3364.     B    │  │▒▒▒▒▒▒▒▒└──┼───┘   │   │▒▒▒▒▒▒▒▒└──┼───┘   │   │        └──┼───┘
  3365.          │  └───────────┘       │   └───────────┘       │   └───────────┘
  3366.  ────────┼──────────────────────┼───────────────────────┼─────────────────────
  3367.          │    ┌───────────┐     │     ┌───────────┐     │     ┌───────────┐
  3368.   B is a │    │▒A▒┌──────┐│     │     │▒A▒┌──────┐│     │     │ A ┌──────┐│
  3369.   subset │    │▒▒▒│▒▒▒▒B▒││     │     │▒▒▒│    B ││     │     │   │▒▒▒▒B▒││
  3370.    of A  │    │▒▒▒│▒▒▒▒▒▒││     │     │▒▒▒│      ││     │     │   │▒▒▒▒▒▒││
  3371.          │    │▒▒▒└──────┘│     │     │▒▒▒└──────┘│     │     │   └──────┘│
  3372.          │    └───────────┘     │     └───────────┘     │     └───────────┘
  3373.  ────────┴──────────────────────┴───────────────────────┴─────────────────────
  3374.  
  3375.  
  3376.  5.3.3.3  Set-Union Operator
  3377.  
  3378.  The set-union operator (+) merges two sets into a third set. If either set
  3379.  is a subset of the other, combining the two sets results in a set that is
  3380.  the same as the larger set. In the example below, two sets with unique
  3381.  members are merged, resulting in a larger set:
  3382.  
  3383.       set1 := ['A'..'Z'];
  3384.       set2 := ['a'..'z'];
  3385.       set3 := set1 + set2;
  3386.       set3 := ['A'..'Z','a'..'z']; { same as previous assignment }
  3387.  
  3388.  In the next example, the two character sets have overlapping members. The
  3389.  united set consists of ['A'..'Z'] with any overlapping members represented
  3390.  only once.
  3391.  
  3392.       set1 := ['A'..'L'];
  3393.       set2 := ['H'..'Z'];
  3394.       set3 := set1 + set2;
  3395.       set3 := ['A'..'Z'];{ equivalent to previous assignment }
  3396.  
  3397.  The third example shows a set being united with a subset. This results in
  3398.  set3 and set1 having the same members.
  3399.  
  3400.       set1 := ['A'..'L'];
  3401.       set2 := ['F'..'J'];
  3402.       set3 := set1 + set2;
  3403.       set3 := ['A'..'L'];  { equivalent to previous assignment }
  3404.  
  3405.  The union operator is also important for adding to the membership of a set.
  3406.  In the following example, a character set is initialized and then a FOR loop
  3407.  is used to add the characters A,B,C,D,E,F,...,:
  3408.  
  3409.       set1 := []; { initialize }
  3410.       FOR ch := 'A' TO 'Z' DO
  3411.           set1 := set1 + [ch];
  3412.  
  3413.  Note the presence of the set brackets around the ch variable. They are
  3414.  required in order to make [ch] a single-element set.
  3415.  
  3416.  Refer to Figure 5.1 for an illustration of the union operator.
  3417.  
  3418.  5.3.3.4  Set-Difference Operator
  3419.  
  3420.  The set-difference operator (-) creates a set that contains all of the
  3421.  members of the first set that do not appear in the second set. For example,
  3422.  in the statement
  3423.  
  3424.       set3 := set1 - set2;
  3425.  
  3426.  set3 will contain all of the elements in set1 that are not in set2.
  3427.  
  3428.  If set1 and set2 have the same members, then set3 becomes an empty set. If
  3429.  set2 is a subset of set1, then set3 comprises of the members of set1 that
  3430.  are not common to set2.
  3431.  
  3432.  In the following example, the difference between two sets with unique
  3433.  members is assigned to a third set. The resulting set has the same members
  3434.  as set1 since the operand sets have nothing in common:
  3435.  
  3436.       set1 := ['A'..'Z'];
  3437.       set2 := ['a'..'z'];
  3438.       set3 := set1 - set2;
  3439.       set3 := ['A'..'Z']; { equivalent to previous assignment }
  3440.  
  3441.  In the next example, the two character sets have overlapping members. The
  3442.  resulting set is made up of ['A'..'G']:
  3443.  
  3444.       set1 := ['A'..'L'];
  3445.       set2 := ['H'..'Z'];
  3446.       set3 := set1 - set2;
  3447.       set3 := ['A'..'G']; { equivalent to previous assignment }
  3448.  
  3449.  The next example shows the difference of a set with its subset. The result
  3450.  is that set3 contains ['A'..'E'], the members of the first set not found in
  3451.  the second one.
  3452.  
  3453.       set1 := ['A'..'L'];
  3454.       set2 := ['F'..'J'];
  3455.       set3 := set1 - set2;
  3456.       set3 := ['A'..'E', 'K', 'L']; { equivalent to previous assignment }
  3457.  
  3458.  The difference operator can also be used to strip single members from set
  3459.  variables. The last example for the union operator can be rewritten to use
  3460.  the difference operator in the following way. The character set is
  3461.  initialized to ['A'..'Z'] and a FOR loop is used to eliminate the A, B, D,
  3462.  F, H, characters and so on.
  3463.  
  3464.       oddeven := 0;
  3465.       set1 := ['A'..'Z']; { initialize }
  3466.       FOR ch := 'B' TO 'Z' DO
  3467.           BEGIN
  3468.           IF (NOT Odd( Oddeven )) THEN
  3469.               set1 := set1 - [ch];
  3470.           Inc( Oddeven );
  3471.           END;
  3472.  
  3473.  Refer to Figure 5.1 for an illustration of the difference operator.
  3474.  
  3475.  5.3.3.5  Set-Intersection Operator
  3476.  
  3477.  The set-intersection operator (*) is used to extract all of the elements
  3478.  that are in two similarly typed sets. For example, with
  3479.  
  3480.       set3 := set1 * set2;
  3481.  
  3482.  set3 will contain all of the elements that are in both set1 and set2.
  3483.  
  3484.  In the next example, the two character sets have overlapping members. The
  3485.  intersection set is ['H'..'L']:
  3486.  
  3487.       set1 := ['A'..'L'];
  3488.       set2 := ['H'..'Z'];
  3489.       set3 := set1 * set2;
  3490.       set3 := ['H'..'L'];
  3491.  
  3492.  The intersection of a set with its subset returns the members of the subset.
  3493.  The next example shows such an operation. The result is that set3 and set2
  3494.  have the same members:
  3495.  
  3496.       set1 := ['A'..'L'];
  3497.       set2 := ['F'..'J'];
  3498.       set3 := set1 * set2;
  3499.       set3 := ['F'..'J'];
  3500.  
  3501.  In the following example, two sets with unique members are intersected. The
  3502.  resulting set is empty, since the operand sets have nothing in common:
  3503.  
  3504.       set1 := ['A'..'Z'];
  3505.       set2 := ['a'..'z'];
  3506.       set3 := set1 * set2;
  3507.       set3 := [];
  3508.  
  3509.  
  3510.  Chapter 6  Arrays and Records
  3511.  ───────────────────────────────────────────────────────────────────────────
  3512.  
  3513.  This chapter presents data types that hold organized collections of data in
  3514.  a definite order.
  3515.  
  3516.  An "array" is a collection of data items of the same type. Programs use
  3517.  arrays in situations where a standard data format is repeated many times.
  3518.  For example, numbers representing the Gross National Product for all the
  3519.  years from 1900 to 1980 might be placed in an array.
  3520.  
  3521.  A "record" is a collection of data items having different types. Programs
  3522.  use records in situations where a variety of data have a close association.
  3523.  For example, all of the information on a given employee──name, salary, and
  3524.  security clearance──might be placed in a single record.
  3525.  
  3526.  Finally, this chapter presents the "variant record" type, which is an
  3527.  extension of the record type. A variant record allows you to use different
  3528.  data formats to access the same area of memory, enabling you to create a
  3529.  record that holds different kinds of information at different times.
  3530.  
  3531.  Each of the major sections in this chapter introduces a data type, shows how
  3532.  to declare it, then shows how to access its components.
  3533.  
  3534.  
  3535.  6.1  Arrays
  3536.  
  3537.  An array is a collection of elements that share the same data type and a
  3538.  common variable name. You access an element in the array by specifying the
  3539.  position of the element. An integer or ordinal value indicates the position
  3540.  and is called an "index."
  3541.  
  3542.  Pascal lets you declare arrays of any type. You can even declare arrays of
  3543.  arrays, which are, in essence, two-dimensional arrays. You can declare
  3544.  arrays with any number of dimensions. Multidimensional arrays have many
  3545.  applications and appear often in Pascal programs. For example, a program
  3546.  that requires a grid will use a two-dimensional array, and a program that
  3547.  maps three-dimensional space will use a three-dimensional array. The
  3548.  simplest arrays are one-dimensional. They consist of a linear sequence of
  3549.  elements.
  3550.  
  3551.  
  3552.  6.1.1  Declaring Arrays
  3553.  
  3554.  In many languages (such as C), arrays must always start at a particular
  3555.  index number (such as 0 or 1). In Pascal, however, you can define the bounds
  3556.  of an array with almost any ordinal type:
  3557.  
  3558.       ARRAY [IndexType] OF ElementType
  3559.  
  3560.  To declare a multidimensional array, you declare an index type for each
  3561.  dimension:
  3562.  
  3563.       ARRAY [IndexType, IndexType ...] OF type
  3564.  
  3565.  IndexType can be any ordinal type except LongInt or subranges of LongInt.
  3566.  Most often, programs use subranges in array declarations. The lower and
  3567.  upper bounds of the subrange give the lowest and highest index, and also
  3568.  determine the size of the array. For example:
  3569.  
  3570.       TYPE
  3571.           income_per_year = ARRAY[1977..1989] OF LongInt;
  3572.           class_size = ARRAY[1..12] OF Integer;
  3573.           grid = ARRAY[-5..5, -10..10] OF Real;
  3574.  
  3575.  As you can see, even negative numbers can serve as array bounds. Because
  3576.  Pascal is so structured, you can use many different integer subranges and
  3577.  enumerated types to index arrays. For example:
  3578.  
  3579.       TYPE
  3580.           pay = LongInt;
  3581.           rank = (private, sergeant, lt, captain, major, general);
  3582.           officers = lt..general ;
  3583.           letters = 'A'..'Z';
  3584.           my_arr = ARRAY[1..10] OF Real;
  3585.  
  3586.       VAR
  3587.           low_pay : ARRAY[private .. sergeant] OF pay;
  3588.           high_pay : ARRAY[officers] OF pay;
  3589.           ascii_code : ARRAY[letters] OF Word;
  3590.           big_arr : ARRAY[letters] OF my_arr;
  3591.  
  3592.  The last example above, big_arr, is really a two-dimensional array and is
  3593.  equivalent to the following declaration, which specifies the ranges of the
  3594.  two dimensions explicitly:
  3595.  
  3596.       big_arr : ARRAY[letters, 1..10] OF Real;
  3597.  
  3598.  Bear in mind the difference between the index type of an array and the
  3599.  element type. The item in brackets defines the index type of the array and
  3600.  is significant in the following ways:
  3601.  
  3602.    ■  The index type determines the range and meaning of indexes. The next
  3603.       section describes how you use indexes to access elements.
  3604.  
  3605.    ■  The index type determines the number of elements in the array. For
  3606.       example, an array with subrange 1..500 has 500 elements. An array with
  3607.       subrange 101..103 has three elements.
  3608.  
  3609.  The elements are declared by the data type at the end of the array
  3610.  declaration. As mentioned above, you can use any data type, including any of
  3611.  the advanced data types mentioned later in this book. Each element of the
  3612.  array has this element type.
  3613.  
  3614.  
  3615.  6.1.2  Accessing Array Elements
  3616.  
  3617.  To refer to an element of an array, use the syntax
  3618.  
  3619.       Name [ Index ]
  3620.  
  3621.  in which Name is the name of the array variable, and Index has the index
  3622.  type used to declare the array. If the index type is a subrange, Index must
  3623.  fall into the specified range or the program produces errors. Consider the
  3624.  following declarations:
  3625.  
  3626.       VAR
  3627.           trio : ARRAY[1..3] OF Word;
  3628.           income : ARRAY[1980..1983] OF LongInt;
  3629.  
  3630.  In the example above, the elements of trio are referred to as
  3631.  
  3632.       trio[1]
  3633.       trio[2]
  3634.       trio[3]
  3635.  
  3636.  The elements of income are referred to as
  3637.  
  3638.       income[1980]
  3639.       income[1981]
  3640.       income[1982]
  3641.       income[1983]
  3642.  
  3643.  Each of the elements above has the data type declared for the array──Word in
  3644.  the case of trio and LongInt in the case of income. You can refer to an
  3645.  element in any context that is valid for a simple variable of the same type.
  3646.  You can alter an element, pass it to a procedure, or assign its value to
  3647.  another variable. The following statements are all valid:
  3648.  
  3649.       trio[1] := 50;
  3650.       trio[2] := trio[1] DIV 2;
  3651.       Writeln( ' Result is : ', trio[1] + trio[2] );
  3652.  
  3653.  The array index can be a variable as well as a constant. In fact, the power
  3654.  of arrays in programming comes largely from the use of variable indexes. The
  3655.  following code uses a loop variable to efficiently initialize a large array
  3656.  of random numbers:
  3657.  
  3658.       VAR
  3659.           i : Integer;
  3660.           big_arr : ARRAY[1..1000] OF Word;
  3661.       BEGIN
  3662.           FOR i := 1 TO 1000 DO
  3663.            big_arr[i] := Random(100);
  3664.  
  3665.  To refer to an element of a multidimensional array, use the syntax
  3666.  
  3667.       Name [Index, Index ... ]
  3668.  
  3669.  in which each Index is of the type and range of the corresponding index in
  3670.  the array declaration. For example, the following code uses a nested loop to
  3671.  efficiently initialize a two-dimensional array of random numbers:
  3672.  
  3673.       VAR
  3674.           i, j : Integer;
  3675.           results : ARRAY[1..max_row, 1..max_col] OF Word;
  3676.       BEGIN
  3677.           FOR i := 1 TO max_row DO
  3678.            FOR j := 1 TO max_col DO
  3679.                results[i, j] := Random(100);
  3680.  
  3681.  The {$R+} directive causes the program to check for out-of-bound indexes at
  3682.  run time. Use the {$R+} directive during program development, but you may
  3683.  want to turn the directive off ({$R-}) once you finish writing and
  3684.  debugging. This lets the program run faster.
  3685.  
  3686.  
  3687.  6.1.3  Declaring Constant Arrays
  3688.  
  3689.  To declare a constant array, first define the array type, then declare the
  3690.  array and the initial values in a CONST statement. As shown in the examples
  3691.  below, follow the array type with an equal sign (=) and a list of initial
  3692.  values enclosed in parentheses. Separate elements with commas.
  3693.  Multidimensional arrays require additional levels of parentheses. For
  3694.  example, in a two-dimensional array, place parentheses around the values for
  3695.  each row.
  3696.  
  3697.       CONST
  3698.           max_row   = 5;
  3699.           grid_size = 2;
  3700.  
  3701.       TYPE
  3702.           small_int_arr = ARRAY[1..max_row] OF Integer;
  3703.           hex_digit_arr = ARRAY[1..16] OF Char;
  3704.  
  3705.           grid_xy = 1..grid_size;
  3706.           char_grid_type = ARRAY[grid_xy, grid_xy] OF Char;
  3707.  
  3708.       CONST
  3709.           topic_index : small_int_arr = ( 11, 12, 13, 14, 15 );
  3710.           hex : hex_digit_arr =
  3711.               ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  3712.                 'A', 'B', 'C', 'D', 'E', 'F' );
  3713.           default_state : char_grid_type =
  3714.               ( ( 'a', 'j' ), ( 'W', 'T' ) );
  3715.  
  3716.  
  3717.  6.1.4  Passing Arrays as Parameters
  3718.  
  3719.  This section describes how to pass an entire array to a procedure or
  3720.  function. Note that there is no restriction on how you pass individual
  3721.  elements, which can be passed just like simple variables of the same type.
  3722.  
  3723.  To pass an array as a parameter, first define the array as an independent
  3724.  type. Then declare the function to take a parameter of this predefined type,
  3725.  and pass a value of this same type to the function. This requirement is
  3726.  necessary for Pascal to ensure that the array is passed correctly.
  3727.  
  3728.  The following code illustrates how to successfully pass an array:
  3729.  
  3730.       TYPE
  3731.           res_arr = ARRAY[1..20] OF Integer;
  3732.       VAR
  3733.           results : res_arr; { results is a res_arr type variable }
  3734.  
  3735.        { =====init_arr===== }
  3736.        { init_arr accepts a variable with res_arr type and initializes it }
  3737.       PROCEDURE init_arr( VAR new_arr : res_arr );
  3738.           BEGIN
  3739.           .
  3740.           .
  3741.           .
  3742.           END;
  3743.  
  3744.       BEGIN
  3745.           { call init_arr, passing it the results variable
  3746.             which has type res_arr
  3747.           }
  3748.           init_arr( results );
  3749.  
  3750.  You don't need to predefine a string type in order to pass a string.
  3751.  However, if the Var-String Checking option (in the dialog box for Compiler
  3752.  directives in the Options menu) is in effect (the default), the length of
  3753.  the string you pass must match exactly the length of the string the
  3754.  procedure expects. For example, if procedure print_str is declared as
  3755.  
  3756.       PROCEDURE print_str ( data_str : STRING[20] );
  3757.  
  3758.  then you can only pass strings of type STRING[20].
  3759.  
  3760.  If Var-String Checking is turned off, then you can pass strings to
  3761.  parameters without regard to length. You can also pass variables of type
  3762.  STRING.
  3763.  
  3764.  Like all variables, arrays can be passed by value or by reference. When you
  3765.  pass an array by value, the entire array is copied to the stack. This can
  3766.  cause you to run out of stack memory quickly if you work with large arrays.
  3767.  
  3768.  
  3769.  6.1.5  Using the Debugger with Arrays
  3770.  
  3771.  When you want to display an array during debugging, you can specify three
  3772.  different kinds of Watch expressions:
  3773.  
  3774.    1. The entire array. You can watch all the elements of an array simply by
  3775.       specifying the array's name. The Watch window displays array elements
  3776.       as a list of comma-delimited items enclosed in parentheses. You may
  3777.       need to scroll through the Watch window to see all of the elements.
  3778.  
  3779.    2. Specific array elements. You can watch individual elements of an array
  3780.       by specifying either a constant or variable as an index. If you use a
  3781.       variable, then QuickPascal displays a different element whenever the
  3782.       variable changes.
  3783.  
  3784.    3. A subset of the array elements (a "subarray"). You can specify a
  3785.       subarray by using the following syntax:
  3786.  
  3787.       ArrayName[ Index], Number
  3788.  
  3789.       For example, a[4],5 permits you to view the subarray a[4] to a[9]. You
  3790.       can also use a variable as the index of the first item.
  3791.  
  3792.  
  3793.  6.2  Records
  3794.  
  3795.  A record is a collection of variables that can have different types. Each
  3796.  variable within the record has a name to differentiate it from other
  3797.  variables in the record. This name is called a "field." To access an item in
  3798.  a record, you give both the record name and the field.
  3799.  
  3800.  Records are common in practical applications. For example, consider a
  3801.  program that maintains airplane reservations. For each reservation, there
  3802.  are several relevant pieces of information: customer name, flight number,
  3803.  date, and time. You can combine all of this information into a single
  3804.  record.
  3805.  
  3806.  Although you could keep track of the different data items in separate
  3807.  variables, placing them together in a single record makes programs easier to
  3808.  write and maintain. Furthermore, as you'll see in Chapter 10, "Binary
  3809.  Files," records are convenient units of data to read and write to disk.
  3810.  
  3811.  
  3812.  6.2.1  Declaring Records
  3813.  
  3814.  Declare a record with the following syntax. The record can form part of a
  3815.  type definition or variable declaration.
  3816.  
  3817.       RECORD
  3818.          FieldDeclarations
  3819.       END
  3820.  
  3821.  In the syntax display above, FieldDeclarations is a variable declaration.
  3822.  The variable name determines the name of the Field (see examples). You must
  3823.  separate each field from the next with a semicolon.
  3824.  
  3825.  The following lines show some simple record type definitions:
  3826.  
  3827.       TYPE
  3828.           complex = RECORD
  3829.            x_real,
  3830.            y_imag : Real;
  3831.            END;
  3832.  
  3833.           mail_rec = RECORD
  3834.               name   : STRING[20];
  3835.               street : STRING[25];
  3836.               city   : STRING[5];
  3837.               state  : STRING[2];
  3838.               zip    : LongInt;
  3839.               END;
  3840.  
  3841.  Because of the highly structured nature of Pascal, each field can have any
  3842.  data type. A field can be an array or even another record. When a record
  3843.  appears as a field within another record, it is said to be "nested." For
  3844.  example, the type mail_list_rec shown below contains last_deleted_rec,
  3845.  which is another record, as one of its fields.
  3846.  
  3847.       mail_list_rec = RECORD
  3848.           num_recs : Word;
  3849.           last_update,
  3850.           last_mailing : STRING;
  3851.           last_deleted_rec : mail_rec;
  3852.           END;
  3853.  
  3854.  Arrays of records are common data types. For example, the following data
  3855.  structure creates a table of the grades of students in a math class:
  3856.  
  3857.       TYPE
  3858.           table_rows = 1..30;
  3859.           stu_rec = RECORD
  3860.                name : STRING [20];
  3861.                test1_score,
  3862.                test2_score,
  3863.                test3_score : Word;
  3864.                grade_pt : REAL;
  3865.                END;
  3866.  
  3867.       VAR
  3868.           table : ARRAY[table_rows] OF stu_rec;
  3869.  
  3870.  
  3871.  6.2.2  Accessing Record Fields
  3872.  
  3873.  To access an individual field of a record, you give both the name of the
  3874.  record variable and the name of the field:
  3875.  
  3876.       RecordName.FieldName
  3877.  
  3878.  The result is a data object of the type declared for the field. You can use
  3879.  the resulting item in any context that would be valid for an ordinary
  3880.  variable of the same type.
  3881.  
  3882.  Consider the following record type:
  3883.  
  3884.       TYPE
  3885.           mail_rec = RECORD
  3886.               name   : STRING[20];
  3887.               street : STRING[25];
  3888.               city   : STRING[5];
  3889.               state  : STRING[2];
  3890.               zip    : LongInt;
  3891.               END;
  3892.           mail_array = ARRAY [1..max_mailing] OF mail_rec;
  3893.  
  3894.       VAR
  3895.           my_mail : mail_rec;
  3896.           mailing : mail_array;
  3897.  
  3898.  The following example assigns a string to the name field of the my_mail
  3899.  record variable declared above.
  3900.  
  3901.       my_mail.name := 'John Doe';
  3902.  
  3903.  The expression my_mail.name is a string variable that can be assigned a
  3904.  value or passed to a function, just like any other string variable. For
  3905.  example, the following statement displays the name:
  3906.  
  3907.       Writeln( 'Addressee name is ', my_mail.name );
  3908.  
  3909.  The syntax gets a little more complicated when you refer to an item within
  3910.  the array of records. For example, the following statement initializes the
  3911.  name field of the third array element:
  3912.  
  3913.       mailing[3].name := 'Hugo Bletch';
  3914.  
  3915.  Consider how Pascal analyzes this expression. You can understand any complex
  3916.  expression by following similar logic:
  3917.  
  3918.    1. The symbol mailing is declared as an array of records.
  3919.  
  3920.    2. The expression mailing[3] is therefore an individual record.
  3921.       Specifically, it is the third record of the array.
  3922.  
  3923.    3. The expression mailing[3].name refers to the name field in the third
  3924.       record of the array. The result is a variable of type STRING[20].
  3925.  
  3926.  The data path to an object grows as you increase the levels of nesting. For
  3927.  example, consider the definition of the mail_rec type above plus the
  3928.  following additional statements:
  3929.  
  3930.       TYPE
  3931.           MailArr = ARRAY[1..100] OF mail_rec;
  3932.       VAR
  3933.           mailing_list  : RECORD
  3934.               title     : STRING;
  3935.               addresses : MailArr;
  3936.               END;
  3937.  
  3938.  Given these declarations, you can access an individual name field as
  3939.  
  3940.       mailing_list.addresses[5].name
  3941.  
  3942.  The next section shows a technique for shortening the length of such
  3943.  expressions.
  3944.  
  3945.  
  3946.  6.2.3  Using the WITH Statement to Access Fields
  3947.  
  3948.  While defining the full data path in a record makes your code more readable,
  3949.  it also increases the length of the identifiers. The QuickPascal WITH
  3950.  statement enables you to omit the name of a record variable from a block of
  3951.  statements. It has the following syntax:
  3952.  
  3953.       WITH RecordName DO Statement
  3954.  
  3955.  The Statement following DO can refer to fields in RecordName directly. For
  3956.  example, you can assign a string to the name field of record variable
  3957.  my_mail with the following statement:
  3958.  
  3959.       WITH my_mail DO
  3960.           name := 'John Doe';
  3961.  
  3962.  To follow the DO keyword with more than one statement, use a BEGIN...END
  3963.  statement block.
  3964.  
  3965.  In the case of nested records, the WITH statement may contain a record name
  3966.  modified by a field name. Always specify the outermost record first. For
  3967.  example, suppose name is a field of mail_rec, which in turn is a field of
  3968.  the record variable mail_list_rec. The following statement assigns a string
  3969.  to the name field:
  3970.  
  3971.       WITH mail_list_rec.mail_rec DO
  3972.           name := 'Hugo Bletch';
  3973.  
  3974.  
  3975.  6.2.4  Constant Records
  3976.  
  3977.  To declare a constant record, first define the record type, then declare the
  3978.  record and the initial value in a CONST statement. The initial value of a
  3979.  record consists of the following syntax:
  3980.  
  3981.       ( FieldName: Constant; FieldName : Constant ... )
  3982.  
  3983.  The initial value for each field follows the rules for its type. For
  3984.  example, each row of an array initializer must be enclosed in parentheses.
  3985.  In the case of nested records, additional levels of parentheses and fields
  3986.  are required, as shown below.
  3987.  
  3988.       TYPE
  3989.           complex = RECORD
  3990.               x_real,
  3991.               y_imag : Real;
  3992.               END;
  3993.           square_matrix = RECORD
  3994.               mat_size    : Byte;
  3995.               imag        : complex;
  3996.               determinant : Real;
  3997.               mat_x       : ARRAY[1..3, 1..3] OF Real;
  3998.               END;
  3999.       CONST
  4000.           origin  : complex = ( x_real : 0.0; y_imag : 0.0 );
  4001.           def_mat : square_matrix = (
  4002.               mat_size    : 3;
  4003.               imag        : ( x_real : 1.0; y_imag : 1.0 );
  4004.               determinant : 0.0;
  4005.               mat_x       :
  4006.                   ( (1.0, 1.0, 1.0),
  4007.                     (2.0, 2.0, 2.0),
  4008.                     (3.0, 3.0, 3.0)
  4009.                   )
  4010.               );
  4011.  
  4012.  
  4013.  6.2.5  Assigning Records to Record Variables
  4014.  
  4015.  Pascal supports the use of the assignment operator (:=) to assign the value
  4016.  of one record variable to another record. You can also assign one array to
  4017.  another if you declare the array type as a field within a record.
  4018.  
  4019.  For example, if rec_a and rec_b are both arrays of the same type, you can
  4020.  use the following statement to assign one record to another:
  4021.  
  4022.       rec_a := rec_b;
  4023.  
  4024.  As a further example, assume matrix1 and matrix2 are both records of the
  4025.  same type, and that this type contains an array field mat_x. You can use
  4026.  the following statement to assign one array field to another:
  4027.  
  4028.       matrix1.mat_x := matrix2.mat_x
  4029.  
  4030.  
  4031.  6.2.6  Using the Debugger with Records
  4032.  
  4033.  The Debug window displays the fields of a record as a list of data items
  4034.  enclosed in parentheses. Appending ",R" to the name of a watched record when
  4035.  it is put into the Debug window displays the names of the fields and their
  4036.  values. A colon separates the name of a field and its current value with
  4037.  this display format. You may need to scroll through the window to see all of
  4038.  the fields in the record.
  4039.  
  4040.  
  4041.  6.3  Variant Records
  4042.  
  4043.  A variant record lets you provide a variety of data formats for the same
  4044.  area of memory. Whenever you refer to the variant record, you indicate which
  4045.  format to use. This capability is useful in the following situations:
  4046.  
  4047.    ■  You need flexibility within a general type of record. Suppose you want
  4048.       to create a record type to store data on each employee in a company.
  4049.       You'll want certain common fields, such as name, to apply to every
  4050.       employee. However, you may need to store different kinds of information
  4051.       on different kinds of employees.
  4052.  
  4053.       Variant records provide the needed flexibility. Instead of including
  4054.       all possible fields for all possible employees in every record (which
  4055.       would waste memory), you can create fields specific to each subgroup of
  4056.       employees.
  4057.  
  4058.    ■  You want to read data in one way and write it out in another way. While
  4059.       this situation is comparatively rare, it offers a perfect use for
  4060.       variant records. For example, you can simulate the behavior of
  4061.       microprocessors which permit the same register to be accessed either a
  4062.       byte at a time or a word at a time.
  4063.  
  4064.  
  4065.  6.3.1  Declaring Variant Records
  4066.  
  4067.  The syntax for a variant record declaration is:
  4068.  
  4069.       RECORD
  4070.          FieldDeclarations
  4071.          CASE «Tag:»TagType OF
  4072.          CaseDeclarations
  4073.          END
  4074.  
  4075.  The FieldDeclarations make up the "fixed fields" of the record and are
  4076.  optional. The Tag is an optional field that you can use to indicate which of
  4077.  the cases (data formats) is active. Each CaseDeclaration has the following
  4078.  format:
  4079.  
  4080.       CaseLabel:  (FieldDeclarations)
  4081.  
  4082.  Separate each CaseDeclaration with a semicolon. The Tag and each CaseLabel
  4083.  must be of the TagType, which can be any valid ordinal type (such as an
  4084.  integer or user-defined type).
  4085.  
  4086.  Each case declares a different series of fields. However, each case is
  4087.  overlaid in the same area of the record. Pascal allocates enough memory for
  4088.  the case that has the largest total size. This area is called the "variant
  4089.  portion" of the record. One case might use this area to store a string
  4090.  field. Another might use it to store floating-point fields.
  4091.  
  4092.  The following example shows a variant record that models the registers of
  4093.  the 8086 processor:
  4094.  
  4095.       TYPE
  4096.           regtype = (reg16, reg08);
  4097.           registers86 = RECORD
  4098.            CASE Integer OF
  4099.            0 : ( ax, bx, cx, dx, si, di, bp, sp, flags : Word; );
  4100.            1 : ( al, ah, bl, bh, cl, ch, dl, dh : Byte; );
  4101.            END;
  4102.  
  4103.  This type definition allows you to access the same data either as one 16-bit
  4104.  item, such as ax, or as two 8-bit items, such as al and ah.
  4105.  
  4106.  
  4107.  6.3.2  Accessing Variant Record Fields
  4108.  
  4109.  You access all of the fields in the record the same way, regardless of
  4110.  whether they are fixed fields or defined within the variant portion of the
  4111.  record. (Consequently, the names of all fields in both the fixed and variant
  4112.  portions must be unique.) In addition, if a tag field is declared, you can
  4113.  set it to indicate which case is active.
  4114.  
  4115.  For example (assuming the declaration of registers86 at the end of the last
  4116.  section), the following statements load data into al and ah, and then
  4117.  display ax. Note that ax contains the same data as al and ah combined.
  4118.  
  4119.       VAR
  4120.           my_regs : registers86;
  4121.       BEGIN
  4122.           my_regs.ah := $FF;
  4123.           my_regs.al := $10;
  4124.           Writeln ('AX now contains', my_regs.ax);
  4125.  
  4126.  For another example, a simplified variant record scheme for the personnel
  4127.  system of a company might look like this:
  4128.  
  4129.       TYPE
  4130.           clearance = (topsecret, secret, medium, low, known_spy);
  4131.           drink_type = ( martini, wine, champagne, teetotaler );
  4132.           games_type = ( tennis, squash, golf );
  4133.           title = ( secretary, engineer, exec );
  4134.  
  4135.           emp_rec = RECORD
  4136.                name : STRING[20];
  4137.                CASE job : title OF
  4138.                secretary : ( wpm, steno : Word );
  4139.                engineer : ( security : clearance; IQ : Byte );
  4140.                exec : ( beverage : drink_type;
  4141.                      pastime : games_type;
  4142.                      washroom_code : LongInt );
  4143.                END;
  4144.       VAR
  4145.           new_emp : emp_rec;
  4146.  
  4147.  The following code initializes the new_emp record variable for a recently
  4148.  hired engineer:
  4149.  
  4150.       new_emp.name := 'Jane Eyre';
  4151.       new_emp.job := engineer;
  4152.       new_emp.security := secret;
  4153.       new_emp.IQ := 120;
  4154.  
  4155.  Now suppose that Jane Eyre is promoted into management. The old data
  4156.  relevant to engineers (security clearance and IQ) is no longer relevant to
  4157.  Jane in her new position. Fortunately, the variant record type lets you
  4158.  reuse this same area of memory to put in data relevant to managers.
  4159.  
  4160.  First, the program makes Jane's promotion official:
  4161.  
  4162.       new_emp.job := exec;
  4163.  
  4164.  The program then enters Jane's new data. To accommodate Jane's rise up the
  4165.  corporate ladder, the program records her favorite drink, favorite pastime,
  4166.  and key code for the executive washroom. This information is overwritten
  4167.  onto the engineer data, which is no longer needed.
  4168.  
  4169.       new_emp.beverage := martini;
  4170.       new_emp.pastime := tennis;
  4171.       new_emp.washroom_code := 12345007;
  4172.  
  4173.  
  4174.  Chapter 7  Units
  4175.  ───────────────────────────────────────────────────────────────────────────
  4176.  
  4177.  Units extend the usefulness of QuickPascal by giving your programs access to
  4178.  additional declarations and procedures. A "unit" is defined as a related
  4179.  collection of such declarations and procedures. Referencing a unit's name at
  4180.  the beginning of a program gives you access to that unit's contents just as
  4181.  though they were a part of standard Pascal. QuickPascal comes with several
  4182.  predefined (or "standard") units, and you can also write your own.
  4183.  
  4184.  This chapter explains both how to use QuickPascal standard units and how to
  4185.  create your own units.
  4186.  
  4187.  
  4188.  7.1  Understanding Units
  4189.  
  4190.  If you've done much programming in another language, you're probably
  4191.  familiar with libraries. Libraries usually provide specialized routines not
  4192.  normally part of the language. Pascal units work on a similar concept but
  4193.  have a broader scope and application. They contain not only procedures and
  4194.  functions but also variables, constants, and type declarations. In addition,
  4195.  a unit can execute some code when the program starts and do anything a
  4196.  program can, like open files or get user input. These are important
  4197.  differences from libraries in another programming language.
  4198.  
  4199.  Once you tell QuickPascal that your program uses a particular unit, you can
  4200.  access the procedures, functions, and declarations defined in that unit just
  4201.  as you would any standard procedure, function, or declaration.
  4202.  
  4203.  Units effectively extend the portion of a program between the header and the
  4204.  main program body──the area where all the declarations appear. Instead of
  4205.  writing a tremendous number of declarations, procedures, and functions, you
  4206.  can use a unit and get the same results.
  4207.  
  4208.  If you use some of your own procedures repeatedly, you can create a custom
  4209.  unit and make the procedures available to all your programs. And, rewriting
  4210.  large programs to use units removes some of the constraints of the 64K
  4211.  segment limit. Each unit gets its own segment, so creating just one unit
  4212.  doubles your programming space. Programs can use up to 81 units.
  4213.  
  4214.  QuickPascal standard units broaden your programs' general capabilities; the
  4215.  custom units you write provide any specific functionality you need.
  4216.  
  4217.  
  4218.  7.2  Using Units in a Program
  4219.  
  4220.  You access a unit by adding the USES statement and the unit's name under the
  4221.  PROGRAM declaration. For example, the lines
  4222.  
  4223.       USES
  4224.           Crt, Dos;
  4225.  
  4226.  let you use any of the procedures defined in either the Crt or Dos units in
  4227.  your program, just as you would any local procedure. Any variables,
  4228.  constants, or types declared in the unit are available to your program. (If
  4229.  you write programs that need many variables or constants, declaring them in
  4230.  a custom unit can simplify your main program and make debugging easier.)
  4231.  
  4232.  Here's a more complete example that shows a unit in context:
  4233.  
  4234.       PROGRAM lil_unit_test;
  4235.       USES
  4236.           Crt;
  4237.  
  4238.       BEGIN
  4239.           Write( 'Press any key to end' );
  4240.           REPEAT  UNTIL KeyPressed;
  4241.       END.
  4242.  
  4243.  Once you put the unit in the USES list, you're free to use its contents just
  4244.  like any other Pascal procedures, functions, and declarations.
  4245.  
  4246.  
  4247.  7.3  Standard QuickPascal Units
  4248.  
  4249.  QuickPascal comes with several standard units. They enhance your control
  4250.  over the screen and text, DOS routines, printer use, and graphics.
  4251.  QuickPascal automatically inserts one other standard unit, System, into
  4252.  every program you compile. It supplies all of the standard procedures and
  4253.  functions.
  4254.  
  4255.  QuickPascal standard units include the following:
  4256.  
  4257.  Unit name                   Description
  4258.  
  4259.  Crt                         Handles keyboard input, screen/window
  4260.                              management, color selection, and cursor control.
  4261.  
  4262.  Dos                         Manipulates DOS files and directories, and
  4263.                              performs other DOS functions.
  4264.  
  4265.  Printer                     Gives programs access to a printer.
  4266.  
  4267.  MSGraph                     Creates graphics and displays text in different
  4268.                              sizes and type styles.
  4269.  
  4270.  System                      Contains the run-time system including the
  4271.                              QuickPascal standard procedures and functions.
  4272.                              This unit is automatically used by all
  4273.                              QuickPascal programs; you do not need to
  4274.                              explicitly declare it.
  4275.  
  4276.  If you consistently use procedures not available in the standard units,
  4277.  consider creating your own unit. Like a program, a custom unit can reference
  4278.  other units. Both types of units──standard and custom──add power and
  4279.  flexibility to your programs.
  4280.  
  4281.  
  4282.  7.4  Creating Your Own Units
  4283.  
  4284.  QuickPascal standard units supply most of the procedures you commonly need.
  4285.  Yet you may need a special function repeatedly or want to initialize a large
  4286.  number of variables, constants, or data types. Or you may wish to share
  4287.  procedures with other programmers. In any case, a convenient solution is to
  4288.  write your own unit.
  4289.  
  4290.  
  4291.  7.4.1  Writing a New Unit
  4292.  
  4293.  QuickPascal units look similar to other Pascal programs, with the following
  4294.  differences:
  4295.  
  4296.    ■  Units begin with a different keyword.
  4297.  
  4298.    ■  An INTERFACE section indicates what the program using the unit can
  4299.       access.
  4300.  
  4301.    ■  An IMPLEMENTATION section defines any procedures and functions in your
  4302.       unit.
  4303.  
  4304.  Beginning a Unit
  4305.  
  4306.  Just as any program begins with the keyword PROGRAM, every unit begins with
  4307.  a UNIT keyword:
  4308.  
  4309.       UNIT new_unit;
  4310.  
  4311.  The UNIT keyword tells QuickPascal to compile the file into a unit rather
  4312.  than an executable program. The compiled unit receives the same name as its
  4313.  source file, with the .QPU extension added. Unit source files usually keep
  4314.  the .PAS extension.
  4315.  
  4316.  Interface Portion
  4317.  
  4318.  Everything in the next section, the INTERFACE portion of the unit, becomes
  4319.  "public" information to any programs that use the unit. It includes any
  4320.  units needed by this unit; the declarations of any public variables,
  4321.  constants, or data types; and the calling format for any procedures or
  4322.  functions (including declarations of all parameters). The actual code for
  4323.  the procedures and functions comes later.
  4324.  
  4325.  Consider the following sample INTERFACE section:
  4326.  
  4327.       INTERFACE  { Public information }
  4328.  
  4329.       USES
  4330.           Crt;
  4331.       CONST
  4332.           days_per_year = 365;
  4333.  
  4334.       PROCEDURE center_line( message : STRING );
  4335.       FUNCTION cube( num : Real ) : Real;
  4336.  
  4337.  These lines tell QuickPascal that this unit uses the standard Crt unit,
  4338.  establishes the constant days_per_year, and makes one procedure
  4339.  (center_line) and one function (cube) publicly available.
  4340.  
  4341.  Implementation Portion
  4342.  
  4343.  The second part of a unit, the IMPLEMENTATION portion, defines the
  4344.  procedures given in the INTERFACE portion and any data necessary to
  4345.  implement those procedures. This data may include any new definitions of
  4346.  variables, constants, types, labels, and support procedures (procedures that
  4347.  assist the primary procedures). The complete code for every PROCEDURE or
  4348.  FUNCTION declared in the INTERFACE section must appear in the IMPLEMENTATION
  4349.  section.
  4350.  
  4351.  The following IMPLEMENTATION section includes both a new variable and a
  4352.  support function:
  4353.  
  4354.       IMPLEMENTATION
  4355.       { Private information available only to new_unit }
  4356.  
  4357.       VAR
  4358.           back : Word;
  4359.  
  4360.       FUNCTION  cube( num : Real ) : Real;
  4361.  
  4362.       VAR
  4363.           temp : Real;
  4364.       BEGIN
  4365.           temp := Sqr( num );
  4366.           cube := temp * num;
  4367.       END;
  4368.  
  4369.       FUNCTION calc_start( message : STRING ) : Integer;
  4370.       BEGIN
  4371.           calc_start := (80 - Length( message )) DIV 2;
  4372.       END;
  4373.  
  4374.       PROCEDURE center_line( message : STRING );
  4375.       BEGIN
  4376.           GotoXY( calc_start( message ), WhereY );
  4377.           Writeln( message );
  4378.       END;
  4379.  
  4380.       { Initialization part }
  4381.       BEGIN
  4382.           Writeln( 'What background color would you like ?' );
  4383.           Write( '(0..7) :' );
  4384.           Readln( back );
  4385.           TextBackground( back );
  4386.           ClrScr;
  4387.       END.
  4388.  
  4389.  In QuickPascal, VAR, CONST, PROCEDURE, and FUNCTION declarations may come
  4390.  in any order. However, you must  declare supporting procedures and
  4391.  functions──in this case the calc_start function, called by the center_line
  4392.  procedure──prior to calling them from within another procedure. Be sure to
  4393.  include the definition for every procedure listed in the INTERFACE portion
  4394.  of the program. Omitting any definition causes an Unsatisfied forward
  4395.  reference error.
  4396.  
  4397.  Although the compiler does not require you to list each procedure's
  4398.  parameters (as it did in the INTERFACE portion), it is generally considered
  4399.  good programming practice to do so. Keep in mind that if you do repeat the
  4400.  parameters, they must exactly match the parameters listed in the INTERFACE
  4401.  portion.
  4402.  
  4403.  Viewing the Entire Unit
  4404.  
  4405.  Together, the three previous examples form a complete unit. A program that
  4406.  uses new_unit gets access to the constant days_per_year, the procedure
  4407.  center_line, and the function cube. However, the program cannot access the
  4408.  calc_start function or the temp variable because they appear in the
  4409.  IMPLEMENTATION section; that section remains entirely "hidden" to the
  4410.  program using the unit.
  4411.  
  4412.  When you finish writing a unit, save the source code with the unit's name
  4413.  and the .PAS extension. The next step, compiling the unit, is discussed in
  4414.  the following section.
  4415.  
  4416.  
  4417.  7.4.2  Compiling a Unit
  4418.  
  4419.  You compile a unit in the same manner you compile a program. Choose the
  4420.  Compile File command on the Make menu. If your code contains errors that
  4421.  prevent the unit from compiling, QuickPascal displays an error message. When
  4422.  you remove the message, the cursor appears at the point QuickPascal detected
  4423.  the error. Correct the error and repeat the process until the unit compiles.
  4424.  
  4425.  Alternately, you can compile the main program and a new unit in one step by
  4426.  choosing the Rebuild Main File command on the Make menu (or specify the /R
  4427.  option if you compile from the QPL command line). Again, if QuickPascal
  4428.  encounters any errors, the compilation halts and an error message appears.
  4429.  You must compile all of the custom units a program references before you can
  4430.  compile the main program.
  4431.  
  4432.  Once your unit compiles properly, you can use it in any of your programs
  4433.  just as you would use any of the standard units. If you change a program
  4434.  that uses a custom unit, you need to recompile only that program (and not
  4435.  the unit). However, if you change the unit itself, you may need to recompile
  4436.  both the unit and the program. The Build Main File and Rebuild Main File
  4437.  commands on the Make menu simplify this task.
  4438.  
  4439.  The Build Main File command recompiles all the source files that changed
  4440.  since the last Build Main File command was executed. Rebuild Main File
  4441.  recompiles the program and all of its associated custom units, regardless of
  4442.  whether anything changed. See the QP Advisor or "Compiling a Multiple-Module
  4443.  Program" in Chapter 3 of Up and Running for a step-by-step approach to
  4444.  compiling.
  4445.  
  4446.  
  4447.  7.4.3  Tips for Programming with Units
  4448.  
  4449.  When you program with units, avoid using identifiers with the same name in
  4450.  different units and circular references between units.
  4451.  
  4452.  Identifiers with the Same Name
  4453.  
  4454.  When two or more units contain identifiers (such as constant or procedure
  4455.  names) that share the same name, the identifier "belongs" to the unit most
  4456.  recently used. QuickPascal uses units in the order they appear in the USES
  4457.  statement.
  4458.  
  4459.  To avoid any confusion, you can reference an identifier in a manner similar
  4460.  to referencing fields in records using this syntax:
  4461.  
  4462.       UnitName.Identifier
  4463.  
  4464.  For example, if you have a ClrScr procedure called SCRSTUFF.QPU, you can
  4465.  reference it in your code as ScrStuff.ClrScr in a unit, and the standard
  4466.  ClrScr can be referenced as Crt.ClrScr. Then the order of the units in the
  4467.  USES declaration becomes irrelevant.
  4468.  
  4469.  Circular Referencing
  4470.  
  4471.  QuickPascal permits two units to reference each other. UNIT_A can use
  4472.  UNIT_B, and UNIT_B can use UNIT_A. However, QuickPascal does not support
  4473.  circular referencing between three or more units. QuickPascal won't compile
  4474.  a program in which UNIT_A uses UNIT_B which uses UNIT_C which uses UNIT_A
  4475.  (an A-B-C-A loop). Careful planning can avoid problems with circular
  4476.  referencing.
  4477.  
  4478.  The order of the units listed after the USES declaration is not important
  4479.  unless their procedures have the same names, or the initialization code in
  4480.  one unit interferes with the initialization code in another unit.
  4481.  
  4482.  
  4483.  ───────────────────────────────────────────────────────────────────────────
  4484.  Part 2  Programming Topics
  4485.  
  4486.  Part 2 of Pascal by Example assumes you either have read Part 1, "Pascal
  4487.  Basics," or already know Pascal's basic concepts. Part 2 covers more
  4488.  advanced programming concepts that give your programs additional
  4489.  flexibility. Topics include enhancing keyboard and screen control, saving
  4490.  and extracting data from files, and using pointers. The final chapter is
  4491.  devoted entirely to advanced topics that experienced programmers may find
  4492.  particularly interesting.
  4493.  
  4494.  While Part 1 was designed to be read sequentially, this part is much more
  4495.  topical. You may read the chapters in any order. If you're using Pascal by
  4496.  Example to learn Pascal, however, consider reading the chapter on text files
  4497.  before you try working with binary files.
  4498.  
  4499.  
  4500.  Chapter 8  The Keyboard and Screen
  4501.  ───────────────────────────────────────────────────────────────────────────
  4502.  
  4503.  Virtually all programs receive or send out information in some way, perhaps
  4504.  accepting it from the keyboard or a file, perhaps printing it to the screen
  4505.  or a printer. QuickPascal supports all of the input and output features of
  4506.  standard Pascal and includes a unit specifically designed to enhance your
  4507.  programs' appearance and ease of use.
  4508.  
  4509.  This chapter contains two parts. The first covers the basics of Pascal input
  4510.  and output and how to format output. The second part introduces the Crt unit
  4511.  and how to use it to refine control of both the keyboard and the screen.
  4512.  
  4513.  
  4514.  8.1  Basic Input and Output
  4515.  
  4516.  Pascal provides the Read and Readln procedures for input and the Write and
  4517.  Writeln procedures for output. These procedures can handle both single data
  4518.  items and lists of items separated by commas. All four use similar syntax:
  4519.  
  4520.       Read(«FileName, » InputVariable) «, InputVariable...»
  4521.  
  4522.  and
  4523.  
  4524.       Write(«FileName, » OutputVariable «: Width«:Decimals»»
  4525.          «,OutputVariable « :Width«:Decimals»»»)
  4526.  
  4527.  where FileName optionally refers to a disk file or device (such as a printer
  4528.  or a modem), and Width and Decimals refer to formatting information. Without
  4529.  FileName, Read accepts data from the standard input device (usually the
  4530.  keyboard) and Write sends data to the standard output device (usually the
  4531.  screen).
  4532.  
  4533.  If either Read or Write encounters an error, the program terminates with a
  4534.  run-time error message. You can include the {$I-} compiler directive in your
  4535.  program if you want your program to continue despite Read or Write run-time
  4536.  errors.
  4537.  
  4538.  If you choose to use the {$I-} compiler directive, you need to call the
  4539.  function IOResult immediately after each input or output operation. The
  4540.  value returned by IOResult indicates whether an error occurred. QuickPascal
  4541.  ignores any input or output attempts after an error, unless you call
  4542.  IOResult.
  4543.  
  4544.  Sections 8.1.1 and 8.1.2 cover the basic use of the Read and Write
  4545.  statements. Section 8.2 goes into the formatting details (of Width and
  4546.  Decimals). For further information on using Read and Write with data files
  4547.  and other devices (by using FileName), read Chapter 9, "Text Files," and
  4548.  Chapter 10, "Binary Files."
  4549.  
  4550.  
  4551.  8.1.1  Read and Readln Procedures
  4552.  
  4553.  The Read and Readln procedures place data into one or more "input
  4554.  variables." Most commonly, Read and Readln receive their input from the
  4555.  keyboard or a data file.
  4556.  
  4557.  The input variables must belong to one of the predefined data types (except
  4558.  Boolean) or a subrange type you defined, and they must be of a type that can
  4559.  accept the kind of data you're trying to read. For example, an input
  4560.  variable of type Char cannot accept a Real value. See Chapter 2,
  4561.  "Programming Basics," for information on assigning data types.
  4562.  
  4563.  Readln attempts to read data into the list of input variables. If the number
  4564.  of data items typed on a single line exceeds the number of variables, the
  4565.  additional data items are ignored. However, if Readln receives fewer data
  4566.  items than the number of input variables, it expects the additional data to
  4567.  appear on subsequent lines. For example, the Readln statement in the
  4568.  following excerpt expects three values:
  4569.  
  4570.       Writeln( 'For the investment what are: present value,' );
  4571.       Writeln( 'annual return, and number of years invested?' );
  4572.       Readln( present_value, rate_of_return, years_invested );
  4573.  
  4574.  Suppose you run the program and in response to the prompt
  4575.  
  4576.       For the investment what are: present value,
  4577.       annual return, and number of years invested?
  4578.  
  4579.  you type
  4580.  
  4581.       3000.00 1.05
  4582.  
  4583.  and press ENTER. The program will then wait for you to enter the final
  4584.  value. You must remember to use spaces, tabs, or carriage returns to
  4585.  separate the values. Using commas causes a run-time error. If you're
  4586.  concerned about the possibility of such errors, you can either write the
  4587.  program with one Readln statement for each item entered, or read the data as
  4588.  a string of text and create a procedure to separate the string into numbers.
  4589.  
  4590.  The Read procedure, in contrast, reads only enough data to fill its input
  4591.  variables. But subsequent Read or Readln procedures may get the remaining
  4592.  data.
  4593.  
  4594.  Suppose you rewrote the example above as
  4595.  
  4596.       Writeln( 'For the investment what are: present value?' );
  4597.       Read( present_value )
  4598.       Writeln( 'the annual return?' );
  4599.       Read( rate_of_return );
  4600.       Writeln( 'and the number of years invested?' );
  4601.       Read( years_invested );
  4602.  
  4603.  and ran the program with this response
  4604.  
  4605.       For the investment what are: present value?
  4606.       3000.00 12.8 37
  4607.  
  4608.  The program would then accept 3000.00 as present_value and immediately print
  4609.  the two remaining prompts without stopping:
  4610.  
  4611.       the annual return?
  4612.       and the number of years invested?
  4613.  
  4614.  and set rate_of_return and years_invested equal to 12.8 and 37,
  4615.  respectively. Unless your program performs some type of error checking,
  4616.  using such a series of Read statements can potentially introduce errors.
  4617.  
  4618.  Like Readln, Read accepts all of the Pascal standard data types but inputs
  4619.  them differently. Character data is not delimited; Read assigns any kind of
  4620.  input to character variables, including carriage returns, spaces, and so on.
  4621.  Strings input with Read can include any combination of ASCII text except for
  4622.  a carriage return, which signifies the end of the string. For numeric data,
  4623.  Read ignores any leading blanks, begins with the first number or sign (+ or
  4624.  -), and continues until it reaches the next white-space character or
  4625.  carriage return.
  4626.  
  4627.  
  4628.  8.1.2  Write and Writeln Procedures
  4629.  
  4630.  The Write and Writeln procedures enable you to send variables, constants,
  4631.  and  strings to the screen, disk files, or other output devices. The
  4632.  variables or constants you use in Write and Writeln may be any of the Pascal
  4633.  standard data types or a subrange of any of these types.
  4634.  
  4635.  Write and Writeln differ only in that Writeln terminates the line after
  4636.  sending its data to the output device. Subsequent output appears on the next
  4637.  line.
  4638.  
  4639.  You often use the Write statement to print prompts on the screen. Writeln
  4640.  displays full lines of text and is useful for leaving blank lines on the
  4641.  screen for aesthetic purposes.
  4642.  
  4643.  Write is often used with Read for simple prompt-and-reply input:
  4644.  
  4645.       Write( 'Please enter two integers separated by a space: ' );
  4646.       Read( Int1, Int2 );
  4647.  
  4648.  For complete lines of text and for blank lines, you might use Writeln:
  4649.  
  4650.       Writeln( 'The Very Model of a Modern Major General' );
  4651.       Writeln( '========================================' );
  4652.       Writeln;
  4653.       Writeln('By Shorty Bonaparte');
  4654.  
  4655.  Note that the strings of text appear at the left edge of the screen.
  4656.  
  4657.  By themselves, Write and Writeln do not provide much numerical formatting;
  4658.  they print numbers in a default format according to the type of variable.
  4659.  For example, the output from
  4660.  
  4661.       real_num := 3.1415;
  4662.       long_int := 1234567;
  4663.       Writeln( 'A real number prints out as ', real_num,'.' );
  4664.       Writeln( 'And a long integer prints as ', long_int,'.' );
  4665.  
  4666.  looks like
  4667.  
  4668.       A real number prints out as 3.14150000000154E+0000.
  4669.       And a long integer prints as 1234567.
  4670.  
  4671.  The next section explains how to change these default formats.
  4672.  
  4673.  
  4674.  8.1.3  Formatted Output with Write and Writeln
  4675.  
  4676.  The default numerical output format for real numbers is scientific notation.
  4677.  For integers, the default output format is a sequence of digits. And, Pascal
  4678.  normally prints strings as left justified. But Write and Writeln do allow
  4679.  you to change the default formats.
  4680.  
  4681.  Recall that the Write and Writeln statements follow the syntax:
  4682.  
  4683.       Write(«FileName, » OutputVariable «: Width«:Decimals»»
  4684.          «,OutputVariable « :Width«:Decimals»»»)
  4685.  
  4686.  The two optional fields Width and Decimals control formatting. The Width
  4687.  field sets the maximum number of spaces available for printing the variable
  4688.  called OutputVariable and indicates right or left justification by its sign
  4689.  (positive for right justification, negative for left).
  4690.  
  4691.  Pascal formats text and integer data in a similar fashion. These statements
  4692.  print out the integer my_int and the string my_str in a space six columns
  4693.  wide and right justified:
  4694.  
  4695.       Writeln( my_int:6 );
  4696.       Writeln( my_str:6 );
  4697.  
  4698.  If either my_int or my_str exceeds a width of six columns, QuickPascal
  4699.  prints the entire integer or string with the default format.
  4700.  
  4701.  Variables of type Real can also specify Decimals, which indicates the number
  4702.  of digits to appear to the right of the decimal point. You cannot specify
  4703.  Decimals for integers or strings.
  4704.  
  4705.  The Write and Writeln procedures do not truncate strings or any type of
  4706.  numerical data. If OutputVariable is wider than Width, Width is overridden
  4707.  and the entire OutputVar is printed. For floating-point types, Write and
  4708.  Writeln always print the number of decimal places specified by Decimals,
  4709.  even if it means overriding the Width specification.
  4710.  
  4711.  The following line right justifies a real numberrealnum and displays it with
  4712.  four decimal places:
  4713.  
  4714.       Writeln( realnum:12:4 )
  4715.  
  4716.  Provided realnum contains 12 or fewer digits, the last digit lies in column
  4717.  12.
  4718.  
  4719.  This example
  4720.  
  4721.       int_var  := 12345;
  4722.       str_var  := 'This sentence is a string.';
  4723.       real_var := 9870.65434;
  4724.  
  4725.       Writeln( '1234567890123456789012345678901234567890 Ruler' );
  4726.       Writeln( '         10        20        30        40' );
  4727.       Writeln( int_var  : 8 );
  4728.       Writeln( int_var  : 85 );
  4729.       Writeln( str_var  : 35 );
  4730.       Writeln( str_var  : -3 );
  4731.       Writeln( real_var : -2 : 6 );
  4732.       Writeln( real_var : 14 : 2 );
  4733.  
  4734.  produces the following output:
  4735.  
  4736.       1234567890123456789012345678901234567890   Ruler
  4737.                10        20        30        40
  4738.          12345
  4739.       12345
  4740.                This sentence is a string.
  4741.       This sentence is a string.
  4742.       9870.654340
  4743.              9870.65
  4744.  
  4745.  Formatting needs vary from program to program. If you frequently format your
  4746.  data in a particular style, however, you may find it helpful to write a
  4747.  procedure that automatically follows that format.
  4748.  
  4749.  
  4750.  8.1.4  DOS Redirection: Input and Output Files
  4751.  
  4752.  A process called "DOS redirection" allows you to specify both input and
  4753.  output files for QuickPascal programs. An input file contains responses to
  4754.  all of the questions asked by the program (provided they are input with Read
  4755.  or Readln statements). An output file receives all of the information
  4756.  written by Write and Writeln statements that usually go to the screen.
  4757.  
  4758.  8.1.4.1  Standard Redirection
  4759.  
  4760.  Redirected programs run in a completely normal manner; they just accept
  4761.  their input from a file instead of the keyboard, and send their output to a
  4762.  file instead of the screen. You can run redirected programs only from the
  4763.  DOS command line. To use redirection from within the QuickPascal
  4764.  environment, you must first choose the DOS Shell command from the File menu.
  4765.  The general redirection syntax is
  4766.  
  4767.       ProgramName «<InputFile» >OutputFile»
  4768.  
  4769.  You can include the InputFile only, the OutputFile only, or both. Both must
  4770.  be text files. If you run with just InputFile
  4771.  
  4772.       ProgramName <InputFile
  4773.  
  4774.  you still see the program's queries and other output on the screen. However,
  4775.  unless you specifically write your program to echo user input, you do not
  4776.  see the input data on the screen because it comes directly from the input
  4777.  file.
  4778.  
  4779.  If you run ProgramName and include only the OutputFile
  4780.  
  4781.       ProgramName >OutputFile
  4782.  
  4783.  you need to know the order in which the program requires its input; all
  4784.  output, including prompts for input, go to OutputFile. DOS creates a new
  4785.  output file each time you run the program and specify OutputFile. If a file
  4786.  named OutputFile already exists, the old one is erased and a new one is
  4787.  created. But, if you add an additional > symbol
  4788.  
  4789.       ProgramName >>OutputFile
  4790.  
  4791.  DOS appends the new output to the end of the existing file named OutputFile.
  4792.  
  4793.  For example, suppose you write a database program calledADDRESS.EXE that
  4794.  reads employee names and returns their mailing addresses. Using Quick Pascal
  4795.  or some other editor that saves text files, you can create a list of
  4796.  employee names and save it as NAME.TXT. Then if you enter
  4797.  
  4798.       ADDRESS <NAME.TXT
  4799.  
  4800.  you can print the list of employee addresses on the screen. Entering
  4801.  
  4802.       ADDRESS <NAME.TXT >ADD.TXT
  4803.  
  4804.  creates a new file called ADD.TXT that contains the employee addresses. And,
  4805.  if you write another input file called MORENAME.TXT, then the command
  4806.  
  4807.       ADDRESS <MORENAME.TXT >>ADD.TXT
  4808.  
  4809.  adds the additional addresses to the end of the existing ADD.TXT file.
  4810.  
  4811.  8.1.4.2  The Crt Unit and DOS Redirection
  4812.  
  4813.  The Crt unit overrides DOS redirection. Programs with Crt in their USES
  4814.  statements ignore input and output files.
  4815.  
  4816.  If you want to run programs that use the Crt unit with input and output
  4817.  files, you must include the following lines in your program prior to the
  4818.  first input or output statement:
  4819.  
  4820.       Assign( Input, '' );
  4821.       Reset( Input );
  4822.       Assign( Output, '' );
  4823.       Rewrite( Output );
  4824.  
  4825.  The Crt unit changes the names of the standard input and output sources; the
  4826.  four lines above reassign them to their original settings.
  4827.  
  4828.  
  4829.  8.2  The Crt Unit
  4830.  
  4831.  If you have read Chapter 7, "Units," you're familiar with the concept of
  4832.  units. The Crt unit is a standard unit that provides data types and
  4833.  procedures for keyboard input, cursor control, screen control, and windows.
  4834.  This section describes some of the variables and all of the procedures
  4835.  provided in the Crt unit. You can obtain more information on each of these
  4836.  variables and procedures through the on-line help system.
  4837.  
  4838.  If you plan to use the Crt and MSGraph units together, be sure you read
  4839.  Chapter 13, "Using Graphics." QuickPascal imposes some restrictions on
  4840.  programs that call both the Crt and the MSGraph units.
  4841.  
  4842.  
  4843.  8.2.1  Using the Crt Unit
  4844.  
  4845.  Many of the complete sample programs presented in this manual use the Crt
  4846.  unit. To access its functions, procedures, variables, and constants in your
  4847.  own programs, add the statement USES Crt; immediately after your program
  4848.  declaration.
  4849.  
  4850.  When you start a graphics program you need to tell QuickPascal how you want
  4851.  it to display text. You pass a particular constant to the TextMode procedure
  4852.  based on your desired text mode. The constants appear in Table 8.1.
  4853.  
  4854.  Table 8.1  Crt Text-Mode Constants
  4855.  
  4856.  Constant
  4857.  Identifier        Value             Display
  4858.  
  4859.  BW40              0                 40 by 25 monochrome text screen
  4860.  
  4861.  BW80              2                 80 by 25 monochrome text screen
  4862.  
  4863.  Mono              7                 80 by 25 monochrome text screen
  4864.  
  4865.  CO40              1                 40 by 25 color text screen
  4866.  
  4867.  CO80              3                 80 by 25 color text screen
  4868.  
  4869.  Font8x8           256               EGA/VGA 43 lines
  4870.  
  4871.  The Crt unit also establishes the color constants shown in Table 8.2. When
  4872.  you print text in different colors or create windows with different colored
  4873.  backgrounds, refer to the color by its value.
  4874.  
  4875.  Table 8.2  Crt Color Constants
  4876.  
  4877.  Color          Value     Color            Value
  4878.  
  4879.  Black          0         LightBlue        9
  4880.  
  4881.  Blue           1         LightGreen       10
  4882.  
  4883.  Green          2         LightCyan        11
  4884.  
  4885.  Cyan           3         LightRed         12
  4886.  
  4887.  Red            4         LightMagenta     13
  4888.  
  4889.  Magenta        5         Yellow           14
  4890.  
  4891.  Brown          6         White            15
  4892.  
  4893.  LightGray      7         Blink            128
  4894.  
  4895.  DarkGray       8
  4896.  
  4897.  In addition to the constants, the Crt unit exports the variables listed in
  4898.  Table 8.3. They let your programs access status and informational settings
  4899.  used by QuickPascal, such as the previous video mode or current text
  4900.  attributes.
  4901.  
  4902.  Table 8.3  Variables Provided by the Crt Unit
  4903.  
  4904.  Variable          Data Type         Purpose
  4905.  
  4906.  CheckBreak        Boolean           Indicates the state of CTRL+BREAK
  4907.                                      checking
  4908.  
  4909.  CheckEof          Boolean           Enables/disables checking for
  4910.                                      end-of-file character on keyboard input
  4911.  
  4912.  CheckSnow         Boolean           Enables/disables "snow checking" for the
  4913.                                      screen
  4914.  
  4915.  DirectVideo       Boolean           Enables/disables direct video
  4916.                                      output
  4917.  
  4918.  LastMode          Word              Saves previous video mode
  4919.  
  4920.  TextAttr          Byte              Contains the current text display
  4921.                                      attribute
  4922.  
  4923.  WindMin           Word              Saves the previous coordinates of the
  4924.                                      upper left corner of the active window
  4925.  
  4926.  WindMax           Word              Saves the previous coordinates of the
  4927.                                      lower right corner of the active window
  4928.  
  4929.  The variable DirectVideo plays an important role in speeding up screen
  4930.  output. Setting DirectVideo to True sends the output of Write and Writeln
  4931.  statements directly to your screen's memory. Unfortunately, this technique
  4932.  does not work on some computer configurations. Setting DirectVideo to False,
  4933.  on the other hand, employs your machine's Basic Input Output System (BIOS).
  4934.  BIOS always displays correctly but works at a slower speed; experiment to
  4935.  see which setting works better for your computer.
  4936.  
  4937.  The WindMin and WindMax variables store the coordinates of the active
  4938.  window. Use the predefined Hi and Lo functions to read the high and low
  4939.  bytes that are packed in the WindMin or WindMax variables, as shown below:
  4940.  
  4941.       UpperLeftX  := Lo(WindMin) + 1;
  4942.       UpperLeftY  := Hi(WindMin) + 1;
  4943.       LowerRightX := Lo(WindMax) + 1;
  4944.       LowerRightY := Hi(WindMax) + 1;
  4945.  
  4946.  Table 8.4 lists all of the Crt procedures and functions; by browsing
  4947.  through it you'll get a feel for the Crt unit. The "Purpose" column
  4948.  describes what each procedure does. On-line help gives a more complete
  4949.  explanation, and the sample programs show other uses of these procedures.
  4950.  
  4951.  Table 8.4  Procedures and Functions Provided by the Crt Unit
  4952. ╓┌─────────────────┌────────────────┌────────────────────────────────────────╖
  4953.  Procedure         Purpose                    Example
  4954.  
  4955.  AssignCrt         Associates a text-file     AssignCrt(DataFile) ;
  4956.                    variable with the
  4957.                    screen
  4958.  Procedure         Purpose                    Example
  4959.  
  4960.                   screen
  4961.  
  4962.  ClrEol            Clears to the end of       ClrEol;
  4963.                    the line
  4964.  
  4965.  ClrScr            Clears the window and      ClrScr;
  4966.                    places the cursor at
  4967.                    the upper left corner
  4968.  
  4969.  Delay             Delays the program for     Delay(1000){1 sec. };
  4970.                    a specified number of
  4971.                    milliseconds
  4972.  
  4973.  DelLine           Removes the line at        DelLine;
  4974.                    the current cursor
  4975.                    location
  4976.  
  4977.  GotoXY            Moves the cursor to        GotoXY( 40,25 );
  4978.                    the specified window
  4979.  Procedure         Purpose                    Example
  4980.  
  4981.                   the specified window
  4982.                    coordinates
  4983.  
  4984.  HighVideo         Displays characters in     HighVideo;
  4985.                    high intensity
  4986.  
  4987.  InsLine           Inserts an empty line      InsLine;
  4988.                    at the current cursor
  4989.                    location
  4990.  
  4991.  KeyPressed        Returns True if there      WHILE KeyPressed DO ...
  4992.                    is a character in the
  4993.                    keyboard buffer
  4994.  
  4995.  LowVideo          Displays characters in     LowVideo;
  4996.                    low intensity
  4997.  
  4998.  NormVideo         Restores screen            NormVideo;
  4999.                    attri-butes to those
  5000.  Procedure         Purpose                    Example
  5001.  
  5002.                   attri-butes to those
  5003.                    in effect when the
  5004.                    program started
  5005.  
  5006.  NoSound           Turns the speaker off      NoSound;
  5007.  
  5008.  ReadKey           Reads the next             akey := ReadKey;
  5009.                    character from the
  5010.                    keyboard buffer
  5011.                    without showing it on
  5012.                    the screen
  5013.  
  5014.  Sound             Turns the speaker on       Sound( frequency );
  5015.  
  5016.  TextBackground    Selects the background     TextBackground( Black );
  5017.                    color for subsequent
  5018.                    text display
  5019.  
  5020.  TextColor         Selects the foreground     TextColor( Red );
  5021.  Procedure         Purpose                    Example
  5022.  
  5023. TextColor         Selects the foreground     TextColor( Red );
  5024.                    color for subsequent
  5025.                    text display
  5026.  
  5027.  TextMode          Selects the display        TextMode( BW80 );
  5028.                    mode
  5029.  
  5030.  WhereX            Returns the X coor-        posx := WhereX;
  5031.                    dinate of the cursor
  5032.                    location
  5033.  
  5034.  WhereY            Returns the Y coor-        posy := WhereY;
  5035.                    dinate of the cursor
  5036.                    location
  5037.  
  5038.  Window            Defines a new display      Window( 2,2,40,10 );
  5039.                    window
  5040.  
  5041.  
  5042.  8.2.2  Character Input
  5043.  
  5044.  You can read characters from the keyboard in a couple of different ways. In
  5045.  the case of the Read and Readln procedures, as each letter is typed in, it
  5046.  is put into the input variables and displayed on the screen. The Crt
  5047.  function ReadKey also reads input from the keyboard, but can read only one
  5048.  character at a time; it does not display the character on the screen.
  5049.  
  5050.  Although you may want to do your primary data collection with Read and
  5051.  Readln, the ReadKey function lets you add finishing touches to your
  5052.  programs. For example, the following line pauses program execution until the
  5053.  user presses ENTER:
  5054.  
  5055.       REPEAT UNTIL ReadKey = Chr( 13 );
  5056.  
  5057.  ReadKey also reads function keys, cursor-control keys, control keys, and
  5058.  alternate keys. These special keys send a sequence of characters. The first
  5059.  is a null character (that is, ASCII 0). The second character is an extended
  5060.  key code. (See Appendix A for a table of extended key codes.)
  5061.  
  5062.  The following excerpt illustrates a typical character processor for a
  5063.  keyboard-driven program. It checks for the special character code #0 and the
  5064.  standard alphanumeric keys. If the first key is special, a second CASE
  5065.  statement identifies which key is pressed. Once the character processor
  5066.  determines the key pressed, it invokes the applicable procedure.
  5067.  
  5068.       VAR
  5069.           done : Boolean;
  5070.           ch, ch2   : Char;
  5071.  
  5072.       BEGIN
  5073.           done := False;
  5074.  
  5075.           REPEAT
  5076.               ch:= ReadKey;
  5077.               CASE ch OF
  5078.                   #0: BEGIN
  5079.                       ch2:= ReadKey;
  5080.                       CASE ch2 OF
  5081.                           59:    { Got F1 key }
  5082.                               DoF1;  { F1 procedure }
  5083.                           .
  5084.                           .
  5085.                           .
  5086.                           END;{ CASE ch2 }
  5087.                       END;{ #0 }
  5088.                   '0'..'9',
  5089.                   'A'..'Z',
  5090.                   'a'..'z':
  5091.                       DoAlphaDigit( ch );  { Procedure for normal keys }
  5092.                   ELSE
  5093.                       done :=True;
  5094.                   END;
  5095.           UNTIL done;
  5096.  
  5097.  The CRT1.PAS program in the QuickPascal Advisor demonstrates how to detect
  5098.  special keys using ReadKey. You can compile and run the program, which does
  5099.  the following:
  5100.  
  5101.    ■  Moves the 'o' character on the screen when you press the UP, DOWN,
  5102.       LEFT, and RIGHT arrow keys.
  5103.  
  5104.    ■  Simulates a bouncing ball effect if you press the F2 function key.
  5105.  
  5106.    ■  Hides the cursor.
  5107.  
  5108.    ■  Exits when you press the F1 function key.
  5109.  
  5110.  If you don't like the sound effects, set the constant music to false.
  5111.  
  5112.  In the CRT1.PAS program, the IF statement
  5113.  
  5114.       IF c1 = #0
  5115.  
  5116.  becomes true when you press a special key. In that case, ReadKey returns two
  5117.  characters, the first of which is always ASCII #0. The path taken through
  5118.  the WHILE loops depends on which key you press.
  5119.  
  5120.  The cursor_off procedure calls BIOS to turn off the cursor. The Crt
  5121.  procedures Sound and Delay are also used in this program. On-line help has
  5122.  more information about these two procedures.
  5123.  
  5124.  
  5125.  8.2.3  Cursor and Screen Control
  5126.  
  5127.  The Crt unit provides several procedures for screen and cursor control.
  5128.  Their names reflect their actions: DelLine, HighVideo, GotoXY, and so on.
  5129.  For more information about a specific procedure, use the QuickPascal Advisor
  5130.  help.
  5131.  
  5132.  This section refers you to two example programs in the QP Advisor. The
  5133.  program CRT2.PAS uses Crt procedures to insert and delete lines, change
  5134.  video intensity, and produce sound effects. The program requires very few
  5135.  code lines to accomplish these tasks.
  5136.  
  5137.  The CRT3.PAS program uses the GotoXY function to control the cursor. Table
  5138.  8.6 shows the GotoXY statements used to move the cursor relative to its
  5139.  current position, assuming the cursor is not at the edge of a window.
  5140.  
  5141.  Table 8.6  Statement Effects
  5142.  
  5143.  Example                         Description
  5144.  
  5145.  GotoXY( WhereX-1, WhereY )      Moves one character to the left
  5146.  
  5147.  GotoXY( WhereX+1, WhereY )      Moves one character to the right
  5148.  
  5149.  GotoXY( WhereX, WhereY-1 )      Moves up one line
  5150.  
  5151.  GotoXY( WhereX, WhereY+1 )      Moves down one line
  5152.  
  5153.  GotoXY( 1, WhereY )             Moves to the beginning of the current line
  5154.  
  5155.  GotoXY(80, WhereY)              Moves to the end of the current line
  5156.  
  5157.  The CRT3.PAS program prompts you to type a character and then "bounces" that
  5158.  character around the screen. The character changes direction and beeps when
  5159.  it reaches the edge of the screen. The character's speed increases each time
  5160.  the character changes direction. When it reaches a maximum speed, the
  5161.  character slows back down to its original speed.
  5162.  
  5163.  
  5164.  8.2.4  Using Windows
  5165.  
  5166.  The Crt unit provides easy control of text windows. The Window procedure
  5167.  allows you to define a new active area of the screen.
  5168.  
  5169.  Window accepts row and column coordinates for the upper left and lower right
  5170.  corners of the new window. These coordinates must be integers of type Byte.
  5171.  The rows range from 1-25 (or 1-43 or 1-50, depending on the text mode) and
  5172.  the columns range from 1-80.
  5173.  
  5174.  The Window procedure is analogous to choosing the size of a piece of paper
  5175.  to draw on; the coordinates you pass to the procedure tell QuickPascal how
  5176.  big to make the piece of paper. For example, the following statement defines
  5177.  the entire screen area:
  5178.  
  5179.       Window( 1, 1, 25, 80 );
  5180.  
  5181.  Many applications use the top and bottom lines of the screen to display a
  5182.  menu or help text. To exclude these lines from the active screen area, use
  5183.  
  5184.       Window( 1, 2, 24, 80 );
  5185.  
  5186.  If you write an application that draws a frame around the screen, you may
  5187.  want to use the following statement to reduce the active screen area:
  5188.  
  5189.       Window( 2, 2, 24, 79 );
  5190.  
  5191.  When you create a new window, the upper left corner of the display area is
  5192.  (1, 1). Thus,GotoXY(1, 1) moves the cursor to the upper left corner of the
  5193.  active window, regardless of window size and location. GotoXY is analogous
  5194.  to setting your pen down at a specific place on the piece of paper. WhereX
  5195.  and WhereY return you to your current location.
  5196.  
  5197.  Choose the foreground and background colors for the active window with the
  5198.  TextColor and TextBackground procedures. To continue the earlier comparison,
  5199.  TextColor lets you select your pen's color and TextBackground selects the
  5200.  color of the piece of paper. Note that after changing the background color,
  5201.  you must clear the screen to see the new color. Clearing the screen resets
  5202.  the cursor to (1, 1).
  5203.  
  5204.  The program CRT4.PAS shows how these procedures work (although you need a
  5205.  color monitor to see the colors). It illustrates
  5206.  
  5207.    ■  Windows that move while keeping their size fixed
  5208.  
  5209.    ■  Windows that simultaneously move and change sizes
  5210.  
  5211.    ■  Screen and cursor control of text within a window
  5212.  
  5213.  CRT4.PAS gives you an idea of what you can do with windows. With a few
  5214.  similar lines of code, you can improve your screen's visual impact and add
  5215.  clarity and emphasis to your programs.
  5216.  
  5217.  
  5218.  Chapter 9  Text Files
  5219.  ───────────────────────────────────────────────────────────────────────────
  5220.  
  5221.  Text files store data as lines of ASCII characters. The lines do not have to
  5222.  be the same length. Each line terminates with an end-of-line character
  5223.  (carriage return). Any text editor or word processor that reads ASCII files
  5224.  can edit these files, including the QuickPascal environment.
  5225.  
  5226.  Pascal writes and reads text files sequentially, in much the same way an
  5227.  audio cassette player records or plays back a tape. Adding basic file input
  5228.  and output (I/O) capabilities to your programs lets you store and retrieve
  5229.  data from this "tape" for both long- and short-term use.
  5230.  
  5231.  This chapter covers how to name, open, read, write, and close a file, and
  5232.  how to redirect text information between your disk, screen, and printer. It
  5233.  also lists the standard procedures used to work with text files.
  5234.  
  5235.  
  5236.  9.1  Working with Text Files
  5237.  
  5238.  Working with text files means taking a few straightforward actions:
  5239.  
  5240.    ■  Declaring a file variable and a file name
  5241.  
  5242.    ■  Creating a new file or opening an existing one
  5243.  
  5244.    ■  Writing or appending data to a file
  5245.  
  5246.    ■  Reading data from a file
  5247.  
  5248.    ■  Closing a file
  5249.  
  5250.  The following sections address these steps in detail.
  5251.  
  5252.  As a general introduction, look at the following sample program:
  5253.  
  5254.       PROGRAM filetest;
  5255.       VAR
  5256.           datafile : Text;
  5257.           i        : Integer;
  5258.  
  5259.       BEGIN
  5260.           Assign( datafile, 'RAN_DATA.DAT' );
  5261.           Rewrite( datafile );
  5262.  
  5263.           FOR i := 1 TO 100 DO
  5264.               BEGIN
  5265.               Writeln( datafile, Random(50) );
  5266.               END;
  5267.  
  5268.           Close( datafile );
  5269.       END.
  5270.  
  5271.  The rest of this chapter frequently refers back to this example.
  5272.  
  5273.  
  5274.  9.1.1  Declaring a File Variable and File Name
  5275.  
  5276.  Declaring a file variable means telling QuickPascal how you want to refer to
  5277.  the file from within the program. It is the variable name by which the
  5278.  program knows the file. You declare it in the same way you would declare any
  5279.  other text variable:
  5280.  
  5281.       VAR
  5282.       FileVar : Text
  5283.  
  5284.  When you later read or write information to the file, you refer to the file
  5285.  by the name you give FileVar. For example,
  5286.  
  5287.       VAR datafile : Text;
  5288.  
  5289.  creates a file variable with the name datafile.
  5290.  
  5291.  QuickPascal also needs to know what name you want to assign to the text file
  5292.  that is saved on the disk. The Assign procedure associates the file variable
  5293.  with the disk file name:
  5294.  
  5295.       Assign (FileVar, FileName)
  5296.  
  5297.  QuickPascal accepts this assignment as meaning, "Whenever I say to read or
  5298.  write to the file variable FileVar, send the information to the disk file
  5299.  with the name FileName." You can make the two names similar, but keep in
  5300.  mind that FileName must follow the DOS file-naming conventions. For
  5301.  instance,
  5302.  
  5303.       Assign( datafile, 'RAN_DATA.DAT' );
  5304.  
  5305.  equates the file variable datafile with the disk file RAN_DATA.DAT.
  5306.  
  5307.  For more versatility, you can use a string variable in place of a literal
  5308.  string such as 'RAN_DATA.DAT'. Using a string variable allows your program
  5309.  to prompt for a file name. For example,
  5310.  
  5311.       VAR
  5312.           datafile : Text;
  5313.           filename : String;
  5314.  
  5315.       BEGIN
  5316.           Write('Enter name of data file to open: ');
  5317.           Readln( filename );
  5318.           Assign( datafile, filename );
  5319.           Reset( datafile );
  5320.           .
  5321.           .
  5322.           .
  5323.  
  5324.  
  5325.  9.1.2  Opening a Text File
  5326.  
  5327.  With the file variable and the file name both assigned, you can either
  5328.  create (and then open) a new file or open an existing one.
  5329.  
  5330.  9.1.2.1  Opening a New Text File
  5331.  
  5332.  The standard procedure Rewrite creates and opens a new text file. It uses
  5333.  the general syntax:
  5334.  
  5335.       Rewrite(FileVar)
  5336.  
  5337.  The example program at the beginning of the chapter creates and opens a new
  5338.  text file with the line:
  5339.  
  5340.       Rewrite( datafile );
  5341.  
  5342.  Since the Assign procedure associated the file variable datafile with the
  5343.  name RAN_DATA.DAT, this Rewrite statement creates a new file on the disk
  5344.  also called RAN_DATA.DAT.
  5345.  
  5346.  Note that if a file already exists with the same name, Rewrite destroys the
  5347.  old file. So, it's best to use only file names you know are "safe," or have
  5348.  your program ask the user to confirm the name selected.
  5349.  
  5350.  Once you open a new file, you can immediately write new text to it.
  5351.  
  5352.  9.1.2.2  Opening an Existing Text File
  5353.  
  5354.  You can perform both read and write operations with an existing text file.
  5355.  Keep in mind, however, that trying to open a nonexistent file causes a "File
  5356.  not found" run-time error. A short procedure that verifies the existence of
  5357.  the file could save you some time.
  5358.  
  5359.  To open files for reading data, use the Reset procedure:
  5360.  
  5361.       Reset(FileVar)
  5362.  
  5363.  For example, to open the file named RAN_DATA.DAT created by this chapter's
  5364.  example program, you would use
  5365.  
  5366.       Reset( datafile );
  5367.  
  5368.  Reset opens the file and moves the "file pointer" (an internal bookmark that
  5369.  tells the program where it is in the file) to the first character in the
  5370.  file, ready to begin reading.
  5371.  
  5372.  If you want to add text to the end of an existing file, open the file with
  5373.  the Append procedure:
  5374.  
  5375.       Append(FileVar)
  5376.  
  5377.  To append data to the RAN_DATA.DAT file, type in:
  5378.  
  5379.       Append( datafile );
  5380.  
  5381.  This opens the file and sets the file pointer to the end of the file. Text
  5382.  that is currently in the file remains unaltered.
  5383.  
  5384.  Once you open a file, you can immediately read text from it or write new
  5385.  text to it.
  5386.  
  5387.  
  5388.  9.1.3  Writing Text to a File
  5389.  
  5390.  You write to a text file in much the same way as you write to the screen.
  5391.  You still use the Write or Writeln procedure and any of its standard
  5392.  formatting codes, but you specify the file variable as well.
  5393.  
  5394.  In the example at the beginning of this chapter, the loop
  5395.  
  5396.       FOR i := 1 TO 100 DO
  5397.          BEGIN
  5398.          Writeln( datafile, Random(50) );
  5399.          END;
  5400.  
  5401.  sends 100 random integers to the RAN_DATA.DAT text file specified by the
  5402.  datafile file variable.
  5403.  
  5404.  You can just as easily write text or formatted numbers to the file, but
  5405.  remember that even formatted numbers are stored in the file as text. Any
  5406.  acceptable form of Write or Writeln can send data to a text file.
  5407.  
  5408.  
  5409.  9.1.4  Reading Text from a File
  5410.  
  5411.  Use the Read or Readln procedure to read data from an open text file,
  5412.  specifying the file variable. For example,
  5413.  
  5414.       Readln( datafile, line_o_text );
  5415.  
  5416.  reads a line of text from the text file associated with the variable
  5417.  datafile into the string line_o_text. Read has the same effect but reads one
  5418.  variable at a time rather than an entire line.
  5419.  
  5420.  In the example at the beginning of this chapter, with the FOR loop, the
  5421.  program creates a file called RAN_DATA.DAT filled with 100 random numbers
  5422.  between 0 and 50 (some numbers appear more than once). You could write a
  5423.  nearly identical program to read the data back from the file by altering the
  5424.  loop to
  5425.  
  5426.       FOR i := 1 TO 100 DO
  5427.           BEGIN
  5428.           Readln( datafile, random_number_string );
  5429.           END;
  5430.  
  5431.  Readln replaces Writeln, and you must declare random_number_string as a
  5432.  variable of type STRING. (You would also need to open the file for reading
  5433.  with Reset rather than writing.) To change random_number_string back into
  5434.  numerical data, you need to add
  5435.  
  5436.       Val( random_number_string, ran_num, errpos )
  5437.  
  5438.  after the Readln procedure. (You would also need to declare ran_num and
  5439.  errpos as integers.) In cases where you don't know the length of a file in
  5440.  advance, you can use a loop that checks for the end of the file with the Eof
  5441.  function. For example, if you didn't know the length of the RAN_DATA.DAT
  5442.  file, you could rewrite the previous loop as
  5443.  
  5444.       WHILE NOT Eof( datafile ) DO
  5445.           BEGIN
  5446.           Readln( datafile, random_number_string );
  5447.           Writeln( random_number_string );
  5448.           END;
  5449.  
  5450.  Eof returns a Boolean result. It returns False as you read through the
  5451.  contents of a file and True after you read the file's last entry. The
  5452.  end-of-line function, Eoln, works in a similar manner, but returns True when
  5453.  you reach the end of a line.
  5454.  
  5455.  
  5456.  9.1.5  Closing a Text File
  5457.  
  5458.  You need to close a file when you finish working with it. All files close
  5459.  with the same instruction:
  5460.  
  5461.       Close(FileVar)
  5462.  
  5463.  where FileVar specifies an open file.
  5464.  
  5465.  Trying to close a file that is not open causes a run-time error. However,
  5466.  unless you use a number of similarly named file variables, the compiler
  5467.  usually catches potential errors as either undefined variables (often caused
  5468.  by typing mistakes) or type mismatches (caused by placing a non-Text
  5469.  variable in the place of a file variable).
  5470.  
  5471.  For example, trying to compile the program from the beginning of this
  5472.  chapter with the line
  5473.  
  5474.       Close( datfile );  { 'datafile' misspelled }
  5475.  
  5476.  results in an Unknown identifier compiler error.
  5477.  
  5478.  QuickPascal automatically closes any text files still open when a program
  5479.  ends.
  5480.  
  5481.  
  5482.  9.2  Increasing the Speed for Input and Output
  5483.  
  5484.  The run-time system employs a buffer that temporarily collects text during
  5485.  Read and Write operations to text files. By default, the run-time system
  5486.  uses a 128-byte buffer.
  5487.  
  5488.  Larger buffers enhance the speed of I/O-intensive programs, but tend not to
  5489.  affect programs with moderate or low levels of I/O activity. The larger the
  5490.  buffer, the greater the speed. However, unless your programs bog down due to
  5491.  I/O operations specifically, the default buffer size usually suffices. Keep
  5492.  in mind that while increasing the buffer size can speed up a program, it
  5493.  also increases the program's size.
  5494.  
  5495.  The standard procedure SetTextBuf lets you allocate different buffer sizes.
  5496.  It uses the general syntax
  5497.  
  5498.       SetTextBuf(FileVar, Buffer«, Size»)
  5499.  
  5500.  where Buffer refers to a variable to use as the buffer and Size optionally
  5501.  indicates the size of the buffer in bytes. You can declare Buffer as any
  5502.  type of variable, but you usually use an array of type Char. For example, if
  5503.  you wanted to increase the buffer size of the program presented earlier, you
  5504.  could rewrite the beginning as
  5505.  
  5506.       PROGRAM filetest;
  5507.       VAR
  5508.           datafile : Text;
  5509.           i        : Integer;
  5510.           buffer   : ARRAY[1..2048] OF Char;
  5511.  
  5512.       BEGIN
  5513.           Assign( datafile, 'RAN_DATA.DAT' );
  5514.           Rewrite( datafile );
  5515.           SetTextBuf( datafile, buffer );
  5516.           .
  5517.           .
  5518.           .
  5519.  
  5520.  This call to SetTextBuf provides a large array for intermediate storage.
  5521.  It's something of an overkill for a group of 100 random integers (based on
  5522.  the rest of the example), but would work well for reading or writing large
  5523.  text files.
  5524.  
  5525.  ───────────────────────────────────────────────────────────────────────────
  5526.  WARNING
  5527.     Buffer allocation must occur before or immediately after you open the
  5528.     text file. Changing the file buffer size after I/O operations have
  5529.     already occurred can lead to data loss.
  5530.  ───────────────────────────────────────────────────────────────────────────
  5531.  
  5532.  
  5533.  9.3  Redirecting Text Output
  5534.  
  5535.  QuickPascal lets you access a number of standard DOS devices (such as a
  5536.  printer and the screen) by specifying the device as an output file name. For
  5537.  example, by reassigning the file variable, the same Writeln statement could
  5538.  send text data to a disk file, the printer, or the screen.
  5539.  
  5540.  DOS devices use predefined names. The two most common are the printer name,
  5541.  PRN (assumed to connect to the LPT1 port), and the screen name, CON (short
  5542.  for "console"). Data sent to a device goes to the appropriate computer port.
  5543.  
  5544.  To see how reassignment works, consider a program that, at the end of a
  5545.  particularly grueling data-generating session, presents the user with a
  5546.  choice of sending the data to the printer, console, or file. Based on the
  5547.  selection, the program assigns a file variable to the appropriate device or
  5548.  text file.
  5549.  
  5550.  If OutFileVar is the file variable, choosing the printer leads to the
  5551.  assignment
  5552.  
  5553.       Assign( OutFileVar, 'PRN' );
  5554.  
  5555.  to direct the output to the printer. (You could also get the same effect by
  5556.  using the Printer unit and substituting LST for the file variable. Printer
  5557.  provides the LST text file variable already opened on the LPT1 printer
  5558.  port.)
  5559.  
  5560.  Similarly,
  5561.  
  5562.       Assign( OutFileVar, 'CON' );
  5563.  
  5564.  and
  5565.  
  5566.       Assign( OutFileVar, NewFile );
  5567.  
  5568.  direct the output to the screen and a disk file, respectively. (NewFile
  5569.  must be declared as a string, and must contain the name of the new disk
  5570.  file.)
  5571.  
  5572.  The file variable OutFileVar now refers to the correct output location,
  5573.  regardless of whether that output is the printer, screen, or a new text
  5574.  file. The program opens the device or file with
  5575.  
  5576.       Rewrite( OutFileVar );
  5577.  
  5578.  sends the data to the file with
  5579.  
  5580.       Writeln( OutFileVar, TextOut );
  5581.  
  5582.  and closes the file when finished with
  5583.  
  5584.       Close( OutFileVar );
  5585.  
  5586.  With some planning, the same section of program code can perform three
  5587.  different functions: print data, send to the screen, or send to a file. The
  5588.  only difference is the file name assigned to the file variable. In the
  5589.  example above, a CASE statement, or similar decision-making structure, would
  5590.  assign an appropriate file-name variable based on the user's menu selection.
  5591.  
  5592.  
  5593.  9.4  Standard Procedures and Functions for Input and Output
  5594.  
  5595.  A number of the QuickPascal standard procedures and functions apply to all
  5596.  types of data files──text, typed, and untyped. Table 9.1 summarizes those
  5597.  procedures and functions available to all file types.
  5598.  
  5599.  Table 9.1  Standard Procedures and Functions for All File Types
  5600.  
  5601.  Routine           Purpose
  5602.  
  5603.  Assign            Associates a file buffer with a filename
  5604.  
  5605.  Close             Closes the file buffer
  5606.  
  5607.  Eof               Returns the end-of-file status
  5608.  
  5609.  Erase             Deletes a file
  5610.  
  5611.  IOResult          Returns the error status of the last I/O
  5612.  
  5613.  Read              Reads one or more elements from a file
  5614.  
  5615.  Rename            Renames a file
  5616.  
  5617.  Reset             Opens an existing file
  5618.  
  5619.  Rewrite           Creates and opens a new file, after closing and erasing
  5620.                    any file with the same name
  5621.  
  5622.  Write             Writes one or more elements to a file
  5623.  
  5624.  Several other standard procedures and functions apply only to text files.
  5625.  They appear in Table 9.2 below.
  5626.  
  5627.  Table 9.2  Standard Procedures and Functions for Text Files
  5628.  
  5629.  Routine           Purpose
  5630.  
  5631.  Append            Opens an existing text file for adding more text to the
  5632.                    end of the file
  5633.  
  5634.  Eoln              Returns the end-of-line status
  5635.  
  5636.  Flush             Clears the text buffer
  5637.  
  5638.  Readln            Reads one or more data items, one line at a time
  5639.  
  5640.  SeekEof           Returns the end-of-file status, ignoring any blanks, tabs,
  5641.                    and end-of-line markers
  5642.  
  5643.  SeekEoln          Returns the end-of-line status, ignoring any blanks and
  5644.                    tabs
  5645.  
  5646.  SetTextBuf        Assigns a text file I/O buffer
  5647.  
  5648.  Writeln           Writes one or more data items and appends an end-of-line
  5649.                    marker
  5650.  
  5651.  
  5652.  Chapter 10  Binary Files
  5653.  ───────────────────────────────────────────────────────────────────────────
  5654.  
  5655.  A binary file contains program data stored on disk. Each item in a binary
  5656.  file is stored in the same binary representation used by a QuickPascal
  5657.  program. Binary files provide optimal storage of numbers, Booleans, and
  5658.  enumerated types. For example, to store the integer 21,000 in a text file,
  5659.  you write a string of at least five characters to disk. To store the number
  5660.  in a binary file, you write just two bytes. However, you cannot display a
  5661.  binary file directly or view it with a word processor.
  5662.  
  5663.  You access each binary file as either a typed file or an untyped file:
  5664.  
  5665.    ■  A "typed file" contains a series of discrete units called
  5666.       "components." Each component must have the same type, which can be
  5667.       almost any data type supported by QuickPascal but is typically a
  5668.       record.
  5669.  
  5670.    ■  An "untyped file"  is treated as a raw, unstructured series of bytes.
  5671.       None of the text-oriented read and write functions is available.
  5672.       Typically, programs use untyped files for large block operations such
  5673.       as copying an entire file to another. Any file can be declared as an
  5674.       untyped file.
  5675.  
  5676.  
  5677.  10.1  Typed Files
  5678.  
  5679.  Like an array, a typed file is a series of components all having the same
  5680.  type. Unlike an array, a typed file has no definite size. A file starts at
  5681.  length zero and automatically grows as you append data. Furthermore, files
  5682.  serve as permanent records that exist after the program terminates.
  5683.  
  5684.  In essence, typed files are formatted data files. The format is determined
  5685.  by the component type, which you should choose carefully to solve a given
  5686.  programming task. For example, to implement an airline reservation system,
  5687.  you would set up a record type to store all of the needed data for one
  5688.  reservation. Then you might create a file made up of these records. Every
  5689.  program in the system must use this same record type to correctly read the
  5690.  file.
  5691.  
  5692.  The structure of a typed file supports random access. For example, in a file
  5693.  of records, you can directly access record 367 without having to read
  5694.  through the first 366.
  5695.  
  5696.  
  5697.  10.1.1  Declaring Typed Files
  5698.  
  5699.  Not surprisingly, the syntax for defining a typed file is similar to that
  5700.  for arrays:
  5701.  
  5702.       FILE OF ComponentType
  5703.  
  5704.  In the syntax display above, ComponentType can be any valid data type, with
  5705.  one restriction: the component type cannot be a file type or a type that
  5706.  contains a file type. Thus, files of arrays are legal, as are arrays of
  5707.  files. However, a file of arrays of files is illegal.
  5708.  
  5709.  The component type is frequently a record. (In fact, other programming
  5710.  languages often use the term "record" to refer to a component of a file.)
  5711.  For example, you might define a record type to hold a name and phone number
  5712.  for one person. To create a permanent list of phone numbers for many people,
  5713.  you could create a file made up of these records.
  5714.  
  5715.  The following code shows examples of valid file types:
  5716.  
  5717.       TYPE
  5718.           phonerec = RECORD
  5719.               name          : STRING[20];
  5720.               long_distance : Boolean;
  5721.               phone         : LongInt;
  5722.               END;
  5723.  
  5724.           phone_list = FILE OF phonerec;
  5725.           math_file = FILE OF ARRAY[1..10] OF Real;
  5726.       VAR
  5727.           master_list   : ARRAY[1..20] OF phone_list;
  5728.           celebs        : phone_list;
  5729.           lucky_numbers : FILE OF Integer;
  5730.  
  5731.  
  5732.  10.1.2  Accessing Data in a Typed File
  5733.  
  5734.  After declaring a file variable, you may assign it to a physical disk file
  5735.  with the Assign procedure, as described in the last chapter. Then you can
  5736.  open the file for writing (with Rewrite) or for both reading and writing
  5737.  (with Reset).
  5738.  
  5739.  For example, the following code declares a file of integers, assigns the
  5740.  file variable to the disk file MYFILE.DAT (in the root directory of drive
  5741.  C:), and then opens the file for reading and writing:
  5742.  
  5743.       VAR
  5744.           intfile : FILE OF Integer;
  5745.       BEGIN
  5746.           Assign( intfile, 'C:\MYFILE.DAT' );
  5747.           Reset( intfile );
  5748.  
  5749.  After you open the file, you can read and write any number of components
  5750.  sequentially with the Read and Write procedures. (The next section shows how
  5751.  to use random access.) These procedures work with both text files and typed
  5752.  files, and take the same syntax in either case. But with typed files, each
  5753.  item you read or write must be a variable of the component type.
  5754.  
  5755.  For example, if int_file is a file of integers, and a, b, and c are integer
  5756.  variables, you can read or write the file as follows:
  5757.  
  5758.       Write( int_file, a,b,c );  { Write a, b, c to the file }
  5759.       Read( int_file, n );       { Read next integer in file }
  5760.  
  5761.  The Read and Write procedures do not do any text formatting when used with
  5762.  typed files. In the example above, the procedures read and write the numeric
  5763.  value of the integers directly to and from the disk. If int_file were a text
  5764.  file, the Read procedure would translate the numbers to character strings
  5765.  before writing them.
  5766.  
  5767.  As you read components of a file, use the Eof function or FileSize function
  5768.  (described below) to make sure you don't read past the end of the file. The
  5769.  Eof function returns True if the last read operation took you beyond the end
  5770.  of the file.
  5771.  
  5772.  You can use a number of other procedures with typed files, including the
  5773.  procedures listed in Table 9.1 in Chapter 9, "Text Files." In addition, you
  5774.  can use those listed below.
  5775.  
  5776.  Procedure                  Description
  5777.  
  5778.  FilePos                     Takes a file variable as a parameter, and
  5779.                              returns the current file position (in terms of
  5780.                              components or blocks)
  5781.  
  5782.  FileSize                    Takes a file variable as a parameter, and
  5783.                              returns the size of the file in bytes; result
  5784.                              has type LongInt
  5785.  
  5786.  Seek                        Takes a file variable and a long integer as
  5787.                              parameters, and moves the file position to the
  5788.                              component or block designated by the integer
  5789.  
  5790.  Truncate                    Takes a file variable as a parameter, and
  5791.                              truncates the file at the current file position
  5792.  
  5793.  The FilePos and Seek procedures let you treat a binary file as a
  5794.  random-access file, and the next section provides more detail on how to use
  5795.  them. Note that you do not have to treat a typed file as strictly a
  5796.  sequential or random-access file. You can use any combination of functions
  5797.  supported for the file type.
  5798.  
  5799.  
  5800.  10.1.3  Using Random Access
  5801.  
  5802.  You can use random-access procedures with any typed file. "Random access" is
  5803.  the capability to read or write components to any place in the file and in
  5804.  any order.
  5805.  
  5806.  Random access is like placing a phone call. You can immediately connect to
  5807.  any place in the system by giving the right number. Sequential access is
  5808.  like reading a novel. You advance from one page to the next in the order
  5809.  given.
  5810.  
  5811.  The two principal random-access procedures are Seek and FilePos. The Seek
  5812.  procedure sets the file buffer to the component denoted by the number you
  5813.  specify. The first component is denoted by 0, the second by 1, and so on.
  5814.  The syntax is
  5815.  
  5816.       Seek (FileVar, Position)
  5817.  
  5818.  in which FileVar is a file variable, and Position is a constant or variable
  5819.  of type LongInt. For example:
  5820.  
  5821.       TYPE
  5822.          phone_rec = RECORD
  5823.              name,
  5824.              notes : STRING;
  5825.              number : LongInt;
  5826.              END;
  5827.       VAR
  5828.           phone_list : FILE OF phone_rec;
  5829.           rec10, rec11, rec15, rec25 : phone_rec;
  5830.       BEGIN
  5831.           Assign( phone_list, 'FONEHOME.DAT' );
  5832.           Reset( phone_list );
  5833.           Seek( phone_list, 9 );    { Get 10th & 11th record }
  5834.           Read( phone_list, rec10, rec11 );
  5835.           Seek( phone_list, 14 );    { Get 15th record }
  5836.           Read( phone_list, rec15 );
  5837.           Seek( phone_list, 24 );    { Get 25th record }
  5838.           Read( phone_list, rec25 );
  5839.  
  5840.  The example above copies records at predefined locations in the file. More
  5841.  often, a practical application determines the record number interactively.
  5842.  For example, the following code prompts the user for the record number and
  5843.  data, then enters this data into the file:
  5844.  
  5845.       VAR
  5846.           phone_list : FILE OF phone_rec;
  5847.           temp_rec : phone_rec;
  5848.           n : LongInt;
  5849.       BEGIN
  5850.           Assign( phone_list, 'FONEHOME.DAT' );
  5851.           Reset( phone_list );
  5852.           Write( 'Enter record number: ' );
  5853.           Readln( n );
  5854.           Write( 'Enter name: ' );  { Prompt for data }
  5855.           Readln( temp_rec.name );
  5856.           Write( 'Enter number: ' );
  5857.           Readln( temp_rec.number );
  5858.           Seek( phone_list, n );  { Access record requested }
  5859.           Write( phone_list, temp_rec ); { Write data to file }
  5860.  
  5861.  The FilePos function takes a file variable as a parameter and returns the
  5862.  number (again, a LongInt) of the current component.
  5863.  
  5864.  The Eof function is useful for both sequential-access and random-access
  5865.  operations. This function takes a file variable as its parameter and returns
  5866.  True if the current component is past the end of the file. Thus, it tells
  5867.  you when you have read to the end of the file or have a record number
  5868.  corresponding to a nonexistent file component.
  5869.  
  5870.  So far, you have seen how to read and overwrite existing files. You can
  5871.  append the end of files with
  5872.  
  5873.       Seek(f, FilePos(f))
  5874.  
  5875.  and rewrite files completely with the Rewrite procedure. But there is no
  5876.  easy way to insert new components into the middle of a file. The only way to
  5877.  insert a component is to read an entire file into memory, manipulate the
  5878.  contents, and write the file to disk again.
  5879.  
  5880.  
  5881.  10.2  Untyped Files
  5882.  
  5883.  Untyped file variables support direct, low-level I/O operations with any
  5884.  file. The BlockRead and BlockWrite functions used with untyped files allow
  5885.  for fast data transfer for copy and backup of files. You can also use
  5886.  untyped file I/O to create sequential binary files with variable-length
  5887.  records.
  5888.  
  5889.  Untyped files differ from typed files in that
  5890.  
  5891.    ■  Untyped files can contain any type of data, even text.
  5892.  
  5893.    ■  Untyped files can be read or written with any record length using
  5894.       BlockRead and BlockWrite.
  5895.  
  5896.  To declare a type or variable as an untyped file, just use the FILE keyword.
  5897.  For example,
  5898.  
  5899.       TYPE
  5900.           low_level = FILE;
  5901.       VAR
  5902.           my_file : low_level;
  5903.  
  5904.  The Read and Write procedures, supported for use with text files and typed
  5905.  files, are not supported with untyped files. (Otherwise, any procedure
  5906.  supported for typed files is also supported for untyped files.) Instead, use
  5907.  the BlockRead and BlockWrite procedures to access data. BlockRead and
  5908.  BlockWrite read and write records to a file. In this context, "record"
  5909.  denotes a data block of a specific size. The default block size is 128 bytes
  5910.  if you use the standard file-open sequence:
  5911.  
  5912.       Assign( file_var, 'FILE' );
  5913.       Reset( file_var );
  5914.  
  5915.  With the default block size, the BlockRead procedure reads in units of 128
  5916.  bytes at a time. If the last BlockRead finds fewer than 128 bytes, an error
  5917.  occurs. Rarely are the contents of a file exactly equal to 128 * n. To avoid
  5918.  errors, you have two alternatives:
  5919.  
  5920.    1. Create a file by writing records of a fixed size with BlockWrite. Then
  5921.       the file size will be exactly divisible by the size of the record.
  5922.  
  5923.    2. Create a record size of one byte (since every file size is a multiple
  5924.       of one) by using the statements below:
  5925.  
  5926.          Assign( file_var, 'FILE' );
  5927.          Reset( file_var, 1 );
  5928.  
  5929.  Reset and Rewrite have an optional parameter to define the number of bytes
  5930.  in a record. Once the record size is set to one byte, the procedures
  5931.  BlockRead andBlockWrite transfer multiples of one byte whenever they
  5932.  execute. No error occurs at the end of the file.
  5933.  
  5934.  The syntax for BlockRead is
  5935.  
  5936.       BlockRead(FileVar, Buffer, Count «,NumRead»)
  5937.  
  5938.  where BlockRead reads Count records (or the number of records remaining,
  5939.  whichever is less) from the file into Buffer. The Buffer parameter can be
  5940.  any variable large enough to hold the number of bytes read. The actual
  5941.  number of complete records read is returned in the optional parameter
  5942.  NumRead. Use NumRead to determine whether BlockRead was successful. If the
  5943.  parameter NumRead is omitted and BlockRead reads fewer than Count records,
  5944.  an I/O error occurs. The parameter list of BlockWrite is the same as that
  5945.  for BlockRead.
  5946.  
  5947.  The following simple program, DUPLICAT.PAS, shows a typical use of block I/O
  5948.  to copy a file:
  5949.  
  5950.       PROGRAM duplicat;
  5951.       CONST
  5952.           max_buf=16384;
  5953.       VAR
  5954.           file_name, copyfile_name : STRING;
  5955.           source, target : FILE;
  5956.           buffer : ARRAY [1..max_buf] OF Char; { 16K buffer }
  5957.           bytes_read, bytes_written : Word;
  5958.  
  5959.       BEGIN
  5960.           Write( 'Enter source file_name -> ' );
  5961.           Readln( file_name );
  5962.           Write( 'Enter name of target file -> ' );
  5963.           Readln( copyfile_name );
  5964.           Assign( source, file_name );
  5965.           Reset( source, 1 );          { 1 byte-block size }
  5966.           Assign( target, copyfile_name );
  5967.           Rewrite( target, 1 );        { 1 byte-block size }
  5968.           REPEAT
  5969.            BlockRead( source, buffer, SizeOf( buffer ), bytes_read );
  5970.            BlockWrite( target, buffer, bytes_read, bytes_written )
  5971.           UNTIL ( bytes_read = 0 ) OR ( bytes_read <> bytes_written );
  5972.           Close( source );
  5973.           Close( target );
  5974.       END.
  5975.  
  5976.  The program detects the end of the file by looking for either of the
  5977.  following two conditions:
  5978.  
  5979.    1. No records were read by the last BlockRead call.
  5980.  
  5981.    2. The requested number of records does not match the actual number of
  5982.       records read.
  5983.  
  5984.  The block I/O techniques presented in the program above are used to
  5985.  implement an extended version of the DOS COPY command in the sample program
  5986.  EXCOPY.PAS, available on-line in QuickPascal. The other sample program
  5987.  components are the command-line arguments; the FindFirst and FindNext
  5988.  routines; a binary tree to detect duplicate file names; I/O error checking
  5989.  used with BlockRead and BlockWrite; and screen output informing the user of
  5990.  the file copy progress.
  5991.  
  5992.  The EXCOPY.PAS procedure copyfile has the task of actually copying the
  5993.  files, one at a time. Notice the following aspects of the procedure:
  5994.  
  5995.    ■  The Reset and Rewrite statements are accompanied by the {$I-} directive
  5996.       to prevent run-time errors from stopping the program. After calling
  5997.       Reset and Rewrite, the value of the function IOResult is compared with
  5998.       0. If it is not 0, the procedure terminates. This behavior protects
  5999.       against errors resulting from bad file names or attempts to copy files
  6000.       that cannot be accessed.
  6001.  
  6002.    ■  After the BlockWrite procedure is executed, the parameters bytes_read
  6003.       and byte_written are compared. If they are not equal, the destination
  6004.       disk becomes full while copying the current file.
  6005.  
  6006.    ■  If the file cannot be copied, then a message is displayed to that
  6007.       effect and the target file is erased. Consequently, any partially used
  6008.       disk space is freed for other smaller files to be copied. In addition,
  6009.       the above procedure also wipes off zero-byte files that would otherwise
  6010.       appear in the target directory.
  6011.  
  6012.  To run EXCOPY.EXE, first compile the program EXCOPY.PAS. The current
  6013.  directory should contain the source files you wish to copy. Enter the
  6014.  command line arguments as
  6015.  
  6016.       EXCOPY TargetDirectory «FileList»
  6017.  
  6018.  The FileList argument can contain one or more file names separated by
  6019.  spaces. Each file name can contain the wildcard characters * and ?. If you
  6020.  omit FileList, EXCOPY uses the default file specification *.* as the file
  6021.  list.
  6022.  
  6023.  
  6024.  Chapter 11  Pointers and Dynamic Memory
  6025.  ───────────────────────────────────────────────────────────────────────────
  6026.  
  6027.  A pointer is a variable that contains the numeric address of another data
  6028.  object. A pointer provides indirect access to data. For example, if you have
  6029.  a pointer to a record and you pass this pointer to a procedure, then the
  6030.  procedure can manipulate any field by using the pointer. The procedure does
  6031.  not need its own copy of the data.
  6032.  
  6033.  In Pascal, you use pointers primarily as handles to dynamic-memory objects.
  6034.  "Dynamic memory" consists of memory that the program explicitly requests at
  6035.  run time. Dynamic memory gives you many advantages. It lets your memory
  6036.  usage grow and contract as your needs require──you do not need to specify a
  6037.  maximum size or limit.
  6038.  
  6039.  Because dynamic memory is allocated at run time, your program cannot know in
  6040.  advance where the block is located. Pascal, therefore, returns a pointer
  6041.  when it allocates dynamic memory. The pointer provides the access to the
  6042.  data.
  6043.  
  6044.  Dynamic memory enables you to create powerful data structures such as linked
  6045.  lists and binary trees. These structures, described at the end of this
  6046.  chapter, are networks of data in which pointers provide the connecting
  6047.  links.
  6048.  
  6049.  In QuickPascal, you can also use pointers to point to ordinary (non-dynamic)
  6050.  variables. This chapter begins by explaining the basics of pointers using
  6051.  nondynamic variables.
  6052.  
  6053.  
  6054.  11.1  Declaring and Accessing Pointers
  6055.  
  6056.  Using pointers consists of three major steps, which you must always do in
  6057.  this order:
  6058.  
  6059.    1. Declare the pointer as a specific type.
  6060.  
  6061.    2. Initialize the pointer.
  6062.  
  6063.    3. Use the pointer by assigning its value, testing its value, or accessing
  6064.       the value that it points to.
  6065.  
  6066.  
  6067.  11.1.1  Declaring Pointers
  6068.  
  6069.  Like other variables, pointers have definite types and can only point to a
  6070.  variable of the appropriate type. You can declare a pointer with the
  6071.  following syntax:
  6072.  
  6073.       PointerName : ^DataType
  6074.  
  6075.  Example pointer declarations are shown below:
  6076.  
  6077.       TYPE
  6078.           totals = ARRAY[1..10] OF Integer;
  6079.       VAR
  6080.           int_ptr   : ^Integer;
  6081.           char_ptr  : ^Char;
  6082.           str_ptr   : ^STRING;
  6083.           real_ptr  : ^Real;
  6084.           total_ptr : ^totals;
  6085.  
  6086.  After you declare a pointer, it does not point to any meaningful value; you
  6087.  can produce errors if you try to use it. The first thing you must do after
  6088.  declaring a pointer is initialize it.
  6089.  
  6090.  
  6091.  11.1.2  Initializing Pointers
  6092.  
  6093.  After declaring a pointer, you must initialize it to an address. You can
  6094.  always initialize a pointer to the special NIL value. This value indicates
  6095.  that the pointer is temporarily turned off──it has no object to point to.
  6096.  Your program can test for this condition and take appropriate actions. The
  6097.  NIL value is useful in indicating the end of a tree or linked list. Here is
  6098.  an example of an assignment to NIL:
  6099.  
  6100.       my_ptr := NIL;
  6101.  
  6102.  To assign the address of a variable to a pointer, you can use either the
  6103.  address-of (@) operator, or the Addr function. The syntax is
  6104.  
  6105.       Pointer := Addr(Variable)
  6106.       Pointer := @Variable
  6107.  
  6108.  The Variable can be any variable of the type that appears in the declaration
  6109.  of Pointer. An example is shown below:
  6110.  
  6111.       VAR
  6112.           an_int   : Byte;
  6113.           byte_ptr : ^Byte;
  6114.  
  6115.       BEGIN
  6116.           an_int := 5;
  6117.  
  6118.           { These assignment statements put the same
  6119.             address in pointer byte_ptr.
  6120.             Both Writeln statements print the number 5.
  6121.           }
  6122.           byte_ptr := Addr( an_int );
  6123.           Writeln( byte_ptr^ );
  6124.  
  6125.           byte_ptr := @an_int;
  6126.           Writeln( byte_ptr^ );
  6127.       END.
  6128.  
  6129.  In Section 11.2, you learn how to assign a value to a pointer by making a
  6130.  dynamic-memory procedure call.
  6131.  
  6132.  
  6133.  11.1.3  Manipulating Pointers
  6134.  
  6135.  Pointer manipulation in Pascal is extremely limited. In addition to the
  6136.  methods described above, the only way to manipulate a pointer is to assign
  6137.  it the value of another pointer of the same type. For example, the statement
  6138.  
  6139.       ptr1 := ptr2;
  6140.  
  6141.  causes ptr1 to point to the same location that ptr2 does. This kind of
  6142.  assignment is frequently useful in dealing with data structures such as
  6143.  linked lists (shown in Section 11.3).
  6144.  
  6145.  Once you declare and initialize a pointer, you can use it in one of the
  6146.  following ways:
  6147.  
  6148.    ■  Assign the value of the pointer itself to another pointer.
  6149.  
  6150.    ■  Test the value of the pointer itself.
  6151.  
  6152.    ■  Access the value of the variable pointed to.
  6153.  
  6154.  The number of operations you can do with the value of the pointer is
  6155.  limited. As described above, you can assign the value of a pointer to
  6156.  another pointer of the same type. You can also test a pointer for equality
  6157.  to NIL or to another pointer. For example, the statement
  6158.  
  6159.       IF (ptr1 = ptr2) THEN ...
  6160.  
  6161.  executes the statement following THEN if ptr1 and ptr2 point to the same
  6162.  variable. Note that if ptr1 and ptr2 point to different locations, then the
  6163.  expression ptr1 = ptr2 evaluates as False, even if the objects that ptr1 and
  6164.  ptr2 point to are equal.
  6165.  
  6166.  You can also access the value of the variable indicated by the pointer. This
  6167.  value can be manipulated in any way you can manipulate the variable itself.
  6168.  Use the following syntax to access the variable indicated by the pointer:
  6169.  
  6170.       PointerName^
  6171.  
  6172.  This operation is called "dereferencing" the pointer. For example, the
  6173.  following code sets the value of x to 5, and then assigns this value to y:
  6174.  
  6175.       VAR
  6176.           x, y     : Byte;
  6177.           byte_ptr : ^Byte;
  6178.  
  6179.       BEGIN
  6180.           byte_ptr := Addr( x );{ byte_ptr now points to x }
  6181.           byte_ptr^ := 5;        { assign 5 to x }
  6182.           y := byte_ptr^;         { assign value of x to y }
  6183.       END.
  6184.  
  6185.  In testing pointer values, bear in mind the difference between a pointer and
  6186.  the variable pointed to. For example, the statement
  6187.  
  6188.       IF (ptr1^ = ptr2^) THEN ...
  6189.  
  6190.  executes the statement following THEN if the objects pointed to by ptr1 and
  6191.  ptr2 are equal. Contrast this example with the previous IF-statement
  6192.  example, which evaluated to True only if ptr1 and ptr2 pointed to the same
  6193.  object.
  6194.  
  6195.  
  6196.  11.2  Dynamic-Memory Allocation
  6197.  
  6198.  In most programs, you need to evaluate the maximum amount of memory the
  6199.  program will require. If the amount of data becomes larger than you foresaw,
  6200.  you must rewrite the program and then recompile. However, dynamic memory
  6201.  lets your memory usage grow along with the needs of the program. The amount
  6202.  of physical memory available is the only ultimate limit to dynamic memory.
  6203.  
  6204.  The use of pointers is essential to all dynamic-memory operations. When
  6205.  QuickPascal allocates memory, it returns a pointer. The pointer gives you
  6206.  access to the memory block.
  6207.  
  6208.  There are two basic ways of dynamically allocating memory:
  6209.  
  6210.    1. Allocating one object at a time (New and Dispose)
  6211.  
  6212.    2. Allocating a block of memory (GetMem and FreeMem)
  6213.  
  6214.  
  6215.  11.2.1  Allocating a Single Object
  6216.  
  6217.  By using the New procedure, you allocate space equal to the size of the data
  6218.  type associated with the pointer. The syntax is
  6219.  
  6220.       New(Pointer)
  6221.  
  6222.  Once the New function executes, QuickPascal assigns the address of the
  6223.  dynamic-memory block to Pointer. The Pointer must be a variable previously
  6224.  declared. The following example shows how to declare a pointer of type Byte;
  6225.  allocate memory through the pointer; and then use the dynamic variable to
  6226.  hold, manipulate, and display a value.
  6227.  
  6228.       VAR
  6229.           int_ptr : ^Byte;
  6230.  
  6231.       BEGIN
  6232.           int_ptr := NIL;
  6233.           { create a Byte-type dynamic variable }
  6234.           New( int_ptr );
  6235.           int_ptr^ := 100;      { assign a value to the dynamic
  6236.                                         variable }
  6237.           Inc( int_ptr^, 10 );  { increment it }
  6238.           Writeln( int_ptr^ );  { display its value }
  6239.           Dispose( int_ptr );
  6240.       END.
  6241.  
  6242.  As described in the previous section, int_ptr^ is an example of a
  6243.  dereferenced pointer. int_ptr itself is a pointer, which can only be
  6244.  manipulated in a few restricted ways. However, int_ptr^ is equivalent to an
  6245.  ordinary variable of type Byte. Use int_ptr^ anywhere you would use a Byte
  6246.  variable.
  6247.  
  6248.  To remove a dynamic-memory object created with the New function, use the
  6249.  Dispose function. See Section 11.3, "Linked Lists," for more examples of
  6250.  New and Dispose.
  6251.  
  6252.  
  6253.  11.2.2  Allocating a Memory Block
  6254.  
  6255.  The GetMem and FreeMem functions are similar to New and Dispose. However,
  6256.  GetMem and FreeMem deal with entire blocks of memory rather than one object
  6257.  at a time. Once a block is allocated, you access it as if it were an array
  6258.  of indefinite size.
  6259.  
  6260.  Use the GetMem procedure to select the size of a dynamic-memory block. The
  6261.  size should be a multiple of the size of the element type of the array.
  6262.  Therefore, if size is the number of elements you want to allocate, and
  6263.  base_type is the element type of the array, then pass the following
  6264.  parameters to GetMem:
  6265.  
  6266.       Size * SizeOf(base_type)
  6267.  
  6268.  A common way to use GetMem is to declare an array type of max_elements
  6269.  elements first, where max_elements is the largest possible number of
  6270.  elements of the base type. Because the type is an array, you can access
  6271.  memory throughout the block with an array index. For example, the following
  6272.  code makes the necessary declarations and then calls GetMem to return a
  6273.  memory block:
  6274.  
  6275.       CONST
  6276.           max_elements = 65520 DIV SizeOf( base_type );
  6277.       TYPE
  6278.           big_array = ARRAY[1..max_elements] OF base_type;
  6279.       VAR
  6280.           array_ptr : ^big_array;
  6281.       BEGIN
  6282.           .
  6283.           .
  6284.           .
  6285.           GetMem( array_ptr, size * SizeOf(base_type) );
  6286.  
  6287.  The array_ptr now points to an array of type base_type. You can treat
  6288.  array_ptr just like any array. The largest index in this array is size. To
  6289.  access any element in this array, use the following syntax:
  6290.  
  6291.       ArrayPointer^[Index]
  6292.  
  6293.  The example shown below requests a memory block 100 elements long. In this
  6294.  case, array_size is set to 100, but at run time, the program could set
  6295.  array_size to whatever length it needed.
  6296.  
  6297.       CONST
  6298.           max_elements = 65520 DIV SizeOf( Real );
  6299.       TYPE
  6300.           some_reals : ARRAY[1..max_elements] OF Real;
  6301.       VAR
  6302.           rptr       : ^some_reals;
  6303.           i          : Byte;
  6304.           array_size : Word;
  6305.       BEGIN
  6306.           array_size := 100;
  6307.           GetMem( rptr, array_size * SizeOf(Real) );
  6308.           FOR i := 1 TO array_size DO
  6309.               BEGIN
  6310.               rptr^[i] := i;
  6311.               Writeln( rptr^[i] );
  6312.               END;
  6313.           FreeMem( rptr, array_size * SizeOf( Real ));
  6314.       END.
  6315.  
  6316.  The FreeMem procedure frees up memory blocks allocated by GetMem. If you no
  6317.  longer need to use a particular memory block, it is a good idea to free the
  6318.  memory. Otherwise, the program can use up all of the available memory over
  6319.  time. The FreeMem procedure takes the same parameters that GetMem does. Make
  6320.  sure that the size you specify in FreeMem matches the size allocated with
  6321.  GetMem.
  6322.  
  6323.  Table 11.1 summarizes the procedures provided by QuickPascal for use with
  6324.  pointers.
  6325.  
  6326.  Table 11.1  Pointer Procedures
  6327.  
  6328.  Routine           Purpose                             Example
  6329.  
  6330.  Addr              Returns the address of a data       aptr :=  Addr( I );
  6331.                    object (same as the @ operator)
  6332.  
  6333.  Dispose           Disposes of a dynamic variable      Dispose( nextptr );
  6334.  
  6335.  FreeMem           Disposes of a dynamic variable      FreeMem( aptr, 512 );
  6336.                    of given size in bytes
  6337.  
  6338.  GetMem            Creates a dynamic variable of a     GetMem( aptr, 512 );
  6339.                    given size in bytes
  6340.  
  6341.  New               Creates a dynamic variable          New( aptr );
  6342.  
  6343.  
  6344.  11.3  Linked Lists
  6345.  
  6346.  Stacks, queues, and trees are data structures that are linked lists. A
  6347.  "linked list" is a collection of dynamically allocated records, each having
  6348.  a field that is a pointer to the next record. Essentially, the pointers
  6349.  serve as the connectors between any two items. By altering the value of the
  6350.  pointers, you can sort or reorganize the list in any way──without physically
  6351.  moving any of the stored records.
  6352.  
  6353.  If your program implements a straightforward algorithm, you do not need to
  6354.  use these data structures. However, these structures give you a great deal
  6355.  of power to solve complex computing tasks. They can grow to any size, and
  6356.  they let the program traverse, analyze, and restructure a network of data
  6357.  paths. The only limit to the complexity is your own imagination.
  6358.  
  6359.  The LIST.PAS program adds records to a list, deletes them, and prints the
  6360.  contents of the list. The data is stored in a record declared as:
  6361.  
  6362.       TYPE
  6363.           rec_ptr = ^stack_rec;
  6364.           stack_rec = RECORD
  6365.               data     : Integer;
  6366.               next_rec : rec_ptr;
  6367.               END;
  6368.  
  6369.  Note that the second field of type stack_rec points to another record──also
  6370.  of type stack_rec. Though this self reference may seem paradoxical, it is
  6371.  perfectly legal. It simply means that the second field is the connector to
  6372.  another record of the same type. The data field contains the data to be
  6373.  stored. For more complex programs, the record could have any number of
  6374.  appropriate data fields.
  6375.  
  6376.  To create a list, first declare a pointer to the start of the list and
  6377.  initialize this pointer to NIL:
  6378.  
  6379.       VAR
  6380.           stack_ptr : rec_ptr;
  6381.       BEGIN
  6382.           stack_ptr := NIL;
  6383.  
  6384.  The program has two major procedures, push and pop. These procedures model
  6385.  the behavior of the PUSH and POP instructions of the processor. The linked
  6386.  list in this program is a last-in, first-out mechanism, just like the stack
  6387.  of the 8086 microprocessor. The push procedure adds items to the front of
  6388.  the list, and pop removes these items from the front as well. Therefore, the
  6389.  last item stored is also the first item retrieved.
  6390.  
  6391.  The code in the push procedure inserts a new record at the front of the list
  6392.  and then assigns the new value (x) to the data field. These actions simulate
  6393.  the action of pushing x onto the top of a stack.
  6394.  
  6395.       VAR
  6396.           temp :  rec_ptr;
  6397.       BEGIN
  6398.           New( temp );
  6399.           temp^.next_rec := stack_ptr;
  6400.           stack_ptr := temp;
  6401.           stack_ptr^.data := x;
  6402.       END;
  6403.  
  6404.  The above lines of code show the four steps required for the push procedure
  6405.  to add a new record to the linked list. These four steps are listed below:
  6406.  
  6407.    1. The first statement, New(temp), allocates a memory location large
  6408.       enough to hold a record with the fields data and next_rec. The pointer
  6409.       temp now points to this new record.
  6410.  
  6411.    2. To insert this record at the front of the list, the code reassigns two
  6412.       pointer values. First, the procedure sets the next_rec field to point
  6413.       to the current item at the front of the list. The pointer variable
  6414.       stack_ptr points to the front of the list, so the following line of
  6415.       code assigns the value stack_ptr to the next_rec field of the new
  6416.       record (the new record is referred to as temp^).
  6417.  
  6418.          temp^.next_rec := stack_ptr;
  6419.  
  6420.    3. Next, the pointer stack_ptr must be reassigned to temp. The result is
  6421.       that the item previously at the front of the list is now the second
  6422.       item (because of step 2), and the new record is at the very front.
  6423.  
  6424.    4. Now that the new record has been created and inserted, you can simply
  6425.       load the new data into the record. The following statement assigns the
  6426.       value of x to the data field of the new record. Note that because of
  6427.       step 3, the new record can be referred to as stack_ptr^.
  6428.  
  6429.          stack_ptr^.data := x;
  6430.  
  6431.       Note that temp still points to the new record, but now temp can be
  6432.       ignored because stack_ptr also points to this record.
  6433.  
  6434.  Figure 11.1 illustrates the push procedure.
  6435.  
  6436.  
  6437.  Figure 11.1  The Push Procedure
  6438.  
  6439.  Initial Condition
  6440.                                ┌───────────┐  ┌───────────┐
  6441.                                │   data    │  │   data    │
  6442.                 ┌───────────┐┌├───────────┤┌├───────────┤         ┌─────────
  6443.                 │ stack_ptr ├┘ │ next_rec  ├┘ │ next_rec  ├─ ∙∙∙ ─│   NIL
  6444.                 └───────────┘  └───────────┘  └───────────┘         └─────────
  6445.  
  6446.  Step 1   New (temp) ;
  6447.                 ┌───────────┐
  6448.                 │   data    │
  6449.  ┌───────────┐┌├───────────┤
  6450.  │ stack_ptr ├┘ │ next_rec  │
  6451.  └───────────┘  └───────────┘
  6452.  
  6453.  Step 2   temp^.next_rec := stack_ptr;
  6454.                                ┌───────────┐  ┌───────────┐
  6455.                 ┌───────────┐  │   data    │  │   data    │
  6456.                 │ stack_ptr ├┬├───────────┤┌├───────────┤         ┌─────────
  6457.                 └───────────┘│ │ next_rec  ├┘ │ next_rec  ├─ ∙∙∙ ─│   NIL
  6458.                 ┌───────────┐│ └───────────┘  └───────────┘         └─────────
  6459.                 │   data    ││
  6460.  ┌───────────┐┌├───────────┤│
  6461.  │ stack_ptr ├┘ │ next_rec  ├┘
  6462.  └───────────┘  └───────────┘
  6463.  
  6464.  Step 3   stack_ptr := temp;
  6465.                                ┌───────────┐  ┌───────────┐
  6466.                 ┌───────────┐  │   data    │  │   data    │
  6467.               ┌─┤ stack_ptr │┌├───────────┤┌├───────────┤         ┌─────────
  6468.               │ └───────────┘│ │ next_rec  ├┘ │ next_rec  ├─ ∙∙∙ ─│   NIL
  6469.               │ ┌───────────┐│ └───────────┘  └───────────┘         └─────────
  6470.               │ │   data    ││
  6471.  ┌───────────┐├├───────────┤│
  6472.  │ stack_ptr ├┘ │ next_rec  ├┘
  6473.  └───────────┘  └───────────┘
  6474.  
  6475.  Step 4   stack_ptr^.data := x;
  6476.                                ┌───────────┐  ┌───────────┐
  6477.                 ┌───────────┐  │   data    │  │   data    │
  6478.               ┌─┤ stack_ptr │┌├───────────┤┌├───────────┤         ┌─────────
  6479.               │ └───────────┘│ │ next_rec  ├┘ │ next_rec  ├─ ∙∙∙ ─│   NIL
  6480.               │ ┌───────────┐│ └───────────┘  └───────────┘         └─────────
  6481.               │ │           │┐┌───────────┐
  6482.               └├───────────┤│└┤     x     │
  6483.                 │ next_rec  ├┘ └───────────┘
  6484.                 └───────────┘
  6485.  
  6486.  
  6487.  The pop procedure works by executing the series of steps in reverse, as
  6488.  shown in the code below:
  6489.  
  6490.       z := stack_ptr^.data;
  6491.       temp := stack_ptr;
  6492.       stack_ptr := stack_ptr^.next_rec;
  6493.       Dispose(temp);
  6494.  
  6495.    1. The procedure pops the value off the top of the stack by saving the
  6496.       value in the data field of the first record. The following statement
  6497.       saves this value by loading it into z, the output value of the
  6498.       procedure.
  6499.  
  6500.          z := stack_ptr^.data;
  6501.  
  6502.    2. Note that the current record at the front of the list must be deleted.
  6503.       The pointer temp points to this record, so that the procedure can use
  6504.       temp to delete the record later on.
  6505.  
  6506.          temp := stack_ptr;
  6507.  
  6508.    3. Then the pointer to the top of the stack, stack_ptr, is moved so it
  6509.       points to stack_ptr^.next_rec. Note that stack_ptr points to the top
  6510.       record in the list, which is a record with two fields: the data field
  6511.       and the next_rec field. The next_rec field in that top record currently
  6512.       points to the next record in the list. The statement
  6513.  
  6514.          stack_ptr := stack_ptr^.next_rec;
  6515.  
  6516.       moves stack_ptr to point to the same record that stack_ptr^.next_rec
  6517.       is pointing to. By assigning stack_ptr to point to the same record to
  6518.       which the top record was pointing, there is now no pointer in the list
  6519.       pointing to the record that was on top. The record has been removed
  6520.       from the list.
  6521.  
  6522.    4. The old record at the front of the list, pointed to by temp, is now
  6523.       deleted from memory.
  6524.  
  6525.          Dispose( temp );
  6526.  
  6527.  Figure 11.2 illustrates the pop procedure.
  6528.  
  6529.  Figure 11.2  The Pop Procedure
  6530.  
  6531.  Step 1   z := stack_ptr^.data;
  6532.                                  ┌───────────┐
  6533.                                ┌│     Z     │
  6534.                                │ └───────────┘
  6535.                   ┌───────────┐│ ┌───────────┐
  6536.                   │   data    ├┘ │   data    │
  6537.  ┌───────────┐ ┌─├───────────┤┌├───────────┤         ┌──────────┐
  6538.  │ stack_ptr ├─┘  │ next_rec  ├┘ │ next_rec  ├─ ∙∙∙ ─│   NIL    │
  6539.  └───────────┘    └───────────┘  └───────────┘         └──────────┘
  6540.  
  6541.  Step 2   temp := stack_ptr;
  6542.                   ┌───────────┐  ┌───────────┐
  6543.                   │   data    │  │   data    │
  6544.  ┌───────────┐ ┌─├───────────┤┌├───────────┤         ┌──────────┐
  6545.  │ stack_ptr ├─┤  │ next_rec  ├┘ │ next_rec  ├─ ∙∙∙ ─│   NIL    │
  6546.  └───────────┘ │  └───────────┘  └───────────┘         └──────────┘
  6547.  ┌───────────┐ │
  6548.  │   temp    ├─┘
  6549.  └───────────┘
  6550.  
  6551.  Step 3   stack_ptr := stack_ptr^.next_rec;
  6552.               ┌────────────────┐
  6553.               │   ┌───────────┐│ ┌───────────┐
  6554.               │   │   data    ││ │   data    │
  6555.  ┌───────────┐│┌─├───────────┤├├───────────┤         ┌──────────┐
  6556.  │ stack_ptr ├┘│  │ next_rec  ├┘ │ next_rec  ├─ ∙∙∙ ─│   NIL    │
  6557.  └───────────┘ │  └───────────┘  └───────────┘         └──────────┘
  6558.  ┌───────────┐ │
  6559.  │   temp    ├─┘
  6560.  └───────────┘
  6561.  
  6562.  Step 4   Dispose (temp) ;
  6563.               ┌────────────────┐
  6564.               │  ┌ ─ ─ ─ ─ ─ ┐ │ ┌───────────┐
  6565.               │                │ │   data    │
  6566.  ┌───────────┐│  │           │ └├───────────┤         ┌──────────┐
  6567.  │ stack_ptr ├┘                  │ next_rec  ├─ ∙∙∙ ─│   NIL    │
  6568.  └───────────┘   └ ─ ─ ─ ─ ─ ┘   └───────────┘         └──────────┘
  6569.  
  6570.  
  6571.  11.4  Binary Trees
  6572.  
  6573.  Binary trees are one of the many types of tree structures that can be
  6574.  created with pointer variables. Binary trees are more complex than the
  6575.  linked lists described in the previous section. Each record (called a
  6576.  "node") has not one, but two pointers. Each of these pointers connects the
  6577.  node to two other nodes (called "children")──one on its left and one on its
  6578.  right.
  6579.  
  6580.  Figure 11.3 shows a sample binary tree. As you can see, a left child always
  6581.  contains a smaller value than the "parent node," and the right child
  6582.  contains a value larger than the parent node. Moreover, the entire subtree
  6583.  of a given node contains smaller values than the parent, if on the left
  6584.  side, or larger values than the parent, if on the right side. This
  6585.  organization permits efficient searching for any value in the tree.
  6586.  
  6587.  
  6588.  Figure 11.3  A Binary Tree
  6589.  
  6590.                                 Root Node
  6591.                                  ┌─────────┐
  6592.                          ┌───────┤   702   ├─────────┐
  6593.                          │       └─────────┘         │
  6594.                     ┌────┴────┐                 ┌────┴────┐
  6595.            ┌────────┤   659   │                 │   833   ├────────┐
  6596.            │        └─────────┘                 └─────────┘        │
  6597.       ┌────┴────┐                                             ┌────┴────┐
  6598.       │   100   ├────────┐                                    │   853   │
  6599.       └─────────┘        │                                    └─────────┘
  6600.                     ┌────┴────┐
  6601.            ┌────────┤   362   ├────────┐
  6602.            │        └─────────┘        │
  6603.       ┌────┴────┐                 ┌────┴────┐
  6604.       │   239   │        ┌────────┤   640   │
  6605.       └─────────┘        │        └─────────┘
  6606.                     ┌────┴────┐
  6607.            ┌────────┤   622   │
  6608.            │        └─────────┘
  6609.       ┌────┴────┐
  6610.       │   619   │
  6611.       └─────────┘
  6612.  
  6613.  
  6614.  This sample binary tree contains only simple arithmetic nodes. However, you
  6615.  can create binary trees containing any kind of information (as long as each
  6616.  node is the same type). You can also organize or sort items in the tree
  6617.  using a variety of methods.
  6618.  
  6619.  To build a binary tree, first declare the following types:
  6620.  
  6621.       TYPE
  6622.           node_ptr = ^node;
  6623.           node = RECORD
  6624.               left,
  6625.               right : node_ptr;
  6626.               data  : Word;
  6627.               END;
  6628.  
  6629.  Similar to the record type of a linked list, the Node type contains pointers
  6630.  that point to other records of the same type. If the node currently lacks a
  6631.  left or right child, the corresponding pointer field should be initialized
  6632.  to NIL.
  6633.  
  6634.  To create a tree, first declare a pointer to the start of the tree and
  6635.  initialize this pointer to NIL,  as shown below. Although no node currently
  6636.  exists in the tree, one will be inserted eventually. The first node will
  6637.  become the original ancestor of all other nodes in the tree.
  6638.  
  6639.       VAR
  6640.           root_ptr : node_ptr;
  6641.       BEGIN
  6642.           root_ptr := NIL;
  6643.  
  6644.  You can start building the tree by inserting values, which can be generated
  6645.  in a number of ways──standard input, a data file, or with the Random
  6646.  function. Each time you get a value, add it to the tree by using the
  6647.  following five steps:
  6648.  
  6649.    1. Examine root_ptr, the pointer to the first node of the tree. The
  6650.       expression root_ptr^ is equivalent to the first node, if one exists. If
  6651.       no node exists, root_ptr is equal to NIL.
  6652.  
  6653.    2. If the pointer is NIL, add a new record and assign the pointer to point
  6654.       to this record. Load the data field of the new record with the value to
  6655.       be added.
  6656.  
  6657.    3. If the pointer is not NIL, then a node exists and must be compared to
  6658.       the new value. Compare the value to the data field of the node.
  6659.  
  6660.    4. If the new value is less than the one at the node, use the left field
  6661.       as the new pointer value, and go to step 2.
  6662.  
  6663.    5. If the new value is greater than the one at the node, use the right
  6664.       field as the new pointer value, and go to step 2.
  6665.  
  6666.  You can use a recursive procedure to implement these steps. Linked lists and
  6667.  trees are often good subjects for recursive solutions. As explained in
  6668.  Chapter 3, "Procedures and Functions," a "recursive" procedure presents a
  6669.  simplified algorithm by calling itself repeatedly. In the case of binary
  6670.  trees, a recursive procedure traces left and right branches repeatedly until
  6671.  it finds a matching value at the end of the tree.
  6672.  
  6673.  In the case of the steps discussed above, you initially call the procedure
  6674.  by passing the value you want to insert and root_ptr as arguments. The
  6675.  procedure calls itself to trace the left or right subtree, each time passing
  6676.  the left or right field as the pointer argument, as follows:
  6677.  
  6678.       PROCEDURE insert( x : Integer; VAR ptr : node_ptr );
  6679.           BEGIN
  6680.               IF (ptr = NIL) THEN
  6681.                   create_node( x, ptr )
  6682.               ELSE IF (x <ptr^.data) THEN
  6683.                   insert( x, ptr^.left )
  6684.               ELSE
  6685.                   insert( x, ptr^.right );
  6686.           END;
  6687.  
  6688.  Notice how short the above procedure is. The actual work of inserting the
  6689.  node is the last step of the process, and it is carried out by a separate
  6690.  procedure written just for that purpose:
  6691.  
  6692.       PROCEDURE create_node( x : Integer; VAR ptr : node_ptr );
  6693.           BEGIN
  6694.               New( ptr );
  6695.               ptr^.data  := x;
  6696.               ptr^.left  := NIL;
  6697.               ptr^.right := NIL;
  6698.           END;
  6699.  
  6700.  The initialization of the left and right fields to NIL is critical.
  6701.  Otherwise, the insert procedure produces unpredictable results when it
  6702.  reaches the end of the tree and attempts a comparison. The procedures above
  6703.  effectively use NIL as the end-of-the-tree indicator.
  6704.  
  6705.  
  6706.  Chapter 12  Advanced Topics
  6707.  ───────────────────────────────────────────────────────────────────────────
  6708.  
  6709.  This chapter gives you a look inside QuickPascal. You can accomplish most
  6710.  any standard programming task by using the techniques presented in prior
  6711.  chapters. This chapter helps you deal with special situations, such as
  6712.  running out of dynamic memory, analyzing internal data formats, and linking
  6713.  to assembly language.
  6714.  
  6715.  The bitwise operators are of special interest to assembly-language
  6716.  programmers, but are useful even if you don't use assembly language. You can
  6717.  use the bitwise operators to mask out bits within an integer, manipulate
  6718.  individual bits, or test a variable to see which bits are on.
  6719.  
  6720.  After presenting the bitwise operators, the chapter shows a general picture
  6721.  of how QuickPascal organizes memory. Then the chapter illustrates internal
  6722.  data formats and explains how to link to assembly language.
  6723.  
  6724.  
  6725.  12.1  The Bitwise Operators
  6726.  
  6727.  You can access and manipulate bits by using the standard Boolean operators
  6728.  and the shift operators.
  6729.  
  6730.  The logical operators NOT, AND, OR, and XOR work as bitwise operators when
  6731.  you use them with integer types. Bitwise operations take two data items of
  6732.  the same size and compare each bit in one operand to the corresponding bit
  6733.  in the other. For example, consider the following statement:
  6734.  
  6735.  Result := $FF00 AND $9055; QuickPascal implements this statement by
  6736.  comparing each bit in the constant $FF00 to each corresponding bit in the
  6737.  constant $9055. The AND operator sets a bit in the result to 1, if and only
  6738.  if the corresponding bits in both operands have a value of 1:
  6739.  
  6740.                 $FF00 = 1111 1111  0000 0000
  6741.            AND  $90FF = 1001 0000  1111 1111
  6742.  
  6743.       Result    $9000 = 1001 0000  0000 0000
  6744.  
  6745.  The result of the operation is $9000. In the example above, using the AND
  6746.  operator with the constant $FF00 in effect masks out the low 8 bits of a
  6747.  16-bit integer. You can create other constants to selectively mask out any
  6748.  combination of bits. For example, you can use the AND operator to test
  6749.  whether a value is a multiple of four by masking out all but the lowest two
  6750.  bits and determining whether the result is zero:
  6751.  
  6752.       IF (x AND $0003 = 0) THEN
  6753.           Writeln( 'x is multiple of 4.' );
  6754.  
  6755.  Conversely, you can use the OR operator to set specific bits to 1. All of
  6756.  the bitwise operators work in a similar way. The following list shows how
  6757.  each operator works:
  6758.  
  6759.  Operator                    Sets a bit to 1 if:
  6760.  
  6761.     NOT                      The corresponding bit in operand is 0. (This
  6762.                              operator takes just one operand.)
  6763.  
  6764.     AND                      Both corresponding bits in the operands have the
  6765.                              value 1.
  6766.  
  6767.     OR                       Either one of the corresponding bits in the
  6768.                              operand has the value 1.
  6769.  
  6770.     XOR                      Either one, but not both, of the corresponding
  6771.                              bits has the value 1.
  6772.  
  6773.  The AND, OR, and XOR operators all take two operands each. With each of
  6774.  these operators, the two integers you specify must be of the same type. For
  6775.  all the bitwise operators, the integer operands may be 8, 16, or 32 bits
  6776.  long. (Thus, operations with LongInt types are valid.)
  6777.  
  6778.  The SHL and SHR operators take an integer operand and move the bits by the
  6779.  number of positions specified by the second operand. For example, the binary
  6780.  number for 12 is
  6781.  
  6782.       $0C = 00001100
  6783.  
  6784.  When you execute the statement 12 SHR 2, each of the bits is moved two
  6785.  positions to the right, and the result looks like this:
  6786.  
  6787.       $03 = 00000011
  6788.  
  6789.  The result of the shift is the number 3. Note that shifting right by two is
  6790.  equivalent to dividing by 4. Left and right shifts are equivalent to
  6791.  multiplying and dividing by a power of two.
  6792.  
  6793.  The general syntaxes for SHL and SHR are
  6794.  
  6795.       IntVar SHL NumPositions
  6796.       IntVar SHR NumPositions
  6797.  
  6798.  The result is always of the same type as IntVar. The NumPositions argument
  6799.  determines the number of bit positions to shift. If this number is equal to
  6800.  or larger than the number of bits in IntVar, the result is always zero.
  6801.  
  6802.  
  6803.  12.2  QuickPascal Memory Map
  6804.  
  6805.  The memory map described in this section shows how a QuickPascal program
  6806.  uses memory. This information can help you develop strategies for very large
  6807.  programs that may run out of memory quickly.
  6808.  
  6809.  When you execute a QuickPascal program, it uses all available memory
  6810.  (subject to the limits imposed by the {$M} compiler directive as described
  6811.  below). Figure 12.1 shows the general layout of memory in a QuickPascal
  6812.  program, and the rest of the section explains the meaning of items in this
  6813.  layout.
  6814.  
  6815.  Figure 12.1  Quick Pascal Memory Map
  6816.  
  6817.                                Top of DOS Memory
  6818.                         ┌─────────────────────────────┐
  6819.                         │ Free List (grows downwards) │
  6820.                         ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤──── Free_Ptr
  6821.                         │                             │
  6822.                         │         Free Memory         │
  6823.                         │                             │
  6824.                         ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤──── Heap_Ptr
  6825.                         │                             │
  6826.                         │    Heap (grows upwards)     │
  6827.                         │                             │
  6828.                         ├─────────────────────────────┤──── HeapOrg
  6829.                         │   Stack (grows downwards)   │
  6830.                         ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤──── SSeg:SPtr
  6831.                         │          Free Stack         │
  6832.                         ├─────────────────────────────┤──── SSeg:0000
  6833.                         │      Global Variables       │
  6834.                       ┌─├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
  6835.                       │ │      Typed Constants        │
  6836.                       │ ├─────────────────────────────┤──── DSeg:0000
  6837.                       │ │  System Unit Code Segment   │
  6838.                       │ ├─────────────────────────────┤
  6839.                       │ │   First Unit Code Segment   │
  6840.                       │ ├─────────────────────────────┤
  6841.        Contents of    │
  6842.        an executable ─┤ │  Other Unit Code Segments   │
  6843.        file image     │
  6844.                       │ ├─────────────────────────────┤
  6845.                       │ │   Last Unit Code Segment    │
  6846.                       │ ├─────────────────────────────┤
  6847.                       │ │                             │
  6848.                       │ │  Main Program Code Segment  │
  6849.                       │ │                             │
  6850.                       └─├─────────────────────────────┤
  6851.                         │ Program Segment Prefix (PSP)│
  6852.                         └─────────────────────────────┘──── PrefixSeg
  6853.  
  6854.  
  6855.  The solid lines in Figure 12.1 show demarcations between segments. A
  6856.  "segment" is an area of memory that can be up to 64K in length. Thus, as you
  6857.  can see from Figure 12.1, the code segment of the main program can never be
  6858.  more than 64K. However, for very large programs, you can surpass the 64K
  6859.  limit by simply adding additional units. The amount of code for each unit
  6860.  can be as large as 64K.
  6861.  
  6862.  All global variables and typed constants are placed in a single segment.
  6863.  Thus, the total size of these data items cannot exceed 64K across all units.
  6864.  
  6865.  The stack is placed in its own segment. You can set the stack size with the
  6866.  {$M} compiler directive. Increasing the stack lets the program accommodate
  6867.  more procedure calls, but may take away from memory available for the heap.
  6868.  (See Appendix B, "Compiler Directives.")
  6869.  
  6870.  The heap consists of the rest of available RAM, unless you use the {$M}
  6871.  compiler directive to set maximum heap size. The heap contains all dynamic
  6872.  variables allocated through calls to New or GetMem. If you know in advance
  6873.  that your program must have a certain amount of heap space to run properly,
  6874.  you can use the {$M} compiler directive to specify a minimum heap size. DOS
  6875.  will not load the program unless it can allocate enough memory for the
  6876.  requested minimum heap size.
  6877.  
  6878.  Certain variables appear in the QuickPascal memory map. These are public
  6879.  variables declared in the System unit, and your program can access them at
  6880.  any time (no special declaration is needed to use the System unit).
  6881.  
  6882.  The following list describes the public variables appearing in the memory
  6883.  map:
  6884.  
  6885.  Variable  Description
  6886.  
  6887.  FreePtr                     Pointer to beginning of "free list," (as
  6888.                              described in Section 12.3). The free list is a
  6889.                              series of records that describes the size and
  6890.                              location of free mem-ory blocks inside the heap.
  6891.                              As the free list grows, FreePtr decreases
  6892.                              because the free list grows downward.
  6893.  
  6894.  HeapPtr                     Pointer to top of heap. As the amount of space
  6895.                              needed by dynamic variables increases, HeapPtr
  6896.                              increases. It decreases only when memory is
  6897.                              freed from the top of the heap.
  6898.  
  6899.  HeapOrg                     Pointer to beginning of heap. This variable has
  6900.                              the generic type POINTER, as do the other
  6901.                              pointers declared in the System unit. You cannot
  6902.                              dereference pointers of this type, but you can
  6903.                              assign the value of such a pointer to any other
  6904.                              pointer, regardless of type.
  6905.  
  6906.  PrefixSeg                   Word variable containing segment address of
  6907.                              Program Segment Prefix (PSP). When DOS loads
  6908.                              your program, it constructs the PSP to store
  6909.                              command-line arguments, interrupt vectors, and
  6910.                              other information. See the MS-DOS Programmer's
  6911.                              Reference or MS-DOS Encyclopedia for more
  6912.                              information.
  6913.  
  6914.  
  6915.  12.3  Managing the Heap
  6916.  
  6917.  The simplest way to create dynamic variables is to make calls to New and
  6918.  GetMem. In small programs, you can generally call these procedures without
  6919.  worrying about how the heap is managed. However, if your programs make heavy
  6920.  use of dynamic memory, you can sometimes avoid running out of memory by
  6921.  knowing how the heap works.
  6922.  
  6923.  The basic model of the heap is simple. Initially, the New and GetMem
  6924.  procedures simply increment HeapPtr by the size of the memory block you
  6925.  request and return a pointer to the beginning of the block.
  6926.  
  6927.  The structure of the heap becomes more complicated when you start freeing
  6928.  blocks of memory. If you free memory at the current top of the heap, HeapPtr
  6929.  is decreased by the amount of memory freed. More often, however, the block
  6930.  to be freed is somewhere in the middle of the heap. In this case, the
  6931.  free-memory procedure (Dispose or FreeMem) adds a record to the free list to
  6932.  record the existence of the freed block.
  6933.  
  6934.  The free block is then like a hole in the middle of the heap. The next time
  6935.  you request memory, the allocation procedure attempts to allocate memory
  6936.  from a free block if it can. Furthermore, if a block is freed adjacent to an
  6937.  existing free block, the two blocks form one larger free block, and the free
  6938.  list is updated to reflect this fact.
  6939.  
  6940.  The maximum number of free blocks that Pascal programs can maintain is
  6941.  8,192. Programs rarely reach this limit. However, you can use a variety of
  6942.  techniques to manage the heap, each of which is explained in a section
  6943.  below:
  6944.  
  6945.    ■  Use the Mark and Release functions to free memory efficiently
  6946.  
  6947.    ■  Determine the amount of heap space left and the number of records in
  6948.       the free list
  6949.  
  6950.    ■  Set the FreeMin variable to prevent a deadlock between the heap and the
  6951.       free list
  6952.  
  6953.    ■  Write a customized HeapError function to control how the program
  6954.       responds when you run out of heap space (the default action is to abort
  6955.       execution)
  6956.  
  6957.  
  6958.  12.3.1  Using Mark and Release to Free Memory
  6959.  
  6960.  The Mark and Release procedures provide a simpler and more efficient way to
  6961.  free memory than Dispose or FreeMem. However, the use of Mark and Release
  6962.  requires that you release memory in the reverse order to that in which you
  6963.  allocated it. Not all programs can adhere to this requirement.
  6964.  
  6965.  The Mark procedure sets a pointer to point to the current top of the heap.
  6966.  The pointer can have any base type. Then if you allocate more dynamic
  6967.  memory, the new memory is higher in memory than the marked location (because
  6968.  the heap grows upward).
  6969.  
  6970.  The Release procedure takes a single pointer as an argument, just as Mark
  6971.  does, and releases all dynamic memory above the pointer. In other words, it
  6972.  releases all memory allocated after the Mark procedure was called. The
  6973.  Release procedure basically works by setting HeapPtr to the value of the
  6974.  pointer argument.
  6975.  
  6976.  For example, the following code allocates five dynamic variables:
  6977.  
  6978.       New( PtrA );
  6979.       New( PtrB );
  6980.       Mark( Ptrs );
  6981.       New( PtrC );
  6982.       New( PtrD );
  6983.  
  6984.  The next line of code releases the pointers declared after the Mark( Ptrs )
  6985.  statement. Specifically, it frees the memory pointed to by PtrC and PtrD:
  6986.  
  6987.       Release( Ptrs );
  6988.  
  6989.  Mark and Release impose significant limitations on program logic because
  6990.  they do not let you randomly free any block of memory; you can only free
  6991.  contiguous blocks at the top of the heap. However, if you use these
  6992.  procedures, you have none of the problems that sometimes occur with a free
  6993.  list.
  6994.  
  6995.  Note that the Mark and Release procedures are not compatible with Dispose
  6996.  and FreeMem. Once you use one of the latter two procedures, you should not
  6997.  call Release.
  6998.  
  6999.  
  7000.  12.3.2  Determining Free Memory and Size of the Free List
  7001.  
  7002.  Two functions let you determine the amount of free memory available:
  7003.  
  7004.    ■  The MemAvail function returns the total number of free bytes in the
  7005.       heap.
  7006.  
  7007.    ■  The MaxAvail function returns the size of the largest single free block
  7008.       in the heap──in other words, the largest amount of memory that you can
  7009.       successfully request.
  7010.  
  7011.  Generally, the MaxAvail function is the more useful of the two functions,
  7012.  since it lets you know whether any given call to New or GetMem will be
  7013.  successful. Both the MemAvail and MaxAvail functions return a LongInt
  7014.  result, and neither takes any parameters.
  7015.  
  7016.  Determining the size of the free list also helps you discover if you are
  7017.  going to run out of memory. The free list is declared as follows:
  7018.  
  7019.       TYPE
  7020.           FreeRec = RECORD
  7021.               OrgPtr, EndPtr : Pointer;
  7022.               END;
  7023.  
  7024.           FreeList = ARRAY [0..8190] OF FreeRec;
  7025.           FreeListP = ^FreeList;
  7026.  
  7027.  Each record in the free list defines a single free memory block; the  OrgPtr
  7028.  and EndPtr fields define the beginning and ending address, respectively, of
  7029.  the block. EndPtr points to the first byte after the end of the block. You
  7030.  can calculate the number of free blocks recorded in the free list by using
  7031.  the following statement:
  7032.  
  7033.       free_blocks := (8192 - Ofs( FreePtr^ ) DIV 8) MOD 8192;
  7034.  
  7035.  When the offset portion of the address in FreePtr is 0, the free list is
  7036.  empty. As a free block is added to the list, FreePtr decreases by eight
  7037.  bytes, and the free list also grows downward by eight.
  7038.  
  7039.  
  7040.  12.3.3  Preventing Deadlock with the Free List
  7041.  
  7042.  If the top of the heap is very close to the bottom of the free list, then
  7043.  deadlock can occur when you try to free blocks of memory. If the blocks to
  7044.  be freed are at the top of the heap, there is no problem. Otherwise, the
  7045.  free list is blocked from expanding.
  7046.  
  7047.  This problem always causes a run-time error. You can neither allocate new
  7048.  dynamic memory nor free any existing memory. The only way to prevent this
  7049.  situation is to set a minimum amount of space between the locations pointed
  7050.  to by HeapPtr and FreePtr.
  7051.  
  7052.  The FreeMin system variable sets the minimum free memory space (between the
  7053.  top of the heap and bottom of the free list). This variable has type Word
  7054.  and represents a distance in bytes. The New and FreeMem procedures will not
  7055.  allocate a block of memory if doing so would make the space between the heap
  7056.  and the free list fall below FreeMin. The MemAvail and MaxAvail functions
  7057.  also take FreeMin into account by subtracting it from the free memory area.
  7058.  
  7059.  
  7060.  12.3.4  Writing a Heap-Error Function
  7061.  
  7062.  By default, the New and GetMem procedures simply abort program execution
  7063.  when they cannot return a memory block of the requested size. However, you
  7064.  can create a customized function to respond to heap errors.
  7065.  
  7066.  Your heap-error function is called by New and GetMem as needed, and it can
  7067.  return one of three values:
  7068.  
  7069.  Value                  Meaning
  7070.  
  7071.  0                      Failure; abort program
  7072.  
  7073.  1                      Failure; return the NIL pointer value to the
  7074.                         caller of New or GetMem, but do not abort program
  7075.  
  7076.  2                      Memory successfully freed; authorize New or
  7077.                         GetMem to retry
  7078.  
  7079.  Your function should return the value 2 only if it was able to find a
  7080.  disposable dynamic variable in your program and free the necessary memory.
  7081.  If the function returns 2 without freeing memory first, then it will be
  7082.  called again.
  7083.  
  7084.  To create a heap-error function, first declare a function similar to the one
  7085.  below:
  7086.  
  7087.       {$F+}
  7088.       FUNCTION heap_err( size : Word ) : Integer;
  7089.       {$F-}
  7090.       BEGIN
  7091.           { Cause FreeMem to return NIL but continue execution }
  7092.           heap_err := 1;
  7093.       END;
  7094.  
  7095.  You can give the function any name you want and supply any statements in the
  7096.  body of the function. However, the rest of the declaration should be the
  7097.  same. In particular, the {$F+} compiler directive is necessary to make sure
  7098.  the function is compiled for far calls. The single parameter of type Word
  7099.  gives the size of the memory block that New or GetMem failed to allocate.
  7100.  
  7101.  After declaring your heap-error function, set the HeapError system variable
  7102.  to point to your function:
  7103.  
  7104.       HeapError := @heap_err;
  7105.  
  7106.  
  7107.  12.4  Internal Data Formats
  7108.  
  7109.  Knowledge of internal data formats is useful if you want to write
  7110.  assembly-language procedures that access data from QuickPascal programs.
  7111.  Furthermore, you can use this knowledge to help determine what data types
  7112.  most efficiently store information for a given program.
  7113.  
  7114.  Section 12.4.1 describes the data formats for all types except
  7115.  floating-point numbers. The floating-point data types are described
  7116.  separately in Section 12.4.2, because of their complexity.
  7117.  
  7118.  
  7119.  12.4.1  Non-Floating-Point Data Types
  7120.  
  7121.  The list that follows describes the internal formats of most data types,
  7122.  including structured variables such as arrays and records. Note that any
  7123.  given type is stored the same way, whether it is a simple variable or part
  7124.  of a record.
  7125.  
  7126.  Where an integer value is designated as signed, QuickPascal uses the two's
  7127.  complement format for storing negative numbers. This format represents the
  7128.  negative of a number by logically negating each bit (as is done by the NOT
  7129.  operator) and then adding 1. For example, since 00000011 represents 3, then
  7130.  11111100 is the logical negation, and 11111101 is the two's complement of 3.
  7131.  
  7132.  Therefore, if you declare a Byte constant as -3, QuickPascal stores this
  7133.  value as 11111101. Generally speaking, you never have to carry out
  7134.  two's-complement conversion yourself. It is simply an internal format
  7135.  understood by both Quick-Pascal and the 8086 family of processors.
  7136.  
  7137.  As a consequence, all nonnegative numbers have 0 in the most significant
  7138.  bit; all negative numbers have 1 in the most significant bit. The unsigned
  7139.  formats do not consider any number to be negative.
  7140.  
  7141. ╓┌───────────────────────────┌───────────────────────────────────────────────╖
  7142.  Data Type                   Internal Format
  7143.  
  7144.  Char                        An unsigned byte. (Values range from 0 to 255.)
  7145.  
  7146.  Byte                        A signed byte. (Values range from -128 to 127.)
  7147.  
  7148.  Integer                     A signed word. (Values range from -32768 to
  7149.                              32767.)
  7150.  
  7151.  Word                        An unsigned word. (Values range from 0 to
  7152.                              65535.)
  7153.  
  7154.  LongInt                     A signed double word. (Values range from
  7155.                              -2147483648 to 2147483647.)
  7156.  
  7157.  Comp                        A signed eight-byte integer. If the
  7158.                              most-significant bit is 1 and the other bits are
  7159.                              all 0, this type has the special value NAN (Not
  7160.                              a Number). Note that with the {N+} directive,
  7161.                              QuickPascal uses the 8087 coprocessor, if
  7162.                              installed, to do calculations with Comp types.
  7163.  Data Type                   Internal Format
  7164.  
  7165.                             installed, to do calculations with Comp types.
  7166.  
  7167.  Boolean                     A byte assuming the value 0 (False) or 1 (True).
  7168.  
  7169.  Enumerated types            Stored as an unsigned byte if the type can
  7170.                              assume 256 values or fewer. Otherwise, the
  7171.                              enumerated type is stored as an unsigned word.
  7172.                              The first item in an enumerated-type definition
  7173.                              corresponds to the value 0, and the rest are
  7174.                              numbered sequentially in the order given.
  7175.  
  7176.  STRING types                A sequence of bytes in which the first byte
  7177.                              stores the current length of the string (which
  7178.                              may be less than its maximum length), and the
  7179.                              rest of the bytes store the string data.
  7180.                              QuickPascal allocates enough space for a
  7181.                              string's maximum length plus one additional byte
  7182.                              (for the length indicator). The generic STRING
  7183.                              type is equivalent to STRING[255], which has the
  7184.  Data Type                   Internal Format
  7185.  
  7186.                             type is equivalent to STRING[255], which has the
  7187.                              maximum limit.
  7188.  
  7189.  CSTRING                     Similar to STRING types, except that the first
  7190.                              byte is not a length indicator, and the string
  7191.                              data includes a terminating null byte. Maximum
  7192.                              limit is 255 + null bytes. The generic CSTRING
  7193.                              type defaults to this length.
  7194.  
  7195.  SET types                   Stored as a bit array, in which each bit
  7196.                              corresponds to a specific element. If a bit is
  7197.                              on, then the corresponding element is present.
  7198.                              QuickPascal allocates one byte for each element,
  7199.                              up to a maximum of 32 bytes (256 elements).
  7200.                              Elements within a set are stored in order of
  7201.                              their ordinal value, in which the first element
  7202.                              is stored in the least-significant bit.
  7203.  
  7204.  ARRAY types                 Elements of the array are stored contiguously in
  7205.  Data Type                   Internal Format
  7206.  
  7207. ARRAY types                 Elements of the array are stored contiguously in
  7208.                              memory, starting from the lowest-indexed
  7209.                              component and going in sequence to the highest.
  7210.                              In multidimensional arrays, the rightmost index
  7211.                              changes fastest. Thus, in the array declared as
  7212.                              Grid[Rows,Cols], all elements for an entire row
  7213.                              are stored next to each other in memory.
  7214.  
  7215.  RECORD types                A sequence of variables stored contiguously in
  7216.                              memory (the fields appear in memory in the same
  7217.                              order they do in source code). With variant
  7218.                              records, each variant case starts at the same
  7219.                              address.
  7220.  
  7221.  Pointer types               A double word that contains the offset address
  7222.                              in the low word and the segment address in the
  7223.                              high word. The special value NIL is a generic
  7224.                              pointer containing the value zero.
  7225.  
  7226.  
  7227.  12.4.2  Floating-Point Data Types
  7228.  
  7229.  Figure 12.2 displays the formats used to represent floating-point numbers of
  7230.  different levels of precision. QuickPascal uses these data formats whether
  7231.  or not a math coprocessor is installed. If a coprocessor is not installed,
  7232.  then Quick-Pascal provides procedures to execute floating-point
  7233.  calculations. In cases of rounding, these procedures are not guaranteed to
  7234.  produce precisely the same results as a coprocessor.
  7235.  
  7236.  Figure 12.2  QuickPascal Data Formats
  7237.  
  7238.  Real Type
  7239.     Width   1                    39                         8
  7240.           ┌───┬──────────────────────────────────────┬─────────────┐
  7241.           │ s │                  f                   │      e      │
  7242.           └───┴──────────────────────────────────────┴─────────────┘
  7243.     Bits   47                                                     0
  7244.  
  7245.  Single Type
  7246.     Width   1        8                           23
  7247.           ┌───┬─────────────┬──────────────────────────────────────┐
  7248.           │ s │      e      │                    f                 │
  7249.           └───┴─────────────┴──────────────────────────────────────┘
  7250.     Bits   31                                                     0
  7251.  
  7252.  Double Type
  7253.     Width   1        11                          52
  7254.           ┌───┬─────────────┬──────────────────────────────────────┐
  7255.           │ s │      e      │                    f                 │
  7256.           └───┴─────────────┴──────────────────────────────────────┘
  7257.     Bits   63                                                     0
  7258.  
  7259.  Extended Type
  7260.     Width   1        15     1                    63
  7261.           ┌───┬───────────┬───┬────────────────────────────────────┐
  7262.           │ s │      e    │ i │                  f                 │
  7263.           └───┴───────────┴───┴────────────────────────────────────┘
  7264.     Bits   79                                                     0
  7265.  
  7266.  Comp Type
  7267.     Width   1                             63
  7268.           ┌───┬────────────────────────────────────────────────────┐
  7269.           │ s │                           d                        │
  7270.           └───┴────────────────────────────────────────────────────┘
  7271.     Bits   63                                                     0
  7272.  
  7273.  
  7274.  With each of the formats illustrated in Figure 12.2, the sign bit (s)
  7275.  indicates a negative number if on, and a nonnegative number if off. The
  7276.  fractional part (f) indicates the fractional part of the mantissa. To save
  7277.  space, the data formats all assume that the units' digit in the mantissa is
  7278.  equal to 1. Finally, the exponent is adjusted downward by a different number
  7279.  for each format. This means that for the four-byte Single type, the value
  7280.  represented is equal to
  7281.  
  7282.       -1^(s) * 1.f * 2^(e-127)
  7283.  
  7284.  For example, assume a variable of this type contains the following values:
  7285.  
  7286.       s = 0
  7287.       e = 127  decimal
  7288.       f = 11110100000000000000000  binary
  7289.  
  7290.  The value of the variable is 1.111101 binary. Note that (in contrast to
  7291.  Figure 12.2 and the example above) data storage on 8086 processors actually
  7292.  runs from least-significant bit (stored lowest in memory) to
  7293.  most-significant bit. This fact may make bytes appear to be backward if you
  7294.  use a debugging tool to dump a word at a time.
  7295.  
  7296.  The exponents are automatically reduced by 129 for the six-byte Real format,
  7297.  by 1,023 for the Double format, and by 16,383 for the Extended format. This
  7298.  automatic reduction lets the exponent field represent a negative number
  7299.  (producing a floating-point value between 1 and -1).
  7300.  
  7301.  In each format, if all bits are set to zero, then the resulting
  7302.  floating-point number is equal to zero.
  7303.  
  7304.  
  7305.  12.5  Linking to Assembly Language
  7306.  
  7307.  You can link QuickPascal programs to modules written with the Microsoft
  7308.  Macro Assembler and other assemblers. However, be forewarned that
  7309.  Quick-Pascal has a number of conventions and restrictions that differ from
  7310.  other Microsoft languages. When you write an assembly-language procedure to
  7311.  link to QuickPascal, your assembly-language module can include
  7312.  
  7313.    ■  Procedures and functions that the main program can call
  7314.  
  7315.    ■  References to global variables or data from a unit declared in an
  7316.       INTERFACE statement
  7317.  
  7318.    ■  Static data that is private to your assembly-language module
  7319.  
  7320.  However, the assembly-language module cannot declare public data for the
  7321.  QuickPascal code to reference. Furthermore, an assembly-language module must
  7322.  place all instructions in a single segment named CODE, and cannot use the
  7323.  GROUP directive. (Segment conventions are described in Section 12.5.2.)
  7324.  Sections 12.5.1-12.5.6 list steps for writing an assembly-language module,
  7325.  in roughly the order that you would observe them in writing the code. You
  7326.  should read all of these sections carefully before attempting to write an
  7327.  assembly-language procedure (although the section on return values is
  7328.  optional and applies only when you write a function). Section 12.5.7 has a
  7329.  simple, but complete, example.
  7330.  
  7331.  
  7332.  12.5.1  Setting Up a Link to Assembly Language
  7333.  
  7334.  To link to assembly language, you must write an EXTERNAL declaration for
  7335.  each assembly-language procedure you're going to call and use the {$L}
  7336.  compiler directive to link in the object file.
  7337.  
  7338.  To write an EXTERNAL declaration, simply write a procedure or function
  7339.  heading as you would normally and follow the heading with the keyword
  7340.  EXTERNAL as shown below:
  7341.  
  7342.       FUNCTION compute( a, b, c: Integer ) : Integer; EXTERNAL;
  7343.       PROCEDURE switcheroo( VAR a, b, c : Byte ); EXTERNAL;
  7344.  
  7345.  Place the {$L} compiler directive at the beginning of your QuickPascal main
  7346.  program. The {$L} directive takes a single argument: the base file name of
  7347.  an object file. Note that you do not use LINK or any other utility to
  7348.  produce a program. Instead, QuickPascal sets up the link by copying the
  7349.  object file into the program and changing the file into its own internal
  7350.  object-code format. (The disk-based copy of the object file is not affected,
  7351.  however.)
  7352.  
  7353.  
  7354.  12.5.2  Segment and Data Conventions
  7355.  
  7356.  Observe the following conventions when declaring segments and data:
  7357.  
  7358.    ■  Place all executable statements in a segment named CODE or CSEG. Place
  7359.       all data declarations in a segment named DATA or DSEG. All other
  7360.       segments are ignored, as are group statements.
  7361.  
  7362.    ■  The segments can have the BYTE or WORD align type, but QuickPascal will
  7363.       always give segments word alignment.
  7364.  
  7365.    ■  Do not specify class names.
  7366.  
  7367.    ■  Declare PUBLIC each procedure that you want to call from QuickPascal.
  7368.       However, QuickPascal ignores any PUBLIC data declarations.
  7369.  
  7370.    ■  Do not initialize variables in the DATA segment. (Variables must be
  7371.       initialized with instructions.)
  7372.  
  7373.    ■  You can access data declared by the main program (or declared in an
  7374.       INTERFACE statement) by declaring it with the EXTRN directive. However,
  7375.       you cannot use the HIGH or LOW operators with external data. See
  7376.       Section 12.4, "Internal Data Formats," for information on how to
  7377.       evaluate each data type.
  7378.  
  7379.  
  7380.  12.5.3  Entering the Procedure
  7381.  
  7382.  The entry sequence for QuickPascal procedures is the same as that for other
  7383.  Microsoft languages. The first two instructions set up the BP register as
  7384.  the "framepointer," which points to the stack area of the current procedure.
  7385.  All parameters and local variables are available through BP:
  7386.  
  7387.       push     bp
  7388.       mov      bp, sp
  7389.       sub      sp, local_size
  7390.  
  7391.  In the code above, local_size is a placeholder for the total size of local
  7392.  parameters you wish to use. This last step is entirely optional. Your
  7393.  procedure may be able to use registers to hold all temporary values. If not,
  7394.  you can use locations on the stack to hold data. (Specifically, these
  7395.  locations are below the address pointed to by BP, but no more than
  7396.  local_size bytes below this address.)
  7397.  
  7398.  Procedures called by QuickPascal must preserve the values of the BP, SP, SS,
  7399.  and DS registers. BP and SP are preserved by the standard entry and exit
  7400.  code. If you need to alter DS or SS, you must push their values onto the
  7401.  stack and pop them just before returning as shown below:
  7402.  
  7403.       push     bp
  7404.       mov      bp, sp
  7405.       sub      sp, local_size
  7406.       push     ds
  7407.  
  7408.  
  7409.  12.5.4  Accessing Parameters
  7410.  
  7411.  All parameters and local variables are accessed as indirect memory operands
  7412.  using the BP register. To determine the location of each parameter, you need
  7413.  to understand the QuickPascal calling conventions. This section summarizes
  7414.  those conventions and gives some examples with specific procedures.
  7415.  
  7416.  QuickPascal pushes each parameter onto the stack──in the order that the
  7417.  parameters appear in the source code──before making the actual call.
  7418.  Consequently, the first parameter is highest in memory. (Recall that the
  7419.  stack grows downward.) You can pass parameters by value or by reference. In
  7420.  Pascal, VAR parameters are passed by reference; other parameters are passed
  7421.  by value.
  7422.  
  7423.  When a parameter is passed by reference, QuickPascal pushes a four-byte
  7424.  pointer to the data. The offset portion of the pointer is always pushed
  7425.  first and is therefore higher in memory.
  7426.  
  7427.  When a parameter is passed by value, Pascal takes different actions
  7428.  depending on the data type:
  7429.  
  7430.    ■  If the value parameter is Char, Boolean, any Pointer, any integer, or
  7431.       any floating-point type, QuickPascal pushes the parameter onto the
  7432.       stack.
  7433.  
  7434.    ■  If the parameter is a string or set type, QuickPascal passes a pointer
  7435.       to the data. This action is really the same as passing by reference. If
  7436.       you want to avoid any possibility of altering the data, make a
  7437.       temporary copy of the data, then work with the temporary copy.
  7438.       (QuickPascal procedures use this technique.)
  7439.  
  7440.    ■  If the parameter is an array or record type, QuickPascal pushes the
  7441.       variable directly onto the stack if it is no more than four bytes long.
  7442.       Otherwise, it pushes a pointer to the data.
  7443.  
  7444.  This calling convention for value parameters is not shared by other
  7445.  Microsoft high-level languages, which always push a value parameter directly
  7446.  onto the stack.
  7447.  
  7448.  Note that the PUSH instruction can push only one word of data at a time.
  7449.  QuickPascal always pushes the most-significant portion first, consistent
  7450.  with the 8086-processor convention that the most-significant portion is
  7451.  stored highest in memory.
  7452.  
  7453.  As noted in the next section, the QuickPascal code sometimes places an extra
  7454.  parameter on the stack for functions. (This extra parameter is a pointer to
  7455.  the location of the return value.)
  7456.  
  7457.  Finally, the QuickPascal code calls the procedure with a CALL instruction.
  7458.  Calls to external procedures are always far, so you should declare your
  7459.  assembly procedure with the FAR keyword.
  7460.  
  7461.  As an example, consider the following procedure call:
  7462.  
  7463.       PROCEDURE quad( VAR x : Real; a, b, c : Integer ); EXTERNAL;
  7464.       .
  7465.       .
  7466.       .
  7467.       quad( result, 2, 3, 4 );
  7468.  
  7469.  QuickPascal implements the call by placing the following items on the stack,
  7470.  and in this order:
  7471.  
  7472.    1. The segment address of result
  7473.  
  7474.    2. The offset address of result
  7475.  
  7476.    3. The value 2 (as a word)
  7477.  
  7478.    4. The value 3
  7479.  
  7480.    5. The value 4
  7481.  
  7482.    6. The segment of the return address (the address to return to when done)
  7483.  
  7484.    7. The offset of the return address
  7485.  
  7486.  A far CALL instruction places the last item, the return address, on the
  7487.  stack. After you perform the entry sequence, the BP register points to the
  7488.  location just below the return address. Each parameter can then be accessed
  7489.  with the following equates:
  7490.  
  7491.       result  EQU     <[BP + 12]>
  7492.       a       EQU     <[BP + 10]>
  7493.       b       EQU     <[BP +  8]>
  7494.       c       EQU     <[BP +  6]>
  7495.  
  7496.  As another example, consider the procedure call
  7497.  
  7498.       PROCEDURE repeat_it( num : LongInt;
  7499.                            stuff : STRING ); EXTERNAL;
  7500.       .
  7501.       .
  7502.       .
  7503.       repeat_it( n, 'This is a message.' );
  7504.  
  7505.  With this procedure, QuickPascal implements the call by placing the
  7506.  following items on the stack:
  7507.  
  7508.    1. The most-significant word of the long integer n
  7509.  
  7510.    2. The least-significant word of n
  7511.  
  7512.    3. The segment address of the string data
  7513.  
  7514.    4. The offset address of the string data
  7515.  
  7516.    5. The segment of the return address
  7517.  
  7518.    6. The offset of the return address
  7519.  
  7520.  A far CALL instruction places the last two items on the stack. After
  7521.  writing the entry sequence, you can access the parameters with the following
  7522.  equates:
  7523.  
  7524.       num     EQU     <[BP + 10]>
  7525.       stuff   EQU     <[BP +  6]>
  7526.  
  7527.  
  7528.  12.5.5  Returning a Value
  7529.  
  7530.  This section affects functions only. From the standpoint of assembly code,
  7531.  functions are just procedures that have a return value. To return a value to
  7532.  Quick-Pascal code, follow these conventions:
  7533.  
  7534.    ■  For a six-byte Real type, place the result in DX:BX:AX, in which DX
  7535.       holds the most-significant word and AX the least.
  7536.  
  7537.    ■  For a string, CSTRING, Comp, or floating-point type other than Real,
  7538.       QuickPascal passes an additional parameter. This parameter is pushed
  7539.       first and is a pointer to a temporary storage location. Place the
  7540.       result of the function in this location.
  7541.  
  7542.    ■  For ordinal types (including Char, Boolean, and any integer), place the
  7543.       result in AL if one byte, AX if two bytes, and DX:AX if a double word
  7544.       (in which DX holds the most-significant byte).
  7545.  
  7546.    ■  QuickPascal does not support functions that return array or record
  7547.       types. (However, you can set the value of an array or record if
  7548.       QuickPascal passes it as a VAR parameter.)
  7549.  
  7550.  
  7551.  12.5.6  Exiting the Procedure
  7552.  
  7553.  To exit the procedure, first pop any registers you preserved on the stack.
  7554.  Then write the following instructions:
  7555.  
  7556.       mov  sp,bp
  7557.       pop  bp
  7558.       ret  param_size
  7559.  
  7560.  In the code given above, param_size is a placeholder for the total size of
  7561.  parameters in your procedure. This last instruction is necessary to
  7562.  completely restore the stack.
  7563.  
  7564.  
  7565.  12.5.7  A Complete Example
  7566.  
  7567.  The following example of linking QuickPascal programs to modules written in
  7568.  assembly language is simple, but complete. The Pascal source code contains
  7569.  the following statements:
  7570.  
  7571.       {$L SWITCH }
  7572.       PROCEDURE switch( VAR a, b : Word ); EXTERNAL;
  7573.       .
  7574.       .
  7575.       .
  7576.       switch( x, y );
  7577.  
  7578.  Since the parameters are VAR parameters, QuickPascal passes pointers to the
  7579.  parameters. Then, the assembly code must use indirect register operands to
  7580.  access the data. The following procedure exchanges the values of the two
  7581.  variables.
  7582.  
  7583.       ; SWITCH.ASM
  7584.       ;
  7585.       CODE    SEGMENT WORD PUBLIC
  7586.  
  7587.               ASSUME  CS:CODE
  7588.  
  7589.               PUBLIC  switch       ; Make procedure public
  7590.  
  7591.       a       EQU    <[BP + 10]>    ; Parameters for switch
  7592.       b       EQU    <[BP + 6]>     ;  procedure
  7593.  
  7594.       switch  PROC    FAR
  7595.               push    bp           ; Entry sequence
  7596.               mov     bp,sp
  7597.               push    ds
  7598.  
  7599.               lds     si, a        ; Load ptr into ds:si
  7600.               les     di, b        ; Load ptr into es:di
  7601.               mov     bx, es:[di]  ; temp = b
  7602.               xchg    bx, [si]     ; Switch temp and a
  7603.               xchg    bx, es:[di]  ; Switch temp and b
  7604.  
  7605.               pop     ds
  7606.               mov     sp,bp        ; Exit sequence
  7607.               pop     bp
  7608.               ret     8            ; Total of 8 bytes in
  7609.                                    ;  parameters
  7610.       switch  ENDP
  7611.  
  7612.  
  7613.  ───────────────────────────────────────────────────────────────────────────
  7614.  Part 3  Graphics and Objects
  7615.  
  7616.  Part 3 of Pascal by Example covers QuickPascal support of graphics, fonts,
  7617.  and object-oriented programming. Each of these topics requires a moderate
  7618.  degree of experience with Pascal. You need to be comfortable with all of the
  7619.  topics in Part 1 and may find familiarity with Part 2 helpful.
  7620.  
  7621.  The graphics and fonts chapters show you how to use the QuickPascal graphics
  7622.  unit to create colorful and informative screen displays. The chapter on
  7623.  object-oriented programming introduces the fundamental concepts of
  7624.  programming with objects and how to implement those concepts in QuickPascal.
  7625.  
  7626.  
  7627.  Chapter 13  Using Graphics
  7628.  ───────────────────────────────────────────────────────────────────────────
  7629.  
  7630.  This chapter explains how to call graphics routines that set points, draw
  7631.  lines, change colors, and draw shapes such as rectangles and circles. The
  7632.  first section describes the general structure of any graphics program,
  7633.  defines important graphics terms, and works through an example program step
  7634.  by step, showing how to use the basic routines. Subsequent sections explain
  7635.  video modes, coordinate systems, and animation.
  7636.  
  7637.  To run any graphics programs, your computer must have graphics capability.
  7638.  The QuickPascal graphics facility supports the Color Graphics Adapter (CGA),
  7639.  the Enhanced Graphics Adapter (EGA), and the Video Graphics Adapter (VGA)
  7640.  video modes available on IBM(R) and IBM-compatible computers. The graphics
  7641.  facility also supports the Olivetti(R) (and AT&T(R)) enhanced video modes
  7642.  and the Hercules(R) monochrome graphics mode.
  7643.  
  7644.  This chapter discusses the techniques for writing graphics programs but does
  7645.  not cover every graphics procedure and function (there are more than 75).
  7646.  You can explore additional topics by using the QP Advisor and the example
  7647.  programs. Be sure to check the README.DOC file for any last-minute changes
  7648.  or additions.
  7649.  
  7650.  
  7651.  13.1  Getting Started with Graphics
  7652.  
  7653.  The subject of graphics and color display on computers is fairly complex.
  7654.  This chapter and the next one, "Using Fonts," provide an introduction to
  7655.  graphics and fonts. The next sections cover common graphics terms and list
  7656.  sources for more information on graphics.
  7657.  
  7658.  
  7659.  13.1.1  Graphics Terms
  7660.  
  7661.  There are several concepts you need to know before you can create graphics
  7662.  programs. The following list explains the most useful terms:
  7663.  
  7664.    ■  The "x axis" determines the horizontal position on the screen. The
  7665.       "origin" (point 0, 0) is in the upper left corner. The maximum number
  7666.       of horizontal "pixels" (picture elements) varies from 320 to 640 to
  7667.       720, depending on the graphics card installed and the graphics mode in
  7668.       effect.
  7669.  
  7670.    ■  The "y axis" is the vertical position on the screen. The origin is the
  7671.       upper left corner. The number of vertical pixels ranges from 200 to
  7672.       480.
  7673.  
  7674.    ■  Each graphics mode offers a "palette" from which you may choose the
  7675.       colors to be displayed. You may have access to 2, 4, 8, 16, or 256
  7676.       "color indexes," depending on the graphics card in the computer and the
  7677.       graphics mode in effect. The color is the index to the palette of
  7678.       colors displayable by a particular graphics adapter.
  7679.  
  7680.    ■  The CGA modes offer four fixed palettes containing predefined colors
  7681.       that may not be changed. In EGA, MCGA, and VGA graphics modes, you may
  7682.       change any of the color indexes by providing a color value that
  7683.       describes the mix of colors you wish to use.
  7684.  
  7685.    ■  A color index is always a short integer. A color value is always a long
  7686.       integer. When you're calling graphics functions that require
  7687.       color-related parameters, you should be aware of the difference between
  7688.       color indexes and color values.
  7689.  
  7690.  
  7691.  13.1.2  For More Information
  7692.  
  7693.  The following books cover a variety of graphics topics that you may find
  7694.  useful. They are listed only for your convenience. With the exception of its
  7695.  own publications, Microsoft does not endorse these books or recommend them
  7696.  over others on the same subject.
  7697.  
  7698.    ■  Artwick, Bruce. Microcomputer Displays, Graphics and Animation.
  7699.       Englewood Cliffs, NJ: Prentice-Hall, Inc., 1985.
  7700.  
  7701.            Discussion of microcomputer graphics and animation from the
  7702.            creator of Microsoft Flight Simulator.
  7703.  
  7704.    ■  Kliewer, Bradley D. EGA/VGA: A Programmer's Reference Guide. New York,
  7705.       NY: Intertext Publications, Inc., 1988.
  7706.  
  7707.            A detailed discussion of the EGA and VGA video modes.
  7708.  
  7709.    ■  Norton, Peter, and Richard Wilton. The New Peter Norton Programmer's
  7710.       Guide to the IBM PC and PS/2. Redmond, WA: Microsoft Press, 1985.
  7711.  
  7712.            The standard guide to the inside of the IBM PC and PS/2 families
  7713.            of computers. Several chapters are devoted to video modes.
  7714.  
  7715.    ■  Wilton, Richard. Programmer's Guide to PC and PS/2 Video Systems.
  7716.       Redmond, WA: Microsoft Press, 1987.
  7717.  
  7718.            An advanced guide to all the PC and PS/2 video modes.
  7719.  
  7720.  
  7721.  13.2  Writing Your First Graphics Program
  7722.  
  7723.  In QuickPascal, all graphics programs must follow these six basic steps:
  7724.  
  7725.    1. Use the MSGraph unit.
  7726.  
  7727.    2. Set a video mode.
  7728.  
  7729.    3. Determine the video parameters.
  7730.  
  7731.    4. Set up a coordinate system.
  7732.  
  7733.    5. Create and display graphics.
  7734.  
  7735.    6. Restore initial video configuration and exit the program.
  7736.  
  7737.  A simple graphics program, 1STGRAPH.PAS, is shown below. In the next few
  7738.  pages, this program will be dissected into the six required elements of a
  7739.  graphics program.
  7740.  
  7741.       PROGRAM FirstGraphics;
  7742.       { 1STGRAPH.PAS: Demonstrates basic graphics program structure }
  7743.  
  7744.       USES
  7745.           MSGraph;
  7746.  
  7747.       VAR
  7748.           a, i : integer;
  7749.           vc   : _VideoConfig;
  7750.  
  7751.       BEGIN  { Begin main program. }
  7752.  
  7753.           _ClearScreen( _GClearScreen);
  7754.  
  7755.       { Set the highest resolution video mode and check for success }
  7756.           a := _SetVideoMode( _MaxResMode );
  7757.  
  7758.       IF ( a = 0 ) THEN
  7759.               BEGIN
  7760.                 Writeln( 'No valid graphics mode; hit RETURN to continue' );
  7761.                 Readln;
  7762.                 a := _SetVideoMode( _DefaultMode );
  7763.                 Halt ( 0 );
  7764.               END;
  7765.  
  7766.           { Find out some screen characteristics. }
  7767.           _GetVideoConfig( vc );
  7768.           Writeln( 'mode: ',vc.Mode );
  7769.           Writeln( 'Horizontal resolution: ', vc.NumXPixels );
  7770.           Writeln( 'Vertical resolution: ', vc.NumYPixels );
  7771.           Writeln( 'Number of colors: ', vc.NumColors );
  7772.  
  7773.           { Draw a colored rectangle and ellipse in lower left quadrant. }
  7774.           _SetColor( 4 );
  7775.           _Rectangle( _GBorder, 0, vc.NumYPixels - 1,
  7776.                       vc.NumXPixels DIV 4,
  7777.                       vc.NumYPixels * 3 DIV 4 );
  7778.           _Ellipse( _GBorder, 0, vc.NumYPixels - 1,
  7779.                     vc.NumXPixels DIV 4,
  7780.                     vc.NumYPixels * 3 DIV 4 );
  7781.  
  7782.     { Draw a line from the corner of the rectangle to the screen center. }
  7783.           _SetColor( 3 );
  7784.           _MoveTo( vc.NumXPixels DIV 4, vc.NumYPixels * DIV 4 );
  7785.           _LineTo( vc.NumXPixels DIV 2, vc.NumYPixels DIV 2 );
  7786.  
  7787.           { Wait for RETURN key and then restore video mode. }
  7788.           Readln;
  7789.           _SetVideoMode(_DefaultMode);
  7790.       END.
  7791.  
  7792.  Using the MSGraph Unit
  7793.  
  7794.  The first step in creating 1STGRAPH.PAS is to use the MSGraph unit within
  7795.  your QuickPascal program. The MSGraph unit contains the constants, data
  7796.  types, procedures, and functions used in graphics programs. QuickPascal
  7797.  units are explained in Chapter 7, "Units." The MSGraph unit is the library
  7798.  of graphics features you need to access from your program.
  7799.  
  7800.  The USES section of your program calls MSGraph. This section appears
  7801.  immediately after the PROGRAM declaration line and looks like this:
  7802.  
  7803.       USES
  7804.           MSGraph;
  7805.  
  7806.  Note that some components of the MSGraph unit are incompatible with some
  7807.  functions within the Crt unit. Some of the Crt unit routines that deal with
  7808.  the display (such as HighVideo or DirectVideo) conflict with the graphics
  7809.  functions.
  7810.  
  7811.  The following Crt routines are safe to use with the MSGraph unit:
  7812.  
  7813.       AssignCRT       NoSound     WhereY
  7814.       Delay           ReadKey     Window
  7815.       GotoXY          Sound
  7816.       KeyPressed      WhereX
  7817.  
  7818.  Setting the Video Mode
  7819.  
  7820.  Before you can start drawing pictures on the screen, your program must tell
  7821.  the graphics adapter to switch from text mode to graphics mode. Call
  7822.  _SetVideoMode, passing it a predefined constant that tells it which mode to
  7823.  display. The constants listed in Table 13.1 are defined in the MSGraph
  7824.  unit. The dimensions are listed in columns for video text modes and in
  7825.  pixels for graphics mode.
  7826.  
  7827.  Table 13.1  Constants Set by _SetVideoMode
  7828.  
  7829.  Constant                Video Mode                        Mode Type/Hardware
  7830.  
  7831.  _DefaultMode            Restores to original mode         Both/All
  7832.  _MaxResMode             Highest resolution                Graphics/All
  7833.  _MaxColorMode           Maximum colors                    Graphics/All
  7834.  _TextBW40               40 column text, 16 gray           Text/CGA
  7835.  _TextC40                40 column text, 16/8 color        Text/CGA
  7836.  _TextBW80               80 column text, 16 gray           Text/CGA
  7837.  _TextC80                80 column text, 16/8 color        Text/CGA
  7838.  _MRes4Color             320 X 200, 4 color                Graphics/CGA
  7839.  _MResNoColor            320 X 200, 4 gray                 Graphics/CGA
  7840.  _HResBW                 640 X 200, BW                     Graphics/CGA
  7841.  _TextMono               80 column text, BW                Text/MDPA
  7842.  _HercMono               720 X 348, BW for HGC             Graphics/HGC
  7843.  _MRes16Color            320 X 200, 16 color               Graphics/EGA
  7844.  _HRes16Color            640 X 200, 16 color               Graphics/EGA
  7845.  _EResNoColor            640 X 350, BW                     Graphics/EGA
  7846.  _EResColor              640 X 350, 4 or 16 color          Graphics/EGA
  7847.  _VRes2Color             640 X 480, BW                     Graphics/VGA/MCGA
  7848.  _VRes16Color            640 X 480, 16 color               Graphics/VGA
  7849.  _MRes256Color           320 X 200, 256 color              Graphics/VGA/MCGA
  7850.  _OResColor              640 X 400, 1 of 16 colors         Graphics/Olivetti
  7851.  
  7852.  These video modes are described more fully in Section 13.3.
  7853.  
  7854.  The special modes _MaxResMode and _MaxColorMode are used when you simply
  7855.  want QuickPascal to determine the highest resolution or maximum color mode
  7856.  available.
  7857.  
  7858.  If the _SetVideoMode function returns a 0, it means the hardware does not
  7859.  support the selected mode. You may continue to select alternate video modes
  7860.  until a nonzero value is returned. If the hardware configuration doesn't
  7861.  support any of the selected video modes, then exit the program.
  7862.  
  7863.  The segment of example program 1STGRAPH.PAS below sets the video mode for
  7864.  maximum resolution and then checks for success. If the function
  7865.  _SetVideoMode fails (and returns a 0), the program prints a message,
  7866.  restores the video mode, and halts.
  7867.  
  7868.       { Set the highest resolution mode and check for success }
  7869.       a := _SetVideoMode( _MaxResMode );
  7870.       IF ( a = 0 ) THEN
  7871.           BEGIN
  7872.           Writeln( 'No valid graphics mode; hit RETURN to continue' );
  7873.           Readln;
  7874.            a := _SetVideoMode( _DefaultMode );
  7875.           Halt (0);
  7876.           END;
  7877.  
  7878.  Determining the Video Parameters
  7879.  
  7880.  After entering graphics mode, you can check the current video configuration.
  7881.  This is necessary when you have used the constant _MaxResMode or
  7882.  _MaxColorMode, since you do not know the specifics of the video mode
  7883.  selected.
  7884.  
  7885.  The video configuration requires a special structure called _VideoConfig,
  7886.  which is defined in the MSGraph unit. The _GetVideoConfig procedure finds
  7887.  the current video configuration information.
  7888.  
  7889.  The _VideoConfig structure contains the following elements:
  7890.  
  7891.       { Structure for GetVideoConfig }
  7892.  
  7893.         _VideoConfig = RECORD
  7894.           NumXPixels : Integer; { Horizontal resolution }
  7895.           NumYPixels : Integer; { Vertical resolution }
  7896.           NumTextCols : Integer; { Number of text columns available }
  7897.           NumTextRows : Integer; { Number of text rows available }
  7898.           NumColors : Integer; { Number of actual colors }
  7899.           BitsPerPixel : Integer; { Number of bits per pixel }
  7900.           NumVideoPages : Integer; { Number of available video pages }
  7901.           Mode : Integer; { Current video mode }
  7902.           Adapter : Integer; { Active display adapter }
  7903.           Monitor : Integer; { Active display monitor }
  7904.           Memory : Integer; { Adapter video memory in K bytes }
  7905.         END;
  7906.  
  7907.  The variables within the _VideoConfig structure are initialized when you
  7908.  call _GetVideoConfig. In the following example program, the video mode is
  7909.  set to _MaxResMode. The program then calls _GetVideoConfig to determine the
  7910.  screen dimensions (in pixels) and the maximum number of colors allowed.
  7911.  
  7912.       { Find out some screen characteristics. }
  7913.       _GetVideoConfig( ,vc );
  7914.       Writeln( 'mode: 'vc.Mode );
  7915.       Writeln( 'Horizontal resolution: ', vc.NumXPixels );
  7916.       Writeln( 'Vertical resolution: ', vc.NumYPixels );
  7917.       Writeln( 'Number of colors: ', vc.NumColors );
  7918.  
  7919.  Setting Up Coordinate Systems
  7920.  
  7921.  The third step in writing a QuickPascal graphics program is to establish the
  7922.  "coordinate system."
  7923.  
  7924.  A coordinate system is used to identify a pixel location relative to a
  7925.  horizontal and vertical axis. In a graphics mode, each pixel on the screen
  7926.  can be located by means of a unique pair of coordinates. The QuickPascal
  7927.  graphics unit supports the following coordinate systems:
  7928.  
  7929.    ■  Text coordinates
  7930.  
  7931.    ■  Physical coordinates
  7932.  
  7933.    ■  Viewport coordinates
  7934.  
  7935.    ■  Window coordinates
  7936.  
  7937.  Since this example graphics program does not specify otherwise, the
  7938.  coordinate system used is that provided by the physical screen dimensions.
  7939.  This is known as the "physical-coordinate system."
  7940.  
  7941.  But how much screen is available to work with? There might be 720 X 348, 640
  7942.  X 480, 640 X 400, 640 X 350, or 640 X 200 pixels. The _VideoConfig structure
  7943.  elements NumXPixels and NumYPixels provide the screen dimensions. On a VGA
  7944.  card, a request for maximum resolution would set the screen to the
  7945.  _VRes16Color mode. The horizontal resolution is 640 pixels and vertical
  7946.  resolution is 480. The horizontal resolution might be 640, but the pixels
  7947.  are numbered 0-639.
  7948.  
  7949.  The physical-coordinate system places the origin (or the coordinate pair
  7950.  (0,0)) at the upper left corner of the screen. Increasing positive values of
  7951.  the x coordinate extend from left to right. Increasing positive values of
  7952.  the y coordinate extend from top to bottom.
  7953.  
  7954.  The physical-coordinate system is dependent on the hardware and display
  7955.  configuration and cannot be changed. The other coordinate systems and the
  7956.  procedures used to translate between them are described in Section 13.4.
  7957.  
  7958.  Drawing Graphics
  7959.  
  7960.  The next few lines in this example program draw a rectangle and an ellipse
  7961.  and then a line to the center of the screen. Prior to drawing the rectangle
  7962.  or the line, the graphics color is set to two different values. The program
  7963.  segment that draws the rectangle and ellipse is shown below:
  7964.  
  7965.       { Draw a colored rectangle and ellipse in lower left quadrant. }
  7966.       _SetColor( 4 );
  7967.       _Rectangle( _GBorder, 0, vc.NumYPixels - 1,
  7968.                   vc.NumXPixels DIV 4, vc.NumYPixels * 3 DIV 4 );
  7969.       _Ellipse( _GBorder, 0, vc.NumYPixels - 1,
  7970.                 vc.NumXPixels DIV 4, vc.NumYPixels * 3 DIV 4 );
  7971.  
  7972.  The call to the_Rectangle procedure has five arguments. The first argument
  7973.  is the "fill flag," which may be either _GBorder or_GFillInterior. The fill
  7974.  flag determines whether the figure  will be drawn with just the border or as
  7975.  a solid figure. Choose _GBorder if you want a rectangle of four lines (a
  7976.  border only, in the current line style). Or you can choose _GFillInterior if
  7977.  you want a solid rectangle (filled in with the current color and fill
  7978.  pattern).
  7979.  
  7980.  The second and third arguments are the x and y coordinates of one corner of
  7981.  the rectangle. The fourth and fifth arguments are the coordinates of the
  7982.  opposite corner. The coordinates for the two corners are defined in terms of
  7983.  the measurements obtained from _GetVideoConfig. As a result, this program
  7984.  draws the rectangle correctly in any graphics mode.
  7985.  
  7986.  The _Ellipse procedure draws an ellipse on the screen. Its parameters
  7987.  resemble the parameters for _Rectangle. Both procedures require a fill flag
  7988.  and two corners of a "bounding rectangle." When the ellipse is drawn, four
  7989.  points touch the edges of the bounding rectangle. A bounding rectangle
  7990.  defines the space in which a rounded figure is drawn. For a rounded figure,
  7991.  the center of the bounding rectangle defines the center of the rounded
  7992.  figure. A bounding rectangle is defined by the coordinates of the upper left
  7993.  corner and the lower right corner of the rectangle.
  7994.  
  7995.  When the program first enters a graphics mode or sets a viewport (as
  7996.  discussed in Section 13.4), the drawing position is set to the center of
  7997.  the screen. Since the _Rectangle procedure takes the corners of the
  7998.  rectangle as parameters, there is no need to set the drawing position. But
  7999.  after the figures are drawn, the program moves the current drawing position
  8000.  (with the _MoveTo procedure) and draws a line from the new position to the
  8001.  center of the screen:
  8002.  
  8003.       { Draw a line from the corner of the rectangle to the screen center. }
  8004.       _SetColor( 3 );
  8005.       _MoveTo( vc.NumXPixels DIV 4, vc.NumYPixels * 3 DIV 4 );
  8006.       _LineTo( vc.NumXPixels DIV 2, vc.NumYPixels DIV 2 );
  8007.  
  8008.  Again, the coordinates are given in terms of NumXPixels and NumYPixels to
  8009.  ensure portability across video modes.
  8010.  
  8011.  Restoring Initial Video Configuration and Exiting
  8012.  
  8013.  The final step in any graphics program is to restore the default video mode.
  8014.  In the sample program, the Readln procedure waits for the RETURN key. The
  8015.  _SetVideoMode function restores the screen to the default mode, which sets
  8016.  the screen back to normal.
  8017.  
  8018.       { Wait for RETURN key and then restore the video mode. }
  8019.       Readln;
  8020.       a := _SetVideoMode(_DefaultMode);
  8021.  
  8022.  
  8023.  13.3  Using Video Modes
  8024.  
  8025.  The QuickPascal MSGraph unit provides support for the following video
  8026.  adapters and displays:
  8027.  
  8028.    ■  Monochrome Display Printer Adapter (MDPA)
  8029.    ■  Hercules-compatible graphics adapters
  8030.    ■  CGA
  8031.    ■  EGA
  8032.    ■  VGA
  8033.    ■  MCGA
  8034.    ■  Olivetti-compatible graphics adapters (including AT&T 6300 series)
  8035.  
  8036.  The sections that follow explain how to select a video mode, then discuss
  8037.  the major CGA, EGA, and VGA graphics modes. A complete listing of the
  8038.  QuickPascal video modes that can be set by the _SetVideoMode function is
  8039.  shown in Table 13.1.
  8040.  
  8041.  ───────────────────────────────────────────────────────────────────────────
  8042.  NOTE
  8043.     If you use a Hercules graphics card, you must run the MSHERC.COM program
  8044.     before attempting to display any graphics in _HercMono mode. If your
  8045.     computer also has a color graphics card, you must run MSHERC.COM with the
  8046.     /H (/HALF) option to set the Hercules graphics card in HALF mode.
  8047.     Otherwise, the results will be unpredictable.
  8048.  ───────────────────────────────────────────────────────────────────────────
  8049.  
  8050.  The _VideoConfig structure, which is returned by _GetVideoConfig, includes
  8051.  fields that denote the type of graphics adapter and monitor in use. Table
  8052.  13.2 lists the constants for graphics adapters.
  8053.  
  8054.  Table  13.2  Constants for Graphics Adapters
  8055.  
  8056.  Constant      Value       Description
  8057.  
  8058.  _MDPA         0x0001      Monochrome Display Adapter
  8059.  _CGA          0x0002      Color Graphics Adapter
  8060.  _EGA          0x0004      Enhanced Graphics Adapter
  8061.  _VGA          0x0008      Video Graphics Array
  8062.  _MCGA         0x0010      MultiColor Graphics Array
  8063.  _HGC          0x0020      Hercules Graphics Card
  8064.  _OCGA         0x0042      Olivetti Color Graphics Adapter
  8065.  _OEGA         0x0044      Olivetti Enhanced Graphics Adapter
  8066.  _OVGA         0x0048      Olivetti Video Graphics Array
  8067.  
  8068.  Table 13.3  lists the constants for monitors.
  8069.  
  8070.  Table 13.3  Constants for Monitors
  8071.  
  8072.  Constant        Value              Description
  8073.  
  8074.  _Mono           0x0001             Monochrome
  8075.  _Color          0x0002             Color
  8076.  _ENHColor       0x0004             Enhanced Color
  8077.  _AnalogMono     0x0008             Analog Monochrome only
  8078.  _AnalogColor    0x0010             Analog Color only
  8079.  _Analog         0x0018             Analog Monochrome and Color modes
  8080.  
  8081.  
  8082.  13.3.1  Selecting a Video Mode
  8083.  
  8084.  Before you can display graphics, you must put the graphics adapter into a
  8085.  graphics mode. As shown in the example program, the _SetVideoMode function
  8086.  performs this task. Before calling _SetVideoMode, you must decide which
  8087.  graphics modes are acceptable for your purposes. There are several ways you
  8088.  can select an appropriate graphics mode:
  8089.  
  8090.    ■  Set the mode to a specified value. If you want the program to work in a
  8091.       high-resolution EGA color mode, use _SetVideoMode to set the mode to
  8092.       _EResColor:
  8093.  
  8094.          a := _SetVideoMode( _EResColor );
  8095.  
  8096.    ■  Request either the highest resolution available using the _MaxResMode
  8097.       constant or the greatest color selection, _MaxColorMode, available with
  8098.       your monitor and adapter configuration:
  8099.  
  8100.          { Selects highest resolution }
  8101.          a := _SetVideoMode( _MaxResMode );
  8102.          { or, selects most colors }
  8103.          a := _SetVideoMode( _MaxColorMode );
  8104.  
  8105.    ■  Specify a specific resolution mode (such as 320 x 200 pixels) by first
  8106.       determining the video adapter type (from the _VideoConfig structure)
  8107.       and then setting the appropriate mode for the adapter. The program
  8108.       fragment below shows how this technique can be used to set a 320 x 200
  8109.       pixel resolution mode.
  8110.  
  8111.  
  8112.     _GetVideoConfig( vc );    { Find out adapter type }
  8113.        IF (vc.Monitor = _Mono) THEN
  8114.           BEGIN
  8115.           Writeln( 'This program requires a color monitor.' );
  8116.           Halt( 0 );
  8117.           END
  8118.        ELSE
  8119.           CASE vc.Adapter OF
  8120.              _MDPA, _HGC :
  8121.                 BEGIN
  8122.                 Writeln( 'This program requires a color graphics adapter.' );
  8123.                 Halt( 0 );
  8124.                 END;
  8125.              _CGA, _OCGA : a := _SetVideoMode( _MRes4Color );
  8126.              _EGA, _OEGA : a := _SetVideoMode( _MRes16Color );
  8127.              _MCGA, _VGA : a := _SetVideoMode( _MRes256Color );
  8128.              END;
  8129.        { To get video configuration after mode is set }
  8130.        _GetVideoConfig( vc );
  8131.  
  8132.  
  8133.  This program fragment begins by calling _GetVideoConfig, which determines
  8134.  the graphics configuration and tells the type of adapter currently in use.
  8135.  If the monitor is monochrome, the program displays a message and halts.
  8136.  Next, the CASE statement enters the appropriate graphics mode. Finally, the
  8137.  program segment calls _GetVideoConfig to get the new configuration values
  8138.  after the mode has been set.
  8139.  
  8140.  To view every possible graphics mode, you can run the example program
  8141.  GRAPHIC.PAS, shown below. Sections 13.3.2-13.3.6 explain the various
  8142.  graphics modes.
  8143.  
  8144.       PROGRAM graphic;  { Displays every graphic mode }
  8145.  
  8146.       USES
  8147.           Crt, MSGraph;
  8148.       CONST
  8149.           modes : ARRAY [0..11] OF Integer =
  8150.                  (_MRes4Color, _MResNoColor, _HResBW, _HercMono,
  8151.                   _MRes16Color, _HRes16Color, _EResNoColor, _EResColor,
  8152.                   _VRes2Color, _VRes16Color, _MRes256Color, _OResColor );
  8153.       VAR
  8154.           vc : _VideoConfig;
  8155.           ch, key   : Char;
  8156.           which : Char;
  8157.           a : Integer;
  8158.  
  8159.       PROCEDURE print_menu;
  8160.       { Prints a menu on the screen }
  8161.       BEGIN
  8162.  
  8163.           Writeln( 'Please choose a graphics mode' );
  8164.           Writeln( 'Type "x" to exit' );
  8165.           Writeln;
  8166.           Writeln( '0  _MRES4COLOR' );
  8167.           Writeln( '1  _MRESNOCOLOR' );
  8168.           Writeln( '2  _HRESBW' );
  8169.           Writeln( '3  _HERCMONO' );
  8170.           Writeln( '4  _MRES16COLOR' );
  8171.           Writeln( '5  _HRES16COLOR' );
  8172.           Writeln( '6  _ERESNOCOLOR' );
  8173.           Writeln( '7  _ERESCOLOR' );
  8174.           Writeln( '8  _VRES2COLOR' );
  8175.           Writeln( '9  _VRES16COLOR' );
  8176.           Writeln( 'a  _MRES256COLOR' );
  8177.           Writeln( 'b  _ORESCOLOR' );
  8178.       END;
  8179.  
  8180.  
  8181.       PROCEDURE show_mode( which : Char );
  8182.       { Shows the different video modes. }
  8183.  
  8184.       VAR
  8185.           nc, i  : Integer;
  8186.           height : Integer;
  8187.           width  : Integer;
  8188.           mode   : STRING;
  8189.           r : Real;
  8190.           e,m : Integer;
  8191.  
  8192.       BEGIN
  8193.           mode := which;
  8194.           IF (mode < '0') OR (mode > '9') THEN
  8195.              IF mode = 'a' THEN
  8196.                  mode := '10'
  8197.              ELSE IF mode = 'b' THEN
  8198.                   mode := '11'
  8199.                  ELSE Halt; { Exit procedure }
  8200.           Val( mode, r, e );
  8201.           m := Trunc( r );
  8202.           a :=  _SetVideoMode( modes[m] );
  8203.           IF (a <> 0) THEN
  8204.               BEGIN
  8205.               _GetVideoConfig( vc );
  8206.               nc := vc.NumColors;
  8207.               width := vc.NumXPixels DIV nc;
  8208.               height := vc.NumYPixels DIV 2;
  8209.               FOR i := 1 TO ( nc - 1 ) DO
  8210.                   BEGIN
  8211.                   _SetColor( i );
  8212.                   _Rectangle( _GFillInterior, i * width,
  8213.                               0, ( i + 1 ) * width, height );
  8214.                   END;
  8215.               END { IF a not equal to 0 }
  8216.           ELSE
  8217.               BEGIN
  8218.               Writeln( 'Video mode ', which, ' is not available.' );
  8219.               Writeln( 'Please press ENTER.' );
  8220.               END;
  8221.  
  8222.           Readln;  { Wait for ENTER to be pressed }
  8223.           a := _SetVideoMode( _DefaultMode );
  8224.           print_menu;
  8225.       END;
  8226.  
  8227.  
  8228.       BEGIN  { Begin main program }
  8229.  
  8230.           key := ' ';
  8231.           _ClearScreen( _GClearScreen );
  8232.           print_menu;
  8233.           WHILE ( key <> 'x' ) DO
  8234.               BEGIN
  8235.               key := ReadKey;
  8236.               show_mode( key );
  8237.               END;
  8238.       END.
  8239.  
  8240.  
  8241.  13.3.2  CGA Color Graphics Modes
  8242.  
  8243.  The CGA color graphics modes _MRes4Color and _MResNoColor display four
  8244.  colors selected from one of several predefined palettes of colors. They
  8245.  display these foreground colors against a  background color that can be any
  8246.  one of the 16 available colors. With the CGA hardware, the palette of
  8247.  foreground colors is predefined and cannot be changed. Each palette number
  8248.  is an integer as shown in Table 13.4.
  8249.  
  8250.  Table 13.4  Available CGA Colors
  8251.  
  8252.  Palette     ┌────────────────Color Index────────────┐
  8253.  Number      1                   2                   3
  8254.  
  8255.  0           Green               Red                 Brown
  8256.  1           Cyan                Magenta             Light Gray
  8257.  2           Light Green         Light Red           Yellow
  8258.  3           Light Cyan          Light Magenta       White
  8259.  
  8260.  
  8261.  The _MResNoColor graphics mode produces palettes containing various shades
  8262.  of gray on black-and-white monitors. The _MResNoColor mode displays colors
  8263.  when used with a color display. However, only two palettes are available
  8264.  with a color display. You can use the _SelectPalette function to select one
  8265.  of these predefined palettes. Table 13.5 shows the correspondence between
  8266.  the color indexes and the palettes.
  8267.  
  8268.  Table 13.5  CGA Colors:  _MResNoColor Mode
  8269.  
  8270.  Palette     ┌────────────────Color Index────────────┐
  8271.  Number      1                   2                   3
  8272.  
  8273.  0           Blue                Red                 Light Gray
  8274.  1           Light Blue          Light               Red White
  8275.  
  8276.  
  8277.  You may use the _SelectPalette function in conjunction with the _MRes4Color,
  8278.  _MResNoColor, and _OResColor graphics modes. To change palettes in other
  8279.  graphics modes, use the _RemapPalette or _RemapAllPalette routines.
  8280.  
  8281.  In _OResColor mode, you can choose one of 16 foreground colors by passing a
  8282.  value in the range 0-15 to the _SelectPalette function. The background color
  8283.  is always black.
  8284.  
  8285.  The following program sets the video mode to _MRes4Color and then cycles
  8286.  through background colors and palette combinations. It works on computers
  8287.  equipped with CGA, EGA, MCGA, or VGA cards. A color monitor is required.
  8288.  
  8289.       PROGRAM cga;
  8290.       { Demonstrates CGA colors }
  8291.  
  8292.       USES
  8293.           MSGraph;
  8294.  
  8295.       CONST
  8296.           bkcolor : ARRAY [0..7] OF LongInt = (_Black, _Blue, _Green, _Cyan,
  8297.                     _Red, _Magenta, _Brown, _White);
  8298.  
  8299.           bkcolor_name : ARRAY [0..7] OF STRING = ('BLACK  ', 'BLUE   ',
  8300.                          'GREEN  ', 'CYAN   ', 'RED    ', 'MAGENTA',
  8301.                          'BROWN  ', 'WHITE  ');
  8302.  
  8303.       VAR
  8304.           a, i, j, k : Integer;
  8305.  
  8306.       BEGIN  { Begin main program }
  8307.           a := _SetVideoMode( _MRes4Color );
  8308.           FOR i := 0 TO 3 DO
  8309.               BEGIN
  8310.               a := _SelectPalette( i );
  8311.               FOR k := 0 TO 7 DO
  8312.                   BEGIN
  8313.                   _SetBkColor( bkcolor[k] );
  8314.                   FOR j := 0 TO 3 DO
  8315.                       BEGIN
  8316.                       _SetTextPosition( 1, 1 );
  8317.                       Writeln( 'Background color: ', bkcolor_name[k] );
  8318.                       Writeln( '   Palette: ', i );
  8319.                       Writeln( '   Number: ', j );
  8320.                       _SetColor( j );
  8321.                       _Rectangle( _GFillInterior, 160, 100, 320, 200 );
  8322.                       Readln;  { Wait for ENTER to be pressed }
  8323.                       END; { for j }
  8324.                   END; { for k }
  8325.               END; { for i }
  8326.  
  8327.           a := _SetVideoMode( _DefaultMode ); { restore original palette }
  8328.       END.
  8329.  
  8330.  
  8331.  13.3.3  EGA, MCGA, and VGA Palettes
  8332.  
  8333.  The beginning of this chapter mentioned the difference between color indexes
  8334.  and color values. An analogy might make things clearer. Imagine a painter
  8335.  who owns 64 tubes of paint and a painter's palette that has room for only 16
  8336.  globs of paint at any one time. A painting created under these constraints
  8337.  could contain only 16 colors (selected from a total of 64). The EGA graphics
  8338.  modes (such as _EResColor) are similar: they have 16 color indexes chosen
  8339.  from a total of 64 color values.
  8340.  
  8341.  The color values available in the EGA, MCGA, and VGA palettes are not
  8342.  predetermined like the CGA palettes. Instead, the color values available in
  8343.  EGA, MCGA, and VGA palettes are created by a process of "color mixing" of
  8344.  red, green, and blue elements. The next two sections describe color mixing.
  8345.  
  8346.  VGA Color Mixing
  8347.  
  8348.  VGA offers the widest variety of color values: 262,144 (256K). Depending on
  8349.  the graphics mode, the VGA palette size may be 2, 16, or 256. When you
  8350.  select a color value, you specify a level of intensity ranging from 0-63 for
  8351.  each of the red, green, and blue color values. The long integer that defines
  8352.  a color value consists of four bytes (32 bits):
  8353.  
  8354.       MSB                             LSB
  8355.       zzzzzzzz zzBBBBBB zzGGGGGG zzRRRRRR
  8356.  
  8357.  The most-significant byte must contain all zeros. The two high bits in the
  8358.  remaining three bytes must also be 0. To mix a light red (pink), turn red
  8359.  all the way up, and mix in some green and blue:
  8360.  
  8361.       00000000 00100000 00100000 00111111
  8362.  
  8363.  To represent this value in hexadecimal, use the number $0020203F.
  8364.  
  8365.  For white, turn all the elements on; for black, set all elements to 0.
  8366.  
  8367.  EGA Color Mixing
  8368.  
  8369.  Mixing colors in EGA modes is similar to the mixing described above, but
  8370.  there are fewer intensities for the red, green, and blue components. In the
  8371.  modes that offer 64 colors, the R, G, and B values cover 2 bits and can
  8372.  range from 0-3. The long integer that defines an RGB color looks like this:
  8373.  
  8374.       MSB                             LSB
  8375.       zzzzzzzz zzBB???? zzGG???? zzRR????
  8376.  
  8377.  The bits marked z must be zeros and the bits marked with question marks can
  8378.  be any value. This format is used for compatibility with VGA color mixing.
  8379.  To form a pure red color value, you would use the constant $00000030. For
  8380.  cyan (blue plus green), use $00303000.
  8381.  
  8382.  
  8383.  13.3.4  EGA Color Graphics Modes
  8384.  
  8385.  If you have an EGA adapter, you should use the video mode _MRes16Color,
  8386.  _HRes16Color, or _EResColor for the best color-graphics results. The CGA
  8387.  modes also display on the EGA but with the lower CGA resolution and
  8388.  decreased color options.
  8389.  
  8390.  The _RemapPalette function assigns a new color value to a color index. For
  8391.  example, when you first enter an EGA graphics mode, color index 1 equals the
  8392.  color value blue. To reassign the pure red color value to color index 1, you
  8393.  could use this line:
  8394.  
  8395.       a := _RemapPalette( 1, $000030 );
  8396.  
  8397.  Or, use the symbolic constant _Red, which is defined using the MSGraph unit:
  8398.  
  8399.       a := _RemapPalette( 1, _Red );
  8400.  
  8401.  After this function call, any object currently drawn in color index 1
  8402.  instantly switches from blue to red.
  8403.  
  8404.  The first value is an Integer in the range 0-15 and the second value is a
  8405.  LongInt defined as a mixture of red, green, and blue (you may also use
  8406.  symbolic constants such as _Red).
  8407.  
  8408.  The _RemapAllPalette procedure changes all of the color indexes
  8409.  simultaneously. You pass it an array of color values. The first color value
  8410.  in the list becomes the new color associated with the color index 0.
  8411.  
  8412.  The number in a function call that sets the color (such as _SetColor) is an
  8413.  index into the palette of available colors. In the default text palette, an
  8414.  index of 1 refers to blue but the palette could be remapped to change index
  8415.  1 to any other available color. As a result, the color produced by that
  8416.  pixel value also changes. The number of color indexes depends on the number
  8417.  of colors supported by the current video mode.
  8418.  
  8419.  The _RemapPalette and _RemapAllPalette routines work in all modes but only
  8420.  with the EGA, MCGA, or VGA hardware. The _RemapPalette fails, returning a
  8421.  value of -1 when you attempt to remap a palette without the EGA, MCGA, or
  8422.  VGA hardware.
  8423.  
  8424.  The following program draws a rectangle with a red interior. In the default
  8425.  EGA palette, the color index 4 is red. This color index is changed to _Blue
  8426.  in this program.
  8427.  
  8428.       PROGRAM ega;
  8429.       { Demonstrates EGA/VGA palettes }
  8430.  
  8431.       USES
  8432.           MSGraph;
  8433.  
  8434.       CONST
  8435.           crlf = #13 + #10;
  8436.  
  8437.       VAR
  8438.           a : LongInt;
  8439.           m : Integer;
  8440.  
  8441.       BEGIN  { Begin main program }
  8442.  
  8443.           m := _SetVideoMode( _MaxColorMode );
  8444.           _SetColor( 4 );
  8445.           _Rectangle( _GFillInterior, 50, 50, 150, 150 );
  8446.  
  8447.           _SetTextPosition( 1, 1 );
  8448.           _OutText( 'Normal palette' + crlf );
  8449.           _OutText( 'Press ENTER' );
  8450.           Readln;
  8451.  
  8452.           a := _RemapPalette( 4, _Blue );
  8453.  
  8454.           _SetTextPosition( 1, 1 );
  8455.           _OutText( 'Remapped palette' + crlf );
  8456.           _OutText( 'Press ENTER' );
  8457.           Readln;
  8458.  
  8459.           a := _RemapPalette( 4, _Red );
  8460.  
  8461.           _SetTextPosition( 1, 1 );
  8462.           _OutText( 'Restored palette' + crlf );
  8463.           _OutText( 'Press ENTER to clear the screen' );
  8464.           Readln;
  8465.  
  8466.           _ClearScreen( _GClearScreen );
  8467.           m := _SetVideoMode( _DefaultMode );
  8468.       END.
  8469.  
  8470.  
  8471.  13.3.5  VGA Color Graphics Modes
  8472.  
  8473.  The VGA card adds graphics modes _VRes2Color, _VRes16Color, and
  8474.  _MRes256Color to your repertoire. EGA and CGA modes can also be used with
  8475.  the VGA hardware, but with either lower resolution or fewer color choices.
  8476.  
  8477.  The VGA color graphics modes operate with a range of 262,144 (256K) color
  8478.  values. The _VRes2Color graphics mode displays two colors, the _VRes16Color
  8479.  graphics mode displays 16 colors, and the _MRes256Color graphics mode
  8480.  displays 256 colors from the available VGA colors.
  8481.  
  8482.  The _RemapPalette function changes a color index to a specified color value.
  8483.  The function below remaps the color index 1 to the color value given by the
  8484.  symbolic constant _Red (which represents red). After this statement is
  8485.  executed, whatever was displayed as blue now appears as red:
  8486.  
  8487.       { reassign color index 1 to VGA red }
  8488.       a := _RemapPalette( 1, _Red );
  8489.  
  8490.  Use the _RemapAllPalette procedure to remap all of the available color
  8491.  indexes simultaneously. The procedure's argument references an array of
  8492.  color values that reflects the remapping. The first color number in the list
  8493.  becomes the new color associated with color index 0.
  8494.  
  8495.  Symbolic constants for the default color numbers are supplied so that the
  8496.  remapping of VGA colors is compatible with EGA practice. The names of these
  8497.  constants are self-explanatory. For example, the color numbers for black,
  8498.  red, and light yellow are represented by the symbolic constants _Black,
  8499.  _Red, and _Yellow.
  8500.  
  8501.  All of the VGA display modes operate with any VGA video monitor. Colors are
  8502.  displayed as shades of gray when a monochrome analog display is connected.
  8503.  
  8504.  The program HORIZON.PAS illustrates what can be done with the range of 256
  8505.  colors if you have a VGA card:
  8506.  
  8507.       PROGRAM horizon; { Demonstrates VGA graphics with cycling of 256 colors}
  8508.  
  8509.       USES
  8510.           Crt, MSGraph;
  8511.       CONST
  8512.           Red = $00002a;
  8513.           GRN = $002a00;
  8514.           BLU = $2a0000;
  8515.           WHT = $2a2a2a;
  8516.           step = 21;
  8517.       VAR
  8518.           vc : _VideoConfig;
  8519.           rainbow : ARRAY [1..512] OF LongInt;
  8520.           i, a      : Integer;
  8521.           col, gray : LongInt;
  8522.           rec : _XYCoord;
  8523.  
  8524.       BEGIN  { Begin main program }
  8525.           a := _SetVideoMode( _MRes256Color );
  8526.           IF (a = 0) THEN
  8527.               BEGIN
  8528.               Writeln( 'This program requires a VGA or MCGA card' );
  8529.               Halt( 0 );
  8530.               END;
  8531.  
  8532.           FOR col := 0 TO 63 DO
  8533.               BEGIN
  8534.               gray := col OR (col SHL 8) OR (col SHL 16);
  8535.               rainbow[col] :=   BLU AND gray;
  8536.               rainbow[col + 256] := BLU AND gray;
  8537.               rainbow[col + 64] := BLU OR gray;
  8538.               rainbow[col + 64 + 256] := BLU OR gray;
  8539.               rainbow[col + 128] := Red OR (WHT AND NOT gray);
  8540.               rainbow[col + 128 + 256] := Red  OR (WHT AND NOT gray);
  8541.               rainbow[col + 192] := Red OR NOT gray;
  8542.               rainbow[col + 192 + 256] := Red OR NOT gray;
  8543.               END;
  8544.  
  8545.       _SetViewOrg( 160, 85, rec );
  8546.  
  8547.           FOR i := 0 TO 254 DO
  8548.               BEGIN
  8549.               _SetColor( 255 - i );
  8550.               _MoveTo( i, i - 255 );
  8551.               _LineTo( -i, 255 - i );
  8552.               _MoveTo( -i, i - 255 );
  8553.               _LineTo( i, 255 - i );
  8554.               _Ellipse( _GBorder, -i, -i DIV 2, i, i DIV 2 );
  8555.               END;
  8556.  
  8557.           i := 0;
  8558.           WHILE NOT KeyPressed DO
  8559.               BEGIN
  8560.               _RemapAllPalette( rainbow[i] );
  8561.               i := i + step;
  8562.               IF (i >= 256) THEN i := 0;
  8563.               END;
  8564.  
  8565.           a := _SetVideoMode( _DefaultMode );
  8566.       END.
  8567.  
  8568.  
  8569.  13.3.6  Using the Color Video Text Modes
  8570.  
  8571.  All video adapters offer support for video text modes (_TextC40,
  8572.  _TextC80, _TextBW40, _TextBW80, _TextMono ). Using the video text modes, you
  8573.  can display color text without having to enter a graphics mode. On a color
  8574.  monitor, you can display normal or blinking text in any of 16 foreground
  8575.  colors with any of 8 background colors. On a monochrome monitor, you can
  8576.  specify text "colors" in exactly the same way, although the color values are
  8577.  interpreted differently by the hardware.
  8578.  
  8579.  The text procedures and functions described in this section can be used in
  8580.  the video text modes as well as in all of the graphics modes.
  8581.  
  8582.  Selecting Text Colors
  8583.  
  8584.  In a video text mode, each displayed character requires two bytes of video
  8585.  memory. The first byte contains the ASCII code representing the character
  8586.  and the second byte contains the display attribute. In the CGA color video
  8587.  text modes, the attribute byte determines the color and whether it will
  8588.  blink. Sixteen colors are available: the CGA indexes, and the default EGA
  8589.  and VGA indexes. Since the EGA and VGA palettes can be remapped, these
  8590.  values can be made to correspond to any set of 16 colors with the
  8591.  appropriate palette mapping.
  8592.  
  8593.  Using Text Colors
  8594.  
  8595.  Use the _GetTextColor and _GetBkColor functions to find foreground and
  8596.  background colors of the current text.
  8597.  
  8598.  Values in the range 0-15 are interpreted as normal color. Values in the
  8599.  range 16-31 are the same colors as those in the range 0-15 but with blinking
  8600.  text.
  8601.  
  8602.  Set the foreground and background colors in a video text mode with the
  8603.  _SetTextColor and _SetBkColor functions. These functions use a single
  8604.  argument that specifies the index to be used for text displayed with the
  8605.  _OutText procedure. The color indexes for color video text modes are defined
  8606.  in Table 13.6.
  8607.  
  8608.  Table 13.6  Text Colors
  8609.  
  8610.  Number      Color       Number     Color
  8611.  
  8612.  0           Black       8          Gray
  8613.  1           Blue        9          Light Blue
  8614.  2           Green       10         Light Green
  8615.  3           Cyan        11         Light Cyan
  8616.  4           Red         12         Light Red
  8617.  5           Magenta     13         Light Magenta
  8618.  6           Brown       14         Yellow
  8619.  7           White       15         Light White
  8620.  
  8621.  On monochrome displays, you can select text attributes in the same way as
  8622.  for color displays. For underlined text, use Blue or Light Blue. For
  8623.  highlighted text, use any color from Light Green to White. For blinking
  8624.  text, add 16 to the color constant.
  8625.  
  8626.  Displaying Text Colors
  8627.  
  8628.  The _SetTextPosition procedure moves the cursor to a row and column for
  8629.  displaying color text. The _OutText procedure displays the text.
  8630.  
  8631.  The following program displays a chart showing all possible combinations of
  8632.  text and background colors:
  8633.  
  8634.       PROGRAM coltext;  { Displays text in color }
  8635.  
  8636.       USES
  8637.           Crt, MSGraph;
  8638.  
  8639.       VAR
  8640.           message1, s, t : STRING;
  8641.           blink, fgd, bgd : Integer;
  8642.           a : Integer;
  8643.  
  8644.       BEGIN  { Begin main program }
  8645.  
  8646.           _ClearScreen( _GClearScreen );
  8647.           _OutText( 'Color text attributes:' );
  8648.  
  8649.           FOR blink := 0 TO 1 DO
  8650.               FOR bgd := 0 TO 6 DO
  8651.                   BEGIN
  8652.                   _SetBkColor( bgd );
  8653.                   _SetTextPosition( bgd + (blink  * 9) + 3, 1 );
  8654.                   _SetTextColor( 7 );
  8655.                   Str( bgd, s );
  8656.                   message1 := 'Bgd: ';
  8657.                   _OutText( message1 );
  8658.                   _OutText( s );
  8659.                   s := s + ' ';
  8660.                   message1 := '  Fgd: ';
  8661.                   _OutText( message1 );
  8662.                   FOR fgd := 0 TO 15 DO
  8663.                       BEGIN
  8664.                       _SetTextColor( fgd + (blink*16) );
  8665.                       a := fgd + (blink*16);
  8666.                       Str( a, s );
  8667.                       s := s + '  ';
  8668.                       _OutText( s );
  8669.                       END;
  8670.                   END; { FOR loops }
  8671.  
  8672.           REPEAT UNTIL KeyPressed;
  8673.           a := _SetVideoMode( _DefaultMode );
  8674.  
  8675.       END.
  8676.  
  8677.  
  8678.  13.4  Understanding Coordinate Systems
  8679.  
  8680.  Before you can write a program to print a word or to display graphics on the
  8681.  screen, you need a system that describes exactly where to print or display
  8682.  the item.
  8683.  
  8684.  QuickPascal provides several coordinate systems. The physical coordinate
  8685.  system has already been used in the program 1STGRAPH.PAS. This section
  8686.  discusses the four coordinate systems supported by QuickPascal and shows how
  8687.  to translate between systems.
  8688.  
  8689.  The coordinate systems supported by QuickPascal are:
  8690.  
  8691.    ■  Text coordinates
  8692.  
  8693.    ■  Physical coordinates
  8694.  
  8695.    ■  Viewport coordinates
  8696.  
  8697.    ■  Real coordinates in a window
  8698.  
  8699.  
  8700.  13.4.1  Text Coordinates
  8701.  
  8702.  QuickPascal divides the text screen into rows and columns. See Figure 13.1.
  8703.  
  8704.  Two important conventions to keep in mind about video text mode are:
  8705.  
  8706.    1. Numbering starts at 1, not 0. An 80-column screen contains columns
  8707.       1-80.
  8708.  
  8709.    2. The row is always listed before the column.
  8710.  
  8711.  If the screen is in a video text mode that displays 25 rows and 80 columns
  8712.  (as in Figure 13.1), the rows are numbered 1-25 and the columns are
  8713.  numbered 1-80. In routines such as _SetTextPosition, which is called in the
  8714.  next example program, the parameters you pass are row and column (in that
  8715.  order). Some monitors (such as the EGA or VGA) support more than 25 text
  8716.  rows. For these monitors, use the _SetVideoModeRows or _SetTextRows
  8717.  functions to specify the number of rows to display.
  8718.  
  8719.  
  8720.  13.4.2  Physical Screen Coordinates
  8721.  
  8722.  Suppose you write a program that calls _SetVideoMode and puts the screen
  8723.  into the VGA graphics mode _VRes16Color. This gives you a screen containing
  8724.  640 horizontal pixels and 480 vertical pixels. The individual pixels are
  8725.  named by their location relative to the x axis and y axis, as shown in
  8726.  Figure 13.2. Two important differences between text coordinates and graphics
  8727.  coordinates are:
  8728.  
  8729.    1. Numbering starts at 0, not 1. If there are 640 pixels, they're
  8730.       numbered 0-639.
  8731.  
  8732.    2. The x coordinate is listed before the y coordinate.
  8733.  
  8734.  The upper left corner is called the "origin."  The x and y coordinates for
  8735.  the origin are always (0, 0). If you use variables to refer to pixel
  8736.  locations, declare them as integers. The "viewport" is the region where
  8737.  graphics will be displayed.
  8738.  
  8739.  Changing the Origin with _SetViewOrg
  8740.  
  8741.  The _SetViewOrg procedure changes the location of the viewport's origin. You
  8742.  pass two integers, which represent the x and y coordinates of a physical
  8743.  screen location. For example, the following line would move the origin to
  8744.  the physical screen location (50, 100):
  8745.  
  8746.       _SetViewOrg( 50, 100, xyorg );
  8747.  
  8748.  The effect on the screen is illustrated in Figure 13.3.
  8749.  
  8750.  The number of pixels hasn't changed, but the names given to the points have
  8751.  changed. The x axis now ranges from -50 to +589 instead of 0 to 639. The y
  8752.  axis now covers the values -100 to +379. (If you own an adapter other than
  8753.  the VGA, the numbers are different but the effect is the same.) All standard
  8754.  graphics functions and procedures are affected by the new origin, including
  8755.  _MoveTo, _LineTo, _Rectangle, _Ellipse, _Arc, and _Pie.
  8756.  
  8757.  For example, if you call the _Rectangle procedure after relocating the
  8758.  viewport origin, and pass it the coordinate values (0, 0) and (40, 40), the
  8759.  rectangle would be drawn 50 pixels from the left edge of the screen and 100
  8760.  pixels from the top. It would not appear in the upper left corner.
  8761.  
  8762.  The values passed to _SetViewOrg are always physical screen locations.
  8763.  Suppose you called the same procedure twice:
  8764.  
  8765.       _SetViewOrg( 50, 100, xyorg );
  8766.       _SetViewOrg( 50, 100, xyorg );
  8767.  
  8768.  The viewport origin would not move to (100, 200). It would remain at the
  8769.  physical screen location (50, 100).
  8770.  
  8771.  Defining a Clipping Region with _SetClipRgn
  8772.  
  8773.  The _SetClipRgn procedure creates an invisible rectangular area on the
  8774.  screen called a "clipping region." Attempts to draw inside the clipping
  8775.  region are successful, while attempts to draw outside the region are not.
  8776.  
  8777.  When you first enter a graphics mode, the default clipping region is the
  8778.  entire screen. QuickPascal ignores any attempts to draw outside the clipping
  8779.  region.
  8780.  
  8781.  Changing the clipping region requires one call to _SetClipRgn. Suppose
  8782.  you've entered the CGA graphics mode _MRes4Color, which has a screen
  8783.  resolution of 320 x 200. If you draw a diagonal line from the top left to
  8784.  the bottom right corner, the screen looks like Figure 13.4. You could
  8785.  create a clipping region with the following:
  8786.  
  8787.       _SetClipRgn( 10, 10, 309, 189 );
  8788.  
  8789.  Then draw a line with the statement:
  8790.  
  8791.       _LineTo( 0, 0, 319, 199 );
  8792.  
  8793.  With the clipping region in effect, the same _LineTo command given above
  8794.  would put the line shown in Figure 13.5 on the screen.
  8795.  
  8796.  The broken lines don't actually appear on the screen. They indicate the
  8797.  outer bounds of the clipping region.
  8798.  
  8799.  
  8800.  13.4.3  Viewport Coordinates
  8801.  
  8802.  Viewport coordinates are yet another coordinate system supported by
  8803.  QuickPascal. You can establish a new viewport within the boundaries of the
  8804.  physical screen by using the _SetViewport procedure. A standard viewport has
  8805.  two distinguishing features:
  8806.  
  8807.    1. The origin of a viewport is in the upper left corner.
  8808.  
  8809.    2. The clipping region matches the outer boundaries of the viewport.
  8810.  
  8811.  The _SetViewport procedure does the same thing as calling _SetViewOrg and
  8812.  _SetClipRgn together.
  8813.  
  8814.  
  8815.  13.4.4  Real Coordinates in a Window
  8816.  
  8817.  QuickPascal supports a system of real coordinates for use in a window. This
  8818.  system lets you use floating-point values in graphics.
  8819.  
  8820.  Functions and procedures that refer to coordinates on the physical screen
  8821.  and within the viewport require integer values. However, in real-life
  8822.  graphing applications, you might wish to use floating-point values──stock
  8823.  prices, the price of wheat, average rainfall, and so on.
  8824.  
  8825.  Setting Window Coordinates
  8826.  
  8827.  The _SetWindow procedure allows you to scale the screen to almost any size.
  8828.  In addition, the window-related functions and procedures take
  8829.  double-precision floating-point values instead of integers.
  8830.  
  8831.  If, for example, you wanted to graph 12 months of average temperatures that
  8832.  range from -40 to +100, you could add the following line to your program:
  8833.  
  8834.       _SetWindow( TRUE, 1.0, -40.0, 12.0, 100.0 );
  8835.  
  8836.  The first argument is the invert flag, which puts the lowest y value in the
  8837.  bottom left corner. The minimum and maximum Cartesian coordinates follow
  8838.  (the decimal point marks them as floating-point values). The new
  8839.  organization of the screen is shown in Figure 13.6.
  8840.  
  8841.  This procedure makes the temperatures for January and December appear on the
  8842.  left and right edges of the screen. In an application like this, it might be
  8843.  better to number the x axis from 0.0 to 13.0, to provide some extra space.
  8844.  
  8845.  If you plot a point with _SetPixel_w or draw a line with _LineTo_w, the
  8846.  values are automatically scaled to the established window. You can also find
  8847.  the position of the graphics cursor at any time with
  8848.  _GetCurrentPosition_wxy.
  8849.  
  8850.  Programming Real-Coordinate Graphics
  8851.  
  8852.  The four steps to using real-coordinate graphics are:
  8853.  
  8854.    1. Enter a graphics mode with _SetVideoMode.
  8855.  
  8856.    2. Use _SetViewPort to create a viewport area. This step is optional if
  8857.       you plan to use the entire screen.
  8858.  
  8859.    3. Create a real-coordinate window with _SetWindow, passing a Boolean
  8860.       invert flag and four Double x and y coordinates for the minimum and
  8861.       maximum values.
  8862.  
  8863.    4. Draw graphics shapes with _Rectangle_w and other procedures. Do not
  8864.       confuse _Rectangle (the viewport procedure) with _Rectangle_w (the
  8865.       window procedure for drawing rectangles). All window procedures end
  8866.       with an underscore and a letter w or an underscore and wxy.
  8867.  
  8868.  Real-coordinate graphics can give you a lot of flexibility. For example, you
  8869.  can fit either axis into a small range (such as 151.25 to 151.45) or into a
  8870.  large range (-50,000 to +80,000), depending on the type of data you're
  8871.  graphing. In addition, by changing the window coordinates, you can create
  8872.  the effects of zooming in or panning across a figure.
  8873.  
  8874.  An Example of Real-Coordinate Graphics
  8875.  
  8876.  The program below illustrates how to use the real-coordinate window
  8877.  routines.
  8878.  
  8879.       PROGRAM realg; { Example of real-coordinate graphics }
  8880.       USES
  8881.           MsGraph, Crt;
  8882.  
  8883.       CONST
  8884.           bananas : ARRAY[0..20] OF Single =
  8885.               (
  8886.               -0.3,   -0.2, -0.224, -0.1,   -0.5, +0.21, +2.9,
  8887.               +0.3,   +0.2,  0.0,   -0.885, -1.1, -0.3,  -0.2,
  8888.                0.001, 0.005, 0.14,   0.0,   -0.9, -0.13, +0.3
  8889.                );
  8890.       VAR
  8891.           halfx, halfy,
  8892.           a   : Integer;
  8893.           vc  : _VideoConfig;
  8894.           ch  : Char;
  8895.  
  8896.       FUNCTION four_colors : Boolean;
  8897.       BEGIN
  8898.           four_colors := False;
  8899.           IF (_SetVideoMode( _MaxColorMode ) > 0) THEN
  8900.               BEGIN
  8901.               _GetVideoConfig( vc );
  8902.               IF (vc.NumColors >= 4) THEN
  8903.                   four_colors := True;
  8904.               END;
  8905.  
  8906.       END;
  8907.  
  8908.       PROCEDURE grid_shape;
  8909.       VAR
  8910.           i, x1, y1, x2, y2  : Integer;
  8911.           x, y               : Real;
  8912.           s                  : STRING[80];
  8913.       BEGIN
  8914.  
  8915.           FOR i := 1 TO vc.NumColors DO
  8916.               BEGIN
  8917.               _SetTextPosition( i, 2 );
  8918.               _SetTextColor( i );
  8919.               Str( i, s );
  8920.               _OutText( 'Color ' + s );
  8921.               END;
  8922.  
  8923.           _SetColor( 1 );
  8924.           _Rectangle_w( _GBorder, -1.0,  -1.0,  1.0,  1.0  );
  8925.           _Rectangle_w( _GBorder, -1.02, -1.02, 1.02, 1.02 );
  8926.  
  8927.           x := -0.9;
  8928.           i :=  0;
  8929.           WHILE  x < 0.9 DO
  8930.               BEGIN
  8931.               _SetColor( 2 );
  8932.               _MoveTo_w( x, -1.0 );   _LineTo_w( x, 1.0 );
  8933.               _MoveTo_w( -1.0, x );   _LineTo_w( 1.0, x );
  8934.               _SetColor( 3 );
  8935.               _MoveTo_w( x - 0.1, bananas[i] );
  8936.               Inc( i );
  8937.               _LineTo_w( x, bananas[i] );
  8938.               x := x + 0.1;
  8939.               END;
  8940.  
  8941.           _MoveTo_w( 0.9, bananas[i] );
  8942.           Inc(i);
  8943.           _LineTo_w( 1.0, bananas[i] );
  8944.           END;
  8945.  
  8946.       PROCEDURE three_graphs;
  8947.       VAR
  8948.           upleft, botright : _WXYCoord;
  8949.           xwidth, yheight, cols, rows : Integer;
  8950.  
  8951.  
  8952.       BEGIN
  8953.           _ClearScreen( _GClearScreen );
  8954.           xwidth  := vc.NumXPixels;
  8955.           yheight := vc.NumYPixels;
  8956.           halfx   := xwidth  DIV 2;
  8957.           halfy   := yheight DIV 2;
  8958.           cols    := vc.NumTextCols;
  8959.           rows    := vc.NumTextRows;
  8960.  
  8961.           { first window }
  8962.           _SetViewport( 0, 0, halfx-1, halfy-1 );
  8963.           _SetTextWindow( 1, 1, rows DIV 2, cols DIV 2 );
  8964.           _SetWindow( False, -2.0, -2.0, 2.0, 2.0 );
  8965.           grid_shape;
  8966.           _Rectangle( _GBorder, 0, 0, halfx-1, halfy-1 );
  8967.  
  8968.           { second window }
  8969.           _SetViewport( halfx, 0, xwidth-1, halfy-1 );
  8970.           _SetTextWindow( 1, cols DIV 2+1, rows DIV 2, cols );
  8971.           _SetWindow( False, -3.0, -3.0, 3.0, 3.0 );
  8972.           grid_shape;
  8973.           _Rectangle_w( _GBorder, -3.0, -3.0, 3.0, 3.0 );
  8974.  
  8975.           { third window }
  8976.           _SetViewport( 0, halfy, xwidth-1, yheight-1 );
  8977.           _SetTextWindow( rows DIV 2+2, 1, rows, cols );
  8978.           _SetWindow( True, -3.0, -1.5, 1.5, 1.5 );
  8979.           grid_shape;
  8980.           upleft.wx   := -3.0;
  8981.           upleft.wy   := -1.5;
  8982.           botright.wx :=  1.5;
  8983.           botright.wy :=  1.5;
  8984.           _Rectangle_wxy( _GBorder, upleft, botright);
  8985.  
  8986.       END;
  8987.  
  8988.       BEGIN { main program }
  8989.  
  8990.       IF four_colors THEN
  8991.         BEGIN
  8992.         _OutText( 'This program requires a CGA, EGA, or VGA graphics card' );
  8993.         three_graphs;
  8994.         END;
  8995.  
  8996.       ch := ReadKey;
  8997.       a := _SetVideoMode( _DefaultMode );
  8998.  
  8999.       END.
  9000.  
  9001.  The main body of the program is short. It calls the four_colors function
  9002.  (defined below), which attempts to enter a graphics mode in which at least
  9003.  four colors are available. If it succeeds, the three_graphs function is
  9004.  called. This function uses the numbers in the bananas array to draw three
  9005.  graphs. The REALG.PAS screen output is shown in Figure 13.7.
  9006.  
  9007.  It's worth noting that the graphs are all drawn using the same numbers.
  9008.  However, the program uses three different real-coordinate windows. The two
  9009.  windows in the top half are the same size in physical coordinates, but they
  9010.  have different window sizes. In all three cases, the grid is two units wide.
  9011.  In the upper left corner, the window is four units wide; in the upper right,
  9012.  the window is six units wide, which makes the graph appear smaller.
  9013.  
  9014.  In two of the three graphs, one of the lines goes off the edge, outside the
  9015.  clipping region. The lines do not intrude into the other windows, since
  9016.  defining a window creates a clipping region.
  9017.  
  9018.  Finally, note that the graph on the bottom of the screen seems to be upside
  9019.  down with respect to the two graphs above it. This is the result of setting
  9020.  the invert flag to True.
  9021.  
  9022.  Entering a Graphics Mode
  9023.  
  9024.  The first step in any graphics program is to enter a graphics mode. The
  9025.  function four_colors performs this step:
  9026.  
  9027.       FUNCTION four_colors : Boolean;
  9028.       BEGIN
  9029.           four_colors := False;
  9030.           IF (_SetVideoMode( _MaxColorMode ) > 0) THEN
  9031.               BEGIN
  9032.               _GetVideoConfig( vc );
  9033.               IF (vc.NumColors >= 4) THEN
  9034.                   four_colors := True;
  9035.               END;
  9036.  
  9037.       END;
  9038.  
  9039.  
  9040.  The _GetVideoConfig procedure places some information into the structure
  9041.  _VideoConfig called screen. Then you use the member screen.adapter of the
  9042.  _VideoConfig structure in a CASE statement construct to turn on the matching
  9043.  graphics mode. The symbolic constants _CGA and the rest are defined in the
  9044.  MSGraph unit. The modes containing the letter "O" are Olivetti modes.
  9045.  
  9046.  If the computer is equipped with a color card, this function returns a True.
  9047.  If it is not, it returns a False, which causes the program to skip the
  9048.  function three_graphs and to end the program.
  9049.  
  9050.  If the four_colors function works properly, the program calls the function
  9051.  below, which prints the three graphs.
  9052.  
  9053.       PROCEDURE three_graphs;
  9054.       VAR
  9055.           upleft, botright : _WXYCoord;
  9056.           xwidth, yheight, cols, rows : Integer;
  9057.  
  9058.       BEGIN
  9059.           _ClearScreen( _GClearScreen );
  9060.           xwidth  := vc.NumXPixels;
  9061.           yheight := vc.NumYPixels;
  9062.           halfx   := xwidth  DIV 2;
  9063.           halfy   := yheight DIV 2;
  9064.           cols    := vc.NumTextCols;
  9065.           rows    := vc.NumTextRows;
  9066.  
  9067.  
  9068.           { first window }
  9069.           _SetViewport( 0, 0, halfx-1, halfy-1 );
  9070.           _SetTextWindow( 1, 1, rows DIV 2, cols DIV 2 );
  9071.           _SetWindow( False, -2.0, -2.0, 2.0, 2.0 );
  9072.           grid_shape;
  9073.           _Rectangle( _GBorder, 0, 0, halfx-1, halfy-1 );
  9074.  
  9075.           { second window }
  9076.           _SetViewport( halfx, 0, xwidth-1, halfy-1 );
  9077.           _SetTextWindow( 1, cols DIV 2+1, rows DIV 2, cols );
  9078.           _SetWindow( False, -3.0, -3.0, 3.0, 3.0 );
  9079.           grid_shape;
  9080.           _Rectangle_w( _GBorder, -3.0, -3.0, 3.0, 3.0 );
  9081.  
  9082.           { third window }
  9083.           _SetViewport( 0, halfy, xwidth-1, yheight-1 );
  9084.           _SetTextWindow( rows DIV 2+2, 1, rows, cols );
  9085.           _SetWindow( True, -3.0, -1.5, 1.5, 1.5 );
  9086.           grid_shape;
  9087.           upleft.wx   := -3.0;
  9088.           upleft.wy   := -1.5;
  9089.           botright.wx :=  1.5;
  9090.           botright.wy :=  1.5;
  9091.           _Rectangle_wxy( _GBorder, upleft, botright);
  9092.  
  9093.       END;
  9094.  
  9095.  Working with Windows
  9096.  
  9097.  Although entering a graphics mode automatically clears the screen, it
  9098.  doesn't hurt to be sure, so three_graphs calls the _ClearScreen procedure:
  9099.  
  9100.       _ClearScreen( _GClearScreen );
  9101.  
  9102.  The _GClearScreen constant causes the entire physical screen to clear.
  9103.  Other options include _GViewport and _GWindow, which clear the current
  9104.  viewport and the current text window, respectively.
  9105.  
  9106.  The First Window
  9107.  
  9108.  After assigning values to some variables, the procedure three_graphs
  9109.  creates the first window:
  9110.  
  9111.       _SetViewPort( 0, 0, halfx - 1, halfy - 1 );
  9112.       _SetTextWindow( 1, 1, rows / 2, cols / 2 );
  9113.       _SetWindow( False, -2.0, -2.0, 2.0, 2.0 );
  9114.  
  9115.  First a viewport is defined to cover the upper left quarter of the screen.
  9116.  Next, a text window is defined within the boundaries of that border. (Note
  9117.  the numbering starts at 1 and the row location precedes the column.)
  9118.  Finally, a window is defined. The False constant forces the y axis to
  9119.  increase from top to bottom. The corners of the window are (-2.0, -2.0) in
  9120.  the upper left and (2.0, 2.0) in the bottom right corner.
  9121.  
  9122.  Next, the function grid_shape is called, and a border is added to the
  9123.  window:
  9124.  
  9125.       grid_shape;
  9126.       _Rectangle( _GBorder, 0, 0, halfx-1, halfy-1 );
  9127.  
  9128.  Note that this is the standard _Rectangle procedure, which takes coordinates
  9129.  relative to the viewport (not window coordinates).
  9130.  
  9131.  Two More Windows
  9132.  
  9133.  The two other windows are similar to the first. All three call grid_shape
  9134.  (defined below), which draws a grid from location (-1.0, -1.0) to (+1.0,
  9135.  +1.0). The grid appears in different sizes because the coordinates in the
  9136.  windows vary. The second window ranges from (-3.0, -3.0) to (+3.0, +3.0),
  9137.  so the width of the grid is one-third the width of the second window, while
  9138.  it is one-half the width of the first.
  9139.  
  9140.  Note also that the third window contains True as the first argument. This
  9141.  causes the y axis to increase from bottom to top, instead of top to bottom.
  9142.  As a result, this graph appears to be upside down in relation to the other
  9143.  two.
  9144.  
  9145.  After calling grid_shape, the program frames each window with one of the
  9146.  following procedures:
  9147.  
  9148.       _Rectangle( _GBorder, 0, 0, halfx -1, halfy -1 );
  9149.       _Rectangle_w( _GBorder, -3.0, -3.0, 3.0, 3.0 );
  9150.       _Rectangle_wxy( _GBorder, upleft, botright );
  9151.  
  9152.  All three procedures contain a fill flag as the first argument. The
  9153.  procedure _Rectangle takes integer arguments that refer to the viewport
  9154.  screen coordinates. The procedure _Rectangle_w takes four double-precision,
  9155.  floating-point values referring to window coordinates: upper left x, upper
  9156.  left y, lower right x, and lower right y. The procedure _Rectangle_wxy
  9157.  takes two arguments: the addresses of structures of type _WXYCoord, which
  9158.  contains two Double types named wx and wy. The structure is defined in the
  9159.  MSGraph unit. The values are assigned just before _Rectangle_wxy is called.
  9160.  
  9161.  Drawing Graphics
  9162.  
  9163.  The grid_shape procedure is shown here:
  9164.  
  9165.       PROCEDURE grid_shape;
  9166.       VAR
  9167.           i, x1, y1, x2, y2  : Integer;
  9168.           x, y               : Real;
  9169.           s                  : STRING[80];
  9170.       BEGIN
  9171.  
  9172.           FOR i := 1 TO vc.NumColors DO
  9173.               BEGIN
  9174.               _SetTextPosition( i, 2 );
  9175.               _SetTextColor( i );
  9176.               Str( i, s );
  9177.               _OutText( 'Color ' + s );
  9178.               END;
  9179.  
  9180.           _SetColor( 1 );
  9181.           _Rectangle_w( _GBorder, -1.0,  -1.0,  1.0,  1.0  );
  9182.           _Rectangle_w( _GBorder, -1.02, -1.02, 1.02, 1.02 );
  9183.  
  9184.           x := -0.9;
  9185.           i :=  0;
  9186.           WHILE  x  0.9 DO
  9187.               BEGIN
  9188.               _SetColor( 2 );
  9189.               _MoveTo_w( x, -1.0 );   _LineTo_w( x, 1.0 );
  9190.               _MoveTo_w( -1.0, x );   _LineTo_w( 1.0, x );
  9191.               _SetColor( 3 );
  9192.               _MoveTo_w( x - 0.1, bananas[i] );
  9193.               Inc( i );
  9194.               _LineTo_w( x, bananas[i] );
  9195.               x := x + 0.1;
  9196.               END;
  9197.  
  9198.           _MoveTo_w( 0.9, bananas[i] );
  9199.           Inc(i);
  9200.           _LineTo_w( 1.0, bananas[i] );
  9201.           END;
  9202.  
  9203.  First, the number of available color indexes is assigned to the numc
  9204.  variable and a loop displays all of the available colors:
  9205.  
  9206.       FOR i := 1 TO numc DO
  9207.  
  9208.           BEGIN
  9209.           _SetTextPosition( i, 2 );
  9210.           _SetTextColor( i );
  9211.           Str(i, s);
  9212.           _OutText( 'Color ' + s );
  9213.           END;
  9214.  
  9215.  The names of the procedures are self-      explanatory. The advantage of usin
  9216.  _OutText in graphics mode is that you can control the text color and limit
  9217.  output to the currently defined text window.
  9218.  
  9219.  The procedure and function names that end with _w work the same as their
  9220.  viewport equivalents, except you pass double-precision, floating-point
  9221.  values instead of integers. For example, you pass integers to _LineTo but
  9222.  floating-point values to _LineTo_w.
  9223.  
  9224.  
  9225.  13.5  Animation
  9226.  
  9227.  The QuickPascal MSGraph unit provides several functions that can be used to
  9228.  animate your graphics programs. These functions provide two means of
  9229.  animation:
  9230.  
  9231.    1. Video-page animation (also used in text modes)
  9232.  
  9233.    2. Bit-mapped animation
  9234.  
  9235.  "Video-page animation" takes advantage of the fact that the EGA, VGA, and
  9236.  Hercules video cards have enough memory to store more than one video
  9237.  display page. You can animate by switching between the pages. "Bit-mapped
  9238.  animation" captures bit-mapped images of the screen and then stores them in
  9239.  a memory buffer. These images can be redisplayed at a new location to
  9240.  perform animation.
  9241.  
  9242.  This section discusses these two animation techniques and shows two sample
  9243.  programs that bring your graphics to life.
  9244.  
  9245.  
  9246.  13.5.1  Video-Page Animation
  9247.  
  9248.  Most video adapters contain enough memory so that more than one display page
  9249.  can be stored at a time. The MSGraph unit provides several functions that
  9250.  allow you to manipulate these video pages. Two terms are used to describe
  9251.  these pages──the "active page" is the page where text and graphics commands
  9252.  operate; the "visual page" is the page that you see displayed.
  9253.  
  9254.  The number of video pages available in an adapter depends on the amount of
  9255.  video memory on the adapter and the mode in which the adapter is used. For
  9256.  example, the CGA adapter with 16K of video memory supports four video pages
  9257.  in the text mode _TextC80. An EGA adapter with a full 256K of video memory
  9258.  has room for two video pages even in the high resolution _EResColor mode.
  9259.  Virtually all adapters provide for multiple pages in text modes; only the
  9260.  EGA and VGA adapters with 256K of video memory support two video pages in
  9261.  the high-resolution graphics modes. The Hercules graphics mode (_HercMono)
  9262.  can support two pages, but only if it is the only graphics adapter present
  9263.  and only when MSHERC.COM is started without the /H option.
  9264.  
  9265.  Use the procedure _GetVideoConfig to obtain information about the video
  9266.  configuration. After calling _GetVideoConfig, use the NumVideoPages element
  9267.  of the _VideoConfig structure to determine the number of video pages
  9268.  supported.
  9269.  
  9270.  A simple use of video pages is to draw graphics offscreen (on the active
  9271.  page) and then make this active page the visual page. In this way, you do
  9272.  not see the process of creating the graphics; you only see the final result.
  9273.  This process of drawing offscreen and then switching can be extended to
  9274.  provide animation.
  9275.  
  9276.  The procedure _SetVisualPage changes the page that you see. The procedure
  9277.  _SetActivePage changes the page where drawing takes place. The first page in
  9278.  any graphics system is number 0, the second is number 1, and so on. A
  9279.  corresponding set of functions _GetActivePage and _GetVisualPage return the
  9280.  value of the current active or visual page, respectively.
  9281.  
  9282.  To animate any sequence of screens using video-page animation, use the
  9283.  following steps:
  9284.  
  9285.    1. Perform regular graphics initialization (select video mode, check for
  9286.       error, and so on).
  9287.  
  9288.    2. Draw on the active page.
  9289.  
  9290.    3. Swap the visual and active pages.
  9291.  
  9292.    4. Repeat steps 2 and 3 until finished with animation.
  9293.  
  9294.    5. Restore the screen and exit the program.
  9295.  
  9296.  The example program PAGES.PAS uses four video pages to animate a simple set
  9297.  of character images in text mode.
  9298.  
  9299.       PROGRAM page_animation;
  9300.       { PAGES.PAS: Use video pages to animate screens }
  9301.  
  9302.       USES
  9303.           MSGraph, Crt;
  9304.  
  9305.       CONST
  9306.           jumper   :  ARRAY[0..3] OF STRING =
  9307.                    ('/O\',  '-O-', '\O/', 'WOW');
  9308.       VAR
  9309.           a, i     :  Integer;
  9310.           oldvpage :  Integer;
  9311.           oldapage :  Integer;
  9312.           vc : _VideoConfig;
  9313.           oldcursor : Boolean;
  9314.  
  9315.       BEGIN  { Begin main program }
  9316.  
  9317.           _ClearScreen( _GClearScreen);
  9318.  
  9319.           oldapage  := _GetActivePage;
  9320.           oldvpage  := _GetVisualPage;
  9321.  
  9322.           { Set the video mode for a large text size }
  9323.           a := _SetVideoModeRows( _TextBW40, 25 );
  9324.           _GetVideoConfig( vc );
  9325.  
  9326.           IF ((a = 0) OR (vc.NumVideoPages < 4)) THEN
  9327.             BEGIN
  9328.             Writeln( '_TEXTBW40 mode not available; hit Return to continue');
  9329.             Readln;
  9330.             a := _SetVideoMode( _DefaultMode );
  9331.             Halt( 0 );
  9332.             END;
  9333.  
  9334.           { Turn off flashing cursor. }
  9335.           oldcursor := _DisplayCursor( False );
  9336.  
  9337.           { Draw image on each page. }
  9338.           FOR i := 0 TO 3 DO
  9339.               BEGIN
  9340.               _SetActivePage( i );
  9341.               _SetTextPosition( 12, 20 );
  9342.               _OutText( jumper[i] );
  9343.               END;
  9344.  
  9345.           { Cycle through pages 0 to 3. }
  9346.           REPEAT
  9347.               FOR i := 0 TO 3 DO
  9348.                   BEGIN
  9349.                   _SetVisualPage( i );
  9350.                   Delay( 500 );
  9351.                   END;
  9352.           UNTIL KeyPressed;
  9353.  
  9354.           { Restore everything before ending the program. }
  9355.           a := _SetVideoMode( _DefaultMode );
  9356.           _SetActivePage( oldapage );
  9357.           _SetVisualPage( oldvpage );
  9358.  
  9359.       END.
  9360.  
  9361.  The program PAGES.PAS begins with a call to _ClearScreen and then sets the
  9362.  video mode using the _SetVideoModeRows function. The cursor is turned off
  9363.  with the _DisplayCursor function.
  9364.  
  9365.  The FOR loop draws a graphics image on each of four pages. The loop goes to
  9366.  each of the video pages and outputs the graphics image using the _OutText
  9367.  procedure. When you run this program, notice that you don't see any of this
  9368.  activity. Since the drawing is taking place on the active pages, it is not
  9369.  visible on the screen (the visual page).
  9370.  
  9371.  Once the pages have been drawn, the REPEAT loop cycles through each page and
  9372.  makes it the visual page (so you can see it) and then delays 500
  9373.  milliseconds between calls to _SetVisualPage.
  9374.  
  9375.  Finally, when a key is pressed, the program ends by restoring the video mode
  9376.  to _DefaultMode using the _SetVideoMode function.
  9377.  
  9378.  
  9379.  13.5.2  Bit-Mapped Animation
  9380.  
  9381.  Bit-mapped animation gives you the ability to draw graphics figures and
  9382.  store them in memory for later use in animation. The _ImageSize function
  9383.  determines the amount of memory required to store a specified bit-mapped
  9384.  image. The image is specified in terms of a bounding rectangle. The
  9385.  _GetImage procedure copies the bit map of pixels inside a specified
  9386.  rectangle to a buffer area in memory. The _PutImage procedure copies a
  9387.  bit-mapped image from a memory buffer to the screen at a location specified
  9388.  by the program.
  9389.  
  9390.  The _PutImage procedure uses a CopyMode argument to control how the stored
  9391.  image interacts with what is already on the screen. The CopyMode argument
  9392.  specifies one of the following screen display operations:
  9393.  
  9394.  Constant                    Action
  9395.  
  9396.  _Gand                       Logical AND of the transfer image and screen
  9397.                              image
  9398.  
  9399.  _Gor                        Superimposition of the transfer image onto the
  9400.                              existing screen image
  9401.  
  9402.  _GPReset                    Direct transfer from memory to screen; color
  9403.                              inverted
  9404.  
  9405.  _GPSet                      Direct transfer from memory to screen
  9406.  
  9407.  _Gxor                       Screen inversion only where a point exists in
  9408.                              the transfer image
  9409.  
  9410.  The two CopyMode arguments best suited for animation are _Gxor and _GPSet.
  9411.  
  9412.  Animation done using _GPSet is faster, but erases the screen background. In
  9413.  contrast, _Gxor is slower, but preserves the screen background.
  9414.  
  9415.  Animation with _Gxor is done with the following four steps:
  9416.  
  9417.    1. Put the object on the screen with _Gxor.
  9418.  
  9419.    2. Calculate the new position of the object.
  9420.  
  9421.    3. Put the object on the screen a second time at the old location, using
  9422.       _Gxor again──this time to remove the old image.
  9423.  
  9424.    4. Go to step 1, but this time put the object at the new location.
  9425.  
  9426.  Movement done with these four steps leaves the background unchanged after
  9427.  step 3. Flicker can be reduced by minimizing the time between steps 4 and 1,
  9428.  and by making sure that there is enough time delay between steps 1 and 3. If
  9429.  more than one object is being animated, every object should be processed at
  9430.  once, one step at a time.
  9431.  
  9432.  If it is not important to preserve the background, animation can be
  9433.  performed using the _GPSet option. If the border of the bounding rectangle
  9434.  around the image is as large as or larger than the maximum distance the
  9435.  object will move, then each time the image is put in a new location, the
  9436.  border will erase all traces of the image in the old location.
  9437.  
  9438.  Using Bit-Mapped Images
  9439.  
  9440.  The process of animating using bit-mapped images follows these eight steps:
  9441.  
  9442.    1. Perform regular graphics initialization (select graphics mode, check
  9443.       for error, and so on).
  9444.  
  9445.    2. Draw the graphics image using the MSGraph unit procedures and
  9446.       functions.
  9447.  
  9448.    3. Use the _ImageSize function to determine the amount of memory required
  9449.       to store the image.
  9450.  
  9451.    4. Use the GetMem procedure to allocate the amount of memory needed (as
  9452.       found in step 2).
  9453.  
  9454.    5. Call _GetImage to copy the bit map of pixels from the screen to the
  9455.       memory buffer created in step 3.
  9456.  
  9457.    6. Call _PutImage to display the image stored in memory. This display can
  9458.       be at any location on the screen.
  9459.  
  9460.    7. Repeat steps 3 through 6 (possibly with different images) until
  9461.       finished with animation.
  9462.  
  9463.    8. Restore the screen and exit the program.
  9464.  
  9465.  An Example of Bit-Mapped Animation
  9466.  
  9467.  The program ANIMATE.PAS demonstrates this process, drawing a rectangle and
  9468.  then redisplaying it at random locations on the screen.
  9469.  
  9470.       PROGRAM Animate;
  9471.       { ANIMATE.PAS: Demonstrates animation using image buffers }
  9472.  
  9473.       USES
  9474.           MSGraph, Crt;
  9475.  
  9476.       CONST
  9477.           max_buffer = 65520;
  9478.  
  9479.       VAR
  9480.           q      : Integer;
  9481.           vc     : _VideoConfig;
  9482.           buffer : POINTER;
  9483.           imsize : LongInt;
  9484.           x0, y0 : Integer;
  9485.           x, y   : Integer;
  9486.  
  9487.       BEGIN  { Begin main program. }
  9488.  
  9489.           _ClearScreen( _GClearScreen);
  9490.  
  9491.           { Set the video mode and check for success }
  9492.           q := _SetVideoMode( _MaxResMode );
  9493.           IF (q = 0) THEN
  9494.               BEGIN
  9495.               Writeln( 'Graphics mode unavailable; hit Return to continue' );
  9496.               Readln;
  9497.               q := _SetVideoMode( _DefaultMode );
  9498.               Halt( 0 );
  9499.               END;
  9500.  
  9501.           { Find out some screen characteristics. }
  9502.           _GetVideoConfig( vc );
  9503.  
  9504.           { Draw and store a simple figure. }
  9505.           _SetColor( 3 );
  9506.           x := vc.NumXPixels DIV 4;
  9507.           y := vc.NumYPixels DIV 4;
  9508.  
  9509.           _Rectangle( _GFillInterior, 0, 0, x, y );
  9510.           imsize := _ImageSize( 0, 0, x, y );
  9511.           IF (imsize > max_buffer) THEN
  9512.               BEGIN
  9513.               Writeln( 'Image too big.' );
  9514.               Readln;
  9515.               Halt( 0 );
  9516.               END
  9517.           ELSE
  9518.               BEGIN
  9519.               GetMem( buffer, imsize );
  9520.               IF (buffer = NIL) THEN
  9521.                   BEGIN
  9522.                   Writeln( 'Not enough heap memory.' );
  9523.                   Readln;
  9524.                   Halt( 0 );
  9525.                   END;
  9526.               END;
  9527.  
  9528.           _GetImage( 0, 0, x, y, buffer^ );
  9529.           _ClearScreen( _GClearScreen );
  9530.  
  9531.           { Draw axes centered on the screen }
  9532.           _SetColor (2);
  9533.           x0 := vc.NumXPixels DIV 2 -1;
  9534.           y0 := vc.NumYPixels DIV 2 -1;
  9535.           _MoveTo ( x0 ,0);
  9536.           _LineTo( x0, vc.NumYPixels );
  9537.           _MoveTo( 0, y0 );
  9538.           _LineTo( vc.NumXPixels, y0 );
  9539.  
  9540.           _SetTextPosition(1,1);
  9541.           _OutText( '_Gxor');
  9542.           WHILE NOT KeyPressed DO
  9543.               BEGIN
  9544.               _PutImage( Random( vc.NumXPixels - x ),
  9545.                         Random( vc.NumYPixels - y ), buffer^, _Gxor );
  9546.               Delay( 500 );
  9547.               END;
  9548.  
  9549.           _ClearScreen( _GClearScreen );
  9550.           q := _SetVideoMode( _DefaultMode );
  9551.       END.
  9552.  
  9553.  Initializing Graphics and Drawing the Image
  9554.  
  9555.  The ANIMATE.PAS program begins by clearing the screen, initializing the
  9556.  random-number generator, and setting the video mode to the highest
  9557.  resolution possible.
  9558.  
  9559.  The following code fragment draws and stores a simple image:
  9560.  
  9561.          { Draw and store a simple figure. }
  9562.           _SetColor( 3 );
  9563.           x := vc.NumXPixels DIV 4;
  9564.           y := vc.NumYPixels DIV 4;
  9565.  
  9566.           _Rectangle( _GFillInterior, 0, 0, x, y );
  9567.           imsize := _ImageSize( 0, 0, x, y );
  9568.           IF (imsize > max_buffer) THEN
  9569.               BEGIN
  9570.               Writeln( 'Image too big.' );
  9571.               Readln;
  9572.               Halt( 0 );
  9573.               END
  9574.           ELSE
  9575.               BEGIN
  9576.               GetMem( buffer, imsize );
  9577.               IF (buffer = NIL) THEN
  9578.                   BEGIN
  9579.                   Writeln( 'Not enough heap memory.' );
  9580.                   Readln;
  9581.                   Halt( 0 );
  9582.                   END;
  9583.               END;
  9584.  
  9585.           _GetImage( 0, 0, x, y, buffer^ );
  9586.           _ClearScreen( _GClearScreen );
  9587.  
  9588.  Allocating Memory
  9589.  
  9590.  The _Rectangle procedure draws a rectangle that is about one-sixteenth the
  9591.  size of the screen. The _ImageSize function uses the same bounding rectangle
  9592.  measurements to determine the amount of memory required to hold this figure.
  9593.  The GetMem procedure then allocates the necessary memory space for the
  9594.  image. Because GetMem can allocate at most 65,520 bytes (64K-16), you must
  9595.  check the image size before requesting the buffer. If the image is larger
  9596.  than 65,520 bytes, you will need to allocate additional buffers and copy
  9597.  part of the image to each buffer.
  9598.  
  9599.  The _GetImage procedure copies the image from the screen and stores it into
  9600.  the memory area specified by buffer. Finally, the _ClearScreen procedure
  9601.  clears this image off the screen. A more complex program could make use of
  9602.  the active and visual pages discussed in the previous section so that the
  9603.  image is drawn on the active page and the _ClearScreen procedure is not
  9604.  needed.
  9605.  
  9606.  Next, the program draws a coordinate axis centered on the screen. This will
  9607.  clarify later how the stored image interacts with images already displayed
  9608.  on the screen.
  9609.  
  9610.  Displaying the Image
  9611.  
  9612.  The program then repeatedly calls _PutImage to display the stored image at
  9613.  random locations on the screen. The process ends when a key is pressed. Once
  9614.  a key is pressed, the program ends with a call to restore the video mode.
  9615.  
  9616.           WHILE NOT KeyPressed DO
  9617.               BEGIN
  9618.               _PutImage( Random( vc.NumXPixels - x ),
  9619.                    Random( vc.NumYPixels - y ), buffer^, _Gxor );
  9620.               Delay( 500 );
  9621.               END;
  9622.  
  9623.           _ClearScreen( _GClearScreen );
  9624.           q := _SetVideoMode( _DefaultMode );
  9625.       END.
  9626.  
  9627.  The _PutImage procedure takes four arguments. The first two specify the x
  9628.  and y coordinates of the upper left corner where the image is to be
  9629.  displayed from the memory buffer. The third argument specifies the memory
  9630.  buffer created by GetMem and used by _GetImage to store the bit map.
  9631.  Finally, the last argument specifies the interaction between the stored
  9632.  image and the currently displayed image. Notice that in this case (using the
  9633.  _Gxor argument) that the image is inverted when it overlaps a currently
  9634.  displayed figure (like the axes or another rectangle).
  9635.  
  9636.  
  9637.  Chapter 14  Using Fonts
  9638.  ───────────────────────────────────────────────────────────────────────────
  9639.  
  9640.  You can write QuickPascal programs that generate graphics and display text.
  9641.  In any graphics image, QuickPascal can display various styles and sizes of
  9642.  type. These collections of stylized text characters are called "fonts."
  9643.  Fonts are simple to learn and easy to use. Yet they can add a touch of
  9644.  polish to your program.
  9645.  
  9646.  This chapter explains how to use fonts. It assumes you have already read
  9647.  Chapter 13, "Using Graphics." You should understand such terms as "graphics
  9648.  mode" and "text mode," and be familiar with such procedures as _SetVideoMode
  9649.  and _MoveTo.
  9650.  
  9651.  Note that the QuickPascal fonts can be used only in graphics modes. Fonts
  9652.  cannot be used in text modes.
  9653.  
  9654.  
  9655.  14.1  Overview of QuickPascal Fonts
  9656.  
  9657.  Each font in QuickPascal consists of a typeface and several type sizes.
  9658.  
  9659.  "Typeface" is a printer's term that refers to the style of the displayed
  9660.  text── Courier, for example, or Roman. The list on the following page shows
  9661.  six of the typefaces available with the QuickPascal font functions.
  9662.  
  9663.  "Type size" measures the screen area occupied by individual characters. This
  9664.  term is also borrowed from the printer's lexicon, but for our purposes, it
  9665.  is specified in units of screen pixels. For example, "Courier 169" denotes
  9666.  text of Courier typeface, with each character occupying a screen area of 16
  9667.  vertical pixels by 9 horizontal pixels.
  9668.  
  9669.  The QuickPascal font functions use two methods to create fonts. The first
  9670.  technique generates the typefaces Courier, Helv, and Tms Rmn through a
  9671.  "bit-mapping" technique. Bit mapping defines character images with binary
  9672.  data. Each bit in the map corresponds to a screen pixel. If a bit is 1, its
  9673.  associated pixel is set to the current screen color. A bit value of 0 clears
  9674.  the pixel. Video adapters use this same technique to display text in
  9675.  graphics mode.
  9676.  
  9677.  The second method creates the remaining three type styles──Modern, Script,
  9678.  and Roman──as "vector mapped" fonts. Vector mapping represents each
  9679.  character in terms of lines and arcs. In a literal sense, vector-mapped
  9680.  characters are drawn on the screen. You might think of bit-mapped characters
  9681.  as being stenciled.
  9682.  
  9683.  Each method of creating fonted text has advantages and disadvantages.
  9684.  Bit-mapped characters are formed more completely since the pixel mapping is
  9685.  predetermined. However, they cannot be scaled to arbitrary sizes.
  9686.  Vector-mapped text can be scaled to any size, but the characters lack the
  9687.  solid appearance of the bit-mapped characters.
  9688.  
  9689.  Any function or procedure affecting the current graphics position (such as
  9690.  the _MoveTo procedure or the _LineTo procedure) will also affect the font
  9691.  display when _OutGText is called. Other routines (such as _SetColor or
  9692.  _RemapPalette) that affect drawing characteristics also affect font text
  9693.  output.
  9694.  
  9695.  The QuickPascal fonts appear on your screen as shown on page 216 in the
  9696.  printed version of this book.
  9697.  
  9698.  Table 14.1 describes the characteristics of each font. Notice that
  9699.  bit-mapped fonts come in preset sizes as measured in pixels. The exact size
  9700.  of any font character depends on the screen resolution and display type.
  9701.  
  9702.  Table 14.1  Typefaces and Type Sizes in QuickPascal
  9703.  
  9704.  Typeface        Mapping           Size (in Pixels)         Spacing
  9705.  
  9706.  Courier         Bit               138, 169, 2012           Fixed
  9707.  
  9708.  Helv            Bit               135, 167, 208            Proportional
  9709.                                    1315, 166, 198
  9710.  
  9711.  Tms Rmn         Bit               105, 126, 158            Proportional
  9712.                                    169, 2012, 2616
  9713.  
  9714.  Modern          Vector            Scaled                   Proportional
  9715.  
  9716.  Script          Vector            Scaled                   Proportional
  9717.  
  9718.  Roman           Vector            Scaled                   Proportional
  9719.  
  9720.  
  9721.  14.2  Using Fonts in QuickPascal
  9722.  
  9723.  Data for both bit-mapped and vector-mapped fonts reside in files on disk. A
  9724.  .FON extension identifies the files. The names of the .FON files indicate
  9725.  their content. For example, the MODERN.FON, ROMAN.FON, and SCRIPT.FON files
  9726.  hold data for the three vector-mapped fonts.
  9727.  
  9728.  The QuickPascal .FON files are identical to the font files supplied with
  9729.  Microsoft QuickC(R), Version 2.0, as well as being identical to the .FON
  9730.  files used in the Microsoft Windows operating environment. Consequently, you
  9731.  can use any of the Windows .FON files with the QuickPascal font functions.
  9732.  The Windows .FON files are also available for purchase separately. In
  9733.  addition, several vendors offer software that create or modify .FON files,
  9734.  allowing you to design your own fonts.
  9735.  
  9736.  Your programs should follow these four steps to display fonted text:
  9737.  
  9738.    1. Set a graphics video mode
  9739.  
  9740.    2. Register fonts
  9741.  
  9742.    3. Set the current font from the register
  9743.  
  9744.    4. Display text using the current font
  9745.  
  9746.  Sections 14.2.1-14.2.3 describe each of the font-specific steps in detail.
  9747.  The procedure for using video modes for graphics is discussed in Section
  9748.  13.3. An example program in the final section of this chapter demonstrates
  9749.  how to display the various fonts available in the QuickPascal .FON files.
  9750.  
  9751.  
  9752.  14.2.1  Registering Fonts
  9753.  
  9754.  The fonts you plan to use must be organized into a list in memory, a process
  9755.  called "registering." The register list contains information about the
  9756.  available .FON files. You register fonts by calling the function
  9757.  _RegisterFonts. This function reads header information from the specified
  9758.  .FON files. It builds a list of file information but does not read mapping
  9759.  data from the files.
  9760.  
  9761.  The MSGraph unit defines the _RegisterFonts function as
  9762.  
  9763.       Function _RegisterFonts( PathName : CSTRING ) : Integer
  9764.  
  9765.  The argument PathName is a string containing a file name. The file name is
  9766.  the name of the .FON file for the desired font. The file name can include
  9767.  wild cards, allowing you to register several fonts with one call to
  9768.  _RegisterFonts. For example, the function call below registers all of the
  9769.  .FON files in the current directory and checks for a successful
  9770.  registration:
  9771.  
  9772.       result := _RegisterFonts( '*.FON' );
  9773.       IF (result < 0) THEN
  9774.           BEGIN
  9775.           Writeln( 'Unable to register fonts');
  9776.           Halt( 0 );
  9777.           END;
  9778.  
  9779.       { the rest of your fonts program goes here }
  9780.  
  9781.  As illustrated above, the _RegisterFonts function returns a negative number
  9782.  if it is unable to register any fonts or if the .FON file is corrupt. If it
  9783.  successfully reads one or more .FON files, _RegisterFonts returns the number
  9784.  of fonts registered.
  9785.  
  9786.  
  9787.  14.2.2  Setting the Current Font
  9788.  
  9789.  To set a font as the current font, call the function _SetFont. This
  9790.  function checks to see if the requested font is registered, then reads the
  9791.  mapping data from the appropriate .FON file. A font must be registered and
  9792.  marked current before your program can display text using that font.
  9793.  
  9794.  The MSGraph unit defines the _SetFont function as
  9795.  
  9796.       Function _SetFont( Options : CSTRING ) :Integer
  9797.  
  9798.  The Options argument is a string that describes the desired characteristics
  9799.  of the font. The string uses letter codes that describe the desired font, as
  9800.  outlined here:
  9801.  
  9802.  Option Code                 Meaning
  9803.  
  9804.  t'FontName'                 Typeface of the font in single quotes. The
  9805.                              FontName string is one of the following:
  9806.  
  9807.                                   courier          modern
  9808.                                   helv             script
  9809.                                   tms rmn          roman
  9810.  
  9811.                              Notice that the FontName string is surrounded by
  9812.                              a pair of two single quotes. This is necessary
  9813.                              to embed the FontName string within the Options
  9814.                              string, and Pascal uses single quotes to specify
  9815.                              a string. Notice the space in "tms rmn."
  9816.  
  9817.                              Other products' font files use other names for
  9818.                              FontName. Refer to the vendor's documentation
  9819.                              for these names.
  9820.  
  9821.  hy                          Character height, where y is the height in
  9822.                              pixels.
  9823.  
  9824.  wx                          Character width, where x is the width in pixels.
  9825.  
  9826.  f                           Select only a fixed-spaced font.
  9827.  
  9828.  p                           Select only a proportional-spaced font.
  9829.  
  9830.  v                           Select only a vector-mapped font.
  9831.  
  9832.  r                           Select only a bit-mapped font.
  9833.  
  9834.  b                           Select the best fit from the registered fonts.
  9835.                              This option instructs _SetFont to accept the
  9836.                              closest-fitting font if a font of the specified
  9837.                              size is not registered.
  9838.  
  9839.                              If at least one font is registered, the b option
  9840.                              always guarantees that _SetFont will be able to
  9841.                              set a current font. If you do not specify the b
  9842.                              option and an exact matching font is not
  9843.                              registered, _SetFont will fail. In this case,
  9844.                              any existing current font remains current.
  9845.  
  9846.                              The _SetFont function uses four criteria for
  9847.                              selecting the best fit. In descending order of
  9848.                              precedence the four criteria are pixel height,
  9849.                              typeface, pixel width, and spacing (fixed or
  9850.                              proportional). If you request a vector-mapped
  9851.                              font, _SetFont sizes the font to correspond with
  9852.                              the specified pixel height and width. If you
  9853.                              request a bit-mapped font, _SetFont chooses the
  9854.                              closest available size. If the requested type
  9855.                              size for a bit-mapped font fits exactly between
  9856.                              two registered fonts, the smaller size takes
  9857.                              precedence.
  9858.  
  9859.  nx                          Select font number x, where x is less than or
  9860.                              equal to the value returned by _RegisterFonts.
  9861.                              For example, the option n3 makes the third
  9862.                              registered font current, assuming that three or
  9863.                              more fonts are registered.
  9864.  
  9865.                              This option is primarily useful for cycling
  9866.                              through all registered fonts in a loop. Because
  9867.                              .FON files often contain several fonts, and the
  9868.                              files are loaded into memory in reverse order
  9869.                              from which they are registered, it is difficult
  9870.                              to know which font will be number 3.
  9871.  
  9872.  Option codes are not case-sensitive and can be listed in any order. You can
  9873.  separate codes with spaces or any other character that is not a valid option
  9874.  code. The _SetFont function ignores all invalid codes.
  9875.  
  9876.  For example, the function call below specifies that the font should be a
  9877.  "script" typeface with a character height of 30 pixels and a character width
  9878.  of 24 pixels. If the function is unable to do this, a "best fit" font is
  9879.  requested. The multiple single quotes around script are required since the
  9880.  entire argument used by _SetFont is a string. The double single quote
  9881.  specifies to QuickPascal that the string contains a single quote.
  9882.  
  9883.       result := _SetFont( 't''script''h30w24b' );
  9884.       IF (result =  -1) THEN
  9885.           BEGIN
  9886.           Writeln( 'Unable to set requested font');
  9887.           Halt( 0 );
  9888.           END;
  9889.  
  9890.       Writeln( 'Font set' );
  9891.       { the rest of your font program goes here }
  9892.  
  9893.  As illustrated above, the _SetFont function returns a -1 if it is unable to
  9894.  set the requested font. If it successfully sets a current font, the value 0
  9895.  is returned.
  9896.  
  9897.  Once a font is set as the current font, the _SetFont function updates a data
  9898.  area with the parameters of the current font. The data area is in the form
  9899.  of a _FontInfo record, defined in the MSGraph unit as
  9900.  
  9901.     { structure for GetFontInfo }
  9902.  
  9903.         _FontInfo = RECORD
  9904.             fonttype  : Integer;      { b0 set = vector,clear = bit map     }
  9905.             ascent    : Integer;      { pix dist from top to baseline       }
  9906.             pixwidth  : Integer;      { character width in pixels, 0 = prop }
  9907.             pixheight : Integer;      { character height in pixels          }
  9908.             avgwidth  : Integer;      { average character width in pixels   }
  9909.             filename  : CSTRING[81];  { file name including path            }
  9910.             facename  : CSTRING[32];  { font name                           }
  9911.             END;
  9912.  
  9913.  If you wish to retrieve the parameters of the current font, call the
  9914.  function _GetFontInfo, which is defined in the MSGraph unit as
  9915.  
  9916.       Function _GetFontInfo( VAR FInfo : _FontInfo ) : Integer
  9917.  
  9918.  
  9919.  14.2.3  Displaying Text Using the Current Font
  9920.  
  9921.  Now you can display the font-based text. This step consists of two parts:
  9922.  
  9923.    1. Select a screen position for the text with the graphics procedure
  9924.       _MoveTo. Note that all of the font-based text is displayed using
  9925.       graphics functions. Consequently, the _MoveTo procedure (rather than
  9926.       the text procedure _SetTextPosition) positions the text. The _MoveTo
  9927.       procedure takes pixel coordinates as arguments. The coordinates specify
  9928.       the upper left point of the first character in the text string.
  9929.       Optionally, you can use the procedure _SetGTextVector to change the
  9930.       orientation of the text on the screen.
  9931.  
  9932.    2. Display the font-based text at that position with the procedure
  9933.       _OutGText.
  9934.  
  9935.  
  9936.  14.3  A Few Hints on Using Fonts
  9937.  
  9938.  Fonted text is simply another form of graphics, and using fonts effectively
  9939.  requires little programming effort. Still, there are a few things to watch:
  9940.  
  9941.    ■  Remember that the video mode should be set only once to establish a
  9942.       graphics mode. If you generate an image (as with the _Rectangle
  9943.       procedure) and wish to incorporate fonted text above it as a title,
  9944.       don't reset the video mode prior to calling the font routines. Doing so
  9945.       will blank the screen, destroying the original image.
  9946.  
  9947.    ■  The _SetFont function reads specified .FON files to obtain mapping data
  9948.       for the current font. Each call to _SetFont causes a disk access and
  9949.       overwrites the old font data in memory. If you wish to show text of
  9950.       different styles on the same screen, display all of the text of one
  9951.       font before moving on to the others. By minimizing the number of calls
  9952.       to _SetFont, you'll save time spent in disk I/O and memory reloads.
  9953.  
  9954.    ■  When your program finishes with the fonts, you might want to free the
  9955.       memory occupied by the register list. Call the _UnRegisterFonts
  9956.       procedure to do this. As its name implies, this procedure frees the
  9957.       memory previously allocated by _RegisterFonts. The register information
  9958.       for each type size of each font takes up approximately 140 bytes of
  9959.       memory. Thus the amount of memory returned by _UnRegisterFonts is
  9960.       significant only if you have many fonts registered.
  9961.  
  9962.    ■  As for screen aesthetics, the same suggestions for the printed page
  9963.       apply to fonted screen text. Typefaces are more effective when they are
  9964.       not competing with each other for attention. Restricting the number of
  9965.       styles per screen to one or two generally results in a more pleasing,
  9966.       less cluttered image.
  9967.  
  9968.  
  9969.  14.4  Example Program
  9970.  
  9971.  The QuickPascal font functions shine when they are used in conjunction with
  9972.  your other graphics functions. They allow you to dress up any image on the
  9973.  screen, yet they can make a visual impression when used by themselves, as
  9974.  the example program SAMPLER.PAS illustrates. This program displays sample
  9975.  text in all of the available fonts, then exits when a key is pressed. Make
  9976.  sure the .FON files are in the current directory before running the program.
  9977.  
  9978.  Notice that SAMPLER.PAS calls the graphics procedure _MoveTo to establish
  9979.  the starting position for each text string. Section 13.4 "Understanding
  9980.  Coordinate Systems," describes the _MoveTo procedure. The function _SetFont
  9981.  takes a character string as an argument. The string is an options list that
  9982.  specifies typeface and the best fit for a character height of 30 pixels and
  9983.  a width of 24 pixels.
  9984.  
  9985.       PROGRAM sampler;  { Demonstrates using different fonts }
  9986.  
  9987.       USES
  9988.            Crt, MSGraph;
  9989.  
  9990.       CONST
  9991.            CRLF = #13 + #10;
  9992.            nfonts = 6;
  9993.  
  9994.            texttypes : ARRAY[ 1..2, 1..nfonts ] OF CSTRING[8] =
  9995.               (
  9996.               ( 'roman', 'courier', 'helv', 'tms rmn', 'modern', 'script' ),
  9997.               ( 'ROMAN', 'COURIER', 'HELV', 'TMS RMN', 'MODERN', 'SCRIPT' )
  9998.               );
  9999.  
  10000.            faces : ARRAY[ 1..nfonts ] OF CSTRING[12] =
  10001.                 (
  10002.                  ' t''roman''',
  10003.                  ' t''cour''',
  10004.                  ' t''helv''',
  10005.                  ' t''tms rmn''',
  10006.                  ' t''modern''',
  10007.                  ' t''script'''
  10008.                 );
  10009.            fontpath : CSTRING = '*.FON';
  10010.       VAR
  10011.           list : CSTRING;
  10012.           vc   : _VideoConfig;
  10013.           i, a : Integer;
  10014.           stra : STRING[3];
  10015.           ch   : Char;
  10016.  
  10017.  
  10018.       BEGIN  { Begin main program }
  10019.  
  10020.            { Read header information from all .FON files in
  10021.              the current directory
  10022.            }
  10023.            a := _RegisterFonts( fontpath );
  10024.            IF  a < 0 THEN
  10025.                BEGIN
  10026.                _OutText('Error: Cannot register fonts.' + CRLF);
  10027.                Halt(1);
  10028.                END;
  10029.  
  10030.            { Set the highest available video mode }
  10031.            a := _SetVideoMode( _MaxResMode );
  10032.            Str( a, stra );
  10033.            _OutText( 'MaxresMode = ' + stra );
  10034.  
  10035.            { Copy video configuration into structure vc }
  10036.            _GetVideoConfig(vc);
  10037.  
  10038.            { Display six lines of sample text }
  10039.            FOR i := 1 TO nfonts DO
  10040.                 BEGIN
  10041.                 list := faces[i] + 'bh24w24' ;
  10042.                 a:= _SetFont( list );
  10043.  
  10044.                 IF ( a <> -1) THEN
  10045.                      BEGIN
  10046.                      _SetColor( i + 1);
  10047.                      _MoveTo( 0, i * 30 );
  10048.                      _OutGText( texttypes[2,i] );
  10049.                      _MoveTo( vc.NumXPixels DIV 2, i * 30 );
  10050.                      _OutGText( texttypes[1,i]+CRLF );
  10051.                      END
  10052.                 ELSE
  10053.                      BEGIN
  10054.                      a := _SetVideoMode(_DefaultMode);
  10055.                      _OutText('Error: Cannot set font.');
  10056.                      Halt(1);
  10057.                      END;
  10058.                END;
  10059.  
  10060.            ch := ReadKey;
  10061.  
  10062.            a := _SetVideoMode(_DefaultMode);
  10063.  
  10064.            _UnRegisterFonts;  { Returns memory used by fonts }
  10065.  
  10066.       END.
  10067.  
  10068.  
  10069.  Chapter 15  Object-Oriented Programming
  10070.  ───────────────────────────────────────────────────────────────────────────
  10071.  
  10072.  Object-oriented programming is widely hailed as the programming style of the
  10073.  future. QuickPascal offers you object-oriented programming today, through
  10074.  its object extensions to standard Pascal. Although they make only a few
  10075.  syntactic additions to the language, the QuickPascal object extensions
  10076.  provide a powerful and efficient framework for creating programs.
  10077.  
  10078.  
  10079.  15.1  Overview
  10080.  
  10081.  Standard Pascal programs, along with programs written in other procedural
  10082.  languages, are organized around a set of data structures, with separate
  10083.  procedures and functions manipulating the data. An example is a graphics
  10084.  program that declares each shape as a unique TYPE. Various routines draw,
  10085.  erase, and move the shapes, likely using a CASE statement to differentiate
  10086.  between them.
  10087.  
  10088.  Object-oriented programs operate differently. Instead of being organized
  10089.  around data, they are organized around a set of "objects." An object is a
  10090.  structure that combines both data and routines into one type. It is similar
  10091.  to a Pascal RECORD type, but can store both functions and procedures as well
  10092.  as data.
  10093.  
  10094.  Objects have a property called "inheritance." Once an object has been
  10095.  declared, another object can be derived that inherits all of the data and
  10096.  routines associated with the parent type. New data and routines can be
  10097.  added, or existing inherited routines modified.
  10098.  
  10099.  A graphics program that was written with object extensions to QuickPascal
  10100.  would declare an initial "generic shape" object. The generic shape would
  10101.  define all of the data and routines──such as draw, erase, and size──that
  10102.  were common to every shape. New shapes would be derived from the generic
  10103.  shape, and then these new shapes would declare additional data fields,
  10104.  override existing routines, and add new ones.
  10105.  
  10106.  One of the primary benefits of object-oriented programming is the ease with
  10107.  which programs can be changed and portions reused. In the hypothetical
  10108.  standard Pascal graphics application, to add an octagon shape to the
  10109.  program, you would need to declare an entire new type as well as modify each
  10110.  routine that dealt with the shapes. With object extensions to QuickPascal,
  10111.  you would define an octagon object, already derived from the generic shape
  10112.  object, and add or modify any data or routines the octagon exclusively used.
  10113.  The old routines would still operate the same way on old types of objects.
  10114.  Instead of making changes throughout the entire program, all of the changes
  10115.  would occur in one localized area and apply only to that object or its
  10116.  descendants.
  10117.  
  10118.  The example in Section 15.5 demonstrates basic object-oriented programming
  10119.  techniques.
  10120.  
  10121.  
  10122.  15.2  Object Programming Concepts
  10123.  
  10124.  Object-oriented extensions are based on four concepts: classes, objects,
  10125.  methods, and inheritance. A "class" is similar to a Pascal RECORD. It
  10126.  describes an overall structure for any number of types based upon it. The
  10127.  main difference between a class and a record is that a class combines data
  10128.  fields (called "instance variables") and procedures and functions (called
  10129.  "methods") that act upon the data. Instance variables can include standard
  10130.  Pascal data types as well as objects.
  10131.  
  10132.  An "object" is a variable of a class (often called a class instance). Like a
  10133.  class, an object is declared as a TYPE. All objects derived from a class are
  10134.  considered members of that class and share similar characteristics of the
  10135.  superclass.
  10136.  
  10137.  "Methods" are procedures and functions encapsulated in a class or object.
  10138.  Calling a method is referred to as "passing a message to an object." The
  10139.  object extensions to QuickPascal create programs that do most of their work
  10140.  by sending messages to objects, and by instructing objects to send messages
  10141.  to each other. Methods are stored in an object-type method table and do not
  10142.  occupy memory when an object is declared as a variable.
  10143.  
  10144.  Members of the same class exhibit similar behavior through inheritance. This
  10145.  means the variable instances and methods found in a superclass are also
  10146.  present in objects derived from the superclass. Additionally, objects have
  10147.  their own space for storing data and methods local to the object. If
  10148.  necessary, an object can also override a parent class's method, replacing
  10149.  the inherited method's instructions with its own. If it does, only the
  10150.  descendant object's methods are altered, while the parent's remain
  10151.  unchanged.
  10152.  
  10153.  
  10154.  15.3  Using Objects
  10155.  
  10156.  As mentioned before, the object extensions to QuickPascal add only a few new
  10157.  keywords and types. All of the standard Pascal identifiers, constructs, and
  10158.  routines are available when programming with objects. The differences in
  10159.  using object extensions are in the areas of declaring class and object data
  10160.  structures and of calling procedures and functions through methods.
  10161.  
  10162.  
  10163.  15.3.1  Setting the Method Compiler Directive
  10164.  
  10165.  The first step in using object extensions is to enable the Method compiler
  10166.  directive. The {$M+} directive should appear at the beginning of any source
  10167.  file that uses objects. (The {$M+} directive is enabled by default.) This
  10168.  directive instructs the compiler to check whether or not memory for an
  10169.  object has been allocated before the object's method is executed. See
  10170.  Appendix B, "Compiler Directives," for more information.
  10171.  
  10172.  
  10173.  15.3.2  Creating Classes
  10174.  
  10175.  Since all objects are derived from classes, classes are created first. A
  10176.  class should incorporate all data and methods that descendant objects will
  10177.  have in common.
  10178.  
  10179.  You use the following syntax to declare an object class:
  10180.  
  10181.       TYPE
  10182.         ClassName = OBJECT
  10183.            DataFields
  10184.              {PROCEDURE|FUNCTION}«Methods»
  10185.          END;
  10186.  
  10187.  The parts of the syntax are defined below:
  10188.  
  10189.  Argument                    Discription
  10190.  
  10191.  ClassName                   A unique name that identifies the class.
  10192.  
  10193.  OBJECT                      A QuickPascal keyword that instructs the
  10194.                              compiler to treat the structure as an object.
  10195.  
  10196.  DataFields                  The declaration of one or more data structures.
  10197.                              The syntax is the same as that used for
  10198.                              declaring the fields of a record.
  10199.  
  10200.  Methods                     A list of method declarations. Each method
  10201.                              declaration is like a procedure or function
  10202.                              heading, except that the name may be qualified
  10203.                              with the name of the class:
  10204.                              ClassName.MethodName. Although not required,
  10205.                              such a qualification is good programming style.
  10206.                              Methods are declared immediately following the
  10207.                              class and object type declarations.
  10208.  
  10209.  For example, the following code fragment creates a generic shape for a
  10210.  graphics program:
  10211.  
  10212.       TYPE
  10213.            shape = OBJECT
  10214.                 color: colors;
  10215.                 height, width: Integer;
  10216.                 PROCEDURE shape.init;
  10217.                 PROCEDURE shape.draw;
  10218.                 PROCEDURE shape.move(hoz, vert: Integer);
  10219.                 FUNCTION shape.area: Integer;
  10220.            END;
  10221.  
  10222.            PROCEDURE shape.init
  10223.                BEGIN { code for init method here }
  10224.                .
  10225.                .
  10226.                .
  10227.                END;
  10228.  
  10229.            PROCEDURE shape.draw;
  10230.                BEGIN  { code for draw method here }
  10231.                .      { remainder of methods }
  10232.                .
  10233.                .
  10234.                END;
  10235.  
  10236.  
  10237.  15.3.3  Creating Subclasses
  10238.  
  10239.  Once a class has been created, subclasses can be defined. The syntax for
  10240.  creating a subclass is similar to that of a class:
  10241.  
  10242.       TYPE
  10243.         ObjectName = OBJECT( ParentClass )
  10244.            DataFields
  10245.             {PROCEDURE|FUNCTION}«Methods» «; OVERRIDE »
  10246.          END;
  10247.  
  10248.  The two special aspects of declaring objects are the use of parent class and
  10249.  of overriding inherited methods. The argument ParentClass is the name of a
  10250.  parent class. Since the subclass is derived from a class, you would enclose
  10251.  the name of the class in parentheses.
  10252.  
  10253.  If the subclass redefines a method from the parent class, the OVERRIDE
  10254.  statement should appear after the method header.
  10255.  
  10256.  For example, the following code fragment declares a descendant of the shape
  10257.  class:
  10258.  
  10259.       TYPE
  10260.            circle = OBJECT(shape)
  10261.                 radius: Integer;
  10262.                 PROCEDURE circle.init; OVERRIDE;
  10263.                 PROCEDURE circle.draw; OVERRIDE;
  10264.                 FUNCTION circle.area: Integer; OVERRIDE;
  10265.                 PROCEDURE circle.changeradius(new_radius: Integer);
  10266.            END;
  10267.  
  10268.  Because the  circle type is being derived from the shape class, there is no
  10269.  need to declare all of the instance variables and methods from shape. The
  10270.  only variables and methods that need declaring are those that are new and
  10271.  exclusive to the circle object. In this case, the new items are the radius
  10272.  field and the changeradius method. A circle object will have color, height,
  10273.  width, and radius fields.
  10274.  
  10275.  Since the init, draw, and area methods will be different for circle than
  10276.  they were for shape, the OVERRIDE keyword instructs the compiler to use the
  10277.  method local to circle when one of these messages is passed to the object.
  10278.  
  10279.  
  10280.  15.3.4  Defining Methods
  10281.  
  10282.  After a method has been associated with an object, it must be defined.
  10283.  Methods are defined with the PROCEDURE or FUNCTION keywords. The actual
  10284.  statements that compose the method are defined after all classes and
  10285.  subclasses have been created. Either the PROCEDURE or FUNCTION keyword
  10286.  precedes the object name, followed by a period (.) and the method name.
  10287.  Methods that are overridden follow the same syntax. (See the example at the
  10288.  end of the chapter.)
  10289.  
  10290.  The first method you should define is one that initializes all of the
  10291.  object's data fields, allocates memory, or performs any other actions the
  10292.  object may need before being used. This method should be called immediately
  10293.  after space has been allocated for the object.
  10294.  
  10295.  Instance variables that belong to the object can be accessed from within a
  10296.  method by using their identifier preceded by the pseudovariable Self, as
  10297.  shown below:
  10298.  
  10299.       PROCEDURE circle.init;
  10300.           BEGIN
  10301.                Self.color  := blue;
  10302.                Self.height := 20;
  10303.                Self.width  := 20;
  10304.                Self.radius := 0;
  10305.           END;
  10306.  
  10307.  Self simply instructs the object to operate on itself.
  10308.  
  10309.  An object's data may be accessed by a program directly, as if the object
  10310.  were a record:
  10311.  
  10312.       the_radius := circle.radius;
  10313.  
  10314.  Also, to call a method belonging to the object from within a different
  10315.  method, you may precede it with the Self variable. In the code fragment
  10316.  below, Self.draw is equivalent to circle.draw.
  10317.  
  10318.       PROCEDURE circle.move(hoz, vert: Integer);
  10319.           BEGIN
  10320.           .
  10321.           .
  10322.           .
  10323.                Self.draw;
  10324.           END;
  10325.  
  10326.  Note that you are not restricted solely to using methods when you use object
  10327.  extensions to QuickPascal. Methods are only used with objects. Standard
  10328.  Pascal procedures and functions can be implemented to manipulate other forms
  10329.  of data.
  10330.  
  10331.  
  10332.  15.3.5  Using INHERITED
  10333.  
  10334.  The INHERITED keyword negates an override of an inherited method. If the
  10335.  class of method performs only a portion of what an object needs to have
  10336.  done, the parent method can be called from the descendant method.
  10337.  
  10338.  For example, suppose that in the shape initialization method, you set the
  10339.  following values:
  10340.  
  10341.       PROCEDURE shape.init;
  10342.           BEGIN
  10343.                color  := blue;
  10344.                height := 20;
  10345.                width  := 20;
  10346.           END;
  10347.  
  10348.  If  the circle object used these values, but overrode the method to
  10349.  initialize its own data field, INHERITED could be used to call the ancestor
  10350.  method. This would initialize the common fields without needing to
  10351.  initialize them in the descendant method.
  10352.  
  10353.       PROCEDURE circle.init;
  10354.           BEGIN
  10355.                radius := 0;
  10356.                INHERITED Self.init;
  10357.           END;
  10358.  
  10359.  
  10360.  15.3.6  Declaring Objects
  10361.  
  10362.  Declaring an object is similar to declaring a dynamic variable. The syntax
  10363.  is
  10364.  
  10365.       VAR
  10366.              ObjectIdentifier : Class
  10367.  
  10368.  ObjectIdentifier is the QuickPascal identifier for the object, and Class is
  10369.  the type of the object.
  10370.  
  10371.  For example, this code declares an object of the class circle:
  10372.  
  10373.       VAR
  10374.           my_circle: circle;
  10375.  
  10376.  
  10377.  15.3.7  Allocating Memory
  10378.  
  10379.  Before an object can be used in a program, memory space must be allocated
  10380.  for it. This is done with the Pascal New procedure:
  10381.  
  10382.       New( my_circle );
  10383.  
  10384.  A common mistake in object-oriented programming is forgetting to allocate
  10385.  memory for an object and then trying to access it. Allocating memory for
  10386.  objects should be one of the first actions of the program body.
  10387.  
  10388.  
  10389.  15.3.8  Calling Methods
  10390.  
  10391.  After classes and objects have been declared, and memory has been allocated
  10392.  for the object, you can call a method (that is, send a message to the
  10393.  object) from within the main body of the program to manipulate the object's
  10394.  instance variables. Sending a message is similar to calling a procedure or
  10395.  function in standard Pascal. The only difference is that you specify both
  10396.  the object and the method.
  10397.  
  10398.  For example, different methods for my_circle are executed by
  10399.  
  10400.       my_circle.draw;
  10401.       my_circle.move( 30, 30 );
  10402.       circle_area := my_circle.area;
  10403.  
  10404.  
  10405.  15.3.9  Testing Membership
  10406.  
  10407.  The Member function tests if a particular object is in a class, as shown
  10408.  below:
  10409.  
  10410.       IF Member( a_circle, shape ) THEN
  10411.          num_shapes := num_shapes + 1;
  10412.  
  10413.  The function is passed the object and the class. It returns True if the
  10414.  object is an instance of, or a decendant of, the class.
  10415.  
  10416.  
  10417.  15.3.10  Disposing of Objects
  10418.  
  10419.  When you are finished using an object, the memory allocated for it should be
  10420.  freed. This is done with the Dispose procedure:
  10421.  
  10422.       Dispose( my_circle );
  10423.  
  10424.  Before disposing of an object, be sure it will not be used further in the
  10425.  program.
  10426.  
  10427.  Often, a free method is declared to reallocate data-structure memory, close
  10428.  files, and perform other housecleaning. Such a method should be called
  10429.  before using Dispose.
  10430.  
  10431.  
  10432.  15.4  Object Programming Strategies
  10433.  
  10434.  The greatest difficulty facing programmers who are learning object
  10435.  extensions to QuickPascal is the need to plan and write their programs in an
  10436.  object-oriented manner. All too often, a programmer's first object-oriented
  10437.  programs exhibit a procedural style with objects sprinkled in haphazardly.
  10438.  Programming in this fashion reduces the value of object extensions for
  10439.  producing efficient and reusable code. Sections 15.4.1-15.4.5 discuss
  10440.  several issues you should keep in mind as you create object-oriented
  10441.  programs.
  10442.  
  10443.  
  10444.  15.4.1  Object Style Conventions
  10445.  
  10446.  Although style conventions for programs are often a matter of personal
  10447.  preference, adoption of certain style conventions for object programming can
  10448.  make your source code easier to read. For example, since both a Pascal
  10449.  record and an object use a period (.) to access their data fields and
  10450.  methods, it can be difficult to distinguish objects from records. This is
  10451.  made more complicated by the difficulty telling whether an identifier
  10452.  following an object is an instance variable or a method.
  10453.  
  10454.  Here are some style conventions for object programming:
  10455.  
  10456.    ■  Classes and objects are preceded with an uppercase "T." This identifies
  10457.       the variable as an object type, as shown below:
  10458.  
  10459.          Tcircle = OBJECT(Tshape)
  10460.  
  10461.    ■  Object instance variables are preceded with a lowercase "f." The "f"
  10462.       indicates at a glance that the identifier is a field. Identifiers
  10463.       without the "f" are methods, as in the following example:
  10464.  
  10465.          the_radius := Tcircle.fradius;
  10466.          Tcircle.draw;
  10467.  
  10468.    ■  Global variables are preceded with a lowercase "g." This is helpful in
  10469.       identifying objects that can be passed messages from objects outside
  10470.       their own class (see below):
  10471.  
  10472.          gTtemp_circle.color := blue;
  10473.  
  10474.  
  10475.  15.4.2  Object Reusability
  10476.  
  10477.  The essence of object-oriented programming is reusability. When you create
  10478.  objects, you should give some thought to their future use, both in terms of
  10479.  the current program and for later ones. It's best to create classes from
  10480.  which other objects can be derived. Inheriting methods is generally more
  10481.  important than inheriting data. On a larger scale, class libraries are
  10482.  useful for dealing with common tasks. A set of related classes can reside in
  10483.  a UNIT and be called at any time.
  10484.  
  10485.  
  10486.  15.4.3  Modularity
  10487.  
  10488.  Object extensions to QuickPascal lend themselves to modularized programs. A
  10489.  class's methods can easily be kept together, instead of being strung out
  10490.  through the source code. A properly constructed object program should
  10491.  require only a few localized modifications to add and alter methods.
  10492.  
  10493.  
  10494.  15.4.4  Methods
  10495.  
  10496.  Methods should be treated as replaceable components of the object building
  10497.  blocks of QuickPascal. Methods are designed to serve a single purpose; a
  10498.  method that is multipurpose is more difficult to modify because it performs
  10499.  a variety of tasks. Whenever you want to perform an action, create a method
  10500.  to do it. Methods should be short, at most several dozen statements in
  10501.  length.
  10502.  
  10503.  
  10504.  15.4.5  Data Fields
  10505.  
  10506.  It isn't necessary to declare an instance variable for each data item an
  10507.  object may use. If more than one object method uses a specific data item,
  10508.  the data should be incorporated as an instance variable. If only a single
  10509.  method accesses the data, it can be passed as a parameter to the method. You
  10510.  should use object instance variables in place of global variables to promote
  10511.  modularity.
  10512.  
  10513.  
  10514.  15.5  Example Program
  10515.  
  10516.  This example shows how a typical object-oriented program works. A class is
  10517.  declared (geo_shape), with two subclasses ( rectangle and circle). Both the
  10518.  subclasses demonstrate how to add instance variables and methods and how to
  10519.  override existing parent methods. The body of the OBJECTDE.PAS program has
  10520.  examples of defining methods, accessing instance variables, and declaring
  10521.  and disposing of objects.
  10522.  
  10523.       Program objectdemo;
  10524.       { Demonstrates object techniques with geometric shapes }
  10525.  
  10526.       {M+}
  10527.  
  10528.       TYPE
  10529.  
  10530.            geo_shape = OBJECT
  10531.                        area: Real;
  10532.                        height: Real;
  10533.                        what: STRING;
  10534.                        PROCEDURE geo_shape.init;
  10535.                        PROCEDURE geo_shape.say_what;
  10536.                        FUNCTION geo_shape.get_area : Real;
  10537.            END;
  10538.  
  10539.            rectangle = OBJECT(geo_shape)
  10540.                        len: Real;
  10541.                        FUNCTION rectangle.is_square : Boolean;
  10542.                        PROCEDURE rectangle.init; OVERRIDE;
  10543.                        FUNCTION rectangle.get_area : Real; OVERRIDE;
  10544.            END;
  10545.  
  10546.            circle = OBJECT(geo_shape)
  10547.                     radius: Real;
  10548.                     PROCEDURE circle.init; OVERRIDE;
  10549.                     FUNCTION circle.get_area : Real; OVERRIDE;
  10550.            END;
  10551.  
  10552.       PROCEDURE geo_shape.init;
  10553.       BEGIN
  10554.           Self.area := 0;
  10555.           Self.height := 0;
  10556.           Self.what := 'Geometric shape';
  10557.       END;
  10558.  
  10559.       PROCEDURE geo_shape.say_what;
  10560.       BEGIN
  10561.           Writeln(Self.what);
  10562.       END;
  10563.  
  10564.       FUNCTION geo_shape.get_area : Real;
  10565.       BEGIN
  10566.           Self.area := Self.height * Self.height;
  10567.           get_area := Self.height;
  10568.       END;
  10569.  
  10570.       PROCEDURE circle.init;
  10571.       BEGIN
  10572.           INHERITED Self.init;
  10573.           Self.radius := 4;
  10574.           Self.what := 'circle';
  10575.       END;
  10576.  
  10577.       FUNCTION circle.get_area: Real;
  10578.       BEGIN
  10579.           Self.area := ( Pi * Sqr( Self.radius ) );
  10580.           get_area := Self.area;
  10581.       END;
  10582.  
  10583.       PROCEDURE rectangle.init;
  10584.       BEGIN
  10585.           INHERITED Self.init;
  10586.           Self.height := 5;
  10587.           Self.len := 5;
  10588.           Self.what := 'Rectangle';
  10589.       END;
  10590.  
  10591.       FUNCTION rectangle.is_square: Boolean;
  10592.       BEGIN
  10593.           is_square := False;
  10594.           IF Self.len = Self.height THEN
  10595.           is_square := True;
  10596.       END;
  10597.  
  10598.       FUNCTION rectangle.get_area: Real;
  10599.       BEGIN
  10600.           Self.area := ( Self.len * Self.height );
  10601.           get_area := Self.area;
  10602.       END;
  10603.  
  10604.       VAR
  10605.  
  10606.           the_circle : circle;
  10607.           the_rect : rectangle;
  10608.  
  10609.       BEGIN
  10610.           New( the_circle );
  10611.           the_circle.init;
  10612.           New( the_rect );
  10613.           the_rect.init;
  10614.  
  10615.           the_circle.say_what;
  10616.           Writeln( 'area: ', the_circle.get_area );
  10617.  
  10618.           Writeln;
  10619.  
  10620.           the_rect.say_what;
  10621.           Writeln( 'area: ', the_rect.get_area );
  10622.  
  10623.           Dispose( the_circle );
  10624.           Dispose( the_rect );
  10625.       END.
  10626.  
  10627.  
  10628.  Appendix A  ASCII Character Codes and Extended Key Codes
  10629.  ───────────────────────────────────────────────────────────────────────────
  10630.  
  10631.  
  10632.  A.1  ASCII Character Codes
  10633.  
  10634.  The ASCII character codes for printable and control characters are listed on
  10635.  the following two pages. The value of each character is its ordinal value
  10636.  within the type Char. For example, Ord ( 'P' ) returns the decimal value
  10637.  80.
  10638.  
  10639.  Ctl D  H  O  C Code  D  H  O   C   D  H  O   C   D  H  O   C
  10640.  
  10641.  ^@   0  00  000     NUL│  32  20  040    │  64  40  100  @ │  96  60  140  `
  10642.  ^A   1  01  001    SOH│  33  21  041  ! │  65  41  101  A │  97  61  141  a
  10643.  ^B   2  02  002    STX│  34  22  042  " │  66  42  102  B │  98  62  142  b
  10644.  ^C   3  03  003    ETX│  35  23  043  # │  67  43  103  C │  99  63  143  c
  10645.  ^D   4  04  004    EOT│  36  24  044  $ │  68  44  104  D │ 100  64  144  d
  10646.  ^E   5  05  005    ENQ│  37  25  045  % │  69  45  105  E │ 101  65  145  e
  10647.  ^F   6  06  006    ACK│  38  26  046  & │  70  46  106  F │ 102  66  146  f
  10648.  ^G   7  07  007    BEL│  39  27  047  ' │  71  47  107  G │ 103  67  147  g
  10649.  ^H   8  08  010    BS │  40  28  050  ( │  72  48  110  H │ 104  68  150  h
  10650.  ^I   9  09  011     HT │  41  29  051  ) │  73  49  111  I │ 105  69  151  i
  10651.  ^J  10  0A  012     LF │  42  2A  052  * │  74  4A  112  J │ 106  6A  152  j
  10652.  ^K  11  0B  013     VT │  43  2B  053  + │  75  4B  113  K │ 107  6B  153  k
  10653.  ^L  12  0C  014     FF │  44  2C  054  , │  76  4C  114  L │ 108  6C  154  l
  10654.  ^M  13  0D  015     CR │  45  2D  055  - │  77  4D  115  M │ 109  6D  155  m
  10655.  ^N  14  0E  016    SO │  46  2E  056  . │  78  4E  116  N │ 110  6E  156  n
  10656.  ^O  15  0F  017    SI │  47  2F  057  / │  79  4F  117  O │ 111  6F  157  o
  10657.  ^P  16  10  020    DLE│  48  30  060  0 │  80  50  120  P │ 112  70  160  p
  10658.  ^Q  17  11  021    DC1│  49  31  061  1 │  81  51  121  Q │ 113  71  161  q
  10659.  ^R  18  12  022    DC2│  50  32  062  2 │  82  52  122  R │ 114  72  162  r
  10660.  ^S  19  13  023    DC3│  51  33  063  3 │  83  53  123  S │ 115  73  163  s
  10661.  ^T  20  14  024    DC4│  52  34  064  4 │  84  54  124  T │ 116  74  164  t
  10662.  ^U  21  15  025    NAK│  53  35  065  5 │  85  55  125  U │ 117  75  165  u
  10663.  ^V  22  16  026    SYN│  54  36  066  6 │  86  56  126  V │ 118  76  166  v
  10664.  ^W  23  17  027    ETB│  55  37  067  7 │  87  57  127  W │ 119  77  167  w
  10665.  ^X  24  18  030    CAN│  56  38  070  8 │  88  58  130  X │ 120  78  170  x
  10666.  ^Y  25  19  031    EM │  57  39  071  9 │  89  59  131  Y │ 121  79  171  y
  10667.  ^Z  26  1A  032     SUB│  58  3A  072  : │  90  5A  132  Z │ 122  7A  172  z
  10668.  ^[  27  1B  033    ESC│  59  3B  073  ; │  91  5B  133  [ │ 123  7B  173  {
  10669.  ^\  28  1C  034    FS │  60  3C  074  < │  92  5C  134  \ │ 124  7C  174  |
  10670.  ^]  29  1D  035    GS │  61  3D  075  = │  93  5D  135  ] │ 125  7D  175  }
  10671.  ^^  30  1E  036    RS │  62  3E  076  > │  94  5E  136  ^ │ 126  7E  176  ~
  10672.  ^_  31  1F  037    US │  63  3F  077  ? │  95  5F  137  _ │ 127  7F  177 
  10673.  
  10674.  
  10675.  
  10676.  D   H  O   C  D   H  O   C  D   H  O   C  D   H  O   C
  10677.  
  10678.  128  80  200  Ç │ 160  A0  240  á │ 192  C0  300  └ │ 224  E0  340  α
  10679.  129  81  201  ü │ 161  A1  241  í │ 193  C1  301  ┴ │ 225  E1  341  ß
  10680.  130  82  202  é │ 162  A2  242  ó │ 194  C2  302  ┬ │ 226  E2  342  Γ
  10681.  131  83  203  â │ 163  A3  243  ú │ 195  C3  303  ├ │ 227  E3  343  π
  10682.  132  84  204  ä │ 164  A4  244  ñ │ 196  C4  304  ─ │ 228  E4  344  Σ
  10683.  133  85  205  à │ 165  A5  245  Ñ │ 197  C5  305  ┼ │ 229  E5  345  σ
  10684.  134  86  206  å │ 166  A6  246  ª │ 198  C6  306  ╞ │ 230  E6  346  µ
  10685.  135  87  207  ç │ 167  A7  247  º │ 199  C7  307  ╟ │ 231  E7  347  τ
  10686.  136  88  210  ê │ 168  A8  250  ¿ │ 200  C8  310  ╚ │ 232  E8  350  Φ
  10687.  137  89  211  ë │ 169  A9  251  ⌐ │ 201  C9  311  ╔ │ 233  E9  351  Θ
  10688.  138  8A  212  è │ 170  AA  252  ¬ │ 202  CA  312  ╩ │ 234  EA  352  Ω
  10689.  139  8B  213  ï │ 171  AB  253  ½ │ 203  CB  313  ╦ │ 235  EB  353  δ
  10690.  140  8C  214  î │ 172  AC  254  ¼ │ 204  CC  314  ╠ │ 236  EC  354  ∞
  10691.  141  8D  215  ì │ 173  AD  255  ¡ │ 205  CD  315  ═ │ 237  ED  355  φ
  10692.  142  8E  216  Ä │ 174  AE  256  « │ 206  CE  316  ╬ │ 238  EE  356  ε
  10693.  143  8F  217  Å │ 175  AF  257  » │ 207  CF  317  ╧ │ 239  EF  357  ∩
  10694.  144  90  220  É │ 176  B0  260  ░ │ 208  D0  320  ╨ │ 240  F0  360  ≡
  10695.  145  91  221  æ │ 177  B1  261  ▒ │ 209  D1  321  ╤ │ 241  F1  361  ±
  10696.  146  92  222  Æ │ 178  B2  262  ▓ │ 210  D2  322  ╥ │ 242  F2  362  ≥
  10697.  147  93  223  ô │ 179  B3  263  │ │ 211  D3  323  ╙ │ 243  F3  363  ≤
  10698.  148  94  224  ö │ 180  B4  264  ┤ │ 212  D4  324  ╘ │ 244  F4  364  ⌠
  10699.  149  95  225  ò │ 181  B5  265  ╡ │ 213  D5  325  ╒ │ 245  F5  365  ⌡
  10700.  150  96  226  û │ 182  B6  266  ╢ │ 214  D6  326  ╓ │ 246  F6  366  ÷
  10701.  151  97  227  ù │ 183  B7  267  ╖ │ 215  D7  327  ╫ │ 247  F7  367  ≈
  10702.  152  98  230  ÿ │ 184  B8  270  ╕ │ 216  D8  330  ╪ │ 248  F8  370  °
  10703.  153  99  231  Ö │ 185  B9  271  ╣ │ 217  D9  331  ┘ │ 249  F9  371  ∙
  10704.  154  9A  232  Ü │ 186  BA  272  ║ │ 218  DA  332  ┌ │ 250  FA  372  ·
  10705.  155  9B  233  ¢ │ 187  BB  273  ╗ │ 219  DB  333  █ │ 251  FB  373  √
  10706.  156  9C  234  £ │ 188  BC  274  ╝ │ 220  DC  334  ▄ │ 252  FC  374  ⁿ
  10707.  157  9D  235  ¥ │ 189  BD  275  ╜ │ 221  DD  335  ▌ │ 253  FD  375  ²
  10708.  158  9E  236  ₧ │ 190  BE  276  ╛ │ 222  DE  336  ▐ │ 254  FE  376  ■
  10709.  159  9F  237  ƒ │ 191  BF  277  ┐ │ 223  DF  337  ▀ │ 255  FF  377   
  10710.  
  10711.  
  10712.  
  10713.  A.2  Extended-Key Codes
  10714.  
  10715.  The Crt unit's ReadKey function returns a pair of values (instead of a
  10716.  single ASCII character) when a key on an extended keyboard is pressed. The
  10717.  first code is a null character (ASCII 0), which indicates that the next
  10718.  character will be an extended-key code.
  10719.  
  10720.  This table lists the key or key combination along with the decimal
  10721.  extended-key code. This information is also available in the on-line help
  10722.  (in a form that can be pasted directly into your program).
  10723.  
  10724. ╓┌────────────────┌───────────────┌────────────────┌───────────────┌─────────╖
  10725.  Key Type         Extended Key    Code             Extended Key    Code
  10726.  
  10727.  Arrow            UP               72              DOWN             80
  10728.                   LEFT             75              RIGHT            77
  10729.                   PGUP             73              PGDN             81
  10730.                   HOME             71              END              79
  10731.                   INS              82              DEL              83
  10732.  
  10733.  Key Type         Extended Key    Code             Extended Key    Code
  10734.  
  10735. 
  10736.  CTRL +           CTRL+PRTSC      114              CTRL+HOME       119
  10737.  Arrow            CTRL+LEFT       115              CTRL+END        117
  10738.                   CTRL+RIGHT      116
  10739.                   CTRL+PGUP       132              Null key          3
  10740.                   CTRL+PGDN       118              SHIFT+TAB        15
  10741.  
  10742.  ALT +            ALT+A            30              ALT+N            49
  10743.  Letter           ALT+B            48              ALT+O            24
  10744.                   ALT+C            46              ALT+P            25
  10745.                   ALT+D            32              ALT+Q            16
  10746.                   ALT+E            18              ALT+R            19
  10747.                   ALT+F            33              ALT+S            31
  10748.                   ALT+G            34              ALT+T            20
  10749.                   ALT+H            35              ALT+U            22
  10750.                   ALT+I            23              ALT+V            47
  10751.                   ALT+J            36              ALT+W            17
  10752.                   ALT+K            37              ALT+X            45
  10753.                   ALT+L            38              ALT+Y            21
  10754.  Key Type         Extended Key    Code             Extended Key    Code
  10755.  
  10756.                  ALT+L            38              ALT+Y            21
  10757.                   ALT+M            50              ALT+Z            44
  10758.  
  10759.  ALT +            ALT+1           120              ALT+6           125
  10760.  Number           ALT+2           121              ALT+7           126
  10761.                   ALT+3           122              ALT+8           127
  10762.                   ALT+4           123              ALT+9           128
  10763.                   ALT+5           124              ALT+0           129
  10764.                   ALT+MINUS       130              ALT+=           131
  10765.  
  10766.  Function         F1               59              F6               64
  10767.                   F2               60              F7               65
  10768.                   F3               61              F8               66
  10769.                   F4               62              F9               67
  10770.                   F5               63              F10              68
  10771.  
  10772.  SHIFT +          SHIFT+F1         84              SHIFT+F6         89
  10773.  Function         SHIFT+F2         85              SHIFT+F7         90
  10774.                   SHIFT+F3         86              SHIFT+F8         91
  10775.  Key Type         Extended Key    Code             Extended Key    Code
  10776.  
  10777.                  SHIFT+F3         86              SHIFT+F8         91
  10778.                   SHIFT+F4         87              SHIFT+F9         92
  10779.                   SHIFT+F5         88              SHIFT+F10        93
  10780.  
  10781.  CTRL +           CTRL+F1          94              CTRL+F6          99
  10782.  Function         CTRL+F2          95              CTRL+F7         100
  10783.                   CTRL+F3          96              CTRL+F8         101
  10784.                   CTRL+F4          97              CTRL+F9         102
  10785.                   CTRL+F5          98              CTRL+F10        103
  10786.  
  10787.  ALT +            ALT+F1          104              ALT+F6          109
  10788.  Function         ALT+F2          105              ALT+F7          110
  10789.                   ALT+F3          106              ALT+F8          111
  10790.                   ALT+F4          107              ALT+F9          112
  10791.                   ALT+F5          108              ALT+F10         113
  10792.  
  10793.  Extended         F11             133              F12             134
  10794.  Function         SHIFT+F11       135              SHIFT+F12       136
  10795.                   CTRL+F11        137              CTRL+F12        138
  10796.  Key Type         Extended Key    Code             Extended Key    Code
  10797.  
  10798.                  CTRL+F11        137              CTRL+F12        138
  10799.                   ALT+F11         139              ALT+F12         140
  10800.  
  10801.  
  10802.  
  10803.  
  10804.  Appendix B  Compiler Directives
  10805.  ───────────────────────────────────────────────────────────────────────────
  10806.  
  10807.  QuickPascal uses a variety of compiler directives to affect code output.
  10808.  They can be classed as
  10809.  
  10810.    ■  Switch Directives (on and off switches that control compiler options)
  10811.  
  10812.    ■  Parameter Directives (commands that require numbers or a file name)
  10813.  
  10814.    ■  Conditional Directives (commands that compile specific portions of the
  10815.       source file, depending if certain conditions are met)
  10816.  
  10817.  Compiler directives can be specified in the source file, in the QuickPascal
  10818.  environment, or at the DOS command line.
  10819.  
  10820.  
  10821.  B.1  Switch Directives
  10822.  
  10823.  Switch directives are identified by a single letter enclosed in braces. The
  10824.  letter is preceded by a dollar sign ($) and is followed by either a plus (+)
  10825.  that enables the switch, or a minus (-) that disables it. If you do not
  10826.  specify a switch directive, the default setting will automatically be used.
  10827.  
  10828.  A space must not come between the left brace and the dollar sign. If one
  10829.  does, the compiler considers the line to be a comment. This is useful for
  10830.  commenting out directives.
  10831.  
  10832.  Multiple switches can be set in a single statement. For example,
  10833.  
  10834.       {$A-,S+,R-,V+}
  10835.  
  10836.  Certain switch directives must be declared globally at the beginning of the
  10837.  program, just after the header. That is, they must appear before any data
  10838.  declaration or code. Other switch directives can occur locally, anywhere
  10839.  within the source file.
  10840.  
  10841.  The individual switch directives are described below.
  10842.  
  10843.  Align Data
  10844.  
  10845.  {$A+} or {$A-}
  10846.  Default: {$A+}
  10847.  Type: Local
  10848.  
  10849.  Changes from byte alignment to word alignment of typed constants and
  10850.  variables. When the align data directive is enabled, all typed constants and
  10851.  variables larger than one byte are aligned on a machine-word boundary.
  10852.  Although word alignment does not affect program execution on the 8088
  10853.  processor, on 80x86 processors, programs can execute faster since word-sized
  10854.  items are accessed in one memory cycle.
  10855.  
  10856.  When the directive is disabled, no alignment occurs, and all typed constants
  10857.  and variables are placed at the next available memory address.
  10858.  
  10859.  Boolean Evaluation
  10860.  
  10861.  {$B+} or {$B-}
  10862.  Default: {$B-}
  10863.  Type: Local
  10864.  
  10865.  Determines which type of code will be generated for the Boolean AND and OR
  10866.  operators. When the directive is enabled, code is created for the complete
  10867.  Boolean expression, even if the result is already known. If the directive is
  10868.  disabled (the default), the evaluation stops as soon as the result becomes
  10869.  evident, resulting in faster programs. Enable this switch when the second or
  10870.  subsequent operands in the Boolean expression include a procedure or
  10871.  function call that needs to be executed, regardless of the value of the
  10872.  first operand.
  10873.  
  10874.  Debug Information
  10875.  
  10876.  {$D+} or {$D-}
  10877.  Default: {$D+}
  10878.  Type: Global
  10879.  
  10880.  Inserts debugging information into your compiled program. When the directive
  10881.  is enabled, you can use the QuickPascal debugger to single step through code
  10882.  to view VARS, you need {$L+}. Additionally, when a run-time error is
  10883.  reported, QuickPascal will move to the line in the source code that caused
  10884.  the error.
  10885.  
  10886.  FAR Calls
  10887.  
  10888.  {$F+} or {$F-}
  10889.  Default: {$F-}
  10890.  Type: Local
  10891.  
  10892.  Determines which model is used for compiled procedures and functions. When
  10893.  the FAR calls directive is enabled, procedures and functions always use the
  10894.  FAR call model. If the directive is disabled, QuickPascal determines the
  10895.  call model based on location of the routine. If the procedure or function is
  10896.  declared in the INTERFACE portion of a unit, the FAR model is used.
  10897.  Otherwise, the NEAR model is used.
  10898.  
  10899.  See Chapter 12, "Advanced Topics," for a full discussion of FAR and NEAR
  10900.  call models.
  10901.  
  10902.  I/O Checking
  10903.  
  10904.  {$I+} or {$I-}
  10905.  Default: {$I+}
  10906.  Type: Local
  10907.  
  10908.  Generates code that examines the result of an input/output procedure. If an
  10909.  I/O error occurs while the directive is enabled, the program displays a
  10910.  run-time error and terminates. If the I/O checking directive is disabled,
  10911.  you should use the IOResult function to check for I/O errors. See Chapter 9,
  10912.  "Text Files," for more information on I/O routines.
  10913.  
  10914.  Local Symbol Data
  10915.  
  10916.  {$L+} or {$L-}
  10917.  Default: {$L+}
  10918.  Type: Global
  10919.  
  10920.  Generates symbol information in your compiled code. When the directive is
  10921.  enabled, you can use the QuickPascal debugger to examine and modify
  10922.  variables within your program. The local symbol data directive is used in
  10923.  conjunction with the debug information directive. If the debug information
  10924.  directive is disabled, the local symbol data directive is automatically
  10925.  disabled.
  10926.  
  10927.  Method Checking
  10928.  
  10929.  {$M+} or {$M-}
  10930.  Default: {$M-}
  10931.  Type: Global
  10932.  
  10933.  Determines if memory for an object has been allocated before an object's
  10934.  method is called. If the directive is enabled and an object hasn't had
  10935.  memory allocated, a run-time error is generated. You should always enable
  10936.  the method-checking directive when you are using objects and programming in
  10937.  object-oriented Pascal.
  10938.  
  10939.  Numeric Processing
  10940.  
  10941.  {$N+} or {$N-}
  10942.  Default: {$N-}
  10943.  Type: Global
  10944.  
  10945.  Specifies which model of floating-point code is generated by the compiler.
  10946.  When the directive is enabled, all floating-point calculations are performed
  10947.  by the 80x87 math coprocessor. If the directive is disabled, all real type
  10948.  calculations are performed in software using the run-time libraries.
  10949.  
  10950.  When the directive is enabled ($N+), the resulting program will run only on
  10951.  machines equipped with 80x87 coprocessors. Attempting to run the program on
  10952.  a computer that is not 80x87-equipped generates an error message informing
  10953.  the user that a coprocessor is required.
  10954.  
  10955.  When the directive is disabled ($N-), the resulting program will run on any
  10956.  machine, whether or not it has a coprocessor. However, the code will not use
  10957.  the coprocessor, even if present. Use of the $N- directive involves some
  10958.  differences in the way Extended and Comp data types are handled. See Chapter
  10959.  2, "Programming Basics," for more information.
  10960.  
  10961.  Range Checking
  10962.  
  10963.  {$R+} or {$R-}
  10964.  Default: {$R-}
  10965.  Type: Local
  10966.  
  10967.  Generates code that determines if all array and string indexing expressions
  10968.  are within bounds, and all assignments to scalar and subrange variables are
  10969.  within range. If range checking fails while the directive is enabled, a
  10970.  run-time error is reported. The range checking directive should be used
  10971.  only for debugging, as it decreases program performance and increases
  10972.  program size.
  10973.  
  10974.  Stack Checking
  10975.  
  10976.  {$S+} or {$S-}
  10977.  Default: {$S+}
  10978.  Type: Local
  10979.  
  10980.  Creates code that checks whether enough space remains on the stack for local
  10981.  variables before executing a procedure or function. If a stack overflow
  10982.  occurs when the directive is enabled, the program displays a run-time error
  10983.  message and terminates.
  10984.  
  10985.  VAR String Checking
  10986.  
  10987.  {$V+} or {$V-}
  10988.  Default: {$V+}
  10989.  Type: Local
  10990.  
  10991.  Compares the declared type of a VAR type string parameter with the string
  10992.  type actually being passed as a parameter when the directive is enabled. The
  10993.  types must be identical. If the directive is disabled, no type checking is
  10994.  performed.
  10995.  
  10996.  
  10997.  B.2  Parameter Directives
  10998.  
  10999.  Parameter directives instruct the compiler to take a certain action, based
  11000.  on parameters that are passed to the compiler. A space always separates the
  11001.  single directive letter and the parameter.
  11002.  
  11003.  The individual parameter directives are described below.
  11004.  
  11005.  Include
  11006.  
  11007.  {$IFilename}
  11008.  Type: Local
  11009.  
  11010.  Includes a named source file in the compilation immediately after the
  11011.  directive. QuickPascal assumes the .INC extension if none is specified. If
  11012.  the include file is in a different path from the one defined in the
  11013.  QuickPascal Environment command in the Options menu, you must type in the
  11014.  entire pathname.
  11015.  
  11016.  You cannot have an include file within a BEGIN...END block.
  11017.  
  11018.  Link
  11019.  
  11020.  {$LObjectFile}
  11021.  Type: Local
  11022.  
  11023.  Links a relocatable object file containing external routines written in
  11024.  assembly language. If the .OBJ file is in a different path from the one
  11025.  defined in the QuickPascal Environment command in the Options menu, you must
  11026.  type in the entire pathname. For additional information on linking with
  11027.  assembly language, see Chapter 12, "Advanced Topics."
  11028.  
  11029.  Memory
  11030.  
  11031.  {$MStackSize,MinHeap,MaxHeap}
  11032.  Default: {$M 16384, 0, 655360}
  11033.  Type: Global
  11034.  
  11035.  Sets stack and heap memory allocationy. Three parameters are passed: stack
  11036.  size, minimum heap size, and maximum heap size. Each parameter must be an
  11037.  integer within a specific range. See the list of ranges in Table B.1 below.
  11038.  
  11039.  Table B.1  Minimum and Maximum Memory Allocation Parameters
  11040.  
  11041.  Parameter      Minimum Value       Maximum Value
  11042.  
  11043.  StackSize      1024                65520
  11044.  MinHeap        0                   655360
  11045.  MaxHeap        MinHeap             655360
  11046.  
  11047.  
  11048.  B.3  Conditional Directives
  11049.  
  11050.  Conditional directives produce different code from the same source file if a
  11051.  conditional identifier is defined. The conditional directives are, in
  11052.  effect, a variation of the IF control statement. If a condition is true,
  11053.  code between the {$IFxxxCondition} and {$ENDIF} directives is compiled. If
  11054.  the condition is false, the compiler skips the code between the two
  11055.  directives. An optional {$ELSE} directive provides further control.
  11056.  
  11057.       {$DEFINE DEBUG}
  11058.  
  11059.       {$IFDEF DEBUG}
  11060.           Writeln('tax: ', tax, 'total: ', total)
  11061.       {$ELSE}
  11062.           Writeln('amount: ',tax + total);
  11063.       {ENDIF}
  11064.  
  11065.  You define and undefine identifiers with the {$DEFINE Name} and
  11066.  {$UNDEF Name} directives. Conditional identifiers are treated separately
  11067.  from similarly named program constants and variables. In addition to
  11068.  user-defined control identifiers, QuickPascal incorporates several
  11069.  predefined identifiers (listed in Table B.2 below).
  11070.  
  11071.  Table B.2  QuickPascal Predefined Conditional Identifiers
  11072.  
  11073.  Conditional
  11074.  Identifier     State                      Purpose
  11075.  
  11076.  VER10          Defined                    Indicates the compiler version
  11077.  MSDOS          Defined                    Indicates the operating system
  11078.  CPU86          Defined                    Indicates the 80x86 CPU family
  11079.  CPU87          Defined if an 80x87 is     Indicates the use of an 80x87 CPU
  11080.                 present at compile time
  11081.  
  11082.  Conditional directives may be placed anywhere in the source file. The
  11083.  individual conditional directives are described below.
  11084.  
  11085.  DEFINE
  11086.  
  11087.  {$DEFINE Identifier}
  11088.  
  11089.  Defines a conditional identifier. The identifier exists through the rest of
  11090.  the compilation, or until undefined with the UNDEF directive.
  11091.  
  11092.  ELSE
  11093.  
  11094.  {$ELSE}
  11095.  
  11096.  Compiles or skips the source based on the identifier in the preceding IF
  11097.  directive.
  11098.  
  11099.  ENDIF
  11100.  
  11101.  {$ENDIF}
  11102.  
  11103.  Ends the conditional compilation associated with the most recent IF
  11104.  directive.
  11105.  
  11106.  IFDEF
  11107.  
  11108.  {$IFDEF Identifier}
  11109.  
  11110.  Compiles the source that follows if the identifier has been defined.
  11111.  
  11112.  IFNDEF
  11113.  
  11114.  {$IFNDEF Identifier}
  11115.  
  11116.  Compiles the source following the directive if the identifier is undefined.
  11117.  
  11118.  IFOPT
  11119.  
  11120.  {$IFOPT Switch}
  11121.  
  11122.  Compiles the source that follows if the specified switch has been set as
  11123.  described. For example,
  11124.  
  11125.       {$IFOPT I-}
  11126.           result := IOResult;
  11127.       {$ENDIF}
  11128.  
  11129.  UNDEF
  11130.  
  11131.  {$UNDEF Identifier}
  11132.  
  11133.  Undefines a previously declared identifier for the remainder of the
  11134.  compilation or until the identifier is once again defined.
  11135.  
  11136.  
  11137.  Appendix C  Summary of Standard Units
  11138.  ───────────────────────────────────────────────────────────────────────────
  11139.  
  11140.  QuickPascal comes with several standard units that expand your programs'
  11141.  capabilities. These units──collections of variables, constants, data types,
  11142.  procedures, and functions──enhance your control over the screen and text,
  11143.  DOS routines, printer use, and graphics. QuickPascal automatically inserts
  11144.  the System standard unit whenever you compile a program. System supplies all
  11145.  of the QuickPascal standard procedures and functions.
  11146.  
  11147.  Crt Unit
  11148.  
  11149.  The Crt unit extends your control of the screen, keyboard input, and sound.
  11150.  Crt has procedures and functions that manipulate the screen colors, cursor
  11151.  position, text attributes, and windows. It also contains functions for
  11152.  checking keyboard activity and lets you turn the internal speaker on and
  11153.  off.
  11154.  
  11155.  Dos Unit
  11156.  
  11157.  The Dos unit gives you access to file-handling and operating-system
  11158.  routines. Programs that need to access DOS procedures and functions in order
  11159.  to set or get the time and date, to determine disk sizes and available disk
  11160.  space, or to control software interrupts can do so with the Dos unit. Other
  11161.  procedures and functions within the Dos unit let you manipulate file names
  11162.  and file attributes.
  11163.  
  11164.  Printer Unit
  11165.  
  11166.  The Printer unit lets you use a printer. It declares a text file, "Lst," and
  11167.  sends the output of Write and Writeln calls that use Lst to the printer port
  11168.  rather than to the screen. Printer accepts any type of formatting available
  11169.  to Write and Writeln. (Lst directs its output to LPT1. Make sure your
  11170.  printer connects to that port.)
  11171.  
  11172.  MSGraph Unit
  11173.  
  11174.  The MSGraph unit provides graphics in QuickPascal. It  supports a large
  11175.  number of procedures and functions that draw geometric shapes, fill figures
  11176.  with colors and patterns, manipulate images, create windows and viewports,
  11177.  and display text in various sizes and type styles. You can display graphics
  11178.  on a wide variety of video adapters and can choose the best combination of
  11179.  screen resolution and colors for your needs.
  11180.  
  11181.  System Unit
  11182.  
  11183.  The System unit contains all of the standard procedures and functions
  11184.  (Writeln, Pred, Copy, and so on), which you think of as always being
  11185.  available. You do not need to include System in the USES list; QuickPascal
  11186.  automatically uses the System unit whenever you compile a program.
  11187.  
  11188.  
  11189.  Appendix D  Quick Reference Guide
  11190.  ───────────────────────────────────────────────────────────────────────────
  11191.  
  11192.  
  11193.  D.1  Keywords, Procedures, and Functions
  11194.  
  11195.  Use the QP Advisor for complete descriptions and information.
  11196.  
  11197.  Abs( x )
  11198.  
  11199.       Returns the absolute value of x.
  11200.  
  11201.  ABSOLUTE
  11202.  
  11203.       Declares a variable to reside at a specific memory location.
  11204.  
  11205.  Addr( x )
  11206.  
  11207.       Returns the address of x, where x is any variable, typed constant,
  11208.       procedure identifier, or function identifier.
  11209.  
  11210.  AND
  11211.  
  11212.       Acts as logical or bitwise AND operator.
  11213.  
  11214.  Append( FileVariable)
  11215.  
  11216.       Opens an existing Text file FileVariable to append additional text.
  11217.  
  11218.  ArcTan( x )
  11219.  
  11220.       Returns the arctangent of x.
  11221.  
  11222.  ARRAY [ Ranges ] OF Type
  11223.  
  11224.       Defines Type as the base type, and Ranges as the range of indices, for
  11225.       an array type, variable, or constant.
  11226.  
  11227.  Assign( FileVariable, Name )
  11228.  
  11229.       Assigns the file variable FileVariable to the external file named in
  11230.       Name.
  11231.  
  11232.  BEGIN
  11233.  
  11234.       Starts a statement block.
  11235.  
  11236.  BlockRead( FileVariable, Buffer, Count «, RecordsRead» )
  11237.  
  11238.       Reads Count number of records into memory from the file specified by
  11239.       FileVariable, beginning with the first byte occupied by Buffer.
  11240.       Optionally BlockRead returns the number of records successfully read in
  11241.       RecordsRead.
  11242.  
  11243.  BlockWrite( FileVariable, Buffer, Count «, RecordsWritten» )
  11244.  
  11245.       Writes Count number of records from memory to the file specified by
  11246.       FileVariable, beginning with the first byte occupied by Buffer.
  11247.       Optionally BlockWrite returns the number of records successfully
  11248.       written in RecordsWritten.
  11249.  
  11250.  CASE Selector OF Constant: StatementBlock; ... END
  11251.  
  11252.       Executes the StatementBlock whose Constant matches Selector.
  11253.  
  11254.  ChDir( NewDir )
  11255.  
  11256.       Makes NewDir the current directory.
  11257.  
  11258.  Chr( x )
  11259.  
  11260.       Returns the ASCII character with ordinal value x.
  11261.  
  11262.  Close( FileVariable )
  11263.  
  11264.       Closes the open file specified by the file variable FileVariable.
  11265.  
  11266.  Concat( Str1 «, Str2 ,  Str3...» )
  11267.  
  11268.       Returns the  argument strings (Str1, Str2, etc.) as a single string.
  11269.  
  11270.  CONST
  11271.  
  11272.       Starts a constant definition section.
  11273.  
  11274.  Copy( String, Start, Count )
  11275.  
  11276.       Returns a substring of String, Count characters long, beginning with
  11277.       character number Start.
  11278.  
  11279.  Cos( x )
  11280.  
  11281.       Returns the cosine of x.
  11282.  
  11283.  CSeg
  11284.  
  11285.       Returns the value of the CS register.
  11286.  
  11287.  CSTRING «[Length]»
  11288.  
  11289.       Defines a variable or constant as a series of up to 255 characters
  11290.       ending in a null byte, as in the C programming language. The integer
  11291.       Length specifies the maximum length of the string.
  11292.  
  11293.  Dec( x «, Step» )
  11294.  
  11295.       Decrements the variable x by 1; if Step is specified, x is decremented
  11296.       by Step.
  11297.  
  11298.  Delete( String, Start, Count )
  11299.  
  11300.       Returns a copy of String with Count characters removed, beginning with
  11301.       character number Start.
  11302.  
  11303.  Dispose( p )
  11304.  
  11305.       Removes the dynamic variable that p points to and returns the memory to
  11306.       the heap.
  11307.  
  11308.  DIV
  11309.  
  11310.       Acts as the integer division operator. i DIV j returns the quotient of
  11311.       i divided by j rounded to the integer nearest zero.
  11312.  
  11313.  DO
  11314.  
  11315.       Introduces the statement block with WHILE, FOR, and WITH.
  11316.  
  11317.  DOWNTO
  11318.  
  11319.       Indicates that a FOR statement's end value is less than its start value
  11320.       and that the control variable is decremented by 1 with each iteration.
  11321.  
  11322.  DSeg
  11323.  
  11324.       Returns the value of the DS register.
  11325.  
  11326.  ELSE
  11327.  
  11328.       Begins the default clause in an IF...THEN...ELSE or CASE statement.
  11329.  
  11330.  END
  11331.  
  11332.       Ends a statement block.
  11333.  
  11334.  Eof «( FileVariable )»
  11335.  
  11336.       Returns the end-of-file status for the file FileVariable. Eof returns
  11337.       True if the end of the file has been reached, otherwise it returns
  11338.       False. If you omit FileVariable, Eof checks the status of the standard
  11339.       input file.
  11340.  
  11341.  Eoln «( FileVariable )»
  11342.  
  11343.       Returns the end-of-line status. Eoln returns True if the end of the
  11344.       line has been reached, otherwise it returns False. If you omit
  11345.       FileVariable, Eoln checks the status of the standard input file.
  11346.  
  11347.  Erase( FileVariable )
  11348.  
  11349.       Erases the file referred to by FileVariable.
  11350.  
  11351.  Exit
  11352.  
  11353.       In a procedure or function, causes control to return to the main
  11354.       program. In the main program, Exit halts program execution.
  11355.  
  11356.  Exp ( x )
  11357.  
  11358.       Returns the exponential of x, that is, the value e raised to the power
  11359.       of x.
  11360.  
  11361.  EXTERNAL
  11362.  
  11363.       Identifies a separately compiled procedure or function written in
  11364.       assembly language.
  11365.  
  11366.  FILE «OF ComponentType»
  11367.  
  11368.       Declares a file type composed of values of type ComponentType.
  11369.  
  11370.  FilePos( FileVariable )
  11371.  
  11372.       Returns the current position of an open file referred to by file
  11373.       variable FileVariable.
  11374.  
  11375.  FileSize( FileVariable )
  11376.  
  11377.       Returns the size (in bytes) of an open file referred to by file
  11378.       variable FileVariable.
  11379.  
  11380.  FillChar( Variable, Count, Character )
  11381.  
  11382.       Fills Count contiguous bytes of memory with Character (either an ASCII
  11383.       value or a literal character enclosed in single quotes), starting with
  11384.       the first byte occupied by Variable.
  11385.  
  11386.  Flush( FileVariable )
  11387.  
  11388.       Writes to disk the buffer of the text file referred to by FileVariable.
  11389.  
  11390.  FOR ControlVariable := StartVal {TO|DOWNTO} EndVal DO
  11391.        StatementBlock
  11392.  
  11393.       Executes StatementBlock as long as the value of ControlVariable is
  11394.       between its start value StartVal and its end value EndVal, inclusive.
  11395.       Use TO if EndVal is greater than StartVal, or DOWNTO if EndVal is less
  11396.       than StartVal. FOR increments (with TO) or decrements (with DOWNTO)
  11397.       ControlVariable by 1 each time it executes StatementBlock.
  11398.  
  11399.  FORWARD
  11400.  
  11401.       Declares a procedure but omits its definition until a second
  11402.       declaration. This permits mutually referencing procedures.
  11403.  
  11404.  Frac( x )
  11405.  
  11406.       Returns the fractional portion of the real number x.
  11407.  
  11408.  FreeMem( Pointer, Size )
  11409.  
  11410.       Frees Size bytes of dynamic memory at address Pointer.
  11411.  
  11412.  FUNCTION Identifier«(  «VAR» Param1«, Param2 ...» :Ptype1
  11413.           «; «VAR» Param3«, Param4 ...» :Ptype2 ...»)» :Typename
  11414.  
  11415.       Defines a function named Identifier that returns a value of type
  11416.       Typename. Any parameters (Param1, Param2, etc.) to the function must be
  11417.       declared along with their type (Ptype1, Ptype2, etc.). Every function
  11418.       must return a value.
  11419.  
  11420.  GetDir( Drive, Path )
  11421.  
  11422.       Returns the current directory in the string Path, given the integer
  11423.       Drive. Setting Drive to  0 uses the current drive, Drive to 1 uses
  11424.       drive A, Drive to 2 uses drive B, and so on.
  11425.  
  11426.  GetMem( Pointer, Size )
  11427.  
  11428.       Creates a new dynamic variable of Size bytes from heap memory, setting
  11429.       location Pointer.
  11430.  
  11431.  GOTO LabelName
  11432.  
  11433.       Unconditionally transfers program control to the statement at label
  11434.       LabelName.
  11435.  
  11436.  Halt«( Code )»
  11437.  
  11438.       Halts program execution and returns to DOS. You can optionally include
  11439.       the program's exit code.
  11440.  
  11441.  Hi( x )
  11442.  
  11443.       Returns the high-order byte of x, a word or integer.
  11444.  
  11445.  IF BooleanExpression THEN StatementBlock1 «ELSE StatementBlock2 »
  11446.  
  11447.       Executes StatementBlock1 if BooleanExpression is True, otherwise
  11448.       executes StatementBlock2.
  11449.  
  11450.  IMPLEMENTATION
  11451.  
  11452.       Indicates the beginning of the unit section that defines the unit's
  11453.       procedures and functions. The identifiers declared in this section are
  11454.       private.
  11455.  
  11456.  IN
  11457.  
  11458.       Acts as the member-of operator for sets. IN is used to test for the
  11459.       presence of an element in a set.
  11460.  
  11461.  Inc( x  «, Step» )
  11462.  
  11463.       Increments the variable x by 1; if Step is specified, x is incremented
  11464.       by Step.
  11465.  
  11466.  INHERITED
  11467.  
  11468.       Modifies a message to refer to the parent method.
  11469.  
  11470.  INLINE( MachineCode )
  11471.  
  11472.       Defines machine code that is inserted into the program.
  11473.  
  11474.  Insert( String, SubString, Start )
  11475.  
  11476.       Inserts the string SubString into the string String, beginning at
  11477.       character number Start. If the resulting string is more than 255
  11478.       characters long, it is truncated to 255 characters.
  11479.  
  11480.  Int( x )
  11481.  
  11482.       Returns the integral portion of the real number x.
  11483.  
  11484.  INTERFACE
  11485.  
  11486.       Indicates the beginning of the unit section that declares the
  11487.       variables, constants, procedures, and functions available to the
  11488.       calling program.
  11489.  
  11490.  INTERRUPT
  11491.  
  11492.       Declares a procedure as an interrupt procedure. Interrupt procedures
  11493.       may handle program interrupts.
  11494.  
  11495.  IOResult
  11496.  
  11497.       Returns the status of the most recent I/O operation. A status of 0
  11498.       indicates the I/O operation was successful.
  11499.  
  11500.  LABEL {Identifier| 0..9999}
  11501.  
  11502.       Declares the label Identifier or a number. Labels are the destination
  11503.       of GOTO statements.
  11504.  
  11505.  Length( String )
  11506.  
  11507.       Returns the length of the string String.
  11508.  
  11509.  Ln( x )
  11510.  
  11511.       Returns the natural logarithm of x.
  11512.  
  11513.  Lo( x )
  11514.  
  11515.       Returns the low-order byte of x, a word or integer.
  11516.  
  11517.  Mark( Pointer )
  11518.  
  11519.       Saves the current top of the heap in Pointer.
  11520.  
  11521.  MaxAvail
  11522.  
  11523.       Returns the size (in bytes) of the largest continuous block of free
  11524.       memory in the heap.
  11525.  
  11526.  MemAvail
  11527.  
  11528.       Returns the total free memory in the heap in bytes.
  11529.  
  11530.  Member( ObjectVariable, ClassId )
  11531.  
  11532.       Returns True if ObjectVariable is a member of the class ClassId, and
  11533.       False otherwise.
  11534.  
  11535.  MkDir( NewDir )
  11536.  
  11537.       Creates a new directory with the path given in the string NewDir.
  11538.  
  11539.  MOD
  11540.  
  11541.       Acts as the modular division operator. The expression i MOD j returns
  11542.       the remainder of i DIV j.
  11543.  
  11544.  Move( Source, Destination, Count )
  11545.  
  11546.       Copies Count bytes of Source to Destination.
  11547.  
  11548.  New(  Pointer)
  11549.  
  11550.       Allocates space for a new dynamic variable in the heap and sets Pointer
  11551.       to the address of the new variable. The type of pointer determines how
  11552.       much memory New allocates.
  11553.  
  11554.  NIL
  11555.  
  11556.       Indicates the value of a pointer that does not point to anything.
  11557.  
  11558.  NOT
  11559.  
  11560.       Acts as the logical or bitwise negation operator.
  11561.  
  11562.  OBJECT«(Parent)»
  11563.  
  11564.       Defines an object type or class, descended from the Parent class.
  11565.  
  11566.  Odd(  x )
  11567.  
  11568.       Returns True if the integral x is odd, False if x is even.
  11569.  
  11570.  OF
  11571.  
  11572.       Identifies the base type of an ARRAY, FILE, or SET, or introduces the
  11573.       constant list found in a CASE statement.
  11574.  
  11575.  Ofs( x )
  11576.  
  11577.       Returns the offset of x, a variable, typed constant, procedure, or
  11578.       function name.
  11579.  
  11580.  OR
  11581.  
  11582.       Acts as the logical or bitwise OR operator.
  11583.  
  11584.  Ord( x )
  11585.  
  11586.       Returns the ordinal number of x (an ordinal-type variable).
  11587.  
  11588.  OVERRIDE
  11589.  
  11590.       Redefines a parent method to do something new.
  11591.  
  11592.  PACKED
  11593.  
  11594.       Required by standard Pascal. Serves only to distinguish types in
  11595.       QuickPascal.
  11596.  
  11597.  ParamCount
  11598.  
  11599.       Returns the number of command-line parameters.
  11600.  
  11601.  ParamStr( i )
  11602.  
  11603.       Returns a string that is the i th command-line parameter. Parameter
  11604.       number 0 is the program path in DOS versions 3.1 and later.
  11605.  
  11606.  Pi
  11607.  
  11608.       Returns the value Pi (3.1415926535897932385). The precision of the
  11609.       value varies, depending on the floating-point hardware present.
  11610.  
  11611.  Pointer
  11612.  
  11613.       Defines a variable as a generic pointer type. A variable of type
  11614.       POINTER must be assigned to a variable of a specific pointer type
  11615.       before it can be dereferenced.
  11616.  
  11617.  Pos( SubString, String )
  11618.  
  11619.       Returns the starting position of string SubString in string String.
  11620.  
  11621.  Pred( x )
  11622.  
  11623.       Returns the predecessor of x in the list of values of its ordinal type.
  11624.  
  11625.  PROCEDURE Identifier«( «VAR» Param1«, Param2 ...» : Ptype1
  11626.                      «; «VAR» Param3«, Param4 ...» : Ptype2 ...»)»
  11627.  
  11628.       Defines a procedure named Identifier. Any parameters (Param1, Param2,
  11629.       etc.) to the procedure must be declared along with their type (Ptype1,
  11630.       Ptype2, etc.).
  11631.  
  11632.  PROGRAM «ProgName»
  11633.  
  11634.       Declares a program with the name ProgName.
  11635.  
  11636.  Ptr( Seg, Off )
  11637.  
  11638.       Converts a segment and offset to a pointer address;Seg and Off are both
  11639.       of type Word.
  11640.  
  11641.  Random«( Limit )»
  11642.  
  11643.       Returns a random real number between 0 and 1, or a random whole number
  11644.       between 0 and Limit.
  11645.  
  11646.  Randomize
  11647.  
  11648.       Initializes the random number generator.
  11649.  
  11650.  Read(«FileVariable,»Var1 «, Var2 ...»)
  11651.  
  11652.       Reads one or more values from the standard input device or, optionally,
  11653.       from the file specifier FileVariable.
  11654.  
  11655.  Readln(«FileVariable,»Var1 «, Var2 ... »)
  11656.  
  11657.       Executes the Read procedure, then skips to the beginning of the next
  11658.       line of the text file specified by FileVariable.
  11659.  
  11660.  RECORD FieldList END
  11661.  
  11662.       Creates a compound variable consisting of the items listed in
  11663.       FieldList. The different fields in a record can have different types.
  11664.  
  11665.  Release( Pointer )
  11666.  
  11667.       Returns the value of the heap-top pointer to Pointer, which was
  11668.       previously obtained by Mark.
  11669.  
  11670.  Rename( FileVariable, NewName )
  11671.  
  11672.       Renames the external file specified by file variable FileVariable to
  11673.       the name string NewName.
  11674.  
  11675.  REPEAT StatementBlock UNTIL BooleanExpression
  11676.  
  11677.       Executes StatementBlock as long as BooleanExpression remains False.
  11678.       When BooleanExpression becomes True, control passes to the statement
  11679.       following the UNTIL statement.
  11680.  
  11681.  Reset( FileVariable «, Size» )
  11682.  
  11683.       Opens the file specified by FileVariable with a data transfer unit size
  11684.       of Size.
  11685.  
  11686.  Rewrite( FileVariable «, Size» )
  11687.  
  11688.       Creates and opens a new file FileVariable, with a data transfer unit
  11689.       size of Size. If FileVariable already exists, it is truncated to a
  11690.       length of zero.
  11691.  
  11692.  RmDir( Dir )
  11693.  
  11694.       Removes the empty directory named Dir.
  11695.  
  11696.  Round( x )
  11697.  
  11698.       Rounds the real number x to the nearest whole number.
  11699.  
  11700.  RunError«( ErrorNum )»
  11701.  
  11702.       Halts program execution with the run-time error number ErrorNum, or
  11703.       run-time error 0 if ErrorNum is not specified.
  11704.  
  11705.  Seek( FileVariable, Pos )
  11706.  
  11707.       Moves the current position of the file specified by FileVariable to the
  11708.       position Pos.
  11709.  
  11710.  SeekEof «( FileVariable)»
  11711.  
  11712.       Returns the end-of-file status for the text file specified
  11713.       by FileVariable, skipping blanks, tabs, and end-of-line markers. The
  11714.       actual file position does not change.
  11715.  
  11716.  SeekEoln«( FileVariable )»
  11717.  
  11718.       Returns the end-of-line status for the text file specified by
  11719.       FileVariable, skipping blanks and tabs.
  11720.  
  11721.  Seg( x )
  11722.  
  11723.       Returns the segment containing x, a variable, typed constant,
  11724.       procedure, or function name.
  11725.  
  11726.  Self
  11727.  
  11728.       Refers to the object that received a message, used by methods.
  11729.  
  11730.  SET OF OrdinalType
  11731.  
  11732.       Identifies OrdinalType as the base type for a set type, constant, or
  11733.       variable.
  11734.  
  11735.  SetTextBuf( FileVariable, Buffer «, Size» )
  11736.  
  11737.       Assigns the text file FileVariable a buffer of Size bytes in memory
  11738.       starting at Buffer. If Size is omitted, SizeOf( Buffer) is assumed.
  11739.  
  11740.  SHL
  11741.  
  11742.       Acts as the bitwise shift-left operator. The expression i SHL j shifts
  11743.       the value of i to the left by j bits.
  11744.  
  11745.  SHR
  11746.  
  11747.       Acts as the bitwise shift-right operator. The expression i SHR j shifts
  11748.       the value of i to the right by j bits.
  11749.  
  11750.  Sin( x )
  11751.  
  11752.       Returns the sine of x.
  11753.  
  11754.  SizeOf( x )
  11755.  
  11756.       Returns the size of the variable, typed constant, or type x in bytes.
  11757.  
  11758.  SPtr
  11759.  
  11760.       Returns the value of the SP register, the current offset of the stack
  11761.       pointer in the stack segment.
  11762.  
  11763.  Sqr( x )
  11764.  
  11765.       Returns the square of x.
  11766.  
  11767.  Sqrt( x )
  11768.  
  11769.       Returns the square root of x, a positive integer or real number.
  11770.  
  11771.  SSeg
  11772.  
  11773.       Returns the value of the SS register, the stack segment address of the
  11774.       stack pointer.
  11775.  
  11776.  Str(  Number«: Width«: Decimals»», String)
  11777.  
  11778.       Converts the numeric value Number to the string String. The arguments
  11779.       Width and Decimals specify the total width and number of decimal places
  11780.       that will appear in the string.
  11781.  
  11782.  STRING« [ Length] »
  11783.  
  11784.       Defines a variable or constant as a series of up to 255 characters. The
  11785.       integer Length specifies the maximum length of the string.
  11786.  
  11787.  Succ( x )
  11788.  
  11789.       Returns the successor to x in the list of values of its ordinal type.
  11790.  
  11791.  Swap( x )
  11792.  
  11793.       Exchanges the high- and low-order bytes of x, an integer or word.
  11794.  
  11795.  THEN StatementBlock
  11796.  
  11797.       Used in the second half of an IF...THEN statement. If the condition in
  11798.       the IF portion of the statement is True, the statements in
  11799.       StatementBlock execute.
  11800.  
  11801.  TO
  11802.  
  11803.       Indicates that a FOR statement's ending value is greater than its
  11804.       starting value and that the control variable will be incremented by
  11805.       one.
  11806.  
  11807.  Trunc( x )
  11808.  
  11809.       Truncates a real value x to an integer.
  11810.  
  11811.  Truncate( FileVariable )
  11812.  
  11813.       Truncates the file specified by FileVariable at the current file
  11814.       position.
  11815.  
  11816.  TYPE
  11817.  
  11818.       Begins a type definition section.
  11819.  
  11820.  UNIT Identifier
  11821.  
  11822.       Identifies the code that follows as a unit and gives the unit the name
  11823.       Identifier.
  11824.  
  11825.  UNTIL BooleanExpression
  11826.  
  11827.       Terminates a REPEAT statement when BooleanExpression becomes True.
  11828.  
  11829.  UpCase( Char )
  11830.  
  11831.       Returns character Char in uppercase.
  11832.  
  11833.  USES Identifier«, Identifier»...
  11834.  
  11835.       Identifies units required by the program to resolve references to
  11836.       identifiers defined within the units.
  11837.  
  11838.  Val( String, Number, ErrorPosition )
  11839.  
  11840.       Converts the numeric string String to its numeric representation
  11841.       Number. If String does not represent a number, ErrorPosition returns
  11842.       the position of the first offending character.
  11843.  
  11844.  VAR
  11845.  
  11846.       Begins a variable declaration section or declares a variable parameter.
  11847.  
  11848.  WHILE BooleanExpression DO StatementBlock
  11849.  
  11850.       Executes StatementBlock as long as BooleanExpression remains True. When
  11851.       BooleanExpression becomes False, control passes to the statement
  11852.       following StatementBlock.
  11853.  
  11854.  WITH RecordName1 «, RecordName2 ...»DO StatementBlock
  11855.  
  11856.       Allows statements within StatementBlock to refer to the fields of one
  11857.       or more records without specifying the names of the records
  11858.       (RecordName1, RecordName2...).
  11859.  
  11860.  Write(«FileVariable,»Var1«, Var2...» )
  11861.  
  11862.       Writes one or more values to the standard output device or to the file
  11863.       specified by FileVariable.
  11864.  
  11865.  Writeln(«FileVariable ,»Var1«, Var2...» )
  11866.  
  11867.       Executes the Write procedure, then sends an end-of-line marker to the
  11868.       standard output device or the file specified by a FileVariable.
  11869.  
  11870.  XOR
  11871.  
  11872.       Acts as the logical or bitwise exclusive-or operator.
  11873.  
  11874.  
  11875.  D.2  Crt Procedures and Functions
  11876.  
  11877.  AssignCrt( FileVariable )
  11878.  
  11879.       Associates a Text file variable FileVariable with the CRT device
  11880.       (screen).
  11881.  
  11882.  ClrEol
  11883.  
  11884.       Clears a line from the cursor to the end of the line.
  11885.  
  11886.  ClrScr
  11887.  
  11888.       Clears the window and moves the cursor to the upper left corner.
  11889.  
  11890.  Delay( Microseconds)
  11891.  
  11892.       Pauses program execution for a specified length of time.
  11893.  
  11894.  DelLine
  11895.  
  11896.       Deletes the line at the current cursor location.
  11897.  
  11898.  GotoXY( x,  y )
  11899.  
  11900.       Moves the cursor to designated column and row.
  11901.  
  11902.  HighVideo
  11903.  
  11904.       Turns on high-intensity video for the current foreground color.
  11905.  
  11906.  InsLine
  11907.  
  11908.       Inserts a blank line at the current cursor location.
  11909.  
  11910.  KeyPressed
  11911.  
  11912.       Returns True if the keyboard buffer contains a character.
  11913.  
  11914.  LowVideo
  11915.  
  11916.       Turns off high-intensity video for the current foreground color.
  11917.  
  11918.  NormVideo
  11919.  
  11920.       Restores the text colors and attributes that were in effect at program
  11921.       start-up.
  11922.  
  11923.  NoSound
  11924.  
  11925.       Turns off the computer's speaker.
  11926.  
  11927.  ReadKey
  11928.  
  11929.       Returns one character from the keyboard buffer but does not echo
  11930.       character on the screen.
  11931.  
  11932.  Sound( Frequency )
  11933.  
  11934.       Generates a tone from the computer's speaker at the specified
  11935.       frequency.
  11936.  
  11937.  TextBackground( Color )
  11938.  
  11939.       Sets the background color for character output.
  11940.  
  11941.  TextColor( Color )
  11942.  
  11943.       Sets the foreground color and blinking attribute for character output.
  11944.  
  11945.  TextMode( Mode )
  11946.  
  11947.       Sets the display to the specified text mode.
  11948.  
  11949.  WhereX
  11950.  
  11951.       Returns the current x-coordinate of the text cursor.
  11952.  
  11953.  WhereY
  11954.  
  11955.       Returns the current y-coordinate of the text cursor.
  11956.  
  11957.  Window( x1, y1, x2, y2 )
  11958.  
  11959.       Defines a text display window. The coordinates give the upper left and
  11960.       lower right corners of the window.
  11961.  
  11962.  
  11963.  D.3  Dos Procedures and Functions
  11964.  
  11965.  DiskFree( DriveNumber )
  11966.  
  11967.       Returns the number of bytes of free space on the specified drive.
  11968.  
  11969.  DiskSize( DriveNumber )
  11970.  
  11971.       Returns the total capacity in bytes of the specified drive.
  11972.  
  11973.  DosExitCode
  11974.  
  11975.       Returns the exit code from a child process.
  11976.  
  11977.  DosVersion
  11978.  
  11979.       Returns the version number of the operating system.
  11980.  
  11981.  EnvCount
  11982.  
  11983.       Returns the number of variables defined in the DOS environment.
  11984.  
  11985.  EnvStr( EnvironmentStringIndex )
  11986.  
  11987.       Returns the value of a variable from the DOS environment.
  11988.  
  11989.  Exec( ProgramPath, CommandLine)
  11990.  
  11991.       Loads and runs a child process while suspending parent process.
  11992.  
  11993.  FExpand( FilePath )
  11994.  
  11995.       Expands a name to a fully qualified DOS path name.
  11996.  
  11997.  FindFirst( SearchPattern,  Attributes, Matched)
  11998.  
  11999.       Searches the specified directory for the first file matching the given
  12000.       SearchPattern and set of attributes.
  12001.  
  12002.  FindNext( Matched)
  12003.  
  12004.       Searches the specified directory for the next file matching the
  12005.       SearchPattern and attributes specified in a previous call to FindFirst.
  12006.  
  12007.  FSearch( FilePath, DirectoryList )
  12008.  
  12009.       Searches for a file in a list of directories.
  12010.  
  12011.  FSplit( FilePath, Directory, FileName, Extension )
  12012.  
  12013.       Separates a path name into its directory, basename, and extension
  12014.       parts.
  12015.  
  12016.  GetCBreak( Breaking)
  12017.  
  12018.       Gets the current state of DOS CTRL+BREAK checking.
  12019.  
  12020.  GetDate( Year, Month, Day, DayOfWeek)
  12021.  
  12022.       Gets the current system date.
  12023.  
  12024.  GetEnv( EnvironmentStringLabel )
  12025.  
  12026.       Returns the current value of a DOS environment variable.
  12027.  
  12028.  GetFAttr(  FileVariable, Attribute)
  12029.  
  12030.       Gets a file's attributes.
  12031.  
  12032.  GetFTime( FileVariable, TimeStamp)
  12033.  
  12034.       Gets the LongInt representing a file's date and time of modification.
  12035.  
  12036.  GetIntVec( InterruptNumber, Vector)
  12037.  
  12038.       Gets the vector address for a given interrupt number.
  12039.  
  12040.  GetTime( Hour, Minute, Second, Sec100)
  12041.  
  12042.       Gets the current system time.
  12043.  
  12044.  GetVerify( Verifying)
  12045.  
  12046.       Gets the current state of the DOS verify flag.
  12047.  
  12048.  Intr( InterruptNumber, RegisterValues )
  12049.  
  12050.       Calls a software interrupt, loading and returning register values.
  12051.  
  12052.  Keep( ExitCode)
  12053.  
  12054.       Terminates a program but keeps it resident in memory.
  12055.  
  12056.  MsDos( RegisterValues )
  12057.  
  12058.       Calls DOS interrupt $21.
  12059.  
  12060.  PackTime( DateTime, TimeStamp)
  12061.  
  12062.       Converts an unpacked DateTime record to a packed LongInt TimeStamp
  12063.  
  12064.  SetCBreak( Breaking)
  12065.  
  12066.       Turns DOS CTRL+BREAK checking on or off.
  12067.  
  12068.  SetDate( Year, Month, Day)
  12069.  
  12070.       Sets the current system date.
  12071.  
  12072.  SetFAttr( FileVariable, Attribute)
  12073.  
  12074.       Sets a file's attributes.
  12075.  
  12076.  SetFTime( FileVariable, TimeStamp)
  12077.  
  12078.       Sets a file's date and time of file modification record.
  12079.  
  12080.  SetIntVec( InterruptNumber, Vector)
  12081.  
  12082.       Installs a new interrupt handler. If a program changes an interrupt
  12083.       vector, it must restore it before terminating.
  12084.  
  12085.  SetTime( Hour, Minute, Second, Sec100 )
  12086.  
  12087.       Sets the system time.
  12088.  
  12089.  SetVerify( Verifying )
  12090.  
  12091.       Sets the state of the DOS verify flag.
  12092.  
  12093.  SwapVectors
  12094.  
  12095.       Swaps interrupt vectors with previously saved values.
  12096.  
  12097.  UnpackTime( TimeStamp, DateTime )
  12098.  
  12099.       Converts the LongInt TimeStamp argument to an unpacked DateTime record.
  12100.  
  12101.  
  12102.  D.4  Printer Unit Interface
  12103.  
  12104.  The printer unit does not contain any procedures or functions. It connects
  12105.  the file variable Lst with the printer port. Using Lst in a Write or Writeln
  12106.  statement sends the text to the printer:
  12107.  
  12108.       Write( Lst, 'This text goes to the printer.' );
  12109.  
  12110.  Lst is a text variable assigned to the file variable PRN and preconnected to
  12111.  the LPT1 printer port.
  12112.  
  12113.  
  12114.  D.5  MSGraph Procedures and Functions
  12115.  
  12116.  _Arc(x1,y1,x2,y2,x3,y3,x4,y4 )
  12117.  
  12118.       Draws an arc given the bounding rectangle and beginning and ending
  12119.       points in viewport coordinates.
  12120.  
  12121.  _Arc_wxy( wxy1, wxy2, wxy3, wxy4 )
  12122.  
  12123.       Draws an arc given the bounding rectangle and beginning and ending
  12124.       points in window coordinates in _WXYCoord records.
  12125.  
  12126.  _ClearScreen( Area )
  12127.  
  12128.       Clears the specified area of the screen.
  12129.  
  12130.  _DisplayCursor( Toggle )
  12131.  
  12132.       Specifies whether to turn the cursor back on or leave it off after
  12133.       executing graphics routines.
  12134.  
  12135.  _Ellipse( Control, x1, y1, x2, y2 )
  12136.  
  12137.       Draws an ellipse given the fill control and bounding rectangle in
  12138.       viewport coordinates.
  12139.  
  12140.  _Ellipse_w( Control, wx1, wy1, wx2, wy2)
  12141.  
  12142.       Draws an ellipse given the fill control and bounding rectangle in
  12143.       window coordinates.
  12144.  
  12145.  _Ellipse_wxy( Control, wxy1, wxy2)
  12146.  
  12147.       Draws an ellipse given the fill control and bounding rectangle in
  12148.       window coordinates in the _WXYCoord records wxy1 and wxy2.
  12149.  
  12150.  _FloodFill( x, y, Boundary )
  12151.  
  12152.       Fills an area with the current color and fill mask. If the point (x, y)
  12153.       lies inside the figure, it fills the interior; if (x, y) lies outside
  12154.       the figure, it fills the background. x and y are given in viewport
  12155.       coordinates.
  12156.  
  12157.  _FloodFill_w( wx, wy, Boundary)
  12158.  
  12159.       Fills an area with the current color and fill mask. If the point (x, y)
  12160.       lies inside the figure, it fills the interior; if (x, y) lies outside
  12161.       the figure, it fills the background. wx and wy are given in window
  12162.       coordinates.
  12163.  
  12164.  _GetActivePage
  12165.  
  12166.       Returns the current active page number.
  12167.  
  12168.  _GetArcInfo( Start, End, Paint )
  12169.  
  12170.       Returns information about the most recently drawn _Arc or _Pie. The
  12171.       Start point, End point, and Paint point are returned in _XYCoord
  12172.       records.
  12173.  
  12174.  _GetBkColor
  12175.  
  12176.       Returns the current background color.
  12177.  
  12178.  _GetColor
  12179.  
  12180.       Returns the current color index.
  12181.  
  12182.  _GetCurrentPosition( xy)
  12183.  
  12184.       Returns the current graphics cursor position in viewport coordinates in
  12185.       the _XYCoord record xy.
  12186.  
  12187.  _GetCurrentPosition_wxy( wxy)
  12188.  
  12189.       Returns the current graphics cursor position in window coordinates in
  12190.       the _WXYCoord record wxy.
  12191.  
  12192.  _GetFillMask( Mask)
  12193.  
  12194.       Returns the current fill mask in mask, if one is defined.
  12195.  
  12196.  _GetFontInfo( FInfo)
  12197.  
  12198.       Gets the current font characteristics and returns them in the _FontInfo
  12199.       record FInfo.
  12200.  
  12201.  _GetGTextExtent( TextString)
  12202.  
  12203.       Returns the pixel width required to print the string TextString in the
  12204.       current font with the _OutGText function.
  12205.  
  12206.  _GetGTextVector( Vector )
  12207.  
  12208.       Returns the current rotation vector that is applied to font-based text
  12209.       output in the _XYCoord record Vector. The default is (1,0).
  12210.  
  12211.  _GetImage( x1, y1, x2, y2, Image)
  12212.  
  12213.       Stores the screen image inside the bounding rectangle specified in
  12214.       viewport coordinates. Stores the image in the buffer Image. The
  12215.       rectangle must be completely within the current clipping region.
  12216.  
  12217.  _GetImage_w( wx1, wy1, wx2, wy2, Image )
  12218.  
  12219.       Stores the screen image inside the bounding rectangle specified in
  12220.       window coordinates. Stores the image in the buffer Image. The rectangle
  12221.       must be completely within the current clipping region.
  12222.  
  12223.  _GetImage_wxy( wxy1, wxy2, Image)
  12224.  
  12225.       Stores the screen image inside the bounding rectangle specified in
  12226.       window coordinates in the _WXYCoord records wxy1 and wxy2. Stores the
  12227.       image in the buffer Image. The rectangle must be completely within the
  12228.       current clipping region.
  12229.  
  12230.  _GetLineStyle
  12231.  
  12232.       Returns the current line-style mask.
  12233.  
  12234.  _GetPhysCoord( x, y, xy)
  12235.  
  12236.       Translates the viewport coordinates (x, y) into physical screen
  12237.       coordinates and returns them in the _XYCoord record xy.
  12238.  
  12239.  _GetPixel( x, y)
  12240.  
  12241.       Returns the pixel value (color index) at the location specified in
  12242.       viewport coordinates (x, y).
  12243.  
  12244.  _GetPixel_w( wx, wy)
  12245.  
  12246.       Returns the pixel value (color index) at the location specified in
  12247.       window coordinates (wx, wy).
  12248.  
  12249.  _GetTextColor
  12250.  
  12251.       Returns the color index (attribute) of the current text color.
  12252.  
  12253.  _GetTextCursor
  12254.  
  12255.       Returns the current cursor attribute (shape) in text modes.
  12256.  
  12257.  _GetTextPosition( r, c)
  12258.  
  12259.       Returns the current row and column position of the text cursor.
  12260.  
  12261.  _GetTextWindow( r1, c1, r2, c2)
  12262.  
  12263.       Returns the boundaries of the current text window in row and column
  12264.       coordinates.
  12265.  
  12266.  _GetVideoConfig( vc)
  12267.  
  12268.       Returns the current graphics environment configuration in the
  12269.       _VideoConfig record vc.
  12270.  
  12271.  _GetViewCoord( x, y, xy)
  12272.  
  12273.       Translates physical coordinates (x, y) into viewport coordinates,
  12274.       returning the viewport coordinates in the _XYCoord record xy.
  12275.  
  12276.  _GetViewCoord_w( wx, wy, xy)
  12277.  
  12278.       Translates window coordinates (wx, wy) into viewport coordinates,
  12279.       returning the viewport coordinates in the _XYCoord record xy.
  12280.  
  12281.  _GetViewCoord_wxy( wxy, xy)
  12282.  
  12283.       Translates window coordinates given in the _WXYCoord record wxy into
  12284.       viewport coordinates, returning the viewport coordinates in the
  12285.       _XYCoord record xy.
  12286.  
  12287.  _GetVisualPage
  12288.  
  12289.       Returns the current visual page number.
  12290.  
  12291.  _GetWindowCoord( x, y, wxy)
  12292.  
  12293.       Translates viewport coordinates (x, y) into window coordinates and
  12294.       returns them in the _WXYCoord record wxy.
  12295.  
  12296.  _ImageSize( x1, y1, x2, y2)
  12297.  
  12298.       Returns the number of bytes needed to store the image inside the
  12299.       bounding rectangle specified by the viewport coordinates (x1, y1), (x2,
  12300.       y2). This function returns a LongInt.
  12301.  
  12302.  _ImageSize_w( wx1, wy1, wx2, wy2)
  12303.  
  12304.       Returns the number of bytes needed to store the image inside the
  12305.       bounding rectangle specified by the window coordinates (wx1, wy1),
  12306.       (wx2, wy2). This function returns a LongInt.
  12307.  
  12308.  _ImageSize_wxy( wxy1, wxy2 )
  12309.  
  12310.       Returns the number of bytes needed to store the image inside the
  12311.       bounding rectangle specified by the window coordinates in the _WXYCoord
  12312.       records wxy1 and wxy2. This function returns a LongInt.
  12313.  
  12314.  _LineTo( x, y)
  12315.  
  12316.       Draws a line from the current position to the point specified in
  12317.       viewport coordinates, using the current color, line-style mask, and
  12318.       logical write mode.
  12319.  
  12320.  _LineTo_w( wx, wy)
  12321.  
  12322.       Draws a line from the current position to the point specified in window
  12323.       coordinates, using the current color, line-style mask, and logical
  12324.       write mode.
  12325.  
  12326.  _MoveTo( x, y)
  12327.  
  12328.       Moves the graphics cursor to the point specified by the viewport
  12329.       coordinates (x, y).
  12330.  
  12331.  _MoveTo_w( wx, wy)
  12332.  
  12333.       Moves the graphics cursor to the point specified by the window
  12334.       coordinates (wx, wy).
  12335.  
  12336.  _OutGText( TextString)
  12337.  
  12338.       Prints TextString on the screen using the current font and graphics
  12339.       color at the current graphics cursor position.
  12340.  
  12341.  _OutMem( TextString, Length)
  12342.  
  12343.       Prints TextString on the screen at the current text cursor position.
  12344.       This procedure treats ASCII 0, 10, and 13 as graphics characters.
  12345.       Formatting is not supported.
  12346.  
  12347.  _OutText( TextString)
  12348.  
  12349.       Prints TextString on the screen at the current text cursor position.
  12350.       Formatting is not supported, except for carriage return and line feed.
  12351.  
  12352.  _Pie( Control, x1, y1, x2, y2, x3, y3, x4, y4)
  12353.  
  12354.  
  12355.       Draws a pie-shaped wedge given the fill control, bounding rectangle,
  12356.       starting point, and ending point. All coordinates are given in viewport
  12357.       coordinates.
  12358.  
  12359.  _Pie_wxy( Control, wxy1, wxy2, wxy3, wxy4)
  12360.  
  12361.       Draws a pie-shaped wedge given the fill control, bounding rectangle,
  12362.       starting point, and ending point. All coordinates are given in
  12363.       _WXYCoord records.
  12364.  
  12365.  _PutImage( x, y, Image, Action)
  12366.  
  12367.       Transfers the image stored in the buffer Image to the screen (using the
  12368.       logical operator Action) with the upper left corner at the specified
  12369.       viewport coordinates. If the image does not completely fit in the
  12370.       current clipping region, the image is not transferred.
  12371.  
  12372.  _PutImage_w( wx, wy, Image, Action)
  12373.  
  12374.       Transfers the image stored in the buffer Image to the screen (using the
  12375.       logical operator Action) with the upper left corner at the specified
  12376.       window coordinates. If the image does not completely fit in the current
  12377.       clipping region, the image is not transferred.
  12378.  
  12379.  _Rectangle( Control, x1, y1, x2, y2)
  12380.  
  12381.       Draws a rectangle given the fill control and bounding rectangle in
  12382.       viewport coordinates, using the current color, line-style mask, and
  12383.       logical write mode.
  12384.  
  12385.  _Rectangle_w( Control, wx1, wy1, wx2, wy2)
  12386.  
  12387.       Draws a rectangle given the fill control and bounding rectangle in
  12388.       window coordinates, using the current color, line-style mask, and
  12389.       logical write mode.
  12390.  
  12391.  _Rectangle_wxy( Control, wxy1, wxy2)
  12392.  
  12393.       Draws a rectangle given the fill control and bounding rectangle in the
  12394.       window coordinates specified in the _WXYCoord records wxy1 and wxy2 and
  12395.       using the current color, line-style mask, and logical write mode.
  12396.  
  12397.  _RegisterFonts( PathName)
  12398.  
  12399.       Registers the fonts in the file given in PathName.
  12400.  
  12401.  _RemapAllPalette( NewPalette)
  12402.  
  12403.       Remaps the entire color palette simultaneously, given an array of color
  12404.       values.
  12405.  
  12406.  _RemapPalette( Index, Value)
  12407.  
  12408.       Remaps the color index Index to the color value Value.
  12409.  
  12410.  _ScrollTextWindow( Count)
  12411.  
  12412.       Scrolls the current text window by Count lines. If Count is positive,
  12413.       the text scrolls up; if negative, the text scrolls down.
  12414.  
  12415.  _SelectPalette( Number)
  12416.  
  12417.       Sets the active palette to palette number Number in CGA and Olivetti
  12418.       graphics modes.
  12419.  
  12420.  _SetActivePage( Page)
  12421.  
  12422.       Makes page number Page the active page for graphics output, available
  12423.       only for configurations that support multiple screen pages.
  12424.  
  12425.  _SetBkColor( Color)
  12426.  
  12427.       Sets the current background color to Color.
  12428.  
  12429.  _SetClipRgn( x1, y1, x2, y2)
  12430.  
  12431.       Limits the display of subsequent graphics and font text to the bounding
  12432.       rectangle, given in viewport coordinates.
  12433.  
  12434.  _SetColor( Color)
  12435.  
  12436.       Sets the current graphics color to color index Color.
  12437.  
  12438.  _SetFillMask( Mask)
  12439.  
  12440.       Defines the current fill mask.
  12441.  
  12442.  _SetFont( Options)
  12443.  
  12444.       Selects a new active font from one of the registered fonts. Font
  12445.       selection is based on the characteristics specified by Options.
  12446.  
  12447.  _SetGTextVector( xvect, yvect)
  12448.  
  12449.       Sets the current rotation vector that is applied to font-based text
  12450.       output. The default is horizontal text (1,0).
  12451.  
  12452.  _SetLineStyle( Style)
  12453.  
  12454.       Sets the current line-style mask to Style. The line-style mask affects
  12455.       the output of _LineTo and _Rectangle.
  12456.  
  12457.  _SetPixel( x, y)
  12458.  
  12459.       Changes the specified pixel to the current color. The point is
  12460.       specified in viewport coordinates.
  12461.  
  12462.  _SetPixel_w( wx, wy)
  12463.  
  12464.       Sets the specified pixel to the current color. The point is specified
  12465.       in window coordinates.
  12466.  
  12467.  _SetTextColor( Color)
  12468.  
  12469.       Sets the text color to color index Color. Subsequent text output from
  12470.       _OutText and _OutMem appears in the new color.
  12471.  
  12472.  _SetTextCursor( Attr)
  12473.  
  12474.       Sets the cursor shape in text mode.
  12475.  
  12476.  _SetTextPosition( r, c)
  12477.  
  12478.       Moves the text cursor position to the row and column specified,
  12479.       relative to the current text window.
  12480.  
  12481.  _SetTextRows( Rows)
  12482.  
  12483.       Sets the number of rows available for text modes.
  12484.  
  12485.  _SetTextWindow( r1, c1, r2, c2)
  12486.  
  12487.       Defines the upper left and lower right boundaries of the current text
  12488.       window. Output from _OutText and _OutMem is limited to this window.
  12489.  
  12490.  _SetVideoMode( Mode)
  12491.  
  12492.       Selects a screen mode for a particular hardware/display configuration.
  12493.  
  12494.  _SetVideoModeRows( Mode, Rows)
  12495.  
  12496.       Sets the screen mode and number of text rows for a particular hardware/
  12497.       display configuration.
  12498.  
  12499.  _SetViewOrg( x, y, Org)
  12500.  
  12501.       Moves the viewport origin to the physical coordinates (x, y), and
  12502.       returns the previous origin in the _XYCoord record Org.
  12503.  
  12504.  _SetViewport( x1, y1, x2, y2)
  12505.  
  12506.       Defines the graphics viewport (defines the clipping region and sets the
  12507.       viewport origin to the upper left corner of the region) given the
  12508.       bounding rectangle in physical coordinates.
  12509.  
  12510.  _SetVisualPage( Page)
  12511.  
  12512.       Makes Page the current visual page (requires a configuration that
  12513.       supports multiple screen pages).
  12514.  
  12515.  _SetWindow( FInvert, x1, y1, x2, y2)
  12516.  
  12517.       Creates a floating-point graphics window given a bounding rectangle in
  12518.       window coordinates. If FInvert is True, the window is inverted
  12519.       vertically.
  12520.  
  12521.  _SetWriteMode( WMode )
  12522.  
  12523.       Sets the logical operation applied to line-drawing output to one of the
  12524.       following: _Gor, _Gand, _GPReset, _GPSet, or _Gxor. Only LineTo and
  12525.       _Rectangle are affected.
  12526.  
  12527.  _UnRegisterFonts
  12528.  
  12529.       Disables fonts and frees memory previously allocated by _RegisterFonts.
  12530.  
  12531.  _WrapOn( Option)
  12532.  
  12533.       Controls text wrapping for _OutText and _OutMem when the output reaches
  12534.       the edge of the text window. By default, text wrapping is enabled.
  12535.  
  12536.