Reading A PDS With EXCP

Now we can look at using EXCP channel programming to read a partitioned dataset.  We will start with the structure of the directory.  The directory consists of a set of 256 byte blocks, each with an 8 byte key.  The number of directory blocks is determined when the PDS is initially allocated.  An EOF mark occurs immediately following the last directory block.

+-+-----+   +-+-----+   +-+-----+   +-+-----+     +---+
|K| Dir |   |K| Dir |   |K| Dir |   |K| Dir | ... |EOF|
+-+-----+   +-+-----+   +-+-----+   +-+-----+     +---+

Each directory block begins with a 2 byte value indicating how many bytes in the block are used for directory information (including the 2 bytes for the length). Member entries begin immediately following the length.

+--+--------+--------+--------+     +--------+-------+
|LL|Mbr-1   |Mbr-2   |Mbr-3   | ... |Mbr-n   |00...00|
+--+--------+--------+--------+     +--------+-------+

Member entries are variable in length. There is a fixed portion which is always present followed by optional User Data. The user data area is between 0 and 62 bytes.

+--------+---+-+-----------+
|Mbr-Name|TTR|C| User Data |
+--------+---+-+-----------+

The fixed portion of the member entry is 12 bytes. The first eight bytes contain the member name. The next three bytes contain a TTR pointer to the beginning block of the entry. The C field contains the size of the user data (in half words) in bits 3 through 7 (x’1F’). If bit 0 is set the entry is an Alias. To get the size of the user data we locially AND the C field with a mask of x’1F’ and then multiply by 2.

Member entries are stored in ascending sequence.  The key contains the highest member name contained in the block.  The logical end of the directory is marked with a member name of x’FFFFFFFFFFFFFFFF’.

The key allows us to locate the directory block containing a specific entry without having to read through the contents of every block.  By using a Search Key High or Equal  the correct directory block can be quickly located.

         DC    X'69',AL3(MBRNAME),X'40',X'40',AL2(8)       Search Key Equal or High
         DC    X'08',AL3(*-8),X'40',x'00',AL2(0)           TIC back to Search
         DC    X'06',AL3(IOBUF),X'00',X'00',AL2(256)       Read Data

If we run this channel program against a PDS we should expect the I/O to either complete successfully and read a block of data or it should terminate with a No Record Found condition. Anything else should indicate some type of error condition. This technique depends on the logical EOF in the PDS directory (x’FFFFFFFFFFFFFFFF’) since the Search CCW will not detect the EOF record.

If the record is not found we need to continue our search on the next track of the dataset. We can update our Seek address (MBBCCHHR) and reissue the I/O. This should continue until a record is located and read or an exceptional condition occurs (indicating the PDS structure is damaged).

If our dataset is allocated on a cylinder boundry we can use the multi-track seach command to serach an entire cylinder at a time.

         DC    X'E9',AL3(MBRNAME),X'40',X'40',AL2(8)       Search Key Equal or High Multi-Track
         DC    X'08',AL3(*-8),X'40',x'00',AL2(0)           TIC back to Search
         DC    X'06',AL3(IOBUF),X'00',X'00',AL2(256)       Read Data

If the I/O terminates with a No Record Found condition we update our Seek address to the next cylinder of the dataset. To do this we would add the number of tracks in a cylinder to our relative track address (in the case of a 3350 we would add 30 tracks).

This brings up the question of how do we know if our dataset is allocated in tracks or cylinders. The answer is found in the DEB extent information. Our routine to convert from TTR to MBBCCHHR uses the DEB extent information but I did not present any details, just the code to do the conversion. Here is the format of a DEB extent entry.

         X    - File Mask
         AL3  - UCB Address
         XL2  - BIN
         XL2  - Extent Begin CC
         XL2  - Extent Begin HH
         XL2  - Extent End CC
         XL2  - Extent End HH
         FL2  - Number of Tracks In This Extent

The File Mask is the parameter for a Set File Mask CCW. We have already looked at the Set File Mask parameters but here is a quick refresh of the bits we are interested in.

