KICKS

A transaction processing system for CMS & TSO

KICKS KooKbooK recipe

Simple KICKS COBOL programs, part III

Accessing external data – Pointers & BLL cells (peek/poke for mainframer’s!)

Last time you saw how to use the LINK and XCTL api’s to combine several COBOL programs into a more powerful KICKS application. This time you will build on that by learning to access information outside the confines of your own COBOL program, using pointers (COBOL2) and BLL cells (legacy COBOL).

Your program stores most of its non-file variable information in its working-storage section. But this information is highly transient – it goes away when your program ends. You can use a commarea to pass as much of it as you want to an immediate successor, but sometimes you need to save some of it for ‘later’, but not necessarily ‘next’ use. Temporary storage is one way to persist your data, but there is another, faster way that can be used if shared access is not necessary. That is to use main storage in your own KICKS region. This is called using ‘common’ storage, or storage that is available to transactions but does not go away when the transaction ends.

There are two such common areas: the CWA (common work area) and the TCTTEUA (terminal user area). The CWA is available (read/write) to all transactions. The TCTTEUA is available (read/write) to all transactions running on the same terminal (in KICKS that would be all transactions since there is only one terminal, but in CICS there is a separate TCTTEUA for each terminal).  Both areas are established when KICKS starts, and both are sized as specified in the SIT using the CWAL and TCTUAL macro arguments. They are initialized to binary zeros and retain changes are made to them until KICKS is shutdown.

Your program can find out how big these areas are using the EXEC KICKS ASSIGN call, and where they are located using the EXEC KICKS ADDRESS call. For example

EXEC KICKS ASSIGN CWALEN(LEN) END-EXEC.

Puts the length of the CWA into LEN, and

EXEC KICKS ADDRESS CWA(BLL-CWA) END-EXEC.

Puts the address of the CWA into BLL-CWA

But how does knowing where an area of storage is located help you access it from a COBOL program? While probably not obvious, you do this using the LINKAGE SECTION. Normally COBOL uses the linkage section to access data passed to it by another program. It turns out that (with a little help) COBOL can also use the LINKAGE SECTION to access almost any area of main storage. When a COBOL program is called it is the caller that sets what the COBOL program’s LINKAGE items address. The help COBOL needs is a way to set the LINKAGE items addresses from the called program.

Legacy COBOL uses the BLL-CELLS technique, named for the fact that it provides a way to change the values in the program’s own Base Linkage Locator Cells, which are where the COBOL program stores the ‘base registers’ for the calling argument list values.

Modern COBOL uses pointer variables. (ie items defined with USAGE POINTER).

Let's see how this works. Suppose we want to see if the CSA contains all low-values (what KICKS sets it to on startup), moving high-values to it if so. Our COBOL program's DATA DIVISION could look like

WORKONG-STORAGE SECTION.
77 CWA-LEN PIC S9(4) COMP.
<CB2>
77 CWA-BLL USAGE POINTER.
</CB2>
LINKAGE SECTION.
01 DFHCOMMAREA PIC X(01).
<NCB2>
01 MORE-ARGS.
05 FILLER PIC S9(8) COMP.
05 CWA-BLL PIC S9(8) COMP.
</NCB2>
01 COMMON-WORK-AREA PIC X(100).

where, as previously discussed, the <CB2> and <NCB2> blocks are for code only used for modern and for legacy COBOL compilers (respectively). We could perform the low-values test, high-values initialize as follows

EXEC KICKS ASSIGN CWALENG(CWA-LEN) END-EXEC.
EXEC KICKS ADDRESS CWA(CWA-BLL) END-EXEC.
<NCB2>
IF CWA-BLL GREATER THAN ZERO THEN
</NCB2>
<CB2>
SET ADDRESS OF COMMON-WORK-AREA TO CWA-BLL.
IF ADDRESS OF COMMON-WORK-AREA NOT NULL THEN
</CB2>
IF CWA-LEN EQUAL 100 THEN
IF COMMON-WORK-AREA EQUAL LOW-VALUES THEN
MOVE HIGH-VALUES TO COMMON-WORK-AREA.

Here's what's happening behind the scene. When KICKS calls your program it passes one or two arguments. It always passes the address of the EIB as the first argument. If there is a Comm-Area it passes the address of the Comm-Area as a second argument. If you compiled the above program and looked at the PROCEDURE DIVISION statement the preprocessor passed to the compiler you would see

PROCEDURE DIVISION USING
KIKEIB,
KIKCOMMAREA,
COMMON-WORK-AREA.

for modern COBOL, or

PROCEDURE DIVISION USING
KIKEIB,
KIKCOMMAREA,
MORE-ARGS,
COMMON-WORK-AREA.

for legacy COBOL.

