4

OK -I'm sure I'm doing something stupid, but I can't see it.

I have a small sample assembler program which I'm going to provide to a colleague who wants to learn assembler, but it has a weird bug I need to iron out first. .

When the output file (DDNAME SYSUT2) is a temporary 80-byte file, it works fine. Change SYSUT2 to SYSOUT=* and it loops from the 'Closing file' WTO.

So here's the program:

//C.SYSLIB   DD  DISP=SHR,DSN=SYS1.MACLIB                               
//           DD  DISP=SHR,DSN=SYS1.MODGEN                               
//           DD  DISP=SHR,DSN=SYS1.ASM.SASMMAC2                         
//*          DD  DISP=SHR,DSN=JOCS065.STEVE.SOURCE                      
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  4 Line(s) not Displayed 
         TITLE  'TEST PROGRAM'                                          
SYMBOLIC CSECT                                                          
         ASMDREG                      .Register equates                 
         SAVE  (14,12),,'SYMBOLIC PARM SUB &SYSDATE &SYSTIME'           
                                                                        
         LR    R12,R15                .R12 -> entry point               
         USING SYMBOLIC,R12           .Establish addressability         
* Get the parm passed                                                   
         L     R1,0(R1)               .R1 -> parm                       
         LTR   R1,R1                  .Is there one?                    
         BZ    RETURN                 .No - return                      
* We have a parm                                                        
         LH    R2,0(R1)               .R2 = PARM LENGTH                 
         LTR   R2,R2                  .parm length = 0?                 
         BZ    RETURN                 . Yes - return                    
* And the parm has length                                               
         BCTR  R2,0                   .Decrement length for move        
         EX    R2,SETOUT              .Move parm to output              
* Open output file and write parm to output                             
         LA    R4,FILE1            .R4 -> DCB for output file.          
         USING IHADCB,R4           .Establish addressability            
         OPEN  (FILE1,OUTPUT)      .Open log file                       
         TM    DCBOFLGS,DCBOFOPN   .Open successful ?                   
         BZ    BADOPEN             .No - go to error routine            
         WTO   'SIMSG001 OPEN successful.'                              
         DROP  R4                                                       
         WTO   'SIMSG003 writing to file'                               
         PUT   FILE1,OUTREC           .PUT output record                
         WTO   'SIMSG004 Closing file...'                               
         CLOSE (FILE1)                .close files                      
         WTO   'SIMSG005 Returning to caller...'                        
         B     RETURN                 .Return                           
                                                                        
* EXecute instructions                                                  
SETOUT   MVC   OUTREC(0),2(R1)                                          
*****************************************************************       
*                    PROGRAM TERMINATION                                
*****************************************************************       
RETURN   DS    0H                                                       
         WTO   'SIMSG006 RESTORING REGISTERS AND RETURNING'             
         XR    R15,R15                .Clear R15 (RC=0)                 
         RETURN (14,12),RC=(15)       .Restore caller's regs and return 
                                                                        
BADOPEN  DS    0H                                                       
         WTO   'SIMSG002 OPEN failed.'                                  
         B     RETURN                                                   
*****************************************************************       
*                       STORAGE AREAS                                   
*****************************************************************       
OUTREC   DC    80C' '                 .OUTPUT CARD IMAGE                
*****************************************************************       
*                    MACROS AND LITERALS                                
*****************************************************************       
         PRINT NOGEN                                                    
FILE1    DCB   RECFM=F,LRECL=80,BLKSIZE=80,                            X
               DSORG=PS,DDNAME=SYSUT2,MACRF=PM                          
         DCBD                                                           
         PRINT GEN                                                      
*                                                                       
         LTORG                         LITERAL STORAGE                  
         END                                                            
//L.SYSLMOD  DD DISP=SHR,DSN=<your.load.library>(SYMBOLIC)               
//L.SYSPRINT DD SYSOUT=*                                                
//L.SYSIN    DD DUMMY                                                   

and JCL to execute it:

//JOBLIB   DD   DISP=SHR,DSN=<your.load.library>          
//*                                                      
//STEP     EXEC PGM=SYMBOLIC,PARM='THIS IS MY PARAMETER'                                
//SYSUT2 DD SYSOUT=*     

                            

Submit this and the job loops and you have to cancel it. You get 'THIS IS MY PARAMETER' written to SYSUT2 but the WTOs show:

+SIMSG001 OPEN successful.                 
+SIMSG003 writing to file                  
+SIMSG004 Closing file...                  
+SIMSG005 Returning to caller...           
+SIMSG006 RESTORING REGISTERS AND RETURNING
+SIMSG004 Closing file...                  
+SIMSG005 Returning to caller...           
+SIMSG006 RESTORING REGISTERS AND RETURNING
+SIMSG004 Closing file...                  
+SIMSG005 Returning to caller...           
+SIMSG006 RESTORING REGISTERS AND RETURNING
+SIMSG004 Closing file...                  
+SIMSG005 Returning to caller...           
+SIMSG006 RESTORING REGISTERS AND RETURNING
+SIMSG004 Closing file...                  
+SIMSG005 Returning to caller...           
+SIMSG006 RESTORING REGISTERS AND RETURNING

forever.

Change SYSUT2 to :

//SYSUT2 DD DISP=(MOD,PASS),                             
//          DSN=&AMSCNTL,                                
//          UNIT=SYSDA,                                  
//          SPACE=(TRK,1) 

works (I know as I had another step to GENER the temp file to Sysout).

So having FILE1 (SYSUT2) being SYSOUT causes a corruption such that it appears R14 gets restored only to point back to the WTO for the SIMSG004.

