28 min read

> "Batch COBOL runs in the background while everyone sleeps. CICS COBOL runs in the foreground while everyone watches. That changes everything about how you write code."

Chapter 29: CICS Fundamentals

"Batch COBOL runs in the background while everyone sleeps. CICS COBOL runs in the foreground while everyone watches. That changes everything about how you write code." — Priya Kapoor, systems architect, GlobalBank

Up to this point in the textbook, every COBOL program you have written has been a batch program — it reads input, processes it, and writes output, all without human interaction during execution. But the vast majority of mainframe COBOL programs that run during business hours are online programs — interactive applications that display screens to users, accept input, process requests, and return results in real time.

The technology that makes this possible is CICS: the Customer Information Control System. CICS is a transaction processing monitor that sits between the user's terminal (or modern equivalent) and the COBOL application. It manages thousands of simultaneous users, handles screen I/O, controls transactions, and ensures that every interaction either completes fully or rolls back cleanly.

In this chapter, you will learn to write CICS COBOL programs from the ground up: defining screen layouts with BMS maps, sending and receiving screen data, designing pseudo-conversational transactions, and handling errors. By the end, you will build a working account inquiry screen for GlobalBank.

29.1 What Is CICS and Why Does It Matter?

CICS (pronounced "kicks") was first released by IBM in 1969. Over five decades later, it processes an estimated 1.2 million transactions per second worldwide — more than any other transaction processing system. Airlines, banks, insurance companies, retailers, and government agencies rely on CICS for their mission-critical online operations.

CICS in the Application Stack

┌─────────────────────────────────────────────┐
│  Terminal / Browser / API Client             │
├─────────────────────────────────────────────┤
│  CICS Transaction Server                    │
│  ┌─────────┐ ┌──────────┐ ┌──────────────┐ │
│  │Terminal  │ │Task      │ │Program       │ │
│  │Control   │ │Control   │ │Control       │ │
│  ├─────────┤ ├──────────┤ ├──────────────┤ │
│  │Storage  │ │Interval  │ │Transient Data│ │
│  │Control   │ │Control   │ │Control       │ │
│  ├─────────┤ ├──────────┤ ├──────────────┤ │
│  │File     │ │Temporary │ │Journal       │ │
│  │Control   │ │Storage   │ │Control       │ │
│  └─────────┘ └──────────┘ └──────────────┘ │
├─────────────────────────────────────────────┤
│  DB2 / VSAM / IMS DB                        │
├─────────────────────────────────────────────┤
│  z/OS                                       │
└─────────────────────────────────────────────┘

Key CICS Concepts

Region: A CICS address space running on z/OS. A single z/OS system may run multiple CICS regions — for example, separate regions for production, testing, and development.

Transaction: A unit of work initiated by a user, identified by a 1-4 character transaction ID (e.g., AINQ for account inquiry). When a user types a transaction ID and presses Enter, CICS starts a task.

Task: A running instance of a transaction. Multiple users can start the same transaction simultaneously — each gets their own task with its own storage.

Program: The COBOL program (or assembler, PL/I, Java, etc.) that executes for a transaction. A transaction may invoke multiple programs.

Terminal: The user's screen — historically a 3270 terminal, now often a terminal emulator or a web interface connected through CICS Transaction Gateway.

💡 The Key Insight: CICS is a multitasking system. While a batch region runs one program at a time, a single CICS region may run thousands of tasks concurrently, each serving a different user. This shared environment is why CICS programs must never modify shared storage, must limit their execution time, and must release resources promptly.

29.2 EXEC CICS ... END-EXEC: The CICS Programming Interface

CICS COBOL programs communicate with the CICS runtime through commands enclosed in EXEC CICS ... END-EXEC blocks, similar to how DB2 programs use EXEC SQL ... END-EXEC.

           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    ERASE
           END-EXEC

The CICS translator (analogous to the DB2 precompiler) processes these commands before compilation, converting them into standard COBOL CALLs to CICS runtime services.

Compilation Process for CICS Programs

Source.cbl → [CICS Translator] → Translated.cbl →
[DB2 Precompiler] → Modified.cbl + DBRM →
[COBOL Compiler] → Object.obj → [Linkage Editor] → Load Module

If a program uses both CICS and DB2 (which is common), it goes through both translators before compilation.

CICS Command Syntax Rules

  1. Every CICS command starts with EXEC CICS and ends with END-EXEC
  2. Command options use keyword-value pairs: OPTION(value)
  3. Host variables are referenced by name without colons (unlike DB2)
  4. Commands must be in Area B (columns 12-72)
  5. No periods inside EXEC CICS blocks
      *    CORRECT — no period inside the EXEC CICS block
           EXEC CICS
               READ FILE('ACCTFILE')
                    INTO(WS-ACCT-RECORD)
                    RIDFLD(WS-ACCT-KEY)
                    LENGTH(WS-REC-LEN)
           END-EXEC

      *    INCORRECT — period inside the block
           EXEC CICS
               READ FILE('ACCTFILE').    <-- SYNTAX ERROR
                    INTO(WS-ACCT-RECORD)
           END-EXEC

29.3 The Execute Interface Block (EIB)

Every CICS program has access to the Execute Interface Block (EIB) — a read-only data structure that CICS automatically passes to your program. The EIB contains essential information about the current task, transaction, and terminal.

      *--------------------------------------------------------------
      * Key EIB fields (provided automatically by CICS)
      *--------------------------------------------------------------
      * EIBAID     - PIC X(1)   - Attention identifier (which key
      *                            the user pressed)
      * EIBCALEN   - PIC S9(4) COMP - Length of COMMAREA passed
      *                            (0 on first invocation)
      * EIBDATE    - PIC S9(7) COMP - Current date (0CYYDDD)
      * EIBFN      - PIC X(2)   - Last CICS command code
      * EIBRESP    - PIC S9(8) COMP - Response code from last
      *                            CICS command
      * EIBRESP2   - PIC S9(8) COMP - Extended response code
      * EIBTASKN   - PIC S9(7) COMP - Task number
      * EIBTRMID   - PIC X(4)   - Terminal identifier
      * EIBTRNID   - PIC X(4)   - Transaction identifier
      * EIBTIME    - PIC S9(7) COMP - Current time (0HHMMSS)

The EIB is your program's primary source of context. The EIBAID field tells you which key the user pressed (Enter, PF1 through PF24, Clear, PA1-PA3). The EIBCALEN field tells you whether this is the first invocation of a pseudo-conversational transaction (EIBCALEN = 0) or a return from a previous interaction.

AID Key Values

CICS provides copybook DFHAID with symbolic names for all attention identifier values:

           COPY DFHAID.

      *    After RECEIVE MAP, check which key was pressed:
           EVALUATE EIBAID
               WHEN DFHENTER
                   PERFORM 2000-PROCESS-INQUIRY
               WHEN DFHPF3
                   PERFORM 9000-EXIT-TRANSACTION
               WHEN DFHPF12
                   PERFORM 9000-EXIT-TRANSACTION
               WHEN DFHCLEAR
                   PERFORM 1000-SEND-EMPTY-MAP
               WHEN OTHER
                   MOVE 'Invalid key pressed'
                       TO MSGO
                   PERFORM 1500-SEND-MAP-DATAONLY
           END-EVALUATE

📊 Common AID Keys: The most frequently used keys in CICS applications are Enter (submit), PF3 (exit/back), PF7 (page up/scroll backward), PF8 (page down/scroll forward), PF12 (cancel), and Clear (reset screen). Your applications should always handle at least these keys.

29.4 BMS Maps: Defining Screen Layouts

Basic Mapping Support (BMS) is the CICS facility for defining 3270 screen layouts. A BMS map definition is written in assembler-like macro language (not COBOL) and assembled into two outputs:

  1. Physical map — a load module that controls screen formatting (colors, positioning, attributes)
  2. Symbolic map — a COBOL copybook that defines the data fields your program reads and writes

BMS Map Definition Structure

A mapset contains one or more maps. Each map contains field definitions:

MAPSET (DFHMSD) ── contains ──▶ MAP (DFHMDI) ── contains ──▶ FIELDS (DFHMDF)

A Complete BMS Map Definition

Here is the BMS definition for GlobalBank's account inquiry screen:

ACCTSET  DFHMSD TYPE=&SYSPARM,                                X
               MODE=INOUT,                                     X
               LANG=COBOL,                                     X
               STORAGE=AUTO,                                   X
               CTRL=FREEKB,                                    X
               TIOAPFX=YES
*
ACCTINQ  DFHMDI SIZE=(24,80),                                 X
               LINE=1,                                         X
               COLUMN=1
*
* --- Screen Title ---
         DFHMDF POS=(1,25),                                    X
               LENGTH=30,                                      X
               ATTRB=(ASKIP,BRT),                              X
               INITIAL='GLOBALBANK ACCOUNT INQUIRY'
