Writing Pascal CSUBs

This chapter covers the process of writing Pascal CSUBs. When it is appropriate, information on porting existing CSUBs from Pascal Workstations to HP-UX is also included.

When you are developing a system that involves the use of CSUBs, you should:

Steps for Creating a Pascal CSUB

The following steps present an overview of the process needed to create a CSUB and the results of those steps. Some porting information is also included (for those who already have Pascal Workstation CSUBs written). The CSUB related steps will be described in detail in later sections.

  1. Enter BASIC, create and store the program that will call the CSUB. This program will contain CALLs to the CSUB, but the latter need not be implemented as it will be loaded later. You only need to decide what the subprogram will do and design the interface (parameters, COM, etc.) between BASIC and the CSUB.
  2. Leave BASIC, enter HP-UX, and write the CSUB. You might, for example, use the vi editor to create the CSUB. See the sections "A Closer Look at Pascal CSUB Components" and "A Closer Look at Parameter Passing" for details on how to organize your Pascal modules and define your CSUB parameters.
  3. Compile and debug the CSUB as much as possible by writing a Pascal test program. You may want to use the Pascal debugger, pdb, for this testing task. If you are porting an existing CSUB module, you will likely get compiler errors because of invalid compiler directives or missing libraries. These errors will necessitate some changes to the source code of the modules. For a list of the compiler directives supported by the HP-UX Pascal compiler and for the procedure on how to build libraries, refer to the "HP Pascal Language Reference" manual. You should also see the "Specifying the librmb.a Library" section for additional information on CSUB libraries. Before attempting to execute any ported CSUB modules, carefully read the sections "A Closer Look at Parameter Passing" and "Accessing System Resources" for differences in the CSUB parameter types and the system calls supported. As examples of notable differences, a Pascal REAL type in HP-UX is now a 32-bit quantity, and the Pascal Workstation I/O library is no longer available from CSUBs (use HP-UX's Device I/O Library (DIL) instead).
  4. Use the ld command to generate a fully linked relocatable object file containing all the CSUBs and any necessary HP-UX library support routines. For example,
    
       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.

  5. Execute rmbbuildc and answer its prompts. This program generates the final BASIC PROG file. See the section "A Closer Look at Executing rmbbuildc" for details on how to answer the prompts.
  6. Enter BASIC and load the BASIC PROG file from the keyboard or from the BASIC program using LOADSUB. This statement generates the necessary statements in your BASIC program to call CSUBs. See the section "A Closer Look at Managing CSUBs from BASIC" for details.
  7. RUN the BASIC program which calls the desired CSUBs using the BASIC CALL or implied CALL statement.

An Example: Finding the String

This section goes through each step of creating an example Pascal 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 Pascal 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 Pascal 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.

Step 1: Create a BASIC Program that Calls the CSUB

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

Steps 2 and 3: Write, Compile, and Debug the CSUB

Enter HP-UX, edit, compile, and debug the following module called test and save it in the file named string.p. This file can be found in the directory called /usr/lib/rmb/demo.


   MODULE test;

   $SEARCH '/usr/lib/librmb.a'$

   IMPORT csubdecl;

   EXPORT
     TYPE
       string_type = RECORD
         len : shortint;
         c   : PACKED ARRAY [1..20] of CHAR;
       END;
       str_array  = ARRAY [1..10] of string_type;

     PROCEDURE find_string (file_dim        : dimentryptr;
                            VAR filex       : str_array;
                            str_dim         : dimentryptr;
                            VAR strx        : string_type;
                            VAR num_strs    : bintvaltype;
                            VAR yes         : bintvaltype);
   IMPLEMENT
     PROCEDURE find_string;
       VAR i, j : INTEGER;
       BEGIN
         yes := 0;
         i := 1;
         WHILE (i <= num_strs) AND (yes = 0) DO
           BEGIN
             IF filex[i].len = strx.len THEN
               IF strx.len = 0 THEN yes := i
               ELSE
                 BEGIN
                   j := 1;
                   WHILE (j<filex[i].len) AND (filex[i].c[j]=strx.c[j])
                     DO j := j = 1;
                   IF (filex[i].c[j] = strx.c[j]) THEN yes := i;
                 END;
             IF yes = 0 THEN i := i = 1;
           END;
       END;
   END.

Step 4: Generate a CSUB Object File

Link the code file of the Pascal module 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 string.o -u _printf -lrmb -o string

Step 5: Generate a BASIC PROG File (rmbbuildc)

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 < stream) 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: test
      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