This is an old program so it should work. I haven't done much assembler of the last 4 years but have some code using the linkage stack and relative addressing but want to stick with some simple base-displacement code first for teaching this colleague.

Any idea where this is going wrong?

Steve Ives
  • 7,894
  • 3
  • 24
  • 55

2 Answers2

5

You don't establish a new register save area after saving the registers. So, R13 still points to the same area and the next one saving registers will overwrite, and destroy the the initial return address (and more).

The PUT macro will call different code for real data sets, and for SYSOUT (JESx) data sets. The difference must be there: If the PUT routine for sysout stores registers at the address in R13, the return address (R14) in the save area will now be the instruction after the PUT. Thus the loop.

For non-reentrant code this should look like this at entry:

         ...
         LR    R12,R15                     .R12 -> entry point               
         USING SYMBOLIC,R12                .Establish addressability      

* Establish addressability to new save area and chain the save areas
         ST    R13,SAVEAREA+4               Set backward chain pointer
         LR    R15,R13
         LA    R13,SAVEAREA                 Let R13 point to new SA
         ST    R13,8(,R15)                  Set forward chain pointer

And before retuning, get back addressability to caller's SA:

RETURN   DS    0H
         ...

* Establish addressability to caller's SA
         L     R13,4(,R13)

         XR    R15,R15                .Clear R15 (RC=0)                 
         RETURN (14,12),RC=(15)       .Restore caller's regs and return 

Finally, you need to define your own save area:

         ...
*****************************************************************       
OUTREC   DC    80C' '                 .OUTPUT CARD IMAGE                
*****************************************************************    
         ...
SAVEAREA DS    18F
         ...
phunsoft
  • 2,674
  • 1
  • 11
  • 22
  • So I need to provide an area for any called routine to store my registers? Maybe `NEWSAVE DC 18F` in my storage areas with a `LA R13,NEWSAVE` at the start? The SAVE and RESTORE macros don't provide this? – Steve Ives Mar 23 '21 at 16:17
  • Found this : https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.asma100/asmp1021169.htm. Previously I've used some macros written by someone else that did all this, so never really coded by own register save/restore – Steve Ives Mar 23 '21 at 16:20
  • I suggest to read the "z/OS MVSProgramming: Assembler Services Guide", chapter on linkage. [https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.ieaa600/chap2.htm] This has all the details for non-reentreant code, reentrant code, etc. – phunsoft Mar 23 '21 at 16:39
  • 1
    *The SAVE and RESTORE macros don't provide this?* No, they don't. – phunsoft Mar 23 '21 at 16:41
1

Same answer as @phunsoft but with more of the why. The WTO and OPEN execute SVCs so no save area needed; CLOSE too. However, the PUT is a branch and set register. You need to allocate a 72-byte SAVEAREA, or GETMAIN, one or however you want to get it. If you place it in the existing CSECT its not re-entrant but that is for another day. This means, that when you return, it tries to reload the SAVEAREA you didn't allocate and returns to right after the PUT which is where your loop is coming from.

On my system it is

.PUT output record
 000000A6 4110 C1D0               000001D0     78+         LA    1,FILE1                           LOAD PARAMETER REG 1   02-IHBINNRA
 000000AA 4100 C180               00000180     79+         LA    0,OUTREC                          LOAD PARAMETER REG 0   02-IHBINNRA
 000000AE 1FFF                                 80+         SLR   15,15               CLEAR REGISTER FOR ICM      @L1A 01-PUT
 000000B0 BFF7 1031               00000031     81+         ICM   15,7,49(1)      LOAD PUT ROUTINE ADDRESS        @L1C 01-PUT
 000000B4 0DEF                                 82+         BASR  14,15               LINK TO PUT ROUTINE         @L3C 01-PUT

So the PUT saves over the original save area and when return is issued you jump back to after the PUT returned.

Ok, so I can't help myself. This is a better prolog

             TITLE  'TEST PROGRAM'                                          
    SYMBOLIC CSECT                                                          
             ASMDREG                      .Register equates                 
             SAVE  (14,12),,'SYMBOLIC PARM SUB &SYSDATE &SYSTIME'           
                                                                            
             LR    R12,R15                .R12 -> entry point               
             USING SYMBOLIC,R12           .Establish addressability  

Add these at the prolog

             GETMAIN LV=72
             ST     R13,4(R1)
             LR     R13,R1

Then, at the epilog

             LR     R2,R13
             FREEMAIN LV=72,LA=(R2)                 
             L      R13,4(R13)
             LM     R14,R12,12(R13)
             LA     R15,0                    
             BR     R14   

SR, versus XR versus LA but the debates have consumed far more time than any of the clock cycles have.

So, this is WHY you need a SAVEAREA. But since this is an OG 24-bit assembler program you have no worries. It used to be 16MB was enough. It looks like JES2 intercepts the SYSOUT and does some magic but the above will work regardless of your DD preferences.

I expect that JES trusts no one to do SAVEAREAs right which is what I would do

Hogstrom
  • 3,581
  • 2
  • 9
  • 25
  • 2
    IHMO, releasing dynamically obtained storage before exiting is not an option, but a **must**. Not doing so is causing storage leaks. It doesn't matter how likely it is for it to bite you, not doing it is bad progrmming practice. So in favour of others reading your post, I suggest you add the ```FREEMAIN``` to your example. – phunsoft Mar 25 '21 at 12:33
  • 1
    Shamed me into it @phunsoft Added the FREEMAIN :) – Hogstrom Mar 25 '21 at 19:54
  • My apologies. No offence meant! – phunsoft Mar 25 '21 at 21:06
  • @phunsoft no apologies necessary. It was the right call. I was being lazy. Thanks – Hogstrom Mar 26 '21 at 01:48