Redesigning Assembler Language Development.

By Don V Nielsen, May 19, 2000

Disclaimer:
Bixoft converted "Redesigning Assembler Language Development" into a web-document with permission of the original author for re-publication. We also applied several minor changes. Nevertheless, the copyrights of Mr. Nielsen still apply to the document below.
All samples in this document are no more than examples, and are in no way guaranteed to be free of errors or omissions. You may use (parts of) these code-samples, but it remains in all aspects your own responsibility to test your programs.

Contact:
Mr. Nielsen will gladly accept your comments, remarks etc. e-mail Don Nielsen.

This document contains the following chapters:

Introduction.

The purpose of this paper is to bring to light coding methods and techniques using Assembler Language and the HLASM Assembler. The HLASM Assembler has evolved with much more power than many people realize. Knowledge of this power and understanding how to utilize it may save you many hours of coding and development, as well as reduce your maintenance efforts.

Programming concepts such as abstraction, reuse, derivation, and qualification can be implemented in assembler language just as they are in higher level languages, like C, COBOL, and Basic. Expanded use of copybooks is encouraged. New methods of implementing copybooks with the USING statement will be introduced. In addition, how and why DSECTs should be defined at the top of the source code will be explained.

Two simple applications are used to demonstrate how to work with the new concepts. The first application is a simple census program that reads records from a dataset, and then stores state abbreviations and their quantity in a table. The application demonstrates all the new coding techniques for building tables. It also demonstrates how these techniques can reduce or eliminate coding changes when the elements of the table change.

The second application is also a census program. This application does not use a table. Instead, it uses a dataset sorted by state abbreviation. It tallies state quantities as records are read by comparing the state abbreviation of the current record to the state abbreviation with the previous record. This application demonstrates how one DSECT can be used to simultaneously map two different records.

At first, learning these techniques might seem like a lot of work. Implementing them might appear to be a lot of typing. However, as time passes and the techniques are employed more frequently, their use will become second nature, and you will find the improvement in readability a great bonus. Your maintenance efforts will be dramatically reduced. In some instances, maintenance may even be eliminated.

The sample programs in Appendix A and Appendix B use HLASM Toolkit macros. These macros add programming structures found in other languages, such as IF/ELSE/ENDIF, DO loops, and others. The macros simplify coding and increase readability of the code.

Bixoft Remark:
In stead of the toolkit, the macros employed in Bixoft's eXtended Assembly language may be used for structure and even better readability.

Background Terminology.

Abstraction.

Webster defines abstraction as: "the act or process of separating or removing." Applying the term abstraction to computer programming means to separate the programmer from the underlying details of the process. Calling a function is one example of abstraction. A function is called to complete a task and return a result. We do not need to know how the function performs its task. We simply trust that it is going to perform that task correctly and return the correct result.

This trust is the most difficult concept that we assembler programmers need to exercise. Most of us assembler programmers want to know all the details. However, implementing abstraction will simplify our coding. This requires trusting that some things are simply because they are, and that it is not necessary for us to know why they are. This concept takes effort to accept. You should realize that the concept has been applied and accepted for a long time with regard to system routines.

Reuse.

Reuse is the concept of implementing the past effort of others in order to save development time and effort. A called module is one form of reuse. A macro is another example. When a standard set of logic can be coded as a called module or a macro, then others in the future can implement that logic without having to develop it (redeveloping the wheel, so to say). Yet another form of reuse is the copybook. The copybook is the definition of some structure. Others will not have to expend time and effort coding a structure if that structure is already available as a copybook - or a macro.

Derivation.

Derivation is receiving or obtaining something from a source. In the definition of a field, we may derive an attribute of the field from a previously defined field. For example, a program needs to save a copy of the ZIP code from an input record. The program utilizes a macro or a copybook that has already defined the attributes of the ZIP code. The existing attributes from the existing definition in the macro or copybook can be derived and used in the definition of the new field.

Building Blocks.

The Field.

A field is the simplest of structures. It represents one piece of data. To the assembler, a field represents a number of bytes in storage and not much else. Some may consider the type of data to be important. However, to the assembler, the definition of a field as packed, hexadecimal, or character is for the convenience of the programmer. The most meaningful attribute of a field is the amount of storage it occupies.