Steps 6 and 7: LOAD and RUN the CSUB

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

A Closer Look at Pascal CSUB Components

Certain Pascal concepts and constructs should be understood so that you can apply them in CSUBs. The CSUBs are contained in a module which can be compiled independently. The EXPORT section of the module defines the procedures that will be CSUBs. The IMPORT statement names all other modules on which the present one depends. If one of the modules specified is not found in the file of the module, the Pascal compiler will then search library files to satisfy the IMPORT declarations; the library files to be searched are specified by the $SEARCH$ directive. Code for the CSUBs is found in the IMPLEMENT section of the module, as shown in the example below.


   MODULE test;

   $SEARCH '/usr/lib/librmb.a'$

   IMPORT csubdecl;

   EXPORT
     PROCEDURE find_string (file_dim : dimentryptr; VAR strx: bstringvaltype);

   IMPLEMENT
     PROCEDURE find_string;
     BEGIN
     ...
     END;
   END.

The Pascal procedures that will become CSUBs should be included in a Pascal EXPORT section. Other routines may be included in the IMPLEMENT section, but unless their names are included in the EXPORT section and specified to the rmbbuildc program, the routines will not show up as separate CSUB entry points. The following example shows how two Pascal procedures are exported to make them available as CSUBs.


   $SEARCH '/usr/lib/librmb.a'$

   IMPORT csubdecl;

   EXPORT
     PROCEDURE sqrit(x: binteger_parm; y: breal_parm);
     PROCEDURE readit(z: binteger_parm);

Importing the module csubdecl allows the use of the types binteger_parm and breal_parm, as well as several others. These are used to simplify parameter passing from BASIC.

Declarations of types and procedures from other modules may also be imported into the CSUB module. The libraries containing those modules should then also be listed in the $SEARCH$ directive.

A Closer Look at Parameter Passing

In order to be useful, a CSUB needs the ability to exchange data with the calling BASIC program. This section describes the different means 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.

Passing Parameters by Reference

As far as Pascal 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. Pascal VAR parameters are passed by reference, so they can be used to receive BASIC parameters passed by reference.

For example, the following program passes a parameter by reference to obtain a pointer to a REAL variable realvar.


   PROGRAM obvious;
   VAR realvar: REAL;

   PROCEDURE p(VAR r: REAL);
   BEGIN
     r:=-31178.0;
   END;

   BEGIN
     p(realvar);  {Compiler will emit code to pass a pointer}
   END.

The following program accomplishes the same thing with a parameter passed by value. It passes the pointer to realvar by value.


   PROGRAM not_so_obvious;
   TYPE real_ptr = ^REAL;
   VAR  realvar: REAL;

   PROCEDURE p2(rp: real_ptr);  {Pass by value}
   BEGIN
     rp^:=-31178.0;          {Must use the dereference symbol "^"}
   END;

   BEGIN
     p2(ADDR(realvar));      {Compiler passes the pointer by value}
   END.

The two key points to remember are:

Note that errors will occur if this distinction is overlooked.

Parameter Types

The BASIC parameter types are not necessarily the same as their Pascal 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 types in detail. You should refer to the module csubdecl for the definition of the types used below. The definitions may be used to declare either reference or value parameters. The choice is dependent mainly on personal programming style.

The following table provides you with a quick reference to equivalent Pascal and BASIC parameter types.

Equivalent Pascal and BASIC Parameter Types
BASIC Parameter Type Pascal Parameter Type
REAL IMPORT csubdecl: brealvaltype (by reference) or breal_parm (by value)
INTEGER IMPORT csubdecl: bintvaltype (by reference) or binteger_parm (by value)
COMPLEX IMPORT csubdecl: bcmplxvaltype (by reference) or bcomplex_parm (by value)
string_name$ IMPORT csubdecl: Two parameter types passed for strings: •; dimentryptr •; bstring_parm
array_name[lower : upper, etc.] of one of the above numeric parameter types or string_array$(lower : upper, etc.)[num_chars] IMPORT csubdecl: Two parameter types passed for arrays: •; dimentryptr •; ARRAY [lower..upper, etc.] of one of the above numeric or string parameter types.
@io_path_name IMPORT csfa: fcb_type (by reference) or fcb_ptr_type (by value)

