HP OpenVMS Systems Documentation

Content starts here

User Manual

Previous Contents Index

12.2 Declaring Subprograms and Parameters

You declare a subprogram by naming it in an EXTERNAL statement in the calling program. You may also declare the data type of each parameter. If the subprogram is a function, the EXTERNAL statement also lets you specify the data type of the returned value.

The following statements are subprogram declarations using the EXTERNAL statement:


Note that the parameter lists contain only data type and dimension information; they cannot contain any format or actual parameters. When the external procedure is invoked, HP BASIC ensures that the actual parameter data type matches the data type specified in the EXTERNAL declaration. However, HP BASIC does not check to make sure that the parameters declared in the EXTERNAL statement match those in the external routine. You must ensure that these parameters match.

You can pass data of any HP BASIC data type to an HP BASIC subprogram, including RFAs and RECORDs. HP BASIC allows you to pass up to 255 parameters, separated by commas. The data can be any one of the following:

  • Constants
  • Variables
  • Expressions
  • Functions
  • Array elements
  • Entire arrays (but not virtual arrays)

For passing constants, variables, functions, and array elements, name them in the argument list. For example:

CALL SUB01(var1, var2)

CALL SUB02(Po_num%, Vouch, 66.67, Cust_list(5), FNA(B%))

However, when passing an entire array, you must use a special format. You specify the array name followed by commas enclosed in parentheses. The number of commas must be the number of array dimensions minus one. For example, array_name() is a one-dimensional array, array_name(,) is a two-dimensional array, array_name(,,) is a three-dimensional array, and so on.

The following example creates a three-dimensional array, loads the array with values, and passes the array to a subprogram as a parameter. The subprogram can access and change values in array elements, and these changes remain in effect when control returns to the main program.

PROGRAM fill_array
DECLARE LONG I,J,K, three_d(10,10,10)
EXTERNAL SUB example_sub (LONG DIM(,,))
FOR I = 0 TO 10
    FOR J = 0 TO 10
        FOR K = 0 TO 10
            three_d(I,J,K) = I + J + K
        NEXT K
    NEXT J

CALL  example_sub( three_d(,,))

SUB example_sub( LONG X( , , ))

If you do not specify data types for parameters, the default data type is determined by:

  • The last specified parameter data type
  • An OPTION statement
  • An HP BASIC compilation qualifier (for example, /REAL_SIZE=DOUBLE)
  • The system default

The last specified parameter data type overrides all the other default data types, the defaults specified in the OPTION statement override any compilation qualifiers and system defaults, and so on. When you know the length of a string or the dimensions of an array at compile time, you can achieve optimum performance by passing them BY REF. When you call programs written in other languages, the practice of declaring subprograms and specifying the data types of parameters becomes more important because other languages might not use the HP BASIC default parameter-passing mechanisms. For more information about calling subprograms written in other languages, see Chapter 19.

12.3 Compiling Subprograms

an HP BASIC source file can contain multiple program units. When you compile such a file, HP BASIC produces a single object file containing the code from all the program units. You can then link this object file to create an executable image.

If the main program and subprograms are in separate source files, you can compile them separately from the DCL level. The following command causes HP BASIC to create MAIN.OBJ, SUB1.OBJ, and SUB2.OBJ by separating the file names with commas:

$ BASIC main,sub1,sub2

To link these programs, you must specify all object files as input to the OpenVMS Linker.

Alternatively, you can compile multiple modules into a single object file at the DCL command level by separating the file names with a plus sign (+) as follows:

$ BASIC main+sub1+sub2

The plus signs used to separate the file names instruct HP BASIC to create a single object file called MAIN.OBJ from the three source modules. To link this program, you specify only one input file to the linker.

When creating a multiple-unit program, follow these rules:

  • If the source file contains line numbers, then the line numbers for each subprogram must be numerically greater than the highest line number of all preceding subprograms.
  • Line numbers must be unique and no greater than 32767.
  • Each subprogram must end with an END SUB or END FUNCTION statement before the next subprogram begins.
  • If the source file contains line numbers, then text following an END SUB or END FUNCTION statement must begin on a numbered line.
  • If the source file does not contain line numbers, then text following an END SUB or END FUNCTION statement must begin on a new physical line.

