tirsdag den 8. september 2015

How the assembler works (3)

I will now start a series of articles about macro language. What is a macro? It is an interpretal programming language understood by the assembler and meant to generate real symbolic instructions. You can use macroes to generate instructions that must be in every program, usually start and end of a program. Another typical way to use macroes is to make a table with entries that can be loaded by a program and used as parameters.

The smallest macro you can make is the this one

MACRO
NAME
MEND
  1. The first entry tells the assembler that this is a Macro. 
  2. Next line is its name. 
  3. The last line tells the assembler that it is the end of the macro. 

Between the name and MEND come a number of instructions and the NAME-line takes some variables to the macro. Think macro language as a separate programming language and the macro as a program.

Table example

Let us start with a table of names. It is just for fun and should give you the feel of how to use macroes.

TABLE1   TABLE START
         TABLE ENTRY,PROGRAMMER=YES
         TABLE ENTRY,PROGRAMMER=YES,CNAME=SONJA
         TABLE ENTRY,CNAME=CARL,SURNAME=NIESLEN
         TABLE ENTRY,CNAME=EDWARD,SURNAME=GRIEG,PROGRAMMER=NEVER
         TABLE END
The table contains four entries with a START and an END. Each table entry contains the christian name, surname of a person, and an indication whether he or she is a programmer or not. Probably a silly table but good as an example.

START

The first two lines are as the minimum macro above
          MACRO
&LABEL   TABLE &STATE,&CNAME=JENS,&SURNAME=ELBAEK,&PROGRAMMER=
  1. We start every macro with MACRO
  2. All names prefixed with "&" are variables and those after the name are parameters to the macro some with a default value. &LABEL is also a variable but it takes no default value. &STATE has no equal sign because it is a positional parameter, the rest are in the keyword=value form 


Later on we need to count the number of entries, so we will define a global variable called &COUNT which we can access in all six invocations of the TABLE-macro. That is opposed to a local variable that will be reinitiated during each invocation.
          GBLA  &COUNT

Testing STATE

The &STATE parameter takes three values as you might have noticed in the table example:
  • START
  • ENTRY
  • END
We check that and go to the appropriate section in the macro by:

          AIF   ('&STATE' EQ 'START').START
          AIF   ('&STATE' EQ 'ENTRY').ENTRY
          AIF   ('&STATE' EQ 'END').END
          MNOTE 8,'THERE MUST BE A STATE
          MEXIT
All macro instructions start with an "A" so these are IF-statements. The test is surrounded by brackets. If the variable contains alphanummeric value you must enclose the variable with quote. If the statement is true the assembler will proceed at the label prefixed by a dot. If none of the IF's are true there will be written a message and the assembler will stop with condition code 8.

.START

.START    ANOP
&LABEL    CSECT
&LABEL    RMODE ANY
          DC    CL32'&LABEL - &SYSDATE..&SYSTIME'
&COUNT    SETA  1
          ENTRY ENTRY&COUNT
          AGO   .END_OF_MACRO
  1. The .START-label is a No OPeration, again prefixed with "A". 
  2. Every assembler program must have a CSECT and our gets the name in the &LABEL (TABLE1). 
  3. We wish to be able to load it in 31-bit address storage. 
  4. Then we create a constant in the program/table that tells the name and data and time when assembled. 
  5. We set the global counter to one by the SETA-instruction which is used for nummeric SET. SETC is used for alphanummeric SET of variables. 
  6. We set the entrypoint at the first entry 
  7. and finally go to end of the macro. We could have used MEXIT as above but I prefer this way.
That was the start of the table and we are now ready to create each entry in the table

.ENTRY 

.ENTRY    ANOP
ENTRY&COUNT DS  0F
&COUNT    SETA  &COUNT+1
          DC    A(ENTRY&COUNT)
          DC    CL8'&CNAME'
          DC    CL8'&SURNAME'
          AIF   ('&PROGRAMMER' EQ 'YES').PROGRAMMER
          DC    C'0'
          AGO   .END_OF_MACRO