Notice that in both cases there are more arguments than KICKS has actually passed! The additional arguments are only accessible if your program makes them so. How does that work? In modern COBOL you do that by using the SET statement to set the address of the argument to some value, normally that of a pointer variable. Legacy COBOL does not have a SET statement, so things get a little more complicated.

The way the legacy 'BLL' technique works is that one of the calling argument values is made very special: it is the address of its own slot in the calling argument list. If your COBOL program accesses that argument it is actually accessing that slot in the list itself. Suppose BLLLL is the relevant item in the LINKAGE SECTION:

LINKAGE SECTION.
01 PARMS PIC X(80).
01 BLLLL.
05 FILLER PIC S9(8) COMP.
05 BLL-CCC PIC S9(8) COMP.
05 BLL-DDD PIC S9(8) COMP.
05 BLL-EEE PIC S9(8) COMP.
01 CCCCCC PIC S9(8) COMP.
01 DDDDDD PIC S9(8) COMP.
01 EEEEEE PIC X(12).

Then BLLLL’s first 05 (FILLER) is the address of BLLLL itself, and the subsequent 05’s (BLL-CCC, BLL-DDD, BLL-EEE, BLL-FFF) are the addresses for the following items in the LINKAGE SECTION (CCCCCC, DDDDDD, EEEEEE). If you move something to BLL-CCC you are setting the address COBOL will use to read or write CCCCCC. Same for BLL-DDD and DDDDDD, and for BLL-EEE and EEEEEE. None of the names are significant, it’s the order. The BLL cells are the 2nd item you code in the LINKAGE SECTION (which we named BLLLL). Within that, FILLER is the address of BLLLL itself, BLL-CCC is what we named the address for the 01 item after BLLLL (which we named CCCCCC), BLL-DDD is what we named the address of the 01 item after that (which we named DDDDDD), and so on.