The Lay-out of a Record or Table Entry.

All records and table entries can be described with a lay-out. A lay-out is a structure that maps data. It defines the locations and lengths of fields, and enforces structure. The length of a lay-out is the cumulative value of the field lengths plus any padding caused by field alignment. Records and table entries are functionally the same.

Always code lay-outs for table entries and records. This is the first, most important step when beginning to code. Coding lay-outs simplify the development and maintenance of programs. If a lay-out is to be shared across multiple programs, then save the lay-out as a macro or a copybook. This will reduce coding time and effort on future projects.

The Table.

A table is an area of storage dedicated to holding a series of related data. A table consists of one or more entries, where the lay-out of each entry is identical. Visually, a two-dimensional table may be thought of as a piece of lined paper; the paper represents the table, and each of the lines represents an entry.

Tables, like fields, have attributes. A table has a starting address, which is the first byte of the first entry. It has an ending address, which is the last byte of the last entry. The space that the table occupies can be divided evenly by the entry length, yielding the maximum number of entries that the table can hold.

A simple census table application is used to demonstrate the concepts presented. The table will consist of two fields: state abbreviation and census count. Later, counts by gender will be added to the table entry lay-out. This will demonstrate how a table and program can be constructed such that a change in a lay-out's definition will not incur any coding changes. Maintenance to the program's code will have been eliminated.

Step 1, Map the Data.

The first step in developing the program is mapping the input dataset. The program reads input data through a DD name INFILE. It uses a DCB by the same name to read the input data. The data is read directly into a record structure defined by a copybook named CMS0001A. The copybook contains all the field definitions, and is implemented as a DSECT. The base structures (record lay-out and DCB) are coded as follows:

         ...
INPUT_LAYOUT DSECT
         COPY  CMS0001A                * Standard record lay-out
L_INPUT_LAYOUT EQU *-INPUT_LAYOUT      * Calculate length of lay-out
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Define storage for input
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...

When using a macro in stead of a copybook (or a macro using a copybook), the sample above could be recoded as:

         ...
INPUT_LAYOUT CMS0001A DSECT=YES        * Lay-out and length of input
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Define storage for input
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...

The concepts of reuse, abstraction, and derivation have all been applied in the above example. The use of the macro or copybook CMS0001A is an example of reuse and abstraction. First, an existing lay-out is being reused rather than redeveloping it. Always try to use an existing macro or copybook when available. The benefits of using a macro or copybook are:

  1. a macro or copybook enforces standardized field names across many programs
  2. only one definition of the record's lay-out exists
  3. a change in the lay-out is applied to only the copybook, not many programs
  4. time and effort re-coding the lay-out for each program is eliminated

Use of the macro or copybook implements abstraction, too. The macro or copybook hides the details of the record lay-out. Yes, it is important to know the record lay-out that one is working with. However, detailed knowledge of the lay-out's structure is seldom required. Abstraction is also applied with the definition of L_INPUT_LAYOUT. This statement uses the assembler to calculate the length of the defined lay-out. It is abstract because you do not need to know the length; you trust that the length has been calculated for your use.

Derivation is used in the definition of the INREC field. The assembler sets the length of INREC using the calculated record length, L_INPUT_LAYOUT. This is also an example of how to implicitly define a field's length. With HLASM, the length attribute of a field can be assigned from a previously defined field. Deriving the length attribute allows HLASM to allocate storage based on the existing length value.

HLASM allows you to define a field's length explicitly or implicitly. An explicit definition of a field's length has the actual length coded. For example, the above INREC definition could have been coded explicitly using "INREC DS CL200" This is a simpler method of defining INREC. However, there are two reasons for discouraging the use of explicit lengths:

  1. doing so requires prior knowledge of a structure's length
  2. the length of the structure may change

In order to allocate storage explicitly, one must have prior knowledge of the space required. One would have to research the macro or copybook, determine its length, and code that length in the explicit definition. In addition, much effort is required to update programs when the length of the lay-out changes. All programs that implement the lay-out will require research in order to update its explicit storage allocations.

