1. Programa chamador e sub-programa

A comunicação entre programas COBOL segue uma hierarquia simples:

  • Programa chamador (main program) — inicia a execução, chama o sub-programa com CALL e recebe o controle de volta quando o sub-programa termina
  • Sub-programa (called program) — recebe controle via CALL, executa sua lógica e devolve o controle ao chamador com GOBACK

🦕 Analogia — CALL é como fazer uma ligação

O programa chamador faz uma ligação (CALL) para o sub-programa, passa as informações necessárias (USING) e aguarda na linha. O sub-programa atende, processa, talvez altere os dados recebidos, e desliga (GOBACK) — o chamador retoma exatamente de onde parou, agora com os dados possivelmente atualizados.

2. CALL estático vs dinâmico

Existem duas formas de invocar um sub-programa:

COBOL CALL estático e dinâmico
             * CALL ESTÁTICO: nome literal entre aspas
             * O linker resolve o endereço em tempo de link-edit
             * O sub-programa é incluído no load module junto com o chamador
           CALL 'VALCPF' USING WS-CPF
                                  WS-RETORNO

             * CALL DINÂMICO: nome em variável (sem aspas)
             * O loader resolve o endereço em tempo de execução
             * Permite decidir qual programa chamar em runtime
       WORKING-STORAGE SECTION.
       01  WS-NOME-PROG  PIC X(08) VALUE 'VALCPF  '.

       PROCEDURE DIVISION.
           MOVE 'VALDATA' TO WS-NOME-PROG
           CALL WS-NOME-PROG USING WS-DATA
                                     WS-RETORNO
CALL EstáticoCALL Dinâmico
NomeLiteral entre aspas 'PROG'Variável sem aspas
ResoluçãoLink-edit (antes de rodar)Runtime (durante execução)
Load moduleSub-programa incluso no mesmo móduloSub-programa carregado separado
FlexibilidadeBaixa — nome fixoAlta — pode variar em runtime
PerformanceLigeiramente mais rápidoOverhead mínimo no primeiro CALL
ManutençãoRequer re-link ao trocar sub-programaBasta substituir o load module

✅ Qual usar no dia a dia?

Em produção bancária, o padrão mais comum é CALL dinâmico com o nome do sub-programa em um campo de Working Storage. Isso facilita a substituição de versões sem relinkar o chamador, e é o que a maioria dos frameworks de batch em mainframe exige. Reserve o CALL estático para utilitários críticos de performance onde o overhead do loader importa.

3. LINKAGE SECTION

A LINKAGE SECTION é declarada no sub-programa e descreve as áreas de memória que o chamador vai compartilhar via USING. Ela não aloca memória — apenas nomeia e descreve o layout do espaço que o chamador passa:

COBOL Estrutura do sub-programa com LINKAGE SECTION
       IDENTIFICATION DIVISION.
       PROGRAM-ID. VALCPF.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SOMA-DIG    PIC 9(04).   *uso interno do sub-programa
       01  WS-RESTO       PIC 9(02).

             * LINKAGE SECTION: parâmetros recebidos do chamador
       LINKAGE SECTION.
       01  LK-CPF         PIC X(11).
       01  LK-RETORNO     PIC X(02).
           88  CPF-VALIDO   VALUE '00'.
           88  CPF-INVALIDO VALUE '01'.
           88  CPF-ESPACOS  VALUE '02'.

             * PROCEDURE DIVISION lista os parâmetros recebidos
       PROCEDURE DIVISION USING LK-CPF
                                  LK-RETORNO.
       0000-INICIO.
           PERFORM 1000-VALIDA-CPF
           GOBACK.

       1000-VALIDA-CPF.
           IF LK-CPF = SPACES
               SET CPF-ESPACOS TO TRUE
           ELSE
               PERFORM 1100-CALCULA-DIGITOS
           END-IF.

💗 Diferença entre WORKING-STORAGE e LINKAGE SECTION

  • WORKING-STORAGE — memória própria do programa, inicializada quando o programa é carregado e mantida entre CALLs (se o programa permanecer residente)
  • LINKAGE SECTION — não aloca memória própria; aponta para a área de memória do chamador. O que você modifica em LK-CPF está modificando diretamente o campo do chamador (exceto se usar BY CONTENT)