Note that in a multiple-unit program that contains line numbers, any comments or statements following an END, END SUB, or END FUNCTION statement become part of the preceding subprogram unless they begin on a numbered line. In a multiple-unit program that does not contain line numbers, however, any comments following an END, END SUB, or END FUNCTION statement become part of the following subprogram if one exists.

In the following example, the function Strip changes all brackets to parentheses in the string A$ or alpha, and strips all trailing spaces and tabs:

  B$ = Strip( A$ )

IF (POS( alpha, "[", 1%)) > 0%
   THEN Strip = EDIT$(alpha, 128% +64%)
   ELSE Strip = EDIT$(alpha, 128%)

12.4 Invoking Subprograms

The following sections describe how to invoke subprograms and pass parameters to subprograms.

12.4.1 Invoking SUB Subprograms

The CALL statement transfers control to a subprogram, and optionally passes arguments to it. The parameters in the CALL statement specify variables, constants, expressions, array elements, or entire arrays to be passed to the subprogram. You can also specify a function in the argument list. HP BASIC passes the value returned by the function to the subprogram. If possible, HP BASIC converts the actual arguments to the data type specified in the EXTERNAL statement. HP BASIC signals an error when the conversion is not possible.

The following example shows an HP BASIC main program calling a BASIC subprogram. The main program prompts for three integers: A, B, and C. It then passes these variables as parameters to the SUB subprogram. The subprogram prints the sum of these variables and returns control to the calling program.

PROGRAM get_input
  INPUT "Please type three integers"; A, B, C
  CALL SUB01 (A, B, C)

  PRINT "The sum is"; X + Y + Z

12.4.2 Invoking FUNCTION Subprograms

The following example performs the same task as the SUB program; however, this example uses a FUNCTION subprogram that returns the value to the main program and the main program prints the result:

PROGRAM invoke_funct
  INPUT "Please type three integers"; A, B, C
  PRINT "The sum is"; FUN01(A, B, C)

  FUN01 =  X + Y + Z

If you do not assign a value to the function name and you do not specify a return value on an EXIT FUNCTION or END FUNCTION statement, the function returns zero or the null string.

Note that when writing FUNCTION subprograms, you must specify a data type for the function in both the main program EXTERNAL statement and the subprogram FUNCTION statement. This data type keyword specifies the data type of the value returned by the function subprogram. You should ensure that the data type specified in an EXTERNAL FUNCTION statement matches the data type specified in the FUNCTION statement.

If you declare a FUNCTION subprogram with an EXTERNAL statement and use the CALL statement to invoke the function, it executes correctly but the function value is not available. Note that BASIC still performs parameter validation when you invoke a function with the CALL statement.

Note that you cannot use the CALL statement to invoke a string or packed decimal function.

12.5 Returning Program Status

A PROGRAM unit lets you return a status from an HP BASIC image by optionally including an integer expression with the END PROGRAM and EXIT PROGRAM statements. After executing a program, you can examine this status by checking the DCL symbol $STATUS. By default, HP BASIC returns a status of 1, indicating success. Success is signaled with an odd numbered status value, while an error is signaled with an even numbered value. $STATUS contains the same value as the integer expression for the exit status in the EXIT and END PROGRAM statements. Note that if a program is terminated with an EXIT PROGRAM statement, the expression on the EXIT PROGRAM statement overrides any expression on the END PROGRAM statement.

In the following example, exit_status contains the status value returned by the program. After program execution, $STATUS has the value of exit_status. You can examine the value of $STATUS and display the corresponding message text with the lexical function F$MESSAGE at DCL level, as shown in the following example:

  DECLARE INTEGER exit_status,                     &
          REAL capital
  EXTERNAL SUB play_safe(INTEGER),                 &
  Exit_status = 1%
  INPUT "Enter the amount of your free capital $";capital
  SELECT capital
     CASE = 0
             exit_status = SS$_BADPARAM
             EXIT PROGRAM exit_status
     CASE < 5000
             CALL play_safe(capital)
     CASE < 15000
             CALL minor_risk(capital)
     CASE < 50000
             CALL major_risk(capital)
             PRINT "I can't cope with that amount, try again."
  GOTO How_much
