CSUB Prototyper Utility

The CSUB prototyper saves time in the creation of CSUBs. It consists of CSUBs and BASIC functions that let you call CSUB routines from BASIC in their "native" language (FORTRAN, Pascal, C, or assembly). In order to call CSUB routines in this manner, prototyper functions convert actual parameter types of the BASIC calling routine to the formal parameter types of the CSUB routine being called.

Before continuing, you should review the chapter in this manual that pertains to your particular CSUB language.

Why Use the CSUB Prototyper?

The CSUB prototyper has two functions:

Creating CSUBs

The following table gives a comparison between the procedural steps for creating a CSUB using the standard method and using the prototyper.
Comparison of CSUB Creation Procedures
Step Standard CSUB Procedure Prototyper CSUB Procedure
1 Create the BASIC program that is to call the CSUB(s). Same.
2 Exit BASIC and enter the HP-UX environment. Same.
3 Select an editor (vi for example) and write a program that contains your CSUBs. This program can be written in FORTRAN, Pascal, C, or assembly language. Run the program and debug it until it works. Same.
4 Provide the proper interface for the CSUB(s) by defining the formal parameters of the CSUB(s). This step is not required.
5 Link the necessary libraries to your compiled CSUB to generate a CSUB object file. Same.
6 Execute rmbbuildc This step is not required.
7 Load the CSUB(s) by executing the LOADSUB command from the keyboard or the BASIC program that is calling the CSUB(s). Load the CSUB(s) by specifying the CSUB object file to the appropriate prototyper CSUB.

The table shows that the CSUB prototyper shortens the steps required to create a CSUB. This will save time, but you will need to have enough memory to hold the object code for an entire program that contains the CSUB(s).

Calling CSUBs Dynamically

The CSUB prototyper also opens up new possibilities for a BASIC application whose requirements for compiled language routines can only be determined at run-time. In this situation, you need not anticipate the usage requirement of the application by defining a CSUB interface for all of the possible routines that may be invoked. Instead, the selected routines may be called by name with the CSUB prototyper. By carefully restricting this method of accessing CSUBs to those routines that are not time critical, you can use this feature to give an application greater flexibility in the area of dynamic code loading without impairing its performance.

Using the Prototyper to Create a CSUB

This section develops a complete example that illustrates all the major steps involved in using the CSUB prototyper. You may find it useful to refer to the section "Steps for Creating a CSUB" in chapter 2, 3, or 4 of this manual, depending on the language you are using to create your CSUB. In our example, we will use the C language.

Writing CSUB Routines in C

The procedure for writing CSUB routines consists of five steps.

  1. Using the CSUB prototyper in a BASIC program.
  2. Exiting BASIC to HP-UX.
  3. Writing C subroutines.
  4. Generating a relocatable object file.
  5. Running the BASIC program.

Step 1: Using the CSUB Prototyper in a BASIC program

This BASIC program shows a typical session with the CSUB prototyper.

 
100   REAL A            ! Real variable file descriptor.
110   REAL J
120   REAL R
130   DIM S$[30]
140   ! Load the CSUB prototyper library.
150   LOADSUB ALL FROM "CPR"
160   ! Load the object file containing the CSUB routines.
170   Cprload(A,"example")
180   ! Call procedure "sub1"
190   Cpr(FNInit(A,"_sub1"),FNI(50),FNL(J,1),FNS(S$,1))
200   PRINT "J is",J
210   PRINT "S$ is",S$
220   ! Call function "sub2" which returns a 64-bit floating
230   ! point value.
240   R=FNCpr64(FNInit(A,"_sub2"),FND(45))
250   PRINT "R is",R
260   ! Unload the object file.
270   Cprunload(A)
280   END
 

The output of this program is as follows:

 
J is      50
S$ is    sub1
R is      90
 

This program illustrates the major operations provided by the CSUB prototyper:

Step 2: Exiting BASIC to HP-UX

To exit the BASIC environment, execute the following statement:

 
QUIT [Return]
 

The BASIC default window should disappear. The window that the rmb command was executed in will appear with your HP-UX shell prompt.

Step 3: Writing C Subroutines

This step shows an example C program called example.c that contains two CSUB routines. There are two important things to remember during this step:

Here is a listing of the program example.c, which defines two simple C routines:

 
void sub1(i, j, str)
short i;
int *j;
char *str;
{
char *s="sub1";
*j=i;
strncpy(str, s, strlen(s)+1);
}
  
double sub2(r)
double r;
{
return r*2;
}
 

The first routine called sub1 simply assigns the value of its first numeric parameter to its second numeric parameter, a variable parameter, and copies characters into its string parameter. The second routine called sub2 is a function which doubles the value of its input parameter and returns the result. You should note that, unlike CSUBs, these routines can have both value and variable formal parameters.

After compiling and testing the routines in C, you are now ready to generate a relocatable object file so that you can call the two routines from BASIC, using the prototyper.

Step 4: Generating a Relocatable Object File

The process of generating a relocatable object file as input to the prototyper is the same as that used for creating a CSUB object file. In other words, you use the HP-UX ld command to link your object files with the required libraries. The syntax for this command is as follows:

 
ld -rd -a archive example.o -u _printf -lrmb -o example
 

The above command generates the fully linked relocatable object file example, which will be used as input to the prototyper.

Step 5: Running the BASIC Program

Before running the BASIC program called test_csubs, enter the BASIC environment by typing:

 
rmb [Return]
 

Now load the program called test_csubs (found in /usr/lib/rmb/demo) by executing the following command:

 
GET "test_csubs" [Return]
 

Note that you may have to use the statement MASS STORAGE IS to move to the directory that contains test_csubs.

