Programas reais não são monolitos — eles chamam e são chamados por outros módulos. No z/OS, existe uma convenção de linkage rigorosa que define como registradores são passados, como parâmetros são transmitidos e como o retorno acontece. Essa convenção é o contrato entre programas Assembler, COBOL, PL/I e os serviços do sistema operacional.
1. A convenção de linkage OS/390
A convenção padrão do z/OS (herdada do OS/390 e MVS) define o protocolo de chamada entre módulos:
| Registrador | Papel na chamada |
R1 | Ponteiro para a lista de parâmetros (caller carrega antes de chamar) |
R13 | Ponteiro para a savearea do caller (caller carrega antes de chamar) |
R14 | Endereço de retorno (caller carrega antes de chamar com BALR/BASSM) |
R15 | Endereço do módulo chamado (caller carrega) / Return Code (callee seta) |
O callee (programa chamado) deve:
- Salvar R14-R12 na savearea apontada por R13:
STM R14,R12,12(R13)
- Encadear a savearea: salvar R13 antigo, atualizar R13 para a própria savearea
- Executar o trabalho
- Colocar o Return Code em R15
- Restaurar R13 e depois R14-R12:
LM R14,R12,12(R13)
- Retornar via
BR R14
2. BALR e BR — chamada e retorno
A instrução BALR Rx,Ry carrega em Rx o endereço da instrução seguinte, depois desvia para o endereço em Ry. Se Ry=0, não desvia — por isso usamos BALR R12,0 para capturar o PC sem desviar.
Chamada manual (sem a macro CALL):
LA R1,PARMLIST R1 ← endereço da lista de parâmetros
L R15,=V(SUBROT) R15 ← endereço do módulo externo
BALR R14,R15 R14 ← endereço de retorno; desvia para R15
(execução continua aqui após o retorno)
LTR R15,R15 Testa Return Code
BNZ ERRO Se RC != 0, tratar erro
O =V(SUBROT) é um literal de endereço externo:
V = external address — o link-editor resolve o endereço do módulo SUBROT
O BR R14 (Branch Register) desvia para o endereço em R14, que é o endereço de retorno salvo pelo caller. É o equivalente ao return de linguagens de alto nível.
3. Passagem de parâmetros
Parâmetros são passados via lista de ponteiros apontada por R1. O último ponteiro tem o bit mais significativo setado (X'80000000') para indicar o fim da lista:
Caller — montar lista de parâmetros:
LA R1,PARMS R1 ← endereço da lista
L R15,=V(SUBROT) R15 ← endereço do módulo
BALR R14,R15
PARMS DC A(PARAM1) Ponteiro para parâmetro 1
DC A(PARAM2) Ponteiro para parâmetro 2
DC A(PARAM3+X'80000000') Último parâmetro (bit hi setado)
PARAM1 DC F'100'
PARAM2 DC CL8'ENTRADA'
PARAM3 DS F Parâmetro de saída
Callee — receber parâmetros:
L R2,0(R1) R2 ← endereço do PARAM1
L R3,4(R1) R3 ← endereço do PARAM2
L R4,8(R1) R4 ← endereço do PARAM3 (mascarar bit alto)
N R4,=X'7FFFFFFF' Remove o indicador de último parâmetro
L R5,0(R2) R5 ← valor de PARAM1 (100)
MVC WORK(8),0(R3) Copia PARAM2 para área local
4. A macro CALL
A macro CALL encapsula toda a mecânica de chamada — muito mais simples que o código manual:
Chamada com CALL:
CALL SUBROT,(PARAM1,PARAM2,PARAM3),VL
(VL = Variable Length — seta o bit alto no último parâmetro)
Equivalente gerado pelo assembler:
STM R14,R1,CALLSAVE Salva registradores de trabalho
LA R1,CALLPARM R1 ← lista de parâmetros gerada
L R15,=V(SUBROT) R15 ← endereço do módulo
BALR R14,R15
LM R14,R1,CALLSAVE
5. Subrotinas internas
Para lógica que não precisa ser em módulo separado, você pode usar subrotinas internas com BAL/BAS e BR:
Subrotina interna — chamada com BAL:
BAL R10,FORMATR R10 ← endereço de retorno; desvia para FORMATR
(continua aqui após a subrotina retornar)
FORMATR DS 0H Início da subrotina interna
MVI OUTREC,C' ' Faz algum trabalho...
MVC OUTREC+1(10),TITULO
BR R10 Retorna ao chamador (R10)
Subrotinas internas NÃO seguem a linkage convention completa
porque estão no mesmo CSECT — sem savearea separada
⚠️ BAL vs BALR — atenção ao endereçamento
BAL R10,label gera um endereço de 24-bit. Em programas compilados com AMODE 31 (endereços de 31 bits), use BAS R10,label para garantir que o endereço de retorno seja preservado corretamente. BAS preserva todos os 31 bits do endereço.
6. Chamada entre COBOL e Assembler
COBOL usa a mesma convenção de linkage — por isso é possível chamar módulos Assembler a partir de COBOL e vice-versa sem adaptadores:
COBOL chamando módulo Assembler:
CALL 'ASMMOD01' USING PARAM1 PARAM2 PARAM3.
IF RETURN-CODE NOT = 0
DISPLAY 'ERRO NO MODULO ASSEMBLER: ' RETURN-CODE
END-IF.
O módulo Assembler recebe os parâmetros via R1 (lista de ponteiros)
e retorna o RC em R15 — que o COBOL coloca em RETURN-CODE.
No módulo Assembler (lado receptor):
ASMMOD01 CSECT
STM R14,R12,12(R13) Prólogo padrão
BALR R12,0
USING *,R12
ST R13,SAVAREA+4
LA R13,SAVAREA
* Acessar parâmetros passados pelo COBOL
L R2,0(R1) R2 ← endereço de PARAM1
L R3,4(R1) R3 ← endereço de PARAM2
L R4,8(R1) R4 ← endereço de PARAM3
N R4,=X'7FFFFFFF' Remove bit alto do último parâmetro
* Fazer o trabalho com os dados
L R5,0(R2) Carregar valor de PARAM1
... lógica ...
ST R6,0(R4) Gravar resultado em PARAM3
L R13,SAVAREA+4 Epílogo padrão
LM R14,R12,12(R13)
SR R15,R15 RC = 0
BR R14
SAVAREA DS 18F
END ASMMOD01