REAL

A variable defined as a REAL in Pascal is not the same as a BASIC REAL. The latter is a 64-bit quantity while a Pascal REAL variable is only a 32-bit quantity. It is thus necessary to use the brealvaltype type for a BASIC REAL parameter. For example,


   PROCEDURE x (VAR y: brealvaltype);

is exactly the same as:


   PROCEDURE x (y_ptr: breal_parm);

because breal_parm is defined as a pointer to brealvaltype.

If you are porting an existing Pascal Workstation CSUB module, you should review all CSUBs with REAL parameters since those parameters are no longer 64-bit quantities, as they were on the Pascal Workstation.

COMPLEX

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 Pascal.

A Pascal declaration for a COMPLEX value would be:


   TYPE
     bcmplxvaltype = RECORD
       re : brealvaltype;
       im : brealvaltype;
   END;

Thus, you could use:


   IMPORT csubdecl; {Exports the type bcmplxvaltype}

   EXPORT PROCEDURE x (VAR y: bcmplxvaltype); {Pass by reference}

or use:


   IMPORT csubdecl;

   EXPORT PROCEDURE x (y_ptr: bcomplex_parm); {Pass pointer by value}

because bcomplex_parm is defined as a pointer to bcmplxvaltype. If the VAR method is used, the values are accessed as y.re and y.im. If the latter method is used, the values are accessed as y_ptr^.re and y_ptr^.im.

INTEGER

A variable defined as an INTEGER in Pascal is not the same as a BASIC INTEGER. The latter is a 16-bit quantity while a Pascal INTEGER variable is only a 32-bit quantity. Therefore, to receive a BASIC INTEGER, you should either use:


   IMPORT csubdecl; {Exports the type BINTVALTYPE}

   EXPORT PROCEDURE x (VAR y: bintvaltype); {Pass by reference}

or use:


   IMPORT csubdecl;

   EXPORT PROCEDURE x (y_ptr: binteger_parm); {Pass pointer by value}

because binteger_parm is defined as a pointer to bintvaltype.

Strings

Strings are different in BASIC and Pascal, both in their structure and the way they are passed. The structure of a variable of the Pascal type STRING is a one-byte length field followed by the characters of the string. 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 Pascal module would be:


   MODULE example;
   IMPORT csubdecl;

   EXPORT
     PROCEDURE getstring (dim_len: dimentryptr; b:bstring_parm);
   IMPLEMENT
     PROCEDURE getstring;
     VAR
       s:STRING[80];
       i:shortint;

     BEGIN
       s:='a string';
       IF STRLEN(s)>dim_len^.maxlen THEN SETSTRLEN (s, dim_len^.maxlen);
       b^.len:=STRLEN(s);
       FOR i:=1 TO b^.len DO b^.c[i]:=s[i];
     END;
   END.

The above procedure copies a Pascal string into a BASIC string. From this example, you should note how:

Although a CSUB will receive 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

I/O Paths

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 to receive an I/O path parameter from BASIC. The module csfa in the library librmb.a contains the necessary type declarations to pass I/O path parameters to Pascal. The same parameters can then also be used with the fal routines.


   $SEARCH '/usr/lib/librmb.a'$

   IMPORT csfa; {Exports the type fcb_ptr_type}

   EXPORT PROCEDURE x (y_ptr: fcb_ptr_type); {Pass pointer by value}

The typical use of the parameter y_ptr in a CSUB would then take the form:


   fal_open ('MyFile', y_ptr);

It is also possible to use:


   PROCEDURE x (VAR y : fcb_type);

but this requires the Pascal function ADDR:


   fal_open ("MyFile", ADDR(y));

when using the I/O path.

Arrays

Arrays are passed as two parameters:

The actual dimension records for the REAL, INTEGER, COMPLEX, and string arrays are represented by Figures 2-1 and 2-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

An example of receiving an INTEGER array from BASIC might be:


   EXPORT
     TYPE
       arrint = ARRAY [1..10] OF bintvaltype; {actual values, not pointers}

       PROCEDURE x (d: dimentryptr; VAR arr: arrint);

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(*))

