HP OpenVMS Systems Documentation
HP BASIC for OpenVMS
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:
EXTERNAL SUB my_sub (LONG, STRING) EXTERNAL GFLOAT FUNCTION my_func (GFLOAT, LONG, GFLOAT) EXTERNAL REAL FUNCTION determinant (LONG DIM(,))
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:
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 OPTION TYPE = EXPLICIT 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 NEXT I CALL example_sub( three_d(,,)) END PROGRAM SUB example_sub( LONG X( , , )) . . . END SUB
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.
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:
PROGRAM scan EXTERNAL STRING FUNCTION Strip (STRING) A$ = "USER$DISK:[BASIC.TRYOUTS]" B$ = Strip( A$ ) PRINT B$ END PROGRAM FUNCTION STRING Strip( STRING alpha ) IF (POS( alpha, "[", 1%)) > 0% THEN Strip = EDIT$(alpha, 128% +64%) ELSE Strip = EDIT$(alpha, 128%) END IF END FUNCTION
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 OPTION TYPE = EXPLICIT EXTERNAL SUB SUB01(LONG, LONG, LONG) DECLARE LONG A, B, C INPUT "Please type three integers"; A, B, C CALL SUB01 (A, B, C) END PROGRAM SUB SUB01 (LONG X, LONG Y, LONG Z) PRINT "The sum is"; X + Y + Z END SUB
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 EXTERNAL LONG FUNCTION FUN01(LONG, LONG, LONG) DECLARE LONG A, B, C INPUT "Please type three integers"; A, B, C PRINT "The sum is"; FUN01(A, B, C) END PROGRAM FUNCTION LONG FUN01 (LONG X, LONG Y, LONG Z) FUN01 = X + Y + Z END FUNCTION
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:
PROGRAM Venture DECLARE INTEGER exit_status, & REAL capital EXTERNAL LONG CONSTANT SS$_BADPARAM EXTERNAL SUB play_safe(INTEGER), & minor_risk(INTEGER),major_risk(INTEGER) Exit_status = 1% SET NO PROMPT How_much: 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) CASE ELSE PRINT "I can't cope with that amount, try again." END SELECT GOTO How_much . . . END PROGRAM exit_status
After program execution, you can examine the status of the program at DCL level:
$ SHOW SYMBOL $STATUS $ 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.
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 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 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.
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
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
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
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
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:
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:
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.