*
* --- Date and Time ---
DTEFLD   DFHMDF POS=(1,65),                                   X
               LENGTH=10,                                      X
               ATTRB=(ASKIP,NORM)
*
TIMFLD   DFHMDF POS=(1,76),                                   X
               LENGTH=5,                                       X
               ATTRB=(ASKIP,NORM)
*
* --- Account Number Entry ---
         DFHMDF POS=(3,2),                                     X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Account Number:'
*
ACCTNO   DFHMDF POS=(3,18),                                   X
               LENGTH=10,                                      X
               ATTRB=(UNPROT,BRT,IC),                          X
               JUSTIFY=LEFT
         DFHMDF POS=(3,29),                                    X
               LENGTH=1,                                       X
               ATTRB=ASKIP
*
* --- Account Information Display ---
         DFHMDF POS=(5,2),                                     X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Account Name'
*
ANAME    DFHMDF POS=(5,18),                                   X
               LENGTH=50,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(7,2),                                     X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Account Type'
*
ATYPE    DFHMDF POS=(7,18),                                   X
               LENGTH=20,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(9,2),                                     X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Balance'
*
ABAL     DFHMDF POS=(9,18),                                   X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(11,2),                                    X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Status'
*
ASTAT    DFHMDF POS=(11,18),                                  X
               LENGTH=10,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(13,2),                                    X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Open Date'
*
AODATE   DFHMDF POS=(13,18),                                  X
               LENGTH=10,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(15,2),                                    X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Branch'
*
ABRANCH  DFHMDF POS=(15,18),                                  X
               LENGTH=20,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(17,2),                                    X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Last Activity'
*
ALACT    DFHMDF POS=(17,18),                                  X
               LENGTH=26,                                      X
               ATTRB=(ASKIP,BRT)
*
* --- Message Line ---
MSG      DFHMDF POS=(21,2),                                   X
               LENGTH=60,                                      X
               ATTRB=(ASKIP,BRT)
*
* --- Function Key Legend ---
         DFHMDF POS=(23,2),                                    X
               LENGTH=40,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Enter=Inquiry  PF3=Exit  Clear=Reset'
*
         DFHMDF POS=(24,2),                                    X
               LENGTH=30,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Transaction: AINQ'
*
         DFHMSD TYPE=FINAL
         END

Understanding BMS Macros

DFHMSD — Map Set Definition: - TYPE=&SYSPARM — Allows generating physical or symbolic map from same source - MODE=INOUT — Map is used for both sending and receiving data - LANG=COBOL — Generate COBOL copybook for symbolic map - STORAGE=AUTO — Each task gets its own copy of the map area - TIOAPFX=YES — Include Terminal I/O Area prefix (required for COBOL)

DFHMDI — Map Definition: - SIZE=(24,80) — Standard 3270 screen: 24 rows, 80 columns - LINE=1, COLUMN=1 — Map starts at top-left corner

DFHMDF — Field Definition: - POS=(row,col) — Screen position - LENGTH=n — Field length in characters - ATTRB — Field attributes: - ASKIP — Auto-skip (read-only, cursor skips over) - UNPROT — Unprotected (user can type in this field) - BRT — Bright (high intensity) - NORM — Normal intensity - DRK — Dark (hidden — useful for passwords) - IC — Initial Cursor position - NUM — Numeric only - INITIAL — Default text to display - JUSTIFY — LEFT or RIGHT alignment

💡 The Stopper Field: Notice the unnamed field after ACCTNO with LENGTH=1 and ATTRB=ASKIP. This is called a "stopper field" — it marks the end of the input area and prevents the cursor from overrunning into adjacent fields. Every input field should have a stopper field immediately after it.

The Symbolic Map (Generated Copybook)

When you assemble the BMS definition with TYPE=DSECT, it generates a COBOL copybook. This copybook defines both input (I) and output (O) versions of each named field:

      *--------------------------------------------------------------
      * Symbolic map generated from ACCTSET BMS definition
      *--------------------------------------------------------------
       01  ACCTINQI.
           02  FILLER                  PIC X(12).
           02  DTEFDLL                 PIC S9(4) COMP.
           02  DTEFLDF                 PIC X(1).
           02  DTEFLDI                 PIC X(10).
           02  TIMFLDL                 PIC S9(4) COMP.
           02  TIMFLDF                 PIC X(1).
           02  TIMFLDI                 PIC X(5).
           02  ACCTNOL                 PIC S9(4) COMP.
           02  ACCTNOF                 PIC X(1).
           02  ACCTNOI                 PIC X(10).
           02  ANAMEL                  PIC S9(4) COMP.
           02  ANAMEF                  PIC X(1).
           02  ANAMEI                  PIC X(50).
           02  ATYPEL                  PIC S9(4) COMP.
           02  ATYPEF                  PIC X(1).
           02  ATYPEI                  PIC X(20).
           02  ABALL                   PIC S9(4) COMP.
           02  ABALF                   PIC X(1).
           02  ABALI                   PIC X(15).
           02  ASTATL                  PIC S9(4) COMP.
           02  ASTATF                  PIC X(1).
           02  ASTATI                  PIC X(10).
           02  AODATEL                 PIC S9(4) COMP.
           02  AODATEF                 PIC X(1).
           02  AODATEI                 PIC X(10).
           02  ABRANCHL               PIC S9(4) COMP.
           02  ABRANCHF               PIC X(1).
           02  ABRANCHI               PIC X(20).
           02  ALACTL                  PIC S9(4) COMP.
           02  ALACTF                  PIC X(1).
           02  ALACTI                  PIC X(26).
           02  MSGL                    PIC S9(4) COMP.
           02  MSGF                    PIC X(1).
           02  MSGI                    PIC X(60).

       01  ACCTINQO REDEFINES ACCTINQI.
           02  FILLER                  PIC X(12).
           02  FILLER                  PIC X(3).
           02  DTEFLDO                 PIC X(10).
           02  FILLER                  PIC X(3).
           02  TIMFLDO                 PIC X(5).
           02  FILLER                  PIC X(3).
           02  ACCTNOO                 PIC X(10).
           02  FILLER                  PIC X(3).
           02  ANAMEO                  PIC X(50).
           02  FILLER                  PIC X(3).
           02  ATYPEO                  PIC X(20).
           02  FILLER                  PIC X(3).
           02  ABALO                   PIC X(15).
           02  FILLER                  PIC X(3).
           02  ASTATO                  PIC X(10).
           02  FILLER                  PIC X(3).
           02  AODATEO                 PIC X(10).
           02  FILLER                  PIC X(3).
           02  ABRANCHO               PIC X(20).
           02  FILLER                  PIC X(3).
           02  ALACTO                  PIC X(26).
           02  FILLER                  PIC X(3).
           02  MSGO                    PIC X(60).

Understanding the Symbolic Map Structure

For each named BMS field, the symbolic map generates three subfields:

Suffix Type Purpose
L (Length) PIC S9(4) COMP Length of data entered by user
F (Flag) PIC X(1) Modified Data Tag — nonzero if user changed the field
I (Input) PIC X(n) Data received from the terminal
O (Output) PIC X(n) Data to send to the terminal

The I and O versions REDEFINE the same storage. You use the I suffix fields when reading user input (after RECEIVE MAP) and the O suffix fields when populating the screen (before SEND MAP).

⚠️ Common Mistake: A field's L value is 0 if the user did not modify it, even if the field contains data from a previous SEND MAP. Always check the length field before processing input — a zero length means the user did not touch that field.

29.5 SEND MAP and RECEIVE MAP: Screen I/O

Sending a Map to the Terminal

      *--------------------------------------------------------------
      * Send the full map (first display — erase screen first)
      *--------------------------------------------------------------
           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    ERASE
           END-EXEC

Key SEND MAP options:

Option Meaning
ERASE Clear the screen before displaying the map
ERASEAUP Erase only unprotected fields (leaves labels intact)
DATAONLY Send only data fields, not attributes or labels
MAPONLY Send only the physical map (labels/attributes), no data
CURSOR(position) Position the cursor at a specific offset
ALARM Sound the terminal alarm (for error notifications)

The most common patterns:

      *    First display: send everything, erase screen
           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    ERASE
           END-EXEC

      *    Subsequent update: send only changed data fields
           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    DATAONLY
           END-EXEC

      *    Error display: send data with alarm
           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    DATAONLY
                    ALARM
           END-EXEC

Receiving Input from the Terminal

           EXEC CICS
               RECEIVE MAP('ACCTINQ')
                       MAPSET('ACCTSET')
                       INTO(ACCTINQI)
           END-EXEC