.PROGRAMMER ANOP
          DC    C'1'
          AGO   .END_OF_MACRO

  1. ENTRY starts with its assembler label (prefixed with dot)
  2. We create a lable suffixed with counter value
  3. and adds 1 to the counter
  4. We need an address to the next entry so we makes a address constant
  5. Then a constant with the first name
  6. and one with the surname
  7. We test whether the parameter &PROGRAMMER is "YES". 
  8. If not we define a constant with "0"
  9. and goes to the end
  10. otherwise we go to the lable .PROGRAMMER and 
  11. make a constant with the value 1 that indicates the person is a programmer
  12. and go to end.
We have produced one entry.
ENTRY1  DS    0F
        DC    A(ENTRY2)

        DC    CL8'JENS'
        DC    CL8'ELBAEK'
        DC    C'1'

There will come three additional entries. All entries will be linked together with the address in A(ENTRY&COUNT). Some call it a linked list.

.END

.END      ANOP
ENTRY&COUNT DC  A(-1)
          END


  1. The .END label
  2. The last addess constant will contain X'FFFFFFFF' in order tel the program that the table has ended
  3. The assemler instruction "END" that ends the assembler when it translates the symbolic machine instructions, - NOT the macro intrepretations.

.END_OF_MACRO

.END_OF_MACRO ANOP
              MEND
  1. The label at the end of the macro
  2. and the actual end. That will be the last statement in every macro

TABLE Macro

This is the full macro:

          MACRO
&LABEL    TABLE &STATE,&CNAME=JENS,&SURNAME=ELBAEK,&PROGRAMMER=
          GBLA  &COUNT
          AIF   ('&STATE' EQ 'START').START
          AIF   ('&STATE' EQ 'ENTRY').ENTRY
          AIF   ('&STATE' EQ 'END').END
          MNOTE 8,'THERE MUST BE A STATE'
          MEXIT
.START    ANOP
&LABEL    CSECT
&LABEL    RMODE ANY
          DC    CL32'&LABEL - &SYSDATE..&SYSTIME'
&COUNT    SETA  1
          ENTRY ENTRY&COUNT
          AGO   .END_OF_MACRO
.ENTRY    ANOP
ENTRY&COUNT DS  0F
&COUNT    SETA  &COUNT+1
          DC    A(ENTRY&COUNT)
          DC    CL8'&CNAME'
          DC    CL8'&SURNAME'
          AIF   ('&PROGRAMMER' EQ 'YES').PROGRAMMER
          DC    C'0'
          AGO   .END_OF_MACRO
.PROGRAMMER ANOP
          DC    C'1'
          AGO   .END_OF_MACRO
.END      ANOP
ENTRY&COUNT DC  A(-1)
          END
.END_OF_MACRO ANOP
          MEND


   Loc  Object Code    Addr1 Addr2  Stmt   Source Statement                      
                                      1 TABLE1   TABLE START
000000                00000 00084     2+TABLE1    CSECT
                                      3+TABLE1    RMODE ANY
000000 E3C1C2D3C5F14060               4+          DC    CL32'TABLE1 - 09/08/15.15.05'
                                      5+          ENTRY ENTRY1
                                      6           TABLE ENTRY,PROGRAMMER=YES
000020                                7+ENTRY1      DS  0F
000020 00000038                       8+          DC    A(ENTRY2)
000024 D1C5D5E240404040               9+          DC    CL8'JENS'
00002C C5D3C2C1C5D24040              10+          DC    CL8'ELBAEK'
000034 F1                            11+          DC    C'1'
                                     12          TABLE ENTRY,
                                                PROGRAMMER=YES,CNAME=SONJA
000038                               13+ENTRY2      DS  0F
000038 00000050                      14+          DC    A(ENTRY3)
00003C E2D6D5D1C1404040              15+          DC    CL8'SONJA'
000044 C5D3C2C1C5D24040              16+          DC    CL8'ELBAEK'
00004C F1                            17+          DC    C'1'
                                     18          TABLE ENTRY,CNAME=CARL,SURNAME=NIESLEN
