KICKS

A transaction processing system for CMS & TSO

KICKS KooKbooK recipe

Designing and implementing simple BMS maps

At a low level, KICKS is capable of reading from and writing to 3270 terminals using raw 3270 data streams. In a command level COBOL program this is done using

EXEC KICKS RECEIVE … END-EXEC
EXEC KICKS SEND    … END-EXEC

sequences. This is frequently used for simple operations like clearing the screen or writing an ‘all done’ message of some kind where the burden of preparing the necessary 3270 data stream is light.

When the input or output is more complex it is much easier to program at a higher level - so KICKS is also capable of reading from and writing to 3270 terminals using BMS (basic mapping support) screen definitions. In a command level COBOL program this is done using

EXEC KICKS RECEIVE MAP … END-EXEC
EXEC KICKS SEND    MAP … END-EXEC

sequences. What BMS does is relieve you of constantly recoding (and testing!) the building and dissecting of 3270 data streams. A couple other advantages: (a) the screen layouts (maps) are defined in a standardized, high level way that is independent of the programs that use the maps; (b) since BMS map definition and its associated programming api is part of a worldwide standard that’s been around over 30 years, it is much easier to understand and modify a program that uses maps than a program that uses its own scheme for manipulating 3270 data streams.

So let’s try a couple of simple maps, starting with one that just writes “hello world” in the middle of a blank screen. Assuming that’s a 24x80 screen, middle means centered on line 12, column 40, so the text should start at column 34. This map source could look like this:

TESTMSD KIKMSD MODE=OUT, CTRL=FREEKB
TESTMAP KIKMDI SIZE=(24,80)
KIKMDF POS=(12,34),LENGTH=11,INITIAL='hello world'
KIKMSD TYPE=FINAL
END

The first line (KIKMSD macro) takes care of releasing the keyboard after the screen is displayed and names the MAPSET. The second line tells BMS you are using a 24x80 screen and names the MAP. The third line says what you want printed and where you want it. Notice that you must specify the LENGTH even though it’s implied in the INITIAL argument. This is typical; the map generator isn’t smart enough to figure out such things. The last two lines are the common ending for all BMS assemblies.

You sometimes see references to the KIKMSD, KIKMDI, and KIKMDF statements as macros and the map generation process as assemblies because IBM’s CICS uses the assembler to process its BMS map generation statements (DFHMSD, DFHMDI, DFHMDF), but in KICKS the statements are not macros, just control statements, and they are processed by the KIKMG utility instead of the assembler. In TSO the KIKMAPS proc takes care of almost all the setup for running the KIKMG utility. So to process your “TESTMAP world” map you would run a job like the following (but your JCL may need to vary slightly depending on how KICKS was installed on your machine).