01.. .... - Do Not Allow Write
...1 1... - Do Not Allow Seek Cyl or Seek Head
...1 0... - Do Not Allow Seek Cyl (Seek Head allowed)

Below is an extent entry from a DEB.

 58001E68 000000EB 001000EB 0019000A

The file mask tells us the dataset is opened for input only (write commands not allowed). Because Seek Cyl/Head is not allowed we can tell our dataset was allocated in tracks and multi-track commands are not allowed. The UCB address of the dasd device is 001E68. The BIN is always x’0000′. The extent begins at Cylinder x’00EB’ Track x’0010′ and ends at Cylinder x’00EB’ Track x’0019′ and there are 10 (x’000A’) tracks in the extent.

Here is the extent entry for a dataset allocated in cylinders.

 50001E68 000001AE 000001AE 001E001E

Notice that here the file mask allows Seek Head so we can use multi-track commands.

EXCP05 – Read PDS Member

Find Block Containing Member Entry

***********************************************************************
*                       FIND DIRECTORY BLOCK                           
*                                                                      
*    ENTRY: R1 = A(CL8'MEMBER NAME')                                   
*                                                                      
*    EXIT:  R15 = RC                                                   
*                 0 - ENTRY LOCATED                                    
*                 8 - ERROR                                            
*           R1  = A(DIR BLOCK)                                         
*                                                                      
***********************************************************************

Will will call our routine with register 1 containing the address of an 8-byte member name. On exit register 15 will contain a return of 0 if a block was located that may contain the member entry. A return code of zero does not mean the directory entry exists. It only means that IF the entry exists it will be in the block returned. Any other condition returns a value of 8 as the return code and indicates some type of error has occured. Register 1 will contain a pointer to the buffer containing the block that may contain the entry.

FINDBLK  DS    0H                                                
         ST    R14,XTFIBLK        SAVE RETURN ADDRESS            
*                                                                
         MVC   SRCHNAME,0(R1)     COPY MEMBER NAME TO SEARCH PARM

We begin by saving the return address and then moving the member name to our search key area used by our Search CCW.

         MVI   DIRSRCH,X'69' SEARCH KEY HI/EQ              
         MVI   MULTITRK,0    CLEAR MULTITRACK FLAG         
         LA    R2,EXCPDCB              POINT TO DCB        
         L     R2,44(,R2)              POINT TO DEB        
         LA    R2,32(,R2)              FIRST EXTENT INFO   
         SLR   R1,R1                                       
         IC    R1,0(R2)      GET FILE MASK                 
         N     R1,=A(X'18')  ISOLATE SEEK HEAD             
         C     R1,=A(X'10')  ALLOW SEEK HEAD               
         NOP   FINDB010        NO - BRANCH                 
         BNE   FINDB010        NO - BRANCH                 
*                                                          
         MVI   MULTITRK,1    INDICATE MULTI-TRACK ALLOWED  
         MVI   DIRSRCH,X'E9' SEARCH KEY HI/EQ M/T          
FINDB010 DS    0H

Now we determine if we can use the Multi-Track form of our search. We begin by defaulting to the non-multitrack command code. We also clear the flag byte that indicates multitrack in use. We check the file mask in the DEB to determine if Seek Head is allowed and if so we update the flag byte and the Search command code.

Notice the NOP instruction. I included this so I could change it to an unconditional branch that forces the search to operate in non-multitrack mode for testing even if the dataset is allocated in cylinders.

         LA    R7,0          RELATIVE TRACK NUMBER        
*                                                         
FINDB020 DS    0H                                         
         LR    R1,R7         RELATIVE TRACK NUMBER        
         BAL   R14,GETCCHH   CONVERT TO MBBCCHHR          
         LTR   R15,R15            CHECK RETURN CODE       
         BNZ   FINDBXT8           - BRANCH IF NO GOOD     
*                                                         
         MVC   IOBSEEK(8),MBBCCHHR                        
         MVI   IOBSEEK+7,0        PUT IN RECORD NUMBER    
