Inside COBOL #8
by
Shawn M. Gordon
President of S.M.Gordon & Associates

This month I am going to tie together a few things that I have shown in previous columns as well as introduce some new topics in the process. The first thing I am going to do is introduce you to my highly proprietary process for loading function keys. Over the years I have written at least five different methods of loading function keys before I finally hit on this method. They all had their strengths and weaknesses, but this was the easiest to code, and executed the fastest. It doesn’t have the modularity of keeping them in a flat file, but it is a hell of a lot faster. Keep in mind as we go through this, that this is meant to spark your imagination, go ahead and use whatever bits that you think are worth using.

Ok, go ahead and take a look at the sample code so we are both talking about the same thing. All the variables in the WORKING-STORAGE section are what I define by default, I keep them in an INCLUDE file to keep it simple. As an aside, did you know that using $INCLUDE instead of COPY will cause your code to compile faster?

The variables KEY-POINT and KEY-BUFF are used to actually display the function keys and are referenced in the macros, so we will cover them shortly. The SET-FKEY variable defines the escape sequence to load a single function key with two characters of data. I always use the default values for the function keys, which are two characters, you will need to modify that if you want a different length. The escape sequence is described in detail in the terminal manual for the 2392 terminal. You can also find many of these described in the WRQ manual for the Reflection product. The KEY-ON and KEY-OFF escape sequences will turn on and off the display of the function keys respectively.

The definitions of KEY1 thru KEY8 are the default value for the function keys. It’s as easy to use the default values as it is to input special values, so to avoid conflict with some other escape sequence I always use the default values. So now that we have covered the variables and escape sequences needed to program the function keys, let’s look at how to actually do it.

The PREPFKEY macro should be called every time you are going to be loading and displaying a function key sequence. It clears out the buffer we use to load the function keys and initializes the buffer pointer to 1.

The LOADFKEY macro will take three parameters to determine the label that will display in the function key, the data in the function key, and which function key number to load. You can specify other parameters if you wish, but for me I take the defaults for the rest that I have put in WORKING-STORAGE. The real trick here is the STRING command and how it is being used to concatenate data into a variable. As you know, when you MOVE data to a variable it first clears the receiving variable out, and then transfer’s the data. There is no way to use MOVE to link to strings together. Using the STRING command will allow you to link multiple strings together in one long concatenated field, but what many people don’t realize is that you can specify a pointer on the STRING command and keep concatenating data through successive calls.

So what we are doing in LOADFKEY is building our escape sequence for each function key one at a time and then stuffing it into KEY-BUFF. We are using KEY-POINT to maintain the pointer to KEY-BUFF of the end of the last string that was moved in. We set KEY-POINT to 1 initially to make sure it points at the first byte. If you have KEY-POINT set to 0, nothing will ever end up in KEY-BUFF because there is no zeroth byte. The reason I use this methodology will become clear shortly.

The SHOWFKEY macro first appends the KEY-ON escape sequence to KEY-BUFF. This is to make sure that the function keys will be displayed and not just loaded, this is just a safety precaution. We next subtract one from KEY-POINT because it is pointing to the next available byte, not the last used byte, and we need the exact length of the string. Now I convert KEY-POINT to a negative value in preparation for the call to the PRINT intrinsic. Finally I call the PRINT intrinsic specifying a carriage control value of %320, this is the equivalent of saying DISPLAY WITH NO ADVANCING, and will keep the cursor for issuing a carriage return/line feed at the end.

Now why did I load up a buffer and use the PRINT intrinsic? Let me explain something about terminal I/O first. Every time you issue the DISPLAY command it causes a CPU interrupt, this takes potential CPU time away from something else. If I had done these function keys one at a time I would have issued 9 CPU interrupts, one for each function key, and one to turn on the function keys. Now all I/O to eventually uses the FREAD and FWRITE intrinsics no matter what it is you use in your program, ie. ACCEPT, DISPLAY, READ, WRITE. Since you can DISPLAY virtually anything the compiler has to go through several steps before it get’s to the FWRITE.

As I recall the DISPLAY statement actually is five call’s above FWRITE, so now those 9 DISPLAY statements convert into about 45 calls to the CPU. The PRINT intrinsic is only 2 or 3 levels above FWRITE, and since we buffer up all of our data into one buffer we only have to make the one CPU interrupt to load all of the function keys. The advantage to PRINT over FWRITE is that it is fairly low level, doesn’t require you to FOPEN the terminal, and it is pretty easy to code for. It is overkill for some things since you have to specify a length to the call, and using code to calculate the length of a string could kill any speed advantage to using PRINT in the first place.

WORKING-STORAGE SECTION. 
* 
 01 WS-OPTION            PIC X(02)        VALUE SPACES. 
* **************  FKEY STUFF *
 01 KEY-POINT            PIC S9(9)        COMP VALUE 0. 
 01 KEY-BUFF             PIC X(300)       VALUE SPACES. 
