Chapter 21: Input/Output Macros
The IBM System/360 and those that have followed in the family have evolved an elaborate I/O system in an attempt to maintain efficiency in processing extremely large data sets. Even the early System/360 designs had several levels in the I/O architecture: logical IOCS (Input/Output Control System), Physical IOCS, and the Channel subsystem. While such a multi–level organization can be very efficient, it is somewhat hard to program.
From an Assembler Language level, the proper control and use of I/O operations requires several sequences of instructions. Many times, these sequences appear as a fixed set of instructions in a fixed order, with optional parameters. Such sequences immediately suggest the use of macros with keyword parameters. Indeed, this is the common practice.
To review from the previous chapter, the use of a macro is based on a macro definition that is then invoked by what is called the “macro invocation”. As a standard example, we recall the decimal divide macro. This first definition is that of a positional macro.
Again, we note an obvious fact. Teaching examples tend to be short and explicit. This sample macro is so simple that few programmers would actually use it. However, the I/O macros that are the subject of this chapter are complex; nobody writes the equivalent code.
A MACRO begins with the key word MACRO, includes a prototype and a macro body, and ends with the trailer keyword MEND. Parameters to a MACRO are prefixed by the ampersand “&”. Here is the example definition.
Prototype DIVID ",&DIVIDEND,&DIVISOR
Model Statements ZAP &QOUT,&DIVIDEND
The macros used in the I/O system seem all to be keyword macros. The definition of a keyword macro differs from that of a positional macro only in the form of the prototype. Each symbolic parameter must be of the form &PARAM=[DEFAULT]. What this says is that the symbolic parameter is followed immediately by an “=”, and is optionally followed by a default value. As a keyword macro, the above example can be written as:
Prototype DIVID2 "=,&DIVIDEND=,&DIVISOR=
Model Statements ZAP
Here are a number of equivalent invocations of this macro, written in the keyword style. Note that this definition has not listed any default values.
It is possible to use labels defined in the body of the program as default values.
With this definition, the two invocations are exactly equivalent.
The invocation of the macro DIVID2 will expand as follows:
Having reviewed the syntax of keyword macros, we now turn to the main topic of this chapter: a brief discussion of the Input/Output Control System and associated macros. Following the lead of Peter Abel [R_02], the focus will be on the following:
DCB Data Control Block, used to define files.
OPEN This makes a file available to a program, for either input or output.
terminates access to a file in an orderly way.
For a buffered output
approach, this ensures that all data have been output properly.
GET This makes a record available for processing.
writes a record to an output file. In a
buffered output, this may
write only to an output buffer for later writing to the file.
Each I/O macro that we shall discuss expands into a sequence of calls to operating system routines, most probably in the LIOCS (Logical I/O Control System) level. For this reason, we should review the general–purpose registers used by the operating system.
0 and 1 Logical
IOCS macros, supervisor macros, and other IBM
macros use these registers to pass addresses.
by logical IOCS and other supervisory routines to hold the
address of a save area. This area holds the contents of the user
program’s general purpose registers and restores them on return.
14 and 15 Logical
IOCS uses these registers for linkage. A
GET or PUT
will load the address of the following instruction into register 14
and will load the address of the actual I/O routine into register 15.
This use of
registers 13, 14, and 15 follows the IBM standard for
subroutine linkage, which will be discussed in a later chapter.
One “take away” from this discussion is the fact that user programs should reference and use only registers 3 through 12 of the general–purpose register set. Some general–purpose registers are less “general purpose” than others.
In IBM terminology, a data set is a collection of data records that can be made available for processing. The term is almost synonymous with the modern idea of a disk file; for most of this text the two terms will be viewed as equivalent. One should realize that the idea of a data set is more general than that of a disk file. Data sets can be found on a DASD (Direct Access Storage Device, either a magnetic disk or a magnetic drum), on magnetic tape, or on a sequence of paper punch cards. The term “data set” is a logical construct.
In order to understand the standard forms of record organization, one must recall that magnetic tape was often used to store data. This storage method had been introduced in the 1950’s as a replacement for large boxes of punched paper cards. The standard magnetic tape was 0.5 inches wide and either 1200 or 2400 feet in length. The tape was wound on a removable reel that was about 10.5 inches in diameter. The IBM 727 and 729 were two early models.
The IBM 727 was officially announced on September 25, 1963 and marketed until May 12, 1971. The figure at left was taken from the IBM archives, and is used by permission.
It is important to remember that the tape drive is an electro–mechanical unit. Specifically, the tape cannot be read unless it is moving across the read/write heads. This implies a certain amount of inertia; physical movement can be started and stopped quickly, but not instantaneously.
One physical manifestation of this problem with inertia is the inter–record gap on the magnetic tape. If the tape contains more than one physical record, as do almost all tapes, there must be a physical gap between the records to allow for starting and stopping the tape. In other words, the data layout on the tape might resemble the following:
One issue faced early by the IBM design teams was the percentage of tape length that had to be devoted to these inter–record gaps. There were several possible solutions, and each one was pursued. Better mechanical control of the tape drive has always been a good choice.
Another way to handle this problem would be to write only large physical records. Larger records lead to a smaller percentage of tape length devoted to the inter–record gaps. The efficiency problem arises with multiple small records, such as images of 80–column cards.
One way to improve the efficiency of storage for small records on a magnetic tape is to group the records into larger physical records and store these on tape. The following example is based on the one above, except that each physical record now holds four records. Note the reduction of the fraction of tape length devoted to inter–record gaps.
This process of making more efficient use of tape storage is called record blocking. The program reads or writes logical records that have meaning within the context of that program. These logical records are blocked into physical records for efficiency of storage. In a particular data set, all physical records will contain the same number of logical records; the blocking factor is a constant. The only exception is the last physical record, which may be only partially filled.
set of 17 logical records written to a tape with a blocking factor of 5. There would be four physical records on the
Physical record 1 would contain logical records 1 – 5,
physical record 2 would contain logical records 6 – 10,
physical record 3 would contain logical records 11 – 15, and
physical record 4 would contain logical records 16 and 17.
On a physical tape, it is likely that the last physical record will be the same size as all others and be padded out with dummy records. In the above example, physical record 4 might contain two logical records and three dummy records. This is a likely conjecture.
Magnetic tape drives are not common in most computer systems these days, but the design feature persists into the design of the modern data set.
Use of the I/O Facilities
In order to use the data management facilities offered by the I/O system, a few steps are necessary. The program must do the following:
the physical characteristics of the data to be read or written with respect to
data set organization, record sizes, record blocking, and buffering to be used.
2. Logically connect the data set to the program.
3. Access the records in the data set using the correct macros.
terminate access to the data set so that buffered data (if any)
can be properly handled before the connection is broken.
While some of these steps might be handled automatically by the run–time system of a modern high–level language, each must be executed explicitly in an assembler program.
Style Conventions for Invoking I/O Macros
Some of the I/O macros, especially the file definition macro, require a number of parameters in order to specify the operation. This gives rise to a stylistic convention designed to improve the readability of the program. The most common convention used here is to use the keyword facility and list only one parameter per line.
While one possibly could use positional parameters in invoking an I/O macro, this would require any reader to consult a programming reference in order to understand what is intended. Of course, it is possible for a programmer to forget the proper argument order.
Here is a file definition macro invocation written in the standard style.
FILEIN DCB DDNAME=FILEIN, X
Note the “X” in column 72 of each of the lines except the last one. This is the continuation character indicating that the next physical line is a continuation of the logical line. To reiterate a fact, it is the presence of a non–blank character in column 72 that makes the next line a continuation. Peter Abel [R_02] places a “+” in that column; that is good practice.
Here is another style that would probably work. It is based on old FORTRAN usage.
FILEIN DCB DDNAME=FILEIN, 1
Note that every line except the last has a comma following the parameter. This is due to the fact that the parameter string after the DCB should be read as a single line as follows:
The File Definition Macro
The DCB (Data Control Block) is the file definition macro that is most commonly used in the programs that we shall encounter. As noted above, it is a keyword macro. While the parameters can be passed in any order, it is good practice to adopt a standard order and use that exclusively. Some other programmer might have to read your work.
The example above shows a DCB invocation that has been shown to work on the particular mainframe system now being used by Columbus State University. It has the form:
Filename DCB DDNAME=Symbolic_Name, X
The name used as the label for the DCB is used by the other macros in order to identify the file that is being accessed. Consider the following pair of lines.
The example macro has one problem that might lead to confusion. Consider the line:
Filename DCB DDNAME=Symbolic_Name, X
The file name is the same as the symbolic name. This is just a coincidence. In fact it is the filename, which is the label associated with the DCB, that must match the other macros.
Here is an explanation of the above entries in the invocation of the DCB macro.
DDNAME identifies the file’s symbolic name, such as SYSIN for the primary system input device and SYSPRINT for the primary listing device. Here we use a slightly nonstandard name FILEIN, which is associated with SYSIN by a job control statement near the end of the program. That line is as follows:
//GO.FILEIN DD *
The “*” in this statement stands for the standard input device, which is SYSIN. This statement associates the symbolic name FILEIN with SYSIN.
identifies the data set organization.
Typical values for this are:
PS Physical sequential, as in a set of cards with one record per card.
DEVD defines a particular I/O unit. The only value we shall use is DA, which indicates a direct access device, such as a disk. All of our I/O will be disk oriented; even our print copy will be sent to disk and not actually placed on paper.
specifies the format of the records. The
two common values of the parameter are:
F Fixed length and unblocked
FB Fixed length and blocked.
specified the length (in bytes) of the logical record. A typical value would be
a positive decimal number. Our programs will all assume the use of 80–column punched cards for input, so that we set LRECL=80.
BLKSIZE specifies the length (in bytes) of the physical record. Our sample invocation does not use this parameter, which then assumes its default value. If the record format is FB (fixed length and blocked), the block size must be an even multiple of the logical record size. If the record format is F (fixed length and unblocked), the block size must equal the logical record size. It is probably a good idea to accept the default value for this parameter.
EODAD is a parameter that is specified only for input operations. It specifies the symbolic address of the line of code to be executed when an end–of–file condition is encountered.
MACRF specifies the macros to be used to access the records in the data set. In the case of GET and PUT, it also specifies whether a work area is to be used for processing the data. The work area is a block of memory set aside by the user program and used by the program to manipulate the data. We use MACRF=(GM) to select the work area option.
The OPEN Macro
This macro opens the data set and makes its contents available to the program. More than one dataset can be opened with a single macro invocation. The upper limit on datasets for a single OPEN statement is 16, but that number would produce unreadable code. As a practical matter, your author would prefer an upper limit of two or three datasets for each invocation of the OPEN macro.
Consider the following two sequences of macro invocations. Each sequence does the same thing; it opens two datasets.
Sequence 1 is a single statement.
Sequence 2 has two statements, which could appear in either order.
Each of these statements assumes that the two Data Control Blocks are defined.
Define the input file here
PRINTER DCB Define the output file here
The general format of the OPEN macro for one file is as follows [R_21, page 67].
[LABEL] OPEN (ADDRESS[,(OPTIONS)]
Multiple files can be opened at the same time, by continuing the argument list.
[LABEL] OPEN (ADDRESS1[,(OPTIONS1),ADDRESS2[,(OPTIONS2)]
Note that the first argument for opening the dataset is the file name used as the label for the DCB that defines the dataset. This is the label (address) associated with the DCB, not the symbolic name of the file (SYSIN, SYSPRINT, etc.).
It is also possible to pass the address of the DCB in a general–purpose register. When a register is used for this purpose, it is enclosed in parentheses. Here are two equivalent code sequences, each of which opens FILEIN for INPUT.
* OPTION 2
Note the parentheses around the second argument in each of the two individual invocations of the OPEN macro. This is a use of the sublist option for macro parameters [R_17, p. 302]. A sublist is a character string that consists of one or more entries separated by commas and enclosed in parentheses. What is happening here is that the macro definition is written for a sublist as a symbolic parameter, and this is a sublist of exactly one item.
There is one advantage in creating a separate OPEN statement for each file to be opened. If the macro fails, the line number of the failing statement will be returned. With only one file per line, the offending file is identified immediately.
The Close Macro
This macro deactivates the connection to a dataset in an orderly fashion. For output datasets, this will flush any data remaining in the operating system buffers to the dataset, so that nothing is lost by closing the connection. If needed, this macro will update any catalog entries for the dataset; in the Microsoft world this would include the file attributes.
Once a dataset is closed, it may be accessed again only after it has once again been opened.
While it may be possible to execute a program and terminate the execution without issuing a CLOSE for each open file, this is considered very bad programming practice.
The general format of the CLOSE macro for one file is as follows [R_21, page 27].
[LABEL] CLOSE (ADDRESS[,(OPTIONS)]
Multiple files can be closed at the same time, by continuing the argument list.
[LABEL] CLOSE (ADDRESS1[,(OPTIONS1),ADDRESS2[,(OPTIONS2)]
that has been successfully used in our lab assignments seems not to be of
this form. Here are the lines that we have used to close the INPUT and PRINTER.
A90END CLOSE FILEIN
The format above is that preferred for use when running under the DOS operating system, which is an IBM product not related to the better known Microsoft product. Our programs are run under a variant of the OS operating system. According to the standard format for OS, the above statements should have been written as follows.
A90END CLOSE (FILEIN)
Apparently, either form of the CLOSE macro for a single file will work.
When closing more than one file with a single CLOSE macro, one must allow for the fact that the options do exist, even if not commonly used. Here is the proper format.
A90END CLOSE (FILEIN,,PRINTER)
two commas following FILEIN. This
OPTIONS1 is not used. Were only one comma present, the assembler would try to interpret the string PRINTER as an option for closing FILEIN. The lack of options following the string PRINTER
The next two system macros to be discussed are GET and PUT. Before discussing either of these, it is important to note an I/O mode that will not be discussed here. This is called “locate mode”; it allows direct processing of data in the system buffers, so that the program need not define a work area. As this is a very minor advantage [R_02, page 262], we shall omit this feature and assume that each GET and PUT references a work area.
The GET Macro
This macro makes available the next record for processing. The record input overwrites the previous contents of the input area. There are two general formats as used with a work area.
[label] GET Filename,Workarea
[label] GET (1),(0)
In each of
these formats, the
following examples, the file name is FILEIN and the work area is
FILEIN DCB Define the input file
RECDIN DS CL80 This is the input work area
The second uses the use of general–purpose registers 0 and 1 in the standard manner to store the addresses of the file definition area and the work area
LA 1,FILEIN Address of the file definition
LA 0,RECDIN Address of the work area
GET (1),(0) Read a record into RECDIN. Note the
standard use of the parentheses.
The PUT Macro
This macro writes a record from the output work area. There are two general formats.
[label] PUT Filename,Workarea
[label] PUT (1),(0)
In each of
these formats, the
following examples, the file name is PRINTER and the work area is
PRINTER DCB Define the input file
DATOUT DS CL133 This is the output work area
The second uses the use of general–purpose registers 0 and 1 in the standard manner to store the addresses of the file definition area and the work area
LA 1,PRINTER Address of the file definition
LA 0,DATOUT Address of the work area
PUT (1),(0) Copy data from work area to printer.
Expansion of the I/O Macros
47 OPEN (PRINTER,(OUTPUT))
000014 48+ CNOP 0,4
000014 4510 C016 0001C 49+ BAL 1,*+8
000018 8F 50+ DC AL1(143)
000019 000098 51+ DC AL3(PRINTER)
00001C 0A13 52+ SVC 19
53 OPEN (FILEIN,(INPUT))
00001E 0700 54+ CNOP 0,4
000020 4510 C022 00028 55+ BAL 1,*+8
000024 80 56+ DC AL1(128)
000025 0000F8 57+ DC AL3(FILEIN)
000028 0A13 58+ SVC 19
59 PUT PRINTER,PRHEAD
00002A 4110 C092 00098 61+ LA 1,PRINTER
00002E 4100 C1A2 001A8 62+ LA 0,PRHEAD
000032 1FFF 63+ SLR 15,15
000034 BFF7 1031 00031 64+ ICM 15,7,49(1)
000038 05EF 65+ BALR 14,15
66 GET FILEIN,RECORDIN
00003A 4110 C0F2 000F8 68+ LA 1,FILEIN
00003E 4100 C152 00158 69+ LA 0,RECORDIN
000042 1FFF 70+ SLR 15,15
000044 BFF7 1031 00031 71+ ICM 15,7,49(1)
000048 05EF 72+ BALR 14,15
95 A90END CLOSE (FILEIN)
000074 96+ CNOP 0,4
000074 4510 C076 0007C 97+A90END BAL 1,*+8
000078 80 98+ DC AL1(128)
000079 0000F8 99+ DC AL3(FILEIN)
00007C 0A14 100+ SVC 20
101 CLOSE (PRINTER)
00007E 0700 102+ CNOP 0,4
000080 4510 C082 00088 103+ BAL 1,*+8
000084 80 104+ DC AL1(128)
000085 000098 105+ DC AL3(PRINTER)
000088 0A14 106+ SVC 20
116 PRINTER DCB DSORG=PS,
119+* DATA CONTROL BLOCK
000098 121+PRINTER DC 0F'0' ORIGIN ON
122+* DIRECT ACCESS DE
000098 0000000000000000 123+ DC BL16'0' FDAD, DVTB
0000A8 00000000 124+ DC
125+* COMMON ACCESS ME
0000AC 00 126+ DC AL1(0) BUFNO, NUM
0000AD 000001 127+ DC AL3(1) BUFCB, BUF
0000B0 0000 128+ DC AL2(0) BUFL, BUFF
0000B2 4000 129+ DC BL2'0100000000000000' DSO
0000B4 00000001 130+ DC A(1) IOBAD FOR
131+* FOUNDATION EXTEN
0000B8 00 132+ DC BL1'00000000' BFTEK, BFA
0000B9 000001 133+ DC AL3(1) EODAD (END
0000BC 82 134+ DC BL1'10000010' RECFM (REC
0000BD 000000 135+ DC AL3(0) EXLST (EXI
136+* FOUNDATION BLOCK
0000C0 D7D9C9D5E3C5D940 137+ DC CL8'PRINTER' DDNAME
0000C8 02 138+ DC BL1'00000010' OFLGS (OPE
0000C9 00 139+ DC BL1'00000000' IFLGS (IOS
0000CA 0050 140+ DC BL2'0000000001010000' MAC
141+* BSAM-BPAM-QSAM I
0000CC 00 142+ DC BL1'00000000' OPTCD, OPT
0000CD 000001 143+ DC AL3(1) CHECK OR I
0000D0 00000001 144+ DC A(1) SYNAD, SYN
0000D4 0000 145+ DC H'0' INTERNAL A
0000D6 0000 146+ DC AL2(0) BLKSIZE, B
0000D8 00000000 147+ DC F'0' INTERNAL A
0000DC 00000001 148+ DC A(1) INTERNAL A
149+* QSAM INTERF
0000E0 00000001 150+ DC A(1) EOBAD
0000E4 00000001 151+ DC A(1) RECAD
0000E8 0000 152+ DC H'0' QSWS (FLAG
0000EA 0085 153+ DC AL2(133) LRECL
0000EC 00 154+ DC BL1'00000000' EROPT, ERR
0000ED 000001 155+ DC AL3(1) CNTRL
0000F0 00000000 156+ DC H'0,0' RESERVED A
0000F4 00000001 157+ DC A(1) EOB, INTER
160 * INPUT FILE - DATA CONTROL BLOCK
163 FILEIN DCB DSORG=PS,
166+* DATA CONTROL BLOCK
0000F8 168+FILEIN DC 0F'0' ORIGIN ON
169+* DIRECT ACCESS DE
0000F8 0000000000000000 170+ DC BL16'0' FDAD, DVTB
000108 00000000 171+ DC
172+* COMMON ACCESS ME
00010C 00 173+ DC AL1(0) BUFNO, NUM
00010D 000001 174+ DC AL3(1) BUFCB, BUF
000110 0000 175+ DC AL2(0) BUFL, BUFF
000112 4000 176+ DC BL2'0100000000000000' DSO
000114 00000001 177+ DC A(1) IOBAD FOR
178+* FOUNDATION EXTEN
000118 00 179+ DC BL1'00000000' BFTEK, BFA
000119 000074 180+ DC AL3(A90END) EODAD (END
00011C 90 181+ DC BL1'10010000' RECFM (REC
00011D 000000 182+ DC AL3(0) EXLST (EXI
183+* FOUNDATION BLOCK
000120 C6C9D3C5C9D54040 184+ DC CL8'FILEIN' DDNAME
000128 02 185+ DC BL1'00000010' OFLGS (OPE
000129 00 186+ DC BL1'00000000' IFLGS (IOS
00012A 5000 187+ DC BL2'0101000000000000' MAC
188+* BSAM-BPAM-QSAM I
00012C 00 189+ DC BL1'00000000' OPTCD, OPT
00012D 000001 190+ DC AL3(1) CHECK OR I
000130 00000001 191+ DC A(1) SYNAD, SYN
000134 0000 192+ DC H'0' INTERNAL A
000136 0000 193+ DC AL2(0) BLKSIZE, B
000138 00000000 194+ DC F'0' INTERNAL A
00013C 00000001 195+ DC A(1) INTERNAL A
196+* QSAM INTERF
000140 00000001 197+ DC A(1) EOBAD
000144 00000001 198+ DC A(1) RECAD
000148 0000 199+ DC H'0' QSWS (FLAG
00014A 0050 200+ DC AL2(80) LRECL
00014C 00 201+ DC BL1'00000000' EROPT, ERR
00014D 000001 202+ DC AL3(1) CNTRL
000150 00000000 203+ DC H'0,0' RESERVED A
000154 00000001 204+ DC A(1) EOB, INTER