To run the program test_csubs, type:

 
RUN [Return]
 

or press [RUN] ([f3]).

Deciding Whether or Not to Create a Standard CSUB

After verifying the execution of compiled routines using the CSUB prototyper, you have two options to finalize the implementation of your program:

  1. You can invoke the compiled routines using the prototyper.
  2. You can convert the prototyper calls into direct CSUB calls.

Reasons for Choosing the First Option

It makes sense to invoke the compiled routines using the prototyper if:

Reasons for Choosing the Second Option

By converting the prototyper calls into direct CSUB calls, you can optimize the access time required to load the compiled routines with a CSUB interface. This option is also useful if you want to STORE a BASIC program with all of its CSUBs to make it self contained. The task of providing a CSUB interface is most easily done by defining a procedural interface for each of the selected compiled routines, as shown below for the current example:

 
void csub1(i, j, dim, str)
short *i;
double *j;
dimentryptr dim;
bstring_parm str;
  
{
  int jj;
  
  jj=(*j);
  sub1(*i, &jj, str->c);
  str->len=strlen(str->c)+1;
  *j=jj;
}
  
double csub2(result, r)
double *result;
double *r;
  
{
  *result=(*r)*2;
}
 

The main concern during this conversion process is to provide a CSUB interface with BASIC parameters that are compatible with those of the formal parameters of the compiled routines. This requirement may necessitate some parameter processing, as shown above, before and after the call to the compiled routines.

Passing Parameters

This section explains the parameter passing conventions of the prototyper and lists its parameter type mappings.

In order to correctly use the CSUB prototyper, it is necessary that you know its parameter passing conventions and the mappings that it allows between the types of BASIC parameters used to call compiled routines and the types of formal parameters and return values defined by the compiled routines.

Parameter Passing Conventions

The prototyper execute CSUB and functions can accept up to 16 parameters which correspond to the formal parameters of a given compiled routine. The BASIC variables or constants which are specified as parameters to these execute CSUBs may necessitate some type conversions before being passed to a compiled routine. Thus, you need to follow these conventions:

Mapping of Parameter Types

This section presents the mapping between the actual and formal parameters of compiled routines and mapping between BASIC return types and prototyper execute CSUBs. The following tables present this information.
Mapping Between Actual and Formal Parameters of a CSUB
CSUB Formal\Parameter Type Parameter\Function Variable Parameter\Supported BASIC Actual\Parameter Type Languages\Supported
8-bit character FNC Yes INTEGER all
16-bit integer FNI Yes INTEGER all
32-bit integer FNI No INTEGER all
32-bit integer FNL Yes REAL all
32-bit floating-point FNR Yes REAL all
64-bit floating-point FND Yes REAL all
32-bit pointer FNP Yes REAL Pascal, C
string FNS Yes string all
aggregate FNA Yes REAL Pascal, C

You should note that the language of the routine can affect these specifications, and that some parameter types have restrictions on the use of variable parameters.
Mapping Between BASIC Return Value Types and\Prototyper execute CSUBs or Functions
Compiled Routine\Return Value Type Execute CSUB\or Function BASIC Return\Value Type
void Cpr
8-bit character FNCpr8 INTEGER
16-bit integer FNCpr16 INTEGER
32-bit integer FNCpr32l REAL
32-bit floating-point FNCpr32r REAL
64-bit floating-point FNCpr64 REAL
32-bit pointer FNCpr32p REAL

In addition to the information in these tables, each formal parameter and return value of a compiled routine may have some considerations that need to be discussed. These considerations are as follows:
8-bit character The value of the INTEGER actual parameter should be within the range of a byte. The prototyper takes care of making the proper format conversions before and after the call to the compiled routine.

32-bit integer The INTEGER actual parameter may not be a variable parameter since INTEGER variables are only 16 bits wide. In order to have a variable parameter, you should use a REAL variable with a value in the range of a 32-bit integer. The prototyper takes care of making the proper format conversions before and after the call to the compiled routine.

32-bit floating-point The value of the REAL actual parameter should be within the range of a 32-bit floating-point return value. The prototyper takes care of making the proper format conversions before and after the call to the compiled routine.

32-bit pointer Because BASIC does not define 32-bit scalar variables, the prototyper stores pointer values in REAL variables. You should not attempt to print or use such variables in BASIC since their values are only meaningful when passed to compiled routines. The prototyper takes care of making the proper format conversions before and after the call to the compiled routine.

String The maximum length of a string actual parameter should always be greater than the current length of the string by one because the prototyper converts the storage for the string to a format suitable for the language of the compiled routine. This means that a string literal may not be used as a parameter to the string parameter function. Furthermore, variable string parameters should have a maximum length large enough to accommodate any new string value set by the compiled routine.

Aggregate An aggregate, a Pascal record or a C struct/union, is specified by passing the pointer to it and its storage size. The prototyper takes care of passing the correct information to the compiled routine. There are two ways you can create an aggregate in BASIC: use a dynamic memory allocation routine in on of the compiled languages, or get a pointer to a named BASIC COM whose definition mirrors that of the aggregate.

Handling Prototyper Errors

Prototyper CSUBs signal a run-time error with a BASIC CSUB run-time error message. In order to retrieve the actual error number, you should call the error CSUB (Cprerr) provided with the CSUB prototyper library (CPR). This CSUB returns a positive error number for each error generated by one of the prototyper CSUBs. The error numbers for the prototyper CSUBs are listed in the following table.
Prototyper CSUB Errors
CSUB or\Function Error Number Description
Cprload 1 Out of memory
Cprload 2 Object file access error
Cprload 3 Relocation error
FNInit 1 Compiled routine not found in object file
FNS 1 Maximum string length too small