Skip to content

Instantly share code, notes, and snippets.

@mainframed
Last active May 3, 2024 13:38
Show Gist options
  • Save mainframed/a8e94ec1e2d791eaf96d9aac981e2c10 to your computer and use it in GitHub Desktop.
Save mainframed/a8e94ec1e2d791eaf96d9aac981e2c10 to your computer and use it in GitHub Desktop.

Simple (LOL) CICS DOGE "Good Morning" screen (i.e. overly complicated hello world)

CICS is like this mystery wrapped in an enigma wrapped in a riddle. Sure you've heard about it, your company might even rely on it day to day for some really important transactions. But if you wanted to quickly learn how to write and deploy a CICS transaction (or application) its basically impossible. Sure you could grab the Murach book, which is great, but its not a 101 level tutorial, they don't even comment about how to use CEDA to install your cics program and mention compiling COBOL and assembling maps in passing.

Throughout this writeup I'm going to refer to CICS like a web server. Yes, i know its the proto-webserver. I don't care, its the roaring 20's. With that in mind, some quick terminology:

  • Transaction: 4 characters long, think of this like a URL. We'll use DOGE as our transaction.
  • Region: This is like "servers". Imagine you had 3 tomcat servers running on different ports, same deal here, except we call them regions and they have different application IDs (yay SNA).

