When you are developing a system that uses 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 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 prompts you to enter your name (up to a maximum of 80 characters). It then passes your name in a string variable called String$ to the CSUB called parameters. The CSUB changes the first character of you name to an asterisk (*) and passes the name change back to the calling program along with the string length parameter called int_val. The CSUB also passes the real variable called real_val back to the calling program.
Enter BASIC, edit and store this program in a file named param_val. This file can be found in the directory called /usr/lib/rmb/demo.
100 LOADSUB ALL FROM "Parm_vals"
110 DIM String$[80]
120 INTEGER Int_val
130 REAL Real_val
140 LINPUT "Enter your name and press [Return].",String$
150 Parameters(String$,Int_val,Real_val)
160 PRINT "Your name has been changed to: """;String$;""""
170 PRINT
180 PRINT "Your name contains ";
190 PRINT Int_val;
200 PRINT "characters."
210 PRINT
220 PRINT "Real_val = ";Real_val
230 DELSUB Parameters
240 END
Enter HP-UX, edit, compile, and debug the following function called parameters, and save it in the file named atest.c. The file atest.c can be found in the directory called /usr/lib/rmb/demo. Note that, if you decide to change this C CSUB into an assembly CSUB, follow the steps provided in the next section.
#include <csubdecl.h>
typedef struct
{
shortint len;
char c[80];
} str_type;
parameters (str_dim, str_val, int_val, real_val)
dimentryptr str_dim;
str_type *str_val;
binteger_parm int_val;
breal_parm real_val;
{
str_val->c[0] = '*';
*int_val = str_val->len;
*real_val = 78.783;
}
Enter HP-UX, edit, compile, and debug the function called parameters that is provided in the previous section, and save it in a file named atest.c. The file atest.c can be found in the directory called /usr/lib/rmb/demo. To change the C source file to an assembly source file, type the following:
cc -S atest.c
The C compiler option -S creates an assembly code file called atest.s from the source C CSUB file called atest.c. Your assembly code file should look similar to the following:
global _parameters
_parameters:
link.l %a6,&LF1
movm.l &LS1,(%sp)
mov.l 12(%a6),%a0
movq &42,%d0
mov.b %d0,2(%a0)
mov.l 12(%a6),%a0
mov.l 16(%a6),%a1
mov.w (%a0),(%a1)
mov.l 20(%a6),%a0
mov.l L12=0x4,4(%a0)
mov.l L12,(%a0)
L11:
unlk %a6
rts
set LF1,-0
set LS1,0
lalign 4
L12:
long 0x4053b21c,0xac083127
data
Once the assembly source file has been created, you are ready to create an .o file out of it by typing:
as atest.s
When you execute the HP-UX command ls, you will notice the file called atest.o is in your current working directory.
Link the code file atest.o with the BASIC CSUB library librmb.a 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 atest.o -u _printf -lrmb -o atest
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: Parm_vals
CSUB object file names(s): atest
Module name: [Return]<>
CSUB name: parameters
Parameter name: string$
Parameter type is string
Is this an array? (y/n): n
Is this an optional parameter? (y/n): n
Parameter name: int_val
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: real_val
Parameter type (I/R/C for Integer/Real/Complex): r
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, Parm_vals is automatically loaded from the BASIC program. Therefore, you only need to re-enter the BASIC system, GET "param_val", and RUN the program. The output should be:
Your name has been changed to: "*ohn J. Doe"
Your name contains 11 characters.
Real_val = 78.783
Any C function you implement may easily be transformed into a CSUB if you follow these guidelines:
The include file csubdecl.h was defined to facilitate this parameter matching between a BASIC program and C CSUBs. It provides all the necessary definitions to allow you to specify types for the formal parameters of C CSUBs. These types are limited to those that are supported for CSUBs and are described in detail in the next section.
In order to be useful, a CSUB needs the ability to exchange data with the calling BASIC program. This section describes the different way 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 C 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. Therefore, in order to receive a referenced BASIC parameter, a C CSUB needs to match it with a formal parameter declared as a pointer to the BASIC parameter.
The following example shows how the variable realvar is passed as a parameter by reference to a function using the address of the variable.
p(r)
double *r;
{
r=-31178.0;
}
main()
{
double realvar;
p(&realvar); /* Note the & symbol */
}
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 C counterparts. It is important that the parameter types of a CSUB be correct so that BASIC and the CSUB can interface properly. This section will explain the types in detail. You should refer to the include file csubdecl.h for the definition of the types used below.
The following table provides you with a quick reference to equivalent C and BASIC parameter types.
BASIC Parameter Type | Assembly\Parameter Size | C Parameter Type |
---|---|---|
REAL | 8 bytes | #include <csubdecl.h> breal_parm |
INTEGER | 2 bytes | #include <csubdecl.h> binteger_parm |
COMPLEX | 16 bytes | #include <csubdecl.h> bcomplex_parm |
string_name$ | 4 bytes for both parameters | #include <csubdecl.h> Two parameter types passed for strings: ; dimentryptr ; bstring_parm |
ary_nm[lower : upper, etc.] of one of the above numeric parameter types or str_ary$(low : up, etc.)[n_chars] | 4 bytes for both parameters | #include <csubdecl.h> Two parameter types passed for arrays: ; dimentryptr ; one of the above numeric or string array types. |
@io_path_name | 4 bytes | #include <csubdecl.h> fcb_ptr_type |
A variable defined as a double in C maps into a BASIC REAL. Therefore, a BASIC REAL parameter can be defined in a CSUB as:
#include <csubdecl.h>
x(y)
breal_parm y;
since breal_parm is defined as a pointer to double.
A variable defined in BASIC as COMPLEX is a floating point value with real and imaginary parts. There is no built-in COMPLEX type in C.
A C declaration for a COMPLEX value would be:
struct bcmplxvaltype
{
double re;
double im;
}
Thus, you could use:
#include <csubdecl.h>
x(y)
bcomplex_parm y;
because bcomplex_parm is defined as a pointer to bcmplxvaltype.
A variable defined as an int in C is not the same as a BASIC INTEGER. The latter is a 16-bit quantity while a C integer is a 32-bit quantity. Therefore, to receive a BASIC INTEGER, you should use:
#include <csubdecl.h>
x(y)
binteger_parm y;
because binteger_parm is defined as a pointer to bintvaltype.
Strings are different in BASIC and C, both in their structure and the way they are passed. The structure of a C string is a set of characters terminated by a NULL character while the BASIC string has a two-byte length field followed by the characters of the string.
BASIC passes its strings as two parameters:
An example of how this would look in a C function is as follows:
#include <csubdecl.h>
static char *s="a string";
getstring(dim_len, b)
dimentryptr dim_len;
bstring_parm b;
{
short i;
if (strlen(s)>dim_len->maxlen) s[dim_len->maxlen]=0;
b->len=strlen(s);
for (i=0; i<b->len; i==) b->c[i]=s[i];
}
The above function copies a C 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. The size of this block is 190 bytes. 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 C CSUB receives an I/O path parameter from BASIC. The include file csubdecl.h contains the necessary type declarations to pass I/O path parameters to C. The same parameters can then be used with the fal routines.
#include <csubdecl.h>
x(y_ptr)
fcb_ptr_type y_ptr;
The typical use of the parameter y_ptr in a CSUB would then take the form:
csfa_fal_open(filename, y_ptr);
Arrays are passed as two parameters:
The actual dimension structures for the REAL, INTEGER, COMPLEX, and string arrays are represented by Figures 3-1 and 3-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 Structure
String Array Dimension Structure
An example of receiving an INTEGER array from BASIC is as follows:
x(d, arr)
dimentryptr d;
bintvaltype 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 C 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 bintvaltype arr[], the sixth element in the BASIC array will correspond to the first element in the C CSUB array. Array elements are stored in row-major order in both BASIC and C.
The DIM statement should be used in conjunction with the appropriate C 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 structure. 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 C declaration of the array will be invalid the next time the array is accessed in a CSUB. Therefore, if you accessed arr[2][3] in C, you would not get the same value as Arr(2,3) from BASIC. To be immune from the effects of REDIM, you should declare a multi-dimensional BASIC array as a one-dimensional array in C and do explicit subscript calculations based on the information in the dimension structure of the array.
You should also note that the type bstringvaltype should not be used in specifying the value area for a string array since it defines a string with the maximum allowable number of characters. Instead, you should declare a different type, with the maximum allowable number of characters in the array strings set equal to the dimension of the equivalent BASIC string array. Thus, for the following BASIC definition of a string array,
DIM S$(1:10)[20]
you would define the type for a C string in the array as:
typedef struct
{
shortint len;
char c[20]; /* Same maximum length as BASIC */
} str_type;
The include file csubdecl.h defines useful types which may be used in the declaration of C CSUB parameters. These declarations are listed below.
#define STRINGLIMIT 32767 /* maximum length of a string */
#define MAXDIM 6 /* maximum dimensions in an array */
#define MAXARRAYSIZE 16777215 /* maximum bytes in an array */
typedef unsigned char byte;
typedef short shortint; /* two byte integer */
typedef short bintvaltype; /* BASIC integer */
typedef double brealvaltype; /* BASIC real */
struct bcmplxvaltype /* BASIC complex type */
{
brealvaltype re;
brealvaltype im;
};
struct bstringvaltype /* BASIC string type */
{
shortint len;
char c[STRINGLIMIT];
};
struct boundentry /* describes array bound */
{
shortint low; /* lower limit */
shortint length; /* number of elements */
};
union dimentry /* dimension record union */
{
shortint maxlen; /* string scalar */
struct /* array */
{
byte dims; /* number of dimensions */
byte pad;
short totalsize; /* total size of an array */
union
{ /* numeric array */
struct boundentry bound[MAXDIM]; /* dimension boundaries */
struct /* string array */
{
shortint maxlen; /* maximum string length */
struct boundentry bound[MAXDIM]; /* dimension boundaries */
} strval;
} arrval;
} arrdim;
};
typedef union dimentry *dimentryptr; /* pointer to dimension union
*/
typedef bintvaltype *binteger_parm; /* pointer to BASIC integer */
typedef brealvaltype *breal_parm; /* pointer to BASIC real */
typedef struct bcmplxvaltype *bcomplex_parm; /* pointer to BASIC complex */
typedef struct bstringvaltype *bstring_parm; /* pointer to BASIC string */
typedef char *fcb_ptr_type;
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 C 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 dereference the pointer.
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 C, the CSUB should perform the following test:
my_csub(required, optional)
breal_parm required, optional;
{
if (optional)... /* It was passed in */
Another way for a BASIC program and a C CSUB to interchange data is via BASIC COM blocks. In order to access a BASIC COM block from a C CSUB, you should use the function find_com. When passed the name of the COM block that is to be accessed, this function will return a pointer to the beginning of the value area of that block. 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. When find_com cannot find the COM block requested, it returns a NIL pointer.
In order to read and store values in the variables of a COM block, you will need to define a C structure to map the variables into members of the structure. This will require you to know the layout of the block in advance since there is no way of determining this layout from the C CSUB at run-time.
In defining the C structure for accessing a COM block, you should know that:
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:
#include <csubdecl.h>
typedef struct
{
shortint len;
char c[10];
} strtype;
typedef struct
{
strtype d; /* Do not use bstringvaltype! */
brealvaltype c;
bintvaltype b[5];
bintvaltype a; /* Note the reverse order of the members */
} *comtype;
extern comtype find_com();
x()
{
comtype comptr;
bintvaltype vala;
comptr=find_com("Num1"); /* Observe same case of letters as BASIC does */
vala=comptr->a;
...
A COM block is subject to being moved in memory at RUN, LOAD, and GET time. Therefore, to guarantee that you always get the current location of a block, you should not remember its address in a CSUB but always use the function find_com instead. Similarly, you should not attempt to remember the address of a variable defined in a COM block.
After thoroughly testing your CSUBs in a C 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 C 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 C 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:
read_result(return_int)
binteger_parm 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 "Advanced Transfer Techniques" chapter of the "BASIC Interfacing Techniques" manual 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 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 C 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 where 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 C, a CSUB run-time error is generated with a signal condition. You may either trap this condition with your own signal handler or let the signal propagate to the BASIC code. With either choice, there are precautions you should take to avoid corrupting the execution of BASIC.
In order to trap a signal condition within a CSUB, you will need to install your own signal handler for the selected signal in the code of a CSUB. You should then make sure that the same code will restore the original handler for that signal when the CSUB terminates; otherwise, BASIC may behave unpredictably following the call to the CSUB. In order to assign a handler for a specific signal, you should use the system routine sigvector(2) exclusively.
If you decide to let a signal propagate to the BASIC code, you should verify that it is a valid signal, as defined by the ON EXT SIGNAL statement. You should always trap all other signals with a signal handler and handle them within your CSUB; BASIC may not handle invalid signals in a consistent fashion and may behave unpredictably when it receives them. On the other hand, the disposition of valid signals by BASIC is determined by whether a system signal event-initiated branch is in effect at the time the CSUB is called and is exactly specified by the ON EXT SIGNAL statement.
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 calling an error CSUB or by reading some global variables (e.g. COM block variables) alos accessible to the reporting 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 where BASIC will not respond at all to user input or behaves 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.
When allocating memory from the heap, you may use the standard memory allocation package malloc (3C or 3X). Although there is no initialization procedure required, you are still responsible for explicitly releasing any memory allocated within a CSUB. If you fail to do so, you may encounter an out-of-memory condition after repeated CSUB calls. The amount of heap memory available for CSUBs is determined by the:
Operations on the standard I/O streams, writing to the screen and reading from the keyboard are not supported. Therefore, C routines like printf and scanf should not be used. 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 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$) |
kdbcrt_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 a set of routines found in the library &greek;ibrmb.a| to allow operations on its file types. These file operations include:
Since CSUBs that use these routines 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 |