[Actually that's a bit simplified. It assumes none of the LINKAGE SECTION items following the BLL cells item exceeds 4096 bytes in length, in which case there is only one BLL cell per item. If an item is more than 4096 bytes long it will have multiple BLL cells, and your BLL cells LINKAGE item must account for that. For example, if CCCCCC were 5000 bytes long you might replace BLL-CCC with BLL-CCC-1 and BLL-CCC-2. You would load the address of CCCCCC into BLL-CCC-1, then compute the address to go into BLL-CCC-2 as ‘ADD BLL-CCC-1, 4096 GIVING BLL-CCC-2. For those wanting to go deeper into this I recommend the Summerville book "Advanced CICS Design techniques, ..." . It has a whole chapter on the LINKAGE SECTION.]

So how does BLLLL (or MORE-ARGS in the previous example) get it's very special value? That depends on the environment. In KICKS it is setup by a call to KIKBLLIN, inserted by the preprocessor as one of the first statements in the PROCEDURE DIVISION. This subroutine (source included in the COBOL glue routine) is specific to the MVT ANSI legacy COBOL compiler associated with the MVS 3.8 Turnkey and VM/370 6pack systems.

If you’ve used this BLL cells approach in the past you might notice we haven't talked about SERVICE RELOAD – which is a directive to a COBOL compiler to force a reload of a changed BLL it might have otherwise ‘cached’ as part of its optimization strategy. SERVICE RELOAD is not supported by the MVT ANSI legacy COBOL compiler. Fortunately it seems it is also unnecessary; the compiler apparently does not do that kind of optimization.

Whether you use pointers or the BLL cell technique to access storage outside your program is really just question of establishing addressability. The next question is what to do with it. Hereafter when we speak of pointers read "BLL" if you are using the legacy compiler.

We've already seen using this addressability for access to the CWA. Access to the TCTTEUA is the same, except that the ASSIGN and ADDRESS calls specify TCTUALENG and TCTUA respectively. Another similar area is the TWA (Transaction Work Area) defined in the PCT for each transaction; for this area the ASSIGN and ADDRESS calls specify TWALENG and TWA respectively.

There are several other types of storage your application can access with pointers.

You can use the LOAD api to obtain the address of  (pointer to) a program or table KICKS will bring in from an external load library. In the case of a program you would probably just execute it, but if it's a table you could use the pointer to examine entries in the table, or to change them. You can see this technique used in KEDFPGM, where the KEDFILTR table is loaded and edited to control use of the debug facility. Another example is in KEBRPGM, where the KEBRHELP table is loaded and displayed when help is requested.

You can also use the GETMAIN api to obtain the address of (pointer to) an area of storage sized as you specify. You can see this technique used in KEBRPGM, where it obtains the 32k buffer it will use for queue input and output dynamically instead of using an area of WORKING-STORAGE.

Also consider direct manipulation of pointers to obtain information from the system. The most commonly used such information is available using the ASSIGN api, but sometimes you may need something that isn't.  As shown below, access to the contents of system control blocks is pretty easy - and is not restricted to programs in KICKS (or CICS).

In modern COBOL you can use pointers directly in batch jobs, and in legacy COBOL you can use BLL cells in batch jobs if you can get the very special variable properly setup. What could you do with such pointers in batch? Check out file 214 on the CBT tape for some exciting ideas!

Regarding setup of the very special variable in legacy COBOL, see member SETUPBLL of file 563 on the CBT tape. Or see the assembler routine in the following...

//BLLTEST JOB CLASS=A,MSGLEVEL=(1,1),MSGCLASS=A
//COB EXEC PGM=IKFCBL00,PARM='LOAD,LIST,PMAP,DMAP,BUF=8192'
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD UNIT=SYSDA,SPACE=(460,(700,100))
//SYSUT2 DD UNIT=SYSDA,SPACE=(460,(700,100))
//SYSUT3 DD UNIT=SYSDA,SPACE=(460,(700,100))
//SYSUT4 DD UNIT=SYSDA,SPACE=(460,(700,100))
//SYSLIN DD DISP=(,PASS),UNIT=SYSDA,SPACE=(80,(500,100))
//SYSIN DD *
ID DIVISION.
PROGRAM-ID. BLLTEST.
AUTHOR. MIKE NOEL.
*///////////////////////////////////////////////////////////////
* THIS IS A TEST/DEMONSTRATION PROGRAM TO ILLUSTRATE USE OF
* THE 'BLL-CELLS' APPROACH TO ACCESSING EXTERNAL DATA FROM AN
* MVT ANSI COBOL (AS DISTRIBUTED WITH THE MVS TURNKEY SYSTEM
* FOR MVS38J, TK3) PROGRAM, AND OF MY 'BLLINIT' SUBROUTINE USED
* TO INITIALIZE THE BLL-CELLS.
*
* © MIKE NOEL, 2009, ALL RIGHTS RESERVED.
* DEVELOPED AS PART OF THE 'KICKS FOR THE TURNKEY' PROJECT
* SEE (http://www.kicksfortso.com)
* BUT PERMISSION IS GRANTED
* FOR USE, WITHOUT WARRANTY OF ANY KIND, FOR ANY PURPOSE, AS
* LONG AS THE COPYRIGHT NOTICES ARE NOT REMOVED.
*//1/////////2/////////3/////////4/////////5/////////6/////////7
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
77 CR-NOTICE PIC X(55) VALUE
'BLLTEST, COPYRIGHT MIKE NOEL, 2009, ALL RIGHTS RESERVED'.
LINKAGE SECTION.
01 OS-PARMS PIC X(80).
01 BLL-CELLS.
05 FILLER PIC S9(8) COMP.
05 BLL-CVT PIC S9(8) COMP.
05 BLL-FOURWD PIC S9(8) COMP.
05 BLL-ASCB PIC S9(8) COMP.
05 BLL-JOBNAMEP PIC S9(8) COMP.
05 BLL-JOBNAME PIC S9(8) COMP.
01 CVT PIC S9(8) COMP.
01 FOURWD PIC S9(8) COMP.
01 ASCB PIC S9(8) COMP.
01 JOBNAMEP PIC S9(8) COMP.
01 JOBNAME PIC X(8).
PROCEDURE DIVISION USING OS-PARMS, BLL-CELLS,
CVT, FOURWD, ASCB, JOBNAMEP, JOBNAME.
* ** INITIALIZE BLL-CELLS **
CALL 'BLLINIT' USING BLL-CELLS.
* GET CVT
MOVE +16 TO BLL-CVT.
DISPLAY 'CVT ', CVT.
* GET 4 WORD LIST
MOVE CVT TO BLL-FOURWD.
DISPLAY 'FOUR WORD LIST ', FOURWD.
* GET ASCB
ADD +12, FOURWD GIVING BLL-ASCB.
DISPLAY 'ASCB ', ASCB.
* GET JOBNAMEP (+172 FOR BATCH, +176 FOR TSO)
ADD +172, ASCB GIVING BLL-JOBNAMEP.
DISPLAY 'JOBNAME POINTER (BATCH) ', JOBNAMEP.
* DISPLAY JOBNAME
MOVE JOBNAMEP TO BLL-JOBNAME.
DISPLAY 'JOBNAME (BATCH) ', JOBNAME.
MOVE 0 TO RETURN-CODE.
STOP RUN.
/*
//ASM EXEC PGM=IFOX00,
// PARM='OBJECT,XREF(SHORT),LIST,SYSPARM(IFOX00)'
//SYSLIB DD DSN=SYS1.MACLIB,DISP=SHR
//SYSUT1 DD UNIT=SYSDA,SPACE=(CYL,(2,1))
//SYSUT2 DD UNIT=SYSDA,SPACE=(CYL,(2,1))
//SYSUT3 DD UNIT=SYSDA,SPACE=(CYL,(2,1))
//SYSPRINT DD SYSOUT=*
//SYSPUNCH DD DUMMY
//SYSGO DD DDNAME=SYSLIN
//SYSLIN DD DISP=(,PASS),UNIT=SYSDA,SPACE=(80,(200,200))
//SYSIN DD *
* ///////////////////////////////////////////////////////////////////
* A CALL TO THIS ROUTINE SHOULD BE THE FIRST STATEMENT OF AN MVT
* ANSI COBOL PROGRAM THAT USES THE 'BLL-CELLS' APPROACH TO ACCESSING
* EXTERNAL DATA. THIS ROUTINE INITIALIZES THE BLL FOR THE BLL-CELLS
* RECORD (BY WHATEVER NAME) TO POINT TO ITSELF.
*
* IE - CALL 'BLLINIT' USING BLL-CELLS.
*
* AFTER THIS ROUTINE RETURNS THE COBOL PROGRAM MAY USE THE BLL-CELLS
* RECORD AS WIDELY DOCUMENTED TO ADDRESS EXTERNAL DATA.
*
* CODE IS **HIGHLY** SPECIFIC TO THE MVT ANSI COBOL COMPILER
* (CB545 V2 LVL78 01MAY72) DISTRIBUTED WITH THE TK3 VERSION OF THE
* TURNKEY MVS38J SYSTEM.
*
* LOGIC: THE ARGUMENT ISN'T USED EXCEPT TO CAUSE THE COMPILER TO
* REFERENCE THE APPROPRIATE BLL CELL (THE ONE FOR BLL-CELLS ITSELF) IN
* THE INSTRUCTION STREAM IT GENERATES FOR THE CALL. THIS SUBROUTINE
* USES R14 TO LOOK AT THAT INSTRUCTION STREAM AND LOCATE THE REFERENCE,
* FROM WHICH IT OBTAINS THE BLL ADDRESS. IT THEN PUTS THE ADDRESS INTO
* THE LOCATION REFERENCED BY THE ADDRESS. THIS LOCATES THE BLL-CELLS
* RECORD IN THE 'PARAMETER LIST' ITSELF(!) AND ENABLES ELEMENTS OF THE
* BLL-CELLS RECORD TO SET THE ADDRESSES OF FOLLOWING ARGMUMENTS IN
* THE LIST, AS THE BLL-CELLS APPROACH REQUIRES.
*
* © MIKE NOEL, 2009, ALL RIGHTS RESERVED. DEVELOPED AS PART OF THE
* 'KICKS FOR THE TURNKEY' PROJECT (http://www.kicksfortso.com) BUT
* PERMISSION IS GRANTED FOR USE, WITHOUT WARRANTY OF ANY KIND, FOR ANY
* PURPOSE, AS LONG AS THE COPYRIGHT NOTICES ARE NOT REMOVED.
* //////1/////////2/////////3/////////4/////////5/////////6/////////7
BLLINIT START 0
USING *,15
STM 14,12,12(13)
LR 3,14 GET CALLING CODE ADDR
S 3,=F'26' BKUP TO BLL LOAD
CLC 0(2,3),=XL2'58E0' IS THIS A 'L 14,??'
BE BLL2 YES
LA 1,1
LA 15,0
ABEND (1),DUMP
BLL2 MVC BLL3+2(2),2(3) MOVE BLL ADDR TO MY LA
EX 0,BLL3 EX TO DO LA OF BLL
ST 2,0(,2) SAVE ITS OWN ADDR IN BLL
LM 14,12,12(13)
BR 14
BLL3 LA 2,0
LTORG
DC C'BLLINIT, COPYRIGHT MIKE NOEL, 2009, ALL RIGHTS RESERVED'
END
/*
//LKED EXEC PGM=IEWL,COND=(5,LT),
// PARM='LIST,XREF,LET'
//SYSLIN DD DSN=*.COB.SYSLIN,DISP=(OLD,DELETE)
// DD DSN=*.ASM.SYSGO,DISP=(OLD,DELETE)
//SYSLMOD DD DSN=&GODATA(RUN),DISP=(NEW,PASS),UNIT=SYSDA,
// SPACE=(1024,(50,20,1))
//SYSLIB DD DSN=SYS1.COBLIB,DISP=SHR
//SYSUT1 DD UNIT=SYSDA,SPACE=(1024,(50,20))
//SYSPRINT DD SYSOUT=*
//*
//GO EXEC PGM=*.LKED.SYSLMOD,COND=(5,LT)
//SYSUDUMP DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD *
//

Which when run produces something like this (addresses may vary)

CVT 00130256
FOUR WORD LIST 00000536
ASCB 16696992
JOBNAME POINTER (BATCH) 16620528
JOBNAME (BATCH) BLLTEST

Copyright © Mike Noel, 2008-2014; last updated 12/6/2014 for KICKS 1.5.0