COBOLMainframe

COBOL Subprograms: CALL, USING, BY REFERENCE, and LINKAGE SECTION

TT
TopicTrick
COBOL Subprograms: CALL, USING, BY REFERENCE, and LINKAGE SECTION

COBOL modular programming uses the CALL statement to invoke subprograms, passing data between caller and called program through a defined parameter list. This is how large mainframe systems are composed from reusable business logic modules — a CICS front-end calls an account validation subprogram, which calls a database access subprogram, which calls a logging subprogram. Understanding CALL mechanics is essential for both writing new modules and maintaining existing ones.

Subprogram Anatomy

A COBOL subprogram looks almost identical to a main program, with two key differences: it receives data through a LINKAGE SECTION, and it returns with GOBACK (not STOP RUN):

cobol
IDENTIFICATION DIVISION.
PROGRAM-ID. VALACCT.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-LOCAL-BALANCE     PIC S9(11)V99 COMP-3.
01 WS-VALIDATION-RESULT PIC X(1).

LINKAGE SECTION.
01 LS-PARMS.
   05 LS-ACCOUNT-ID     PIC X(10).
   05 LS-AMOUNT         PIC S9(9)V99 COMP-3.
   05 LS-RETURN-CODE    PIC S9(4) COMP.
   05 LS-ERROR-MSG      PIC X(50).

PROCEDURE DIVISION USING LS-PARMS.

MAIN-LOGIC.
    MOVE ZERO TO LS-RETURN-CODE
    MOVE SPACES TO LS-ERROR-MSG
    PERFORM VALIDATE-ACCOUNT-ID
    IF LS-RETURN-CODE = ZERO
        PERFORM VALIDATE-AMOUNT
    END-IF
    GOBACK.

VALIDATE-ACCOUNT-ID.
    IF LS-ACCOUNT-ID = SPACES
        MOVE -1 TO LS-RETURN-CODE
        MOVE 'ACCOUNT ID IS REQUIRED' TO LS-ERROR-MSG
    END-IF.

VALIDATE-AMOUNT.
    IF LS-AMOUNT <= ZERO
        MOVE -2 TO LS-RETURN-CODE
        MOVE 'AMOUNT MUST BE POSITIVE' TO LS-ERROR-MSG
    END-IF.

The LINKAGE SECTION defines the parameter structure. The PROCEDURE DIVISION USING clause lists the parameters. GOBACK returns control to the calling program.

CALL Statement

Static CALL (Literal Program Name)

cobol
WORKING-STORAGE SECTION.
01 WS-CALL-PARMS.
   05 WS-ACCOUNT-ID     PIC X(10).
   05 WS-AMOUNT         PIC S9(9)V99 COMP-3.
   05 WS-RETURN-CODE    PIC S9(4) COMP.
   05 WS-ERROR-MSG      PIC X(50).

PROCEDURE DIVISION.
    MOVE 'ACC0012345' TO WS-ACCOUNT-ID
    MOVE 500.00 TO WS-AMOUNT

    CALL 'VALACCT' USING WS-CALL-PARMS

    IF WS-RETURN-CODE NOT = ZERO
        DISPLAY 'VALIDATION FAILED: ' WS-ERROR-MSG
        PERFORM HANDLE-VALIDATION-ERROR
    END-IF.

CALL 'VALACCT' uses a literal program name — this is a static call. The linker resolves the reference at link-edit time and both programs share a single load module.

Dynamic CALL (Identifier Program Name)

cobol
WORKING-STORAGE SECTION.
01 WS-PROG-NAME         PIC X(8).

PROCEDURE DIVISION.
    MOVE 'VALACCT ' TO WS-PROG-NAME
    CALL WS-PROG-NAME USING WS-CALL-PARMS.

When the program name is stored in a field (WS-PROG-NAME), the CALL is dynamic. The runtime loads the program from the load library at first call. Subsequent CALLs within the same run unit find the program already resident in memory.

Dynamic CALL enables runtime polymorphism — you can change WS-PROG-NAME based on a configuration table to call different validation routines without recompiling the calling program.

Passing Parameters

BY REFERENCE (Default)

