MMCC MASTHEAD
Mid-Michigan Computer Consultants - Bay City, Michigan
 


CONTENTS       (old style)
Mid-Michigan Computer Consultants
509 Center
Bay City, Michigan

Sales (989) 892-9242
Support (989) 686-8860

Plb-C210.cfm v1.0


plb-t010.cfm
 

ANSI Standard PL/B Language and Visual PL/B

CALL / ROUTINE

NOTE TO OUR READERS
This web resource is written for our own use. But we feel strongly that the PL/B language should be shared with the software community. So feel free to use this at will! BUT PLEASE... if you find errors or omissions or have a better way to do something. TELL US! Dialog helps us all. Send e-mail to: support@mmcctech.com

CALL   /    CALLS
ROUTINE LROUTINE and PROCEDURE
LOADMOD

SPECIAL NOTE

Before even starting this section it is important to explain a confusing bit of terminology. In the PL/B community the term LOADMOD is frequently used to describe a separately compiled external sub-program. In truth, LOADMOD is a PL/B verb used to load an external program into memory without executing the program. You must pay attention to the context when you hear or see "LOADMOD". For this discussion, the term will only be used to decribe the PL/B Verb case.

SUBROUTINES

Subroutines are a staple of the programming art. They are implemented in one form or another in all languages. The concept is to write a general purpose routine to handle a specific function and then to use that routine from many places in the program. For example, one routine could be written to find the day of the week for any date. That routine could then be used from many places.

Subroutines are commonly implemented for use in a CALL/RETURN manner. The subroutine can take many forms and be stored in many ways. When it is needed, a CALL instruction is written in the main code and that transfers control to the subroutine. The subroutine does it's thing then issues a RETURN to go back to the instruction following the CALL in the main routine.

In most languages, CALL/RETURN structures are implemented with a "stack". Stacks are common elements of all operating systems and languages. The concept is that data can be PUSHED onto a stack. Data is later retrieved by POPPING the top entry off the stack. It's a last in, first out scenario. Many items can be pushed onto the stack. Popping an item out gets the most recent. For CALL/RETURN, the address of the instruction following the CALL is pushed onto the stack. A RETURN instruction pops out the top entry of the stack and transfers control to that element. Events can be "nested" by multiple CALLS without returns. Each RETURN goes back to the routine that called it.

In-line vs External subroutines


There are two common methods for writing subroutines. The first, and most common, is the "in-line" subroutine. In this form, the subroutine code appears in the source code for the program and is compiled as part of the overall program.

"External" subroutines are separately compiled programs which are not a direct part of the main program. External routines are called in much the same way as an in-line routine but the similarity ends there. The in-ilne routine is simply a code snippit; the external is a full program with it's own start, body, and ending.

There are two benefits of in-line routines. First, the routine is slightly more efficient because it is part of a single program. Second, there may be less programming needed because the in-line routine has access to all of the variables and other components of the main program.

The disadvantage of an in-line routine is that it is less likely to become a re-usable component which would be used in many programs. The solution to that is to write the in-line routine and store it as a separate code module which is included in a main program with the INCLUDE statement. As an example, MMCC has a standard date conversion module that was wirtten as an INCLUDE unit. Each program that needs date handling will INCLUDE this module. The programmer can then CALL any of the standard sub-routines within that INCLUDE unit.

The disadvandate to an INCLUDE unit is that when it is changed; the programmer must remember to re-compile every program that includes the module.

External routines have the advantage of being independent of the programs which call them. In the date routine example above, when one changes the date logic then every program which includes that module must be recompiled to get the change. With an external, only the external program itself must be recompiled. Immediately, every program which calls the external will get the change the next time it runs.

INCLUDE routines are nice Because of their simplicity. If the routine is unlikely to be changed, and if the routine is small, then including the code in the mainline is good. If, on the otherhand, the routine will likely be changed, or if the routine is large, it may be better implemented as an external program.

PL/B Subroutine techniques

Simple Subroutines

PL/B provides a number of techniques for implementing subroutines. The simplest is just a CALL to an execution label. Control is transferred to that label in the program where processing continues. When (or if) a RETURN statement is encountered, control is returned to the statement following the CALL.

There is no defined communication of data between this type of CALL and the sub-routine being called. Both routines are part of the same program and both have access to the same memory space. The sub-routine may modify data in memory which the calling routine may later use. For example:
WORKING_DATE   DIM   8
DAY_OF_WEEK    DIM  10

      MOVE    "20010501" to WORKING_DATE
      CALL    GET_DAY_OF_WEEK
      DISPLAY DAY_OF_WEEK
      rest of program.....
	  
