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
CALLe 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 comGOBACK
🦕 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:
* 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ático | CALL Dinâmico | |
|---|---|---|
| Nome | Literal entre aspas 'PROG' | Variável sem aspas |
| Resolução | Link-edit (antes de rodar) | Runtime (durante execução) |
| Load module | Sub-programa incluso no mesmo módulo | Sub-programa carregado separado |
| Flexibilidade | Baixa — nome fixo | Alta — pode variar em runtime |
| Performance | Ligeiramente mais rápido | Overhead mínimo no primeiro CALL |
| Manutenção | Requer re-link ao trocar sub-programa | Basta 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:
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-CPFestá modificando diretamente o campo do chamador (exceto se usarBY 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:
* 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:
* 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
| Modo | O que passa | Sub-prog altera chamador? | Uso típico |
|---|---|---|---|
BY REFERENCE | Endereço de memória | Sim | Parâmetros de entrada/saída |
BY CONTENT | Cópia do valor | Não | Parâmetros somente de entrada |
BY VALUE | O valor em si (não endereço) | Não | Interoperabilidade C/Java/LE APIs |
6. GOBACK vs STOP RUN
Esta é uma das confusões mais comuns — e mais perigosas — em programas COBOL:
* 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:
* 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:
* 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:
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.
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
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
* 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
* 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
* 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
* 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.