After RECEIVE MAP, the symbolic map's input fields contain the user's data. Check the L (length) field to determine which fields the user modified:

           EXEC CICS
               RECEIVE MAP('ACCTINQ')
                       MAPSET('ACCTSET')
                       INTO(ACCTINQI)
           END-EXEC

      *    Check if user entered an account number
           IF ACCTNOL > 0
      *        User typed something in the account number field
               PERFORM 2000-LOOKUP-ACCOUNT
           ELSE
               MOVE 'Please enter an account number'
                   TO MSGO
               PERFORM 1500-SEND-DATAONLY
           END-IF

Dynamic Attribute Modification

One of the most powerful BMS techniques is modifying field attributes at runtime. The symbolic map's F (flag/attribute) field for each named field allows you to change how a field appears on the screen dynamically.

CICS provides the DFHBMSCA copybook with attribute byte constants:

           COPY DFHBMSCA.

      *    Key attribute constants:
      *    DFHBMPRF — Protected field (user cannot type)
      *    DFHBMUNN — Unprotected, numeric only
      *    DFHBMUNP — Unprotected (user can type)
      *    DFHBMBRY — Bright (high intensity)
      *    DFHBMDAR — Dark (hidden — for passwords, etc.)
      *    DFHBMFSE — Field with MDT set (appears modified)

A common use case is highlighting error fields in bright red to draw the user's attention:

       3000-VALIDATE-INPUT.
           MOVE 'Y' TO WS-VALID-FLAG

      *    Validate account number format
           IF ACCTNOI NOT NUMERIC
               MOVE DFHBMBRY TO ACCTNOF
               MOVE 'Account number must be numeric' TO MSGO
               MOVE 'N' TO WS-VALID-FLAG
           END-IF

      *    If validation failed, resend with highlighted errors
           IF WS-VALID-FLAG = 'N'
               PERFORM 1500-SEND-DATAONLY
           END-IF.

Another common pattern is conditionally protecting fields based on user authorization. A teller might see certain fields as protected (read-only) while a supervisor sees them as unprotected (editable):

      *    Set field protection based on user role
           IF WS-USER-ROLE = 'TELLER'
               MOVE DFHBMPRF TO ATYPEF
               MOVE DFHBMPRF TO ASTATF
      *        Teller can only view type and status, not change
           ELSE
               MOVE DFHBMUNP TO ATYPEF
               MOVE DFHBMUNP TO ASTATF
      *        Supervisor can edit type and status
           END-IF

The Modified Data Tag (MDT)

Every field on a 3270 screen has a Modified Data Tag (MDT) bit in its attribute byte. When the user types anything in a field, the hardware sets the MDT for that field. When the user presses Enter, only fields with their MDT set are transmitted to the program.

This is important for two reasons:

  1. Bandwidth efficiency: In the era of slow network connections, transmitting only modified fields saved significant time. Even today, with terminal emulators, it reduces data transfer.

  2. Change detection: After RECEIVE MAP, you can check if a field was modified by examining its length field (L suffix). A length of 0 means the field was not modified — even if it contains data from a previous SEND MAP.

However, the MDT can be tricky. If you SEND MAP with DATAONLY and a field already has data from a previous send, the MDT is not set. If the user does not touch that field and presses Enter, the field's length will be 0 even though the field visually contains data.

      *--------------------------------------------------------------
      * MDT Gotcha: field contains data but length = 0
      *--------------------------------------------------------------
      *    Scenario: We previously sent account name "JOHN SMITH"
      *    User did not touch the name field, just changed status
      *    After RECEIVE MAP:
      *      ANAMEI  = "JOHN SMITH"  (still contains previous data)
      *      ANAMEL  = 0             (user did not modify it)
      *
      *    If your code checks ANAMEL > 0 to process the name,
      *    the name will be "skipped" even though it appears on screen
      *--------------------------------------------------------------

      *    Solution: For fields that should always be processed,
      *    set the MDT in the attribute byte when you SEND:
           MOVE DFHBMFSE TO ACCTNOF
      *    DFHBMFSE = field with MDT set — will always be transmitted

💡 When to Set MDT Explicitly: Set the MDT (using DFHBMFSE) on fields that your program always needs to receive, regardless of whether the user modified them. This is particularly important in update screens where you need to compare the screen values with the database values to detect changes. For inquiry screens where you only care about the search field, you typically do not need to set MDTs manually.

Screen Layout Design Principles

Maria Chen follows these principles when designing BMS screens at GlobalBank:

  1. Consistent layout across transactions. Title on row 1 (left), date/time on row 1 (right), function key legend on row 23, transaction ID on row 24. Users should never have to hunt for the exit key.

  2. Labels left-aligned, data right of label. Labels start at column 2. Data fields start at a consistent column (typically 18 or 20). This creates a clean visual grid.

  3. Group related fields. Account number and name together, financial fields together, dates together. Use blank rows to separate groups.

  4. Input fields are bright and unprotected. Output fields are bright and protected. Labels are normal intensity and protected. This visual distinction helps users immediately see where they can type.

  5. Error messages on a dedicated message line. Row 21 or 22 is reserved for messages. The message field should be bright and wide enough for descriptive text.

  6. Maximum one input field per screen for inquiry transactions. If the user needs to enter more than one search criterion, consider a dedicated search screen.

29.6 Pseudo-Conversational Design: The Heart of CICS Programming

This is perhaps the most important concept in CICS programming, and the one that trips up beginners most often.

The Problem with Conversational Programming

In a conversational design, the program sends a screen, waits for the user to respond, then processes the response — all within a single task:

Task starts → Send screen → WAIT for user → Receive input → Process → Send result → WAIT → ...

The problem: while the program waits for the user to type (which might take 30 seconds, or 5 minutes, or an hour), it holds onto CICS resources — memory, task control blocks, and potentially DB2 connections. If 1,000 users are all staring at their screens thinking, that is 1,000 tasks consuming resources while doing nothing.

The Pseudo-Conversational Solution

In pseudo-conversational design, the program sends a screen and then ends. When the user presses a key, CICS starts a new task to process the input:

Task 1: Start → Send screen → RETURN(TRANSID)
              (Task 1 ends — resources freed)

User thinks for 2 minutes...

Task 2: Start → Receive input → Process → Send result → RETURN(TRANSID)
              (Task 2 ends — resources freed)

From the user's perspective, it looks like one continuous conversation. But from CICS's perspective, each interaction is a separate task, and resources are held only during actual processing.

Implementing Pseudo-Conversational Design

The key is the RETURN command with TRANSID and COMMAREA:

      *--------------------------------------------------------------
      * End the current task and specify what happens next
      *--------------------------------------------------------------
           EXEC CICS
               RETURN TRANSID('AINQ')
                      COMMAREA(WS-COMMAREA)
                      LENGTH(WS-COMMAREA-LEN)
           END-EXEC
  • TRANSID('AINQ') — When the user presses a key, start transaction AINQ again
  • COMMAREA(WS-COMMAREA) — Pass this data to the next task (since all working storage is lost when the task ends)
  • LENGTH(WS-COMMAREA-LEN) — Length of the COMMAREA data

The COMMAREA: Bridging Tasks

Since each pseudo-conversational interaction is a separate task, all working storage is lost between interactions. The Communication Area (COMMAREA) is your mechanism for preserving state:

       WORKING-STORAGE SECTION.
       01  WS-COMMAREA.
           05  WS-CA-STATE         PIC X(1).
               88  WS-CA-FIRST-TIME       VALUE ' '.
               88  WS-CA-MAP-DISPLAYED    VALUE 'M'.
               88  WS-CA-DATA-SHOWN       VALUE 'D'.
           05  WS-CA-ACCT-NUM     PIC X(10).
           05  WS-CA-PREV-KEY     PIC X(1).

       01  WS-COMMAREA-LEN        PIC S9(4) COMP VALUE 12.

       LINKAGE SECTION.
       01  DFHCOMMAREA.
           05  LS-CA-STATE         PIC X(1).
           05  LS-CA-ACCT-NUM     PIC X(10).
           05  LS-CA-PREV-KEY     PIC X(1).

The COMMAREA arrives in the LINKAGE SECTION as DFHCOMMAREA. On the first invocation (user just typed the transaction ID), EIBCALEN is 0 and DFHCOMMAREA is empty. On subsequent invocations, EIBCALEN equals the length of data passed.