*                                                         
         LA    R1,DIRSRCH         POINT TO CHANNEL PROGRAM
         ST    R1,IOBCCWAD        SAVE INTO IOB           
*                                                         
         XC    ECB,ECB            CLEAR ECB               
         EXCP  IOB                ISSUE I/O REQUEST       
*                                                         
         WAIT  1,ECB=ECB          WAIT FOR I/O TO COMPLETE
*                                                         
         BAL   R14,PRINTIOB       GO FORMAT IOB

We being by setting our relative track number to zero. The PDS directory always beings on the first track of the dataset. We then calculate the MBBCCHHR and update the IOB seek address. We put the address of our channel program into the IOB. This is necessary since we use the same IOB with different channel programs for reading directory blocks and data blocks. We then schedule the I/O request and wait for it to complete.

DIRSRCH  DC    X'69',AL3(SRCHNAME),X'40',X'00',AL2(8)   
DIRTIC   DC    X'08',AL3(DIRSRCH),X'40',X'00',AL2(0)    
DIRREAD  DC    X'06',AL3(DIRBLKIO),X'00',X'00',AL2(256)

Here is our channel program to find and read the directory block. Note that we do not set the Suppress Length Indication (SLI) bit in the Read CCW. Directory blocks are always 256 bytes in length and an incorrect length condition indicates some type of error.

*                                                        
         CLI   IOBECBAD,X'7F'     NORMAL COMPLETION      
         BE    FINDBXT0             YES - FOUND OUR BLOCK
*                                                        
         CLI   IOBECBAD,X'41'     DEVICE STATUS AVAILABLE
         BNE   FINDBXT8            NO - ERROR            
*                                                        
         TM    IOBCSWFL,X'02'     UNIT CHECK             
         BNO   FINDBXT8            NO - ERROR            
*                                                        
         TM    IOBSENSE+1,X'08'   NO RECORD FOUND        
         BNO   FINDBXT8             NO - ERROR           
*                                                        
         CLI   MULTITRK,1         MULTITRACK ENABLED     
         BNE   FINDB030              NO - NEXT TRACK     
*                                                        
         A     R7,=F'30'          3350 HAS 30 TRK/CYL    
         B     FINDB020           GO SEARCH NEXT CYL     
*                                                 
*                                                 
FINDB030 DS    0H                                 
         A     R7,=F'1'           SEARCH NEXT     
         B     FINDB020                      TRACK

If the I/O completed normally then we have successfuly located and read a directory block. We can return the block to the caller. Otherwise we check for Unit Check and No Record Found indicating we reached the end of a track or cylinder without locating the requested block. If we are in multitrack mode we update our relative track address to the next cylinder. Here I have hardcoded a value of 30 since a 3350 has 30 tracks/cyl. If we are not in multitrack mode we update to the next track and continue our search.

A PDS directory that exceedes one cylinder would be very large. A 3350 track will hold 36 directory blocks. Since there are 30 tracks/cyl a cylinder would contain 1,080 directory blocks.

FINDBXT0 DS    0H                                       
         LA    R15,0              SET RC                
         LA    R1,DIRBLKIO        POINT TO I/O BUF      
         B     FINDBEXT           AND EXIT              
*                                                       
*                                                       
FINDBXT8 DS    0H                                       
         LA    R15,8              SET RC                
         B     FINDBEXT           AND EXIT              
*                                                       
*                                                       
FINDBEXT DS    0H                                       
         L     R14,XTFIBLK        RESTORE RETURN ADDRESS
         BR    R14

And finally our exit points. For a successful read we set the return code to zero and load the address of the I/O buffer into register 1.

Find Directory Entry In Directory Block

***********************************************************************
*                       FIND DIRECTORY ENTRY                           
*                                                                      
*    ENTRY: R1 = A(CL8'MEMBER NAME')                                   
*                                                                      
*    EXIT:  R15 = RC                                                   
*                 0 - ENTRY LOCATED                                    
*                 4 - ENTRY NOT LOCATED                                
*                 8 - ERROR                                            
*           R1  = A(DIR ENTRY)                                         
*                                                                      
***********************************************************************

