When I first ran the EXCP01 program I expected the EOF to be written on the same track as the data blocks. There is no reason the EOF could not be contained on the same track. Over the years I have learned that EOF marks don’t always appear exactly where you might expect to find them. In fact sometimes there is no EOF record. If all tracks in all extents are full and there is no room for an EOF record then end of file is assumed to occur after the last block on the last track. What we do see from our first example is that just because we read a short track (a track that could contain more blocks) it doesn’t mean we shouldn’t continue on with the next track.
Here we will modify the EXCP01 program to continue reading until the EOF record is encountered. After a great amount of time and consideration I have decided to name it EXCP02. See the downloads for the full program.
Track Balance
Before continuing we need to consider how many blocks will fit on a track. I normally think of a 3350 as having a track size of 19,069 bytes. This is a number that has been etched into my brain. This is not the track size, it is actually the size of the largest non-keyed block you can write on a track. The 3350 track size is 19254. This leads to the next question – why the difference? To answer the question we need to go back to the physical layout of a track.
[HA] [R0-C] [R0-D] [R1-C] [R1-K] [R1-D] ... [Rn-C] [Rn-K] [Rn-D]
A track always begins with the Home Address record followed by the special Record Zero. The read of the track is used for data records which consist of a Count area, an optional Key area, and a Data area. What we failed to show was the gaps between the records. A gap is some physical space between records that is necessary for the electronics of the read/write heads to function.
^Ix^ {G1} [HA] {G2} [R0-C] {G2} [R0-D] {G3} ---> [R1-C] {G2} [R1-K] {G2} [R1-D] {G3} ... ---> [Rn-C] {G2} [Rn-K] {G2} [Rn-D] {G4}
Here is a more detailed picture of a track. It begins with the Index Mark. Next is a Gap-1 that occurs between the index mark and the home address. Next is the Home Address which is followed by a Gap-2. A Gap-2 occurs inbetween the count, key, and data areas of a record. A Gap-3 occurs inbetween records (after a data area and before the next count area). Finally a Gap-4 occurs after the last data record on the track. Gap-1, Gap-2, and Gap-3 all have specific sizes. Gap-4 is always as large as the unused space after the last block.
When we are writing blocks to a track we need to remember that we must account for the size of the gaps in addition to the size of the blocks. Earlier I said the 3350 track size was 19,254. Actually that is the size after accounting for the Home Address, Record Zero, and their associated gaps. For non-keyed records there is a overhad size of 185 bytes for each record. This 185 bytes is for the count area and gaps. If we take our useable track size (19,254) and subtract the overhead for one block (185) we get the maximum record size 19,069. For keyed blocks we have to add in the key length and use an overhead of 267 to account for the additional gap.
Now we can calculate how much space remains on a track as we write blocks to it. Here is an example using 4,096 blocks with no keys.
TrackBal Blocksize Overhead New TrkBal 19,254 -4096 -185 14,973 14,973 -4096 -185 10,692 10,692 -4096 -185 6,411 6,411 -4096 -185 2,130 2,130 * * *
We start with our maximum useable track size, subtract out the blocksize and overhead and get a new Track Balance of 14,973. We continue until we get a result less than the blocksize + overhead and at that point no additional records will fit on the track. Here we find that four 4K blocks will fit on one 3350 track. For fixed length blocks we can do a one-time calcuation. For variable length blocks we must calculate as we go.
Reading A Block of Unknown Blocksize
In our first program we read blocks with a blocksize of 80. We knew what the blocksize was and was able to specify it in our CCW. What if we don’t know the size of the block? We can issue a read for a size greater than the largest block (19,069 for a 3350) and see how much data we get back. The only problem is if the size of the block read does not match the size specified in the Read CCW we will get an Incorrect Length exceptional condition. We can cause the channel to ignore this condition by setting the Supress Length Indication (SLI) bit our Read CCW. When the read completes the residual length in the CSW will tell us the number of bytes NOT transfered by the Read command. In other words it will contain the value of the CCW count minus the size of the data transfered. We can use this number to calculate how many bytes were read. We simply subtract the residual count from the CCW length value.
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(*-*),X'20',X'00',AL2(32768)
Here is our updated CCW channel program. Notice I set the size on the Read CCW to 32,768. For a 3350 we can use any number greater than 19,069. Also note that I used zero A(*-*) for the address in the Reac CCW. This is because I will getmain a 32K buffer and update the CCW with the address returned from GETMAIN.
GETMAIN R,LV=32768 * STCM R1,B'0111',CCWREAD+1
Now all that is left is to calculate the actual size of the record we just read.
SLR R1,R1 ICM R1,B'0011',CCWREAD+6 DATA LENGTH FROM CCW SLR R2,R2 ICM R2,B'0011',IOBRESDL RESIDUAL LEN FROM CSW SR R1,R2 CALC BYTES READ
And now we can format and print this value as part of our IOB formatting.
READ020 DS 0H CLI IOBECBAD,X'41' DEVICE STATUS AVAILABLE ? BNE EXIT NO - BRANCH * TM IOBCSWFL,X'02' UNIT CHECK ? BNO EXIT NO - BRANCH * TM IOBSENSE+1,X'08' NO RECORD FOUND BNO EXIT NO - BRANCH * LA R7,1(,R7) NEXT TRACK B READ000 LOOP BACK TO READ
Here we modify our code to deal with a No Record Found condition. If we get a Unit Check we look at the sense bytes to see if it was a No Reocrd Found. If so we increment our relative track number and loop back to process the next track. If the exception is anything else we end execution.
I wanted to use a larger blocksize for testing. I could still use blocked records with IEBGENER but IEBDG makes more sense.
//IEBDG EXEC PGM=IEBDG //SYSPRINT DD SYSOUT=* //SEQOUT DD DSN=TCS3.EXCP02.DATA, // DISP=(NEW,CATLG), // UNIT=SYSDA,VOL=SER=WORK01, // SPACE=(CYL,(2,2)), // DCB=(BLKSIZE=4096,LRECL=4096,RECFM=F) //SYSIN DD * DSD OUTPUT=(SEQOUT) FD NAME=FLD1,LENGTH=8,STARTLOC=1,FORMAT=AL,ACTION=TL CREATE QUANTITY=12,NAME=(FLD1),FILL=X'00' END
bla
I/O REQUEST COMPLETION CODE = 7F CSW = 095EC0 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 7000 (28,672) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000001 BYTES READ = 1000 ( 4,096) I/O REQUEST COMPLETION CODE = 7F CSW = 095EC0 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 7000 (28,672) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000002 BYTES READ = 1000 ( 4,096) I/O REQUEST COMPLETION CODE = 7F CSW = 095EC0 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 7000 (28,672) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000003 BYTES READ = 1000 ( 4,096) I/O REQUEST COMPLETION CODE = 7F CSW = 095EC0 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 7000 (28,672) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000004 BYTES READ = 1000 ( 4,096) I/O REQUEST COMPLETION CODE = 41 CSW = 095EB0 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0005 ( 5) --- DEVICE STATUS = CE DE UC --- CHANNEL STATUS = IL SENSE = 0008 SEEK = 00000001AE000005 I/O REQUEST COMPLETION CODE = 7F CSW = 095EC0 DEV STAT = 0C CHAN STAT = 00 RESIDUAL = 7000 (28,672) --- DEVICE STATUS = CE DE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000101 BYTES READ = 1000 ( 4,096) I/O REQUEST COMPLETION CODE = 41 CSW = 095EB0 DEV STAT = 0E CHAN STAT = 40 RESIDUAL = 0005 ( 5) --- DEVICE STATUS = CE DE UC --- CHANNEL STATUS = IL SENSE = 0008 SEEK = 00000001AE000102 I/O REQUEST COMPLETION CODE = 41 CSW = 095EC0 DEV STAT = 0D CHAN STAT = 00 RESIDUAL = 8000 (32,768) --- DEVICE STATUS = CE DE UE --- CHANNEL STATUS = SENSE = 0000 SEEK = 00000001AE000201
Here is a summary of what happened –
- Four records were successfully read from Cyl=01A3 Trk=0000
- The attempt to read a fifth record terminated with No Record Found
- We incremented the track to Cyl=01A3 Trk=0001
- We successfully read one record
- The attempt to read a second record terminated with No Record Found
- We incremented the track to Cyl=01A3 Trk=0002
- The attempt to read the first record terminated with Unit Exception
Unit Execption indicates a record with a data length of zero was read which indicates a End Of File. We are successful in our quest to find the EOF record!