So we'll start real simple, you need a few things:

  1. Resource Pool - You know how when you define webservers you tell it paths it can use for root. This is essentially the same thing. This is probably the thing I struggled with the most. You'll see later how to define a a program to CICS and I always wondered "hey, why arent we defining actual datasets here? All we specify are members, what is going on?" Well, wonder no more, CICS, like almost everything on a mainframe is started with a job. In the job is the "option" DFHRPL. This option specifies all the datasets CICS will look in for programs (this, bee tee dubs is called 'dataset concatenation'). Think of it like a $PATH for CICS. I've modified my startup JCL and added USER.CICSLOAD to DFHRPL, you can find the job in SDSF typically. IMPORTANT When you create your 'load library' make sure you define it as a LIBRARY not a PDS or CICS won't be able to load it.

  2. A Map - Sometimes referred to BMS or Mapset. This is assembler using a bunch of macros to make a nice looking screen in TN3270. Trust me its super fun and way better than manually writting TN3270 in hlasm. The code below is our DOGE ascii art example map (thanks /u/cmang on reddit https://www.reddit.com/r/doge/comments/21viok/such_textmode_very_ascii/):

* HLASM BMS Map
CHKACCSS TITLE 'Dogecoin BMS'
         PRINT NOGEN
DOGECN   DFHMSD TYPE=&SYSPARM,  * Either DESCT or MAP                  X
               MODE=OUT,       * One of IN/OUT/INOUT                   X
               LANG=COBOL,                                             X
               MAPATTS=(COLOR,HILIGHT),                                X
               TIOAPFX=YES,                                            X
               CTRL=FREEKB     * Free the keyboard
* Below is the MAP name, you use this in COBOL to reference the MAP
* You could have more than one map, hence size/line/column
DOGECN1  DFHMDI SIZE=(24,80),   * How big is the screen                X
               LINE=1,         * Where do we start this map            X
               COLUMN=1
*.~'~.~'~.~'~.~'~.~'~.~'~.~'~.~'~.~'~.~'~.
* Good resource
* https://www.ibm.com/support/knowledgecenter/SSGMCP_5.2.0/com.ibm.cics
*   .ts.applicationprogramming.doc/topics/dfhp473.html
* Colors: [DEFAULT], BLUE, RED, PINK, GREEN,
*         TURQUOISE, YELLOW, NEUTRAL (white)
* DOGECICS
*                               Y.                      _
*                               YiL                   .```.
*                               Yii;      WOW       .; .;;`.
*                 MUCH COIN     YY;ii._           .;`.;;;; :
*                               iiYYYYYYiiiii;;;;i` ;;::;;;;
*                           _.;YYYYYYiiiiiiYYYii  .;;.   ;;;
*                        .YYYYYYYYYYiiYYYYYYYYYYYYii;`  ;;;;
*                      .YYYYYYY$$YYiiYY$$$$iiiYYYYYY;.ii;`..
*                     :YYY$!.  TYiiYY$$$$$YYYYYYYiiYYYYiYYii.
*                     Y$MM$:   :YYYYYY$!"``"4YYYYYiiiYYYYiiYY.
*                  `. :MM$$b.,dYY$$Yii" :`   :YYYYllYiiYYYiYY
*               _.._ :`4MM$!YYYYYYYYYii,.__.diii$$YYYYYYYYYYY
*               .,._ $b`P`     "4$$$$$iiiiiiii$$$$YY$$$$$$YiY;
*                  `,.`$:       :$$$$$$$$$YYYYY$$$$$$$$$YYiiYYL
*                   "`:$$.    .;PPb$~.,.``T$$YY$$$$YYYYYYiiiYYU:
*                 ` ;$P$;;: ;;;;i$y$"!Y$$$b;$$$Y$YY$$YYYiiiYYiYY
*                   $Fi$$ .. ``:iii.`-";YYYYY$$YY$$$$$YYYiiYiYYY
*                   :Y$$rb ````  `_..;;i;YYY$YY$$$$$$$YYYYYYYiYY:
*                    :$$$$$i;;iiiiidYYYYYYYYYY$$$$$$YYYYYYYiiYYYY.
*                     `$$$$$$$YYYYYYYYYYYYY$$$$$$YYYYYYYYiiiYYYYYY
*                     .i!$$$$$$YYYYYYYYY$$$$$$YYY$$YYiiiiiiYYYYYYY
*                    :YYiii$$$$$$$YYYYYYY$$$$YY$$$$YYiiiiiYYYYYYi`
         DFHMDF POS=(1,1),      * Where to put the text                X
               LENGTH=8,                                               X
               COLOR=BLUE,                                             X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='DOGECICS'
*
         DFHMDF POS=(2,31),      * Where to put the text               X
               LENGTH=25,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='Y.                      _'
*
         DFHMDF POS=(3,31),      * Where to put the text               X
               LENGTH=27,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='YiL                   .```.'
*
         DFHMDF POS=(4,31),      * Where to put the text               X
               LENGTH=28,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='Yii;      WOW       .; .;;`.'
*
         DFHMDF POS=(5,17),      * Where to put the text               X
               LENGTH=42,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='MUCH COIN     YY;ii._           .;`.;;;; :'
*
         DFHMDF POS=(6,31),      * Where to put the text               X
               LENGTH=28,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='iiYYYYYYiiiii;;;;i` ;;::;;;;'
*
         DFHMDF POS=(7,27),      * Where to put the text               X
               LENGTH=32,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='_.;YYYYYYiiiiiiYYYii  .;;.   ;;;'
*
         DFHMDF POS=(8,24),      * Where to put the text               X
               LENGTH=35,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='.YYYYYYYYYYiiYYYYYYYYYYYYii;`  ;;;;'
*
         DFHMDF POS=(9,22),      * Where to put the text               X
               LENGTH=37,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='.YYYYYYY$$YYiiYY$$$$iiiYYYYYY;.ii;`..'
*
         DFHMDF POS=(10,21),      * Where to put the text              X
               LENGTH=39,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL=':YYY$!.  TYiiYY$$$$$YYYYYYYiiYYYYiYYii.'
*
         DFHMDF POS=(11,21),      * Where to put the text              X
               LENGTH=40,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='Y$MM$:   :YYYYYY$!"``"4YYYYYiiiYYYYiiYY.'
*
         DFHMDF POS=(12,18),      * Where to put the text              X
               LENGTH=42,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='`. :MM$$b.,dYY$$Yii" :`   :YYYYllYiiYYYiYY'
*
         DFHMDF POS=(13,15),      * Where to put the text              X
               LENGTH=45,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='_.._ :`4MM$!YYYYYYYYYii,.__.diii$$YYYYYYYYYYY'
*
         DFHMDF POS=(14,15),      * Where to put the text              X
               LENGTH=46,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='.,._ $b`P`     "4$$$$$iiiiiiii$$$$YY$$$$$$YiY;'
*
         DFHMDF POS=(15,18),      * Where to put the text              X
               LENGTH=44,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='`,.`$:       :$$$$$$$$$YYYYY$$$$$$$$$YYiiYYL'
*
         DFHMDF POS=(16,19),      * Where to put the text              X
               LENGTH=44,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='"`:$$.    .;PPb$~.,.``T$$YY$$$$YYYYYYiiiYYU:'
*
         DFHMDF POS=(17,17),      * Where to put the text              X
               LENGTH=46,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='` ;$P$;;: ;;;;i$y$"!Y$$$b;$$$Y$YY$$YYYiiiYYiYY'
*
         DFHMDF POS=(18,19),      * Where to put the text              X
               LENGTH=44,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='$Fi$$ .. ``:iii.`-";YYYYY$$YY$$$$$YYYiiYiYYY'
*
         DFHMDF POS=(19,19),      * Where to put the text              X
               LENGTH=45,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL=':Y$$rb ````  `_..;;i;YYY$YY$$$$$$$YYYYYYYiYY:'
*
         DFHMDF POS=(20,20),      * Where to put the text              X
               LENGTH=45,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL=':$$$$$i;;iiiiidYYYYYYYYYY$$$$$$YYYYYYYiiYYYY.'
*
         DFHMDF POS=(21,21),      * Where to put the text              X
               LENGTH=44,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='`$$$$$$$YYYYYYYYYYYYY$$$$$$YYYYYYYYiiiYYYYYY'
*
         DFHMDF POS=(22,21),      * Where to put the text              X
               LENGTH=44,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL='.i!$$$$$$YYYYYYYYY$$$$$$YYY$$YYiiiiiiYYYYYYY'
*
         DFHMDF POS=(23,20),      * Where to put the text              X
               LENGTH=45,                                              X
               COLOR=YELLOW,                                           X
               ATTRB=(NORM,PROT),                                      X
               INITIAL=':YYiii$$$$$$$YYYYYYY$$$$YY$$$$YYiiiiiYYYYYYi`'
*
         DFHMSD TYPE=FINAL
         END

Imagine having to make that yourself, there's some dirty python to make the DFHMDF macro calls with a given ascii art in the bonus section below.

To assemble this HLASM we need JCL. Thankfully CICS comes with a procedure called DFHMAPS. On ADCD (zPDT) systems this is typically in DFH###.CICS.SDFHPROC(DFHMAPS) where ### is your CICS version (for example 5.2 would be DFH520.CICS.SDFHPROC).

//COBCOMP  JOB 'Compile COBOL',NOTIFY=&SYSUID
//IBMLIB   JCLLIB ORDER=DFH520.CICS.SDFHPROC
//* make sure that the MAPLIB is pointed
//* to your CICS loadlib. If you are not sure about this
//* you can go to The JESJCL of the job for your CICS
//* region in Spool and check for DFHRPL dd statement.
//CPLSTP   EXEC DFHMAPS,MAPLIB='USER.CICSLOAD',
//         INDEX='DFH520.CICS',
//         DSCTLIB='DOGE.CICS',
//         MAPNAME='DOGEGM'
//* MAPLIB is where the compiled CICS BMS MAP is stored!
//* The DD below is the source of the program to assemble!
//COPY.SYSUT1  DD DSN=DOGE.CICS(DOGEGMSC),DISP=SHR

Some quick explanation here:

  • JCLLIB: Thats the PDS where DFHMAPS is stored, this tells JES where to look
  • MAPLIB: Where we want our compiled code to go
  • INDEX: Where CICS libraries and stuff can be found
  • DSCTLIB: Where to put the data definitions we'll use in COBOL to refer to the map (e.g. COPY)
  • MAPNAME: The filename of both the DSECT and assembled binary
  1. COBOL - Your actual COBOL program that does something (doesn't need to be COBOL, could be C, JAVA, PL/I, HLASM). Here's a quick example that loads our map:
       IDENTIFICATION DIVISION.
       PROGRAM-ID.   DOGE01.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY DOGEGM.

       PROCEDURE DIVISION.
       00000-MAIN.

           EXEC CICS
                SEND MAP('DOGECN1')
                     MAPSET('DOGEGM')
                     ERASE
           END-EXEC.

           EXEC CICS RETURN END-EXEC.

       00000-EXIT.
           GOBACK.

Of note, MAPSET must match what you set MAPNAME to when you assembled the map.

And of course you know you'll need JCL, same as above we use DFHYITVL instead of DFHMAPS.

//COBCOMP  JOB 'Compile COBOL',NOTIFY=&SYSUID
//IBMLIB   JCLLIB ORDER=DFH520.CICS.SDFHPROC
//* make sure that the PROGLIB in the DFHYITVL is pointed
//* to your CICS loadlib. If you are not sure about this
//* you can go to The JESJCL of the job for your CICS
//* region in Spool and check for DFHRPL dd statement.
//CPLSTP   EXEC DFHYITVL,PROGLIB='USER.CICSLOAD',
//         INDEX='DFH520.CICS',
//         DSCTLIB='USER.CICSLOAD',
//         AD370HLQ='IGY520',
//         LE370HLQ='CEE'
//* PROGLIB is where the compiled CICS program is stored!
//* The DD below is the source of the program to compile!
//TRN.SYSIN  DD DSN=DOGE.CICS(HLWRLDMP),DISP=SHR
//* The DD below is where COBOL COPY files are stored
//* COPY in COBOL is like IMPORT in python
//* So this tells the compiler where to find them
//COB.SYSLIB DD
//           DD DSN=DOGE.CICS,DISP=SHR
//* In PROGLIB there could be a member called DOGE
//* The (R) means 'replace' if the member already exists
//LKED.SYSIN DD *
   NAME DOGE(R)
//

Some quick explanation here:

  • JCLLIB: Thats the PDS where DFHYITVL is stored, this tells JES where to look
  • PROGLIB: Where we want our compiled code to go
  • INDEX: Where CICS libraries and stuff can be found
  • AD370HLQ: The HLQ to find your COBOL compiler
  • LE370HLQ: The HLQ to find the common environment libraries

Installing our code in CICS

With that all done you now need to install it in CICS.

To "install" a cics program think of again it like a web server.

We need a transaction (like a url) to connect the terminal to. That transaction needs to have some kind of program behind it (think like php). We also need a map file (this would be like an html template).

Now, cics isn't as smart as a modern webserver and doesn't know about files or things like that. It keeps everything in 'tables'. So we DEFINE a program using CEDA. We add it to a group. A group is like a folder for this application, so everything we want should go in one group.

CEDA DEFINE PROG(DOGE) GROUP(DOGECOIN)

Next, we need to link the transaction to the program, CICS now knows there's a compiled exec called 'DOGE' in a dataset defined by DFHRPL (this is a section in the CICS job that is running, you can look it up in SDSF under JESJCL for that CICS regions job). In our example we've added USER.CICSLOAD to DFHRPL. Which means there's a file called 'DOGE' in USER.CICSLOAD: USER.CICSLOAD(DOGE) is the program we just defined. Think of DFHRPL like the path in a web server setup.

Next we need to tell CICS how to find our 3270 template. This is the assembled HLASM program from above. It's the same command as above:

CEDA DEFINE MAPSET(DOGEGM) GROUP(DOGECOIN)

You just need to make sure that the MAPSET, the file and the COPY in your COBOL is the same. So here we have a cobol program with a copy statement that says COPY DOGEGM., this copies the data definitions for our map in COBOL, we also have the statement MAPSET(DOGEGM) in COBOL which means 'there's a mapset in CICS defined with a name of DOGEGM use that', we have a file USER.CICSLOAD(DOGEGM) and we have the CEDA above with MAPSET(DOGEGM). Easy peasy.

With those defined we now need to define a transaction that will use the program USER.CICSLOAD(DOGE) when we call it in CICS.

CEDA DEFINE TRANSACTION(DOGE) GROUP(DOGECOIN) PROGRAM(DOGE)

I like to think of groups like folders. In the DOGECOIN group we have the following:

Group: DOGECOIN:  
    Transaction: DOGE  
        Program: DOGE
        Mapset: DOGEGM

If this were a web server I would break it down like:

/DOGEGOIN/DOGE <---- redirects to DOGE.php  
/DOGECOIN/DOGE.php  
/DOGECOIN/DOGEGM.html  

Except, this is all just setup, like staging. We have a group with a program, transaction and map, but we haven't enabled them. Do do that we can install the whole group:

CEDA INSTALL GROUP(DOGECOIN)

At this point you can hit F3 and type DOGE to access your transaction!

Making Changes

All of this only works for the first time you compile a program and install it. What if, after CICS is booted you need to incorporate your changes after recompiling the map or COBOL? Why you can use CEMT:

CEMT SET PROGRAM(DOGE) NEWCOPY

Long story short, CICS keeps a copy of the program in memory, so you need to tell it to reload the code from disc, which is what the CEMT command is telling CICS to do. It's sorta like clearing the server cache and forcing it to get a new copy from disk.

But what if you change the mapset? You'd think CEMT would have a command like SET MAPSET(DOGEGM) NEWCOPY but lol nope, it treats your MAPSET like a program, so if you make changes to a mapset all you need to do is:

CEMT SET PROGRAM(DOGEGM) NEWCOPY

There's a lot more to writting CICS programs but this should be enough to get you started!

BONUS

Python code to convert ascii art to CICS macro calls:

#!/usr/bin/env python3

# -------1---------2---------3---------4---------5---------6---------7---------8

doge = '''DOGECICS
                              Y.                      _
                              YiL                   .```.
                              Yii;      WOW       .; .;;`.
                MUCH COIN     YY;ii._           .;`.;;;; :
                              iiYYYYYYiiiii;;;;i` ;;::;;;;
                          _.;YYYYYYiiiiiiYYYii  .;;.   ;;;
                       .YYYYYYYYYYiiYYYYYYYYYYYYii;`  ;;;;
                     .YYYYYYY$$YYiiYY$$$$iiiYYYYYY;.ii;`..
                    :YYY$!.  TYiiYY$$$$$YYYYYYYiiYYYYiYYii.
                    Y$MM$:   :YYYYYY$!"``"4YYYYYiiiYYYYiiYY.
                 `. :MM$$b.,dYY$$Yii" :`   :YYYYllYiiYYYiYY
              _.._ :`4MM$!YYYYYYYYYii,.__.diii$$YYYYYYYYYYY
              .,._ $b`P`     "4$$$$$iiiiiiii$$$$YY$$$$$$YiY;
                 `,.`$:       :$$$$$$$$$YYYYY$$$$$$$$$YYiiYYL
                  "`:$$.    .;PPb$~.,.``T$$YY$$$$YYYYYYiiiYYU:
                ` ;$P$;;: ;;;;i$y$"!Y$$$b;$$$Y$YY$$YYYiiiYYiYY
                  $Fi$$ .. ``:iii.`-";YYYYY$$YY$$$$$YYYiiYiYYY
                  :Y$$rb ````  `_..;;i;YYY$YY$$$$$$$YYYYYYYiYY:
                   :$$$$$i;;iiiiidYYYYYYYYYY$$$$$$YYYYYYYiiYYYY.
                    `$$$$$$$YYYYYYYYYYYYY$$$$$$YYYYYYYYiiiYYYYYY
                    .i!$$$$$$YYYYYYYYY$$$$$$YYY$$YYiiiiiiYYYYYYY
                   :YYiii$$$$$$$YYYYYYY$$$$YY$$$$YYiiiiiYYYYYYi`
'''


hlasm = '''
         DFHMDF POS=({row},{column}),      * Where to put the text             X
               LENGTH={len},                                             X
               COLOR=YELLOW,                                          X
               ATTRB=(NORM,PROT),                                     X
               INITIAL='{text}'
*'''


for i in doge.splitlines():
    print("* {}".format(i))

row = 1
max_len = 46
for line in doge.splitlines():
    column1 = len(line) - len(line.lstrip()) + 1
    f_row = format(row, '02d')
    f_col = format(column1, '02d')
    text = line[column1-1:]
    f_len = format(len(text), '02d')
    if len(text) > 46:
        print("RUH ROH")
        print("MANUAL INTERVENTION REQUIRED FOR ROW {} COLUMN {}".format(f_row, f_col))
        print(text)
    else:
        temp = list(hlasm.format(row=f_row,column=f_col,len=f_len,text=text))
        new_hlasm = "".join(temp)
        print(new_hlasm)
    row = row + 1
@DougieLawson
Copy link

Do you have a similar thing for a cics+db2?

There's nothing to do with Db2, so the CICS one will just work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment