> "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."
In This Chapter
- 29.1 What Is CICS and Why Does It Matter?
- 29.2 EXEC CICS ... END-EXEC: The CICS Programming Interface
- 29.3 The Execute Interface Block (EIB)
- 29.4 BMS Maps: Defining Screen Layouts
- 29.5 SEND MAP and RECEIVE MAP: Screen I/O
- 29.6 Pseudo-Conversational Design: The Heart of CICS Programming
- 29.7 CICS Error Handling: RESP and RESP2
- 29.8 HANDLE AID: Traditional Key Handling
- 29.9 Basic CICS Commands
- 29.10 The Transaction Lifecycle
- 29.11 GlobalBank Scenario: Online Account Inquiry
- 29.12 MedClaim Scenario: Claim Status Lookup
- 29.13 CICS Resource Definitions
- 29.14 CICS and DB2: Working Together
- 29.15 CICS in Modern Architectures
- 29.16 Common Mistakes and How to Avoid Them
- 29.17 Debugging CICS Programs with CEDF
- Summary
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
- Every CICS command starts with
EXEC CICSand ends withEND-EXEC - Command options use keyword-value pairs:
OPTION(value) - Host variables are referenced by name without colons (unlike DB2)
- Commands must be in Area B (columns 12-72)
- 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:
- Physical map — a load module that controls screen formatting (colors, positioning, attributes)
- 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
Lvalue 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:
-
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.
-
Change detection: After RECEIVE MAP, you can check if a field was modified by examining its length field (
Lsuffix). 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:
-
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.
-
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.
-
Group related fields. Account number and name together, financial fields together, dates together. Use blank rows to separate groups.
-
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.
-
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.
-
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 againCOMMAREA(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-EXECwithout 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: Call and Return
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
GOTObetween programs — one-way, no return. LINK is like aPERFORM— 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:
-
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.
-
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 SYNCPOINTrather thanEXEC SQL COMMITto keep the CICS and DB2 units of work synchronized. -
Use singleton SELECTs when possible. A cursor open/fetch/close cycle requires three DB2 calls. A direct
SELECT INTOrequires one. For primary-key lookups in CICS transactions, always useSELECT INTO. -
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.
-
Use WITH UR for read-only inquiries. If the transaction is purely read-only (inquiry, not update), specify
WITH URon the SELECT to avoid acquiring any locks. This prevents the online inquiry from being blocked by batch programs and vice versa. -
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:
- Separate the business logic (validation, database access, calculations) into a program that communicates via COMMAREA or channels/containers
- Write the BMS screen handling in a separate program that calls the business logic via LINK
- Test the business logic program independently — pass test data via COMMAREA without any screen interaction
- 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:
- Type
CEDFand press Enter. CEDF responds with "THIS TERMINAL — EDF MODE ON" - Type your transaction ID (e.g.,
AINQ) and press Enter - CEDF intercepts at each EXEC CICS command, showing parameters before execution
- Press Enter to proceed to the next intercept point
- 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