søndag den 27. september 2015

How the assembler works (6)

Checking for reentrant code

I wrote about how you could make your program reentrant some blog posts ago. The assembler can actually help you check for non-reentrant code.
          PRINT GEN           
 PGM4     RSECT               
 PGM4     RMODE ANY           
          ENTRY PGM4          
          BASR  11,0          
          USING *,11          
          MVC   NAME,=CL8'PGM4'
          B     RETURN        
 NAME     DS    CL8           
 RETURN   RETURN RC=0         
          LTORG               
    
      END                  
 PGM4     BINDER REUS=RENT    
          END
Non-reentrant code
All you have to do is replace CSECT with RSECT on the first line and tell the Linkage Editor, that the module must be Link Edited to reentrant.

  Loc  Object Code    Addr1 Addr2  Stmt   Source Statement              
                                      1          PRINT GEN              
000000                00000 00028     2 PGM4     RSECT                  
                                      3 PGM4     RMODE ANY              
                                      4          ENTRY PGM4             
000000 0DB0                           5          BASR  11,0             
                 R:B  00002           6          USING *,11             
000002 D207 B00A B01E 0000C 00020     7          MVC   NAME,=CL8'PGM4'  
** ASMA036W Reentrant check failed                                      
** ASMA435I Record 7 in EXJEL.EXJEL004.JOB07544.D0000101.? on volume:   
000008 47F0 B012            00014     8          B     RETURN           
00000C                                9 NAME     DS    CL8              
                                     10 RETURN   RETURN RC=0            
000014                               12+RETURN   DS    0H               
000014 41F0 0000            00000    13+         LA    15,0(0,0)        
000018 07FE                          14+         BR    14               
000020                               15          LTORG                  
000020 D7C7D4F440404040              16                =CL8'PGM4'       
                                     17          END                    

Assembler list with reentrant error.
The result is an error where you do not respect the rules for reentrant coding:
** ASMA036W Reentrant check failed

tirsdag den 22. september 2015

How the assembler works (5)

How to avoid Linkage Editor SYSIN

You have probably tried to make your own JCL to assemble and link-edit your program. It can be a little annoying to define the sysin to the Linkage Editor / Binder, either as a member in a PDS or as SYSIN as below.
//LNK.SYSIN DD *                            
  SETOPT PARM(LET,LIST,XREF,NCAL,REUS=RENT)
  MODE AMODE(31),RMODE(ANY)                
  NAME PGM4(R)                             