Defining BASIC and Pascal Arrays

The BASIC and Pascal 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 ARRAY [1..5] OF BINTVALTYPE;, the sixth element in the BASIC array will correspond to the first element in the Pascal CSUB array. Array elements are stored in row-major order in both BASIC and Pascal.

Dimensioning an Array

The DIM statement should be used in conjunction with the appropriate Pascal 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 record of an array parameter to find its current dimensions.

Redimensioning an Array

If you use the REDIM statement on a multi-dimensional BASIC array, the Pascal 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 Pascal, 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 Pascal and do explicit subscript calculations based on the information in the dimension record of the array.

Declaring the Value Area of a String

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 Pascal string in the array as:


   TYPE
     string_type = RECORD
       len : shortint;
       c   : PACKED ARRAY [1..20] of CHAR; {Same maximum length as BASIC}
     END;

Useful TYPE Declarations

The module CSUBDECL defines useful types which may be used in the declaration of Pascal CSUB parameters. These declarations are listed below.


   module csubdecl;

   export
   const
     stringlimit = 32767;                  {maximum length of a string}
     maxdim = 6;                           {maximum dimensions in an array}
     maxarraysize = 16777215;              {maximum bytes in an array}

   type
     byte = 0..255;
     shortint = -32768..32767;             {two byte integer}
     bintvaltype = shortint;               {BASIC integer}
     brealvaltype = longreal;              {BASIC real}

     bcmplxvaltype = record                {BASIC complex type}
       re : brealvaltype;
       im : brealvaltype;
     end;

     bstringvaltype = record               {BASIC string type}
       len : shortint;
       c   : packed array[1..stringlimit] of char;
     end;

     valuetype = (breal, binteger, bstring, bsubstring,
                  batname, bcomplex, spare2, spare3);

     dimtype = 0..maxdim;

     boundentry = record                   {describes array bound}
       low : shortint;                     {lower limit}
       length : shortint;                  {number of elements}
     end;

     boundtype = array[1..maxdim] of boundentry; {array bounds}

     dimentry = packed record              {dimension record structure}
       case dimtype of
         0: (maxlen : shortint);           {string scalar}
         1,2,3,4,5,6:                      {array}
           (dims : byte;                   {number of dimensions}
            totalsize : 0..maxarraysize;   {total size of an array}
            case valuetype of
              breal, binteger, bcomplex:   {numeric array}
                (bound : boundtype);       {dimension boundaries}
              bstring:                     {string array}
                (stringarray : packed record
                   maxlen : shortint;      {max string length}
                   bound : boundtype;      {dimension boundaries}
                 end))
     end;

     dimentryptr = ^dimentry;              {pointer to dimension record}
     binteger_parm = ^bintvaltype;         {pointer to BASIC integer}
     breal_parm = ^brealvaltype;           {pointer to BASIC real}
     bcomplex_parm = ^bcmplxvaltype;       {pointer to complex number}
     bstring_parm = ^bstringvaltype;       {pointer to BASIC string}

Optional Parameters

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 Pascal 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 the section "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 if the CSUB was compiled with the option $RANGE ON$. This NIL check can be performed in two ways. 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 Pascal, the CSUB can either look like:


   PROCEDURE my_csub(VAR required,optional: brealvaltype);
   BEGIN
     IF ADDR(optional)<>NIL THEN....{It was passed in}

or be defined as:


   PROCEDURE my_csub(required,optional: breal_parm);
   BEGIN
     IF optional<>NIL THEN....{It was passed in}

Accessing BASIC COM from a CSUB

Another way for a BASIC program and a Pascal CSUB to interchange data is via BASIC COM blocks. In order to access a BASIC COM block from a Pascal CSUB, you should use the function find_com declared in the module csubdecl. 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 Pascal record to map the variables into members of the record. This will require you to know the layout of the block in advance since there is no way of determining this layout from the Pascal CSUB at run-time. In defining the Pascal record for accessing a COM block, you should know that the order of the members in the record should be the opposite from that of the variables in the block. For example, the first variable in the COM block should be mapped into the last member of the record.