4. USING — passando parâmetros

Os parâmetros são listados em USING tanto no CALL quanto no PROCEDURE DIVISION do sub-programa. A ordem importa — o primeiro parâmetro do CALL corresponde ao primeiro da LINKAGE:

COBOL Correspondência de parâmetros
             * CHAMADOR — WORKING-STORAGE
       01  WS-CPF-ENTRADA   PIC X(11).
       01  WS-CODIGO-RETORNO PIC X(02).
       01  WS-AREA-DADOS.
           05  WS-NOME         PIC X(40).
           05  WS-SALDO        PIC S9(13)V99.

             * CHAMADOR — CALL
           CALL 'VALCPF' USING WS-CPF-ENTRADA   *→ LK-CPF
                                WS-CODIGO-RETORNO *→ LK-RETORNO
                                WS-AREA-DADOS    *→ LK-AREA-DADOS

             * SUB-PROGRAMA — LINKAGE SECTION
       LINKAGE SECTION.
       01  LK-CPF           PIC X(11).       *← WS-CPF-ENTRADA
       01  LK-RETORNO       PIC X(02).       *← WS-CODIGO-RETORNO
       01  LK-AREA-DADOS.                      *← WS-AREA-DADOS
           05  LK-NOME         PIC X(40).
           05  LK-SALDO        PIC S9(13)V99.

       PROCEDURE DIVISION USING LK-CPF
                                  LK-RETORNO
                                  LK-AREA-DADOS.

⚠️ Os layouts precisam ser compatíveis

O COBOL não verifica em tempo de compilação se o layout do campo no chamador bate com o layout na LINKAGE do sub-programa. Se o chamador passa um campo PIC X(11) e a LINKAGE descreve PIC X(20), o sub-programa vai ler além dos limites do campo — comportamento indefinido e potencial abend. Sempre garanta que os tamanhos e tipos coincidam, ou use uma copybook compartilhada.

5. BY REFERENCE vs BY CONTENT vs BY VALUE

Cada parâmetro pode ser passado de três formas diferentes, que determinam se o sub-programa pode ou não alterar os dados do chamador:

COBOL Modos de passagem de parâmetro
             * BY REFERENCE (padrão quando omitido)
             * Sub-programa acessa a mesma área de memória do chamador
             * Alterações no sub-programa afetam o chamador
           CALL 'SUBPROG' USING BY REFERENCE WS-SALDO

             * BY CONTENT
             * Uma cópia do conteúdo é criada e passada ao sub-programa
             * Alterações no sub-programa NÃO afetam o chamador
           CALL 'SUBPROG' USING BY CONTENT WS-TAXA
                                              WS-PRAZO

             * BY VALUE
             * Passa o valor (não o endereço) — usado para interoperabilidade
             * com programas C, Java ou rotinas do sistema
           CALL 'CEEDAYS' USING BY REFERENCE WS-DATA-IN
                               BY REFERENCE WS-FORMATO
                               BY REFERENCE WS-LILIAN
                               BY REFERENCE WS-FC

             * Misturando modos no mesmo CALL
           CALL 'SUBPROG' USING BY REFERENCE WS-ENTRADA
                                BY CONTENT  WS-PARAMETRO
                                BY REFERENCE WS-SAIDA
ModoO que passaSub-prog altera chamador?Uso típico
BY REFERENCEEndereço de memóriaSimParâmetros de entrada/saída
BY CONTENTCópia do valorNãoParâmetros somente de entrada
BY VALUEO valor em si (não endereço)NãoInteroperabilidade C/Java/LE APIs

6. GOBACK vs STOP RUN

Esta é uma das confusões mais comuns — e mais perigosas — em programas COBOL:

COBOL GOBACK vs STOP RUN
             * GOBACK — devolve controle ao chamador
             * No programa PRINCIPAL: equivale ao STOP RUN
             * No SUB-PROGRAMA: volta para o ponto após o CALL
           GOBACK.

             * STOP RUN — encerra TODA a thread de execução
             * No programa PRINCIPAL: correto
             * No SUB-PROGRAMA: encerra o job inteiro, sem voltar ao chamador!
           STOP RUN.    *NUNCA use isso num sub-programa