Here is our routine to locate a directory entry in a PDS directory block. On entry register 1 contains a pointer to the member name. A return code of zero indicates the entry was found and the address of the entry is returned in register 1. A return code of 4 indicates the entry does not exist and a return code of 8 indicates an error has occured.

FINDMBR  DS    0H                                    
         ST    R14,XTFIMBR        SAVE RETURN ADDRESS
*                                                    
         BAL   R14,FINDBLK        GO FIND DIR BLOCK  
         LTR   R15,R15            CHECK RC           
         BNZ   FINDMXT4           BRANCH IF ERROR

We being by saveing the return address. Next we call the FINDBLK routine to locate the block that will contain the entry if it exists.

         SLR   R3,R3                                          
         ICM   R3,B'0011',0(R1)   GET LENGTH OF DIRECTORY DATA
         S     R3,=F'2'           ADJUST OUT LENGTH FLD       
         LA    R2,2(,R1)          POINT TO FIRST ENTRY

We begin the search of the directory block by loading the number of bytes remaining into register 3 and loading register 2 with a pointer to the first member entry. As we continue processing the block R3 will contain the remainding number of bytes and R2 will point to the current member entry being processed.

FINDM010 DS    0H                                                 
         LTR   R3,R3              END OF DATA                     
         BNP   FINDMXT4             YES - ENTRY NOT FOUND         
*                                                                 
         CLC   0(8,R2),SRCHNAME   ENTRY WE ARE LOOKING FOR?       
         BE    FINDMXT0             YES - BRANCH                  
*                                                                 
         SLR   R1,R1                                              
         ICM   R1,B'0001',11(R2)  GET C FIELD                     
         N     R1,=A(X'1F')       LENGTH OF USER DATA IN HALFWORDS
         SLL   R1,1               MULTIPLY BY 2                   
         A     R1,=F'12'          ADD IN FIXED SIZE               
*                                                                 
         AR    R2,R1              NEXT ENTRY                      
         SR    R3,R1              ADJUST LENGTH                   
         B     FINDM010           LOOP BACK

Here is the loop that searches for the member entry. We begin by checking to see if there are any more entries to process. If not then the entry was not found. Next the current entry name is compared to the search name which was saved in the FINDBLK routine. If it matches we found the entry and we are done. If not match we load the C field of the TTRC field into R1. We AND against a mask to keep only the length of the user data and use a SLL to multiply the value by 2 since it is in half words. Next the fixed size is added to obtain the total length of the entry. We adjust our current entry pointer and length remaining and then loop back.

FINDMXT0 DS    0H                                       
         LA    R15,0              SET RC                
         LR    R1,R2              POINT TO DIR ENTRY    
         B     FINDMEXT           AND EXIT              
*                                                       
*                                                       
FINDMXT4 DS    0H                                       
         LA    R15,4              SET RC                
         B     FINDMEXT           AND EXIT              
*                                                       
*                                                       
FINDMEXT DS    0H                                       
         L     R14,XTFIMBR        RESTORE RETURN ADDRESS
         BR    R14

Here are the exit points where the return code is set and if successful the address of the entry is returned in register 1.

Find Routine Driver

 FIND     DS    0H                                   
          ST    R14,XTFIND        SAVE RETURN ADDRESS
 *                                                   
          MVC   PL09MBR,0(R1)      SAVE MEMBER NAME  
          BAL   R14,FINDMBR        FIND DIR ENTRY    
          LTR   R15,R15              WAS IT LOCATED  
          BZ    FIND020                 YES - BRANCH

Here is a testing routine to drive our find member process. On entry R1 points to the member name to be located. The find member routine will be called and the results will be printed.

*                                                       
         MVI   PL09RC,C'4'       SET RC IN MESSAGE      
         C     R15,=F'4'         WAS RC=4               
         BE    FIND010                                  
*                                                       
         MVI   PL09RC,C'8'       SET RC=8               