//MAP JOB
//JOBPROC DD DSN=USERID.KICKSSYS.V1R5M0.PROCLIB,DISP=SHR << Legacy MVS
//JOBPROC JCLLIB ORDER=(USERID.KICKSSYS.V1R5M0.PROCLIBZ) << Z/OS
// EXEC KIKMAPS,MAPNAME=TESTMSD
//COPY.SYSUT1 DD *
... map source as above ...
/*

Select one or the other of the two JOBPROC cards, depending on your operating system. USERID is your TSO logon id.  When this job finishes there will be a binary version of the map installed as member TESTMSD in USERID.KICKS.V1R5M0.KIKRPL and a COBOL copybook version as member TESTMSD in USERID.KICKS.V1R5M0.COBCOPY.

In CMS the KIKMG exec takes care of almost all the setup for running the KIKMG utility. So to process your “hello world” map you store it in a file (TESTMSD MAPSRC A) then run the exec like this:

KIKMG TESTMSD

When this exec finishes there will be a binary version of the map installed as member TESTMSD in KIKURPL TXTLIB and a COBOL copybook version as member TESTMSD in KIKCOUSR MACLIB.

The binary version is used by KICKS to position the output on the screen.

The COBOL copybook version is included into COBOL programs that use the map, as in the 5th line below.

ID DIVISION.
PROGRAM-ID. TESTCOB.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY TESTMSD.<<
PROCEDURE DIVISION.
EXEC KICKS
SEND MAP('TESTMAP') MAPSET('TESTMSD') ERASE
END-EXEC.
EXEC KICKS RETURN END-EXEC.

Of course to compile this in TSO you use the KICKS compile proc (K2KCOBCL in legacy MVS, or KIKCB2CL in Z/OS). You would run a job like the following (but your JCL may need to vary slightly depending on how KICKS was installed on your machine).

//PROGRAM JOB
//JOBPROC DD DSN=USERID.KICKSSYS.V1R5M0.PROCLIB,DISP=SHR << Legacy MVS
//JOBPROC JCLLIB ORDER=(USERID.KICKSSYS.V1R5M0.PROCLIBZ) << Z/OS
// EXEC K2KCOBCL (or KIKCB2CL in Z/OS)
//COPY.SYSUT1 DD *
... COBOL source as above ...
/*
//LKED.SYSIN DD *
INCLUDE SKIKLOAD(KIKCOBGL)
ENTRY TESTCOB
NAME TESTCOB(R)

To compile this in CMS you store it in a file (TESTMAP COBOL A) then run the KICKS compile exec (K2KCOBCL in VM/370, or KIKCB2CL in Z/VM) like this

K2KCOBCL TESTCOB<< VM/370

or

KIKCB2CL TESTCOB<< Z/VM

Note that if instead of a compile you see "COBOL COMPILER LIBRARY NOT FOUND" that means you need to access that library before you run the compile exec. Type "GETCOB" to do so, then re-run the compile exec.

Before you can run the new map and program you have to tell KICKS about them. This is done by adding program entries for the mapset and program to the PPT, and by adding a transaction code to the PCT (so there is some way to run it!). These entries are

KIKPPT TYPE=ENTRY,PROGRAM=TESTMSD,USAGE=MAP
KIKPPT TYPE=ENTRY,PROGRAM=TESTCOB,PGMLANG=CMDLVL
*
KIKPCT TYPE=ENTRY,TRANSID=TCOB,PROGRAM=TESTCOB

Note these entries are already present in the tables that ship with KICKS.  These entries are used by the KICKS api test suite and many KooKbooK recipes.  They are of course also available for your own program use while testing - but take care, only one set of programs and maps can be using them at any given time!

When the transaction is run by starting KICKS and typing TCOB on a clear screen, the result is

               
  hello world  
               

So much for static fields, now let’s add one line to both the map and the program to get a display of time of day on the TESTMAP screen.

TESTMSD KIKMSD MODE=OUT, CTRL=FREEKB
TESTMAP KIKMDI SIZE=(24,80)
KIKMDF POS=(12,34),LENGTH=11,INITIAL='hello world'
CTIME KIKMDF POS=(14,37),LENGTH=6 <<
KIKMSD TYPE=FINAL
END

and

ID DIVISION.
PROGRAM-ID. TESTCOB.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY TESTMSD.
PROCEDURE DIVISION.
MOVE TIME-OF-DAY TO CTIMEO. <<
EXEC KICKS
SEND MAP('TESTMAP') MAPSET('TESTMSD') ERASE
END-EXEC.
EXEC KICKS RETURN END-EXEC.

Notice that the name of the OUTPUT field in COBOL was the map field name with an ‘O’ appended; input fields have ‘I’s appended; if you want to change a value on the screen you have to provide a name for the field when you define the map! When you run this transaction again (after recompiling both the map and the program, in that order) the screen looks as below.

               
  hello world  
               
     075223    
               

Let’s get a formatted display of the time. A quick COBOL solution would be to move time-of-day to a working-storage variable defined with an appropriate PICTURE clause, maybe do a little light editing with EXAMINE/INSPECt, then move that field to CTIMEO as above.  BMS provides assistance for this in the form of the PICOUT argument of the KIKMDF map generator statement.

TESTMSD KIKMSD MODE=OUT,CTRL=FREEKB
TESTMAP KIKMDI SIZE=(24,80)
KIKMDF POS=(12,34),LENGTH=11,INITIAL='hello world'
CTIME KIKMDF POS=(14,37),LENGTH=8,PICOUT='99B99B99' <<
KIKMSD TYPE=FINAL
END

As before LENGTH must be specified even though it appears it could be derived from PICOUT. Also note there is no syntax check of the picture specification in the map generation process. Any syntax errors are flagged when the resulting COBOL copybook is compiled with a program.

ID DIVISION.
PROGRAM-ID. TESTCOB.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY TESTMSD.
PROCEDURE DIVISION.
MOVE TIME-OF-DAY TO CTIMEO. <<
<CB2> <<
INSPECT CTIMEO REPLACING ALL ' ' BY ':'. <<
</CB2> <<
<NCB2> <<
EXAMINE CTIMEO REPLACING ALL ' ' BY ':'. <<
</NCB2> <<
EXEC KICKS
SEND MAP('TESTMAP') MAPSET('TESTMSD') ERASE
END-EXEC.
EXEC KICKS RETURN END-EXEC.

What it the world is that strange angle bracket stuff in the A-margin?

As you know legacy and modern COBOL have a few incompatibilities. One is that the old EXAMINE syntax has been replaced by the INSPECT syntax. In order to allow the proper syntax to be passed to the compiler, the preprocessor honors this angle bracket "markup"; <CB2> beginning the marked block, </CB2> ending the marked block;  to tell it what code should be generated only if it is processing "COBOL2" syntax  Similarly, it honors angle bracket "markup"; <NCB2> beginning the marked block, </NCB2> ending the marked block;  to tell it what code should be generated only if it is not processing "COBOL2" syntax  So the above code, processed by the K2KCOBCL exec/proc for legacy COBOL will not use the <CB2></CB2> block with the INSPECT verb, but instead use the <NCB2></NCB2> block with the EXAMINE verb. And vice versa when processed by the KIKCB2CL exec/proc for modern COBOL.

Since you will likely use one compiler or the other, but not both, you will probably seldom use these preprocessor markup tags in your own programs. But other tags such as <NOP>, <REM>, <NOKICKS>, etc may be of use. They are documented in the Compiling COBOL prgms part of the User's Guide.

After compiling with the appropriate proc/exec, this time when you run the transaction you see

               
  hello world  
               
    07:58:53   
               

Output only maps can be useful at times, and apparently input only maps (specified by MODE=IN on the KIKMSD statement) are useful too (never used one personally), but most maps are used for both input and output (specified by MODE=INOUT on the KIKMSD statement). INOUT maps are usually SENT with blank or default values in named fields and the user modified input values are RECEIVED. Let’s try that with the TESTMAP map.

TESTMSD KIKMSD MODE=INOUT,CTRL=(FREEKB,FRSET) <<
TESTMAP KIKMDI SIZE=(24,80)
KIKMDF POS=(12,34),LENGTH=11,INITIAL='hello world'
CTIME KIKMDF POS=(14,37),LENGTH=6,ATTRB=(NUM,IC,FSET) <<
KIKMSD TYPE=FINAL
END

The first map change is to make it MODE=INOUT of course. Also on the KIKMSD statement FRSET is added which presets (defaults) the MDT (modified data tag) bit off for all the fields in the mapset. When a field’s MDT bit is off no data will be received from the field unless the user changes it. On the CTIME KIKMDF statement certain attributes are set: NUM means the field is numeric so only numeric information can be entered; IC means the cursor should be set at the beginning of this field; and FSET turns on the MDT bit for this field. With the MDT bit on for CTIME, data put there when the map is sent will be received even if the user does not change it. You may also notice PICOUT has been removed – that’s only to make the logic of the following COBOL program a little easier to follow.

ID DIVISION.
PROGRAM-ID. TESTCOB.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY TESTMSD.
01 SAVE-CTIMEO PIC X(6).<<
PROCEDURE DIVISION.
MOVE TIME-OF-DAY TO CTIMEO, SAVE-CTIMEO. <<
* INSPECT CTIMEO REPLACING ALL ' ' BY ':'. <<
EXEC KICKS
SEND MAP('TESTMAP') MAPSET('TESTMSD') ERASE
END-EXEC.
EXEC KICKS<<
RECEIVE MAP('TESTMAP') MAPSET('TESTMSD') <<
END-EXEC.<<
IF CTIMEI EQUAL SAVE-CTIMEO<<
MOVE ALL '0' TO CTIMEO<<
ELSE MOVE ALL '9' TO CTIMEO.<<
EXEC KICKS<<
SEND MAP('TESTMAP') MAPSET('TESTMSD') ERASE <<
END-EXEC.<<
EXEC KICKS RETURN END-EXEC.

First there is a variable to save the value we initially send to CTIMEO. It would seem OK to just use CTIMEO itself but KICKS BMS COBOL copybooks are usually constructed with the ‘01’ levels (TESTMAPI and TESTMAPO in this case) redefined (using the same storage). So the first line of the procedure division is changed to move the time to our saving variable as well as to CTIMEO.

The second line of the procedure division is now a comment replacing the angle bracket markup in the preceding example. None of that code is now needed since PICOUT has been removed from the map.

Then, as before, EXEC KICKS SEND MAP is used to display the message and the (unformatted) time of day. After that a RECEIVE MAP is used to retrieve whatever the user enters in the time of day field.

Control does not return until the user completes his interaction with the screen and presses the Enter key (or some other attention key like PF1, etc). At that point program execution resumes with the IF CTIME … statement which compares what it previously sent to the screen with what it got back. If they are the same it sends the screen back with the current time all zeros, otherwise it sends the screen back with the current time all nines.

Then, as before, EXEC KICKS RETURN is used to terminate the transaction.

Operationally, you type TEST on a clear screen and press enter, seeing as a result.

               
  hello world  
               
     083203    
               

This time the cursor is on the first character of the time of day (it was at the upper left corner of the previous screens). If you don’t change anything but just press enter the screen changes to.

               
  hello world  
               
     000000    
               

But if you do change something – for example type all ones then press enter – the screen changes to.

               
  hello world  
               
     999999    
               

If you typed in and ran these examples you probably noticed that if you typed anything (except clear) after you finished running one of them KICKS would give you an APCT abend. That’s not what you’d like to see in a real application, and we’ll look into why that happens and what to do about it next week.

You’ve seen how to prepare, generate and use simple KICKS BMS maps. Not all BMS maps are so simple! Most have many input fields and use of color and highlighting is common. Mapsets are often defined with multiple maps to accommodate different screen sizes.


Copyright © Mike Noel, 2008-2015; last updated 2/8/2015 for KICKS 1.5.0