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:
-
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 addedUSER.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. -
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
- 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
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!
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!
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
There's nothing to do with Db2, so the CICS one will just work.