Now we are ready to take on the rest of the code necessary to execute our own channel programs using EXCP. An I/O request is represented by an Input Output Block (IOB). Here is the layout of the IOB.
IOB DS 0F IOBFLAGS DS XL2 IOB FLAGS * IOBSENSE DS XL2 SENSE BYTES 0 AND 1 IOBECBAD DS A ADDRESS OF ECB IOBCSW DS A CHANNEL STATUS WORD IOBCSWFG DS XL2 CSW FLAGS IOBRESDL DS H RESIDUAL COUNT IOBCCWAD DS A ADDRESS OF CCW CHAIN IOBDCBAD DS A ADDRESS OF DCB DS 2A IOBSEEK DS XL3 SEEK ADDRESS MBBCCHHR IOBSRCH DS XL5 SEARCH ADDRESS CCHHR
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.
IOBFLAGS Two bytes of flag bits that provide information about our I/O request to the EXCP code. Some of the flags in the first byte we are interested in are:
IOBDATCH EQU X'80' - Data Chaining Used In CCW Program IOBCMDCH EQU X'40' - Command Chaining Used In CCW Program IOBUNREL EQU X'02' - Unrelated (Non-Sequential) Request IOBSPSVC EQU X'01' - Do Not Use BSAM, BPAM, QSAM I/O Appendages
IOBSENSE 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.
IOBECBAD Address of an ECB that is posted when the EXCP request completes. The high order byte will contain the completion for the request.
7F - Normal I/O Completion 42 - Extent Error 41 - I/O Completed With Error/Exceptional Status
There are other completion codes that may be returned but these what we normally expect to encounter.
IOBCSW CCW address from the CSW.
IOBCSWFG Device Status and Channel Status bytes from the CSW.
IOBRESDL Residual Count from the CSW.
IOBCCWAD Is the address of the first CCW in the channel program.
IOBDCBAD Address of the DCB.
IOBSEEK DASD Seek address (MBBCCHHR)
Here are the highlights of a program that reads a dsad device using excp. I don’t include all of the program here, just the parts directly related to performing an EXCP. Please refer to program EXCP01 in the downloads.
OPEN (EXCPDCB,INPUT) TM EXCPDCB+48,X'10' BO OPEN020
We begin by opening our EXCP DCB. Here we check the DCB open flags to verify the DCB open request was successful.
EXCPDCB DCB DSORG=PS,MACRF=E,DDNAME=EXCP
Here is the DCB definition. Note the MACRF type of ‘E’ for EXCP.
IOB DS 0F IOBFLAGS DC XL2'4300' IOBSENSE DC XL2'0000' IOBECBAD DC A(ECB) IOBCSW DC A(0) IOBCSWFL DC XL2'0000' IOBRESDL DC H'00' IOBCCWAD DC A(CCWSRCH) IOBDCBAD DC A(EXCPDCB) DC 2A(0) IOBSEEK DC XL3'000000' MBB IOBSRCH DC XL5'0000000000' CCHHR
Here is our IOB. Note the IOBFLAGS value. The IOB points to an ECB, our opened DCB, and our CCW chain.
ECB DC F'0' * * DS 0D CCWSRCH DC X'31',AL3(IOBSRCH),X'40',X'00',AL2(5) CCWTIC DC X'08',AL3(CCWSRCH),X'40',X'00',AL2(0) CCWREAD DC X'06',AL3(IOBUF),X'00',X'00',AL2(80)
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.
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’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 – 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.
* *********************************************************************** * CONVERT RELATIVE TRAK TO CCHH * R1 = RELATIVE TRACK (TT) * RESULT STORED IN MBBCCHHR * (MODIFIED R1,R2,R3,R4,R5,R15) *********************************************************************** * GETCCHH DS 0H ST R14,XTGET
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.
LA R2,EXCPDCB POINT TO DCB L R2,44(,R2) POINT TO DEB SLR R3,R3 IC R3,16(,R2) NUMBER OF EXTENTS LA R5,32(,R2) FIRST EXTENT INFO SLR R4,R4 SLR R2,R2
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.
GET010 DS 0H ICM R4,B'0011',14(R5) NUMBER OF TRACKS IN EXTENT CR R1,R4 DOES IT FIT IN THIS EXTENT BL GET020 YES - BRANCH
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.
SR R1,R4 ADJUST RELATIVE TRACK LA R5,16(,R5) POINT TO NEXT EXTENT LA R2,1(,R2) BUMP EXTENT COUNT BCT R3,GET010 LOOP BACK * B GETRC12 TRACK NOT VALID
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.
GET020 DS 0H XC MBBCCHHR(0),MBBCCHHR CLEAR RESULT AREA STC R2,MBBCCHHR SAVE "M" (EXTENT #) LH R2,6(,R5) START CC AH R1,8(,R5) ADD IN START HH SLR R0,R0 L R15,=F'30' 3350 - 30 TRKS/CYL DR R0,R15 AR R2,R1 UPDATE CC STCM R2,B'0011',MBBCCHHR+3 SAVE CC STCM R0,B'0011',MBBCCHHR+5 SAVE HH
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 “M” 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 “CC” value. The remainder is our “TT” value. These values are saved into the result area.
SLR R15,R15 SET RC L R14,XTGET BR R14 * * GETRC12 DS 0H LA R15,12 SET RC L R14,XTGET BR R14
And we finish up with our exit code.
LA R6,1 RECORD NUMBER, INIT TO ONE LA R1,0 RELATIVE TRACK NUMBER BAL R14,GETCCHH CONVERT TO MBBCCHHR LTR R15,R15 CHECK RETURN CODE BZ READ010 - BRANCH IF GOOD * WTO 'TTR CONVERSION FAILED',ROUTCDE=(1,11) B EXIT
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.
READ010 DS 0H MVC IOBSEEK(8),MBBCCHHR STC R6,IOBSEEK+7 PUT IN RECORD NUMBER * 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 BNE EXIT * LA R6,1(,R6) NEXT RECORD NUMBER B READ010
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.
//GENR EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT2 DD DSN=TCS3.EXCP01.DATA, // DISP=(NEW,CATLG), // UNIT=SYSDA,VOL=SER=WORK01, // SPACE=(CYL,(2,2)), // DCB=(BLKSIZE=80,LRECL=80,RECFM=F) //SYSUT1 DD * 0001 0002 0003
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.
I/O REQUEST COMPLETION CODE = 7F CSW = 095E70 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 ( ) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000001 I/O REQUEST COMPLETION CODE = 7F CSW = 095E70 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 ( ) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000002 I/O REQUEST COMPLETION CODE = 7F CSW = 095E70 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 0000 ( ) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000003 I/O REQUEST COMPLETION CODE = 41 CSW = 095E60 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0005 ( 5) --- DEVICE STATUS = CE DE UC --- CHANNEL STATUS = IL SENSE = 0008 SEEK = 00000001AE000004
Here we see four I/O requests. The first three have a completion code of X’7F’ indicating no exceptional condition. Next the CSW is formatted. The CSW points past the end of the last CCW executed (we have to back up 8 bytes to get the CCW address). The Device Status and Channel Status are displayed as hex as well as being formatted on the next two lines. The residual length is zero indicating we have read 80 bytes of data as expected. Finally we format the SEEK address and we can see the record number increasing for each request. Since we wrote three records using IEBGENER we expect to read three records. When we attempt to read record number four we get a completion code of X’41’ indicating an exceptional condition has occured. We also notice that in addition to Channel End and Device End the Unit Status bit is set. We also have Incorrect Length set in the Channel Status, a residual length of 5, and Sense bytes containing X’0008′. The CCW address points to the TIC CCW so the failing CCW is 8 bytes before it – the Search ID Equal CCW. This is why the residual length is 5. The Sense data tells us why we received an Unit Exception.
Sense Byte 0 1... .... - Command Reject - Invalid command or command sequence .1.. .... - Intervention Required - Device not ready ..1. .... - Bus Out Parity Check - Parity check occured transfering data ...1 .... - Equipment Check - Unusual hardware error .... 1... - Data Check - Error reading from device .... .1.. - Overrun - Data received later than expected .... ..00 - Not used, zero Sense Byte 1 1... .... - Permanent Error - Unrecoverbale error has occured .1.. .... - Invalid Track Format - Write exceedes track capacity ..1. .... - End of Cylinder - Multitrack opeartion has encountered cylinder end ...0 .... - Not used, zero .... 1... - No Record Found - Two index points encoutered without interveaning read .... .1.. - File Protected - File Mask violated .... ..1. - Write Inhibited .... ...1 - Operation Incomplete
Since we have a Sense value of 0008 we see that no record was found. This is exactly what we should expect since we wrote three records to the data set. That is unless you were expecting the EOF mark to be the fourth record. The EOF is in our dataset, but it is the first record on the next track.