The Pseudo-Conversational Control Flow

       PROCEDURE DIVISION.
       0000-MAIN.
      *--------------------------------------------------------------
      * Determine if this is the first invocation or a return
      *--------------------------------------------------------------
           EVALUATE TRUE
               WHEN EIBCALEN = 0
      *            First invocation — send empty map
                   PERFORM 1000-FIRST-TIME
               WHEN OTHER
      *            Return from user — process input
                   MOVE DFHCOMMAREA TO WS-COMMAREA
                   PERFORM 2000-PROCESS-INPUT
           END-EVALUATE
           GOBACK.

       1000-FIRST-TIME.
           INITIALIZE ACCTINQO
           MOVE 'Enter account number and press Enter'
               TO MSGO
           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    ERASE
           END-EXEC
           MOVE 'M' TO WS-CA-STATE
           EXEC CICS
               RETURN TRANSID('AINQ')
                      COMMAREA(WS-COMMAREA)
                      LENGTH(WS-COMMAREA-LEN)
           END-EXEC.

       2000-PROCESS-INPUT.
      *    Check which key the user pressed
           EVALUATE EIBAID
               WHEN DFHPF3
                   EXEC CICS RETURN END-EXEC
               WHEN DFHCLEAR
                   PERFORM 1000-FIRST-TIME
               WHEN DFHENTER
                   PERFORM 3000-HANDLE-ENTER
               WHEN OTHER
                   MOVE 'Invalid key pressed'
                       TO MSGO
                   PERFORM 1500-SEND-DATAONLY
           END-EVALUATE.

⚠️ Critical Concept: EXEC CICS RETURN END-EXEC without a TRANSID ends the conversation entirely — the user sees a blank screen or returns to the CICS menu. EXEC CICS RETURN TRANSID('AINQ')... ends the task but tells CICS to restart the transaction when the user presses a key. Confusing these two forms is one of the most common CICS bugs.

29.7 CICS Error Handling: RESP and RESP2

CICS provides two approaches to error handling. The traditional approach uses HANDLE CONDITION; the modern approach uses RESP/RESP2. We will focus on the modern approach, as it is the standard in current development.

The RESP/RESP2 Approach

       WORKING-STORAGE SECTION.
       01  WS-RESP                 PIC S9(8) COMP.
       01  WS-RESP2                PIC S9(8) COMP.

       PROCEDURE DIVISION.
           EXEC CICS
               READ FILE('ACCTFILE')
                    INTO(WS-ACCT-RECORD)
                    RIDFLD(WS-ACCT-KEY)
                    LENGTH(WS-REC-LEN)
                    RESP(WS-RESP)
                    RESP2(WS-RESP2)
           END-EXEC

           EVALUATE WS-RESP
               WHEN DFHRESP(NORMAL)
                   PERFORM 3100-DISPLAY-ACCOUNT
               WHEN DFHRESP(NOTFND)
                   MOVE 'Account not found' TO MSGO
                   PERFORM 1500-SEND-DATAONLY
               WHEN DFHRESP(DISABLED)
                   MOVE 'Account file is disabled' TO MSGO
                   PERFORM 1500-SEND-DATAONLY
               WHEN DFHRESP(IOERR)
                   MOVE 'I/O error on account file' TO MSGO
                   PERFORM 9000-LOG-AND-ABEND
               WHEN OTHER
                   STRING 'Unexpected error: RESP='
                          WS-RESP ' RESP2=' WS-RESP2
                       DELIMITED BY SIZE
                       INTO MSGO
                   END-STRING
                   PERFORM 9000-LOG-AND-ABEND
           END-EVALUATE

Common RESP Values

DFHRESP Name Meaning
NORMAL Command completed successfully
NOTFND Record/resource not found
DUPREC Duplicate record on write
DISABLED File/resource is disabled
INVREQ Invalid request
IOERR I/O error
LENGERR Length error
MAPFAIL Map receive failed (no data on screen)
PGMIDERR Program not found
QIDERR Queue not found

The Traditional HANDLE CONDITION (For Reference)

You will encounter HANDLE CONDITION in older programs. It works like an exception handler, routing control to a paragraph when a specific condition occurs:

      *    Traditional approach — still seen in legacy programs
           EXEC CICS
               HANDLE CONDITION
                   NOTFND(8100-NOT-FOUND)
                   IOERR(8200-IO-ERROR)
                   ERROR(8900-GENERAL-ERROR)
           END-EXEC

      *    This READ will transfer control to a handler on error
           EXEC CICS
               READ FILE('ACCTFILE')
                    INTO(WS-ACCT-RECORD)
                    RIDFLD(WS-ACCT-KEY)
                    LENGTH(WS-REC-LEN)
           END-EXEC
      *    If we get here, the READ was successful

🔵 Derek's Question: "Why is RESP preferred over HANDLE CONDITION?" Maria's answer: "HANDLE CONDITION creates a hidden GOTO — your program transfers to a paragraph without any visible branching logic at the point of the command. It makes the control flow difficult to follow. RESP puts the error check right next to the command that might fail, like an IF statement after a function call. Readability is a feature."

29.8 HANDLE AID: Traditional Key Handling

Similar to HANDLE CONDITION, HANDLE AID routes control based on which key the user pressed:

      *    Traditional — avoid in new code
           EXEC CICS
               HANDLE AID
                   PF3(9000-EXIT)
                   PF12(9000-EXIT)
                   CLEAR(1000-RESET)
                   ANYKEY(8000-INVALID-KEY)
           END-EXEC

The modern alternative is simply checking EIBAID after receiving input, as shown in section 29.6. The EVALUATE structure is more readable and maintainable.

29.9 Basic CICS Commands

SEND and RECEIVE (Low-Level)

While SEND MAP and RECEIVE MAP handle formatted screen I/O, the lower-level SEND and RECEIVE commands handle raw text:

      *    Send a text message (no map)
           EXEC CICS
               SEND TEXT FROM(WS-MESSAGE)
                    LENGTH(WS-MSG-LEN)
                    ERASE
           END-EXEC

      *    Receive raw terminal input
           EXEC CICS
               RECEIVE INTO(WS-INPUT)
                       LENGTH(WS-INPUT-LEN)
           END-EXEC

XCTL: Transfer Control

XCTL transfers control to another program without returning. The calling program's storage is released:

           EXEC CICS
               XCTL PROGRAM('ACCTMENU')
                    COMMAREA(WS-COMMAREA)
                    LENGTH(WS-COMMAREA-LEN)
                    RESP(WS-RESP)
           END-EXEC

LINK invokes another program and expects it to return. The calling program's storage is preserved:

           EXEC CICS
               LINK PROGRAM('ACCTVAL')
                    COMMAREA(WS-VALIDATION-DATA)
                    LENGTH(WS-VAL-LEN)
                    RESP(WS-RESP)
           END-EXEC
      *    Control returns here after ACCTVAL finishes

RETURN: End the Task

      *    End the conversation entirely
           EXEC CICS RETURN END-EXEC

      *    End this task, restart on next user input
           EXEC CICS
               RETURN TRANSID('AINQ')
                      COMMAREA(WS-COMMAREA)
                      LENGTH(WS-COMMAREA-LEN)
           END-EXEC

🔗 XCTL vs. LINK: Think of XCTL as a GOTO between programs — one-way, no return. LINK is like a PERFORM — the called program returns to the caller. Use XCTL when transferring to a different transaction's main program. Use LINK when calling a subroutine (validation routine, logging utility, etc.) that should return control.

29.10 The Transaction Lifecycle

Let us trace the complete lifecycle of a CICS transaction to solidify your understanding:

Step 1: Transaction Initiation

The user types AINQ on a clear screen and presses Enter. CICS: 1. Recognizes AINQ as a registered transaction ID 2. Looks up the associated program (ACCTINQ) in the Processing Control Table (PCT) 3. Loads the program if not already in memory 4. Creates a new task with its own EIB and storage 5. Passes control to the program's PROCEDURE DIVISION

Step 2: First Invocation

The program checks EIBCALEN. It is 0 (no COMMAREA), meaning this is the first invocation: 1. Initialize the output map area 2. SEND MAP with ERASE to display the empty inquiry screen 3. RETURN with TRANSID('AINQ') and COMMAREA 4. Task ends. All working storage is freed.

Step 3: User Interaction

The user sees the inquiry screen with an empty account number field. The cursor is positioned at the account number field (thanks to the IC attribute in the BMS definition). The user types A001234567 and presses Enter.

Step 4: Second Invocation

CICS starts a new task for transaction AINQ: 1. EIBCALEN > 0, so the program knows this is a return 2. DFHCOMMAREA contains the data from the previous RETURN 3. Program copies DFHCOMMAREA to WS-COMMAREA 4. EIBAID = DFHENTER — the user pressed Enter 5. RECEIVE MAP to get the user's input 6. ACCTNOL > 0 — user entered an account number 7. Read the account from DB2 or VSAM 8. Populate the output map fields 9. SEND MAP with DATAONLY to display the results 10. RETURN with TRANSID and COMMAREA 11. Task ends.

Step 5: Continued Interaction or Exit

The user can now enter a different account number and press Enter (which starts Step 4 again) or press PF3 to exit. If PF3 is pressed: 1. New task starts 2. Program checks EIBAID = DFHPF3 3. RETURN (without TRANSID) — conversation ends 4. User sees the CICS blank screen or menu

