{"id":195,"date":"2013-02-13T21:06:12","date_gmt":"2013-02-14T03:06:12","guid":{"rendered":"http:\/\/tommysprinkle.com\/mvssp\/?p=195"},"modified":"2013-07-04T12:47:30","modified_gmt":"2013-07-04T17:47:30","slug":"reading-a-pds-with-excp","status":"publish","type":"post","link":"https:\/\/tommysprinkle.com\/mvssp\/2013\/02\/13\/reading-a-pds-with-excp\/","title":{"rendered":"Reading A PDS With EXCP"},"content":{"rendered":"<p>Now we can look at using EXCP channel programming to read a partitioned dataset.\u00a0 We will start with the structure of the directory.\u00a0 The directory consists of a set of 256 byte blocks, each with an 8 byte key.\u00a0 The number of directory blocks is determined when the PDS is initially allocated.\u00a0 An EOF mark occurs immediately following the last directory block.<\/p>\n<pre>+-+-----+   +-+-----+   +-+-----+   +-+-----+     +---+\r\n|K| Dir |   |K| Dir |   |K| Dir |   |K| Dir | ... |EOF|\r\n+-+-----+   +-+-----+   +-+-----+   +-+-----+     +---+<\/pre>\n<p>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.<\/p>\n<pre>+--+--------+--------+--------+     +--------+-------+\r\n|LL|Mbr-1   |Mbr-2   |Mbr-3   | ... |Mbr-n   |00...00|\r\n+--+--------+--------+--------+     +--------+-------+<\/pre>\n<p>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.<\/p>\n<pre>+--------+---+-+-----------+\r\n|Mbr-Name|TTR|C| User Data |\r\n+--------+---+-+-----------+<\/pre>\n<p>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&#8217;1F&#8217;). 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&#8217;1F&#8217; and then multiply by 2.<\/p>\n<p>Member entries are stored in ascending sequence.\u00a0 The key contains the highest member name contained in the block.\u00a0 The logical end of the directory is marked with a member name of x&#8217;FFFFFFFFFFFFFFFF&#8217;.<\/p>\n<p>The key allows us to locate the directory block containing a specific entry without having to read through the contents of every block.\u00a0 By using a Search Key High or Equal\u00a0 the correct directory block can be quickly located.<\/p>\n<pre>         DC    X'69',AL3(MBRNAME),X'40',X'40',AL2(8)       Search Key Equal or High\r\n         DC    X'08',AL3(*-8),X'40',x'00',AL2(0)           TIC back to Search\r\n         DC    X'06',AL3(IOBUF),X'00',X'00',AL2(256)       Read Data<\/pre>\n<p>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&#8217;FFFFFFFFFFFFFFFF&#8217;) since the Search CCW will not detect the EOF record.<\/p>\n<p>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).<\/p>\n<p>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.<\/p>\n<pre>         DC    X'E9',AL3(MBRNAME),X'40',X'40',AL2(8)       Search Key Equal or High Multi-Track\r\n         DC    X'08',AL3(*-8),X'40',x'00',AL2(0)           TIC back to Search\r\n         DC    X'06',AL3(IOBUF),X'00',X'00',AL2(256)       Read Data<\/pre>\n<p>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).<\/p>\n<p>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.<\/p>\n<pre>         X    - File Mask\r\n         AL3  - UCB Address\r\n         XL2  - BIN\r\n         XL2  - Extent Begin CC\r\n         XL2  - Extent Begin HH\r\n         XL2  - Extent End CC\r\n         XL2  - Extent End HH\r\n         FL2  - Number of Tracks In This Extent<\/pre>\n<p>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.<\/p>\n<pre>01.. .... - Do Not Allow Write\r\n...1 1... - Do Not Allow Seek Cyl or Seek Head\r\n...1 0... - Do Not Allow Seek Cyl (Seek Head allowed)<\/pre>\n<p>Below is an extent entry from a DEB.<\/p>\n<pre> 58001E68 000000EB 001000EB 0019000A<\/pre>\n<p>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&#8217;0000&#8242;. The extent begins at Cylinder x&#8217;00EB&#8217; Track x&#8217;0010&#8242; and ends at Cylinder x&#8217;00EB&#8217; Track x&#8217;0019&#8242; and there are 10 (x&#8217;000A&#8217;) tracks in the extent.<\/p>\n<p>Here is the extent entry for a dataset allocated in cylinders.<\/p>\n<pre> 50001E68 000001AE 000001AE 001E001E<\/pre>\n<p>Notice that here the file mask allows Seek Head so we can use multi-track commands.<\/p>\n<h2>EXCP05 &#8211; Read PDS Member<\/h2>\n<h3>Find Block Containing Member Entry<\/h3>\n<pre>***********************************************************************\r\n*                       FIND DIRECTORY BLOCK                           \r\n*                                                                      \r\n*    ENTRY: R1 = A(CL8'MEMBER NAME')                                   \r\n*                                                                      \r\n*    EXIT:  R15 = RC                                                   \r\n*                 0 - ENTRY LOCATED                                    \r\n*                 8 - ERROR                                            \r\n*           R1  = A(DIR BLOCK)                                         \r\n*                                                                      \r\n***********************************************************************<\/pre>\n<p>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.<\/p>\n<pre>FINDBLK  DS    0H                                                \r\n         ST    R14,XTFIBLK        SAVE RETURN ADDRESS            \r\n*                                                                \r\n         MVC   SRCHNAME,0(R1)     COPY MEMBER NAME TO SEARCH PARM<\/pre>\n<p>We begin by saving the return address and then moving the member name to our search key area used by our Search CCW.<\/p>\n<pre>         MVI   DIRSRCH,X'69' SEARCH KEY HI\/EQ              \r\n         MVI   MULTITRK,0    CLEAR MULTITRACK FLAG         \r\n         LA    R2,EXCPDCB              POINT TO DCB        \r\n         L     R2,44(,R2)              POINT TO DEB        \r\n         LA    R2,32(,R2)              FIRST EXTENT INFO   \r\n         SLR   R1,R1                                       \r\n         IC    R1,0(R2)      GET FILE MASK                 \r\n         N     R1,=A(X'18')  ISOLATE SEEK HEAD             \r\n         C     R1,=A(X'10')  ALLOW SEEK HEAD               \r\n         NOP   FINDB010        NO - BRANCH                 \r\n         BNE   FINDB010        NO - BRANCH                 \r\n*                                                          \r\n         MVI   MULTITRK,1    INDICATE MULTI-TRACK ALLOWED  \r\n         MVI   DIRSRCH,X'E9' SEARCH KEY HI\/EQ M\/T          \r\nFINDB010 DS    0H<\/pre>\n<p>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.<\/p>\n<p>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.<\/p>\n<pre>         LA    R7,0          RELATIVE TRACK NUMBER        \r\n*                                                         \r\nFINDB020 DS    0H                                         \r\n         LR    R1,R7         RELATIVE TRACK NUMBER        \r\n         BAL   R14,GETCCHH   CONVERT TO MBBCCHHR          \r\n         LTR   R15,R15            CHECK RETURN CODE       \r\n         BNZ   FINDBXT8           - BRANCH IF NO GOOD     \r\n*                                                         \r\n         MVC   IOBSEEK(8),MBBCCHHR                        \r\n         MVI   IOBSEEK+7,0        PUT IN RECORD NUMBER    \r\n*                                                         \r\n         LA    R1,DIRSRCH         POINT TO CHANNEL PROGRAM\r\n         ST    R1,IOBCCWAD        SAVE INTO IOB           \r\n*                                                         \r\n         XC    ECB,ECB            CLEAR ECB               \r\n         EXCP  IOB                ISSUE I\/O REQUEST       \r\n*                                                         \r\n         WAIT  1,ECB=ECB          WAIT FOR I\/O TO COMPLETE\r\n*                                                         \r\n         BAL   R14,PRINTIOB       GO FORMAT IOB<\/pre>\n<p>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.<\/p>\n<pre>DIRSRCH  DC    X'69',AL3(SRCHNAME),X'40',X'00',AL2(8)   \r\nDIRTIC   DC    X'08',AL3(DIRSRCH),X'40',X'00',AL2(0)    \r\nDIRREAD  DC    X'06',AL3(DIRBLKIO),X'00',X'00',AL2(256)<\/pre>\n<p>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.<\/p>\n<pre>*                                                        \r\n         CLI   IOBECBAD,X'7F'     NORMAL COMPLETION      \r\n         BE    FINDBXT0             YES - FOUND OUR BLOCK\r\n*                                                        \r\n         CLI   IOBECBAD,X'41'     DEVICE STATUS AVAILABLE\r\n         BNE   FINDBXT8            NO - ERROR            \r\n*                                                        \r\n         TM    IOBCSWFL,X'02'     UNIT CHECK             \r\n         BNO   FINDBXT8            NO - ERROR            \r\n*                                                        \r\n         TM    IOBSENSE+1,X'08'   NO RECORD FOUND        \r\n         BNO   FINDBXT8             NO - ERROR           \r\n*                                                        \r\n         CLI   MULTITRK,1         MULTITRACK ENABLED     \r\n         BNE   FINDB030              NO - NEXT TRACK     \r\n*                                                        \r\n         A     R7,=F'30'          3350 HAS 30 TRK\/CYL    \r\n         B     FINDB020           GO SEARCH NEXT CYL     \r\n*                                                 \r\n*                                                 \r\nFINDB030 DS    0H                                 \r\n         A     R7,=F'1'           SEARCH NEXT     \r\n         B     FINDB020                      TRACK<\/pre>\n<p>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.<\/p>\n<p>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.<\/p>\n<pre>FINDBXT0 DS    0H                                       \r\n         LA    R15,0              SET RC                \r\n         LA    R1,DIRBLKIO        POINT TO I\/O BUF      \r\n         B     FINDBEXT           AND EXIT              \r\n*                                                       \r\n*                                                       \r\nFINDBXT8 DS    0H                                       \r\n         LA    R15,8              SET RC                \r\n         B     FINDBEXT           AND EXIT              \r\n*                                                       \r\n*                                                       \r\nFINDBEXT DS    0H                                       \r\n         L     R14,XTFIBLK        RESTORE RETURN ADDRESS\r\n         BR    R14<\/pre>\n<p>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.<\/p>\n<h3>Find Directory Entry In Directory Block<\/h3>\n<pre>***********************************************************************\r\n*                       FIND DIRECTORY ENTRY                           \r\n*                                                                      \r\n*    ENTRY: R1 = A(CL8'MEMBER NAME')                                   \r\n*                                                                      \r\n*    EXIT:  R15 = RC                                                   \r\n*                 0 - ENTRY LOCATED                                    \r\n*                 4 - ENTRY NOT LOCATED                                \r\n*                 8 - ERROR                                            \r\n*           R1  = A(DIR ENTRY)                                         \r\n*                                                                      \r\n***********************************************************************<\/pre>\n<p>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.<\/p>\n<pre>FINDMBR  DS    0H                                    \r\n         ST    R14,XTFIMBR        SAVE RETURN ADDRESS\r\n*                                                    \r\n         BAL   R14,FINDBLK        GO FIND DIR BLOCK  \r\n         LTR   R15,R15            CHECK RC           \r\n         BNZ   FINDMXT4           BRANCH IF ERROR<\/pre>\n<p>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.<\/p>\n<pre>         SLR   R3,R3                                          \r\n         ICM   R3,B'0011',0(R1)   GET LENGTH OF DIRECTORY DATA\r\n         S     R3,=F'2'           ADJUST OUT LENGTH FLD       \r\n         LA    R2,2(,R1)          POINT TO FIRST ENTRY<\/pre>\n<p>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.<\/p>\n<pre>FINDM010 DS    0H                                                 \r\n         LTR   R3,R3              END OF DATA                     \r\n         BNP   FINDMXT4             YES - ENTRY NOT FOUND         \r\n*                                                                 \r\n         CLC   0(8,R2),SRCHNAME   ENTRY WE ARE LOOKING FOR?       \r\n         BE    FINDMXT0             YES - BRANCH                  \r\n*                                                                 \r\n         SLR   R1,R1                                              \r\n         ICM   R1,B'0001',11(R2)  GET C FIELD                     \r\n         N     R1,=A(X'1F')       LENGTH OF USER DATA IN HALFWORDS\r\n         SLL   R1,1               MULTIPLY BY 2                   \r\n         A     R1,=F'12'          ADD IN FIXED SIZE               \r\n*                                                                 \r\n         AR    R2,R1              NEXT ENTRY                      \r\n         SR    R3,R1              ADJUST LENGTH                   \r\n         B     FINDM010           LOOP BACK<\/pre>\n<p>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.<\/p>\n<pre>FINDMXT0 DS    0H                                       \r\n         LA    R15,0              SET RC                \r\n         LR    R1,R2              POINT TO DIR ENTRY    \r\n         B     FINDMEXT           AND EXIT              \r\n*                                                       \r\n*                                                       \r\nFINDMXT4 DS    0H                                       \r\n         LA    R15,4              SET RC                \r\n         B     FINDMEXT           AND EXIT              \r\n*                                                       \r\n*                                                       \r\nFINDMEXT DS    0H                                       \r\n         L     R14,XTFIMBR        RESTORE RETURN ADDRESS\r\n         BR    R14<\/pre>\n<p>Here are the exit points where the return code is set and if successful the address of the entry is returned in register 1.<\/p>\n<h3>Find Routine Driver<\/h3>\n<pre> FIND     DS    0H                                   \r\n          ST    R14,XTFIND        SAVE RETURN ADDRESS\r\n *                                                   \r\n          MVC   PL09MBR,0(R1)      SAVE MEMBER NAME  \r\n          BAL   R14,FINDMBR        FIND DIR ENTRY    \r\n          LTR   R15,R15              WAS IT LOCATED  \r\n          BZ    FIND020                 YES - BRANCH<\/pre>\n<p>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.<\/p>\n<pre>*                                                       \r\n         MVI   PL09RC,C'4'       SET RC IN MESSAGE      \r\n         C     R15,=F'4'         WAS RC=4               \r\n         BE    FIND010                                  \r\n*                                                       \r\n         MVI   PL09RC,C'8'       SET RC=8               \r\nFIND010  DS    0H                                       \r\n         LA    R1,=C' '                                 \r\n         LA    R15,1                                    \r\n         BAL   R14,PRINT         PRINT BLANK LINE       \r\n         LA    R1,PL09                                  \r\n         LA    R15,PL09L                                \r\n         BAL   R14,PRINT         PRINT RC LINE          \r\n         SLR   R1,R1             INDICATE MBR NOT FOUND \r\n         B     FINDXT            EXIT<\/pre>\n<p>If the return code from FINDMBR was not zero we print a message with the member name and the RC.<\/p>\n<pre>FIND020  DS    0H                                          \r\n         ST    R1,XBLKAD          SAVE PTR TO MEMBER ENTRY \r\n*                                                          \r\n         SLR   R3,R3                                       \r\n         ICM   R3,B'0001',11(R1)       C FIELD             \r\n         N     R3,=A(X'1F')            USER                \r\n         SLL   R3,1                        DATA LEN        \r\n         LA    R3,12(,R3)                                  \r\n         ST    R3,XBLKLEN              LENGTH TO DUMP<\/pre>\n<p>If found we save the pointer to the entry and calculate the entry length.<\/p>\n<pre>*                                                           \r\n         LA    R1,=C' '                                     \r\n         LA    R15,1                                        \r\n         BAL   R14,PRINT          PRINT BLANK LINE          \r\n*                                                           \r\n         MVI   PL09RC,C'0'                                  \r\n         LA    R1,PL09                                      \r\n         LA    R15,PL09L                                    \r\n         BAL   R14,PRINT          PRINT RETURN CODE LINE    \r\n*                                                           \r\n         LA    R1,XBLKAD                                    \r\n         L     R15,=V(XDUMP)                                \r\n         BALR  R14,R15            DUMP MEMBER ENTRY         \r\n         L     R1,XBLKAD          RESTORE DIR ENTRY ADDRESS \r\n         B     FINDXT                                       \r\n*                                                       \r\nFINDXT   DS    0H                                       \r\n         L     R14,XTFIND         RESTORE RETURN ADDRESS\r\n         BR    R14<\/pre>\n<p>Finally a status message is printed and the directory entry is dumped.<\/p>\n<h3>Testing The Find Routine<\/h3>\n<pre>         LA    R1,=CL8'ACCOUNT'              \r\n         BAL   R14,FIND           FIND MEMBER\r\n*                                            \r\n         LA    R1,=CL8'MSTRJCL'              \r\n         BAL   R14,FIND           FIND MEMBER\r\n*                                            \r\n         LA    R1,=CL8'ZZZZZZZZ'             \r\n         BAL   R14,FIND           FIND MEMBER<\/pre>\n<p>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.<\/p>\n<p>Reading Member Data<\/p>\n<pre>***********************************************************************\r\n*                       READ A BLOCK BY TTR                            \r\n*                                                                      \r\n*    ENTRY: R1 = 0TTR                                                  \r\n*                                                                      \r\n*    EXIT:  R15 = RC                                                   \r\n*                 0 - BLOCK SUCCESSFULLY READ                          \r\n*                 4 - NO RECORD FOUND                                  \r\n*                 8 - EOF                                              \r\n*                12 - ERROR                                            \r\n*           R0  = BLOCK SIZE                                           \r\n*           R1  = DATA                                                 \r\n*                                                                      \r\n***********************************************************************<\/pre>\n<p>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.<\/p>\n<pre>READTTR  DS    0H                                       \r\n         STM   R14,R12,12(R13)    SAVE CALLERS REGISTERS\r\n         LA    R14,SAVERTTR       CHAIN                 \r\n         ST    R13,4(,R14)             ON               \r\n         ST    R14,8(,R13)               SAVE           \r\n         LR    R13,R14                       AREA       \r\n*                                                       \r\n         LR    R7,R1         SAVE TTR<\/pre>\n<p>We being by saving the caller&#8217;s registers and establishing a new save area. The TTR is copied into R7.<\/p>\n<pre>         LR    R1,R7         RELATIVE TRACK NUMBER   \r\n         SRL   R1,8          SHIFT OFF R (KEEP TT)   \r\n         BAL   R14,GETCCHH   CONVERT TO MBBCCHHR     \r\n         LTR   R15,R15            CHECK RETURN CODE  \r\n         BNZ   READTXTC           - BRANCH IF NO GOOD\r\n*                                                         \r\n         LA    R1,READSRCH        POINT TO CHANNEL PROGRAM\r\n         ST    R1,IOBCCWAD        SAVE INTO IOB           \r\n*                                                         \r\n         MVC   IOBSEEK(8),MBBCCHHR                        \r\n         STCM  R7,B'0001',IOBSEEK+7   PUT IN RECORD NUMBER<\/pre>\n<p>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.<\/p>\n<pre>READSRCH DC    X'31',AL3(IOBSRCH),X'40',X'00',AL2(5) \r\nREADTIC  DC    X'08',AL3(READSRCH),X'40',X'00',AL2(0)\r\nREADREAD DC    X'06',AL3(*-*),X'20',X'00',AL2(19069)<\/pre>\n<p>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.<\/p>\n<pre>         XC    ECB,ECB            CLEAR ECB               \r\n         EXCP  IOB                ISSUE I\/O REQUEST       \r\n*                                                         \r\n         WAIT  1,ECB=ECB          WAIT FOR I\/O TO COMPLETE\r\n*                                                         \r\n         BAL   R14,PRINTIOB       GO FORMAT IOB           \r\n*                                                         \r\n         CLI   IOBECBAD,X'7F'     NORMAL COMPLETION       \r\n         BE    READTXT0             YES - FOUND OUR BLOCK<\/pre>\n<p>Now we issue the EXCP and wait for the I\/O to complete. We then check see see if a block was successfully read.<\/p>\n<pre>          CLI   IOBECBAD,X'41'     DEVICE STATUS AVAILABLE \r\n          BNE   READTXTC            NO - ERROR             \r\n *                                                         \r\n          TM    IOBCSWFL,X'01'     UNIT EXCEPTION          \r\n          BO    READTXT8            YES - EOF RC=8         \r\n *                                                         \r\n          TM    IOBCSWFL,X'02'     UNIT CHECK              \r\n          BNO   READTXTC            NO - ERROR             \r\n *                                                         \r\n          TM    IOBSENSE+1,X'08'   NO RECORD FOUND         \r\n          BO    READTXT4             YES - RC=4            \r\n          B     READTXTC           ELSE - ERROR<\/pre>\n<p>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.<\/p>\n<pre>READTXT0 DS    0H                                           \r\n         LA    R15,0              SET RC                    \r\n         SLR   R1,R1                                        \r\n         SLR   R0,R0                                        \r\n         ICM   R0,B'0011',READREAD+6  READ SIZE FROM CCW    \r\n         ICM   R1,B'0011',IOBRESDL    RESIDUAL SIZE FROM CSW\r\n         SR    R0,R1                  CALC RECORD SIZE READ \r\n         ICM   R1,B'0111',READREAD+1  DATA BUFFER ADDRESS   \r\n         B     READTEXT           AND EXIT<\/pre>\n<p>If a record was read we calculate the size by subtracting the CWS Residual value from the size in the Read CCW.<\/p>\n<pre>READTXT4 DS    0H                         \r\n         LA    R15,4              SET RC  \r\n         B     READTEXT           AND EXIT\r\n*                                         \r\n*                                         \r\nREADTXT8 DS    0H                         \r\n         LA    R15,8              SET RC  \r\n         B     READTEXT           AND EXIT\r\n*                                         \r\n*                                         \r\nREADTXTC DS    0H                         \r\n         LA    R15,12             SET RC  \r\n         B     READTEXT           AND EXIT\r\n*                                         \r\n*                                         \r\nREADTEXT DS    0H                         \r\n         L     R13,4(,R13)                \r\n         L     R14,12(,R13)               \r\n         LM    R2,R12,28(R13)             \r\n         BR    R14<\/pre>\n<p>And finally the exit code.<\/p>\n<h3>Read Block Test Routine<\/h3>\n<pre>         LA    R1,=CL8'IEFBR14'                   \r\n         BAL   R14,FIND           FIND MEMBER     \r\n*                                                 \r\n         LTR   R1,R1              ANY DIR ENTRY   \r\n         BZ    EXIT                NO - EXIT      \r\n*                                                 \r\n         SLR   R2,R2                              \r\n         ICM   R2,B'0111',8(R1)   GET TTR         \r\n         ST    R2,CURTTR          SAVE CURRENT TTR<\/pre>\n<p>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.<\/p>\n<pre>RDMBR010 DS    0H                                  \r\n         L     R1,CURTTR          CURRENT BLOCK TTR\r\n         BAL   R14,READTTR        GO READ BLOCK    \r\n*                                                  \r\n         B     *+4(R15)           BRANCH ON RC     \r\n         B     RDMBR020           - READ SUCCESSFUL\r\n         B     RDMBR030           - NO RECORD FOUND\r\n         B     RDMBR040           - EOF            \r\n         B     RDMBR050           - ERROR<\/pre>\n<p>Next we call our READTTR routine to read a block by TTR. A branch table is used to evaluate the return code.<\/p>\n<pre>RDMBR020 DS    0H                                       \r\n         ST    R0,XBLKLEN         SAVE BLOCK LEN        \r\n         ST    R1,XBLKAD          SAVE BLOCK ADDRESS    \r\n         LA    R1,=C' '                                 \r\n         LA    R15,1                                    \r\n         BAL   R14,PRINT          PRINT BLANK LINE      \r\n         LA    R1,XBLKAD                                \r\n         L     R15,=V(XDUMP)                            \r\n         BALR  R14,R15            DUMP BLOCK            \r\n*                                                       \r\n         SLR   R1,R1                                    \r\n         ICM   R1,B'0001',CURTTR+3  GET CURRENT RECORD  \r\n         LA    R1,1(,R1)            INCREMENT RECORD    \r\n         STCM  R1,B'0001',CURTTR+3  SAVE BACK           \r\n         B     RDMBR010             GO READ NEXT RECORD<\/pre>\n<p>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.<\/p>\n<pre>RDMBR030 DS    0H                                       \r\n         SLR   R1,R1                                    \r\n         ICM   R1,B'0011',CURTTR+1  GET CURRENT TRACK   \r\n         LA    R1,1(,R1)            INCREMENT TRACK     \r\n         STCM  R1,B'0011',CURTTR+1  SAVE TRACK          \r\n         MVI   CURTTR+3,1           RECORD 1            \r\n         B     RDMBR010             GO READ NEXT RECORD<\/pre>\n<p>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.<\/p>\n<pre>RDMBR040 DS    0H                                    \r\n         LA    R1,=C' '                              \r\n         LA    R15,1                                 \r\n         BAL   R14,PRINT            PRINT BLANK LINE \r\n*                                                    \r\n*                    1...5...10...15...20...25       \r\n         LA    R1,=C'*** END OF FILE ***'            \r\n         LA    R15,19                                \r\n         BAL   R14,PRINT            PRINT EOF MESSAGE\r\n         B     EXIT<\/pre>\n<p>For End Of File we just print a message and exit.<\/p>\n<pre>RDMBR050 DS    0H                                   \r\n         LA    R1,=C' '                             \r\n         LA    R15,1                                \r\n         BAL   R14,PRINT            PRINT BLANK LINE\r\n*                                                   \r\n*                    1...5...10...15...20...25      \r\n         LA    R1,=C'*** I\/O ERROR ***'             \r\n         LA    R15,17                               \r\n         BAL   R14,PRINT            PRINT ERR MSG   \r\n         B     EXIT<\/pre>\n<p>For an I\/O Error we print a message and exit.<\/p>\n<h2>Program Execution Output<\/h2>\n<pre>I\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000D5000000                                           \r\n   BYTES READ = 0100 (   256)                                        \r\n\r\n   FIND MEMBER = &lt;ACCOUNT &gt;   RC = 0                                  \r\n0000 <span style=\"color: #339966;\">C1C3C3D6 E4D5E340<\/span> <span style=\"color: #ff0000;\">00FA1B<\/span><span style=\"color: #800080;\">2C<\/span> 00FA2100  00000000 C2E20010 58105800 00008800\r\n0020 00010000<\/pre>\n<p>Here is the eight-byte member name followed by the TTR (x&#8217;00FA1B&#8217;) and C (x&#8217;2C&#8217;).\u00a0 The remainder is the user data section that contains information about the load module.<\/p>\n<pre>I\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000D5000000                                           \r\n   BYTES READ = 0100 (   256)                                        \r\n\r\n   FIND MEMBER = &lt;MSTRJCL &gt;  RC = 0   \r\n0000 <span style=\"color: #339966;\">D4E2E3D9 D1C3D340<\/span> <span style=\"color: #ff0000;\">012202<\/span><span style=\"color: #800080;\">2C<\/span> 01220700  00000000 03F20003 C003C000 00008800\r\n0020 00010000<\/pre>\n<p>Here is the find output for MSTRJCL.<\/p>\n<pre>I\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000D5000000                                           \r\n   BYTES READ = 0100 (   256)                                        \r\n\r\n   FIND MEMBER = +ZZZZZZZZ+  RC = 4<\/pre>\n<p>Here is the output for a member name that does not exist.<\/p>\n<pre>I\/O REQUEST                                                           \r\n   COMPLETION CODE = 7F                                               \r\n   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0) \r\n   --- DEVICE STATUS  = CE DE                                         \r\n   --- CHANNEL STATUS =                                               \r\n   SENSE = 0000                                                       \r\n   SEEK = 00000000D5000000                                            \r\n   BYTES READ = 0100 (   256)                                         \r\n\r\n   FIND MEMBER =   RC = 0                                   \r\n0000 <span style=\"color: #008000;\">C9C5C6C2 D9F1F440<\/span> <span style=\"color: #ff0000;\">010316<\/span><span style=\"color: #800080;\">2C<\/span> 01040400  00000000 C3F20000 08000800 00008800\r\n0020 00010000<\/pre>\n<p>Here is the find output for IEFBR14.<\/p>\n<pre>I\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A55 (19,029)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000DD001316                                           \r\n   BYTES READ = 0028 (    40)                                        \r\n\r\n0000 20000000 00010020 00000000 00000000  07000000 00000000 C9C5C6C2 D9F1F440\r\n0020 00000000 01000004                                                       \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4982 (18,818)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000DD001317                                           \r\n   BYTES READ = 00FB (   251)                                        \r\n\r\n0000 80FA0100 00000000 00000000 00000000  00000000 00000000 00000000 00000000\r\n0020 00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000\r\n              LINES 000975C0-00097640 SAME AS ABOVE                          \r\n00E0 00000000 00000000 00000000 00000000  00000000 00000000 000000           \r\n\r\nI\/O REQUEST                                                           \r\n   COMPLETION CODE = 41                                               \r\n   CSW = 096A00 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0005 (     5) \r\n   --- DEVICE STATUS  = CE DE UC                                      \r\n   --- CHANNEL STATUS = IL                                            \r\n   SENSE = 0008                                                       \r\n   SEEK = 00000000DD001318                                            \r\n\r\nI\/O REQUEST                                                           \r\n   COMPLETION CODE = 7F                                               \r\n   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A6B (19,051) \r\n   --- DEVICE STATUS  = CE DE                                         \r\n   --- CHANNEL STATUS =                                               \r\n   SENSE = 0000                                                       \r\n   SEEK = 00000000DD001401                                            \r\n   BYTES READ = 0012 (    18)                                         \r\n\r\n0000 801102F5 F7F5F2E2 C3F1F0F4 40030873  346F\r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A69 (19,049)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000DD001402                                           \r\n   BYTES READ = 0014 (    20)                                        \r\n\r\n0000 80138800 0278349F 0BD9E2C9 F4F0F9F5  F0F2F8F4          \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A69 (19,049)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000DD001403                                           \r\n   BYTES READ = 0014 (    20)                                        \r\n\r\n0000 0D000000 00040000 06000000 40000008  00020008          \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 096A10 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 4A75 (19,061)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000DD001404                                           \r\n   BYTES READ = 0008 (     8)                                        \r\n\r\n0000 1BFF07FE F0F4F1F4    \r\n\r\nI\/O REQUEST                                                           \r\n   COMPLETION CODE = 41                                               \r\n   CSW = 096A10 DEV STAT = 0D CHAN STAT = 00 RESIDUAL = 4A7D (19,069) \r\n   --- DEVICE STATUS  = CE DE UE                                      \r\n   --- CHANNEL STATUS =                                               \r\n   SENSE = 0000                                                       \r\n   SEEK = 00000000DD001405                                            \r\n\r\n*** END OF FILE ***<\/pre>\n<p>Here is the output from reading the data blocks for the PDS member IEFBR14.<\/p>\n<pre>                                                                     \r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 41                                              \r\n   CSW = 0969E8 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0008 (     8)\r\n   --- DEVICE STATUS  = CE DE UC                                     \r\n   --- CHANNEL STATUS = IL                                           \r\n   SENSE = 0008                                                      \r\n   SEEK = 00000000D5000000                                           \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 41                                              \r\n   CSW = 0969E8 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0008 (     8)\r\n   --- DEVICE STATUS  = CE DE UC                                     \r\n   --- CHANNEL STATUS = IL                                           \r\n   SENSE = 0008                                                      \r\n   SEEK = 00000000D5000100                                           \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 41                                              \r\n   CSW = 0969E8 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0008 (     8)\r\n   --- DEVICE STATUS  = CE DE UC                                     \r\n   --- CHANNEL STATUS = IL                                           \r\n   SENSE = 0008                                                      \r\n   SEEK = 00000000D5000200                                           \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 0969F8 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (     0)\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000000D5000300                                           \r\n   BYTES READ = 0100 (   256)                                        \r\n\r\n   FIND MEMBER =   RC = 0\r\n0000 D4E2E3D9 D1C3D340 0122022C 01220700  00000000 03F20003 C003C000 00008800\r\n0020 00010000<\/pre>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Now we can look at using EXCP channel programming to read a partitioned dataset.\u00a0 We will start with the structure of the directory.\u00a0 The directory consists of a set of 256 byte blocks, each with an 8 byte key.\u00a0 The number of directory blocks is determined when the PDS is initially allocated.\u00a0 An EOF mark &#8230;<\/p>\n<p><a href=\"https:\/\/tommysprinkle.com\/mvssp\/2013\/02\/13\/reading-a-pds-with-excp\/\" class=\"more-link\">Continue reading &lsquo;Reading A PDS With EXCP&rsquo; &raquo;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[34],"tags":[],"class_list":["post-195","post","type-post","status-publish","format-standard","hentry","category-reading-a-pds-with-excp"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3x7AW-39","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/posts\/195","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/comments?post=195"}],"version-history":[{"count":12,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/posts\/195\/revisions"}],"predecessor-version":[{"id":216,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/posts\/195\/revisions\/216"}],"wp:attachment":[{"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/media?parent=195"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/categories?post=195"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/tags?post=195"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}