Estrutura de um mapset BMS
Um mapset é um arquivo de macros assembler que define um conjunto de mapas (telas). A relação é:
| Conceito | Analogia | Macro BMS |
|---|---|---|
| Mapset | Livro de formulários | DFHMSD |
| Mapa | Uma página do livro | DFHMDI |
| Campo | Um campo do formulário | DFHMDF |
Um mapset pode conter vários mapas. Cada mapa representa uma tela diferente. Ao fazer SEND MAP, você especifica qual mapa (e de qual mapset) deseja enviar. O mapset tem um nome de até 8 caracteres e é o nome do módulo de carga (load module) gerado pelo assembly.
BANCMAP1 — três mapas (MAPSALDO, MAPEXTRA, MAPTRANS) dentro de um único load module. Você carrega o mapset uma vez e usa o mapa que precisar em cada momento.
DFHMSD — definindo o mapset
DFHMSD é a macro que abre e fecha o mapset. Ela aparece duas vezes: no início (com os parâmetros) e no final (com TYPE=FINAL).
BANCMAP1 DFHMSD TYPE=MAP, * gera o mapa físico
TIOAPFX=YES, * reserva prefixo TIOA (obrigatório CICS/VS)
TERM=3270-2, * terminal alvo: 3270 modelo 2 (24x80)
LANG=COBOL, * copybook gerado em COBOL
MODE=INOUT, * mapa usado para entrada e saída
CTRL=FREEKB, * libera teclado ao enviar
STORAGE=AUTO * DSECT do copybook em modo automático
...mapas aqui...
DFHMSD TYPE=FINAL * fecha o mapset
END
| Parâmetro | Valores comuns | Significado |
|---|---|---|
| TYPE | MAP / DSECT / FINAL | MAP=gera físico+simbólico, DSECT=só simbólico, FINAL=fecha |
| TIOAPFX | YES / NO | YES é obrigatório para programas COBOL modernos — reserva 12 bytes de prefixo TIOA |
| TERM | 3270 / 3270-2 / ALL | Modelo de terminal alvo. 3270-2 = 24 linhas × 80 colunas |
| LANG | COBOL / ASM / PLI | Linguagem do copybook simbólico gerado |
| MODE | IN / OUT / INOUT | INOUT é o mais comum — mapa serve para enviar e receber |
| CTRL | FREEKB / ALARM / PRINT | FREEKB libera o teclado após SEND (senão teclado fica travado) |
| STORAGE | AUTO | Gera DSECT com REDEFINES; necessário para COBOL |
DFHMDI — definindo o mapa
DFHMDI define cada mapa (tela) dentro do mapset. O nome que você dá ao rótulo da macro é o nome do mapa usado no SEND MAP.
MAPSALDO DFHMDI SIZE=(24,80), * 24 linhas, 80 colunas
LINE=1, * começa na linha 1
COLUMN=1 * começa na coluna 1
| Parâmetro | Significado |
|---|---|
| SIZE=(linhas,colunas) | Tamanho do mapa. Para 3270-2 use SIZE=(24,80) |
| LINE | Linha inicial do mapa na tela (1 a 24) |
| COLUMN | Coluna inicial do mapa na tela (1 a 80) |
| CTRL | Sobrescreve o CTRL do DFHMSD para este mapa específico |
| HEADER=YES | Este mapa é um cabeçalho — usado em layouts com múltiplos mapas na mesma tela |
| TRAILER=YES | Este mapa é um rodapé |
DFHMDF — definindo campos
DFHMDF define cada campo da tela. É a macro mais usada e com mais opções. Cada campo tem uma posição física na tela, um comprimento e um conjunto de atributos.
* campo de label (texto fixo, protegido)
DFHMDF POS=(3,2), * linha 3, coluna 2
LENGTH=6, * 6 caracteres
ATTRB=(PROT,NORM), * protegido, brilho normal
INITIAL='CONTA:' * texto fixo
* campo de entrada (editável pelo usuário)
NRCONTA DFHMDF POS=(3,9),
LENGTH=10,
ATTRB=(UNPROT,NORM), * editável
PICIN='9(10)', * picture de entrada
PICOUT='9(10)' * picture de saída
* campo de saída (resultado, protegido)
NMSALDO DFHMDF POS=(5,9),
LENGTH=15,
ATTRB=(PROT,NORM),
PICOUT='X(15)'
| Parâmetro | Significado |
|---|---|
| POS=(linha,coluna) | Posição do byte de atributo na tela. O campo começa 1 coluna depois |
| LENGTH | Comprimento do campo em caracteres (não inclui o byte de atributo) |
| ATTRB | Atributos do campo — veja seção seguinte |
| INITIAL | Valor inicial exibido na tela (textos fixos, labels) |
| PICIN | Picture COBOL para dados recebidos do terminal (RECEIVE MAP) |
| PICOUT | Picture COBOL para dados enviados ao terminal (SEND MAP) |
| VALIDN | MUSTFILL / MUSTENTER / TRIGGER — validação automática pelo BMS |
POS=(3,9), o byte de atributo fica em (3,9) e o primeiro caractere do campo fica em (3,10). Esse byte de 1 caractere é "consumido" pelo terminal e nunca aparece na tela.
Atributos de campo — ATTRB em detalhe
O parâmetro ATTRB controla como o campo aparece e se comporta. É uma lista de tokens separados por vírgula entre parênteses.
Proteção (obrigatório escolher um)
| Token | Comportamento | Uso típico |
|---|---|---|
PROT | Campo protegido — o cursor pula sobre ele, usuário não edita | Labels, títulos, campos de saída |
UNPROT | Campo desprotegido — cursor para aqui, usuário pode digitar | Campos de entrada de dados |
ASKIP | Auto-skip — campo protegido, cursor avança automaticamente para o próximo UNPROT após preenchimento | Separadores, campos de saída após campos de entrada |
Intensidade (obrigatório escolher um)
| Token | Aparência na tela | Uso típico |
|---|---|---|
NORM | Brilho normal | Campos comuns |
BRT | Brilho alto (destaque) | Títulos, campos importantes, mensagens de erro |
DRK | Campo invisível (dark) — não aparece na tela | Senhas, campos que o usuário não deve ver |
Outros atributos (opcionais)
| Token | Efeito |
|---|---|
NUM | Campo numérico — terminal só aceita dígitos e sinal; ativa tecla de ponto decimal |
IC | Initial Cursor — posiciona o cursor neste campo ao enviar a tela |
FSET | Force SET — ativa o MDT mesmo que o usuário não tenha digitado nada; o campo sempre é enviado no RECEIVE |
* campo de senha: editável, invisível
SENHA DFHMDF POS=(7,9),
LENGTH=8,
ATTRB=(UNPROT,DRK)
* campo de valor: editável, só números, cursor inicial
VLRTRANS DFHMDF POS=(9,9),
LENGTH=13,
ATTRB=(UNPROT,NORM,NUM,IC),
PICIN='9(11)V99',
PICOUT='ZZZ.ZZZ.ZZZ,99'
* título em destaque
DFHMDF POS=(1,25),
LENGTH=30,
ATTRB=(PROT,BRT),
INITIAL=' SISTEMA BANCARIO EXEMPLO '
MDT — Modified Data Tag
O MDT (Modified Data Tag) é um bit dentro do byte de atributo de cada campo. Quando o usuário digita algo em um campo, o terminal automaticamente acende o MDT desse campo. Quando o usuário pressiona Enter, o terminal envia ao CICS apenas os campos com MDT aceso — os campos não modificados ficam de fora para economizar banda.
Implicações no programa COBOL
Quando o RECEIVE MAP é executado, os campos cujo MDT está apagado chegam com espaços (ou zeros para numérico) — não com o valor que está visível na tela. Isso causa um bug clássico:
* situação: tela exibe NR-CONTA '12345', usuário não toca o campo
* e pressiona Enter direto para confirmar
EXEC CICS RECEIVE MAP('MAPSALDO')
MAPSET('BANCMAP1')
INTO(MAPA-I)
END-EXEC
* resultado: NRCONTAI em MAPA-I chega com SPACES
* porque o usuário não modificou o campo (MDT estava apagado)
* solução 1: usar FSET no ATTRB do campo no BMS
* solução 2: gravar o valor na COMMAREA e recuperar de lá
* solução 3: usar ATTRB=(UNPROT,NORM,FSET) via DFHBMSCA no SEND
Controlando o MDT via DFHBMSCA
O copybook DFHBMSCA define constantes para manipular atributos em runtime. Você atribui ao campo de atributo do mapa (sufixo A no copybook simbólico):
COPY DFHBMSCA.
* forçar MDT aceso no campo NRCONTA antes do SEND
MOVE DFHBMFSE TO NRCONTAA * FSET: UNPROT+NORM+MDT aceso
* tornar campo protegido em runtime (após confirmar dados)
MOVE DFHBMPRF TO NRCONTAA * PROT+NORM
* apagar campo (tornar dark)
MOVE DFHBMDAR TO NRCONTAA * ASKIP+DRK
| Constante DFHBMSCA | Atributo equivalente |
|---|---|
| DFHBMUNP | UNPROT + NORM |
| DFHBMUNN | UNPROT + NORM + NUM |
| DFHBMPRF | PROT + NORM |
| DFHBMASF | ASKIP + NORM + FSET (MDT aceso) |
| DFHBMFSE | UNPROT + NORM + FSET |
| DFHBMBRY | UNPROT + BRT |
| DFHBMDAR | ASKIP + DRK |
| DFHBMEOF | Apaga conteúdo do campo na tela (erase-EOF) |
Posicionamento de cursor
Há três formas de posicionar o cursor ao enviar uma tela:
1. IC no DFHMDF (estático)
Define onde o cursor sempre vai parar ao enviar o mapa. Só pode haver um IC por mapa.
NRCONTA DFHMDF POS=(3,9),
LENGTH=10,
ATTRB=(UNPROT,NORM,IC), * cursor sempre cai aqui
PICIN='9(10)'
2. CURSOR no SEND MAP (posição fixa em runtime)
Passa a posição absoluta na tela como número. Linha e coluna são combinados: posição = (linha-1) × 80 + (coluna-1).
* posicionar cursor na linha 5, coluna 9
* posição = (5-1) * 80 + (9-1) = 328
EXEC CICS SEND MAP('MAPSALDO')
MAPSET('BANCMAP1')
CURSOR(328)
END-EXEC
3. Cursor simbólico (mais flexível)
Move -1 para o campo de comprimento do campo no copybook simbólico (sufixo L). O CICS interpreta isso como "posicione o cursor aqui". Depois usa CURSOR sem valor no SEND MAP.
* posicionar cursor no campo NRCONTA dinamicamente
MOVE -1 TO NRCONT AL * campo de length = -1 sinaliza cursor simbólico
EXEC CICS SEND MAP('MAPSALDO')
MAPSET('BANCMAP1')
CURSOR * sem valor = usa cursor simbólico
END-EXEC
Cor e realce em terminais 3270
Terminais 3270 coloridos suportam atributos estendidos que vão além do byte de atributo básico. Para usar cor e realce, o campo precisa do parâmetro EXTATT=YES no DFHMDF e o mapa precisa de EXTATT=YES ou COLOR no DFHMSD.
MENSAGEM DFHMDF POS=(23,2),
LENGTH=76,
ATTRB=(PROT,NORM),
COLOR=RED, * texto vermelho
HILIGHT=REVERSE, * vídeo reverso
INITIAL=' '
| Parâmetro | Valores | Efeito |
|---|---|---|
| COLOR | DEFAULT / BLUE / RED / PINK / GREEN / TURQUOISE / YELLOW / NEUTRAL | Cor do campo |
| HILIGHT | OFF / BLINK / REVERSE / UNDERLINE | Realce visual |
| PS | BASE / Graphic Character Set | Conjunto de caracteres programável |
Em runtime, você muda cor via EXEC CICS SEND TEXT ou via os campos de atributo estendido no copybook simbólico (sufixo C para cor, sufixo H para hilight):
COPY DFHBMSCA.
* destacar campo de mensagem em vermelho ao exibir erro
MOVE DFHRED TO MENSAGEMC * campo de cor
MOVE DFHBLINK TO MENSAGEMH * campo de hilight (pisca)
MOVE 'CONTA NAO ENCONTRADA' TO MENSAGEMO
O copybook simbólico — como ler o mapa no COBOL
Quando o mapset é montado, o assembler gera dois objetos: o mapa físico (load module usado pelo CICS para enviar a tela) e o copybook simbólico (código COBOL que representa os campos do mapa para uso no programa). O copybook é colocado em uma biblioteca e incluído com COPY.
Para cada campo com nome no BMS, o copybook gera quatro variáveis:
| Sufixo | Tipo | Significado |
|---|---|---|
L (length) | PIC S9(4) COMP | Comprimento do dado. Move -1 para cursor simbólico. No RECEIVE, contém o número de chars digitados. |
A (attribute) | PIC X(1) | Byte de atributo do campo. Use DFHBMSCA para atribuir valores. |
I (input) | PIC X(n) ou numérico | Valor recebido do terminal no RECEIVE MAP. |
O (output) | PIC X(n) ou numérico | Valor a ser enviado ao terminal no SEND MAP. |
Exemplo de copybook gerado para o campo NRCONTA:
01 MAPSALDOI.
02 FILLER PIC X(12). * prefixo TIOA
02 NRCONT AL PIC S9(4) COMP. * length
02 NRCONT AA PIC X. * attribute
02 NRCONTAI PIC 9(10). * input
...
01 MAPSALDOO REDEFINES MAPSALDOI.
02 FILLER PIC X(12).
02 FILLER PIC X(3). * len + attr
02 NRCONTAO PIC 9(10). * output
...
No programa COBOL, o uso típico é:
WORKING-STORAGE SECTION.
COPY BANCMAP1. * inclui MAPSALDOI e MAPSALDOO
COPY DFHBMSCA. * constantes de atributo
COPY DFHAID. * constantes de teclas AID
PROCEDURE DIVISION.
ENVIAR-TELA.
MOVE WS-NUMERO-CONTA TO NRCONTAO
MOVE WS-SALDO TO SALDOO
EXEC CICS SEND MAP('MAPSALDO')
MAPSET('BANCMAP1')
FROM(MAPSALDOO)
ERASE
END-EXEC
EXEC CICS RETURN
TRANSID(EIBTRNID)
COMMAREA(WS-COMMAREA)
END-EXEC.
RECEBER-ENTRADA.
EXEC CICS RECEIVE MAP('MAPSALDO')
MAPSET('BANCMAP1')
INTO(MAPSALDOI)
END-EXEC
MOVE NRCONTAI TO WS-NUMERO-CONTA.
Montando o mapset — JCL de assembly
O fonte BMS é código assembler e precisa ser montado (assembled) antes de ser usado. O JCL de assembly chama o assembler e o linkeditor, gerando o load module e o copybook.
//* JCL para montar um mapset BMS
//ASMBMS JOB (CONTA),'ASSEMBLY BMS',CLASS=A,MSGCLASS=X
//*
//ASM EXEC PGM=ASMA90,
// PARM='DECK,NOLOAD,XREF(SHORT)'
//SYSLIB DD DSN=CICSTS.SDFHMAC,DISP=SHR ← macros CICS/BMS
//SYSIN DD DSN=BANCO.BMS.SOURCE(BANCMAP1),DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSPUNCH DD DSN=&&OBJDECK,UNIT=SYSDA,
// SPACE=(CYL,(1,1)),DISP=(NEW,PASS)
//*
//LKED EXEC PGM=IEWL,
// PARM='LIST,XREF,LET,NCAL'
//SYSLIN DD DSN=&&OBJDECK,DISP=(OLD,DELETE)
//SYSLMOD DD DSN=CICS.LOADLIB(BANCMAP1),DISP=SHR ← load module
//SYSPRINT DD SYSOUT=*
//*
//* Segundo passo: gerar apenas o copybook COBOL (DSECT)
//ASMDSC EXEC PGM=ASMA90,
// PARM='DECK,NOLOAD'
//SYSLIB DD DSN=CICSTS.SDFHMAC,DISP=SHR
//SYSIN DD *
BANCMAP1 DFHMSD TYPE=DSECT, * só gera o copybook
LANG=COBOL,
STORAGE=AUTO,
TIOAPFX=YES
/* (conteúdo do BMS aqui) */
DFHMSD TYPE=FINAL
END
//SYSPRINT DD SYSOUT=*
//SYSPUNCH DD DSN=BANCO.COPYLIB(BANCMAP1),DISP=SHR ← copybook
Exemplo completo — tela de consulta de conta
Um mapset real com tela de consulta, campos de entrada e saída, mensagem de erro e posicionamento dinâmico de cursor.
Fonte BMS (BANCMAP1)
BANCMAP1 DFHMSD TYPE=MAP,
TIOAPFX=YES,
TERM=3270-2,
LANG=COBOL,
MODE=INOUT,
CTRL=FREEKB,
STORAGE=AUTO
* ── MAPA DE CONSULTA DE SALDO ───────────────────────────────────────
MAPSALDO DFHMDI SIZE=(24,80),
LINE=1,
COLUMN=1
* linha 1 - título
DFHMDF POS=(1,1),LENGTH=79,
ATTRB=(PROT,BRT),
INITIAL=' BANCO EXEMPLO S.A. - CONSULTA DE SALDO'
* linha 3 - label e campo de número de conta
DFHMDF POS=(3,2),LENGTH=16,
ATTRB=(PROT,NORM),
INITIAL='NUMERO DA CONTA:'
NRCONTA DFHMDF POS=(3,19),LENGTH=10,
ATTRB=(UNPROT,NORM,IC),
PICIN='9(10)',
PICOUT='9(10)'
DFHMDF POS=(3,30),LENGTH=1,
ATTRB=(ASKIP,NORM) * stop field após entrada
* linha 5 - campo de nome do titular (somente saída)
DFHMDF POS=(5,2),LENGTH=8,
ATTRB=(PROT,NORM),
INITIAL='TITULAR:'
NMTITULA DFHMDF POS=(5,11),LENGTH=40,
ATTRB=(PROT,NORM),
PICOUT='X(40)'
* linha 7 - campo de saldo
DFHMDF POS=(7,2),LENGTH=6,
ATTRB=(PROT,NORM),
INITIAL='SALDO:'
VLSALDO DFHMDF POS=(7,9),LENGTH=15,
ATTRB=(PROT,BRT),
PICOUT='ZZZ.ZZZ.ZZZ,99'
* linha 9 - campo de saldo disponível
DFHMDF POS=(9,2),LENGTH=11,
ATTRB=(PROT,NORM),
INITIAL='DISPONIVEL:'
VLDISPON DFHMDF POS=(9,14),LENGTH=15,
ATTRB=(PROT,NORM),
PICOUT='ZZZ.ZZZ.ZZZ,99'
* linha 23 - mensagem de erro/informação
MENSAGEM DFHMDF POS=(23,2),LENGTH=76,
ATTRB=(PROT,NORM),
COLOR=RED,
PICOUT='X(76)'
* linha 24 - instrução de uso
DFHMDF POS=(24,2),LENGTH=50,
ATTRB=(PROT,NORM),
INITIAL='PF3=VOLTAR ENTER=CONSULTAR CLEAR=LIMPAR'
DFHMSD TYPE=FINAL
END
Trecho do programa COBOL
WORKING-STORAGE SECTION.
COPY BANCMAP1.
COPY DFHBMSCA.
COPY DFHAID.
01 WS-COMMAREA.
05 WS-PRIMEIRA-VEZ PIC X VALUE 'S'.
05 WS-NR-CONTA PIC 9(10).
PROCEDURE DIVISION.
INICIO.
IF EIBCALEN = 0
MOVE 'S' TO WS-PRIMEIRA-VEZ
ELSE
MOVE DFHCOMMAREA TO WS-COMMAREA
END-IF
EVALUATE TRUE
WHEN WS-PRIMEIRA-VEZ = 'S'
PERFORM ENVIAR-TELA-LIMPA
WHEN EIBAID = DFHPF3
EXEC CICS XCTL PROGRAM('PGMMENU') END-EXEC
WHEN EIBAID = DFHENTER
PERFORM PROCESSAR-CONSULTA
WHEN OTHER
PERFORM ENVIAR-TELA-LIMPA
END-EVALUATE
EXEC CICS RETURN
TRANSID(EIBTRNID)
COMMAREA(WS-COMMAREA)
LENGTH(LENGTH OF WS-COMMAREA)
END-EXEC.
ENVIAR-TELA-LIMPA.
MOVE LOW-VALUES TO MAPSALDOO
MOVE 'S' TO WS-PRIMEIRA-VEZ
EXEC CICS SEND MAP('MAPSALDO')
MAPSET('BANCMAP1')
FROM(MAPSALDOO)
ERASE
END-EXEC.
PROCESSAR-CONSULTA.
EXEC CICS RECEIVE MAP('MAPSALDO')
MAPSET('BANCMAP1')
INTO(MAPSALDOI)
RESP(WS-RESP)
END-EXEC
IF NRCONTAI = ZEROS
MOVE 'INFORME O NUMERO DA CONTA' TO MENSAGEMO
MOVE -1 TO NRCONTAL
EXEC CICS SEND MAP('MAPSALDO')
MAPSET('BANCMAP1')
FROM(MAPSALDOO)
CURSOR
END-EXEC
GO TO FIM-PROCESSAR
END-IF
MOVE NRCONTAI TO WS-NR-CONTA
PERFORM BUSCAR-SALDO-NO-VSAM
MOVE WS-NR-CONTA TO NRCONTAO
MOVE WS-NOME TO NMTITULAO
MOVE WS-SALDO TO VLSALDOO
MOVE WS-DISPONIVEL TO VLDISPONO
EXEC CICS SEND MAP('MAPSALDO')
MAPSET('BANCMAP1')
FROM(MAPSALDOO)
END-EXEC.
FIM-PROCESSAR.
EXIT.