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:

RegistradorPapel na chamada
R1Ponteiro para a lista de parâmetros (caller carrega antes de chamar)
R13Ponteiro para a savearea do caller (caller carrega antes de chamar)
R14Endereço de retorno (caller carrega antes de chamar com BALR/BASSM)
R15Endereço do módulo chamado (caller carrega) / Return Code (callee seta)

O callee (programa chamado) deve:

  1. Salvar R14-R12 na savearea apontada por R13: STM R14,R12,12(R13)
  2. Encadear a savearea: salvar R13 antigo, atualizar R13 para a própria savearea
  3. Executar o trabalho
  4. Colocar o Return Code em R15
  5. Restaurar R13 e depois R14-R12: LM R14,R12,12(R13)
  6. 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