GET_DAY_OF_WEEK
      figure out the day of the week
      from the WORKING_DATE.  Put the
      results in DAY_OF_WEEK
      RETURN

There are two forms of the simple CALL instruction: CALL and CALLS. Both forms work exactly the same way. The only difference is how the subroutine is named:

CALL USING


A second calling method provides for the communication of data elements between two routines. The same CALL and CALLS instructions are used, but additional information is communicated by including the USING phrase:
    CALL    GET_AGE USING MEMBER_BIRTHDATE, MEMBER_AGE
    CALL    GET_AGE USING MEMBER_JOINDATE,  YEARS_IN_CLUB
In this case, the called subroutine does not specifically know or care where the data to be used is stored. Instead it will be given a pointer to the variables named in the USING phrase. Those variables can be used in any way that the subroutine desires.

In the example, the first using variable could be a date from which to calculate elapsed years (the age). The second variable could be calculated by the subroutine and then used by the calling routine on return.

The benefits of the USING construction can be substantial. In a simple CALL, both routines use the same, pre-defined memory space. To accomplish the same objectives of the example using simple calls would require something like this:
    MOVE   MEMBER_BIRTHDATE to WORKING_DATE
    CALL   GET_AGE
    MOVE   WORKING_AGE to MEMBER_AGE
	
    MOVE   MEMBER_JOINDATE to WORKING_DATE
    CALL   GET_AGE
    MOVE   WORKING_AGE to YEARS_IN_CLUB

WHEN IS "USING" USED?



The key to the USING phrase is in the way the subroutine is defined. For simple CALL routines, all that is needed for the subroutine is an execution label, some code, and a RETURN:
   GET_AGE  calculation code
            more code, etc.
            RETURN
Subroutines which are to be called with USING, must be specifically defined using either ROUTINE, LROUTINE or PRODECURE. For example:
   GET_AGE  LROUTINE INPUT_DATE, OUTPUT_AGE
            calculation code
            more code, etc.
            RETURN
Subroutines of this type may be coded as part of the main-line program or they may be separately compiled routines which are outside of the calling program.

ROUTINE, LROUTINE and PROCEDURE

These three formats can be used almost interchangebly. The exceptions are
  1. A PROCEDURE is written with a USING clause and a (L)ROUTINE is not:
          date_routine PROCEDURE USING INPUT_DATE, OUTPUT_AGE
          date_routine ROUTINE         INPUT_DATE, OUTPUT_AGE
    
  2. (L)ROUTINE arguments (parameters) can be either pointers or defined variables.
    PROCEDURE arguments must be defined variables.

  3. PROCEDURES may be called with literals in the calling string. (L)ROUTINES may not:
          CALL   DATE_ROUTINE USING "20011214", OUTPUT_AGE
    
  4. PROCEDURES and ROUTINES may be external routines.
    LROUTINES are "local" and appear within the main program unit.

DEFINED VARIABLES vs POINTERS

Defined variables are the traditional method of creating variables in a program. You write something like:
     MEMBER_BIRTHDATE  DIM    8
The compiler allocates memory space for the variable and defines it according to your definition.

A pointer, on the other hand does not carry any definition information nor does it allocate memory. Instead it simply tells the compiler that the variable is a DIM or a FORM type and that the actual memory location and size will be provided at run time.

The objective of a pointer is to abstract the actual data variable from the definition of the variable. What that means is that you can, for example, define a batch of variables then refer to them in a subroutine by reference only.

A POINTER variable is a regular DIM or FORM and is defined almost like any other variable. The only difference is that where a normal variable has the number of bytes following the DIM/FORM or has an initial value, the POINTER has a carat (^).

When calling a PROCEDURE or (L)ROUTINE, the CALL is written using the specific defined variable names. The PROCEDURE or (L)ROUTINE, on the other hand, is written using different variable names and those variables are defined as POINTERs. At the time of the call, the POINTERS are given the address of the actual data and the routine works on the actual data. For example:
MEMBER_BIRTHDATE   DIM     8
MEMBER_AGE         FORM    3
JOINED_DATE        DIM     8
YEARS_IN_CLUB      FORM    3
      CALL    GET_AGE USING MEMBER_BIRTHDATE, MEMBER_AGE
      CALL    GET_AGE USING JOINED_DATE,      YEARS_IN_CLUB
	        .... more program code....
	  