/*                                          
Linkage Editor SYSIN
I will now show you a macro that will do it for you. You can either make a new macro, as I did, or put it into the previous PROGRAM macro. That is up to you.
Actually, you do not need a macro. You just need to issue some PUNCH statements at the start or the end of your program. PUNCH writes a record on the same DD-card as the assembler writes the object deck. I have made a macro in order to hide what is done and make it easier for you.
        MACRO                                               
&PGM     BINDER &AMODE=31,&RMODE=ANY,&REUS=SERIAL            
.*************************************                       
.** PUNCH SYSIN TO THE BINDER       **                       
.*************************************                       
        PUNCH ' SETOPT PARM(LET,LIST,XREF,NCAL,REUS=&REUS.)'
        PUNCH ' MODE AMODE(&AMODE.),RMODE(&RMODE.)'         
        PUNCH ' NAME &PGM.(R)'                              
        MEND                                                
BINDER macro
The BINDER macro takes some parameters so you can change some of the Binder sysin. They have some very common default values, so you can probably do with:
[program name]  BINDER
Take a look at this very simple program:
//[userid]004 JOB 1,[userid],CLASS=8,MSGCLASS=Z,MSGLEVEL=(1,1),
//        NOTIFY=&SYSUID,                                  
//        REGION=0M                                        
//PROCLIB  JCLLIB ORDER=[userid].SRC.JCL                      
//ASMLNK  EXEC ASMLNK                                      
//ASM.SYSIN DD *                                           
PGM4     CSECT                                             
PGM4     RMODE ANY                                         
        ENTRY PGM4                                        
        DC    CL8'PGM4'                                   
        END                                               
PGM4     BINDER                                            
        END                                               
/*                                                         
//                                                         
Source of program with Binder sysin
That is all you need. There is a pitfall that annoyed me a while. The BINDER macro is in a separate source program. That is why you must type “END” after your program and then the BINDER macro and end that with an “END” too. It is called “Batch Assembling”. That protects the PUNCH records to intertwine with the object code of your program.
 Loc  Object Code    Addr1 Addr2  Stmt   Source Statement      
000000                00000 00008     1 PGM4     CSECT          
                                     2 PGM4     RMODE ANY      
                                     3          ENTRY PGM4     
000000 D7C7D4F440404040               4          DC    CL8'PGM4'
                                     5          END            
Loc  Object Code    Addr1 Addr2  Stmt   Source Statement                     
                                   1 PGM4     BINDER                        
                                   2+         PUNCH ' SETOPT PARM(LET,LIST,XREF,NCAL,REUS=SERIAL)'
                                   3+         PUNCH ' MODE AMODE(31),RMODE(ANY)'
                                   4+         PUNCH ' NAME PGM4(R)'         
                                   5          END
Assembler List of the two “programs”
ESD      ..  ..PGM4    ........                    
TXT ...  ..  ..PGM4                                
END                            1569623400 010615265
SETOPT PARM(LET,LIST,XREF,NCAL,REUS=SERIAL)        
MODE AMODE(31),RMODE(ANY)                          
NAME PGM4(R)                                       
Object code to the Linkage Editor / Binder
The first three lines of the example above are the object code from the program that starts with ESD and ends with END as any object code. After that come all the sysin parameters from your PUNCH.
The last figure is the JCL-procedure you can use. There are of course no SYSIN in the LNK step.
//ASMLNK   PROC                                              
//ASM      EXEC PGM=ASMA90,PARM='DECK,OBJECT'                
//SYSLIB   DD DISP=SHR,DSN=SYS1.MACLIB                       
//         DD DISP=SHR,DSN=[userid].SRC.ASM                     
//SYSUT1   DD DSN=&&TEMP1,UNIT=SYSDA,SPACE=(1700,(800,400))  
//SYSPRINT DD SYSOUT=*,DCB=BLKSIZE=1089                      
//SYSPUNCH DD SYSOUT=*                                       
//SYSLIN   DD DSN=&&OBJ,UNIT=SYSDA,SPACE=(80,(200,50)),      
//            DISP=(MOD,PASS),DCB=BLKSIZE=3200               
//SYSIN    DD DUMMY                                          
//LNK      EXEC PGM=HEWL,                                    
//            COND=(0,NE,ASM)                                
//SYSLIN   DD DSN=&&OBJ,DISP=(OLD,DELETE)                    
//SYSLMOD  DD DSN=[userid].LOADLIB,DISP=SHR                     
//SYSUT1   DD DSN=&&TEMP2,SPACE=(1024,(50,20))               
//SYSPRINT DD SYSOUT=*,DCB=(RECFM=FB,LRECL=121,BLKSIZE=1210)
//         PEND                                              
Procedure for assembler and binder

The parm to ASMA90 is ‘DECK,OBJECT’ which makes the assembler write exactly the same object code and punch to both SYSLIN and SYSPUNCH. You can NOT direct PUNCH to one DD-card and the object deck to another.

torsdag den 17. september 2015

How the assembler works (4)

How to let a macro do all the tedious coding for you

This time I will make a macro with you that can be used to make all the tedious work at the start of the program and at the end. I will show the macro in its full at last in the blog. You will then be able to copy and paste it to your own system and tailor it to your needs.(*1)


The Macro is called PROGRAM and it takes four states which are coded right after PROGRAM

PROGRAM EQU

(not mandatory but strongly recommended)
Defines the register EQU
[DSECT name]
PROGRAM STORAGE
[,STORAGEREG=]
First occurrence. Coded before and after your fields
STORAGEREG=[register]
Default is ‘R12’. Base reg for storage DSECT

PROGRAM STORAGE

Second occurrence. Calculates the length of your storage
program name
PROGRAM START
[,SAVEAREA=]
[,SADSECT=]
SAVEAREA=[name of savearea]. Default SAVEAREA’
SADSECT=[Name of the savearea DSECT. Default ‘SA’
return label
PROGRAM END
[,RETURNCODE=]
RETURNCODE=[return code to calling program.]
Default ‘0’


Examples

A minimum program could look like this:
       PROGRAM EQU                                                     00010000
       PROGRAM STORAGE                                                 00020000
HW      DS      0F                                                      00020100
HWLEN   DS      H                                                       00020200
HWTEXT  DS      CL16                                                    00021000
       PROGRAM STORAGE                                                 00030000
PGM3    PROGRAM START                                                   00040000
       MVC     HWTEXT,=CL(L'HWTEXT)'HELLO WORLD'                       00050002
       MVC     HWLEN,=Y(L'HWTEXT)                                      00060000
       LA      R2,HW                                                   00061002
       WTO     TEXT=(R2),ROUTCDE=11                                    00070002
RETURN  PROGRAM END                                                     00080002
       END                                                             00090000


and the output will be this.
19.51.28 JOB05893  +PGM3 - 09/17/15-19.51
19.51.28 JOB05893  +HELLO WORLD
As you can see. Coding gets much easier.




Assembler list

It is much easier to understand with examples. I show you (or some of) the results of each invocation of the macro PROGRAM. One example per state.


EQU
             1         PROGRAM EQU
00000           2+R0       EQU   0                                                        01-PROGR
00001           3+R1       EQU   1                                                        01-PROGR
00002           4+R2       EQU   2                                                        01-PROGR
00003           5+R3       EQU   3                                                        01-PROGR
00004           6+R4       EQU   4                                                        01-PROGR
00005           7+R5       EQU   5                                                        01-PROGR
00006           8+R6       EQU   6                                                        01-PROGR
00007           9+R7       EQU   7                                                        01-PROGR
00008          10+R8       EQU   8                                                        01-PROGR
00009          11+R9       EQU   9                                                        01-PROGR
0000A          12+R10      EQU   10                                                       01-PROGR
0000B          13+R11      EQU   11                                                       01-PROGR
0000B          14+BASEREG  EQU   11                                                       01-PROGR
0000C          15+R12      EQU   12                                                       01-PROGR
0000D          16+R13      EQU   13                                                       01-PROGR
0000E          17+R14      EQU   14                                                       01-PROGR
0000F          18+R15      EQU   15                                                       01-PROGR


STORAGE

              19         PROGRAM STORAGE                                                 00020000
00000 00012       20+STORAGE     DSECT                                                       01-PROGR
              21 HW      DS      0F                                                      00020100
000000         22 HWLEN   DS      H                                                       00020200
000002         23 HWTEXT  DS      CL16                                                    00021000
               24         PROGRAM STORAGE                                                 00030000
               25+STORAGE_LEN       EQU *-STORAGE                                         01-PROGR


START

            26 PGM3    PROGRAM START                                                   00040000
00000 00048    27+SA             DSECT                                                    01-PROGR
000000         28+SAVEAREA       DS    18F    SAVE AREA                                   01-PROGR
00048          29+SA_LEN              EQU *-SA                                            01-PROGR
00000 00188    30+PGM3     CSECT                                                          01-PROGR
               31+PGM3     AMODE 31                                                       01-PROGR
               32+PGM3     RMODE ANY                                                      01-PROGR
               33+         ENTRY PGM3                                                     01-PROGR
              and a lot more ….


The macro will
  • Define the savearea DSECT
  • Save registers in callers savearea
  • Getmain a new savearea for this program
  • set the base register (BASEREG)
  • Getmain the storage for this programs variables/fields
  • and set its base register


END



             131 RETURN  PROGRAM END                                                     00080002
               132+*************************
               133+*
               134+*************************
000120         135+RETURN   DS    0H                                                       01-PROGR
    4120 0012 136+         LA    R2,STORAGE_LEN                                           01-PROGR
000124         140+         CNOP  0,4                                                      02-FREEM
              141+         B     *+12-4*0-2*0                BRANCH AROUND DATA ØO2C      02-FREEM
                                                                and a lot more ….


Freemains areas previous getmained by the macro and returns with cond code (default zero).

Bonus info

The macro above calls the IBM-provided macro FREEMAIN but the assembler does not write the called FREEMAIN macro out in the assembler list. I don’t know why but it writes the calling level and first five characters of the called macro in the very right of the line. That can be useful in a debug situation.


Macro Code

You do not need to read the next if you do not want to know how the macro works. If you will just use it as any other IBM provided macro you just clip the examples below and paste them one after the other into one PROGRAM macro.


Initial code


        MACRO
&LABEL   PROGRAM &STATE,&BASEREG=BASEREG,
              &STORAGEREG=R12,&SAVEAREA=SAVEAREA,&SADSECT=SA,
              &RETURNCODE=0
.***************************************************************
.** DEFINE THIS MACROS GLOBAL VARIABLES
.***************************************************************
        GBLC  &GBLC_SAVEAREA,&GBLC_SADSECT,&GBLC_BASEREG
        GBLC  &GBLC_LABEL,&GBLC_STORAGEREG
        GBLB  &STORAGE_DEFINED,&TRUE,&FALSE
&TRUE    SETB  1
&FALSE   SETB  0
.***************************************************************
.** DETERMINE WHICH SECTION TO EXECUTE
.***************************************************************
        AIF   ('&STATE' EQ 'START').START
        AIF   ('&STATE' EQ 'END').END
        AIF   ('&STATE' EQ 'EQU').EQU
        AIF   ('&STATE' EQ 'STORAGE').STORAGE
        MNOTE 8,'FIRST PARAMETER WRONG.'
        AGO   .MACROEND


We need several Global Literals in this macro so I define them first. That is "best practice" to do it at the top of the macro. I use a boolean test so I set &TRUE and &FALSE. That makes the boolean test and set more readable.
I then test for each state and go to each section accordingly. You will of course get a cond code of eight and an error text if you do not obey the rules of the macro.

EQU
The EQU will create the EQU for the general registers
.EQU        ANOP
R0       EQU   0
R1       EQU   1
R2       EQU   2
R3       EQU   3
R4       EQU   4
R5       EQU   5
R7       EQU   7
R8       EQU   8
R9       EQU   9
R10      EQU   10
R11      EQU   11
BASEREG  EQU   11
R12      EQU   12
R13      EQU   13
R14      EQU   14
R15      EQU   15
         AGO   .MACROEND


Storage

This section is divided into two; one before storage definition and one after.


.STORAGE    ANOP
           AIF   (&STORAGE_DEFINED).STODEF
           AIF    ('&LABEL' EQ '').NO_LABEL
&GBLC_LABEL SETC '&LABEL'
           AGO   .DEFINE
.NO_LABEL   ANOP
&GBLC_LABEL SETC 'STORAGE'
.******************************
.DEFINE     ANOP
&GBLC_STORAGEREG SETC '&STORAGEREG'
&GBLC_LABEL DSECT
&STORAGE_DEFINED SETB (&TRUE)
           AGO   .MACROEND
.******************************
.STODEF     ANOP
&GBLC_LABEL._LEN  EQU *-&GBLC_LABEL
           AGO   .MACROEND


  • First we check which section of STORAGE we will enter by checking the boolean &STORAGE_DEFINED.
  • In the first section we then check to see whether the &LABEL is set. That is used to give name to the DSECT we are about to define. If it is not defined we use the default 'STORAGE'.
  • We can now start defining the DSECT (in bold) and its base register which is in &STORAGEREG. At last we set the boolean (&STORAGE_DEFINED) to &TRUE to indicate that the first definition of storage has been done. (Please note the SETBinary)
  • The second section just calculates the length of the storage. That will be used during getmain of the storage. We never need to know how big our storage is (*2).


START

.START   ANOP
&GBLC_SAVEAREA   SETC '&SAVEAREA'
&GBLC_SADSECT    SETC '&SADSECT'
&GBLC_STORAGEREG SETC '&STORAGEREG'
.**************************
.** DSECT FOR THE REGISTER
.** SAVE AREA
.**************************
&GBLC_SADSECT  DSECT
&GBLC_SAVEAREA DS    18F    SAVE AREA
&GBLC_SADSECT._LEN  EQU *-&GBLC_SADSECT
.**************************
.** START THE CSECT
.**************************
&LABEL   CSECT
&LABEL   AMODE 31
&LABEL   RMODE ANY
        ENTRY &LABEL
        SAVE  (14,12),,'&LABEL - &SYSDATE..&SYSTIME'
        BASR  &BASEREG,0
        USING *,&BASEREG
        LR    2,1         SAVE THE PARM REG FOR LATER USE
        WTO   '&LABEL - &SYSDATE.-&SYSTIME'
RSA      GETMAIN RC,LV=&GBLC_SADSECT._LEN
        ST    1,8(,13)
        ST    13,4(,1)
        LR    13,1
        ST    2,0(,13)
        USING &GBLC_SADSECT,13
        AIF   (NOT &STORAGE_DEFINED).STARTEND
        GETMAIN RC,LV=&GBLC_LABEL._LEN
        LR    &GBLC_STORAGEREG,1
        USING &GBLC_LABEL,&GBLC_STORAGEREG
.STARTEND ANOP
        L     1,0(,13)
        AGO   .MACROEND
  • I start by setting the GBLC’s from the input parameters to the macro
  • and then defines the DSECT for the savearea.
  • The real program starts with a CSECT, AMODE, RMODE, and ENTRY.
  • We save the calling program’s registers
  • Load the base register and set the USING register. Probably 11.
  • Then I save the pointer to the parm (R1) in R2 in order to save it for future use.
  • Then I write the date of assembling out  with WTO. Very nice if the program is not used a million times. You know what I mean.
  • Now it’s time to get some storage for the save area. Try to figure out what the four instruction between GETMAIN and USING are doing. I will probably come back to that in a later blog post.
  • If we also have used the STORAGE state of PROGRAM we will also get some storage for that and set the base register for that storage (Default R12).
  • (Future use): The last instruction sets the register 1 to point to the parm as it does at start in any assembler program.


END

.END     ANOP
        AIF   ('&LABEL' EQ '').NO_LABEL
&LABEL   DS    0H
        AGO   .RETURN
.NOLABEL ANOP
RETURN   DS    0H
.RETURN  ANOP
        AIF   (NOT &STORAGE_DEFINED).NO_STORAGE_DEFINED
        LA    R2,&GBLC_LABEL._LEN
        FREEMAIN RC,LV=&GBLC_LABEL._LEN,A=(&GBLC_STORAGEREG)
.NO_STORAGE_DEFINED ANOP
        L     R13,&GBLC_SAVEAREA+4
        LA    R12,&GBLC_SAVEAREA
        LA    R2,&GBLC_SADSECT._LEN
        FREEMAIN RC,LV=&GBLC_SADSECT._LEN,A=(R12)
        LM    R14,R12,12(R13)
        RETURN RC=&RETURNCODE
        LTORG
        AGO   .MACROEND
  • If the END state has a label we use that label so we can branch to it. You might wish to have more than one END with different RETURNCODEs. Each of these generates all the code here and one could argue that is waste. Yes, - feel free to change the macro to solve it.
  • If we define the storage and getmain’ed it we will of course freemain it again. Every good boy and girl clear up in storage after themselves!!
  • That goes for the save area as well.
  • We return to the caller with a return code.
  • And finally, we define literals.


We conclude the macro with these two lines.


.MACROEND ANOP
         MEND


(*1) - PROGRAM will NOT make you assembler program reentable.
(*2) - I will talk about base register limitations later. Let us assume that 4096 bytes are enough