MODULE 11 · SUBPROGRAMS AND COPYBOOKS · 1/8

Why shops split code into subprograms

12 min10 XPQuiz

So far every program you have written has been one self-contained unit: read input, do the work, STOP RUN. Real mainframe systems do not look like that. A bank's overnight batch is hundreds of programs, and most of them CALL shared subprograms to do common jobs: validate an account number, work out the number of days between two dates, calculate tax, format an address, write an audit record.

Why split code up?

  • Reuse. The rule for "is this a valid IBAN" should exist once. If forty programs each had their own copy, forty copies would need fixing the day the rule changes, and one would be missed.
  • Separate compilation. Each subprogram is its own source member, compiled on its own. A change to the tax routine means recompiling and testing one small program, not every batch job that uses it.
  • Load modules. On z/OS the compiled and link-edited program is a load module stored in a load library (a PDS or PDSE). When a job step runs EXEC PGM=PAYRUN, the system loads PAYRUN, and PAYRUN can in turn load TAXCALC from the library when it calls it.
  • Team ownership. One team owns the date routines, another owns customer validation. The interface between them is the parameter list.

The vocabulary

The program that runs first is the main program; the one it calls is the called program or subprogram. Together they form a run unit. The caller passes data with CALL ... USING; the called program describes that data in its LINKAGE SECTION and names it on PROCEDURE DIVISION USING. It finishes with GOBACK, which returns to the statement after the CALL.

Here is the whole idea in one source file. TAXCALC is written inside PAYRUN, between PAYRUN's procedure code and its END PROGRAM. That is a nested program, which you will write yourself later in this module:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. PAYRUN.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-GROSS         PIC 9(5)V99 VALUE 3000.00.
       01  WS-TAX           PIC 9(5)V99.
       01  WS-TAX-ED        PIC Z(4)9.99.
       PROCEDURE DIVISION.
           DISPLAY "PAYRUN: CALLING TAXCALC"
           CALL "TAXCALC" USING WS-GROSS WS-TAX
           MOVE WS-TAX TO WS-TAX-ED
           DISPLAY "PAYRUN: TAX IS " WS-TAX-ED
           STOP RUN.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TAXCALC.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-GROSS         PIC 9(5)V99.
       01  LS-TAX           PIC 9(5)V99.
       PROCEDURE DIVISION USING LS-GROSS LS-TAX.
           COMPUTE LS-TAX ROUNDED = LS-GROSS * 0.20
           DISPLAY "TAXCALC: DONE"
           GOBACK.
       END PROGRAM TAXCALC.
       END PROGRAM PAYRUN.
PAYRUN: CALLING TAXCALC
TAXCALC: DONE
PAYRUN: TAX IS   600.00

TAXCALC has no storage for LS-TAX. When it computes into LS-TAX it is writing straight into PAYRUN's WS-TAX, because the CALL passed the address of that field. That is why PAYRUN sees the result.

Copybooks: the other half of sharing

Programs that pass a customer record to each other must agree on its layout to the byte. So nobody types the layout twice: it lives in a copybook, a source member that the compiler pastes in wherever a program says COPY CUSTREC. Change the copybook, recompile the programs that use it, and they all agree again. You will use copybooks at the end of this module.

On the job

When you join a mainframe team, one of the first things to learn is which common routines already exist: date conversion, check digits, error logging, abend handling. Writing your own when the shop has one is a classic newcomer mistake, and code review will catch it. Ask where the copybook and load libraries are, and how the shop's change tool (Endevor, ChangeMan) promotes a new version of a module.

Check your understanding

1. A subprogram changes the rounding rule for tax. With dynamic CALLs, what has to be recompiled?
2. Which statement ends a subprogram and returns control to its caller?
3. Where does a subprogram describe the data its caller passes in?