KICKS

A transaction processing system for CMS & TSO

KICKS KooKbooK recipe

Using Alternate 3270 screen sizes in COBOL and C programs

Applications are often written to optimally accommodate the dimensions of the terminal session the user connects with. In CICS (KICKS) this is usually done by using an appropriately sized map from a mapset. For this to work you need:

The common way to create the several maps is to pick the likely terminal types (3270-2, -3, -4, ...) (or some subset), create a map for the -2 (24x80), then duplicate that map the required number of times, changing only the map name (to match the terminal type), the field names (to match the terminal type) and field screen coordinates to make a 'nice' looking screen of that dimension.

Suppose you have a simple 24x80 map like this

KB12    DFHMSD TYPE=&SYSPARM,MODE=INOUT,TIOAPFX=YES,CTRL=FREEKB,       *
               LANG=COBOL,TERM=3270-2,STORAGE=AUTO
KB1224  DFHMDI SIZE=(24,80),COLOR=NEUTRAL,EXTATT=YES
KB1224T DFHMDF                                                         *
               COLOR=RED,                                              *
               ATTRB=(ASKIP,NORM),                                     *
               INITIAL=' Top of 24 line screen  '                      *
               POS=(1,1),LENGTH=24
KB1224M DFHMDF                                                         *
               COLOR=YELLOW,HILIGHT=BLINK,                             *
               ATTRB=(ASKIP,NORM),                                     *
               INITIAL=' Press PF3 to quit '                           *
               POS=(12,32),LENGTH=18
KB1224B DFHMDF                                                         *
               COLOR=GREEN,                                            *
               ATTRB=(UNPROT,NORM,FSET,IC),                            *
               INITIAL='__________________________________',           *
               POS=(24,2),LENGTH=34
        DFHMSD TYPE=FINAL
        END

Where the naming convention used is: First 4 chars = KB12 (KooKbooK recipe 12), next 2 refer to screen height (24), and the last character for fields T (top), M (middle), B(bottom).

You could add a second map for a 43x80 screen as follows

KB12    DFHMSD TYPE=&SYSPARM,MODE=INOUT,TIOAPFX=YES,CTRL=FREEKB,       *
               LANG=COBOL,TERM=3270-2,STORAGE=AUTO
KB1224  DFHMDI SIZE=(24,80),COLOR=NEUTRAL,EXTATT=YES
KB1224T DFHMDF                                                         *
               COLOR=RED,                                              *
               ATTRB=(ASKIP,NORM),                                     *
               INITIAL=' Top of 24 line screen  '                      *
               POS=(1,1),LENGTH=24
KB1224M DFHMDF                                                         *
               COLOR=YELLOW,HILIGHT=BLINK,                             *
               ATTRB=(ASKIP,NORM),                                     *
               INITIAL=' Press PF3 to quit '                           *
               POS=(12,32),LENGTH=18
KB1224B DFHMDF                                                         *
               COLOR=GREEN,                                            *
               ATTRB=(UNPROT,NORM,FSET,IC),                            *
               INITIAL='__________________________________',           *
               POS=(24,2),LENGTH=34
*
KB1243  DFHMDI SIZE=(43,80),COLOR=NEUTRAL,EXTATT=YES
KB1243T DFHMDF                                                         *
               COLOR=RED,                                              *
               ATTRB=(ASKIP,NORM),                                     *
               INITIAL=' Top of 43 line screen  '                      *
               POS=(1,1),LENGTH=24
KB1243M DFHMDF                                                         *
               COLOR=YELLOW,HILIGHT=BLINK,                             *
               ATTRB=(ASKIP,NORM),                                     *
               INITIAL=' Press PF3 to quit '                           *
               POS=(21,32),LENGTH=18
KB1243B DFHMDF                                                         *
               COLOR=GREEN,                                            *
               ATTRB=(UNPROT,NORM,FSET,IC),                            *
               INITIAL='__________________________________',           *
               POS=(43,2),LENGTH=34
        DFHMSD TYPE=FINAL
        END

where the new map and its fields are named as the above convention. At this point you have two maps: how do you decide which to use?

The 3270 datastream protocol provides a way to switch between a primary and an alternate size. In CICS (KICKS) the ASSIGN api is used to return the primary (aka 'default') and the alternate size, and the current in use size (ie, what size was used in last screen write).

To retrieve the primary (default) screen size use

EXEC KICKS ASSIGN DEFSCRNHT(height) END-EXEC.   and

EXEC KICKS ASSIGN DEFSCRNWD(width) END-EXEC.

where height and width are PIC S9(4) COMP (COBOL) or short (C).

To retrieve the alternate screen size use

EXEC KICKS ASSIGN ALTSCRNHT(height) END-EXEC.   and

EXEC KICKS ASSIGN ALTSCRNWD(width) END-EXEC.

To retrieve the current screen size use

EXEC KICKS ASSIGN SCRNHT(height) END-EXEC.   and

EXEC KICKS ASSIGN SCRNWD(width) END-EXEC.

The usual (though obviously not rigorous) way to decide what map to use is to assume the default is 24x80, obtain the ALTSCRNHT and then select the map with the largest height less than or equal to ALTSCRNHT. If none, simply use the default.

The last issue is how to tell CICS (KICKS) to use the DEFAULT or ALTERNATE screen size. Note that it is not sufficient to simply use a map of the size you want: you must also tell CICS (KICKS) to use either the Default or Alternate 3270 command to send the map you choose. This is done by including (or not) the keyword ALTERNATE in the SEND MAP (or SENT TEXT, or SEND CONTROL)

For example, to send the 24x80 map above you'd use

EXEC KICKS SEND MAP('KB1224') MAPSET('KB12') END-EXEC.

and to send the 43x80 map you'd use

EXEC KICKS SEND MAP('KB1243') MAPSET('KB12') ALTERNATE END-EXEC.

Here's what it looks like in COBOL and C

...
PROCEDURE DIVISION. EXEC CICS ASSIGN ALTSCRNHT(RC) END-EXEC. IF RC = 43 THEN MOVE +1 TO WHICH. REPEAT-LOOP. IF WHICH = 0 THEN MOVE LOW-VALUES TO KB1224O EXEC CICS SEND MAP('KB1224') MAPSET('KB12') ERASE END-EXEC EXEC CICS RECEIVE MAP('KB1224') MAPSET('KB12') RESP(RC) END-EXEC MOVE KB1224BI TO QUIT ELSE MOVE LOW-VALUES TO KB1243O EXEC CICS SEND MAP('KB1243') MAPSET('KB12') ERASE ALTERNATE END-EXEC EXEC CICS RECEIVE MAP('KB1243') MAPSET('KB12') RESP(RC) END-EXEC MOVE KB1243BI TO QUIT. IF EIBAID NOT = DFHPF3 AND QUIT NOT = 'QUIT' THEN GO TO REPEAT-LOOP. EXEC CICS SEND CONTROL ERASE END-EXEC. EXEC CICS RETURN END-EXEC.

...
int main(KIKEIB *eib) { short rc, which=0; #include "kb12.h" EXEC CICS ASSIGN ALTSCRNHT(rc); if (rc == 43) which = 1;

do { if (which == 0) { memset(&kb1224, 0, sizeof(kb1224)); EXEC CICS SEND MAP("kb1224") MAPSET("kb12") ERASE; EXEC CICS RECEIVE MAP("kb1224") MAPSET("kb12") RESP(rc); } else { memset(&kb1243, 0, sizeof(kb1243)); EXEC CICS SEND MAP("kb1243") MAPSET("kb12") ERASE ALTERNATE; EXEC CICS RECEIVE MAP("kb1243") MAPSET("kb12") RESP(rc); } if((eib->eibaid == DFHPF3) || (!strncmp(&kb1224.kb1224i.kb1224bi,"QUIT",4)) || (!strncmp(&kb1243.kb1243i.kb1243bi,"QUIT",4))) { EXEC CICS SEND CONTROL ERASE ; EXEC CICS RETURN ; } } while (1); }

One thing to note about the above is the need for the code to check fields in both maps (actually just the 'right' map) for possible input. Even in this simple example that seems clumsy - imagine if you've a dozen input fields on the map and/or need to set output highlighting! Especially galling since the maps are so similar!

There are a variety of ways to handle this.

I prefer the third option. It's easy and self documenting. Here's how it looks:

...
PROCEDURE DIVISION. EXEC CICS ASSIGN ALTSCRNHT(RC) END-EXEC. IF RC = 43 THEN MOVE +1 TO WHICH. REPEAT-LOOP. MOVE LOW-VALUES TO KB1224O. IF WHICH = 0 THEN EXEC CICS SEND MAP('KB1224') MAPSET('KB12') ERASE END-EXEC EXEC CICS RECEIVE MAP('KB1224') MAPSET('KB12') RESP(RC) END-EXEC ELSE EXEC CICS SEND MAP('KB1243') MAPSET('KB12') ERASE ALTERNATE FROM(KB1224O) END-EXEC EXEC CICS RECEIVE MAP('KB1243') MAPSET('KB12') RESP(RC) INTO(KB1224I) END-EXEC. MOVE KB1224BI TO QUIT. IF EIBAID NOT = DFHPF3 AND QUIT NOT = 'QUIT' THEN GO TO REPEAT-LOOP. EXEC CICS SEND CONTROL ERASE END-EXEC. EXEC CICS RETURN END-EXEC.