FIND010  DS    0H                                       
         LA    R1,=C' '                                 
         LA    R15,1                                    
         BAL   R14,PRINT         PRINT BLANK LINE       
         LA    R1,PL09                                  
         LA    R15,PL09L                                
         BAL   R14,PRINT         PRINT RC LINE          
         SLR   R1,R1             INDICATE MBR NOT FOUND 
         B     FINDXT            EXIT

If the return code from FINDMBR was not zero we print a message with the member name and the RC.

FIND020  DS    0H                                          
         ST    R1,XBLKAD          SAVE PTR TO MEMBER ENTRY 
*                                                          
         SLR   R3,R3                                       
         ICM   R3,B'0001',11(R1)       C FIELD             
         N     R3,=A(X'1F')            USER                
         SLL   R3,1                        DATA LEN        
         LA    R3,12(,R3)                                  
         ST    R3,XBLKLEN              LENGTH TO DUMP

If found we save the pointer to the entry and calculate the entry length.

*                                                           
         LA    R1,=C' '                                     
         LA    R15,1                                        
         BAL   R14,PRINT          PRINT BLANK LINE          
*                                                           
         MVI   PL09RC,C'0'                                  
         LA    R1,PL09                                      
         LA    R15,PL09L                                    
         BAL   R14,PRINT          PRINT RETURN CODE LINE    
*                                                           
         LA    R1,XBLKAD                                    
         L     R15,=V(XDUMP)                                
         BALR  R14,R15            DUMP MEMBER ENTRY         
         L     R1,XBLKAD          RESTORE DIR ENTRY ADDRESS 
         B     FINDXT                                       
*                                                       
FINDXT   DS    0H                                       
         L     R14,XTFIND         RESTORE RETURN ADDRESS
         BR    R14

Finally a status message is printed and the directory entry is dumped.

Testing The Find Routine

         LA    R1,=CL8'ACCOUNT'              
         BAL   R14,FIND           FIND MEMBER
*                                            
         LA    R1,=CL8'MSTRJCL'              
         BAL   R14,FIND           FIND MEMBER
*                                            
         LA    R1,=CL8'ZZZZZZZZ'             
         BAL   R14,FIND           FIND MEMBER

We load R1 with the address of a member name and call the driver. I am using SYS1.LINKILB for testing since it has a large number of directory entries. ACCOUNT is located in the first directory block and MSTRJCL is located in the last. I also did a locate of ZZZZZZZZ which does not exist and should return RC=4.

Reading Member Data

***********************************************************************
*                       READ A BLOCK BY TTR                            
*                                                                      
*    ENTRY: R1 = 0TTR                                                  
*                                                                      
*    EXIT:  R15 = RC                                                   
*                 0 - BLOCK SUCCESSFULLY READ                          
*                 4 - NO RECORD FOUND                                  
*                 8 - EOF                                              
*                12 - ERROR                                            
*           R0  = BLOCK SIZE                                           
*           R1  = DATA                                                 
*                                                                      
***********************************************************************

On entry register 1 contains the TTR of the block to be read. On exit R0 will contain the size of the block read and R1 will point to the data in the I/O buffer. R15 contains a return code.

READTTR  DS    0H                                       
         STM   R14,R12,12(R13)    SAVE CALLERS REGISTERS
         LA    R14,SAVERTTR       CHAIN                 
         ST    R13,4(,R14)             ON               
         ST    R14,8(,R13)               SAVE           
         LR    R13,R14                       AREA       
*                                                       
         LR    R7,R1         SAVE TTR

We being by saving the caller’s registers and establishing a new save area. The TTR is copied into R7.

         LR    R1,R7         RELATIVE TRACK NUMBER   
         SRL   R1,8          SHIFT OFF R (KEEP TT)   
         BAL   R14,GETCCHH   CONVERT TO MBBCCHHR     
         LTR   R15,R15            CHECK RETURN CODE  
         BNZ   READTXTC           - BRANCH IF NO GOOD
*                                                         
         LA    R1,READSRCH        POINT TO CHANNEL PROGRAM
         ST    R1,IOBCCWAD        SAVE INTO IOB           
*                                                         
         MVC   IOBSEEK(8),MBBCCHHR                        
         STCM  R7,B'0001',IOBSEEK+7   PUT IN RECORD NUMBER