000050                               19+ENTRY3      DS  0F
000050 00000068                      20+          DC    A(ENTRY4)
000054 C3C1D9D340404040              21+          DC    CL8'CARL'
00005C D5C9C5E2D3C5D540              22+          DC    CL8'NIESLEN'
000064 F0                            23+          DC    C'0'
                                     24          TABLE ENTRY,
                                      CNAME=EDWARD,SURNAME=GRIEG,PROGRAMMER=NEVER
000068                               25+ENTRY4      DS  0F
000068 00000080                      26+          DC    A(ENTRY5)
00006C C5C4E6C1D9C44040              27+          DC    CL8'EDWARD'
000074 C7D9C9C5C7404040              28+          DC    CL8'GRIEG'
00007C F0                            29+          DC    C'0'
                                     30          TABLE END
00007D 000000
000080 FFFFFFFF                      31+ENTRY5      DC  A(-1)
                                     32+          END



Take a look at the assembler list. Where has each entry got its values from and why?
By the way, why are there three X'00' at location counter
00007D. There are three others hidden. Find them.

Next time we will make a more complicated MACRO.



onsdag den 2. september 2015

How the assembler works (2)

How to create a translate table

This time we are going to manipulate the location counter a bit and show how we can use it for our benefit during programming. Let us set up the game. We are supposed to make a program that receives data as a parameter and we shall tranlate all upper case letters to lower case (and leave the lower case letters as is). We need a translate table of 256 of characters that describes this translation.

First thing first. We will make a table containing all hexadecimal values from X'00' to X'FF', 256 bytes. We call it LC (Lower Case). 

The next line will change the location counter back to the start of LC, Take a look at the location counter on the third line which equals the first line. The instruction ORG is doing that. 

The third line makes the whole table of 256 characters, This is a kind of magic but obvious when you know how to do it. We define a constant (DC) of 256 single characters (AL1). Each of these 256 characters takes the value of the current location counter, which increases with one for each character, subtracts the start of this table (LC). The result is as you see on the left. We have filled up the empty LC definition with values from X'00' to X'FF'.

00019E                              183 LC       DS    CL256        
00029E                0029E 0019E   184          ORG   LC           
00019E 0001020304050607             185          DC    256Al1(*-LC) 
0001A6 08090A0B0C0D0E0F                                             
0001AE 1011121314151617                                             
0001B6 18191A1B1C1D1E1F                                             
0001BE 2021222324252627                                             
0001C6 28292A2B2C2D2E2F                                             
0001CE 3031323334353637                                             
0001D6 38393A3B3C3D3E3F                                             
0001DE 4041424344454647                                             
0001E6 48494A4B4C4D4E4F                                             
0001EE 5051525354555657                                             
0001F6 58595A5B5C5D5E5F                                             
0001FE 6061626364656667                                             
000206 68696A6B6C6D6E6F                                             
00020E 7071727374757677                                             
000216 78797A7B7C7D7E7F                                             
00021E 8081828384858687                                             
000226 88898A8B8C8D8E8F                                             
00022E 9091929394959697                                             
000236 98999A9B9C9D9E9F                                             
00023E A0A1A2A3A4A5A6A7                                             
000246 A8A9AAABACADAEAF                                             
00024E B0B1B2B3B4B5B6B7                                             
000256 B8B9BABBBCBDBEBF                                             
00025E C0C1C2C3C4C5C6C7                                             
000266 C8C9CACBCCCDCECF                                             
00026E D0D1D2D3D4D5D6D7                                             
000276 D8D9DADBDCDDDEDF                                             
00027E E0E1E2E3E4E5E6E7                                             
000286 E8E9EAEBECEDEEEF                                             
00028E F0F1F2F3F4F5F6F7                                             
000296 F8F9FAFBFCFDFEFF                                             
See *(1)

However, that is not going to help us a lot. We need to replace all upper case letters with lower case letters. 
00029E                0029E 0025F   186          ORG   LC+C'A'       
00025F 8182838485868788             187          DC    9AL1(*-LC-64) 
We do that by manipulating the location counter once again. This time to the start of the upper case alphabet. The second line shows that it is at X'00025F'. Then we replace nine characters with their equivalent lower case. We know they have a value 64 less then the upper case letter. The EBCDIC characters come in bundles of nine except for the last part from 'S' that is eight.
000267 89                                                            
000268                00268 0026F   188          ORG   LC+C'J'       
00026F 9192939495969798             189          DC    9AL1(*-LC-64) 
000277 99                                                            
000278                00278 00280   190          ORG   LC+C'S'       
000280 A2A3A4A5A6A7A8A9             191          DC    8AL1(*-LC-64) 