...
int main(KIKEIB *eib) { short rc, which=0; #include "kb12.h" EXEC CICS ASSIGN ALTSCRNHT(rc); if (rc == 43) which = 1;

do { memset(&kb1224, 0, sizeof(kb1224)); if (which == 0) { EXEC CICS SEND MAP("kb1224") MAPSET("kb12") ERASE; EXEC CICS RECEIVE MAP("kb1224") MAPSET("kb12") RESP(rc); } else { EXEC CICS SEND MAP("kb1243") MAPSET("kb12") ERASE ALTERNATE FROM(kb1224.kb1224o); EXEC CICS RECEIVE MAP("kb1243") MAPSET("kb12") RESP(rc); INTO(kb1224.kb1224i); } if((eib->eibaid == DFHPF3) || (!strncmp(&kb1224.kb1224i.kb1224bi,"QUIT",4))) { EXEC CICS SEND CONTROL ERASE ; EXEC CICS RETURN ; } } while (1); }

Observe that regardless of what screen size we use we only have to set the output map to low values once (for KB1224O) and we only need to reference one input field (KB1224BI), and that the reason we can do so is explicit in our use of FROM and INTO with the 43 line maps. If this seems like a small thing wait until you need to reference 40 fields on each of several maps...

This takes care of defining several maps, determining which map should be used, and specifying which map is to be used.

What about dropping STORAGE=AUTO? How should you handle manual allocation of storage in C? The symbolic map will be generated with a null pointer named bmsmapbr (or some other name if you specify BASE= but if so just substitute that name for bmsmapbr in the following).

There are at least 4 options:

EXEC CICS GETMAIN SET(bmsmapbr) LENGTH(sizeof(*bmsmapbr));

bmsmapbr = malloc(sizeof(*bmsmapbr));

EXEC CICS ADDRESS TWA(bmsmapbr);

(but make sure the TWA is large enough first, ie, use EXEC CICS ASSIGN TWALENG to check)

char mymapspace[60]; /* make sure it's at least as big as sizeof(*bmsmapbr) */

bmsmapbr = &mymapspace;

Here's an example using the above map (wo/STORAGE=AUTO)

...
int main(KIKEIB *eib) { short rc, which=0; char dsect[60]; // plenty, sizeof(*bmsmapbr) = 53 #include "kb12.h" //EXEC CICS GETMAIN SET(bmsmapbr) LENGTH(sizeof(*bmsmapbr)) ; //EXEC CICS ADDRESS TWA(bmsmapbr) ; //bmsmapbr = malloc(sizeof(*bmsmapbr)); bmsmapbr = &dsect; EXEC CICS ASSIGN ALTSCRNHT(rc); if (rc == 43) which = 1;

do { memset(bmsmapbr, 0, sizeof(*bmsmapbr)); if (which == 0) { EXEC CICS SEND MAP("kb1224") MAPSET("kb12") ERASE FROM(bmsmapbr->kb1224o); EXEC CICS RECEIVE MAP("kb1224") MAPSET("kb12") RESP(rc); INTO(bmsmapbr->kb1224i); } else { EXEC CICS SEND MAP("kb1243") MAPSET("kb12") ERASE ALTERNATE FROM(bmsmapbr->kb1243o); // or (bmsmapbr->kb1224o) - same thing... EXEC CICS RECEIVE MAP("kb1243") MAPSET("kb12") RESP(rc); INTO(bmsmapbr->kb1243i); // or (bmsmapbr->kb1224i) - same thing... } if((eib->eibaid == DFHPF3) || (!strncmp(bmsmapbr->kb1224i.kb1224bi,"QUIT",4))) { EXEC CICS SEND CONTROL ERASE ; EXEC CICS RETURN ; } } while (1); }

A couple of observations:

Just for fun, try coding this using a TWA. Hint: you'll need to add a TWA to the transaction's PCT entry to make it work...


Copyright Mike Noel, 2015; last updated 11/23/2015 for KICKS 1.5.0