 Microsoft(R) FORTRAN ADVANCED TOPICS
 VERSION 5.0

 FOR MS(R) OS/2 AND MS-DOS(R) OPERATING SYSTEMS

 MICROSOFT CORPORATION

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

 (c)Copyright Microsoft Corporation, 1989. All rights reserved.

 Printed and bound in the United States of America.

 Microsoft, MS, MS-DOS, CodeView, and QuickC are registered trademarks of
 Microsoft Corporation.

 AT&T is a registered trademark of American Telephone and Telegraph Company.

 Hercules is a registered trademark and InColor is a trademark of Hercules
 Computer Technology.

 IBM is a registered trademark of International Business Machines
 Corporation.

 Intel is a registered trademark of Intel Corporation.

 Olivetti is a registered trademark of Ing. C. Olivetti.

 Texas Instruments is a registered trademark of Texas Instruments, Inc.

 VAX is a registered trademark of Digital Equipment Corporation.

 WANG is a registered trademark of Wang Laboratories.

 Document No. LN0828-500-R00-0289
 Part No. 06384
 10  9 8 7 6 5 4 3 2 1


 Table of Contents
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Introduction
      About This Manual
      Document Conventions

 Part 1  Working with Microsoft FORTRAN

 Chapter 1  Controlling Floating- Point Operations
 1.1  Choosing a Math Package
       1.1.1  The 8087/287/387 Package
       1.1.2  The Emulator Package
       1.1.3  The Alternate Math Package
 1.2  Using the Floating-Point Options (/FP)
       1.2.1  The /FPi87 Option
       1.2.2  The /FPc87 Option
       1.2.3  The /FPi Option
       1.2.4  The /FPc Option
       1.2.5  The /FPa Option
 1.3  Library Considerations in Selecting /FP Options
       1.3.1  Choosing In-Line Instructions or Calls
       1.3.2  Using a Standard Library for Linking
       1.3.3  Ensuring Compatibility between Options
       1.3.4  Special Procedure for MS-DOS 3.2 Users
       1.3.5  Using $FLOATCALLS and $NOFLOATCALLS
 1.4  Using the NO87 Environment Variable
 1.5  Handling 8087/287/387 Floating-Point Exceptions
       1.5.1  Controlling the Processing Environment
       1.5.2  Reading and Setting Status and Control Values
       1.5.3  Floating-Point Exceptions under PC-DOS 3.2
 1.6  Using Non-IBM(R)-Compatible Computers

 Chapter 2  Selecting a Memory Model
 2.1  What Is a Memory Model?
 2.2  Understanding Memory Models
       2.2.1  Code and Data Segments
       2.2.2  Near, Far, and Huge Addresses
       2.2.3  The Default Data Segment
 2.3  Working with FORTRAN Memory Models
       2.3.1  Specifying a Memory Model
       2.3.2  Data-Handling Strategies and Limits
       2.3.3  Limits on Code Size
 2.4  Selecting and Adjusting the Memory Model
       2.4.1  Using Standard Memory Models
       2.4.2  The NEAR, FAR, and HUGE Attributes
       2.4.3  The $LARGE and $NOTLARGE Metacommands
       2.4.4  Using Library Routines with Different Memory Models
       2.4.5  Compiler Options Affecting Memory Usage

 Chapter 3  FORTRAN Programming under OS/2
 3.1  Compiling and Linking under OS/2
 3.2  Running under Both DOS and OS/2
 3.3  Interfacing FORTRAN with OS/2
 3.4  Understanding Multitasking, Threads, and Dynamic-Link Libraries
       3.4.1  Multitasking and Threads
       3.4.2  Dynamic-Link Libraries
 3.5  Multithread Programs
       3.5.1  Library Support for Multithread Programs
       3.5.2  Thread Control Routines
 3.6  A Multithread FORTRAN Program
       3.6.1  Sample Program Operation
       3.6.2  Compiling and Linking the Sample Multithread Program
       3.6.3  Troubleshooting Multithread Programs
 3.7  Using FORTRAN Dynamic-Link Libraries
       3.7.1  Creating a Dynamic-Link Library
       3.7.2  The LOADDS Attribute
       3.7.3  Multithread Dynamic-Link Libraries: An Overview
       3.7.4  Creating a Multithread Dynamic-Link Library
       3.7.5  Creating a Custom FORTRAN Run-Time Definitions File
       3.7.6  Using a Multithread Dynamic-Link Library
       3.7.7  Compiling for Dynamic-Link and Multithread Capabilities
       3.7.8  FORTRAN OS/2 Libraries
 3.8  Limits on Multithread and Dynamic-Link Programs
       3.8.1  Large and Huge Memory Models (/MT, /MD)
       3.8.2  Math Support (/FPi87)
       3.8.3  Limited CHARACTER*(*) Support
       3.8.4  The C Attribute and REAL Numbers
       3.8.5  Open Files
 3.9  Recommendations
 3.10  Additional Information

 Chapter 4  Mixed-Language Programming
 4.1  Making Mixed-Language Calls
       4.1.1  Naming Convention Requirement
       4.1.2  Calling Convention Requirement
       4.1.3  Methods for Passing Parameters
       4.1.4  Compiling and Linking Considerations
 4.2  Calling Other High-Level Languages
       4.2.1  The INTERFACE Statement
       4.2.2  An Alternative Interface to C
       4.2.3  The ALIAS Attribute
       4.2.4  FORTRAN Calls to C and Pascal
       4.2.5  C Functions Available with FORTRAN Run-Time Libraries
       4.2.6  FORTRAN Calls to BASIC
 4.3  Using Assembly-Language Routines
 4.4  Calling FORTRAN from Other Languages

 Chapter 5  Transferring Data between Languages
 5.1  Passing by Reference or Value
       5.1.1  BASIC Arguments
       5.1.2  C Arguments
       5.1.3  FORTRAN Arguments
       5.1.4  Pascal Arguments
 5.2  Passing Numeric, Logical, and String Data
       5.2.1  Integer and Real Numbers
       5.2.2  FORTRAN COMPLEX Types
       5.2.3  FORTRAN LOGICAL Type
       5.2.4  Strings
 5.3  Handling Special Data Types
       5.3.1  Arrays
       5.3.2  Structures, Records, and User-Defined Types
       5.3.3  External Data
       5.3.4  Pointers and Address Variables
       5.3.5  Common Blocks
       5.3.6  Using a Varying Number of Parameters

 Chapter 6  Record Structures: File Formats and Access
 6.1  Understanding Structures: Forms and Access Modes
 6.2  Using Formatted Records
       6.2.1  Formatted Sequential Records
       6.2.2  Formatted Direct Records
 6.3  Using Unformatted Records
       6.3.1  Unformatted Sequential Records
       6.3.2  Unformatted Direct Records
 6.4  Using Binary Records
       6.4.1  Binary Sequential Records
       6.4.2  Binary Direct Records

 Chapter 7  Optimizing Programs
 7.1  Compiling and Linking Strategies
       7.1.1  Using Debugging and Integer-Size Options
       7.1.2  Using 8087/287/387 Math Options
       7.1.3  Using Overlays
 7.2  Coding Strategies
       7.2.1  Removing Error-Message Text during SETUP
       7.2.2  Using Consistent File-Access and Format Types
       7.2.3  Specifying Edit Lists
       7.2.4  Using the BLOCKSIZE Option
       7.2.5  Using Integer and Real Variables
       7.2.6  Using Arrays and EQUIVALENCE Statements

 Part 2  The Microsoft FORTRAN Graphics Library

 Chapter 8  Graphics Library Routines
 8.1  Using Graphics and Character-Font Functions
 8.2  Defining Graphics Characteristics
       8.2.1  Selecting Display Options
       8.2.2  Setting Graphics Coordinates
       8.2.3  Setting Palettes
       8.2.4  Setting Figure Attributes
 8.3  Displaying Graphics Output
       8.3.1  Creating a Graphics Display
       8.3.2  Displaying Text
       8.3.3  Transferring Images
       8.3.4  Displaying Font-Based Characters
 8.4  Routines Available under OS/2

 Chapter 9  Drawing Graphic Elements
 9.1  Working with Graphics Modes
       9.1.1  Checking the Current Video Mode
       9.1.2  Setting the Video Mode
       9.1.3  Writing a Graphics Program (SINE)
 9.2  Adding Color
       9.2.1  Color Graphics Modes
       9.2.2  Using the Color Video Text Modes
 9.3  Understanding Coordinate Systems
       9.3.1  Text Coordinates
       9.3.2  Graphics Coordinates
       9.3.3  Real Coordinates Program (REALG)
       9.3.4  Drawing the Graphs

 Chapter 10  Using Fonts from the Graphics Library
 10.1  Available Typefaces
 10.2  Using Microsoft FORTRAN Fonts
        10.2.1  Registering Fonts
        10.2.2  Setting the Font and Displaying Text
 10.3  Example Program (SAMPLER)
 10.4  Using Fonts Effectively

 Chapter 11  Graphics Library Reference
      arc, arc_w
      clearscreen
      displaycursor
      ellipse, ellipse_w
      floodfill, floodfill_w
      getactivepage
      getbkcolor
      getcolor
      getcurrentposition, getcurrentposition_w
      getfillmask
      getfontinfo
      getgtextextent
      getimage, getimage_w
      getlinestyle
      getphyscoord
      getpixel, getpixel_w
      gettextcolor
      gettextcursor
      gettextposition
      getviewcoord, getviewcoord_w
      getvideoconfig
      getvisualpage
      getwindowcoord
      imagesize, imagesize_w
      lineto, lineto_w
      moveto, moveto_w
      outgtext
      outtext
      pie, pie_w
      putimage, putimage_w
      rectangle, rectangle_w
      registerfonts
      remapallpalette, remappalette
      selectpalette
      setactivepage
      setbkcolor
      setcliprgn
      setcolor
      setfillmask
      setfont
      setlinestyle
      setpixel, setpixel_w
      settextcolor
      settextcursor
      settextposition
      settextrows
      settextwindow
      setvideomode
      setvideomoderows
      setvieworg
      setviewport
      setvisualpage
      setwindow
      unregisterfonts
      wrapon

 Index

 Listing of Tables

 Table 1.1  Summary of Floating-Point Options
 Table 1.2  Floating-Point Exceptions
 Table 1.3  Mask Settings for Operation Exceptions
 Table 2.1  Data Allocation in Large Memory Models
 Table 2.2  Effects of the NEAR Attribute
 Table 2.3  Effects of the FAR and HUGE Attributes
 Table 3.1  FORTRAN OS/2 Libraries
 Table 4.1  Language Equivalents for Routine Calls
 Table 4.2  Parameter-Passing Defaults
 Table 5.1  Equivalent Numeric Data Types
 Table 5.2  Equivalent Array Declarations
 Table 9.1  Video Modes for Text and Graphics
 Table 9.1  Fill Patterns
 Table 9.2  Available CGA Colors
 Table 9.3  CGA Colors:  $MRESNOCOLOR Mode
 Table 10.1  Typefaces and Type Sizes in the FORTRAN Graphics Library
 Table R.2  $MRES4COLOR Mode Palette Colors
 Table R.3  $MRESNOCOLOR Mode CGA Palette Colors
 Table R.4  $MRESNOCOLOR Mode EGA Palette Colors
 Table R.5  Symbolic Constants for Screen Mode
 Table R.6  Modes Selected by $MAXCOLORMODE and $MAXRESMODE


 Introduction
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Microsoft(R) FORTRAN Version 5.0 includes many enhancements since its last
 release. The language is now fully compatible with the Systems Application
 Architecture (SAA) FORTRAN extensions and many of the VAX(R) extensions. It
 supports new constructs such as compound data types, SELECT CASE
 decision-making blocks, and other features that extend its utility. This new
 version of Microsoft FORTRAN offers enhanced OS/2 systems support, including
 the use of dynamically linked libraries and multiple threads of execution.

 For complete flexibility, programs written with this new version can be
 designed to run under DOS, OS/2, or both operating systems. In addition, an
 extensive graphics library lets data and figures become an integral part of
 any FORTRAN application.

 This chapter introduces Microsoft FORTRAN Advanced Topics and describes the
 document conventions used in this manual. If you want to write and run
 FORTRAN programs immediately, the Getting Started with Microsoft FORTRAN and
 the Microsoft FORTRAN Reference man-uals will provide sufficient resources
 to get you up and running quickly.


 About This Manual

 Microsoft FORTRAN Advanced Topics contains two parts. Part 1, "Working with
 Microsoft FORTRAN," discusses how to get the most out of Microsoft FORTRAN
 5.0, and includes such topics as selecting the appropriate memory models,
 using OS/2-specific features, or creating programs with parts written in
 different programming languages.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Microsoft documentation uses the term "OS/2" to refer to the OS/2
    SystemsÄÄMicrosoft Operating System/2 (MS(R) OS/2) and IBM(R) OS/2.
    Similarly, the term "DOS" refers to both the MS-DOS(R) and IBM Personal
    Computer DOS operating systems. The name of a specific operating system
    is used when it is necessary to note features that are unique to that
    system.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The first two chapters discuss memory models and alternatives for handling
 floating-point math. Chapter 3, "FORTRAN Programming under OS/2," covers the
 special features available under OS/2, such as multitasking, dynamic-link
 libraries, virtual memory access, and protected and real modes. Chapter 4,
 "Mixed-Language Programming," describes how to access routines programmed in
 another Microsoft language. And, if you need to exchange data between
 languages, read Chapter 5.

 Chapter 6, "Record Structures: File Formats and Access," reviews the
 different file format and access combinations available to Microsoft
 FORTRAN. The last chapter of the Part 1, "Optimizing Programs," covers
 strategies for optimizing both the size and speed of an application, at both
 the compiling/linking and coding levels.

 Part 2, "The Microsoft FORTRAN Graphics Library," presents topics and
 considerations in writing graphics applications.

 Chapter 8 introduces the routines provided in the graphics library and their
 categories. Chapter 9, "Drawing Graphic Elements," describes graphics
 programming in detail, and steps through several example programs. Chapter
 10, "Using Fonts from the Graphics Library," covers the different fonts
 available and how to use the font manipulating routines. The final chapter,
 "Graphics Library Reference," describes the use of each routine in detail.

 The graphics functions in the Microsoft FORTRAN Graphics Library are
 designed to work in the MS-DOS or IBM Personal Computer DOS (PC-DOS)
 environment; the text-based routines are also available under OS/2.

 The chapters in this manual can be read either individually or
 consecutively, although the graphics chapters in Part 2 follow a more
 sequential structure. The majority of the chapters in the Part 1 stand by
 themselves and can be read independently.

 Microsoft FORTRAN conforms to the American National Standard Programming
 Language FORTRAN 77, as described in the American National Standards
 Institute (ANSI) X3.9-1978 standard.

 Microsoft FORTRAN Advanced Topics assumes you have a working knowledge of
 both the computer and ANSI standard FORTRAN, and that you recognize the
 Microsoft extensions to FORTRAN.


 Document Conventions

 This book uses the following typographic conventions:

 Examples                                   Description

 LLIBFOR7.LIB, LINK, FL,                    Uppercase (capital) letters
 FGRAPH.FI, /4Yb                            indicate file names and DOS-level
                                            commands. Uppercase is also used
                                            for command-line options (unless
                                            the application accepts only
                                            lowercase).

 C     Comment line                         This kind of type  is used for
       WRITE (*, *) 'Hello                  program examples, program output,
      + World'                              and error messages within the
                                            text.

                                            A capital C marks the beginning
                                            of a comment in sample programs.
                                            Continuation lines are indicated
                                            by a plus (+ ) in column 6.

 $TRUNCATE, INTRINSIC,WRITE,                Bold capital letters indicate
 DosSleep                                   language-specific keywords with
                                            special meaning to FORTRAN (or,
                                            in the case of DosSleep, with
                                            special meaning to the OS/2
                                            environment). Keywords are a
                                            required part of statement
                                            syntax, unless enclosed in double
                                            brackets, as explained below.

 near, adr                                  Bold lowercase letters are used
                                            for keywords of languages that
                                            use lowercase letters by
                                            convention. For example, in the
                                            sentence, "The BASIC INTEGER and
                                            C short types are equivalent,"
                                            the word INTEGER is a Microsoft
                                            BASIC keyword and short is a
                                            keyword from Microsoft C.

 Apostrophes: '' ' '                        In Microsoft FORTRAN, an
                                            apostrophe is entered as a single
                                            right quotation mark ('), not a
                                            single left quotation mark (`).
                                            Note that in the typeface used in
                                            program examples, such as
                                            'string', apostrophes look like
                                            this: '.

 expression                                 Words in italics indicate
                                            placeholders for information that
                                            you must supply. A file name is
                                            an example of this kind of
                                            information.

 ®option¯                                  Items inside bold chevrons
                                            are optional.

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

 Repeating elements...                      Three dots following an item
                                            indicate that more items having
                                            the same form may be entered.

 CALL GetNum (i, *10)                       A  column of three dots indicates
 .                                          that part of the example program
 .                                          has intentionally been omitted.
 .
 SUBROUTINE GetNum (i, *)

 KEY NAMES                                  Small capital letters are used
                                            for the names of keys and key
                                            sequences, such as ENTER and
                                            CTRL+C.

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

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

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

                                            The key names used in this manual
                                            correspond to the names on the
                                            IBM Personal Computer keyboard.
                                            Other machines may use different
                                            names.

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

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


 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Part 1  Working with Microsoft FORTRAN

 Part 1 covers several topics that let you tailor applications written in
 Microsoft FORTRAN to your specific needs. These topics include options for
 handling floating-point math operations, control over memory models and
 segment usage, and special features available under OS/2, such as multiple
 threads of execution and dynam-ically linked libraries.

 Two chapters discuss how to call other-language programs from  Microsoft
 FORTRANÄÄincluding C, Pascal, BASIC, and assembly languages, such as
 Microsoft Macro Assembler (MASM). Review these chapters if you plan to port
 programs written in FORTRAN to other computers and you want them to access
 data created by other applications. Since FORTRAN supports several record
 structures, one chapter reviews each of the different structures and its
 particular strengths. The last chapter describes strategies for optimizing
 programs for both size and speed.


 Chapter 1  Controlling Floating- Point Operations
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Microsoft FORTRAN 5.0 lets you control how your programs handle
 floating-point math operations. In calculation-intensive programs, a well
 chosen floating-point option can speed execution and increase precision.
 This chapter describes the math packages you can install in FORTRAN
 libraries when you run the SETUP program. It also discusses the FL command
 options for choosing the appropriate library for linking and controlling
 floating-point instructions. (For specific information on the FL command,
 see Chapter 7 in the Microsoft FORTRAN Reference.)

 This chapter also explains how to override floating-point options by
 changing libraries at link time; how to control an 8087, 80287, or 80387
 coprocessor with the NO87 environment variable; and how to handle the
 8087/80287/80387 floating-point exceptions. Finally, if you run your program
 on a computer that is not IBM compatible, review the last section of the
 chapter for any special considerations for your machine.


 1.1  Choosing a Math Package

 The Microsoft FORTRAN Compiler offers a choice of these floating-point math
 packages:

   þ  8087/287/387 (default) package
   þ  Emulator package
   þ  Alternate math package

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The math options support the Intel(R) 8087, 80287, and 80387 math
    coprocessors. For brevity, this book uses the term "8087/287/387" to
    refer to all three coprocessors.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 When you run the SETUP program, you choose one of these math packages. SETUP
 includes the math package you choose in the library it builds. Any programs
 linked to that library use the math package included in the library; you
 must apply the appropriate FL option to make sure that the library you want
 is used at link time.

 Use the following descriptions of the math packages to help you choose the
 option that best suits your needs when you run SETUP. (You can run SETUP as
 many times as necessary to build the libraries you want.)


 1.1.1  The 8087/287/387 Package

 The FORTRAN 8087/80287/80387 math package takes full advantage of a math
 coprocessor to perform floating-point operations. Intensive floating-point
 operations tend to slow down computer operations. The 8087/287/387
 coprocessors are math chips dedicated to performing floating-point
 operations rapidly.

 This package requires an installed math coprocessor chip; programs compiled
 to use this package fail to run without the coprocessor (an error message
 reading floating point not loaded is issued). This is the default math
 package that SETUP uses if you do not explicitly choose another package.


 1.1.2  The Emulator Package

 The emulator package uses an 8087, 80287, or 80387 coprocessor if one is
 installed. If no coprocessor is available, this package emulates many of the
 8087/287/387 functions with software. The emulator package is the best
 choice if you want to maximize program precision and speed and you want the
 program to run on systems with and without coprocessors.

 The emulator package can perform basic operations to the same degree of
 precision as an 8087/287/387 chip. However, the emulator routines used for
 transcendental math functions differ slightly from the corresponding
 8087/287/387 functions. This can cause a slight difference (usually within 2
 bits) in the results of such operations when performed with the emulator
 instead of with an 8087/287/387.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    When you use an 8087, 80287, or 80387 coprocessor or the emulator, the
    interrupt-enable, precision, underflow, and denormalized-operand
    exceptions are masked by default. The remaining exceptions are unmasked.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 1.1.3  The Alternate Math Package

 The alternate math package gives you the smallest and fastest programs
 possible without a coprocessor. However, the program results are not
 accurate to as many significant digits as results given by the emulator
 package or 8087/287/387 math coprocessors.

 The alternate math package uses a subset of the Institute of Electrical and
 Electronics Engineers, Inc. (IEEE) standard format numbers. Infinities,
 NANs, and denormal numbers are not used.


 1.2  Using the Floating-Point Options (/FP)

 The /FP options of the FL command control how a program handles
 floating-point operations. Based on the /FP option and the memory-model
 option you choose, the FL command embeds a library name in the object file
 it creates. (See Table 1.1, "Summary of Floating-Point Options," for a list
 of the library names used for each combination.) Unless the /Lp or /Lr
 option is specified, this library becomes the default library; that is, the
 linker searches in the standard places for a library with that name. If it
 finds such a library, the linker uses that library to resolve external
 references in the object file. Otherwise, the linker displays a message
 indicating that it could not find the library. (The /Lp and /Lr options use
 the OS/2 dual-mode naming conventions. See Chapter 3 for a discussion of
 OS/2 default library names. Unless otherwise indicated, this chapter assumes
 that you use the DOS naming conventions.)

 This mechanism allows the linker to link the object file automatically with
 the requested library. However, there are exceptions to the rule; you are
 allowed to link with a different library in some cases (as explained later
 in this section and in Section 1.3, "Library Considerations in Selecting /FP
 Options").

 You should use only one of these options on the FL command line. The option
 applies to the entire command line, regardless of its position. (Note that x
 represents a wild card, so that xLIBFORA.LIB refers to either LLIBFORA.LIB
 or MLIBFORA.LIB.) The available options are listed below.

      Option                           Effect

      /FPa                             Generates floating-point calls;
                                       selects xLIBFORA.LIB

      /FPc                             Generates floating-point calls;
                                       selects xLIBFORE.LIB

      /FPc87                           Generates floating-point calls;
                                       selects xLIBFOR7.LIB

      /FPi                             Generates in-line instructions;
                                       selects xLIBFORE.LIB

      /FPi87                           Generates in-line instructions;
                                       selects  xLIBFOR7.LIB (default)

 Each /FP option includes two specifications:

   1. The floating-point instructions, either in-line 8087/287/387
      instructions or calls to floating-point library functions. The letter
      "i" indicates in-line instructions; the letters "c" or "a" indicate
      floating-point calls.

   2. The default floating-point package to use when you link the program.

 Table 1.1 summarizes the /FP options and their effects.

 Table 1.1  Summary of Floating-Point Options

                                                             Use of
 Option         Method            Advantages                 Coprocessor

 /FPi87         In line           Smallest memory            Required
                                  requirements and
                                  fastest option
                                  available with a
                                  coprocessor

 /FPc87         Calls             Slower than /FPi87,        Required
                                  but allows library
                                  changes at link time

 /FPi           In line           Larger than /FPi87,        Used if
                                  but works without a        present
                                  coprocessor; maximizes
                                  precision without a
                                  coprocessor

 /FPc           Calls             Slower than /FPi, but      Used if
                                  allows changing            present
                                  library at link time

 /FPa           Calls             Fastest and smallest       Ignores
                                  option available           coprocessor
                                  without a coprocessor,
                                  but sacrifices
                                  accuracy to speed


 The /AL (large) memory-model option is the default. Therefore, if no
 memory-model option is given on the same FL command line, the default
 library for each floating-point option is LLIBFORx.LIB (where x is 7, E, or
 A, depending on the math package the library supports). If the /AM
 memory-model option is given, the default library is MLIBFORx.LIB.

 The compiler may evaluate some expressions. Such evaluations always use the
 highest precision possible independent of the floating-point option you
 choose.

 Sections 1.2.1-1.2.5 discuss the /FP options and the advantages and
 disadvantages of each option.


 1.2.1  The /FPi87 Option

 The default floating-point option is /FPi87, which includes the name of an
 8087/287/387 library (either LLIBFOR7.LIB or MLIBFOR7.LIB, depending on the
 memory model) in the object file. At link time, you can change your mind and
 link explicitly with an emulator library (either with LLIBFORE.LIB or
 MLIBFORE.LIB, depending on the memory model). If you use this option and
 link with an 8087/287/387 library, an 8087, 80287, 80387 coprocessor must be
 present at run time; otherwise, the program fails and the math package
 displays an error message reading

            floating point not loaded

 the /FPi87 option.

 If you compile with /FPi87 and link with an emulator library, and a
 coprocessor is present at run time, you can use the NO87 environment
 variable if you wish to suppress the use of the coprocessor. (See Section
 1.4 for a description of NO87.) If you link with an 8087/287/387 library,
 the /FPi87 option is the fastest and smallest option available for
 floating-point operations.

 The Microsoft FORTRAN Compiler does not generate true in-line 8087/287 code
 when you use the /FPi87 option. Instead, the compiler inserts software
 interrupts to library-code software that direct each interrupt to use either
 the emulator or coprocessor, as appropriate. If you have linked the emulator
 code to your program, these interrupts will call the emulator if a math chip
 is not available.

 If your program is designed to run only with a coprocessor, you can speed up
 the program by assembling and linking the following code, which eliminates
 this special processing. In addition, all divide-by-zero errors will be
 masked.

      PUBLIC FIARQQ, FICRQQ, FIDRQQ, FIERQQ, FISRQQ, FIWRQQ
      PUBLIC FJARQQ, FJCRQQ, FJSRQQ

      FIARQQ EQU O
      FICRQQ EQU O
      FIDRQQ EQU O
      FIERQQ EQU O
      FISRQQ EQU O
      FIWRQQ EQU O
      FJARQQ EQU O
      FJCRQQ EQU O
      FJSRQQ EQU O

      extrn __fpmath:for
      extrn __fpsignal:for
      extrn __fptaskdata:for

      CDATA segment word common 'DATA'
          dw 0
          dw__fpmath
          dw__fpsignal
          dw__fptaskdata
      CDATA ends

      END


 1.2.2  The /FPc87 Option

 The /FPc87 option generates function calls to routines in the 8087/287/387
 library (LLIBFOR7.LIB or MLIBFOR7.LIB, depending on the memory model) that
 perform the corresponding 8087/287/387 instructions. As with the /FPi87
 option, you must have an 8087, 80287, or 80387 coprocessor installed to run
 programs compiled with this option and linked to an 8087/287/387 library.
 How-ever, the /FPc87 option also allows you to change your mind at link time
 and link to an alternate math library instead of emulator or 8087/287/387
 libraries. (See Section 1.3, "Library Considerations in Selecting /FP
 Options," for information about changing libraries at link time.)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Certain optimizations are not performed when /FPc87 is used, reducing the
    efficiency of your code. Since arithmetic of different precision may
    occur, the results may vary slightly.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 1.2.3  The /FPi Option

 The /FPi option generates in-line instructions for an 8087, 80287, or 80387
 coprocessor and places the name of the emulator library (LLIBFORE.LIB or
 MLIBFORE.LIB) in the object file. This option is particularly useful when
 you do not know in advance whether or not an 8087/287/387 coprocessor will
 be available at run time. If you distribute your program for use on other
 computers, you should use the /FPi option.

 If a coprocessor is present at run time, the program uses the coprocessor.
 If not, the emulator carries out the floating-point operations. If a
 coprocessor is not present at run time, the /FPi option is the most
 efficient way to maximize floating-point precision.


 1.2.4  The /FPc Option

 The /FPc option generates floating-point calls to the emulator library and
 then places the name of the emulator library (LLIBFORE.LIB or MLIBFORE.LIB,
 depending on the memory model) in the object file. The /FPc option gives you
 more flexibility than /FPi, since it allows linking with either an
 8087/287/387 or  alternate math library instead of an emulator library. (See
 Section 1.3 below for information about changing libraries at link time.)
 This option is also recommended if you link to libraries other than the ones
 SETUP builds.


 1.2.5  The /FPa Option

 The /FPa option generates floating-point calls and selects the alternate
 math library (LLIBFORA.LIB or MLIBFORA.LIB, depending on the memory model).
 This library provides the fastest and smallest library if you do not use an
 8087, 80287, or 80387 coprocessor. Like the /FPc option, /FPa lets you
 decide at link time whether to use an emulator or 8087/287/387 library.


 1.3  Library Considerations in Selecting /FP Options

 Sometimes you may want to use other libraries in addition to the default
 library for the floating-point option you chose on the FL command line. For
 example, you may want to create your own libraries or other collections of
 subprograms in object-file form and link these libraries at a later time
 with object files that you have compiled using different FL options. The
 following sections discuss these cases and how to handle them. Although the
 discussion assumes that you put your precompiled object files into
 libraries, the same considerations apply if you use individual object files.


 1.3.1  Choosing In-Line Instructions or Calls

 To choose an appropriate floating-point option, you must first decide
 whether you want to use in-line instructions and compile with the /FPi87 or
 /FPi option, or if you want to use floating-point function calls and compile
 with the /FPc87, /FPc, or /FPa option.

 If you choose in-line instructions for your precompiled object files, you
 cannot use the alternate math package (that is, you cannot link with an
 xLIBFORA.LIB library). However, in-line instructions give the best
 performance if you run your program on machines with an 8087, 80287, or
 80387 coprocessor.

 If you choose calls, your code executes more slowly, but at link time you
 can use any standard FORTRAN library (that is, any library created by the
 SETUP program) that supports the current memory model.


 1.3.2  Using a Standard Library for Linking

 You must use only one standard FORTRAN library when you link. You can
 control which library is used in one of two ways:

   1. At link time, as the first name in the list of object files to be
      linked, give an object file that contains the name of the desired
      library. For example, if you want to use the alternate math library,
      you must give the name of an object file compiled using the /FPa
      option. All floating-point calls in this object file refer to the
      alternate math library.

   2. At link time, select the /NOD (no-default library search) option and
      then give the name of the library file containing the floating-point
      package you want to use in the "Libraries" field or in response to the
      "Libraries" prompt. This library overrides the library names in the
      object files; all floating-point calls refer to the named library.

 Each library in the object files being linked is added to the "linker search
 list" (that is, the list of libraries that the linker searches to resolve
 calls external to FORTRAN). This searching can complicate the choice of a
 standard library.

 Suppose, for example, that you use the /FPa option on a large memory-model
 program to create a set of object files and then use the LIB utility to
 combine these object files into a library. Very possibly, each object file
 would include a default library name (that is, you did not use the /Zl
 option to compile). If you want to link this library with an object file
 created using the /FPc87 option, note that both LLIBFOR7.LIB and
 LLIBFORA.LIB are in the linker search list (assuming you compiled with the
 default memory-model option). The linker searches libraries on the command
 line first, so it searches LLIBFOR7.LIB before it searches LLIBFORA.LIB.
 Since LLIBFOR7.LIB would resolve all external references correctly, this
 mechanism works correctly.

 You can ensure that the standard library of your choice is used for linking
 by explicitly giving the library name on the LINK command line. In this
 case, LINK always searches the library you specify before it searches any
 libraries named in the object files. However, make sure you specify this
 library after you have specified any of your own libraries on the LINK
 command line. If you do not specify it, and your library contains a
 different search directive, you may encounter problems.

 For example, suppose the object modules in library B were compiled with the
 /FPc87 option, so that each module contains a search directive for the
 library LLIBFOR7.LIB. You want to link with an object file A compiled with
 the /FPa option, so that this object file contains a search directive for
 LLIBFORA.LIB. The following command line links library B with the object
 file A:

      LINK A,,,LLIBFOR7+B;

 The linker then searches the libraries in the following order:

   1. LLIBFOR7.LIB (since it is specified first on the command line)

   2. B (since it is specified second on the command line)

   3. LLIBFORA.LIB (since the object module A contains a search directive
      for this library)

   4. LLIBFOR7.LIB (since the modules in your library B contain search
      directives for this library)

 The link procedure would be as follows:

   1. The linker searches LLIBFOR7.LIB and resolves all references in object
      file A to standard run-time routines, since you specified this library
      on the command line.

   2. The linker closes LLIBFOR7.LIB and searches the next library in the
      list to satisfy references to routines in your library B. These
      routines normally contain references to standard run-time routines.
      Since LLIBFORA.LIB is the next library to be searched, this library
      satisfies the references in B. How-ever, this is not the library you
      intended to use, since you compiled B with the /FPc87 option, which
      uses LLIBFOR7.LIB to resolve references to standard run-time routines.

 As indicated in the example above, you cannot mix libraries in this fashion;
 you may get linker errors if you try. Note that if you had specified the
 library B+LLIBFOR7.LIB instead of LLIBFOR7.LIB+B on the LINK command line,
 the linker would have searched LLIBFOR7.LIB instead of the library
 LIBFORA.LIB to resolve standard run-time references in B, and the linking
 operation would have proceeded correctly.

 To avoid this kind of ambiguity and to make absolutely sure you are
 specifying the correct standard library for linking, use the /NOD linker
 option. This option causes the linker to search only the libraries that you
 specify on the command line.

 Alternately, use the /NOD: libraryname form to suppress the default search
 for a specific library. This is useful if a library specified on the command
 line includes search directives for other libraries that you do not wish to
 specify explicitly on the command line. For example, the OS/2 protected-mode
 libraries contain search directives for the support library, DOSCALLS.LIB.

 Perhaps the safest course of all, especially when you are distributing
 libraries to other users, is to compile the object files that make up the
 library with the /Zl option. This option tells the compiler not to include
 search directives in the object files. Later on, when you link the library
 with different object files, the standard library used for linking depends
 only on the floating-point and memory-model options used to compile the
 later object files. The /FPc compiler option is recommended for maximum
 flexibility when linking with such libraries.

 In the example below, the source file CALC.FOR is compiled with the default
 floating-point option, /FPi87:

      FL /c CALC.FOR
      LINK CALC+ANOTHER+SUM;

 /FPi87 generates in-line instructions and selects the 8087/287/387 library
 (that is, LLIBFOR7.LIB, since no floating-point option is given and the
 large-model library is the default).

 In the following example, CALC.FOR  is compiled with the alternate math
 option (/FPa):

      FL /c /FPa CALC.FOR
      FL CALC ANOTHER SUM /link LLIBFORE.LIB /NOD

 When the FL command is used to link, the /link field (options appearing
 after /link) specifies the /NOD option so that the LLIBFORA.LIB library
 (whose name is embedded in the object fileCALC.OBJ ) is not searched. This
 field gives the name LLIBFORE.LIB, which causes all floating-point calls to
 refer to the emulator library instead of the alternate math library.

 In the example below, SAMPLE.FOR  is compiled with the /FPc87 option, which
 selects the 8087/287/387 library LLIBFOR7.LIB, as the default library:

      FL /FPc87 SAMPLE.FOR /link /NOD:LLIBFOR7.LIB LLIBFAP.LIB

 However, the command line uses the /NOD: option to suppress the selection of
 LLIBFOR7.LIB, and tells the linker to use LLIBFAP.LIB instead (the
 OS/2-protected mode of the alternate math library). The /NOD: option
 instructs the linker to ignore the next library specified on the command
 line, but to include any subsequent libraries (and any references they
 contain to other libraries). Thus, DOSCALLS.LIB (referenced in LLIBFAP.LIB)
 is still searched.


 1.3.3  Ensuring Compatibility between Options

 Each time you compile a source file, you can specify a floating-point
 option. When you link more than one object file to produce an executable
 program file, you must ensure that floating-point operations are handled in
 a consistent way and that the current environment allows the linker to find
 the required library. Left to itself, the linker may or may not pick the
 library you intended. (See the Microsoft FORTRAN Getting Started manual for
 information about choosing floating-point options for the libraries you
 build with the SETUP program.)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    If you are building your own libraries of routines that contain
    floating-point operations, the /FPc floating-point option is recommended
    for all compilations, as it offers the greatest flexibility.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 1.3.4  Special Procedure for MS-DOS 3.2 Users

 This section is important only if your system has all of the following:

   þ  You use MS-DOS Version 3.2.
   þ  You boot from a hard disk drive.
   þ  Your computer uses a math coprocessor (such as the 80387).
   þ  You run programs that use floating-point math.

 For systems that satisfy all of the preceding conditions, you may be able to
 eliminate floating-point math problems by installing a small patch in DOS.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The floating-point problem has been eliminated in versions of DOS higher
    than 3.2. This includes DOS 3.21 and 3.3.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 If you are not sure whether you need the patch, take these steps:

   1. Copy the program PATCH87.EXE (included in this release) to the root
      directory of your hard drive.

   2. Reboot your system from the hard disk. Do not perform any floppy-disk
      operations after rebooting. It is very important to avoid floppy-disk
      I/O after rebooting because it will affect the reliability of the
      diagnostic test you are about to perform.

   3. If necessary, use the CD command from DOS to move to the root directory
      of your hard disk.

   4. Run the PATCH87.EXE program by entering this command at the DOS prompt:

           PATCH87

 The program then performs a diagnostic test on your system to determine
 whether it needs the DOS patch and, if so, whether it can be performed
 successfully.

 If the test indicates that you should install the DOS patch, take these
 steps:

   1. Format a blank floppy disk. (Do not use the /S formatting option to
      transfer system files to the disk. See Chapter 3 of the Microsoft
      FORTRAN Reference, "Nonrepeatable Edit Descriptors," for more
      information on /S.)

   2. Use the SYS command to copy IO.SYS and MSDOS.SYS (or PCDOS.SYS) from
      the root directory of your hard disk to the new floppy disk. For
      instance, if you boot from drive C:, you would enter the following
      commands:

         C:
         SYS A:

   3. Use the COPY command to copy COMMAND.COM and SYS.COM to the same floppy
      disk.

   4. Use the COPY command to copy the program PATCH87.EXE (included in this
      release) to the same floppy disk.

   5. Change the current drive and directory to the floppy disk, by entering

         A:

   6. Install the DOS patch by entering

         PATCH87 /F

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    If you experience any disk errors during steps 2 through 6, do not
    proceed with step 7. Reboot from your hard disk and repeat the entire
    process.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

   1. If you do not experience any errors, use the SYS command to transfer
      the files IO.SYS and MSDOS.SYS (or PCDOS.SYS) from the floppy disk back
      to your hard disk. For instance, if your system boots from the root
      directory of drive C:, you would enter the following command at the DOS
      prompt:

          A:
          SYS C:

   2. The DOS patch has been installed. Reboot the system.


 1.3.5  Using $FLOATCALLS and $NOFLOATCALLS

 The $FLOATCALLS and $NOFLOATCALLS metacommands control whether
 floating-point operations are processed through calls to library subroutines
 or by in-line instructions. These metacommands are no longer recommended.
 The /FP options offer similar control and are more flexible because they
 select the library to be used as well as the processing method.

 However, if you have existing code that contains these metacommands, keep in
 mind that $FLOATCALLS and $NOFLOATCALLS control only the processing method
 for floating-point operations. They do not affect the library name that the
 compiler places in the object file; this is still determined by the /FP and
 /A options that you choose on the FL command line.

 Also note that these metacommands take precedence over the floating-point
 options you give on the FL command line. This may mean, for example, that a
 source file containing $NOFLOATCALLS may result in an object file that
 contains in-line instructions, even if it were compiled with the /FPa
 option. Such an object file would not link correctly with an alternate math
 library.


 1.4  Using the NO87 Environment Variable

 Programs compiled with the /FPc or /FPi option use an 8087/287/387
 coprocessor at run time if one is installed. You can override this and force
 the use of the emulator instead by setting the NO87 environment variable.
 (See Section 2.5.1 or your DOS documentation for a discussion of environment
 variables.)

 If NO87 is set to any value when the program executes, the program ignores
 an 8087/287/387 coprocessor. The value of the NO87 setting appears on the
 standard output as a message. The message displays only if an 8087/287/387
 is present and suppressed; if no coprocessor is present, no message appears.
 If you don't want a message printed out, set NO87 equal to one or more
 spaces.

 Note that only the presence or absence of the NO87 definition is important
 in suppressing use of the coprocessor. The actual value of the NO87 setting
 only affects whether a message prints on the screen.

 The NO87 variable takes effect with any program linked with the emulator
 library (LLIBFORE.LIB or MLIBFORE.LIB). It has no effect on programs linked
 with LLIBFOR7.LIB, MLIBFOR7.LIB, MLIBFORA.LIB, or LLIBFORA.LIB.

 The following is an example of how NO87 can be used:

      SET NO87=Use of coprocessor suppressed

 This statement causes the message Use of coprocessor suppressed to appear
 on the screen when running a program that can use an installed 8087, 80287,
 or 80387 coprocessor. Note that if the equal sign is followed by a blank
 space, the coprocessor is suppressed but no message is displayed.

 The example below assigns no variable to NO87; the statement does not affect
 the coprocessor Note that no characters or spaces can follow the equal sign.

      SET NO87=

 Programs that can use an 8087/287/387 coprocessor do use it, if one is
 present.


 1.5  Handling 8087/287/387 Floating-Point Exceptions

 The five exceptions to floating-point arithmetic required by the IEEE
 standard are supported by the 8087/287/387 coprocessor and the real-math
 support routines. Exceptions that would result in a NAN ("Not a Number")
 error message are enabled by default. The others are disabled.

 These exceptions are not affected by the $DEBUG metacommand; instead, they
 are controlled by a "status" word and a "control" word, as explained below.

 Table 1.2 explains the six exceptions and their default and alternative
 actions.

 Table 1.2  Floating-Point Exceptions

 Floating-Point                                     Default
 Exception               Explanation                Action

 Invalid operation       Results in a NAN, such     Enabled; gives
                         as the square root of      run-time error message
                         -1 or 0*INF                M6101

 Divide by zero          Evaluates to a real        Enabled; gives
                         number divided by          run-time error message
                         zero: r/0.0                M6103



 Overflow                Results in a number        Enabled; gives
                         greater than maximum       run-time error message
                         representable number       M6104

 Underflow               Results in a number        Disabled; returns zero
                         smaller than the
                         smallest valid
                         representable number

 Denormalized            Results in a number
 operand                 so small that it can
                         only be approximately
                         represented

 Precision (or           Occurs whenever a          Disabled; returns
 inexact)                result is subject to       properly rounded
                         rounding error             result


 If any of these six exceptions are disabled, you can get NAN, Infinite, or
 Indefinite values in your variables. If you print such a value, the output
 field will contain NAN, INF, or IND, padded with periods to the field width.
 If the output field has fewer than three spaces, only periods are printed.


 1.5.1  Controlling the Processing Environment

 The 8086/286/386 and 8087/287/387 processors are controlled by the status
 word and the control word, respectively. This section describes the uses of
 these memory locations.

 When one of the floating-point-exception conditions given in Table 1.2 above
 occurs, the appropriate bit in the status word is set. This flag remains set
 until you clear it.

 Figure 1.1 shows the format of the status word.

 When the bit corresponding to one of these exceptions is set (equals 1) in
 the control word, that exception is masked, and the operation that caused
 the exception proceeds with its default action. Alternately, if a bit is
 reset (equals 0), the corresponding exception generates an error message,
 halts the operation, and terminates the program. In either case, the
 exception is logically connected (with OR) into the status word. See Chapter
 1 in the Microsoft FORTRAN Reference for more information on logical
 operators.

 Figure 1.2 shows the format of the control word.

 Besides masking-exception conditions, the control word is used to set the
 following modes for the internal arithmetic required by the IEEE standard:

      Mode                        Description

      Infinity control            Chooses affine mode (the mode
                                  where the familiar +INF and -INF
                                  style of arithmetic is used) or
                                  projective mode (the mode where
                                  +INF and -INF are considered to
                                  be the same number). The
                                  principal effect of projective
                                  mode is to change the nature of
                                  comparisons; projective INF does
                                  not compare with anything but
                                  itself.

      Round control               Rounds to nearest (or even), up,
                                  down, or truncates.

      Precision control           Determines the bit of the
                                  mantissa (24, 53, or 64) at which
                                  rounding takes place. All results
                                  are stored to 64 bits regardless
                                  of precision control which
                                  affects only the rounding in the
                                  internal form. When stored, any
                                  result is rounded to the storage
                                  precision again.

 The current defaults for the control word are shown in the following list:

      Mode or Mask                Default

      Infinity control            Affine
      Round control               Nearest
      Precision control           64 bits
      Interrupt-enable mask       Unmasked
      Precision mask              Masked
      Underflow mask              Masked
      Overflow mask               Unmasked
      Zero-divide mask            Unmasked
      Denormalized-operand mask   Masked (cannot be changed)
      Invalid-operation mask      Unmasked

 Table 1.3 below defines the mask settings for the overflow, zero-divide, and
 invalid-operation exceptions associated with several optional control words.
 A control-word value of decimal 4914 specifies the default-mask settings
 that are customary during 8087/287/387 operations.

 Table 1.3  Mask Settings for Operation Exceptions

      Control Word       Overflow     Zero Divide

      4914 = 16#1332     Unmasked     Unmasked
      4915 = 16#1333     Unmasked     Unmasked
      4918 = 16#1336     Unmasked     Masked
      4919 = 16#1337     Unmasked     Masked
      4922 = 16#133A     Masked       Unmasked
      4923 = 16#133B     Masked       Unmasked
      4926 = 16#133E     Masked       Masked
      4927 = 16#133F     Masked       Masked


 1.5.2  Reading and Setting Status and Control Values

 You can use the following procedures and functions to read or set the values
 of the status and control wordsÄÄthe INTEGER*2 arguments passed to the
 appropriate routines as described below. Their values determine the behavior
 of the 8086/286/386 (status-word) and 8087/287/387 (control-word)
 processors.

 The store-status-word (SSWRQQ) function returns the value of the status
 word. Use the following declaration for this procedure:

            INTERFACE TO INTEGER*2 FUNCTION SSWRQQ()
            END

 The store-control-word (SCWRQQ) function returns the value of the control
 word. Use the following declaration for this procedure:

            INTERFACE TO INTEGER*2 FUNCTION SCWRQQ()
            END

 The load-control-word (LCWRQQ) procedure sets the control word to a given
 value. LCWRQQ has the following declaration:

            INTERFACE TO SUBROUTINE LCWRQQ(CW)
            INTEGER*2 CW
            END

 Always use LCWRQQ to change the control word. To ensure that special
 routines handling stack-exceptions and denormal propagation will work
 correctly, the control-word and auxiliary variables must be set up in the
 same way that LCWRQQ organizes them. (Denormals are numbers very close to
 zeroÄÄon the order of 10^(-38) or smallerÄÄthat allow gradual underflow. The
 Microsoft FORTRAN numeric inquiry functions such as NEAREST and PRECISION do
 not apply to denormalized numbers. See IEEE math or Intel chip
 specifications for detailed information on denormals.)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Altering the 8087/287/387 control word with a macro assembler FLDCW
    (floating-point load-control word) instruction is not recommended if you
    use an 8087/287/387 coprocessor with a Microsoft language.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Since the denormal exception is not a part of the IEEE standard, the
 Microsoft FORTRAN exception handler intercepts denormal exceptions to give
 the effect of being masked. The 8087/287/387 chips themselves do not mask
 denormals. You cannot alter this handling of denormals regardless of the
 denormal bit value in the LCWRQQ control word.

 The Microsoft FORTRAN exception handler allows for software masking of
 invalid operations, but does not allow the math chip to mask them. If you
 choose to use the software masking, be aware that this can affect program
 performance if you run software on other computers or compile code written
 for Microsoft FORTRAN with another compiler.


 1.5.3  Floating-Point Exceptions under PC-DOS 3.2

 Programs running under IBM Personal Computer DOS (PC-DOS) 3.2 that
 explicitly trap for floating-point exceptions may not operate when
 floating-point exceptions occur.

 Use the following procedure to patch IBM PC-DOS 3.2:

   1. Use the SYS command from PC-DOS to transfer IBMDOS.COM and IBMBIO.COM
      to a writable disk.

   2. Copy COMMAND.COM, SYS.COM, and DEBUG.COM from your PC-DOS 3.2 floppy
      disk to the new disk.

   3. Copy STKPAT.BAT, STKPAT.SCR, RMRHS.EXE, and SETRHS.EXE from the \PATCH
      directory (included in this release) to the new disk.

   4. Reboot the system from the new disk.

   5. Run STKPAT.BAT. This patches the IBMBIO.COM on the new disk.

   6. Use the SYS command from PC-DOS to transfer the patch to any other IBM
      PC-DOS 3.2 disk.

 If you are running any version of DOS 3.2 other than IBM PC-DOS 3.2, contact
 your hardware manufacturer to see if the problem exists in your DOS 3.2
 version.


 1.6  Using Non-IBM(R)-Compatible Computers

 If your computer is not an IBM or a closely compatible computer and you want
 to use an 8087/287/387 coprocessor, make sure that the computer handles
 exceptions correctly. All Microsoft languages that support the 8087/287/387
 coprocessors must intercept exceptions for accurate results and to detect
 error conditions properly. Most other language vendors do not detect or
 correct these errors.

 The exception handler in the emulator and the 8087/287/387 libraries (that
 is, LLIBFORE.LIB or MLIBFORE.LIB, and LLIBFOR7.LIB or MLIBFOR7.LIB,
 respectively) works without modification on the following computers:

   1. The IBM PC family

   2. Computers that are closely compatible with the IBM PC, such as the
      WANG(R) PC and the AT&T(R) 6300

   3. The Texas Instruments(R) Professional Computer

   4. Any machine that uses nonmaskable interrupts (NMI) for 8087/287/387
      exceptions

 If your computer is listed above, or if you are sure that it is completely
 IBM compatible, you can immediately begin linking your programs. If it is
 not listed, you may need to modify the 8087/287/387 libraries.

 The distribution disk contains an assembly-language source file, EMOEM.ASM,
 to help make any necessary modifications. Any machine that sends the
 8087/287/387 exception to an 8259 Priority Interrupt Controller (master or
 master/slave) can be supported easily by a simple table change to the
 EMOEM.ASM module. The source file contains further instructions on how to
 modify EMOEM.ASM and patch executable files.


 Chapter 2  Selecting a Memory Model
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 You can gain greater control over how your program uses memory by specifying
 the program's memory model. This chapter explains the concept of memory
 models and discusses in detail each of the different models provided with
 the Microsoft FORTRAN Compiler: medium, large, and huge models. Later
 sections advise you on how to select an appropriate model for your program
 and how to make any necessary adjustments to that model.

 Adjusting a memory model to meet your program's requirements can produce
 more efficient code than using the default model. Understand the default
 models first, then learn to modify them to fit specific needs.


 2.1  What Is a Memory Model?

 A "memory model" is a set of predefined rules the compiler follows to
 transfer the program code and data into "segments" of 64K (kilobyte) blocks
 in memory. The memory model defines how the compiler organizes code and data
 into segments and what kind of addresses (near, far, or huge) will access
 the information in each segment. A near address is a 16-bit "offset"
 (location relative to the beginning of the current segment) that can access
 a maximum of 64K of mem-ory; a far address or a huge address is a 32-bit
 address that can access all avail-able memory. (See Section 2.2.2 for a
 discussion of the segmented memory and addresses of the 8086/80286
 microprocessor families.)

 When you select a memory model, you are telling the compiler that it can
 make certain assumptions about the program's characteristics and generate
 machine code accordingly. For example, when you select the large memory
 model (the default model for FORTRAN), the compiler allows for programs with
 more than 64K of code and 64K of data. The compiler must generate far
 addresses to access both code and data. In the medium model, the compiler
 expects only 64K or less of data, allowing it to generate near addresses to
 access data items.


 2.2  Understanding Memory Models

 To understand how memory models work, you need a basic understanding of the
 segmented architecture of the 8086 family of processors. The remainder of
 this section gives an overview of the addressing conventions used on the
 8086 processors and how they relate to memory models. Consult such
 references as the Programmer's Guide to the IBM PC for a detailed
 description of segments and their use.


 2.2.1  Code and Data Segments

 When you load a program into memory, its code (compiled executable
 statements) and data (information the program uses) are placed in separate
 storage areas. The processor treats the stored code as a sequence of
 operations to be carried out. A program refers to other code locations by
 making calls to subroutines or functions. Stored data are any values needed
 by the program, for which an area of memory is reserved, including variable
 names, arrays, or common blocks.

 All processors make this fundamental distinction between code and data.
 However, the way in which a program's code and data are stored and accessed
 depends on the architecture and addressing schemes of the particular
 processor being used.

 On the 8086, 80286, and 80386, code and data occupy separate segments and
 are accessed through separate segment registers. (See Section 2.2.2 for an
 explanation of the different types of addresses and the use of segment
 registers on the 8086/286/386.)


 2.2.2  Near, Far, and Huge Addresses

 The 8086 processor and its relatives are 16-bit machines. Normally, a
 machine using 16-bit addresses can access only 64K of memory. The 8086
 family of processors uses a special addressing scheme to overcome this
 limitation.

 To extend the amount of memory that can be addressed by a program, physical
 memory on the 8086 is divided into segments, each up to 64K long. The
 starting point of each segment in memory is represented by a 16-bit address.


 2.2.2.1  Near Addresses

 The 8086 reserves four registers to hold segment base addresses: CS (code
 segment), DS (data segment), SS (stack segment), and ES (extra segment). A
 segment address, however, points only to the "base" (beginning) of the
 segment. To refer to a particular item within a segment, you must also give
 the location of the item within the segment. This requires a 16-bit offset
 address, which gives the address of an item relative to the base of a
 particular segment. A 16-bit offset is called a "near" address.

 Wherever possible, it is preferable for the compiler to generate near
 addresses, to access code and data items. Near addresses are much more
 efficient than far addresses, because they require less space and take less
 time to calculate.

 Although a complete 8086/286/386 address is 32 bits long, the 8086/286/386
 uses reserved segment registers in a way that makes it possible to access
 some items with just near addresses. To see how this works, take the
 simplest case, where a program has one code segment and one data segment.
 The 8086 family has two machine registers that are dedicated to accessing
 the code and data segments: the CS and DS registers. When a program loads,
 the CS register is set to the address of the code segment, and the DS
 register is set to the address of the data segment. Since these registers
 are reserved for this purpose, the built-in 8086 instructions assume that
 the appropriate segment addresses can be found there. So the instructions
 require only the 16-bit offset of an item within a segment.


 2.2.2.2  Far Addresses

 Many programs, however, contain more than 64K of code or more than 64K of
 data. The addressing scheme becomes more complicated for larger programs.

 A complete address on the 8086/286/386, then, requires 32 bits: 16 bits for
 the segment address and 16 bits for the offset. A full 32-bit address is
 known as a "far" address.

 When program code exceeds 64K, it occupies more than one code segment. The
 Microsoft FORTRAN Compiler places the code from each "module" (compiled
 source file) in its own segment. Each module is therefore restricted to 64K
 or less, since it must fit in a segment. All calls to subroutines and
 functions require the compiler to use far addresses, since the address of
 the code must include both the segment (the location of the module) and the
 offset (the location of the subroutine or function within the module). This
 increases the size of the program and makes it less efficient, but allows it
 to grow beyond 64K.

 Similarly, programs with more than 64K of data occupy more than one data
 segment. The compiler divides data into different classes (for example,
 global uninitialized data, constants, and global initialized data) and
 assigns different classes to different segments.

 With multiple data segments, some data items are placed in the "default data
 segment," the data segment addressed by the DS register. (See Section
 2.2.3 for more information about this data segment.) The compiler only
 needs to generate a 16-bit offset address to access these items, since it
 uses the DS register to "point" to this segment. However, it must generate
 full 32-bit (far) addresses to access any data items outside the default
 data segment. This increases the size and makes programs less efficient,
 but it allows the program to access large amounts of data, a common
 requirement for FORTRAN programs.


 2.2.2.3  Huge Addresses

 One further complication can arise when a program has very large data items.
 A program may contain a single data item (array or common block) that
 exceeds 64K. Normally, the compiler calculates addresses of elements within
 a single data item using 16-bit (near) arithmetic. To do this, it assumes
 that all elements of the data item lie within the same segment, so the same
 base address can be used for the address of all elements. When a single data
 item exceeds 64K, this assumption no longer holds true.

 To access elements within a data item that exceeds 64K, the compiler must
 calculate addresses using 32-bit (far) arithmetic. In Microsoft FORTRAN, a
 single data item larger than 64K is known as a "huge" data item, and the
 address of the item is a "huge" address. A huge address, like a far address,
 is a full 32-bit address, but the huge address has the additional
 implication that 32-bit offsets are required to access all individual
 elements of the data item.

 Only data items can have huge addresses. The code for each module is
 restricted to 64K or less, so that it never exceeds one segment. Huge
 addresses are even less efficient than far addresses, but they resolve
 data-handling difficulties in programs that require very large data items.


 2.2.3  The Default Data Segment

 Even in programs with multiple data segments, the address of a single data
 segment can remain in DS throughout program execution. This segment is known
 as the "default data segment." Items in this segment can be addressed with
 near addresses.

 Local data items (but not formal arguments) smaller than the "data
 threshold" (that is, the maximum allowable size for a single piece of data)
 are placed in this segment, unless a FAR or HUGE attribute is used to move
 the item outside the segment. (The data threshold is set with the /Gt
 option, described briefly in Section 2.4.5.1.) In addition, the default
 data segment always contains certain internal data, regardless of the data
 threshold value.

 Every program has a default data segment. In a program with only one data
 segment, the default data segment is the only data segment. See Section
 2.3.2.1 for more information about the contents and use of the default
 data segment.


 2.3  Working with FORTRAN Memory Models

 This section introduces the characteristics of the FORTRAN memory models
 (medium, large, and huge) and discusses strategies for working within the
 size limits for code and data. (Section 2.4 below gives additional
 information on selecting a memory model and adjusting it to program-specific
 needs.)

 Briefly, the standard memory models have the following characteristics:

      Model                                      Characteristics

      Large (default)                Total program code and data can
                                     each exceed 64K. Each module is
                                     given its own code segment;
                                     therefore, each module is limited
                                     to 64K. The compiler creates
                                     multiple code and data segments
                                     as needed. However, formal array
                                     arguments are restricted to 64K,
                                     unless the argument is explicitly
                                     declared with the HUGE attribute.

      Medium                         Total program data are restricted
                                     to 64K. Total program code can
                                     exceed 64K. Each module is given
                                     its own code segment and
                                     therefore limited to 64K.

      Huge                           This model has the same
                                     characteristics as the large
                                     model, but all formal array
                                     arguments are assumed to exceed
                                     64K.

 Many of the code and data size limits apply to all three models. In fact,
 the only difference between the large and huge memory models is that the
 large model assumes arrays are smaller than 64K (both adjustable- and
 assumed-size arrays), while huge model assumes they are larger than 64K. The
 medium model differs from the large model only in its handling of common
 blocks, and use of near addresses rather than far addresses to pass
 subroutine arguments. See Section 2.3.2.4 for more information.

 Since the three memory models are similar in most respects, the following
 discussion of memory-model characteristics applies to all three models,
 except where noted.


 2.3.1  Specifying a Memory Model

 Specify a memory model for your programs by taking the following steps:

   1. When you run the SETUP program, you can either tell SETUP to build a
      library that supports the default (large) memory model or choose a
      different memory model. Based on your response, SETUP builds support
      for the preferred memory model into the run-time library it creates. If
      you want to use different memory models for different programs, create
      a separate library for each memory model.

   2. When you compile a program with the FL command, you can give a
      memory-model option on the command line. This option allows FL to link
      the program to a library that supports that memory model. If you do not
      specify a memory model, FL links to a large-model library by default.

      Note that if you use the OS/2 dual-mode names (described in Section
      3.1, "Compiling and Linking Under OS/2," in Chapter 3 of this manual),
      you may also need to specify the appropriate FL option /Lr, /Lp, or /Lc
      to select the correct library.

 The Microsoft FORTRAN Compiler defaults to the large memory model. The large
 model can accommodate programs that use more than 64K of total code and
 data. If the program uses multiple modules, each module can have up to 64K
 of code in a unique segment. For programs that require more than 64K of
 data, the compiler creates multiple data segments as needed by the program.
 See Sections 2.3.2.5 and 2.3.2.6 for specific restrictions of the large
 memory model.

 The large memory model can accommodate most of the programs compiled with
 the Microsoft FORTRAN Compiler. However, if a program does not fit in the
 large memory model, you must either shift to the huge memory model or use
 some of the techniques described in Chapter 7, "Optimizing Programs."
 Conversely, if a program runs under the large model, you may be able to
 improve its performance and decrease its size by changing to the medium
 memory model.

 Any large-model library (such as either LLIBFORE.LIB, LLIBFOR7.LIB, or
 LLIBFORA.LIB) works with the large or huge model. The medium model requires
 the library MLIBFORE.LIB, MLIBFOR7.LIB, or MLIBFORA.LIB.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    See Chapter 7,  for a description of other ways you can improve program
    efficiency by choosing libraries during installation, choosing compiler
    command-line options, and using I/O options within the program.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 2.3.2  Data-Handling Strategies and Limits

 The ways in which the three FORTRAN memory models allocate data result in
 different limits on data size. The sections below describe these
 data-allocation methods and the resulting limits. They also suggest
 strategies for working within these limits.

 See Table 2.1, "Data Allocation in Large Memory Models," in Section 2.3.2.6
 for a summary of data allocation.


 2.3.2.1  Limits on the Default Data Segment

 Keeping data in the default data segment increases a program's efficiency.
 Items in the default data segment are usually accessed with near (16-bit)
 addresses, since the program only requires an offset from the address in the
 DS register. In the large and huge models, however, 32-bit addresses are
 used to pass arguments to other routines, unless the formal arguments are
 declared with the NEAR attri-bute (see Section 2.4.2 for more information).

 The default data segment contains the following:

   þ  The program stack, which holds arguments passed to subprograms and
      local data declared in an AUTOMATIC statement. Normally the stack is
      2K, although you can change its size. In a multithread and/or
      dynamic-link library application running under OS/2, the program stack
      receives its own segment.

   þ  Floating-point and character constants, including constants generated
      by the compiler and I/O routines. These constants may differ in number
      or value from constants specified in the source program.

   þ  Data allocated or used by the run-time library, including run-time data
      and internal forms of formats for formatted I/O in medium-model
      programs.

   þ  Space for file I/O buffers for medium-model programs and space for
      dynamic allocation of file-control blocks (FCBs). Whenever an I/O
      package links with a program, FCBs are allocated for the terminal
      (console), for internal files, and for any additional files the program
      opens.

      For every FCB allocated, an associated I/O buffer is allocated as well;
      for medium-model programs, these buffers are allocated in the default
      data segment. In large-model programs, I/O buffers are allocated
      outside the default data segment. If you specify redirection on the FL
      command line, the compiler allocates an additional FCB and I/O buffer.

   þ  Arrays smaller than the data-threshold value in the large and huge
      models. The data threshold is a cutoff value set by the /Gt option
      which the compiler uses in allocating data. Any arrays larger than or
      the same size as the data threshold are stored in separate segments
      outside the default data segment and accessed with far addresses.

   þ  Subprogram entry and exit information, if the /4Yb option (or the
      $DEBUG metacommand) is in effect.

   þ  Allocatable arrays, if no far heap space is available.

 In the medium memory model, the default data segment also contains all
 common blocks not explicitly declared with the HUGE or FAR attribute.

 The total space required by the items listed above, including all local
 variables and arrays allocated in the default data segment, cannot exceed
 64K and still remain in the default data segment. If the program violates
 this restriction, you have the following options:

   þ  Move some data items out of the default data segment by using the FAR
      or HUGE attribute. See Section 2.4.2 for more information on these
      attributes.

   þ  Use the /Gt option, described in Section 2.4.5.1, to move all data
      items larger than a given size out of the default data segment. Note
      that this option does not work with medium-model programs.

   þ  Use the BLOCKSIZE option in OPEN statements in the source program to
      change the size of the I/O buffers allocated to the units you open. By
      default, each unit is allocated a 1,024-byte I/O buffer; however, the
      BLOCKSIZE option allows you to specify a different buffer size. Note
      that this solution is effective only in medium-model programs, since
      these buffers are not stored in the default data segment in large and
      huge models.

   þ  Remove the /4Yb option (or the $DEBUG metacommand) from all debugged
      programs.

 Any of the above strategies help reduce the amount of information stored in
 the default data segment. As your program approaches 64K, heeding one or
 more of these suggestions may keep the entire program within the default
 segment.


 2.3.2.2  Limits on Fixed-Size Arrays Exceeding 64K

 Fixed-size arrays larger than 64K are automatically allocated as many
 segments as necessary outside the default data segment. The compiler
 generates huge ad-dresses for these arrays since they cross segment
 boundaries. The only limit on the number of these huge arrays in the program
 is available memory.

 No individual scalar object, including an array element, can, however, span
 a segment boundary. If possible, the compiler offsets the start of an array
 in the segment so that this spanning does not occur. This cannot be done for
 character and structure arrays whose element size is not a power of two and
 whose length is greater than 128K. Common blocks cannot be adjusted either,
 since Microsoft FORTRAN does not require different program units to use the
 same declarations or ordering of variables. An error results if your array
 or common block declaration tries to allocate a scalar item across a segment
 boundary.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Arrays smaller than 64K (65,536 bytes) but larger than 65,521 bytes
    should be considered larger than 64K and declared with the HUGE attribute
    (see Section 2.4.2 for more information). Even though such arrays do
    not exceed the 64K limit, they may be too large for a segment if the
    segment does not start on a paragraph boundary.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 2.3.2.3  Limits on Adjustable- and Assumed-Size Arrays

 Adjustable-size and assumed-size arrays can appear in a FORTRAN program as
 formal arguments to subprograms. The size of an adjustable- or assumed-size
 array is determined at execution time by the size of the array passed as the
 cor-responding actual argument to the subprogram. (See the DIMENSION
 statement entry in Chapter 4 of the Microsoft FORTRAN Reference for more
 information on these types of arrays.)

 Although the size of such an array is actually determined at execution time,
 the compiler must decide on an addressing convention at compile time to
 generate references to the array. To do so, the compiler assumes either that
 the array is 64K or smaller, in which case it can generate near or far
 addresses, depending on where the array is storedÄÄor, that the array is
 larger than 64K, in which case huge addressing is required.

 In the large and medium models, the compiler assumes that adjustable- and
 assumed-size arrays are 64K or smaller, and therefore generates near or far
 addresses to access them. If the actual size of an adjustable-size or
 assumed-size array exceeds 64K at execution time, the program's results are
 unpredictable.

 To arrange the program so that it will correctly handle adjustable-size and
 assumed-size arrays larger than 64K, you have the following choices:

   þ  You can specify the HUGE attribute when you declare a particular array
      as a formal argument. This attribute tells the compiler that the actual
      argument may be (but is not required to be) larger than 64K, causing
      the compiler to generate a huge address for that particular array.

   þ  You can use the huge memory model (described in Section 2.4.1.2). In
      the huge model, the compiler assumes that all adjustable-size and
      assumed-size arrays are larger than 64K and generates huge addresses to
      access them. To improve program efficiency in the huge model, you can
      use either the NEAR or FAR attribute with any adjustable-size or
      assumed-size array whose actual size will always be smaller than 64K.

 Note that both of the methods mentioned above work in all cases, whether or
 not the actual size of the adjustable-size or assumed-size array is larger
 than 64K.

 Fixed-size formal array arguments are treated as if they were assumed-size,
 except that a fixed-size formal array argument larger than 64K is implicitly
 treated as huge. All of the preceding comments about assumed-size arrays
 apply to fixed-size arrays, too. (This treatment of arrays promotes
 compatibility with earlier versions of FORTRAN that did not support assumed-
 or adjustable-size arrays, but did support their functionality by ignoring
 the last dimension of an array.)


 2.3.2.4  Limits on Common Blocks

 In the large and huge memory models, each common block in the program is
 allocated as many segments as it needs outside the default data segment. If
 the common block is larger than 64K, the compiler generates huge addresses.
 If the common block is 64K or smaller, the compiler generates far addresses.

 Some restrictions apply to the variables of a common block larger than 64K.
 No individual array element or variable in a common block can span a segment
 boundary. The compiler allocates common blocks so that they always start at
 the beginning of a segment. This restriction means that the boundary between
 bytes 65,536 and 65,537 of the common block must fall between two variables
 or between two elements of an array.

 If an array in a common block spans a segment boundary and is passed as an
 actual argument, the corresponding formal argument must be declared with the
 HUGE attribute.

 If you have small, frequently accessed common blocks in your program, you
 may benefit from declaring such blocks with the NEAR attribute. This
 attribute causes the compiler to place the common block in the default data
 segment, where it can be accessed with more efficient near addresses.
 However, this is an option only if you have room in the default data
 segment. (See Section 2.3.2.1, "Limits on the Default Data Segment," for
 more information.)

 As an alternative, if you have room in the default data segment, you may be
 able to use the medium memory model. The medium memory model places all
 common blocks in the default data segment except the following:

   þ  Blank common blocks explicitly declared with the FAR attribute
   þ  Named common blocks larger than 64K


 2.3.2.5  Limits on Arguments Passed to Subprograms

 Normally, FORTRAN arguments are passed by reference. When a subroutine is
 called, the compiler places the addresses of the arguments on the program
 stack. Such a method of passing arguments places some restrictions on the
 different memory models:

   þ  In the large and huge models, the compiler uses far (32-bit) addresses
      in order to pass arguments to subprograms, even if the arguments are in
      the default data segment.

   þ  In the medium memory model, the compiler uses near addresses to pass
      arguments to subprograms, with the assumption that all program data
      resides in the default data segment.

 The following strategies can help you bypass these stack restrictions:

   þ  In large and huge models, if a formal argument is declared with the
      NEAR attribute in the INTERFACE statement, the compiler uses a near
      (16-bit) address to pass the actual argument to the subprogram. Since
      near addresses are more efficient than far addresses, this will improve
      program efficiency. However, any actual arguments passed to NEAR formal
      arguments must reside in the default data segment, or unpredictable
      behavior may result.

   þ  In the medium memory model, if you want to pass an argument that is
      outside the default data segment (for example, an array declared with
      the FAR attribute, or any fixed-size array larger than 64K), you must
      declare the corresponding formal argument with the FAR or HUGE
      attribute.

   þ  In Microsoft FORTRAN, you can also pass arguments by value. When you
      use the VALUE attribute with an argument, you specify that the argument
      is to be passed by value instead of by reference. Instead of placing
      the address of the argument on the stack, the compiler places a copy of
      the argument's value on the stack.

   þ  The stack is normally part of the default data segment. The default
      stack size is 2K; you can change the size by using the /F option with
      FL, the /STACK option with LINK, or the EXEMOD utility. When passing
      arguments by value to a subprogram, be sure the total size required by
      the arguments does not exceed available stack space. Passing entire
      arrays by value can cause problems. Since arrays can exceed the stack
      size all by themselves, passing an array by value can cause a stack
      overflow with unpredictable results.


 2.3.2.6  Summary of Data Allocation

 Table 2.1 summarizes data allocation in the large memory model. Differences
 between the large, huge, and medium models appear in footnotes to the table.

 Table 2.1  Data Allocation in Large Memory Models

 Type of Data            Storage                    Address Type      Restrict

 Local variables         Default data segment       Near             Combined
 and fixed-size                                                       all such
 arrays smaller than                                                  variable
 the data threshold                                                  arrays,
                                                                      stack an
                                                                      data sto
                                                                      default
                                                                      segment,
                                                                      exceed 6

 Arrays smaller than     Separate segments          Far               Availabl
 64K but larger than     outside default data
 the data threshhold     segment

 Arrays larger than      As many segments as        Huge              Availabl
 64K                     needed outside default
                         data segment

 Common blocks          As many segments as        Far if  the       Availabl
                         needed outside default     common block
                         data segment               is 64K or
                                                    smaller; huge
                                                    otherwise


 Also, for the large memory model the compiler assumes that all formal array
 arguments are 64K or smaller, unless specifically declared with the HUGE
 attribute. This is also true of the medium model. In the huge model, the
 compiler assumes that the adjustable-size and assumed-size arrays are larger
 than 64K, unless specifically declared with the NEAR or FAR attribute.


 2.3.3  Limits on Code Size

 The Microsoft FORTRAN Compiler places the code from each module in its own
 segment. Since the maximum size of a segment is 64K, the compiled code in
 each module cannot exceed 64K. You can determine how much code each program
 module contains by looking at a source-file or map-file listing.

 The following sections describe some strategies you can use to minimize the
 size of your program code and make it more efficient. (See Chapter 7,
 "Optimizing Programs," for descriptions of other strategies.)


 2.3.3.1  Creating Programs Larger than 64K

 If a module exceeds the 64K code-size limit, you must reduce its size by
 breaking it into two or more source files. An effective way is to break the
 source program into sections based on functionality and compile them
 separately. Working with the sections separately does not affect the final
 program size (although it may increase the total size of the object files),
 but it does alter the number of memory segments used. Keeping each piece
 focused on a particular aspect of the program also makes the source code
 easier to understand and maintain.

 Conversely, you can combine the code from two or more modules into one
 segment by using the /NT option (described in Section 2.4.5.2). Modules with
 identical text-segment names are loaded into the same segment, provided
 their combined length does not exceed the 64K segment limit.


 2.3.3.2  Improving Efficiency with the NEAR Attribute

 FORTRAN uses far (32-bit) addresses to reference progam code because the
 reference must provide both the segment address and the offset of the code
 item within the segment. If the program uses some subprograms very heavily,
 you may increase program efficiency by using the NEAR attribute.

 When applied to a subprogram, the NEAR attribute specifies that the
 subprogram code resides in the same segment as the calling routine. Since
 the segment ad-dress remains constant, the compiler can generate a near
 (16-bit) address to call the subprogram.

 To use the NEAR attribute on a subprogram, you must make sure that the
 subprogram actually appears in the correct segment. You can do this by
 placing the subprogram in the same source file as the calling routine, thus
 ensuring that they compile into a single module.

 If you want to keep the routines in separate source files, you can use the
 /NT option to set the text-segment names for both modules. If you give both
 modules the same text-segment name, they will be loaded into the same
 segment.

 See Section 2.4.2 of this manual and Chapter 1 of the Microsoft FORTRAN
 Reference for more information on the NEAR attribute.


 2.4  Selecting and Adjusting the Memory Model

 This section describes the methods you can use to select and adjust memory
 models. You can choose one of the three standard FORTRAN memory models by
 giving one of the /A options on the FL command line; these options are
 described in Section 2.4.1 below.

 When you use the standard memory models, the FL command handles library
 support for you. FL automatically links with a library corresponding to the
 memory model you specify on the command line, provided that you had already
 created this library at the time you ran the SETUP program. Unless you
 specify otherwise, FL uses a large-model library for linking; this is also
 the default for the library created by the SETUP program if you do not
 choose a memory model during installation. The large and huge models use the
 same library; the medium model has its own library.

 The advantage of using standard models for your program is simplicity. For
 the standard models, memory management is specified by compiler options and
 does not require the use of extended keywords. This is an important
 consideration if you expect to transfer your program to other systems,
 particularly systems that do not use segmented architecture. Note that
 the threshold option (/Gt), the text-segment-naming option (/NT), and the
 data-segment-naming option (/ND) are also portable ways to control
 allocation.

 The disadvantage of using standard memory models exclusively is that they
 may not produce the most efficient code. For example, if you have an
 otherwise large-model program with one assumed-size array that may exceed
 64K, it is to your advantage to declare the single large array with the HUGE
 attribute. Switching to the huge model forces the compiler to consider all
 assumed- and adjustable-size arrays to be huge.

 Sections 2.4.1-2.4.3 describe ways to adjust allocation for one or more
 items without changing the entire memory model. The methods described in
 these sections give you greater control over the program's structure and
 performance. However, to understand the effects of using the options and
 attributes described in these sections and to use them safely, you need a
 thorough understanding of FORTRAN memory models and the 8086 architecture.


 2.4.1  Using Standard Memory Models

 The /AL, /AH, or /AM option selects a memory model. You can use only one
 memory-model option on the FL command line. The option applies to all source
 files on the command line and has no effect on object files given on the
 command line.

      Options                Model Selected

      /AL                    Large model (default; corresponds
                             to the default library generated
                             by SETUP)

      /AH                    Huge model

      /AM                    Medium model

 If you compile separate source files of a program at different times, you
 must specify the same memory model for all of them.

 The following discussion assumes the standard DOS library naming
 conventions. If you use the OS/2 dual-mode command-line options /Lp, /Lr, or
 /Lc, then the naming conventions appropriate to OS/2 dual-modes apply. (See
 Section 3.4, "Understanding Multitasking, Threads, and Dynamic-Link
 Libraries," for more information on OS/2 library names.)


 2.4.1.1  Large Model (/AL)

 The /AL option tells the compiler to use the large memory model. The large
 memory model is the default, so you do not have to give this option
 explicitly. The large-model option allows the compiler to create multiple
 segments as needed for both code and data.

 The default for large-model programs is that both code and data items are
 accessed with far addresses. You override the default with the NEAR or HUGE
 attribute for data and the NEAR attribute for code.

 The /AL option causes the name of the large-model, standard FORTRAN
 libraryÄÄLLIBFORE.LIB, LLIBFOR7.LIB, or LLIBFORA.LIB, depending on the /FP
 option you specified on the FL command lineÄÄto be placed in each object
 file created. This allows the linker to use the required library
 automatically, provided that the library exists (that is, you built it using
 SETUP).


 2.4.1.2  Huge Model (/AH)

 The /AH option tells the compiler to use the huge memory model.

 The huge model differs from the large model only in the treatment of
 adjustable- and assumed-size arrays. With the huge model, the compiler
 assumes that such arrays are larger than 64K and generates huge addresses to
 access them; in the large models, the compiler assumes that such arrays are
 smaller than 64K unless they are explicitly declared with the HUGE
 attribute.

 These huge addresses make huge models less efficient than large models.
 How-ever, the huge model is useful when you want most or all adjustable-size
 and assumed-size arrays in the program to handle arrays larger than 64K
 correctly.

 When using the huge model, you can use the NEAR or FAR attribute with any
 formal array argument whose actual size is always smaller than 64K. This
 attribute tells the compiler to generate near or far addresses for the
 array; these addresses are more efficient than huge addresses. (See Section
 2.4.2 of this manual and Chapter 1 of the Microsoft FORTRAN Reference for
 more information about the FAR and NEAR attributes, respectively.)

 As an alternative to using the huge model, you can use the large model,
 specifying the HUGE attribute for any adjustable-size or assumed-size array
 whose actual size may exceed 64K. (See Section 2.4.2 of this manual and
 Chapter 1 of the Microsoft FORTRAN Reference for more information on the
 HUGE attribute.)

 The same libraries are used for huge model as for large model, so the /AH
 option places the name of the appropriate large-model FORTRAN library (that
 is, LLIBFORE.LIB, LLIBFOR7.LIB, or LLIBFORA.LIB) in each object file.


 2.4.1.3  Medium Model (/AM)

 The /AM option tells the compiler to use the medium memory model.

 The medium model differs from the large model in two respects: common blocks
 are allocated differently, and arguments to subprograms are passed
 differently.

 In medium models, blank common blocks not explicitly declared with the FAR
 or HUGE attributeÄÄand named common blocks that are 64K or smallerÄÄare
 placed in the default data segment. This allocation method allows the
 compiler to generate near addresses to access these common blocks. As a
 result, medium-model programs are usually more efficient in terms of speed
 and space than large- and huge-model programs. Common blocks larger than 64K
 are placed outside the default data segment and must be accessed with huge
 addresses.

 The argument-passing convention used by the medium model is also smaller and
 faster than in the large and huge models. In the medium model, the compiler
 passes actual arguments to subprograms using near addresses (instead of the
 far addresses used in large and huge models). This is possible because the
 compiler assumes that all data reside in the default data segment. If you
 want to pass data from outside the default data segment to a subprogram in
 the medium model, you must declare the corresponding formal argument in the
 subprogram with the FAR or HUGE attribute.

 Even if you cannot use the medium model for the program, you can force the
 compiler to use more efficient near addresses for passing arguments by
 declaring formal arguments with the NEAR attribute. You must be sure,
 however, that the actual arguments to be passed to the near formal arguments
 are located in the default data segment.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Input and output (I/O) on far and huge items are not allowed in the
    medium model. How-ever, you can copy these items to a temporary location
    in the default data segment to perform I/O on them.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The size of the default data segment cannot exceed 64K, and contains the
 stack and variables, in addition to common blocks. This restricts the
 practical size of the common blocks to less than 64K in order to remain
 within the segment boundaries. (See Section 2.3.2.1 for a description of
 the contents of the default data segment.)

 In practice, few FORTRAN programs can meet this restriction. You may still
 be able to use the medium model, however, by specifying the FAR and HUGE
 attributes in the program. For example, if the program contains large,
 infrequently accessed common blocks, you can use the FAR or HUGE attribute
 to move these blocks out of the default data segment. Smaller common blocks
 that are accessed more frequently can remain in the default data segment.

 You can also use the FAR and HUGE attributes to move other large data items,
 such as arrays, out of the default data segment. This creates more room in
 the segment for common blocks.

 As an alternative to using the medium memory model, you can use the NEAR
 attribute with specific common blocks to cause them to be placed in the
 default data segment. This improves program efficiency when you have one or
 more small, heavily used common blocks.

 See Section 2.4.2 of this manual and Chapter 1 of the Microsoft FORTRAN
 Reference for more information about the FAR, HUGE, and NEAR attributes.

 The name of the appropriate medium-model libraryÄÄMLIBFORE.LIB,
 MLIBFOR7.LIB, or MLIBFORA.LIB, depending on the /FP option you specified on
 the FL command lineÄÄis placed in each object file created, provided the
 library exists (that is, you built it using SETUP).


 2.4.2  The NEAR, FAR, and HUGE Attributes

 The NEAR, FAR, and HUGE attributes are keywords used in a FORTRAN program to
 override default-addressing conventions. Section 1.6 of the Microsoft
 FORTRAN  Reference describes the syntax of these attributes and outlines
 restrictions on their use. This section describes the effects of these
 attributes on code and data items in the three FORTRAN memory models.

 The NEAR, FAR, and HUGE attributes give you more flexibility than selecting
 one of the standard memory models. For example, you may be able to avoid
 switching from the large to the huge memory model if you use the HUGE
 attribute on any adjustable- or assumed-size arrays that may exceed 64K. On
 the other hand, even if the program requires the huge model, you can improve
 program efficiency by identifying small, frequently accessed items that
 could benefit from being placed in the default data segment.

 The FAR attribute is also useful for programs that have more data than can
 fit in the default data segment. You can declare less frequently accessed
 items with the FAR attribute in order to move them out of the default data
 segment, which leaves room for the more heavily used items. You can also use
 the /Gt option, described in Section 2.4.5.1, to move some data items out
 of the default data segment.

 Keep in mind, however, that the NEAR, FAR, and HUGE attributes are
 extensions to the FORTRAN language. They are meaningful only on processors,
 such as the 8086, which have a segmented architecture. If portable code is a
 high priority, do not use these attributes. The standard memory-model
 options, along with the /Gt option, allow you to alter the memory model in a
 portable way; since these options are given at compile time, they do not
 affect the source code.

 The discussion of each memory model in the preceding sections suggests
 strategies for using the NEAR, FAR, and HUGE attributes with each model.
 Tables 2.2 and 2.3 summarize the effects of each attribute on items in each
 of the three standard memory models.

 Table 2.2  Effects of the NEAR Attribute

                         Large                      Huge
 Item                    Memory Model               Memory Model

 Variables and           No effect for items        Same as large model
 fixed-size arrays       smaller than data
                         threshold value.
                         Arrays larger than
                         threshold value but
                         smaller than 64K are
                         placed in default data
                         segment.

 Formal arguments        Actual arguments are       Same as large model
                         passed with near
                         addresses and must be
                         in default data
                         segment.

 Common blocks           Placed in default data     Same as large model
                         segment; near
                         addresses

 Subprograms             Near calls                 Near calls

 Assumed-size and        Actual arguments are       Same as large model
 adjustable-size         passed with near
 arrays                  addresses and must be
                         in default data
                         segment.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Table 2.3  Effects of the FAR and HUGE Attributes

                         Large                      Huge
 Item                    Memory Model               Memory Model

 Variables and           If variable or array       Same as large model
 fixed-size arrays       is smaller than 64K,
                         it is moved out of the
                         default data segment
                         and accessed with far
                         addresses. If array is
                         larger than 64K, the
                         FAR or HUGE attribute
                         has no effect and huge
                         addresses are
                         generated.

 Formal arguments        FAR has no effect. If      If FAR is used, far
                         HUGE is applied to an      addresses are gen-
                         array, huge addresses      erated instead of huge
                         are generated.             addresses. HUGE has
                                                    no effect.

 Common blocks           No effect                  No effect










 Subprograms            No effect                  No effect

 Assumed-size,           FAR has no effect.         If FAR is used, far
 allocatable, and        Huge addresses are         addresses are gen-
 adjustable-size         generated with HUGE        erated instead of huge
 arrays                  attribute.                 addresses. HUGE has
                                                    no effect.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 2.4.3  The $LARGE and $NOTLARGE Metacommands

 The $LARGE and $NOTLARGE metacommands are related to the NEAR, FAR, and HUGE
 attributes, although their meanings are slightly different. Use of the
 $LARGE and $NOTLARGE metacommands is no longer recommended; the attributes
 are preferred.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The term "large" can be confusing since it is used both for the $LARGE
    metacommand and for the large memory model. The $LARGE metacommand is not
    related to the large memory model; instead, the size defined by the
    $LARGE metacommand corresponds to the huge memory model (for formal
    arguments to subprograms) or the HUGE attribute (for fixed-size arrays).
    The $LARGE and $NOTLARGE metacommands are retained for compatibility with
    previous versions of FORTRAN.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Fixed-size arrays declared under the $LARGE metacommand are treated the same
 as arrays declared with the HUGE attribute: arrays are placed outside the
 default data segment and must be accessed with far addresses for arrays 64K
 or smaller or with huge addresses for arrays larger than 64K. In the huge
 memory model, fixed-size arrays can be accessed with near addresses if they
 are smaller than a segment and smaller than the specified threshold value.
 In the medium and large memory models, declaring an array with the $NOTLARGE
 metacommand (either explicitly or by default) has no effect on the array's
 allocation. This is also true for fixed-size arrays in the huge model.


 2.4.4  Using Library Routines with Different Memory Models

 The standard libraries built by SETUP are designed to correspond to the
 three memory models. The libraries impose these restrictions on FORTRAN
 programs:

   þ  Arrays containing format specifiers cannot be declared with a HUGE
      attribute, specified in a $LARGE metacommand, or span more than one
      segment, in any memory model.

   þ  Huge arrays cannot be used as internal files in any memory model.

   þ  In medium-model programs, all items involved in I/O operations must be
      near, including items in I/O lists, format specifications, and internal
      files.

   þ  In medium-model programs, far format specifiers are illegal.


 2.4.5  Compiler Options Affecting Memory Usage

 Some FL command-line optionsÄÄ/Gt, /NT, and /NDÄÄtell the compiler to change
 the manner in which it allocates memory. These are discussed briefly below.
 (Chapter 7 of the Microsoft FORTRAN Reference, "The FL Command,"describes
 these compiler options in detail.)


 2.4.5.1  Data Threshold Option (/Gt)

 The /Gt option sets the data threshold. The "data threshold" is a cutoff
 value the compiler uses in allocating data. In the large and huge models,
 each array or variable larger than or the same size as the assigned
 threshold valueÄÄbut smaller than 64KÄÄis stored in a new data segment
 outside the default data segment and accessed with a far address.

 Decreasing the threshold value is useful when you want to move data out of
 the default data segment without declaring them with the FAR attribute.
 Increasing the threshold value allows you to store arrays larger than 32,767
 bytes (the default value) in the default data segment, provided you have
 room for them. You can also accomplish this by declaring such arrays with
 the NEAR attribute.


 2.4.5.2  Naming Segments Option (/NT)

 The /NT option lets you override the default-naming conventions used by the
 FORTRAN compiler and supply your own name for text segments.

 "Text" is simply another term for "code"; therefore, a text segment is a
 code segment. Segments with the same name are loaded into the same physical
 segment in memory. For example, you can use the /NT option to give two
 different modules the same text-segment name, thus ensuring that they will
 be loaded into the same segment in memory. This is useful when you want to
 use the NEAR attribute with a subprogram.

 In all three of the FORTRAN memory models, the compiler places the code from
 each module in a separate segment with a distinct name, which is formed by
 using the module base name along with the suffix "_TEXT." See Section
 2.3.3, "Limits on Code Size," for more information on code size
 restrictions.


 2.4.5.3  Naming Data Option (/ND)

 The /ND option lets you override the default-naming conventions used by the
 Microsoft FORTRAN Compiler and supply your own name for data segments. The
 default data segment is named "_DATA." The compiler places data stored
 outside the default data segment in private segments with unique names.
 These items are: huge arrays; arrays larger than the threshold value; items
 declared with the FAR or HUGE attribute; and common blocks in large and huge
 models. (See Section 2.2.1, "Code and Data Segments," for more information
 on the names.)

 The /ND option places data from the module in a uniquely named data segment.
 When execution transfers to that module, the compiler loads the DS register
 with the base address of the named data segment; when execution leaves the
 module, the compiler restores DS to its previous value (usually DGROUP). As
 a result, during execution in that module, the compiler can use near
 addresses for calls to data residing in the named segment. This technique
 particularly improves the performance of routines that repeatedly access
 data.


 Chapter 3  FORTRAN Programming under OS/2
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This chapter presents an overview of the steps required to compile and link
 FORTRAN programs within the OS/2 environment and discusses some important
 programming strategies for use with OS/2. The chapter assumes you are
 already familiar with OS/2 programming. Microsoft FORTRAN now supports
 multithread and dynamic-link-library programming under OS/2, as described in
 this chapter. The Microsoft CodeView and Utilities User's Guide, included
 with this release, contains a guide to using the Microsoft CodeView(R)
 debugger in a multithread or dynamic-link-library environment.

 Section 3.10, "Additional Information," refers you to other publications
 that can deepen your understanding of OS/2. These publications address
 concerns particular to multithread programming and provide more com-plete
 discussions of dynamic-link libraries.


 3.1  Compiling and Linking under OS/2

 If you have set up FORTRAN for programming only under OS/2, you do not need
 to use any additional options to compile and link with the FL command. Use
 FL just as you would under DOS. If you wish to alter your current library
 and directory arrangement, you can rerun the SETUP program and create any
 new arrangement you want.

 In discussions throughout this chapter, the run-time libraries are referred
 to with the general single-mode naming convention, xLIBFORy.LIB (where x
 equals L or M and refers to a large or medium memory model, and y equals 7,
 E, or A and refers to the math coprocessor, emulator, or alternate math
 library). However, when the discussion calls for dual-mode development (that
 is, when you want to build libraries for both OS/2 protected-mode and
 real-mode development), then a new naming convention is used. It will
 distinguish between versions of the libraries that use the same memory model
 and floating-point options, but operate under real mode or protected mode.
 Those libraries follow the naming convention xLIBFy®R|P¯.LIB, with R used
 for real mode and P indicating protected mode.

 When you set up FORTRAN to compile under both real and protected modes, you
 must use one of the library options when compiling and linking with FL.
 Specifying the /Lr option selects the real-mode libraries so your program
 runs under DOS or in the OS/2 compatibility box. The /Lp option selects the
 OS/2 protected mode versions of the libraries and creates a program that
 runs only under OS/2 in protected mode. For example, to compile BESSEL.FOR
 for OS/2 protected mode, you would use the following command:

      FL /Lp BESSEL.FOR

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    You must use the OS/2 (protected-mode) version of the CodeView debugger,
    CVP.EXE, to debug protected-mode programs.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 3.2  Running under Both DOS and OS/2

 You can create programs that run under DOS or in real and protected modes
 under OS/2 by linking your program for protected mode and "binding" it.
 Binding resolves references to OS/2 dynamic-link libraries so the program
 runs in both modes and under either operating system.

 To bind a program, link it with the protected-mode library. Then, use the
 BIND utility. For example, the following commands compile, link, and bind
 the file BESSEL.FOR:

      FL /Lp BESSEL.FOR
      BIND BESSEL APILMR.OBJ C:\LIB\DOSCALLS.LIB C:\LIB\API.LIB

 You must include the full path to the DOSCALLS.LIB  and  API.LIB  files in
 the command line. Alternately, you can compile, link, and bind the program
 automatically by specifying the /Fb option with the FL command. For example,

      FL /Lp /Fb BESSEL.FOR

 will automatically invoke the BIND utility for you.

 The AMPILMR.OBJ file is always required if you are using the large memory
 model. It also is required in the medium model if you use the ALLOCATE
 statement or the near and far heap in mixed-language programs. Although it
 is not explicitly required in other situtations, it is recommended that you
 link with AMPILMR.OBJ.

 If you write your own routines that use OS/2 system functions and you want
 to bind the resulting program, you must use only a subset of the Application
 Program Interface (API) functions, known as "Family API functions." See the
 Microsoft OS/2 Programmer's Reference for a list of Family API functions.

 See the Microsoft CodeView and Utilities User's Guide for complete
 information about the BIND command.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Bound programs cannot be debugged using the CodeView debugger. However,
    you can use the OS/2 version of CodeView, CVP.EXE, to debug a
    protected-mode version of the program.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 3.3  Interfacing FORTRAN with OS/2

 To facilitate access to OS/2 routines, this release of Microsoft FORTRAN
 provides include files that contain the FORTRAN interfaces and declarations
 of all the standard OS/2 system routines. The interface files (the file
 names ending with .FI) must be included as the first executable statement in
 a source file that references OS/2 routines. However, the declaration files
 (files ending with .FD) must appear at the beginning of each subprogram that
 calls OS/2 routines; they declare the structures and symbolic constants
 needed by the same OS/2 routines.

 To reduce compilation time and memory requirements, include only those
 particular parts of the file that you want. To do so, you need to set
 conditional-compilation flags when referencing the include files. See
 BSE.FI, BSE.FD, OS2.FI, OS2.FD, PM.FI, and PM.FD on the distribution disks
 for specific details. For information on the base OS/2 routines (BSE files),
 see the Microsoft Operating System/2 Programmer's Toolkit, Programmer's
 Reference. For information on the Presentation Manager routines (PM files),
 see the Microsoft Operating System/2 Programmer's Toolkit, Presentation
 Manager Reference, Volume 1.


 3.4  Understanding Multitasking, Threads, and Dynamic-Link Libraries

 Special library support for OS/2 programming is provided with Microsoft
 FORTRAN Version 5.0. This version provides libraries for creating types of
 programs not previously supported, including multiple-thread programs and
 dynamically linked libraries.

 Sections 3.4.1-3.4.2 briefly describe multitasking and dynamic-link
 libraries; they are aspects of programming that OS/2 supports but MS-DOS
 does not.


 3.4.1  Multitasking and Threads

 "Multitasking" occurs when multiple, mutually independent programs share the
 same central processing unit (CPU) simultaneously. This term does not imply,
 however, that more than one person is interacting with the processor.

 OS/2 performs the scheduling and allocation of real hardware resources to
 "threads," which are paths of execution belonging to one or more "processes"
 (pieces of program code).

 A thread can share the code and data segments of a process with other
 threads in the same process, but it has its own stack, unique register
 values, and current instruction address. The system grants processor time to
 each thread, so that all threads of a process execute simultaneously. A
 thread is also the smallest unit of execution that OS/2 can schedule.

 The OS/2 environment offers the following types of multitasking:

      Type of
      Multitasking                     Description

      Screen group                     One or more processes that share
                                       a logical screen and keyboard;
                                       highest-level multitasking
                                       element. A word processor and a
                                       data base operating
                                       simultaneously under OS/2 will
                                       normally run in two different
                                       screen groups.

      Process                          Part of an executing program that
                                       is the unit of ownership for
                                       resources such as memory, open
                                       files, semaphores (see Section
                                       3.6.1 below), and one or more
                                       threads.

      Thread                           Execution path within a process;
                                       smallest multitasking unit
                                       managed by OS/2.

 A sample program supplied with this release highlights the use of threads.
 The program, FMHELLO.FOR, is a multithread version of the classic "Hello
 world" program.

 A pictorial representation of screen groups, processes, and threads is shown
 in Figure 3.1.

 In the figure above, each screen group consists of two processes. Each
 process can be composed of several threads.

 In any process, Thread 1 is the main thread. All threads in a process act
 independently. Unless you take steps to make one visible to another, each
 thread executes completely independently of another thread. They do,
 however, share the same variables and resources.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    It is not recommended that you use mixed-language programming in
    conjunction with multithread programs or dynamic-link libraries.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 3.4.2  Dynamic-Link Libraries

 A "dynamic-link library" is a set of routines linked to a program either at
 load time or at run time, rather than at the time of compilation. OS/2
 supports dynamic-link libraries; MS-DOS does not. The easiest way to
 understand dynamic linking is to contrast it with the more familiar method
 of static linking.

 Static linking involves the following steps:

   1. Write and compile a program that uses a standard FORTRAN I/O statement,
      such as WRITE. This produces an object file that contains a reference
      to the external function in the standard FORTRAN library that supports
      this high-level statement.

   2. Link the object file with the library file containing the external
      function. This linking step combines the two files by including the
      code for the referenced function into the final executable file. In
      other words, the external reference is resolved at link time.

   3. Run the executable file produced in step 2. This executable file is
      totally self- contained because it contains the code for the external
      function.

 Static linking is useful, but it has some disadvantages. For example, the
 final exe-cutable code cannot be upgraded or changed without relinking with
 the main program's object files. When there is a new release of a commercial
 program, the entire executable file must be replaced with a new version.

 Another disadvantage of static linking is that common sets of code cannot be
 shared. Any and all executable files that use the WRITE statement must
 explicitly link with the necessary routine. Thus, the code needed to
 support WRITE may be duplicated in many different executable files on a disk
 or in memory.

 Executable files that take advantage of dynamic-link libraries are smaller
 than statically linked files because they do not contain the code for
 external functions. They can also be upgraded by supplying new versions of
 the dynamic-link libraries. No relinking of the executable file is required.

 A less obvious but very important advantage of using dynamic-link libraries
 is that executable files may share code. A common set of routines can be
 placed in a dynamic-link library and accessed by any number of executable
 files.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The OS/2 operating system uses dynamic-link libraries extensively. All of
    the OS/2 system functions are presented as external procedures that a
    user program can call.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Dynamic linking involves the following basic steps:

   1. Write and compile a program that references an external function, just
      as you would for static linking.

   2. Create a special .DEF (definitions) file that specifies which functions
      the main program will import from the dynamic-link library.

   3. Link the main program with the appropriate "imports library." An
      imports library is a file that contains a reference to the external
      function in the dynamic-link library. This file does not contain code
      from the external function.

   4. DOSCALLS.LIB is an example of an imports library; if you did not link
      with DOSCALLS.LIB, the .DEF file would have to specify each OS/2 API
      function used in your program.

      You must describe the functions your program imports from dynamic-link
      libraries either in the .DEF file or in an imports library. The
      resulting file, after linking, does not contain any code from the
      external dynamic-link library.

   5. Create a dynamic-link library that contains the code referenced in the
      main program.

   6. Execute the program created in step 3. When this executable file is
      run, OS/2 loads the code and discovers the special reference to the
      dynamic-link library. For each external dynamic-link-library reference,
      OS/2 searches the dynamic-link-library directory and resolves the
      external reference at load time.


 3.5  Multithread Programs

 The simplest multithread program is an independent self-contained program
 created by the static linking method. The following sections describe the
 support needed by multithread processes and the special software routines
 used in han-dling them.


 3.5.1  Library Support for Multithread Programs

 This section discusses the multithread library LLIBFMT.LIB. See Section
 3.7.4, "Creating a Multithread Dynamic-Link Library," for a discussion of
 FDLLOBJS.LIB.

 LLIBFMT.LIB is a statically linked, multithread, large-model FORTRAN
 li-brary. Use this library to construct statically linked executable files
 that support multiple threads of execution. A multithread program created
 with this library is a large model program by default, but can be adjusted
 to use the huge model. Note that LLIBFMT.LIB does not support the medium
 memory model.

 LLIBFMT.LIB is used to create a program that is entirely self-contained and
 that does not share FORTRAN run-time code or data with any other programs or
 dynamic-link libraries. Note also that LLIBFMT.LIB uses the emulator math
 package.

 A multithread program that uses this library must be linked only with the
 files LLIBFMT.LIB and DOSCALLS.LIB. No other FORTRAN run-time libraries
 should be used in linking. When you build object files with LLIBFMT.LIB, use
 the /MT command-line option to ensure that the code links with the correct
 library. For a complete description of the /MT option, see Chapter 7 of the
 Microsoft FORTRAN Reference.


 3.5.2  Thread Control Routines

 The FORTRAN run-time library allows thread creation through the BEGINTHREAD
 function. Thread termination should be performed by calling the FORTRAN
 run-time thread-termination function ENDTHREAD. Threads terminate
 automatically when they complete execution.  The function THREADID is used
 in the multithread environment; it returns an INTEGER*2 representing the
 identification number used by OS/2 to manage that thread within a process.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    The OS/2 Application Program Interface (API) call DosCreateThread
    should not be used with FORTRAN. If the low-level API calls
    DosCreateThread and DosExit are used, the results are unpredictable.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The next three sections provide the specifications for the FORTRAN run-time
 thread-management functions. In each of these sections, you will find:

   þ  A description of the routine, including any return values

   þ  An example showing the INTERFACE statement for the function and the
      order and type of any arguments

   þ  A discussion of any arguments

   þ  Notes or comments on the use of the routine


 3.5.2.1  The BEGINTHREAD Function

 The BEGINTHREAD function creates a new thread. Multithread programs must
 call BEGINTHREAD repeatedly, once for every thread created.

 If BEGINTHREAD successfully creates a new thread, it returns an
 identification number for the new thread. If BEGINTHREAD fails, a run-time
 error results. Such an error can occur if you specify an odd address stack;
 specify an odd or zero-length stack; attempt to create more threads than the
 run-time library can support (32); or experience a low-level system error.
 The example below shows the required interface to BEGINTHREAD and
 demonstrates how it can be used.

 Example

            INTERFACE TO INTEGER*2 FUNCTION
           + BEGINTHREAD(RTN,STK,SIZE,ARG)
            INTEGER*4 RTN [VALUE]       ! pass LOCFAR(thread_routine)
            INTEGER*1 STK(*)
            INTEGER*4 SIZE
            INTEGER*4 ARG
            END

      C  Example call to BEGINTHREAD
            EXTERNAL CHILD
            INTEGER*1 STACK(2048)
            INTEGER*2 TID
            TID = BEGINTHREAD (LOCFAR(CHILD), STACK(1), 2048, 0)
            .
            .
            .
            END

 The arguments used in the BEGINTHREAD function are discussed below:

      Argument                    Description

      RTN                         The far address of the routine
                                  you want the new thread to
                                  execute. For BEGINTHREAD to work
                                  properly, you must pass it the
                                  number returned by the LOC or
                                  LOCFAR function by value. The
                                  thread terminates automatically
                                  when it returns from the routine
                                  supplied by the RTN  argument.
                                  You also can terminate the thread
                                  by calling ENDTHREAD within the
                                  far routine.

      STK                         The memory address of an area to
                                  be used as the new thread's
                                  stack. The address must be on a
                                  word boundary (an even number)
                                  and must specify an area at least
                                  as long as the SIZE  argument.
                                  Usually the address refers to
                                  either a static array or an area
                                  created by the ALLOCATE
                                  statement.

      SIZE                        The size of the thread's stack,
                                  in bytes. The SIZE argument must
                                  be even, positive, and non-zero.

      ARG                         A parameter to pass to the newly
                                  created thread, passed by far
                                  reference. Typically, ARG  is the
                                  address of a data item, which may
                                  be a structure containing
                                  multiple data fields. If you do
                                  not need to pass an argument to
                                  the child thread, set ARG  equal
                                  to zero.

                                  Note that in the sample interface
                                  above, ARG  is declared as
                                  INTEGER*4. In general, you can
                                  modify the INTERFACE statement to
                                  reflect the type of the actual
                                  argument you pass. This
                                  modification avoids type mismatch
                                  warnings from the compiler.

                                  If you pass different types of
                                  arguments to more than one
                                  threadÄÄwhich all use the same
                                  interfaceÄÄthe compiler will warn
                                  you with a recoverable error
                                  message.

                                  See Appendix D in the Microsoft
                                  FORTRAN Reference for more
                                  information on these types of
                                  errors. You can either ignore the
                                  messages or move each BEGINTHREAD
                                  call to a separate module with a
                                  custom interface.

 When a thread is created, the system calls the function supplied by the
 argument RTN. The new thread can access all files and resources owned by the
 parent process.

 If you write multiple-thread programs that make FORTRAN run-time calls from
 child threads, take care to allocate adequate stack space. To be safe,
 allocate at least 2K bytes for each thread's stack. In general, you should
 have 2K of stack space free when calling any API routine.


 3.5.2.2  The ENDTHREAD Subroutine

 The subroutine ENDTHREAD terminates a thread created by the BEGINTHREAD
 function. ENDTHREAD does not require any arguments. The example below shows
 the interface and a typical call to ENDTHREAD.


 Example

            INTERFACE TO SUBROUTINE ENDTHREAD()
            END

            C  SAMPLE USAGE OF ENDTHREAD
                   IF (i.EQ.0)
                     CALL ENDTHREAD()
            .
            .
            .

 Threads terminate automatically when they finish executing. However, the
 subroutine ENDTHREAD lets a thread conditionally terminate itself before it
 com-pletes its normal execution path.


 3.5.2.3  The THREADID Function

 The function THREADID returns to a thread its own unique identification
 number within a process. THREADID does not take any arguments. The example
 below shows the required interface to THREADID and demonstrates how it can
 be used.

 Example

            INTERFACE TO INTEGER*2 FUNCTION THREADID()
            END
            .
            .
            .
            RESULT(THREADID())=.TRUE.
            RETURN
            END

 Use THREADID when a thread needs to know its own identification number. For
 example, it could serve as an index to a global array that manages thread
 status.


 3.6  A Multithread FORTRAN Program

 The sections below describe the steps to compile, link, and run a
 multithread version of the classic "Hello world" program. The source code
 for this program is included on the distribution diskettes as FMHELLO.FOR.

 The FORTRAN multithread "Hello world" program FMHELLO brings up one thread
 for each command-line argument. Each thread will print "Hello world from
 thread n!" the number of times specified in the corresponding argument. The
 multithread library supports a maximum of 32 threads. For FMHELLO, since the
 main body is itself a thread, the maximum number of child threads is 31.


 3.6.1  Sample Program Operation

 To try the multithread implementation of "Hello world," type

      FMHELLO arg1 ®arg2...¯

 For example,

      FMHELLO 2 4 6

 brings up 3 threads; the first thread says "hello" two times, the second
 thread says "hello" four times, and the third thread says "hello" six times.

 In operation, FMHELLO follows these steps:

   1. Brings up the requested number of threads with BEGINTHREAD
   2. Waits until all threads have been brought up
   3. Begins multithread execution and waits for completion

 The synchronization in step 2 keeps the threads from all starting as the
 same thread (with the same thread ID number). Multiple threads with the same
 ID number can occur if the first spawned thread completes execution before
 the next request to create a thread is acted upon. The amount of time
 allotted to any individual thread is actually much larger than the execution
 time spent in each thread.

 An alternative method of thread synchronization is to use semaphores. A
 "semaphore" is a software flag used by OS/2 to precisely coordinate the
 activities of two or more threads. Microsoft FORTRAN does not directly
 support semaphores. If you wish, you can access semaphores through the OS/2
 API calls. See the OS/2 documentation (such as Microsoft Operating System /2
 Programmer's Reference) for a complete discussion of the required functions.

 In order to synchronize its threads, FMHELLO uses the DosSleep function,
 which causes a thread to periodically "wake up." During these wake-ups, the
 thread can check for a software flag. Polling a flag using DosSleep has some
 drawbacks, however, because

   þ  The thread wakes up unnecessarily and performs some processing only to
      find it has to go to sleep again.

   þ  The thread oversleeps (that is, the event it is waiting for has already
      occurred, but the thread sleeps until the sleep request expires).

 Often, your program will have to wait for a process to complete. If you do
 not know exactly how long that process will take, it is better to make the
 thread wait for a semaphore rather than use DosSleep.

 In the case of FMHELLO, however, the use of DosSleep is more appropriate
 because

   þ  DosSleep is easily understood and clearly functional.

   þ  The program is not particularly time dependent, so the above
      disadvantages will not adversely affect it. Also, we know that this is
      a one-time event and that the main code "quickly" wakes up the
      child-code portion of the program. That is, the DosSleep code path
      occurs once at child start-up time and is never entered again during
      child execution.

 Also, the sleep time is short and predetermined. The parent code sets the
 flag as soon as all of the threads have been started. In other words, there
 is no chance for the parent code to do other things and leave the thread
 waiting for DosSleep for long periods of time.


 3.6.2  Compiling and Linking the Sample Multithread Program

 The program FMHELLO.FOR is a statically linked multithread program. The
 steps in compiling and linking this program are as follows:

   1. Ensure that the files LLIBFMT.LIB and DOSCALLS.LIB are available.

      Either place them in the current working directory or in a directory
      specified in the LIB environment variable.

      The file LLIBFMT.LIB takes the place of the regular run-time-library
      file used in linking. See Section 3.5.1 for more information on
      LLIBFMT.LIB.

      The file DOSCALLS.LIB provides support for any OS/2 calls made in the
      multithread program. In this example, the OS/2 call DosSleep is used.
      In addition, the LLIBFMT.LIB file refers to DOSCALLS.LIB in the form of
      OS/2 calls.

   2. Compile and link the program with the following conditions set:

        þ  Large code address size
        þ  Far or huge data address size
        þ  Segment setup of SS not equal to DS; DS fixed

      The first two conditions are specified by either the large (the
      default) or huge memory models. The last condition is specified by
      using the /MT option with the FL command. (See Section 3.8.1 for a
      complete description of the /MT option.)  The complete FL invocation is
      shown below:

             FL /MT FMHELLO.FOR

      When compiling multithread programs, you can specify any of the
      floating-point options except /FPa (the alternate math package). To
      maximize efficiency, however, use the default floating-point option
      (/FPi87). The library LLIBFMT.LIB includes the emulator math package,
      but will use a math coprocessor if one is present. The other
      floating-point options (except /FPa) will also work, but cause their
      respective versions of the default library name to be placed in the
      object files. These differences are only a concern if you link as a
      separate step and use the /NOD: option to suppress default library
      searches.

   3. Link the resulting object file with LLIBFMT.LIB and DOSCALLS.LIB only.
      (The FL driver automatically takes care of this step.)

      Note that the default memory model (large) implies a far data address
      size. You can also specify huge data address with the /AH command-line
      option, but the linker will use the same library.

   4. Compile and link your programs separately, invoking the linker as
      follows to link the object file with LLIBFMT.LIB:

          link FMHELLO,,,/NOD:LLIBFOR7 LLIBFMT.LIB;

      LLIBFMT.LIB contains search directives for DOSCALLS.LIB, so you do not
      need to reference it explicitly. The /NOD: option turns off the default
      library search; you may need to specify a library other than LLIBFOR7
      if you specified a floating-point option other than the default.

   5. Run the program under OS/2.


 3.6.3  Troubleshooting Multithread Programs

 Several problems may occur when creating, linking, or executing a
 multithread FORTRAN program. Two of the more common ones are listed below:

      Problem                               Probable Cause

      LINK searches for LLIBFOR7.LIB or     If you link in a separate step
      LLIBFORE.LIB.                         and omit the /NOD option from the
                                            LINK command, the linker searches
                                            for the default library. The
                                            default library should not be
                                            used with multithread programs.

                                            The /NOD: option tells the linker
                                            to ignore the default library and
                                            use the specified library instead
                                            (see also Section 3.6.2). This
                                            problem may also be avoided by
                                            compiling with the /Zl option,
                                            which suppresses
                                            default-library-search records in
                                            the object files. However, you
                                            must then explicitly specify
                                            LLIBFMT.LIB on the link command
                                            line.

      Protection violation error            Many OS/2 programming errors will
      occurs.                               cause a protection violation. One
                                            cause of this error is failure to
                                            specify the condition "SS is not
                                            equal to DS" in the FL command
                                            invocation.

                                            Specify the correct conditions
                                            with the /MT option. Recompile
                                            the program with /MT. See the
                                            Microsoft FORTRAN Reference for
                                            specific FL command information.
                                            Another common cause of
                                            protection violations is
                                            over-addressing a common block or
                                            array without using the $DEBUG
                                            metacommand.


 3.7  Using FORTRAN Dynamic-Link Libraries

 Microsoft FORTRAN 5.0 includes support for dynamic link libraries. You can
 write routines in FORTRAN in such a way that they can be dynamically linked.
 In addition, this release provides an object-file library, FDLLOBJS.LIB,
 that can be used to create a dynamically linked multithread FORTRAN run-time
 library.

 Dynamic-link libraries can be debugged with the protected-mode CodeView
 debugger. (See Chapter 12 in the Microsoft CodeView and Utilities User's
 Guide.)


 3.7.1  Creating a Dynamic-Link Library

 FORTRAN 5.0 allows you to build your own dynamic-link libraries.To create
 such a library, take the following steps:

   1. Write a subroutine as you normally would (in a separate file).

   2. Use the LOADDS attribute on any subroutine name that will be used as an
      entry point to the dynamic-link library.

   3. Build a definitions (.DEF) file that exports the appropriate routine
      names. (That is,  build a file that uses the EXPORT module definition
      statement to name the routines you want to include in the library
      available to the calling program.)

   4. Compile and link the routine to build it as a dynamic-link library.

 The example in Section 3.7.4 steps through this process in detail. A
 dynamic-link routine written in this way will require a dynamically linked
 FORTRAN run-time library if the routine requires run-time support.


 3.7.2  The LOADDS Attribute

 You can use this attribute for routines that serve as entry points to your
 own dynamic-link libraries. The LOADDS attribute causes the data segment
 (DS) register to be loaded with the base address of the default data segment
 of the dynamic-link library upon entering the routine named with the
 attribute. The previous DS value is restored when the routine terminates.

 You do not need to specify the LOADDS attribute on local routines called
 from within the dynamic-link library. The correct DS register is set up
 during the call to the dynamic-link library provided the LOADDS attribute
 was specified on the entry routine. This attribute must appear with the
 names of routines that act as interfaces to a dynamic-link library.


 3.7.3  Multithread Dynamic-Link Libraries: An Overview

 The FDLLOBJS.LIB OS/2 support file is an object-file library used to create
 a dynamically linked FORTRAN run-time library. This library is provided as
 an object library with an associated definition file (FDLLOBJS.DEF) that
 defines the entry points into the FORTRAN run-time library. These entry
 points let you choose only those run-time modules that your program requires
 for a particular application. Three additional start-up object modules are
 also required to support dynamic linking in FORTRAN:

      Start-up Module                  Supported files

      FRTEXE.OBJ                       Start-up code for executable
                                       files

      FRTDLL.OBJ                       Start-up code for user-written
                                       dynamic-link libraries

      FRTLIB.OBJ                       Start-up code for dynamically
                                       linked FORTRAN run-time library

 A FORTRAN run-time library created with FDLLOBJS.LIB is dynamically linked
 and available to a multithread program and optionally to a group of closely
 associated dynamic-link libraries. User-written dynamic-link libraries that
 require run-time support must use an appropriately created, dynamically
 linked FORTRAN run-time library (see Section 3.7.6).

 The close association of the multithread program, the FORTRAN run-time
 library, and a group of dynamic-link libraries is shown in Figure 3.2.

 The main program (PROGRAM.EXE) and the two dynamic-link libraries (DLL1.DLL
 and DLL2.DLL) share the FORTRAN run-time data in the file FRTLIB.DLL. Each
 PROG.EXE, DLL1.DLL, and DLL2.DLL file has its own private data segments.

 The FORTRAN run-time dynamic-link library is closely tied to the program and
 the other dynamic-link libraries, because it contains global FORTRAN
 run-time data, thread-identification numbers, and FORTRAN run-time routines
 required by the program and other dynamic-link libraries.

 If you build a program using the dynamically linked multithread support of
 the FORTRAN run-time library, it can share the FORTRAN run-time library with
 one or more dynamic-link libraries that are closely related. FORTRAN
 run-time global data used to manage I/O are shared.

 Threads are managed in a dynamic-link library by the FORTRAN functions
 BEGINTHREAD and ENDTHREAD. See Section 3.5.2 for a description of these
 functions.


 3.7.4  Creating a Multithread Dynamic-Link Library

 The process of creating a multithread dynamic-link FORTRAN run-time library
 under OS/2 is outlined below. This example of how to create a dynamic-link
 library of the FORTRAN run-time routines is also contained in FDLLOBJS.CMD.

   1. Create a definitions file (.DEF) that specifies the functions in the
      FORTRAN run-time library you want the dynamic-link library to export
      (see Section 3.7.5, below). The file FDLLOBJS.DEF, included with this
      release, is a sample definitions file that includes all of the FORTRAN
      run-time functions currently supported.

   2. Link the special start-up file FRTLIB.OBJ with the files FDLLOBJS.LIB,
      DOSCALLS.LIB, and the definitions file from step 1. This linking
      creates a customized FORTRAN run-time dynamic-link library file. If you
      use the sample file FDLLOBJS.DEF, you'll create a library named
      FRTLIB.DLL. The following files are then linked together:

             File Name                   Description

            FRTLIB.OBJ            Start-up code for library files

            FDLLOBJS.LIB          FORTRAN run-time-library objects

            DOSCALLS.LIB          OS/2 support library

            FDLLOBJS.DEF          Definition module from step 1

            FRTLIB.DLL            Output from LINK

      To accomplish this, type the command

              LINK  FRTLIB.OBJ,FRTLIB.DLL,,FDLLOBJS.LI  B/NOE,
              FDLLOBJS.DEF


   3. Run the Microsoft Import Library Manager (IMPLIB) on the definitions
      file from step 1, and the support file DIFFHLP.DEF supplied with this
      release, to create a customized imports library file containing the
      exported functions. The form of the command is shown below:

              IMPLIB FRTLIB.LIB FDLLOBJS.DEF DIFFHLP.DEF

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The name FRTLIB.DLL is used only as an example. You should use a unique
    name particular to your application. If you do not, you run the risk of
    having a FORTRAN run-time library named FRTLIB.DLL built with any
    customized routines overwritten by another software vendor's dynamic-link
    library.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 3.7.5  Creating a Custom FORTRAN Run-Time Definitions File

 The general process described in Section 3.7.4, "Creating a Multithread
 Dynamic-Link Library," can be used to build a dynamic-link FORTRAN run-time
 library which includes only those routines required by a particular
 application (or set of applications). To do this, create a definitions
 (.DEF) file that specifies exactly those routines required by your program.
 You can build a custom definitions file through the following process:

   1. Print a copy of the sample definitions file, FDLLOBJS.DEF. It will act
      as a reference.

   2. Compile your program using the /MD option on the FL command line
      with-out specifying a library. As a result, the linker will list an
      "unresolved external" error for every routine referenced in the
      program. Note the names of each unresolved symbol.

   3. Create a new file as your custom definitions file. Place the names that
      appear in both the linker's unresolved symbols list and in the sample
      definitions file in your new file's EXPORT list. Use the copy of
      FDLLOBJS.DEF as a syntax guideline for your new file.

      You can now substitute your custom definitions file in place of the
      file FDLLOBJS.DEF in the instructions listed in steps 2 and 3 in
      Section 3.7.4. Be certain to uniquely name any dynamic-link FORTRAN
      run-time library created this way.


 3.7.6  Using a Multithread Dynamic-Link Library

 Once you create a FORTRAN run-time multithread dynamic-link library, it can
 be used by a program and the program's associated dynamic-link libraries.
 This release of FORTRAN includes a sample program that calls both a
 user-written dynamic-link library and a customized version of a dynamic-link
 FORTRAN run-time library (such as the one created in Section 3.7.4). The
 sample files FMTMAIN.FOR, FMTMAIN.DEF, FMTDLL.FOR, and FMTDLL.DEF on the
 distribution disks demonstrate dynamic linking.

 In order to use a run-time multithread dynamic-link library, follow the
 steps below as an example. The batch file MKFDLL.CMD included in this
 release also shows this process.

   1. Compile and link the main program FMTMAIN.FOR to produce the file
      FMTMAIN.EXE.

      Use the FL command

           FL /MD FMTMAIN.FOR FRTEXE.OBJ FRTLIB.LIB FMTMAIN.DEF

      See Section 3.8.1 for more information on the /MD option. The above
      FL command links the following files:

      File Name                   Description

            FMTMAIN.OBJ           Output from the compilation of

            FRTEXE.OBJ            Start-up code for executable
                                  files

            FRTLIB.LIB            Customized FORTRAN run-time
                                  imports library

            DOSCALLS.LIB          OS/2 support library

            FMTMAIN.DEF           Definitions (.DEF) file for
                                  FMTMAIN.FOR

            FMTMAIN.EXE           Output from LINK

      Given the object file FMTMAIN.OBJ compiled with the /MD option, you
      can also invoke the linker separately, with the following command:

           LINK FMTMAIN+FRTEXE,,,FRTLIB.LIB,FMTMAI N.DEF

      You must explicitly link the special start-up file FRTEXE.OBJ into any
      executable program that requires dynamically linked FORTRAN run-time
      support.

      An alternate method to the above process would be to build an imports
      library for FMTDLL.DLL and link FMTMAIN.OBJ with the imports library.
      This method avoids creating a definitions file which imports the
      functions.

   2. Compile and link a dynamic-link-library module (FMTDLL.FOR) to produce
      FMTDLL.DLL. Do this by entering the command

           FL /MD /FeFMTDLL.DLL FMTDLL.FOR FRTDLL.OBJ FRTLIB.LIB FMTDLL.DEF

      The following files are now linked together:

      File Name                   Description

            FMTDLL.OBJ            Output from the compilation of
                                  FMTDLL.FOR

            FRTDLL.OBJ            Start-up code for
                                  dynamic-link-library files

            FRTLIB.LIB            Customized FORTRAN run-time
                                  imports library

            DOSCALLS.LIB          OS/2 support library

            FMTDLL.DEF            Dynamic-link-library definitions
                                  file

            FMTDLL.DLL            Output from LINK

      The FL command compiles and links the dynamic-link library. The /Fe
      option allows you to rename the output from the linker to the .DLL
      extension.

      Given the object file, FMTDLL.OBJ, compiled with /MD, you can also
      invoke the linker separately with the following command:

           LINK FMTDLL+FRTDLL,FMTDLL.DLL,,FRTLIB.L IB,FMTDLL.DEF

      You must explicitly link the special start-up file FRTDLL.OBJ with any
      user-written dynamic-link library.

      FMTDLL.DLL performs I/O functions, requiring run-time support (provided
      by the example file FRTLIB.LIB). If the dynamic-link library does not
      require run-time support, omit this file from the command line.

   3. Place the FMTDLL.DLL file (from step 2) and the FRTLIB.DLL file in a
      directory on your LIB PATH so OS/2 can find it. You can now run the
      program FMTMAIN.EXE under OS/2.


 3.7.7  Compiling for Dynamic-Link and Multithread Capabilities

 The program FMHELLO discussed in Section 3.6 can also be compiled to use the
 dynamically linked multithread capabilities available with Microsoft FORTRAN
 5.0. Assuming that the example dynamic-link FORTRAN run-time library is on
 your LIB PATH, compile FMHELLO with the following command:

      FL /MD FMHELLO.FOR FRTEXE.OBJ FRTLIB.LIB

 The /MD option implies multithread support, so the /MT option is not
 required.


 3.7.8  FORTRAN OS/2 Libraries

 Table 3.1 shows the different FORTRAN libraries available under OS/2 and the
 forms of execution they support.

 Table 3.1  FORTRAN OS/2 Libraries
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                Executable        Executable          Dynamic-Link
                Single-Thread     Multiple-Thread     Multiple-Thread
 Component      Library           Library             Run-Time Library

 .OBJ           ---               ---                 FRTDLL.OBJ
                                                      FRTEXE.OBJ
                                                      FRTLIB.OBJ

 .LIB           mLIBFfP.LIB       LLIBFMT.LIB         FDLLOBJS.LIB
                                  DOSCALLS.LIB        FDLLOBJS.DEF
                                                      DOSCALLS.LIB


 3.8  Limits on Multithread and Dynamic-Link Programs

 There are some restrictions on running multithread and dynamic-link
 programs. You must work within these limitations when you run a multithread
 and/or a dynamically linked program. Conventional DOS FORTRAN programs do
 not face these restrictions.


 3.8.1  Large and Huge Memory Models (/MT, /MD)

 You can use both the /MT and /MD options in conjunction with the normal
 memory model switches /AL and /AH. However, the /AM (medium model) option is
 strictly disallowed with the /MT and /MD options since the run-time names
 differ under the medium model. See Chapter 7 the Microsoft FORTRAN Reference
 for more information on these FL command-line switches.


 3.8.2  Math Support (/FPi87)

 The default-floating-point option, /FPi87, is still in effect for /MT and
 /MD compilation. Note, however, that any other form of /FP is allowed except
 /FPa (the alternate math option). The LLIBFMT.LIB and FDLLOBJS.LIB files
 contain the emulator math package. Although the emulator works when linked
 to object files compiled with any math option, it is recommended that the
 in-line forms (/FPi and /FPi87) be used for efficiency. A coprocessor will
 be used if it is available.


 3.8.3  Limited CHARACTER*(*) Support

 The FORTRAN Compiler uses a global symbol, _FCclenv, to tell a function the
 actual length of a character argument or function. If two threads update
 this variable simultaneously, unpredictable behavior may result. Do not use
 CHARACTER*(*) or restrict its use to a single thread in multithread
 programs.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    The GETARG() subroutine returns its buffer argument in the CHARACTER*(*)
    form. Therefore, any calls to GETARG() must be restricted to a single
    thread and use no other CHARACTER*(*) calls.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 3.8.4  The C Attribute and REAL Numbers

 Functions returning REAL values cannot use the C attribute. A global symbol,
 _fac, is used to return REAL values when you specify the C attribute for a
 function. If two threads access this variable simultaneously, the results
 cannot be predicted. Statically linked multithread programs will resolve
 this conflict. Therefore, you may not become aware of the problem. It may or
 may not influence your program. In a dynamically linked program the _fac
 symbol cannot be accessed outside of the FORTRAN run-time data segment, and
 therefore cannot be used. The simplest way to avoid this difficulty is to
 use the default calling convention with any function returning a REAL value.


 3.8.5  Open Files

 You can open up to 40 files simultaneously in programs that use the
 multithread and/or dynamic-link capabilities of FORTRAN. (Since the system
 reserves a few of these files for its own use and others manage devices, the
 actual number of files a process can open is slightly less than 40.)

 Using all available units prior to any terminal I/O may result in an OS/2
 lack of storage space error. You can prevent this by forcing I/O to the
 terminal before reaching the system limit.


 3.9  Recommendations

 The following points offer some general guidelines for creating multithread
 and dynamic-link programs. They are not requirements for creating such
 programs, but recommendations to help you around some common pitfalls.

   þ  Recompile all code in a multithread or dynamic-link program with the
      appropriate compile-time option (/MT or /MD), since it is necessary
      that the compiler assume that the segment addresses stored in the SS
      (which contains the stack address) and DS (which contains the near data
      address) registers are not equal. See the discussion of the /MD and /MT
      options in Chapter 7 of the  Microsoft FORTRAN Reference for more
      information.

   þ  Allow threads to conduct I/O only on those units which they explicitly
      open. Since threads within a process share the resources of that
      process, it is possi-ble for one thread to perform I/O to units opened
      by other threads in the same process. This allows the possibility of
      delaying one request in the event that both threads attempt to access
      the same file simultaneously.

   þ  For multithread and dynamic-link programs, note that if the program
      terminates prematurely due to a MATH-class error, then the system does
      not flush the final buffer of any output files, since the termination
      code may not execute properly.

   þ  Because threads within a single process share resources, take care when
      using the preconnected units (units 0, 5, and 6 all connect to the
      terminal) in multithread programs. If a thread reconnects one of these
      units to a file, then when the process dies or a thread later closes
      the unit, it reassumes its preconnected status to the terminal. Any
      other threads which may have been reading or writing to that unit may
      unexpectedly read and write to the terminal.

   þ  Do not use a STOP statement inside a routine which is executing as a
      thread in a multithread program. The STOP statement terminates the
      process (including flushing and closing all open files), not just the
      thread that encountered the statement. Instead, use the ENDTHREAD
      subroutine to cause a thread to terminate itself.

   þ  In a multithread program, a thread that causes a run-time error in a
      statement without an explicit error-recovery sequence will cause the
      entire process to terminateÄÄwith a run-time error.

   þ  Files opened within a thread should be explicitly closed with the CLOSE
      statement. Closing the files returns the resource to the process and
      makes the files available to other threads. Thread termination does not
      implicitly close files in the way that process termination does.


 3.10  Additional Information

 The following publications contain more information on OS/2 programming and
 philosophy:

 Cortesi, David E. The Programmer's Essential OS/2 Handbook. Redwood City,
 CA: M&T Publishing Co, 1988.

 Jamsa, Kris A. Using OS/2. Berkeley, CA: Osborne McGraw-Hill, 1988.

 Letwin, Gordon. Inside OS/2. Redmond, WA: Microsoft Press, 1987. (An
 explanation of OS/2 from Microsoft's Chief Architect, Systems Software.)

 Microsoft Systems Journal, May 1987. (Issue devoted to OS/2.)

 Petzold, Charles. PC Magazine column "Environments." (A series of articles
 on OS/2 programming topics begun with the October 13, 1987 issue.)

 Young, Michael J. Programmer's Guide to OS/2. San Francisco, CA: SYBEX,
 1988.


 Chapter 4  Mixed-Language Programming
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Microsoft FORTRAN supports calls to routines written in Microsoft C and
 Pascal. In addition, if the main program is in Microsoft QuickBASIC 4.0 or
 later (or Microsoft BASIC Compiler 6.0 or later), then a FORTRAN routine can
 call a BASIC routine.This chapter describes the elements of mixed-language
 programming: how languages differ and how to resolve these differences.

 If you understand the basic principles involved in making a mixed-language
 call, then you may wish to turn directly to the detailed syntax information
 in Sections 4.2-4.4. These sections outline procedures for calling routines
 written in other high-level languages and in assembly language from
 Microsoft FORTRAN.


 4.1  Making Mixed-Language Calls

 Mixed-language programming always involves a call to a function, procedure,
 or subroutine. For example, a FORTRAN main module may need to execute a
 specific task programmed in a different language. Instead of writing a new
 FORTRAN routine, you might decide to call a preexisting function written
 inÄÄsayÄÄC.

 Mixed-language calls with Microsoft languages necessarily involve multiple
 modules. Instead of compiling all of your source modules with the same
 compiler, you use different compilers. In the instance mentioned above, you
 would compile the main-module source file with the FORTRAN compiler; compile
 a second source file (written in C) with the C compiler; and then link the
 two object files together.

 Figure 4.1 illustrates how the syntax of a mixed-language call works, using
 the example mentioned above.

 In the illustration above, the FORTRAN call to C is CALL Prn (), similar to
 a call to a FORTRAN subprogram. However, there are two differences between
 this mixed-language call and a call between two FORTRAN modules:

   þ  The subprogram Prn  is actually implemented in C, using standard C
      syntax.

   þ  FORTRAN must use the INTERFACE statement in order to create
      compatibility with C.

      The INTERFACE statement is an example of a mixed-language interface
      statement (it is FORTRAN's interface statement). Each language provides
      its own form of interface.

 Despite syntactic differences, C functions, Pascal procedures, BASIC
 procedures,  Microsoft Macro Assembler MASM routines, and FORTRAN
 subroutines and functions are all similar. The principal difference is that
 some kinds of routines return values and others do not. You can interchange
 routines that have a return value, and you can interchange routines that
 have no return value. Note that in this chapter, the term "routine" refers
 to any function, procedure, or subroutine that can be called from another
 module.

 Table 4.1 below shows the correspondence between routine calls in different
 languages.

 Table 4.1  Language Equivalents for Routine Calls

      Language            Return Value            No Return Value

      FORTRAN             Function                Subroutine
      C                   Function                (Void) function
      Pascal              Function                Procedure
      BASIC               FUNCTION procedure      Subprogram
      Macro Assembler     Procedure               Procedure

 For example, a BASIC module can make a subprogram call to a FORTRAN
 subroutine. BASIC needs to make a FUNCTION call in order to call a FORTRAN
 function; otherwise, the call can be made, but the return value will be
 lost. This adapts the calling program to the conventions of the called
 routine.

 Note that FORTRAN cannot call BASIC DEF FN functions or GOSUB subroutines,
 since they are not independent from other programs or procedures; that is,
 they are contained within the main module of a BASIC program.


 4.1.1  Naming Convention Requirement

 The term "naming convention" refers to the way a compiler alters the name of
 the routine before placing it into an object file.

 When you issue a mixed-language call, it is important that you adopt a
 consistent naming convention. If the name of the called routine is stored
 differently in each object file, then the linker will not be able to find a
 match. Instead, it will report an error.

 Object files contain not only machine code but also the names of all
 routines and variables that need to be accessed publicly. The linker must be
 able to compare the name of a routine called in one module to the name of a
 routine defined in another module and recognize a match. Names are stored in
 ASCII format. You can see precisely how they are stored if you use the DEBUG
 utility to dump an object file's bytes.

 FORTRAN, Pascal, and BASIC use roughly the same naming conventions. They
 translate each letter to uppercase. BASIC type declaration characters (%, &,
 !, #, $) are dropped.

 However, each language recognizes a different number of characters.
 Microsoft FORTRAN recognizes the first 31 characters of any name (or 6, if
 you use the $TRUNCATE metacommand); C the first 31; Pascal the first 8; and
 BASIC the first 40. If a name is longer than the number of characters the
 language will recognize, additional characters are dropped. For this reason,
 any time a FORTRAN program using the $TRUNCATE metacommand calls a routine
 from another language with a name longer than 6 characters, it must use the
 ALIAS statement. (See Section 4.2.3 for more information on ALIAS.)

 C uses a quite different convention. The C compiler does not translate any
 letters to uppercase, but inserts a leading underscore (_) in front of the
 name of each routine.

 Differences in naming conventions are taken care of automatically when you
 use mixed-language keywords, as long as you follow two rules:

   1. If you use the $TRUNCATE metacommand, keep all routine names called by
      FORTRAN to 6 characters or less in length.

   2. Do not use the /NOIGNORE linker option (which causes the linker to
      distinguish between Prn and prn). With C modules, this means that you
      will have to be careful not to rely upon differences between uppercase
      and lowercase letters.

      The CL driver and Microsoft QuickC use the /NOIGNORE option when
      linking. To solve the problems this creates, either link separately
      with the LINK utility or use all lowercase letters in your C modules.

 Figure 4.2 illustrates a complete mixed-language development example, which
 shows how naming conventions enter into the process.

 Note that the FORTRAN Compiler inserts a leading underscore in front of Prn
 as it places the name into the object file, because the C attribute directs
 the FORTRAN compiler to use the C naming convention. FORTRAN will also
 convert all letters to lowercase when this keyword is used. Note that
 converting letters to lowercase is not part of the C naming convention, but
 it is consistent with the programming style of most C programs.


 4.1.2  Calling Convention Requirement

 The term "calling convention" refers to the way a language implements a
 call. The choice of calling convention affects the actual machine
 instructions that a compiler generates in order to executeÄÄand return
 fromÄÄa function, procedure, or subroutine call.

 The calling convention is a low-level protocol. It is crucial that the
 routine issuing a call and the routine being called recognize the same
 protocol. Otherwise, the processor may receive inconsistent instructions,
 causing the system to crash.

 The use of a calling convention affects programming in two ways:

   1. The calling routine uses a calling convention to determine the order in
      which to pass arguments (parameters) to another routine. Usually this
      convention can be specified in a mixed-language interface.

   2. The called routine uses a calling convention to determine the order in
      which to receive the parameters that were passed to it. In most
      languages, including FORTRAN, the routine's heading specifies this
      convention. BASIC, however, always uses its own convention to receive
      parameters.

 In other words, each call to a routine uses a certain calling convention,
 and each routine heading specifies or assumes some calling convention. The
 two conventions must be compatible. With each language except BASIC, it is
 possible to change either calling convention. However, it is usually
 simplest to adopt the convention of the called routine. For example, a
 FORTRAN function would use its own convention to call another FORTRAN
 function and use the C convention to call C.

 FORTRAN, Pascal, and BASIC use the same standard calling convention. C,
 however, uses a quite different convention. The Microsoft BASIC, FORTRAN,
 and Pascal calling conventions each push parameters onto the stack in the
 order in which they appear in the source code. For example, the FORTRAN
 statement CALL Calc(A,B) pushes the argument A onto the stack before it
 pushes B. These conventions specify that the stack is restored by the called
 routine, before returning control to the caller. The stack is restored by
 removing parameters.

 The C calling convention pushes parameters onto the stack in the reverse
 order in which they appear in the source code. For example, the C function
 call calc(a,b); pushes b onto the stack before it pushes a. In contrast
 with other high-level languages, the C calling convention specifies that a
 calling routine always restores the stack immediately after the called
 routine returns control.

 The FORTRAN, Pascal, and BASIC conventions produce slightly less object
 code. However, the C convention makes calling with a variable number of
 parameters possible. Because the first parameter is always the last one
 pushed, it is always on the top of the stack; therefore it has the same
 address relative to the frame pointer, regardless of how many parameters
 were actually passed. You don't need to be aware of all variations in
 calling conventions in order to do mixed-language programming successfully,
 but you do need to remember that such differences exist.


 4.1.3  Methods for Passing Parameters

 Section 4.1.2 discusses the overall protocol (the calling convention) that
 two routines use to communicate with each other; this section discusses how
 an individual item of data (a parameter) is actually exchanged.

 If your routines do not agree on how a parameter is to be sent, then a
 called routine will receive bad data. It is also possible that the program
 could cause a system error.

 Microsoft compilers support three methods for passing a parameter:

      Method                      Description

      By near reference           Passes a variable's near (offset)
                                  address.

                                  This method gives the called
                                  routine direct access to the
                                  variable itself. Any change the
                                  routine makes to the parameter
                                  will be reflected in the calling
                                  routine.

      By far reference            Passes a variable's far
                                  (segmented) address.

                                  This method is similar to passing
                                  by near reference, except that a
                                  longer address is passed. This
                                  method is slower than passing by
                                  near reference, but it is
                                  necessary when you pass data that
                                  is outside the default data
                                  segment. This is not an issue in
                                  BASIC or Pascal, unless you have
                                  specifically requested far
                                  memory.

      By value                    Passes only the variable's value,
                                  not address.

                                  With this method, the called
                                  routine knows the value of the
                                  parameter, but has no access to
                                  the original variable. Changes to
                                  a value parameter have no effect
                                  on the value of the parameter in
                                  the calling routine, once the
                                  routine terminates.

 The fact that there are different parameter-passing methods has two
 implications for mixed-language programming:

   þ  You need to make sure that the called routine and the calling routine
      use the same method for passing each parameter (argument). In most
      cases, you will need to check the parameter-passing defaults used by
      each language and possibly make adjustments. Each language has keywords
      or language features that allow you to change parameter-passing
      methods.

   þ  You may want to use a particular parameter-passing method rather than
      using the defaults of any language because of characteristics of your
      programming task.

 Table 4.2 summarizes the parameter-passing defaults for each language. Each
 language has methods for overriding these defaults.

 Table 4.2  Parameter-Passing Defaults

               By Near         By Far
 Language      Reference       Reference         By Value

 FORTRAN       All            All             ---
 C             Near arrays     Far arrays        Nonarrays
 Pascal        VAR, CONST      VARS, CONSTS      Other parameters
 BASIC         All BASIC       All               ---


 4.1.4  Compiling and Linking Considerations

 After you write your source files and resolve the issues of naming
 conventions and parameter-passing procedures raised in Sections
 4.1.1-4.1.3, you are ready to compile individual modules and then link
 them together. To do this successfully, the different modules need to use
 compatible memory modelsÄÄthat is, they must agree on where information is
 stored. These modules also need to know which libraries to reference in
 order to avoid unresolved external calls or duplicate routine definitions.


 4.1.4.1  Using Compatible Memory Models

 With Microsoft FORTRAN, Pascal, and BASIC, no special options are required
 to compile source files that are part of a mixed-language program.

 With Microsoft C, however, you need to be aware that not all memory models
 will be compatible with other languages. FORTRAN, Pascal, and BASIC use only
 far (segmented) code addresses. Always compile C modules in medium,  large,
 or huge models because these models also use far code addresses. Compiling
 in small or compact models causes the mixed-language program to crash as
 soon as a call is made to or from C. This problem can be averted if you
 apply the FAR keyword to a C function definition, in order to specify that
 the function uses a far call and return.

 Differences in the size of data addresses can be resolved through compile
 options or in the source code. Your choice of memory model affects the
 default data pointer size in FORTRAN and C, although this default can be
 overridden with the NEAR and FAR attributes in FORTRAN, or the near and far
 keywords in C. The FORTRAN and C memory models also affect whether data
 objects are located in the default data segment; if a data object is not
 located in the default data segment, it cannot be directly passed by near
 reference. For more information on memory models, see Section 2.3.2 in
 Chapter 2, "Selecting a Memory Model."


 4.1.4.2  Specifying Libraries

 In many cases, linking modules compiled with different languages can be done
 easily. When you use the linker with the FORTRAN libraries you may need to
 specify the /NOD (no-default libraries) option and specify all the libraries
 you need directly on the LINK command line. You can also specify these
 libraries with an automatic-response file (or batch file).

 If your program uses both FORTRAN and C, then specify the library for the
 most recent of the two language products first. If you use C 4.0 with
 FORTRAN 4.0 or later, specify the FORTRAN library first. If you use C 5.1 or
 later, specify the C library first. In addition, make sure you choose a
 C-compatible library when you install FORTRAN.

 If you are listing BASIC libraries on the LINK command line, the BASIC
 libraries must precede all others, as in the following example:

      LINK /NOD mod1 mod2,,,GRAFX+LLIBCE+LLIBFORE

 Here two object modules, mod1  and mod2, are linked with the C 5.1 and
 FORTRAN 5.0 large-model libraries. In addition, the example links in an
 extra library, GRAFX.


 4.2  Calling Other High-Level Languages

 In addition to the compiling and linking considerations mentioned above,
 your program will also need to follow some internal conventions for calling
 a routine written in another language. These considerations include creating
 an interface to the called routine and using keywords appropriate to the
 particular language called.

 For information on transferring data between languages, consult Chapter 5.


 4.2.1  The INTERFACE Statement

 To call another language routine from within a FORTRAN function, first write
 an interface to the routine with the INTERFACE statement. This statement
 allows you to use special keywords (attributes) that affect the way FORTRAN
 carries out calls. These keywords allow you to adjust naming conventions,
 calling conventions, and parameter-passing methods so that you can make
 routines from other languages compatible with FORTRAN.

 Here are the guidelines to interfaces for routines from other languages:

   1. Write an INTERFACE statement for each routine you call.

      Write the interface to a FUNCTION if the routine returns a value, or to
      a SUBROUTINE if the routine does not return a value. The INTERFACE
      statement needs to precede any calls to the routine.

   2. Apply the C attribute to the routine if it is written in C (unless the
      C module is compiled with the /Gc routine or is modified with the
      fortran or pascal keyword).

      The C attribute causes the routine to be called with the C naming and
      calling conventions. It also changes the default parameter-passing
      method for all parameters to pass by value. To apply the C attribute,
      type [C] immediately after the name of the routine.

   3. If the routine is called from Pascal, you may want to apply the PASCAL
      attribute to the routine. This keyword does not change calling or
      naming conventions, but causes all parameters to pass by value.

      To apply the PASCAL attribute, type [PASCAL] immediately after the
      name of the routine.

   4. If $TRUNCATE is on and the name of the routine is longer than 6
      characters, use the ALIAS attribute. The use of ALIAS is explained in
      Section 4.2.3 below.

   5. Adjust the way parameters are passed by applying the NEAR, FAR, VALUE,
      and REFERENCE attributes to parameter declarations.

      The REFERENCE attribute can be useful because the C and PASCAL keywords
      automatically change the default parameter-passing method to passing by
      value. For any given parameter, REFERENCE reverts the method to passing
      by reference. (By default, FORTRAN passes by far reference unless you
      are using the medium memory model available with FORTRAN, Versions 4.0
      or later.)

      To apply an attribute to a parameter declaration, put the attribute in
      brackets, along with any other attributes that modify the same object,
      and place the attribute and brackets immediately after the parameter.
      (See the examples below.)

   6. Once the proper interface is set up, call the routine just as you would
      call any FORTRAN function or subroutine.

 Examples

 In the examples below, the variables N, I, and J are not significant,
 except for their types and attributes.

 This first example declares the subroutine TEST with the PASCAL attribute.

            INTERFACE TO SUBROUTINE TEST [PASCAL] (N)
            INTEGER*2 N [NEAR, REFERENCE]
            END

 The subroutine takes one argument, N, which contains both of the NEAR and
 REFERENCE attributes. Therefore N is passed by near reference.

 The second example declares a C function, CFUN, which returns a value of
 type REAL*8.

            INTERFACE TO REAL*8 FUNCTION CFUN [C] (I,J)
            REAL*8 I [REFERENCE]
            REAL*8 J
            END

 The argument I is passed by far reference because of the REFERENCE
 attribute; the argument J is passed by value because the C attribute
 changes the default.


 4.2.2  An Alternative Interface to C

 Step 2 of the above guidelines suggests modifying the behavior of FORTRAN
 with the C attribute when calling a C routine. Alternately, since the pascal
 and fortran keywords are functionally equivalent, you can modify the
 behavior of C by applying either keyword to the function definition heading.
 Or, you can compile the C module with the /Gc option, which specifies that
 all C functions, calls, and public symbols use the FORTRAN/Pascal/BASIC
 conventions.

 For example, the following C function uses the FORTRAN/Pascal/BASIC
 conventions to receive an integer parameter:

            int fortran fun1(n)
            int n;


 4.2.3  The ALIAS Attribute

 The ALIAS attribute allows you to refer to a routine by one name in your
 code while the object code uses another name. The attribute is particularly
 helpful when the called routine has a cryptic name.

 The ALIAS attribute uses this syntax ALIAS: ' aliasname '. Note that
 aliasname is the name FORTRAN will actually place in the object code
 whenever the declared routine is called. When you use this feature,
 aliasname is precisely what FORTRAN will place in the object code;
 therefore, if you are linking to a C routine, be sure to type a leading
 underscore (_) before the name.

 The ALIAS feature is necessary when using the $TRUNCATE metacommand and the
 name of a routine exceeds 6 characters. Without ALIAS, FORTRAN would place
 only the first 6 characters into the object file, while the other language
 would place 8 or more characters into the other object file (up to 40 in the
 case of BASIC). This difference would prevent the linker from finding a
 match. Without $TRUNCATE, Microsoft FORTRAN will accept up to 31 characters.

 In the example below, ALIAS is used because PRINTNUM  is longer than 6
 characters. Note that the leading underscore should be used only when
 linking to a routine that uses the C naming convention.

      $TRUNCATE
            INTERFACE TO PRINTNUM [C, ALIAS:'_printnum'] (N)
            INTEGER*2 N
            END


 4.2.4  FORTRAN Calls to C and Pascal

 Writing FORTRAN interfaces to C is fairly straightforward. Calling Pascal
 from FORTRAN is also simple, because the PASCAL attribute causes FORTRAN to
 use the Pascal default of passing data by value. This section applies the
 steps outlined in Section 4.2.1 to two examples of FORTRAN-C programs. A
 brief analysis follows each example.


 4.2.4.1  Calling C from FORTRANÄÄNo Return Value

 The example below demonstrates a FORTRAN main module calling a C function,
 maxparam. This function returns no value but adjusts the lower of two
 parameters to equal the higher argument.

      C     FORTRAN SOURCE FILE - CALLS C FUNCTION, NO RETURN
      C     VALUE
      C
            INTERFACE TO SUBROUTINE MAXPARAM [C] (I,J)
            INTEGER*2 I [NEAR, REFERENCE]
            INTEGER*2 J [NEAR, REFERENCE]
            END

      C
      C     C ATTRIBUTE DIRECTS FORTRAN TO USE C CONVENTIONS
      C     EACH PARAMETER PASSED BY NEAR REFERENCE
      C
            INTEGER*2 I,J
            I = 5
            J = 7
            WRITE (*,*) 'I = ',I,' J = ', J
            CALL MAXPARAM(I,J)
            WRITE (*,*) 'I = ',I,' J = ', J
            END

      /* C source file */
      /* Compile in MEDIUM or LARGE memory model */
      /* Maxparam declared VOID because no return value */

      void maxparam(p1, p2)
      int near *p1;  /* Integer params received by near ref. */
      int near *p2;  /* NEAR keyword not needed in MEDIUM model */
      {
         if (*p1 > *p2)
                 *p2 = *p1;
         else
                 *p1 = *p2;
       }

 Both modules place _maxparam  (consistent with the C naming convention) into
 an object file. The C attribute in the INTERFACE statement causes the
 MAXPARAM  function to be called with the C calling convention, which pushes
 param-eters in reverse order and specifies other lower-level differences.

 Since the function maxparam  may alter the value of either parameter, both
 must be passed by reference. Near reference is implemented in FORTRAN with
 the NEAR and REFERENCE attributes and in C by using near pointers. The
 REFERENCE attribute is necessary in FORTRAN, since the C keyword changes the
 default-passing method to pass by value.

 Far reference could have been specified by leaving off the NEAR keyword from
 the FORTRAN parameter declarations. In that case, the C module would need to
 use far pointers.


 4.2.4.2  Calling C from FORTRANÄÄFunction Call

 The example below demonstrates a FORTRAN main module calling a C function,
 fact. This function returns the factorial of an integer value.

      C     FORTRAN SOURCE FILE - CALLS C FUNCTION
      C
            INTERFACE TO INTEGER*2 FUNCTION FACT [C] (N)
            INTEGER*2 N
            END

      C
      C     C ATTRIBUTE DIRECTS FORTRAN TO USE C CONVENTIONS
      C     PARAMETER PASSED BY VALUE, WHICH IS DEFAULT WHEN
      C     C ATTRIBUTE IS IN USE
      C
            INTEGER*2 FACT
            INTEGER*2 I,J
            I = 3
            J = 4
            WRITE (*,*) 'The factorial of I   is ',FACT(I)
            WRITE (*,*) 'The factorial of J   is ',FACT(J)
            WRITE (*,*) 'The factorial of I+J is ',FACT(I+J)
            END

      /* C source file */
      /* Compile in MEDIUM or LARGE model */
      /* Factorial function, returning integer */

      int fact(n)
      int n; /* Integer received by value, the C default */
      {
           int    result = 1;

           while  (n)
                  result *= n--; /* Parameter n modified here */
           return (result);
      }

 The C attribute in the INTERFACE statement causes FACT to be called with
 the C naming convention (as _fact). The C attribute in the INTERFACE
 statement causes FACT to be called with the C calling convention, which
 pushes parameters in reverse order and specifies other lower-level
 differences.

 The C function above should receive the parameter by value. Otherwise, the
 function will corrupt the parameter's value in the calling module. Passing
 by value is the default method for C; it is also the default method for
 FORTRAN whenever the C attribute is in use.


 4.2.4.3  Calling Pascal from FORTRANÄÄProcedure Call

 The example below demonstrates a FORTRAN main module calling a Pascal
 procedure, Maxparam. This procedure adjusts the lower of two parameters to
 equal the higher argument.

      C     FORTRAN SOURCE FILE - CALLS PASCAL PROCEDURE
      C
            INTERFACE TO SUBROUTINE MAXPARAMETER
           + [ALIAS:'MAXPARAM'] (I,J)
            INTEGER*2 I [NEAR]
            INTEGER*2 J [NEAR]
            END

      C
      C     ALIAS NECESSARY BECAUSE 'MAXPARAMETER' LONGER
      C     THAN 8 CHARS.
      C     EACH PARAMETER PASSED BY NEAR REFERENCE
      C
            INTEGER*2 I,J
            I = 5
            J = 7
            WRITE (*,*) 'I = ',I,' J = ', J
            CALL MAXPARAMETER(I,J)
            WRITE (*,*) 'I = ',I,' J = ', J
            END

      { Pascal source code - Maxparam procedure. }

      module Psub;
       procedure Maxparam(var a:integer; var b:integer);

      { Two integer parameters are received by near reference. }
      { Near reference is specified with the VAR keyword. }

       begin
          if a > b then
             b := a
          else
             a := b
       end;
      end.

 By default, FORTRAN places all 12 characters of MAXPARAMETER into the
 object file, whereas Pascal places only 8. The ALIAS attribute resolves this
 conflict: both modules place MAXPARAM into an object file. FORTRAN and
 Pascal use the same convention for calling.

 Since the procedure Maxparam may alter the value of either parameter, both
 must be passed by reference. Near reference was implemented in FORTRAN with
 the NEAR attribute, and in Pascal with the VAR keyword. The PASCAL attribute
 is not used here because no parameter is being passed by value.

 Far reference could have been specified by leaving off NEAR from the FORTRAN
 parameter declarations, using VARS instead of VAR in the Pascal module.


 4.2.4.4  Calling Pascal from FORTRANÄÄFunction Call

 The example below demonstrates a FORTRAN main module calling a Pascal
 function, Fact. This function returns the factorial of an integer value.

      C     FORTRAN SOURCE FILE - CALLS PASCAL FUNCTION
      C
            INTERFACE TO INTEGER*2 FUNCTION FACT [PASCAL] (N)
            INTEGER*2 N
            END

      C
      C     PARAMETER PASSED BY VALUE, WHICH IS DEFAULT WHEN
      C     PASCAL ATTRIBUTE IS IN USE
      C
            INTEGER*2 FACT
            INTEGER*2 I,J
            I = 3
            J = 4
            WRITE (*,*) 'The factorial of I   is ',FACT(I)
            WRITE (*,*) 'The factorial of J   is ',FACT(J)
            WRITE (*,*) 'The factorial of I+J is ',FACT(I+J)
            END

      { Pascal source code - factorial function. }

      module Pfun;
       function Fact (n : integer) : integer;

      { Integer parameters received by value, the Pascal default. }

       begin
          Fact := 1;
          while n > 0 do
              begin
                  Fact := Fact * n;
                  n := n - 1;           { Parameter n modified here.}
              end;
       end;
      end.

 FORTRAN and Pascal use a similar naming convention. The ALIAS statement is
 unnecessary because the function name does not exceed the Pascal limit of 8
 characters. FORTRAN and Pascal use the same calling convention.

 The Pascal function above should receive the parameter by value. Otherwise,
 the function will corrupt the parameter's value in the calling module.
 Passing by value is the default method for Pascal; it is also the default
 method for FORTRAN whenever the PASCAL attribute is in use.


 4.2.5  C Functions Available with FORTRAN Run-Time Libraries

 The FORTRAN run-time libraries MLIBFORx.LIB and LLIBFORx.LIB include the
 system routine and a subset of the spawnlp routine (as well as other
 rou-tines) originally from the C library. FORTRAN programs access these
 routines in the FORTRAN run-time libraries; however, these routines may
 present compatibility problems with future versions of Microsoft C. Sections
 4.2.5.1-4.2.5.2 show how to access these C routines and discuss future
 compatibility issues. (The demonstration program DEMOEXEC.FOR included with
 the Microsoft FORTRAN Compiler also gives examples of how to call these C
 routines.)


 4.2.5.1  Accessing system and spawnlp Functions

 The system function has the following declaration in C:

           int system (string)
           char *string;

 The system function passes a specified C string (ending with a null
 character) to the DOS command interpreter (COMMAND.COM), which interprets
 and exe-cutes the string as a DOS command. This allows DOS commands (such as
 DIR or DEL), batch files, and programs to be executed.

 The following program fragment shows how to access the system function from
 a FORTRAN program to display all files in the current directory with the
 file extension .FOR.

            INTEGER*2 SYSTEM
      C     RETURN TYPE MUST BE DECLARED;
      C     NOTICE THE C LITERAL STRING  '...'C
            I = SYSTEM ('DIR *.FOR'C)

 The INTERFACE statement required to access system is given below. The C
 attribute is specified after the function name. The argument string includes
 the REFERENCE attribute to indicate that the argument is passed by
 reference.

            INTERFACE TO INTEGER*2 FUNCTION SYSTEM [C]
           + (STRING[REFERENCE])
            CHARACTER*1 STRING
            END

 The spawnlp function has the following declaration in C:

      int spawnlp (mode,path,arg0,arg1,...,argn)
         int mode;    /* spawn mode */
         char *path;  /* pathname of program to execute */
         char *arg0;  /* should be the same as path */
         char *arg1,...,*argn;    /* command line arguments */
         /* argn must be NULL */

 This function creates and executes a new child process. There must be enough
 memory to load and execute the child process. The mode argument determines
 which form of spawnlp is executed. For the version of the spawnlp function
 in the FORTRAN run-time libraries, this value must be 0, which tells spawnlp
 to suspend the parent program and execute the child program. When the child
 program terminates, the parent program resumes execution. The return value
 from spawnlp is -1 if an error occurs. If the child process runs
 successfully, the return value is the return code from the child process.

 The path argument specifies the file to be executed as the child process.
 The path can specify a full path name from the root directory, a partial
 path name from the current working directory, or a file name. If the path
 argument does not have a file-name extension or end with a period (.), the
 spawnlp call first appends the extension .COM and searches for the file. If
 spawnlp cannot find the file, it appends the extension .EXE and tries to
 find the file again. The spawnlp routine also searches for the file in any
 of the directories specified in the PATH environment variable using the same
 procedure.

 The INTERFACE statement required to access spawnlp from a FORTRAN program is
 given below. The C attribute must appear after the function name. The
 VARYING attribute indicates that a variable number of arguments may be
 passed to the function.

            INTERFACE TO INTEGER*2 FUNCTION SPAWNLP
           + [C,VARYING] (MODE)
            INTEGER*2 MODE
            END

 If you use the $STRICT or $TRUNCATE metacommands, you must use a name 6
 characters or less in the INTERFACE statement and use the ALIAS attribute to
 associate the FORTRAN name SPAWN with the C identifier spawnlp.

 The following program fragment illustrates how to call the spawnlp function
 from a FORTRAN program:

      C     (THE RETURN TYPE MUST BE DECLARED)
            INTEGER*2 SPAWNLP
            .
            .
            .
      C      EXECUTE AS A CHILD PROCESS
            I = SPAWNLP(0, LOC('EXEMOD'C), LOC('EXEMOD'C), c
           + LOC('DEMOEXEC.EXE'C), INT4(0))

 Notice in this example that the method used to pass strings to spawnlp is
 different from the method used to pass strings to system. This is because
 the string arguments to spawnlp are undeclared in the INTERFACE statement
 and assumed to be passed by value. The spawnlp function expects the
 addresses of the strings rather than the actual characters, so the FORTRAN
 program uses the LOC intrinsic function to pass the address. (Remember that
 functions with the C attribute pass arguments by value.) The last argument
 to the spawnlp routine must be a C null pointer (an integer 0), so the
 FORTRAN program must use either the INT2(0)  or the INT4(0)  intrinsic
 function (depending on the memory model) to pass this pointer by value as
 the last argument.


 4.2.5.2  FORTRAN Libraries and Future Versions of Microsoft C

 If you plan to mix Microsoft FORTRAN modules with modules compiled using
 Microsoft C, you should use the SETUP utility to prepare new copies of your
 FORTRAN run-time libraries. When you create these copies, tell SETUP that
 you want the copies to interface with C. In these copies, SETUP removes the
 C routines. Later, when you link FORTRAN modules with C modules, you should
 link with the appropriate C run-time library for the memory model that you
 are using. This link will take care of unresolved references from
 MLIBFORx.LIB and LLIBFORx.LIB. You can still use your original copies of
 MLIBFORx.LIB and LLIBFORx.LIB for programs that do not include C modules.


 4.2.6  FORTRAN Calls to BASIC

 Calls from FORTRAN programs to BASIC (MS QuickBASIC 4.0 or later or
 Microsoft BASIC Compiler 6.0 or later) programs are not directly supported.
 No BASIC routine can be executed unless the main program is in BASIC,
 because a BASIC routine requires the environment to be initialized in a way
 that is unique to BASIC. No other language will perform this special
 initialization.

 It is possible for a program to start up in BASIC, call a FORTRAN function
 that does most of the work of the program, and then call BASIC subprograms
 and function procedures as needed. Figure 4.3 illustrates how this can be
 done.

 Here are the recommended rules for calling BASIC from FORTRAN:

   1. Start up in a BASIC main module. You will need to use QuickBASIC,
      Version 4.0 or later, and may want to use the DECLARE statement to
      write an interface to the principal FORTRAN routine. (See Section
      5.1.1, "BASIC Arguments," for more information.)

   2. Write an interface in FORTRAN for each BASIC routine you plan to call.
      Since BASIC and FORTRAN use the same calling convention, no special
      keyword is required to make FORTRAN compatible with BASIC.

   3. Make sure that all data are passed by near reference. BASIC can pass
      data in a variety of ways, but is unable to receive data in any form
      other than near reference.

      With near pointers, the program assumes that the data are in the
      default data segment. If you want to pass data that are not in the
      default data segment (this is only a consideration with large-model
      programs), then first copy the data to a variable that is in the
      default data segment.

 The example below demonstrates a BASIC program which calls a FORTRAN
 subroutine. The FORTRAN subroutine then calls a BASIC function that returns
 twice the number passed it, and a BASIC subprogram that prints two numbers.

      ' BASIC source
      '
      DEFINT A-Z
      DECLARE SUB Fprog ()
      CALL Fprog
      END
      '
      FUNCTION Dbl(N) STATIC
          Dbl = N*2
      END FUNCTION
      '
      SUB Printnum(A,B) STATIC
          PRINT "The first number is ";A
          PRINT "The second number is ";B
      END SUB


      C     FORTRAN subroutine
      C     Calls a BASIC function that receives one integer,
      C     and a BASIC subprogram that takes two integers.
      C
            INTERFACE TO INTEGER*2 FUNCTION DBL (N)
            INTEGER*2 N [NEAR]
            END
      C
            INTERFACE TO SUBROUTINE PRINTNUM(N1, N2)
            INTEGER*2 N1 [NEAR]
            INTEGER*2 N2 [NEAR]
            END
      C
      C     Parameters must be declared NEAR in the parameter
      C     declarations; BASIC receives ONLY 2-byte pointers

      C
            SUBROUTINE FPROG
            INTEGER*2 DBL
            INTEGER*2 A, B
            A = 5
            B = 6
            WRITE (*, *) 'Doubling 5 equals ', DBL(A)
            CALL PRINTNUM(A, B)
            END

 In the example above, note that the NEAR attribute is used in the FORTRAN
 routines, so that near addresses will be passed to BASIC instead of far
 addresses.


 4.3  Using Assembly-Language Routines

 You may want to consider writing routines in assembly language when you need
 extreme speed (or when another language would run too slowly). With the
 Microsoft Macro Assembler (MASM) you can write assembly modules that can be
 linked to modules developed with Microsoft FORTRAN. This section outlines
 the recommended programming guidelines for writing assembly routines
 compatible with Microsoft FORTRAN and gives specific examples.

 Writing assembly routines for Microsoft FORTRAN is easiest when you use the
 simplified segment directives provided with the Microsoft Macro Assembler,
 Version 5.0. In general, this chapter assumes that you have MASM 5.0.

 A FORTRAN program can call an external assembly procedure with the use of
 the INTERFACE statement. However, the INTERFACE statement is not strictly
 necessary unless you intend to change one of the FORTRAN defaults. However,
 you may find the following guidelines helpful:

   1. Declare procedures called from FORTRAN as FAR.

   2. Observe the FORTRAN calling convention.

      a.   Upon exit, the procedure must reset SP to the value it had before
           the arguments were placed on the stack. This is accomplished with
           the instruction RET size, where size is the total size of all the
           parameters.

      b.   Arguments are placed on the stack in the same order in which they
           appear in the FORTRAN source code. The first parameter will be
           highest in memory (because it is also the first parameter to be
           placed on the stack, and the stack grows downward).

      c.   By default, FORTRAN parameters are passed by reference as far
           addresses if the FORTRAN module is compiled in the large or huge
           memory model, and as near addresses if the FORTRAN module is
           compiled in the medium model. Versions of FORTRAN prior to 4.0 are
           always large model.

   3. Observe the FORTRAN naming convention.

      FORTRAN only recognizes the first 6 characters of any name if the
      metacommand $TRUNCATE is specified, while the assembler recognizes the
      first 31. By default, FORTRAN recognizes 31 characters.

 In the following example, FORTRAN calls an assembly procedure that
 calculates "A x 2^(B)," where A and B are the first and second parameters,
 respectively. This is done by shifting the bits in A to the left, B times.

 The following example shows how FORTRAN would call the assembly procedure:

            INTERFACE TO INTEGER*2 POWER2(A,B)
            INTEGER*2 A,B
            END
      C
            INTEGER*2 A,B
            A = 3
            B = 5
            WRITE (*,*) '3 times 2 to the power of 5 is ',
           + POWER2(A,B)
            END

 To understand how to write the assembly procedure, consider how the
 parameters are placed on the stack, as illustrated in Figure 4.4.

 Figure 4.4 assumes large-model FORTRAN. If you compile the FORTRAN module in
 medium model, then each argument will be passed as a two-byte, not four-byte
 address. The return address is four bytes long because procedures called
 from FORTRAN must always be FAR.

 Arg 1  (parameter 1) is higher in memory than Arg 2  because FORTRAN pushes
 arguments (parameters) in the same order that they appear.

 The assembly procedure can be written as follows:

      .MODEL LARGE
      .CODE
                 PUBLIC  Power2
      Power2     PROC
                 push    bp           ; Entry sequence - save old BP
                 mov     bp,sp        ; Set stack frame pointer

                 les     bx,[bp+10]   ; Load Arg1 into
                 mov     ax,es:[bx]   ;   AX
                 les     bx,[bp+6]    ; Load Arg2 into
                 mov     cx,es:[bx]   ;   CX
                 shl     ax,cl        ; AX = AX * (2 to power of CX)
                                      ; Leave return value in AX

                 pop     bp           ; Exit sequence - restore old BP
                 ret     8            ; Return and restore 8 bytes
       Power2    ENDP
                 END

 In the example above, each argument must be loaded using the four-byte
 address that was pushed onto the stack. The procedure loads four-byte
 addresses with the les instruction, which loads the destination operand (in
 this case, BX) with the source operand, and also loads ES with the object
 two bytes higher in memory. Thus, the instruction

                les     bx,[bp+10]

 loads BX with the value at BP+10 (an offset address), and ES with the
 value at BP+12 (a segment address), which is necessary to set up the next
 instruction.

 Upon exit, the stack is restored with the instruction ret 8, since the total
 size of parameters pushed onto the stack is eight bytes.

 4.4  Calling FORTRAN from Other Languages

 Other compiled Microsoft languagesÄÄC, Pascal and BASICÄÄsupport calls to
 compiled FORTRAN subroutines and functions. If there is a set of FORTRAN
 routines you use regularly, but you also program in one or more of these
 languages, you can save a duplication of effort by calling these FORTRAN
 routines rather than translating them into other languages.

 Note that, unlike Pascal and C, FORTRAN does not have public variables. Only
 functions, subroutines, and common blocks are public; individual FORTRAN
 variables cannot be declared public. Therefore, if you want a procedure
 written in another language to directly access a FORTRAN variable, that
 variable must appear in a FORTRAN common block. For example, you can declare
 a common block in a FORTRAN routine, then access it from a C program by
 declaring an external structure with the same form.

 Each language has its own conventions and procedures for accessing Microsoft
 FORTRAN. They are explained in the section of that language's manual which
 covers mixed-language programming.


 Chapter 5  Transferring Data between Languages
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Chapter 4 introduced the general concepts of passing parameters by reference
 and by value. In some situations you may want to pass parameters by a
 particular method or to pass different types of data (logicals, strings, or
 arrays as opposed to strictly numerical data). This chapter provides you
 with the information you'll need.

 The first section of this chapter discusses ways to change each language's
 standard manner of passing data. The second section covers numeric, logical,
 and string data; the third addresses special situations involving structured
 data (such as arrays) and externally accessed data.


 5.1  Passing by Reference or Value

 This section describes how to override the default method each language uses
 to pass data. The section contains four parts, each of which summarizes
 parameter-passing methods in a particular language and discusses how to pass
 arguments by value, by near reference, and by far reference. To successfully
 pass data between languages, you must consider how each parameter is passed
 by the calling routine and how each is received by the called routine.


 5.1.1  BASIC Arguments

 The default for BASIC is to pass all arguments by near reference. Every
 BASIC subprogram or function always receives parameters by near reference.

 A BASIC argument passes by value when the called routine is first declared
 with a DECLARE statement  and the BYVAL keyword is applied to the argument.
 Note that BYVAL applies to one argument only; if your routine uses three
 arguments and you want all of them to pass by value, you must put the BYVAL
 option in front of each argument.

 BASIC will pass each argument in a call by far reference when CALLS is used
 to invoke a routine. Using SEG to modify a parameter in a preceding DECLARE
 statement will also cause BASIC to pass that parameter by far reference.


 5.1.2  C Arguments

 The default for C is to pass all arrays by reference (near or far, depending
 on the memory model) and all other data types by value. C uses far data
 pointers for compact, large, and huge models, and near data pointers for
 small and medium models. FORTRAN supports medium, large, and huge memory
 models.

 The C default is to pass all nonarrays (which includes all data types other
 than those explicitly declared as arrays) by value.

 Arrays can be passed by value by declaring them as the only member of a
 structure.

 In C, passing a pointer to an object is equivalent to passing the object
 itself by reference. The default for arrays is to pass by reference. In C,
 near reference is the default for passing pointers in small and medium
 models. Far reference is the default in the compact, large, and huge models.


 5.1.3  FORTRAN Arguments

 The FORTRAN default is to pass and receive all arguments by reference. The
 size of the address passed depends on the memory model.

 A parameter passes by value when declared with the VALUE attribute. This
 declaration can occur either in an INTERFACE statement (which determines how
 to pass a parameter) or in a function or subroutine declaration (which
 determines how to receive a parameter).

 A function or subroutine declared with the PASCAL or C attribute will pass
 by value all parameters declared in its parameter list except for those
 parameters declared with the REFERENCE attribute. This change in default
 passing method applies to function and subroutine definitions, as well as to
 an INTERFACE statement.

 Passing by reference is the default for FORTRAN. However, if either the C or
 PASCAL attribute is applied to a function or subroutine declaration, then
 you need to apply the REFERENCE attribute to any parameter of the routine
 that you want passed by reference.

 Near reference is the default for medium-model FORTRAN programs; far
 reference is the default for large- and huge-model programs.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Versions of FORTRAN prior to 4.0 always compile in large memory model.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 You can apply the NEAR attribute to reference parameters in order to specify
 near reference. You can apply the FAR attribute to reference parameters in
 order to specify far reference. These keywords override the default, but
 have no effect when they specify the same method as the default.

 You can apply more than one attribute to a given parameter. In that case,
 enclose both attributes in brackets, separated by a comma:

            REAL*4 X [NEAR, REFERENCE]


 5.1.4  Pascal Arguments

 The Pascal default is to pass all arguments by value. Parameters are passed
 by near reference when declared as VAR or CONST. Parameters also pass by
 near reference when the ADR of a variable, or a pointer to a variable, is
 passed by value. In other words, the address of the variable is first
 determined, then passed by value. This is essentially the same method used
 in C.

 Parameters pass by far reference when declared as VARS or CONSTS. Parameters
 are also passed by far reference when the ADRS of a variable is passed by
 value.


 5.2  Passing Numeric, Logical, and String Data

 This section explains procedures for passing and receiving different types
 of data. The discussion focuses on the differences in string formats and on
 the methods of passing strings between FORTRAN and other languages.


 5.2.1  Integer and Real Numbers

 Integers and reals are usually the simplest kinds of data to pass between
 languages. However, the type of numerical data is named differently in each
 language; furthermore, not all data types are available in every language,
 and another type may have to be substituted in some cases.

 Table 5.1 shows equivalent data types in each language.

 Table 5.1  Equivalent Numeric Data Types
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 BASIC             C                     FORTRAN               Pascal

 x%                short                 INTEGER*2             INTEGER2
 INTEGER           int                   ---                   INTEGER (defaul
 ---               unsigned short       ---                   WORD
 ---               unsigned              ---                   ---
 x&                long                  INTEGER*4             INTEGER4
 LONG              ---                   INTEGER (default)     ---
 ---               unsigned long        ---                   ---
 x!                float                REAL*4                REAL4
 x  (default)      ---                   REAL                  REAL (default)
 SINGLE            ---                   ---                   ---
 x#                double                REAL*8                REAL8
 DOUBLE            ---                   DOUBLE PRECISION      ---
 ---               unsigned char        CHARACTER*1          BOOLEAN


 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    C sometimes performs automatic data conversions that the other languages
    do not perform. You can prevent C from performing such conversions by
    declaring a variable as the only member of a structure and then passing
    this structure. For example, you can pass a variable x of type float by
    first declaring it as a structure. If you pass a variable of type char or
    float by value and do not take this precaution, then the C conversion may
    cause the program to fail.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 5.2.2  FORTRAN COMPLEX Types

 The FORTRAN types COMPLEX*8 and COMPLEX*16 are not directly implemented in
 any other language. However, you can write precisely equivalent structures
 in C, records in Pascal, or user-defined types in BASIC.

 The type COMPLEX*8 has two fields: The first is a four-byte floating-point
 number that contains the real component, and the second is a four-byte
 floating- point number that contains the imaginary component.

 The type COMPLEX*16 is similar to COMPLEX*8, with the only difference being
 that each field contains an eight-byte floating-point number. The type
 COMPLEX is equivalent to the type COMPLEX*8.

      ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
      ³    Float real component      ³    Float imaginary component  ³
      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
                   4 bytes                        4 bytes

                        FORTRAN COMPLEX Data Format

 5.2.3  FORTRAN LOGICAL Type

 The FORTRAN LOGICAL type is not equivalent to either the Pascal BOOLEAN or C
 char type. Instead, a FORTRAN LOGICAL*2 is stored as a one-byte indicator
 value (1=true, 0=false), followed by an unused byte. A FORTRAN  LOGICAL*4 is
 stored as a one-byte indicator value followed by three unused bytes. The
 type LOGICAL is equivalent to LOGICAL*4, unless $STORAGE:2 is in effect.

 To pass or receive a FORTRAN LOGICAL type, declare a C structure, Pascal
 record, or BASIC user-defined type, with the appropriate fields.


 5.2.4  Strings

 Strings are stored in a variety of formats. Therefore, some transformation
 is frequently required to pass strings between languages.

 This section presents the string formats used in each language and then
 describes methods for passing strings from FORTRAN to these languages.


 5.2.4.1  BASIC Strings

 Strings are stored in BASIC as four-byte string descriptors, as illustrated
 below:

           ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
           ³        Length        ³   Address (offset)    ³
           ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
           ÀÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÙ
                    2 bytes                2 bytes

                    BASIC String Descriptor Format

 The first field of the string descriptor contains a two-byte integer
 indicating the length of the actual string text. The second field contains
 the address of this text. This address is an offset into the default data
 area. BASIC's string management routines reassign this address whenever the
 length of the string changes, yet these management routines are only
 available to BASIC. Therefore, FORTRAN should not alter the length of a
 BASIC string.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    When you pass a string from BASIC to FORTRAN, the called routine should
    under no circumstances alter the length of the string. Other languages
    lack BASIC's string space management routines. Therefore, altering the
    length of a BASIC string is liable to corrupt parts of the BASIC string
    space. Changes that do not affect length, however, are relatively safe.
    The routine that receives the string must not call any BASIC routine. If
    it does, BASIC's string space management routines may change the location
    of the string data without warning.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 FORTRAN must receive by near reference. The NEAR attribute makes this
 adjustment, since the FORTRAN default is to receive by far reference.


 5.2.4.2  C Strings

 C stores strings as simple arrays of bytes and uses a null character
 (numerical 0, ASCII NUL) as a delimiter. For example, consider the string
 declared as follows:

      char str[] = "String of text"

 The string is stored in 15 bytes of memory as

       ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿
       ³ S ³ t ³ r ³ i ³ n ³ g ³   ³ o ³ f ³   ³ t ³ e ³ x ³ t ³\0 ³
       ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ
       C String Format

 Since str is an array like any other, it is passed by reference, just as
 other C arrays are. To pass by value, declare the array as a member of a
 structure. (See Section 5.1.2, "C Arguments," for more information.)

 When a C string appears in an argument list, C passes the address of the
 string. C can easily pass data to a fixed-length FORTRAN string.

 To pass strings to FORTRAN, make sure the called routine receives the string
 by reference and allocates sufficient space. FORTRAN should expect to
 receive fixed-length strings. Note, however, that C cannot pass
 variable-length strings to FORTRAN; the FORTRAN string data are not placed
 on the stack, but require special low-level variables found only in a
 FORTRAN program.


 5.2.4.3  Pascal Strings

 Pascal has two types of strings, each of which uses a different format: a
 fixed-length type STRING and the variable-length type LSTRING.

 The format used for STRING is identical to the FORTRAN string format. The
 format of an LSTRING stores the length in the first byte. For example,
 consider an LSTRING declared as

      VAR STR:LSTRING(14);
      STR := 'String of text'

 The string is stored in 15 bytes of memory. The first byte indicates the
 length of the string text. The remaining bytes contain the string text
 itself:

       ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿
       ³14 ³ S ³ t ³ r ³ i ³ n ³ g ³   ³ o ³ f ³   ³ t ³ e ³ x ³ t ³
       ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ
       Pascal String Format

 The Pascal data type LSTRING is not compatible with the format used by
 FORTRAN. You can pass an LSTRING indirectly, however, by first assigning it
 to a STRING variable. Pascal supports such assignments by performing a
 conversion of the data. The Pascal STRING and the FORTRAN CHARACTER*n types
 are equivalent. Therefore, Pascal fixed-length string variables can be
 freely passed to  FORTRAN.


 5.2.4.4  FORTRAN Strings

 FORTRAN stores strings as a series of bytes at a fixed location in memory.
 Note that there is no delimiter at the end of the string as in C. Consider
 the string declared as follows:

            STR = 'String of text'

 The string is stored in 14 bytes of memory as:

       ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿
       ³ S ³ t ³ r ³ i ³ n ³ g ³   ³ o ³ f ³   ³ t ³ e ³ x ³ t ³
       ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ
       FORTRAN String Format

 Strings are passed by reference, just like other FORTRAN data.
 Variable-length FORTRAN strings of type CHARACTER*(*) (available in
 Versions 4.0 and later) cannot effectively be passed to other languages.
 However, fixed-length strings can be passed without much difficulty; the
 principal limitation is that the FORTRAN INTERFACE must declare the length
 of the string in advance.

 By default, FORTRAN passes strings by reference. However, if you apply the C
 or PASCAL attribute to a routine, then the default changes to passing by
 value. The actual string data do not include a delimiter, unless you use the
 C-string feature described below.

 FORTRAN cannot directly pass strings to BASIC because BASIC expects to
 receive a string descriptor when passed a string. However, there is the
 following indirect method:

   1. Allocate a fixed-length string in FORTRAN, declare an array of two-byte
      integers, and treat the array as a string descriptor.

   2. Assign the address of the string to the first element (using the LOC
      function), and assign the length of the string to the second element.

   3. Pass the integer array itself by reference. BASIC can receive and
      process this array just as it would a string descriptor.

 The C-string feature overrides the normal FORTRAN format and produces
 strings that can be effectively manipulated by C. When the C-string feature
 is used, a null byte is appended to the end of the string, and backslashes
 that appear in a literal-string text are treated as escapes.

 You convert FORTRAN strings to C strings by adding C immediately after a
 string constant. Do not insert commas or any other intervening punctuation,
 only spaces. Note that the length of the string increases by one byte
 because of the additional null byte. You need to allow for this when you
 declare string variables.

 The following FORTRAN example passes the address of a string to C. The
 string is in the C format.

            INTERFACE TO SUBROUTINE CONV [C] (S1)
            CHARACTER*5 S1 [REFERENCE]
            END
      C
            CHARACTER*5 S1
            S1 = 'abcd' C
            CALL CONV (S1)

 Note that an additional byte is allocated for S1, in consideration of the
 null byte added by the C-string conversion (done on the line above the
 call). Also note that the REFERENCE keyword was necessary because the C
 attribute in the first line changes the parameter-passing default to calling
 by value.

 FORTRAN and Pascal use the same fixed-length string types and can therefore
 easily pass strings between one another.

 FORTRAN modules should only pass fixed-length strings to Pascal. The Pascal
 routines, in turn, should expect to receive fixed-length strings. To specify
 a fixed-length string parameter in Pascal, you first need to declare a type,
 as in the example below.

      C     FORTRAN SOURCE CODE
      C
            INTERFACE TO SUBROUTINE PS (S1)
            CHARACTER*4 S1
            END
      C
            S1 = 'wxyz'
            CALL PS (S1)
            END

      { Pascal module }

      module Psmod;
      type stype4 = string(4);
      procedure ps (vars str1 : stype4);


 5.3  Handling Special Data Types

 Special types of data that are either structured (contain more than one
 field) or accessed externally need considerations beyond those already
 discussed. For example, not all languages index arrays in the same fashion.
 Custom structures created in one language may not interact properly with
 another language.


 5.3.1  Arrays

 When programming with more than one language, be aware of two special
 problems specific to arrays:

   1. Arrays are implemented differently in BASIC, so you must take special
      precautions when you pass an array from BASIC to FORTRAN.

   2. Arrays are declared and indexed differently in each language.

 This section considers each of these problems in turn.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Arrays cannot be passed by value in C, unless declared within a
    structure. However, it is usually most efficient to pass arrays by
    reference.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 5.3.1.1  Cautions for Passing BASIC Arrays

 Most Microsoft languages permit you to reference arrays directly. In
 Microsoft FORTRAN, for example, an address name is equivalent to the address
 of the first element. This simple implementation is possible because the
 location of data for an array never changes.

 BASIC uses an "array descriptor," however, which is similar in some respects
 to a BASIC string descriptor. The array descriptor is necessary because
 BASIC may shift the location of array data in memory; BASIC handles memory
 allocation for arrays dynamically.

 FORTRAN does not have an equivalent to the BASIC array descriptor. More
 importantly, it lacks access to BASIC's space-management routines for
 arrays. Therefore, you may safely pass arrays from BASIC only if you follow
 these three rules:

   1. Pass the array's address by applying the VARPTR function to the first
      element of the array and passing the result by value. To pass the far
      address of the array, apply both the VARPTR and VARSEG functions and
      pass each result by value. The receiving language gets the address of
      the first element and considers it to be the address of the entire
      array. Arrays can then be accessed with the normal array-indexing
      syntax. The example below illus-trates how this works.

   2. The routine that receives the array must not, under any circumstances,
      make a call back to BASIC. If it does, then the location of the array
      data may change, and the address that was passed to the routine will
      become meaningless.

   3. BASIC may pass any member of an array by value. With this method, the
      above precautions do not apply.

 The following example demonstrates how a BASIC array can be passed to
 FORTRAN:

      REM   BASIC SOURCE FILE
      OPTION BASE 1
      DEFINT A-Z
      DIM A(20)
      DECLARE SUB ArrFix(BYVAL Addr AS INTEGER)
      .
      .
      .
      CALL ArrFix(VARPTR(A(1)))
      PRINT A(1)
      END

      C     FORTRAN SOURCE FILE
      C
            SUBROUTINE ARRFIX (ARR)
            INTEGER*2 ARR [NEAR] (20)
            ARR(1) = 5
            END

 In this example, BASIC considers that the argument passed is the near
 address of an array element. FORTRAN considers it to be the near address of
 the array itself. Both languages are correct.

 The parameter was declared BYVAL Addr AS INTEGER because a near (two-byte)
 address needed to be passed. If you wanted to pass a far (four-byte)
 address, then the proper code would be the following:

            DECLARE SUB ArrFix (BYVAL SegAddr AS INTEGER,
           + BYVAL Addr AS INTEGER)
            CALL ArrFix (VARSEG(A(0)), VARPTR(A(0)))

 The first field is the segment returned by VARSEG. If you use CDECL, then be
 sure to pass the offset address before the segment address, because CDECL
 causes parameters to be passed in reverse order:

            DECLARE SUB ArrFix CDECL (BYVAL Addr AS INTEGER,
           + BYVAL SegAddr AS INTEGER)
            CALL ArrFix(VARPTR(A((0)), VARSEG(A(0)))

 Note that you can apply LBOUND and UBOUND to a BASIC array to determine
 lower and upper bounds and then pass the results to another routine. This
 way, the size of the array does not need to be determined in advance.


 5.3.1.2  Array Declaration and Indexing

 Each language varies in the way that arrays are declared and indexed. Array
 indexing is purely a source-level consideration and involves no
 transformation of data. Following are the differences in the way elements
 are indexed by each language:

 Indexing Method                       Explanation

 Default lower boundary                By default, FORTRAN indexes the
                                       first element of an array as 1.
                                       BASIC and C index it as 0. Pascal
                                       lets the programmer begin
                                       indexing at any integer value.
                                       Recent versions of Microsoft
                                       FORTRAN also give you the option
                                       of specifying lower bounds at any
                                       integer values.

 Row-major order vs. column-major      Affects only arrays with more
 order                                 than one dimension. With
                                       row-major order used by C and
                                       Pascal, the rightmost dimension
                                       changes the fastest. With
                                       column-major order used by
                                       FORTRAN, and BASIC by default,
                                       the leftmost dimension changes
                                       the fastest. Thus, in Pascal the
                                       first four elements of array
                                       X(3,3)  are:

                                       X[1,1] X[1,2] X[1,3] X[2,1]

                                       In FORTRAN, the four elements
                                       are:

                                       X(1,1) X(2,1) X(3,1) X(1,2)

 The example above assumes that both the Pascal and FORTRAN arrays use lower
 bounds of 1. Table 5.2 shows equivalences for array declarations in each
 language. In this table, r is the number of elements of the row dimension
 (which changes more slowly), and c is the number of elements of the column
 dimension (which changes more quickly).

 Table 5.2  Equivalent Array Declarations

 Language          Array Declaration                   Comments

 FORTRAN           type x(c, r)                        With default lower
                                                       bounds of 1

 C                 type x[r][c]                        When passed by
                                                       reference

                   struct                              When passed by value
                   { type x[r][c];}x

 Pascal            x:array[a..a+r-1, b..b+c-1]         With default lower
                   of type                             bounds of 1

 BASIC             DIM x(c-1, r-1)                     With default lower
                                                       bounds of 0

 The declarations above extend to any number of dimensions. For example, the
 C declaration

      int arr1 [2][10][15][20]

 is equivalent to the FORTRAN declaration

            INTEGER*2 ARR1(20, 15, 10, 2)

 Each of the following describes the same place in memory for an array in the
 respective language:

      ARR1(9,3)          ! in FORTRAN, assuming lower bounds of 1

      arr1[2][8]         /* in C */

      Arr1[3,9]          {in Pascal, assuming lower bounds of 1}

      ARR1(8,2)          (in BASIC, assuming lower bounds of 0)

 When you compile BASIC with the BC command line, select the /R compile
 option, specifying that row-major rather than column-major order is to be
 used.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The constants used in a C array declaration represent dimensions, not
    upper bounds, as they do in other languages. Therefore, the last element
    in the C array declared as  int arr[5][5] is not arr[5][5], but
    arr[4][4].
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 5.3.2  Structures, Records, and User-Defined Types

 The C struct type, the BASIC user-defined type, the FORTRAN STRUCTURE type,
 and the Pascal record type are equivalent. Therefore, these data types can
 be passed between C, Pascal, FORTRAN, and BASIC.

 However, these types may be affected by the storage method. By default, C,
 FORTRAN, and Pascal use word alignment (unpacked storage) for all data
 except byte-sized objects and arrays of byte-sized objects. Figure 5.1
 shows the contrast between packed and unpacked storage.

 The unpacked storage method specifies that occasional bytes may be added
 as padding, so that the word and double-word objects start on an even
 boundary. In addition, all nested structures and records start on a word
 boundary.

 If a structure or record is being passed between a calling routine and the
 called routine, both routines must agree on a storage method. Otherwise,
 data will not be properly received. The simplest method for ensuring
 compatibility between all three languages is to turn on packing for C,
 FORTRAN, and Pascal modules. The packed storage method may sacrifice some
 speed, but it has the advantage of creating smaller executable files.

 The following example shows how FORTRAN can create a structure. This same
 structure appears in FGRAPH.FD, the graphics library include file that
 initializes the graphics routines. Several of these graphics routines use
 similar structures for holding coordinates or pieces of related information.

 Example

 An example of the xycoord structure is shown below.

            INTERFACE TO SUBROUTINE getcurrentposition
           + [FAR, C, ALIAS:"__f_getcurrentposition"](s)
            STRUCTURE/xycoord/
            INTEGER*2 xcoord ! This example creates a structure
            INTEGER*2 ycoord ! containing two INTEGER*2 values
            END STRUCTURE
            RECORD/xycoord/s
            END


 5.3.3  External Data

 You can always share data between FORTRAN and other languages by passing
 parameters. In the case of local variables and all BASIC variables, passing
 parameters is the only convenient way to share data.

 However, FORTRAN, C, and Pascal routines can access "external" data (data
 that can be directly accessed by any other module) directly. External refers
 to data that are both static and publicÄÄthat is, data that you have stored
 in a specific place in memory (static data) and made publicly available to
 other modules. Compilers make a data object (variable, structure, or array)
 available by placing its name, along with size and type information, into
 the object file.

 External data can be defined in a FORTRAN, C, Pascal, or assembly module.
 Note that a data definition is distinct from an external declaration. A
 definition causes a compiler to create a data object; an external
 declaration informs a compiler that the object is to be found in another
 module.

 There are three requirements for programs that share external data between
 languages:

   1. One of the modules must define the static data.

      You can define a static data object in a FORTRAN or C module by
      defining a data object outside all functions and subroutines. (Do not
      use the static keyword in C with a data object you wish to be public.)

   2. The other modules that will access the data must declare the data as
      external.

      In FORTRAN and Pascal, you can declare data as external by adding the
      EXTERN attribute to the data declaration. In C, you can declare data as
      external by using an extern declaration, similar to the extern
      declaration for functions.

   3. Naming-convention differences must be resolved.

      In FORTRAN and Pascal, you can adopt the C naming convention by
      applying the C attribute to the data declaration. Likewise in C, you
      can adopt the FORTRAN/Pascal/BASIC naming convention by applying
      fortran or pascal to the data declaration.


 Examples

 The examples below illustrate the general language features of external data
 described above.

      /* C source code */

      int        thing1;         /* Thing1 is public and static */
      extern     int thing2;     /* Thing2 is defined in another module */
      static     int thing3;     /* Thing3 is static, but not public */

      ctest()
      {
      .
      .
      .
      C     FORTRAN SOURCE CODE
      C
            INTEGER*2 THING1 [C, EXTERN]
            INTEGER*2 THING2 [C]
      C
      C     THING1 DEFINED IN ANOTHER MODULE, USING C CONVENTION (_thing1)
      C
      C     THING2 DEFINED HERE, USING C CONVENTION (_thing2)
      .
      .
      .

      { Pascal source code }

      module Ptest;
       procedure Test;
          var
             thing1 [C, EXTERN] : integer; { Both vars defined elsewhere }
             thing2 [C, EXTERN] : integer; { and use C naming convention }
      .
      .
      .

 In these examples, the variables thing1  and thing2  are defined and
 declared with the C naming convention so they will be placed into each
 object file as _thing1  and _thing2. However, you can specify the FORTRAN/
 Pascal/BASIC naming convention by using the following C statements:

      int        fortran  thing1;
      extern     int  fortran thing2;

 The C attribute can then be dropped from the FORTRAN and Pascal source code.
 Each object file will contain the names THING1  and THING2.


 5.3.4  Pointers and Address Variables

 Rather than pass data directly, you may want to pass the address of a piece
 of data. Passing the address is equivalent to passing the data by reference.
 In some cases, such as BASIC arrays (see Section 5.3.1.1), passing an
 address is the only way to share particular kinds of data between two
 languages.

 Unlike Pascal and C, FORTRAN does not have formal address types. How-ever,
 FORTRAN does provide ways for storing and passing addresses.

 FORTRAN programs determine near and far addresses with the LOC, LOCFAR, and
 LOCNEAR functions. You store the result as INTEGER*2 (with LOCNEAR) or as
 INTEGER*4 (with LOCFAR). LOC has the same purpose as LOCNEAR in the medium
 memory and as LOCFAR in the large memory model.

 If you pass the result of LOC, LOCNEAR, or LOCFAR to another language, be
 sure to pass by value.


 5.3.5  Common Blocks

 You can pass individual members of a FORTRAN or BASIC common block in an
 argument list just as you can with any data. However, you can also give a
 different-language module access to the entire common block at once.

 Pascal and C modules can reference the items of a common block by first
 declaring a structure or record, with fields that correspond to the common
 block variables (see Section 5.3.5.1 below). BASIC modules also employ a
 user-defined type to access the fields of a FORTRAN common block.

 Having defined a structure, record, or user-defined type with the
 appropriate fields, the Pascal or C module must then connect with the common
 block itself. The next two sections present methods for gaining access to
 common blocks.


 5.3.5.1  Passing the Address of the Common Block

 To pass the address of a common block, simply pass the address of the first
 variable in the block (that is, pass the first variable by reference.) The
 receiving C or Pascal module should expect to receive a structure or record
 by reference.

 Example

 In the example below, the C function initcb  receives the address of the
 variable N, which it considers to be a pointer to a structure with three
 fields:

      C     FORTRAN SOURCE CODE
      C
            COMMON /CBLOCK/N,X,Y
            INTEGER*2 N
            REAL*8 X,Y
      .
      .
      .
            CALL INITCB(N)
      .
      .
      .
      /* C source code */

      struct block_type {
               int     n;
               double  x;
               double  y;
      };

      initcb(block_hed)
      struct block_type *block_hed;
      {
               block_hed->n = 1;
               block_hed->x = 10.0;
               block_hed->y = 20.0;
      }


 5.3.5.2  Accessing Common Blocks Directly

 You can access FORTRAN common blocks directly by defining a structure (or
 record in Pascal) with the appropriate fields and then using the methods
 described in Section 5.3.3, "External Data."


 Example

 The example below declares cblock as an external structure. You can
 reference the individual fields of cblock which will correspond to those of
 the common block CBLOCK  in the FORTRAN source file.

      struct block_type {
               int     n;
               double  x;
               double  y;
      };

      extern struct block_type fortran cblock;


 5.3.6  Using a Varying Number of Parameters

 Some C functions, most notably printf, can be called with a different number
 of arguments each time. To call such a function from FORTRAN, you need to
 sup-press the type checking that normally forces a call to be made with a
 fixed number of parameters. You can call such routines with a variable
 number of parameters by including the VARYING attribute in your interface to
 the routine, along with the C attribute. You must use the C attribute
 because a variable number of parameters is only feasible with the C calling
 convention.

 The VARYING attribute prevents FORTRAN from enforcing a matching number of
 parameters. Each time you call the routine, you will be able to pass more or
 fewer parameters than are declared in the interface to the routine. However,
 each actual parameter that you pass will be type checked against whatever
 formal parameters you have declared in the interface. FORTRAN compares the
 type of the first actual parameter to the first formal parameter (if any),
 the second actual parameter to the second formal parameter, and so on.

 Because the number of parameters is not fixed, the routine you call should
 have some mechanism for determining how many parameters to expect. Often
 this information is indicated by the first parameter. For example, the C
 function printf scans the format string passed as the first parameter. The
 number of fields in the format string determines how many additional
 parameters the function should expect.

 Example

 The example below demonstrates the use of the VARYING attribute to call
 printf directly from FORTRAN (the program needs to be compiled and linked to
 the C large memory model so that printf is linked in).

            INTERFACE TO INTEGER*2 FUNCTION printf [C, VARYING] ()
            END

            CHARACTER*30 str1, str2
            INTEGER*2 n, printf
            str1 = 'This is %s string, number %d.'C
            str2 = 'formatted'C
            n = 1
            n = printf(LOC(str1), LOC(str2), n)
            END

 In FORTRAN, you can write the interface to printf so that the format string
 can be of varying lengths, by using the LOC feature.


 Chapter 6  Record Structures: File Formats and Access
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This chapter describes the record structures (structure of files) created by
 Microsoft FORTRAN. The data format (or "form") and file access type define a
 "record structure." Each record structure has its own advantages, and
 depending on the application you have in mind, you may find that the ease of
 use of one type is more important than the greater speed or compact size of
 another.

 If you want more general information about FORTRAN input/output and file
 access, see Chapter 3, "The Input-Output System," in the Microsoft FORTRAN
 Reference.


 6.1  Understanding Structures: Forms and Access Modes

 A "record" refers to one entry in a data file. All of the records within a
 single data file use the same method for storing information. Possible
 methods are:

   þ  Formatted
   þ  Unformatted
   þ  Binary

 You can send and retrieve information from a data file in one of two "access
 modes":

   þ  Sequential
   þ  Direct

 You can access sequential data one record after the other; direct data can
 be accessed by specifying which record in the file you want. Altogether, six
 record structures are possible: formatted sequential, formatted direct,
 unformatted sequential, unformatted direct, binary sequential, and binary
 direct. The following sections discuss each of these record structures in
 detail.


 6.2  Using Formatted Records

 Formatted files are those files opened with FORM='FORMATTED'. They consist
 of a series of formatted records of ASCII characters. Each record ends with
 ASCII carriage-return (CR) and line-feed (LF) characters. If you are not
 concerned about compact size or high-speed data writing and retrieval,
 formatted files are typically the easiest structure to use. All internal
 files (character substrings, character variables, character array elements,
 character arrays, or noncharacter arrays) must be formatted.


 6.2.1  Formatted Sequential Records

 A formatted sequential file is a series of formatted records written and
 read sequentially. Records may vary in length and may be empty, as shown in
 Figure 6.1.

 The following program fragment creates the formatted sequential file shown in
 Figure 6.2:

            I=4
            OPEN (33, FILE='FSEQ')
      C     (FORMATTED SEQUENTIAL BY DEFAULT)
            WRITE (33, '(A,I3)') 'RECORD', I/3

            WRITE (33, '()')
            WRITE (33, '(11HThe 3rd One)')
            CLOSE (33)
            END


 6.2.2  Formatted Direct Records

 In a formatted direct file, all of the records are exactly the same length
 and may be written or read in any order. The record length equals the length
 specified in the RECL= specifier, plus two bytes for the carriage-return
 (CR) and line-feed (LF) characters that serve as record separators. Prior to
 writing the record, these bytes are all undefined.

 If data does not completely fill a record, the compiler pads the remaining
 portion of the record with blank spaces. The blanks ensure that the file
 contains only completely filled records, all of the same length.

 The following program fragment creates the sample formatted direct file
 shown in Figure 6.3:

            OPEN (33, FILE='FDIR',FORM='FORMATTED',
           + ACCESS='DIRECT',RECL=10)
            WRITE (33, '(A)', REC=1) 'RECORD ONE'

            WRITE (33, '(I5)', REC=3) 30303
            CLOSE (33)
            END


 6.3  Using Unformatted Records

 An unformatted file (FORM='UNFORMATTED') is a series of records composed of
 "physical blocks" as described below. Each record contains a sequence of
 values. The lack of formatting makes files of this type more compact than
 files that store the same information in a formatted form. Access is also
 faster than with formatted files.


 6.3.1  Unformatted Sequential Records

 Unformatted sequential files must access data sequentially. Records may vary
 in length.

 Unformatted sequential files are organized in "chunks" of 130 bytes or less
 called "physical blocks." Each physical block consists of the data you send
 to the file (up to 128 bytes) plus two one-byte "length bytes" inserted by
 the compiler. The length bytes indicate the actual amount of data present.

 Figure 6.4 shows the physical block structure; each "L" represents a length
 byte.

 A "logical record" refers to an unformatted record, which contains one or
 more physical blocks (see Figure 6.5). Logical records can be as big as you
 like; the compiler will use as many physical blocks as necessary.

 When you create a logical record consisting of more than one physical block,
 the compiler sets the length byte to 129 to indicate that the data in the
 current physical block continues on into the next physical block. Then the
 length of the data portion of the last physical block in the record has a
 length (in bytes) of

      MOD (length, 128)

 where length equals the total data length in that logical record. (MOD is
 the remainder intrinsic function.) For example, if you write 140 bytes of
 data, the logical record has the structure shown in Figure 6.5.

 The first and last bytes in an unformatted sequential file are reserved;
 the first contains a value of 75, and the last holds a value of 130.
 FORTRAN uses these bytes for error checking and end-of-file references.
 Figure 6.6 illustrates these bytes in a sample unformatted sequential file.

 The following program fragment creates the unformatted sequential file shown
 in Figure 6.6:

            CHARACTER XYZ(3)
            INTEGER*4 IDATA (35)
            DATA IDATA /35 * -1/, XYZ /'x', 'y', 'z'/
      C
      C     (-1 IS REPRESENTED BY FF FF FF FF HEXADECIMAL)
      C
            OPEN (33, FILE='UFSEQ',FORM='UNFORMATTED')

      C     SEQUENTIAL BY DEFAULT)
      C
      C     WRITE OUT A 140-BYTE RECORD (128 AND THEN 12 MORE)
      C     FOLLOWED BY A 3-BYTE RECORD

            WRITE (33) IDATA
            WRITE (33) XYZ
            CLOSE (33)
            END


 6.3.2  Unformatted Direct Records

 An unformatted direct file is a series of unformatted records. You can write
 or read the records in any order you choose. All records have the same
 length, given by the RECL= specifier. No delimiting bytes separate records
 or otherwise indicate record structure.

 You can write a partial record to an unformatted direct file. Version 5.0 of
 Microsoft FORTRAN pads these records to the fixed record length with ASCII
 NUL characters; in files created by versions of FORTRAN prior to 4.0, random
 values may be used to pad records. Unwritten records in the file contain
 undefined data, as shown in the sample unformatted direct file in Figure
 6.7.

 The following program fragment creates the sample unformatted direct file
 shown in Figure 6.7:

            OPEN (33, FILE='UFDIR', RECL=10,
           + FORM = 'UNFORMATTED', ACCESS = 'DIRECT')
            WRITE (33, REC=3) .TRUE., 'abcdef'
            WRITE (33, REC=1) 2049
            CLOSE (33)
            END


 6.4  Using Binary Records

 Specifying FORM='BINARY' is particularly useful when a FORTRAN program reads
 a file created by a program written in another language. Usually, such files
 do not have an internal structure or a file structure that corresponds to
 one of the standard FORTRAN file structures. Additionally, binary files are
 compact; if you store large amounts of data, consider a binary file format.


 6.4.1  Binary Sequential Records

 A binary sequential file is a series of values written and read in the same
 order, and stored as binary numbers. No discernible record boundaries exist,
 and no special bytes indicate file structure. Data are read and written
 without changes in form or length. For any I/O list item, the sequence of
 bytes in memory is the sequence of bytes in the file (see Figure 6.8
 below).


 The following program creates the sample binary sequential file shown in
 Figure 6.8:

      $STORAGE:4
            INTEGER*1 BELLS(4)
            INTEGER IWYS(3)
            CHARACTER*4 CVAR
            DATA BELLS /4*7/
            DATA CVAR /' is '/,IWYS/'What',' you',' see'/

      C     THIS PROGRAM WRITES THE SENTENCE
      C     'What you see is what you get!'
      C     FOLLOWED BY FOUR BELL CHARACTERS (07 HEXADECIMAL)

            OPEN (33, FILE='BSEQ',FORM='BINARY')

      C     (SEQUENTIAL BY DEFAULT)

            WRITE (33) IWYS, CVAR
            WRITE (33) 'what ', 'you get!'
            WRITE (33) BELLS
            CLOSE (33)
            END


 6.4.2  Binary Direct Records

 A binary direct file stores records as a series of binary numbers,
 accessible in any order. Each record in the file has the same length, as
 specified by the RECL= argument to the OPEN statement.

 You can write partial records to binary direct files; any unused portion of
 the record will contain undefined data.

 A single read or write operation can transfer more data than a record
 contains by continuing the operation into the next record(s) of the file.
 Performing such an operation on an unformatted direct file would cause an
 error. Valid I/O operations for unformatted direct files produce identical
 results when they are performed on binary direct files, provided the
 operations do not depend on zero padding in partial records (see Figure
 6.9 below).

 The following program fragment creates the sample binary direct file
 shown in Figure 6.9:

      $STORAGE:4
            OPEN (33, FILE='BDIR',RECL=10,
           + FORM='BINARY',ACCESS='DIRECT')
            WRITE (33, REC=1) 'abcdefghijklmno'
            WRITE (33) 4,5
            WRITE (33,REC=40) 'pq'
            CLOSE (33)
            END


 Chapter 7  Optimizing Programs

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This chapter discusses techniques for creating smaller and faster executable
 files with the Microsoft FORTRAN Compiler. Although some of these ideas
 apply to only a particular class of programs, others are general coding
 practices that can be used in virtually all programs.

 The topics covered include program structure and compiler options that
 improve the compiler's efficiency; coding strategies that decrease the size
 of data files created by a program; linking and memory usage strategies;
 conditions for using integer versus real variables; and the efficient use of
 arrays.


 7.1  Compiling and Linking Strategies

 The various FORTRAN compiling and linking options enhance program control
 and debugging, but some of these options provide this control at the expense
 of size and speed. With a few judicious choices, you can shrink the size of
 your program files and sometimes increase their execution speed. The
 following sections cover the compiling and linking options that reduce the
 size of executable files and increase their speed.


 7.1.1  Using Debugging and Integer-Size Options

 Three FL command-line options can significantly affect the amount of memory
 your source program requires: the /4Yb (debug), /Zi (CodeView debugging),
 and /4I2 (integer-size) options.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The /4Yb and /4I2 options correspond to the $DEBUG and $STORAGE:2
    metacommands, respectively, and have the same effects.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 A program that includes the information generated by the debug option can
 contain up to 40 percent more code than the same program without the
 debugging information. After you successfully compile, link, and run a
 program, you can reduce its size and improve its execution speed by
 recompiling without the /4Yb option (or by removing the $DEBUG metacommands
 and recompiling).

 Similarly, the /Zi option prepares the code for debugging with the CodeView
 debugger. It produces an object file containing full symbolic debugging
 informationÄÄincluding the symbol table and line numbersÄÄfor use with the
 Microsoft CodeView window-oriented debugger. If you completed debugging with
 the CodeView debugger, you can reduce the file size by recompiling the
 finished program without the /Zi option.

 If your program does not require four bytes of storage for INTEGER and
 LOGICAL variables (that is, you do not explicitly need long integers or
 logicals), you can include the /4I2 option at compile time to reduce the
 size of gen-erated code. This option allocates two bytes (instead of four,
 the default) for each INTEGER and LOGICAL variable that you declare without
 an explicit length specification. This allocation shrinks program size
 because two-byte arithmetic operations require less supporting code than
 four-byte operations.

 The $DEBUG and $STORAGE metacommands are discussed in Chapter 6 in the
 Microsoft FORTRAN Reference.


 7.1.2  Using 8087/287/387 Math Options

 When you compile your program, you can specify a floating-point option that
 selects the math package the program will use and the default library for
 linking. If you have an 8087, 80287, or 80387 math coprocessor, you can
 choose the /FPi87 or /FPc87 option, both of which generate instructions in
 your executable file for the coprocessor. If you link with a library that
 supports 8087/287/387 coprocessors (LLIBFOR7.LIB or MLIBFOR7.LIB), the
 resulting executable files will be smaller than executable files resulting
 from other math options. The math coprocessor also speeds up programs which
 make extensive use of floating-point calculations.

 Software emulation of the floating-point processors adds to the program's
 size. You may reduce the size of executable files by as much as 7K by
 choosing one of these coprocessor options. See Chapter 1, "Controlling
 Floating-Point Operations," for more information.


 7.1.3  Using Overlays

 At link time, specifying appropriate program modules as overlays reduces
 file size. An overlay refers to two or more modules that use the same memory
 space when they execute. For example, assume two different modules, A and
 B, both contain routines needed by a main program (see Figure 7.1). If the
 main program calls a routine in module A, the program loads A from the
 disk into a particular space in memory. If the program later calls a routine
 in module B, the program loads module B into the same space previously
 occupied by module A.

 The program loads overlaid modules into the same memory space only when (and
 if) they are needed. In general, programs that use overlays are smaller and
 require less memory than programs that do not. However, programs that use
 overlays may run more slowly because of the additional time needed to read
 and reread the overlay code from disk into memory. (See Chapter 7 in the
 Microsoft FORTRAN Reference for more information about overlays.)


 7.2  Coding Strategies

 The manner in which you write a program can dramatically affect both its
 speed and its size. The types and sizes of files, variables, and arrays you
 createÄÄand how efficiently you use themÄÄcan enhance or detract from your
 program's performance.

 The following sections describe ways in which you can write source code to
 minimize the amount of data space the compiler needs and to minimize the
 object and executable file sizes.


 7.2.1  Removing Error-Message Text during SETUP

 SETUP gives you two options for handling error messages: to build a library
 that displays the entire error message (the default) or a library that
 displays only the number of the error message, which you can then look up in
 Appendix D of the Microsoft FORTRAN Reference. Using a library without the
 error-message text results in an executable file approximately 2K smaller
 than the default.


 7.2.2  Using Consistent File-Access and Format Types

 Each file format (formatted, unformatted, or binary) and access type
 (sequential or direct) requires that the compiler incorporate specific
 supporting code in your program. If the design of your program permits, try
 to restrict file access to the smallest number of access types and formats.


 7.2.3  Specifying Edit Lists

 The edit lists you specify for I/O statements significantly affect the
 number of run-time routines supporting your program. If you remove
 formatting information or reference the same specifier for multiple calls,
 such as three WRITE statements referencing the same FORMAT statement, you
 decrease the space of your program. The following sections offer specific
 hints for reducing code size.


 7.2.3.1  Left Tabbing

 The TL edit descriptor (or using the T edit descriptor to do left tabbing)
 increases the size of your executable files. The run-time code used to
 support left tabbing is included whenever the compiler detects the explicit
 use of these edit descriptors, or when a variable is used to contain the
 format string using the descriptor. As a result, you can reduce the size of
 your executable files if you avoid using left tabbing whenever possible.
 (See Chapter 3 of the Microsoft FORTRAN Reference for a description of the
 TL and T edit descriptors.)


 7.2.3.2  Formatted or List-Directed I/O

 You will create smaller executable files if you consistently use either
 formatted or list-directed I/O statements and avoid mixing the two. This
 will require fewer run-time routines to support your program and therefore
 decrease its size.


 7.2.3.3  Character Variables as Format Specifiers

 Microsoft FORTRAN translates format strings into a more compact internal
 form wherever possible. However, if the format is specified as a character
 vari-able, this translation must occur at run time instead of compile time.
 The associated run-time code for this adds significantly to the size of the
 executable file. As a result, you create smaller executable files if you use
 character constants or FORMAT statements as format specifiers. (See Chapter
 3 of the Microsoft FORTRAN Reference for a description of these
 alternatives.)


 7.2.4  Using the BLOCKSIZE Option

 Using the BLOCKSIZE option in OPEN statements allows you to choose an
 appropriate trade-off between execution speed and memory requirements for
 your program.

 The BLOCKSIZE option gives you control over the size of the internal buffer
 associated with each file your program opens. This buffer is designed to
 speed up execution time when your program reads or writes a large number of
 small items to a file. By default, the run-time system assigns a buffer size
 of 1,024 bytes for sequential-access files. However, you can use the
 BLOCKSIZE option to allocate a buffer of a different size.

 Using the BLOCKSIZE option gives you the following trade-off between program
 speed and memory requirements:

   þ  Larger buffer sizes speed up I/O operations but increase the amount of
      memory allocated for these operations.

   þ  Smaller buffer sizes result in slower I/O, but they save memory for
      other purposes.

 Since the buffers are only allocated when the file is opened, using the
 option BLOCKSIZE will not affect the size of the program's executable file.
 However, as a program approaches the memory limits of the machine, a smaller
 buffer size might allow the program to stay within those limits.

 The value that you specify for the BLOCKSIZE option only indirectly
 determines the buffer. The value is rounded up to a multiple of 512 for a
 sequential-access file for performance reasons. DOS normally formats disks
 into 512-byte sectors and performs I/O operations to and from disk files in
 accordance with these sector boundaries. Therefore, the most efficient I/O
 occurs when the buffer size is also a multiple of 512 for sequential-access
 files. (See Chapter 3 of the  Microsoft FORTRAN Reference for an explanation
 of the rules for determining the actual buffer sizes of direct-access and
 terminal files.)

 Therefore, if you specify BLOCKSIZE = 1800  when you open a sequential file,
 the actual buffer size will be the next multiple of 512 not less than 1,800,
 or 2,048 (4 * 512). BLOCKSIZE will support buffers up to a maximum of 32,256
 bytes; any values passed to BLOCKSIZE larger than this will result in a
 buffer of 32,256 bytes.


 7.2.5  Using Integer and Real Variables

 Although most FORTRAN programs use both floating-point and integer data,
 programs that use only integer data are considerably smaller than programs
 that use floating-point data. Remember, too, that in some situations you can
 use integers in place of floating-point numbers. Depending on the situation,
 you may be able to multiply your numbers by a known factor to convert them
 to integers, perform any manipulations, and convert them back for final
 printing.

 If you write a program that uses only integer data (for example, a program
 that prints out the time but does nothing else), remember either to
 explicitly declare variables with type INTEGER, or to use variable names
 that begin with letters that default to INTEGER type. By default, these are
 the letters "I" through "N." Use the IMPLICIT statement to specify different
 default letters for each type. (See Chapter 4 of the Microsoft FORTRAN
 Reference for more information.)


 7.2.6  Using Arrays and EQUIVALENCE Statements

 The ways in which you declare arrays and use them in EQUIVALENCE statements
 can significantly affect the amount of information needed to compile your
 program. The use of arrays may affect whether the compiler runs out of
 memory when processing your source files. The fewer assumptions the compiler
 must make about the types and uses of arrays, the more efficient the
 compilation.

 The following sections offer suggestions for using arrays in ways that
 minimize the amount of data the compiler requires.


 7.2.6.1  Minimizing Constant Use in Array Declarations

 If possible, minimize the number of different constants you use in array
 declarations. Also, place array declarations that use the most constants
 first. These actions improve efficiency during compilation because the
 compiler saves copies of the constants and reuses the copies, if possible.

 The two examples below illustrate this. You compile the declarations shown
 in the first example more efficiently by declaring the variables X, Y, and Z
 with the same bounds, as shown in the second example:

            INTEGER X(11), Y(10), Z(11)

            INTEGER X(11), Y(11), Z(11)

 If it is necessary to declare array Y with a bound of 10, the program would
 compile more efficiently by placing the declarations of X and ZÄÄwhich have
 the same constant boundsÄÄfirst, as in the example below:

            INTEGER X(11), Z(11)

            INTEGER Y(10)


 7.2.6.2  Minimizing Items Declared in Each Type Statement

 Declare as few items as possible in each type statement. This reduces the
 complexity of each declaration and, as a result, saves the compiler data
 space when processing each declaration. This technique has an especially
 significant effect for array declarations.


 7.2.6.3  Declaring Array Types before Dimensioning

 Explicitly declare an array's type before you dimension it. Also, before you
 use arrays in EQUIVALENCE statements, make sure that each array in the
 statement list has been completely declared, dimensioned, and (if necessary)
 declared in a COMMON statement.

 The examples below show how to declare the array A to use memory most
 efficiently during compilation.

            INTEGER A(10,20)

            INTEGER A
            DIMENSION A(10,20)

            INTEGER A
            COMMON A(10,20)

 In the declarations shown below, the compiler requires extra data space
 because the type of the array X is not given before X is used in the
 EQUIVALENCE statement.

            DIM X(20)
            EQUIVALENCE (X(1), J)
            INTEGER X

 In the following declarations, the compiler requires extra data space
 because it cannot determine the types of A and B until it has processed
 the second line; it does not know that B is an array until it has processed
 the second line, and it does not know that A and B are in a common block
 until it has processed the third line:

            EQUIVALENCE (A(10,20), B)
            INTEGER A, B(20,30)
            COMMON /CBA/A, /CBB/B

 In each case, the compiler must make assumptions that increase the amount of
 processing and the amount of data space it needs to hold intermediate
 processing results. To improve efficiency, rewrite the declarations as
 follows:

            INTEGER A(10,20), B(20,30)
            COMMON /CBA/A, /CBB/B
            EQUIVALENCE (A(10,20), B(1,1))

 In this series of declarations, the compiler knows immediately that A and B
 refer to two-dimensional integer arrays and the size of each. The subsequent
 references to A and B leave no ambiguities for the compiler.


 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Part 2  The Microsoft FORTRAN Graphics Library

 Part 2 introduces the Microsoft FORTRAN Graphics Library and its use. The
 graphics library supports traditional pixel-based routines such as line
 drawing, in addition to a collection of font routines that can scale and
 print text in a variety of typefaces. Numerous examples in the chapters and
 reference section show how the routines are actually used.


 Chapter 8  Graphics Library Routines
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The Microsoft FORTRAN DOS run-time graphics library includes a complete set
 of graphics functions. It supports pixel-based graphics, real-coordinate
 graphics, and character fonts. In addition, the associated text-based
 routines are also available under OS/2. This chapter introduces the major
 categories of library routines and their use. Chapters 9 and 10 cover
 graphics and fonts, respectively, in more detail. For a complete description
 of the syntax and use of each individual routine, see Chapter 11, "Graphics
 Library Reference."

 Review the contents of this chapter carefully. It provides an overview of
 all of the routines, and introduces some important fundamental concepts.
 Several of the routines affect the output of other routines; reading the
 entire chapter will give you an understanding of how the different classes
 of routines interact.

 You can access this graphics library from Microsoft FORTRAN as well as other
 Microsoft languages that support the FORTRAN calling conventions. The
 graphics package supports the IBM(R) (and compatible) Enhanced Graphics
 Adapter (EGA) ; the Color Graphics Adapter (CGA) ; the Multicolor Graphics
 Array (MCGA); certain operating modes of the Video Graphics Array (VGA)
 hardware configuration; and the Olivetti(R) video mode present on AT&T
 personal computers. The package also supports the Hercules(R) Graphics Card,
 Graphics Card Plus, InColor Card, and 100-percent compatible cards.


 8.1  Using Graphics and Character-Font Functions

 The graphics and font routines are supplied in a run-time library, named
 GRAPHICS.LIB, that you must link to your programs using graphics; the OS/2
 library GRTEXTP.LIB supports the text-based graphics routines. Graphics
 routines are functions and subroutines that draw lines, rectangles,
 ellipses, and similar graphic elements on the screen. The font routines
 allow your computer to create text in a variety of styles, sizes, and
 colors.

 Every program using the graphics library must also explicitly declare any
 routines it uses. This means that you will need to either write an interface
 to each routine you call, or reference the include files FGRAPH.FI and
 FGRAPH.FD; the OS/2 library GRTEXTP.LIB uses these same files. The FGRAPH.FI
 include file contains procedure declarations in INTERFACE statements; the
 include file FGRAPH.FD contains both structure and symbolic constant
 declarations and EXTERNAL statement declarations for the graphics
 procedures. Together, they contain the declarations necessary to access all
 of the graphics library routines. To use them, include FGRAPH.FI at the
 beginning of your program's source files, and FGRAPH.FD in every subprogram
 that calls a graphics routine.

 The GRAPHICS.LIB run-time library includes routines corresponding to the
 various tasks involved in creating and manipulating graphics:

 Category                              Task

 Configuring mode and drawing          Selects the proper display mode
 environment                           for the hardware and establishes
                                       memory areas for the writing and
                                       displaying of images

 Setting coordinates                   Specifies the origin and the
                                       display area on the screen

 Setting graphics palettes             Specifies colors available to
                                       graphics routines

 Setting attributes                    Specifies background and
                                       foreground colors and mask and
                                       line styles for graphics routines

 Displaying images                     Draws and fills figures on the
                                       screen

 Outputting text                       Writes text to the screen

 Transferring images                   Stores images in memory and
                                       retrieves them

 Displaying fonts                      Displays text in specified fonts
                                       compatible with Microsoft Windows
                                       286/386

 The following sections explain each of these tasks in detail. Note that many
 of the graphics routine names exceed 6 characters, so avoid using the
 $TRUNCATE metacommand in programs that call graphics routines.


 8.2  Defining Graphics Characteristics

 Before you are actually ready to create graphics on your screen, you must
 invoke routines that define the way both text and graphics will be
 displayed. Sections 8.2.1-8.2.4 discuss four groups of these routines.


 8.2.1  Selecting Display Options

 The Microsoft FORTRAN run-time graphics library contains a number of
 routines that let you define how you want to display text and graphics.
 These routines determine the graphics environment characteristics, establish
 operational modes (text or graphics), and control the cursor.

      Routine                          Use

      displaycursor                    Turns the cursor back on or
                                       leaves the cursor off after
                                       executing a graphics routine

      getvideoconfig                   Obtains information about the
                                       current graphics environment

      setactivepage                    Sets the memory location for the
                                       working page; lets you animate
                                       objects

      settextrows                      Sets the number of rows available
                                       for standard text output

      setvideomode                     Sets a display mode for the
                                       screen

      setvideomoderows                 Sets the display mode and the
                                       number of rows available for
                                       standard text output

      setvisualpage                    Sets a memory location for the
                                       display page

 The displaycursor routine lets you control whether the cursor will be
 restored after completing a graphics routine. Either setting you choose
 remains in effect until the next time you call the routine.

 The getvideoconfig routine returns information about the current hardware
 environment. This information is used by several of the other routines. (For
 example, getvideoconfig returns the value of screen size needed by
 videoconfig.)

 The setactivepage and setvisualpage routines set aside memory for the
 working page and the displayed page, respectively, in configurations that
 support multiple video pages.

 The setvideomode routine selects an operating mode (such as the resolution)
 for the monitor. The settextrows routine sets the number of text rows to be
 displayed. The setvideomoderows routine combines the two previous functions;
 it sets the display mode and the number of text rows available.


 8.2.2  Setting Graphics Coordinates

 The coordinate-setting routines control where graphics may appear on the
 screen. Microsoft FORTRAN graphics routines recognize three sets of
 coor-dinates:

   1. Fixed "physical coordinates," which are determined by the hardware and
      the configuration routines used

   2. "Viewport coordinates," which you define in the application

   3. "Window coordinates," which you define to simplify scaling of
      floating-point data values

 Unless you change it, the viewport-coordinate system is identical to the
 physical- coordinate system. The physical origin (0, 0) is always in the
 upper left corner of the display. The x-axis extends in the positive
 direction left to right, while the  y-axis extends in the positive direction
 top to bottom.

 The routines also let you convert between physical-, viewport-, and
 window-coordinate systems. (For more detailed information on coordinate
 graphics, see Chapter 9, "Drawing Graphic Elements.")

      Routine                          Use

      getcurrentposition               Determines current position in
                                       viewport coordinates

      getcurrentposition_w             Determines current position in
                                       window coordinates

      getphyscoord                     Converts viewport coordinates to
                                       physical coordinates

      getviewcoord                     Converts physical coordinates to
                                       viewport coordinates

      getviewcoord_w                   Converts window coordinates to
                                       viewport coordinates in xycoord
                                       structure

      getwindowcoord                   Converts viewport coordinates to
                                       window coordinates

      setcliprgn                       Limits graphic output to part of
                                       the screen

      setvieworg                       Positions the viewport-coordinate
                                       origin

      setviewport                      Limits graphic output and
                                       positions the viewport-coordinate
                                       origin within a limited area

      setwindow                        Defines a window-coordinate
                                       system

 The dimensions of the x-  and y-axes depend on the configuration routine
 used and the current mode (text or graphics). These values are accessible
 through the numxpixels and numypixels values returned by getvideoconfig.
 Similarly,  getvideoconfig also returns the range of colors available in the
 current mode through the bitsperpixel value.

 The origin of the viewport-coordinate system can be moved to a new position
 relative to the physical origin with the setvieworg routine. However, you
 can still determine the location of any point with the getcurrentposition
 and getcurrentposition_w routines.

 The window-coordinate system lets you easily scale any set of data to fit on
 the screen. You define whatever range of coordinates works well for your
 data (such as 0 to 5,000) as the range for the window-coordinate axes. By
 telling the program that you want the window-coordinate system to fit in a
 particular area on the screen (map to a particular set of viewport
 coordinates), you can scale a chart or drawing to any size you wish. The
 setwindow routine defines a window-coordinate system bound by the specified
 values.

 The getphyscoord routine converts any viewport coordinates to physical
 coordinates, and the getviewcoord routine translates from physical
 coordinates to viewport coordinates. Similarly, getviewcoord_w changes
 window coordinates to viewport coordinates, and getwindowcoord translates
 viewport coordinates into window coordinates.

 The setcliprgn routine defines a restricted active display area on the
 screen (a "clipping region"). The setviewport function does the same thing
 and also resets the viewport origin to the upper left corner of the clipping
 region.


 8.2.3  Setting Palettes

 Each color on the screen is represented by a unique value called a "color
 index." To associate a color index with the color on the screen, the
 graphics library uses the concept of a "palette." A palette is simply a
 "mapping" (association) of color indexes to the actual display colors.

 The palette routines assign colors to the functions and subroutines that
 draw in color. They can also alter the colors of items already drawn on the
 screen.

 The three palette routines determine the mapping between the color indexes
 and the colors drawn on the screen.

      Routine                          Use

      remapallpalette                  Assigns a color index to all of
                                       the displayable colors

      remappalette                     Assigns a color index to a
                                       specific color

      selectpalette                    Selects a predefined palette

 Depending on the particular graphics mode, a one-, two-, four-, or eight-bit
 value represents each screen pixel. This bit-value representation of a
 color called the "color value," is often required by routines as one of
 their arguments.

 Most video modes support only one palette. If you select the
 medium-resolution graphics mode $MRES4COLOR, $MRESNOCOLOR, or $ORESCOLOR,
 however, you can access multiple palettes. (In these modes, the palette
 consists of a background color and three other colors.) The selectpalette
 routine selects a particular palette from the available palettes.

 EGA and VGA hardware also provide the capability of remapping a palette,
 allowing any available color index to be mapped to any color value. Two
 routines allow the EGA and VGA to provide this capability: the remappalette
 function remaps one color value, and remapallpalette remaps the entire
 palette.

 The remappalette, remapallpalette, setbkcolor, and getbkcolor routines are
 the only routines that recognize color values defined for the EGA or VGA.

 Many graphics routines operate only on certain hardware or in certain
 graphics modes. These routines return an error code if you use them on an
 invalid hardware environment. The reference section (Chapter 11) indicates
 which routines return error information; if you use these routines, be sure
 to check the returned value for any errors.


 8.2.4  Setting Figure Attributes

 The output routines that draw arcs, ellipses, and other primitive figures do
 not specify color or line-style information. Instead, they rely on several
 "attributes" set independently by the routines listed below.

      Routine                          Use

      getbkcolor                       Returns the current background
                                       color

      getcolor                         Returns the current color

      getlinestyle                     Returns the current line style

      getfillmask                      Returns the current fill mask

      setbkcolor                       Sets the current background color
                                       to a new color

      setcolor                         Sets the current color to a new
                                       color

      setfillmask                      Sets the current fill mask to a
                                       new color

      setlinestyle                     Sets the current line style to a
                                       new line style

 The getcolor and setcolor routines obtain or set the "current color"
 attribute, which the floodfill routine and the closed-figure drawing
 routines all use. Similarly, the getbkcolor and setbkcolor routines retrieve
 or set the "current background color," used by the clearscreen function.

 The getfillmask and setfillmask routines return or set the "current fill
 mask" attribute. The mask is an 8-by-8-bit array with each bit representing
 a pixel. If a bit is 0, the pixel in memory is left untouched: the mask is
 transparent to that pixel. If a bit is 1, the pixel is assigned the current
 color value. The array acts as a template that repeats over the entire fill
 area. It "masks" the background with a pattern of pixels drawn in the
 current color, creating a large number of fill patterns. These routines are
 particularly useful for shading and slight color-variation effects.

 The getlinestyle and setlinestyle routines retrieve and set the "current
 line style." The line style is determined by a 16-bit-long template with
 each bit cor-responding to a pixel. If a bit is 0, the pixel keeps the
 current background color (that is, appears transparent). If a bit is 1, the
 pixel appears in the current color. The template repeats over the entire
 length of the line. The two routines let you create a wide variety of dashed
 lines.


 8.3  Displaying Graphics Output

 The remaining graphics routines actually allow you to draw geometric
 features, display text, display font-based characters, and transfer images
 between memory and the screen. These routines are described in Sections
 8.3.1-8.3.3.


 8.3.1  Creating a Graphics Display

 The Microsoft FORTRAN run-time graphics library routines draw geometric
 figures using the coordinate system you specify. They use the current line
 style, fill mask, background color, and foreground color.

 If you want anything other than the default line style (solid), mask (no
 mask), background color (green), or foreground color (highest index on the
 current palette), you must call the appropriate routine prior to calling the
 drawing routine. Subsequent output routines employ the same attributes until
 they are changed.

 The name of each routine describes its task or the figure it draws.

      Routine                          Use

      arc, arc_w                       Draws an arc

      clearscreen                      Erases the screen (fills it with
                                       the current background color)

      ellipse, ellipse_w               Draws an ellipse or circle

      floodfill, floodfill_w           Fills an area of the screen with
                                       the current mask in the current
                                       color

      getcurrentposition,              Finds the coordinates of the
      getcurrentposition_w             current graphic-output position

      getpixel, getpixel_w             Returns a pixel's value

      lineto, lineto_w                 Draws a line from the current
                                       graphic-output position to a
                                       specified point

      moveto, moveto_w                 Moves the current graphic-output
                                       position to a specified point

      pie, pie_w                       Draws a pie-slice-shaped figure

      rectangle,  rectangle_w          Draws a rectangle

      setpixel, setpixel_w             Sets a pixel's value

 Most of these routines have multiple forms. Routine names that end with "_w"
 use the window-coordinate system and DOUBLE PRECISION argument values.
 Routines without this suffix use the viewport-coordinate system.

 Curved figures, such as arcs and ellipses, are centered within a "bounding
 rectangle," which is specified by the upper left and lower right corners of
 the rectangle. The center of the rectangle becomes the center for the
 figure, and the rectangle's borders determine the size of the figure. In
 Figure 8.1 the points (x1, y1) and (x2, y2) define the bounding rectangle.


 8.3.2  Displaying Text

 These routines let you send text to the screen in both graphics and text
 modes. The routines for outputting text in specialized fonts are described
 in Chapter 10, "Using Fonts from the Graphics Library." Unlike the standard
 I/O statements, these routines recognize the text-window boundaries used in
 applications that use windows.

      Routine                     Use

      displaycursor               Sets the cursor on or off after
                                  leaving a graphics routine

      gettextcolor                Returns the current text color

      gettextcursor               Returns the current cursor
                                  attribute (text modes only)

      gettextposition             Returns the current text-output
                                  position

      outtext                     Sends text to the screen at the
                                  current position

      settextcolor                Sets the current text color to a
                                  new color

      settextcursor               Sets the current cursor attribute
                                  (text modes only)

      settextposition             Relocates the current text
                                  position

      settextwindow               Sets the current text-display
                                  window

      wrapon                      Turns line wrapping on or off

 These routines do not provide text-formatting capabilities. If you want to
 print integer or floating-point values, you must convert the values into a
 string (using an internal WRITE statement) before calling these routines.
 The text routines specify all screen positions in character-row and column
 coordinates.

 The settextwindow routine is the text equivalent to the setcliprgn graphics
 routine, except that it restricts only the display area for text printed
 with outtext. It does not affect the standard I/O statements, such as WRITE.
 The outtext routine displays a string on the screen. You can retrieve or
 alter the current text color with the gettextcolor and settextcolor
 routines, respectively. The routines gettextposition and settextposition let
 you determine or change the location of the next text sent to the screen.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    The WRITE statement sends its carriage return (CR) and line feed (LF) to
    the screen at the beginning of the first I/O statement following the
    WRITE statement. This can cause unpredictable text positioning if you mix
    the graphics routines settextposition and settext with the WRITE
    statement. Minimize this effect by using the backslash (\) format
    descriptor (suppress CR-LF) in the associated FORMAT statement.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The wrapon function enables or disables automatic "line wrapping." Line
 wrapping ends a line of text and starts a new one when a word at the end of
 a line is too long. Without line wrapping the same word would be split,
 often incoherently, at the end of the first line, with the remainder
 appearing on the next line.


 8.3.3  Transferring Images

 The getimage and putimage groups of routines transfer images between mem-ory
 and the screen. When you hold an image in memory, the application allocates
 a buffer (that is, it sets aside part of the computer's memory) for the
 image. The imagesize routines calculate the size in bytes of the buffer
 needed to store a given image.

 Routines that end with "_w" use window coordinates; the other functions in
 this set use viewport coordinates.

      Routine                          Use

      getimage, getimage_w             Stores a screen image in memory

      imagesize, imagesize_w           Returns image size in bytes

      putimage, putimage_w             Retrieves an image from memory
                                       and displays it

 In systems that support multiple pages, you can send graphics to a page in
 memory while displaying another page. Once you complete the page in memory,
 display it on the screen and begin drawing a new page in memory. By
 repeating this process you can animate objects on the screen.


 8.3.4  Displaying Font-Based Characters

 Since the FORTRAN Graphics Library provides a variety of fonts, indicate
 which font you wish to use when displaying font-based characters. Once you
 select a font, you can make inquiries about the width of a string or the
 current font characteristics. The following functions control the display of
 font-based characters.

      Routine                          Use

      getfontinfo                     Returns the current font
                                      characteristics

      getgtextextent                  Determines the width of specified
                                      text in the current font

      outgtext                        Sends text in the current font to
                                      the screen at the specified pixel
                                      position

      registerfonts                   Initializes the font library

      setfont                         Finds a single font that matches
                                      a specified set of
                                      characteristics and makes this
                                      font the current font for use by
                                      the outgtext function

      unregisterfonts                 Frees memory allocated by
                                      registerfonts

 Characters may be drawn ("mapped") in one of two ways: as bit-mapped letters
 (a "picture" of the letter) or as vector-mapped characters (a series of
 lines and arcs that make up a letter). See Chapter 10, "Using Fonts from
 the Graphics Library," for detailed explanations and examples of how to
 apply fonts and the characteristics of the fonts supplied with Microsoft
 FORTRAN.


 8.4  Routines Available under OS/2

 The OS/2 graphics library, GRTEXTP.LIB, contains the text-based routines
 available under DOS. The functionality and use of the OS/2 routines are the
 same as DOS, and the descriptions in Chapter 11, "Graphics Library
 Reference," all apply. When linking, however, you need to supply GRTEXTP.LIB
 in place of the DOS GRAPHICS.LIB. Note that both DOS and OS/2 use the same
 graphics include files.

 The library GRTEXTP.LIB supports the following routines:

      clearscreen          getvisualpage        settextwindow
      displaycursor        outtext              setvideomode
      getactivepage        setactivepage        setvideomoderows
      getbkcolor           setbkcolor           setvisualpage
      gettextcolor         settextcolor         wrapon
      gettextcursor        settextcursor
      gettextposition      settextposition
      getvideoconfig       settextrows



 Chapter 9  Drawing Graphic Elements
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Graphics can make your screens and data more understandable and informative.
 All of the building blocks needed to create a wide variety of drawings are
 discussed in this chapter. The chapter assumes you have read the overview
 presented in Chapter 8. Note that the OS/2 graphics library, GRTEXTP.LIB,
 does not support font-based routines. Refer to Chapter 8 for a list of the
 OS/2 routines available under DOS.

 The DOS graphics routines provided with the Microsoft FORTRAN Compiler set
 points, draw lines, change colors, and draw shapes such as circles,
 rectangles, and arcs. The first section defines some basic graphics terms,
 lists the steps to using high-resolution graphics, and works through a
 sample program step by step, showing how to use the elementary routines. The
 remaining sections explain coordinate systems in detail and how to display
 graphics inside viewports and windows.


 9.1  Working with Graphics Modes

 Before your program can begin drawing graphic elements, you must specify the
 type of monitor being used, the desired screen resolution, and whether to
 display in color or black and whiteÄÄin short, the "mode" you want to use
 when displaying graphics. This section shows you how to determine an
 appropriate graphics mode; how to make that mode active; and how to write a
 typical graphics program.

 This chapter refers to the following terms frequently. Be sure you are
 familiar with them:

   þ  The "origin" (point 0,0) is the upper left corner of your screen. The
      x- and y-axes start at the origin. You can change the origin in some
      coordinate systems.

   þ  The horizontal direction is represented by the x-axis. The maximum
      number of horizontal "pixels" (picture elements) varies between 320 and
      720, depending on the graphics card installed and the graphics mode in
      effect.

   þ  The vertical direction is represented by the y-axis. The number of
      vertical pixels ranges from 200 to 480.

   þ  Each graphics mode offers a color palette. You may access 2, 4, 8, 16,
      or 256 "color indexes" (an index number associated with a specific
      screen color), depending on the graphics card in the computer and the
      graphics mode in effect.

   þ  The CGA modes use four fixed "palettes" containing predefined colors
      that cannot be changed. The EGA, MCGA, and VGA modes allow you to
      change any of the color indexes by providing a color value that
      describes the mix of colors you want to use.

   þ  A color index is always an INTEGER*2 number. A color value is always an
      INTEGER*4 value. When you call graphics routines that require
      color-related parameters, be aware of the difference between color
      indexes and color values. (See Section 9.2.1.2, "Color Mixing.")

 To prepare a system to display graphics, you need to

   1. Use the getvideoconfig procedure to find the type of video adapter
      installed. (See Section 9.1.1, "Checking the Current Video Mode.")

   2. Set the desired graphics mode for the adapter with the setvideomode
      procedure. (See Section 9.1.2, "Setting the Video Mode.")

   3. Draw the graphics. (See Section 9.1.3, "Writing a Graphics Program.")

 When you finish creating the graphics, remember to return to the default
 text mode before leaving the program.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    You must always explicitly link the graphics library. See Chapter 7, "The
    FL Command," of the Microsoft FORTRAN Reference for information about
    linking libraries.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 9.1.1  Checking the Current Video Mode

 In order to avoid compatibility problems, call the getvideoconfig routine
 before entering a video mode. Information about the current system is
 returned in the videoconfig structure defined in FGRAPH.FD and outlined in
 the program example below.

 The videoconfig structure is defined below. You need to recognize the fields
 and their usage because several other graphics routines require them as
 arguments:

      STRUCTURE/videoconfig/
       INTEGER*2 numxpixels             ! Number of pixels on x-axis
       INTEGER*2 numypixels             ! Number of pixels on y-axis
       INTEGER*2 numtextcols            ! Number of text columns available
       INTEGER*2 numtextrows            ! Number of text rows available
       INTEGER*2 numcolors              ! Number of color indexes
       INTEGER*2 bitsperpixel           ! Number of bits per pixel
       INTEGER*2 numvideopages          ! Number of available video pages
       INTEGER*2 mode                   ! Current video mode
       INTEGER*2 adapter                ! Active display adapter
       INTEGER*2 monitor                ! Active display monitor
       INTEGER*2 memory                 ! Adapter video memory in K bytes
      END STRUCTURE

 You initialize the fields in a structure variable of type videoconfig when
 you call getvideoconfig.


 9.1.2  Setting the Video Mode

 Once you know the current video configuration, you must tell the graphics
 adapter to switch from text mode to graphics mode. To do this, call the
 setvideomode routine. This routine will pass a constant to the adapter that
 tells the adapter which mode to display (see Table 9.1)

 The include file FGRAPH.FD defines these names as symbolic constants with
 the appropriate values. The screen dimensions for each mode are listed in
 pixels for graphics modes and in columns for text modes. The mode
 type/hardware column indicates the expected environment.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    If you use a Hercules adapter, you must run the MSHERC.COM program before
    attempting to display any graphics.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 If setvideomode returns a 0, the hardware does not support the selected
 mode. You can continue to select alternate video modes until the routine
 returns a nonzero value. You might use a sequence of high- to low-resolution
 calls to the setvideomode routine in order to choose the highest resolution
 available. The SELECT CASE constructs work particularly well. If the
 hardware configuration does not support any of the selected video modes, the
 program cannot display graphics.

 Table 9.1 below shows the names for the different video modes.

 Table 9.1  Video Modes for Text and Graphics

 Constant              Video Mode                          Mode Type/Hardware

 $DEFAULTMODE          Restores to original mode           Both/All

 $TEXTBW40             40 column text, 16 gray             Text/CGA

 $TEXTC40              40 column text, 16/8 color          Text/CGA

 $TEXTBW80             80 column text, 16 gray             Text/CGA

 $TEXTC80              80 column text, 16/8 color          Text/CGA

 $MRES4COLOR           320 x 200, 4 color                  Graphics/CGA

 $MRESNOCOLOR          320 x 200, 4 gray                   Graphics/CGA

 $HRESBW               640 x 200, BW                       Graphics/CGA

 $TEXTMONO             80 column text, BW                  Text/MDA

 $HERCMONO             720   348, BW for HGC               Graphics/HGC

 $MRES16COLOR          320 x 200, 16 color                 Graphics/EGA

 $HRES16COLOR          640 x 200, 16 color                 Graphics/EGA

 $ERESNOCOLOR          640 x 350, BW                       Graphics/EGA

 $ERESCOLOR            640 x 350, 4 or 16 color            Graphics/EGA

 $VRES2COLOR           640 x 480, BW                       Graphics/VGA/
                                                           MCGA

 $VRES16COLOR          640 x 480, 16 color                 Graphics/VGA

 $MRES256COLOR         320 x 200, 256 color                Graphics/VGA/
                                                           MCGA
 $ORESCOLOR            640                                 400, 1 of 16 colors

 $MAXCOLORMODE        See Tables R.5 and R.6 for          Graphics/CGA
                       maximum colors

 $MAXRESMODE          See Tables R.5 and R.6 for          Graphics/CGA
                       maximum resolution


 9.1.3  Writing a Graphics Program (SINE)

 Like many programs, graphics programs work well in modular form. The use of
 discrete routines aids debugging by isolating the separate functional
 components of the program. The following program and its associated
 subroutines illustrate the processes involved in initializing, drawing, and
 closing a graphics program.

 The SINE program creates a sine wave. Its procedures use many of the common
 graphics routines. The main program (see below) follows a modular structure,
 calling five functions which carry out the actual graphics commands. All of
 the functions are defined later in this chapter.

      CC  SINE.FOR - Illustrates basic graphics commands.

            INCLUDE 'FGRAPH.FI'

            CALL graphicsmode()
            CALL drawlines()
            CALL sinewave()
            CALL drawshapes()
            CALL endprogram()
            END

      C     Definitions of subroutines go here
      .
      .
      .

 The SINE program's output appears in Figure 9.1.


 9.1.3.1  Activating a Graphics Mode

 Before your program can display graphics, you must specify a graphics mode.
 Your program can then call setvideomode to set the video adapter to the
 appropriate graphics mode.

 SINE turns on the graphics mode by calling the subroutine graphicsmode
 below. The subroutine selects the highest possible resolution for the
 current video adapter (such as CGA and VGA).

            SUBROUTINE graphicsmode()

            INCLUDE 'FGRAPH.FD'

            INTEGER*2            dummy, maxx, maxy
            RECORD /videoconfig/ myscreen
            COMMON               maxx, maxy

      C
      C     Find graphics mode.
      C
            CALL getvideoconfig( myscreen )
            SELECT CASE( myscreen.adapter )
               CASE( $CGA )
                  dummy = setvideomode( $HRESBW )
               CASE( $OCGA )
                  dummy = setvideomode( $ORESCOLOR )
               CASE( $EGA, $OEGA )
                  IF( myscreen.monitor .EQ. $MONO ) THEN
                      dummy = setvideomode( $ERESNOCOLOR )
                  ELSE
                      dummy = setvideomode( $ERESCOLOR )
                  END IF
               CASE( $VGA, $OVGA, $MCGA )
                  dummy = setvideomode( $VRES2COLOR )
               CASE( $HGC )
                  dummy = setvideomode ( $HERCMONO )
               CASE DEFAULT
                  dummy = 0
            END SELECT

            IF( dummy.EQ.0) STOP 'Error: cannot set graphics mode'

      C
      C     Determine the minimum and maximum dimensions.
      C
            CALL getvideoconfig( myscreen )
            maxx = myscreen.numxpixels - 1
            maxy = myscreen.numypixels - 1
            END

 The subroutine begins by calling getvideoconfig and passes the address of
 the videoconfig structure myscreen. In videoconfig, the element called
 "adapter" tells the subroutine the type of adapter in use. Given that
 information and a SELECT CASE block, the program enters the appropriate
 graphics mode.

 To find the resolution of the video mode the program chose, you must pass
 the videoconfig structure to the getvideoconfig routine, using the same
 structure:

            CALL getvideoconfig( myscreen )
            maxx = myscreen.numxpixels - 1
            maxy = myscreen.numypixels - 1

 If your computer uses an EGA card, the subroutine graphicsmode sets the
 video mode to $ERESNOCOLOR. The horizontal screen size is 640 pixels and the
 vertical screen size is 350. The two assignments give these values
 to maxx and maxy, less 1. The horizontal resolution might be 640, but the
 pixels are numbered 0-639. Thus, the maxx variableÄÄthe highest available
 pixel numberÄÄmust be 1 less than the total number of pixels:

            myscreen.numxpixels - 1.

 Two short functions convert from an imaginary 1,000-by-1,000-pixel screen to
 whatever video mode is in effect. From now on, the program assumes it has
 1,000 pixels in each direction. To draw the points on the
 screen, newx and newy change each point to their physical (pixel)
 coordinates, as follows:

      CC  NEWX - This function finds new x-coordinates.

            INTEGER*2 FUNCTION newx( xcoord )

            INTEGER*2 xcoord, maxx, maxy
            REAL*4    tempx
            COMMON    maxx, maxy

            tempx = maxx / 1000.0
            tempx = xcoord * tempx + 0.5
            newx  = tempx
            END

      CC  NEWY - This function finds new y-coordinates.

            INTEGER*2 FUNCTION newy( ycoord )

            INTEGER*2 ycoord, maxx, maxy
            REAL*4    tempy
            COMMON    maxx, maxy

            tempy = maxy / 1000.0
            tempy = ycoord * tempy + 0.5
            newy  = tempy
            END


 9.1.3.2  Drawing Lines on the Screen

 SINE calls the subroutine drawlines next. As the name implies, this
 subroutine draws several lines on the screen. In this case, a rectangle
 around the outer edges of the screen and three horizontal lines that divide
 the screen into quarters are drawn (see Figure 9.1 above).

      CC  DRAWLINES - This subroutine draws a box and
      CC              several lines.

            SUBROUTINE drawlines()

            INCLUDE 'FGRAPH.FD'

            EXTERNAL         newx,newy
            INTEGER*2        dummy, newx, newy, maxx, maxy
            RECORD /xycoord/ xy
            COMMON           maxx, maxy

      C
      C     Draw the box.
      C
            dummy = rectangle( $GBORDER, 0, 0, maxx, maxy )
            CALL setvieworg( 0, newy( INT2( 500 ) ), xy )
      C
      C     Draw the lines.
      C
            CALL    moveto( 0, 0, xy )
            dummy = lineto( newx( INT2( 1000 ) ), 0 )
            CALL    setlinestyle( #AA3C )
            CALL    moveto( 0, newy( INT2( -250 ) ), xy )
            dummy = lineto(newx(INT2( 1000 ) ),
           +        newy( INT2( -250 ) ) )
            CALL    setlinestyle( #8888 )
            CALL    moveto( 0, newy( INT2( 250 ) ), xy )
            dummy = lineto( newx( INT2( 1000 ) ),
           +        newy( INT2( 250 ) ) )
            END

 The first argument to the rectangle routine is the "fill flag," which may be
 either $GBORDER or $GFILLINTERIOR. Choose $GBORDER if you want a rectangle
 of four lines (a border only, in the current line style). Choose
 $GFILLINTERIOR   if you want a solid rectangle (filled in with the current
 color and fill pattern). Choosing the color and fill pattern is discussed
 later in this chapter (see Tables 9.1-9.3).

 The second and third arguments are the x- and y-coordinates of the upper
 left corner of the rectangle. The fourth and fifth arguments are the
 coordinates for the opposite (lower right) corner. Since the coordinates for
 the two corners are ( 0, 0 ) and ( maxx, maxy ), the call to rectangle
 frames the screen:

            dummy = rectangle( $GBORDER, 0, 0, maxx, maxy )

 The call to the setlinestyle routine changes the line style from a solid
 line to a dashed line. A series of 16 bits tells the routine the pattern to
 follow. A "1" indicates a solid line segment and "0" an empty segment.
 Therefore, to the computer, a solid line looks like 1111 1111 1111 1111. A
 dashed line might look like 1111 1111 0000 0000 (long dashes) or 1111 0000
 1111 0000 (short dashes). You may choose any combination of 1s and 0s. The
 16-bit binary number also may be represented as a hexadecimal or decimal
 number. In the example below, the hexadecimal constant #AA3C equals the
 binary value 1010 1010 0011 1100. However you could use the decimal value
 43,580 as the argument just as effectively.

 To draw lines, first set any appropriate line style. Then move to where you
 want the line to begin and draw from that point to where you want the line
 to end. The SINE program, for example, uses the following:

            CALL setlinestyle( #AA3C )
            CALL moveto( 0, newy( INT2( -250 ) ), xy )
            dummy = lineto( newx( INT2( 1000 ) ),
           +                newy( INT2( -250 ) ) )

 The moveto routine positions an imaginary pixel cursor at a spot on the
 screen (nothing visible appears on the screen), and the lineto routine draws
 a line. The negative value -250 might seem to be an impossible screen
 coordinate. How-ever, the program calls setvieworg and changes the
 viewport-coordinate system to run from -500 at the top of the screen to +500
 at the bottom. Admittedly, this goes against intuitive convention, but
 illustrates the routine's ability to alter the viewport coordinates (or, to
 "viewport") to whatever dimensions or orientation you prefer. (Viewports and
 the setvieworg routine are explained in more detail in Section 9.3.2.1.)


 9.1.3.3  Drawing the Sine Curve

 With the axes and frame in place, SINE is ready to draw the sine curve. The
 sinewave routine calculates the x and y positions for two cycles of the sine
 wave and plots them on the screen:

      CC  SINEWAVE - This subroutine calculates and plots a sine
      CC             wave.

            SUBROUTINE sinewave()

      INCLUDE 'FGRAPH.FD'

            INTEGER*2        dummy, newx, newy, locx, locy, i
            DOUBLE PRECISION rad, PI
            EXTERNAL         newx, newy

            PARAMETER        ( PI = 3.14159 )

      C
      C     Calculate each position and display it on the screen.
      C
            DO i = 0, 999, 3
               rad   = -SIN( PI * i / 250.0 )
               locx  = newx( i )
               locy  = newy( INT2( rad * 250.0 ) )
               dummy = setpixel( locx, locy )
            END DO
            END

 The graphics routine setpixel takes the two location parameters, locx and
 locy, and turns on the pixel at that position.


 9.1.3.4  Adding Shapes

 After drawing the sine curve, the SINE program calls drawshapes to put two
 rectangles and two ellipses on the screen. The fill flag alternates between
 $GBORDER and $GFILLINTERIOR:

      CC  DRAWSHAPES - Draws two boxes and two ellipses.

            SUBROUTINE drawshapes()

            INCLUDE 'FGRAPH.FD'

            EXTERNAL  newx, newy
            INTEGER*2 dummy, newx, newy

      C
      C     Create a masking (fill) pattern.
      C
            INTEGER*1 diagmask(8),  linemask(8)
            DATA diagmask / #93, #C9, #64, #B2, #59, #2C,
           +                #96, #4B /
            DATA linemask / #FF, #00, #7F, #FE, #00, #00,
           +                #00, #CC /
      C
      C     Draw the rectangles.
      C

            CALL setlinestyle( #FFFF )
            CALL setfillmask( diagmask )
            dummy = rectangle( $GBORDER,
           +       newx( INT2( 50 ) ),  newy( INT2( -325 ) ),
           +       newx( INT2( 200 ) ), newy( INT2( -425 ) ) )
            dummy = rectangle( $GFILLINTERIOR,
           +       newx( INT2( 550 ) ), newy( INT2( -325 ) ),
           +       newx( INT2( 700 ) ), newy( INT2( -425 ) ) )
      C
      C     Draw the ellipses.
      C
            CALL setfillmask( linemask )
            dummy = ellipse( $GBORDER,
           +       newx( INT2( 50 ) ), newy( INT2(325 ) ),
           +       newx( INT2( 200 ) ), newy( INT2(425 ) ) )
            dummy = ellipse( $GFILLINTERIOR,
           +       newx( INT2( 550 ) ), newy( INT2( 325 ) ),
           +       newx( INT2( 700 ) ), newy( INT2( 425 ) ) )
            END

 Note that setlinestyle resets the line pattern to solid. If you omit this
 routine, the first rectangle would be drawn with dashes instead of a solid
 line.

 The ellipse routine draws an ellipse using parameters similar to those for
 the rectangle routine, both requiring a fill flag and two corners of a
 bounding rectangle. (Figure 8.1 in Chapter 8 shows how an ellipse uses a
 bounding rectangle.)

 The $GFILLINTERIOR flag fills the shape with the current fill pattern. To
 create a pattern, you must pass the address of an eight-byte array of
 unsigned bytes to the setfillmask routine. Then diagmask is initialized with
 the pattern shown in Table 9.1.

 Table 9.1  Fill Patterns
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

             Bit Pattern                Value in diagmask
 Bit No.     7  6  5  4  3  2  1  0

             Û        Û        Û  Û     diagmask(0) = #93

             Û  Û        Û        Û     diagmask(1) = #C9

                Û  Û        Û           diagmask(2) = #64

             Û     Û  Û        Û        diagmask(3) = #B2

                Û     Û  Û        Û     diagmask(4) = #59

                   Û     Û  Û           diagmask(5) = #2C

             Û        Û     Û  Û        diagmask(6) = #96

                Û        Û     Û  Û     diagmask(7) = #4B


 9.1.3.5  Exiting Graphics Mode

 The final routine called by the SINE program, the endprogram, waits for the
 ENTER key to be pressed and then sets the screen back to normal:

      CC  ENDPROGRAM - This subroutine waits for the ENTER key to
      CC               be pressed, then resets the screen before
      CC               returning.

            SUBROUTINE endprogram()

            INCLUDE 'FGRAPH.FD'
            INTEGER*2  dummy

            READ (*,*)      ! Wait for ENTER to be pressed
            dummy = setvideomode( $DEFAULTMODE )
            END

 Note that resetting the screen is an important step in any graphics program.
 Other applications may make assumptions about your monitor and its
 resolution with unpredictable results. These assumptions could conflict with
 any changes your program made to the current graphics mode. Resetting to the
 original mode resolves any potential conflicts.


 9.2  Adding Color

 The Microsoft FORTRAN Graphics Library supports CGA, EGA, and VGA color
 graphics. Color can add variety and impact to your graphics, but it must be
 suited to your application.

 Color graphics can be an improvement over black and white, but using color
 requires a compromise. When you request the maximum number of colors, you
 sacrifice some resolutionÄÄa maximum resolution of 320-by-200 pixels instead
 of a higher resolution. For example, the program COLOR (see Section
 9.2.1.5) always selects the highest possible resolution. In a color mode,
 the program always creates a screen only 320 pixels wide and 200 pixels
 high.


 9.2.1  Color Graphics Modes

 The Microsoft FORTRAN Graphics Library has graphics modes particular to each
 of the CGA, EGA, and VGA adapters. In addition, higher resolution hardware
 can support the lower resolution modes. That is, EGA and VGA hardware can
 both display the CGA modes, and VGA hardware can also display EGA modes. Any
 hardware that supports color graphics modes also supports the
 black-and-white graphics modes.


 9.2.1.1  CGA Color Graphics Modes

 The CGA color graphics modes $MRES4COLOR and $MRESNOCOLOR display four
 colors selected from one of several predefined color palettes. They display
 these foreground colors against a background color, which can be any one of
 the 16 available colors. With the CGA hardware, the palette of foreground
 colors is predefined and cannot be changed. Each palette number is an
 integer, as shown in Table 9.2.

 Table 9.2  Available CGA Colors

                               Color Index

      Palette
      Number        1               2                 3
      0             Green           Red               Brown
      1             Cyan            Magenta           Light gray
      2             Light green     Light red         Yellow
      3             Light cyan      Light magenta     White

 On black-and-white monitors, the $MRESNOCOLOR graphics mode produces
 palettes containing various shades of gray. It displays colors when used
 with a color display. However, only two palettes are available with a color
 display. You can use the selectpalette routine to select one of these
 predefined palettes. Table 9.3 shows the $MRESNOCOLOR color indexes and
 palettes.

 Table 9.3  CGA Colors:  $MRESNOCOLOR Mode

                                Color Index
      Palette
      Number        1               2             3
      0             Blue            Red           Light gray
      1             Light blue      Light red     White

 The selectpalette routine works only with the graphics modes $MRES4COLOR and
 $MRESNOCOLOR. To change palettes in other graphics modes, use the
 remappalette or remapallpalette functions.

 The following program sets the video mode to $MRES4COLOR and then cycles
 through the background colors and palette combinations. It works on
 computers equipped with CGA, EGA, MCGA, or VGA cards and requires a color
 monitor.

      CC  CGA.FOR - Demonstrates CGA colors.

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2 dummy2, i, j, k
            INTEGER*4 dummy4
            INTEGER*4 bkcolor(8) /
           +          $BLACK, $BLUE   , $GREEN, $CYAN,
           +          $RED  , $MAGENTA, $BROWN, $WHITE /

            CHARACTER*7  bkcolorname(8) /
           +             'BLACK', 'BLUE'   , 'GREEN', 'CYAN',
           +             'RED'  , 'MAGENTA', 'BROWN', 'WHITE' /

            RECORD / rccoord / curpos

            IF( setvideomode( $MRES4COLOR ) .EQ. 0 )
           +    STOP 'Error:  cannot set CGA graphics mode'

            DO i = 0, 3
               dummy2 = selectpalette( i )
               DO k = 1, 8
                  dummy4 = setbkcolor( bkcolor(k) )
                  DO j = 0, 3
                     CALL settextposition( 1, 1, curpos )
                     WRITE (*, 9000) bkcolorname(k), i, j
                     dummy2 = setcolor( INT4( j ) )
                     dummy2 = rectangle( $GFILLINTERIOR,
           +                             160, 100, 320, 200 )
                     READ (*,*)     ! Wait for ENTER to be pressed
                  END DO
               END DO
            END DO
            dummy2 = setvideomode( $DEFAULTMODE )

       9000 FORMAT( ' background color: ', A / ' palette:', I3 /
           +        ' color:  ', I3 / )

            END


 9.2.1.2  Color Mixing

 At the beginning of this chapter, we mentioned the difference between color
 indexes and color values. Imagine an analogy: a painter owns 64 tubes of
 paint but has a palette only large enough to hold 16 globs of paint. A
 painting created under these constraints could contain only 16 colors
 (selected from a total of 64).

 One of the EGA graphics modes ($ERESCOLOR) is similar: 16 color indexes
 chosen from a total of 64 color values. Color indexes are sometimes called
 "color attributes," or "pixel values"; color values are sometimes called
 "actual colors."

 The VGA offers the widest variety of color values: 262,144 (256K). The VGA
 palette size may contain 2, 16, or 256 color indexes, depending on the
 graphics mode. When you select a color value, you specify a level of
 intensity with a range of 0-63 for each of the red, green, and blue color
 values. The long integer that defines a color value consists of four bytes
 (32 bits),

      MSB                             LSB
      zzzzzzzz zzBBBBBB zzGGGGGG zzRRRRRR

 where z  refers to zero and B, G, and R  represent the bit values for blue,
 green, and red intensities. The most significant byte (MSB ) must contain
 all zeros. The two high bits in the remaining three bytes must also be 0. To
 mix a light red (pink), turn red all the way up and mix in some green and
 blue:

      00000000 00100000 00100000 00111111

 In hexadecimal notation this number equals #0020203F. You could also use the
 statement function

      RGB (r,g,b) = (#3f3f3f.AND.(ISHFT(b,16).OR.ISHFT(9,8).OR.r))

 where r, g, and b  were previously declared as integers.

 You can pass decimal values (more familiar and intuitive to most of us) to
 this macro. Keep in mind that 1 (binary 00000001) represents a low color
 intensity and that 63 (binary 00111111) equals full color intensity. Then,
 to create pure yellow (100-percent red plus 100-percent green) and assign it
 to a variable y1, use this line:

            y1 = RGB( 63, 63, 0 )

 For white, turn all of the colors on:

            RGB( 63, 63, 63 )

 For black, set all of the colors to 0:

            RGB( 0, 0, 0 )

 Mixing colors in EGA modes is similar to the mixing described above, but
 there are fewer intensities for the red, green, and blue components. In the
 modes that offer 64 colors, the R, G, and B  values cover only two bits and
 can therefore range from decimal 0 to 3. The integer that defines an RGB
 color looks like this:

      MSB                             LSB
      zzzzzzzz zzBB???? zzGG???? zzRR????

 The bits marked z must be zeros and the bits marked with question marks can
 be any value (although zeros tend to improve readability). To form a pure
 red color value, you would use the hexadecimal constant #00000030. For cyan
 (blue plus green), use  #00303000. The RGB statement function defined above
 is easily modified for EGA monitors:

            EGARGB (r,g,b) = ( #303030 .AND.1   ( ISHFT( b,20 )
           + .OR.ISHFT( g,12 ).OR.ISHFT( r,4 ) ) )

 In this statement function, you would pass values in the range of 0-3
 instead of 0-63. You could now assign the pure red as

            r1 = EGARGB( 0,0,3 )


 9.2.1.3  EGA Color Graphics Modes

 The $MRES16COLOR, $HRES16COLOR, or $ERESCOLOR video modes display color
 graphics best with an EGA adapter. The CGA modes will also display on the
 EGA adapter, but with lower resolution and decreased color options.

 The remappalette routine assigns a new color value to a color index. For
 example, when you first enter an EGA graphics mode, color index 1 equals the
 color value blue. To reassign the pure red color value to color index 1, you
 could use this line:

            dummy4 = remappalette( 1, #000030 )

 Or, use the symbolic constant $RED, which is defined in the FGRAPH.FI file:

            dummy4 = remappalette( 1, $RED )

 After this routine call, any object currently drawn in color index 1 will
 instantly switch from blue to red.

 For EGA graphics, the first value is an integer in the range of 0-15, and
 the second value is an INTEGER*4 defined as a mixture of red, green, and
 blue.

 The remapallpalette routine changes all of the color indexes simultaneously.
 You pass it an array of color values. The first color value in the list
 becomes the new color associated with the color index 0, the second becomes
 associated with the color index 1, and so on.

 The argument passed to routines that set colors (such as setcolor) is the
 index for a color in the current palette. In the default text palette, an
 index of 1 refers to blue. When a palette changes, a color's index number
 also changes. As a result, the color produced by a given index number also
 changes. If you create a drawing in one palette and then change the palette,
 the color indexes will suddenly refer to different colors and the entire
 drawing will change color. The number of  color indexes available depends on
 the number of colors supported by the current video mode.

 The remappalette and remapallpalette routines work in all modes but only
 with the EGA, MCGA, or VGA hardware. The routines remappalette and
 remapallpalette fail and return a value of -1 if you attempt to remap a
 palette without the necessary hardware.

 The following program draws a rectangle with a red interior. In the default
 EGA palette, color index 4 is red. In the program EGA, this color index is
 changed to $BLUE.

      CC  EGA.FOR - Demonstrates use of EGA palettes.

            INCLUDE 'FGRAPH.FI'
            INCLUDE 'FGRAPH.FD'

            INTEGER*2        dummy2
            INTEGER*4        dummy4
            RECORD /xycoord/ xy

            dummy2 = setvideomode( $ERESCOLOR )
            dummy2 = setcolor( 4 )
            dummy2 = rectangle( $GFILLINTERIOR, 50, 50, 200, 200 )
      C
      C     Display normal palette message.
      C
            CALL settextposition( 1, 1, xy )
            CALL outtext( 'Normal palette   ')
            CALL outtext( 'Press  ENTER to continue')
            READ (*,*)          ! Wait for ENTER to be pressed
            dummy4 = remappalette( 4, $BLUE )
      C
      C     Display new palette message.
      C
            CALL settextposition( 1, 1, xy )
            CALL outtext( 'Remapped palette ')
            CALL outtext( 'Press ENTER to continue')
            READ (*,*)
            dummy4 = remappalette( 4, $RED )
      C
      C     Display original palette message.
      C
            CALL settextposition( 1, 1, xy )
            CALL outtext( 'Restored palette ')
            CALL outtext( 'Press ENTER to clear the screen')
            READ (*,*)
      C
      C     Return to original video mode.
      C
            CALL clearscreen( $GCLEARSCREEN )
            dummy2 = setvideomode( $DEFAULTMODE )
            END


 9.2.1.4  VGA Color Graphics Modes

 The VGA card adds graphics modes $VRES2COLOR, $VRES16COLOR, and
 $MRES256COLOR to your repertoire. EGA and CGA modes are available with the
 VGA hardware, providing either lower resolution or fewer color choices.

 The VGA color graphics modes operate with a range of 262,144 (256K) color
 values. The graphics mode $VRES2COLOR displays 2 colors, the graphics mode
 $VRES16COLOR displays 16 colors, and the graphics mode $MRES256COLOR
 displays 256 colors from the available VGA colors.

 The remappalette routine changes a color index to a specified color value.
 The following function remaps the color index 1 to the color value given by
 the symbolic constant $RED. After this statement is executed, whatever was
 displayed as blue will now appear as red:

      remappalette( 1, dummy 4=$RED )  ! Reassign color index 1
                                       ! to VGA red

 The remapallpalette routine remaps one or more color indexes simultaneously.
 The routine's argument references an array of color values that reflect the
 remapping. The first color number in the list becomes the new color
 associated with color index 0, the second with color index 1, and so on.

 Symbolic constants (names) for the default color numbers are supplied so
 that the remapping of VGA colors is compatible with EGA practice. The names
 are self-descriptive: the color numbers for black, light yellow, and red are
 represented by the symbolic constants $BLACK, $LIGHTYELLOW, and $RED.

 All of the VGA display modes operate with any VGA monitor. Colors appear as
 shades of gray on compatible monochrome monitors.

 The example program HORIZON illustrates the impact possible with the full
 range of 256 colors available with a VGA card:

      CC  HORIZON.FOR - Illustrates VGA graphics with cycling of
      CC                256 colors.

            INCLUDE 'FGRAPH.FI'
            INCLUDE 'FGRAPH.FD'

            INTEGER*2   MYRED, MYBLU, MYWHT, STEP
            PARAMETER ( MYRED = #000003FF )
            PARAMETER ( MYBLU = #003F0000 )
            PARAMETER ( MYWHT = #003F3F3F )
            PARAMETER ( STEP  = 21 )

            INTEGER*2        dummy, i, j
            INTEGER*4        rainbow(0:511), col, ngray


      RECORD /rccoord/ curpos
            RECORD /xycoord/ xy
      C
      C     Check to see if adapter can handle 256 colors.
      C
            IF( setvideomode( $MRES256COLOR ) .EQ. 0 )
           +    STOP 'This program requires a VGA card.'
      C
      C     Create the colors.
      C
            DO col = 0, 63
               ngray = ( col .OR. ISHFT( col,8 ) )
           +                .OR. ISHFT( col,16 )
               rainbow( col)            = MYBLU .AND. ngray
               rainbow( col + 256)      = rainbow( col )
               rainbow( col + 64)       = MYBLU .OR. ngray
               rainbow( col + 64 + 256) = rainbow( col + 64 )
               rainbow( col + 128)      = MYRED .OR.
           +                           ( MYWHT .AND. .NOT. ngray )
               rainbow( col + 128 + 256) = rainbow( 64 + 128 )
               rainbow( col + 192)       = MYRED .AND. .NOT. ngray
               rainbow( col + 192 + 256) = rainbow( col + 192 )
            END DO
            CALL setvieworg( 160, 85, xy )
      C
      C     Draw shapes on screen.
      C
            DO i = 0, 254
               dummy = setcolor( INT4( 255 - i ) )
               CALL    moveto(  i, i - 255, xy )
               dummy = lineto( -i, 255 - i )
               CALL    moveto( -i, i - 255, xy )
               dummy = lineto(  i, 255 - i )
               dummy = ellipse( $GBORDER, -i, -i / 2, i, i / 2 )
            END DO
      C
      C     Cycle through the colors.
      C
            i = 0
            DO j = 1, 256
               dummy = remapallpalette( rainbow(i) )
               i     = MOD( i + STEP, 256 )
            END DO

            dummy = settextcolor( 15 )
            CALL settextposition( 25, 1, curpos )
            CALL outtext( 'Press ENTER to exit')
            READ (*,*)
            dummy = setvideomode( $DEFAULTMODE )
            END


 9.2.1.5  Color Graphics Programs (COLOR and GRAPHIC)

 The sample program COLOR sets a mode with as many colors as possible for the
 available hardware:

      CC  COLOR.FOR - Sets a medium resolution mode
      CC              with maximum color choices.

            INCLUDE 'FGRAPH.FI'
            INCLUDE 'FGRAPH.FD'

            INTEGER*2            dummy
            RECORD /videoconfig/ vc

      C
      C     Set mode for maximum number of colors.
      C
            IF( setvideomode( $MAXCOLORMODE ) .EQ. 0 )
           +    STOP 'Error:  no color graphics capability'
            CALL getvideoconfig( vc )

            WRITE (*, 9000) vc.numcolors, vc.numxpixels,
           +                vc.numypixels
            READ (*,*)       ! Wait for ENTER to be pressed
            CALL clearscreen( $GCLEARSCREEN )
            dummy = setvideomode( $DEFAULTMODE )

       9000 FORMAT( ' available colors: ', I5 /
           +        ' horizontal pixels:', I5 /
           +        ' vertical pixels:  ', I5 )
            END

 Use the routine clearscreen, which clears the screen in any video mode). To
 view every possible graphics mode, run the program GRAPHIC shown below.
 Explanations of various color graphics modes (CGA, EGA, and VGA) follow.

      CC  GRAPHIC.FOR - Displays every graphics mode.

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2 key
            EXTERNAL  printmenu
            EXTERNAL  showmode

            CALL printmenu( key )
            DO WHILE( key .NE. 0 )
               CALL showmode( key )
            END DO
            END

      SUBROUTINE printmenu( key )

            INCLUDE  'FGRAPH.FD'

            INTEGER*2  dummy, key

            key = -1
            DO WHILE( (key .LT. 0)  .OR.  (key .GT. 12) )
               dummy = setvideomode( $DEFAULTMODE )
               WRITE (*, 9000)
               READ (*,*) key
            END DO

       9000 FORMAT( ' Please ENTER a graphics mode.' /
           +        ' ( To exit, ENTER 0. )' /// ' 0 Exit' /
           +        '  1 $MRES4COLOR'   / '  2 $MRESNOCOLOR' /
           +        '  3 $HRESBW'       / '  4 $HERCMONO'    /
           +        '  5 $MRES16COLOR'  / '  6 $HRES16COLOR' /
           +        '  7 $ERESNOCOLOR'  / '  8 $ERESCOLOR'   /
           +        '  9 $VRES2COLOR'   / ' 10 $VRES16COLOR' /
           +        ' 11 $MRES256COLOR' / ' 12 $ORESCOLOR'   / )
            END


            SUBROUTINE showmode( which )

            INCLUDE  'FGRAPH.FD'

            INTEGER*2 which, dummy, i, height, width
            INTEGER*2 modes(12) /
           +   $MRES4COLOR, $MRESNOCOLOR, $HRESBW, $HERCMONO ,
           +   $MRES16COLOR, $HRES16COLOR, $ERESNOCOLOR,
           +   $ERESCOLOR, $VRES2COLOR, $VRES16COLOR,
           +   $MRES256COLOR, $ORESCOLOR /

            RECORD /videoconfig/ screen

            IF( setvideomode( modes(which) ) .NE. 0 ) THEN
               CALL getvideoconfig( screen )
               width  = screen.numxpixels / screen.numcolors
               height = screen.numypixels / 2
               DO i = 0, screen.numcolors - 1
                  dummy = setcolor( INT4( i ) )
                  dummy = rectangle( $GFILLINTERIOR, i * width, 0,
           +                       ( i + 1 ) * width, height )
               END DO
            ELSE
               WRITE (*, 9000)
            END IF

      READ (*,*)       ! Wait for ENTER to be pressed
            dummy = setvideomode( $DEFAULTMODE )
            CALL printmenu( which )

       9000 FORMAT ( ' Video mode is not available.' /
           +         ' Please press ENTER.   ' / )
            END


 9.2.2  Using the Color Video Text Modes

 Two color video text modes, $TEXTC40 and  $TEXTC80, work with the CGA, EGA,
 and VGA displays. These modes display steady or blinking text in any one of
 the 16 foreground colors with any one of the 8 background colors.

 In a video text mode, each displayed character requires two bytes of video
 memory. The first byte contains the ASCII code representing the character;
 the second byte contains the display attribute. In the CGA color video text
 modes, the attribute byte determines the color and whether or not it will
 blink. Sixteen colors are available: the CGA color values, and the default
 EGA and VGA color values. Since the EGA and VGA palette can be remapped,
 these values can correspond to any set of 16 colors with the appropriate
 palette mapping.


 9.2.2.1  Using Text Colors

 Use gettextcolor and getbkcolor to find the current text mode's foreground
 and background colors. Values in the range 0-15 represent normal colors.
 Values in the range of 16-31 are the same colors as those in the range 0-15,
 but with blinking text.  Set the foreground and background colors with
 settextcolor and setbkcolor. Both use a single argument specifying the color
 value for text displayed with outtext. The text mode color indexes are
 defined in Table 9.4.

 Table 9.4  Text Colors

      Number   Color       Number    Color

      0        Black       8         Dark gray
      1        Blue        9         Light blue
      2        Green       10        Light green
      3        Cyan        11        Light cyan
      4        Red         12        Light red
      5        Magenta     13        Light magenta
      6        Brown       14        Light brown
      7        White       15        Light white

 Use settextposition to move the cursor to a particular row and column for
 displaying text. The outtext routine prints the text on the screen.


 9.2.2.2  Text Color Program (COLTEXT)

 The following program displays a chart showing all possible combinations of
 text and background colors.

      CC  COLTEXT.FOR - Displays text color with various color or
      CC                monochrome attributes.

            INCLUDE 'FGRAPH.FI'
            INCLUDE 'FGRAPH.FD'

            INTEGER*2          dummy2, blink, fgd
            INTEGER*4          dummy4, bgd
            CHARACTER*2        str
            RECORD / rccoord / curpos

            CALL clearscreen( $GCLEARSCREEN )
            CALL outtext('Text color/monochrome attributes:')

            DO  blink = 0, 16, 16
               DO bgd = 0, 7
                  dummy4 = setbkcolor( bgd )
                  CALL settextposition( INT2( bgd ) +
           +          ( ( blink / 16 ) * 9) + 3, 1, curpos )
                  dummy2 = settextcolor( 15 )
                  WRITE (str, '(I2)') bgd
                  CALL outtext( 'Back:' // str // '  Fore:')
      C
      C         Loop through 16 foreground colors. For monochrome,
      C         these will be underscore and low/high intensity.
      C
                  DO fgd = 0, 15
                     dummy2 = settextcolor( fgd + blink )
                     WRITE ( str, '(I2)' ) fgd + blink
                     CALL outtext( '  ' // str )
                  END DO
               END DO
            END DO

            WRITE (*, '(///// A \)') ' Press ENTER to exit . . . '
            READ (*,*)
            dummy2 = setvideomode( $DEFAULTMODE )
            END


 9.3  Understanding Coordinate Systems

 Chapter 8, "Graphics Library Routines," introduced the general concepts
 behind the different coordinate systems available to the Microsoft FORTRAN
 Graphics Library. Each system performs a particular task and uses
 coordinates appropriate to that function. Text coordinates work in rows and
 columns; physical coordinates serve as an absolute reference and as a
 starting place for creating custom window and viewport coordinates. As a
 convenience, the Microsoft FORTRAN Graphics Library supports several
 routines for converting to the different coordinate systems.


 9.3.1  Text Coordinates

 Before you can write a program to print a word on the screen, you need a
 system that describes to the compiler where you want to write the text. The
 text modes divide the text screen into rows and columns (see Figure 9.2).

 Keep in mind these text-mode conventions:

   þ  Numbering starts at 1. An 80-column screen contains columns 1-80.
   þ  The row is always listed before the column.

 If the screen supports a video text mode that displays 25 rows and 80
 columns (as in Figure 9.2), the rows are numbered 1-25 and the columns are
 numbered 1-80. The text-positioning routines, such as settextposition, use
 row and column coordinates.


 9.3.2  Graphics Coordinates

 As stated in Chapter 8, "Graphics Library Routines," there are three
 coordinate systems for describing the location of pixels on the screen:
 physical coordinates, viewport coordinates, and window coordinates. The
 coordinate systems identify pixel locations on the screen. They all apply
 the following conventions:

   þ  Numbering starts at 0, not 1. If there are 640 pixels, they are
      numbered 0-639.
   þ  The - x-coordinate is listed before the y-coordinate.

 Sections 9.3.2.1-9.3.2.3 describe each coordinate system in detail.


 9.3.2.1  Physical Coordinates

 Suppose you write a program that calls setvideomode and puts the screen into
 the VGA graphics mode $VRES16COLOR. This gives you a screen containing 640
 horizontal pixels and 480 vertical pixels. Each individual pixel is referred
 to by its location relative to the x-axis and y-axis, as shown in Figure
 9.3 below.

 The upper left corner is the "origin." The x- and y-coordinates for the
 origin are always (0, 0).

 Physical coordinates refer to each pixel directly and are therefore integers
 (that is, the screen cannot display a fractional pixel). If you use
 variables to refer to pixel locations, declare them as integers.

 When you first enter graphics mode, the "viewport" or drawing area is
 equivalent to the physical screen. The setvieworg routine changes the
 location of the viewport's origin. You pass it two integers, which represent
 the x and y physical screen coordinates for the new origin. For example, the
 following line moves the origin to the physical screen location (50, 100):

            CALL setvieworg(50, 100)

 The effect on the screen is illustrated in Figure 9.4 below.

 The number of pixels hasn't changed, but the coordinates used to refer to
 the points have changed. The x axis now ranges from -50 to +589 instead of 0
 to 639. The y axis now covers the values -100 to +379. If you use an adapter
 other than the VGA, the numbers are different but the effect is the same.

 All graphics routines that use viewport coordinates are affected by the new
 origin, including moveto, lineto, rectangle, ellipse, arc, and pie. For
 example, if you call the rectangle routine after relocating the viewport
 origin, and pass it the values (0, 0) and (40, 40), the upper left corner of
 the rectangle would appear 50 pixels from the left edge of the screen and
 100 pixels from the top. It would not appear in the upper left corner of the
 screen.

 The setcliprgn routine creates an invisible rectangular area on the screen
 called a "clipping region." You can draw inside the clipping region, but
 attempts to draw outside the region fail (that is, nothing appears outside
 the clipping region).

 The default clipping region occupies the entire screen. The graphics library
 ignores any attempts to draw outside the screen.

 Change the clipping region by calling setcliprgn. For example, suppose you
 entered the CGA graphics mode $MRES4COLOR, which has a screen resolution of
 320 by 200 pixels. If you draw a diagonal line from (0, 0) to (319, 199),
 the top left to the bottom right corner, the screen looks like Figure 9.5.

 You could create a clipping region by entering

            CALL setcliprgn(10, 10, 309, 189)

 With the clipping region in effect, the same lineto command would put the
 line shown in Figure 9.6 on the screen. The dashed lines indicate the outer
 bounds of the clipping region and do not actually print on the screen.


 9.3.2.2  Viewport Coordinates

 The viewport is the area on the screen where you actually draw. Viewport
 coordinates represent the pixels within the current viewport. The
 setviewport routine establishes a new viewport within the boundaries of the
 physical screen. A standard viewport has two distinguishing features:

   1. The origin of a viewport is in the upper left corner.

   2. The default clipping region matches the outer boundaries of the
      viewport.

 The setviewport routine has the same effect as the setvieworg and setcliprgn
 routines combined.

 In Figure 9.5 the clipping region allows a call to lineto to draw a line
 that touches the rectangle's opposite corners. Figure 9.6 uses the same
 lineto call, but a smaller clipping region keeps the line from touching the
 surrounding rectangle.

 Note that you must first establish a viewport if you wish to use window
 coordinates. Window coordinates use the current viewport as their
 boundaries. The sample program REALG.FOR in Section 9.3.3 shows how to
 create multiple window-coordinate sets, each in a separate viewport, on a
 single screen.


 9.3.2.3  Window Coordinates

 Functions that refer to coordinates on the physical screen and within the
 viewport require integer values. However, in many applications you may need
 to use floating-point valuesÄÄfor frequency, viscosity, mass, and so on. The
 setwindow routine allows you to scale the screen to almost any size. In
 addition, window-related functions accept double-precision values.

 A window "overlays" the current viewport; window graphics appear in the
 portion of screen defined as the current viewport.

 For example, you may want to graph 12 months of average Venusian
 temperatures that range from -50 to +450. You could do so by adding the
 following line to your program:

            dummy = setwindow(.TRUE., 1.0, -50.0, 12.0, 450.0)

 The first argument is the invert flag, which puts the lowest y value in the
 bottom left corner. The minimum x- and y- and maximum x- and y-coordinates
 follow; the decimal point marks them as floating-point values. The new
 organization of the screen is shown in Figure 9.7.

 Note that January and December plot on the left and right edges of the
 screen. In an application like this, it might be better to number the x-axis
 from 0.0 to 13.0, to provide some extra space.

 If you next plot a point with setpixel_w or draw a line with lineto_w, the
 values are automatically scaled to the established window.

 To use window coordinates with floating-point values, take the following
 steps:

   1. Enter a graphics mode with setvideomode.

   2. Use setviewport to create a viewport area. This step is optional if you
      plan to use the entire screen.

   3. Create a real-coordinate window with setwindow, passing a LOGICAL
      invert flag and four DOUBLE PRECISION x- and y-coordinates for the
      minimum and maximum values.

   4. Draw graphics shapes with rectangle_w and similar routines. Do not
      confuse rectangle (the viewport routine) with rectangle_w (the window
      routine for drawing rectangles). All window functions end with an
      underscore (_) and the letter "w."

 Real-coordinate graphics give you much flexibility. For example, you can fit
 either axis into a small range (such as 151.25-151.45) or into a large range
 (-50000.-+80000.), depending on the type of data you graph. In addition, by
 changing the window coordinates, you can create the effects of zooming in or
 panning across a figure.


 9.3.3  Real Coordinates Program (REALG)

 The program below illustrates some ways to use the real-coordinate windowing
 functions.

      CC  REALG.FOR - Illustrates real coordinate graphics.

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            LOGICAL  fourcolors
            EXTERNAL fourcolors

            IF( fourcolors() ) THEN
               CALL threegraphs()
            ELSE
              WRITE (*,*) ' This program requires a CGA, EGA, or',
           +              ' VGA graphics card.'
            END IF
            END

      C     Additional functions defined below
      .
      .
      .


 The main body of the program is very short. It calls the fourcolors
 function (defined below), which attempts to enter a graphics mode with at
 least four colors. If it succeeds, the threegraphs subroutine is called,
 which will draw three graphs. See the program output in Figure 9.8.

 Note that the gridshape subroutine (defined below) which draws the graphs
 uses the same data in each case. However, the program uses three different
 real-coordinate windows. The two viewports in the top half are the same size
 in physical coordinates, but they have different window sizes. Each window
 uses different maximum and minimum values. In all three cases, the grid is 2
 units wide. In the upper left corner, the window is 4 units wide; in the
 upper right, the window is 6 units wide, which makes the graph appear
 smaller.

 In two of the three graphs, one of the lines goes off the edge, outside the
 clipping region. The lines do not intrude into the other viewports, since
 defining a viewport creates a clipping region.

 Finally, note that the graph on the bottom inverts the data with respect to
 the two graphs above it.


 9.3.3.1  Checking the Adapter

 The first step in any graphics program is to enter a graphics mode. The
 function fourcolors  performs this step:

      CC  FOURCOLORS - Function to enter graphics mode for REALG.

            LOGICAL FUNCTION fourcolors()

            INCLUDE 'FGRAPH.FD'

            INTEGER*2            dummy
            RECORD /videoconfig/ screen
            COMMON               screen

      C
      C     Set to maximum number of available colors.
      C
            CALL getvideoconfig( screen )
            SELECT CASE( screen.adapter )
               CASE( $CGA, $OCGA )
                  dummy = setvideomode( $MRES4COLOR )
               CASE( $EGA, $OEGA )
                  dummy = setvideomode( $ERESCOLOR )
               CASE( $VGA, $OVGA )
                  dummy = setvideomode( $VRES16COLOR )
               CASE DEFAULT
                  dummy = 0
            END SELECT

            CALL getvideoconfig( screen )
            fourcolors = .TRUE.
            IF( dummy .EQ. 0 ) fourcolors = .FALSE.
            END

 The getvideoconfig routine transfers screen information into the videoconfig
 structure screen. Then the element screen.adapter in the SELECT CASE
 construct turns on the matching graphics mode. The modes containing the
 letter "O" are Olivetti modes.

 The function fourcolors returns .TRUE. if the computer supports color.
 Otherwise it returns .FALSE., which causes the program to skip the
 subroutine threegraphs.


 9.3.4  Drawing the Graphs

 If the fourcolors function returns .TRUE., the main program calls the
 subroutine below, which prints the three graphs.

      CC  THREEGRAPHS - Displays three graphs for REALG.

            SUBROUTINE threegraphs()

            INCLUDE  'FGRAPH.FD'

            INTEGER*2            dummy, halfx, halfy
            INTEGER*2            xwidth, yheight, cols, rows
            RECORD /videoconfig/ screen
            COMMON               screen

            CALL clearscreen( $GCLEARSCREEN )
            xwidth  = screen.numxpixels
            yheight = screen.numypixels
            cols    = screen.numtextcols
            rows    = screen.numtextrows
            halfx   = xwidth / 2
            halfy   = (yheight / rows) * ( rows / 2 )
      C
      C     First window
      C
            CALL setviewport( 0, 0, halfx - 1, halfy - 1 )
            CALL settextwindow( 1, 1, rows / 2, cols / 2 )
            dummy = setwindow( .FALSE., -2.0, -2.0, 2.0, 2.0 )
            CALL gridshape( INT2( rows / 2 ) )
            dummy = rectangle( $GBORDER, 0, 0, halfx-1, halfy-1 )
      C
      C     Second window
      C
            CALL setviewport( halfx, 0, xwidth - 1, halfy - 1 )
            CALL settextwindow( 1, (cols/2) + 1, rows/2, cols)
            dummy = setwindow( .FALSE., -3.0, -3.0, 3.0, 3.0 )
            CALL gridshape( INT2( rows / 2 ) )
            dummy = rectangle_w( $GBORDER, -3.0, -3.0, 3.0, 3.0 )   C
      C     Third window
      C
            CALL setviewport( 0, halfy, xwidth - 1, yheight - 1 )
            CALL settextwindow( (rows / 2 ) + 1, 1, rows, cols )
            dummy = setwindow( .TRUE., -3.0, -1.5, 1.5, 1.5 )
            CALL gridshape( INT2( (rows / 2) + MOD( rows, 2 ) ) )
            dummy = rectangle_w( $GBORDER, -3.0, -1.5, 1.5, 1.5 )

      READ (*,*)         ! Wait for ENTER to be pressed
            dummy = setvideomode( $DEFAULTMODE )
            END

 Although entering a graphics mode automatically clears the screen, it
 doesn't hurt to be sure, so threegraphs calls the clearscreen routine:

            CALL clearscreen( $GCLEARSCREEN )

 The $GCLEARSCREEN constant clears the entire physical screen. Other options
 include $GVIEWPORT and $GWINDOW, which clear the current viewport and the
 current text window, respectively.

 After assigning values to some variables, the threegraphs routine creates
 the first window:

            CALL setviewport( 0, 0, halfx - 1, halfy - 1 )
            CALL settextwindow( 1, 1, rows / 2, cols / 2 )
            dummy = setwindow( .FALSE., -2.0, -2.0, 2.0, 2.0 )

 The first line defines a viewport which covers the upper left quarter of the
 screen. Next, a text window is defined within the boundaries of that border.
 Note that the numbering starts at 1 and the row location precedes the
 column. Finally, the third line creates a window with both x and y values
 ranging from -2.0 to 2.0. The .FALSE. constant forces the y-axis to increase
 from top to bottom, which is the reverse of the "normal" convention.

 Next, the function gridshape inserts the grid and plots the data, and a
 border is added to the window:

            CALL gridshape( INT2( rows / 2 ) )
            dummy = rectangle( $GBORDER, 0, 0,
           +                   halfx - 1, halfy - 1 )

 Note that this is the standard rectangle routine, which takes coordinates
 relative to the viewport, not window coordinates.

 The two other windows are similar to the first. All three windows call the
 gridshape function (defined below), which draws a grid from location (-1.0,
 -1.0) to (1.0, 1.0). The grid appears in different sizes because the
 coordinates in the windows vary. The second window ranges from (-3.0, -3.0)
 to (3.0, 3.0), and the third from (-3.0,-1.5) to (1.5, 1.5), so the widths
 change accordingly.

 Also note that the third window contains a .TRUE. inversion argument. This
 causes the y-axis to increase from bottom to top, instead of top to bottom.
 As a result, this graph appears upside down with respect to the other two.

 After calling gridshape, the program frames each window, using a statement
 such as the following:

      dummy = rectangle_w( $GBORDER, -3.0, -1.5, 1.5, 1.5 )

 All three functions contain a fill flag as the first argument. The routine
 rectangle takes integer arguments that refer to the viewport coordinates.
 The rectangle_w routine takes four double-precision floating-point values
 referring to window coordinates: upper left x, upper left y, lower right x,
 and lower right y.

 The gridshape subroutine plots the data on the screen:

      CC  GRIDSHAPE - This subroutine plots data for the REALG
      CC              program.

            SUBROUTINE gridshape( numc )

            INCLUDE 'FGRAPH.FD'

            INTEGER*2            dummy, numc, i
            CHARACTER*2          str
            DOUBLE PRECISION     bananas(21), x
            RECORD /videoconfig/ screen
            RECORD /wxycoord/    wxy
            RECORD /rccoord/     curpos
            COMMON               screen
      C
      C     Data for the graph:
      C
            DATA bananas/ -0.3  , -0.2 , -0.224, -0.1, -0.5  ,
           +               0.21 ,  2.9 ,  0.3  ,  0.2,  0.0  ,
           +              -0.885, -1.1 , -0.3  , -0.2,  0.001,
           +               0.005,  0.14,  0.0  , -0.9, -0.13 ,
           +               0.31 /

      C
      C     Print colored words on the screen.
      C
            IF(screen.numcolors .LT. numc)
           +                numc = screen.numcolors-1
            DO i = 1, numc
               CALL settextposition( i, 2, curpos )
               dummy = settextcolor( i )
               WRITE ( str, '(I2)' ) i
               CALL outtext( 'Color ' // str )
            END DO
      C
      C     Draw a bordered rectangle around the graph.
      C

            dummy = setcolor( 1 )
            dummy = rectangle_w( $GBORDER, -1.00, -1.00, 1.00,
           +                                1.00 )
            dummy = rectangle_w( $GBORDER, -1.02, -1.02, 1.02,
           +                                1.02 )
      C
      C     Plot the points.
      C
            x = -0.90
            DO i = 1, 19
               dummy = setcolor( 2 )
               CALL    moveto_w( x, -1.0, wxy )
               dummy = lineto_w( x,  1.0 )
               CALL    moveto_w( -1.0, x, wxy )
               dummy = lineto_w(  1.0, x )
               dummy = setcolor( 14 )
               CALL    moveto_w( x - 0.1, bananas( i ), wxy )
               dummy = lineto_w( x, bananas( i + 1 ) )
               x     = x + 0.1
            END DO

            CALL    moveto_w( 0.9, bananas( i ), wxy )
            dummy = lineto_w( 1.0, bananas( i + 1 ) )
            dummy = setcolor( 3 )
            END

 First, the number of available color indexes is assigned to the rows
 variable and a DO loop displays all of the available colors:

      IF(screen.numcolors .LT. numc) numc = screen.numcolors - 1
            DO i = 1, numc
               CALL settextposition( i, 2, curpos )
               dummy = settextcolor( i )
               WRITE ( str, '(I2)' ) i
               CALL outtext( 'Color ' // str )
            END DO

 The names of the functions are self-explanatory. Using outtext in graphics
 mode  allows you to control the text color and position (unlike WRITE).

 The routine names that end with "_w" work the same as their viewport
 equivalents, except that you pass double-precision floating-point values
 instead of integers. For example, you pass integers to lineto, but double
 precision values to lineto_w.

 Once you create various graphic elements, you may wish to use the
 font-oriented routines to polish the appearance of titles, headings,
 comments, or labels. The next chapter describes in more detail how to print
 text in the various fonts supplied with the Microsoft FORTRAN Graphics
 Library.


 Chapter 10  Using Fonts from the Graphics Library
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The Microsoft FORTRAN Graphics Library includes DOS routines that print text
 in various sizes and type styles. These routines provide much more control
 over the appearance of your text than the WRITE statement; they add visual
 interest to your screen displays.

 A "font" is a set of stylized text characters of a particular size. This
 chapter assumes you have read Chapter 9, "Drawing Graphics Elements," and
 that you understand the general terminology it introduces. You should also
 be familiar with the basic properties of both the setvideomode and moveto
 routines.

 The first section of this chapter reviews the fonts provided with the
 FORTRAN Graphics Library and some of the associated terminology. The next
 two sections cover the actual process of registering, setting, and using
 fonts in the graphics environment. The last section describes problems
 commonly encountered in using fonts and suggests solutions.


 10.1  Available Typefaces

 The Microsoft FORTRAN Graphics Library includes six typefaces. A "typeface"
 (or "type style") refers to the style of the displayed textÄÄCourier, for
 example, or Times Roman. The list below shows the typefaces available with
 Microsoft FORTRAN.

 "Type size" measures the screen area occupied by individual characters. The
 term comes from the printer's lexicon, but uses screen pixels as the unit of
 measure rather than the traditional "points." For example, "Courier 129"
 denotes the Courier typeface, with each character occupying a screen area of
 12 vertical pixels by 9 horizontal pixels. The word "font," therefore,
 implies both a typeface and a type size.

 The graphic library's font routines use two methods to create fonts. The
 first technique generates Courier, Helv, and Tms Rmn fonts through a
 "bit-mapping" (or "raster-mapping") technique. Bit-mapping describes each
 character in a binary data "map." Each bit in the map corresponds to a
 screen pixel. If the bit equals 1, its associated pixel is set to the
 current screen color. Bit values of 0 appear in the current background
 color. Video adapters use this same technique to display text in graphics
 mode.

 The three remaining type stylesÄÄModern, Script, and RomanÄÄuse a
 "vector-mapping" technique to create the fonts. Vector-mapping represents
 each of the characters as lines and arcs. In a literal sense, the computer
 draws each vector-mapped character on the screen. In contrast, bit-mapping
 resembles stenciling or placing a picture of each character on the screen.

 Each method has advantages and disadvantages. Bit-mapped characters appear
 smoother because of the predetermined pixel-mapping. However, they cannot be
 scaled. You can scale vector-mapped text to any size, but the characters
 tend to lack the solid appearance of the bit-mapped characters.

 Table 10.1 lists available sizes for each font. Notice that the bit-mapped
 fonts come in preset sizes measured in pixels. The exact size of any font
 character depends on screen resolution and display type.

 Table 10.1  Typefaces and Type Sizes in the FORTRAN Graphics Library

 Typeface      Mapping             Size (in pixels)     Spacing

 Courier       Bit                 108, 129, 1512       Fixed
 Helv          Bit                 105, 127, 158        Proportional
                                   189, 2212, 2816
 Tms Rmn       Bit                 105, 126, 158        Proportional
                                   169, 2012, 2616
 Modern        Vector              Scaled               Proportional
 Script        Vector              Scaled               Proportional
 Roman         Vector              Scaled               Proportional


 A.2  Using Microsoft FORTRAN Fonts

 Disk files with the .FON extension contain the data for both bit-mapped and
 vector-mapped fonts. The base names of the files indicate their content. For
 example, the files MODERN.FON, SCRIPT.FON, and ROMAN.FON hold data for the
 three vector-mapped fonts.

 FORTRAN's .FON files are identical to the .FNT files used in the Microsoft
 Windows operating environment. If you run Windows, you can use any of its
 .FNT files with FORTRAN's font functions. (You can also purchase Windows
 .FNT files separately.) In addition, several vendors offer software that
 create or modify .FNT files, allowing you to design your own fonts.

 To use the fonts in your program, you should

   1. Register the fonts
   2. Set a current font from the register
   3. Display text using the current font

 The following sections describe each of the steps in detail. See Section
 10.3 for an example program that demonstrates these steps.


 10.2.1  Registering Fonts

 You must first organize the fonts you want to use into a list in memory, a
 process called "registering." The register list gives the computer
 information about the available .FON files. Register the fonts by calling
 registerfonts. The routine reads header information from specified .FON
 files and builds a list of file information, but the routine does not read
 mapping data from the files.

 The FGRAPH.FD file declares the registerfonts routine as

            INTERFACE TO FUNCTION registerfonts(filename)
            INTEGER*2 registerfonts
           + [FAR, C, ALIAS:"__f_registerfonts"]
            CHARACTER*(*) filename[FAR, REFERENCE]
            END

 The argument refers to the file name of the desired .FON file. The name can
 include wild cards, which allow you to register several fonts with one call
 to the registerfonts routine.

 If the computer successfully reads one or more .FON files, registerfonts
 returns the number of fonts registered. If the function fails, it returns a
 negative error code. (Refer to Appendix D of the Microsoft FORTRAN Reference
 for a description of the error codes.)


 10.2.2  Setting the Font and Displaying Text

 Before the computer can display text in a particular font, it must know
 which of the fonts you want to use. After you register the fonts you expect
 to use, setfont makes one of the registered fonts the current (or "active")
 font. It then reads the mapping data from the appropriate .FON file into
 memory.

 The FGRAPH.FD include file declares the setfont routine as

            INTERFACE TO FUNCTION setfont(options)
            INTEGER*2 setfont[FAR, C, ALIAS:"__f_setfont"]
            CHARACTER*(*) options[FAR, REFERENCE]
            END

 The function's argument consists of letter codes that describe the desired
 font.

      Option                           Meaning

      t'fontname'                      Typeface of the font in single
                                       quotes. The fontname may be one
                                       of the following:

                                       courier          modern
                                       helv             script tms
                                       rmn              roman

                                       Notice the space in the name "tms
                                       rmn." Additional font files use
                                       other names for fontname. Refer
                                       to the vendor's documentation for
                                       these names.

      hy                               Character height, where y is the
                                       height in pixels.

      wx                               Character width, where x is the
                                       width in pixels.

      f                                Select only a fixed-spaced font.

      p                                Select only a proportional-spaced
                                       font.

      v                                Select only a vector-mapped font.

      r                                Select only a bit-mapped (or
                                       "raster-mapped") font.

      b                                Select the best fit from the
                                       registered fonts. The b option
                                       instructs setfont to accept the
                                       closest-fitting font if a font of
                                       the specified size is not
                                       registered.

                                       If at least one font is
                                       registered, the b option always
                                       sets a current font. If you do
                                       not specify the b option and an
                                       exact matching font is not
                                       registered, setfont will fail. In
                                       this case, any existing current
                                       font remains current.

                                       The best fit is based on four
                                       criteria: pixel height, typeface,
                                       pixel width, and spacing (fixed
                                       or proportional). If you request
                                       a vector-mapped font, setfont
                                       sizes the font to correspond with
                                       the specified pixel height and
                                       width; a bit-mapped font, setfont
                                       chooses the closest available
                                       size. If the requested type size
                                       fits exactly between two
                                       registered fonts, the smaller
                                       size takes precedence.

      nx                               Select font number x, where x is
                                       less than or equal to the value
                                       returned by registerfonts. For
                                       example, the option n3 makes the
                                       third registered font the current
                                       font (provided three or more
                                       fonts are registered).

 You can list the option codes in any order and in upper- or lowercase. The
 options may be separated with spaces.

 The setfont routine updates a data area with specifics about the current
 font. The data area stores the information in a structure defined by the
 include file FGRAPH.FD to have the following form:

      STRUCTURE/fontinfo/
         INTEGER       type        ! Set = vector,clear = bit map
         INTEGER       ascent      ! Pix dist from top to base
         INTEGER       pixwidth    ! Character width in pixels
         INTEGER       pixheight   ! Character height in pixels
         INTEGER       avgwidth    ! Average character width
         CHARACTER*81  filename    ! Filename including path
         CHARACTER*32  facename    ! Font name
      END STRUCTURE

 If you want the parameters of the current font, call the getfontinfo
 routine. Once you register the fonts you want and make one the active font,
 follow these two steps to display the text on the screen:

   1. Select a starting position for the text with the moveto routine.

   2. Send the text to the screen at that position (in the current font) with
      outgtext.

 The moveto routine takes pixel coordinates as arguments. Those coordinates
 are for the upper left corner of the cell of the first character in the
 string. The following section describes a sample program included with
 Microsoft FORTRAN that displays the provided fonts.


 10.3  Example Program (SAMPLER)

 SAMPLER displays sample text in the available fonts, returning to DOS when
 you press a key. First, make sure the .FON files are in the current
 directory.

      CC  SAMPLER.FOR - Displays sample text in various fonts.
      CC

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2            NFONTS
            INTEGER*2            dummy, i, iend
            CHARACTER*30         list
            CHARACTER*64         fontpath
            RECORD /videoconfig/ vc
            RECORD /xycoord/     xy

      PARAMETER ( NFONTS   = 6 )

            CHARACTER* ( 7 )  text ( 2 * NFONTS )/
           +                   "COURIER",       "courier" ,
           +                   "HELV"   ,       "helv"    ,
           +                   "TMS RMN",       "tms rmn" ,
           +                   "MODERN" ,       "modern"  ,
           +                   "SCRIPT" ,       "script"  ,
           +                   "ROMAN"  ,       "roman"   /

            CHARACTER*( 10 ) option (NFONTS) /
           +                   "t'courier'",    "t'helv'"  ,
           +                   "t'tms rmn'",    "t'modern'",
           +                   "t'script'" ,    "t'roman'" /
      C
      C     Locate .FON files and register fonts by reading header
      C     information from all files.
      C
            IF( registerfonts( '*.FON' ). LT. 0 ) THEN
           +  WRITE (*, '(A/)') ' Enter directory for .FON files:'
               READ  (*, '(A )')  fontpath
               iend = INDEX( fontpath, ' ' )
               fontpath(iend:iend + 5) = '\*.FON'
               IF( registerfonts( fontpath ). LT. 0 )
           +       STOP 'Error:  cannot find font files'
            ENDIF

      C
      C     Find graphics mode.
      C
            IF( setvideomode( $MAXRESMODE ) .EQ. 0 )
           +    STOP 'Error:  cannot set graphics mode'

      C
      C     Copy video configuration into structure vc.
      C
            CALL getvideoconfig( vc )

      C
      C     Display six lines of sample text.
      C
            DO i = 1, NFONTS
               list = option(i) // 'h30w24b'
               IF( setfont( list ) .EQ. 0 ) THEN
                  dummy = setcolor( INT4( i ) )
                  CALL moveto( 0, (i - 1) *
           +                vc.numypixels / NFONTS, xy )
                  CALL outgtext( text((i * 2) - 1) )

      CALL moveto( vc.numxpixels/2,
           +                (i - 1) * vc.numypixels / NFONTS, xy )
                  CALL outgtext( text(i * 2) )
            ELSE
                  dummy = setvideomode( $DEFAULTMODE )
                  STOP 'Error:  cannot set font'
               END IF
            END DO

            READ (*,*)
            dummy = setvideomode( $DEFAULTMODE )
      C
      C     Return memory when finished with fonts.
      C
            CALL unregisterfonts()
            END

 Notice that SAMPLER calls the setfont routine to specify the typeface and
 best fit for a character height of 30 pixels and a width of 24 pixels. Then
 the graphics routine moveto establishes the starting point for each text
 string. When the program finds the font, it sends a message to the screen.


 10.4  Using Fonts Effectively

 Printing with fonts is straightforward, and using fonts requires little
 programming effort. However, keep the following in mind when working with
 fonts:

   þ  The video mode should be set only once. If you create an image and want
      to incorporate font-based text into it, do not reset the video mode
      prior to calling the font routines. Doing so will clear the screen,
      which destroys the original image.

   þ  The setfont function reads specified .FON files to obtain mapping data
      for the current font. Each call to setfont overwrites the old font data
      in memory. If you want to show text of different styles on the same
      screen, display all of the text of one font before moving on to the
      others. Minimizing the number of calls to setfont reduces the time
      spent in disk I/O and memory reloads.

   þ  When your program finishes with the fonts library, you might wish to
      free the memory occupied by the register list. Call the subroutine
      unregisterfonts to do this. As its name implies, this subroutine frees
      the memory allocated by registerfonts. The register information for
      each type size of each font takes up approximately 140 bytes of memory,
      so the amount of memory returned by unregisterfonts is significant only
      if you have many fonts registered.

 For additional syntax and usage information on each of the font routines,
 read the appropriate material on individual routines in the following
 chapter,  "Graphics Library Reference."


 Chapter 11  Graphics Library Reference
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This chapter describes each routine in the Microsoft FORTRAN DOS graphics
 library, GRAPHICS.LIB, in alphabetical order. The text-based routines are
 also supported under OS/2 by the library GRTEXTP.LIB, as noted in the text
 and in Chapter 8. In some cases, similar or related routines are clustered
 in the same description, with differences noted where appropriate.

 The name of each routine appears at the top of the page. The Interface
 section shows the complete interface needed in order to use the routine,
 although you will find it more convenient to reference the include file
 FGRAPH.FDÄÄwhich contains the same declarationsÄÄthan to recreate each
 interface yourself. The Description section outlines what the routine does,
 along with any pertinent additional information. The Return Value section
 describes any values returned by the routine. Such values can help in
 testing for errors before you use the results of the routine call. The See
 Also section lists similar or related routines. Finally, the Example section
 provides a typical application of the routine, or refers you to a routine
 with the same or similar application. (This section does not appear with
 every reference entry.)


 arc, arc_w

 þ  Interface

      INTERFACE TO FUNCTION arc(x1, y1, x2, y2, x3, y3, x4, y4)
      INTEGER*2 arc[FAR,
      C, ALIAS:"__arc"] INTEGER*2 x1, y1      ! Upper left corner of bounding
      INTEGER*2 x2, y2                        ! Lower right corner of bounding
      INTEGER*2 x3, y3                        ! Start vector
      INTEGER*2 x4, y4                        ! End vector
      END

      INTERFACE TO FUNCTION arc_w(wx1, wy1, wx2, wy2, wx3, wy3, wx4, wy4)
      INTEGER*2 arc_w[FAR, C, ALIAS:"__f_arc_w"]
      DOUBLE PRECISION wx1, wy1               ! Upper left corner of bounding
      DOUBLE PRECISION wx2, wy2               ! Lower right corner of bounding
      DOUBLE PRECISION wx3, wy3               ! Start vector
      DOUBLE PRECISION wx4, wy4               ! End vector
      END

 þ  Description

 The arc routines draw elliptical arcs.

 The arc routine uses the viewport-coordinate system. The center of the arc
 is the center of the bounding rectangle defined by the points (x1, y1) and
 (x2, y2). The arc starts at the point where it intersects the vector defined
 by (x3, y3) and ends where it intersects the vector defined by (x4, y4).

 The arc_w routine uses the window-coordinate system. The center of the arc
 is the center of the bounding rectangle defined by the window-coordinate
 pairs (wx1, wy1) and (wx2, wy2). The arc starts at the point where it
 intersects the vector defined by (wx3, wy3)  and ends where it intersects
 the vector defined by (wx4, wy4).

 In both cases, the arc is drawn using the current color, moving
 counterclockwise. Since an arc does not define a closed area, it is not
 filled.

 þ  Return value

 The arc and arc_w routines return a nonzero value if the arc is successfully
 drawn; otherwise, they return 0.

 þ  See also

      ellipse, lineto, pie, rectangle, setcolor

 þ  Example

            INCLUDE   'FGRAPH.FI'
            INCLUDE   'FGRAPH.FD'
            INTEGER*2 dummy

            dummy = setvideomode( $MRES16COLOR )
            dummy = arc( 80, 50, 240, 150, 0, 50, 240, 150 )
            READ (*,*)          ! Press ENTER to exit
            dummy = setvideomode( $DEFAULTMODE )
            END

 This program draws the arc shown in Figure R.1.

 See also the example program for setpixel.


 clearscreen

 þ  Interface

      INTERFACE TO SUBROUTINE
    + clearscreen[FAR, C, ALIAS:"__clearscreen"](area)
      INTEGER*2 area                   ! Target area
      END

 þ  Description

 The clearscreen subroutine erases the target area, filling it with the
 current background color. The area parameter can be one of the following
 symbolic constants (defined in FGRAPH.FD):

      Constant                     Action

      $GCLEARSCREEN               Clears and fills the entire
                                  screen

      $GVIEWPORT                  Clears and fills only within the
                                  current viewport

      $GWINDOW                    Clears and fills only within the
                                  current text window

 This subroutine is also available under OS/2.

 þ  Return value

 There is no return value.

 þ  See also

      getbkcolor, setbkcolor

 þ  Example

 See the example program for gettextcolor and setvieworg.


 displaycursor

 þ  Interface

      INTERFACE TO FUNCTION displaycursor(toggle)
      INTEGER*2 displaycursor[FAR, C, ALIAS:"__displaycursor"]
      INTEGER*2 toggle               ! Cursor state
      END

 þ  Description

 On entry into each graphics routine, the screen cursor is turned off. The
 displaycursor function determines whether the cursor will be turned back on
 when programs exit graphic routines. If toggle is set to $GCURSORON, the
 cursor will be restored on exit. If toggle is set to $GCURSOROFF, the cursor
 will be left off.

 This function is also available under OS/2.

 þ  Return value

 Returns the previous value of toggle.

 þ  See also

      gettextcursor, settextcursor

 þ  Example

 See the example program for settextcursor.


 ellipse, ellipse_w

 þ  Interface

      INTERFACE TO FUNCTION ellipse(control, x1, y1, x2, y2)
      INTEGER*2 ellipse[FAR, C, ALIAS:"__ellipse"]
      INTEGER*2 control                  ! Fill flag
      INTEGER*2 x1, y1                   ! Upper left corner of bounding recta
      INTEGER*2 x2, y2                   ! Lower right corner of bounding rect
      END

      INTERFACE TO FUNCTION ellipse_w(control, wx1, wy1, wx2, wy2)
      INTEGER*2 ellipse_w[FAR, C, ALIAS:"__f_ellipse_w"]
      INTEGER*2 control                  ! Fill flag
      DOUBLE PRECISION wx1, wy1          ! Upper left corner of bounding recta
      DOUBLE PRECISION wx2, wy2          ! Lower right corner of bounding rect
      END

 þ  Description

 The ellipse functions draw an ellipse. The border is drawn in the current
 color. In the ellipse function, the center of the ellipse is the center of
 the bounding rectangle defined by the viewport-coordinate points (x1, y1)
 and (x2, y2).

 In the ellipse_w function, the center of the ellipse is the center of the
 bounding rectangle defined by the window-coordinate points (wx1, wy1) and
 (wx2, wy2).

 If the bounding-rectangle arguments define a point or a vertical or
 horizontal line, no figure is drawn.

 The control argument can be one of the following symbolic constants:

       Constant                    Action

       $GFILLINTERIOR              Fills the ellipse using the
                                   current fill mask

       $GBORDER                    Does not fill the ellipse

 The control option given by $GFILLINTERIOR is equivalent to a subsequent
 call to the floodfill function using the center of the ellipse as the start
 point and the current color (set by setcolor) as the boundary color.

 þ  Return value

 The ellipse and ellipse_w functions return a nonzero value if the ellipse is
 drawn successfully; otherwise, they return 0.

 þ  See also

      arc, floodfill, lineto, pie, rectangle, setcolor, setfillmask

 þ  Example

            INCLUDE   'FGRAPH.FI'
            INCLUDE   'FGRAPH.FD'
            INTEGER*2 dummy

            dummy = setvideomode( $MRES16COLOR )
            dummy = ellipse( $GFILLINTERIOR, 80, 50, 240, 150 )
            READ (*,*)          ! Press ENTER to exit
            dummy = setvideomode( $DEFAULTMODE )
            END

 This program draws the shape shown in Figure R.2.

 See also the example program for setpixel and setvieworg.


 floodfill, floodfill_w

 þ  Interface

      INTERFACE TO FUNCTION floodfill(x, y, boundary)
      INTEGER*2 floodfill[FAR, C, ALIAS:"__floodfill"]
      INTEGER*2 x, y            ! Start point
      INTEGER*2 boundary        ! Fills boundary color
      END

      INTERFACE TO FUNCTION floodfill_w(wx, wy, boundary)
      INTEGER*2 floodfill_w[FAR, C, ALIAS:"__f_floodfill_w"]
      DOUBLE PRECISION wx, wy    ! Start point
      INTEGER*2 boundary         ! Fills boundary color
      END

 þ  Description

 These functions fill an area using the current color and fill mask.

 The floodfill function begins filling at the viewport-coordinate point (x,
 y).

 The floodfill_w function begins filling at the window-coordinate point (wx,
 wy).

 If this point lies inside a figure, the interior is filled; if it lies
 outside a figure, the background is filled. The point must be inside or
 outside the figure to be filled, not on the figure boundary itself. Filling
 occurs in all directions, stopping at the color of boundary.

 þ  Return value

 The floodfill and floodfill_w functions return a nonzero value if the fill
 is successful. They return 0 if the fill could not be completed; if the
 start point lies on the boundary color; or if the start point lies outside
 the clipping region.

 þ  See also

      ellipse, getcolor, getfillmask, pie, setcliprgn, setcolor, setfillmask

 þ  Example

 See the example program for setlinestyle.


 getactivepage

 þ  Interface

      INTERFACE TO FUNCTION getactivepage()
      INTEGER*2 getactivepage[FAR, C, ALIAS:"__getactivepage"]
      END

 þ  Description

 The getactivepage function returns the current active page number.

 þ  Return value

 Returns the page number of the current active page. All hardware
 combinations support at least one page (page number 0).

 This function is also available under OS/2.

 þ  See also

      getactivepage, gettextcolor, gettextposition, getvisualpage,
      outtext, settextcolor,  settextposition, settextwindow, setvideomode,
      setvisualpage, wrapon

 þ  Example

      CC    PAGE.FOR - Illustrates animation effects by changing the video pag
      CC             The video page functions demonstrated include:
      CC             getactivepage  getvisualpage  setactivepage  setvisualpag

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2        dummy, oldapage, oldvpage
            INTEGER*2        i, j, k, l
            CHARACTER*3      jumper(4,3)
            RECORD /rccoord/ curpos

            DATA jumper / '\o/', '_o_', ' o ', ' o ',
           +              ' O ', ' O ', '/O\', ' O ',
           +              '/ \', '( )', '/ \', '( )' /

            oldapage  = getactivepage()
            oldvpage  = getvisualpage()
            IF( setvideomode( $TEXTBW40 ) .EQ. 0 )
           +    STOP 'Error:  cannot set 40-column mode'
            dummy     = displaycursor( $GCURSOROFF )

      C
      C     Draw 24 images on each page.
      C
            DO i = 1, 4
               dummy = setactivepage( i - 1 )
               DO j = 1, 22, 7
                  DO k = 1, 36, 7
                     DO l = 0, 2

      CALL settextposition( j + l, k, curpos )
                        CALL outtext( jumper(i,l + 1) )
                     END DO
                  END DO
               END DO
            END DO

      C
      C     Cycle 10 times through pages 0-3.
      C
            DO i = 1, 10
               DO j = 0, 3
                  dummy = setvisualpage( j )
                  CALL delay( INT2( 10 ) )
               END DO
            END DO

      C
      C     Restore original page.
      C
            dummy = setvideomode( $DEFAULTMODE )
            dummy = setactivepage( oldapage )
            dummy = setvisualpage( oldvpage )
            END


      CC  DELAY - Pauses for a specified number of 1/100 seconds.
      CC
      CC  Params:  Wait - pause time in 0.01 seconds

            SUBROUTINE delay( wait )
            INTEGER*2 wait, tick0, tick1, tick, kount, dummy

            kount = 0
            CALL GETTIM( dummy, dummy, dummy, tick0 )
            DO WHILE( kount .LT. wait )
               CALL GETTIM( dummy, dummy, dummy, tick1 )
               tick = tick1 - tick0
               IF( tick .LT. 0 ) tick = tick + 100
               tick0 = tick1
               kount = kount + tick
            END DO
            RETURN
            END

 This program fills the screen with animated figures.


 getbkcolor

 þ  Interface

      INTERFACE TO FUNCTION getbkcolor()
      INTEGER*4 getbkcolor[FAR, C, ALIAS:"__getbkcolor"]
      END

 þ  Description

 The getbkcolor function retrieves the pixel value of the current background
 color. The default is 0.

 In a color text mode (such as $TEXTC80), setbkcolor accepts (and getbkcolor
 returns) a color index. For example, setbkcolor(2) sets the background color
 to pixel value 2. The actual color displayed depends on the palette mapping
 for color index 2. The default is green in a color text-mode.

 In a color graphics mode (such as $ERESCOLOR), setbkcolor accepts (and
 getbkcolor returns) a color value (as used in remappalette). The value for
 the background color is given by the symbolic constants defined in the
 FGRAPH.FD include file. For example, setbkcolor($GREEN) sets the background
 color in a graphics mode to green. These symbolic constants are provided as
 a convenience in defining and manipulating the most common colors. The
 actual range of colors is, in general, much greater.

 The function setcolor accepts an INTEGER*2 value as an argument. It is a
 color index.

 Whenever an argument is INTEGER*4, it refers to a color value, and whenever
 it is INTEGER*2, it refers to a color index. The two exceptions are the
 functions setbkcolor and getbkcolor, described above. A more complete
 discussion of colors is given in the reference page for the remappalette
 function.

 This function is also available under OS/2.

 þ  Return value

 Returns the current background color value.

 þ  See also

      remappalette, setbkcolor

 þ  Example

 See the example program for gettextcolor.


 getcolor

 þ  Interface

      INTERFACE TO FUNCTION getcolor()
      INTEGER*2 getcolor[FAR, C, ALIAS:"__getcolor"]
      END

 þ  Description

 The getcolor function returns the current color index. The default is the
 highest legal value of the current palette.

 þ  Return value

 Returns the current color index.

 þ  See also

      setcolor

 þ  Example

             PROGRAM colors

             INCLUDE 'FGRAPH.FI'
             INCLUDE 'FGRAPH.FD'

             INTEGER*2 loop, loop1, dummy2       REAL rnd1, rnd2

             dummy2 = setvideomode( $MRES16COLOR )
             DO loop1 = 1, 20
                dummy2 = setcolor
           +             ( MOD( getcolor()+1, 16 ) ) ! Set next color
                DO loop = 1, 3200
      C
      C            Set a random pixel, normalized to be on the screen C

                   CALL RANDOM( rnd1 )
                   CALL RANDOM( rnd2 )
                   dummy2 = setpixel( INT2( rnd1*320 ),
           +                          INT2( rnd2*200 ) )
                END DO
             END DO

             dummy2 = setvideomode( $DEFAULTMODE )
             END

 This program assigns different colors to randomly selected pixels.


 getcurrentposition, getcurrentposition_w

 þ  Interface

      INTERFACE TO SUBROUTINE
    + getcurrentposition[FAR, C, ALIAS:"__f_getcurrentposition"](s)
      STRUCTURE/xycoord/
              INTEGER*2 xcoord                    ! x-coordinate
              INTEGER*2 ycoord                    ! y-coordinate
      END STRUCTURE
      RECORD/xycoord/s [FAR, REFERENCE]           ! Returns with current posit
      END

      INTERFACE TO SUBROUTINE
    + getcurrentposition_w[FAR, C, ALIAS:"__f_getcurpos_w"](s)
      STRUCTURE/wxycoord/
              DOUBLE PRECISION wx                 ! x-window coordinate
              DOUBLE PRECISION wy                 ! y-window coordinate
      END STRUCTURE
      RECORD/wxycoord/s [FAR, REFERENCE]          ! Returns with current posit
      END

 þ  Description

 The getcurrentposition functions return the coordinates of the current
 graphics output position in s. The getcurrentposition function returns the
 position as an xycoord structure, defined in FGRAPH.FD. The
 getcurrentposition_w function returns the position as the wxycoord
 structure, defined in FGRAPH.FD.

 The current position can be changed by the arc, lineto, and moveto
 functions.

 Only graphics output starts at the current position; it does not affect text
 output, which begins at the current text position, a separate concept (see
 settextposition).

 þ  Return value

 There is no return value.

 þ  See also

      moveto

 þ  Example

            PROGRAM Movept

            INCLUDE 'FGRAPH.FI'
            INCLUDE 'FGRAPH.FD'

            RECORD/xycoord/ position

            CHARACTER*255 buffer
            INTEGER*2 dummy2
            REAL rnd1, rnd2

            dummy2 = setvideomode( $MRES16COLOR )
            CALL RANDOM( rnd1 )
            CALL RANDOM( rnd2 )
            CALL moveto( INT2( rnd1*160+80 ),
           + INT2( rnd2*100+50 ), position )
            CALL getcurrentposition( position )
            WRITE( buffer,"( ' x=',I3,', y=',I3,\ )" ) position.xcoord,
           +                                           position.ycoord
            CALL settextposition( position.xcoord/8,   position.ycoord/8,
           +                      position )
            CALL outtext( buffer( 1:LEN_TRIM(buffer ) ) )

            READ(*, *)          ! Wait for ENTER to be pressed
            dummy2 = setvideomode( $DEFAULTMODE )
            END

 This program moves the current graphics output position to a random point,
 calls getcurrentposition to obtain the coordinates, and then writes the
 coordinates to a buffer. It then sets the current text position to those
 coordinates and outputs the coordinates to the screen, beginning at the
 current position.


 getfillmask


 þ  Interface

      INTERFACE TO SUBROUTINE
    + getfillmask[FAR, C, ALIAS:"__getfillmask"](mask)
      INTEGER*1 mask [FAR, REFERENCE](8)
      END

 þ  Description

 Some graphics routines (floodfill, pie, ellipse, and rectangle) can fill
 part or all of the screen with the current color or background color. The
 filling can be controlled with the current fill mask.

 The getfillmask subroutine returns the current fill mask. The mask is an
 8-by-8-bit array, where each bit represents a pixel. A 1 bit sets the
 corresponding pixel to the current color, while a 0 bit leaves the pixel
 unchanged. The mask is repeated over the entire fill area. If no fill mask
 is set, or mask is all zeros, only the current color is used in fill
 operations. If no mask is present, the subroutine sets mask to all zeros.

 þ  Return value

 There is no return value.

 þ  See also

      floodfill, ellipse, pie, rectangle, setfillmask

 þ  Example

            PROGRAM Fill

            INCLUDE 'FGRAPH.FI'
            INCLUDE 'FGRAPH.FD'

            INTEGER*1 style( 8,6 )/  #78,#30,#30,#78,#30,#30,#78,#30,
           +                         #78,#32,#30,#78,#30,#38,#78,#32,
           +                         #78,#39,#38,#78,#63,#36,#78,#33,
           +                         #78,#65,#36,#78,#33,#38,#78,#62,
           +                         #78,#66,#63,#78,#65,#65,#78,#37,
           +                         #78,#66,#65,#78,#66,#65,#78,#66 /

            INTEGER*1 oldstyle( 8 ) ! Placeholder for old style
            INTEGER loop

            dummy2 = setvideomode( $MRES4COLOR )
            CALL getfillmask( oldstyle )
      C
      C     Draw an ellipse under the middle few rectangles in a
      C     different color.
      C

            dummy2 = setcolor( 2 )
            dummy2 = ellipse( $GFILLINTERIOR, 120, 75, 200, 125 )
            dummy2 = setcolor( 3 )
            DO loop = 1,6
      C
      C     Make six rectangles.
      C
            CALL setfillmask( style( 1,loop ) )
            dummy2 = rectangle( $GFILLINTERIOR, loop*40+5, 90,
           +                  ( loop+1 )*40, 110 )
            END DO

            CALL setfillmask( oldstyle )   ! Restore old style
            READ (*,*)                     ! Wait for ENTER to be pressed
            dummy2 = setvideomode( $DEFAULTMODE )
            END

 This program draws an ellipse overlaid with six rectangles, each with a
 different fill mask.


 getfontinfo

 þ  Interface

      INTERFACE TO FUNCTION getfontinfo(font)
      INTEGER*2 getfontinfo
    + [FAR, C, ALIAS:"__f_getfontinfo"]
      STRUCTURE/fontinfo/
      INTEGER*2 type             ! Set=vector, clear=bit map
      INTEGER*2 ascent           ! Pixel distance from top to baseline
      INTEGER*2 pixwidth         ! Character width in pixels, 0=prop
      INTEGER*2 pixheight        ! Character height in pixels
      INTEGER*2 avegwidth        ! Average width in pixels
      CHARACTER*81 filename      ! File name including path
      CHARACTER*32 facename      ! Font name
      END STRUCTURE
      RECORD/fontinfo/font [FAR, REFERENCE]
      END

 þ  Description

 The getfontinfo function sets the current font characteristics in a fontinfo
 structure, defined in FGRAPH.FD.

 The fontinfo structure contains the following elements:

      Element                     Contents

      type                        Specifies vector (1) or bit-map
                                  (0) font

      ascent                      Specifies pixel distance from top
                                  to baseline

      pixwidth                    Specifies the character width in
                                  pixels; a 0 indicates a
                                  proportional font

      pixheight                   Specifies the character height in
                                  pixels

      avgwidth                    Specifies the average character
                                  width in pixels

      filename                    Specifies the file name including
                                  the path

      facename                    Specifies the font name


 þ  Return value

 Returns zero if font is returned successfully.

 þ  See also

      getgtextextent, outgtext, registerfonts, setfont, unregisterfonts


 getgtextextent

 þ  Interface

      INTERFACE TO FUNCTION getgtextextent(text)
      INTEGER*2 getgtextextent
    + [FAR, C, ALIAS:"__f_getgtextextent"]
      CHARACTER *(*)text [FAR, REFERENCE]        ! Text to be analyzed
      END

 þ  Description

 The getgtextextent function returns the width in pixels that would be
 required to print the text string, including any trailing blanks, with the
 current font using outgtext.

 This function is particularly useful for determining the size of text that
 uses proportional-spaced fonts.

 þ  Return value

 Returns the width of a text string in pixels.

 þ  See also

      getfontinfo, outgtext, registerfonts, setfont, unregisterfonts

 þ  Example

 See the example program for registerfonts.


 getimage, getimage_w

 þ  Interface

      INTERFACE TO SUBROUTINE
    + getimage[FAR, C, ALIAS:"__getimage"](x1, y1, x2, y2, image)
      INTEGER*2 x1, y1              ! Upper left corner of bounding rectangle
      INTEGER*2 x2, y2              ! Lower right corner of bounding rectangle
      INTEGER*1 image [FAR, REFERENCE](*)
      END

      INTERFACE TO SUBROUTINE
    + getimage_w[FAR, C, ALIAS:"__f_getimage_w"](wx1, wy1, wx2, wy2, image)
      DOUBLE PRECISION wx1, wy1     ! Upper left corner of bounding rectangle
      DOUBLE PRECISION wx2, wy2     ! Lower right corner of bounding rectangle
      INTEGER*1 image [FAR, REFERENCE](*)
      END

 þ  Description

 The getimage subroutines store the screen image defined by a specified
 bounding rectangle into the buffer that image points to.

 The getimage subroutine defines the bounding rectangle with the
 viewport-coordinate points (x1, y1) and (x2, y2).

 The getimage_w subroutine defines the bounding rectangle with the
 window-coordinate points (wx1, wy1) and (wx2, wy2).

 The buffer must be large enough to hold the image. Determine the size by
 calling the appropriate imagesize function at run time, or by using the
 formula described on the imagesize reference page.

 þ  Return value

 There is no return value.

 þ  See also

      imagesize, putimage

 þ  Example

 See the example program for imagesize.


 getlinestyle

 þ  Interface

      INTERFACE TO FUNCTION getlinestyle()
      INTEGER*2 getlinestyle[FAR, C, ALIAS:"__getlinestyle"]
      END

 þ  Description

 Some graphics routines (lineto, pie, and rectangle) output straight lines to
 the screen. The type of line can be controlled with the current line-style
 mask.

 The getlinestyle function returns the current line-style-mask number. The
 mask is a 16-bit array, where each bit represents a pixel in the line being
 drawn. If the bit is 1, then the corresponding pixel is set to the color of
 the line (the current color). If the bit is 0, the corresponding pixel is
 left unchanged. The mask is repeated over the length of the line. The
 default mask is #FFFF (a solid line).

 þ  Return value

 If a mask has been set, returns the current line-style mask number. If no
 mask has been set, getlinestyle returns the default mask.

 þ  See also

      lineto, pie, rectangle, setlinestyle

 þ  Example

 See the example program for setlinestyle.


 getphyscoord

 þ  Interface

      INTERFACE TO SUBROUTINE
    + getphyscoord[FAR, C, ALIAS:"__f_getphyscoord"](x, y, s)
      INTEGER*2 x, y
      STRUCTURE/xycoord/
         INTEGER*2 xcoord                         ! x-coordinate
         INTEGER*2 ycoord                         ! y-coordinate
      END STRUCTURE
      RECORD/xycoord/s [FAR, REFERENCE]           ! Returns the physical coord
      END

 þ  Description

 The getphyscoord subroutine translates the viewport-coordinate point (x, y)
 to physical coordinates, returning them in the xycoord structure s, defined
 in FGRAPH.FD.

 þ  Return value

 There is no return value.

 þ  See also

      getviewcoord

 þ  Example

 See the example program for setviewport.


 getpixel, getpixel_w

 þ  Interface

      INTERFACE TO FUNCTION getpixel(x, y)
      INTEGER*2 getpixel[FAR, C, ALIAS:"__getpixel"]
      INTEGER*2 x, y            ! Pixel position
      END

      INTERFACE TO FUNCTION getpixel_w(wx, wy)
      INTEGER*2 getpixel_w[FAR, C, ALIAS:"__f_getpixel_w"]
      DOUBLE PRECISION wx, wy   ! Pixel position in window coordinates
      END

 þ  Description

 The getpixel functions retrieve the pixel value at a specified location. The
 getpixel function uses the viewport coordinate (x, y). The getpixel_w
 function uses the window coordinate (wx, wy). The range of possible pixel
 values and their color translation is determined by the current video mode
 and palette, respectively.

 þ  Return value

 If successful, the getpixel and getpixel_w functions return the pixel value.
 If the function fails (for example, the point lies outside the clipping
 region, or the program is in a text mode), it returns -1.

 þ  See also

      remapallpalette, remappalette, selectpalette, setpixel, setvideomode

 þ  Example

            PROGRAM colors

            INCLUDE 'FGRAPH.FI'
            INCLUDE 'FGRAPH.FD'

            INTEGER*2 loop, dummy2
            REAL rnd1, rnd2

            dummy2 = setvideomode( $MRES16COLOR )
            dummy2 = rectangle( $GFILLINTERIOR, 80, 50, 240, 150 )
            DO loop = 1, 8000
               dummy2 = setcolor( MOD( getcolor()+1, 16 ) )  ! Set next
           +   color
      C
      C        Set a random pixel, but only if it is already on.
      C
               CALL RANDOM( rnd1 )
               CALL RANDOM( rnd2 )
               IF ( getpixel( INT2( rnd1*320 ), INT2( rnd2*200 ) )
           +   .NE. 0 ) THEN
                  dummy2 = setcolor( MOD( loop, 16 ) )
                  dummy2 = setpixel( INT2( rnd1*320 ), INT2( rnd2*200 ) )
               END IF
            END DO

            dummy2 = setvideomode( $DEFAULTMODE )
            END

 This program assigns different colors to randomly selected pixels.


 gettextcolor

 þ  Interface

      INTERFACE TO FUNCTION gettextcolor()
      INTEGER*2 gettextcolor[FAR, C, ALIAS:"__gettextcolor"]
      END

 þ  Description

 The gettextcolor function returns the color index of the current text color.
 The text color is set only by the settextcolor function and affects text
 output with the outtext function only. The setcolor function sets the color
 for text output using the outgtext function.

 The default is the highest legal color index of the current palette.

 This function is also available under OS/2.

 þ  Return value

 Returns the color index of the current text color.

 þ  See also

      selectpalette, setcolor, settextcolor

 þ  Example

      CC  TEXT.FOR - Illustrates text output functions including:
      CC             clearscreen      getbkcolor        gettextcolor
      CC             gettextposition  outtext           setbkcolor
      CC             settextcolor     settextposition
      CC

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2          dummy2, blink, fgd, oldfgd
            INTEGER*4          dummy4, bgd, oldbgd
            CHARACTER*4        str
            RECORD / rccoord / curpos

      C
      C     Save original foreground, background, and text position.
      C
            oldfgd = gettextcolor()
            oldbgd = getbkcolor()
            CALL clearscreen( $GCLEARSCREEN )
      C
      C     First time no blink, second time blinking.
      C
            DO blink = 0, 16, 16
      C
      C        Loop through 8 background colors.
      C
               DO bgd = 0, 7
                  dummy4 = setbkcolor( bgd )
                  CALL settextposition( INT2( bgd )
           +          ( ( blink / 16 ) * 9 )+ 3, 1, curpos )
                  dummy2 = settextcolor( 15 )
                  WRITE ( str, '( I4  )' ) bgd
                  CALL outtext( 'Back:' // str(3:4) // '  Fore:', 14 )
      C
      C           Loop through 16 foreground colors.
      C
                  DO fgd = 0, 15
                     dummy2 = settextcolor( fgd + blink )
                     WRITE ( str, '( I4 )' ) fgd + blink
                     CALL outtext( str )
                  END DO
               END DO
            END DO

            WRITE (*, '(///// A \)') ' Press ENTER to exit . . . '
            READ (*,*)

      C
      C     Restore original foreground and background.
      C
            dummy2 = settextcolor( oldfgd )
            dummy4 = setbkcolor( oldbgd )
            CALL clearscreen( $GCLEARSCREEN )
            END

 This program prints different text colors on a variety of colored
 backgrounds.


 gettextcursor

 þ  Interface

      INTERFACE TO FUNCTION gettextcursor()
      INTEGER*2 gettextcursor[FAR, C, ALIAS:"__gettextcursor"]
      END

 þ  Description

 The gettextcursor function returns the current cursor attribute (the shape).
 Note that this function works in text video modes only.

 This function is also available under OS/2.

 þ  Return value

 Returns the current cursor attribute or -1 if an error occurs (such as
 calling the function in a non-text video mode).

 þ  See also

      displaycursor, settextcursor

 þ  Example

 See the example program for settextcursor.


 gettextposition

 þ  Interface

      INTERFACE TO SUBROUTINE
    + gettextposition[FAR, C, ALIAS:"__f_gettextposition"](s)
      STRUCTURE/rccoord/
         INTEGER*2 row                          ! Row coordinate
         INTEGER*2 col                          ! Column coordinate
      END STRUCTURE
      RECORD/rccoord/s [FAR, REFERENCE]         ! Returns the current text pos
      END

 þ  Description

 The gettextposition function returns the current text position in s, an
 rccoord structure defined in FGRAPH.FD.

 The text position given by the coordinates (1, 1) is defined as the upper
 left corner of the text window.

 Text output from the outtext function begins at the current text position.
 Font text is not affected by the current text position. Graphics output
 begins at the current graphics output position, which is a separate
 position.

 This function is also available under OS/2.

 þ  Return value

 There is no return value.

 þ  See also

      settextposition

 þ  Example

 See the example program for gettextcolor.


 getviewcoord, getviewcoord_w

 þ  Interface

      INTERFACE TO SUBROUTINE
    + getviewcoord[FAR, C, ALIAS:"__f_getviewcoord"](x, y, s)
      INTEGER*2 x, y
      STRUCTURE/xycoord/
         INTEGER*2 xcoord                                ! x-coordinate
         INTEGER*2 ycoord                                ! y-coordinate
      END STRUCTURE
      RECORD/xycoord/s [FAR, REFERENCE]                  ! Returns the viewpor
      END

      INTERFACE TO SUBROUTINE
    + getviewcoord_w[FAR, C, ALIAS:"__f_getviewcoord_w"](wx, wy, s)
      DOUBLE PRECISION wx, wy
      STRUCTURE/xycoord/
         INTEGER*2 xcoord                                ! x-coordinate
         INTEGER*2 ycoord                                ! y-coordinate
      END STRUCTURE
      RECORD/xycoord/s [FAR, REFERENCE]                  ! Returns the viewpor
      END

 þ  Description

 The getviewcoord subroutines translate the specified coordinates (x, y) from
 one coordinate system to viewport coordinates and returns them in the
 xycoord structure s, defined in FGRAPH.FD.

 The getviewcoord subroutines translate in the following manner:

      Routine                     Translation

      getviewcoord                Physical coordinates (x, y) to
                                  viewport coordinates

      getviewcoord_w              Window coordinates (wx, wy) to
                                  viewport coordinates

 þ  Return value

 There is no return value.

 þ  See also

      getphyscoord, getwindowcoord

 þ  Example

 See the example program for setviewport.


 getvideoconfig

 þ  Interface

      INTERFACE TO SUBROUTINE
    + getvideoconfig[FAR, C, ALIAS:"__getvideoconfig"](s)
      STRUCTURE/videoconfig/
         INTEGER*2 numxpixels    ! Number of pixels on x-axis
         INTEGER*2 numypixels    ! Number of pixels on y-axis
         INTEGER*2 numtextcols   ! Number of text columns available
         INTEGER*2 numtextrows   ! Number of text rows available
         INTEGER*2 numcolors     ! Number of actual colors
         INTEGER*2 bitsperpixel  ! Number of bits per pixel
         INTEGER*2 numvideopages ! Number of available video pages
         INTEGER*2 mode          ! Current video mode
         INTEGER*2 adapter       ! Active display adapter
         INTEGER*2 monitor       ! Active display monitor
         INTEGER*2 memory        ! Video adapter memory in K bytes
      END STRUCTURE
      RECORD/videoconfig/s [FAR, REFERENCE]
      END

 þ  Description

 The getvideoconfig function returns the current graphics environment
 configuration in s, a videoconfig structure defined above and in FGRAPH.FD.

 The values returned reflect the currently active monitor as well as the
 currently specified video mode.

 The videoconfig structure contains the following elements:

      Element                          Contents

      numxpixels                       Number of pixels on the x-axis

      numypixels                       Number of pixels on the y-axis

      numtextcols                      Number of text columns available

      numtextrows                      Number of text rows available

      numcolors                        Number of actual colors available

      bitsperpixel                     Number of bits per pixel

      numvideopages                    Number of available video pages

      mode                             Current video mode

      adapter                          Active display adapter

      monitor                          Active display monitor

      memory                           Video adapter memory (in
                                       kilobytes)

 The values for the adapter element of the videoconfig structure are given by
 the symbolic constants shown in the list below. For any applicable adapter
 ($CGA, $EGA, or $VGA), the corresponding Olivetti adapter ($OCGA, $OEGA, or
 $OVGA) represents a superset of graphics capabilities.

      Adapter Constant                 Meaning

      $MDPA                            Monochrome Display Adapter

      $CGA                             Color Graphics Adapter

      $OCGA                            Olivetti(R) (AT&T) Color Graphics
                                       Adapter

      $EGA                             Enhanced Graphics Adapter

      $OEGA                            Olivetti (AT&T) Enhanced Graphics
                                       Adapter

      $VGA                             Video Graphics Array

      $OVGA                            Olivetti (AT&T) Video Graphics
                                       Array

      $MCGA                            Multicolor Graphics Array

      $HGC                             Hercules Graphics Card

 The values for the monitor element of the videoconfig structure are given by
 the symbolic constants listed below:

      Monitor Constant            Meaning

      $MONO                       Monochrome monitor

      $COLOR                      Color (or
                                  enhanced-monitor-emulating color)

      $ENHCOLOR                   Enhanced color

      $ANALOG                     Analog

 In every text mode, including monochrome, getvideoconfig returns the value
 32 for the number of available colors. The value 32 indicates the range of
 values (0-31) accepted by the settextcolor function. This includes 16 normal
 colors (0-15) and 16 blinking colors (16-31). Blinking is selected by adding
 16 to the normal color value. Monochrome text mode has fewer unique display
 attributes, so some color values are redundant. However, because blinking is
 selected in the same manner, monochrome text mode has the same range (0-31)
 as other text modes.

 This subroutine is also available under OS/2.

 þ  Return value

 Returns the current graphics environment configuration.

 þ  Example

 See the example program for setvideomoderows.


 getvisualpage

 þ  Interface

      INTERFACE TO FUNCTION getvisualpage()
      INTEGER*2 getvisualpage[FAR, C, ALIAS:"__getvisualpage"]
      END

 þ  Description

 The getvisualpage function returns the current visual page number.

 þ  Return value

 Returns the page number of the current visual page. All hardware
 combinations support at least one page (page number 0).

 This function is also available under OS/2.

 þ  See also

      getactivepage, gettextcolor, gettextposition, outtext, settextcolor,
      settextposition, settextwindow, setvideomode, setvisualpage, wrapon

 þ  Example

 See the example program for getactivepage


 getwindowcoord

 þ  Interface

      INTERFACE TO SUBROUTINE
    + getwindowcoord[FAR, C, ALIAS:"__getwindowcoord"](x, y, s)
      INTEGER*2 x, y                             ! Physical point to translate
      STRUCTURE/wxycoord/
         DOUBLE PRECISION wx                     ! x-coordinate
         DOUBLE PRECISION wy                     ! y-coordinate
      END STRUCTURE
      RECORD/wxycoord/s [FAR, REFERENCE]         ! Returns the window coordina
      END

 þ  Description

 The getwindowcoord subroutine translates the viewport coordinates (x, y) to
 window coordinates and returns them in the wxycoord structure variable s,
 defined in FGRAPH.FD.

 þ  Return value

 There is no return value.

 þ  See also

      getphyscoord, getviewcoord, getviewcoordw, getviewcoordwxy, moveto

 þ  Example

 See the example program for setviewport.


 imagesize, imagesize_w

 þ  Interface

      INTERFACE TO FUNCTION imagesize(x1, y1, x2, y2)
      INTEGER*4 imagesize[FAR, C, ALIAS:"__imagesize"]
      INTEGER*2 x1, y1            ! Upper left corner of image
      INTEGER*2 x2, y2            ! Lower right corner of image
      END

      INTERFACE TO FUNCTION imagesize_w(wx1, wy1, wx2, wy2)
      INTEGER*4 imagesize[FAR, C, ALIAS:"__f_imagesize_w"]
      DOUBLE PRECISION wx1, wy1   ! Upper left corner of image
      DOUBLE PRECISION wx2, wy2   ! Lower right corner of image
      END


 þ  Description

 The imagesize functions return the number of bytes needed to store the image
 defined by the bounding rectangle, which is specified by the coordinates
 given in the function call. The imagesize function defines the bounding
 rectangle in terms of viewport-coordinate points (x1, y1) and (x2, y2). This
 size is determined by the following formula:

      xwid = abs( x1-x2 )+1
      ywid = abs( y1-y2 )+1
      size = 4 + INT( ( xwid*bits_per_pixel+7 )/8 )*INT( ywid ) )

 The bits_per_pixel value returns from a call to getvideoconfig as
 bitsperpixel.

 The imagesize_w function defines the bounding rectangle in terms of
 window-coordinate points (x1, y1) and (x2, y2).


 þ  Return value

 The imagesize and imagesize_w functions return the storage size of an image
 in bytes.


 þ  See also

      getvideoconfig


 þ  Example

      CC  ANIMATE.FOR - Illustrates animation functions including:
      CC                imagesize     getimage     putimage

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*1        buffer[ALLOCATABLE] (:)
            INTEGER*2        dummy, x, y, error, action(5)
            INTEGER*4        imsize
            CHARACTER*6      descrip(5)
            RECORD /rccoord/ curpos

            DATA action  / $GPSET, $GPRESET, $GXOR, $GOR, $GAND  /
            DATA descrip / 'PSET  ', 'PRESET', 'XOR  ', 'OR  ', 'AND  ' /

      C
      C     Find graphics mode.
      C
            IF( setvideomode( $MAXRESMODE ) .EQ. 0 )
           +    STOP 'Error:  cannot set graphics mode'

            dummy = setcolor( 3 )
            y     = 30
            DO i = 1, 5
               x = 50
               y = y + 40
      C
      C        Display action type.
      C
               CALL settextposition( 1, 1, curpos )
               CALL outtext( descrip(i) )
      C
      C        Draw and measure ellipse, allocate memory for image.
      C
               dummy  = ellipse( $GFILLINTERIOR, x - 15, y - 15, x + 15,
           +                     y + 15 )
               imsize = imagesize( x - 16, y - 16, x + 16, y + 16 )
               ALLOCATE( buffer( imsize ), STAT = error )
               IF( error .NE. 0 ) THEN
                  dummy = setvideomode( $DEFAULTMODE )
                  STOP 'Error:  insufficient memory'
               END IF
      C
      C        Get master copy of ellipse.
      C
               CALL getimage( x - 16, y - 16, x + 16, y + 16, buffer )
      C
      C        Copy row of ellipses with specified action.
      C
               DO x = 55, 255, 5
                  CALL putimage( x - 16, y - 16, buffer, action(i) )
               END DO

      C
      C        Free memory, wait for ENTER key to continue.
      C
               DEALLOCATE( buffer )
               READ (*,*)
            END DO

            dummy = setvideomode( $DEFAULTMODE )
            END

 This program shows different methods of animating an image on the screen.


 lineto, lineto_w

 þ  Interface

      INTERFACE TO FUNCTION lineto(x, y)
      INTEGER*2 lineto[FAR, C, ALIAS:"__lineto"]
      INTEGER*2 x, y             ! End point
      END

      INTERFACE TO FUNCTION lineto_w(wx, wy)
      INTEGER*2 lineto[FAR, C, ALIAS:"__f_lineto_w"]
      DOUBLE PRECISION wx, wy    ! End point
      END

 þ  Description

 The lineto functions draw a line from the current position up to and
 including the destination point. The destination point for lineto is given
 by the viewport-coordinate point (x, y). The destination point for the
 lineto_w function is given by the window-coordinate point (wx, wy).

 The line is drawn using the current color and line style. If no error
 occurs, lineto sets the current position to the view point (x, y) and
 lineto_w sets the current position to the window point (wx, wy).

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    If you use floodfill to fill in a closed figure drawn with lineto calls,
    the figure must be drawn with a solid line-style pattern.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 The lineto and lineto_w routines return a nonzero value if the line is drawn
 successfully; otherwise, they return 0.

 þ  See also

      getcurrentposition, setlinestyle

 þ  Example

            INCLUDE          'FGRAPH.FI'
            INCLUDE          'FGRAPH.FD'
            INTEGER*2        dummy
            RECORD /xycoord/ xy

            dummy = setvideomode( $MRES16COLOR )
            CALL    moveto(  80,  50, xy  )
            dummy = lineto( 240, 150 )
            dummy = lineto( 240,  50 )
            READ (*,*)          ! Press ENTER to exit
            dummy = setvideomode( $DEFAULTMODE )
            END

 This program draws the figure shown in Figure R.3.

 See also the example program for setpixel and setviewport.


 moveto, moveto_w

 þ  Interface

      INTERFACE TO SUBROUTINE moveto[FAR, C, ALIAS:"__f_moveto"](x, y, s)
      INTEGER*2 x, y
      STRUCTURE/xycoord/
         INTEGER*2 xcoord                  ! x-coordinate
         INTEGER*2 ycoord                  ! y-coordinate
      END STRUCTURE
      RECORD/xycoord/s [FAR, REFERENCE]    ! Returns the previous position
      END

      INTERFACE TO SUBROUTINE
     + moveto_w[FAR, C, ALIAS:"__f_moveto_w"](wx, wy, s)
      INTEGER*2 wx, wy
      STRUCTURE/wxycoord/
         DOUBLE PRECISION wx                ! x-window coordinate
         DOUBLE PRECISION wy                ! y-window coordinate
      END STRUCTURE
      RECORD/wxycoord/s [FAR, REFERENCE]    ! Returns the previous position
      END

 þ  Description

 The moveto functions move the current position to the specified point. The
 moveto function uses the view-coordinate point (x, y) as the current
 position. The moveto_w function use the window-coordinate point (wx, wy) as
 the current position. No drawing takes place.

 The function assigns the coordinates of the previous position to s. The
 moveto function assigns the coordinates in s, an xycoord structure defined
 in FGRAPH.FD. The function moveto_w assigns the coordinates in s, a wxycoord
 structure defined in FGRAPH.FD.

 þ  Return value

 There is no return value.

 þ  See also

 lineto

 þ  Example

 See the example program for setpixel and setviewport.


 outgtext

 þ  Interface

      INTERFACE TO SUBROUTINE
    + outgtext[FAR, C, ALIAS:"__f_outgtext"](text)
      CHARACTER*(*) text [FAR, REFERENCE]
      END

 þ  Description

 The outgtext function sends the string text to the screen, including any
 trailing blanks. The text is output using the current font at the current
 graphics position and in the current color.

 No formatting is provided.

 After it outputs the text, outgtext updates the current graphics position.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    This function operates only in graphics video modes (such as
    $MRES4COLOR). Because it is a graphics function, the color of text is set
    by the setcolor function, not by settextcolor.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 There is no return value.

 þ  See also

      moveto, setcolor, setfont

 þ  Example

 See the example program for registerfonts.


 outtext

 þ  Interface

      INTERFACE TO SUBROUTINE outtext[FAR, C, ALIAS:"__f_outtext"](text)
      CHARACTER*(*) text [FAR, REFERENCE]
      END

 þ  Description

 The outtext function sends the string text to the screen, including any
 trailing blanks. No formatting is provided. This function will work in any
 screen mode. Text output begins at the current text position.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    To output text using special fonts, you must use the outgtext function.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This function is also available under OS/2.

 þ  Return value

 There is no return value.

 þ  See also

      setactivepage, settextposition

 þ  Example

 See the example program for gettextcolor and setvideomoderows.


 pie, pie_w

 þ  Interface

      INTERFACE TO FUNCTION pie(i, x1, y1, x2, y2, x3, y3, x4, y4)
      INTEGER*2 pie[FAR, C, ALIAS:"__f_pie"]
      INTEGER*2 i                   ! Fill flag
      INTEGER*2 x1, y1              ! Upper left corner of bounding rectangle
      INTEGER*2 x2, y2              ! Lower right corner of bounding rectangle
      INTEGER*2 x3, y3              ! Start vector
      INTEGER*2 x4, y4              ! End vector
      END

      INTERFACE TO FUNCTION pie_w(i, wx1, wy1, wx2, wy2, wx3, wy3, wx4, wy4)
      INTEGER*2 pie[FAR, C, ALIAS:"__f_pie_w"]
      INTEGER*2 i                   ! Fill flag
      DOUBLE PRECISION wx1, wy1     ! Upper left corner of bounding rectangle
      DOUBLE PRECISION wx2, wy2     ! Lower right corner of bounding rectangle
      DOUBLE PRECISION wx3, wy3     ! Start vector
      DOUBLE PRECISION wx4, wy4     ! End vector
      END

 þ  Description

 The pie functions draw a pie-shaped wedge by drawing an elliptical arc whose
 center and two endpoints are joined by lines.

 The pie function uses the viewport-coordinate system. The center of the arc
 is the center of the bounding rectangle, which is specified by the
 viewport-coordinate points (x1, y1) and (x2, y2). The arc starts where it
 intersects the vector defined by (x3, y3) and ends where it intersects the
 vector (x4, y4).

 The pie_w function uses the window-coordinate system. The center of the arc
 is the center of the bounding rectangle specified by the window-coordinate
 pairs (wx1, wy1) and (wx2, wy2). The arc starts where it intersects the
 vector defined by (wx3, wy3) and ends where it intersects the vector defined
 by (wx4, wy4).

 The wedge is drawn using the current color moving in a counterclockwise
 direction. The control parameter can be one of the following symbolic
 constants:

      Constant                         Action

      $GFILLINTERIOR                   Fills the figure using the
                                       current color and fill mask

      $GBORDER                         Does not fill the figure

 The control option given by $GFILLINTERIOR is equivalent to a subsequent
 call to the floodfill function using the center of the ellipse as the
 starting point and the current color (set by setcolor) as the boundary
 color.

 þ  Return value

 The pie and pie_w functions return a nonzero value if the pie is drawn
 successfully; otherwise, they return 0.

 þ  See also

      arc, ellipse, floodfill, getcolor, lineto, rectangle, setcolor,
      setfillmask

 þ  Example

            INCLUDE   'FGRAPH.FI'
            INCLUDE   'FGRAPH.FD'
            INTEGER*2 dummy

            dummy = setvideomode( $MRES16COLOR )
            dummy = pie( $GBORDER, 80, 50, 240, 150, 240, 12, 0, 150 )
            READ (*,*)          ! Press ENTER to exit
            dummy = setvideomode( $DEFAULTMODE )
            END

 This program draws the "pie" wedge shown in Figure R.4.

 See also the example program for setpixel.


 putimage, putimage_w

 þ  Interface

      INTERFACE TO SUBROUTINE
      + putimage[FAR, C, ALIAS:"__putimage"](x, y, image, action)
      INTEGER*2 x, y                            ! Position of upper left corne
      INTEGER*2 action                          ! Interaction with existing sc
      INTEGER*1 image [FAR, REFERENCE](*)       ! Stored image buffer
      END

      INTERFACE TO SUBROUTINE
      + putimage_w[FAR, C, ALIAS:"__f_putimage_w"](wx, wy, image, action)
      INTEGER*2 wx, wy                          ! Position of upper left corne
      INTEGER*2 action                          ! Interaction with existing sc
      INTEGER*1 image [FAR, REFERENCE](*)       ! Stored image buffer
      END

 þ  Description

 The putimage function transfers to the screen the image stored in image. In
 the putimage function, the upper left corner of the image is placed at the
 viewport-coordinate point (x, y).

 In the putimage_w function, the upper left corner of the image is placed at
 the window-coordinate point (wx, wy).

 The action argument defines the interaction between the stored image and the
 one that is already on the screen. It may be any one of the following
 symbolic constants (defined in FGRAPH.FD):

         Constant                    Meaning

         $GAND                       Transfers the image over an
                                     existing image on the screen. The
                                     resulting image is the logical
                                     AND of the two images: points
                                     that have the same color in both
                                     the existing image and the new
                                     one will remain the same color,
                                     while points that have different
                                     colors are joined by logical AND.

         $GOR                        Superimposes the image onto an
                                     existing image. The new image
                                     does not erase the previous
                                     screen contents.

         $GPRESET                    Transfers the data point by point
                                     onto the screen. Each point has
                                     the inverse of the color
                                     attribute it had when it was
                                     taken from the screen by
                                     getimage, producing a negative
                                     image.

         $GPSET                      Transfers the data point by point
                                     onto the screen. Each point has
                                     the exact color attribute it had
                                     when it was taken from the screen
                                     by getimage.

         $GXOR                       Causes the points on the screen
                                     to be inverted where a point
                                     exists in the image buffer. This
                                     behavior is exactly like that of
                                     the cursor: when an image is put
                                     against a complex background
                                     twice, the background is restored
                                     unchanged. This allows you to
                                     move an object around without
                                     erasing the background. The $GXOR
                                     constant is a special mode often
                                     used for animation.

 þ  Return value

 There is no return value.

 þ  See also

      getimage, imagesize

 þ  Example

 See the example program for imagesize.


 rectangle, rectangle_w

 þ  Interface

      INTERFACE TO FUNCTION rectangle(control, x1, y1, x2, y2)
      INTEGER*2 rectangle[FAR, C, ALIAS:"__rectangle"]
      INTEGER*2 control                       ! Fill flag
      INTEGER*2 x1, y1                      ! Upper left corner
      INTEGER*2 x2, y2                      ! Lower right corner
      END

      INTERFACE TO FUNCTION rectangle_w(control, wx1, wy1, wx2, wy2)
      INTEGER*2 rectangle[FAR, C, ALIAS:"__f_rectangle_w"]
      INTEGER*2 control                      ! Fill flag
      DOUBLE PRECISION wx1, wy1              ! Upper left corner
      DOUBLE PRECISION wx2, wy2              ! Lower right corner
      END

 þ  Description

 The rectangle functions draw a rectangle with the current line style.

 The rectangle function uses the viewport-coordinate system. The
 viewport-coordinate points (x1, y1) and (x2, y2) are the diagonally opposed
 corners of the rectangle.

 The rectangle_w function uses the window-coordinate system. The
 window-coordinate points (wx1, wy1) and (wx2, wy2) are the diagonally
 opposed corners of the rectangle.

 The control parameter can be one of the following symbolic constants defined
 in the include file FGRAPH.FD:

      Constant                    Action

      $GFILLINTERIOR              Fills the figure with the current
                                  color using the current fill mask

      $GBORDER                    Does not fill the rectangle

 If the current fill mask is all zeros, no mask is used. Instead, the
 rectangle is filled with the current color.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    If you try to fill the rectangle with the floodfill function, the
    rectangle must be bordered by a solid line-style pattern.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 The rectangle and rectangle_w functions return a nonzero value if the
 rectangle is drawn successfully, 0 if not.

 þ  See also

      floodfill, getcolor, getlinestyle, setcolor, setlinestyle

 þ  Example

            INCLUDE   'FGRAPH.FI'
            INCLUDE   'FGRAPH.FD'
            INTEGER*2 dummy

            dummy = setvideomode( $MRES16COLOR )
            dummy = rectangle( $GBORDER, 80, 50, 240, 150 )
            READ (*,*)          ! Press ENTER to exit
            dummy = setvideomode( $DEFAULTMODE )
            END

 This program draws the rectangle shown in Figure R.5.

 See also the example program for setpixel, setvieworg, and setviewport.


 registerfonts

 þ  Interface

      INTERFACE TO FUNCTION registerfonts(filename)
      INTEGER*2 registerfonts
    + [FAR, C, ALIAS:"__f_registerfonts"]
      CHARACTER*(*) filename
    + [FAR, REFERENCE]            ! File name of .FON files to register
      END

 þ  Description

 The registerfonts function initializes the fonts graphics system. Font files
 must be registered with the registerfonts function before any other
 font-related library functions (getgtextextent, setfont, outgtext,
 unregisterfonts) can be used.

 The registerfonts function reads the specified files and loads font header
 information into memory. Each font header takes up about 140 bytes of
 memory.

 The filename argument is the path specification and file name of valid .FON
 files. The filename can contain standard DOS wild-card characters.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The font functions affect only the output from the font output function
    outgtext; no other FORTRAN run-time output functions are affected by font
    usage.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 Returns a positive value that indicates the number of fonts successfully
 registered. A negative return value indicates failure. The following
 negative values may be returned:

      Value                  Meaning

      -1                     No such file or directory

      -2                     One or more of the .FON files was
                             not a valid, binary .FON file

      -3                     One or more of the .FON files is
                             damaged

 þ  See also

      getfontinfo, getgtextextent, outgtext, setfont, unregisterfonts

 þ  Example

      CC  FONTS.FOR - Illustrates fonts functions including:
      CC              getfontinfo     outgtext        setfont
      CC              getgtextextent  registerfonts   unregisterfonts

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

      PARAMETER            ( NFONTS = 6 )

            INTEGER*2            dummy, x, y, iend
            INTEGER*4            ifont
            CHARACTER*11         face(NFONTS)
            CHARACTER*10         options(NFONTS)
            CHARACTER*20         list
            CHARACTER*64         fontpath
            RECORD /videoconfig/ vc
            RECORD /xycoord/     xy
            RECORD /fontinfo/    fi

            DATA face    / "Courier"   , "Helvetica"  , "Times Roman",
           +               "Modern"    , "Script"     , "Roman"      /
            DATA options / "t'courier'", "t'helv'"    , "t'tms rmn'" ,
           +               "t'modern'" , "t'script'"  , "t'roman'"   /

            CALL clearscreen( $GCLEARSCREEN )
            WRITE (*,*) 'Enter file specification for .FON files'
            WRITE (*,*) '[pathname\*.FON]:'
            WRITE (*,*)
            READ  (*, '(A)') fondir
      C
      C     Locate .FON files, then register fonts by reading header
      C     information from all files.
      C
            IF( registerfonts( '*.FON' ). LT. 0 ) THEN
               WRITE (*, '(A/)') ' Enter directory for .FON files:'
               READ (*, '(A )') fontpath
               iend = INDEX( fontpath, ' ' )
               fontpath( iend:iend + 5 ) = '\*.FON'
               IF( registerfonts( fontpath ). LT. 0 )
           +       STOP 'Error: cannot find font files'
            ENDIF

      C
      C     Find graphics mode.
      C
            IF( setvideomode( $MAXRESMODE ) .EQ. 0 )
           +    STOP 'Error:  cannot set graphics mode'
            CALL getvideoconfig( vc )

      C
      C     Display each font name centered on screen.
      C
            DO ifont = 1, NFONTS

      C
      C        Build options string.
      C
               list = options( ifont ) // 'h30w24b'

               CALL clearscreen( $GCLEARSCREEN )
               IF( setfont( list ) .EQ. 0 ) THEN
      C
      C           Use length of text and height of font to center text.
      C
                  x = ( vc.numxpixels - getgtextextent
           +           ( face( ifont ) ) ) / 2
                  IF( getfontinfo( fi ) .NE. 0 ) THEN
                     CALL outtext( 'Error:  cannot get font info' )
                     READ (*,*)
                  END IF
                  y = ( vc.numypixels - fi.ascent ) / 2
                  CALL moveto( x, y, xy )
                  IF( vc.numcolors .GT. 2 ) dummy = setcolor( ifont )
                  CALL outgtext( face( ifont ) )
               ELSE
                  CALL outtext( 'Error:  cannot set font' )
               END IF
               READ (*,*)
            END DO
            CALL unregisterfonts()
            dummy = setvideomode( $DEFAULTMODE )
            END

 The program FONTS.FOR displays the fonts provided with the FORTRAN Graphics
 Library


 remapallpalette, remappalette

 þ  Interface

      INTERFACE TO FUNCTION remapallpalette(colors)
      INTEGER*2 remapallpalette[FAR, C, ALIAS:"__remapallpalette"]
      INTEGER*4 colors [FAR, REFERENCE](*)     ! Color value array
      END

      INTERFACE TO FUNCTION remappalette(index, color)
      INTEGER*4 remappalette[FAR, C, ALIAS:"__remappalette"]
      INTEGER*4 color                           ! Color value to assign color
      INTEGER*2 index                           ! Color index to reassign
      END

 þ  Description

 The remapallpalette function remaps all of the available color indexes
 simultaneously. The colors argument points to an array of color values. The
 default array of color indexes is shown below.


      Index      Color       Index     Color

      0          Black       8         Dark gray
      1          Blue        9         Light blue
      2          Green       10        Light green
      3          Cyan        11        Light cyan
      4          Red         12        Light red
      5          Magenta     13        Light magenta
      6          Brown       14        Yellow
      7          White       15        Bright white

 The number of colors mapped depends on the number of colors supported by the
 current video mode. A 16-color mode will map all the colors shown, from 0 to
 15. An eight-color mode will map only the colors from 0 to 7. A four-color
 mode will use one of the standard palettes supported by the EGA (see
 selectpalette). A two-color mode will support the colors 0 and 7 (black and
 white).

 If colors is an eight-element array whose members are 1, 3, 5, 7, 2, 4, 6,
 and 8, these color indexes would be mapped to actual colors in an
 eight-color video mode, as shown below.


      Index      Color     Index     Color

      1          Blue      5         Magenta
      2          Green     6         Brown
      3          Cyan      7         White
      4          Red       8         Dark gray

 You are simply mapping a set of color values to the ordinal numbers
 recognized by the hardware; colors can be larger than the number of colors
 supported by the current video mode. Only the first n colors are available,
 where n is the number of colors supported by the current video mode. The
 remappalette function remaps one color index to color, a color value
 supported by the current video mode. Both functions immediately affect the
 current display.

 The VGA color graphics modes support a palette of 262,144 colors (256K).
 Three bytes of data represent the intensities of red, blue, and green. In
 each byte, the two high-order bits must be 0. The remaining six bits
 represent the intensity of blue, green, and red (reading from high-order
 byte to low-order byte). For example, to make a low-intensity white color,
 equal values of red, green, and blue are used. Thus the three-byte color
 value is

      blue byte            green byte        red byte
      00011111             00011111          00011111
      high order ------------------------>   low order

 Because of the splitting of colors between bytes, the color values are not
 continuous. Symbolic constants for the default color values are supplied to
 provide compatibility with EGA practice. The names of these constants are
 listed below.


      Index      Constant     Index     Constant

      0          $BLACK        8        $GRAY
      1          $BLUE         9        $LIGHTBLUE
      2          $GREEN       10        $LIGHTGREEN
      3          $CYAN        11        $LIGHTCYAN
      4          $RED         12        $LIGHTRED
      5          $MAGENTA     13        $LIGHTMAGENTA
      6          $BROWN       14        $LIGHTYELLOW
      7          $WHITE       15        $BRIGHTWHITE

 Both the remappalette and remapallpalette functions work in all video modes,
 but only with an EGA or VGA.


 þ  Return value

 If successful, remapallpalette returns a nonzero value. If the function is
 inoperative (that is, running under a configuration other than an EGA or
 VGA), it returns 0.

 If successful, remappalette returns the previous color value for the given
 color index. If unsuccessful, it returns -1.


 þ  See also

      selectpalette, setbkcolor, setvideomode


 þ  Example

      CC  PALETTE.FOR - Illustrates functions for assigning color values
      CC                to color indexes.  Functions include:
      CC                remapallpalette    remappalette

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2            dummy2, mode, cells, x, y, xinc, yinc, i
            INTEGER*4            dummy4, pal( 256 ), iblue, ired, igreen
            INTEGER*4            RGB, tmp, inc
            CHARACTER*3          str1, str2
            RECORD /videoconfig/ vc

      C
      C     Make sure all palette numbers are valid.
      C
            DO i = 1, 256
               pal( i ) = $BLACK
            END DO
      C
      C     Loop through each graphics mode that supports palettes.
      C

            DO mode = $MRES4COLOR, $MRES256COLOR
               IF( mode .EQ. $ERESNOCOLOR ) CYCLE
               IF( setvideomode( mode ) .EQ. 0 ) CYCLE

      C
      C        Get configuration variables for current mode.
      C
               CALL getvideoconfig( vc )
               SELECT CASE( vc.numcolors )

                  CASE( 256 )
      C
      C              Active bits in this order:
      C              ???????? ??bbbbbb ??gggggg ??rrrrrr
      C
                     cells = 13
                     inc   = 12

                  CASE( 16 )
      C
      C              If $ERES or $VRES16, active bits in this order:
      C              ???????? ??????bb ??????gg ??????rr
      C
      C              Else in this order:
      C              ???????? ??????Bb ??????Gg ??????Rr
      C
                     cells = 4
                     inc   = 32
                     IF( ( vc.mode .EQ. $ERESCOLOR )  .OR.
           +             ( vc.mode .EQ. $VRES16COLOR ) ) inc = 16

                  CASE( 4 )
      C
      C              Active bits in this order:
      C              ???????? ??????Bb ??????Gg ??????Rr
      C
                     cells = 2
                     inc   = 32

                  CASE DEFAULT
                     CYCLE

               END SELECT

               xinc = vc.numxpixels / cells
               yinc = vc.numypixels / cells

      C
      C        Fill palette arrays in BGR order.
      C
               i = 1
               DO iblue = 0, 63, inc
                  DO igreen = 0, 63, inc
                     DO ired = 0, 63, inc
                        pal( i ) = RGB( ired, igreen, iblue )
      C
      C                 Special case: using 6 bits to represent 16 colors
      C                 If both bits are on for a color, intensity is set
      C                 If one bit is set for a color, the color is on.
      C
                        IF( inc .EQ. 32 )
           +                pal( i + 8 ) = pal( i ) .OR. ( pal( i ) / 2 )
                        i = i + 1
                     END DO
                  END DO
               END DO
      C
      C        If palettes available, remap all palettes at once.
      C        Otherwise, quit.
      C
               IF( remapallpalette( pal ) .EQ. 0 ) THEN
                  dummy2 = setvideomode( $DEFAULTMODE )
                  STOP 'Palettes not available with this adapter'
               END IF
      C
      C        Draw colored squares.
      C
               i = 0
               DO x = 0, ( xinc * cells ) - 1, xinc
                  DO y = 0, ( yinc * cells ) - 1, yinc
                     dummy2 = setcolor( INT4( i ) )
                     dummy2 = rectangle( $GFILLINTERIOR, x, y, x + xinc,
           +                             y + yinc )
                     i      = i + 1
                  END DO
               END DO

               dummy2 = setcolor( INT4( vc.numcolors / 2 ) )
               WRITE (str1, '(I3)') vc.mode
               WRITE (str2, '(I3)') vc.numcolors
               CALL outtext( 'Mode' // str1 // ' has' //
           +                  str2 // ' colors' )
               READ (*,*)

      C
      C        Change each palette entry separately in GRB order.
      C
               i = 0
               DO igreen = 0, 63, inc
                  DO ired = 0, 63, inc
                     DO iblue = 0, 63, inc
                        tmp    = RGB( ired, igreen, iblue )
                        dummy4 = remappalette( i, tmp )
                        IF( inc .EQ. 32 )
           +               dummy4 = remappalette(i + 8, tmp.OR.(tmp / 2))
                        i = i + 1
                     END DO
                  END DO
               END DO

      READ (*,*)      ! Wait for ENTER to be pressed
            END DO

            dummy2 = setvideomode( $DEFAULTMODE )
            END



      CC  RGB - Function for mixing red, green, and blue color elements.
      CC
      CC  Params: r, g, b - Values for red, green, and blue, respectively
      CC
      CC  Return: Mixed color value

            INTEGER*4 FUNCTION RGB( r, g, b )
            INTEGER*4 r, g, b

            RGB = ISHL( ISHL( b, 8 ) .OR. g, 8 ) .OR. r
            RETURN
            END

 The program PALETTE.FOR shows the effects of using and remapping palettes.


 selectpalette

 þ  Interface

      INTERFACE TO FUNCTION selectpalette(number)
      INTEGER*2 selectpalette[FAR, C, ALIAS:"__selectpalette"]
      INTEGER*2 number               ! Palette number
      END

 þ  Description

 The selectpalette function works only under the video modes $MRES4COLOR and
 $MRESNOCOLOR. A palette consists of a selectable background color (Color 0)
 and three set colors. Under the $MRES4COLOR mode the number argument selects
 one of the four predefined palettes shown in Table R.2.

 Table R.2  $MRES4COLOR Mode Palette Colors

                                 Color Index
      Palette
      Number        Color 1         Color 2           Color 3

      0             Green           Red               Brown
      1             Cyan            Magenta           Light gray
      2             Light green     Light red         Yellow
      3             Light cyan      Light magenta     White

 The $MRESNOCOLOR video mode is used with black-and-white displays, producing
 palettes consisting of various shades of gray. It will also produce color
 when used with a color display. The number of palettes available depends
 upon whether a CGA or EGA hardware package is employed. Under a CGA
 configuration, only the two palettes shown in Table R.3 are available.

 Table R.3  $MRESNOCOLOR Mode CGA Palette Colors

                                 Color Index
      Palette
      Number        Color 1         Color 2       Color 3

      0             Blue            Red           Light gray
      1             Light blue      Light red     White

 Under the EGA configuration, the three palettes shown in Table R.4 are
 available in the $MRESNOCOLOR video mode.

 Table R.4  $MRESNOCOLOR Mode EGA Palette Colors

                               Color Index
      Palette
      Number        Color 1         Color 2       Color 3

      0             Green           Red           Brown
      1             Light green     Light red     Yellow
      2             Light cyan      Light red     Yellow

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    With an EGA in $MRESNOCOLOR video mode, Palette 3 is identical to
    Palette 1.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 Returns the value of the previous palette.

 þ  See also

      getvideoconfig, setvideomode

 þ  Example

      CC  CGAPAL.FOR - Illustrates CGA palettes using:
      CC               selectpalette

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2        dummy2
            INTEGER*4        dummy4, bkcolor( 8 )
            CHARACTER*7      bkname( 8 )
            CHARACTER*40     str
            RECORD /rccoord/ curpos

            DATA bkcolor / $BLACK , $BLUE    , $GREEN , $CYAN   ,
           +               $RED   , $MAGENTA , $BROWN , $WHITE  /
            DATA bkname  / 'BLACK', 'BLUE'   , 'GREEN', 'CYAN'  ,
           +               'RED'  , 'MAGENTA', 'BROWN', 'WHITE' /

      C
      C     Set video mode.
      C
            IF( setvideomode( $MRES4COLOR ) .EQ. 0 )
           +    STOP 'Error:  cannot set CGA color mode'
      C
      C     Palette loop
      C%
            DO i = 0, 3
               dummy2 = selectpalette( i )
      C
      C        Background color loop
      C
               DO k = 1, 8
                  CALL clearscreen( $GCLEARSCREEN )
                  dummy4 = setbkcolor( bkcolor(k) )
                  CALL settextposition( 1, 1, curpos )
                  WRITE ( str, 9000 ) bkname( k ), i
                  CALL outtext( str )
      C
      C           Foreground color loop
      C
                  DO j = 1, 3
                     dummy2 = setcolor( INT4( j ) )
                     dummy2 = ellipse( $GFILLINTERIOR, 100, j * 30, 220,
           +                           80 + ( j * 30 ) )
                  END DO
                  READ (*,*)    ! Wait for ENTER to be pressed
               END DO
            END DO

            dummy2 = setvideomode( $DEFAULTMODE )

       9000 FORMAT ('Background: ', A, 10x, 'Palette: ', I2)

            END

 The program CGAPAL.FOR draws a series of line segments while continuously
 changing the current palette.


 setactivepage

 þ  Interface

      INTERFACE TO FUNCTION setactivepage(page)
      INTEGER*2 setactivepage[FAR, C, ALIAS:"__setactivepage"]
      INTEGER*2 page        ! Memory page number
      END

 þ  Description

 For hardware and mode configurations with enough memory to support
 multiple-screen pages, setactivepage specifies where in memory graphics
 output is written. The page argument selects the current active page. The
 default page number is 0.

 Objects on the screen can be animated by alternating the graphics pages
 displayed. Use the setvisualpage function to display a completed graphics
 page while executing graphics statements in another active page.

 These functions can also be used to control text output if you use text
 functions, such as gettextcolor, gettextcursor, gettextposition, outtext,
 settextcolor, settextcursor, settextposition, settextwindow, and wrapon,
 instead of the standard FORTRAN I/O functions.

 This function is also available under OS/2.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The CGA hardware configuration has only 16K of RAM available to support
    multiple-video pages, and only in the text mode. The EGA and VGA
    configurations may be equipped with up to 256K of RAM for multiple-video
    pages in graphics mode.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 If successful, returns the page number of the previous active page. If the
 function fails, it returns -1.

 þ  See also

      getactivepage, getvisualpage, gettextcolor, gettextposition, outtext,
      settextcolor, settextposition, settextwindow, setvideomode,
      setvisualpage, wrapon

 þ  Example

 See the example program for getactivepage.


 setbkcolor

 þ  Interface

      INTERFACE TO FUNCTION setbkcolor(color)
      INTEGER*4 setbkcolor[FAR, C, ALIAS:"__setbkcolor"]
      INTEGER*4 color                ! Desired color
      END

 þ  Description

 The setbkcolor function sets the current background color to the color value
 color.

 In a color text mode (such as $TEXTC80), setbkcolor accepts a color index.
 The value for the default colors is listed in the description of the
 settextcolor function. For example, setbkcolor(2)  sets the background color
 to color index 2. The actual color displayed depends on the palette mapping
 for pixel value 2. The default is green in a color text mode.

 In a color graphics mode (such as $ERESCOLOR), setbkcolor accepts a color
 value. The value for the background color is given by the symbolic constants
 defined in the include file FGRAPH.FD. For example, setbkcolor($GREEN) sets
 the background color in a graphics mode to green. These symbolic constants
 are provided as a convenience in defining and manipulating the most common
 colors. The actual range of colors is much greater.

 The function setcolor accepts an INTEGER*4 value as an argument. It is a
 color index.

 Whenever an argument is INTEGER*4, it refers to a color value, and whenever
 it is INTEGER*2, it refers to a color index. The two exceptions are the
 functions setbkcolor and getbkcolor.

 Since the background color is color index 0, the remappalette function will
 do the same thing that setbkcolor does. Unlike remappalette, however,
 setbkcolor does not require an EGA or VGA environment.

 In a text mode, the setbkcolor function does not affect anything already
 appearing on the display; stebkcolor affects only the subsequent output. In
 a graphics mode, it immediately changes all background pixels.

 This function is also available under OS/2.

 þ  Return value

 Returns the pixel color value of the old background color.

 þ  See also

      getbkcolor, remappalette, selectpalette

 þ  Example

 See the example program for gettextcolor.


 setcliprgn

 þ  Interface

      INTERFACE TO SUBROUTINE
    + setcliprgn[FAR, C, ALIAS:"__setcliprgn"](x1, y1, x2, x2)
      INTEGER*2 x1, y1           ! Upper left corner of clip region
      INTEGER*2 x2, y2           ! Lower right corner of clip region
      END

 þ  Description

 The setcliprgn function limits the display of subsequent graphics output and
 font text output to the part that fits within a particular area of the
 screen (the "clipping region"). The physical points (x1, y1) and (x2, y2)
 are the upper left and lower right corners of the rectangle that defines the
 clipping region. The setcliprgn function does not change the
 viewport-coordinate system; it merely masks the screen.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The setcliprgn function affects graphics and font text output only. To
    mask the screen for text output, use the settextwindow function.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 There is no return value.

 þ  See also

      settextwindow, setvieworg, setviewport, setwindow

 þ  Example

            INCLUDE   'FGRAPH.FI'
            INCLUDE   'FGRAPH.FD'
            INTEGER*2 dummy

            dummy = setvideomode( $MRES16COLOR )
            CALL    setcliprgn( 0, 0, 200, 125 )
            dummy = ellipse( $GBORDER, 80, 50, 240, 200 )
            READ (*,*)          ! Press ENTER to exit
            dummy = setvideomode( $DEFAULTMODE )
            END

 This program draws an ellipse lying partly within a clipping region, as
 shown in Figure R.6.

 See also the example program for setvieworg.


 setcolor

 þ  Interface

      INTERFACE TO FUNCTION setcolor(color)
      INTEGER*2 setcolor[FAR, C, ALIAS:"__setcolor"]
      INTEGER*2 color               ! Desired color index
      END

 þ  Description

 The setcolor function sets the current color index to color. The color
 parameter is masked but always within range. The following graphics
 functions use the current color: arc, ellipse, floodfill, lineto, outgtext,
 pie, rectangle, and setpixel.

 The setcolor function accepts an INTEGER*2 value as an argument. It is a
 color index.

 The default color index is the highest numbered color index in the current
 palette.

 þ  Return value

 Returns the previous color. If setcolor fails (if used in a text mode), it
 returns -1.

 þ  See also

      arc, ellipse, floodfill, getcolor, lineto, outgtext, pie, rectangle,
      selectpalette, setpixel

 þ  Example

 See the example program for setlinestyle.


 setfillmask

 þ  Interface

      INTERFACE TO SUBROUTINE
      + setfillmask[FAR, C, ALIAS:"__setfillmask"](mask)
      INTEGER*1 mask[FAR, REFERENCE](8)     ! Mask array
      END

 þ  Description

 The setfillmask function sets the current fill mask. The mask is an
 8-by-8-bit array, where each bit represents a pixel. A 1 bit sets the
 corresponding pixel to the current color, while a 0 bit leaves the pixel
 unchanged. The mask is repeated over the entire fill area.

 If no fill mask is set (mask is all zerosÄÄthe default), only the current
 color is used in fill operations.

 þ  Return value

 There is no return value.

 þ  See also

      ellipse, floodfill, getfillmask, pie, rectangle

 þ  Example

            PROGRAM Fill
            INCLUDE 'FGRAPH.FD'
            INCLUDE 'FGRAPH.FI'

            INTEGER*1 style(8,6)/ #18, #18, #18, #18, #18, #18, #18, #18,
           +                      #08, #08, #08, #08, #08, #08, #08, #08,
           +                      #18, #00, #18, #18, #18, #00, #18, #18,
           +                      #00, #08, #00, #08, #08, #08, #08, #08,
           +                      #18, #18, #00, #18, #18, #00, #18, #18,
           +                     #08, #00, #08, #00, #08, #00, #08, #00 /

            INTEGER*1 oldstyle(8)  ! Placeholder for old style
            INTEGER loop

            dummy2 = setvideomode( $MRES4COLOR )
            CALL getfillmask( oldstyle )
      C
      C     Draw an ellipse under the middle few rectangles in a
      C     different color.
      C
            dummy2 = setcolor( 2 )
            dummy2 = ellipse( $GFILLINTERIOR, 120, 75, 200, 125 )
            dummy2 = setcolor( 3 )
            DO loop = 1, 6

      C
      C     Make 6 rectangles.
      C
               CALL setfillmask( style(1,loop) )
               dummy2 = rectangle( $GFILLINTERIOR, loop*40+5,90
           +                                      (loop+1)*40, 110 )
            END DO

            CALL setfillmask( oldstyle )   ! Restore old style
            READ (*,*)                     ! Wait for ENTER to be pressed
            dummy2 = setvode( $DEFAULTMODE )
            END

 This program draws an ellipse overlaid with six rectangles, each with a
 different fill mask, as shown in Figure R.7.

 See also the example program for setlinestyle.


 setfont

 þ  Interface

      INTERFACE TO FUNCTION setfont(options)
      CHARACTER *(*) options
    + [FAR, REFERENCE]                     ! String describing font characteri
      INTEGER*2 setfont[FAR, C, ALIAS: "__f_setfont"]
      END

 þ  Description

 The setfont finds a single font, from the set of registered fonts, that has
 the characteristics specified by the options parameter.

 If a font is found, it is set to the current font. The current font is used
 in all subsequent calls to the outgtext function. There can be only one
 active font.

 The setfont function searches the list of registered fonts for a font
 matching the specified characteristics.

 The function's argument consists of letter codes that describe the desired
 font. The option code specified in the options parameter are not case or
 position sensitive.

      Option                      Description

      t'fontname'                 Name the desired typeface

      hy                          Select character height, where y
                                  is the number of pixels

      wx                           Select character width, where x
                                   is the number of pixels

      f                            Select only a fixed-space font
                                   (should not be used with the p
                                   characteristic)

      p                            Select only a proportional-space
                                   font (should not be used with the
                                   f characteristic)

      v                            Select only a vector-mapped font
                                   (should not be used with the r
                                   characteristic)

      r                            Select only a raster-mapped
                                   ("bit-mapped") font (should not
                                   be used with the v
                                   characteristic)

      b                            Select the font that best fits
                                   the other parameters specified

      nx                           Select font number x, where x is
                                   less than or equal to the value
                                   returned by the registerfonts
                                   function

 You can request as many options as desired, except with nx, which should be
 used alone. If mutually exclusive options are requested (such as the pair
 f/p or r/v), the setfont function ignores them. There is no error detection
 for incompatible parameters used with nx.

 The t typeface specification option is specified as a "t" followed by the
 name of the font in apostrophes. The fontname may be any of the following
 valid fonts:

      Font Name                   Description

      Courier                     Fixed-width bit-mapped font with
                                  serifs

      Helv                        Sans serif proportional
                                  bit-mapped font

      Tms Rmn                     Proportional bit-mapped font with
                                  serifs

      Script                      Proportional vector-mapped font
                                  of slanted characters formed from
                                  nearly continuous lines

      Modern                      Proportional vector-mapped font
                                  without serifs

      Roman                       Proportional vector-mapped font
                                  with serifs

 A "b" in the options field causes the setfont routine to automatically
 select the "best fit" font that matches the other characteristics you have
 specified.

 If the b option is specified and at least one font is registered, setfont
 will always be able to set a font and will return 0 to indicate success.

 In selecting a font, the setfont routine uses the following precedence,
 rated from highest precedence to lowest:

   1. Pixel height
   2. Typeface
   3. Pixel width
   4. Fixed or proportional font

 You can also specify a pixel width and height for fonts. If a nonexistent
 value is chosen for either, and the b option is specified, the setfont
 function will choose the closest match.

 A smaller font size has precedence over a larger size. If setfont requests
 Helv 12 with best fit, and only Helv 10 and Helv 14 are available, setfont
 will select Helv 10.

 If you specify the nx parameter, setfont will ignore any other specified
 options and supply only the font number corresponding to x.

 If a nonexistent value is chosen for pixel height and width, the setfont
 function will apply a magnification factor to a vector-mapped font to obtain
 a suitable font size. This automatic magnification does not apply if the r
 (raster-mapped font) option is specified, or if a specific typeface is
 requested and there is no best-fit (b) option specified.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    The font functions affect only the output from the font-output function
    outgtext; no other FORTRAN Graphics Library run-time output functions are
    affected by font usage.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 Returns 0 to indicate success and -1 to indicate an error.

 An error occurs if a request for a specific font fails and the b option was
 not specified, or if fonts have not yet been registered.

 þ  See also

      getfontinfo, getgtextextent, outgtext, registerfonts, unregisterfonts

 þ  Example

 See the example program for registerfonts.


 setlinestyle

 þ  Interface

      INTERFACE TO SUBROUTINE
    + setlinestyle[FAR, C, ALIAS:"__setlinestyle"](mask)
      INTEGER*2 mask        ! Desired line-style mask
      END

 þ  Description

 The setlinestyle function selects the mask (line style) used for line
 drawing. The mask is a 16-bit number, where each bit represents a pixel in
 the line being drawn. If a bit is 1, the corresponding pixel is set to the
 color of the line (the current color). If a bit is 0, the corresponding
 pixel is left unchanged. The template is repeated for the entire length of
 the line. The default mask is #FFFF (a solid line). The setlinestyle
 function does not alter the styles for arcs, ellipses, or "pie-slice"
 wedges.

 þ  Return value

 There is no return value.

 þ  See also

      getlinestyle, lineto, rectangle

 þ  Example

      CC  FILL.FOR - Illustrates color, filling, and linestyle functions
      CC             including:         floodfill            setfillmask
      CC                                getlinestyle         setlinestyle
      CC                                setcolor
      CC
      CC  The getfillmask function is not shown, but its use is similar
      CC  to getlinestyle.

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*1            fill(8)
            INTEGER*2            dummy, xinc, yinc, i,
           +                     irand, xwid, ywid, rseed
            INTEGER*4            ncolor
            REAL*4               rand
            RECORD /xycoord/     xy
            RECORD /videoconfig/ vc

      C
      C     Find graphics mode.
      C
            IF( setvideomode( $MAXRESMODE ) .EQ. 0 )
           +   STOP 'Error:  cannot set graphics mode'
            CALL getvideoconfig( vc )

      C
      C     Size variables to mode.
      C
            xinc = vc.numxpixels / 8.0
            yinc = vc.numypixels / 8.0
            xwid = ( xinc / 2.0 ) - 4.0
            ywid = ( yinc / 2.0 ) - 4.0

      C
      C     Seed random-number generator.
      C
            CALL GETTIM( dummy, dummy, dummy, rseed)
            CALL SEED( rseed )

      C
      C     Draw ellipses and lines with different patterns.
      C
            DO x = xinc, ( vc.numxpixels - xinc ), xinc
               DO y = yinc, ( vc.numypixels - yinc ), yinc
      C
      C           Randomize fill and color.  Array FILL holds
      C           random numbers between 0 and 255.
      C
                  DO i = 1, 8
                     CALL RANDOM( rand )
                     fill(i) = INT1( rand * 256.0 )
                  END DO

                  CALL setfillmask( fill )
                  irand  = rand * 256.0
                  ncolor = MOD( irand, vc.numcolors ) + 1
                  dummy  = setcolor( ncolor )

      C
      C           Draw ellipse and fill with random color.
      C
                  dummy  = ellipse( $GBORDER, x - xwid, y - ywid,
           +                         x + xwid, y + ywid )
                  CALL RANDOM( rand )
                  irand  = rand * 256.0
                  i      = ncolor
                  ncolor = MOD( irand, vc.numcolors ) + 1
                  dummy  = setcolor( ncolor )
                  dummy  = floodfill( x, y, i )
      C
      C           Draw vertical and horizontal lines. Vertical line style
      C           is anything other than horizontal style. Since lines
      C           are overdrawn with several line styles, this has the
      C           effect of combining colors and styles.
      C
                  CALL RANDOM( rand )
                  irand = rand * 256.0
                  CALL setlinestyle( irand )
                  CALL moveto( 0, y + ywid + 4, xy )
                  dummy = lineto( vc.numxpixels - 1, y + ywid + 4 )
      C
      C           Get linestyle, invert bits, and reset linestyle.
      C
                  CALL setlinestyle( 255 - getlinestyle() )
                  CALL moveto( x + xwid + 4, 0, xy )
                  dummy = lineto( x + xwid + 4, vc.numypixels - 1 )
               END DO
            END DO

            READ (*,*)       ! Wait for ENTER to be pressed
            dummy = setvideomode( $DEFAULTMODE )
            END

 This program draws filled ovals and lines on the screen. It uses a variety
 of fill patterns and line styles in different colors.


 setpixel, setpixel_w

 þ  Interface

      INTERFACE TO FUNCTION setpixel(x, y)
      INTEGER*2 setpixel[FAR, C, ALIAS:"__setpixel"]
      INTEGER*2 x, y                                    ! Target pixel
      END

      INTERFACE TO FUNCTION setpixel_w(wx, wy)
      INTEGER*2 setpixel[FAR, C, ALIAS:"__f_setpixel_w"]
      DOUBLE PRECISION wx, wy                           ! Target pixel
      END

 þ  Description

 The setpixel  and the setpixel_w functions set a pixel at a specified
 location to the current color.

 The function setpixel sets the pixel at the viewport-coordinate point (x, y)
 to the current color.

 The function setpixel_w sets the pixel at the window-coordinate point (wx,
 wy) to the current color.

 þ  Return value

 The setpixel and setpixel_w functions return the previous value of the
 target pixel. If the function fails (for example, the point lies outside of
 the clipping region), it returns -1.

 þ  See also

      getpixel, setcolor

 þ  Example

      CC  FIGURE.FOR - Illustrates graphics drawing functions including:
      CC               arc      ineto     pie         setpixel
      CC               ellipse  moveto    rectangle

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2        dummy, x, y
            INTEGER*4        ncolor
            RECORD /xycoord/ xy

      C
      C     Find graphics mode.
      C
            IF ( setvideomode( $MAXRESMODE ) .EQ. 0 )
           +     STOP 'Error:  cannot set graphics mode'

            WRITE (*,*) ' Press ENTER to continue'
      C
      C     Draw pixels.
      C
            ncolor = 2
            dummy  = setcolor( ncolor )
            x      = 10
            DO y = 50, 89, 3
               dummy = setpixel( x, y )
               x     = x + 2
            END DO
            READ (*,*) ! Wait for ENTER to be pressed
      C
      C     Draw lines.
      C
            ncolor = ncolor + 1
            dummy  = setcolor( ncolor )
            x      = 60
            DO y = 50, 89, 3
               CALL moveto( x, y, xy )
               dummy = lineto( x + 20, y )
            END DO
            READ (*,*) ! Wait for ENTER to be pressed
      C
      C     Draw rectangles.
      C
            ncolor = ncolor + 1
            dummy  = setcolor( ncolor )
            x      = 110
            y      = 70
            dummy  = rectangle( $GBORDER,       x - 20, y - 20, x, y )
            dummy  = rectangle( $GFILLINTERIOR, x + 20, y + 20, x, y )
            READ (*,*) ! Wait for ENTER to be pressed
      C
      C     Draw ellipses.
      C
            ncolor = ncolor + 1
            dummy  = setcolor( ncolor )
            x      = 160
            dummy  = ellipse( $GBORDER,       x - 20, y - 20, x, y )
            dummy  = ellipse( $GFILLINTERIOR, x + 20, y + 20, x, y )
            READ (*,*) ! Wait for ENTER to be pressed

      C
      C     Draw arcs.
      C
            ncolor = ncolor + 1
            dummy  = setcolor( ncolor )
            x      = 210
            dummy  = arc( x - 20, y - 20, x, y, x, y - 10, x - 10, y )
            dummy  = arc( x + 20, y + 20, x, y, x + 10, y + 20, x + 20,
           +              y + 10 )
            READ (*,*) ! Wait for ENTER to be pressed
      C
      C     Draw pies.
      C
            ncolor = ncolor + 1
            dummy  = setcolor( ncolor )
            x      = 260
            dummy  = pie( $GBORDER,   x - 20, y - 20, x, y, x,
           +              y - 10, x - 10, y )
            dummy  = pie( $GFILLINTERIOR, x + 20, y + 20, x, y, x + 10,
           +              y + 20, x + 20, y + 10 )
            READ (*,*) ! Wait for ENTER to be pressed
            dummy = setvideomode( $DEFAULTMODE )
            END

 The program FIGURE.FOR draws a series of small shapes in a variety of
 colors.


 settextcolor

 þ  Interface

      INTERFACE TO FUNCTION settextcolor(index)
      INTEGER*2 settextcolor[FAR, C, ALIAS:"__settextcolor"]
      INTEGER*2 index               ! Desired color index
      END

 þ  Description

 The settextcolor function sets the current text color. The value of index
 specifies the new color index; the default value is the same as the maximum
 color index. The settextcolor routine sets the color for the outtext
 function. It does not affect the color of the WRITE statement or the color
 of text output with the outgtext font routine. Use the setcolor function to
 change the color of font output.

 In text color mode, you can specify a color index in the range 0-31. The
 colors in the range 0-15 are interpreted to be normal; colors in the range
 16-31 are the same as those in the range 0-15, except that the text blinks.
 The normal color range is defined below.

      Index      Color       Index     Color

      0          Black       8         Dark gray
      1          Blue        9         Light blue
      2          Green       10        Light green
      3          Cyan        11        Light cyan
      4          Red         12        Light red
      5          Magenta     13        Light magenta
      6          Brown       14        Yellow
      7          White       15        Bright white

 In every text mode, including monochrome, the getvideoconfig function
 returns the value 32 for the number of available colors. The value 32
 indicates the range of values (0-31) accepted by the settextcolor function.
 This includes 16 normal colors (0-15) and 16 blinking colors (16-31). To
 select blinking text, add 16 to the normal color value. Some color values
 appear the same on monochrome monitors. However, because blinking is
 selected in the same manner, the text mode for monochrome monitors uses the
 same range (0-31) as the color text mode.

 This function is also available under OS/2.

 þ  Return value

 Returns the color index of the previous text color.

 þ  See also

      gettextcolor, outtext

 þ  Example

 See the example program for gettextcolor.


 settextcursor

 þ  Interface

      INTERFACE TO FUNCTION settextcursor(attr)
      INTEGER*2 settextcursor[FAR, C, ALIAS:"__settextcursor"]
      INTEGER*2 attr         ! Cursor attribute
      END

 þ  Description

 The settextcursor function sets the cursor shape based on the attribute
 specified by attr. The high-order byte of attr determines the start line of
 the cursor. The low-order byte of attr determines the stop line of the
 cursor. The settextcursor function uses the same format as the BIOS routines
 in setting the cursor. Values for the cursor attribute are shown below.

      Attribute              Cursor Shape

      #0707                  Underline

      #0007                  Full block cursor

      #0607                  Double underline

      #2000                  No cursor

 This function is also available under OS/2. Note that this function works
 only in text video modes.

 þ  Return value

 Returns the previous cursor attribute or -1 if an error occurs, such as
 calling the function in a graphics screen mode.

 þ  See also

      displaycursor, gettextcursor

 þ  Example

      CC  CURSOR.FOR - Illustrates cursor functions including:
      CC               displaycursor  gettextcursor  settextcursor

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2            dummy, oldcursor, newcursor,
           +                     curs_start, curs_end, block
            CHARACTER*40         str
            RECORD /rccoord/     curpos
            RECORD /videoconfig/ vc

      C
      C     Save old cursor shape and make sure cursor is on.
      C

            CALL clearscreen( $GCLEARSCREEN )
            oldcursor = gettextcursor()
            dummy     = displaycursor( $GCURSORON )
            CALL settextposition( 1, 1, curpos )
            CALL outtext( 'Press ENTER to continue . . .' )
      C
      C     Change cursor shape.
      C     Color cursor uses scan lines 0-7, monochrome uses 0-13.
      C
            CALL getvideoconfig( vc )
            block = 7
            IF( vc.mode .EQ. $TEXTMONO ) block = 13
            curs_start = 0
            DO curs_end = block, 0, -1
               CALL settextposition( 10, 1, curpos )
               WRITE (str, 9000) curs_start, curs_end
               CALL outtext( str )
               newcursor = (curs_start * 256) + curs_end
               dummy     = settextcursor( newcursor )
               READ (*,*)  ! Wait for ENTER to be pressed
               curs_start = curs_start + 1
            END DO

            WRITE (str, '(A13)') 'Cursor off:  '
            CALL settextposition( 12, 1, curpos )
            CALL outtext( str, 13 )
            dummy = displaycursor( $GCURSOROFF )
            READ (*,*)  ! Wait for ENTER to be pressed
            WRITE (str, '(A13)') 'Cursor on :  '
            CALL settextposition( 13, 1, curpos )
            CALL outtext( str )
            dummy = settextcursor( block )
            dummy = displaycursor( $GCURSORON )
            READ (*,*)  ! Wait for ENTER to be pressed
      C
      C     Restore original cursor shape.
      C
            dummy = settextcursor( oldcursor )
            CALL clearscreen( $GCLEARSCREEN )

       9000 FORMAT ( 'Start line:', I4, 8x, 'Endline:', I4, 3x )

            END

 The program CURSOR.FOR shows the effect of changing the start and stop lines
 for the cursor, and the effect of turning the cursor off and on.


 settextposition

 þ  Interface

      INTERFACE TO SUBROUTINE
    + settextposition[FAR, C, ALIAS:"__f_settextposition"](row, column, s)
      INTEGER*2 row, column              ! New output start position
      STRUCTURE/rccoord/
            INTEGER*2 row                ! Row coordinate
            INTEGER*2 col                ! Column coordinate
      END STRUCTURE
      RECORD/rccoord/s                   ! Returns the previous text position
      END

 þ  Description

 This function relocates the current text position to the display point (row,
 column). Subsequent text produced with the outtext function (as well as
 standard console I/O routines, such as PRINT) begins at that point.

 The previous text position is returned in the rccoord structure variable s,
 defined in FGRAPH.FD.

 This subroutine is also available under OS/2.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING
    The WRITE statement sent its carriage-return (CR) and line-feed (LF)
    characters to the screen at the beginning of the first I/O statement
    following the WRITE statement. This can cause unpredictable text
    positioning if you mix the graphics routines settextposition and outtext
    with the WRITE statement. You can minimize the effect by using the
    backslash (\) format descriptor (suppress the CR-LF characters) in the
    associated FORMAT statement.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 There is no return value.

 þ  See also

      gettextposition, outtext, settextwindow

 þ  Example

 See the example program for setviewport.


 settextrows

 þ  Interface

      INTERFACE TO FUNCTION settextrows(rows)
      INTEGER*2 settextrows[FAR, C, ALIAS:"__settextrows"]
      INTEGER*2 rows         ! Number of text rows
      END

 þ  Description

 The settextrows function specifies the number of text rows used in text
 modes, requiring hardware that supports 43- or 50-line modes, such as VGA or
 EGA. In the rows argument, $MAXTEXTROWS chooses the number of rows
 available: in text mode, 50 rows (VGA); 43 (EGA); and 25 on all others. In
 graphics modes supporting 30 or 60 rows, it chooses  60. This function is
 also available under OS/2.

 þ  Return value

 Returns the numbers of rows set, or returns 0 if an error occurred.

 þ  See also

      getvideoconfig, setvideomode, setvideomoderows

 þ  Example

      CC  SETROWS.FOR - Illustrates: settextrows

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2    rows
            CHARACTER*2  arg
      C
      C     Get command line argument: 25, 43, or 50.
      C
            CALL GETARG( 1, arg, 2 )
            rows = 0
            IF( arg .EQ. '25' ) rows = 25
            IF( arg .EQ. '43' ) rows = 43
            IF( arg .EQ. '50' ) rows = 50
      C
      C     Make sure new rows are valid and the same as requested rows.
      C
            IF( (rows .EQ. 0) .OR. (settextrows( rows ) .NE. rows) ) THEN
               WRITE (*,*) 'Syntax: SETROWS [ 25 | 43 | 50 ]'
               STOP 01
            END IF
      C
      C     Return 0 if successful
      C
            STOP 00
            END

 This program accepts a command-line argument, checking for a valid number of
 rows for the current display.


 settextwindow

 þ  Interface

      INTERFACE TO SUBROUTINE
      + settextwindow[FAR, C, ALIAS:"__settextwindow"](r1, c1, r2, c2)
      INTEGER*2 r1, c1           ! Upper left corner of window
      INTEGER*2 r2, c2           ! Lower right corner of window
      END

 þ  Description

 The settextwindow function specifies a window in row and column coordinates
 where all the text output to the screen is displayed. The arguments (r1, c1)
 specify the upper left corner of the text window, and the arguments (r2, c2)
 specify the lower right corner of the text window.

 Text is output from the top of the text window down. When the text window is
 full, the lines begin to scroll off the top of the window.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    This function does not affect the output of the font display routine
    outgtext. Use the setviewport function to control the display area for
    fonts.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This subroutine is also available under OS/2.

 þ  Return value

 There is no return value.

 þ  See also

      gettextposition, outtext, settextposition

 þ  Example

 See the example program for setvideomoderows.


 setvideomode


 þ  Interface

      INTERFACE TO FUNCTION setvideomode(mode)
      INTEGER*2 setvideomode[FAR, C, ALIAS:"__setvideomode"]
      INTEGER*2 mode         ! Desired mode
      END

 þ  Description

 The setvideomode function selects a screen mode appropriate for a particular
 hardware/ display configuration. The mode argument can be one of the
 symbolic constants shown in Table R.5 below and defined in FGRAPH.FD.

 $MAXCOLORMODE and $MAXRESMODE in the mode argument choose the graphics mode
 with the most colors or the highest resolution, respectively. They fail for
 adapters that have no graphics modes. Table R.6 below shows the specific
 modes selected by the constants $MAXCOLORMODE and $MAXRESMODE for different
 adapter/monitor combinations.

 In $HERCMONO mode, only monochrome (two-color) text and graphics are
 supported. The screen resolution is 720 by 348 pixels. The text dimensions
 are 80 columns by 25 rows, with a 9-by-14-character box. The bottom two scan
 lines of row 25 are not visible.

 You must install the Hercules driver MSHERC.COM before running your program.
 Type MSHERC  to load the driver. This can be done from an AUTOEXEC.BAT file.

 If you use both a Hercules monochrome and a color video card, install
 MSHERC.COM with the /H (/HALF) option. The /H option causes the driver to
 use one instead of two graphics pages. This prevents the two video cards
 from attempting to use the same memory. You do not have to use the /H option
 if you have only a Hercules card. See your Hercules hardware manual for more
 details on compatibility.

 This function is also available under OS/2.

 þ  Return value

 Returns the number of text rows if the function is successful. If an error
 occurs (that is, if the mode selected is not supported by the current
 hardware configuration), the function returns 0.

 þ  See also

      getvideoconfig

 þ  Example

 See the example program for setvideomoderows.

 Table R.5  Symbolic Constants for Screen Mode

 Mode             Type                 Size        Colors       Adapter
 $DEFAULTMODE     Hardware              Hardware     Hardware      Hardware
                  default               default      default       default
 $TEXTBW40        M/T                   40 x 25      16            CGA
 $TEXTC40         C/T                   40 x 25      16            CGA
 $TEXTBW80        M/T                   80 x 25      16            CGA
 $TEXTC80         C/T                   80 x 25       6            CGA
 $MRES4COLOR      C/G                   320 x 200     4            CGA
 $MRESNOCOLOR     M/G                   320 x 200     4            CGA
 $HRESBW          M/G                   640 x 200     2            CGA
 $TEXTMONO        M/T                   80 x 25       1            MDPA
 $HERCMONO        Hercules graphics     ---          ---           HGC
 $MRES16COLOR     C/G                   320 x 200    16            EGA
 $HRES16COLOR     C/G                   640 x 200    16            EGA
 $ERESNOCOLOR     M/T                   640 x 350     1            EGA
 $ERESCOLOR       C/G                   640 x 350    64            EGA
 $VRES2COLOR      C/G                   640  480      2            VGA
 $VRES16COLOR     C/G                   640  480     16            VGA
 $MRES256COLOR    C/G                   320  200    256            VGA
 $ORESCOLOR       C/G                   640  400    1 of 16        Olivetti co
                                                                   graphics
 $MAXCOLORMODE    C/G                See Table R.6  Maximum        CGA
 $MAXRESMODE      C/G                   Maximum     See Table R.6  CGA


 Table R.6  Modes Selected by $MAXCOLORMODE and $MAXRESMODE

 Adapter/Monitor      $MAXCOLORMODE     $MAXRESMODE

 MDPA                 Fail              Fail
 HGC                  $HERCMONO         $HERCMONO
 CGA Color           $MRES4COLOR       $HRESBW
 CGA Noncolor        $MRESNOCOLOR      $HRESBW
 OCGA                 $MRES4COLOR       $ORESCOLOR
 OEGA Color           $ERESCOLOR        $ORESCOLOR
 EGA Color 256K       $HRES16COLOR      $HRES16COLOR
 EGA Color 64K        $HRES16COLOR      $HRES16COLOR
 EGA > 256K           $ERESCOLOR        $ERESCOLOR
 EGA > 64K            $HRES16COLOR      $ERESCOLOR
 EGA Mono             $ERESNOCOLOR      $ERESNOCOLOR
 MCGA                 $MRES256COLOR     $VRES2COLOR
 VGA/OVGA             $MRES256COLOR     $VRES16COLOR

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    Only standard hardware is described here, but display hardware that is
    strictly compatible with IBM, Hercules, or Olivetti hardware should also
    work as described.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 setvideomoderows

 þ  Interface

      INTERFACE TO FUNCTION setvideomoderows(mode, rows)
      INTEGER*2 setvideomoderows[FAR, C, ALIAS:"__setvideomoderows"]
      INTEGER*2 mode                  ! Desired mode
      INTEGER*2 rows                  ! Number of text rows
      END

 þ  Description

 The setvideomoderows function selects a screen mode for a particular
 hardware/display combination. The options for the symbolic constant mode
 (the screen mode) are given in the reference pages for setvideomode.

 The setvideomoderows argument rows specifies the number of text rows to be
 used in a text mode. Optionally, placing the constant $MAXTEXTROWS in the
 rows argument chooses the maximum number of rows available. In text mode
 this is 50 rows for a VGA, 43 for an EGA, and 25 on all others. In graphics
 modes that support 30 or 60 rows,  $MAXTEXTROWS  chooses 60 rows.

 þ  Return value

 Returns the numbers of rows set. The function returns 0 if an error occurred
 (for example, if the mode is not supported).

 þ  See also

      getvideoconfig, settextrows, setvideomode

 þ  Example

      CC  MODES.FOR - Illustrates configuration and text window
      CC              functions including:
      CC              getvideoconfig     outtext         settextwindow
      CC              setvideomoderows   setvideomode

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            PARAMETER            ( NUM_ROWS =  5 )
            PARAMETER            ( NUM_MODE = 17 )

            INTEGER*2              dummy, irow, x, y
            INTEGER*2              modes( NUM_MODE ), rows( NUM_ROWS )
            CHARACTER*12           names( NUM_MODE ), str
            RECORD / videoconfig / vc

            DATA modes / $TEXTBW40     ,  $TEXTC40     ,  $TEXTBW80    ,
           +             $TEXTC80      ,  $MRES4COLOR  ,  $MRESNOCOLOR ,
           +             $HRESBW       ,  $TEXTMONO    ,  $HERCMONO    ,
           +             $MRES16COLOR  ,  $HRES16COLOR ,  $ERESNOCOLOR ,
           +             $ERESCOLOR    ,  $VRES2COLOR  ,  $VRES16COLOR ,
           +             $MRES256COLOR ,  $ORESCOLOR   /


            DATA names / '    $TEXTBW40', '    $TEXTC40', '   $TEXTBW80',
           +             '     $TEXTC80', ' $MRES4COLOR', '$MRESNOCOLOR',
           +             '      $HRESBW', '   $TEXTMONO', '   $HERCMONO',
           +             ' $MRES16COLOR', '$HRES16COLOR', '$ERESNOCOLOR',
           +             '   $ERESCOLOR', ' $VRES2COLOR', '$VRES16COLOR',
           +             '$MRES256COLOR', '  $ORESCOLOR' /

            DATA rows  / 60, 50, 43, 30, 25 /

            dummy = displaycursor( $GCURSOROFF )
      C
      C     Try each mode.
      C
            DO i = 1, NUM_MODE
               DO j = 1, NUM_ROWS
      C
      C           Try each possible number of rows.
      C
                  irow = setvideomoderows( modes(i), rows(j) )
                  IF( ( irow .EQ. 0 )  .OR.  (rows(j) .NE. irow) ) THEN
                     GOTO 100
                  ELSE
                     CALL getvideoconfig( vc )
                     y = ( vc.numtextrows - 12 ) / 2
                     x = ( vc.numtextcols - 25 ) / 2
                  END IF
      C
      C           Use text window to place output in middle of screen.
      C
                  CALL settextwindow( y, x, vc.numtextrows - y,
           +                          vc.numtextcols - x - 2 )
      C
      C           Display all information on the screen.
      C
                  CALL outtext( 'Video mode:  ' // names(i) )
                  WRITE (str, '(I12)') vc.numxpixels
                  CALL outtext( 'X pixels:    ' // str )
                  WRITE (str, '(I12)') vc.numypixels
                  CALL outtext( 'Y pixels:    ' // str )
                  WRITE (str, '(I12)') vc.numtextcols
                  CALL outtext( 'Text columns:' // str )
                  WRITE (str, '(I12)') vc.numtextrows
                  CALL outtext( 'Text rows:   ' // str )
                  WRITE (str, '(I12)') vc.numcolors
                  CALL outtext( 'Colors:      ' // str )
                  WRITE (str, '(I12)') vc.bitsperpixel
                  CALL outtext( 'Bits/pixel:  ' // str )

                  WRITE (str, '(I12)') vc.numvideopages
                  CALL outtext( 'Video pages: ' // str )
                  WRITE (str, '(I12)') vc.mode
                  CALL outtext( 'Mode:        ' // str )
                  WRITE (str, '(I12)') vc.adapter
                  CALL outtext( 'Adapter:     ' // str )
                  WRITE (str, '(I12)') vc.monitor
                  CALL outtext( 'Monitor:     ' // str )
                  WRITE (str, '(I12)') vc.memory
                  CALL outtext( 'Memory:      ' // str )

                  READ (*,*)  ! Wait for ENTER to be pressed
        100    END DO
            END DO

            dummy = displaycursor( $GCURSORON )
            dummy = setvideomode( $DEFAULTMODE )
            END

 The program MODES.FOR demonstrates each of the possible mode and row
 combinations.


 setvieworg

 þ  Interface

      INTERFACE TO SUBROUTINE
      + setvieworg[FAR, C, ALIAS:"__f_setvieworg"](x, y, s)
      INTEGER*2 x, y                      ! New origin point
      STRUCTURE/xycoord/
            INTEGER*2 xcoord                          ! x-coordinate
            INTEGER*2 ycoord                          ! y-coordinate
      END STRUCTURE
      RECORD/xycoord/s[FAR, REFERENCE]                ! Returns the previous v
      END

 þ  Description

 The setvieworg function moves the view-coordinate origin (0, 0) to the
 physical point (x, y). All other view-coordinate points move the same
 direction and distance.

 The xycoord structure variable s, defined in FGRAPH.FD, returns the physical
 coordinates of the previous view origin.

 þ  Return value

 There is no return value.

 þ  See also

      getphyscoord, getviewcoord, getwindowcoord, setcliprgn, setviewport

 þ  Example

      CC  WINDOW.FOR - Illustrates windows and coordinate systems using
      CC               the following functions:
      CC               clearscreen    ellipse        ellipse_w
      CC               rectangle      rectangle_w    setcliprgn
      CC               setvieworg     setviewport    setwindow
      CC
      CC  Although not all illustrated here, functions ending in _w
      CC  are similar to rectangle_w and ellipse_w.

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2  dummy, xhalf, yhalf, xquar, yquar
            DOUBLE PRECISION  x_upleft, y_upleft, x_botrght, y_botrght
            RECORD / xycoord /     xy
            RECORD / videoconfig / vc

      C
      C     Find graphics mode.
      C
            IF( setvideomode( $MAXRESMODE ) .EQ. 0 )
           +    STOP 'Error:  cannot set graphics mode'
            CALL getvideoconfig( vc )

            xhalf = vc.numxpixels / 2
            yhalf = vc.numypixels / 2
            xquar = xhalf / 2
            yquar = yhalf / 2

      C
      C     First window - integer physical coordinates
      C
            CALL setviewport( 0, 0, xhalf - 1, yhalf - 1 )
            dummy = setcolor( 2 )
            dummy = rectangle( $GBORDER, 0, 0, xhalf - 1, yhalf - 1 )
            dummy = setcolor( 1 )
            dummy = ellipse( $GFILLINTERIOR, xquar / 4, yquar / 4,
           +                 xhalf - (xquar / 4), yhalf - (yquar / 4) )
            READ (*,*) ! Wait for ENTER to be pressed
            CALL clearscreen( $GVIEWPORT )
            dummy = rectangle( $GBORDER, 0, 0, xhalf - 1, yhalf - 1 )

      C
      C     Second window - integer world coordinates with clip region
      C
            CALL setcliprgn( xhalf, 0, vc.numxpixels, yhalf )
            CALL setvieworg( xhalf + xquar - 1, yquar - 1, xy )
            dummy = setcolor( 3 )
            dummy = rectangle( $GBORDER, -xquar + 1, -yquar + 1, xquar,
           +                   yquar )
            dummy = setcolor( 2 )
            dummy = ellipse( $GFILLINTERIOR, (-xquar * 3) / 4,
           +                 (-yquar * 3) / 4, (xquar * 3) / 4,
           +                 (yquar * 3) / 4 )
            READ (*,*) ! Wait for ENTER to be pressed
            CALL clearscreen( $GVIEWPORT )
            dummy = rectangle( $GBORDER, -xquar + 1, -yquar + 1, xquar,
           +                   yquar )

      C
      C     Third window
      C
            CALL setviewport( xhalf, yhalf, vc.numxpixels - 1,
           +                  vc.numypixels - 1 )
            dummy = setwindow( .FALSE., -4.0, -5.0, 4.0, 5.0 )
            dummy = setcolor( 4 )
            dummy = rectangle_w( $GBORDER, -4.0, -5.0, 4.0, 5.0 )
            dummy = setcolor( 3 )
            dummy = ellipse_w( $GFILLINTERIOR, -3.0, -3.5, 3.0, 3.5 )
            READ (*,*) ! Wait for ENTER to be pressed
            CALL clearscreen( $GVIEWPORT )
            dummy = rectangle_w( $GBORDER, -4.0, -5.0, 4.0, 5.0 )

      C
      C     Fourth window
      C
            CALL setviewport( 0, yhalf, xhalf - 1, vc.numypixels - 1 )
            dummy = setwindow( .FALSE., -4.0, -5.0, 4.0, 5.0 )
            x_upleft  = -4.0
            y_upleft  = -5.0
            x_botrght =  4.0
            y_botrght =  5.0
            dummy = setcolor( 5 )
            dummy = rectangle_w( $GBORDER, x_upleft, y_upleft,
           +                     x_botrght, y_botrght )
            x_upleft  = -3.0
            y_upleft  = -3.5
            x_botrght =  3.0
            y_botrght =  3.5
            dummy = setcolor( 4 )
            dummy = ellipse_w( $GFILLINTERIOR, x_upleft, y_upleft,
           +                   x_botrght, y_botrght )

            READ (*,*) ! Wait for ENTER to be pressed
            dummy = setvideomode( $DEFAULTMODE )
            END

 The program WINDOW.FOR draws several windows, using different methods and
 options for each window.


 setviewport

 þ  Interface

      INTERFACE TO SUBROUTINE
    + setviewport[FAR, C, ALIAS:"__setviewport"](x1, y1, x2, y2)
      INTEGER*2 x1, y1          ! Upper left corner of viewport
      INTEGER*2 x2, y2          ! Lower right corner of viewport
      END

 þ  Description

 The setviewport function redefines the graphics viewport. The setviewport
 function defines a clipping region in the same manner as setcliprgn, and
 then sets the viewport-coordinate origin to the upper left corner of the
 region. The physical points (x1, y1) and (x2, y2) are the upper left and
 lower right corners of the rectangular clipping region. Any window
 transformation done with the setwindow function applies only to the viewport
 and not to the entire screen.

 þ  Return value

 There is no return value.

 þ  See also

      setcliprgn, setvieworg, setwindow

 þ  Example

      CC  MAGNIFY.FOR - Illustrates translation between window and view
      CC                coordinate systems using the following functions:
      CC                getphyscoord     getviewcoord   getviewcoord_w
      CC                getwindowcoord   lineto         moveto
      CC                rectangle        rectangle_w    settextposition
      CC                setwindow        setviewport
      CC
      CC  Although not all illustrated here, functions ending in _w
      CC  are similar to rectangle_w.

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2              dummy, rseed, j, k, m, n
            INTEGER*2              coord(3,2,2), fill(2)
            INTEGER*4              i
            REAL*4                 rand
            DOUBLE PRECISION       x(2), y(2)
            CHARACTER*18           text
            RECORD / xycoord  /    xy, xy1
            RECORD / wxycoord /    wxy
            RECORD / rccoord  /    curpos
            RECORD / videoconfig / vc

            DATA text / 'magnification:  1x' /
            DATA fill / $GFILLINTERIOR, $GBORDER /

      C
      C     Find graphics mode.
      C
            IF( setvideomode( $MAXRESMODE ) .EQ. 0 )
           +    STOP 'Error:  cannot set graphics mode'
            CALL getvideoconfig( vc )

      C
      C     Find physical (pixel) coordinates for windows 1, 2, and 3.
      C
            coord(1,1,1) = vc.numxpixels * 3 / 16
            coord(1,1,2) = vc.numypixels * 7 / 32
            coord(1,2,1) = coord(1,1,1) + vc.numxpixels / 8
            coord(1,2,2) = coord(1,1,2) + vc.numypixels / 16
            coord(2,1,1) = vc.numxpixels * 9 / 16
            coord(2,1,2) = vc.numypixels * 5 / 32
            coord(2,2,1) = coord(2,1,1) + vc.numxpixels * 3 / 8
            coord(2,2,2) = coord(2,1,2) + vc.numypixels * 3 / 16
            coord(3,1,1) = 0
            coord(3,1,2) = vc.numypixels / 2
            coord(3,2,1) = vc.numxpixels - 1
            coord(3,2,2) = vc.numypixels - 1

      C
      C     Connect windows with lines.
      C
            dummy = setcolor( 4 )
            DO i = 1, 2
               DO j = 1, 2
                  DO k = 1, 2
                     CALL moveto( coord(i,j,1), coord(i,k,2), xy )
                     dummy = lineto( coord(i + 1,j,1), coord(i + 1,k,2) )
                  END DO
               END DO
            END DO

      C
      C     Label windows and frame with rectangles.
      C
            DO i = 1, 3
               dummy  = setcolor( i )
               row    = ( coord(i,1,2) * 25 ) / vc.numypixels
               column = ( coord(i,1,1) * 80 ) / vc.numxpixels
               CALL settextposition( row, column, curpos )
               CALL outtext( text )
               text(17:17) = '3'


               IF( i .EQ. 2 ) text(17:17) = '8'
               CALL setviewport(  coord(i,1,1), coord(i,1,2) ,
           +                      coord(i,2,1), coord(i,2,2) )
               CALL getviewcoord( coord(i,1,1), coord(i,1,2), xy  )
               CALL getviewcoord( coord(i,2,1), coord(i,2,2), xy1 )
               dummy = rectangle( $GBORDER, xy.xcoord, xy.ycoord,
           +                      xy1.xcoord, xy1.ycoord )
            END DO

      C
      C     Seed random number generator.
      C
            CALL GETTIM( dummy, dummy, dummy, rseed )
            CALL SEED( rseed )
      C
      C     Get random window coordinates (x, y) for rectangles,
      C     where x and y are between 0 and 1000.
      C
            DO i = 8, 15
               dummy = setcolor( i )
               CALL RANDOM( rand )
               x(1)  = rand * 980.0
               x(2)  = rand * ( 999.0 - x(1) ) + x(1)
               CALL RANDOM( rand )
               y(1)  = rand * 980.0
               y(2)  = rand * ( 999.0 - y(1) ) + y(1)
               k     = rand + 1.5

      C
      C        Display rectangles in normal and magnified views.
      C
               DO j = 1, 3
                  CALL setviewport( coord(j,1,1), coord(j,1,2) ,
           +                        coord(j,2,1), coord(j,2,2) )
                  dummy = setwindow( .TRUE., 0.0, 0.0, 1000.0, 1000.0 )
                  dummy = rectangle_w( fill(k), x(1), y(1), x(2), y(2) )

      C
      C           In last window, make rectangle sides 2 pixels wide by
      C           encasing unfilled rectangles with another rectangle.
      C           Convert window coords (x, y) to physical coords,
      C           adjust, and translate back into window coords.
      C
                  IF( (j .EQ. 3)  .AND.  (k .EQ. 2) ) THEN
                     m = -1
                     DO n = 1, 2
                        CALL getviewcoord_w( x(n), y(n), xy )
                        CALL getphyscoord( xy.xcoord, xy.ycoord, xy )
                        CALL getviewcoord( xy.xcoord+m, xy.ycoord+m, xy )
                        CALL getwindowcoord( xy.xcoord, xy.ycoord, wxy )
                        x(n) = wxy.wx
                        y(n) = wxy.wy
                        m    = 1
                     END DO
                  dummy = rectangle_w( fill(k), x(1), y(1), x(2), y(2) )
                  END IF
               END DO
            END DO

            READ (*,*)  ! Wait for ENTER to be pressed
            dummy = setvideomode( $DEFAULTMODE )
            END

 By translating a drawing between window and viewport coordinates, the
 program MAGNIFY.FOR enlarges the window's contents.

 See also the example program for setvieworg.


 setvisualpage

 þ  Interface

      INTERFACE TO FUNCTION setvisualpage(page)
      INTEGER*2 setvisualpage[FAR, C, ALIAS:"__setvisualpage"]
      INTEGER*2 page         ! Visual page number
      END

 þ  Description

 The setvisualpage function selects the current visual page on hardware that
 has an EGA or VGA adapter and enough memory to support multiple-screen
 pages. The page argument specifies the current visual page. The default page
 number is 0.

 This function is also available under OS/2.

 þ  Return value

 Returns the page number of the previous visual page. If the function fails,
 it returns a negative value.

 þ  See also

      getactivepage, getvisualpage, setactivepage, setvideomode

 þ  Example

 See the example program for getactivepage.


 setwindow

 þ  Interface

      INTERFACE TO FUNCTION setwindow(finvert, wx1, wy1, wx2, wy2)
      INTEGER*2 setwindow[FAR, C, ALIAS:"__setwindow"]
      LOGICAL*2 finvert
      DOUBLE PRECISION wx1, wy1          ! Upper left corner of window
      DOUBLE PRECISION wx2, wy2          ! Lower right corner of window
      END

 þ  Description

 The setwindow function defines a window bound by the specified coordinates.
 The arguments (wx1, wy1) specify the upper left corner of the window, and
 the arguments (wx2, wy2) specify the lower right corner of the window.

 The finvert argument specifies the direction of the coordinates. If finvert
 is .TRUE., the y-axis increases from the screen bottom to the screen top
 (Cartesian coordinates). If finvert is .FALSE., the y-axis increases from
 the screen top to the screen bottom (screen coordinates).

 Any window transformation done with the setwindow function applies only to
 the viewport and not to the entire screen.

 If wx1 equals wx2 or wy1 equals wy2, the function will fail.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    This function does not affect the output of the font display routine
    outgtext.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 þ  Return value

 Returns a nonzero value if successful. If the function fails (for example,
 if it is not in a graphics mode), it returns 0.

 þ  See also

      setviewport

 þ  Example

 See the example program for setviweorg and setviewport.


 unregisterfonts

 þ  Interface

      INTERFACE TO SUBROUTINE
      + unregisterfonts[FAR, C, ALIAS:"__unregisterfonts"]( )
      END

 þ  Description

 The unregisterfonts subroutine frees memory previously allocated and used by
 the registerfonts function. The unregisterfonts subroutine removes the
 header information for all fonts and unloads the currently selected font
 data from memory.

 Any attempt to use the setfont or outgtext function after calling
 unregisterfonts causes an error.

 þ  Return value

 There is no return value.

 þ  See also

      getfontinfo, getgtextextent, setfont, outgtext, registerfonts

 þ  Example

 See the example program for registerfonts.


 wrapon

 þ  Interface

      INTERFACE TO FUNCTION wrapon(option)
      INTEGER*2 wrapon[FAR, C, ALIAS:"__wrapon"]
      INTEGER*2 option               ! Wrap condition
      END

 þ  Description

 The wrapon function controls whether text output with the outtext function
 wraps to a new line or is simply clipped when the text output reaches the
 edge of the defined text window. The option argument can be one of the
 following symbolic constants:

      Constant                    Meaning

      $GWRAPOFF                   Truncates lines at window border

      $GWRAPON                    Wraps lines at window border

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
    This function does not affect the output of font routines.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This function is also available under OS/2.

 þ  Return value

 Returns the previous value of option. There is no error return.

 þ  See also

      settextwindow

 þ  Example

      CC  WRAP.FOR - Illustrates:
      CC             wrapon

            INCLUDE  'FGRAPH.FI'
            INCLUDE  'FGRAPH.FD'

            INTEGER*2              dummy2, old_color, old_mode, row, col
            INTEGER*4              dummy4, old_bk
            CHARACTER*22           text
            CHARACTER*80           border
            RECORD / rccoord /     curpos
            RECORD / videoconfig / vc

            DATA text / 'Here text DOES wrap.  ' /

            CALL getvideoconfig( vc )
            old_mode  = vc.mode
            old_color = gettextcolor()
            old_bk    = getbkcolor()
            IF( vc.numtextcols .NE. 80 ) THEN
               IF( setvideomode( $TEXTC80 ) .EQ. 0 )
           +       dummy2 = setvideomode( $TEXTMONO )
            END IF
            dummy2    = settextcolor( 14 )
            dummy4    = setbkcolor( 1 )
            CALL clearscreen( $GCLEARSCREEN )
      C
      C     Draw window borders.
      C
            DO i = 1, 80
               border(i:i) = CHAR( 219 )
            END DO
            CALL settextposition( 13, 1, curpos )
            CALL outtext( border )
            col = 60
            DO row = 1, 25
               IF( row .EQ. 13 ) col = 20
               CALL settextposition( row, col, curpos )
               CALL outtext( border(1:1) )
            END DO
      C
      C     Display wrapped and unwrapped text in windows.
      C
            dummy4 = setbkcolor( 0 )
            CALL settextwindow( 1, 1, 13, 59 )
            CALL settextposition( 1, 1, curpos )
            dummy2 = wrapon( $GWRAPON )
            dummy2 = settextcolor( 10 )
            DO i = 1, 32
               CALL outtext( text )
            END DO
            CALL outtext( text )

            CALL settextwindow( 14, 21, 25, 80 )
            CALL settextposition( 1, 1, curpos )
            dummy2 = wrapon( $GWRAPOFF )
            dummy2 = settextcolor( 12 )
            DO row = 1, 12
               CALL settextposition( row, 1, curpos )
               DO i = 1, 3
                  CALL outtext( text(1:15) // 'NOT ' // text(16:22) )
               END DO
            END DO

            READ (*,*)  ! Wait for ENTER to be pressed
            dummy2 = setvideomode( old_mode )
            dummy2 = settextcolor( old_color )
            dummy4 = setbkcolor( old_bk )
            END

 The program WRAP.FOR demonstrates the effect of wrapping and not wrapping
 text within a text window.


 Index
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 /4I2 and /4I4 options (FL)
 /4Yb and /4Nb options (FL)
 /4Yb option (FL)
 I8087/287/387 coprocessor
    math package
    suppressing use of

 A
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Address sizes
    code
    data
 Address variables
 Addresses
    far
    code
       defined
       large and huge models
       subprogram calls
    huge
       defined
       huge arrays
       huge model
    near
    default data segment
    defined
    medium model
    subprograms
 ADR
    address variable
    keyword
 ADS, address variable
 Affine mode
 /AH option (FL)
 ALIAS keyword
    FORTRAN, use in
 Alternate math library
 /AM option (FL)
 API
    calls, low level
       DosCreateThread
       DosExit
 Apostrophes, use
 arc, arcwxy
 arc, arc_w
 Arguments
    passing
       by value
       medium model
 Array descriptors
 Arrays
    addressing
    adjustable size
    assumed size
    constant use, minimizing in declarations
    declaring for efficient compilation
    EQUIVALENCE statements, used in
    formal arguments, used as
    huge
       addressing
 Assembly interfaces; writing
 Assembly
    calling from
       FORTRAN
    interfaces
       address parameters, used with
       FORTRAN
    passing by
       far reference
       near reference
    procedures
 Attributes
 Attributes, FORTRAN
 Attributes
    FAR
       adjustable-size arrays
       arguments in medium model
       assumed-size arrays
       default data segment
       effects
       huge model
       library routines, used with
       medium model
       using
    HUGE
       alternative to huge model
       common blocks
       default data segment
       effects
       large model
       library routines, used with
       medium model
       using
    lack of portability
    NEAR
       adjustable-size arrays
       alternative to medium model
       assumed-size arrays
       common blocks
       declaring subprograms with
       effects
       huge model
       large and huge models
       library routines, used with
       subprograms
    VALUE
 Axes
    screen

 B
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 BASIC
    arrays
    BYVAL keyword
    calling convention
    calling from
       FORTRAN
    CALLS statement
    common blocks
    compiling
    naming convention
    parameter-passing
       defaults
       methods
    passing by
       far reference
       near reference
       value
    procedures
    string format
    types, user-defined
    VARPTR keyword
    VARSEG keyword
 BEGINTHREAD run-time-library function
 _BEGINTHREAD run-time-library function
 BLOCKSIZE option
 Bold type, use
 BOOLEAN data type
 Bounding rectangle
 Brackets, double, use
 BYVAL keyword

 C
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 C attribute
    FORTRAN, used in
    Pascal, used in
 C language
    calling convention
    calling from
       FORTRAN
    compiling
    FORTRAN, linking with
    functions
    memory models
    naming convention
    parameter-passing
       defaults
       methods
    passing by
       far reference
       near reference
       value
    pointers
    string format
    structures
 C-string feature, FORTRAN
 Calling conventions
 CALLS statement
 CDECL keyword
 ICGA (Color Graphics Adapter)
 Character types
    variables as format specifiers
 Classes
    defined
 clearscreen, library routine
 clearscreen
 Clipping region
 Clipping regions
 Code size
    limits
 Color Graphics Adapter see CGA
 Color indexes
 Color text modes see Video modes, text
 Color values
 Column-major order
 Common blocks
    formal arguments, used as
    large and huge models, used in
    medium model, used in
    memory allocation
    restrictions
 Compact memory model
 Compatibility
    8087/287/387 library
    emulator library
    floating-point options
 Compiler options
    /AT
    /Lp
    /Zl
 COMPLEX data type, FORTRAN
 CONST keyword
 CONSTS keyword
 Conventions, document
 Coordinates
    physical
    text
    view
    viewport
    window
 Coprocessor
    8087/287/387
    suppressing use of
 Courier, use of typeface
 CS register

 D
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Data address size
 Data segments
    data threshold
    default
       contents
       limits
       naming
       near addresses
       threshold, setting
    naming
 Data threshold, setting
 _DATA segment
 $DEBUG metacommand
 .DEF files
    introduced
 Default
    data segment
       contents
       data threshold
       defined
       limits
       object file
 Denormal
    numbers
    propagating
 Display attributes
 displaycursor
 DLL symbolic constant
 Document conventions
 DOSCALLS.LIB library
 DosCreateThread API call
 DosExit API call
 Double-precision reals
 Drawing see graphics
 DS register
    near addresses
    near, far, and huge addresses
 Dynamic linking
 Dynamic-link library
    creating
    defined
    multithread
       creating
       described
       using
    run time
       creating
       global data
       shared strings
       thread identification
    summary of components

 E
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Edit descriptors
    Tc
    TLc
 Edit lists
 EGA (Enhanced Graphics Adapter)
 ellipse, ellipsew, ellipsewxy
 ellipse, ellipse_w
 Ellipses, use
 EMOEM.ASM
 Emulator
    described
    function calls
    in-line instructions
    library
 ENDTHREAD run-time-library function
 Environment variables
    NO87
 EQUIVALENCE statement
 ERESCOLOR video mode
 Error messages
    removing text during SETUP
 ES register
 Exception handling
    control word
    dividing by zero
    invalid operation
    overflow and underflow
    precision
    status byte
 EXEMOD
    stack size, setting
 External data

 F
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Far addresses
    code
    data threshold
    defined
    large and huge models
    subprogram calls
 FAR attribute
    adjustable-size arrays
    assumed-size arrays
    default data segment
    effects
    huge model
    library routines, used with
    medium model, arguments in
    using
 Far reference parameters
    BASIC
    C
    FORTRAN
 FDLLOBJS.LIB library
 FGRAPH.FI
    constants in
 File-control blocks (FCBs)
 Files
    FORTRAN
       access modes
       binary direct
       binary sequential
       data formats
       formatted direct
       formatted sequential
       record structure
       unformatted direct
       unformatted sequential
 Fill flag
 Fill flags
 FL options
    /4I2 and /4I4
    /4Yb and 4Nb
    /AH
    /AL
    /AM
    /F
    /FPa
    /FPc87
       8087/80287 coprocessor
       described
       floating-point operations
    /FPc
       described
       flexibility
       floating-point operations
    /FPi
    /FPi87
    /Gt
       described
       FAR attribute, compared with
       medium memory model
       standard memory models
    /NM
    /NT
    /Zl
    data threshold
       /Gt option
       default value, setting
       moving data items
    debug
    default integer size
    memory model
       huge
       large
       medium
    naming
       modules
       text segments
    stack size, setting
    suppressing
       library selection
 $FLOATCALLS metacommand
 Floating point
    exceptions
       disabling
       listed
    options
       compatibility
       default
       default libraries
       function calls
       in-line instructions
       maximum efficiency with coprocessor
       maximum efficiency without coprocessor
       maximum flexibility
       maximum precision with coprocessor
 floodfill, floodfillw
 .FNT files
 .FON files
 Fonts
    .FNT files
    .FON files
    bit-mapped
    data structure
    displaying
    example program
    functions, listed
    memory allocation
    option list
    spacing
    table
    type sizes
    typefaces
    vector-mapped
 Foreground colors
 Formatted I/O
 FORTRAN
    C, linking with
 fortran keyword
 FORTRAN.LIB
 FORTRAN
    ALIAS keyword
    arrays
    C attribute
    calling convention
    calls to
       BASIC
       C
       other languages
       Pascal
    common blocks
    compiling
    COMPLEX data type
    functions and subroutines
    INTERFACE statement
    LOC function
    LOCFAR function
    LOCNEAR function
    LOGICAL data type
    naming convention
    parameter-passing
       defaults
       keywords
       methods
    PASCAL attribute
    passing by
       far reference
       near reference
       value
    string formats
 /FPa option (FL)
 /FPc option (FL)
    described
    floating-point operations
 /FPc87 option (FL)
 /FPi option (FL)
 /FPi87 option (FL)
 FUNCTION procedures
 functions
 Functions
    listed

 G
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 getactivepage
 getbkcolor, library routine
 getbkcolor
 getcolor
 getcurrentposition, getcurrentpositionw
 getcurrentposition, getcurrentposition_w
 getfillmask
 getfontinfo
 getgtextextent
 getimage, getimage_w
 getlinestyle
 getphyscoord
 getpixel, getpixelw
 getpixel, getpixel_w
 gettextcolor, library routine
 gettextcolor
 gettextcursor
 gettextposition
 getvideoconfig
 getviewcoord, getviewcoordw, getviewcoordwxy
 getviewcoord, getviewcoord_w
 getvisualpage
 getwindowcoord
 GFILLINTERIOR, fill flag
 Ggraphics
    color selection
 Ggraphics
    palettes
 Global data, sharing
 GRAPH.H
    described
 Graphics
    attributes
    color selection
    configuration
    coordinates
    display characteristics
    see also drawing
    font functions. See Fonts
    font functions
       described
       use
    fonts
    image transfer
    output
       arc, arcwxy
       arc, arc_w
       ellipse, ellipsew, ellipsewxy
       ellipse, ellipse_w
       lineto, linetow
       pie, piewxy
       pie, pie_w
       rectangle, rectanglew, rectanglewxy
       rectangle, rectangle_w
    palettes
    parameters
    physical coordinates
    text output
    text support, listed
    text support
       settextrows
       settextwindow
       setvideomoderows
       setwindow
    view coordinates
    viewport coordinates
    window coordinates
 /Gt option (FL)
    described
    FAR attribute, compared with
    medium memory model
    standard memory models

 H
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Header files see GPROC.FI
 Hercules Graphics Card
 HRES16COLOR video mode
 HUGE attribute
    alternative to huge model
    common blocks
    default data segment
    effects
    large model
    library routines, used with
    medium model, arguments in
    using
 Huge memory model, use with C
 Huge
    addresses
       arrays
       defined
       huge model
    memory model. See also Memory models
       /AH option (FL)
       adjustable-size arrays
       assumed-size arrays
       described

 I
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 I/O
    buffers
    formatted
    list-directed
 imagesize, imagesize_w
 IMPLIB (Microsoft Import Library Manager)
 Imports library
 In-line instructions
 Include files
    FGRAPH.FI
 Include files see GRAPH.H
 Infinities
 Infinity arithmetic modes
 INTERFACE statement
 INTERFACE statement, FORTRAN
 Italics, use

 L
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Large memory model
 Large memory model
    /AL option (FL)
    adjustable-size, assumed-size arrays
    described
    see Memory models
 $LARGE metacommand
 LCWRQQ routine
    masking denormals
 LES instruction
 LIB (Microsoft Library Manager)
 LIBPATH variable
 Libraries
    8087/287/387
    8087/80287
    alternate math
    controlling use
    DOSCALLS.LIB
    emulator
    FORTRAN.LIB
    huge model
    imports
    large model
    LLIBFMT.LIB
    LLIBFOR7.LIB
    LLIBFORA.LIB
    LLIBFORE.LIB
    medium model
    memory models
    MLIBFOR7.LIB
    MLIBFORA.LIB
    MLIBFORE.LIB
    multithread programs
    standard memory models, support for
 Library functions see Routines
 Line styles
    see also Presentation Graphics, palettes
 linetow
 lineto
 lineto_w
 LINK options
    /NODEFAULTLIBRARYSEARCH (/NOD)
       standard libraries
    /STACK (/ST)
    stack size, setting
 Linker options
    /NODEFAULTLIBRARYSEARCH (/NOD)
 List-directed I/O
 Listing files
    map
 LLIBFMT.LIB library
 LLIBFOR7.LIB
 LLIBFORA.LIB
 LLIBFORE.LIB
 LOC, FORTRAN function
 LOCFAR, FORTRAN function
 LOCNEAR, FORTRAN function
 LOGICAL data type, FORTRAN
 Low-level graphics
    output
       lineto, lineto_w
 Lower bounds, arrays
 /Lp compiler option
    multithread programs
 LSTRING

 M
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Macro Assembler
    assembly interfaces, writing
    simplified segment directives
 Map file, code size
 Medium memory model
    /AM option (FL)
    adjustable-size, assumed-size arrays
    argument passing
    common blocks
    described
    NEAR and FAR attributes
 Memory models
    adjustable-size arrays
    argument passing
    assumed-size arrays
    see also Attributes, FAR
    see also Attributes, HUGE
    see also Attributes, NEAR
    common blocks
    customizing
    default
    defined
    huge
       /AH option (FL)
       adjustable-size arrays
       arrays
       assumed-size arrays
       described
    large
       /AL option (FL)
       adjustable-size, assumed-size arrays
       described
    library support
    medium
       /AM option (FL)
       adjustable-size, assumed-size arrays
       argument passing
       common blocks
       default data segment
       described
    restrictions
    selecting
    specifying
    standard
 Metacommands
    $DEBUG
    $FLOATCALLS
    $LARGE
    $NOFLOATCALLS
    $NOTLARGE
    $STORAGE
 Microsoft Import Library Manager, see IMPLIB
 Microsoft Library Manager, see LIB
 Microsoft Windows
 Mixed-language programs
    compiling
    linking
 MLIBFOR7.LIB
 MLIBFORA.LIB
 MLIBFORE.LIB
 Modules, naming
 Monotype, use of
 movetow
 moveto
 moveto_w
 MRES16COLOR video mode
 MRES256COLOR video mode
 MRES4COLOR video mode
 MRESNOCOLOR video mode
 MT symbolic constant
 /MT compiler option
 Multitasking
 Multithread programs
    compiling and linking
    see Dynamic-link libraries
    function calls
    library support
    memory allocation
    possible problems
    run-time thread management
       BEGINTHREAD
       ENDTHREAD
       THREADID
       _BEGINTHREAD
    stack checking

 N
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Naming conventions
 Naming
    modules
    segments
 NANs
 Near addresses
    default data segment
    defined
    medium model
    subprograms
 NEAR attribute, FORTRAN
 NEAR attribute
    adjustable-size arrays
    alternative to medium model
    assumed-size arrays
    common blocks
    declaring subprograms with
    effects
    huge model
    large and huge models
    large and huge models
    library routines, used with
    subprograms
 Near reference parameters
    assembly
    BASIC
    C
    FORTRAN
    Pascal
 NO87 variable
 /NODEFAULTLIBRARYSEARCH (/NOD) linker option
 /NODEFAULTLIBRARYSEARCH option (LINK)
    standard libraries
 $NOFLOATCALLS metacommand
 /NOI linker option
 /NM option (FL)
 $NOTLARGE metacommand
 /NT option (FL)

 O
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 origin
 OS/2 support files
    DOSCALLS.LIB
    LLIBFMT.LIB
    FDLLOBJS.LIB
 outgtext
 outtext
 Overlays
    reducing program size

 P
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Packed storage
 Palettes
 Palettes
    display
    remapping
 Parameter-passing methods
 Parameters
    calling conventions, effect of
    passing, see also Passing by
    varying number of
 PASCAL attribute
 pascal keyword
 Pascal
    address variables
    C attribute
    calling convention
    calling from
       FORTRAN
    compiling
    functions and procedures
    LSTRING
    naming convention
    parameter-passing
       defaults
       methods
    passing by
       near reference
    records
    string formats
 Passing arguments
    by value
    medium model
 Passing by
    far reference
       assembly
       BASIC
       C
       FORTRAN
    near reference
       assembly
       BASIC
       C
       FORTRAN
       FORTRAN
       Pascal
    value
       BASIC
       C
       FORTRAN
 pie, piewxy
 pie, pie_w
 Pixel values see Color indexes
 Pixels, defined
 Pointers
 Procedures
 Process, illustrated
 Programs, sample
    COLTEXT.C
    SAMPLER.C
    SAMPLER.FOR
    SINE
 Public data
 putimage, putimage_w

 R
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Receiving parameters and calling conventions
 Record structure
    binary direct files
    binary sequential files
    formatted direct files
    formatted sequential files
    unformatted direct files
    unformatted sequential files
 Records
 rectangle, rectanglew, rectanglewxy
 rectangle, rectangle_w
 REFERENCE attribute
 registerfonts
 Registers
    CS
    DS
       near addresses
       near, far, and huge addresses
    ES
    SS
 remapallpalette
    described
    use
 remappalette
    described
    use
 Remapping, see Palettes, remapping
 Resource, ownership of
 Round control
 Routines, categories
 Routiness
 Row-major order

 S
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Sample programs
    FMHELLO.FOR
    SNAP.FOR
 Screen groups, illustrated
 Segment directives, simplified
 Segments
    code
    data
       default
       naming
    naming
    text
       default
       naming
 selectpalette
    described
    use
 setactivepage
 setbkcolor
 setcliprgn
 setcolor
 setfillmask
    described
    dselection
    use
 setfont
 setlinestyle
 setpixelw
 setpixel
    described
    use
 setpixel_w
 settextcolor
 settextcursor
 settextposition
 settextrows
 settextwindow
 SETUP
    removing error-message text
 setvideomoderows
 setvideomode
    described
    use
 setvieworg
 setviewport
 setvisualpage
 setwindow
 sine
 Single-precision reals
 Small memory model
    C, use with
 Special data types
 SS register
 Stack
    allocating separately from DS
    changing size
    default data segment, used in
    large
 Stack checking
 /STACK option
    (LINK)
       default data segment
 Static data
 Static linking
 $STORAGE metacommand
 Strings
    passing from
       C
       FORTRAN
       Pascal
 Structured data types
 Structures
 Subprograms
 Subroutines
 Symbolic constants, described
 Syntax conventions
    Document conventions

 T
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Tc edit descriptor
 Text segment
    default name
    naming
 _TEXT
 TEXTC40 video mode
 TEXTC80 video mode
 THREADID run-time-library function
 THREADID variable
 Threads
    defined
    ending
    identifying
    illustrated
    synchronizing
 TLc edit descriptor
 Typefaces

 U
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Unpacked storage
 unregisterfonts
 Unsigned integers
 Uppercase, use of
 User-defined types

 V
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 VALUE attribute
 VALUE keyword
 Value parameters
    BASIC
    C
    FORTRAN
    passing
 VAR keyword
 Variables
    address
    LIBPATH
    _THREADID
 VARPTR keyword
 VARS keyword
 VARSEG keyword
 VARYING attribute
 VGA (Video Graphics Array)
 Video adapters
    CGA
    EGA
    Hercules
    VGA
 Video memory
 Video modes
    graphics
       CGA
       described
       EGA
       setting
       setting color
       VGA
    table
    (table)
    text
 Video modesgraphics
    setting
 videoconfig structure
 VRES16COLOR video mode
 VRES2COLOR video mode

 W
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Windows, graphics
 wrapon

 Z
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 /Zl compiler option
 /Zl option (FL)