⚠️ STOP RUN em sub-programa mata o job todo

Se um sub-programa executa STOP RUN, toda a execução termina imediatamente — o chamador não recebe controle de volta, arquivos abertos podem não ser fechados corretamente e o RETURN-CODE pode não ser propagado. Sempre use GOBACK em sub-programas. Use STOP RUN somente no programa principal, no parágrafo de finalização.

💗 Regra de ouro

Adote sempre GOBACK em todos os programas — tanto no principal quanto nos sub-programas. No programa principal ele funciona igual ao STOP RUN. Assim você nunca corre o risco de encerrar o job inadvertidamente a partir de um módulo chamado.

7. RETURN-CODE

O RETURN-CODE é um registrador especial (equivalente ao código de retorno do job step) que pode ser definido pelo sub-programa para sinalizar status ao chamador. No JCL, o COND do step seguinte pode verificar esse valor:

COBOL RETURN-CODE no sub-programa e no chamador
             * SUB-PROGRAMA: define o código de retorno antes do GOBACK
       WORKING-STORAGE SECTION.
       01  WS-RC     PIC 9(04) VALUE ZEROS.

       PROCEDURE DIVISION USING LK-CPF LK-RETORNO.
           PERFORM 1000-PROCESSA
           MOVE WS-RC TO RETURN-CODE
           GOBACK.

             * CHAMADOR: verifica após o CALL
       PROCEDURE DIVISION.
           CALL 'VALCPF' USING WS-CPF WS-RETORNO

           EVALUATE RETURN-CODE
               WHEN 0    CONTINUE              *ok
               WHEN 4    PERFORM 9100-AVISO   *warning
               WHEN OTHER PERFORM 9000-ERRO   *erro
           END-EVALUATE

             * Tratando abend do sub-programa com ON EXCEPTION
           CALL 'VALCPF' USING WS-CPF WS-RETORNO
               ON EXCEPTION
                   MOVE 'SUBPROG NAO ENCONTRADO' TO WS-MSG
                   PERFORM 9000-ERRO-CALL
               NOT ON EXCEPTION
                   PERFORM 2000-CONTINUA
           END-CALL

8. CANCEL — liberando memória

Por padrão, um sub-programa chamado permanece residente em memória após o GOBACK. Na próxima chamada ele não é reinicializado — a Working Storage mantém os valores do CALL anterior. O CANCEL força o descarregamento e reinicialização:

COBOL CANCEL e comportamento da Working Storage
             * Sem CANCEL: sub-programa mantém WS entre chamadas
           CALL 'CONTAROWS' USING WS-CHAVE WS-RESULTADO
                 * acumulador interno de CONTAROWS ainda tem valor do CALL anterior

           CALL 'CONTAROWS' USING WS-CHAVE WS-RESULTADO
                 * resultado pode estar errado por causa do estado residual

             * Com CANCEL: força reinicialização do sub-programa
           CANCEL 'CONTAROWS'
           CALL   'CONTAROWS' USING WS-CHAVE WS-RESULTADO
                 * agora WS de CONTAROWS está nos valores VALUE iniciais

🟣 Para quem já programa — estado residual como feature

O comportamento de preservar o estado entre CALLs pode ser intencional. Sub-programas que acumulam totais, mantêm conexão com DB2 aberta ou guardam configurações carregadas uma só vez dependem dessa característica. Só use CANCEL quando precisar garantir que o sub-programa comece do zero — por exemplo, em loops que chamam o mesmo módulo para contextos diferentes. CANCEL também libera a memória ocupada pelo módulo, o que pode ser relevante em programas que chamam muitos módulos distintos.

9. Exemplo completo

Um programa principal que lê operações de crédito e chama dois sub-programas: um para validar o CPF e outro para calcular o valor das parcelas:

COBOL Programa principal — CALCPARC
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CALCPARC.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT ARQ-ENTRADA ASSIGN TO DDENTRAD
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-FS-ENT.
           SELECT ARQ-SAIDA   ASSIGN TO DDSAIDA
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-FS-SAI.

       DATA DIVISION.
       FILE SECTION.
       FD ARQ-ENTRADA.
       01 REG-ENTRADA.
           05 EN-CPF      PIC X(11).
           05 EN-VALOR    PIC 9(11)V99.
           05 EN-PRAZO    PIC 9(03).
           05 EN-TAXA     PIC V9(06).

       FD ARQ-SAIDA.
       01 REG-SAIDA.
           05 SA-CPF      PIC X(11).
           05 SA-PARCELA  PIC 9(09)V99.
           05 SA-STATUS   PIC X(02).

       WORKING-STORAGE SECTION.
       01  WS-FS-ENT      PIC X(02).
       01  WS-FS-SAI      PIC X(02).
       01  WS-FIM-ENT     PIC X(01).
           88  FIM-ENTRADA  VALUE 'S'.

       01  WS-RET-CPF     PIC X(02).
           88  CPF-OK       VALUE '00'.

       01  WS-PARCELA     PIC 9(09)V99.
       01  WS-RET-CALC   PIC X(02).

       PROCEDURE DIVISION.
       0000-INICIO.
           PERFORM 1000-INICIALIZA
           PERFORM 2000-PROCESSA
           PERFORM 3000-FINALIZA
           GOBACK.

       1000-INICIALIZA.
           MOVE 'N' TO WS-FIM-ENT
           OPEN INPUT  ARQ-ENTRADA
                OUTPUT ARQ-SAIDA.

       2000-PROCESSA.
           READ ARQ-ENTRADA
               AT END SET FIM-ENTRADA TO TRUE
           END-READ
           PERFORM UNTIL FIM-ENTRADA
               PERFORM 2100-TRATA-REGISTRO
               READ ARQ-ENTRADA
                   AT END SET FIM-ENTRADA TO TRUE
               END-READ
           END-PERFORM.

       2100-TRATA-REGISTRO.
                 * Chama sub-programa de validação de CPF
           CALL 'VALCPF' USING BY REFERENCE EN-CPF
                               BY REFERENCE WS-RET-CPF
           END-CALL

           IF NOT CPF-OK
               MOVE EN-CPF      TO SA-CPF
               MOVE 0            TO SA-PARCELA
               MOVE WS-RET-CPF  TO SA-STATUS
               WRITE REG-SAIDA
               EXIT PARAGRAPH
           END-IF

                 * Chama sub-programa de cálculo de parcela
           CALL 'CALCPMT' USING BY CONTENT  EN-VALOR
                               BY CONTENT  EN-TAXA
                               BY CONTENT  EN-PRAZO
                               BY REFERENCE WS-PARCELA
                               BY REFERENCE WS-RET-CALC
           END-CALL

           MOVE EN-CPF      TO SA-CPF
           MOVE WS-PARCELA  TO SA-PARCELA
           MOVE WS-RET-CALC TO SA-STATUS
           WRITE REG-SAIDA.

       3000-FINALIZA.
           CLOSE ARQ-ENTRADA
                 ARQ-SAIDA.
COBOL Sub-programa VALCPF — valida CPF
       IDENTIFICATION DIVISION.
       PROGRAM-ID. VALCPF.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CPF-NUM        PIC 9(11).
       01  WS-SOMA           PIC 9(06).
       01  WS-RESTO          PIC 9(02).
       01  WS-DIG-CALC       PIC 9(01).

       LINKAGE SECTION.
       01  LK-CPF            PIC X(11).
       01  LK-RETORNO        PIC X(02).
           88  RET-CPF-OK      VALUE '00'.
           88  RET-CPF-ESPACOS VALUE '02'.
           88  RET-CPF-INVALIDO VALUE '01'.

       PROCEDURE DIVISION USING LK-CPF
                                  LK-RETORNO.
       0000-INICIO.
           IF LK-CPF = SPACES OR ZEROS
               SET RET-CPF-ESPACOS TO TRUE
               GOBACK
           END-IF
           PERFORM 1000-VALIDA-DIGITOS
           GOBACK.

       1000-VALIDA-DIGITOS.
           MOVE LK-CPF TO WS-CPF-NUM
                 * ... lógica de validação dos dígitos verificadores ...
           SET RET-CPF-OK TO TRUE.  *simplificado
