{"id":142,"date":"2013-02-05T20:49:52","date_gmt":"2013-02-06T02:49:52","guid":{"rendered":"http:\/\/tommysprinkle.com\/mvssp\/?p=142"},"modified":"2013-06-24T21:07:01","modified_gmt":"2013-06-25T02:07:01","slug":"writing-an-excp-program","status":"publish","type":"post","link":"https:\/\/tommysprinkle.com\/mvssp\/2013\/02\/05\/writing-an-excp-program\/","title":{"rendered":"Writing An EXCP Program"},"content":{"rendered":"<p>Now we are ready to take on the rest of the code necessary to execute our own channel programs using EXCP.\u00a0 An I\/O request is represented by an Input Output Block (IOB).\u00a0 Here is the layout of the IOB.<\/p>\n<pre>IOB      DS    0F\r\nIOBFLAGS DS    XL2                 IOB FLAGS                                     \r\n*                                                          \r\nIOBSENSE DS    XL2                 SENSE BYTES 0 AND 1\r\nIOBECBAD DS    A                   ADDRESS OF ECB          \r\nIOBCSW   DS    A                   CHANNEL STATUS WORD     \r\nIOBCSWFG DS    XL2                 CSW FLAGS               \r\nIOBRESDL DS    H                   RESIDUAL COUNT          \r\nIOBCCWAD DS    A                   ADDRESS OF CCW CHAIN    \r\nIOBDCBAD DS    A                   ADDRESS OF DCB          \r\n         DS    2A                                          \r\nIOBSEEK  DS    XL3                 SEEK ADDRESS   MBBCCHHR \r\nIOBSRCH  DS    XL5                 SEARCH ADDRESS    CCHHR<\/pre>\n<p>The IOB is mapped by the system macro IEZIOB but it is much less confusing to define the IOB ourselves sicne we are only concerned with the fields related to EXCP processing.<\/p>\n<p><b>IOBFLAGS\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/b>Two bytes of flag bits that provide information about our I\/O request to the EXCP code.\u00a0 Some of the flags in the first byte we are interested in are:<\/p>\n<pre>IOBDATCH EQU   X'80'    - Data Chaining Used In CCW Program\r\nIOBCMDCH EQU   X'40'    - Command Chaining Used In CCW Program\r\nIOBUNREL EQU   X'02'    - Unrelated (Non-Sequential) Request\r\nIOBSPSVC EQU   X'01'    - Do Not Use BSAM, BPAM, QSAM I\/O Appendages<\/pre>\n<p><b>IOBSENSE\u00a0\u00a0\u00a0\u00a0\u00a0 <\/b>Two byte area where EXCP returns the first two sense bytes from the device if execution of the channel program does not complete successfully and Unit Check is returned in the CSW.<br \/>\n<b>IOBECBAD\u00a0\u00a0\u00a0 <\/b>Address of an ECB that is posted when the EXCP request completes. The high order byte will contain the completion for the request.<\/p>\n<pre>7F - Normal I\/O Completion\r\n42 - Extent Error\r\n41 - I\/O Completed With Error\/Exceptional Status<\/pre>\n<p>There are other completion codes that may be returned but these what we normally expect to encounter.<br \/>\n<b>IOBCSW\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/b>CCW address from the CSW.<br \/>\n<b>IOBCSWFG\u00a0 <\/b>Device Status and Channel Status bytes from the CSW.<br \/>\n<b>IOBRESDL\u00a0\u00a0 <\/b>Residual Count from the CSW.<br \/>\n<b>IOBCCWAD <\/b>Is the address of the first CCW in the channel program.<br \/>\n<b>IOBDCBAD\u00a0 <\/b>Address of the DCB.<br \/>\n<b>IOBSEEK\u00a0\u00a0\u00a0\u00a0\u00a0 <\/b>DASD Seek address (MBBCCHHR)<\/p>\n<p>Here are the highlights of a program that reads a dsad device using excp.\u00a0 I don&#8217;t include all of the program here, just the parts directly related to performing an EXCP.\u00a0 Please refer to program EXCP01 in the downloads.<\/p>\n<pre>         OPEN  (EXCPDCB,INPUT)  \r\n         TM    EXCPDCB+48,X'10' \r\n         BO    OPEN020<\/pre>\n<p>We begin by opening our EXCP DCB. Here we check the DCB open flags to verify the DCB open request was successful.<\/p>\n<pre>EXCPDCB  DCB   DSORG=PS,MACRF=E,DDNAME=EXCP<\/pre>\n<p>Here is the DCB definition. Note the MACRF type of &#8216;E&#8217; for EXCP.<\/p>\n<pre>IOB      DS    0F                      \r\nIOBFLAGS DC    XL2'4300'               \r\nIOBSENSE DC    XL2'0000'               \r\nIOBECBAD DC    A(ECB)                  \r\nIOBCSW   DC    A(0)                    \r\nIOBCSWFL DC    XL2'0000'               \r\nIOBRESDL DC    H'00'                   \r\nIOBCCWAD DC    A(CCWSRCH)              \r\nIOBDCBAD DC    A(EXCPDCB)              \r\n         DC    2A(0)                   \r\nIOBSEEK  DC    XL3'000000'       MBB   \r\nIOBSRCH  DC    XL5'0000000000'   CCHHR<\/pre>\n<p>Here is our IOB. Note the IOBFLAGS value. The IOB points to an ECB, our opened DCB, and our CCW chain.<\/p>\n<pre>ECB      DC    F'0'                                 \r\n*                                                   \r\n*                                                   \r\n         DS    0D                                   \r\nCCWSRCH  DC    X'31',AL3(IOBSRCH),X'40',X'00',AL2(5)\r\nCCWTIC   DC    X'08',AL3(CCWSRCH),X'40',X'00',AL2(0)\r\nCCWREAD  DC    X'06',AL3(IOBUF),X'00',X'00',AL2(80)<\/pre>\n<p>Here is our ECB and our CCW chain. The first CCW is a SEARCH for ID Equal. It is follow by a TIC that points back to the Search. Finally we have a READ DATA CCW that attempts to read 80 bytes.<\/p>\n<p>Now we have to deal with getting the actual disk address (MBBCCHHR) of the record we want to read. We will refer to our dataset using realative track address that are converted to physical address. A relative address simply refers to a dasd dataset as a collection of tracks. It doesn&#8217;t deal with cylinders or extents. The first relative track is zero, followed by track one, etc. MVS does provide a routine to convert from relative address (TTR &#8211; Track\/Record) to physical address (MBBCCHHR). We are familiar with the CCHHR part of the address. The BB is always two bytes of zeros. The M is the extent number (beginning with zero) of the dataset extent containing the track.<\/p>\n<pre>*                                                                      \r\n***********************************************************************\r\n*    CONVERT RELATIVE TRAK TO CCHH                                     \r\n*       R1 = RELATIVE TRACK (TT)                                       \r\n*       RESULT STORED IN MBBCCHHR                                      \r\n*       (MODIFIED R1,R2,R3,R4,R5,R15)                                  \r\n***********************************************************************\r\n*                                                                      \r\nGETCCHH  DS    0H                                                      \r\n         ST    R14,XTGET<\/pre>\n<p>Here is the begenning of our converstion routine. On entry R1 contains the relative track number. The physical address will be calculated and stored in a field called MBBCCHHR. This routine ignores the record number since it does not require any conversion.<\/p>\n<pre>         LA    R2,EXCPDCB              POINT TO DCB     \r\n         L     R2,44(,R2)              POINT TO DEB     \r\n         SLR   R3,R3                                    \r\n         IC    R3,16(,R2)              NUMBER OF EXTENTS\r\n         LA    R5,32(,R2)              FIRST EXTENT INFO\r\n         SLR   R4,R4                                    \r\n         SLR   R2,R2<\/pre>\n<p>First we get the DEB address from the DCB. Next we get the number of dataset extents from the DEB. I am using offsets into the control blocks but you can use the mapping macros (IEZDEB). The first extent begins at offset 32 in the DEB. At this point R3 contains the number of extents and R5 has the address of the first extent data.<\/p>\n<pre>GET010   DS    0H                                                \r\n         ICM   R4,B'0011',14(R5)       NUMBER OF TRACKS IN EXTENT\r\n         CR    R1,R4                   DOES IT FIT IN THIS EXTENT\r\n         BL    GET020                     YES - BRANCH<\/pre>\n<p>Here is the main loop of our conversion routine. Here we get the number of tracks in this extent. We compare it to the relative track nubmer to see if the relative track is in this extent. If so we can exit the loop.<\/p>\n<pre>         SR    R1,R4                   ADJUST RELATIVE TRACK  \r\n         LA    R5,16(,R5)              POINT TO NEXT EXTENT   \r\n         LA    R2,1(,R2)               BUMP EXTENT COUNT      \r\n         BCT   R3,GET010               LOOP BACK              \r\n*                                                             \r\n         B     GETRC12                 TRACK NOT VALID<\/pre>\n<p>If the relative track number is larger than the tracks in the current extent we subtract the number of tracks in the current extent from the relative track number. We then increment our pointer to the next extent entry in the DEB. We also keep track of the extent number in R2. We use a BCT instruction to loop back as long as we have more extents to process. If we exhaust all extents then the relative track is not contained within any dataset extent and we return with a RC of 12 in R15.<\/p>\n<pre>GET020   DS    0H                                          \r\n         XC    MBBCCHHR(0),MBBCCHHR    CLEAR RESULT AREA   \r\n         STC   R2,MBBCCHHR             SAVE \"M\" (EXTENT #) \r\n         LH    R2,6(,R5)               START CC            \r\n         AH    R1,8(,R5)               ADD IN START HH     \r\n         SLR   R0,R0                                       \r\n         L     R15,=F'30'             3350 - 30 TRKS\/CYL   \r\n         DR    R0,R15                                      \r\n         AR    R2,R1                   UPDATE CC           \r\n         STCM  R2,B'0011',MBBCCHHR+3   SAVE CC\r\n         STCM  R0,B'0011',MBBCCHHR+5   SAVE HH<\/pre>\n<p>Once the proper extent is located we complete the conversion process. We begin by clearing the result area and storing the extent number in the &#8220;M&#8221; byte. We get the beginning cylinder of the extent in R2. We then add the beginning head (track) of the extent to our remaining relative track value. We divide this total track value by the number of tracks in a cylinder. For a 3350 this number is 30. This code must be adjusted for other device types. We add the beginning cylinder of the extent to the result to get our &#8220;CC&#8221; value. The remainder is our &#8220;TT&#8221; value. These values are saved into the result area.<\/p>\n<pre>         SLR   R15,R15                 SET RC \r\n         L     R14,XTGET                      \r\n         BR    R14                            \r\n*                                             \r\n*                                             \r\nGETRC12  DS    0H                             \r\n         LA    R15,12                  SET RC \r\n         L     R14,XTGET                      \r\n         BR    R14<\/pre>\n<p>And we finish up with our exit code.<\/p>\n<pre>         LA    R6,1          RECORD NUMBER, INIT TO ONE \r\n         LA    R1,0          RELATIVE TRACK NUMBER      \r\n         BAL   R14,GETCCHH   CONVERT TO MBBCCHHR        \r\n         LTR   R15,R15            CHECK RETURN CODE     \r\n         BZ    READ010            - BRANCH IF GOOD      \r\n*                                                       \r\n         WTO   'TTR CONVERSION FAILED',ROUTCDE=(1,11)   \r\n         B     EXIT<\/pre>\n<p>Now we are read to read some records using EXCP. We will keep our current record number in R6. We start with 1 since that is the first data record (remember R0 is not used for data). We set our relative track number (zero) in R1 and call our conversion routine to get the physical address. We do check the return code to verify the result is valid.<\/p>\n<pre>READ010  DS    0H                                           \r\n         MVC   IOBSEEK(8),MBBCCHHR                          \r\n         STC   R6,IOBSEEK+7  PUT IN RECORD NUMBER           \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             \r\n*                                                           \r\n         CLI   IOBECBAD,X'7F'     NORMAL COMPLETION         \r\n         BNE   EXIT                                         \r\n*                                                           \r\n         LA    R6,1(,R6)          NEXT RECORD NUMBER        \r\n         B     READ010<\/pre>\n<p>Here is the main read loop. First we copy the physical track address into our IOB. We then store in our current record number. Next we clear our ECB, issue the EXCP, and wait for the I\/O to complete. I have written a routine called PRINTIOB that will format various fields from the IOB and print them (see the EXCP01 program for the code). We then check the completion code. If there were no exceptional conditions we increment the record number and issue the EXCP again. We continue until an exceptional condition results.<\/p>\n<pre>\/\/GENR   EXEC  PGM=IEBGENER                 \r\n\/\/SYSPRINT DD  SYSOUT=*                     \r\n\/\/SYSIN    DD  DUMMY                        \r\n\/\/SYSUT2   DD  DSN=TCS3.EXCP01.DATA,        \r\n\/\/         DISP=(NEW,CATLG),                \r\n\/\/         UNIT=SYSDA,VOL=SER=WORK01,       \r\n\/\/         SPACE=(CYL,(2,2)),               \r\n\/\/         DCB=(BLKSIZE=80,LRECL=80,RECFM=F)\r\n\/\/SYSUT1   DD  *                            \r\n0001                                        \r\n0002                                        \r\n0003<\/pre>\n<p>I created a test dataset to read using IEBGENER. It is a simple non-blocked 80\/80 dataset containing three records. Here is the output from our EXCP01 program when run against this test data.<\/p>\n<pre>I\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 095E70 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (      )\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000001AE000001                                           \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 095E70 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (      )\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000001AE000002                                           \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 7F                                              \r\n   CSW = 095E70 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 (      )\r\n   --- DEVICE STATUS  = CE DE                                        \r\n   --- CHANNEL STATUS =                                              \r\n   SENSE = 0000                                                      \r\n   SEEK = 00000001AE000003                                           \r\n\r\nI\/O REQUEST                                                          \r\n   COMPLETION CODE = 41                                              \r\n   CSW = 095E60 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 = 00000001AE000004<\/pre>\n<p>Here we see four I\/O requests.\u00a0 The first three have a completion code of X&#8217;7F&#8217; indicating no exceptional condition.\u00a0 Next the CSW is formatted.\u00a0 The CSW points past the end of the last CCW executed (we have to back up 8 bytes to get the CCW address).\u00a0 The Device Status and Channel Status are displayed as hex as well as being formatted on the next two lines.\u00a0 The residual length is zero indicating we have read 80 bytes of data as expected.\u00a0 Finally we format the SEEK address and we can see the record number increasing for each request.\u00a0 Since we wrote three records using IEBGENER we expect to read three records.\u00a0 When we attempt to read record number four we get a completion code of X&#8217;41&#8217; indicating an exceptional condition has occured.\u00a0 We also notice that in addition to Channel End and Device End the Unit Status bit is set.\u00a0 We also have Incorrect Length set in the Channel Status, a residual length of 5, and Sense bytes containing X&#8217;0008&#8242;.\u00a0 The CCW address points to the TIC CCW so the failing CCW is 8 bytes before it &#8211; the Search ID Equal CCW.\u00a0 This is why the residual length is 5.\u00a0 The Sense data tells us why we received an Unit Exception.<\/p>\n<pre><b>Sense Byte 0<\/b>\r\n\r\n<b>1... .... - Command Reject<\/b> - Invalid command or command sequence \r\n<b>.1.. .... - Intervention Required<\/b> - Device not ready \r\n<b>..1. .... - Bus Out Parity Check<\/b> - Parity check occured transfering data \r\n<b>...1 .... - Equipment Check<\/b> - Unusual hardware error \r\n<b>.... 1... - Data Check<\/b> - Error reading from device \r\n<b>.... .1.. - Overrun<\/b> - Data received later than expected \r\n<b>.... ..00 - Not used, zero<\/b>\r\n\r\n<b>Sense Byte 1<\/b> \r\n\r\n<b>1... .... - Permanent Error<\/b> - Unrecoverbale error has occured \r\n<b>.1.. .... - Invalid Track Format<\/b> - Write exceedes track capacity \r\n<b>..1. .... - End of Cylinder<\/b> - Multitrack opeartion has encountered cylinder end \r\n<b>...0 .... - Not used, zero<\/b> \r\n<b>.... 1... - No Record Found<\/b> - Two index points encoutered without interveaning read \r\n<b>.... .1.. - File Protected<\/b> - File Mask violated \r\n<b>.... ..1. - Write Inhibited<\/b> \r\n<b>.... ...1 - Operation Incomplete<\/b><\/pre>\n<p>Since we have a Sense value of 0008 we see that no record was found.\u00a0 This is exactly what we should expect since we wrote three records to the data set.\u00a0 That is unless you were expecting the EOF mark to be the fourth record.\u00a0 The EOF is in our dataset, but it is the first record on the next track.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Now we are ready to take on the rest of the code necessary to execute our own channel programs using EXCP.\u00a0 An I\/O request is represented by an Input Output Block (IOB).\u00a0 Here is the layout of the IOB. IOB DS 0F IOBFLAGS DS XL2 IOB FLAGS * IOBSENSE DS XL2 SENSE BYTES 0 AND &#8230;<\/p>\n<p><a href=\"https:\/\/tommysprinkle.com\/mvssp\/2013\/02\/05\/writing-an-excp-program\/\" class=\"more-link\">Continue reading &lsquo;Writing An EXCP Program&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":[29],"tags":[],"class_list":["post-142","post","type-post","status-publish","format-standard","hentry","category-writing-an-excp-program"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3x7AW-2i","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/posts\/142","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=142"}],"version-history":[{"count":11,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/posts\/142\/revisions"}],"predecessor-version":[{"id":208,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/posts\/142\/revisions\/208"}],"wp:attachment":[{"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/media?parent=142"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/categories?post=142"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tommysprinkle.com\/mvssp\/wp-json\/wp\/v2\/tags?post=142"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}