We have in Danish three extra vowels that we must translate too *(2).
000288                00288 00219   192          ORG   LC+C'Æ'       
000219 C0                           193          DC    C'æ'          
00021A                0021A 0021A   194          ORG   LC+C'Ø'       
00021A 6A                           195          DC    C'ø'          
00021B                0021B 001F9   196          ORG   LC+C'Å'       
0001F9 D0                           197          DC    C'å'          
0001FA                001FA 0029E   198          ORG   LC+256        
We just use the actual character as offset into the table and replaces it with the lower case.
Please note the last line. It is a common mistake to forget it. What will happen if we forget it? The next instruction or Data Constant will start at location counter 0001FA. That will destroy the LC-table

By the way. The translation table can be used by the TR-instruction, f.ex.
          TR    AREA,LC
Upper case letters in AREA will be converted to lower case.


*(1) - If you wish to see the whole table, as here, you must precede the first line with PRINT DATA, and after the table use PRINT NODATA to set it off again.
*(2) - The Scandinavian languages are a little bit more sofisticated than English. ;-)

mandag den 31. august 2015

How the assembler works (1)

Understanding the location counter

The primary task for the assembler is to convert your symbolic machine instruction to the actual machine instruction. for instance 
000AC D20F A08A B0C0 0008A 000E0   101          MVC   PGM2_EXEC,PGM2_LIST 

The MVC coresponds to the D2 and that will be all for now. At the very left of the line you will see the position of the instruction according to the other instructions in your program. We could call it the offset to the beginning of the program. Here it is in hexadecimal and the offset is called the Location Counter. It is increased by the length of the present instruction so there is space for the next instruction. Not complicated, - but very important!!

First of all you have to keep in mind that this is on the time of assembling your program. An asterisks (*) refers to the current position of your location counter. 
Here comes an example. The first line A(*+8) means current position (look left) which is X' E0' plus 8 equals X'E8'. That is the location counter of the third line. That will later be resolved into the address of the program name, but that is another story.  
0000E0 000000E8                     130+PGM2_LIST DC   A(*+8)
0000E4 00000000                     131+         DC    A(0)
0000E8 D7C7D4F240404040             132+         DC    CL8'PGM2'
                      00010         133 PGM2_LGD EQU   *-PGM2_LIST
The last line will calculate the length of the three instructions above. This is a pure assembler instruction and will NOT increase the location counter, - i.e. it will never end up as an machine instruction. That is why there is no location counter on the left. The assembler variable PGM2_LGD is calculated to the right of the EQU-instruction (EQUal). 
* is the current value of the location counter and subtract it by the location counter for PGM_LIST. That equals to decimal 16 (X'10'). That is what the left most integer tells you.

It is always better to have the length in an variable, because you might put another field into the DSECT and your length of that DSECT will still be correct.

Length used explicit

