Estrutura de um mapset BMS

Um mapset é um arquivo de macros assembler que define um conjunto de mapas (telas). A relação é:

ConceitoAnalogiaMacro BMS
MapsetLivro de formuláriosDFHMSD
MapaUma página do livroDFHMDI
CampoUm campo do formulárioDFHMDF

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.

🦕 Analogia: Pense num sistema bancário com telas de consulta de saldo, extrato e transferência. Todas as três podem estar no mesmo mapset 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âmetroValores comunsSignificado
TYPEMAP / DSECT / FINALMAP=gera físico+simbólico, DSECT=só simbólico, FINAL=fecha
TIOAPFXYES / NOYES é obrigatório para programas COBOL modernos — reserva 12 bytes de prefixo TIOA
TERM3270 / 3270-2 / ALLModelo de terminal alvo. 3270-2 = 24 linhas × 80 colunas
LANGCOBOL / ASM / PLILinguagem do copybook simbólico gerado
MODEIN / OUT / INOUTINOUT é o mais comum — mapa serve para enviar e receber
CTRLFREEKB / ALARM / PRINTFREEKB libera o teclado após SEND (senão teclado fica travado)
STORAGEAUTOGera DSECT com REDEFINES; necessário para COBOL
⚠️ TIOAPFX=YES é obrigatório. Sem ele, o CICS não consegue sobrepor corretamente a área de I/O e o programa recebe lixo nos campos. É um erro clássico de quem cria um mapset pela primeira vez.

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âmetroSignificado
SIZE=(linhas,colunas)Tamanho do mapa. Para 3270-2 use SIZE=(24,80)
LINELinha inicial do mapa na tela (1 a 24)
COLUMNColuna inicial do mapa na tela (1 a 80)
CTRLSobrescreve o CTRL do DFHMSD para este mapa específico
HEADER=YESEste mapa é um cabeçalho — usado em layouts com múltiplos mapas na mesma tela
TRAILER=YESEste 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âmetroSignificado
POS=(linha,coluna)Posição do byte de atributo na tela. O campo começa 1 coluna depois
LENGTHComprimento do campo em caracteres (não inclui o byte de atributo)
ATTRBAtributos do campo — veja seção seguinte
INITIALValor inicial exibido na tela (textos fixos, labels)
PICINPicture COBOL para dados recebidos do terminal (RECEIVE MAP)
PICOUTPicture COBOL para dados enviados ao terminal (SEND MAP)
VALIDNMUSTFILL / MUSTENTER / TRIGGER — validação automática pelo BMS
POS define o byte de atributo, não o início do campo. Se você coloca 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)

TokenComportamentoUso típico
PROTCampo protegido — o cursor pula sobre ele, usuário não editaLabels, títulos, campos de saída
UNPROTCampo desprotegido — cursor para aqui, usuário pode digitarCampos de entrada de dados
ASKIPAuto-skip — campo protegido, cursor avança automaticamente para o próximo UNPROT após preenchimentoSeparadores, campos de saída após campos de entrada

Intensidade (obrigatório escolher um)

TokenAparência na telaUso típico
NORMBrilho normalCampos comuns
BRTBrilho alto (destaque)Títulos, campos importantes, mensagens de erro
DRKCampo invisível (dark) — não aparece na telaSenhas, campos que o usuário não deve ver

Outros atributos (opcionais)

TokenEfeito
NUMCampo numérico — terminal só aceita dígitos e sinal; ativa tecla de ponto decimal
ICInitial Cursor — posiciona o cursor neste campo ao enviar a tela
FSETForce 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.

🦕 Analogia: Imagine um formulário em papel com 20 campos. Só os campos que você preencheu são fotocopiados e enviados. Os campos em branco ficam no formulário original. O MDT é o mecanismo que marca "este campo foi tocado".

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 DFHBMSCAAtributo equivalente
DFHBMUNPUNPROT + NORM
DFHBMUNNUNPROT + NORM + NUM
DFHBMPRFPROT + NORM
DFHBMASFASKIP + NORM + FSET (MDT aceso)
DFHBMFSEUNPROT + NORM + FSET
DFHBMBRYUNPROT + BRT
DFHBMDARASKIP + DRK
DFHBMEOFApaga 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
Cursor simbólico é o padrão recomendado em desenvolvimento moderno. Permite posicionar o cursor em qualquer campo sem calcular posição absoluta — basta mover -1 para o campo L correspondente ao campo onde quer o cursor.

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âmetroValoresEfeito
COLORDEFAULT / BLUE / RED / PINK / GREEN / TURQUOISE / YELLOW / NEUTRALCor do campo
HILIGHTOFF / BLINK / REVERSE / UNDERLINERealce visual
PSBASE / Graphic Character SetConjunto 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
💗 Nem todo terminal suporta cor. Em ambiente de produção real, muitos bancos ainda usam emuladores de terminal configurados como 3279 (monocromático) ou 3279-S2 (colorido). Sempre defina um visual funcional sem cor — a cor é um complemento, não requisito.

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:

SufixoTipoSignificado
L (length)PIC S9(4) COMPComprimento 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éricoValor recebido do terminal no RECEIVE MAP.
O (output)PIC X(n) ou numéricoValor 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
Dois passes de assembly são necessários: o primeiro gera o mapa físico (load module); o segundo gera o copybook simbólico (DSECT). Muitas instalações automatizam isso com um único JCL que faz os dois em sequência.

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.