When you are developing a system that involves the use of CSUBs, you should:
The following steps present an overview of the process needed to create a CSUB and the results of those steps. The CSUB related steps will be described in detail in later sections.
ld -rd -a archive csub.o -u _printf -lrmb -o csub
would create a fully linked relocatable object file called csub using the compiled file csub.o and the library librmb.a. See the section "A Closer Look at Linking CSUB Object Files" for details.
This section goes through each step of creating an example FORTRAN CSUB and how the CSUB is used in a BASIC program. All files used in the HP-UX environment are read from and written to the current directory. In the BASIC environment, the CSUB is loaded from the MASS STORAGE IS device (directory).
This simple program fills an array of string variables and then calls a FORTRAN CSUB which determines if a particular string is found in the array. The BASIC program keeps track of how many valid strings are contained in the array and passes that information to the FORTRAN CSUB. If the INTEGER variable Yes comes back with a value other than zero, it comes back pointing to the array element containing the matching string.
Enter BASIC, edit and store this program in a file named FSTR. This file can be found in the directory called /usr/lib/rmb/demo.
10 LOADSUB ALL FROM "FIND_STRING"
20 DIM File$(1:10)[20]
30 DIM Str$[20]
40 INTEGER Num_strs,Yes
50 File$(1)="HELLO - HOW ARE YOU?"
60 File$(2)="I AM GREAT"
70 File$(3)="WHAT IS YOUR NAME?"
80 File$(4)="WHERE ARE YOU GOING?"
90 File$(5)="FAVORITE COLOR?"
100 File$(6)="I LIKE YOU"
110 Num_strs=6
120 Str$="WHERE ARE YOU GOING?"
130 Find_string(File$(*),Str$,Num_strs,Yes)
140 IF Yes<>0 THEN PRINT "The string was found in number";Yes
150 IF Yes=0 THEN PRINT "The string was not found"
160 DELSUB Find_string
170 END
Enter HP-UX, edit, compile, and debug the following subprogram called find_string, and save it in the file named string.f. This file can be found in the directory called /usr/lib/rmb/demo.
subroutine find_string(file_dim,filex,str_dim,strx,num_strs,yes)
character*30 file_dim
character*22 filex(*)
character*30 str_dim
character*22 strx
integer*2 num_strs
integer*2 yes
integer*2 i, j, size1, size2
yes=0
i=1
read(strx(1:2), '(A2)') size2
do while ((i.le.num_strs).and.(yes.eq.0))
read(filex(i), '(A2)') size1
if (size1.eq.size2) then
if (size2.eq.0) then
yes=i=1
else
j=3
do while ((j.lt.(size1=2)).and.(filex(i)(j:j).eq.strx(j:j)))
j=j=1
end do
if (filex(i)(j:j).eq.strx(j:j)) yes=i
endif
endif
if (yes.eq.0) i=i=1
end do
end
Link the code file string.o with the BASIC CSUB library librmb.a and the necessary HP-UX libraries to generate a fully linked relocatable CSUB object file. The HP-UX ld command should be used for this purpose, as follows:
ld -rd -a archive string.o -u _printf -lrmb -lIO77 -lF77 -lc -o string
Execute the rmbbuildc program and answer the prompts as shown below. Notice that a stream file is generated by the response to the first prompt; you can use this file the next time you execute the program (i.e. rmbbuildc ) to remove the need to interactively answer the prompts again.
RMB-UX Compiled Subprogram File Generator (Version 1.1)
Stream file name: stream
Output BASIC PROG file: FIND_STRING
CSUB object file names(s): string
Module name: [Return]<>
CSUB name: find_string
Parameter name: filex$
Parameter type is string
Is this an array? (y/n): y
Is this an optional parameter? (y/n): n
Parameter name: strx$
Parameter type is string
Is this an array? (y/n): n
Is this an optional parameter? (y/n): n
Parameter name: num_strs
Parameter type (I/R/C for Integer/Real/Complex): i
Is this an array? (y/n): n
Is this an optional parameter? (y/n): n
Parameter name: yes
Parameter type (I/R/C for Integer/Real/Complex): i
Is this an array? (y/n): n
Is this an optional parameter? (y/n): n
Parameter name: [Return]<>
Is there COM in this CSUB? (y/n): n
CSUB name: [Return]<>
Are there any more modules? (y/n): n
In this example, FIND_STRING is automatically loaded from the BASIC program. Therefore, you only need to re-enter the BASIC system, LOAD "FSTR", and RUN the program. The output should be:
The string was found in number 4
Procedures in FORTRAN can be grouped into two main categories:
In order to implement a FORTRAN CSUB, you will need to use the structure of a subroutine subprogram. FORTRAN functions may not be used since they return a value to the calling program.
In order to be useful, a CSUB needs the ability to exchange data with the calling BASIC program. This section describes the different ways of performing this data exchange. The primary method consists of defining the CSUB parameters to match those passed by BASIC. This method requires special attention to the different parameter types and formats of BASIC variables. The second method for a CSUB to exchange data with a BASIC program is via COM blocks. Again, this method necessitates the special handling of the variables defined in the blocks, according to their type and format.
As far as FORTRAN is concerned, BASIC variables are always passed to a CSUB by reference. That is, a pointer to the actual value is passed. When you think you are passing a parameter by value, BASIC actually makes a copy of the value and passes a pointer to it. When you return to the calling program, the copy is destroyed. Since all FORTRAN subprogram parameters are passed by reference, you need to make sure that the formal parameters of a CSUB match those passed by BASIC in number and in type.
The two key points to remember are:
Note that errors will occur if this distinction is overlooked.
The BASIC parameter types are not necessarily the same as their FORTRAN counterparts. It is important that the type of the parameters of a CSUB be correct so that BASIC and the CSUB can interface properly. This section will explain the type differences in detail.
The following table provides you with a quick reference to equivalent FORTRAN and BASIC parameter types.
BASIC Parameter Type | FORTRAN Parameter Type |
---|---|
REAL | real*8 |
INTEGER | integer*2 |
COMPLEX | complex*16 |
string_name$ | Two parameter types passed for strings: ; character*30 ; character*n where n is the DIM length of the string plus 2. |
array_name[lower : upper, etc.] of one of the above numeric parameter types or string_array$(lower : upper, etc.)[num_chars] | Two parameter types passed for arrays: ; character*30 ; one of the above numeric or string array types. |
@io_path_name | character*190 |
A variable defined as a real*8 in FORTRAN maps into a BASIC REAL. Therefore, a BASIC REAL parameter can be defined in a CSUB as:
subroutine x(y)
real*8 y
A variable defined in BASIC as COMPLEX is a floating point value with real and imaginary parts. It maps directly into a FORTRAN variable of type complex*16.
A variable defined in BASIC as INTEGER is a 16-bit quantity. It is therefore equivalent to a FORTRAN variable of type integer*2.
Strings are different in BASIC and FORTRAN, both in their structure and the way they are passed. The structure of a FORTRAN string is simply an array of characters while the BASIC string has a two-byte length field followed by its characters.
BASIC passes its strings as two parameters:
An example of how this would look in a FORTRAN CSUB is as follows:
subroutine getstring(dim_len, b)
character*30 dim_len
character*82 b
integer*2 i
character*20 s
integer*2 s_len, b_maxlen, b_len
s='a string'
s_len=8
read(dim_len(1:2), '(A2)') b_maxlen
if (s_len.gt.b_maxlen) s_len=b_maxlen
b_len=s_len
do i=1, b_len
b(2=i)=s(i)
end do
write(b(1:2), '(A2)') b_len
end
The above subprogram copies a FORTRAN string into a BASIC string. From this example, you should note how:
Although a CSUB receives two parameters for a BASIC string, as far as BASIC is concerned, there is only one actual parameter to be passed, as shown below.
...
10 DIM Str$[80]
20 CALL GETSTRING(Str$)
30 PRINT Str$
40 END
An I/O path is a block of storage used to keep track of the state of a file or I/O device. In FORTRAN, this block is mapped into a 190-character array. See the section "BASIC File I/O" for details on how you would use an I/O path as a file control block with the file access library (fal) routines.
The following example shows how a FORTRAN CSUB receives an I/O path parameter from BASIC.
subroutine x(y_ptr)
character*190 y_ptr
The typical use of the parameter y_ptr in a CSUB would then take the form:
call csfa_fal_open(filename, y_ptr)
Arrays are passed as two parameters:
The actual dimension records for the REAL, INTEGER, COMPLEX, and string arrays are represented by Figures 4-1 and 4-2.
dims | is a byte containing the number of dimensions |
totalsize | is the number of bytes in the entire array |
low(n) | is the lower bound of the nth dimension |
length(n) | is the number of elements in the nth dimension |
maxlen | is the maximum length of any element in a string array. |
REAL, INTEGER, or COMPLEX Array Dimension Record
String Array Dimension Record
The above figures will help you determine the character array bound values to specify to the read and write statements when accessing or modifying a given field in the records. For example, to access the maximum length field (maxlen) of a string array parameter, you can refer to Figure 4-2 and use the following code:
subroutine x(s_dim, s)
character*30 s_dim
character*22 s(*)
integer*2 s_maxlen
read(s_dim(5:7), '(A2')) s_maxlen
An example of receiving an INTEGER array from BASIC might be:
subroutine x(d, arr)
character*30 d
integer*2 arr(*)
Again, this is a single parameter in BASIC. So, in this example, BASIC would send an array defined as:
INTEGER Arr(1:10)
CALL X(Arr(*))
The BASIC and FORTRAN arrays should be defined the same way. This is not mandatory, but helpful. You should remember that an array with bounds [1..5] is the same as an array with bounds [6..10]; both are five-element arrays. If a BASIC array defined as INTEGER Arr(6:10) is passed to a CSUB array parameter defined as integer*2 arr(*), the sixth element in the BASIC array will correspond to the first element in the FORTRAN CSUB array.
You should note that array elements are stored in row-major order in BASIC and in column-major order in FORTRAN. It is therefore best to declare a multi-dimensional BASIC array as a one-dimensional array in FORTRAN and do explicit subscript calculations based on the information in the dimension record of the array.
The DIM statement should be used in conjunction with the appropriate FORTRAN array declaration to define the space for the BASIC array. The REDIM statement does not affect the size of that space. It does however affect the BASE and SIZE functions and the length and low values in the dimension record. Note that a CSUB should therefore check the dimension structure of an array parameter to find its current dimensions.
If you use the REDIM statement on a multi-dimensional BASIC array, the declaration of the array will be invalid the next time the array is accessed in a CSUB. To be immune from the effects of REDIM, it is again best to declare a multi-dimensional BASIC array as a one-dimensional FORTRAN array.
You should pay special attention to the definition of a BASIC string array to make sure that the value area of the array is properly dimensioned in FORTRAN. Each element of the array should have the same structure and size as a single string CSUB parameter with the same dimensions. For example, with the following BASIC definition,
DIM S$(1:10)[20]
you would define the equivalent FORTRAN string array as:
subroutine x(s_dim, s)
character*30 s_dim
character*22 s(10)
You can declare some or all parameters of a CSUB as optional through responses to rmbbuildc. Optional parameters are those which are not required in the parameter list of the calling code. In a FORTRAN CSUB, however, there is no distinction between required and optional parameters as both types of parameters have to be listed in the declaration of the CSUB. See "Subprograms" in the "BASIC Programming Techniques" manual for more information about optional parameters.
BASIC will pass a NIL pointer to the CSUB when one of its parameters that has been declared as optional is omitted. The CSUB should therefore always make an explicit NIL check before trying to use the value of an optional parameter. Otherwise, a run-time error may occur when attempting to access the parameter.
For example, if the BASIC declaration of a CSUB is:
100 CSUB My_csub(REAL R, OPTIONAL REAL Opt)
the variable Opt will have an address of NIL if it is not passed. In FORTRAN, the CSUB should perform the following test:
subroutine my_csub(required, optional)
real*8 required, optional
integer*4 i
i=%loc(optional)
if (i.ne.0) then
...
Another way for a BASIC program and a FORTRAN CSUB to interchange data is via COM blocks. This method allows you to map a BASIC COM block into a FORTRAN COM block in order to access BASIC variables from a CSUB. On entry of the CSUB, you would first copy the contents of a BASIC COM block into a FORTRAN COM block. After accessing or modifying the desired variables in the FORTRAN COM block, you would copy the contents of this block back into the BASIC COM block to update the values of the variables modified by the CSUB.
The routine provided for copying the contents of BASIC and FORTRAN COM blocks is named basic_com. It takes the four following parameters:
character*80 com_name | is the name of the BASIC COM block for which the copy operation is to
be performed. Since upper and lower case letters are significant in a COM
block name, they should be specified the same way BASIC does. To access an
unlabeled COM block, you should specify a string with a single blank as the
COM block name. This string parameter should always be terminated with a
null character.
|
integer*4 com_start | is the starting address of the FORTRAN COM block. It can be retrieved
by using the %loc function with the first variable of the block
as the argument to the function.
|
integer*4 com_end | is the last address of the FORTRAN COM block. It can be retrieved by
using the %loc function with the last variable of the block as the
argument to the function. A dummy variable should be declared in the COM
block for this purpose.
|
integer*4 copy_flag | specifies the direction of the copy operation. A value of 0 will copy the contents of the specified BASIC COM block into the FORTRAN COM block while a value of 1 will copy the contents of the FORTRAN COM block into the BASIC COM block. The routine will also set this parameter to 0 for a successful copy operation and set the parameter to -1 if it was unable to perform the operation because of an invalid parameter value, such as an invalid BASIC COM name. |
In defining the FORTRAN COM block structure for accessing a BASIC COM block, you will need to know the layout of the BASIC block in advance since there is no way of determining this layout from the FORTRAN CSUB at run-time. You should also know that the order of the variables in the FORTRAN block should be the opposite from that of the variables in the BASIC block. For example, the first variable in the FORTRAN block should be mapped into the last variable in the BASIC block. You should also know that basic_com will not prevent you from inadvertently corrupting the COM blocks by writing beyond their boundaries or by storing invalid values.
Also, you should note that only the value area of BASIC strings and arrays are stored in a COM block. Therefore, you should omit the specification of the dimension record for those variables in the FORTRAN COM block.
Finally, you should always use the =A FORTRAN compiler option when compiling FORTRAN CSUBs with COM declarations. This option is necessary to guarantee the alignment of the variables in FORTRAN COM blocks and their BASIC counterparts.
This is an example of accessing a BASIC COM block defined as:
COM /Num1/ INTEGER A,B(1:5),REAL C,D$[10]
To access this block from a CSUB, you could use:
subroutine one
character*12 d
real*8 c
integer*2 b(5)
integer*2 a
common /some_com/ d,c,b,a,dummy
integer*4 flag
flag=0
call basic_com('Num1'//char(0), %loc(d), %loc(dummy), flag)
a=26
c=1214.61
...
flag=1
call basic_com('Num1'//char(0), %loc(d), %loc(dummy), flag)
end
After thoroughly testing your CSUBs in a FORTRAN program, you are now ready to generate a CSUB object file. This object file brings together the CSUB binaries and the necessary libraries to satisfy the external references of those binaries.
The HP-UX linker can be used to generate a fully linked, relocatable CSUB object file suitable for input to the rmbbuildc program. The syntax for linking is as follows:
ld -rd -a archive csub_binaries -u _printf -lrmb [other_libraries] -o csub_obj_file
where:
-rd | are the options to retain relocation information in the output file for subsequent re-linking and to force the definition of "common" storage. |
-a archive | tells the loader to use archive libraries instead of shared libraries. |
csub_binaries | are the .o files generated by the FORTRAN compiler. They contain the code for CSUBs and should always be followed in the above command line by the library -lrmb. |
-u _printf | links needed object files. |
other_libraries | are libraries that are required to resolve all the references not satisfied by librmb.a; they may be your own or HP-UX libraries. |
-o csub_obj_file | takes the output of the ld command and sends it to the file. csub_obj_file. |
In addition to providing useful system access routines for CSUBs, this library also specifies all the external symbols from HP-UX libraries which were used in the actual implementation of a specific version of BASIC. Specifying the library immediately after your CSUB binaries in the ld command will thus ensure the sharing of those symbols between BASIC and your CSUBs. This sharing first removes the need for duplicating the code already available in BASIC in the CSUB object files. More importantly, it is also necessary for the proper operation of libraries, the routines of which maintain global variables which should not be duplicated in the CSUB object files. An example of such routines are the HP-UX memory management routines (malloc 3C includes: calloc, free, mallopt, mallinfo, and realloc).
In general, specifying the library librmb.a alone in your link command will be sufficient to resolve all external references from your CSUB binaries; this is because BASIC uses most of the common HP-UX libraries in its implementation.
Nevertheless, to verify that your CSUB object file is indeed fully linked, you should use the following command, which will notify you of any unresolved external references (e.g., if a CSUB is calling a function in an HP-UX library not specified in your link (ld) command).
nm -u <CSUB_object_file >
In the event that you do have undefined external references, you should resolve them by first determining the libraries in which the symbols are defined and then specifying those libraries in your link command. Again, it is extremely important that you only list those libraries after the library librmb.a.
After successfully generating one or more CSUB object files without unresolved external references, you are now ready to use the rmbbuildc program to create the associated BASIC PROG file for the CSUBs.
The purpose of rmbbuildc is to generate a BASIC subprogram interface for each of the CSUBs. This interface consists of the:
The program will interactively prompt you for all the necessary information about a CSUB to generate its BASIC subprogram interface.
NOTE |
---|
On all the yes/no prompts in rmbbuildc, a response other than [Y] is treated as a negative response. A null response for a prompt is specified by only pressing [Return]. |
The following sections contain steps that explain how to use rmbbuildc.
In HP-UX, execute:
rmbbuildc [Return]
The rmbbuildc program begins by prompting you for the name of an optional stream file.
RMB-UX Compiled Subprogram File Generator (Version 1.1)
Stream file name:
In this file, the program will record all your responses to its prompts so that you can use the file as its standard input in subsequent executions (e.g., rmbbuildc file_name). Note that you may give a null response if you do not want a stream file.
The next prompt is:
Output BASIC PROG file name:
This prompt asks for the BASIC PROG file name that you will specify in a LOADSUB statement to load the desired CSUBs into a BASIC program.
To the prompt:
CSUB object file name(s):
list the names of the CSUB object files generated by the linking procedure. Note that each object file will correspond to a specific version of BASIC. The file names specified should be separated by one or more spaces.
The remaining prompts deal with the specification of each CSUB interface. The following steps explain the prompts that you will encounter during this specification.
Module name:
CSUB name:
This prompt will be repeated until a null CSUB name is specified by pressing the [Return] key.
Parameter name:
Parameter type (I/R/C for Integer/Real/Complex):
Is this an array? (y/n):
Is this an optional parameter? (y/n):
Since you are declaring the interface of the BASIC subprogram for the current CSUB, the names of the parameters that you specify need not match that of the CSUB. However, the types of the parameters between the FORTRAN CSUB and its BASIC subprogram declaration should match. For example, the BASIC CSUB call may be similar to the following:
Read_result(INTEGER Value_ret)
and the actual parameter name in the CSUB may be as follows:
subroutine read_result(return_int)
integer*2 return_int
The legal BASIC types are INTEGER, REAL, COMPLEX, string, and I/O path. If the parameter name ends in a dollar sign ($), then the type is automatically assumed to be string, and if it begins with an @, the type is assumed to be an I/O path. Otherwise, you should answer with [I] , [R], or [C]. An array parameter is assumed to have dimension (*), which indicates that it will be defined in the calling BASIC program. Once a parameter is specified as optional, all the following parameters default to being optional and the prompt will not reappear. Since rmbbuildc cannot check the parameter list of a CSUB to verify that it matches its BASIC declaration, it is your responsibility to make sure that the matching is done correctly. Otherwise, the CSUB will behave unpredictably when called from a BASIC program.
Is there COM in this CSUB? (y/n):
It is not required that you declare a COM that is accessed in the CSUB. By declaring it, however, the COM will remain in memory as long as the CSUB is present, even after the BASIC subprogram that defines it is deleted. If you answer [Y] to the above prompt, the following prompts will appear. Respond to them with the information that they request. Note that the number of COM blocks that you specify will depend on the number of COMs that you want to access. With the exception of the first prompt given below, all of the prompts are repeated for each COM block. For an unlabeled COM, the label will be null.
Number of COM blocks:
COM label:
Item name:
Item type (I/R/C for Integer/Real/Complex):
Is this an array? (y/n):
The questions concerning name, type, and array are the same as for the parameters, with the exception that the bounds of an array in a COM block have to be explicitly specified. To specify these bounds, enter the appropriate information to the prompts given below.
Number of dimensions or *:
Dimension n lower bound:
Dimension n upper bound:
If the number of dimensions is entered as "*" the other dimension prompts are not displayed. This option may be used only if the array is defined either in the calling BASIC routine or a previous CSUB. If there is an explicit number of dimensions, the lower and upper bounds need to be entered for each of the dimensions. If the type of the item is string, the following prompt will appear. In response to this prompt, enter the DIMensioned or maximum string length allowed for this COM item.
String length:
The final question about the COM item is:
Will this be used as a BUFFER? (y/n):
You should answer [N] unless you plan to use the variable as a buffer in a TRANSFER statement. Read the "Transfers and Buffered I/O" chapter of the HP BASIC 6.2 Programming Guide for details. This last prompt completes the set of prompts related to specifying COM blocks. The next prompt to appear will be a request for the name of another CSUB. If you wish to specify another CSUB, you should enter its name at this time and repeat steps 3 and 4 of this section. Otherwise, simply press the [Return] key. This enters a null CSUB name and the following prompt appears:
Are there any more modules? (y/n):
If you respond with [Y] to this prompt, rmbbuildc will prompt you for another module name and you will need to repeat steps 2 through 4 of this section. Otherwise, press [N] and rmbbuildc will proceed to construct the output BASIC PROG file. To facilitate the BASIC coding process, it will also print to a file the COM declarations required by all the CSUBs in BASIC source form. This file will reside in the current directory and will have the PROG file's name with _COM appended to it.
The following table describes all the errors generated by rmbbuildc.
Error | Description
|
---|---|
Opening of file_name failed. | An attempt at creating, writing to, or reading from the specified file
caused an error.
|
Maximum number of input files exceeded. | Too many CSUB object files were specified. The maximum number of files
currently allowed is 10.
|
File file_name has unresolved references. | The specified CSUB object file still contains undefined symbols, which
should be resolved by linking the necessary additional libraries to the object
file.
|
File file_name has no CSUB version stamp. | The specified CSUB object file was not properly linked with the library
librmb.a. You should verify that you have a valid version of this
library.
|
File file_name has an invalid a.out format. | The specified CSUB object file does not conform to the format specified
for an object file.
|
Bound must be between -32768 and 32767. | The lower and upper bounds for a dimension of a COM block array item
must be within the specified range.
|
High bound value is less than low bound value. | The high bound value of an array dimension must be greater than or equal
to the low bound value of the same dimension.
|
Maximum number of elements in a dimension exceeded. | The maximum number of elements in the dimension of an array has been
exceeded.
|
No item was specified. | A COM block without any items was specified.
|
Type must be integer, real, or complex. | The type of this parameter or COM item should be integer, real, or complex.
|
Value must be 6 or less, or be an asterisk. | The number of dimensions for an array must be within the limits specified.
|
Value must be between 1 and 32767. | The value for the requested parameter must be within the specified range.
|
Procedure CSUB_name was not found. | The definition for the specified CSUB was not found in one of the CSUB
object files.
|
No procedure name was specified. | At least one CSUB should be specified during each execution of
rmbbuildc for the output BASIC PROG file to be meaningful.
|
Invalid specification of COM item item_name. | The amount of memory that would be required to store the elements of
the specified COM item exceeds the system limits.
|
Memory overflow. | The program is unable to allocate the necessary memory for processing unusually large CSUB input object files. Re-execute rmbbuildc with a single command line parameter specifying the estimated number of bytes needed to handle the large object files; the default is 1000000 bytes. |
This section shows how to manage CSUBs in BASIC. Topics covered are:
Before you can call a CSUB from a BASIC program, you have to load it in BASIC memory with a command similar to this:
LOADSUB ALL FROM "file_name"
where file_name is the name of a BASIC PROG file generated by rmbbuildc. This command loads and lists all subprograms and CSUBs in the CSUB libraries (a CSUB library is a set of CSUBs generated by a single execution of rmbbuildc) found in the PROG file. Because the CSUBs are now listed in your BASIC program, you have access to all of them.
If you just want to list a particular CSUB in your BASIC program, you would use a command similar to this:
LOADSUB "sub_name" FROM "file_name"
where sub_name is the CSUB that you want listed in your BASIC program, and file_name is the BASIC PROG file where this CSUB is located. With this command, you will only have access to the specified CSUB and to those following it in its library. You should note, however, that just selecting one CSUB from a library does not save you memory because the entire library is always loaded into memory if one of its CSUBS is specified.
Once CSUBs are loaded into a BASIC program, they can be stored in a PROG file with the STORE command. Therefore, it is possible to have a PROG file consisting of several CSUBs generated at different times that were merged together using several LOADSUB and STORE commands. For example, consider a PROG file called file_name with the following CSUB libraries:
(begin CSUB library A)
Csuba
Csubb
(end CSUB library A)
Subc
(begin CSUB library B)
Csubd
(end CSUB library B)
(begin CSUB library C)
Csube
Csubf
Csubg
(end CSUB library C)
Subh
Subi
If Csubd and Csubf are referenced in a program, executing LOADSUB FROM "file_name" will cause CSUB libraries B and C to be loaded in memory. Other CSUB libraries and subprograms are not brought in unless they are also referenced.
All CSUBs belonging to the same CSUB library are listed contiguously and must remain in the order in which they were generated by rmbbuildc. You cannot add BASIC program lines between CSUB declaration statements. However, you can delete CSUBs from a BASIC program by using the DELSUB command. Note that you can only delete the CSUB which comes first in a CSUB library. If you delete a CSUB not listed first in its library, BASIC will generate an error when you attempt to call any of the remaining CSUBs in the library.
In the following example, you may delete the CSUBs Csube and Csubf in order and you will still be able to call Csubg. However, you cannot delete Csubb, leaving Csuba, because BASIC will generate an error when you subsequently attempt to call Csuba.
(begin CSUB library A)
Csuba
Csubb
(end CSUB library A)
Subc
(begin CSUB library B)
Csubd
(end CSUB library B)
(begin CSUB library C)
Csube
Csubf
Csubg
(end CSUB library C)
Subh
Subi
Determining the cause of a CSUB run-time error is a difficult task because there is no BASIC debugger similar to the FORTRAN debugger which will let you step through your CSUB code. For this reason, you should thoroughly test your CSUBs in a test program before attempting to call them from BASIC. In situations when this approach is not practical, such as when using the BASIC file I/O routines, you may need to insert print statements in your CSUBs to monitor their execution in a BASIC program.
In FORTRAN, a run-time error not explicitly handled will cause a program to terminate. You should not allow this condition to happen in a FORTRAN CSUB since it will also cause the invocation of BASIC to abnormally terminate. Instead, you should anticipate all possible causes of CSUB run-time errors and provide code to handle those errors. The error handling code can then report the same errors to BASIC.
In order to trap a FORTRAN run-time error, a CSUB will need to use the IOSTAT=ios, ERR=label, and the END=label specifiers. These specifiers will determine the disposition of an error arising from using one of the language statements. You should note that there exist run-time errors that can not be handled with the above specifiers and that will always cause a program termination. You should make sure that those errors cannot occur in the final version of your CSUBs.
In order to provide a consistent error recovery mechanism for CSUBs, BASIC has defined an error number for reporting all CSUB run-time errors. The basic idea is for a CSUB to exclusively use this error number for reporting all signal and error conditions to BASIC; the parameterless routine csub_error should be used for this purpose.
The calling BASIC code can then recover from this error with an ON ERROR CALL/RECOVER statement and get the actual signals or errors by reading some global variables (e.g. COM block variables) accessible to the reporting CSUB or by calling another CSUB. This approach removes the need to map CSUB error numbers to BASIC error numbers, allows CSUB libraries from different sources to be shared within a program, and simplifies the task of recovering from a CSUB run-time error.
In situations when BASIC will not respond at all to user input or behave unpredictably during and after the execution of a CSUB due to an unrecoverable error condition, the current invocation of BASIC is likely to be corrupted and should be terminated with the kill command.
One of the motivations for using CSUBs with BASIC is the ability to access a rich set of HP-UX system libraries. This section covers the restrictions on the use of these libraries. The restrictions are due to the fact that BASIC also uses the libraries in its implementation.
Operations on the standard I/O streams, writing to the screen and reading from the keyboard are not supported. Therefore, FORTRAN statements like READ and WRITE should not be used with the preconnected logical unit numbers. To allow a CSUB to input characters from the keyboard and to write to the PRINTER IS device, BASIC provides a set of routines in found in the library librmb.a for keyboard and CRT register access, character input and output, scrolling, and cursor manipulation.
Routine | Description |
---|---|
kbdcrt_clear_screen | clears the alpha CRT exactly as the [Clear display] key (or CLEAR SCREEN statement) |
kbdcrt_controlcrt | sends information to a CRT control register |
kbdcrt_controlkbd | sends information to a keyboard control register |
kbdcrt_crtreadchar | reads one character from the specified location on the CRT |
kbdcrt_crtscroll | scrolls the CRT area, from line first to line last, up or down one line |
kbdcrt_cursor | removes the previous cursor and writes a new cursor to any on-screen alpha location |
kbdcrt_disp_at_xy | allows text to be written to any alpha location on the CRT |
kbdcrt_read_kbd | returns the buffer contents trapped and held by ON KBD (same as KBD$) |
kbdcrt_scrolldn | scrolls the PRINT area of the CRT down one line |
kbdcrt_scrollup | scrolls the PRINT area of the CRT up one line |
kbdcrt_statuscrt | returns the contents of a CRT status register |
kbdcrt_statuskbd | returns the contents of a keyboard status register |
kbdcrt_systemd | returns a string containing the results of calling the function SYSTEM$ for a given argument |
Device I/O in CSUBs is provided through the HP-UX device I/O library for HP-IB and GPIO interfaces and through the standard HP-UX termio routines for the RS-232 interface. For more information, read the chapter "Device I/O Library (DIL)" in the HP-UX Concepts and Tutorials: Device I/O and User Interfacing manual.
BASIC provides the a set of routines in the library librmb.a to allow operations on its file types. These file operations include:
Since CSUBs that use this module cannot be tested outside of a BASIC program, more time should be allocated for their implementation to minimize the number of debugging iterations.
Routine | Description |
---|---|
csfa_fal_create | creates an HP-UX file |
csfa_fal_create_bdat | creates a BDAT file |
csfa_fal_create_ascii | creates an ASCII file |
csfa_fal_close | closes a file |
csfa_fal_eof | writes an EOF at the current file position |
csfa_fal_loadsub_all | loads all subprograms from the specified PROG file and appends them to the program in memory |
csfa_fal_loadsub_name | loads the subprogram from the specified PROG file and appends it to the program in memory |
csfa_fal_open | opens a file for reading and writing. |
csfa_fal_position | positions the file pointer to a specified logical record number |
csfa_fal_purge | purges a file |
csfa_fal_read | reads data item(s) from a file |
csfa_fal_read_bdat_int | reads a BASIC 16-bit integer from a BDAT file |
csfa_fal_read_string | reads a string from an ASCII, BDAT, or HP-UX file |
csfa_fal_write | writes data item(s) into a file |
csfa_fal_write_bdat_int | writes a BASIC 16-bit integer to a BDAT file |
csfa_fal_write_string | writes a string to an ASCII, BDAT, or HP-UX file |