COBOL, CICS & JCL
Decompile IBM z/OS and Micro Focus COBOL, byte for byte.
Copybooks expanded, storage laid out to the byte, CICS conversations modeled as state, JCL streams turned into dataset lineage. Then rules, not guesses.
41 WORKING-STORAGE SECTION.42 01 WS-LOAN.43 COPY LNREC REPLACING ==:PFX:== BY ==WS==.
| field | off | len | why |
|---|---|---|---|
| WS-LOAN-IDX(10) | 0 | 10 | char(10), KSDS key |
| WS-PRIN-BALS9(11)V99 COMP-3 | 10 | 7 | 13 digits: (13+1)/2 |
| WS-ANN-RATESV9(6) COMP-3 | 17 | 4 | 6 digits: (6+1)/2, up |
| WS-PMT-CNTS9(4) COMP | 21 | 2 | halfword, 0 to 60 |
| WS-PMT-HISTOCCURS 0 TO 60 | 23 | 13 × n | 8 + 5 per entry |
record length 23 to 803 bytes · depends on WS-PMT-CNT
Dependency graph explorer
Every job, transaction, program and dataset, connected.
Click a node to see what it uses and what uses it. Switch to downstream impact to see everything a copybook change touches, or upstream callers to find which jobs and transactions reach a program. Dynamic CALLs show every resolved candidate.
Complete mainframe syntax
The constructs generic tools skip are the ones that hold the rules.
01 · COPYBOOKS
Expansion before parsing, provenance after.
COPY statements are expanded in compiler order, including nested copybooks and pseudo-text REPLACING such as ==:PFX:== BY ==WS==. Each expanded line remembers its member and original line, so a rule found inside LNCALC cites the copybook, and lists every program that includes it.
Procedure-division copybooks are common in older shops. PYD ENTERPRISES treats shared logic as one rule with many call sites, not as duplicate rules.
1 * LNREC: loan master record, 803 bytes max2 05 :PFX:-LOAN-ID PIC X(10).3 05 :PFX:-PRIN-BAL PIC S9(11)V99 COMP-3.4 05 :PFX:-ANN-RATE PIC SV9(6) COMP-3.5 05 :PFX:-PMT-CNT PIC S9(4) COMP.6 05 :PFX:-PMT-HIST OCCURS 0 TO 60 TIMES7 DEPENDING ON :PFX:-PMT-CNT.8 10 :PFX:-PMT-DATE PIC 9(8).9 10 :PFX:-PMT-AMT PIC S9(7)V99 COMP-3.
1 05 LN-STATUS-AREA PIC X(12).2 05 LN-ACTIVE REDEFINES LN-STATUS-AREA.3 10 LN-NEXT-DUE-DT PIC 9(8).4 10 LN-DAYS-PAST-DUE PIC S9(5) COMP-3.5 10 FILLER PIC X(01).6 05 LN-CLOSED REDEFINES LN-STATUS-AREA.7 10 LN-CLOSE-DT PIC 9(8).8 10 LN-CLOSE-RSN PIC X(02).9 88 LN-PAID-OFF VALUE 'PO'.10 88 LN-CHARGED-OFF VALUE 'CO'.11 10 FILLER PIC X(02).
02 · REDEFINES, ODO & COMP-3
Overlays resolved to offsets, not names.
LN-ACTIVE and LN-CLOSED share the same 12 bytes. LN-DAYS-PAST-DUE is S9(5) COMP-3, 3 bytes at offset 8, overlaying the same storage as LN-CLOSE-RSN and the first byte of the trailing FILLER.
Dataflow runs on bytes, so a write through one name is seen by reads through the other. PYD ENTERPRISES finds the discriminator that selects the variant and emits a JSON Schema oneOf; if no discriminator exists, the overlay is flagged.
03 · CICS & BMS
Pseudo-conversations become explicit state.
First entry (EIBCALEN = 0) sends the map and returns with TRANSID('LNIQ') and a COMMAREA. The next task receives the map and LINKs to the calculator. PYD ENTERPRISES models both tasks as states of one conversation.
BMS map fields (LOANIDI, ACCRO) become request and response properties; the LINKed COMMAREA becomes an OpenAPI operation with its own schema.
1 PROCEDURE DIVISION.2 IF EIBCALEN = 03 MOVE LOW-VALUES TO LNINQMO4 EXEC CICS SEND MAP('LNINQM') MAPSET('LNINQS')5 FROM(LNINQMO) ERASE6 END-EXEC7 EXEC CICS RETURN TRANSID('LNIQ')8 COMMAREA(WS-COMMAREA)9 LENGTH(LENGTH OF WS-COMMAREA)10 END-EXEC11 END-IF12 MOVE DFHCOMMAREA TO WS-COMMAREA13 EXEC CICS RECEIVE MAP('LNINQM') MAPSET('LNINQS')14 INTO(LNINQMI) RESP(WS-RESP)15 END-EXEC16 MOVE LOANIDI TO CA-LOAN-ID17 EXEC CICS LINK PROGRAM('LNACCR03')18 COMMAREA(WS-ACCR-AREA)19 END-EXEC
1//LNDLY01 JOB (LN01),'DAILY ACCRUAL',CLASS=A,MSGCLASS=X2//EXTRACT EXEC PGM=LNEXTR013//LNMAST DD DSN=LOANS.LN.MASTER,DISP=SHR4//ACCRWK DD DSN=LOANS.ACCR.WORK,DISP=OLD5//ACCRUE EXEC PGM=LNACCR02,COND=(4,LT,EXTRACT)6//ACCRWK DD DSN=LOANS.ACCR.WORK,DISP=SHR7//LNMAST DD DSN=LOANS.LN.MASTER,DISP=SHR8//LNHIST DD DSN=LOANS.ACCR.HIST(+1),DISP=(NEW,CATLG,DELETE),9// SPACE=(CYL,(50,10)),DCB=(RECFM=FB,LRECL=120)10//GLPOST EXEC PGM=LNGL01,COND=((8,LE,EXTRACT),(4,LT,ACCRUE))11//LNHIST DD DSN=LOANS.ACCR.HIST(+1),DISP=SHR12//GLOUT DD DSN=GL.POSTING.LOANS,DISP=MOD
04 · JCL & DATASET LINEAGE
COND codes read the way z/OS reads them.
A COND test bypasses the step when it is true. COND=(4,LT,EXTRACT) skips ACCRUE when 4 is less than EXTRACT's return code, so ACCRUE runs only when EXTRACT ends with RC 4 or lower. GLPOST needs EXTRACT below 8 and ACCRUE at 4 or lower.
DD statements bind each program's files to datasets, including GDG generations, so lineage runs from VSAM master through the work file to the general ledger posting file.
Dead code & unreachable paragraphs
Prune before you extract.
Thirty years of change requests leave code nobody runs. Extracting rules from it inflates scope and misleads the build team.
- 01
Build the full CFG
PERFORM THRU ranges, fall-through between paragraphs, GO TO DEPENDING ON targets and every ALTER that can change a GO TO are modeled as edges, so reachability is computed on the real graph rather than on paragraph names.
- 02
Seed from real entry points
Reachability starts at JCL EXEC PGM steps, CICS transactions in your CSD extract, and CALL and LINK targets. A program nobody invokes is reported as an orphan, not deleted.
- 03
Fold constant conditions
Switches set once from a JCL PARM or a constant VALUE clause are propagated. A branch on an 88-level that can never be true under the supplied PARMs is marked conditionally dead, with the PARM that kills it.
- 04
Classify, never delete
Unreachable paragraphs, dead stores and redundant MOVEs are excluded from rule extraction and listed with line ranges. Your source is never modified.
- dead
9000-OLD-RATE-TABLE unreachable
LNACCR02.cbl:3310-3488
No PERFORM, GO TO or fall-through reaches it since CR 3120 (1997).
- cond
IF WS-RUN-MODE = 'Q' never true
LNEXTR01.cbl:402-517
WS-RUN-MODE set only from PARM; jobs pass 'D' and 'M'.
- alter
ALTER 1500-DISPATCH TO PROCEED TO 1580-RETRY
LNGL01.cbl:211
Modeled as a two-state switch; both targets kept reachable.
- dead
Dead store to WS-TEMP-BAL
LNACCR02.cbl:1206
Overwritten at 1219 before any read.
- orphan
Program LNPURG02 has no callers
LNPURG02.cbl
Not in any JCL, CSD or CALL in this scan; confirm with operations.
Source is never modified. Exclusions are reviewable.
“It resolved COMP-3 fields and nested REDEFINES that every generic AI tool we tried got wrong.”
Dialect coverage
Two COBOL front ends, one symbol graph.
| Construct | IBM Enterprise COBOL (z/OS) | Micro Focus COBOL |
|---|---|---|
| COPY, COPY REPLACING, nested copybooks | Full | Full |
| REDEFINES, RENAMES (66), OCCURS DEPENDING ON | Full | Full |
| COMP, COMP-3, COMP-5, DISPLAY SIGN SEPARATE | Full | Full |
| PERFORM THRU, GO TO DEPENDING ON, ALTER | Full | Full |
| EXEC CICS (incl. BMS SEND/RECEIVE MAP, LINK, XCTL) | Full | Full (with CICS option) |
| EXEC SQL (DB2 static and dynamic) | Full | Full |
| IMS DL/I calls (CBLTDLI, PCB masks) | Full | Not applicable |
| JCL, cataloged PROCs, symbolic substitution | Full | Via JCL import |
| Micro Focus directives ($SET), Net Express extensions | Not applicable | Full |
Patterns these constructs create, and how they map to modern designs, are documented in the legacy anti-patterns catalog.
FAQ
Mainframe questions.
Migrating off COBOL? Read the COBOL to Spring Boot guide.
How does PYD ENTERPRISES handle copybooks with the same name in different libraries?
You supply the library concatenation order your compile step uses (Endevor processor groups or the SYSLIB DD), and PYD ENTERPRISES resolves each COPY to the first library containing the member, exactly as the compiler does. Every expanded line keeps a reference to the copybook member and line it came from.
Does it compute storage offsets the way the compiler does?
Yes. Lengths follow the usage rules: DISPLAY one byte per digit, COMP-3 (digits + 1) / 2 rounded up, binary halfword or fullword by digit count. OCCURS DEPENDING ON tables are modeled with their minimum and maximum size, and SYNCHRONIZED slack bytes are inserted when the clause is present.
Can it follow a dynamic CALL through a variable?
When the variable is set from literals in the same program, PYD ENTERPRISES resolves the full candidate set and records the condition that picks each one. When the value comes from a file or a table, the CALL is listed as unresolved with whatever candidates exist, and rules downstream of it are flagged for review.
How are CICS pseudo-conversational programs modeled?
As a state machine across task boundaries. EIBCALEN checks, RETURN TRANSID with a COMMAREA and the fields carried in it become explicit states and transitions, so the extracted interface describes the whole conversation, not only one task.
What about Micro Focus specific syntax?
The Micro Focus front end honors $SET directives such as DIALECT and SOURCEFORMAT, free-format source and Net Express extensions. Choose the dialect per source path in scan.yaml.
Point it at your copybook library.
Run PYD ENTERPRISES on 100,000 lines of COBOL or Java in your private VPC and review OpenAPI specs and DMN tables tomorrow.