The relative track number is converted into a Seek Address. The address of the channel program for reading a block is placed in the IOB as is the full Seek address.

READSRCH DC    X'31',AL3(IOBSRCH),X'40',X'00',AL2(5) 
READTIC  DC    X'08',AL3(READSRCH),X'40',X'00',AL2(0)
READREAD DC    X'06',AL3(*-*),X'20',X'00',AL2(19069)

Here is the channel program to read a block. Notice we attempt to read a block of the maximum size for a 3350. The SLI bit is set. This will allow us to read a block of unknown length.

         XC    ECB,ECB            CLEAR ECB               
         EXCP  IOB                ISSUE I/O REQUEST       
*                                                         
         WAIT  1,ECB=ECB          WAIT FOR I/O TO COMPLETE
*                                                         
         BAL   R14,PRINTIOB       GO FORMAT IOB           
*                                                         
         CLI   IOBECBAD,X'7F'     NORMAL COMPLETION       
         BE    READTXT0             YES - FOUND OUR BLOCK

Now we issue the EXCP and wait for the I/O to complete. We then check see see if a block was successfully read.

          CLI   IOBECBAD,X'41'     DEVICE STATUS AVAILABLE 
          BNE   READTXTC            NO - ERROR             
 *                                                         
          TM    IOBCSWFL,X'01'     UNIT EXCEPTION          
          BO    READTXT8            YES - EOF RC=8         
 *                                                         
          TM    IOBCSWFL,X'02'     UNIT CHECK              
          BNO   READTXTC            NO - ERROR             
 *                                                         
          TM    IOBSENSE+1,X'08'   NO RECORD FOUND         
          BO    READTXT4             YES - RC=4            
          B     READTXTC           ELSE - ERROR

Next we check to see if device status is available in the IOB. If not we return an error condition. We then check for Unit Exception indicating an End Of File condition. Finally we check for a No Record Found condition. If none of these we return an error status.

READTXT0 DS    0H                                           
         LA    R15,0              SET RC                    
         SLR   R1,R1                                        
         SLR   R0,R0                                        
         ICM   R0,B'0011',READREAD+6  READ SIZE FROM CCW    
         ICM   R1,B'0011',IOBRESDL    RESIDUAL SIZE FROM CSW
         SR    R0,R1                  CALC RECORD SIZE READ 
         ICM   R1,B'0111',READREAD+1  DATA BUFFER ADDRESS   
         B     READTEXT           AND EXIT

If a record was read we calculate the size by subtracting the CWS Residual value from the size in the Read CCW.

READTXT4 DS    0H                         
         LA    R15,4              SET RC  
         B     READTEXT           AND EXIT
*                                         
*                                         
READTXT8 DS    0H                         
         LA    R15,8              SET RC  
         B     READTEXT           AND EXIT
*                                         
*                                         
READTXTC DS    0H                         
         LA    R15,12             SET RC  
         B     READTEXT           AND EXIT
*                                         
*                                         
READTEXT DS    0H                         
         L     R13,4(,R13)                
         L     R14,12(,R13)               
         LM    R2,R12,28(R13)             
         BR    R14

And finally the exit code.

Read Block Test Routine

         LA    R1,=CL8'IEFBR14'                   
         BAL   R14,FIND           FIND MEMBER     
*                                                 
         LTR   R1,R1              ANY DIR ENTRY   
         BZ    EXIT                NO - EXIT      
*                                                 
         SLR   R2,R2                              
         ICM   R2,B'0111',8(R1)   GET TTR         
         ST    R2,CURTTR          SAVE CURRENT TTR

We being by loacating the directory entry for a PDS memeber. I chose to use IEFBR14 for this test. We get the TTR pointer to the first block of the member from the directory entry.

RDMBR010 DS    0H                                  
         L     R1,CURTTR          CURRENT BLOCK TTR
         BAL   R14,READTTR        GO READ BLOCK    