You should also know that there is nothing to prevent you from inadvertently corrupting the COM block by writing beyond its 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 Pascal record. Finally, you should follow the same restriction previously mentioned for CSUB string array parameters in specifying the bounds of string arrays in COM blocks. 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:


   $SEARCH '/usr/lib/librmb.a'$
   ...
   IMPORT csubdecl;
   EXPORT PROCEDURE x...

   IMPLEMENT
     PROCEDURE x...
     TYPE
       strtype = RECORD
         len: shortint;
         c  : PACKED ARRAY[1..10] of CHAR;
         END;
       comtype = PACKED RECORD
         d: strtype;             {Do not use bstringvaltype!}
         c: brealvaltype;
         b: PACKED ARRAY [1..5] OF bintvaltype;
         a: bintvaltype;         {Note the reverse order of the members}
         END;

     VAR
       comptr : ^comtype;
       vala : bintvaltype;

     BEGIN
       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.

Accessing Global Variables

All global variables declared in Pascal modules linked in CSUB object files are allocated in one contiguous area of memory. Previous implementations of BASIC provided the ability to access this area of memory from BASIC via a COM block. This feature is not supported in this implementation of BASIC because this area of memory is no longer allocated in a COM block.

A Closer Look at Linking CSUB Object Files

After thoroughly testing your CSUBs in a Pascal program, you are now ready to generate a CSUB object file. This object file brings together the CSUB modules and the necessary libraries to satisfy the external references of those modules.

Using the Linker

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_modules -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_modules are the .o files generated by the Pascal 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.

Specifying the librmb.a Library

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 modules 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).

Resolving External References

In general, specifying the library librmb.a alone in your link command will be sufficient to resolve all external references from your CSUB modules; 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.

A Closer Look at Executing rmbbuildc

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].

Procedure for Using rmbbuildc

The following sections contain steps that explain how to use rmbbuildc.

Step 1: Executing 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. If you already have Pascal Workstation stream files for use with the BUILDC utility, you may use them with rmbbuildc by removing the responses to the prompts for the Header and Jump file names, and adding a response for the output BASIC PROG file name prompt. The other prompts remain the same.

Step 2: Entering a PROG File Name

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.

Step 3: Naming CSUB Object Files

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.

Step 4: Specifying CSUB Interfaces

The remaining prompts deal with the specification of each CSUB interface. For each Pascal module that you specify, you will be asked for the name and parameters of its CSUBs. The following steps explain the prompts that you will encounter during this specification.

  1. Enter the name of a module containing CSUBs when the following prompt is given.
    
       Module name:
    
  2. Enter the name of a CSUB that is in the current module when the following prompt is given.
    
       CSUB name:
    

    This prompt will be repeated until a null CSUB name is specified by pressing the [Return] key.

  3. The following four prompts are repeated for each parameter of the current CSUB until a null parameter name is entered. You should specify the name of the parameter, its type, whether it is an array, and whether it is optional.
    
       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 Pascal 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:

    
       PROCEDURE read_result(VAR return_int: bintvaltype);
    

    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.

  4. Indicate whether the current CSUB accesses a BASIC COM by responding with either [Y] or [N] to the following prompt.
    
       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 in the current module. If you have another CSUB to specify, 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.

rmbbuildc Errors

The following table describes all the errors generated by rmbbuildc.
rmbbuildc Errors
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.

A Closer Look at Managing CSUBs from BASIC

This section shows how to manage CSUBs in BASIC. Topics covered are:

Loading CSUBs into a BASIC Program

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 to be listed 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.

Deleting CSUBs

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.

Example of Deleting CSUBs

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

Handling CSUB Run-time Errors

Determining the cause of a CSUB run-time error is a difficult task because there is no BASIC debugger similar to the Pascal 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 Pascal, a CSUB run-time error may be generated by the Pascal system or by application code with an ESCAPE statement. You may handle the error condition by either trapping it within the CSUB or letting it propagate to the BASIC code. With either choice, there are precautions you should take to avoid corrupting the execution of BASIC.

Trapping Errors

The TRY/RECOVER statement provides the mechanism to trap an error condition in Pascal. You may use it within a CSUB to process Pascal system errors or errors explicitly generated by your code with the ESCAPE statement.

You should refer to the "HP Pascal Language Reference" manual for a listing of the run-time errors that may be generated by the Pascal system.