Timing Breakdown of a Typical Transaction

To understand why pseudo-conversational design is so efficient, consider the timing of a typical inquiry:

Phase Duration Resources Consumed
CICS task startup ~1 ms Task control block, EIB
SEND MAP ~2 ms Terminal I/O buffer
RETURN + task cleanup ~1 ms None (resources freed)
User think time 5-60 seconds NONE (task ended)
CICS task startup ~1 ms Task control block, EIB, COMMAREA
RECEIVE MAP ~1 ms Terminal I/O buffer
DB2 SELECT ~2-5 ms DB2 thread (from pool)
Format output + SEND MAP ~3 ms Terminal I/O buffer
RETURN + task cleanup ~1 ms None (resources freed)

Total active processing time: approximately 10-15 milliseconds. Total elapsed time from the user's perspective: 5-60 seconds (dominated by their think time). The ratio of active processing to elapsed time is often less than 1%. This is why pseudo-conversational design allows a single CICS region to serve thousands of users — at any given moment, fewer than 1% of them are actually consuming processing resources.

In a conversational design, each of those 1,000 users would hold resources for the entire 5-60 second think time. The CICS region would need 1,000 concurrent task areas, 1,000 DB2 threads, and vastly more memory. The math simply does not work for large-scale online systems.

💡 The 1% Rule: In a well-designed pseudo-conversational system, tasks are active for approximately 1% of the user's total elapsed time. This means a CICS region that can support 100 concurrent active tasks can effectively serve 10,000 concurrent users. This is the fundamental scalability advantage of pseudo-conversational design.

29.11 GlobalBank Scenario: Online Account Inquiry

Now let us put it all together. Here is the complete CICS program for GlobalBank's account inquiry transaction:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. ACCTINQ.
      *--------------------------------------------------------------
      * GlobalBank Account Inquiry Transaction (AINQ)
      * Pseudo-conversational CICS program with DB2 access
      *--------------------------------------------------------------
       DATA DIVISION.
       WORKING-STORAGE SECTION.

           COPY DFHAID.
           COPY ACCTSET.
           EXEC SQL INCLUDE SQLCA END-EXEC.
           EXEC SQL INCLUDE DCLACCT END-EXEC.

       01  WS-COMMAREA.
           05  WS-CA-STATE         PIC X(1).
               88  WS-CA-FIRST-TIME       VALUE ' '.
               88  WS-CA-MAP-SENT         VALUE 'M'.
           05  WS-CA-ACCT-NUM     PIC X(10).
       01  WS-COMMAREA-LEN        PIC S9(4) COMP VALUE 11.

       01  WS-RESP                 PIC S9(8) COMP.
       01  WS-RESP2                PIC S9(8) COMP.
       01  WS-CURRENT-DATE        PIC X(10).
       01  WS-CURRENT-TIME        PIC X(5).
       01  WS-FORMATTED-BAL       PIC Z(10)9.99-.
       01  WS-ACCT-TYPE-DESC      PIC X(20).
       01  WS-ACCT-STATUS-DESC    PIC X(10).

       01  WS-CLOSE-DATE-IND      PIC S9(4) COMP.
       01  WS-BRANCH-NAME.
           49  WS-BRANCH-NAME-LEN PIC S9(4) COMP.
           49  WS-BRANCH-NAME-TEXT PIC X(20).

       LINKAGE SECTION.
       01  DFHCOMMAREA             PIC X(11).

       PROCEDURE DIVISION.
       0000-MAIN.
           EVALUATE TRUE
               WHEN EIBCALEN = 0
                   PERFORM 1000-FIRST-TIME
               WHEN OTHER
                   MOVE DFHCOMMAREA TO WS-COMMAREA
                   PERFORM 2000-PROCESS-INPUT
           END-EVALUATE
           GOBACK.

       1000-FIRST-TIME.
           INITIALIZE ACCTINQO
           PERFORM 1100-SET-DATE-TIME
           MOVE 'Enter account number and press Enter'
               TO MSGO
           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    ERASE
                    RESP(WS-RESP)
           END-EXEC
           IF WS-RESP NOT = DFHRESP(NORMAL)
               EXEC CICS ABEND ABCODE('AINQ') END-EXEC
           END-IF
           MOVE SPACES TO WS-CA-STATE
           MOVE 'M' TO WS-CA-STATE
           EXEC CICS
               RETURN TRANSID('AINQ')
                      COMMAREA(WS-COMMAREA)
                      LENGTH(WS-COMMAREA-LEN)
           END-EXEC.

       1100-SET-DATE-TIME.
           EXEC CICS ASKTIME ABSTIME(WS-ABSTIME) END-EXEC
           EXEC CICS FORMATTIME
               ABSTIME(WS-ABSTIME)
               MMDDYYYY(WS-CURRENT-DATE)
               DATESEP('/')
               TIME(WS-CURRENT-TIME)
               TIMESEP(':')
           END-EXEC
           MOVE WS-CURRENT-DATE TO DTEFLDO
           MOVE WS-CURRENT-TIME TO TIMFLDO.

       1500-SEND-DATAONLY.
           PERFORM 1100-SET-DATE-TIME
           EXEC CICS
               SEND MAP('ACCTINQ')
                    MAPSET('ACCTSET')
                    FROM(ACCTINQO)
                    DATAONLY
                    RESP(WS-RESP)
           END-EXEC
           IF WS-RESP NOT = DFHRESP(NORMAL)
               EXEC CICS ABEND ABCODE('AINQ') END-EXEC
           END-IF
           EXEC CICS
               RETURN TRANSID('AINQ')
                      COMMAREA(WS-COMMAREA)
                      LENGTH(WS-COMMAREA-LEN)
           END-EXEC.

       2000-PROCESS-INPUT.
           EVALUATE EIBAID
               WHEN DFHPF3
                   EXEC CICS RETURN END-EXEC
               WHEN DFHCLEAR
                   PERFORM 1000-FIRST-TIME
               WHEN DFHENTER
                   PERFORM 3000-HANDLE-INQUIRY
               WHEN OTHER
                   MOVE 'Invalid key. Use Enter or PF3.'
                       TO MSGO
                   PERFORM 1500-SEND-DATAONLY
           END-EVALUATE.

       3000-HANDLE-INQUIRY.
           EXEC CICS
               RECEIVE MAP('ACCTINQ')
                       MAPSET('ACCTSET')
                       INTO(ACCTINQI)
                       RESP(WS-RESP)
           END-EXEC

           IF WS-RESP = DFHRESP(MAPFAIL)
               MOVE 'Please enter an account number'
                   TO MSGO
               PERFORM 1500-SEND-DATAONLY
               EXIT PARAGRAPH
           END-IF

           IF ACCTNOL = 0 OR ACCTNOI = SPACES
               MOVE 'Account number is required' TO MSGO
               PERFORM 1500-SEND-DATAONLY
               EXIT PARAGRAPH
           END-IF

      *    Store account number for future interactions
           MOVE ACCTNOI TO WS-CA-ACCT-NUM

      *    Look up the account in DB2
           EXEC SQL
               SELECT ACCT_NUMBER, ACCT_NAME, ACCT_TYPE,
                      ACCT_BALANCE, ACCT_STATUS, OPEN_DATE,
                      CLOSE_DATE, BRANCH_CODE, LAST_ACTIVITY
               INTO :ACCT-NUMBER, :ACCT-NAME, :ACCT-TYPE,
                    :ACCT-BALANCE, :ACCT-STATUS, :OPEN-DATE,
                    :CLOSE-DATE :WS-CLOSE-DATE-IND,
                    :BRANCH-CODE, :LAST-ACTIVITY
               FROM ACCOUNT
               WHERE ACCT_NUMBER = :WS-CA-ACCT-NUM
           END-EXEC

           EVALUATE SQLCODE
               WHEN 0
                   PERFORM 3100-DISPLAY-ACCOUNT
               WHEN +100
                   INITIALIZE ACCTINQO
                   MOVE ACCTNOI TO ACCTNOO
                   MOVE 'Account not found' TO MSGO
                   PERFORM 1500-SEND-DATAONLY
               WHEN OTHER
                   STRING 'DB2 error: SQLCODE=' SQLCODE
                       DELIMITED BY SIZE
                       INTO MSGO
                   END-STRING
                   PERFORM 1500-SEND-DATAONLY
           END-EVALUATE.

       3100-DISPLAY-ACCOUNT.
           INITIALIZE ACCTINQO

           MOVE ACCT-NUMBER TO ACCTNOO
           MOVE ACCT-NAME-TEXT TO ANAMEO

           EVALUATE ACCT-TYPE
               WHEN 'SA' MOVE 'Savings' TO ATYPEO
               WHEN 'CH' MOVE 'Checking' TO ATYPEO
               WHEN 'MM' MOVE 'Money Market' TO ATYPEO
               WHEN 'CD' MOVE 'Certificate of Dep' TO ATYPEO
               WHEN OTHER MOVE ACCT-TYPE TO ATYPEO
           END-EVALUATE

           MOVE ACCT-BALANCE TO WS-FORMATTED-BAL
           MOVE WS-FORMATTED-BAL TO ABALO

           EVALUATE ACCT-STATUS
               WHEN 'A' MOVE 'Active' TO ASTATO
               WHEN 'I' MOVE 'Inactive' TO ASTATO
               WHEN 'F' MOVE 'Frozen' TO ASTATO
               WHEN 'C' MOVE 'Closed' TO ASTATO
               WHEN OTHER MOVE ACCT-STATUS TO ASTATO
           END-EVALUATE

           MOVE OPEN-DATE TO AODATEO
           MOVE BRANCH-CODE TO ABRANCHO
           MOVE LAST-ACTIVITY TO ALACTO
           MOVE 'Account found. Enter another or PF3.'
               TO MSGO

           MOVE 'D' TO WS-CA-STATE
           PERFORM 1500-SEND-DATAONLY.