END PROGRAM exit_status

After program execution, you can examine the status of the program at DCL level:

$ STATUS = "%X10"
$ error_text = F$MESSAGE(%X10)
$ SHOW SYMBOL error_text
ERROR_TEXT = "SYSTEM-W-BADPARAM, bad parameter value"

The PROGRAM statement is always optional; EXIT PROGRAM and END PROGRAM are legal without a matching PROGRAM statement. Without a PROGRAM statement, these statements still exit the main compilation unit. The EXIT PROGRAM and END PROGRAM statements are not valid within SUB, FUNCTION, or PICTURE subprograms.

Chapter 13
File Input and Output

This chapter explains BASIC file organizations and record operations that are implemented through OpenVMS Record Management Services (RMS). For a more thorough understanding of file organization and file and record operations, see the OpenVMS Record Management Services Reference Manual.

RMS stores data in physical blocks. A block is the smallest number of bytes BASIC transfers in a read or write operation. On disk, a block is 512 bytes. On magnetic tape, it is 18 to 8192 bytes.

RMS stores one or more data records in each block. A data record can also be divided into smaller units, called fields. A data record can be smaller than, equal to, or larger than a disk block.

13.1 Record Formats

The format of a record determines how RMS stores the record in a block. You specify the record format in an OPEN statement. The following are valid BASIC record formats:

  • Fixed-length records
  • Variable-length records
  • Stream records

13.1.1 Fixed-Length Records

Fixed-length records are all the same length. RMS stores fixed-length records as they appear in the record buffer, including any spaces or null characters following the data; this process is called padding. Processing these records involves less overhead than other record formats; however, this format can use disk storage space less efficiently than variable-length or stream records.

13.1.2 Variable-Length Records

Variable-length records can have different lengths, but no record can exceed a maximum size set for the file. When the record is written to a file, RMS adds a record length header that contains the length of the record (excluding the header) in bytes. When your program retrieves a record, this header is not included in the record buffer. While variable-length records usually make more efficient use of storage space than fixed-length records, manipulation of the record length headers generates processor overhead.

13.1.3 Stream Records

BASIC interprets stream records as a continuous sequence, or stream, of bytes. Unlike the fixed- and variable-length formats, stream records do not contain control information such as record counts, segment flags, or other system-supplied boundaries. Stream records are delimited by special characters or character sequences called terminators. Note that stream record formats are valid only in sequential files.

RMS defines the following types of stream record formats:

  • STREAM records can be delimited by any special character (usually a carriage return/line-feed pair).
  • STREAM_LF records must be delimited by a line-feed character.
  • STREAM_CR records must be delimited by a carriage return.

While you can access existing files of any one of these stream record formats, BASIC creates new stream files only in the STREAM format; you can create files of the other two stream record formats by modifying the RMS FAB control structure in a USEROPEN routine. For more information about USEROPEN routines, see Section 13.8.11.

13.2 File Organizations

HP BASIC provides the following file organizations:

  • Terminal-format
  • Sequential
  • Relative
  • Indexed
  • Virtual

If you do not specify a file organization when creating a file, the default is a terminal-format file (a sequential file with variable-length records). The following sections describe each type of file organization.

13.2.1 Terminal-Format Files

A terminal-format file is a sequential file of variable-length records. Terminal-format files are the default; that is, you create a terminal-format file when you do not specify a file organization when you open a file. You can then use the PRINT, INPUT, INPUT LINE, and LINPUT statements to access a terminal-format file. See Chapter 5 and Chapter 6 for more information about terminal-format files.

13.2.2 Sequential Files