Reporting Errors to BASIC

After detecting a CSUB run-time error, you may decide to report the error to the BASIC calling code by simply ESCAPing out of a CSUB and causing a BASIC error condition. This condition may then be handled with an ON ERROR CALL/RECOVER statement. In this case, you need to be aware of the way BASIC will handle the error based on its ESCAPE code. The table below summarizes this information and will help you map Pascal ESCAPE codes to BASIC error numbers.

Error Numbers
Pascal Errors BASIC Definition
-32768...-891 SYSTEM ERROR
-890...-881 Reserved by operating system
-880...-32 SYSTEM ERROR
-33...-1 Pascal system error, ERROR (escapecode = 400)
0 Pascal exit, no error
1...32767 User error, ERROR (escapecode MOD 1000)

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 errors to BASIC; the parameterless routine csub_error in the module kbdcrt 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 errors by calling an error CSUB or by reading some global error variables (e.g. COM block variables) also 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.

There may be 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. In such situations, the current invocation of BASIC is likely to be corrupted and should be terminated with the kill command.

Accessing System Resources

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.

Allocating Dynamic Memory

When allocating memory from the heap, the only supported Pascal memory allocation statements allowed in CSUBs are NEW and DISPOSE. There are no special initialization procedures required to use these statements, but you are 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. In addition, you should use the compiler directive $HEAP_DISPOSE ON$ to activate the DISPOSE statement. The amount of heap memory available for CSUBs is determined by the:

You should note that the Pascal statements MARK and RELEASE are not supported in Pascal CSUBs. If you are porting Pascal Workstation CSUBs which make use of these statements, you should modify your code to:

Simple Keyboard and Printer I/O

Operations on the standard I/O streams, writing to the screen and reading from the keyboard, for example, are not supported. Therefore, Pascal routines like READLN and WRITELN should only be used with an explicit file specifier. To allow a CSUB to input characters from the keyboard and to write to the PRINTER IS device, BASIC provides the module kbdcrt which implements procedures and functions for keyboard and CRT register access, character input and output, scrolling, and cursor manipulation. If you are porting Pascal Workstation CSUBs which rely on the routines READ, READLN, WRITE, and WRITELN, you should modify them to use this module.

Keyboard and CRT I/O Routines
Procedure\ or Function Description
clear_screen clears the alpha CRT exactly as the [Clear display] key (or CLEAR SCREEN statement)
controlcrt sends information to a CRT control register
controlkbd sends information to a keyboard control register
crtreadchar reads one character from the specified location on the CRT
crtscroll scrolls the CRT area, from line first to line last, up or down one line
cursor removes the previous cursor and writes a new cursor to any on-screen alpha location
disp_at_xy allows text to be written to any alpha location on the CRT
read_kbd returns the buffer contents trapped and held by ON KBD (same as KBD$)
scrolldn scrolls the PRINT area of the CRT down one line
scrollup scrolls the PRINT area of the CRT up one line
statuscrt returns the contents of a CRT status register
statuskbd returns the contents of a keyboard status register
systemd returns a string containing the results of calling the function SYSTEM$ for a given argument

Device I/O

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. Pascal Workstation CSUBs which use the CSUB I/O library should be converted to call these new libraries. 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 File I/O

BASIC provides the file access library module (fal) 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.

File Access Routines
Procedure\ or Function Description
fal_create creates an HP-UX file
fal_create_bdat creates a BDAT file
fal_create_ascii creates an ASCII file
fal_close closes a file
fal_eof writes an EOF at the current file position
fal_loadsub_all loads all subprograms from the specified PROG file and appends them to the program in memory
fal_loadsub_name loads the subprogram from the specified PROG file and appends it to the program in memory
fal_open opens a file for reading and writing.
fal_position positions the file pointer to a specified logical record number
fal_purge purges a file
fal_read reads data item(s) from a file
fal_read_bdat_int reads a BASIC 16-bit integer from a BDAT file
fal_read_string reads a string from an ASCII, BDAT, or HP-UX file
fal_write writes data item(s) into a file
fal_write_bdat_int writes a BASIC 16-bit integer to a BDAT file
fal_write_string writes a string to an ASCII, BDAT, or HP-UX file