🧪 Try It Yourself: In your Student Mainframe Lab (if you have CICS access), or using the CICS TS Liberty Profile for development, try creating this application: 1. Define the BMS map and assemble it 2. Compile the COBOL program with the CICS translator 3. Define the transaction (AINQ) in the CSD 4. Test by typing AINQ at a CICS terminal If you do not have CICS access, study the control flow carefully — understanding pseudo-conversational logic is the essential skill, and you can trace through it mentally.

29.12 MedClaim Scenario: Claim Status Lookup

Sarah Kim needs a simple CICS screen where customer service representatives can look up a claim's current status. The design follows the same pseudo-conversational pattern:

      *--------------------------------------------------------------
      * MedClaim Claim Status Inquiry (Transaction: CINQ)
      * Pseudo-conversational pattern identical to ACCTINQ
      *--------------------------------------------------------------
       PROCEDURE DIVISION.
       0000-MAIN.
           EVALUATE TRUE
               WHEN EIBCALEN = 0
                   PERFORM 1000-SEND-EMPTY-MAP
               WHEN OTHER
                   MOVE DFHCOMMAREA TO WS-COMMAREA
                   EVALUATE EIBAID
                       WHEN DFHENTER
                           PERFORM 2000-LOOKUP-CLAIM
                       WHEN DFHPF3
                           EXEC CICS RETURN END-EXEC
                       WHEN DFHPF5
                           PERFORM 3000-REFRESH-STATUS
                       WHEN OTHER
                           MOVE 'PF3=Exit  PF5=Refresh'
                               TO MSGO
                           PERFORM 1500-SEND-DATAONLY
                   END-EVALUATE
           END-EVALUATE
           GOBACK.

       2000-LOOKUP-CLAIM.
           EXEC CICS
               RECEIVE MAP('CLMSTAT')
                       MAPSET('CLMSET')
                       INTO(CLMSTATI)
                       RESP(WS-RESP)
           END-EXEC

           IF WS-RESP = DFHRESP(MAPFAIL)
               MOVE 'Enter a claim number' TO MSGO
               PERFORM 1500-SEND-DATAONLY
               EXIT PARAGRAPH
           END-IF

           IF CLMNOL = 0 OR CLMNOI = SPACES
               MOVE 'Claim number is required' TO MSGO
               PERFORM 1500-SEND-DATAONLY
               EXIT PARAGRAPH
           END-IF

           EXEC SQL
               SELECT C.CLAIM_NUMBER, C.CLAIM_STATUS,
                      C.CLAIM_AMOUNT, C.SUBMIT_DATE,
                      C.ADJUD_DATE, C.ADJUD_REASON,
                      M.MEMBER_NAME, P.PROVIDER_NAME
               INTO :WS-CLM-NUMBER, :WS-CLM-STATUS,
                    :WS-CLM-AMOUNT, :WS-CLM-SUBMIT,
                    :WS-CLM-ADJUD :WS-ADJUD-IND,
                    :WS-CLM-REASON :WS-REASON-IND,
                    :WS-CLM-MEMBER, :WS-CLM-PROVIDER
               FROM CLAIM C
               JOIN MEMBER M ON C.MEMBER_ID = M.MEMBER_ID
               JOIN PROVIDER P ON C.PROVIDER_ID = P.PROVIDER_ID
               WHERE C.CLAIM_NUMBER = :CLMNOI
           END-EXEC

           EVALUATE SQLCODE
               WHEN 0
                   PERFORM 2100-DISPLAY-CLAIM
               WHEN +100
                   MOVE 'Claim not found' TO MSGO
                   PERFORM 1500-SEND-DATAONLY
               WHEN OTHER
                   STRING 'DB2 error: ' SQLCODE
                       DELIMITED BY SIZE INTO MSGO
                   END-STRING
                   PERFORM 1500-SEND-DATAONLY
           END-EVALUATE.

⚖️ The Modernization Spectrum: Today, many CICS transactions are no longer accessed through 3270 terminals. CICS Transaction Gateway (CTG) exposes transactions as web services. CICS Liberty Profile hosts Java servlets that can invoke COBOL programs via LINK. Modern architectures wrap CICS transactions behind REST APIs, allowing mobile apps and web applications to access the same business logic that has served 3270 users for decades. The COBOL programs remain unchanged — only the presentation layer evolves.

Key Design Decisions in the MedClaim Screen

Sarah Kim made several design decisions that reflect production CICS best practices:

Decision 1: JOIN in the DB2 query. The claim status lookup joins four tables (CLAIM, MEMBER, PROVIDER, PAYMENT) in a single SELECT. This is the same pattern we saw in Chapter 28 — avoid row-by-row lookups. In a CICS context, it is even more critical because the transaction must complete in under one second.

Decision 2: LEFT JOIN for optional data. The PAYMENT table uses a LEFT JOIN because not all claims have payment records. Denied and pending claims have no payments. Without the LEFT JOIN, those claims would not appear in the result at all — a critical bug that would make it impossible for representatives to look up pending claims.

Decision 3: Indicator variables for all nullable fields. The adjudication date, adjudication reason, amount allowed, copay, and amount paid are all nullable (they are NULL for pending claims). Every nullable column requires an indicator variable to avoid the dreaded SQLCODE -305.

Decision 4: PF5 for refresh. Since CICS is pseudo-conversational, the data on screen is a snapshot from the moment the query ran. If the representative leaves the screen displayed for 20 minutes, the claim's status might have changed. PF5 re-queries and refreshes without requiring the representative to retype the claim number — a small but significant usability improvement.

The MedClaim Claim Status BMS Map

Here is the BMS definition for MedClaim's claim status screen, showing the more complex layout with financial fields:

CLMSET   DFHMSD TYPE=&SYSPARM,                                X
               MODE=INOUT,                                     X
               LANG=COBOL,                                     X
               STORAGE=AUTO,                                   X
               CTRL=FREEKB,                                    X
               TIOAPFX=YES
*
CLMSTAT  DFHMDI SIZE=(24,80),                                 X
               LINE=1,                                         X
               COLUMN=1
*
         DFHMDF POS=(1,22),                                    X
               LENGTH=36,                                      X
               ATTRB=(ASKIP,BRT),                              X
               INITIAL='MEDCLAIM CLAIM STATUS INQUIRY'
*
         DFHMDF POS=(3,2),                                     X
               LENGTH=14,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Claim Number: '
*
CLMNO    DFHMDF POS=(3,17),                                   X
               LENGTH=15,                                      X
               ATTRB=(UNPROT,BRT,IC),                          X
               JUSTIFY=LEFT
         DFHMDF POS=(3,33),                                    X
               LENGTH=1,                                       X
               ATTRB=ASKIP
*
         DFHMDF POS=(5,2),                                     X
               LENGTH=10,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Status:   '
CSTAT    DFHMDF POS=(5,17),                                   X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,BRT)
         DFHMDF POS=(5,40),                                    X
               LENGTH=11,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Adjud Date:'
CADATE   DFHMDF POS=(5,52),                                   X
               LENGTH=10,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(7,2),                                     X
               LENGTH=10,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Member:   '
CMEMBR   DFHMDF POS=(7,17),                                   X
               LENGTH=30,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(9,2),                                     X
               LENGTH=10,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Provider: '
CPROV    DFHMDF POS=(9,17),                                   X
               LENGTH=30,                                      X
               ATTRB=(ASKIP,BRT)
         DFHMDF POS=(9,52),                                    X
               LENGTH=8,                                       X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Network:'