You do not need to calculate the length every time. You can use the length as part of your instruction in order to manipulate the assembler to use another lenght. You can always put L' (quote) in front of your field. It will then be an assembler variable containing the length of that field.
000090 D20F A000 B17C 00000 001A0    91   MVC   WSEC,=CL(L'WSEC)'PGM2-WS' 
I wish to move the constant into the field WSEC but I don't know the length of WSEC. It doesn't matter because I will just set the length of the constant to the same as WSEC. The result is that the constant is padded with space to right, - exactly what I want. Take a look at the machine instruction. From the second byte of the instruction you can derive that the length is actually 16, (X'F' + 1).

Take a look at these four instructions and tell me what the second one does,
0000A0 4430 B084            000A8    99          EX    R3,MVC
0000A4 47F0 B08A            000AE   100          B     MVC+L'MVC
0000A8 D200 A016 2002 00016 00002   101 MVC      MVC   WTOTEXT(0),2(R2)
0000AE 4130 A014            00014   102          LA    R3,WTOLGD
Yes, - it branches over the MVC-instruction to the last instruction. The Branch needs an offset and calculates it by taking the location counter of MVC, the label contains both the location counter and length. Both are added together (MVC+l'MVC) and the result 000AE is showen. Look at the last line location counter and you will see it is correct.


lørdag den 29. august 2015

How to make your assembler program reentable

How to make your assembler program reentable

The Linkage Editor or Binder

When I learned assembly language a generation ago, there were two steps in the translation: the assembly and linkage editing. I had no idea what the last one did. I did not consider it important, but in short, Linkage Editor makes your program to the final (load) module, so it can be used by z / Os. Today called the Binder, which corresponds very well to what we call the same function on other platforms. However, there are some important parameters for binder, as you have to keep track of, if not you will have problems.
One of them is reusability, which tells the operating system how the program can be re-used by the operating system. The operating system has many simultaneous "tasks" running, so it is important that the program does not change itself. It might even change its code or values, or others could. If the assembler programmer guarantees that his program does not modify itself (or is modified by others), it could have the label "Reentable". It is a very fine predicate, which tells the operating system that it can load the program into a piece of memory which can be read only. If the program or another, violate the rule, that person thrown by an Abendkode "S0C4".
The operating system looks at "reusability" of each module as it loads it into memory and decides whether to enter the "read only" memory or modifiable memory.
Experienced system programmers will probably remember that one could put more of these predicates on a module, for example. both the "reusable" and "reentable". It should be no more. A load module can only get one predicate:
-------------------------------------------------------------------------------------------------------
Reusability (REUS, RENT and REFR) is handled differently by the binder. While the linkage editor processes the attributes independently, the binder stores them as a single value. The binder assumes that reenterable programs are also serially reusable, and the refreshable programs are also reenterable. This should not cause any processing difficulties.
The binder was designed to always accept an explicit override of a module attribute, whereas the linkage editor sometimes does not. For example, although the JCL can specify RENT in the parm list, when one CSECT being bound into a load module is reusable and the rest are reentrant, the linkage editor ignores the external parameter and assigns the module as reusable. The binder will allow the explicit override of RENT on the JCL to take priority.
------------------------------------------------------------------------------------------------------
From the manual z/OS MVS Program Management: User's Guide and ReferenceBinder processing differences from the linkage editor
If your program is modified anyway during the execution, you should "bind / link" with the REUSE = SERIAL or NONE. In this example, I have said that the module is serial reusable. So it is up to the programmer to manage the changes in the module code or values.
    SETOPT PARM (LET LIST XREF, NCAL, REUS = SERIAL)
    MODE AMODE (24), RMODE (24) NAME [modulename] (R)
The binder writes a list of the values and predicates the module has received. In the example below, the results from the parameters above.
------------------------------------------------------------------------------------------------------
AC               000 
AMODE             24
COMPRESSION     NONE
DC NO EDITABLE   YES
EXCEEDS 16MB      NO
EXECUTABLE       YES
LONGPARM          NO
MIGRATABLE       YES
OL                NO
OVLY              NO
PACK,PRIME     NO,NO
PAGE ALIGN        NO
REFR              NO
RENT              NO
REUS             YES 

RMODE             24
SCTR              NO
SIGN              NO
SSI
SYM GENERATED     NO
TEST              NO
XPLINK            NO
MODULE SIZE (HEX) 000001A0

------------------------------------------------------------------------------------------------------
Note that there are still several lines out regarding reusability, but that is really only in order to be backward compatible. The same also applies if you go into LOADLIB and find the module. Here are also more fields set aside to Reusable, but again, it's just to be backward compatible.
------------------------------------------------------------------------- 
MITPGM1 RU 
MITPGM2 
MITPGM3 RN RU
MITPGM4 RN RU

--------------------------------------------------------------------------

Program MITPGM3 og MITPGM4 are both Reentable, where MITPGM1 is "only" Serial Reusable
I have collected som links if wish to read some mode on the subject:

To be or not to be reentable


In the last chapter I wrote at the end of the article "So it is up to the programmer to manage the changes in the module code or values." I will show how it's done. We must ensure that nothing in the program will be changed. This means that all variables must be located in storage, for each individual process outside of the program; We must therefore make a so-called Getmain - ie retrieve main storage.
          GETMAIN RC,LV=STOR_LGD
          OR R1,R1
          BZ NO_STORAGE_OBTAINED
          LR R10,R1
          USING STORAGE,R10
          MVC EC,=CL(L’EC)’PGM2-STORAGE’

Somewhere in the program outside the control section (the CSECT the one with instructions), insert a DSECT describing your work area.
STORAGE   DSECT
EC        DS     CL16                 EYE CATCHER
VALUE1    DS     F
VALUE2    DS     H
VALUE3    DS     CL120
VALUE4    DS     F
STOR_LGD EQU *-STORAGE

You start to getmaine the area with a length which is calculated by the last instruction in the DSECT. Then you check if there is an address in register 1 (R1) with the OR instruction. If not, then go to the error routine NO_STORAGE_OBTAINED. In the next instruction you move the address into the R10 and then say that when you refer to the fields in this storage, R10 is used. Eventually move a so-called Eye Catcher in front of the area. It's nice if the program will dump its storage. So you can make a "Find" on the Eye Catch Eren, to find the area.

The DSECT describes the look of your space. It starts with DSECT and the name of this DSECT so you can refer to it in the USING. Then come your variables and finally the length of the area is calculated. So we are running ....

Linkage Convensions

Or, in other words, - it was the easy part. There will be more, but right now we issue yet another Getmain which must come very first in the program. It will be needed if we call other programs, so that they can save their registers (R14 to R12) in order to be able to restore them before returning to us. In other operating systems it is called to put on the stack, and retrieve from the stack. In z/OS it is called to comply with Linkage Conventions.

However, we're a program our self that is called - perhaps by the operating system, perhaps by another program. Therefore, we must save the caller's registers. Fortunately, a Macro will do most of it.
          SAVE   (14,12),,'PGM2 - &SYSDATE..&SYSTIME'
The latter is a comment that tells the program name (PGM2) and the time of program assembling. It is not just not recommended, but required that you use it!. It helps tremendously in many cases to make sure we got the right version of the program in a troubleshooting situation. After saving the caller's registers, we need to have a storage area for our registres, if we were to call another program.
RSA      GETMAIN RC,LV=72
         OR    R1,R1
         BZ    NO_STORAGE_OBTAINED
         ST    R1,8(,R13)
         ST    R13,4(,R1)
         LR    R13,R1
         USING SA,R13
         ........
         ........
SA       DSECT
SAVEAREA DS    18F

We pick up 72 bytes, or 18 fullwords (18F). That's all we need. The two Store Instructions (ST) makes a concatenation of register save areas and eventually we let R13 point to our Register Save Area and create adressability to it with USING. Just before we leave the program, we must re-establish the caller's registeres. This is done as follows:
         L     R13,SAVEAREA+4
         LM    R14,R12,12(R13)
         RETURN RC=0
First, R13 points to the callers Register Save Area. Then all registers from this area are fetched - except R13 of course. Finally, we return to the caller. 


Your program is fully reentable and can be laid in the Link Pack Area (LPA). It is a read-only-area, which is accessible to all address spaces (regions). It can also be used simultaneously to multiple tasks / threads. In the next chapter we talk about calling other programs if you still want your program to be reentable. It's not quite that simple as it sounds.

More reading:

Even more about reentable
In last chapter we made a reentrant program and you thought maybe it was everything, but it is not. It turns out that if you have to use some built-in features such as to call another program (LINK), then some of these features actually modify themselves in the program that uses them. I will review an example with LINK.

In my PGM1 I make a LINK to PGM2. I call it with a parameter. Notice the last three instructions that the parameter definition. It is embedded in the program itself. It is  required by the LINK macro, because it generates the address of Parm in A (PARM + X'80000000 '). If I had no PARM or it was a constant in the program, I could have used the standard LINK in a reentrant program.



        STH   R3,PARMLGD              
***************************************
** FLYT VARIABEL LÆNGDE AF TEKST       
***************************************
        BCTR  R3,0                    
        EX    R3,MVC                  
        B     MVC+L'MVC               
MVC      MVC   PARMTXT(0),2(R2)        
PGM2     LINK  EP=PGM2,PARAM=PARM,VL=1                        
PGM2     DS    0H                                             
        CNOP  0,4                                            
        LA    1,IHB0011                         LIST ADDRESS
        B     IHB0011A                          BYPASS LIST  
IHB0011  EQU   *                                              
        DC    A(PARM+X'80000000')                            
IHB0011A EQU   *                                              
        CNOP  0,4                                            
        BAL   15,*+20            BRANCH AROUND CONSTANTS     
        DC    A(*+8)             ADDR. OF PARM. LIST         
        DC    A(0)                DCB ADDRESS PARAMETER      
        DC    CL8'PGM2'           EP PARAMETER               
        SVC   6                   ISSUE LINK SVC             
        B     RETURN                                         
PARM     DS    0F                                             
PARMLGD  DS    H                                              
PARMTXT  DS    CL120                                          

Instead of using one standard LINK, use two - a LIST form and EXEC form. LIST form describes constants and variable fields to the LINK called. The EXEC form performs the actual LINK, just as standard LINK. This means that you define your LIST form along with your other constants and defines an area in your Getmained storage where you can move it up before performing the EXEC form. The macro parameter MF = L says that you want the LIST form and MF = E that you make EXEC form. Note that in MF = E comes the name of the field where you moved LIST form to. It may be a little tricky, but your program is reentrant. And that is beautiful.
STORAGE   DSECT                                     
STORAGE_EYE_CATCHER DS CL16            EYE CATCHER
PARM      DS    0F                                  
PARMLGD   DS    H                                   
PARMTXT   DS    CL120                               
STOR_LGD EQU   *-STORAGE                           
PGM2_EXEC DS   XL(PGM2_LGD)
                      

        STH   R3,PARMLGD                 
******************************************
** FLYT VARIABEL LÆNGDE AF TEKST        **
******************************************
        BCTR  R3,0                       
        EX    R3,MVC                     
        B     MVC+L'MVC                  
MVC      MVC   PARMTXT(0),2(R2) 
******************************************                              
        MVC   PGM2_EXEC,PGM2_LIST                                      
PGM2     LINK  EP=PGM2,PARAM=PARM,VL=1,MF=(E,PGM2_EXEC)                 
PGM2     DS    0H                                                       
*                                                                 1ØL1D
*                                                                 1ØL1D
        LA    1,PGM2_EXEC                       LOAD PARAMETER REG 1   
        LA    14,PARM            PICKUP PARAMETER                      
        ST    14,0(0,1)                         STORE INTO PARAM. LIST
        OI    0(1),X'80'                 SET LAST WORD BIT ON ØG860P40
        CNOP  0,4                                                      
        BAL   15,*+20            BRANCH AROUND CONSTANTS               
        DC    A(*+8)             ADDR. OF PARM. LIST                   
        DC    A(0)                DCB ADDRESS PARAMETER                
        DC    CL8'PGM2'           EP PARAMETER                         
        SVC   6                   ISSUE LINK SVC              ØG381P2A
        B     RETURN                                                   
PGM2_LIST LINK EP=PGM2,SF=L                                             
        CNOP  0,4                                                      
PGM2_LIST DC   A(*+8)             ADDRESS OF EP PARAMETER               
        DC    A(0)           DCB ADDRESS PARAMETER                     
        DC    CL8'PGM2'      EP PARAMETER                              

PGM2_LGD EQU   *-PGM2_LIST                                              


I do not know if you have noticed it, but there's a little wrinkle in my program that I had to use to move the parameter, which can be of variable length. That is the little EX command. It takes the right aligned byte in the register and put it in the second byte of the instruction, as it points to - here MVC. It is a kind of modification of the code, but it is in the CPU and NOT in the program. I could also have used MVCL (Move Long), but it would be too boring and cumbersome.

That was what I know about Reentable programs. Feel free to add notes and idears of how to improve a program regarding reentable. I would really have been pleased if someone had writen such an article for 30 years ago.