COBOL Sub-programa CALCPMT — calcula parcela (Price)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CALCPMT.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-FATOR          9(03)V9(10).
       01  WS-AUX            9(13)V9(10).

       LINKAGE SECTION.
       01  LK-VALOR           PIC 9(11)V99.
       01  LK-TAXA            PIC V9(06).
       01  LK-PRAZO           PIC 9(03).
       01  LK-PARCELA         PIC 9(09)V99.
       01  LK-RETORNO         PIC X(02).
           88  CALC-OK          VALUE '00'.
           88  CALC-ERRO        VALUE '01'.

       PROCEDURE DIVISION USING LK-VALOR
                                  LK-TAXA
                                  LK-PRAZO
                                  LK-PARCELA
                                  LK-RETORNO.
       0000-INICIO.
           IF LK-TAXA = 0 OR LK-PRAZO = 0
               SET CALC-ERRO TO TRUE
               MOVE 0 TO LK-PARCELA
               GOBACK
           END-IF
           PERFORM 1000-CALCULA-PMT
           SET CALC-OK TO TRUE
           GOBACK.

       1000-CALCULA-PMT.
                 * PMT = PV * i / (1 - (1+i)^-n)
                 * Implementação simplificada — em produção usa tabela ou COMPUTE
           COMPUTE LK-PARCELA = LK-VALOR * LK-TAXA.

10. Erros comuns

1. STOP RUN no sub-programa

COBOL Nunca STOP RUN em sub-programa
             * ERRADO: encerra o job inteiro, chamador não continua
       PROCEDURE DIVISION USING LK-CPF LK-RET.
       0000-INICIO.
           PERFORM 1000-PROCESSA
           STOP RUN.   *ERRADO em sub-programa!

             * CORRETO
       0000-INICIO.
           PERFORM 1000-PROCESSA
           GOBACK.

2. Layout incompatível entre chamador e LINKAGE

COBOL Layouts precisam coincidir
             * CHAMADOR passa X(11)
       01  WS-CPF   PIC X(11).
           CALL 'VALCPF' USING WS-CPF

             * SUB-PROGRAMA espera X(14) — vai ler 3 bytes além do campo!
       LINKAGE SECTION.
       01  LK-CPF   PIC X(14).  *ERRADO: tamanho diferente

             * SOLUÇÃO: usar COPY (copybook) nos dois lados
             * ou garantir manualmente que os PIC coincidem

3. Não tratar ON EXCEPTION no CALL dinâmico

COBOL ON EXCEPTION no CALL dinâmico
             * Se o módulo não estiver no STEPLIB/JOBLIB → abend U4096 ou similar
             * Sem ON EXCEPTION, o job aborta sem mensagem clara
           CALL WS-NOME-PROG USING WS-DADOS   *ARRISCADO

             * CORRETO: sempre capture a exceção em CALL dinâmico
           CALL WS-NOME-PROG USING WS-DADOS
               ON EXCEPTION
                   STRING 'MODULO NAO ENCONTRADO: '
                          WS-NOME-PROG
                       DELIMITED SIZE INTO WS-MSG
                   PERFORM 9000-TRATA-ERRO
           END-CALL

4. Estado residual inesperado entre CALLs

COBOL Working Storage do sub-programa persiste entre CALLs
             * Sub-programa acumula contador interno:
             * 1ª chamada: WS-CONTADOR interno = 1
             * 2ª chamada: WS-CONTADOR interno = 2 (não voltou a zero!)
           CALL 'MODULO' USING WS-DADOS
           CALL 'MODULO' USING WS-DADOS   *estado residual do 1º CALL

             * Se o sub-programa precisa começar do zero a cada CALL:
           CANCEL 'MODULO'
           CALL   'MODULO' USING WS-DADOS

🟣 Para quem já programa — copybooks de interface

Em projetos reais, os parâmetros de CALL são padronizados em copybooks de interface: um arquivo CPFCALL.cpy com a definição de LK-AREA-CPF que é copiado tanto no chamador (em WORKING-STORAGE) quanto no sub-programa (em LINKAGE). Isso garante sincronia de layout e evita o problema de tamanhos incompatíveis. Quando um campo muda, basta atualizar a copybook e recompilar os dois módulos. É a base de qualquer arquitetura modular saudável em COBOL de produção.