A sequential file contains records that are stored in the order they are written. Sequential files can contain records of any valid BASIC record format: fixed-length, variable-length, or stream. You usually read a sequential file from the beginning; therefore, a sequential file is most useful when you access the data sequentially each time you use it. You can also access sequential fixed-length records randomly by specifying a record number if the file resides on disk. In either case, sequential files can reside on both disk and magnetic tape devices, and those stored on disk support shared access.

13.2.3 Relative Files

A relative file contains a series of cells that are numbered consecutively from 1 to n, where n represents the relative record number. Each cell can contain only a single record. For fixed-length records, the length of each cell equals the record length plus 1 byte. For variable-length records, the length of the cell equals the maximum record size plus 3 bytes.

You can access records in a relative file either sequentially or randomly. The relative record number is the key value in random access mode; that is, to access a record in a relative file in random access mode, you must know the relative record number of that record. You can add records to a relative file either at the end of the file or into any empty cell.

Relative files are most useful when randomly accessed and when the record can be identified by its cell number (for example, when inventory numbers correspond to cell numbers). Relative files support shared access. You can delete records from relative files, but not sequential files.

13.2.4 Indexed Files

An indexed file contains data records that are sorted in ascending or descending order according to a primary index key value. The index key is a record field (or set of fields) that determines the order in which the records are logically accessed. Keys must be variables declared in a MAP statement. Keys can be any one of the following:

  • Strings
  • WORD integers
  • LONG integers
  • Quadword integers
  • Packed decimal numbers

String keys can also be segmented; the key can be composed of up to eight string variables in a map. Quadword keys must be referenced using a record or group exactly 8 bytes long.

Along with the primary index key value, you can also specify up to 254 alternate keys; RMS creates one index for each key you specify. For each of these keys you can also specify either an ascending or descending collating sequence. Each index is stored as part of the file, and each entry in the index contains a pointer to a record. Therefore, each key you specify corresponds to a sorted list of record pointers.

An indexed file of library books, for example, might be ordered by book title; that is, the title of the book is the primary key for the file. The keys for alternate indexes might include the author's name and the book's Library of Congress number. Neither of these alternate indexes contains the actual records; instead, they contain sorted pointers to the appropriate records.

Indexed files are most useful when randomly accessed or when you want to access the records in more than one way.

13.2.5 Virtual Files

A virtual file is a random access file that stores one or more data records or virtual array elements in each physical 512-byte disk block. You create a virtual file by specifying ORGANIZATION VIRTUAL as part of the OPEN statement. Apart from virtual arrays and compatibility with BASIC and BASIC-PLUS-2, you should use sequential fixed-length instead of virtual files, as they provide the same capabilities. See Section 13.5 for more information about accessing the individual records in a disk block.

13.3 Record Access and Record Context

Record access modes determine the order in which your program retrieves or stores records in a file. They determine the record context: the current record and the next record to be processed. When your program successfully executes any record operation, the current record and next record pointers can change. If a record operation is unsuccessful, these pointers do not change.

The record access modes valid for RMS are:

  • Sequential access---valid on any file organization
  • Random-by-record number access---valid on sequential fixed and all relative files
  • Random-by-key access---valid on indexed files
  • Random-by-RFA (Record File Address) access---valid on any RMS file located on disk

With sequential access, the next record is the next logical record in the file. In the case of relative files, the next logical record is the next existing record (deleted or never-written records are skipped). In the case of indexed files, the next logical record is the record with the next ascending or descending value in the current key of reference depending on that key's collating sequence. You can therefore access relative or indexed files sequentially by not specifying a relative record number or key value.

You can also access sequential fixed-length and relative files randomly by record number; that is, you can specify the record number of the record to be retrieved. For relative files, this record number corresponds to the cell number of the desired record.

You can access indexed files randomly by key. The key specification includes a primary or alternate key and its value. BASIC retrieves the record corresponding to that value in the particular key chosen.

You can access disk files of any organization by Record File Address (RFA); this means that you specify an RFA variable whose value uniquely identifies a particular record. The RFA requires six bytes of information. For more information about RFAs, see Section 13.6.10.

Previous Next Contents Index