In Search of EOF

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!