*                                                  
         B     *+4(R15)           BRANCH ON RC     
         B     RDMBR020           - READ SUCCESSFUL
         B     RDMBR030           - NO RECORD FOUND
         B     RDMBR040           - EOF            
         B     RDMBR050           - ERROR

Next we call our READTTR routine to read a block by TTR. A branch table is used to evaluate the return code.

RDMBR020 DS    0H                                       
         ST    R0,XBLKLEN         SAVE BLOCK LEN        
         ST    R1,XBLKAD          SAVE BLOCK ADDRESS    
         LA    R1,=C' '                                 
         LA    R15,1                                    
         BAL   R14,PRINT          PRINT BLANK LINE      
         LA    R1,XBLKAD                                
         L     R15,=V(XDUMP)                            
         BALR  R14,R15            DUMP BLOCK            
*                                                       
         SLR   R1,R1                                    
         ICM   R1,B'0001',CURTTR+3  GET CURRENT RECORD  
         LA    R1,1(,R1)            INCREMENT RECORD    
         STCM  R1,B'0001',CURTTR+3  SAVE BACK           
         B     RDMBR010             GO READ NEXT RECORD

If a block was successfully read we save the ponter to the data and the data length so we can dump the block. We then increment the record number in our current TTR pointer.

RDMBR030 DS    0H                                       
         SLR   R1,R1                                    
         ICM   R1,B'0011',CURTTR+1  GET CURRENT TRACK   
         LA    R1,1(,R1)            INCREMENT TRACK     
         STCM  R1,B'0011',CURTTR+1  SAVE TRACK          
         MVI   CURTTR+3,1           RECORD 1            
         B     RDMBR010             GO READ NEXT RECORD

A No Record Found condition indicates we have read all the records on the current track. In this case we increment the current relative track number and reset the current record to one.

RDMBR040 DS    0H                                    
         LA    R1,=C' '                              
         LA    R15,1                                 
         BAL   R14,PRINT            PRINT BLANK LINE 
*                                                    
*                    1...5...10...15...20...25       
         LA    R1,=C'*** END OF FILE ***'            
         LA    R15,19                                
         BAL   R14,PRINT            PRINT EOF MESSAGE
         B     EXIT

For End Of File we just print a message and exit.

RDMBR050 DS    0H                                   
         LA    R1,=C' '                             
         LA    R15,1                                
         BAL   R14,PRINT            PRINT BLANK LINE
*                                                   
*                    1...5...10...15...20...25      
         LA    R1,=C'*** I/O ERROR ***'             
         LA    R15,17                               
         BAL   R14,PRINT            PRINT ERR MSG   
         B     EXIT

For an I/O Error we print a message and exit.

Program Execution Output

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000D5000000                                           
   BYTES READ = 0100 (   256)                                        

   FIND MEMBER = <ACCOUNT >   RC = 0                                  
0000 C1C3C3D6 E4D5E340 00FA1B2C 00FA2100  00000000 C2E20010 58105800 00008800
0020 00010000

Here is the eight-byte member name followed by the TTR (x’00FA1B’) and C (x’2C’).  The remainder is the user data section that contains information about the load module.

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000D5000000                                           
   BYTES READ = 0100 (   256)                                        

   FIND MEMBER = <MSTRJCL >  RC = 0   
0000 D4E2E3D9 D1C3D340 0122022C 01220700  00000000 03F20003 C003C000 00008800
0020 00010000

Here is the find output for MSTRJCL.

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000D5000000                                           
   BYTES READ = 0100 (   256)                                        

   FIND MEMBER = +ZZZZZZZZ+  RC = 4

Here is the output for a member name that does not exist.

I/O REQUEST                                                           
   COMPLETION CODE = 7F                                               
   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0) 
   --- DEVICE STATUS  = CE DE                                         
   --- CHANNEL STATUS =                                               
   SENSE = 0000                                                       
   SEEK = 00000000D5000000                                            
   BYTES READ = 0100 (   256)                                         

   FIND MEMBER =   RC = 0                                   
0000 C9C5C6C2 D9F1F440 0103162C 01040400  00000000 C3F20000 08000800 00008800
0020 00010000