BY REFERENCE passes the address of the data item. The called program maps the LINKAGE SECTION directly onto the caller's memory:

cobol
CALL 'VALACCT' USING BY REFERENCE WS-CALL-PARMS.

*> 'BY REFERENCE' is the default — this is equivalent:
CALL 'VALACCT' USING WS-CALL-PARMS.

When the subprogram modifies LS-RETURN-CODE, it is modifying WS-CALL-PARMS's return code field directly. No copying occurs — the change is immediately visible to the calling program after CALL returns.

BY CONTENT

BY CONTENT passes a copy of the data. The called program receives its own copy; modifications do not affect the caller:

cobol
CALL 'CALCINTEREST' USING
    BY CONTENT  WS-PRINCIPAL
    BY CONTENT  WS-INTEREST-RATE
    BY REFERENCE WS-CALCULATED-INTEREST.

Here, PRINCIPAL and RATE are passed as copies (the subprogram cannot accidentally change them), but the RESULT field is passed by reference so the subprogram can return the calculated value.

BY VALUE

BY VALUE passes the actual value for scalar items — used primarily when interfacing with C programs or system routines:

cobol
CALL 'CGETENV' USING
    BY REFERENCE WS-ENV-VAR-NAME
    BY VALUE     FUNCTION LENGTH(WS-ENV-VAR-NAME)
    BY REFERENCE WS-ENV-VAR-VALUE.

Passing Individual Fields vs Group Items

You can pass individual fields or entire group records:

cobol
*> Pass entire group record (most common):
CALL 'UPDATEBAL' USING WS-UPDATE-PARMS.

*> Pass individual fields:
CALL 'UPDATEBAL' USING
    WS-ACCOUNT-ID
    WS-TRANSACTION-AMOUNT
    WS-NEW-BALANCE
    WS-RETURN-CODE.

The LINKAGE SECTION of UPDATEBAL must match the layout — either as a single group with the same substructure, or as four separate 01-level items in the same order:

cobol
LINKAGE SECTION.
*> Matching the individual-field CALL:
01 LS-ACCOUNT-ID         PIC X(10).
01 LS-TRANSACTION-AMOUNT PIC S9(9)V99 COMP-3.
01 LS-NEW-BALANCE        PIC S9(11)V99 COMP-3.
01 LS-RETURN-CODE        PIC S9(4) COMP.

PROCEDURE DIVISION USING
    LS-ACCOUNT-ID
    LS-TRANSACTION-AMOUNT
    LS-NEW-BALANCE
    LS-RETURN-CODE.

CALL Exception Handling

ON EXCEPTION / ON OVERFLOW

cobol
CALL 'EXTPROC' USING WS-PARMS
    ON EXCEPTION
        DISPLAY 'EXTPROC NOT FOUND OR LOAD FAILED'
        MOVE -99 TO WS-RETURN-CODE
    NOT ON EXCEPTION
        PERFORM CHECK-RETURN-CODE
END-CALL.

ON EXCEPTION fires when:

  • The called program cannot be found in the load library (dynamic CALL)
  • The program failed to load due to authorization or storage issues

Without ON EXCEPTION, a load failure causes an S806 abend.

CANCEL Statement

CANCEL releases a dynamically loaded program from storage, forcing it to be reloaded on next CALL. This also reinitializes its WORKING-STORAGE:

cobol
CANCEL 'VALACCT'.
CANCEL WS-PROG-NAME.

Use CANCEL when:

  • You need the subprogram's WORKING-STORAGE reinitialized (it normally retains values between calls)
  • You are done with a subprogram and want to release its storage
  • You need to pick up a new version of a subprogram that was updated in the load library

Do NOT CANCEL a program that is currently executing (including any program in your call chain above you).

Nested CALL Chains

COBOL supports call chains of arbitrary depth. A typical production pattern:

text
MAINPGM → CALL 'TRXPROCESS'
              → CALL 'VALIDATETRX'
                   → CALL 'DBREAD' (database access)
                   → CALL 'DATECHEK' (date validation)
              → CALL 'UPDATEDB'
                   → CALL 'DBWRITE'
              → CALL 'LOGAUDIT'