CNET     DFHMDF POS=(9,61),                                   X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(13,2),                                    X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Amount Billed: '
CBILL    DFHMDF POS=(13,22),                                  X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(14,2),                                    X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Amount Allowed:'
CALLOW   DFHMDF POS=(14,22),                                  X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(15,2),                                    X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Member Copay:  '
CCOPAY   DFHMDF POS=(15,22),                                  X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(16,2),                                    X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Amount Paid:   '
CPAID    DFHMDF POS=(16,22),                                  X
               LENGTH=12,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(18,2),                                    X
               LENGTH=15,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Denial Reason: '
CREASON  DFHMDF POS=(18,22),                                  X
               LENGTH=50,                                      X
               ATTRB=(ASKIP,BRT)
*
MSG      DFHMDF POS=(21,2),                                   X
               LENGTH=60,                                      X
               ATTRB=(ASKIP,BRT)
*
         DFHMDF POS=(23,2),                                    X
               LENGTH=45,                                      X
               ATTRB=(ASKIP,NORM),                             X
               INITIAL='Enter=Lookup PF3=Exit PF5=Refresh PF7=Hist'
*
         DFHMSD TYPE=FINAL
         END

This map illustrates several important points: the financial fields (rows 13-16) are grouped together for easy scanning, the denial reason field is wide enough for meaningful messages (50 characters), and the function key legend includes PF7 for history (which will switch to a different map, as discussed in Chapter 30).

29.13 CICS Resource Definitions

For a CICS program to run, several resources must be defined in the CICS System Definition (CSD):

Transaction Definition

DEFINE TRANSACTION(AINQ)
       GROUP(GLBANKGP)
       PROGRAM(ACCTINQ)
       TWASIZE(0)
       PROFILE(DFHCICST)
       STATUS(ENABLED)

Program Definition

DEFINE PROGRAM(ACCTINQ)
       GROUP(GLBANKGP)
       LANGUAGE(COBOL)
       DATALOCATION(ANY)
       EXECKEY(USER)
       STATUS(ENABLED)

Mapset Definition

DEFINE MAPSET(ACCTSET)
       GROUP(GLBANKGP)
       STATUS(ENABLED)

File Definition (for VSAM access)

DEFINE FILE(ACCTFILE)
       GROUP(GLBANKGP)
       DSNAME(GLOBALBANK.ACCT.MASTER)
       STATUS(ENABLED)
       OPENTIME(STARTUP)
       ADD(YES)
       READ(YES)
       UPDATE(YES)
       BROWSE(YES)
       DELETE(YES)

29.14 CICS and DB2: Working Together

When a CICS program accesses DB2, the CICS-DB2 attachment facility manages the connection. The program's DB2 plan or package is bound separately and associated with the transaction via the RCT (Resource Control Table) or DB2CONN/DB2ENTRY definitions:

DEFINE DB2ENTRY(AINQ)
       GROUP(GLBANKGP)
       ACCOUNTREC(NONE)
       PLAN(AINQPLAN)
       THREADLIMIT(10)
       THREADWAIT(YES)

