I'm trying to write an Assembly Language program for reading and writing a sequential file, using Basic Sequential Access Method (BSAM).
My input file has the following attributes:
Record Length = 80 Bytes
Block Size = 4560 Bytes
Record Format = FB
File Organisation = PS
No. of Records in the Input File = 10,000.
Now, I'm using the below program to read from the input file and write to the output file.
Code:
BSAMFIL START
STM R14,R12,12(R13)
LR R12,R15
USING BSAMFIL,R12
LA R14,SAVEAREA
ST R13,4(R14)
ST R14,8(R13)
LR R13,R14
OPEN (INPUTF,INPUT)
OPEN (OUTPUTF,OUTPUT)
LOOP READ DECB1,SF,INPUTF,CARD
CHECK DECB1
WRITE DECB2,SF,OUTPUTF,CARD
CHECK DECB2
B LOOP
ABTERM WTO 'THE PROGRAM ABENDED'
NOMORE WTO 'END OF FILE REACHED'
CLOSE INPUTF
CLOSE OUTPUTF
L R13,4(R13)
LM R14,R12,12(R13)
SR R15,R15
BR R14
INPUTF DCB DDNAME=INPUTFL, X
MACRF=(R), X
DSORG=PS, X
LRECL=80, X
BLKSIZE=4560, X
RECFM=FB, X
EODAD=NOMORE, X
SYNAD=ABTERM
OUTPUTF DCB DDNAME=OUTPUTFL, X
MACRF=(W), X
DSORG=PS, X
LRECL=80, X
BLKSIZE=4560, X
RECFM=FB
* REGISTER EQUATES
....
My problem is that the OUTPUT file contains 10,032 records in stead of 10,000 records.
I calculated the following:
In one read, i'm reading 4560 Bytes
-> since one record is of 80 Bytes, in one read I'm reading 57 records
-> in 175 Reads, i'm reading 9,975 records.
-> in the 176th read, there are only 25 records remaining in the file to read. However, I'm not re-initialized CARD, which is why, it already contains 57 records and only 25 of these records are re-written.
But during the write process, all 57 records are written to the file again and hence, i have 9,975 + 57 = 10,032 records in my output file, with a repetition of the 32 records.
I do not understand how to resolve this issue, in general.
I can make changes in this particular case - but the no. of records may vary from file to file.
Can someone help me, in resolving this issue.
Even if someone can tell me, where to find the no. of bytes that were read, after the READ statement - it would help me tremendously.
Joined: 30 Nov 2013 Posts: 917 Location: The Universe
You're pretty close. Where you went wrong is you have to compute, after every input I/O, the actual number of bytes you read. This computation depends on a couple of obscure data areas: the IOB (Input/Output Block) and a data area in the IOB, IOBCSW. The CSW (Channel Status Word) is a System/360 data area; it represents data left behind by an I/O interrupt. The BSAM I/O driver constructs a CSW from equivalent data in z/OS. We are only interested in the last two bytes of the CSW, the residual byte count. This is just fancy talk for the number of bytes that were not read in the I/O operation. In other words, if you tried to read 160 bytes, and you only read 80 bytes, the residual byte count will be 80. 160-80 is the actual number of bytes you read. Now here's the complete working (and tested) program.
Code:
BSAMRW CSECT
USING *,12
SAVE (14,12),,*
LR 12,15
LA 15,SAVEAREA
ST 13,4(,15)
ST 15,8(,13)
LR 13,15
OPEN (IDCB,INPUT)
MVC (DCBRECFM-IHADCB)+ODCB,(DCBRECFM-IHADCB)+IDCB
MVC (DCBLRECL-IHADCB)+ODCB,(DCBLRECL-IHADCB)+IDCB
MVC (DCBBLKSI-IHADCB)+ODCB,(DCBBLKSI-IHADCB)+IDCB
OPEN (ODCB,OUTPUT)
GETBUF IDCB,(2)
ST 2,(DECAREA-DECB)+IDECB
ST 2,(DECAREA-DECB)+ODECB
LOOP READ IDECB,SF,IDCB,*-*,'S'
CHECK IDECB
LH 1,(DCBBLKSI-IHADCB)+IDCB
L 15,(DECIOBPT-DECB)+IDECB
LH 0,14(,15)
SR 1,0
STH 1,(DCBBLKSI-IHADCB)+ODCB
WRITE ODECB,SF,ODCB,*-*,'S'
CHECK ODECB
B LOOP
EOF CLOSE (IDCB,,ODCB)
FREEPOOL IDCB
L 13,4(,13)
RETURN (14,12),T,RC=0
SAVEAREA DC 18F'0'
IDCB DCB DSORG=PS,MACRF=R,DDNAME=INPUT,EODAD=EOF,BUFNO=1
ODCB DCB DSORG=PS,MACRF=W,DDNAME=OUTPUT
PUSH PRINT
PRINT NOGEN
DCBD DSORG=PS,DEVD=DA
IHADECB DSECT=YES
POP PRINT
END BSAMRW
There are several differences between my program and your program.
My program copies the RECFM, LRECL, and BLKSIZE DCB attributes from the input DCB to the output DCB. My program does not depend on known DCB attributes.
My program directs OPEN to allocate a buffer pool for the input DCB, and it uses the GETBUF macro to obtain an I/O buffer from the buffer pool and stores the buffer address in the DECBs. This way I do not have to supply an I/O buffer in the program.
It computes the number of bytes read after every READ/CHECK. Not every data block has to be a full data block, and, as you have already discovered, the last data block is seldom full.
It frees the buffer pool after the CLOSE macro. CLOSE does not free the buffer pool for reasons I won't try to list here; you have to do it.
This is the code to compute the actual bytes read.
Code:
LH 1,(DCBBLKSI-IHADCB)+IDCB
L 15,(DECIOBPT-DECB)+IDECB
LH 0,14(,15)
SR 1,0
STH 1,(DCBBLKSI-IHADCB)+ODCB
First, the program loads the input BLKSIZE from the DCB. Next, it loads the address of the IOB from the DECB. This is not a "real" IOB; BSAM no longer uses them. BSAM just constructs parts of a fake IOB so programs, like this one, written for OS/360 will still work. The program loads the residual byte count from the fake CSW in the fake IOB, and subtracts the real residual byte count from the real BLKSIZE. Finally, it updates DCBBLKSI in the output DCB so the WRITE macro can write the correct number of bytes.
Two other notes.
The IHADECB macro is in SYS1.MODGEN, not SYS1.MACLIB, where, in my opinion, it should be.
IOBCSW is at offset 8 in the IOB, and the residual byte count is at offset 6 in the CSW. The 14 used in the LH 0,14(,15) instruction is just 8 + 6.