WORKING_DATE       DIM     ^
CALCULATED_AGE     FORM    ^
GET_AGE  ROUTINE  USING  WORKING_DATE, CALCULATED_AGE
         ... calculate age from date...
         RETURN

PASSING ARGUMENTS

When using POINTERS, all processing is performed on the specific variables in memory. There is only a single copy of the variable and all code points to the same location. When a CALL is performed using a pointer, the subroutine is given the memory address of the variables listed in the CALL and it works on those specific items.

PROCEDURES or (L)ROUTINES can also be called using defined variables. In this case there are individual memory locations for each of the variables. The unique element is that PL/B will internally MOVE the data in the sending variable to the corresponding receiving variable. On return the contents of the receiving variables are (optionally) passed back to the sending variable.

The effect of this is that the program works much as it would if you did a simple call and moved the variables yourself. For example:
    MOVE   MEMBER_BIRTHDATE to WORKING_DATE
    CALL   GET_AGE
    MOVE   WORKING_AGE to MEMBER_AGE
THE RULES:

EXTERNAL ROUTINES

PL/B allows subroutines to be local, part of the mainline program, or external, separately compiled and free stadning routines.

Local routines have the advantage of more simplicity and slight preformance advantages. Since the subroutine is part of the mainline program it requires little special linkages or support.

External routines are entirely separate programs. When the "external" is called, it will be loaded into memory and executed much like a local routine. Unless you specifically unload the external, it will remain in memory and be available if called again.

External routines are a bit more complex because they require all of the general surrounding code that any program would require. You have to define the working variables, provide for startup and shutdown code, and other code.

The main advantage of an external program is that a single external can service any number of mainline programs. If the external is changed and recompiled, the all programs which call it benefit from the changes but those programs do not have to be recompiled.

Externals can be called using pointers, literals or defined variables just the same as an internally coded subroutine would be called.

WRITING AN EXTERNAL



An external is written much as any other program would be written. The unique element is that it is not executed like other programs. Instead, it is CALLED from another program and it is loaded on-the-fly.

Normal programs are executed and processing starts with the first executable statement in the program. Externals may have multiple entry points. When they are called and loaded, the CALL specifies the entry point to be used.

Externals are required to have at least one entry point. They can have more than one. An entry point is simply a PROCEDURE or a ROUTINE statement with an optional USING phrase. For example a single external could contain these subroutines:
  GET_AGE  PROCEDURE USING  INPUT_DATE, OUTPUT_AGE
           .... processing code ....
           RETURN

  GET_DAY_OF_WEEK  PROCEDURE USING  INPUT_DATE, OUTPUT_DAY_NAME
           .... processing code ....
           RETURN
Note that externals may NOT include defined COMMON storage. You can use LABELED COMMON.

CALLING AN EXTERNAL



Externals are called just as a local ROUTINE with one exception: The external program's entry point must be named. The external is coded as:
     "{programname};{entrypoint}"
External programs may be explicitely defined within the mainline program and called using a variable name much as a CALLS would be used. Or the call may be coded explicitely using a literal. For example, the following two calls are effectively the same:
GET_AGE     EXTERNAL   "DATESUB;GET_AGE"
     CALL  GET_AGE USING MEMBER_BIRTHDATE, MEMBER_AGE
     CALL  "DATESUB;GET_AGE" USING MEMBER_BIRTHDATE, MEMBER_AGE

OTHER NOTES

Memory Usage
In February 2002 a question was posted to the Sunbelt web board concerning mamory usage by externals. The response from Sunbelt was as follows:
"An external module uses about 2K more in memory than simply including the file in the original program. Other than that there is very little difference.

"A benefit that has not been mentioned is dynamic loading of the external. If you do not use the LOADMOD statement, but simply CALL or CALLS a function in the external, then the external is not loaded until needed. The benefit is that you may not actually need all of the externals referenced in your program for any given execution. Some may be there for special cases that don't arise every time. So if the external is not needed for a given run then it is never loaded. If there are a lot of externals, then possibly many would never be used."


v1.10

Write to MMCC Technical Support at:               Send e-mail to MMCC.
MMCC - Technical Support
600 W. Midland
Bay City, MI 48708
(989) 686-8860
© 1997 - 2024 MMCC - All Rights Reserved