Key considerations when using DB2 in CICS:

  1. Keep SQL simple and fast. A CICS transaction should respond in under 1 second. Complex queries belong in batch programs or stored procedures. If you need to process 1,000 rows, do not open a cursor in a CICS program — either call a stored procedure that processes them on the DB2 side, or submit the work as a batch job.

  2. Avoid COMMITs in CICS. CICS manages the unit of work. A SYNCPOINT (CICS's COMMIT equivalent) is taken at task end. Explicit COMMITs mid-task are rarely needed and can cause problems. If you need to commit partway through a task (unusual but occasionally necessary), use EXEC CICS SYNCPOINT rather than EXEC SQL COMMIT to keep the CICS and DB2 units of work synchronized.

  3. Use singleton SELECTs when possible. A cursor open/fetch/close cycle requires three DB2 calls. A direct SELECT INTO requires one. For primary-key lookups in CICS transactions, always use SELECT INTO.

  4. Handle -911 and -904 gracefully. Lock contention between online and batch is common, especially during batch windows that overlap with online hours. Display a user-friendly message and ask the user to retry.

  5. Use WITH UR for read-only inquiries. If the transaction is purely read-only (inquiry, not update), specify WITH UR on the SELECT to avoid acquiring any locks. This prevents the online inquiry from being blocked by batch programs and vice versa.

  6. Thread pooling matters. CICS manages a pool of DB2 threads. The THREADLIMIT in the DB2ENTRY definition controls how many concurrent DB2 connections a transaction can use. If all threads are busy, new requests wait (if THREADWAIT=YES) or receive an error. Set THREADLIMIT based on expected concurrency and monitor thread usage.

DB2 Thread Management in CICS

When a CICS task issues its first SQL statement, CICS acquires a DB2 thread from the pool. The thread is held until the task ends (or, in some configurations, until a SYNCPOINT is taken). This means every pseudo-conversational task that touches DB2 acquires and releases a thread for each interaction.

Task 1 starts → Acquires DB2 thread → SELECT → Send map → RETURN
                 (Thread released when task ends)

User thinks for 30 seconds...

Task 2 starts → Acquires DB2 thread → SELECT → UPDATE → Send map → RETURN
                 (Thread released when task ends)

The thread pool size is a critical tuning parameter. Too few threads and tasks queue up waiting; too many threads and DB2 consumes excessive memory. Priya Kapoor monitors thread utilization daily and adjusts limits quarterly based on usage patterns.

Error Message Design for CICS-DB2 Programs

Production CICS programs should never display raw SQLCODEs to users. Instead, translate them into actionable messages:

           EVALUATE SQLCODE
               WHEN 0
                   CONTINUE
               WHEN +100
                   MOVE 'No matching record found' TO MSGO
               WHEN -305
                   MOVE 'Internal error: null data' TO MSGO
                   PERFORM 9100-LOG-DB2-ERROR
               WHEN -803
                   MOVE 'Duplicate record exists' TO MSGO
               WHEN -811
                   MOVE 'Multiple records match — '
                     &  'please be more specific'
                       TO MSGO
               WHEN -904
                   MOVE 'System busy — try again in a '
                     &  'moment' TO MSGO
               WHEN -911
                   MOVE 'Record temporarily locked — '
                     &  'please retry' TO MSGO
               WHEN -922
                   MOVE 'You are not authorized for '
                     &  'this operation' TO MSGO
               WHEN OTHER
                   STRING 'System error (code '
                          SQLCODE '). Contact support.'
                       DELIMITED BY SIZE INTO MSGO
                   END-STRING
                   PERFORM 9100-LOG-DB2-ERROR
           END-EVALUATE

Log the technical details (SQLCODE, SQLERRMC, SQLERRD) to a transient data queue for support staff, but show the user a message they can act on: "please retry," "contact support," or "record not found."

      *    Handle DB2 lock contention in CICS
           EVALUATE SQLCODE
               WHEN -911
                   MOVE 'Record is locked. Please try again.'
                       TO MSGO
                   PERFORM 1500-SEND-DATAONLY
               WHEN -904
                   MOVE 'System busy. Please try again.'
                       TO MSGO
                   PERFORM 1500-SEND-DATAONLY
           END-EVALUATE

⚠️ Production Warning: Never use EXEC SQL COMMIT in a CICS program unless you have a specific reason and understand the implications. CICS manages transaction integrity through its own SYNCPOINT mechanism. An explicit SQL COMMIT can cause the CICS unit of work and the DB2 unit of work to fall out of sync.

29.15 CICS in Modern Architectures

The claim that CICS is "legacy" often comes from people who have never looked at what CICS has become. Modern CICS (CICS TS 6.x) supports:

  • RESTful APIs: CICS can expose COBOL programs as REST endpoints with JSON request/response bodies
  • Event processing: CICS can emit events to Apache Kafka, IBM MQ, or other event brokers
  • Cloud integration: CICS applications can be managed alongside cloud-native applications through z/OS Cloud Broker
  • Liberty Profile: A Java EE/Jakarta EE application server embedded within CICS, allowing Java and COBOL to coexist
  • Node.js integration: CICS supports the CICS SDK for Node.js, allowing JavaScript applications to invoke CICS transactions
  • Docker and Kubernetes: CICS applications can be containerized and orchestrated alongside microservices

Priya Kapoor's architecture for GlobalBank's modernization strategy:

┌─────────────┐     ┌──────────────┐     ┌───────────────────┐
│ Mobile App  │────▶│ API Gateway  │────▶│ CICS Liberty      │
│ (React      │     │ (z/OS        │     │ REST endpoint     │
│  Native)    │     │  Connect)    │     │ calls LINK to     │
└─────────────┘     └──────────────┘     │ existing COBOL    │
                                         │ program ACCTINQ   │
┌─────────────┐     ┌──────────────┐     │                   │
│ Web App     │────▶│ Load         │────▶│ Same COBOL logic  │
│ (Angular)   │     │ Balancer     │     │ serves ALL        │
└─────────────┘     └──────────────┘     │ channels          │
                                         └───────────────────┘
┌─────────────┐
│ 3270        │────────────────────────▶│ Traditional CICS   │
│ Terminal    │                          │ BMS screen         │
└─────────────┘                          └───────────────────┘

Legacy != Obsolete: The COBOL programs you write in this chapter can serve users through 3270 terminals today and through REST APIs tomorrow, without changing a single line of business logic. That is the power of the CICS architecture — it separates presentation from processing. The investment you make in learning CICS programming pays dividends across every modernization strategy.

Priya Kapoor's key insight: "The COBOL programs we wrote in 1995 for 3270 terminals are now being called by React Native mobile apps. We did not plan for mobile in 1995 — obviously. But because we put business logic in separate LINK-able programs, not tangled into the BMS screen handling, we could expose them as services with zero code changes. The original developers practiced good separation of concerns without knowing they were future-proofing their code for technologies that would not exist for two decades."

The Practical Modernization Path

If you are writing a new CICS program today, Priya recommends designing it for multi-channel access from the start:

  1. Separate the business logic (validation, database access, calculations) into a program that communicates via COMMAREA or channels/containers
  2. Write the BMS screen handling in a separate program that calls the business logic via LINK
  3. Test the business logic program independently — pass test data via COMMAREA without any screen interaction
  4. When a REST API or web service interface is needed, write a thin wrapper that maps JSON to the COMMAREA format and calls the same business logic program

This architecture costs slightly more effort upfront but makes modernization incremental and risk-free — the proven business logic never changes, and new presentation layers are added alongside (not instead of) the existing 3270 interface.

29.16 Common Mistakes and How to Avoid Them

Maria Chen maintains a "CICS gotchas" document for new developers at GlobalBank. Here are the top entries:

Mistake 1: Using STOP RUN in a CICS Program

      *    WRONG — will crash the CICS region
           STOP RUN

      *    CORRECT — use RETURN or GOBACK
           EXEC CICS RETURN END-EXEC
      *    or
           GOBACK

STOP RUN terminates the entire CICS region, not just your task. Always use RETURN or GOBACK.

Mistake 2: Performing File I/O with COBOL Verbs

      *    WRONG — COBOL OPEN/READ/WRITE not allowed in CICS
           OPEN INPUT ACCT-FILE
           READ ACCT-FILE INTO WS-RECORD

      *    CORRECT — use EXEC CICS file commands
           EXEC CICS READ FILE('ACCTFILE')
               INTO(WS-RECORD)
               RIDFLD(WS-KEY)
               LENGTH(WS-LEN)
           END-EXEC

Mistake 3: Using DISPLAY for Debugging

      *    WRONG in CICS — DISPLAY goes to SYSOUT, not the terminal
           DISPLAY 'Debug: account = ' WS-ACCT-NUM

      *    CORRECT — use CICS WRITEQ TD for logging
           STRING 'Debug: account = ' WS-ACCT-NUM
               DELIMITED BY SIZE INTO WS-LOG-MSG
           END-STRING
           EXEC CICS
               WRITEQ TD QUEUE('CSMT')
                    FROM(WS-LOG-MSG)
                    LENGTH(WS-LOG-LEN)
           END-EXEC

Mistake 4: Forgetting to Initialize the Map Output Area

      *    WRONG — residual data from previous interaction
           MOVE ACCT-NAME TO ANAMEO

      *    CORRECT — initialize first, then populate
           INITIALIZE ACCTINQO
           MOVE ACCT-NAME TO ANAMEO

Mistake 5: Not Handling MAPFAIL

If the user presses Enter without modifying any field, RECEIVE MAP returns MAPFAIL. Always handle it:

           EXEC CICS
               RECEIVE MAP('ACCTINQ')
                       MAPSET('ACCTSET')
                       INTO(ACCTINQI)
                       RESP(WS-RESP)
           END-EXEC

           IF WS-RESP = DFHRESP(MAPFAIL)
               MOVE 'No input received' TO MSGO
               PERFORM 1500-SEND-DATAONLY
               EXIT PARAGRAPH
           END-IF

29.17 Debugging CICS Programs with CEDF

CEDF (CICS Execution Diagnostic Facility) is the primary debugging tool for CICS programs. It intercepts every EXEC CICS command before and after execution, displaying the command parameters and response codes.

Starting a CEDF Session

To debug a transaction on your terminal:

  1. Type CEDF and press Enter. CEDF responds with "THIS TERMINAL — EDF MODE ON"
  2. Type your transaction ID (e.g., AINQ) and press Enter
  3. CEDF intercepts at each EXEC CICS command, showing parameters before execution
  4. Press Enter to proceed to the next intercept point
  5. After execution, CEDF shows the RESP value and output data

What CEDF Shows You

At each intercept point, CEDF displays: - The CICS command (SEND MAP, RECEIVE MAP, READ FILE, etc.) - All command options with their current values - Host variable contents before execution (at the "about to execute" point) - RESP and RESP2 values after execution (at the "command completed" point) - The EIB contents

TRANSACTION: AINQ  PROGRAM: ACCTINQ  TASK: 00523  DISPLAY: 00

ABOUT TO EXECUTE COMMAND:
  SEND MAP('ACCTINQ')
       MAPSET('ACCTSET')
       FROM(X'40404040...')
       ERASE

OFFSET: X'000234'    LINE: 00125    EIBFN: X'1804'

CEDF Debugging Tips

  • Check EIBCALEN at the first intercept: If it is 0 when you expected a COMMAREA, your pseudo-conversational flow has a bug.
  • Examine host variables before SQL: At the DB2 command intercept, verify your WHERE clause parameters are what you expect.
  • Watch for MAPFAIL: If RECEIVE MAP returns MAPFAIL and you did not expect it, the user may not have modified any field.
  • Step through the EVALUATE logic: CEDF shows exactly which branch your code takes, making state-machine debugging straightforward.

🧪 Try It Yourself: If you have access to a CICS environment (IBM Z Xplore or a local CICS installation), spend time with CEDF. Start a simple transaction under CEDF and step through every EXEC CICS command. Watch the EIB values change. See what happens when you force an error (enter an invalid account number). CEDF is the CICS developer's best friend — mastering it will save you hours of debugging time throughout your career.

Remote Debugging with CEDF

You can also debug a transaction running on another terminal by specifying the terminal ID:

CEDF T001

This starts CEDF for terminal T001 on your terminal, allowing you to watch (and step through) the other user's transaction. This is invaluable for debugging problems that only occur for specific users or terminals.

Summary

This chapter has introduced you to CICS — the transaction processing system that powers the majority of online mainframe applications worldwide. You have learned to define screen layouts with BMS maps, send and receive screen data, design pseudo-conversational transactions that efficiently share CICS resources, handle errors with RESP/RESP2, and integrate DB2 access within CICS transactions.

The pseudo-conversational model is the fundamental design pattern of CICS programming. It requires a shift in thinking — instead of a program that runs continuously and waits for input, you write a program that processes one interaction and ends, preserving state in the COMMAREA for the next invocation. This model enables CICS to serve thousands of concurrent users with remarkable efficiency.

The BMS map system separates screen presentation from program logic. The same COBOL program can serve different screen layouts (for different user roles, screen sizes, or accessibility requirements) simply by changing the map definition. This separation of concerns is a design principle that modern web frameworks have adopted — CICS got there first, in the 1970s. Understanding BMS at the macro level — DFHMSD for the mapset, DFHMDI for individual maps, and DFHMDF for individual fields with their attributes and positions — gives you the vocabulary to read and modify any existing CICS screen definition you encounter in production.

The skills you have built in this chapter — pseudo-conversational design, COMMAREA state management, RESP error handling, and CICS-DB2 integration — form the foundation for everything in the next chapter, where we tackle multi-screen transactions, browse/scroll patterns, and asynchronous processing. Every production CICS application uses these fundamentals. The difference between a novice CICS programmer and an experienced one is not the complexity of the commands they use, but the thoroughness with which they handle every edge case: MAPFAIL, EIBCALEN = 0, lock contention, and residual data in output maps. The defensive habits you develop now — always checking RESP, always initializing maps, always validating EIBCALEN before touching the COMMAREA — will save you from production incidents that are far more visible and urgent than batch job failures, because in CICS, real people are watching the screen and waiting for a response.


"I've trained a dozen junior developers on CICS over the years. The ones who succeed are the ones who internalize the pseudo-conversational model — who stop thinking of the program as a continuous process and start thinking of it as a series of stateless interactions with a small state bridge between them. Once that clicks, everything else falls into place." — Maria Chen, senior developer, GlobalBank