Here is the find output for IEFBR14.

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A55 (19,029)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000DD001316                                           
   BYTES READ = 0028 (    40)                                        

0000 20000000 00010020 00000000 00000000  07000000 00000000 C9C5C6C2 D9F1F440
0020 00000000 01000004                                                       

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4982 (18,818)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000DD001317                                           
   BYTES READ = 00FB (   251)                                        

0000 80FA0100 00000000 00000000 00000000  00000000 00000000 00000000 00000000
0020 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000
              LINES 000975C0-00097640 SAME AS ABOVE                          
00E0 00000000 00000000 00000000 00000000  00000000 00000000 000000           

I/O REQUEST                                                           
   COMPLETION CODE = 41                                               
   CSW = 096A00 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0005 (     5) 
   --- DEVICE STATUS  = CE DE UC                                      
   --- CHANNEL STATUS = IL                                            
   SENSE = 0008                                                       
   SEEK = 00000000DD001318                                            

I/O REQUEST                                                           
   COMPLETION CODE = 7F                                               
   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A6B (19,051) 
   --- DEVICE STATUS  = CE DE                                         
   --- CHANNEL STATUS =                                               
   SENSE = 0000                                                       
   SEEK = 00000000DD001401                                            
   BYTES READ = 0012 (    18)                                         

0000 801102F5 F7F5F2E2 C3F1F0F4 40030873  346F

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A69 (19,049)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000DD001402                                           
   BYTES READ = 0014 (    20)                                        

0000 80138800 0278349F 0BD9E2C9 F4F0F9F5  F0F2F8F4          

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A69 (19,049)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000DD001403                                           
   BYTES READ = 0014 (    20)                                        

0000 0D000000 00040000 06000000 40000008  00020008          

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A75 (19,061)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000DD001404                                           
   BYTES READ = 0008 (     8)                                        

0000 1BFF07FE F0F4F1F4    

I/O REQUEST                                                           
   COMPLETION CODE = 41                                               
   CSW = 096A10 DEV STAT = 0D CHAN STAT = 00 RESIDUAL = 4A7D (19,069) 
   --- DEVICE STATUS  = CE DE UE                                      
   --- CHANNEL STATUS =                                               
   SENSE = 0000                                                       
   SEEK = 00000000DD001405                                            

*** END OF FILE ***

Here is the output from reading the data blocks for the PDS member IEFBR14.

                                                                     
I/O REQUEST                                                          
   COMPLETION CODE = 41                                              
   CSW = 0969E8 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0008 (     8)
   --- DEVICE STATUS  = CE DE UC                                     
   --- CHANNEL STATUS = IL                                           
   SENSE = 0008                                                      
   SEEK = 00000000D5000000                                           

I/O REQUEST                                                          
   COMPLETION CODE = 41                                              
   CSW = 0969E8 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0008 (     8)
   --- DEVICE STATUS  = CE DE UC                                     
   --- CHANNEL STATUS = IL                                           
   SENSE = 0008                                                      
   SEEK = 00000000D5000100                                           

I/O REQUEST                                                          
   COMPLETION CODE = 41                                              
   CSW = 0969E8 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0008 (     8)
   --- DEVICE STATUS  = CE DE UC                                     
   --- CHANNEL STATUS = IL                                           
   SENSE = 0008                                                      
   SEEK = 00000000D5000200                                           

I/O REQUEST                                                          
   COMPLETION CODE = 7F                                              
   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)
   --- DEVICE STATUS  = CE DE                                        
   --- CHANNEL STATUS =                                              
   SENSE = 0000                                                      
   SEEK = 00000000D5000300                                           
   BYTES READ = 0100 (   256)                                        

   FIND MEMBER =   RC = 0
0000 D4E2E3D9 D1C3D340 0122022C 01220700  00000000 03F20003 C003C000 00008800
0020 00010000

Here is the output for finding MSTRJCL when multitrack mode is disabled (remember the NOP to force non-multitrack). This time it takes four EXCP I/O requests to locate the block instead of the one EXCP when searching a cylinder at a time.