Each called program gets its own WORKING-STORAGE. LINKAGE SECTION items point back to the caller's storage — there is no stack copying for BY REFERENCE parameters.

Returning Multiple Values

COBOL has no native return value — all output goes through LINKAGE SECTION fields. The standard convention is:

cobol
LINKAGE SECTION.
01 LS-SERVICE-PARMS.
   05 LS-INPUT-ACCOUNT-ID    PIC X(10).   *> INPUT
   05 LS-INPUT-AMOUNT        PIC S9(9)V99 COMP-3. *> INPUT
   05 LS-OUTPUT-NEW-BALANCE  PIC S9(11)V99 COMP-3. *> OUTPUT
   05 LS-OUTPUT-RETURN-CODE  PIC S9(4) COMP.       *> OUTPUT: 0=ok, negative=error
   05 LS-OUTPUT-ERROR-MSG    PIC X(80).             *> OUTPUT: populated on error

Document clearly which fields are INPUT (set by caller before CALL), OUTPUT (set by subprogram before GOBACK), or both (caller sets, subprogram may update).

Complete Example: Account Update Service

cobol
IDENTIFICATION DIVISION.
PROGRAM-ID. ACCTUPD.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DB-STATUS          PIC S9(4) COMP VALUE ZERO.
01 WS-ACCOUNT-BALANCE    PIC S9(11)V99 COMP-3.

LINKAGE SECTION.
01 LS-REQUEST.
   05 LS-ACCOUNT-ID      PIC X(10).
   05 LS-AMOUNT          PIC S9(9)V99 COMP-3.
   05 LS-TRANS-TYPE      PIC X(2).
      88 LT-CREDIT              VALUE 'CR'.
      88 LT-DEBIT               VALUE 'DR'.
   05 LS-RETURN-CODE     PIC S9(4) COMP.
   05 LS-MESSAGE         PIC X(80).

PROCEDURE DIVISION USING LS-REQUEST.

MAIN-LOGIC.
    MOVE ZERO   TO LS-RETURN-CODE
    MOVE SPACES TO LS-MESSAGE
    PERFORM FETCH-ACCOUNT-BALANCE
    IF LS-RETURN-CODE = ZERO
        PERFORM APPLY-TRANSACTION
    END-IF
    GOBACK.

FETCH-ACCOUNT-BALANCE.
    CALL 'DBFETCH' USING LS-ACCOUNT-ID
                         WS-ACCOUNT-BALANCE
                         WS-DB-STATUS
    IF WS-DB-STATUS NOT = ZERO
        MOVE -1 TO LS-RETURN-CODE
        MOVE 'ACCOUNT NOT FOUND' TO LS-MESSAGE
    END-IF.

APPLY-TRANSACTION.
    EVALUATE TRUE
        WHEN LT-CREDIT
            ADD LS-AMOUNT TO WS-ACCOUNT-BALANCE
        WHEN LT-DEBIT
            IF LS-AMOUNT > WS-ACCOUNT-BALANCE
                MOVE -2 TO LS-RETURN-CODE
                MOVE 'INSUFFICIENT FUNDS' TO LS-MESSAGE
                GOBACK
            END-IF
            SUBTRACT LS-AMOUNT FROM WS-ACCOUNT-BALANCE
        WHEN OTHER
            MOVE -3 TO LS-RETURN-CODE
            MOVE 'UNKNOWN TRANSACTION TYPE' TO LS-MESSAGE
            GOBACK
    END-EVALUATE
    CALL 'DBUPDATE' USING LS-ACCOUNT-ID
                          WS-ACCOUNT-BALANCE
                          WS-DB-STATUS
    IF WS-DB-STATUS NOT = ZERO
        MOVE -4 TO LS-RETURN-CODE
        MOVE 'DATABASE UPDATE FAILED' TO LS-MESSAGE
    END-IF.

Next Steps

Subprograms establish the call structure. The next essential tool is copybooks — COPY statements that share record definitions, SQL declarations, and data layouts across programs without duplication. See COBOL Copybooks, or return to the COBOL Mastery course.