An implicit definition means that a symbol is used to define the field's length. L_INPUT_LAYOUT is an example of an implicit length because the symbol is a representation of the required length. L_INPUT_LAYOUT is the length of CMS0001A as calculated by the assembler. Broad implementation of this concept can reduce maintenance efforts from hours to minutes. Should the actual length of CMS0001A change, the assembler will calculate the new length, assign the length value to L_INPUT_LAYOUT, which is then used to set the length of INREC. Thus, updating the macro or copybook also updates all programs - automatically.

Adding the Census Table.

         ...
INPUT_LAYOUT CMS0001A DSECT=YES        * Lay-out and length of input
         ...
TABLE_ENTRY DSECT
STATE    DS    CL(L'CMSTATE)           * State abbreviation
TOTAL    DS    F                       * Total quantity
L_TABLE_ENTRY EQU *-TABLE_ENTRY        * Calculate length of entry
         ...
MAX_ENTRIES EQU 100                    * Maximum nr of entries in
                                         table
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Define storage for input
...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...
* Define table storage
STATE_CENSUS_TABLE DC (MAX_ENTRIES)XL(L_TABLE_ENTRY)'00'
E_STATE_CENSUS_TABLE EQU *             * End of table
         ...

Two structures and a constant have been added to our code example. The TABLE_ENTRY structure provides a map of the fields used in the table entry. The first field, STATE, is for the state abbreviation and it is the key field for searching the table. Notice that STATE is the second example of derivation. STATE derives its length from CMSTATE. You are not required to know how long CMSTATE is. You trust that STATE will be defined for an equal number of bytes.

The second field, TOTAL is the counter field. It is defined as F, a signed binary fullword, or 32 bits. As each input record is processed, the state abbreviation is located in the table. If the abbreviation is not found, then it is added. The TOTAL field is then incremented.

The assembler must be used to calculate L_TABLE_ENTRY. This is required because the length of STATE is derived. There is no way to know, without research, the actual length of CMSTATE. So, we trust that the assembler derives the length of CMSTATE and assigns it to STATE. The result is the length of STATE being equal to the length of CMSTATE. L_TABLE_ENTRY is equal to L'STATE+L'TOTAL plus any padding bytes required for alignment. Once again, the actual length value is not important. It is important to realize that the length exists and that it has been computed correctly by the assembler.

MAX_ENTRIES is a constant that represents the maximum number of entries that can be stored in the table. It is used in the definition of STATE_CENSUS_TABLE. The table's size is controlled by this constant. A table, as defined earlier, is the space used by all the table entries. Thus, the size of the table is equal to the table's entry length times the maximum number of entries. The actual definition of STATE_CENSUS_TABLE is an example of this calculation. Using MAX_ENTRIES and L_TABLE_ENTRY to implicitly define the attributes of the table, the assembler calculates the space required and allocates that space to the symbol STATE_CENSUS_TABLE

The real power of this implicit definition is realized when the table requires maintenance. All it takes to expand the number of entries that STATE_CENSUS_TABLE may contain is to change the MAX_ENTRIES value. In addition, if the table's entry definition changes, the assembler uses the resulting entry length in establishing the size of STATE_CENSUS_TABLE. This will be demonstrated later when the gender counters are added to TABLE_ENTRY.

Step 2, Build the Program.

Accessing the Fields in the Record and the Table.

         ...
         USING INPUT_LAYOUT,INREC      * Assign DSECT to INREC
         USING TABLE_ENTRY,R3          * Assign DSECT to REGISTER
         ...
FILELOOP GET   INFILE,INREC            * Read record into storage
         LA    R3,STATE_CENSUS_TABLE   * Load ptr to table entry
TBLLOOP  C     R3,=A(E_STATE_CENSUS_TABLE) * If end of table
         BNL   ERRTABLE                * branch to error routine
         CLI   STATE,X'00'             * If state is null
                                         (empty entry)
         BE    ADDSTATE                * then add state to table
         CLC   CMSTATE,STATE           * Input matches table record?
         BE    STFOUND                 * Yes: entry located
         LA    R3,L_TABLE_ENTRY(R3)    * Point next entry in table
         B     TBLLOOP                 * and go check this entry
ADDSTATE MVC   STATE,CMSTATE           * Init new state in table
STFOUND  LA    R1,1                    * Load increment value
         A     R1,TOTAL                * Add quantity for state
         ST    R1,TOTAL                * Save incremented value
         B     FILELOOP                * And go get next record
         ...

After an input record has been read, it is necessary to locate the state abbreviation in the table. Before we can access any data, both the input record and the table entry must be mapped. This is accomplished with the USING statement. USING is not an instruction that is processed like a compare instruction or a branch instruction. It is a directive used by the assembler to determine how and where storage is being mapped. DSECTS are used to describe the storage. A DSECT can map storage as referenced by a symbol, or it can map storage as referenced by the contents of a register. Both implementations are used in the example.

USING INPUT_LAYOUT,INREC applies the CMS0001A lay-out to the storage defined as INREC. Whenever a field from the CMS0001A copybook is referenced by an instruction, the data from that field is pulled from the INREC storage space. CMSTATE is located at position 147 of the CMS0001A lay-out. Whenever CMSTATE is referenced in the program, position 147 of INREC (or INREC+146) is the source of the data.

USING TABLE_ENTRY,R3 applies the TABLE_ENTRY lay-out to the storage being pointed to by register 3. Whenever a field from the TABLE_ENTRY lay-out is referenced by an instruction, the data from that field is located within storage pointed at with R3. STATE is located at position 1 of the table entry lay-out. Whenever STATE is referenced in the program, then the first byte pointed to by R3 [or 0(R3)] is the location of the state abbreviation in the table entry.

The flow of the programming loop is as follows:

Let's go over some key concepts at this point. The first point is the relationship between INPUT_LAYOUT and INREC. INREC was defined using the calculated length of the input record lay-out, or L_INPUT_LAYOUT. While INREC has the proper amount of storage defined, it does not have fields assigned to it. References to fields within INREC are assigned by the USING INPUT_LAYOUT,INREC statement. Thus, whenever a field from INPUT_LAYOUT is referenced, it is the storage from INREC that is accessed.

The symbol L_TABLE_ENTRY is used to increment the table pointer, R3. The assembler uses the calculated value. A change in the table entry length will not affect this code.

Adding Gender Counts to the Table.

         ...
INPUT_LAYOUT CMS0001A DSECT=YES        * Lay-out and length of input
         ...
TABLE_ENTRY DSECT
STATE    DS    CL(L'CMSTATE)           * State abbreviation
TOTAL    DS    F                       * Total quantity
FEMALE   DS    F                       * Female quantity
MALE     DS    F                       * Male quantity
UNKNOWN  DS    F                       * Unknown gender
L_TABLE_ENTRY EQU *-TABLE_ENTRY        * Calculate length of entry
         ...
MAX_ENTRIES EQU 100                    * Max.nr of entries in table
         ...
INREC    DS    CL(L_INPUT_LAYOUT)      * Define storage for input
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOFINFIL
         ...
* Define table storage
STATE_CENSUS_TABLE DC (MAX_ENTRIES)XL(L_TABLE_ENTRY)'00'
E_STATE_CENSUS_TABLE EQU *             * End of table
         ...

Now that we have the basic program developed, new counters are going to be added to the table. In adding these counters, you are going to realize the effort and time saving qualities provided by these programming techniques.

The only change required for the entire program is to add the fields FEMALE, MALE, and UNKNOWN. That's it. The program can be reassembled with no other changes.

Let's go through what has happened and how the assembler resolves the changes. Three fields were added to TABLE_ENTRY. These fields have increased the table entry length by twelve bytes. The calculated value L_TABLE_ENTRY is used throughout the program. The assembler calculates the required table storage using the implicit values of L_TABLE_ENTRY and MAX_ENTRIES and assigns that amount of storage to STATE_CENSUS_TABLE.

No changes are necessary in the processing code. The statements affected by the changes are as follows:

Note:
if the size of CMSTATE is altered in the CMS0001A macro or copybook, this application can be reassembled, error free, with no modifications, whatsoever.

Using the Same Lay-out Twice.

Many programs need to compare the contents of one record to the contents of another record. In this case, we have two instances of the same structure. In the past, this meant coding two separate lay-outs with field names that are mildly different. An example might be ZIP1 and ZIP2.

HLASM provides a feature called the Labelled Using statement to resolve this problem. A Labelled Using statement allows us to assign one DSECT to multiple locations at the same time. The locations may be referenced by a register, or may be assigned to a symbol defined in core.

To demonstrate this power, a new census program will be developed. We will assume the input dataset is sorted into state abbreviation sequence. The program will read records and increment a counter. When the state abbreviation changes, the counter will contain the number of records for the previous state abbreviation.

To accomplish this, the program is going to read a record, compare the state abbreviation on the record just read to the previous record's state abbreviation. If there is a change, then a count will be produced. The record just read will be moved to the storage assigned for the previous record.

         ...
COUNTER  DC    PL4'1'                  * State counter
PREVREC  DS    CL(L_INPUT_LAYOUT)      * Storage for input record
         ...
INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GL,EODAD=EOFINFIL
         ...

The above code establishes a save area for the previous record processed. Once an input record has been processed, it will be moved to PREVREC. In addition, the MACRF on the DCB has been changed from GM to GL. This informs the program that the input record is not moved to a defined storage area with each get. The record read by the system is considered the current record, and it remains in its buffer where it can be referenced with R1.

Note:
if the program is using any system service other than the get macro, or calls any subprogram during the processing of any record, R1 will be considered a volatile register, and cannot be used to keep any data or pointer during or after execution of that service or call. In a real program R1 should be used only as a temporary or working register.

A table is not defined in this application as nothing is being tabled. The program uses the change in state abbreviation to indicate a state count.

         ...
PRV      USING INPUT_LAYOUT,INREC      * Assign DSECT to INREC
CUR      USING INPUT_LAYOUT,R1         * Assign DSECT to register
         ...
FILELOOP GET   INFILE                  * Read record into storage
         CLC   CUR.CMSTATE,PRV.CMSTATE * State changed?
         BNE   CHGSTATE                * Yes: go process changed
                                         state
* Save current record
         MVC   INREC,CUR.INPUT_LAYOUT  * or code "INREC,0(R1)"
         AP    COUNTER,P'1'            * Increment counter
         B     FILELOOP                * and go read next record
         ...

The USING statements are assigned the labels PRV and CUR. The USING labelled PRV uses INPUT_LAYOUT to map storage at PREVREC. The USING labelled CUR uses INPUT_LAYOUT to map storage at 0(R1), which is the location of the record within its buffer. The USING labels are used to qualify a field name so that the assembler understands which instance of the field name you are referring to. PRV.CMSTATE refers to the location of CMSTATE within PREVREC, or PREVREC+146. CUR.CMSTATE refers to the location of CMSTATE, as pointed to by R1, or 146(R1).

Placing DSECTs at the Top of the Program.

Why Place DSECTs at the Top of the Program?

The assembler operates using two passes of the source code. In the first pass of the source code, it identifies all symbols and attempts to identify all the attributes of those symbols. However, some attributes may not be resolved because they cannot be established in a sequential manner. In these cases, the attributes will not be resolved until the second pass of the source code.

Identification of symbols involves reading copybooks and expanding macros. Macros are those single statements that generate code for you. For example, DCB, OPEN, and GET are examples of macros. Structured programming macros from the HLASM Toolkit or Bixoft's eXtended Assembly language like IF/ELSE/ENDIF and DO/END are other examples. Some macros require all symbol attributes to be resolved during the first pass. Without resolution, the assembler may generate errors because it cannot identify field lengths.

The assembler processes the source in sequence. So, placing DSECTs that invoke copybooks at the top of the source code establishes the symbols early on so that a macro's code can be expanded without error.

Personally, I like to have all DSECTs at the top of the program. This is in line with other languages like PASCAL, C, COBOL, and BASIC. It provides one location for all the structures, and provides the programmer with information as to what the program will be working with.

Bixoft Remark:
Our advice is to insert all DSECTs at the top of the program. This has the following advantages:

Additional Housekeeping Required.

The beginning of an assembler program will normally begin with some type of housekeeping entry macro. However, if you place DSECTS above the entry macro, it may cause an assembly error. To prevent the error from occurring, a change in the housekeeping code at the top of the program is required. A program should follow this structure.

ProgName CSECT
*
*        DSECTS and mapping macros go here
*
ProgName "Housekeeping Macro or entry point CSECT logic"
         ...

The CSECT statement at the top prevents the assembly errors from occurring.

Bixoft remark:
When using Bixoft's eXtended Assembly language, the PGM macro is used for generating the entry logic, including all mappings required. E.g.:

ProgName PGM   VERSION=V00R00M00,      * Version number
 *
               HDRTXT='Sample program',
 *
               SAVES=2,                * Internal save-areas
 *
               MAPS=(CMS0001A,         * Private lay-outs
 *
               DCB,DCBE,DECB,IOB)      * Standard IBM lay-outs

In Conclusion.

As this paper demonstrates, programming concepts like abstraction, reuse, and derivation can be utilized in assembler as they are in higher level languages like PASCAL, C, or BASIC. Abstraction can be used to hide the details of structure that would otherwise clutter the source code. Reuse allows us to leverage the accomplishments of others so that our effort is reduced. Derivation allows us to use pre-existing attributes in the definition of like fields. All the concepts give us the ability to make the HLASM assembler work harder for us so we do not have to.

The sample programs show how abstraction and reuse are applied to macro and/or copybook usage. Macros and copybooks standardize our programs. It speeds up the process of understanding the code because field names will be consistent from one module to the next. Copybooks also provide the basis from which we can derive like storage spaces and like field definitions. For example, we can use the definition of CMSTATE in the definition of a table entry so those like fields have like attributes.

The sample programs also demonstrate that broad use of these techniques can dramatically reduce our future maintenance efforts. In some cases, maintenance may be reduced to zero when a record or field definition changes.

It is my hope that you will take an interest in the techniques explained in this paper. The use of these techniques will save you much time and effort in the future. These techniques reduce the impact that change has on the entire module.

Appendix A: Sample Program Using Table.

SAMPLE01 CSECT
INPUT_LAYOUT DSECT
         COPY  CMS0001A                * Standard record lay-out
L_INPUT_LAYOUT EQU *-INPUT_LAYOUT      * Calculate length of lay-out

TABLE_ENTRY DSECT
STATE    DS    CL(L'CMSTATE)           * State abbreviation
TOTAL    DS    FL4                     * Total quantity
FEMALE   DS    FL4                     * Female quantity
MALE     DS    FL4                     * Male quantity
UNKNOWN  DS    FL4                     * Unknown gender
L_TABLE_ENTRY EQU *-TABLE_ENTRY        * Calculate length of entry

MAX_ENTRIES EQU 100                    * 100 Total entries

SAMPLE01 ENTERMPG BASE=R12,VER=A,MAIN=N,TEST=Y,DESC='TABLE EXAMPLE'
         USING INPUT_LAYOUT,INREC      * Assign DSECT to symbol
         USING TABLE_ENTRY,R3          * Assign DSECT to register

         IF    (CLI,FirstTime,EQ,FTyes) * Only during first pass
          MVI  FirstTime,FTno          * Set first-pass indicator
          OPEN (INFILE,INPUT)          * Open input dataset
         ENDIF ,                       *

         DO    INF                     * Do until end of file
READREC   GET  INFILE,INREC            * Read input record

          LA   R3,STATE_CENSUS_TABLE   * Point to start of table
          DO   UNTIL=(C,R3,NL,=A(E_STATE_CENSUS_TABLE))

           IF  (CLI,STATE,EQ,X'00')    * Empty state located?
            MVC STATE,CMSTATE          * Yes: add new state to table
           ENDIF

           IF  (CLC,CMSTATE,EQ,STATE)  * State entry located?
            INCR TOTAL                 * Increment total count
            SELECT CLI,CMGENDER,EQ     * Which gender:
            WHEN CMGENDER_MALE         * Male?
             INCR MALE                 * Yes: increment male counter
            WHEN CMGENDER_FEMALE       * Female?
             INCR FEMALE               * Yes: increment female counter
            ELSE ,                     * Unknown gender, so
             INCR UNKNOWN              * increment unknown count
            ENDSEL
            B  READREC                 * Exit from inner do loop
           ENDIF

           LA  R3,L_TABLE_ENTRY(R3)    * Point next table entry
          ENDDO ,                      * and go check this entry

          WTO  'No more room in table' * Out of table space...
          ABEND 666                    * No more room

         ENDDO ,                       * End of outer loop

EOF      CLOSE INFILE                  * Close input dataset
         LEAVEMPG RC=0                 * End of job

         LTORG
FirstTime DC   XL1'00'                 * First-pass indicator
FTYES    EQU   X'00'                   * Yes: first pass
FTno     EQU   X'FF'                   * No: initialization done
INREC    DS    CL(L_INPUT_LAYOUT)      * define storage for input

INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOF

* Define the table
STATE_CENSUS_TABLE DC (MAX_ENTRIES)XL(L_TABLE_ENTRY)'00'
E_STATE_CENSUS_TABLE EQU *             * Define end-of-table address

         END   SAMPLE01

Appendix B: Sample Program Using Change in State Abbreviation.

SAMPLE02 CSECT

INPUT_LAYOUT DSECT
         COPY  CMS0001A                * Standard record lay-out
L_INPUT_LAYOUT EQU *-INPUT_LAYOUT      * Calculate length of lay-out

SAMPLE02 ENTERMPG BASE=R12,VER=A,MAIN=N,TEST=Y,DESC='ABBR SAMPLE'
PRV      USING INPUT_LAYOUT,INREC      * Assign DSECT to symbol
CUR      USING INPUT_LAYOUT,R3         * Assign DSECT to register

         IF    (CLI,FirstTime,EQ,FTyes) * Only during first pass
          MVI  FirstTime,FTno          * Set first-pass indicator
          OPEN (INFILE,INPUT)          * Open input dataset
          GET   INFILE                 * Read first record
          LR    R3,R1                  * Set pointer to record read
* Copy first record to prevent recognition of a state change
* for the very first record read.
          MVC   PRV.INPUT_LAYOUT,CUR.INPUT_LAYOUT
         ENDIF ,                       *

         DO    INF                     * Do until end of file
          GET  INFILE                  * Read a record
          LR   R3,R1                   * Set ptr to current record
          IF   (CLC,CUR.CMSTATE,NE,PRV.CMSTATE) * Change in state?
           OI  COUNTER+L'COUNTER-1,X'0F' * Make counter unsigned
           UNPK WTO01+24(7),COUNTER    * Insert counter into message
           MVC WTO01+15(L'CMSTATE),PRV.CMSTATE * Add previous state
WTO01      WTO 'STATE: ..   QTY=XXXXXXX ' * Display state and count
           ZAP COUNTER,=P'0'           * Reset counter
          ENDIF
          AP   COUNTER,=P'1'           * Increment counter
* Make current record the previous one
          MVC  PRV.INPUT_LAYOUT,CUR.INPUT_LAYOUT
*         MVC  INREC,0(R3)             * Alternative code for move
         ENDDO

EOF      CLOSE INFILE                  * Close input dataset
         LEAVEMPG RC=0                 * End of job

         LTORG
FirstTime DC   XL1'00'                 * First-pass indicator
FTYES    EQU   X'00'                   * Yes: first pass
FTno     EQU   X'FF'                   * No: initialization done
COUNTER  DC    PL4'1'                  * Initialize counter to one
INREC    DS    CL(L_INPUT_LAYOUT)      * Define storage for input

INFILE   DCB   DDNAME=INFILE,DSORG=PS,MACRF=GM,EODAD=EOF

         END

© Copyright Don V. Nielsen 2000. All rights reserved.

 

This site is a member of WebRing.
You are invited to browse the list of mainframe-loving sites.
Running
    Tyrannosaurus Rex Dinos are not dead. They are alive and well and living in data centers all around you. They speak in tongues and work strange magics with computers. Beware the dino! And just in case you're waiting for the final demise of these dino's: remember that dinos ruled the world for 155-million years!
Dinos and other anachronisms
[ Join Now | Ring Hub | Random | << Prev | Next >> ]
 

To Introduction.
To Background Terminology.
To Building Blocks.
To Step 1, Map the Data.
To Step 2, Build the Program.
To Using the Same Lay-out Twice.
To Placing DSECTs at the Top of the Program.
To In Conclusion.
To Sample Program Using Table.
To Sample Program Using Change in State Abbreviation.



Below you find the logo of our sponsor and logos of the web-standards that this page adheres to.