* 
 01 SET-FKEY.    
03                   PIC X            VALUE %33.    03                   PIC X(02)        VALUE '&f'.    03 FKEY-TYPE         PIC 9            VALUE 2.    03                   PIC X            VALUE 'a'.    03 FKEY-NUM          PIC 9.    03                   PIC X(04)     
   VALUE 'k16d'.    03 FKEY-LEN          PIC 99           VALUE 02.    03                   PIC X            VALUE 'L'.    03 FKEY-LABEL        PIC X(16).    03 FKEY-DATA         PIC X(02).    03                   PIC X            VALUE %15. * 01 KEY-ON.   
 03                   PIC X            VALUE %33.    03                   PIC X(03)        VALUE "&jB". * 01 KEY-OFF.    03                   PIC X            VALUE %33.    03                   PIC X(03)        VALUE "&j@". * 01 KEY1.    05                 
  PIC X      VALUE %33.    05                   PIC X      VALUE 'p'. * 01 KEY2.    05                   PIC X      VALUE %33.    05                   PIC X      VALUE 'q'. * 01 KEY3.    05                   PIC X      VALUE %33.    05                   PIC
 X      VALUE 'r'. * 01 KEY4.    05                   PIC X      VALUE %33.    05                   PIC X      VALUE 's'. * 01 KEY5.    05                   PIC X      VALUE %33.    05                   PIC X      VALUE 't'. * 01 KEY6.    05                
   PIC X      VALUE %33.    05                   PIC X      VALUE 'u'. * 01 KEY7.    05                   PIC X      VALUE %33.    05                   PIC X      VALUE 'v'. * 01 KEY8.    05                   PIC X      VALUE %33.    05                   PI
C X      VALUE 'w'. * PROCEDURE DIVISION. A0000-MACROS.  $DEFINE %PREPFKEY=        MOVE SPACES       TO KEY-BUFF        MOVE 1            TO KEY-POINT# * $DEFINE %LOADFKEY=        MOVE !1  TO FKEY-LABEL        MOVE !2  TO FKEY-DATA        MOVE !3  TO FKEY-N
UM        STRING SET-FKEY DELIMITED BY SIZE INTO KEY-BUFF               WITH POINTER KEY-POINT# * $DEFINE %SHOWFKEY=        STRING KEY-ON DELIMITED BY SIZE INTO KEY-BUFF               WITH POINTER KEY-POINT        SUBTRACT 1 FROM KEY-POINT        MULTIPLY K
EY-POINT BY -1 GIVING KEY-POINT        CALL INTRINSIC "PRINT" USING KEY-BUFF, KEY-POINT, %320# * A1000-INIT.     %PREPFKEY.     %LOADFKEY("  Add    Record "#,KEY1#,1#).     %LOADFKEY(" Change  Record "#,KEY2#,2#).     %LOADFKEY(" Delete  Record "#,KEY3#,3#)
.     %LOADFKEY(" Ignore  Changes"#,KEY4#,4#).     %LOADFKEY("                "#,KEY5#,5#).     %LOADFKEY("                "#,KEY6#,6#).     %LOADFKEY("  Back     Up   "#,KEY7#,7#).     %LOADFKEY("  Stop    Run   "#,KEY8#,8#).     %SHOWFKEY.

     ACCEPT WS-OPTION FREE.     IF WS-OPTION = KEY8        STOP RUN.

Here is an interesting bit of trivia, I wrote a tiny program that just did a simple display of one word. I then changed the program to use the PRINT intrinsic instead of DISPLAY. I decompiled both programs to see what instructions had gotten generated, and from the following two lists you can see the difference.

EXAMPLE WITH DISPLAY VERB:

0.133 177777 .. STT COBOLTRAP . 0.134 177777 .. STT DEBUG . 0.135 177777 .. STT TERMINATE’ . 0.136 177777 .. STT C’DISPLAY’INIT . 0.137 177777 .. STT C’DISPLAY’FIN . 0.140 177777 .. STT C’DISPLAY . 0.141
000027 .. STT %27 . 0.142 000000 .. STT %0 . 0.143 040010 @. STT LENGTH = %10 , INTERNAL = %2

EXAMPLE WITH PRINT INTRINSIC:

0.121 177777 .. STT COBOLTRAP . 0.122 177777 .. STT DEBUG . 0.123 177777 .. STT TERMINATE’ . 0.124 177777 .. STT PRINT . 0.125 000012 .. STT %12 . 0.126 000000 .. STT %0 . 0.127 040006 @. STT
LENGTH = %6 , INTERNAL = %2

I want to wrap up this month by once again reminding you to let me know what you think of this column. If you have tips that you would like to share, or question you want to ask, please feel free. I want to start relying on your input for future columns so let me know if these are to basic, not basic enough, need more detail, need less detail, whatever you want, and I will do my best to accommodate everyone. You can get a hold of me through Interex, or by leaving me electronic mail on Compuserve. Until next month……..