INDEX
*  PL360 TEXTBOOK
+  Preface
1  The IBM System/360 Computer
1.1  Storage
1.2  Registers
1.3  Fixed-Point Arithmetic
1.4  Instructions and Addressing
1.5  Relative Addressing
1.6  Logical Operations
1.7  Shift Operations
1.8  Floating-Point Arithmetic
1.9  Program Status Word
1.10  Summary
1.11  Exercise
2  The PL360 Language
2.1  The Program
2.2  Identifiers
2.3  Reserved and Pre-declared Identifiers
2.4  Special Symbols and Delimiters
2.5  Constants
2.5.1  Fixed-Point Constants
2.5.2  Floating-Point Constants
2.5.3  Summary of Rules for Fixed-Length Values
2.5.4  Variable-Length Strings
2.6  Comments
2.7  Exercise
3  The Main Program
3.1  Segmentation
3.2  Compiler Generated Names
3.3  Compilation and Execution
3.4  Blocks
3.4.1  Declarations
3.4.2  Statements
3.5  Main Programs
3.6  Data Segments
3.7  Program Segments
3.8  Exercise
4  Declarations
4.1  Data Segment Declarations
4.1.1  DUMMY BASE Declarations
4.1.2  CLOSE BASE Declarations
4.2  The Main Program Data Segment
4.3  Cell Declarations
4.3.1  Arrays
4.3.2  Cell Initialization
4.4  Cell References
4.5  Cell Synonyms
4.6  Register Synonyms
4.7  Integer Value Synonyms: EQUATE
4.8  Sample Declarations
4.9  Exercise
5  Assignment Statements
5.1  Register Assignment Statements
5.1.1  Integer Register Assignments
5.1.1.1  Reverse Assignment
5.1.1.2  Operator Restrictions and Precedence
5.1.1.3  Shift Operations
5.1.1.4  Logical Addition and Subtraction
5.1.1.5  Logical Operations
5.1.1.6  Multiplication and Division
5.1.1.7  Integer Register Usage
5.1.2  Floating-Point Register Assignments
5.2  Cell Assignments
5.2.1  Register-to-Storage
5.2.2  Storage-to-Storage
5.3  Exercise
6  Branching, Testing, and Loops
6.1  GOTO Statements
6.2  Conditions
6.2.1  Simple Conditions
6.2.1.1  Relational Operators
6.2.2  Compound Conditions
6.2.3  Special Conditions
6.3  IF Statements
6.4  WHILE Statements
6.5  REPEAT/UNTIL Statements
6.6  FOR Statements
6.7  CASE Statements
6.8  Exercise
7  Functions
7.1  Function Declarations
7.2  Function Statements
7.3  Pre-declared Functions
7.4  Other Functions
7.5  Exercise
8  Procedures
8.1  Local Procedures
8.2  Global Procedures
8.3  External Procedures
8.4  Pre-declared Procedures
8.5  Procedure Synonyms
8.6  Miscellaneous
8.7  Exercise
9  Sample Programs
:A  Appendix A -- Two's Complement Arithmetic
:B  Appendix B -- Floating-Point Description
:C  Appendix C -- EBCDIC Character Table
:D  Appendix D -- PL360 Constructs
:D.1  TABLE OF 2-BYTE INSTRUCTIONS
:D.2  TABLE OF 4-BYTE INSTRUCTIONS
:D.3  TABLE OF PRE-DECLARED FUNCTIONS
:D.4  TABLE OF CELLULAR INSTRUCTIONS
:D.5  Other Constructs of the Language
:E  Appendix E -- Compiler Control Facilities
:E.1  Listing Control
:E.2  Listing Options
:E.3  Operating System Control
:E.4  Program Base Register Control
:E.5  Identification
:E.6  Object Deck Control
:E.7  Copy Facility
:E.8  Conditional Compile Directives
:F  Appendix F -- Compiler Output
:F.1  Compiler Listing Output
:F.2  Compiler Object Program Output
:G  Appendix G -- Linkage Conventions
:G.1  Calling External Routines from PL360
:G.2  Requesting Supervisor Services
:G.3  Calling PL360 Procedures from External Routines
:G.4  Problems Involving Loading of Object Decks
:H  Appendix H -- Compiler Error Diagnostics
:I  Appendix I -- PL360 Syntactic Grammar
:J  Appendix J -- Format of PL360 Programs
:J.1  Indentation
:J.2  Spacing
:J.3  Choice of Identifiers
:J.4  Comments
:J.5  Miscellaneous
:K  Appendix K -- PL360 Under ORVYL
:K.1  Using the PL360 Compiler with ORVYL
:K.2  Input/Output Subroutines for Interactive PL360 Programs
:L  Appendix L -- Exercise Answers

*  PL360 TEXTBOOK

(c) 1977 by Wadsworth Publishing Company, Inc., Belmont, California 94002. All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transcribed, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of the publisher.

Printed in the United States of America

1 2 3 4 5 6 7 8 9 10---81 80 79 78 77

Portions of this text are reprinted from "PL360 Reference Manual" with the permission of the publishers, SCIP Academic Computing Services. Copyright (c) 1975 by the Board of Trustees of The Leland Stanford Junior University.

***********************************************************
*  Library of Congress Cataloging in Publication Data     *
*                                                         *
*  Guertin, Richard L                                     *
*     Introduction to PL360 programming.                  *
*                                                         *
*     Bibliography:  p.                                   *
*     Includes index.                                     *
*     1.  PL360 (computer program language)  I.  Title.   *
*  QA76.73.P267G83          001.6'424           76-51752  *
*  ISBN 0-534-00524-1                                     *
***********************************************************

+  Preface

A few years ago, while I was teaching a short course on PL360 at Stanford University, a student asked: "Is there a textbook on PL360?" I found there wasn't, but recognized the need for one. So I set out to write this textbook with two principle goals in mind. First, describe the PL360 programming language in such a way that each section of the text builds upon material covered in earlier sections. Second, write in a style that would appeal to assembly language programmers as well as PL/1 and FORTRAN programmers.

PL360, by its very nature, is an intermediate level programming language dealing with high level language concepts such as 'block structuring', and assembly language concepts such as 'registers'. This textbook is designed to be used with an intermediate level programming course, a transitional course between higher level languages and pure assembly language programming. Of course, assembly language programmers who wish to 'move up' to a higher style of programming would also find this textbook useful, first as a learning tool, and later as a reference document. In all fairness though, I must point out that PL360 is NOT an assembly language. Most versions of the language do not have 'macro' capability, although expanded macros can be programmed directly in PL360.

The PL360 programming language was originally designed and implemented by Nicklus Wirth and Joe Wells at Stanford University for the IBM System/360 computers. However, the FUNCTION capability of PL360 makes the language extensible to IBM System/370 and Amdahl computers. In fact, PL360 can be used on any computer that supports the basic System/360 instruction set. The other instructions can be programmed through FUNCTION declarations and statements.

I would like to thank the many students and friends who helped me in preparing this textbook by reviewing the preliminary material. The final drafts were reviewed by John Lindsay at Queen's University, and by Steven M. Dreyer at City University of New York. Both of these gentlemen contributed significantly; however, any and all errors or omissions are solely my responsibility. Also, I would like to thank Mike Snell, Jenny Sill, and Anne Kelly at Wadsworth Publishing Company for all their editorial help. Finally, special thanks to Stanford University for providing me with the environment that made writing this text possible.

The PL360 compiler is a proprietary program licensed by Stanford University and is available to universities and other non-profit organizations for a small fee by writing to:

       Program Librarian
       Information Technology Systems and Services
       Polya Hall
       Stanford University
       Stanford, California  94305

Introduction to PL360 Programming

In recent years, many new languages have been introduced to aid in programming digital computers. Most of these languages have been somewhat 'transportable'; that is, the programs written in these languages could be taken from one computer to another with minor changes. They are machine independent languages. FORTRAN, ALGOL, and COBOL are just a few examples.

However, these languages, to be machine independent, are usually restrictive in nature. They seldom use the full instruction power of a computer. As a result, they are sometimes inefficient, wasteful of computer memory space, or incapable of handling some special problems. FORTRAN, for example, is not well suited to character manipulations.

These 'high level' languages are generally not suitable for 'systems programming'. Compilers, programs that translate a language such as FORTRAN into the machine dependent instructions representing the source program, are usually written in a lower level machine dependent language: an assembly language. Assembly language is much more difficult to program, but provides the programmer with access to every machine instruction available on the computer. One difficulty in assembly language programming is that each instruction must be specified on a separate source card by means of some cryptic mnemonic, and usually in varying formats depending on the mnemonic. The programmer must learn a myriad of mnemonics and formats just to get started. This clerical work is not only a burden, but also a rich source of pitfalls.

Clearly, it would be advantageous to be able to write assembly level programs in a high level language style, such as ALGOL. This is what the PL360 programming language provides for the IBM System/360 (and now 370) computers: an ALGOL style machine level language. The benefits of the ALGOL style are so overwhelming that those programmers familiar with ALGOL will hardly recognize the machine level aspects of the language. And those programmers familiar with System/360 assembly language will greatly appreciate the ease of programming in PL360. PL360, therefore, is usually a suitable language choice whenever assembly language is considered. The language contains all the facilities commonly needed to express compiler and supervisor programs; and the programmer is able to determine almost every detailed machine operation. Interestingly enough, the PL360 compiler is an efficient one pass compiler written in its own language!

We shall cover both the IBM System/360 architecture and the ALGOL style of programming in this text. The first chapter provides a basic overview of the System/360 architecture. The second chapter introduces the basic symbols used in PL360 programming and discusses how constants are specified. The remaining chapters describe PL360 programming and language structure with an aim toward defining a complete PL360 program. Some of the Exercise problems given will be complete programs, but only knowledge gained in preceding chapters will be needed to answer such problems.

1  The IBM System/360 Computer

The basic structure of a System/360 digital computer consists of a central processing unit (CPU) and a main storage or memory unit. Input/output operations are usually handled by an operating system (OS), the environment in which a PL360 program is normally run. Direct input/output operations can be performed by a PL360 program, but only indirect input/output operations will be covered in this text.

The central processing unit of a System/360 is directed by instructions stored in memory. Each instruction performs some basic action, such as ADD one operand to another. Instructions are normally taken in sequence, but branching instructions provide for alterations in the flow of control. Most instructions operate on data held either in main storage or in the CPU. The basic unit of addressable information is a byte.

A byte consists of eight bits (binary digits) and is the basic building block of all information. Bytes may be handled separately or grouped together in fields. A halfword is a group of two consecutive bytes and is the basic building block of instructions. A fullword is a group of four consecutive bytes; a double-word is a field consisting of two consecutive fullwords. The first byte of a set of consecutive bytes is sometimes referred to as the left-most, highest, or top byte. Similarly, the last byte is sometimes referred to as the right-most, lowest, or bottom byte.

1.1  Storage

The location of any field, operand, or group of bytes in main storage is specified by the address of the left-most byte of the field. Byte locations are consecutively numbered, left to right, starting with 0; each number is considered the address of the corresponding byte. Thus, a fullword specified at location 1000 consists of bytes 1000, 1001, 1002, and 1003. The addressing arrangement of most System/360 computers uses a 24-bit binary address to accommodate a maximum of 16,777,216 byte addresses. This textbook deals mainly with 24-bit addressing, but System/370 and newer models can accommodate 31-bit addresses (2GB), and PL360 can compile programs that are 31-bit clean.

The length of any field in main storage is either implied by the operation to be performed or is stated explicitly as part of the instruction. When the length is implied, the information is said to have a fixed length, which can be either one, two, four, or eight bytes. When the length of a field is not implied by the operation, but is stated explicitly, the information is said to have a variable length which can vary in 1-byte increments from a minimum of one byte to a maximum of 256 bytes (more for certain System/370 instructions).

Fixed-length fields, such as halfwords, fullwords, and double-words, must be aligned in main storage on an integral boundary commensurate with their length. For example, fullwords (four bytes) must be located in main storage so that the address of the left-most byte is a multiple of 4. A halfword must have an address that is a multiple of 2, and a double-word must have an address that is a multiple of 8. Variable-length fields are not limited to integral boundaries, and may start on any byte location. System/370 and newer models do not impose alignment restrictions, but unaligned fullwords and halfwords cause degradation in speed.

Data fields in main storage are commonly referred to as 'cells'; whereas data fields in the CPU are kept in what are called 'registers'.

1.2  Registers

There are 16 general-purpose 32-bit registers for fixed-point operations, and four floating-point 64-bit registers for floating-point operations. Additions, subtractions, multiplications, divisions, and comparisons can be performed upon one operand in a register and another operand either in a register or in main storage. For some purposes, a pair of general-purpose registers are treated as a single register of 64 bits. In PL360, the general registers are called INTEGER registers, and the floating-point registers are called REAL or LONG REAL registers depending on how they are used.

1.3  Fixed-Point Arithmetic

The general-purpose 32-bit registers serve as accumulators in fixed-point arithmetic and logical operations. All fixed-point arithmetic is done in the general registers using signed operands. The sign bit is the high order bit of the operand, and is 0 for positive operands and 1 for negative operands. A negative operand is maintained as the two's complement of a positive operand. (See Appendix A for a full discussion of two's complement.)

Since binary (base 2) is inconvenient for expressing operands, or doing arithmetic, the hexadecimal (base 16) system is used instead. A group of four bits represents one hexadecimal digit according to the following table:

     BINARY  HEX    BINARY  HEX    BINARY  HEX    BINARY  HEX
      0000    0      0100    4      1000    8      1100    C
      0001    1      0101    5      1001    9      1101    D
      0010    2      0110    6      1010    A      1110    E
      0011    3      0111    7      1011    B      1111    F

Thus, positive 3 would be represented by 00000003 as a 32-bit hexadecimal value, and negative 3 would be represented by FFFFFFFD. Throughout this text, positive values will be shown with no leading sign, and negative values will be shown with a leading underscore character ( 3 and _3). This is done so as not to confuse the sign of a value with the operations of addition and subtraction, represented by the symbols + and -. Thus, + _3 indicates addition of a negative 3. The basic fixed-point operand is the 32-bit word. Halfword storage operands may be used for fixed-point operations of addition, subtraction, and multiplication, and for fetching and storing with general registers. Fullword multiplications and divisions require two adjacent general registers coupled together to provide a two-word capacity for products and dividends. Fixed-point operations will be covered in greater detail in section 5.1.1, Integer Register Assignments.

In PL360, the fixed-point 32-bit fullword operands are called INTEGER operands; and the 16-bit halfword storage operands are called SHORT INTEGER operands.

1.4  Instructions and Addressing

Because the 32-bit word size of the general registers readily accommodates a 24-bit storage address, the general registers are also used for address arithmetic. To fully understand the addressing usage of the general registers, we must first examine instruction formats.

The length of an instruction can be one, two, or three halfwords. All instructions must be located in storage on halfword boundaries. Figure 1 shows the four basic instruction formats.

        ________________________________________________________
       |  FIRST HALFWORD  |  SECOND HALFWORD |  THIRD HALFWORD  |

        ------------------
       | OP CODE |        |
        ------------------
                    I or RR or MR

        -------------------------------------
       | OP CODE |    | X | B |    D         |
        -------------------------------------
                    R or M

        -------------------------------------
       | OP CODE |        | B |    D         |
        -------------------------------------
                    I or RR

        --------------------------------------------------------
       | OP CODE |        | B |    D         | B |    D         |
        --------------------------------------------------------
                    L or L1L2

               FIGURE 1.   BASIC INSTRUCTION FORMATS

In each format, the first instruction halfword consists of two parts. The first byte contains the operation code (op code). The second byte is used either as a single 8-bit field designating a value (I) or length (L), or as two 4-bit fields designating two registers (RR or RX), a mask and a register (MR or MX), or two lengths (L1L2).

The second and third halfwords always have the same format: a 4-bit base register designator (B), followed by a 12-bit displacement value (D). These halfwords provide what is called 'Base+Displacement' addressing in that a storage address is computed as follows.

To the 12-bit displacement value specified by the D-field of the instruction is added the lower 24-bit value contained in the general register specified by the associated B-field of the instruction (except when B is 0). Those instructions with an X-field provide additional addressing capability, called 'indexing'. To the 'Base+Displacement' address is added the lower 24-bit value contained in the general register specified by the X-field of such instructions (except when X is 0). The final storage address is the 24-bit result of either a Base+Displacement or Index+Base+Displacement computation. This computation is done assuming all values are positive (12-bit and 24-bit), extending each value to 32 bits with leading zeros, performing 32-bit additions (ignoring overflow), and extracting the final address from the lower 24 bits of the result. If zero is specified in a B-field or X-field, then positive zero is used for that component of the address calculation rather than the contents of general register 0. Thus, general register 0 never participates in the address calculation. Figure 2 shows a sample address calculation.

        CODE      X   B    D
        ----------------------                 -----------------
       |    |   | 4 | 9 | 008 | ------------> | 0 0 0 0 0 0 0 8 |
        ----------------------                 -----------------

                     General Register 9                +
                      -----------------        -----------------
                     | 4 A 0 0 6 5 0 0 | ---> | 0 0 0 0 6 5 0 0 |
                      -----------------        -----------------

                     General Register 4                +
                      -----------------        -----------------
                     | F F F F F F F C | ---> | 0 0 F F F F F C |
                      -----------------        -----------------

     (Note: General Register 4 contains _4)
                                               -----------------
                          Final Result:       | x x 0 0 6 5 0 4 |
                                               -----------------

               FIGURE 2.   SAMPLE ADDRESS CALCULATION

1.5  Relative Addressing

The B and D fields or X, B, and D fields of an instruction constitute the addressing field of an instruction. The displacement value in an instruction address field provides a relative addressing mechanism, that is, for any given storage address contained in the associated base register, up to 4096 consecutive bytes of storage may be referenced beginning at that base address. Storage can be thought of as being segmented into sections of up to 4096 bytes. These segments may be program instruction or data areas having some specific structure, and may start anywhere in main storage providing alignment rules are not violated.

For example, consider a data area consisting of the following fixed-length fields in the order specified:

     a fullword, two halfwords, a double-word, and three fullwords.

Let's call these seven fixed-length fields XX1 to XX7, draw a diagram of the area (Figure 3), and number the bytes starting at 0.

                         -----------
        Base Address  0 |    XX1    |     fullword
                        |-----------|
                      4 | XX2 | XX3 |     two halfwords
                        |-----------|
                      8 |    XX4    |     double-word
                     12 |           |
                        |-----------|
                     16 |    XX5    |     fullword
                        |-----------|
                     20 |    XX6    |     fullword
                        |-----------|
                     24 |    XX7    |     fullword
                         -----------

                        4 bytes wide

                          FIGURE 3.

Notice that XX5 begins at relative address 16. If this data area were located in main storage beginning at location 1000, and a base register associated with the area, say general register 4, contained the address 1000, then XX5 could be referenced by the address field of an instruction which specifies register 4 in its B-field and 16 in its D-field. It is important to note that the instruction would not be changed if the area were located instead at location 2000 in main storage, and general register 4 contained that address. This makes it easy to move the data storage area without having to change every instruction that refers to it.

1.6  Logical Operations

The logical operations of AND, OR, and XOR (Exclusive-OR), and logical or un-signed addition, subtraction, and comparison can be performed upon one operand in a general register using a fullword operand either in a general register or in main storage. In such operations, all operands are considered un-signed, and all 32 bits of the operands participate in the operations. AND, OR, and XOR logic tables are given below with binary operands and results.

                             AND       OR        XOR
             Operand 1     1 1 0 0   1 1 0 0   1 1 0 0
             Operand 2     1 0 1 0   1 0 1 0   1 0 1 0

             Result:       1 0 0 0   1 1 1 0   0 1 1 0

                           LOGIC TABLES

There are also storage-to-storage operations which allow processing of variable-length data starting at any byte address and continuing left to right for up to 256 bytes. Such operations as translation, comparison, editing, and storage-to-storage copy are possible. Usually the data consists of alphabetic or numeric character codes in Extended Binary-Coded-Decimal Interchange Code (EBCDIC). These operations will be covered in more detail later. An EBCDIC table is given in Appendix C.

1.7  Shift Operations

The general registers may also participate in an operation known as shifting. The contents of a general register (or pair of adjacent general registers) may be shifted to the right or left some number of bit positions.

When shifted to the right, bits are dropped from the right end of the register and either zero bits are inserted from the left end (logical right shift), or the sign bit is propagated (arithmetic right shift).

When shifted to the left, bits are dropped from the left end, and zero bits are inserted from the right end. No error is possible in a logical left shift; but in an arithmetic left shift, an error condition is flagged if a bit differing from the original sign bit is dropped or occupies the sign bit position.

Shifting (usually arithmetic) provides a convenient means of multiplying or dividing by powers of 2. A shift of 1 to the left is equivalent to multiplication by 2; a shift of 1 to the right is equivalent to division by 2 (dropping the remainder).

1.8  Floating-Point Arithmetic

Floating-point values occur in either of two fixed-length formats: 32-bit fullwords called REAL values, and 64-bit double-words called LONG REAL values. These formats differ only in the length of the fractional portions (see Figure 4).

             REAL value  (fullword)
         --------------------------------
        |S| Exponent  |  Fraction        |
         --------------------------------
         0 1         7 8               31
             LONG REAL value  (double-word)
         --------------------------------------------------------
        |S| Exponent  |  Fraction                                |
         --------------------------------------------------------
         0 1         7 8                                      63

                           FIGURE 4.

A REAL value, equivalent to about seven decimal places of precision, permits a maximum of operands to be placed in storage and gives the shortest execution times. The LONG REAL values, used when higher precision is desired, give up to 17 decimal places of precision.

The fraction of a floating-point value is expressed as six hexadecimal digits occupying bits 8-31 for REAL values, and 14 hexadecimal digits occupying bits 8-63 for LONG REAL values.

The radix point of the fraction is assumed to be immediately to the left of the high order fraction digit (immediately before bit 8). To provide the proper magnitude for the floating-point value, the fraction is considered to be multiplied by some power of 16. The 'Exponent' portion of the floating-point value (bits 1-7) is used to indicate this power. The exponent ranges from 0 through 127 representing true (base 16) exponents from _64 through 63, and permits representations of decimal values with magnitudes ranging approximately from 10 to the _78th through 10 to the 75th.

This method of specifying exponents within the computer's representation of a value is known as 'excess 64 notation', in that 64 is added to the true exponent to arrive at the computer's exponent.

Bit position 0 in either format is the sign (S) of the fraction. The fraction (and exponent) of negative values is the same as that of positive values. Only the sign differs. Negative values are not maintained in two's complement as they are for fixed-point values.

Four 64-bit floating-point registers are provided in the hardware, and the arithmetic operations of addition, subtraction, multiplication, division, and comparison can be performed with one operand in a register and another in a register or in storage. The result, developed in a register, is generally of the same length as the operands, either 32-bit or 64-bit.

Most of the floating-point operations produce what is known as a 'normalized' result; that is, the result is adjusted so that the first hexadecimal digit of the fraction is non-zero. For example, 0.001 would become 0.1 with appropriate exponent adjustment. Normalized results retain the greatest precision possible. However, unnormalized addition and subtraction operations are provided to allow for easy transformation of floating-point values to fixed-point format or other similar purposes. (See Appendix B for further discussion of floating- point values.)

1.9  Program Status Word

A two-word quantity called the program status word (PSW), contains the information required for proper instruction execution. The PSW includes, among other things, the address of the next instruction to be executed and a 2-bit quantity called the condition code. The condition code provides decision-making capability. Most of the arithmetic and logical operations can set the code to one of four states (0, 1, 2, and 3). Conditional branch instructions can specify any selection of these four states as the criterion for branching. Specifying all four states results in an unconditional branch; specifying none of the four states results in a do-nothing operation.

The condition code reflects such conditions as: zero, negative, or positive result; overflow; and comparison is equal, low, or high. Once set, the condition code remains unchanged until modified by another instruction that reflects a different status. For example, branch instructions do not change the condition code, but additions and subtractions do change the code.

1.10  Summary

This chapter has presented a brief overview of the System/360 architecture with an emphasis on those items of particular importance to the PL360 programming language. The reader is directed to IBM's Principles of Operation manuals for details concerning these and other System/360 computer operations, such as input/output operations. Later in this text, specific computer instructions will be covered in more detail.

1.11  Exercise

Problem 1.

What is the hexadecimal result (32-bit) of a logical right shift by 3 of the value _8 ?

Problem 2.

Compute the address in decimal by performing the Index+Base+Displacement calculations indicated below given the following general register contents:

     General Register:        0      1       2      3      4      5
     Contents (decimal):   7160    _16  510000  16000  72568   _100

          X  B   D (in decimal)
     (a)  0  2  30
     (b)  1  4   0
     (c)  3  2 200
     (d)  5  2  50
     (e)  0  0 100

Problem 3.

Determine the result in hexadecimal of logical AND, OR, and XOR operations for each value below with all values below. The values are given as 1-byte hexadecimal values.

       0F      F0      18

Problem 4.

The REAL values shown below are given in decimal notation on the left and hexadecimal (machine) notation on the right. Supply the missing decimal or hexadecimal values in the table.

    Decimal   Hexadecimal

       0.5     40800000
       8.0     41800000
       9.0     41900000
(a)    8.5     --------
(b)    ---     42180000
(c)    ---     42800000

2  The PL360 Language

PL360 programs are input to the PL360 compiler on 80-column cards or card images. The compiler processes only columns 1 through 72 of each card; columns 73 through 80 are ignored by the compiler, and are usually used for card sequencing information by the programmer.

The input consists of (one or more) PL360 programs and compiler control cards. A compiler control card is distinguished by the character $ in column 1 of the card. The PL360 program input is considered to be a stream of characters with column 1 of each non-control card immediately following column 72 of the preceding non-control card.

2.1  The Program

A program contains declarations and statements composed of identifiers, basic symbols, constants, strings, and comments. Declarations serve to identify cells of storage, registers, procedures, and other quantities which are involved in the algorithm or problem to be solved by the program. Statements specify the operations to be performed on these quantities.

The remainder of this chapter is devoted to defining identifiers, basic symbols, constants, strings, and comments. In subsequent chapters, we will proceed to construct PL360 programs with declarations and statements.

2.2  Identifiers

Identifiers are used to give a name to some quantity, such as a register or storage cell. Identifiers are composed of the mixed case alphabetic characters and decimal digits, where the first character of the identifier must be alphabetic. They may be of any length, but only the first 10 characters are retained by the PL360 compiler. The following are examples of legal identifiers, the last two of which are considered identical because the first 10 characters match:

       ABLE          HELP           SOMELIKEITCOLD
       BASE16        JackRabbit     SOMELIKEITHOT
       FLAG          K27P5
       FLAG3         X

Case doesn't matter, and identifiers are considered identical regardless of the case in which they are specified. The following are all considered identical identifiers:

       POT           POt            Pot
       pot           poT            pOT

2.3  Reserved and Pre-declared Identifiers

Some identifiers are reserved for exclusive use by the PL360 language. These identifiers have a specific meaning in the language and therefore may not be chosen as user-defined identifiers. The following is a complete list of the reserved identifiers in alphabetic order:

       AND        COMMENT    EXTERNAL   LONG       SEGMENT    THEN
       ARRAY      COMMON     FOR        NULL       SHLA       UNTIL
       BASE       DATA       FUNCTION   OF         SHLL       WHILE
       BEGIN      DO         GLOBAL     OR         SHORT      XOR
       BYTE       DUMMY      GOTO       PROCEDURE  SHRA
       CASE       ELSE       IF         REAL       SHRL
       CHARACTER  END        INTEGER    REGISTER   STEP
       CLOSE      EQUATE     LOGICAL    REPEAT     SYN

There is also a set of pre-declared identifiers that may be redefined by the programmer. However, it is recommended that these identifiers NOT be redefined. A complete list of these 'standard identifiers' follows:

       INTEGER REGISTERS

       R0   R1   R2   R3   R4   R5   R6   R7
       R8   R9   R10  R11  R12  R13  R14  R15
       FLOATING-POINT REGISTERS

       F0   F2   F4   F6    (REAL REGISTERS)
       F01  F23  F45  F67   (LONG REAL REGISTERS)
       CELL NAMES

       B1  B2 ... B15  MEM  (INTEGER CELLS)
       H1  H2 ... H15       (SHORT INTEGER CELLS)
       C1  C2 ... C15       (BYTE CELLS)
       L1  L2 ... L15       (LONG REAL CELLS)
       DSTAR, PSTAR         (DATA & PROGRAM BASE-DISP)
       MONADIC OPERATORS    (SPECIAL SYMBOLS)

       ABS        DEC       GT        LE        NEG
       EQUATE VALUES    (INTEGER VALUES)

       TRUE = _1    FALSE = 0    OVERFLOW = 1
       CARRY = 3    MIXED = 4    ON = 1   OFF = 8
       STRING = length of last string compiled
       DATAFILL = variable array size
       FUNCTIONS

       BALR       EX        MVI       RESET     STCM      TS
       CLC        IC        MVN       SET       STH       UNPK
       CLI        ICM       MVZ       SLDA      STM       XC
       CLM        LA        NC        SLDL      SVC       XI
       CVB        LH        NI        SPM       TEST
       CVD        LM        OC        SRDA      TM
       ED         LTR       OI        SRDL      TR
       EDMK       MVC       PACK      STC       TRT
       EXTERNAL PROCEDURES

       BCDTOVAL   KLOSE     PRINT     READ
       CANCEL     OPEN      PUNCH     WRITE
       GET        PAGE      PUT       VALTOBCD

2.4  Special Symbols and Delimiters

There are a number of special symbols and delimiters used in writing PL360 programs. These special symbols serve as separators between identifiers and constants, and are listed below with a brief description of their use:

       Symbol     Description

         +        plus sign, add
         -        minus sign, subtract
         ++       double plus, add logical or unnormalized
         --       double minus, subtract logical or unnormalized
         *        star, multiply
         /        slash, divide, separator
         @        address symbol
         @@       absolute address initialization
         ( )      left and right parens, subscripts, brackets
         :=       assignment operator
         =:       reverse assignment operator
         :        colon, label indicator
         _        underscore, negative value
         ,        comma, separator
         ;        semicolon, terminator
         .        period, decimal point
         #        hexadecimal number indicator
         '        apostrophe, exponent indicator
         "        quote, character string delimiters
         =        equal sign, compare equal
         <        compare less than
         >        compare greater than
         <=       compare less than or equal
         >=       compare greater than or equal
         ~=       compare not equal
         ~        not indicator
         |        comment delimiter
                  blank, space

2.5  Constants

Numbers or numerical constants written in PL360 programs are divided into three classes, namely:

(a)  fixed-point
(b)  floating-point
(c)  variable-length strings

2.5.1  Fixed-Point Constants

There are two types of fixed-point constants: (32-bit) fullword constants called INTEGER values, and (16-bit) halfword constants called SHORT INTEGER values.

A positive integer value may be written in decimal notation using the digits 0 to 9. A negative value is written as a positive value preceded by an underscore character (_). An integer value may also be written in hexadecimal notation using the character # followed by the hexadecimal digits 0 to 9 and A to F. Negative hexadecimal values must be written in computer form (see Appendix A).

A short integer value is written as an integer value followed by the letter S.

The following are examples of (32-bit) integer values starting with the largest positive value, and ending with the largest negative value that can be written in PL360 language.

         Decimal            Hexadecimal
      2147483647            #7FFFFFFF
           65536            #00010000   or   #10000
               1            #00000001   or   #1
               0            #00000000   or   #0
              _1            #FFFFFFFF
              _2            #FFFFFFFE
          _65536            #FFFF0000
     _2147483647            #80000001

The largest possible negative value is #80000000, but this value can only be written in hexadecimal notation, not decimal notation.

The following are examples of (16-bit) short integer values starting with the largest positive value, and ending with the largest negative value.

         Decimal            Hexadecimal
          32767S            #7FFFS
              1S            #0001S   or   #1S
              0S            #0000S   or   #0S
             _1S            #FFFFS
         _32768S            #8000S

2.5.2  Floating-Point Constants

There are two types of floating-point constants: (32-bit) full- word constants called REAL values, and (64-bit) double-word constants called LONG REAL values. Floating-point constants are generally used in connection with the REAL and LONG REAL floating-point registers.

     A floating-point constant is written in decimal notation as:

The portion of the number preceding the decimal point in (a) or (b), or the number preceding the exponent in (c), may not exceed 2147483647.

An exponent is written as the apostrophe character ('), which signifies 'times 10 to the power of', followed by a positive or negative decimal integer value indicating the power of 10 desired. The final magnitude of the number must range from 10'_75 through 10'75.

When written as specified above, the values are REAL values. A LONG REAL value is written as a REAL value followed by the letter L. A negative value is written as a positive value preceded by an underscore character (_).

A floating-point constant may also be written as a positive or negative decimal integer value followed by the letter R for REAL values, or the letter L for LONG REAL values.

All the above decimal forms of writing floating-point constants yield normalized values. Normalized (or unnormalized) values are a characteristic of how the System/360 computer represents the value internally. Floating-point constants may be written in this internal form, either normalized or unnormalized, in hexadecimal notation. Hexadecimal floating-point constants are written as: the character # followed by (up to) eight hexadecimal digits and the letter R, for REAL values, or (up to) 16 hexadecimal digits and the letter L for LONG REAL values.

The following are examples of REAL (normalized) values, the first column showing a variety of ways of writing 'pi', and the last two values showing the largest and smallest positive non-zero decimal values.

             3.14159          1.75             _15.0
             314159'_5        1R               175.'_2
             0.314159'1       0.  (zero)       7.237'75
             314.159'_2       _7.6             1'_75

The following are samples of real values in hexadecimal notation, starting with the largest and smallest possible positive non-zero normalized values, and ending with an unnormalized long real value which will be discussed later in section 5.1.2.

             Value            Hexadecimal
             7.237'75         #7FFFFFFFR
             5.4 x 10-79      #00100000R
             1.56256'_2       #3F400000R
             0.5              #40800000R
             1.0              #41100000R
             0.0 (zero)       #00000000R   or   #0R
             (special)        #4E00000000000000L

Note: the first three values are only approximately equal to the hexadecimal values given, and the second value is too small to be written in decimal notation. The reason for approximations is that exact values in one base system may not convert to exact values in another base system, in this case, decimal to hexadecimal. For example, decimal 0.5 (5/10) is hexadecimal 0.8 (8/16), but decimal 0.3 (3/10) is a repeating hexadecimal value: 0.4CCCCCCC.... Therefore, floating-point values are only accurate to six or seven decimal digits for real values, and 16 or 17 decimal digits for long real values.

2.5.3  Summary of Rules for Fixed-Length Values

(a)  There are four types of fixed-length values:  INTEGER,
     SHORT INTEGER,  REAL,  and LONG REAL.
(b)  REAL and LONG REAL values may be  written  in  decimal
     digits  with  decimal  point,  exponent, or both;  and
     LONG REAL values must end in the letter L.
(c)  Any value may be written in hexadecimal  (leading  #),
     or   in  decimal  digits  (without  decimal  point  or
     exponent).  SHORT INTEGER values end in S, REAL values
     end in R, and LONG REAL values end in L.
(d)  The length of a hexadecimal value is usually twice the
     byte   length   of   the   fixed-length   value  being
     represented (two hexadecimal digits per byte).
(e)  Negative decimal values are  indicated  by  a  leading
     underscore character (_).

2.5.4  Variable-Length Strings

A string is a sequence of characters, each character taking one byte (two hexadecimal digits). The set of all possible characters is given in Appendix C. The set includes the English alphabet in both upper and lower case, decimal digits, a selection of special symbols, blank (or space), and other characters (non-graphic).

A string is normally written as a sequence of characters enclosed in quotation marks, called a quote string, and may contain a sequence of from one through 255 characters. If a quote mark (") is to be a character of the sequence, it is represented by a pair of consecutive quotes. All characters are significant within quote strings, including blank characters. An "accent" character (`) followed by a normal character is used to represent certain special (non-graphic) characters. If a true accent character is to be entered in the string, it must be doubled (similar to what happens for quote).

Examples:  "A""z"    denotes the sequence  A"z
           """X"""   denotes the sequence  "X"
           """"      denotes the single character  "
           "This is an example of a quoted string"
           "This string has an accent (``) character"

If a quote string cannot be contained on a single input card because of its length, then note that column 72 of one card is followed by column 1 of the next card (excluding control cards). The character $ cannot occur in column 1 of a card (since that signifies a compiler control card).

There are instances where a quote string is not a convenient means of specifying a variable-length string, such as when non-graphic characters are to be used for some special purpose. In such instances, another form of variable-length string may be used, namely a hexadecimal string. A hexadecimal string is written as the character # followed by from one to 16 hexadecimal digits followed by the character X. Each pair of hexadecimal digits represent one character. If the number of hexadecimal digits specified is odd, then a hexadecimal zero is prefixed to the first specified hexadecimal digit to make the total even. A hexadecimal string then may be used to specify from one to eight characters (bytes).

Examples:  #2X       denotes the single character  02
           #C3C1C2X  denotes a 3-byte string equivalent to "CAB"
           #4096FX   denotes a 3-byte packed-decimal value of +4096
           #20202021204B2020X  denotes an 8-byte special editing value

Certain non-graphic characters may be included in a quote string using the accent escape character followed by a graphic character. Here is a table showing the character which follows the accent to obtain the non-graphic character, shown in hexadecimal below the character.

    Table of Accent (`) Characters.
    .
    .   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
    . #01 #02 #03 #37 #2D #2E #2F #16 #05 #25 #0B #0C #0D #0E #0F
    .
    .   P   Q   R   S   T   U   V   W   X   Y   Z   %   [   >   ?
    . #10 #11 #12 #13 #3C #3D #32 #26 #18 #19 #3F #17 #27 #28 #09
    .
    .   !   $   *   )   ;   ~   0   1   \   /   `   :   #   @   ]
    . #04 #14 #15 #2B #55 #FA #20 #21 #22 #07 #79 #08 #09 #00 #1D
    .
    .   =   ^   .   <   (   +   &   "
    . #2C #35 #1E #1F #1B #1C #0A #7F

#20202021204B2020X could be given as: "`0`0`0`1`0.`0`0".

2.6  Comments

Comments may appear freely throughout a PL360 program. A comment is written as the reserved identifier COMMENT followed by a delimiter character and any number of characters except a semicolon, and is terminated by a semicolon (;). A comment may also be written as any number of characters excluding the vertical bar (|) between vertical bars. As with quote strings, the character $ may not appear in column 1 of a card; otherwise a non-quoted $ signifies end-of-card.

       |This is a comment|   $ rest of card is comment
       COMMENT  This is the end of Chapter 2.;

2.7  Exercise

Problem 1.

Which of the following are not valid identifiers, and why?

(a)  IDENTIFIER
(b)  2X
(c)  A.B
(d)  Samson
(e)  EASY2READ
(f)  ICU2
(g)  #F

Problem 2.

Which of the following are not valid constants, and why?

(a)  #AX
(b)  2,124,587
(c)  "This must be right!"
(d)  .2L
(e)  _5'_6L
(f)  #8A S
(g)  PI
(h)  #FACES

Problem 3.

What is the 'type' of the valid constants in Problem 2 above? Specify INTEGER, SHORT INTEGER, LONG REAL, STRING, etc.

3  The Main Program

3.1  Segmentation

The addressing mechanism of the System/360 computers is such that instructions can indicate the absolute address of stored information only relative to a base address contained in a register. The difference D between the desired absolute address and the available base address must satisfy: 0 <= D < 4096. To accomplish this, a PL360 program may be subdivided into individually identified parts, called segments. Every quantity requiring storage space and defined within a program is known by the segment in which it occurs and by its displacement relative to the origin of that segment. The problem then consists of subdividing the program and choosing base registers so that proper instructions can be generated. Of course, the number of times base addresses must be reloaded into base registers should be kept reasonably small; and therefore, the programmer is encouraged to organize the program in such a way as to minimize the number of cross-references between segments by explicitly stating which parts of the program constitute segments.

One natural division is between the instructions of a program and the data operated upon by the instructions. Therefore, in PL360, the program is divided into program segments (instructions) and data segments (data areas). There are two benefits in doing this. First, there are many instances where it is desirable that program and data areas be kept apart as separate entities, such as in reentrant programs. Second, the programmer's knowledge about segment sizes and occurrences of cross-references differs for program and data areas. In the program area case, his knowledge about the eventual size of the compiled program segment is only vague; whereas in the data area case, he knows exactly the amount of storage space needed for declared quantities, and he knows precisely in which places in the program these quantities are referenced.

For the purposes of illustration, we shall assume a main program consists of only one program segment and one data segment. We shall assign the program segment base register R15, and the data segment base register R13. These are the base register assignments assumed by the PL360 compiler unless the compiler is explicitly directed to assign other base registers.

3.2  Compiler Generated Names

Every segment generated by the PL360 compiler must be given a name. This name either is explicitly specified by the programmer, or is generated by the compiler. All generated names are seven characters long, the first three of which are usually SEG and the last four are always the letter N and three decimal digits. Generally the programmer is not concerned about what names are generated, as long as the result is valid and acceptable. But it is important to note that every segment is named.

In our forthcoming examples, neither the program segment nor the data segment will be explicitly named; and therefore, compiler generated names will be used to name each segment. Thus, SEGN001 and SEGN000 will be assigned to the program and data segments respectively.

3.3  Compilation and Execution

The PL360 compiler is a program which, like most computer programs, performs essentially three functions: input, processing, and output.

The input consists of an ordered set of card images upon which is inscribed, in PL360 language, the declarations and statements of a problem program.

The processing is that of compiling or translating the input PL360 program into System/360 machine language or computer instructions.

The output consists of a set of card images, called object decks, each deck describing one segment of the compiled program. The name of the segment, the amount of space the segment would occupy in main storage, and the System/360 machine instructions or other data which compose the segment are all part of this description. Also, information relating to cross-references between segments is included.

The compiler also outputs a listing or printout of the original program along with other information relating to the compilation, such as any error diagnostics which may have occurred.

The object decks are later processed by another program, called a loader. It is the loader's task to load or place into main storage all the segments of the compiled program, resolving cross-references between segments. The loader then transfers control to the starting address of the main program segment, initializing the base register (R15) to that starting address. The program then proceeds to execute.

3.4  Blocks

A block begins with the reserved identifier BEGIN and ends with the reserved identifier END. A block contains declarations, statements, and labels, composed of identifiers, basic symbols, constants, and strings. All declarations must come first in a block, each declaration being terminated by a semicolon (;). Then, statements and labels may occur, each statement terminated by a semicolon (;), and each label terminated by a colon (:). The general form of a block is:

       BEGIN  D;  D;  D;  ...  S;  S;  S;  END

where the D's represent declarations and the S's represent statements. A label (L:) may occur anywhere a statement (S;) may occur. Labels serve to designate branch points within a block, and GOTO statements refer to such points. The two main purposes of a block are:

3.4.1  Declarations

     The following list includes all possible PL360 declarations:

                   -------------------------------
                  | 1.  data segment declarations |
                  | 2.  cell declarations         |
                  | 3.  cell synonyms             |
                  | 4.  register synonyms         |
                  | 5.  integer value synonyms    |
                  | 6.  function declarations     |
                  | 7.  procedure declarations    |
                   -------------------------------

Declarations serve to define data segments and identify storage cells and other quantities. The following is a sample cell declaration which defines alpha, beta, and gamma as integer cells.

       INTEGER  ALPHA, BETA, GAMMA;

Note: although the compiler allows identifiers in mixed case, reserved and pre-declared identifiers will be shown in upper case, and some user-defined identifiers will be shown in lower case to indicate that no specific identifier is intended.

3.4.2  Statements

The following list includes all possible PL360 statements:

                ------------------------------------
               |  1.  block                         |
               |  2.  register assignment statement |
               |  3.  cell assignment statement     |
               |  4.  GOTO statement                |
               |  5.  IF statement                  |
               |  6.  WHILE statement               |
               |  7.  REPEAT/UNTIL statement        |
               |  8.  FOR statement                 |
               |  9.  CASE statement                |
               | 10.  function statement            |
               | 11.  procedure statement           |
               | 12.  NULL statement                |
                ------------------------------------

Notice that a block is an allowed statement; that is, blocks may be nested one inside another. The following two lines serve to illustrate the concept:

     BEGIN  D;  D;  D;  ...  S;  S;  block;  S;  S;  END  (outer block)
        BEGIN  D;  D;  ...  S;  S;  END                   (inner block)

The second line is called the 'inner block' in relation to the first line, and it replaces the word 'block' of the first line. The first line is called the 'outer block' in relation to the second line. Therefore, the inner block is contained within the outer block.

Since other declarations may occur within inner blocks, the question of the 'scope' of a declaration presents itself. (The scope of a declaration is that portion of the program where the declared quantities of a block may legitimately be referenced.)

The restriction is that the scope of any declared quantity is limited to the block in which the declaration occurs and any inner blocks, unless re-declared within an inner block. The declared quantity is known from the point where the declaration occurs within a block through to the END of that block. It is not known outside that block.

Every declared quantity must also be unique to a block. That is, the same identifier may not be declared more than once within a block. However, an inner block may re-declare an identifier, in which case the new meaning for that identifier holds from the point of re-declaration through to the END of that inner block, and then the old or original meaning resumes within the outer block.

The following 5-block example should clarify most of the points covered thus far. The BEGIN's and END's of each block are joined together by a numbered line solely for the purposes of illustration, and only cell declarations are included in the example. Other declarations and statements are left out to keep the example simple, but '....;' shows where they might occur.

         | BEGIN  INTEGER ALPHA, SAVE;
         |    ....;
         |    | BEGIN  REAL BETA, SAVE;
         |    |    ....;
         |    |    | BEGIN  INTEGER GAMMA;
       1 |  2 |  3 |    ....;
         |    |    | END;
         |    |    ....;
         |    |    | BEGIN  INTEGER SAVE;
         |    |  4 |    ....;
         |    |    | END;
         |    |    ....;
         |    | END;
         |    ....;
         |    | BEGIN  REAL DELTA;
         |  5 |    ....;
         |    | END;
         |    ....;
         | END.

ALPHA (declared in block 1) is known throughout all blocks.
BETA  (declared in block 2) is known only in blocks 2, 3, and 4.
GAMMA (declared in block 3) is known only in block 3.
DELTA (declared in block 5) is known only in block 5.
SAVE is declared 3 times;  once in block 1, and again (re-declared) in
block 2, and again (re-declared) in block 4.  The scope of each is:

       SAVE of block 1 is known in blocks 1 and 5 only.
       SAVE of block 2 is known in blocks 2 and 3 only.
       SAVE of block 4 is known in block 4 only.

Note that although SAVE is declared INTEGER in both blocks 1  and  4,
they  are NOT the same SAVE.  A reference to SAVE in any block is a
reference to the only SAVE known to that block and no other.

3.5  Main Programs

A main program is defined as a block followed by a period (.). The preceding 5-block example represents a main program since it terminates with a period. Comments may occur anywhere before the terminating period.

3.6  Data Segments

Assuming no other declarations in the 5-block example just given, all the cell declarations would be collected together by the compiler to form a single data segment which could be diagrammed as:

                        -------------------
                       | ALPHA             |
                       |-------------------|
                       | SAVE (of block 1) |
                       |-------------------|
                       | BETA              |
                       |-------------------|
                       | SAVE (of block 2) |
                       |-------------------|
                       | GAMMA             |
                       |-------------------|
                       | SAVE (of block 4) |
                       |-------------------|
                       | DELTA             |
                        -------------------

This sample data segment would be assigned some base register which, as stated earlier, we shall assume is register R13. Each cell declared within this data segment then would have some relative address depending upon the number of bytes of storage space required by all cells which precede it. (Reread section 1.5, Relative Addressing, if necessary.)

3.7  Program Segments

All of the statements in our 5-block example generate System/360 computer instructions that compose our main program segment. This segment also requires a base register which, as stated earlier, we shall assume is register R15.

Constants and strings referenced by PL360 statements, and which cannot be contained within generated instructions, are placed at the end of the program segment following all generated instructions. These constants and strings are called 'literals', and the collection of all literals at the end of a program segment is called the 'literal pool'. The base register associated with these literals is the program segment's base register, not a data segment's base register as might be assumed.

Under certain circumstances, which will be covered more completely in Chapter 8, a block may define a program segment separate from the main program segment. Usually this further subdividing of the program into more than one program segment is done when the amount of code generated by the problem program would exceed 4096 bytes. Every such program segment is a logically self-contained entity having its own literal pool. Only those literals referenced by the statements of a particular program segment are contained in that segment's literal pool.

3.8  Exercise

The following is a complete main program. Although a majority of this program involves material not yet covered, it is being presented at this point to give you some idea of what PL360 programs look like. The problems in this exercise use this program, but only material covered thus far is used in these problems.

|01|  BEGIN  COMMENT -- THIS IS A SAMPLE PROGRAM WHICH
|02|  * READS IN THE DIMENSIONS OF A BOX, COMPUTES THE
|03|  * VOLUME, AND WRITES THE RESULT.  --;
|04|
|05|  COMMENT -- DECLARE FUNCTIONS AND VARIABLES --;
|06|     FUNCTION REDUCE(6,#0600);  |-- SUBTRACT 1 FROM REGISTER --|
|07|
|08|     ARRAY 133 BYTE INPUT = (" INPUT DIMENSIONS ARE:",111(" "));
|09|        BYTE CARD SYN INPUT(23);  |-- CARD PART OF INPUT --|
|10|     ARRAY 133 BYTE OUTPUT = (" VOLUME =",124(" "));
|11|        BYTE ANSWER SYN OUTPUT(10);  |-- ANSWER PART OF OUTPUT --|
|12|
|13|  COMMENT -- MAIN PROGRAM CODE --;
|14|     WHILE  R0 := @CARD;  READ;  = DO  |-- PROCESS CARD --|
|15|     BEGIN  R0 := @INPUT;  WRITE;  |-- SHOW INPUT DATA --|
|16|        R1 := @CARD;  R2 := 1 =: R5;  R3 := 3;
|17|        WHILE R3 > 0 DO  |-- COMPUTE VOLUME --|
|18|        BEGIN  BCDTOVAL;  R5 := R5 * R0;  REDUCE(R3);
|19|        END;  COMMENT -- NOW GENERATE ANSWER --;
|20|        R1 := @ANSWER;  R3 := 7;  R0 := R5;  VALTOBCD;
|21|        R0 := @OUTPUT;  WRITE;  |-- OUTPUT THE ANSWER --|
|22|     END;
|23|  END.

Problem 1.

Specify the line numbers of the BEGIN and END of all blocks. Line numbers are shown on the left side of the program as |nn| comments.

Problem 2.

Integer and string constants are used in this program. Which lines contain string constants? (See section 2.5.4.)

Problem 3.

Using section 2.3,

4  Declarations

This chapter covers all PL360 declarations except function and procedure declarations which will be covered later in separate chapters. As for the others, data segment declarations define data segments, and cell declarations define the structure and content of data segments. Cell synonyms provide an additional cell naming capability, such as declaring that X and Y are the same cell. Register synonyms allow alternate names for the standard registers, such as stating that ZERO and R0 refer to the same register. And integer value synonyms provide the programmer with the capability of giving a name to an integer value, an integer constant expression.

4.1  Data Segment Declarations

As program segments are composed of computer instructions generated by statements, so also data segments are composed of cells defined by cell declarations. And as statements are bound into program segments by blocks (BEGIN through END), so also cell declarations are bound into data segments by data segment declarations.

Let us first examine data segment declarations. There are six declarations that may begin or initiate a data segment, and one declaration that may end or terminate any data segment. Of the six declarations that begin data segments, five will be discussed here, and the last, called a DUMMY segment, will be discussed separately in the section on DUMMY BASE declarations.

Each of the first five data segment declarations is given below in general syntactic form, where 'name' is an identifier naming the segment, and Rn indicates the selected base register, R0 through R15. (Note: only the first eight characters of a 'name' longer than eight characters are significant in naming a segment.) Remember that each of these declarations is terminated by a semicolon.

              ---------------------------------
             | 1.  COMMON DATA name BASE Rn;   |
             | 2.  COMMON BASE Rn;             |
             | 3.  SEGMENT BASE Rn;            |
             | 4.  GLOBAL DATA name BASE Rn;   |
             | 5.  EXTERNAL DATA name BASE Rn; |
              ---------------------------------

Each of the above declarations specifies three things:

  (a) the type of data segment
  (b) the name of the data segment (possibly compiler defined)
  (c) the base register to be assigned to the data segment

Every data segment is assigned a base register, and that register must be an integer register, R0 through R15, other than the current program segment's base register. At the point in the block where these data segment declarations occur, an instruction is generated in the corresponding program segment to load the specified base register with the base address of the declared data segment. In assembly language, the instruction generated would correspond very closely to:

      L   Rn,=V(name)

Of course, R0 is not allowed to be a base register by the hardware, so no instruction is generated when R0 is specified. We shall cover such usage at the end of DUMMY BASE declarations.

It is the programmer's responsibility to insure the integrity of this data segment base register; that is, the register must contain the base address of the data segment whenever cells declared within that segment are referenced by subsequent statements. Of course, the contents of the register could be saved, the register used for some other purpose, and then the contents restored. But if cells are referenced that depend on a base address and the base register does not contain that address, then either the program will not execute properly or it will abnormally terminate.

The COMMON declarations define what is called a 'common area'. This type of area is normally used in conjunction with FORTRAN programs. The first declaration specifies labeled or named common; and the second declaration specifies unlabeled or blank common (so called because the data segment name is all blank). The same (named) common area may be declared more than once, usually by separately compiled programs, such as a PL360 program and a FORTRAN program. All would refer to the same storage area identified by their common name.

The SEGMENT BASE and GLOBAL DATA declarations both create data segments that must be unique. The only difference between these two declarations is that for the SEGMENT BASE declaration, the compiler generates the data segment name; whereas for the GLOBAL DATA declaration, the programmer explicitly specifies the data segment name.

The EXTERNAL DATA declaration allows reference to GLOBAL DATA segments either created in separately compiled programs to be run with this program, or created elsewhere in this same program. EXTERNAL DATA declarations do not create an actual data segment, but only refer to a data segment created by a GLOBAL DATA declaration somewhere else. As such, the cell declarations that compose an EXTERNAL DATA segment serve only to define the structure of the segment and not the content.

Normally, data segments are declared early in the program so that the cells which compose them may be referenced over the widest range of statements. Thus, for example, if a GLOBAL DATA segment were declared early in a program (in the outermost block), then the need to refer to that same data segment by an EXTERNAL DATA declaration is greatly reduced. However, an EXTERNAL DATA declaration could serve to re-establish the base address of a previously defined GLOBAL DATA segment by writing a statement of the form:

       BEGIN  EXTERNAL DATA name BASE Rn;  END

All of the above data segment declarations either create or refer to data segments that occupy specific areas of main storage. The instructions generated in the program segments load the absolute address of the start or base of these data segments into their associated base registers. The cross-references from program segments to these data segments are resolved by a loader program as described in section 3.3, Compilation and Execution.

4.1.1  DUMMY BASE Declarations

One other declaration may be used to define a data segment. It is the DUMMY BASE declaration, and has the general syntactic form:

DUMMY BASE Rn;

This declaration is discussed separately because it differs substantially from the preceding declarations. For example, dummy segments do not have a name, not even a compiler generated name. Also, no instruction is generated in the program segment to load the base register (Rn) specified. In fact, dummy segments neither create nor refer to any specific area of main storage!

A dummy segment acts as a template or overlay for any desired section of main storage. The cell declarations composing such a segment define only the structure of the template, never the contents of storage.

To use a dummy segment, the program computes, or is given as a parameter during execution, the address of some section of main storage which is assumed to have the same structure as that defined by the dummy segment. Statements of the program then place this address into the base register (Rn) of the dummy segment before other statements reference the cells of that segment.

If the address is changed or replaced by a new address, then the dummy segment or template is moved to overlay a different section of storage beginning at the new address.

For example, let us assume that some section of storage contains telephone book entries, each composed of a 32-character name, a 32-character address, and an 8-character telephone number, in that order. Then the following dummy segment could be used to reference the component parts of any individual entry once the dummy segment's base register (in this case, R7) has been set to the origin address of an entry.

       DUMMY BASE R7;
          ARRAY 32 CHARACTER NAME, ADDRESS;
          ARRAY  8 CHARACTER TELEPHONE;
       CLOSE BASE;

  (We shall cover CLOSE BASE and cell declarations shortly.)

There is one special case of base register specification for any data segment, which is especially useful in DUMMY BASE declarations. That is when R0 is specified as the base register, which really implies no base register at all, since R0 can never be used as a base or index register (see section 1.4, Instructions and Addressing). The cell declarations of such a segment supply only relative displacement values. The base registers for these cells are supplied explicitly by the programmer when these cells are referenced. Therefore, the programmer may choose one or more base registers (R1 through R15) with which to make references to these cells. For instance, if the dummy segment in our preceding telephone book example was declared DUMMY BASE R0 instead of DUMMY BASE R7, then an 8-character telephone number (or other component of the segment) could be referenced using any base register containing the address of an entry. In fact, two entirely separate entries could be referenced simultaneously (such as when comparing two telephone numbers) using two registers, each containing the address of one entry.

4.1.2  CLOSE BASE Declarations

All cell declarations are associated with data segments; that is, once a data segment has been initiated by one of the preceding declarations, the data segment is open to accepting cell declarations. The first cell declared in a data segment is assigned a displacement value of 0. Succeeding cells are assigned higher displacement values.

While a data segment is open, a new data segment may be initiated; that is, data segments may be nested one inside another. Cell declarations would then be associated with the newly opened data segment.

A data segment is closed to accepting further cell declarations upon encountering the END of the block in which the data segment was initiated; but the CLOSE BASE declaration may be used to close a data segment before the END. The CLOSE BASE declaration serves only to prevent subsequent cell declarations (if any) from being associated with the closed data segment. The scope of the cell declarations (composing the data segment) is not affected. The CLOSE BASE declaration is simply the reserved identifiers:

CLOSE BASE;

4.2  The Main Program Data Segment

When the main program is defined as a block terminated by a period (BEGIN ... END.), the compiler initiates a data segment which could be described:

       SEGMENT BASE R13;
          ARRAY 18 INTEGER B13;

The cell declaration (for B13) beginning the data segment provides a save area for linkage to other programs and subroutines. Other types of main programs will be discussed in Chapter 8, Procedures.

4.3  Cell Declarations

The structure of a data segment is defined by the cell declarations that compose it. There are five basic types of cell declarations, each type defining the number of bytes of storage to be reserved for the declared cell as specified in the table below.

                       TYPE          BYTES RESERVED
                       ----          --------------

               BYTE (or CHARACTER)         1
               SHORT INTEGER               2
               INTEGER (or LOGICAL)        4
               REAL                        4
               LONG REAL                   8

The general form of a cell declaration is:

Type id1, id2, ... , idn;

Type id;

where 'Type' is one of the basic types specified in the table above, and the id's are cell identifiers. For example, the following data segment and cell declarations would define the structure of some storage area as diagrammed below. (We saw this diagram in Figure 3 in Chapter 1.)

                  SEGMENT BASE R7;
                     INTEGER  XX1;
                     SHORT INTEGER  XX2, XX3;
                     LONG REAL  XX4;
                     INTEGER  XX5, XX6, XX7;
                  CLOSE BASE;
                         -----------
          R7 ------>  0 |    XX1    |
                        |-----------|
                      4 | XX2 | XX3 |
                        |-----------|
                      8 |    XX4    |
                     12 |           |
                        |-----------|
                     16 |    XX5    |
                        |-----------|
                     20 |    XX6    |
                        |-----------|
                     24 |    XX7    |
                         -----------

                        4 bytes wide

                    FIGURE 5.

The data segment declaration specifies register R7 as the base address register of this area. That is, it defines the value of the B-field of any instructions that refer to the declared cells. The cell declarations in turn specify the D-field of such instructions. (See Instructions and Addressing in Chapter 1, if necessary.)

The displacement value associated with any declared cell is determined by the total number of bytes of storage space reserved by preceding cell declarations of the data segment, and by the alignment rule associated with the type of the cell being defined.

The alignment rule simply states that the displacement value assigned for a cell must be an integral multiple of the basic cell length: 1, 2, 4, or 8 bytes depending on the type. The alignment rule is always applied before the compiler reserves the space associated with the basic cell type. The following example should demonstrate the concept.

                  DUMMY BASE R2;
                     SHORT INTEGER  S;
                     INTEGER  X;
                     BYTE  B;
                     INTEGER  Y;
                  CLOSE BASE;
                          -------
          R2 ------>   0 | S |///|
                         |-------|
                       4 |   X   |
                         |-------|
                       8 |B|/////|
                         |-------|
                      12 |   Y   |
                          -------

                         FIGURE 6.

The slashed areas (/) of the diagram are not exactly waste space, because they can be referenced as we shall see later. However, a tighter packing of the area would result by ordering the declarations as follows:

                  DUMMY BASE R2;
                     INTEGER  X, Y;
                     SHORT INTEGER  S;
                     BYTE  B;
                  CLOSE BASE;

The area would then appear as:

                          -------
          R2 ------>   0 |   X   |
                         |-------|
                       4 |   Y   |
                         |-------|
                       8 | S |B|/|
                          -------

                         FIGURE 7.

The various types not only define the number of bytes of storage to be reserved, but also the manner in which the cells are likely to be used. We shall cover this concept in detail in the next chapter.

4.3.1  Arrays

Each basic cell declaration may be preceded by the reserved word ARRAY followed by a non-negative integer value. An array declaration applies to every cell identifier of the declaration, and it causes the amount of space reserved for each cell to be n-times the basic cell length, where 'n' is the non-negative integer value. For example:

       ARRAY 4 BYTE NAME, ADDRESS;

indicates that four bytes of space must be reserved for both NAME and ADDRESS. The compiler does not 'remember' array declarations, but simply reserves the requested space. Of course, alignment is always done before space is reserved. An ARRAY 0 type of declaration reserves no space of its own; it only aligns the declared cell to the appropriate boundary. Thus:

       ARRAY 0 LONG REAL Y;
       ARRAY 2 INTEGER X;

causes X and Y both to begin at the same double-word boundary. The declaration of Y causes the alignment, and the declaration of X reserves the actual space.

Changing the cell declarations given with Figure 6 by including arrays, we can create exactly the same area as shown below.

                  DUMMY BASE R2;
                     ARRAY 2 SHORT INTEGER  S;
                     INTEGER  X;
                     ARRAY 4 BYTE  B;
                     INTEGER  Y;
                  CLOSE BASE;

                          -------
          R2 ------>   0 | S |   |
                          -------
                       4 |   X   |
                          -------
                       8 |B| | | |
                          -------
                      12 |   Y   |
                          -------

                         FIGURE 8.

Cells of an array are normally referenced by displacements which are a multiple of the basic cell length, starting with 0. Thus, the four bytes of B would be referenced as B(0), B(1), B(2), and B(3). The two short integers of S would be referenced as S(0) and S(2). Note that a reference to S alone is NOT a reference to the entire array! It is only a reference to S(0). Also, S(4) would be a reference to the first halfword of integer X, and S(6) would be a reference to the second halfword of X. Similarly, X(_4) is a reference to all of the declared S array (two short integers = one integer). We shall cover cell references in more detail in section 4.4.

4.3.2  Cell Initialization

Cell declarations of SEGMENT BASE and GLOBAL DATA areas may be initialized at compile time to specific values. Then, when these data segments are loaded into core, the initialized cells will contain the specified values. Non-initialized cells could contain unpredictable values unless the operating system initializes core.

Cell initialization is specified by following a cell identifier with an equal sign (=) and a predefined value or list of values. For example:

       INTEGER  X = 27,  SIX = 6;

Cell identifiers declared to be an array of 'n' cells may be initialized with a list of up to 'n' values. The values must be separated by commas and the entire list must be enclosed in parentheses. For example:

       ARRAY 12 INTEGER TERMS = 10,
                        TABLE = (1,4,4,4,5,6,1,4,4,4,5,6);

In the above example, only the first reserved integer of TERMS is initialized and the remaining 11 reserved integers of TERMS are undefined. All of TABLE is initialized by a parenthesized list of values (12 integers). Any parenthesized initialization list may be preceded by a positive (non-zero) integer replication factor, and any individual value of a list may be replaced by a parenthesized list of values optionally preceded by such a replication factor. Therefore, the preceding example could be rewritten as follows:

       ARRAY 12 INTEGER TERMS = 10,
                        TABLE = 2(1,3(4),5,6);

In all the examples given thus far, the type of the declared cell has been the same as the type of the initializing values. If the initializing values are fixed-length values, then the cell type determines the amount of space initialized by each value. This is made possible by the fact that all fixed-length values are maintained within the PL360 compiler as fullword (fixed-point) or double-word (floating-point) values regardless of their specific type. Therefore, an array of byte cells may be initialized by a list of integer values, and only the lower byte of each value is used in doing the initialization. Similarly, an array of integer cells may be initialized by a list of short integer values with each short integer value sign-extended to fill the integer cell. For example:

       ARRAY 4 BYTE MARKS = (1,5,0,7);
       INTEGER RATION = 9S;

                -----------
       MARKS   |01|05|00|07|
               |-----------|
       RATION  |00 00 00 09|
                -----------

Since the PL360 compiler does not check the type of an initializing fixed-length value against the type of the cell being initialized, it is possible to initialize INTEGER cells with floating-point values or REAL cells with fixed-point values. In general however, cells should be initialized with values of compatible type.

If the initializing value is a variable-length string, then the string determines the amount of space initialized regardless of cell type. Strings are never truncated or expanded. Each character occupies one byte starting with the left-most byte. For example, an 8-byte string would completely initialize any LONG REAL, ARRAY 2 INTEGER, ARRAY 4 SHORT INTEGER, or ARRAY 8 BYTE cell declaration.

SHORT INTEGER and INTEGER cells may be initialized to the Base-Displacement field of a previously declared cell as follows:

       SEGMENT BASE R3;
          INTEGER SOMECELL;
          SHORT INTEGER POINT = @SOMECELL;
       CLOSE BASE;

The value initializing POINT would be equivalent to #3000S since 3 is the Base field value and 000 is the Displacement field value for SOMECELL.

INTEGER cells may also be initialized to the absolute address of a previously declared cell. The initialization is done by the Loader. Absolute address initialization is indicated by using the @@-symbol. The general form of absolute address initialization is:

       INTEGER cellname = @@prevcell(disp/flags)

where "disp" is an additional displacement added to the absolute address of prevcell, and "flags" are a byte-sized value that will be placed in the upper byte of the integer along with the 3-byte absolute address. Such usage is restricted to 24-bit addressing. For example:

       SEGMENT BASE R4;
          INTEGER SOMECELL;
          INTEGER PARMPTR = @@SOMECELL(0/#80);
       CLOSE BASE;

PARMPTR will contain the absolute address of SOMECELL when loading is completed, with hex-80 in the upper byte.

The @@-symbol may also be used to initialize INTEGER cells to the absolute entry point address of previously declared procedures. Also, the @-symbol may be used to initialize SHORT INTEGER and INTEGER cells to the program segment Base and relative Displacement of previously declared procedures. We shall cover procedures in depth in Chapter 8.

DUMMY BASE and EXTERNAL DATA areas may also show cell initialization, but such areas are not actually initialized. Any initialization shown for cells of such areas only serves as documentation of what is expected, not of what may actually exist.

Arrays may also be declared such that the amount of space they take is determined by the amount of initialization given.

       ARRAY DATAFILL celltype cellname = (fill);
 or    ARRAY DATAFILL celltype = (fill);  |-- No cellname --|

The amount of "fill" determines the amount of space. For example:

       ARRAY DATAFILL INTEGER MARKERS = (1,3,5,9);
 and   ARRAY 4 INTEGER MARKERS = (1,3,5,9);

would produce the same storage results. If there is no fill, then ARRAY DATAFILL acts like ARRAY 0. DATAFILL is a compiler pre-declared EQUATE whose value is not likely to be given as an ARRAY size.

DSTAR is a pre-declared data cell address definition of base-displacement form corresponding to the current Bddd location in the data segement. DSTAR can be used to compute the length of data regions. For example:

       ARRAY DATAFILL INTEGER MARKERS = (1,3,5,9);
       EQUATE MARKLEN SYN DSTAR - MARKERS;  |-- length in bytes --|

Since both DATAFILL and DSTAR are pre-declared, you may redeclare them, but then you would lose the fill-defined array capability or relative data location capability. The compiler is not checking the names, just the special values associated with them.

DATAFILL is very handy when defining string arrays:

       ARRAY DATAFILL BYTE MESSAGE = ("This is a message");
       EQUATE MSGSIZE SYN STRING;  |-- Length of array --|

The following examples demonstrate the points covered by this section on cell initialization.

       ARRAY 120 BYTE ITEMS = ( 5, "Items", 8, "Examples", 4, "Help");
       ARRAY 132 BYTE BLANKS = 132(" "),
                      BUFF = 33(" ",2("*")," ");
       INTEGER WORD = "word";
       INTEGER WORDLOC = @@WORD;
       ARRAY 3 SHORT INTEGER INSTRUCTION = (#D200,@BUFF,@BLANKS);

4.4  Cell References

Once cells have been declared, they may be referenced in subsequent declarations and statements. For each cell identifier, the compiler remembers the cell's Index-Base-Displacement, i.e., its X-B-D fields. Normally, the X-field is 0, the B-field is Rn of the Data Segment Declaration containing the declared cell, and the D-field is the relative displacement of the start of the declared cell within the data segment.

A cell reference allows the programmer to modify the X-B-D field and directs the PL360 compiler to use the resultant X-B-D field in satisfying the declaration or statement containing the reference.

Several forms of cell reference are shown below, the last of which indicates a non-subscripted (unmodified) cell reference.

       identifier(Rb+Rx+k)  or  identifier(Rb+Rx-k)
       identifier(Rb+k)     or  identifier(Rb-k)
       identifier(Rb)       or  identifier(Rx)
       identifier(Rx+k)     or  identifier(Rx-k)
       identifier(k)
       identifier

Rb is allowed if the referenced cell has 0 for its B-field. In such a case, Rb replaces the B-field. DUMMY BASE R0 cells are examples of such cases. Rb defines the BASE register and should not be R0.

Rx is allowed if the referenced cell has 0 for its X-field. In such a case, Rx replaces the X-field. Rx defines the INDEX register and should not be R0.

'k' is either a single integer value or an expression of added and/or subtracted integer values. Each integer value is either added to or subtracted from the D-field to create a new D-field which must satisfy 0 <= D < 4096.

It is important to note that the D-field is always a byte displacement and that 'k' is an adjustment in byte amounts. Therefore, given the following integer array:

       ARRAY 5 INTEGER SPACE;

each element of the array takes four bytes and would be referenced as follows:

       SPACE(0)     or   SPACE
       SPACE(4)
       SPACE(8)
       SPACE(12)
       SPACE(16)

Throughout the remainder of this text we shall use the term 'cell' to indicate a cell reference regardless of cell type. Also, whenever the X-field of a cell is non-zero, the cell is 'indexed'.

4.5  Cell Synonyms

Cell synonym declarations provide the programmer with the ability to associate another identifier with a previously declared cell. The general form of a cell synonym declaration is:

Type id1 SYN cell1, id2 SYN cell2, ... , idn SYN celln;

Type id SYN cell;

where 'Type' is one of the basic cell types as given in section 4.3, i.e., BYTE, SHORT INTEGER, INTEGER, REAL, and LONG REAL.

The 'Type' of the newly declared identifier need not match the type associated with the referenced cell. In fact, the PL360 compiler does not check either type or alignment. Therefore, the programmer may define different types of cells having the same address. For example:

       DUMMY BASE R5;
          ARRAY 3 INTEGER Y;
       CLOSE BASE;

       INTEGER YINDEX SYN Y(R3);
       SHORT INTEGER YHI SYN Y,
                     YLO SYN Y(2);

The Index-Base-Displacement fields for the above are:

       Y        05000
       YINDEX   35000
       YHI      05000
       YLO      05002

'Type' may be preceded by 'ARRAY n', but the compiler will ignore the array portion of the declaration because cell synonym declarations never reserve any actual space. Using ARRAY in such a case is more for documentation purposes than anything else. However, if the SYN portion of the declaration were removed, the declaration would become a standard cell declaration which does reserve space. For example:

       ARRAY 132 BYTE OUTPUT;
       ARRAY 80 BYTE CARD SYN OUTPUT(1);
          |-- CARD is 80 bytes of OUTPUT beginning at 2nd byte --|

Cell synonym declarations also allow direct definition of the X-B-D field of a cell identifier by using an integer value in place of a cell reference. For example, the definition of Y above could have been done as follows:

       INTEGER Y SYN #5000;

There is a set of pre-declared cell identifiers that may be used in defining other identifiers. They are:

       MEM, B1, B2, B3, B4, B5, B6, B7, B8, B9,
       B10, B11, B12, B13, B14, B15

These identifiers are given below using declarations that would yield equivalent definitions to those pre-declared by the PL360 compiler.

       DUMMY BASE R0;   INTEGER MEM;   CLOSE BASE;
       DUMMY BASE R1;   INTEGER B1;    CLOSE BASE;
       DUMMY BASE R2;   INTEGER B2;    CLOSE BASE;
       DUMMY BASE R3;   INTEGER B3;    CLOSE BASE;
       DUMMY BASE R4;   INTEGER B4;    CLOSE BASE;
       DUMMY BASE R5;   INTEGER B5;    CLOSE BASE;
       DUMMY BASE R6;   INTEGER B6;    CLOSE BASE;
       DUMMY BASE R7;   INTEGER B7;    CLOSE BASE;
       DUMMY BASE R8;   INTEGER B8;    CLOSE BASE;
       DUMMY BASE R9;   INTEGER B9;    CLOSE BASE;
       DUMMY BASE R10;  INTEGER B10;   CLOSE BASE;
       DUMMY BASE R11;  INTEGER B11;   CLOSE BASE;
       DUMMY BASE R12;  INTEGER B12;   CLOSE BASE;
       DUMMY BASE R13;  INTEGER B13;   CLOSE BASE;
       DUMMY BASE R14;  INTEGER B14;   CLOSE BASE;
       DUMMY BASE R15;  INTEGER B15;   CLOSE BASE;
or     INTEGER MEM SYN 0,  |-- cell defined by integer value --|
               B1 SYN MEM(R1), B2 SYN MEM(R2), B3 SYN MEM(R3),
               B4 SYN MEM(R4), B5 SYN MEM(R5), B6 SYN MEM(R6),
               B7 SYN MEM(R7), B8 SYN MEM(R8), B9 SYN MEM(R9),
               B10 SYN MEM(R10), B11 SYN MEM(R11), B12 SYN MEM(R12),
               B13 SYN MEM(R13), B14 SYN MEM(R14), B15 SYN MEM(R15);

All of these pre-declared cell identifiers may be re-declared to be anything desired by the programmer; however, this is not recommended. The ARRAY 18 INTEGER B13; declaration shown in section 4.2 actually re-declares B13 to be the same as its pre-declared definition. If the declaration had been ARRAY 9 LONG REAL B13; then B13 would have been redefined.

4.6  Register Synonyms

Register synonyms serve to associate identifiers with registers, i.e., to define other identifiers for registers besides the standard register identifiers listed in section 2.3.

There are three types of registers: INTEGER, REAL, and LONG REAL. The general form of a register synonym declaration is:

Type REGISTER id1 SYN reg1, id2 SYN reg2, ... , idn SYN regn;

Type REGISTER id SYN reg;

where 'Type' is either INTEGER, REAL, or LONG REAL and 'reg' is a pre-declared or previously declared register identifier of matching type. For example:

       INTEGER REGISTER ZERO SYN R0,  Z0 SYN ZERO;
       REAL REGISTER FLOAT SYN F2;

A register synonym identifier may be used anywhere a standard register identifier may be used, throughout the scope of the register synonym declaration. For example:

       INTEGER REGISTER RECORD SYN R1;
       DUMMY BASE RECORD;

The DUMMY BASE declaration is equivalent to:

       DUMMY BASE R1;

Frequently, registers are used in place of storage cells, and therefore register synonym identifiers serve better to define the nature of these 'pseudo cells' than do the standard register identifiers. Also, registers that serve a specific purpose in the program, such as the base address register of some data, are often given register synonym identifiers. But regardless of what the intended purpose may be, the programmer should not be led to believe that a different name implies a different physical entity; IT DOES NOT. One of the most common programming errors is the failure to recognize that fact.

Although the pre-declared register identifiers may be re-declared as anything desired by the programmer, imagine the confusion that could cause! It is therefore strongly recommended that the standard register identifiers be treated as though they were basic symbols.

Throughout this text, any references to the pre-declared register identifiers apply to register synonyms as well.

4.7  Integer Value Synonyms: EQUATE

Integer value synonyms are commonly referred to as 'equates' or 'equate declarations'. These declarations serve to associate identifiers with integer values or integer value expressions which are computed by the PL360 compiler at the time the declaration is processed. These integer value identifiers may be used anywhere integer values are used, and their definition remains in effect throughout the scope of the declaration. (See section 2.5.1 for the definition of integer values.)

The general form of an integer value synonym declaration is:

EQUATE id1 SYN exp1, id2 SYN exp2, ... , idn SYN expn;

EQUATE id SYN exp;

where each 'exp' may be of the form

       start operator iv operator iv ... operator iv
or     start

where 'iv' represents any pre-declared, previously declared, or self-defining integer value; and 'start' represents one of the following eight ways to start an equate's expression:

       iv
       ABS iv
       DEC iv
       NEG iv
       NEG ABS iv
       cella - cellb
       "string"
       regname

The pre-declared words ABS, DEC, NEG and NEG ABS are called monadic operators. They indicate taking the absolute value, decrement by 1, sign inversion, or sign inversion of the absolute value of the integer value which follows.

The cella-cellb form represents a value which is the difference between the D-fields of two pre-declared or previously declared cells. The only restriction is that the X and B-fields must be identical for the two referenced cells.

The "string" form is limited to a character string of from one through four characters. The value defined by the "string" is the EBCDIC representation of the characters right-justified and zero-filled to the left if less than four characters. Thus,

       EQUATE ACTSTR SYN "A123",  SEPCHAR SYN ".";

is equivalent to defining the following:

       EQUATE ACTSTR SYN #C1F1F2F3,  SEPCHAR SYN #0000004B;

The regname form specifies a register identifier and the value is taken to be the number of the specified register. Thus,

       EQUATE X SYN R1,  Y SYN R5;

is equivalent to defining the following:

       EQUATE X SYN 1,   Y SYN 5;

Register synonyms are typically used in the regname form since the actual register number may vary depending on the synonym.

Once the expression has been started, it may be continued by as many occurrences of 'operator iv' as desired. The allowed operators are:

     (a)  Arithmetic (see section 1.3)
          +      addition
          -      subtraction
          *      multiplication
          /      division
          ++     logical addition (unsigned)
          --     logical subtraction (unsigned)
     (b)  Logical (see section 1.6)
          AND    conjunction
          OR     inclusive injunction
          XOR    exclusive injunction
     (c)  Shifting (see section 1.7)
          SHLL   shift left (multiply by 2 raised to iv power)
          SHLA   (same as SHLL)
          SHRL   shift right (divide by 2 raised to iv power)
          SHRA   (same as SHRL)

There is no operator precedence! The expression is evaluated in a strictly left-to-right sequence.

There are a number of pre-declared equates which we shall find useful in the next few chapters. They are shown below using declarations equivalent to the compiler's default declarations.

       EQUATE OVERFLOW SYN 1,  ON SYN 1,  MIXED SYN 4,  OFF SYN 8,
              CARRY SYN 3,  FALSE SYN 0,  TRUE SYN _1;
       EQUATE DATAFILL SYN #87654321;
       EQUATE STRING SYN 0;  |-- varies during compilation --|

The last definition, for STRING, cannot be truly duplicated because the value of STRING varies during compilation. Its initial value is 0, but every time the compiler processes a "string" the value of STRING becomes the length (in bytes) of that "string". DATAFILL is a special value used to assign array size by content.

There is also a SHORT EQUATE declaration that provides SHORT INTEGER equivalents. The values defined will be treated like integers ending in S, such as: #40S, _3S, etc.

       SHORT EQUATE LOMASK SYN #FFF8;
Examples:

       EQUATE A SYN 200,  B SYN A+8,  C SYN 4,
              D SYN A/C AND _4;
       INTEGER S = A;
       ARRAY B BYTE X, Y;
       EQUATE E SYN Y(B)-S,  F SYN E-C SHLL 2;
       INTEGER REGISTER XR SYN R1,  YR SYN R5;
       EQUATE G SYN XR, H SYN YR - G + 1;
       ARRAY H INTEGER SPACE;
       EQUATE I SYN "AB", J SYN STRING;

 A=200         B=208         C=4           D=48          E=420
 F=1664        G=1           H=5           I=#C1C2       J=2

4.8  Sample Declarations

The sample declarations given in this section will be used throughout the remainder of this text, particularly in Chapters 5 and 6.

       BEGIN  INTEGER REGISTER ZERO SYN R0,  TEMP SYN R1,
                               I SYN R7,  J SYN R8,  K SYN R9;
          BYTE C1 SYN B1,  C2 SYN B2,  C3 SYN B3;
          SHORT INTEGER H1 SYN B1,  H2 SYN B2,  H3 SYN B3;

          LONG REAL LRA,  LRB = #4E00000000000000L;
          INTEGER IA SYN LRA(4),  IB SYN LRB(4);
          REAL RA SYN LRA,  RB SYN RA(4);

          INTEGER X, Y, Z;
          SHORT INTEGER S1, S2;
          INTEGER BLANKS = "    ";
          ARRAY 132 BYTE LINE = 132(" ");
          ARRAY 16 BYTE TAB = "0123456789ABCDEF";
          ARRAY 256 BYTE TRTAB SYN TAB(_240);

          EQUATE SQUARES SYN 64,
                 STAKL SYN SQUARES * 3,
                 SPACE SYN SQUARES * 5;
          ARRAY STAKL INTEGER STACK;
          ARRAY SPACE BYTE TABLES;
          BYTE FLAGS SYN TABLES(_1),
               TABLE1 SYN FLAGS(SQUARES),
               TABLE2 SYN TABLE1(SQUARES),
               TABLE3 SYN TABLE2(SQUARES),
               TABLE4 SYN TABLE3(SQUARES);

The tables below list the identifiers given in the declarations on the preceding page into two groups. The first group is arranged by the sort order of the identifiers. The second group is arranged by the order in which the identifiers were declared. For each group, the value the compiler associates with the identifier is also given.

         Sorted order                           Declared order
         ------ -----                           -------- -----

       Identifier  Value                      Identifier  Value

       BLANKS       D068                      ZERO         0000
       C1           1000                      TEMP         0001
       C2           2000                      I            0007
       C3           3000                      J            0008
       FLAGS        D3FF                      K            0009
       H1           1000                      C1           1000
       H2           2000                      C2           2000
       H3           3000                      C3           3000
       I            0007                      H1           1000
       IA           D04C                      H2           2000
       IB           D054                      H3           3000
       J            0008                      LRA          D048
       K            0009                      LRB          D050
       LINE         D06C                      IA           D04C
       LRA          D048                      IB           D054
       LRB          D050                      RA           D048
       RA           D048                      RB           D04C
       RB           D04C                      X            D058
       SPACE    00000140                      Y            D05C
       SQUARES  00000040                      Z            D060
       STACK        D100                      S1           D064
       STAKL    000000C0                      S2           D066
       S1           D064                      BLANKS       D068
       S2           D066                      LINE         D06C
       TAB          D0F0                      TAB          D0F0
       TABLES       D400                      TRTAB        D000
       TABLE1       D43F                      SQUARES  00000040
       TABLE2       D47F                      STAKL    000000C0
       TABLE3       D4BF                      SPACE    00000140
       TABLE4       D4FF                      STACK        D100
       TEMP         0001                      TABLES       D400
       TRTAB        D000                      FLAGS        D3FF
       X            D058                      TABLE1       D43F
       Y            D05C                      TABLE2       D47F
       Z            D060                      TABLE3       D4BF
       ZERO         0000                      TABLE4       D4FF

4.9  Exercise

Problem 1.

The following block contains: two other blocks, three data segments (ALPHA, BETA, DELTA), and six cells (TEST, EXAM, FLAGS, TOPS, EXTRA, LAST).

     |01|    BEGIN  GLOBAL DATA ALPHA BASE R6;
     |02|              INTEGER TEST;
     |03|           GLOBAL DATA BETA BASE R7;
     |04|              INTEGER EXAM;
     |05|       |.... (statement group 1) ....|
     |06|           BEGIN  INTEGER FLAGS;
     |07|               CLOSE BASE;
     |08|                  INTEGER TOPS;
     |09|               |.... (statement group 2) ....|
     |10|           END;
     |11|       |.... (statement group 3) ....|
     |12|           BEGIN  INTEGER EXTRA;
     |13|               GLOBAL DATA DELTA BASE R8;
     |14|                  INTEGER LAST;
     |15|               |.... (statement group 4) ....|
     |16|           END;
     |17|       |.... (statement group 5) ....|
     |18|    END

Problem 2.

On the following page is a complete main program which computes and prints odd-sized magic squares from a 15 by 15 down to a 3 by 3.

|01|  BEGIN  ARRAY 133 BYTE LINE = 133(" ");  |-- OUTPUT LINE AREA --|
|02|     ARRAY 256 INTEGER XX;  |-- SPACE FOR UP TO 16 BY 16 SQUARE --|
|03|     INTEGER SIZE, LIMIT;  |-- SQUARE SIZE AND SUB-SQUARE LIMIT --|
|04|     PROCEDURE MAGIC (R6);   |-- MAGIC SQUARE GENERATOR --|
|05|     BEGIN SHORT INTEGER NSQR;  |-- CURRENT SQUARE SIZE --|
|06|        INTEGER REGISTER N SYN R0, I SYN R1, J SYN R2,
|07|                         X SYN R3, IJ SYN R4, K SYN R5;
|08|        NSQR := N;  I := N * NSQR;  NSQR := I;
|09|        I := 1 + N SHRL 1;  J := N;
|10|        FOR K := 1 STEP 1 UNTIL NSQR DO
|11|        BEGIN  IJ := I SHLL 4 + J SHLL 2;
|12|           X := XX(IJ-68);  IF X ~= 0 THEN
|13|           BEGIN I := I - 1;  J := J - 2;
|14|              IF I <= 0 THEN I := I + N;
|15|              IF J <= 0 THEN J := J + N;
|16|              IJ := I SHLL 4 + J SHLL 2;
|17|           END;  XX(IJ-68) := K;  I := I + 1;
|18|           IF I > N THEN I := I - N;
|19|           J := J + 1;  IF J > N THEN J := J - N;
|20|        END;
|21|     END;
|22|
|23|  |-- MAIN PROGRAM CODE STARTS HERE --|
|24|     R0 := 15 =: SIZE;   |-- ESTABLISH INITIAL 15 BY 15 --|
|25|     WHILE R0 > 1 DO  |-- MAIN LOOP FOR SQUARE GENERATION --|
|26|     BEGIN  R1 := 16 - SIZE SHLA 2 =: LIMIT;
|27|        XX := 0;  XX(4/256) := XX;     |-- ZERO OUT --|
|28|        XX(260/256) := XX;             |-- THE WORK --|
|29|        XX(516/256) := XX;             |--  SPACE.  --|
|30|        XX(772/252) := XX;
|31|        R0 := SIZE;  MAGIC;   |-- FORM MAGIC SQUARE --|
|32|        R2 := 1;  R4 := R4-R4;  R7 := R4;  R3 := 5;
|33|        R6 := SIZE;  WHILE R4 < R6 DO  |-- OUTPUT LINES --|
|34|        BEGIN  R1 := @LINE;  R5 := R5-R5;  WHILE R5 < R6 DO
|35|           BEGIN R0 := XX(R7);  VALTOBCD;  R5 := @B5(1);
|36|              R7 := @B7(4);  R1 := @B1(R3);
|37|           END;  R0 := @LINE;  WRITE;  R7 := R7 + LIMIT;
|38|           R4 := @B4(1);  |-- BUMP LINE COUNTER --|
|39|        END;  LINE := " ";  LINE(1/132) := LINE;
|40|        R0 := @LINE;  WRITE;  |-- WRITE BLANK LINE --|
|41|        R0 := SIZE - 2 =: SIZE;  |-- REDUCE SIZE BY 2 --|
|42|     END;  |-- OF MAIN WHILE LOOP --|
|43|  END.

5  Assignment Statements

This chapter and the next cover the majority of all statements used in a PL360 program. Each statement is normally terminated by a semicolon; however, a semicolon is not part of the formal definition of a statement, and therefore many examples of statements will not include the terminating semicolon. The reason for this will become clear when we examine the IF statement in the next chapter.

This chapter covers all PL360 assignment statements. An assignment statement is recognizable by its characteristic form:

item := expression

where 'item' is either a register identifier or a cell reference, and ':=' is the assignment operator that may occur only once in any assignment statement.

An assignment statement is read as follows: 'item' is assigned the value of the 'expression'; that is, the contents of 'item' becomes the value of the 'expression'. The permissible forms of 'expression' depend on the type of 'item'; but it is important to note that every 'expression' is evaluated in a strictly left-to-right sequence and that the contents of 'item' changes at each step in the evaluation. This is quite different from languages such as FORTRAN where the 'expression' is completely evaluated before its value is assigned to the 'item'.

For example, the FORTRAN statement I = J + I is done as follows:

"Take the current value of J and add to it the current value of I; now place the result of the computation into I."

The PL360 statement I := J + I is done as follows:

"Take the value of J and place it into I; now take the value in I and add it to the value in I, i.e., double the value in I."

As you can see the two methods may lead to entirely different results for the final value left in I. The correct PL360 statement that accomplishes the same result as the FORTRAN statement would be:

       I := I + J

In section 4.8, I and J were declared as integer register synonyms for R7 and R8 respectively; therefore, the above statement is equivalent to:

       R7 := R7 + R8

The PL360 statement R7 := R8 + R7 is equivalent to the two statements

       R7 := R8;  R7 := R7 + R7;

5.1  Register Assignment Statements

There are three types of register assignment statements corresponding to the three types of registers: INTEGER, REAL, and LONG REAL. In general, the 'expression' following the assignment operator (:=) consists of one or more terms separated from each other by operators other than the assignment operator; that is,

register := term1 op term2 ... op termn

register := term

Each term may either be a constant value or specify the contents of a designated register or cell. Terms must be of compatible type to the assigned register; that is, INTEGER registers with INTEGER and SHORT INTEGER terms, REAL registers with REAL terms, and LONG REAL registers with REAL and LONG REAL terms.

The first term immediately following the assignment operator may be preceded by one of the monadic operators ABS, NEG, and NEG ABS which indicate taking the absolute value, sign inversion, or sign inversion of the absolute value of the first term, then placing the resulting value into the assigned register. If the first term is not preceded by a monadic operator and is not the same as the assigned register, then the value or contents of the designated register or cell is placed into the assigned register. Thus,

     R1  := R2       place contents of register R2 into register R1
     J   := X        place contents of cell X into register J
     R3  := 50       place the value 50 into register R3
     F0  := 1R       place the value 1.0 into register F0
     F23 := NEG LRB  place the complement of the contents of cell LRB
                     into register F23

The assignment operator causes the generation of zero, one, or two instructions depending on the term which follows the assignment operator and the use or non-use of monadic operators. The following examples illustrate the cases:

     R1 := R1        no instruction because same register on both sides
     R1 := R2        one instruction, load R1 from R2
     R1 := X         one instruction, load R1 from cell X
     R1 := NEG R1    one instruction, complement contents of R1
     R1 := NEG R2    one instruction, load R1 with complement from R2
     R1 := 5000      one instruction, load R1 from a cell containing
                         the integer constant 5000
     R1 := NEG X     two instructions; first instruction corresponding
                         to  R1 := X;  second instruction corresponding
                         to  R1 := NEG R1

As a shorthand for 'register := register op term2 ...' where register is the same on both sides of the assignment operator, PL360 allows 'register op term2 ...' as the definition of a register assignment statement; that is, R1 := R1 can be shortened to R1 only. However, this shorthand form is not recommended since it lacks the clarity of the long form.

5.1.1  Integer Register Assignments

When the assigned register is an integer register, then all terms must be integer or short integer values or cells, or integer registers. The first term immediately following the assignment operator may be preceded by a monadic operator, ABS, DEC, NEG, or NEG ABS. ABS results in the absolute value of the term (zero or positive). DEC results in the value of the term reduced by 1. NEG results in the negation of the term, with positives becoming negative, negatives becoming positive (except for the largest negative), and zero remaining zero. NEG ABS results in the negation of the absolute value of the term (zero or negative).

The first term immediately following the assignment operator may also be one of the following, but they may not be preceded by the monadic operators:

     (a)  "string"       (string of 4 or fewer characters)
     (b)  @ Procname     (Procname is a known PROCEDURE name)
     (c)  @ cell         (cell may be of any type: BYTE, REAL, etc.)
       R1 := "WORD"  is equivalent to  R1 := #E6D6D9C4
     and
       R1 := "2"     is equivalent to  R1 := #000000F2  or  R1 := #F2
       R4 := NEG 4;  |--  R4 = _4 --|
       R9 := #6500;  |--  R9 = some address --|
     then
       R1 := @B9(R4+8);   |--  R1 = #6504 --|

The result in R1 is the same as the address calculation given in section 1.4. If the statement had been R1 := B9(R4+8) then R1 would be assigned the 4-byte contents of the cell whose address was #6504.

Since B1, B2, etc. are cell names corresponding to a D-field of 0, an X-field of 0, and a B-field specifying R1, R2, etc., then the use of the address operator (@) before these cell names simply implies doing address arithmetic with the B-field integer register. Modification of such cell names by a displacement or index register results in nothing more than additive arithmetic. Generally, it is more economical to increment integer register contents by using address arithmetic rather than by using straight addition. For example:

       R1 := @B1(4)     adds 4 to the contents of R1
       R2 := @B2(R1+8)  adds R1+8 to the contents of R2
       R3 := @B2(R1+8)  adds R1+8 to R2 and places result in R3

The examples above could just as well have been done as follows:

       R1 := R1 + 4
       R2 := R2 + R1 + 8
       R3 := R2 + R1 + 8

The major difference between the two methods is that the address arithmetic involves only a single 'load address' instruction and no memory references for constants in the literal pool. The other method takes a constant in the literal pool, and therefore a memory reference, and in the last two cases it takes more instructions. So @-operator arithmetic is more economical providing the following conditions are accepted:

   (1)  The result of the address computation is always greater than or
        equal to 0 and less than 2'24.  This means that the top byte of
        the result will always be 00 hexadecimal.  If a negative result
        is possible, @-operator arithmetic is not appropriate.

   (2)  Addition or subtraction of integer constants must not  violate:
        0 <= ddd < 4096.   Addition or subtraction of constants outside
        that range is not allowed by @-operator arithmetic.

   (3)  Not  more  than  two  integer  registers  may  participate   in
        @-operator  arithmetic, and only integer registers are allowed.
        Addition is the only allowed operation.  Subtraction cannot  be
        done using @-operator arithmetic.

   (4)  Address arithmetic never affects the machine 'condition code'.

Now at first glance, these restrictions seem to eliminate a lot of cases; but in actual fact, much of the arithmetic involving integer registers can be done with @-operator arithmetic. Consider a simple loop through an array of integer cells where an index register is incremented by 4 each time in the loop; such as, R1 := @B1(4). This is just one of many possible examples.

Interestingly, integer register assignments of constants from 0 through 4095 are done with a 'load address' instruction in which the X and B-fields are 0 and the D-field is the constant. Since neither a 'load address' nor a 'load' instruction affects the machine's condition code, then using a 'load address' instruction is more efficient than using a straight 'load' instruction which refers to the constant stored in the literal pool. A memory reference is saved as well as the space needed for the constant!

Once the assignment statement has been started, it may be continued by as many occurrences of 'op term' as desired. The allowed operators are essentially the same as for the integer value synonym declarations described in section 4.7. In fact, you will notice a close similarity between the integer register assignment statement and the integer value synonym declaration.

     (a)  Arithmetic (see sections 1.3 and 1.8)
          +      addition
          -      subtraction
          *      multiplication
          /      division
          ++     logical addition (unsigned)
          --     logical subtraction (unsigned)

     (b)  Logical (see section 1.6)
          AND    conjunction
          OR     inclusive injunction
          XOR    exclusive injunction

     (c)  Shifting (see section 1.7)
          SHLL   shift left logically
          SHLA   shift left arithmetically
          SHRL   shift right logically
          SHRA   shift right arithmetically

     (d)  Reverse Assignment
          =:     reverse assignment

Each operator specifies an action to be taken with two operands. One of these operands is ALWAYS the assigned register and the other is the 'term' following the operator. With the assignment (:=) and reverse assignment (=:) operators, the action is that of moving a quantity from one operand to the other. Reverse assignment is the only operator which acts upon the 'term' using the assigned register. For any other operator, the 'term' which follows the operator acts upon the assigned register. Here are some examples.

(1)   R1 := S1 =: X

Load register R1 with the value contained in the short integer cell S1, extending the sign of the value. Then, store the value in R1 into the integer cell X. Assuming S1 contained _3 to start with, the results in hexadecimal would look like this:

                ----
      S1 =     |FFFD|
                ----
            --------
      R1 = |FFFFFFFD|
            --------
            --------
      X  = |FFFFFFFD|
            --------

The assembly language equivalents would be:

      LH    1,S1    load register 1 from S1
      ST    1,X     store register 1 in X

The assembly language equivalents have been introduced for two main reasons. First, the machine instructions described in IBM's Principles of Operation manuals are shown in assembly language form, so the assembly language equivalents here act as a link into those manuals. Second, the equivalences aid assembly language programmers in making the transition to PL360 by pointing out what instructions in assembly language correspond to the machine instructions generated by the PL360 compiler. However, knowledge of assembly language is not required to learn PL360. Appendix D of this text contains the relations between machine instructions, assembly language, and PL360 constructs.

(2)   R3 := NEG 7 =: S2

Load register R3 with the integer constant 7; negate the contents of R3 thus obtaining negative 7; store the lower half of the contents of R3 into the short integer cell S2. The results would look like this:

            --------
      R3 = |FFFFFFF9|
            --------
                ----
      S2 =     |FFF9|
                ----

The assembly language equivalents would be:

      LA    3,7     place 7 in register 3
      LCR   3,3     complement register 3
      STH   3,S2    store register 3 in S2
(3)   R4 := R4 + X =: X =: R2

Add to R4 the current value in X, store the result back into X, and also place the result in R2. When we are finished, R4, R2, and X will all contain the same value.

The assembly language equivalents would be:

      A     4,X     add X to register 4
      ST    4,X     store register 4 back into X
      LR    2,4     load register R2 from R4

5.1.1.1  Reverse Assignment

We've already looked at the possible terms which may follow the assignment operator, including the use of monadic operators. The term which follows the reverse assignment operator may be only an integer register, integer cell, or short integer cell. Constants are not allowed to follow reverse assignment operators because the contents of a register shouldn't be stored on top of a constant: it wouldn't be constant any more!

5.1.1.2  Operator Restrictions and Precedence

Now let's look at the other operators and the restrictions regarding their use. First, it is important to understand that each 'op term' generates only one machine instruction and that the order in which the instructions are generated is the same as the order in which the 'op terms' appear reading the assignment statement from left to right. There is no operator precedence! Second, each operator acts upon the assigned register using the term following the operator to produce a result which replaces the contents of the assigned register. Third, most operators are restricted to using only certain terms. Consider the following example:

       R2 := NEG 7 * S1 + X SHRA 4 OR R3

This one statement could be broken down into a series of statements each of which represents a single instruction. Each PL360 statement below is followed by a comment which indicates the assembly language equivalent. Remember, R1 := R1 generates no instruction; it only satisfies the requirement of being an assignment statement.

       R2 := 7;               |  LA    2,7  |
       R2 := NEG R2;          |  LCR   2,2  |
       R2 := R2 * S1;         |  MH    2,S1 |
       R2 := R2 + X;          |  A     2,X  |
       R2 := R2 SHRA 4;       |  SRA   2,4  |
       R2 := R2 OR R3;        |  OR    2,3  |

5.1.1.3  Shift Operations

The shift operators allow only an integer constant or an integer register term. When a register is specified, the lower byte of the register contains the shift amount; the remainder of the register contents is ignored. A logical shift moves the entire contents of the assigned register to the left or right the shift amount. Binary zeros are inserted into the register at the left end for right shifts, into the right end for left shifts, and the bits at the opposite end are discarded.

                    32-bit value
                      --------
            ..000000 |FDB97531| 000000..
                      --------
              SHLL 4    (shift value left 4 bits, 1 hex digit)
                       --------
            ..000000F |DB975310| 00000..
                       --------
                        Result

The picture above represents what happens. The value is shifted to the left by moving the box which surrounds the value to the right. The box would move left for values which are right-shifted.

Arithmetic shifts operate in essentially the same way as logical shifts, except that on left shifts the computer continuously checks that the sign bit position always contains the original sign bit. If a bit that is different from the original sign bit occupies the sign bit position, an OVERFLOW condition occurs. This means that the resulting value has lost significant bits of the original value. On an arithmetic right shift, the original sign bit is propagated into the left end as bits are dropped from the right end. Therefore, the resulting value retains the sign of the original value. Considering the shift amount as specifying a power of 2, then left shifting is equivalent to multiplying by a power of 2, and right shifting is equivalent to dividing by a power of 2 dropping any remainder. However, right shifting a negative value can never result in zero, so in that sense, it's not quite the same as integer division.

                    32-bit value
                      --------
            ..FFFFFF |FDB97531| 000000..
                      --------
              SHRA 8    (shift value right eight bits, two hex digits)
                    --------
            ..FFFF |FFFDB975| 31000000..
                    --------
                     Result

Arithmetic shifts, and almost all of the other operators to be discussed, except multiplication and division, affect what is called the machine's 'condition code'. There are four possible conditions for any of these operations. One is the OVERFLOW condition, a case where the resulting value has changed sign improperly. Such a condition is possible with arithmetic left shift (SHLA), addition (+), and subtraction (-). The four possible conditions are:

          Arithmetic     Comparison                 Logical result
          ----------     ----------                 --------------

     (0)  result = 0     operands are equal         zero, no carry
     (1)  result < 0     first operand is low       non-zero, no carry
     (2)  result > 0     first operand is high      zero, carry
     (3)  OVERFLOW                                  non-zero, carry

One of the four arithmetic conditions will occur with the monadic operators, but OVERFLOW is only possible when the original value is the largest possible negative number (#80000000) and either NEG or ABS is used in an attempt to make it positive. (See Appendix A for a discussion of two's complement arithmetic.)

These conditions are covered in greater detail in Chapter 6, Branching, Testing, and Loops. For now it is only important to note that almost every operation affects the 'condition code'.

Let's get back to the integer register assignment statement operators again and look at the rest of them. All the logical operations and division (AND, OR, XOR, ++, --, and / ) are restricted to integer terms: registers, cells, and constants. Short integer cells and short integer constants are not allowed with these operators. Addition, subtraction, and multiplication (+, -, and * ) may use any of the five possible terms: integer registers, cells, or constants, and short integer cells and constants.

5.1.1.4  Logical Addition and Subtraction

Logical addition and subtraction produce the same result as do normal addition and subtraction, but the 'condition code' has a different set of meanings for logical arithmetic. They indicate whether the result is zero or non-zero, and whether or not a CARRY has occurred.

5.1.1.5  Logical Operations

The logical operations of AND, OR, and XOR result only in conditions 0 (meaning zero result), and 1 (meaning non-zero result). Logical OR is typically used to force bits in the assigned register to ones when they may be either zeros or ones originally. Logical AND is typically used to force bits in the assigned register to zeros when they may be either zeros or ones originally. ANDing with a negative power of 2 (such as _2, _4, _8, etc.) will round down to the nearest whole multiple of that power of 2. Thus, the statement R1 := R1 AND _4; is equivalent to R1 := R1 SHRL 2 SHLL 2; leaving R1 with a value that is a whole multiple of 4. The logical XOR operation inverts bits in the assigned register wherever there are bits that are ones in the 'term'. Thus R1 := R1 XOR _1 would invert all bits in R1 effecting a one's complement operation (see Appendix A). Logic tables are given in section 1.6 describing these bit operations.

5.1.1.6  Multiplication and Division

Multiplication and division have a special set of rules regarding their use. Multiplication of two numbers can create a product which has a total number of digits equal to the sums of the digits in the multiplier and multiplicand. Therefore, when we multiply the contents of an integer register (32 bits) by a short integer term (16 bits), the product (48 bits) would not fit in the integer register. But if the upper 17 bits of the product are all sign bits, then the top 16 bits can be discarded leaving a 32-bit result with proper sign. For example,

       R2 := R2 * 10S       |  MH   2,=H'10'  |

Assuming R2 contains the value 100 decimal, then

      Multiplicand = R2   =  00000064  (100 in hexadecimal)
        Multiplier = 10S  =      000A  (10 in hexadecimal)
           Product =     0000000003E8  (1000 in hexadecimal)
      Result in R2 =         000003E8  (1000 in hexadecimal)

The computer assumes that for short integer multiplication the upper 16 bits of the product can always be discarded, even if this assumption is incorrect! But typically short integer multiplication is done with values that will not yield a product greater than 31 significant bits. Notice in the example that the product has only three significant digits, the sum of the number of significant digits in the multiplier and multiplicand.

Multiplication of two integer quantities would result in a 64-bit product. In this case, the designers decided not to discard anything. Instead, the product is placed into two registers as though they were just one big register. These two registers are called an even-odd pair because the upper digits of the product (and sign bit) are placed in an even-numbered register (R0, R2, R4, etc.) and the lower digits are placed in the odd-numbered register next in sequence to the even-numbered register (R1, R3, R5, etc.). Also, and this is very important, the multiplicand must be in the odd-numbered register at the start of the operation, and that register determines the even-odd pair. Thus,

       R1 := R1 * R4        |  MR   0,4  |

       Multiplicand =    R1
         Multiplier =    R4
       Product = R0,R1  (sign at top of R0)

Of course, the product could be such that the even-numbered register contains only sign bits (no significant digits) and the odd-numbered register contains the significant product with proper sign. Still, both registers of the even-odd pair are affected by integer multiplication. You will notice that the assembly language programmer must specify the even-numbered register (0) while the PL360 programmer specifies the odd-numbered register (R1). Specifying the odd-numbered register is more meaningful since the multiplicand must be in that register at the start of an integer multiplication. Also, the significant portion of the product will be in the odd-numbered register after the multiplication if the product is not excessively large.

Division operates in a manner just the opposite of multiplication. The dividend must be in an even-odd register pair specified by an odd-numbered assignment register. The divisor is 'term' which must be an integer term. When the division is completed, the computer places an integer quotient in the odd-numbered register, and an integer remainder in the even-numbered register. Now that may seem simple enough, but there is a problem with integer division which is quite unique. It is possible that the divisor could be sufficiently small, and the dividend sufficiently large, such that the quotient would exceed the capacity of the odd-numbered register meant to receive it. Take for example the division of 50 billion by 10. The result should be 5 billion, but a single integer register cannot contain a value greater than 2,147,483,647 (slightly more than 2 billion). Under such circumstances, a FIXED DIVIDE interrupt occurs and the dividend is left unchanged. Of course, zero is the smallest possible divisor, so division by zero always results in a FIXED DIVIDE interrupt. As a general rule, using the absolute values of both dividend and divisor, a FIXED DIVIDE interrupt will occur whenever the divisor is less than or equal to twice the portion of the dividend which is in the even-numbered register.

Now let's look at a normal division example. Assume that X contains the decimal value 'negative eleven' (_11).

       R7 := 3 * X / 5 =: X   |  replace X by three-fifths of X  |

The assembly language equivalents would be:

       LA    7,3      place 3 in register 7
       M     6,X      multiply register 7 by X (product in 6,7)
       D     6,=F'5'  divide product by 5
       ST    7,X      store quotient in register 7 back into X

This example points out a number of things and poses some questions. First, notice that the assembly language programmer must specify the even-numbered register (as in multiplication), while in PL360, the assignment register must be odd-numbered. Second, you can see that we obtained the dividend in the even-odd register pair by using integer multiplication before the division. If the multiplication had not been necessary, we could have obtained the proper dividend as follows:

       R6 := X =: R7 SHRA 31;  R7 := R7 / 5 =: X;  | one-fifth X |

In the first statement, R6 and R7 are both assigned the value in X, and then the assigned register, R6, is arithmetically right-shifted till it contains only sign bits of X. The second statement then specifies division of R6,R7 by the integer value 5 with the quotient to be stored back into X. If X contained only positive values, the following statements would accomplish the division:

       R6 := R6-R6;  R7 := X / 5 =: X;  | one-fifth X (positive) |

Let's look at the original example (three-fifths X) and consider the results. What are the quotient and the remainder? First of all, the product of positive three times negative eleven is negative thirty-three (3 * _11 = _33). The familiar rules of sign prevail: multiplication of a positive and a negative results in a negative product, multiplication of two positives or two negatives results in a positive product. For the division, we have a negative dividend and positive divisor. Again, the standard rules of sign prevail for the quotient, which turns out to be negative six (_6). The quotient is the nearest whole integer which when multiplied by the divisor would come closest to the original dividend. The remainder is what's left over, in this case, negative three (_3). The sign of the remainder is always the same as the sign of the original dividend, except when the remainder is zero. There is no 'negative zero', so any zero result is simply zero, regardless of sign convention. That's true of products and quotients as well as remainders. Thus, when the (absolute) value of the dividend is smaller than the (absolute) value of the divisor, integer division will yield a zero quotient with a remainder equal to the original dividend (_1/2 = 0 with _1 remainder).

5.1.1.7  Integer Register Usage

What we've covered so far in this section could be called the 'mechanics' of integer register assignment statements; i.e., each operation and what it does. What we shall cover now is 'how' the integer register assignment statements are typically used and how the operations may be combined to obtain a desired result.

There are three basic uses of integer registers:

     (1)  as base registers of data areas
     (2)  as index registers to subscript through arrays
     (3)  as computational scratch pads

When integer registers are used as base registers, their content is rarely changed. Once a data area has been established, it's not likely to move. Of course, a DUMMY BASE area may be considered as an overlay to data in different locations, and as such the base address may be computationally moved about with integer register assignment statements. Take the telephone book entries example in section 4.1.1 as a sample case. Each entry is 72 bytes long (32+32+8) based on R7. If entries were laid out one after the other in core, then R7 := @B7(72) would increment the base register ahead from one entry to the next.

When integer registers are used as index registers, computations are done to move the index from one entry to another while keeping the base register unchanged. Let's say we wish to do in PL360 what would be required to satisfy the following FORTRAN declaration and statement:

       INTEGER  X,  STACK(64,3)
       STACK(I,J) = X

Assume that I and J are integers which, in PL360, can be maintained in integer registers called I and J. Also assume that I contains any integer value from 1 through 64, and that J contains any integer value from 1 through 3.

The ARRAY STAKL INTEGER STACK declaration in section 4.8 satisfies the FORTRAN STACK(64,3) declaration; and X is also satisfactorily declared. Let's assume that the array is to be laid out by columns; that is, the 64 row elements of column 1, followed by the 64 row elements of column 2, and so forth. In PL360, the general formula for computing the 'index' of a 2-dimensional array is:

       index = ((J-1)*rows+(I-1))*size

where 'rows' is the number of row elements in each column, which is 64 in this case; and 'size' is the number of bytes taken by each element, which is 4 in this case because we are dealing with integer cells each of which takes four bytes. The general formula becomes:

       index = ((J-1)*64+(I-1))*4
or     index = (J*64+I-65)*4
or     index = (J*64+I)*4-260

We can now satisfy the FORTRAN statement by writing the following PL360 integer register assignment statements:

       TEMP := J SHLA 6 + I SHLA 2 - 260S;
       K := X =: STACK(TEMP);

We have used integer registers TEMP and K as scratch pads. The index computation in TEMP can be broken down into a series of assignments to make it easier to understand:

       TEMP := J;               | TEMP = J  |   | LR   TEMP,J       |
       TEMP := TEMP SHLA 6;     | times 64  |   | SLA  TEMP,6       |
       TEMP := TEMP + I;        | plus  I   |   | AR   TEMP,I       |
       TEMP := TEMP SHLA 2;     | times 4   |   | SLA  TEMP,2       |
       TEMP := TEMP - 260S;     | minus 260 |   | SH   TEMP,=H'260' |

Now if the array had been declared as STACK(3,64) and we still planned to reference STACK(I,J) with I ranging from 1 through 3 and J ranging from 1 through 64, then the formula would become:

       index = ((J-1)*3+(I-1))*4
or     index = (J*3+I-4)*4
or     index = (J*3+I)*4-16

and we could write:

       TEMP := J * 3S + I SHLA 2 - 16S;
       K := X =: STACK(TEMP);

But wait! There are two changes we can make to simplify matters. First of all, we can move the subtraction of 16 from the TEMP computation into the subscripted reference to STACK. Since the original displacement of STACK is large enough to permit a reduction by 16 and still satisfy 0 <= ddd < 4096, we could write:

       TEMP := J * 3S + I SHLA 2;
       K := X =: STACK(TEMP-16);

We had to subtract 260 in the TEMP computation of our first example because STACK's original displacement is only 256 (#100).

The second improvement we can make is the elimination of the 'multiply by 3S' (short integer multiply). We can write:

       TEMP := J + TEMP + J + I SHLA 2;
       K := X =: STACK(TEMP-16);

We've substituted two add operations for a single multiply operation taking the same number of instruction bytes; but we've eliminated a constant in core (3S), a core reference for that constant, and we've gained execution speed because two adds are faster than one multiply!

Here are some examples of where multiply operations can be eliminated by using other instructions:

       TEMP := J + TEMP;         | TEMP := J * 2S; |
       TEMP := J + TEMP + J;     | TEMP := J * 3S; |
       TEMP := J SHLA 2;         | TEMP := J * 4S; |
       TEMP := J SHLA 2 + J;     | TEMP := J * 5S; |
       TEMP := J SHLA 3 - J;     | TEMP := J * 7S; |
       TEMP := J SHLA 3;         | TEMP := J * 8S; |
       TEMP := J SHLA 3 + J;     | TEMP := J * 9S; |

The last three examples above can be generalized to handle multiply operations by one less than a power of 2, by a power of 2, and by one more than a power of 2, such as 63, 64, and 65.

In both STACK(I,J) examples, we assumed that I and J each started with a low bound value of 1; but if we started both I and J at 0, then we could have eliminated the subtracts of 260 and 16 from our computations. In other words, we could assume that the FORTRAN reference to STACK(0,0) is equivalent to the PL360 reference to STACK(0). It is a general practice when referencing arrays in PL360 to start at subscript (0). Also, as you can see from our examples, a multi-subscripted array must be referenced by a single index register in PL360. There is no reason, of course, why a multi-dimensional array has to be handled by multiple subscripts. In our STACK(3,64) case, if we were interested in doing something like:

       STACK(2,J) = STACK(0,J) + STACK(1,J)

for all J beginning at 0, we could write in PL360 something like:

       TEMP := TEMP - TEMP;   |-- initialize TEMP to 0 --|
  LOOP:  |-- branch point for doing all J's --|
       K := STACK(TEMP) + STACK(TEMP+4) =: STACK(TEMP+8);
       TEMP := @MEM(TEMP+12);  |-- bump TEMP ahead 12 bytes --|

At this point we would test TEMP for still being in range. If TEMP still represents a valid reference, we would branch back to LOOP to repeat the computations for the next group. We shall cover testing and branching in Chapter 6, Branching, Testing, and Loops.

Here are some other examples of integer register assignment statements that point out concepts related to this section:

(1)  Rn := Rn XOR _1;    |-- one's complement of Rn --|
(2)  R1 := Ry + Rz SHRA 1 AND _n;    |-- n = power of 2 --|

     R0 := Ry + Rz =: R1 SHRA 31;    |-- n ~= power of 2 --|
     R1 := R1 / n SHRA 1 * n;

These assignment statements determine the point halfway between the two points defined by Ry and Rz such that this new point is always a multiple of a fixed interval n. This technique is commonly used in 'binary' search processes against a list of fixed-length items.

(3)  R1 := Rn * p/q;     |-- where 2p < q --|

If the original value in Rn were positive and we were not interested in the remainder resulting from the division by q, we could accomplish the same result as p/q by just doing an integer multiply! Since integer multiplication results in a double-length answer, we could assume that the multiplier is a fraction with its radix point assumed before the value rather than following the value. Thus, we could write:

     R1 := Rn * m;       |-- answer in R0 --|

where m is an integer value assumed to be a fraction. For example, if p=1 and q=3 then m=#55555556, an assumed fraction. We could compute any such assumed fraction once by statements such as:

     R0 := p;  R1 := R1-R1 / q;    |-- R1 contains m --|

The above computation of m is exact if the remainder in R0 following the division is zero; otherwise the value in R1 must be increased by one (R1 := R1 + 1) to obtain a more precise value of m.

Once we have obtained the value m by a computation such as the one shown above, we can use m rather than p/q remembering to take the answer from the even-numbered register rather than from the odd-numbered register. Thus, we could rewrite the statement shown in example 2 above for the case where n ~= power of 2:

     R1 := Ry + Rz * m;  R1 := R0 SHRA 1 * n;

(4) Mixed numbers can be handled with integer register assignment statements providing a fixed number of fractional digits is to be maintained. The best example of this is the processing of dollars and cents values. A quantity such as $1.55 could be processed as though it were 155 cents. Additions and subtractions of such numbers, as well as multiplications and divisions by pure integers, always result in cents values. The location of the decimal point is assumed in all calculations. The final presentation of any results would include a leading dollar sign and decimal point in proper positions, but that's nothing more than a pictorial problem.

5.1.2  Floating-Point Register Assignments

Floating-point register assignment statements are much more restrictive than integer register assignment statements. Shifting or logical operations are not allowed and we can't use @-operators or "string" constants either. The only operations possible are assignment, reverse assignment, arithmetic, and the monadic operators, NEG, ABS, and NEG ABS.

     (a)  Arithmetic (see section 1.8)
          +      addition
          -      subtraction
          *      multiplication
          /      division
          ++     addition (unnormalized)
          --     subtraction (unnormalized)
     (b)  Reverse Assignment
          =:     reverse assignment

The form of a floating-point register assignment statement is the same as for integer register assignments, i.e.,

register := term1 op term2 ... op termn

register := term

where 'register' is either a REAL or LONG REAL register identifier, and the expression consists of one or more 'terms' separated from each other by the operators shown above. The 'terms' are restricted to floating-point registers, cells, or constants of compatible type to the assigned register. (LONG REAL registers may use both REAL and LONG REAL terms in an analogous way to INTEGER registers using both INTEGER and SHORT INTEGER terms.)

There are, physically, only four floating-point registers available in the hardware. They are the LONG REAL registers designated by the standard identifiers: F01, F23, F45, and F67. The REAL registers, designated by F0, F2, F4, and F6, are simply the top half of the corresponding LONG REAL registers. Therefore, any operations which affect F01 also affect F0, and any operations which affect F2 also affect the top half of F23, just to cite two examples. The only differences between using the LONG REAL registers as opposed to the REAL registers is the speed with which operations can be done (REAL's are faster than LONG REAL's), the accuracy of the results (LONG REAL's are more accurate than REAL's), and the amount of cell space needed to store floating-point registers (LONG REAL registers require LONG REAL cells of eight bytes in length, and REAL registers require REAL cells of four bytes in length). (See Appendix B.)

REAL and LONG REAL cells must always be referenced using integer registers, never floating-point registers (see section 4.4). BASE and/or INDEX registers in any subscripted cell reference are always limited to integer registers regardless of cell type.

The advantages of floating-point arithmetic are:

     (a)  Mixed numbers (integers and/or  fractions)  can  be  operated
          upon  without  having  to worry about decimal points.  We can
          add 0.125 to 10.5 and get 10.625 without any problems.
     (b)  Division doesn't have a remainder, only a quotient.  So if we
          divide 1.0 by 2.0 we would get 0.5 as expected.
     (c)  Very large and very small values can be processed.

Any work with geometric or trigonometric problems usually requires floating-point arithmetic. Imagine trying to compute the area of a circle knowing its radius using only integer arithmetic!

There are some disadvantages to using floating-point arithmetic. For one thing, floating-point is not always accurate. Integers are integers, pure and simple; but floating-point values may be inaccurate. One-third (1.0/3.0) is not exact in floating-point form; 0.333333 is not one-third, it's 333333/1000000. Since the computer's representation of a floating-point value is limited to only a specific number of digits, repeating fractions can't be exactly represented. Even one-tenth isn't exact when represented in hexadecimal!

Another disadvantage is the limited number of registers available and the limited number of instructions that can operate upon them. Therefore, floating-point operations are seldom encountered in systems programming, information retrieval, or similar programming.

One of the common problems encountered when working with floating- point is the conversion between floating-point and integer values. For example, given a positive integer value in R1, how do we get the corresponding floating-point value in F01? To demonstrate the method, let's use the cells LRB and IB defined in section 4.8 and write the following assignment statements:

          R1 := R1 =: IB;          |-- R1 >= 0 --|
          F01 := F01 - F01 + LRB;

Let's see what happened.  First, we'll draw a diagram of LRB and IB and
look at the relationship between them.
          -------------------
          |4E000000 00000000|
          -------------------
          |        |<- IB ->|       IB overlays the last
          |<----- LRB ----->|       four bytes of LRB

Notice that LRB is initialized to a value which in floating-point is a zero fraction with an exponent equivalent to 16'14 (0E). The exponent indicates that the radix point of the fraction is to be placed 14 hexadecimal digits from the start of the fraction. That would place the radix point at the far right end of the fraction. So instead of 0.00000000000000 x 16'14 we would have 00000000000000. x 16'0, the integer value of zero! Storing R1 into IB simply replaces this zero with the integer value in R1. Then, zeroing F01 and adding LRB forces the computer to normalize the answer. We end up with a normalized floating-point value in F01 which is equivalent to the integer value in R1. Try it with R1 = 3 and you'll get F01 = #4130000000000000L which is equivalent to F01 = 3L.

We can reverse the process provided we don't mind losing any fractional portion of the floating-point value (we only want the integer portion), and the floating-point value is positive and less than the largest possible positive integer that will fit in an integer register (less than 2,147,483,648). Using LRA and IA, we write the statements:

       F01 := F01 ++ #4E00000000000000L =: LRA;
       R1 := IA;

The ++ operator indicates unnormalized addition which could be described in the following way: "The computer compares the exponents of the two values being added. The fraction of the value with the smaller exponent is right-shifted as many hexadecimal digits as the difference between the the exponents. This lines up the radix points of the two fractions according to the larger exponent. Now the fractions can be added together and the result given the larger exponent." That's it! Normalized addition works the same way, but with one more step: "The resulting fraction is left- or right-shifted until the most significant hexadecimal digit occupies the first fractional digit position. Then the exponent is reduced (or increased) accordingly."

Multiplication and division also produce a normalized result. The process is similar to our own paper-and-pencil method for exponential notation. The fractions are multiplied together or properly divided, one into the other. Then the true exponents are added or subtracted to yield a new exponent. The resulting fraction and exponent are reassembled and normalized to create the final result. Thus, 0.5'4 * 0.7'2 yields 0.35'6.

Multiplication and division do not affect the machine's condition code. Assignment (:=) and reverse assignment (=:) also have no effect on the condition code. Addition, subtraction, and comparison (next chapter), and most other operations affect condition codes.

Since all of the arithmetic operations involve shifting to align or normalize the fraction, it is possible for the final exponent to fall outside the range of allowable exponents. This is especially true of multiplication and division since the exponents are being added or subtracted independent of normalization. Whenever the exponent becomes too large or too small, an EXPONENT OVERFLOW or UNDERFLOW occurs usually resulting in a program interrupt. We shall not cover the details of such circumstances in this text; instead, see IBM's Principles of Operation manuals.

5.2  Cell Assignments

Cell assignment statements provide the means for modifying cell content. As with register assignment statements, the form of a cell assignment statement is recognized by:

cell := term1 op term2 ... op termn

cell := term

where 'cell' is a cell reference; the 'terms' are a mixture of constants, "strings", or other cell references; and the 'op's' are logical operators. The first 'term' may also be a register identifier.

5.2.1  Register-to-Storage

We have already encountered cell assignment with registers! The reverse assignment operator (=:) introduced in section 5.1 actually provided cell assignment. Thus,

       R1 := R1 =: IB;

could be written as the cell assignment statement:

       IB := R1;

Exactly the same machine instruction is generated in both cases, equivalent to ST 1,IB in assembly language. The only difference is that now the cell is the assigned quantity. In the register assignment statement we could have continued the expression with adds, shifts, logical operations, etc. Such operations would all affect the assigned register, not the reverse assigned cell. But when the assigned item is a cell, the expression CANNOT be continued with arithmetic or shift operations. There are no computer instructions corresponding to such operations with cells.

So the basic cell assignment statement is:

cell := register

The cell may be fully indexed; that is, an index register may be specified by a non-zero X-field component in the cell reference. Also, the cell and register types must be compatible. The contents of an INTEGER register may be stored into an INTEGER cell, or the lower half of an INTEGER register may be stored into a SHORT INTEGER cell (whatever is in the upper half of the register is ignored). Similarly, the contents of a LONG REAL register may be stored into a LONG REAL cell; and the upper half of a LONG REAL register or all of a REAL register may be stored into a REAL cell.

The following simple example demonstrates cell assignment.

       R1 := X;           |-- fetch X --|
       R0 := R1 SHRA 31;  |-- sign extend --|
       R1 := R1 / 5;      |-- divide by 5 --|
       Y := R0;           |-- store remainder into Y --|

5.2.2  Storage-to-Storage

The storage-to-storage cell assignment facility is built around a small set of machine instructions: MOVE, AND, OR, and XOR character and immediate instructions (MVC, NC, OC, XC, MVI, NI, OI, XI assembly mnemonics). These instructions do not allow an X-field for cell references. So NONE of the cell references in the cell assignment statements described in this section are allowed to specify an index register. The only registers that may occur in such references are the base registers associated with the referenced quantities. The general form of a storage-to-storage cell assignment statement is:

cell := term1 op term2 ... op termn

cell := term

where 'cell' is a unindexed cell reference; the 'terms' are a mixture of constants, "strings", or other unindexed cell references; and the 'op's' are logical operators. The first 'term' may also be a register identifier. Thus,

       LINE := "APPLES"
       Z := 50
       X := X AND Y
       B2 := R1 AND B3

When the term is a variable-length string, the number of bytes affected is determined by the string length regardless of cell type. So the first assignment statement shown above reads: "Beginning with the 0th byte of LINE, assign the six consecutive characters APPLES". If the statement had been LINE(17) := "APPLES", the only difference would have been the starting point, the 17th byte of LINE for six consecutive characters. No check is made for overrunning the space reserved for the referenced cell: that's your responsibility!

When the term is a constant, the basic length of the cell must be less than or equal to the constant length (see section 4.3). Therefore, LONG REAL cells must be assigned LONG REAL constants. REAL or INTEGER cells may be assigned REAL or INTEGER constants. SHORT INTEGER and BYTE cells may be assigned corresponding constants, but SHORT INTEGER cells may also be assigned the lower half of INTEGER constants. Likewise, BYTE cells may be assigned the lowest byte of SHORT INTEGER or INTEGER constants. The only restriction in such cases is that the unused portion of a constant contain only like sign bits (all zeros or ones). Since EQUATE declarations define INTEGER constants, these quantities may be assigned to REAL, INTEGER, SHORT INTEGER, and BYTE cells.

When the term is a cell, the cell types must agree in basic length (not ARRAY length). That is, BYTE with BYTE, SHORT INTEGER with SHORT INTEGER, INTEGER with INTEGER or REAL, REAL with REAL or INTEGER, and LONG REAL with LONG REAL.

Because the machine instructions generated operate on a byte-by- byte basis, cell alignment is not checked. This makes it possible for us to overlay bytes by writing: X(1) := X which would propagate the first byte of X to X(1), then X(1) to X(2), and so on. The basic cell length or string length determines the number of bytes affected.

These machine instructions also allow from one to 256 bytes to be processed. To allow for this variable-length ability, cell assignment statements may specify lengths within this range. The assigned cell must include a subscript in one of the following forms:

       identifier(Rb+k/length)  or  identifier(Rb-k/length)
       identifier(Rb/length)
       identifier(k/length)

'k' is as described in section 4.4; and 'length' is either a single integer value or an expression of added and/or subtracted integer values. The final length must satisfy: 1 <= length <= 256. The slash (/) character does not indicate division. It acts as a separator between the Base-Displacement and length portions of the subscript. Notice that if we wanted to specify just a length for a particular cell reference, we would have to supply a 'k' of zero. Thus, X(0/4) indicates a 4-byte length beginning with the 0th byte of X, even if X were not an INTEGER or REAL cell.

When a length is specified, cell types need not agree in basic length! If fact, cell types become meaningless and we can mix types if necessary. With constants, the specified length may not exceed the basic constant length. With "strings", the shorter of the specified length or string length determines the number of bytes affected. If the first term is a register, the register is stored according to the rules for register-to-storage regardless of length specification.

Here are some examples:

(1)    B6(0/4) := "Sample";    |-- B6 := "Samp"      --|
       B4(4/9) := "Sample";    |-- B4(4) := "Sample" --|
       LINE(0/2) := 30;        |-- LINE(0/2) := 30S  --|
(2)  Blank out the first 120 bytes of LINE.

       LINE := " ";  |-- Blank the first byte of LINE --|
       LINE(1/119) := LINE;  |-- Propagate the blank  --|

(3) Copy the lower LEN bytes of the integer cell based on R2 to the lower LEN bytes of the integer cell based on R5. LEN is assumed to vary from one compilation to the next.

       BEGIN  EQUATE LEN SYN 3;  |-- 1 <= LEN <= 4 --|
          B5(4-LEN/LEN) := B2(4-LEN);  |-- B5(1/3) := B2(1); --|
       END;
(4)  Reference an indexed cell using a temporary base register.

       R9 := @X(R2);  |-- R2 is an index register --|
       B9 := Y;       |-- move 4 bytes from Y to X(R2) --|
(5)  Interchange SQUARES bytes of TABLE1 and TABLE2  (no overlap).

       TABLE1(1/SQUARES) := TABLE1(1)  XOR  TABLE2(1);
       TABLE2(1/SQUARES) := TABLE2(1)  XOR  TABLE1(1);
       TABLE1(1/SQUARES) := TABLE1(1)  XOR  TABLE2(1);

5.3  Exercise

Assume that all the standard identifiers are valid in the following problems (B1,B2,...,R0,R1,R2,...,etc.).

Problem 1.

One of the assignment statements below does NOT cause a compiler diagnostic. All the others have an error. Determine which statement is correct. What basic error exists in the others?

(a)  F0 := F0 SHRA 3;
(b)  R1 := B3 AND 4S;
(c)  R4 := R5 * R6;
(d)  B1(4/4) := B1 + R2;
(e)  B3(0/2) := R1 XOR B3(2);
(f)  R6 := R5 + @B2;
(g)  R8 := B3 / B2;
(h)  B1(4) := B2(_4);

Problem 2.

One of the assignment statements below causes a compiler diagnostic. All the others are free of errors. Determine which statement is incorrect and why.

(a)  R0 := R0 SHLL 7;
(b)  R1 := 4S AND B3;
(c)  R5 := R5 * R6;
(d)  F0 := F0 / 8R;
(e)  B3(0/2) := B3(2) XOR R1;
(f)  R6 := @B2(R5);
(g)  R8 := B3 - B2;
(h)  R2 := NEG _2 + _2;

Problem 3.

Starting with: R1 := #1100; R2 := #1010; do the following assignment statements in sequence; and show what the result is after each assignment (see the logic tables in section 1.6).

     R1 := R1 XOR R2;   |start with R1 and R2 values given|
     R2 := R2 XOR R1;   |use R1 from preceding statement|
     R1 := R1 XOR R2;   |use R1 and R2 from preceding statements|

Problem 4.

This problem refers to the complete program given at the end of section 4.9, problem 2 (page 41).

(a) Rewrite the statement IJ := I SHLL 4 + J SHLL 2; appearing on lines |11| and |16| using multiplication instead of shifts.

(b) Compute the assigned value for: IJ := I SHLL 4 + J SHLL 2; when: I=2 and J=4, I=3 and J=3, I=4 and J=1 .

(c) What positive non-zero values for I and J generate the value IJ=68 such that XX(IJ-68) on lines |12| and |17| references XX(0) ?

6  Branching, Testing, and Loops

Very few programs, if any, are written as just a straight line sequence of computations. One might as well do the computations on a hand calculator if no decision-making is required! So the concept of branching is very important; and PL360 provides very powerful branching facilities. Most of the statements look very much like normal English sentences, which makes them very readable. This chapter covers almost all of these branching and decision-making statements.

6.1  GOTO Statements

GOTO statements are unconditional branching statements. Their form is simply:

GOTO label

where 'label' is an identifier naming some point in a block. A label definition is simply an identifier followed by a colon (label:). Such 'labels' may occur wherever a 'statement' may occur within a block. GOTO statements constitute the ONLY means for referencing labels. Therefore, label names may be chosen freely, independent of other user defined identifiers. In fact, label X and cell X may coexist! The only restriction on label names is that they be unique within any block. That does not imply that they cannot be redefined in a separate or nested block--they may be redefined. For example:

       BEGIN
        X: |-- Label #1 --|
          BEGIN
             GOTO X;  |-- Goto #3 --|
           Y: |-- Label #2 --|
             GOTO Z;  |-- Goto #4 --|
           X: |-- Label #3 --|
             GOTO Y;  |-- Goto #2 --|
          END;
        Z: |-- Label #4 --|
          GOTO X;  |-- Goto #1 --|
       END

This example not only illustrates label redefinition (X in this case); but also illustrates the 'scope' of GOTO statements.

The first GOTO X branches to the X label in the block containing that GOTO statement. That's also true of the last GOTO X which branches to the X label in its same block. If the first X label had been missing, the last GOTO X would be invalid. A GOTO statement may not branch into a block! The GOTO Z statement branches out of the block containing that GOTO statement to a label defined within a containing block.

As a general rule, a label may be referenced by a GOTO statement occurring in the same block, or by a GOTO statement in any contained blocks if the label isn't redefined in those blocks.

The following example further illustrates the rule:

       BEGIN
          BEGIN
             BEGIN
              X: |-- Label #1 --|
             END;
             GOTO X;  |-- Goto #3 --|
          END;
          BEGIN
           X: |-- Label #2 --|
          END;
        X: |-- Label #3 --|
       END

There's an exception to the previously stated rule. A GOTO cannot reference a label defined in a different program segment. We shall cover this restriction in Chapter 8, Procedures.

6.2  Conditions

Conditions provide the basic decision-making facilities in PL360. They are only used in IF and WHILE statements, such as:

IF conditions THEN ...

WHILE conditions DO ...

We will cover IF and WHILE statements in detail in sections 6.3 and 6.4; but we'll use the start of IF statements in this section to illustrate how conditions are used.

6.2.1  Simple Conditions

Simple conditions fall into two categories: those which generate only a conditional branch instruction, and those which generate a compare instruction followed by a conditional branch instruction.

Conditions which generate only a branch instruction test the machine's 'condition code' established by some preceding statement. Thus, if we had done: R1 := R1 + R2; we could test the result of the addition because addition affects the machine's condition code. The relationship between the condition code and the testing conditions is as follows.

Within the computer's program status word are two bits reserved for the condition code. So it is possible to have four different condition code values:

  Code     Arithmetic     Comparison                 Logical result
  ----     ----------     ----------                 --------------
  0        result = 0     operands are equal         zero, no carry
  1        result < 0     first operand is low       non-zero, no carry
  2        result > 0     first operand is high      zero, carry
  3        OVERFLOW                                  non-zero, carry

Since there are four different condition codes and we may want to test for one or more of them simultaneously, the branch instructions provide a 4-bit mask field. These bits are numbered left to right: 0, 1, 2, 3; and a bit turned on in the mask indicates testing the corresponding condition code value. It is possible for the mask field to assume values from 0 through 15. A 0 corresponds to no conditions: no branch. A 15 corresponds to all conditions: an unconditional branch just like GOTO. The values in between test for one or more condition code values and branch accordingly. The table below illustrates the relationship between the mask value and the condition code value.

                 Mask Value     Condition Code Value
                 ----------     --------------------
                     8                    0
                     4                    1
                     2                    2
                     1                    3

In PL360, we can specify the mask value directly as an integer value from 0 through 15 (hexadecimal values #0 through #F). We may also preface these integer values by a not symbol (~) to indicate the complementary set of condition codes. This is an awkward way of testing condition codes, to say the least. But by using EQUATE declarations, you can devise meaningful names which indicate the codes being tested. Some of the pre-declared EQUATE identifiers given in section 4.7 do just that!

             Identifier   Value   Condition Code Tested
             ----------   -----   ---------------------
              OVERFLOW      1       3
              ON            1       3
              MIXED         4       1
              OFF           8       0
              CARRY         3       2 or 3

OVERFLOW tests for the arithmetic overflow condition. ON, MIXED, and OFF test for condition codes resulting from a test-under-mask (TM) instruction which we shall cover in Chapter 7, Functions. CARRY tests the result of logical addition and subtraction (++ and --). Preceding any of these pre-declared identifiers by the not symbol (~) tests the complementary set of condition codes. Thus, ~MIXED tests the condition code values: 0, 2, or 3.

We could invent our own names with EQUATE declarations for any other conditions, such as: EQUATE EQUAL SYN 8; to test for an equals condition. But that isn't necessary because PL360 provides special relational operators that represent most conditions.

6.2.1.1  Relational Operators

         Symbol   Equivalent Value   Condition Codes Tested
         ------   ----------------   ----------------------
           =             8                 0
           ~=            7              1, 2, or 3
           >             2                 2
           >=           10               0 or 2
           <             4                 1
           <=           12               0 or 1

The relational operators by themselves constitute valid conditions, just as do the integer values (or equates) mentioned earlier. Thus,

       R1 := R1 + R2;  IF = THEN ...
       R1 := R1 + R2;  IF OVERFLOW THEN ...
       Z := X XOR Y;   IF ~= THEN ...

A condition may also be defined as the relationship between two quantities. The general form of such a relationship is:

       item1  relation  item2

where 'relation' is one of the relational operators, and the 'items' may be any of the following pairs:

                      item1           item2
                      -----           -----
                      register        register
                      register        constant
                      register        cell
                      cell            constant
                      cell            cell
                      cell            "string"
                      integer reg.    "string"

The same rules and restrictions described for assignment statements apply to the above pairs as well. Just substitute the relational operator for the assignment operator and you won't go wrong. Here are some examples:

       R1 > 0
       F01 ~= 1.5L
       X <= Y
       TABLE1(1/SQUARES) = TABLE2(1)

All of these relationships generate two instructions. The first compares the two quantities to establish the condition code. The other is a conditional branch instruction which depends on the relation. When item1 is a register, the comparison is a signed compare. That is, a positive value in the register compares 'greater than' any negative value. Thus, R1 > _1 is satisfied by any value in R1 greater than or equal to 0.

When item1 is a cell, the comparison is done logically (unsigned). That's also true of an integer register compared to a "string". Thus, X > _1 can never be satisfied if X is an integer cell because _1 is a value with all bits set, making it the largest possible value. Nothing could be greater than the largest value!

There is another condition which could be called a 'flag' condition. This type of condition is defined as just a bytecell, or as a bytecell preceded by the not symbol (~). Thus,

       IF  bytecell THEN ...
or     IF ~bytecell THEN ...

The contents of the specified bytecell is tested for the value #FF (all bits set). IF bytecell THEN ... is satisfied by a bytecell containing #FF, and is not satisfied by any other value. IF ~bytecell THEN ... is not satisfied by a bytecell containing #FF, and is satisfied by any other value.

These 'flag' conditions are used in testing on/off flag bytes (switches). The EQUATE values TRUE (_1) and FALSE (0) may be used to turn flags on or off. TRUE is equivalent to #FF in a bytecell, and FALSE is equivalent to #00 (different from #FF). So the assignment statements:

       bytecell := TRUE       |   SET bytecell flag |
and    bytecell := FALSE      | RESET bytecell flag |

would SET or RESET the specified bytecell. A 'flag' condition is nothing more than a shorthand for:

       IF bytecell  = TRUE  THEN ...     |  bytecell |
and    IF bytecell ~= TRUE  THEN ...     | ~bytecell |

The compare instructions mentioned earlier vary depending on the items being compared. The list below contains representative conditions for every possible compare instruction that can be generated. Assembly language equivalents are given as comments. Only constants, strings, and pre-declared identifiers are used in these examples; and the relation is 'equals' (=), although any relational operator could be substituted. (Conditional branch instructions are not shown.)

       R1 = R2           |  CR    1,2         |
       F0 = F4           |  CER   0,4         |
       F45 = F67         |  CDR   4,6         |

       R2 = 0            |  LTR   2,2         |
       F6 = 0R           |  LTER  6,6         |
       F23 = 0L          |  LTDR  2,2         |

       R3 = 27           |  C     3,=F'27'    |
       R4 = 12S          |  CH    4,=H'12'    |
       F2 = 3.14         |  CE    2,=E'3.14'  |
       F01 = 1.5L        |  CD    0,=D'1.5'   |
    | Note: constants in the preceding four (4) examples |
    |       may be replaced by cells of equivalent type. |

       R5 = "VALS"       |  CL    5,=C'VALS'  |
       B3 = B7           |  CLC   0(4,3),0(7) |
       B9 = "A"          |  CLI   0(9),C'A'   |

Finally, it should be noted that any condition may be immediately preceded by one or more statements, each terminated by a semicolon. So instead of:

       R1 := R1 + R2;  IF > THEN ...
or     R0 := R0-R0;  R1 := R1 / 5;  IF R0 ~= 0 THEN ...

we could write:

       IF R1 := R1 + R2;  > THEN ...
or     IF R0 := R0-R0;  R1 := R1 / 5;  R0 ~= 0 THEN ...

This facility is particularly useful with WHILE statements, as we shall see later.

6.2.2  Compound Conditions

A compound condition is nothing more than a series of simple conditions joined together by AND's or OR's or any combination of those operators. Parenthesis may be used to group conditions. So compound conditions look like this:

       c1 AND c2 AND c3 ...
or     c1 OR  c2 OR  c3 ...
or     c1 AND c2 OR  c3 ...
or     c1 AND (c2 OR c3) ...

where the c's represent simple conditions, each of which may be preceded by one or more statements.

A compound condition consisting of only AND'ed simple conditions is satisfied if and only if ALL simple conditions are satisfied. Failure of any simple condition is a failure for the entire compound condition. Once a failure is detected, the remaining simple conditions are not tested, and any statements which may have preceded them are not done.

Therefore, IF R1 > 0 AND R2 := B1 + B1(4); ~= THEN ... succeeds if both conditions succeed. However, if the first simple condition fails, the register assignment statement is not done. Clearly, if R1 doesn't contain a valid address, we don't want the assignment statement to be done!

A compound condition consisting of only OR'ed simple conditions is NOT satisfied if and only if ALL simple conditions are NOT satisfied. Success of any simple condition is a success for the entire compound condition. Once a success is detected, the remaining simple conditions are not tested, and any statements which may have preceded them are not done.

As a general rule, the simple conditions most likely to succeed should be written first in an OR'ed compound condition. This will speed up processing because unnecessary tests are frequently bypassed. Similarly, the simple conditions most likely to fail (or which must succeed before another condition can be tested) should be written first in an AND'ed compound condition.

When logical operators are mixed, the expression is evaluated strictly left to right, unless parenthesis are used to group subexpressions. For example:

       c1 OR c2 AND c3

This expression is TRUE if either c1 or c2 is TRUE and c3 is TRUE. You could rewrite this expression in either of the following ways:

       c3 AND (c2 OR c1)
       (c1 AND c3) OR (c2 AND c3)

6.2.3  Special Conditions

The DEC, LE, and GT operators can be used to create BCT, BXLE, and BXH machine instructions. These operators were added to the PL360 language only for completeness. They are usually used to control WHILE or REPEAT/UNTIL loops. All of these operators make use of Integer Registers (Rn).

The DEC operator is used in the following forms:

        DEC Rn
       ~DEC Rn

The contents of Rn is reduced by 1 and the result is tested for zero. DEC Rn is TRUE is the result is non-zero, and ~DEC Rn is TRUE if the result is zero. Therefore, IF DEC R5 THEN R6 := R5; usually does two things. It always decrements the contents of R5 by 1, and unless the result is zero, it places the results in R6. But if R5 originally contained 1, this statement would not alter R6 and would leave 0 in R5.

The GT and LE operators are used in the following forms:

        Rx GT Ry
        Rx LE Ry

The contents of Ry is added to the value in Rx and the result is compared to a value contained in another register, either Ry or R(y+1). If "y" of Ry is odd, then Ry contains both the increment and comparison value. If "y" of Ry is even, then an even-odd pair of registers is designated, with Ry containing the increment, and R(y+1) containing the comparison value. For example: R4 GT R6 will add R6 to R4 and then compare the results in R4 to the value contained in R7. In a sense, its like the statement and condition: R4 := R4 + R6; R4 > R7 but done with a single instruction without affecting the condition code.

Rx GT Ry is TRUE if the result in Rx is greater than the comprison value. Rx LE Ry is TRUE if the result in Rx is less than or equal to the comparison value.

6.3  IF Statements

There are two forms of IF statement. The first is:

IF conditions THEN statement;

Conditions may be simple or compound. If the conditions are satisfied, the statement following THEN is done. If the conditions are not satisfied, the statement is bypassed.

The statement following THEN may be any statement: a GOTO label, a BEGIN

..

END block, an assignment statement, even another IF statement. But whatever the statement may be, it cannot be labeled. We are not allowed to branch into the middle of an IF statement.

The fact that an IF conditions THEN may be followed by another IF conditions THEN leads to a very interesting side effect.

       IF c1 THEN IF c2 THEN statement;

acts very much like:

       IF c1   AND   c2 THEN statement;

In other words, the final statement is done only if both sets of conditions are satisfied.

IF conditions THEN statement ELSE statement;

This second form of IF statement provides a statement to be done when conditions succeed, and another statement to be done when they fail.

The statement following THEN (before ELSE) may be any statement except an IF, WHILE, REPEAT/UNTIL, or FOR statement. Of course, it may be a block containing any statements. The statement following ELSE may be any statement, including an IF, WHILE, REPEAT/UNTIL, or FOR statement. We shall call these statements the THEN statement (following THEN) and the ELSE statement (following ELSE). Neither statement may be labeled.

At the beginning of Chapter 5 we stated that statements are normally terminated by a semicolon, but that many examples would not include a terminating semicolon. The reason for this is that the THEN statement of an IF-THEN-ELSE is not terminated by a semicolon! ELSE acts as the terminator of the THEN statement. So all of the examples of statements (including blocks) which did not end in a semicolon were examples of statements that could occur between THEN and ELSE. Each of those statements would be terminated by a semicolon if they were not used as the THEN statement of an IF-THEN-ELSE.

An IF-THEN-ELSE statement is processed in the following way. If the conditions are satisfied, the THEN statement is done and the ELSE statement is bypassed. If the conditions are not satisfied, the THEN statement is bypassed and the ELSE statement is done.

Since the ELSE statement may be another IF statement of either form, we could write something like:

       IF c1 THEN s1 ELSE IF c2 THEN s2 ELSE s3;

s1 is done if c1 succeeds; s2 is done if c1 fails and c2 succeeds; s3 is done when both c1 and c2 fail. Note that s2 is not done if both c1 and c2 would succeed. Only s1 is done and c2 isn't even tested.

Consider the following chained IF statement:

       IF c1 THEN IF c2 THEN s2 ELSE s3;

You cannot have an ELSE associated with the first IF statement. Only the second IF statement can have an associated ELSE statement. In this example, if c1 fails, the remainder of the statement is skipped; c2 is not evaluated and neither s2 or s3 are done. If c1 succeeds, then s2 is done if c2 succeeds, otherwise s3 is done.

Let's examine the following chained compound condition.

       IF c1 OR c2 THEN IF c3 OR c4 THEN s1 ELSE s2;

By substituting AND for THEN IF and using parenthesis, we would get:

       IF (c1 OR c2) AND (c3 OR c4) THEN s1 ELSE s2;

This statement executes differently than the chained IF statement. I leave it to the reader to determine why they are different.

If the THEN statement is a BEGIN...END block ending with a GOTO statement (where the END of the block isn't labeled), there's no reason for coding an ELSE. One might as well write:

       IF conditions THEN
       BEGIN
           : : : : :
          GOTO label;
       END;  statement;

instead of:

       IF conditions THEN
       BEGIN
           : : : : :
          GOTO label;
       END ELSE  statement;

Clearly, if the conditions are satisfied, the block will be done; and it unconditionally branches, thus bypassing the final statement (and possibly others). If the conditions are not satisfied, control passes around the block to the final statement.

There are two advantages to writing this IF statement without the ELSE. First, we can eliminate an unnecessary branch instruction that the compiler would generate following the block intended to branch around the ELSE statement. No ELSE, then no compiler generated branch; it's that simple. The compiler does check for THEN GOTO label and ELSE GOTO label; so that unnecessary branch instructions are not generated in those cases, but blocks are not checked.

The second advantage is that the final statement can be labeled if we don't use the ELSE. That makes the statement accessible by GOTO statements in other sections of the program. An IF statement like:

       IF conditions THEN GOTO label ELSE statement;

might as well be written as:

       IF conditions THEN GOTO label;  statement;

to allow for possible labeling of the statement.

Note: IF conditions THEN GOTO label; generates a single 4-byte conditional branch instruction when 'conditions' are only an integer value condition or relational operator.

To conclude our discussion of IF statements, let's examine the concept of inverse conditions. Many times a set of conditions may be given which are just the inverse of what's desired. Using a bicycle as an example: "If the front tire is not flat AND the back tire is not flat THEN DON'T fix a tire." If we called 'the front tire is not flat' condition c1, 'the back tire is not flat' condition c2, and 'fix a tire' statement s, then we could restate the problem as:

       IF c1 AND c2 THEN DON'T s;

What we'd really like to know is when should we 'fix a tire'; when should we do statement s ? There are two ways to accomplish this in PL360.

The first way is by means of the NULL statement: a statement which does nothing. We could restate the problem as:

       IF c1 AND c2 THEN NULL ELSE s;

If the conditions are satisfied, we do nothing (NULL). Only when a condition fails do we 'fix a tire'.

The second method is better, but requires some understanding of Boolean logic. Let's look at the inversion rule first. The inverse of a simple condition such as c1 is NOT c1 (~c1). The inverse of 'the front tire is not flat' would be 'the front tire is flat'. Below are a few examples of simple conditions and their inverses.

                 Condition (c1)          Inverse (~c1)
                 --------------          -------------
                  R1 < 17S                R1 >= 17S
                  F01 <= F02              F01 > F02
                  X = "FLAT"              X ~= "FLAT"

The inverse of an inverse condition is the original condition again! So the six relational operators shown above are properly paired on each line. The conditions such as OVERFLOW, ON, MIXED, etc. would have ~OVERFLOW, ~ON, ~MIXED, etc. as their inverses.

The second Boolean logic rule involves the distribution of NOT (~) throughout a compound condition. This rule states that:

       ~ [ c1 AND c2 AND c3...]  becomes   ~c1 OR ~c2 OR ~c3...
and    ~ [ c1 OR c2 OR c3...]    becomes   ~c1 AND ~c2 AND ~c3...

All of the logical operators are 'inverted' (AND's replace OR's, OR's replace AND's), and each simple condition is replaced by its inverse. In our bicycle problem, we wanted:

       IF ~[c1 AND c2] THEN s;

Using the Boolean logic rules, we can write:

       IF ~c1 OR ~c2 THEN s;

which makes our original problem read: "IF the front tire is flat OR the back tire is flat THEN fix a tire."

6.4  WHILE Statements

A WHILE statement is defined as:

WHILE conditions DO statement;

The WHILE statement allows repeated execution of the DO statement as long as the specified conditions are satisfied. If the conditions fail the first time, the DO statement is bypassed entirely. Any statement, including another WHILE statement, is allowed to follow DO; however, a GOTO statement would serve no useful purpose.

We could write the equivalent of a WHILE statement using a labeled IF, GOTO, and block. Thus,

       label:  IF conditions THEN
               BEGIN
                  statement;
                  GOTO label;
               END;

In the preceding section (IF statements), we presented a bicycle problem that could be done with a WHILE statement. What if both tires were flat? We'd want to fix both of them. Using the same terminology (c1, c2, and s), we could write the following WHILE statement:

       WHILE ~c1 OR ~c2 DO s;

where statement s is now: 'fix the appropriate tire'. In other words, "WHILE the front tire is flat OR the back tire is flat DO fix the appropriate tire." Obviously, the conditions should eventually fail or we'd have an infinite loop!

One last point: the fact that simple conditions may be preceded by one or more statements means we can write some strange looking WHILE statements:

       WHILE statement; condition DO NULL;

In this case, the statement is done BEFORE the condition is tested.  If
the condition is satisfied, we repeat the  entire  process.   When  the
condition fails, we go on to whatever follows the WHILE statement.

6.5  REPEAT/UNTIL Statements

A REPEAT/UNTIL statement is defined as:

REPEAT statements; ... labels: ... UNTIL conditions;

The REPEAT/UNTIL statement allows repeated execution of the statements between REPEAT and its matching UNTIL. The statements are executed at least once. When the UNTIL is encountered, the conditions are evaluated. If the conditions fail, the REPEAT/UNTIL is repeated. If the conditions are true, execution continues with the next statement following the UNTIL. Any statements are allowed to follow REPEAT, including other REPEAT/UNTIL statements. A GOTO statement may branch to any label within scope, including those which might be defined within the REPEAT/UNTIL statement.

REPEAT/UNTIL statements may be statements following THEN or ELSE of IF statements, but UNTIL may not be directly followed by ELSE. Example:

       IF initial_conditions THEN REPEAT .... UNTIL stop_conditions;

If the initial_conditions are not true, the entire REPEAT/UNTIL is not executed.

We could write the equivalent of a REPEAT/UNTIL statement using a label, IF, and GOTO. Thus,

       label:  statement;
               : : : : :
               statement;
       if ~(conditions) THEN GOTO label;

Compare REPEAT/UNTIL to the WHILE statement example given at the end of the previous section:

       WHILE statement; condition DO NULL;
       REPEAT statement; UNTIL condition;

The difference is that a true condition causes repetition of the WHILE statement, and a false condition causes repetition of the REPEAT/UNTIL statement.

6.6  FOR Statements

The FOR statement is used primarily to control indexing through an array. Its basic form is:

FOR Rn := expression STEP increment UNTIL limit DO statement;

The integer register assignment beginning the FOR statement establishes the initial value of the control register, Rn. The increment must be an INTEGER constant, either positive or negative. This increment is added to the contents of the control register after each execution of the DO statement. The limit must be an INTEGER register, constant or cell, or a SHORT INTEGER cell (a SHORT INTEGER constant is treated as if it were an INTEGER constant). The initial value in the control register, and each succeeding incremented value, is tested against this limit. When the specified increment is positive, the DO statement is executed as long as the control register's value is less than or equal to the limit (Rn <= limit). When the specified increment is negative, the DO statement is executed as long as the control register's value is greater than or equal to the limit (Rn >= limit).

FOR examples:

       FOR R7 := 20 STEP _4 UNTIL 0 DO statement;
       FOR R5 := R5-R5 STEP 5 UNTIL 100 DO statement;
       FOR R0 := 4 STEP 4 UNTIL 64 DO statement;
       FOR R3 STEP 2 UNTIL R9 DO statement;

The last example assumes that the initial value in R3 had been previously computed, and that R9 contains an upper limit.

We can simulate a FOR statement using a block, register assignment statements, and a WHILE statement. Thus,

       Rn := expression;
       WHILE  Rn relation limit  DO
       BEGIN  statement;
          Rn := Rn + increment;
       END;

The 'relation' would be >= if the increment were negative, and <= if the increment were positive. Notice, however, that the WHILE statement method is much more powerful than the simple FOR statement. We are allowed to have a register or cell increment, not just a constant. Also, the conditions of the WHILE statement may be compound conditions. In fact, the loop could be controlled by cell-to-cell or floating-point register relations, not just an integer register relation.

6.7  CASE Statements

The last statement we will cover in this chapter is the CASE statement. Unlike the IF, WHILE, and FOR statements which allowed only one or two subordinate statements, the CASE statement allows a large set of subordinate statements. However, only one of these statements is executed at each execution of the CASE statement. The general form of a CASE statement is:

  CASE Rn OF
  BEGIN
     statement; | 1st subordinate |
     statement; | 2nd subordinate |
     : : : :
     statement; | i-th subordinate |
     : : : :
     statement; | m-th subordinate |
  END

The BEGIN ... END block is required. Neither the block nor any of the subordinate statements may be labeled. Also, register Rn must be an integer register other than R0 (an index register, R1 through R15).

The contents of Rn must be a value from 0 through m, where m is the number of subordinate statements within the CASE statement's block. The subordinate statement whose ordinal number corresponds to the register's value is selected for execution, and all the other subordinate statements are bypassed. If Rn=0, no subordinate statement is executed; if Rn=1, the first subordinate statement is executed; if Rn=i, the i-th subordinate statement is executed; and so on. The original value in Rn is modified by this selection process, becoming the relative address within the program segment of the start of the selected subordinate statement. For example:

       CASE R8 OF
       BEGIN  R1 := R1 AND R2;          | R8 = 1 |
              R1 := R1 OR  R2;          | R8 = 2 |
              NULL;                     | R8 = 3 |
              R1 := R1 XOR R2;          | R8 = 4 |
              BEGIN  R0 := R1 SHRA 31;
                 R1 := R1 / R2;         | R8 = 5 |
              END;
              R1 := R1  *  R2;          | R8 = 6 |
       END

A word of caution: the CASE statement does not check the validity of the original value in Rn! Values outside the range 0 through m will cause unpredictable execution or abnormal program termination. Therefore, when in doubt about the initial value in Rn, it's wise to make the CASE statement part of an IF statement.

       IF Rn >= 0 AND Rn <= m THEN
       CASE Rn OF
       BEGIN  statement;  | 1st  subordinate |
              statement;  | 2nd  subordinate |
               : : : :
              statement;  | m-th subordinate |
       END ELSE statement;  | error processing |

If no error processing statement is desired, we would terminate with just END; instead of the END ELSE statement; termination shown.

We could approximate a CASE statement with IF statements. Thus,

       IF Rn = 0 THEN  NULL  ELSE
       IF Rn = 1 THEN statement ELSE
       IF Rn = 2 THEN statement ELSE
          : : : : : : : : : : :
       IF Rn = m THEN statement;

However, the CASE statement is much more efficient than the IF statement approximation. CASE branches directly to the appropriate statement eliminating all the testing required by the IF's. Also, the subordinate statements of CASE may be any statements including IF's, WHILE's, and FOR's. Those statements would not be allowed between THEN and ELSE in our approximation unless they were contained in blocks.

The approximation was presented to help clarify what logically happens in a CASE statement. But it also points out the fact that a particular task may be accomplished in more than one way. We've seen alternatives for WHILE and FOR statements in preceding sections; and sometimes, Rn := @Bn(k) can be used instead of Rn := Rn+k . This wide variety of alternatives in solving a problem is what makes programming such a challenge. Knowing what statements to choose, and when, is more than a matter of taste: it's an art.

6.8  Exercise

Problem 1.

Write the relationship for the inverse of the conditions given:

(a)  R1 > R2
(b)  F01 <= 0L
(c)  X ~= Y
(d)  OVERFLOW

Problem 2.

       |1|   BEGIN
       |2|      X:  BEGIN
       |3|             X:  BEGIN   GOTO X;
       |4|             X:  END;    GOTO X;
       |5|          END;   GOTO X;
       |6|   END

Does each GOTO refer to only one label? Match the line number of each label with the line number of the GOTO's that refer to that label.

Problem 3.

Change the labels on lines |3| and |4| in the preceding problem to Y and Z. Does any GOTO X refer to an unknown label? If so, what are the line numbers of those GOTO's?

Problem 4.

       IF R1 = 0 THEN s1 ELSE IF < THEN s2 ELSE s3;

In this IF statement, s1, s2, and s3 each represent an assignment statement. Specify the conditions (or inverse conditions) which must be satisfied to:

(a)  cause s1 to be executed
(b)  cause s2 to be executed
(c)  cause s3 to be executed

Problem 5.

       IF R1 > R2 OR R2 < R3 THEN IF R4 > R5 THEN s1 ELSE s2;

In this complex IF statement, s1 and s2 each represent an assignment statement. Specify the conditions (or inverse conditions) which must be satisfied to:

(a)  cause s1 to be executed
(b)  cause s2 to be executed
(c)  cause both s1 and s2 to be bypassed

Do not include untested conditions, but do specify all combinations.

Problem 6.

       BEGIN  |-- BINARY SEARCH CORE ENTRIES THRU SORTED TABLE --|
          EQUATE TABSIZ SYN 4,  KEYSIZ SYN 6;  |-- SIZES --|
          BYTE KEY SYN 3;  |-- RELATIVE START OF KEY IN ENTRY --|
          INTEGER TABLE SYN 0;  |-- RELATIVE START OF TABLE --|
          INTEGER REGISTER LO SYN R1,  HI SYN R2,  POINT SYN R3,
             MID SYN R4,  INCR SYN R5,  MASK  SYN R6,  ENTRY SYN R7;
       |-- ASSUME LO, HI, AND POINT ALREADY CONTAIN ADDRESSES --|
          INCR := TABSIZ;  MASK := NEG INCR;  |-- INCREMENT VALUES --|
          WHILE LO <= HI DO  |-- SEARCH FOR ENTRY THRU TABLE --|
          BEGIN  MID := LO + HI SHRA 1 AND MASK;  |-- MIDDLE --|
             ENTRY := TABLE(MID);  |-- ADDR OF ENTRY FROM TABLE --|
             IF KEY(ENTRY/KEYSIZ) = KEY(POINT) THEN GOTO EXIT;
             IF > THEN LO := MID + INCR   |-- RAISE LO BOUND --|
                  ELSE HI := MID - INCR;  |-- LOWER HI BOUND --|
          END;  MID := MID - MID;  |-- NO FIND, SET MID TO ZERO --|
       EXIT:  |-- MID = 0 IF NO FIND, ELSE MID IS TABLE LOCATION --|
       END

The preceding is an example of a binary search technique.

(a) What would be the first value assigned to MID (in terms of R1) if we assumed: R2 := 14 SHLL 2 + R1; before beginning the binary search? (Note: R1 is LO, R2 is HI.)

(b) What is the maximum number of times that the TABLE addresses could be referenced assuming: R2 := 14 SHLL 2 + R1; before beginning the binary search? (Note: R1 is LO, R2 is HI.)

(c) Why must we AND MASK in the evaluation of MID?

7  Functions

The statements covered in Chapters 5 and 6 are capable of generating nearly 100 different machine instructions (see Appendix D). However, there are other instructions available in the hardware which cannot be generated by those statements. Functions provide us with the means for generating most of these other instructions. Also, there may be instances where we'd like to generate a compare instruction without the branch instruction which normally follows it. Again, functions come to the rescue by allowing us to generate independent instructions.

Every function statement generates a single machine instruction by invoking a function defined by a function declaration or pre-declared by the PL360 compiler. We shall examine function definitions first because they are the link between function statements and machine instructions.

7.1  Function Declarations

A function is defined by a function declaration. Just like all other PL360 declarations, function declarations MUST occur before any statements or labels in a block. The general form of a function declaration is:

FUNCTION id1(type,code), id2( ... );

FUNCTION id(type,code);

A single function is defined by the three quantities: id, type, and code. The 'id' is an identifier which names the function being defined. In a sense, we can think of this function name as being the 'name' of a machine instruction: an instruction mnemonic.

The 'type' of a function definition must be an integer value from 0 through 19. This value selects one of 20 possible instruction formats. We examined instruction formats briefly in section 1.4 where we mentioned that instructions can be either one, two, or three halfwords in length. However, there are many formats possible depending on instruction length and how various fields are to be interpreted within an instruction. For example, a halfword instruction (two bytes) could have its second byte interpreted as an integer value, a pair of register designators, or as a mask and register.

The 20 function types not only specify instruction length, but also the number of fields interpreted and what each is interpreted to be: a value, register, cell address, etc. These fields are defined by the 'parameters' of a function statement. Each parameter is a value, register, cell address, etc., that occupies a specific field position within the final instruction generated. A PL360 label is not an allowed parameter, so instructions corresponding to BCT, BXH, and BXLE in assembly language are difficult to generate, although "<reg> GT <reg>" and "<reg> LE <reg>" conditions can create the BXH and BXLE instructions. Also, a PSTAR cell reference defines the "current program address" as a base-displacement field, which can be used to create indexed branches.

The 'code' of a function definition must be an integer (or short integer) value. The lower two bytes of this value define the first two bytes of the machine instruction to be generated. Therefore, 'code' is usually written as a hexadecimal value of the form: #hhhh, where the h's represent hexadecimal digits. This is the only form we shall use throughout this text.

Since the first byte of every machine instruction is its op-code (operation code), the first two h's define the instruction's op-code. Not all 256 possible op-codes are valid, so reference should be made to IBM's Principles of Operation manuals when selecting op-codes. The PL360 compiler does NOT check op-code validity; but it does check that the op-code is proper for the 'type' of function being defined. The relationship between op-code and instruction length is:

       Op-codes:  #00 through #3F     Length:  1 halfword
       Op-codes:  #40 through #BF     Length:  2 halfwords
       Op-codes:  #C0 through #FF     Length:  3 halfwords

The last two h's of 'code' define the second byte of the instruction. Frequently this byte is defined as 00 because parameters are usually OR'ed or XOR'ed into it by the PL360 compiler. But this byte could be defined with other values depending on the function type specified and op-codes chosen.

      Function    Number of            Instruction Fields
        Type      Parameters      0   8  16       32       48

                                  ---------
         0            0           |       |
                                  ---------
                                  ---------
         1            2           |   |R|R|
                                  ---------
                                  ------------------
         2            2           |   |R|     XL   |
                                  ------------------
                                  ------------------
         3            3           |   |R|R|   C    |
                                  ------------------
                                  ------------------
         4            2           |   | S |   C    |
                                  ------------------
                                  ---------------------------
         5            3           |   | S |   C    |   CL   |
                                  ---------------------------
                                  ---------
         6            1           |   |R| |
                                  ---------
                                  ---------
         7            1           |   | S |
                                  ---------
                                  ------------------
         8            1           |       |   C    |
                                  ------------------
                                  ------------------
         9            2           |   |R| |   CI   |
                                  ------------------
                                  ---------------------------
        10            4           |   |N|N|   C    |   CL   |
                                  ---------------------------
                                  ------------------
        11            2           |   |R|     XD   |
                                  ------------------
                                  ------------------
        12            2           |   |R|     X    |
                                  ------------------
                                  ---------------------------
        13            3           |   | S |   CL   |   CL   |
                                  ---------------------------
                                  ---------------------------
        14            2           |       |   C    |   CL   |
                                  ---------------------------
                                  ------------------
        15            1           |     |     XL   |
                                  ------------------
                                  ------------------
        16            3           |   |R|N|   CL   |
                                  ------------------
                                  ------------------
        17            3           |   |R|N|   C    |
                                  ------------------
                                  ------------------
        18            1           |       | 0 0 R 0|
                                  ------------------
                                  ------------------
        19            1           |       |   CI   |
                                  ------------------

Field definition codes:

Note:  INTEGER MEM SYN 0;           |See section 4.5.|
and    EQUATE ON SYN 1, OFF SYN 8;  |See section 4.7.|

7.2  Function Statements

A function statement causes a single machine instruction to be generated. Since function definitions allow from zero through four parameters, there are five possible forms of function statement:

       id
       id(p1)
       id(p1,p2)
       id(p1,p2,p3)
       id(p1,p2,p3,p4)

The 'id' is the function name associated with a function definition. The p's are parameters of the function. The number of parameters and what each must be is determined by the function 'type' and associated field definition codes given in section 7.1. For functions with more than one parameter, the left-to-right sequence of parameters in the function statement must correspond to the left-to-right sequence of fields defined within the instruction to be generated.

For example: FUNCTION LOAD(12,#5800); invoked by the statement: LOAD(R1,B3(R5+4)) would generate a machine instruction that looks like this: 58153004. This instruction is identical to the instruction generated by the assignment statement: R1 := B3(R5+4).

A function statement consisting of just a function name is only valid for function type 0 which has no parameters. The single halfword instruction generated consists of just the function 'code' given in the function definition, #hhhh.

For example: FUNCTION SR34(0,#1B34); invoked by the statement: SR34 would generate a machine instruction which looks like this: 1B34. This instruction is identical to the instruction generated by the assignment statement: R3 := R3 -- R4 .

Neither of these examples are meant to imply that a function statement should replace an assignment statement. But they do show that most machine instructions generated by an assignment statement can also be generated by a function statement.

7.3  Pre-declared Functions

A large group of functions are pre-declared by the PL360 compiler. Except for TEST, SET, and RESET, all function names were chosen to be the assembly language mnemonics corresponding to the instructions these functions would generate. The pre-declared function definitions are:

       BALR(1,#0500)       MVC(5,#D200)        SRDL(9,#8C00)
       CLC(13,#D500)       MVI(4,#9200)        STC(12,#4200)
       CLI(4,#9500)        MVN(5,#D100)        STCM(17,#BE00)
       CLM(16,#BD00)       MVZ(5,#D300)        STH(12,#4000)
       CVB(12,#4F00)       NC(5,#D400)         STM(3,#9000)
       CVD(12,#4E00)       NI(4,#9400)         SVC(7,#0A00)
       ED(5,#DE00)         OC(5,#D600)         TEST(8,#95FF)
       EDMK(5,#DF00)       OI(4,#9600)         TM(4,#9100)
       EX(2,#4400)         PACK(10,#F200)      TR(5,#DC00)
       IC(2,#4300)         RESET(8,#9200)      TRT(5,#DD00)
       ICM(16,#BF00)       SET(8,#92FF)        TS(8,#9300)
       LA(2,#4100)         SLDA(9,#8F00)       UNPK(10,#F300)
       LH(12,#4800)        SLDL(9,#8D00)       XC(5,#D700)
       LM(3,#9800)         SPM(6,#0400)        XI(4,#9700)
       LTR(1,#1200)        SRDA(9,#8E00)

We shall study some of these function in the remainder of this section, but for complete and accurate descriptions of most of these functions, see IBM's Principles of Operation manuals.

The headings for each function description are of the form:

Function Statement |Function Title| | Function Declaration |

SRDL(R,CI) |Shift Right Double Logical| | SRDL(9,#8C00) |

SLDL(R,CI) |Shift Left Double Logical| | SLDL(9,#8D00) |

SRDA(R,CI) |Shift Right Double Arithmetic| | SRDA(9,#8E00) |

SLDA(R,CI) |Shift Left Double Arithmetic| | SLDA(9,#8F00) |

These instructions shift two integer registers at once in a manner similar to the single register shift operations associated with integer register assignment statements. The specified register (R) must be an even-numbered integer register (R0,R2,R4,etc.). The contents of the even-odd register pair so specified is left- or right-shifted as a unit an amount determined by CI. Thus, SRDL(R2,28) would shift the contents of R2-R3 right logically by 28 bits. The shift amount (I) may range from 0 through 63 bits. A cell reference (C) indicates doing address arithmetic and the lower six bits of the resultant address determines the shift amount. Thus, SLDA(R4,B1(3)) with R1=7 would yield a shift amount of 10 bits (7+3). The contents of R4-R5 would be shifted left arithmetically by 10 bits.

In all these double register shifts, the lower bits in the even- numbered register are followed immediately by the upper bits of the odd-numbered register. For arithmetic shifts (SRDA,SLDA), condition codes are affected, and the top bit in the even-numbered register is the sign bit. SRDA(R,0) can be used to 'test double integer register'.

IC(R,XL) |Insert Character| | IC(2,#4300) |

STC(R,X) |Store Character| | STC(12,#4200) |

The IC function causes the lowest byte of an integer register (R) to be replaced by the contents of a single byte from storage (XL). The upper three bytes of the integer register are unaffected! Thus IC(R0,B3(R2+1)) would replace the lowest byte of R0 with the byte contained in the cell whose address is determined by B3(R2+1). Similarly, IC(R5,"A") would cause the character A to be placed in the current program segment's literal pool, and the instruction would cause the lowest byte of R5 to be replaced by the character A.

The STC function stores the lowest byte of an integer register (R) into a single storage byte (X). Thus, STC(R7,B6) would store the lowest byte of R7 into the single byte cell whose address is given by the contents of R6.

LM(Rx,Ry,C) |Load Multiple| | LM(3,#9800) |

STM(Rx,Ry,C) |Store Multiple| | STM(3,#9000) |

These instructions allow many (multiple) integer registers to be loaded or stored at one time. If the 16 integer registers were assumed to occupy positions similar to the numerals on the face of a clock, then the range of registers processed would be Rx through Ry taken clockwise (R15 followed by R0).

C is the address of the first of as many consecutive words of storage as needed to handle the range of registers determined by Rx through Ry. Usually this is an ARRAY of INTEGER cells. Thus, STM(R1,R4,B7) would generate a single instruction whose affect would be the same as the following set of assignment statements:

       B7 := R1;  B7(4) := R2;  B7(8) := R3;  B7(12) := R4;
Example:

       ARRAY 16 INTEGER REGS;   |Storage area|
          :  :  :  :  :  :
       STM(R1,R0,REGS)          |Store all registers|
          :  :  :  :  :  :
       LM(R3,R0,REGS(8))        |Reload all registers|
                                |   except R1 and R2 |

Note: the base register associated with REGS is being stored by the STM instruction. Since the LM instruction requires that the base register be correct in order to do the load, its content would be replaced by its original (identical) content if it was not R1 or R2.

CLI(S,C) |Compare Logical Immediate| | CLI(4,#9500) |

MVI(S,C) |Move Immediate| | MVI(4,#9200) |

NI(S,C) |AND Immediate| | NI(4,#9400) |

OI(S,C) |OR Immediate| | OI(4,#9600) |

XI(S,C) |XOR Immediate| | XI(4,#9700) |

All of these instructions use a byte from the instruction (S) and a byte of storage (address defined by C). The CLI instruction logically compares S with C's byte. MVI moves S to C's byte. NI, OI, and XI all logically combine S into C's byte.

For example, CLI("A",B1) generates the same compare instruction as would be generated by IF B1 = "A" THEN (excluding IF's branch instruction). Similarly, CLI(0,B5(3)) and IF B5(3/1) = 0 THEN generate equivalent compare instructions. But, CLI(MEM(MIXED+OFF),B7) could not be duplicated by a simple IF statement without inventing an EQUATE symbol whose value was MIXED+OFF. However, we could write:

       IF CLI(MEM(MIXED+OFF),B7); relation THEN ...

where 'relation' is any desired relational operator.

CLC(S,CL,CL) |Compare Logical Characters| | CLC(13,#D500) |

MVC(S,C,CL) |Move Characters| | MVC(5,#D200) |

NC(S,C,CL) |AND Characters| | NC(5,#D400) |

OC(S,C,CL) |OR Characters| | OC(5,#D600) |

XC(S,C,CL) |XOR Characters| | XC(5,#D700) |

These instructions parallel the 'immediate' instructions just discussed, but on a storage-to-storage basis rather than on an instruction-to-storage basis. The C and/or CL fields define the storage addresses of the items to be processed. The S field value defines a count MINUS ONE of the number of bytes to be processed (S=count-1). Thus, MVC(3,B5,B6) would copy four consecutive bytes from B6 to B5 (R6 and R5 would contain the starting address of each storage area). This would be equivalent to the cell assignment: B5(0/4) := B6.

An instruction such as CLC(0,"1","2") could not be duplicated as a simple condition because two literals are involved. Such an instruction would force a LESS THAN (<) condition because "1" is less than "2". Both "1" and "2" would be placed in the literal pool.

TM(S,C) |Test under Mask| | TM(4,#9100) |

This instruction's byte value (S) acts as a bit mask in selecting bits of a storage byte (C) to be tested. Each non-zero bit in mask S tests the corresponding bit of byte C to establish the machine's condition code. An OFF condition occurs if all tested bits are 0's (or the mask is entirely 0). An ON condition occurs if all tested bits are 1's. Otherwise, a MIXED condition occurs when some of the tested bits are 0's AND some are 1's. Thus, TM(3,B6) tests the lowest two bits of the byte at B6 to establish one of the three possible conditions. If only a single bit is tested, then only ON/OFF conditions are possible.

RESET(C) |Bytecell := #00| | RESET(8,#9200) |

SET(C) |Bytecell := #FF| | SET(8,#92FF) |

These functions are both MVI instructions with the S field preset. RESET(C) is equivalent to MVI(0,C) and SET(C) is equivalent to MVI(#FF,C). Most often these functions are used with bytecell flags in conjunction with the 'bytecell' and '~bytecell' conditions.

LA(R,XL) |Load Address| | LA(2,#4100) |

This function requires an integer register designator (R) and either a cell reference (X) or literal (L). When used with a cell, LA is equivalent to an assignment statement such as: R := @X; but, when used with a literal, LA provides a unique facility which cannot be duplicated with an assignment statement. The literal is placed in the literal pool, and LA causes the absolute address of that literal to be placed in the specified integer register (R). For example: LA(R1,"Messages are made like this.") would cause the absolute address of the "string" to be placed in R1.

LTR(Rx,Ry) |Load and Test Register| | LTR(1,#1200) |

This function loads integer register Rx with the contents of integer register Ry, in a manner similar to Rx := Ry, but it also establishes the machine's condition code. The result in Rx is either (0) equal to, (1) less than, or (2) greater than zero. Thus, IF LTR(R4,R7); = THEN ... would have the same effect as the statements R4 := R7; IF R4 = 0 THEN ... but would take one less instruction. In fact, the compare instruction for R4 = 0 is equivalent to LTR(R4,R4).

SVC(S) |Supervisor Call| | SVC(7,#0A00) |

An SVC instruction causes a 'supervisor interrupt' thereby giving control to the operating system (VS,OS,MTS,ORVYL,etc.). The S field of an SVC instruction is usually written as an integer value, such as: SVC(13). The operating system interrogates the S field to determine what task to perform for the requesting program. Normally, other parameters are passed to and from the program in registers R0, R1, R14, and R15. Because R15 can be affected by an SVC, we would either have to choose a different program base register, or be very careful about saving and restoring R15. We shall see how registers other than R15 can be chosen as program base registers in Chapter 8.

BALR(Rx,Ry) |Branch And Link Register| | BALR(1,#0500) |

Before discussing BALR, let's look at the program status word (PSW). Although the PSW is a 64-bit quantity, we are only interested in the lower 32 bits.

        ----------------------------------------
       | ILC | CC  | MASK | INSTRUCTION ADDRESS |
        ----------------------------------------
          2     2     4           24               (bit lengths)

ILC is the 'instruction length code' in halfwords: 1, 2, or 3.

CC is the current 'condition code': 0, 1, 2, or 3.

MASK is a 4-bit quantity with each bit associated with a different program exception (interrupt). The bits in order from left to right correspond to:

           Fixed-point overflow    (OVERFLOW interrupt)
           Decimal overflow        (Packed-decimal exception)
           Exponent underflow      (Floating-point interrupt)
           Significance            (Floating-point processing)

INSTRUCTION ADDRESS is the address of the 'next' instruction. The current instruction started at this address minus ILC halfwords.

The BALR instruction first determines a branch address dependent upon integer register Ry. If Ry = R0, there is no branch address! If Ry ~= R0 (i.e., R1 through R15), the branch address is contained in the lower 24 bits of Ry. This branch address is saved aside by the computer. Then BALR places the lower 32 bits of the PSW into integer register Rx. Thus, Rx receives the next instruction address, condition code, and other information shown above. Finally, when Ry ~= R0, BALR replaces the lower 24 bits of the PSW by the saved aside branch address determined earlier. This causes an unconditional branch to the address originally contained in Ry. If Ry = R0, no branching takes place.

The BALR instruction is usually generated only by procedure statements as we shall see in the chapter on procedures (Chapter 8). Very rarely is a BALR function used directly in PL360 programming. However, we shall examine uses for BALR(Rx,R0) in the next section (7.4) and with the SPM function below.

SPM(R) |Set Program Mask| | SPM(6,#0400) |

The SPM instruction replaces the CC and MASK fields of the PSW by corresponding fields taken from integer register R (same bit positions). For example, we could use BALR(Rx,R0) to save the condition code field (CC) in register Rx, and later restore that condition code with an SPM(Rx) instruction. The MASK, of course, would also be saved and restored by this process, but CC is more volatile than the MASK and is more likely to change.

EX(R,XL) |Execute Instruction| | EX(2,#4400) |

The EX instruction is probably one of the handiest instructions available. It executes another instruction in storage (address defined by XL). This object instruction may be one, two, or three halfwords in length. Therefore, EX provides a one instruction subroutine facility!

The op-code of EX is #44X, and the EX instruction format is two halfwords in length consisting primarily of an integer register designator (R) and an indexable cell address (XL). Regardless of function name, an Execute Instruction is defined as any function whose function 'code' begins with #44X and whose function 'type' specifies a two-halfword instruction format (most likely types 2 or 15). For example, EXEC(15,#4410) defines an Execute Instruction with the register designator preset to 1 (R1). Only the cell address (XL) would have to be specified by an EXEC function statement.

The Execute Instruction operates in essentially the following manner: The next instruction address portion of the program status word (PSW) is updated based on the length of the Execute Instruction. Then the object instruction to be executed is fetched from storage and prepared for execution. The lowest byte of the integer register specified by the R field of the Execute Instruction is logically OR'ed into the second byte of the object instruction (bits 8-15). If register R0 is specified, this modification does not take place. Finally, the (modified) object instruction is executed in a normal manner. The object instruction may not be another Execute Instruction.

There are three important points worth noting. First, the address of the next instruction is determined by the length of the Execute Instruction, not the object instruction. However, if the object instruction were a branch instruction whose conditions were satisfied, then branching would take place. Second, after the object instruction has been fetched from storage, its second byte is modified by having the lowest byte of a specified integer register (R1 through R15) OR'ed into it. The object instruction is not modified in storage. Third, the address field of the Execute Instruction is an indexable address allowing us to choose one object instruction out of a list of instructions depending on the contents of an index register. Here are some examples to demonstrate these points.

Example 1:

       BYTE RESULT;
       ARRAY 6 SHORT INTEGER LOGINS = (
          #9400,@RESULT,     | NI(0,RESULT) |
          #9600,@RESULT,     | OI(0,RESULT) |
          #9700,@RESULT);    | XI(0,RESULT) |
        :  :  :  :  :  :  :
       EX(R1,LOGINS(R2))   |Execute selected instruction|

Assuming the declared cells (RESULT and LOGINS) are based on a register other than R1 or R2; and assuming that R2 contains either a 0, 4, or 8; then the EX instruction would either AND, OR, or XOR the lowest byte of R1 into the RESULT cell.

Example 2:

       EX(R5,#95003004)

The literal #95003004 is an integer value equivalent to the function statement CLI(0,B3(4)), so this EX instruction would compare the lowest byte in R5 with the contents of the byte cell whose address was determined by B3(4). Using a literal such as the one in this example would be an awkward way to specify object instructions. Fortunately, PL360 allows the literal field of an Execute Instruction to be another function statement!

Example 3:

       EX(R5,CLI(0,B3(4)))

This is the same as example 2, but using a CLI function statement as the literal field of the EX instruction.

Example 4:

       EX(R7,CLC(0,B2,B3))

This EX instruction would compare up to 256 characters. Registers R2 and R3 contain the starting addresses of the operands to be compared and the lowest byte of R7 contains the count MINUS ONE of the number of characters to be compared. If the object instruction had been an MVC, then a variable number of characters could have been copied from B3 to B2. The important point is that the lowest byte of the EX instruction's modification register (R) must contain one less than the actual number of bytes to be processed. So values from 0 through 255 in the modification register process from one through 256 bytes. This applies to all storage-to-storage instructions which can process variable-length operands up to 256 bytes (including NC, OC, and XC).

TR(S,C,CL) |Translate| | TR(5,#DC00) |

TRT(S,C,CL) |Translate and Test| | TRT(5,#DC00) |

These functions have three parameters: S, C, and CL. C designates the address of the first byte of an operand in storage that is S+1 bytes long. CL designates the address of the first byte of a table which is normally 256 bytes long.

For the TR function, each byte of the C-operand acts as an index into the CL-table. The byte selected from the CL-table replaces the selecting byte in the C-operand. Therefore, the TR function is normally used to convert an operand from one set of codes to another set of codes. However, TR may also be used to rearrange the bytes of a storage value. For example:

       INTEGER PATTERN, DATAVALUE; |-- declarations --|
       PATTERN := #02010300X; |-- cell assignment of pattern --|
       TR(3,PATTERN,DATAVALUE); |-- rearrange --|

If DATAVALUE contained the string "TOPS", PATTERN would contain the string "POST" following the TR function execution. Note that the CL-table is only the 4-byte DATAVALUE in this case.

For the TRT function, each byte of the C-operand again acts as an index into the CL-table. However, the bytes selected from the CL-table do NOT replace the selecting bytes of the C-operand. Instead, each selected table byte is tested. If the selected byte is zero (=#00X), the TRT function continues to the next selecting byte of the C-operand. If the selected byte is non-zero (>#00X), the TRT function sets the condition code to 1 if other C-operand bytes remain untested, otherwise the condition code is set to 2; then TRT terminates by placing the absolute address of the C-operand selecting byte into the lower 24 bits of R1, and the non-zero byte selected from the CL-table into the lowest byte of R2. If all the bytes selected from the CL-table by the C-operand are zero, the TRT function sets the condition code to 0 and terminates without altering R1 or R2. Essentially, TRT is used to scan an operand for characters of special significance. The CL-table would contain zero bytes for all characters to be skipped, and non-zero bytes for those characters of special significance.

CVD(R,X) |Convert to Decimal| | CVD(12,#4E00) |

CVB(R,X) |Convert to Binary | | CVB(12,#4F00) |

These functions both involve a binary value contained in an integer register (R), and an 8-byte packed-decimal value in storage (X). The storage address (X) must designate a double-word boundary. CVD converts a binary value in R into a packed-decimal value in X. CVB converts a packed-decimal value in X into a binary value in R.

Although the packed-decimal value is eight bytes long for these functions, in general, packed-decimal values can vary in length. Each byte of a packed-decimal value consists of a pair of decimal digits, except for the last byte which contains a decimal digit followed by a sign digit. Each digit takes four bits within a byte. The hexadecimal digits A through F are invalid decimal digits. They are recognized as sign digits with A, C, E, or F interpreted as plus, and B or D interpreted as minus. Normally, a sign digit is either C or D (plus or minus).

Examples:

If register R1 contained the decimal value: positive 4095; then R1 would contain: 00000FFF as a hexadecimal (binary) value. Assuming the declaration: LONG REAL DOUBCELL; the instruction: CVD(R1,DOUBCELL) would convert the contents of R1 into a packed-decimal value in DOUBCELL. The eight bytes of DOUBCELL would contain: 000000000004095C after the conversion.

If DOUBCELL contained the packed-decimal value: 000000000065536D, then the instruction: CVB(R2,DOUBCELL) would convert the packed- decimal value in DOUBCELL into a binary value in R2. R2 would contain: FFFF0000 which is the hexadecimal representation of negative 65536.

The range of packed-decimal values that can be converted by the CVB function is: 000002147483648D through 000002147483647C. Values outside this range cause a program interrupt for fixed-point division.

UNPK(N1,N2,C,CL) |Unpack value| | UNPK(10,#F300) |

PACK(N1,N2,C,CL) |Pack value| | PACK(10,#F200) |

These functions operate with two variable-length storage operands, one in packed format and the other in zoned format.

A packed format operand has the general form:

                      ---------- ... ----------
                      |dd|dd|dd|     |dd|dd|dS|
                      ---------- ... ----------

where the d's and S are any hexadecimal digits. Normally, d's are decimal digits and S is a sign digit, i.e., a packed-decimal value.

A zoned format operand has the general form:

                      ---------- ... ----------
                      |Fd|Fd|Fd|     |Fd|Fd|Sd|
                      ---------- ... ----------

where the d's and S are any hexadecimal digits; and the F's are usually hexadecimal F digits, called zone digits. If the d's are decimal digits, then the Fd's are the EBCDIC characters "0" through "9". Thus, the zoned format value F0F2F5F7 corresponds to the character string "0257".

C and CL designate the address of the left-most byte of each operand. The operands can vary in length from one through 16 bytes. N1+1 is the length of the C-operand, and N2+1 is the length of the CL-operand.

For the UNPK function, a zoned format C-operand is created from a packed format CL-operand. The processing proceeds right to left beginning with the right-most byte of each operand. The following example illustrates what happens:

                          -------------
for      START =          |10|25|63|7D|
                          -------------

UNPK(6,3,RESULT,START)

                 ----------------------
yields  RESULT = |F1|F0|F2|F5|F6|F3|D7|
                 ----------------------

Normally, N1 = 2*N2. If N1 > 2*N2, then F0 bytes are used to fill out the C-operand. If N1 < 2*N2, then excess digits of the CL-operand are ignored. Note that the hexadecimal digits of the right-most byte are reversed in position.

UNPK is usually used following a CVD function to convert the packed-decimal value into a printable form. Only the sign digit of the packed-decimal value needs to be modified before doing the UNPK. For example:

       CVD(R1,X);  OI(#F,X(7));  UNPK(LEN,7,Y,X);

where X is eight bytes on a double-word boundary, and Y is LEN+1 bytes.

For the PACK function, a packed format C-operand is created from a zoned format CL-operand. The processing proceeds right to left beginning with the right-most byte of each operand. The following example illustrates what happens:

                 ----------------
for     ORIGIN = |F3|F2|F5|F7|C6|
                 ----------------

PACK(2,4,ANSWER,ORIGIN)

                       ----------
yields  ANSWER =       |32|57|6C|
                       ----------

Normally, N2 = 2*N1. If N2 < 2*N1, then 0 digits are used to fill out the C-operand. If N2 > 2*N1, then excess bytes of the CL-operand are ignored. Note that the zone digits of the CL-operand are ignored, and that the hexadecimal digits of the right-most byte are reversed in position.

ED(S,C,CL) |Edit| | ED(5,#DF00) |

This function is very complex, but very usful in generating reports. Basically, this function converts one or more packed-decimal values in the CL-operand into zoned format, digit by digit, left to right. The C-operand is S+1 bytes long, and special characters in the C-operand indicate where the zoned digits are to be placed. The special characters, called digit selectors, are: #20X and #21X (20 and 21). Each digit selector always causes one decimal digit to be processed from the CL-operand.

The first character of the C-operand is a fill character (usually a blank, asterisk, or other printable character). Processing of the C-operand proceeds in phases:

Phase 1: The fill character replaces all characters in the C-operand, including digit selectors. Phase 1 ends and phase 2 begins when one of two things happens: (1) a digit selector selects a non-zero decimal digit from the CL-operand, in which case the non-zero zoned digit replaces the digit selector; or (2) a #21X digit selector is replaced by the fill character.

Phase 2: Digit selectors are replaced by zoned digits and other characters are left unchanged until one of two things happens: (1) a #22X special character is encountered; or (2) the byte from the CL-operand containing a decimal digit and sign digit is processed by a digit selector. For (1), the #22X character is replaced by the fill character and processing continues as in phase 1. For (2), the digit is processed, then the sign is examined and discarded. If the sign was positive, processing continues as in phase 1; otherwise, processing continues in the current phase (either 1 or 2).

An example of the use of this function is given in the INPUT procedure of the record compare program in Chapter 9.

7.4  Other Functions

The functions covered in this section are not pre-declared. They are user-defined functions that serve a useful purpose. Their names were freely chosen, and you could define similar functions with other names if desired. Each function is described by a heading line that contains a general form function statement followed by the function's declaration as a comment.

Function Statement | Function Declaration |

REDUCE(R) | REDUCE(6,#0600) |

This function corresponds to a BCTR R,0 instruction in assembly language. REDUCE subtracts one from the contents of integer register R without affecting the machine's condition code. Unlike Rx := Rx -- 1 which requires a constant in storage referenced by a 4-byte subtract instruction, REDUCE(Rx) does not require a constant and REDUCE is only a 2-byte instruction.

SETUP(R) | SETUP(6,#0500) |

This function corresponds to a BALR R,0 instruction in assembly language or the BALR(Rx,R0) function defined in section 7.3. SETUP places the lower 32 bits of the PSW into integer register R. This gives us a convenient means for saving the current condition code, and also provides us with the address of the next instruction. Since we can't use a label in any function, there are some interesting applications for SETUP which circumvent that restriction. The following example demonstrates such an application.

       | R3 contains 1,2,3,...,n |  | Select one message from a list |

                   R3 := R3 SHLL 3;  |-- Multiply by 8 --|
                   SETUP(R4);  |-- Next instruction address to R4 --|
       | B4(0) |   EX(R0,B4(R3+4));   |-- Does one LA instruction --|
       | B4(4) |   EX(R0,B4(R3+8));   |-- Does one  R2 := STRING; --|
       | B4(8) |   GOTO TAG;  |-- Branch around list --|
       | B4(12) |     LA(R1,"Message 1 ... ");  R2 := STRING;
       | B4(20) |     LA(R1,"Message 2 ... ");  R2 := STRING;
       | :  :  :  :  :  :  :  :  :  :  :  :  :  :  :  :  :  :  : |
                      LA(R1,"Message n ... ");  R2 := STRING;
       TAG:  |-- R1 and R2 contain message address and length --|

Since R3 contains 8,16,24,... and R4 contains the absolute address of the first EX instruction, we can use R4 as the base register of a stack of 4-byte instructions and do address arithmetic relative to that base!

We could have done this problem with a CASE statement (those interested might like to try it). But CASE would have generated about 6*n additional instruction bytes. So for 25 messages we have saved nearly 150 bytes.

BRANCH(X) | BRANCH(15,#47F0) |

This function is used in conjunction with the SETUP function previously described. BRANCH provides a computed GOTO facility. Thus:

       | R3 contains 0,1,2,...,n |

            R3 := R3 SHLL 2;  |-- Multiply by 4 --|
            SETUP(R2);  BRANCH(B2(R3+4));
               GOTO TAG0;
               GOTO TAG1;
               GOTO TAG2;
            | : : : : : : : |
               GOTO TAGx;   |  x = n-1  |
         TAGn: | Statements for R3 = n   |
         TAGx: | Statements for R3 = n-1 |
       | : : : : : : : : : : : : : : : : |
         TAG2: | Statements for R3 = 2   |
         TAG1: | Statements for R3 = 1   |
         TAG0: | Statements for R3 = 0   |

Note that BRANCH provides a facility similar to a CASE statement. But unlike CASE, it is possible for statements at any TAG to GOTO any other TAG thus allowing code to be shared. Also note that we didn't have a GOTO TAGn; at the end of the list of GOTO statements because BRANCH jumps around the list directly to TAGn when R3 = n.

CR(Rx,Ry) | CR(1,#1900) |

The CR instruction compares integer register Rx against integer register Ry to establish the machine's condition code. This is the same compare instruction as would be generated by: IF Rx = Ry THEN... (excluding IF's branch instruction). CR is frequently used when two variable-length strings are to be compared. For example:

       | 1ST string defined by starting address in R2, length in R1 |
       | 2ND string defined by starting address in R4, length in R3 |
       | Both R1 and R3 contain string lengths:  1 <= length <= 256 |

           REDUCE(R1);  REDUCE(R3);  |-- Reduce both lengths by 1 --|
           IF R1 <= R3 THEN  EX(R1,CLC(0,B2,B4))
                       ELSE  EX(R3,CLC(0,B2,B4));
           IF = THEN CR(R1,R3);  |-- Compare lengths --|
       | Condition Code established by CLC or CR |

The strings are compared through the length of the shorter, and when that results in an ='s condition, the lengths are compared with CR.

SETB(S,C) | SETB(4,#9600) |

RESETB(S,C) | RESETB(4,#94FF) |

These functions correspond to OI and NI functions. They allow bits within a byte to be set or reset. Thus, SETB(1,cell) sets a bit, and RESETB(1,cell) resets that same bit. TM(1,cell) would result in an ON condition if the bit was set, or an OFF condition if it was reset.

7.5  Exercise

Problem 1.

Write function statements in general form for the following function declarations:

(a)  FUNCTION PAUSE(0,#0AFF);
(b)  FUNCTION SETZONE(8,#960F);
(c)  FUNCTION STCK(8,#B205);
(d)  FUNCTION AP(10,#FA00);

Problem 2.

Assume the following data segment has been defined:

       SEGMENT BASE R6;
          INTEGER  X = 0,  Y = #5040605C;
          LONG REAL FILLER;
          ARRAY 20 INTEGER VALUES;
       CLOSE BASE;

True or false: the value in Y can be interpreted as:

(a)  a quoted string of 4 characters ("string")
(b)  a computer instruction
(c)  a binary value
(d)  a packed-decimal value

Problem 3.

For each item marked TRUE in the above problem, do the following:

(a) Using the EBCDIC table in Appendix C, write the value in Y as a quoted string.

(b) Using Appendix D and the identifiers from the data segment above write a PL360 construct corresponding to the instructon executed by EX(R0,Y) .

(c) Write as a variable-length hexadecimal string the packed-decimal value in FILLER resulting from: R4 := Y; CVD(R4,FILLER);

(d) Can the functions: CVB(R5,X); and UNPK(15,7,X,X); both be done successfully (in the order given)? For the CVB function, what is the binary value in R5 written in hexadecimal form? For the UNPK function, using the EBCDIC table in Appendix C, write the unpacked result as a quoted string.

8  Procedures

In the preceding chapters we've covered all of the declarations, statements, constants, and comments available in writing PL360 programs; everything except procedure declarations and statements. We've saved procedures until last because they can utilize everything, including themselves! Essentially, a procedure is a subroutine: a body of code that can be called upon from many different points to do a particular task, and which has the capability of returning to the caller upon completion. By that definition, a procedure needs: (1) an identifying 'name' to make it known to a caller, (2) a 'return' mechanism so the procedure can return to its caller, and (3) the ability to define code. The basic PL360 procedure declaration satisfies these requirements:

PROCEDURE name (Ra); statement;

where 'name' is the procedure's identifier; Ra, called the 'return address' register, is an integer register designator other then R0; and 'statement' is any PL360 statement which defines the procedure's code. By 'code' we mean the machine instructions generated by the procedure's 'statement'.

Once a procedure has been defined, it may be called upon by a procedure statement. The basic form of a procedure statement is:

name

where 'name' is the name of the procedure to be called. Such a call transfers control to the start of the procedure's code, and also places a return address in Ra. Upon completion of the procedure's code, the procedure normally returns to the statement following the procedure statement by transferring control to the address contained in Ra. The machine instructions used to accomplish these feats are the BAL or BALR instructions of a procedure statement, and a BR instruction following the procedure's code.

There are five types of procedures available in PL360: Simple, COMMON, SEGMENT, GLOBAL, and EXTERNAL. Simple and COMMON procedures are called local procedures and they generate code in the program segment containing their definition. Therefore, a local procedure contributes to the 4096-byte limit of the program segment containing it. SEGMENT and GLOBAL procedures are called global procedures and they generate separate program segments, each with a 4096-byte limit. These segments can have the same or different program base registers, and by having many such segments we can compile very large programs. (The main program segment defined in section 3.5 is actually a global procedure as we shall see later.) EXTERNAL procedures do not generate code. Instead, they refer to procedures which have been generated elsewhere. Pre-compiled subroutines in a library would be an example.

Procedure synonyms provide a mechanism for giving another name to a previously declared procedure. This is most handy in $IF-tested code where different procedure names might be involved.

8.1  Local Procedures

There are two types of local procedure:

PROCEDURE name (Ra); statement; | Simple procedure |

COMMON PROCEDURE name (Ra); statement; | COMMON procedure |

COMMON procedures have all the properties of Simple procedures, but they also define an entry point, a property we will cover in the next section. We will use only Simple procedures in this section.

|010|   BEGIN  |-- Outer block containing Simple procedure --|
|020|      EQUATE COPYLEN SYN 16;
|030|      PROCEDURE COPYCORE (R4);  IF R1 > 0 THEN
|040|      BEGIN  |-- Copy R1 bytes from B2 to B3 --|
|050|         FUNCTION REDUCE(6,#0600);
|060|         REDUCE(R1);  WHILE R1 >= COPYLEN DO
|070|         BEGIN  B3(0/COPYLEN) := B2;
|080|            R3 := @B3(COPYLEN);
|090|            R2 := @B2(COPYLEN);
|100|            R1 := R1 - COPYLEN;
|110|         END;
|120|         EX(R1,MVC(0,B3,B2));
|130|      END;
|140|      ARRAY 100 BYTE AREA;
|150|   |-- Main Code of outer block --|
|160|      R3 := @AREA;
|170|      LA(R2,"This string will be placed in AREA");
|180|      R1 := STRING;
|190|      COPYCORE;
|200|      R3 := @AREA(STRING);
|210|      LA(R2," followed by this string.");
|220|      R1 := STRING;
|230|      COPYCORE;
|240|   END

Although the example is correct, assuming there's a data segment to which AREA can belong, we could have placed the strings into the AREA using only cell assignment statements:

       AREA := "This string will be placed in AREA";
       AREA(STRING) := " followed by this string.";

and thus would have completely obviated the need for a procedure. But the example does provide us with a vehicle for further discussion, and that is its main purpose.

In our example there is one procedure declaration for COPYCORE (lines 030 through 130), and two procedure statements which call upon that procedure (lines 190 and 230). The procedure is valid and very useful for copying data of any length from one section of storage to another, although it probably would be coded with 256 instead of COPYLEN to be most efficient.

The procedure 'name' is COPYCORE, its Ra = R4, and its 'statement' is an IF statement whose THEN statement is a block (BEGIN ... END). Within that block there is a function declaration for REDUCE, some function statements, and a WHILE statement whose DO statement is another block containing cell and register assignment statements.

The REDUCE function is not known outside of the procedure. That would be true of any declarations occurring within the procedure's block. So, COPYLEN, COPYCORE, and AREA are known to the main code of the outer block; and COPYLEN, COPYCORE, and REDUCE are known to the main code of the procedure's block. The fact that a procedure declaration's 'statement' knows its own procedure 'name' means we can write recursive procedures (procedures which call themselves)!

Each call to a procedure establishes a return address in that procedure's return address register (Ra). If the procedure is to return to its caller, the lower 24-bit address portion of Ra must be the same at the end of the procedure's code as it was at the time of the call: it must contain the return address. If during the execution of the procedure's code the contents of Ra is changed, then it is YOUR responsibility to save and restore Ra's return address. Typically, Ra is saved in storage either with a cell assignment statement or by including Ra in the Rx through Ry register range of an STM function.

In our example, none of the registers used by COPYCORE are saved, and R4's content is not changed by the procedure's code. By including a declaration such as:

       |045|   ARRAY 3 INTEGER SAVEREGS;

we could save and restore COPYCORE's work registers with:

       |055|   STM(R1,R3,SAVEREGS);
and    |125|   LM(R1,R3,SAVEREGS);

If saving R4 was necessary, we need only change '3' to '4' in all of these new lines.

While we're on the subject of return address registers, let's look at a frequent programming error involving Ra.

|010|   BEGIN  | outer block |
|020|      PROCEDURE A (R14);
|030|      BEGIN  | Code not involving R14 |
|040|      END;
|050|      PROCEDURE B (R14);
|060|      BEGIN  | Code not involving R14 |
|070|         A;  | Call procedure A |
|080|        | More code not involving R14 |
|090|      END;
|100|   | Main code of outer block |
|110|      B;  | Call procedure B  |
|120|   END  | of outer block |

When procedure B is called from line 110, a return address is placed in R14. Procedure B then calls procedure A from line 070 thus placing a new return address in R14. Procedure A executes and returns to line 080. When procedure B finishes execution at line 090, it returns to the address given in R14 which happens to be a return to line 080 (established by the call to A at line 070). We now have an infinite loop! Clearly, either procedure B must save and restore R14 (using storage or some other temporary register), or procedures A and B need different return address registers.

There are instances when a procedure need not return to its caller. A local procedure may GOTO a label in a block containing the procedure's definition. Consider the following example:

|010|   BEGIN  | outer block |
|020|      PROCEDURE SCAN (R7);
|030|      BEGIN  | Statements which establish condition code |
|040|         IF = THEN GOTO TAG;  | Conditional branch |
|050|      END;
|060|   LOOP:  | Main code of outer block |
|070|      SCAN;  | call procedure |
|080|             | other statements following return |
|090|      SCAN;  | call procedure again |
|100|             | other statements following return |
|110|      GOTO LOOP;  | repeat the process |
|120|   TAG:  | procedure eventually branches directly here |
|130|   END  | of outer block |

In this example, SCAN can return normally (to line 080 or 100), or branch directly to the label (TAG at line 120). The effect would be similar to our replacing the procedure calls at both lines 070 and 090 by lines 030 through 050, eliminating the procedure entirely.

There's another fact concerning local procedures and the block containing their definitions that is often overlooked. Since local procedures generate code prior to the main code of the containing block, the compiler must generate an unconditional branch instruction at the beginning of the block to branch around the local procedures. Otherwise, upon entry to the block, we would execute the code of the first local procedure rather than the main code of the block. Normally, only one such branch is necessary; but if local procedures and certain data segment declarations are alternated, the compiler may have to generate more than one branch (branch around local procedures, load a data segment base register, branch around more local procedures, etc.). Notice the assembly language comments in the following example.

|010|   IF R5 = R6 THEN                      |       CR  5,6          |
|020|   BEGIN  | outer block |               |       BNE T.2          |
|030|      EXTERNAL DATA BLANKS BASE R9;     |       L   9,=V(BLANKS) |
|040|      PROCEDURE SWAP (R7);              |       B   T.1          |
|050|      BEGIN  B2 := B2 XOR B3;           | SWAP  XC  0(4,2),0(3)  |
|060|             B3 := B3 XOR B2;           |       XC  0(4,3),0(2)  |
|070|             B2 := B2 XOR B3;           |       XC  0(4,2),0(3)  |
|080|      END;  | of procedure |            |       BR  7            |
|090|      B3 := B3 OR B9;   | main code |   | T.1   OC  0(4,3),0(9)  |
|100|      SWAP;  | local procedure call |   |       BAL 7,SWAP       |
|110|   END;  | of outer block |             | T.2   EQU *            |

8.2  Global Procedures

There are two types of global procedure:

SEGMENT PROCEDURE name (Ra); statement; | SEGMENT procedure |

GLOBAL PROCEDURE name (Ra); statement; | GLOBAL procedure |

Both generate separate program segments. The concepts of segmentation and segment naming were introduced in Chapter 3. The only difference between these two declarations is that for SEGMENT procedures the compiler generates the program segment name; whereas, for GLOBAL procedures the programmer explicitly specifies the program segment name. So for SEGMENT procedures, the 'name' given in the declaration only serves to identify the procedure for call purposes in a manner similar to Simple procedures. The program segment name is chosen by the compiler. But for GLOBAL procedures, 'name' not only identifies the procedure, but also names the program segment.

Segment names are called Control Section or CSECT names. They identify each segment to the LOADER or linkage editor programs that process the object decks created by the compiler. These names define entry points that can be referenced by EXTERNAL declarations. COMMON procedures also define additional entry points within program segments. It is therefore possible to have a single program segment with multiple entry points. The main advantage in having multiple entry points is that all the procedures making up a single program segment share one literal pool and one program base register.

Global procedures may specify the program base register in their declaration. Thus,

SEGMENT PROCEDURE name (Ra) BASE Rp; statement;

GLOBAL PROCEDURE name (Ra) BASE Rp; statement;

where 'Rp' is an integer register identifier other than R0 or 'Ra'. When the BASE portion of the declaration is not given, the default program base register is assumed. The default is usually R15, but the $BASE=nn compiler control card may be used to change the default program base register setting. (See section E.4)

In any of these global procedure definitions, if the 'statement' were to be terminated by a period (.), rather than a semicolon (;), then we would have the definition of a global procedure main program! We've mentioned several times before that the main program block could be written as a global procedure. On the next page you will find a compilation of both a main program block and an equivalent global procedure main program. Study the compilation carefully and notice the columns of numbers to the left of the PL360 source. These columns are described in Appendix F.

Following the source in each example is information about each segment including the segment's number, name, length in hexadecimal, base register, entry points, and external references. Also, a listing of the program segment's machine instructions is included.

PL360 COMPILATION            MAIN PROGRAM DEFINED AS <BLOCK> .

001 0000  000 0000  0001           BEGIN  |-- START OF MAIN PROGRAM --|
001 0018  000 0048  0002 01
001 0018  000 0048  0003           END.  |-- END OF MAIN PROGRAM --|

SEGMENT 000  NAME = SEGN000    LENGTH = 0048  BASE REG = 13

EXTERNAL SYMBOL DICTIONARY
   SEGN000   ENTRY (SD) AT 0000

SEGMENT 001  NAME = SEGN001    LENGTH = 0028  BASE REG = 15

0000    90ECD00C  18ED58D0  F02450E0  D00450D0
0010    E008D703  E010E010  58D0D004  98ECD00C
0020    07FE0000  00000000

EXTERNAL SYMBOL DICTIONARY
   SEGN001   ENTRY (SD) AT 0000
   SEGN000   EXTERNAL REFERENCE
PL360 COMPILATION            MAIN PROGRAM DEFINED AS GLOBAL PROCEDURE .

001 0000  000 0000  0001     GLOBAL PROCEDURE SEGN001 (R14) BASE R15;
014 0000  000 0000  0002     BEGIN  STM(R14,R12,B13(12));  R14 := R13;
014 0006  000 0000  0003 01     BEGIN  GLOBAL DATA SEGN000 BASE R13;
014 000A  015 0000  0004 02               ARRAY 18 INTEGER B13;
014 000A  015 0048  0005           B13(4) := R14;  B14(8) := R13;
014 0012  015 0048  0006           B14(16) := B14(16) XOR B14(16);
014 0018  015 0048  0007
014 0018  015 0048  0008           BEGIN  |-- START OF MAIN PROGRAM --|
014 0018  015 0048  0009 03
014 0018  015 0048  0010           END;  |-- END OF MAIN PROGRAM --|
014 0018  015 0048  0011 02
014 0018  015 0048  0012           R13 := B13(4);  LM(R14,R12,B13(12));
014 0020  015 0048  0013        END;
014 0020  000 0000  0014 01  END.

SEGMENT 015  NAME = SEGN000    LENGTH = 0048  BASE REG = 13

EXTERNAL SYMBOL DICTIONARY
   SEGN000   ENTRY (SD) AT 0000

SEGMENT 014  NAME = SEGN001    LENGTH = 0028  BASE REG = 15

0000    90ECD00C  18ED58D0  F02450E0  D00450D0
0010    E008D703  E010E010  58D0D004  98ECD00C
0020    07FE0000  00000000

EXTERNAL SYMBOL DICTIONARY
   SEGN001   ENTRY (SD) AT 0000
   SEGN000   EXTERNAL REFERENCE

The preceding block and global procedure programs generate the same number of segments with the same segment names, lengths, base registers, entry points, and external references. Even the generated machine instructions are identical! The segment numbers assigned by the compiler are different, but that difference is not carried beyond the compilation. The only major difference is that for a block program, entry and exit instructions are generated by the compiler; whereas for a global procedure program, all instructions must be explicitly generated by statements or declarations. There is one other difference that is not apparent from looking at the examples. The object decks produced would be identical except for the END cards (see Appendix F).

A close examination of the machine instructions composing SEGN001 reveals that the third instruction (58D0F024) would load register R13 from an integer cell at displacement 024 based on register R15 (a cell within SEGN001). Relative address 0024 of SEGN001 is compiled as an integer cell containing zeros. When the segments are loaded for execution, the external reference to SEGN000 directs the LOADER to fill in this cell with the absolute address assigned to the SEGN000 entry point. Eventually SEGN001 receives control from the LOADER with R15 containing SEGN001's entry point address, R14 containing a return address, and R13 containing the address of an 18-integer save area. SEGN001 first saves all registers except R13 in the LOADER's save area, places R13 in R14, and then loads R13 with the address of our own save area (SEGN000). Then the LOADER's save area address is stored into our save area, our save area address is stored into the LOADER's save area, and the stored value of R15 is zeroed out in the LOADER's save area. These actions complete what is known as the standard OS linkage convention entry process (see Appendix G).

We now begin the main execution phase. In our example programs there are no instructions for this phase, but if there had been instructions (statements, etc.) we would have to be careful about preserving the contents of our program and data area base registers (R15 and R13). The program base register is used to reference all literals (including external references) and for branching (GOTO's, WHILE's, etc.). The data area base register points to our save area (used by procedures we call) and any other cells declared for that segment.

Upon exit from a main program, R13 is reloaded with the LOADER's save area address using the stored value in our save area, all the other saved registers are reloaded from the LOADER's save area, and finally we branch back to the LOADER using the return address register (R14). Note that we return R15 containing zero because we zeroed out the stored value of R15 upon entry. Using the program base register for returning a response to the caller is a fairly common practice. We will examine this concept in more detail a little later.

Let's look now at nested global procedures. The following example contains a wealth of information and we shall be using this example or variations of it throughout the remainder of this section.

001 0000  000 0000  0001     GLOBAL PROCEDURE A (R14);

014 0000  000 0000  0002     BEGIN
014 0000  000 0000  0003 01     PROCEDURE B (R1);
014 0004  000 0000  0004        BEGIN
014 0004  000 0000  0005 02        A;
014 0006  000 0000  0006           B;
014 000A  000 0000  0007        END;
014 000C  000 0000  0008 01     GLOBAL PROCEDURE C (R2) BASE R9;

015 0000  000 0000  0009        BEGIN
015 0000  000 0000  0010 02        PROCEDURE D (R3);
015 0004  000 0000  0011           BEGIN
015 0004  000 0000  0012 03           A;
015 000E  000 0000  0013              B;
015 001A  000 0000  0014              C;
015 001C  000 0000  0015              D;
015 0020  000 0000  0016           END;
015 0022  000 0000  0017 02        A;
015 002C  000 0000  0018           B;
015 0038  000 0000  0019           C;
015 003A  000 0000  0020           D;
015 003E  000 0000  0021        END;

014 000C  000 0000  0022 01     A;
014 000E  000 0000  0023        B;
014 0012  000 0000  0024        C;
014 001C  000 0000  0025     END.

SEGMENT 015  NAME = C          LENGTH = 0048  BASE REG = 09

0000    47F09022  58F09044  05EF5890  E03658F0
0010    90444510  F0045890  102A0529  45309004
0020    07F358F0  904405EF  5890E018  58F09044
0030    4510F004  5890100C  05294530  900407F2
0040    00000000  00000000

EXTERNAL SYMBOL DICTIONARY
   C         ENTRY (SD) AT 0000
   A         EXTERNAL REFERENCE

SEGMENT 014  NAME = A          LENGTH = 0028  BASE REG = 15

0000    47F0F00C  05EF4510  F00407F1  05EF4510
0010    F0045890  F0240529  58F02008  07FE0000
0020    00000000  00000000

EXTERNAL SYMBOL DICTIONARY
   A         ENTRY (SD) AT 0000
   C         EXTERNAL REFERENCE

The first thing to notice about the preceding example is the pair of integer cells that were compiled as zeros at the end of each segment. When the segments are loaded, the LOADER will place into these cells the absolute address of the entry and external reference associated with each segment.

Next you should notice that B is a local procedure of A, and D is a local procedure of C. A and C are program segments which begin with an unconditional branch instruction to branch around their local procedure. Both B and D begin at relative location 0004 within their respective segments.

The procedure statements in the example are all the procedure statements possible. Procedure B cannot call upon C or D because they are defined after B; and A cannot call upon D because D is defined within C and is not known outside of C. (We will examine a way of making D known to A in the next section on external procedures.)

The machine instructions generated by the various procedure statements fall into the following categories. If we use 'a' to mean a return address register (Ra), and 'p' or 'q' to mean a program base register (Rp), then global procedures are called by a BALR instruction (05ap), and local procedures are called by a BAL instruction (45a0pddd). If the procedure being called is in a segment other than the calling segment, the the call instruction is preceded and followed by a load instruction (58p0qddd and 58q0addd).

We mentioned in the main program example that a procedure can return a response in its program base register. In this example there's no problem for procedures of one segment to return such a response to calls from the other segment because the segments have different program base registers. But what about calls to procedures within the same segment, or calls between segments that have the same program base register? PL360 provides the answer with another form of procedure statement:

name (Rm)

Rm is an integer register designator other than the program base register of the segment containing such a call. Immediately following the BALR or BAL instruction, the compiler generates instructions that place the called procedure's program base register into Rm (using an LTR m,p) before restoring the calling procedure's program base register (see Appendix G).

Finally let's consider restrictions on GOTO statements. Since global procedure segments could be loaded anywhere in relation to each other, there's no guarantee that a GOTO statement can generate a branch instruction to a label that would be within range of the program base register. Therefore, GOTO statements are not allowed to branch out of a segment! A 'label' defined in A would not satisfy a 'GOTO label' occurring in C or D. However, if we rewrote B as:

       PROCEDURE B (R1);  GOTO label;

then either C or D could call upon B to effect a branch to the 'label' defined in A! Procedures permit us to circumvent the GOTO restriction.

8.3  External Procedures

The basic form of an external procedure declaration is:

EXTERNAL PROCEDURE name (Ra); statement;

EXTERNAL PROCEDURE name (Ra) BASE Rp; statement;

Since external procedure declarations generate no code, the 'statement' is usually the NULL statement. Any other statement would be compiled and syntax checked as if code were being generated, but no program segment would be produced. However, if the statement was a block containing a global data segment declaration, the data segment would be produced!

An external procedure declaration is used to reference a procedure entry point or program segment not otherwise known to the block containing the external declaration. In effect, an external procedure declaration provides the compiler with all the information needed to satisfy a call to the named procedure. When such a call is made with a procedure statement, the compiler generates the code necessary for linking to the external procedure. The call also causes the compiler to place information in the object deck of the program segment containing the call, information that directs the LOADER to resolve the external reference.

In the nested global procedure example originally given in the preceding section, if local procedure D had been declared as a COMMON procedure, then D would be defined as an entry point. However, D would still not be known to global procedure A because of the rules of 'scope' first discussed in section 3.4.2. By including an external declaration in procedure A such as:

       EXTERNAL PROCEDURE D (R3) BASE R9;  NULL;

it would be possible for procedure A to call upon D. Of course if D was not an entry point, such a call would still require resolution at load time or would result in an undefined external reference.

Notice that the external declaration for D defines D's return address register (R3), but C's program base register (R9). As a COMMON procedure, D is local to C and so uses C's program base register.

If E were declared as an external procedure at the very beginning of global procedure A, then any call to A could be replaced by a call to E. Any reference to E would have to be resolved at load time for the segment containing the reference; but if E was never referenced, we could leave out the external procedure declaration. An external procedure declaration by itself does not force resolution; only a call or similar reference forces resolution at load time.

8.4  Pre-declared Procedures

There are 12 external procedures pre-declared by the PL360 compiler. The equivalent declarations follow:

       EXTERNAL PROCEDURE READ (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE WRITE (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE PAGE (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE PUNCH (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE PRINT (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE OPEN (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE GET (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE PUT (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE KLOSE (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE CANCEL (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE VALTOBCD (R14) BASE R15;   NULL;
       EXTERNAL PROCEDURE BCDTOVAL (R14) BASE R15;   NULL;

If any of these procedures are referenced, suitably compiled or assembled routines must be provided in the link/loading process. At most installations there is a PL360 runtime library containing these routines. All assume that R13 contains the address of an 18-integer save area. The remaining specifications for these routines follows:

   READ   Read an 80-character record from the system  input  data  set
          and assign that record to the memory area designated  by  the
          address  in  register R0.  The condition code returned is set
          to  2  if no record could be returned  due to an  end-of-file
          condition;  otherwise, it is set to 0.
   WRITE  Write a 133-character record to the system listing  data set.
          A   132-character  record  is  taken  from  the  memory  area
          designated by the address in register R0 and prefixed  by  an
          appropriate  carriage control character.  A control character
          indicating a new page  is  used  after  60  lines  have  been
          written  on  a page, otherwise a control character indicating
          the next line is used.  The first line is written  on  a  new
          page.
   PAGE   A control character indicating a new page is established  for
          the next output record transmitted by  WRITE  to  the  system
          listing data set.
   PUNCH  Write the 80-character record designated by  the  address  in
          register R0 to the system punch data set.
   PRINT  Write the 133-character record designated by the  address  in
          register R0 to the system  listing  data  set.   The  calling
          program  provides a control character as the first character.
          These lines are not counted.

All of the preceding routines restore all registers before return. The system input, listing, and punch data sets are: SYSIN, SYSPRINT, and SYSPUNCH in an OS, VS, or DOS environment. In MTS, they are: SCARDS, SPRINT, and SPUNCH. Each of the data sets is opened upon initial reference and is closed by the operating system at the end of the run.

The following routines are not defined in an MTS environment.

   OPEN   At entry, register R0 must be 0 if the data set is to  be  an
          output data set,  or 1 if the data set is to be an input data
          set.   Register R2 must contain the address of an 8-byte area
          containing a unique data set name.  (This  is  taken  as  the
          ddname in an OS environment and as the symbolic data set name
          in a DOS environment.) In an OS environment, register R1 must
          contain  the  address  of  a  100-byte fullword  aligned area
          which, following the open,  will  contain  the  data  control
          block.   In  a  DOS environment, register R1 must contain the
          address of a separately assembled DTF  table  that  describes
          the  data  set.   The data set is made ready for input/output
          operations.  All registers are restored.
   GET    At entry, register R1 must contain the  address  of  a  table
          which describes the data set.  (In  an  OS  environment  this
          table  is  called  the  data  control  block  and  in  a  DOS
          environment  it  is  called  the  DFT  table.)  Upon  return,
          register  R1  contains the address of the next logical record
          in the data set.  (The first call of  GET  returns  with  the
          address of the first logical record.)  When an end-of-file is
          reached, the condition code is set to 2;  normally it is  set
          to 0.  All registers, except R1, are restored.
   PUT    At entry, register R1 must contain the  address  of  a  table
          which describes the  data  set.   Upon  return,  register  R1
          contains  the  address  of  an area in which the next logical
          record to be output is to be built.  All other registers  are
          restored.
   KLOSE  At entry, register R1 must contain the  address  of  a  table
          which describes the data set.  The corresponding data set  is
          closed   and   no  further  input/output  operations  can  be
          performed with it unless  it  is  opened  again.   In  an  OS
          environment,  the  contents  of  register R0 is also an input
          parameter to this subroutine:  if R0 = 0, the DISP option  of
          the   DD   statement   is  used  to  determine  final  volume
          positioning;  if R0 <= 0, the volume is positioned at the end
          of the  data set;  and if R0 > 0, the volume is positioned at
          the beginning of the data set.  All registers are restored.
   CANCEL The job, including all future job steps, is cancelled.   This
          routine is defined only in a DOS environment.

The two subroutines described next are used to convert the EBCDIC representation of a number into an internal representation of that number, or vice versa. A slightly more conventional number representation is used by these routines than that of the PL360 compiler. Numbers are interpreted according to conventional decimal notation. Scale factors for REAL and LONG REAL numbers are indicated by an apostrophe, and LONG REAL numbers must end in an L. Positive and negative numbers are indicated by standard + and -- signs (no sign indicates positive). A number cannot have imbedded blanks and must be terminated by a blank. The imaginary part of a complex number must end in the character I (e.g., 5.7+3.8I).

VALTOBCD This procedure converts an internally stored value to an EBCDIC
         representation.  At entry,

         R1 contains the address of an area to receive the EBCDIC
      representation.
         R2 indicates the type:
              1 = integer
              2 = real
              3 = long real
              4 = complex
              5 = long complex
         R3 contains the field length (>= 1).

         The value to be converted is in R0, F0, F01, F0 and F2, or F01
         and F23, depending on the type (in that order).

         A return code is left in R15:
            0 -> successful conversion
            1 -> field size too small
            2 -> invalid field size
         When the field size is too small to  receive  the  value,  the
         field is filled with asterisks (*).

         All registers, except R14 and R15, are preserved.
BCDTOVAL This procedure converts an EBCDIC representation of a number to
         an internal number.  At entry,

         R1 contains the address of the EBCDIC representation (possibly
      preceded by blanks).
         R2 indicates type (see VALTOBCD).

         The resulting value is left in R0, F0, F01, F0 and F2, or  F01
         and F23, depending upon the type.

         A return code is left in R15:
             0 -> successful scan
             1 -> invalid character in input string
             2 -> missing 'I' on imaginary part
             3 -> nonblank terminator
             4 -> number scanned is not assignment compatible
                  (e.g., a decimal point is found when R2 = 1)
             5 -> integer too large

         Upon  exit,  R1  contains  the  address  of   the   terminator.
         Registers R2-R13 are restored.

Since both of these routines return a response code in their program base register (R15), they are frequently called by that form of procedure statement which allows the program base register to be saved in some other register, such as: VALTOBCD(R6) or BCDTOVAL(R7).

The examples which follow demonstrate the use of some of these pre-declared procedures.

BEGIN  |--- First sample program ---|

   FUNCTION REDUCE(6,#0600);
   |-- This function is used to subtract 1 from
       the contents of an integer register. --|

   ARRAY 80 BYTE  CARD;  |-- Input area for READ --|

   ARRAY 133 BYTE  LINE = 133(" ");
   |-- Output area for WRITE or PRINT, pre-blanked --|

   |-- End of declarations, begin sample statements. --|

   R0 := @CARD;  IF  READ;  ~= THEN GOTO EXIT;
   |-- Read an input card image from the system input data set
       into the CARD area, and exit if no card read.  --|

   R1 := @CARD;  R2 := 1;  IF  BCDTOVAL(R5);  ~= THEN GOTO EXIT;
   |-- Assuming CARD contains an integer value in character form
       terminated by a blank, then convert to binary and place
       in R0.  R5 receives the return code indicating success or
       failure of the conversion.  If ~= condition, then exit. --|

   REDUCE(R0);  |-- Subtract 1 from the returned value. --|

   R1 := @LINE(3);  R2 := 1;  R3 := 15;  VALTOBCD;
   |-- Convert the contents of R0 to character form and store
       right-justified in a 15-character field beginning at
       LINE(3);  i.e., LINE(3) through LINE(17). --|

   R0 := @LINE;  WRITE;
   |-- Write the LINE image to the system listing data set
       with appropriate control character supplied by
       WRITE procedure. --|

EXIT:  |-- End of program --|
END.

BEGIN  |--- Second sample program ---|

   ARRAY 25 INTEGER  DCB1,  DCB2;
   |-- Data Control Block space for two data sets --|

   SHORT INTEGER  LRECL1 SYN DCB1(#52),
                  LRECL2 SYN DCB2(#52);
   |-- Halfwords within the Data Control Blocks which
       contain record lengths.  Normally, used with an
       input data set to determine the length of an input
       record, and with an output data set to establish the
       length of an output record. --|

   |-- End of declarations, begin sample statements. --|

   LA(R2,"INFILE  ");  R0 := 1;  R1 := @DCB1;  OPEN;
   |-- Open an input data set for processing. --|

   LA(R2,"OUTFILE ");  R0 := R0-R0;  R1 := @DCB2;  OPEN;
   |-- Open an output data set for processing. --|

LOOP:  |-- Assume both data sets define records of unknown length. --|

   R1 := @DCB1;  IF  GET;  ~= THEN GOTO FINISH;
   |-- Input a record from the data set defined by the first Data
       Control Block (INFILE).  The address of the first byte
       of the input record is in R1, and the record length is
       contained in LRECL1.  If no  record  was  input,  then
       branch to FINISH (end-of-file condition). --|

   R2 := R1;  |-- Save the input record address. --|

   R3 := LRECL1 =: LRECL2;
   |-- Establish the length of the output record to
       be the same as that of the input record. --|

   R1 := @DCB2;  PUT;
   |-- Determine the address of where to put the output
       record for the data set defined by the second Data
       Control Block (OUTFILE).  The destination address
       is contained in R1 upon return from PUT. --|

   REDUCE(R3);  WHILE R3 >= 256S DO
   BEGIN  B1(0/256) := B2;  R3 := R3 - 256S;
      R1 := @B1(256);  R2 := @B2(256);
   END;  EX(R3,MVC(0,B1,B2));
   |-- Move the input record into the output record area. --|

   GOTO LOOP;  |-- Loop till end-of-file condition. --|

FINISH:  |-- end-of-file --|

   R1 := @DCB1;  R0 := R0-R0;  KLOSE;
   R1 := @DCB2;  R0 := R0-R0;  KLOSE;
   |-- Close input and output data sets. --|

   |-- End of program --|
END.

8.5  Procedure Synonyms

The PROCEDURE synonym declaration provides an alternate procedure name for a previously declared procedure. For example:

      EXTERNAL PROCEDURE GETMAIN (R14);  NULL;
      PROCEDURE CALLMAIN SYN GETMAIN;
      PROCEDURE NEWCALL SYN CALLMAIN;

Any procedure statement that refers to either CALLMAIN or NEWCALL will be treated like a reference to GETMAIN. Within $IF-tested code, this facility makes it possible for different versions. For example:

        EXTERNAL PROCEDURE APPLE (R14);  NULL;
    $IFT
        EXTERNAL PROCEDURE ORANGE (R14);  NULL;
    $END
    $IFF
        PROCEDURE ORANGE SYN APPLE;
    $END
        |-- Call 1st procedure --|   APPLE;
        |-- Call 2nd procedure --|   ORANGE;

In one environment, APPLE and ORANGE are different procedures. In the other environment, ORANGE is an alternate name for APPLE. Here's another example:

    $IFT
        EXTERNAL PROCEDURE VI (R14);  NULL;
        PROCEDURE EDITOR SYN VI;
    $END
    $IFF
        EXTERNAL PROCEDURE XEDIT (R14);  NULL;
        PROCEDURE EDITOR SYN XEDIT;
    $END
        EDITOR;  |-- call the appropriate editor --|

8.6  Miscellaneous

A number of items were touched upon in earlier chapters concerning procedures. One was the GOTO restriction mentioned in Chapter 6. At the end of section 8.2, we discussed how local and COMMON procedures allow us to circumvent the GOTO restriction. Here is an example:

       GLOBAL PROCEDURE A (R14);
       BEGIN  COMMON PROCEDURE B (R14);  GOTO TAG;
          EXTERNAL PROCEDURE C (R14);  NULL;
       |-- Main code of A --|
          C;  |-- call to C --|
       |-- More code in A --|
    TAG: |-- location in A of concern to C --|
       |-- More code in A --|
       END.  |-- of procedure A --|

       GLOBAL PROCEDURE C (R14);
       BEGIN  EXTERNAL PROCEDURE B (R14);  NULL;
       |-- Main code of C --|
          IF conditions THEN  B;  |-- conditional call to B --|
       END.  |-- of procedure C --|

Notice that C either exits normally or calls upon B. A normal return would take us back to the code immediately following the call to C within A; but a call to B would take us back to TAG: within A since B branches directly to TAG.

We also deferred discussion of @-operators and @@-operators in cell initialization and integer register assignment statements when these operators were used with procedure names. For example:

       BEGIN  PROCEDURE FIXER (R5); statement;
          SHORT INTEGER X = @FIXER;
          INTEGER Y = @FIXER;
          INTEGER Z = @@FIXER;

          R1 := @FIXER;
          R2 := Z;
       END

@FIXER in the cell initializations for X and Y creates a Bddd field where B is the program base register number associated with the program segment containing FIXER, and ddd is the displacement within that program segment of the start of FIXER's statement. The SHORT INTEGER cell would contain Bddd in hexadecimal, and the INTEGER cell would contain 0000Bddd in hexadecimal. The INTEGER cell Z would be initialized to the absolute address of the start of FIXER's statement by the loader or linkage editor.

The register assignment: R1 := @FIXER; creates an integer cell in the program's literal pool which is initialized by the loader or linkage editor in the same manner as cell Z. Thus, R1 := @FIXER; produces the same absolute address in R1 as R2 := Z; produces in R2.

We can make use of @procname in register assignments in the following way:

       BEGIN  PROCEDURE LITERALS (R1);
          BEGIN  LA(R1,"message 1");  R2 := STRING;
                 LA(R1,"message 2");  R2 := STRING;
                 LA(R1,"longer message");  R2 := STRING;
             |-- other messages, total of n-1 messages --|
          END;
       |-- assume R3 contains value from 0 thru n --|
          R3 := R3 SHLL 3;  |-- i-th value becomes i*8 --|
          R4 := @LITERALS;  |-- address of start of LITERALS --|
          EX(R0,B4(R3));    |-- sets R1 = address of message --|
          EX(R0,B4(R3+4));  |-- sets R2 = message length --|
       |-- other code making use of R1 and R2 --|
       END

This example demonstrates the use of a procedure as a code generator. We saw a similar example of code generation in section 7.4 under the discussion of the SETUP function; but using a procedure to generate code is a much better method.

Sometimes you don't know if an external procedure actually exists. You can create a "weak external reference" by using the @@-operator.

       BEGIN  EXTERNAL PROCEDURE module (Rn);  NULL;
          Rx := @@module;  |-- creates weak external reference --|
          IF Rx ~= 0 THEN module;  |-- Only call if it exists. --|
       END

The module must not have been referenced in this program segment prior to the @@module usage in the register assignment statement. Notice that the second reference is a normal call to the module, but because the @@module reference occurred first, the module is assigned a weak external reference in this program segement. If the module is linked, Rx would be non-zero, and the normal call can be made. This technique can be used with any optionally linked modules, including data segments.

       BEGIN  EXTERNAL PROCEDURE table (R1);  NULL;
          Rx := @@table;  IF Rx ~= 0 THEN  |-- table linked --|
          BEGIN  |-- process the contents of the table --|
          END
       END

Finally, it should be noted that the form of a procedure statement and the form of a function statement (for functions with no parameters) are exactly the same! Both are just the 'name' of the procedure or function. This fact can be used during the development of a program where it may be inconvenient to code some procedure right away, but where you may still wish to have calls to such procedures in the main code. This can be accomplished by defining a type-0 function in place of the procedure. For example:

       BEGIN  FUNCTION ALPHA(0,#0700);
       |-- main code of the program --|
          ALPHA;  |-- call to procedure ALPHA --|
       END

The 'call' is just a halfword branch instruction that does not branch (0700)! At some future time, ALPHA will be defined as a procedure. If the main code assumes that ALPHA establishes a condition code dependent upon some register content, then a type-0 function might still be used to establish both the register content and condition code. For example, FUNCTION ALPHA(0,#1B22); defines an instruction equivalent to R2 := R2-R2. Of course, if a function can't serve the purpose, then a mock version of the procedure would have be coded.

8.7  Exercise

Write a main block program that will READ a card containing three (3) numbers seperated from each other by blanks.

These numbers represent respectively:

  MONTH  (1 through 12)
  DAY    (1 through 31)
  YEAR   (00 through 99 of 20th century, i.e., 19xx)

Compute the 'Day-of-the-Week', such as: FRIDAY

Write out: 'Day-of-the-Week' 'Name-of-the-Month' 'Day', 19'Year' such as: FRIDAY JAN. 26, 1973

The 'Day-of-the-Week' takes 11 character positions (blank filled)

  The 'Name-of-the-Month' takes 5 character positions (blank filled):

               JAN.   FEB.   MARCH  APRIL  MAY    JUNE
               JULY   AUG.   SEPT.  OCT.   NOV.   DEC.

Check that the DAY specified is correct for the MONTH specified. Also, check that the MONTH and YEAR are valid; i.e., 29 is valid for FEB. if it is a leap year. Note: 1900 was not a leap year.

If an error is detected, write out the input card prefaced by:

   *** ERROR ***

The algorithm for computing the 'Day-of-the-Week' is:

Given the three numbers M, D, and Y; then using integer arithmetic compute the remainder of the division by 7 of

        Y/4 + Y + D + Offset(M)

where 'Offset' is the following table of numbers:

        0,3,3,6,1,4,6,2,5,0,3,5,6,2

and M has been incremented by 12 if JAN. or FEB. of leap year; the remainder, 0 through 6, corresponds to the 'Day-of-the-Week' from SUNDAY through SATURDAY. For years in the 21st century (20xx), add 6 in the formula above.

9  Sample Programs

This chapter contains various sample programs. Some are complete programs; others are global procedure programs that would be linked as a subroutine of other main programs.

Our first example is a complete main program. The first page of this example lists the Job Control Language (JCL) and other information associated with running the program on Stanford University's VS operating system. The job required two steps: the PL360 compile step, and the GO or execution step. Notice that each step requests some PGM to be EXECuted. The PL360 step invokes the PL360 compiler with program source input to come from SYSIN, listing output to go to SYSPRINT, and object deck output to go to SYSGO (or SYSPUNCH). The GO step invokes the LOADER with object deck input to come from SYSLIN, pre-compiled subroutines to come from SYSLIB, list output to go to SYSLOUT, and execution list output to go to SYSPRINT. SYSPUNCH would receive any punch output generated by the program. In this case, the program does not generate punch output, so SYSPUNCH is not used. SYSUDUMP would receive abnormal termination dump output if the program were to abnormally terminate. The IEF messages output by VS indicate what resources were used by each step.

The pages entitled 'PL360 ... PATH FINDER PROGRAM' are all output by the PL360 compile step. Some of the original output pages have been modified in order to fit them on the pages in this text. The first and second PL360 pages contain a portion of the compiler output. Only the 'reference number' and 'block level' fields are shown, along with the source program. The third PL360 page contains segment information including a dump in hexadecimal of the generated segments. The last PL360 page is a cross-reference listing of the identifiers used by the compiled program. (See Appendixes E and F.)

The last page of the example contains output from the LOADER and the executed PL360 program. The LOADER reads the PL360 object decks and loads the program into core resolving any undefined external references using SYSLIB. The LOADER map output shows the absolute address assigned to each segment (SD) or entry point (LR). The output from the executed PL360 program follows beginning with the first ANSWER and continuing to the end of the example.

This first example contains a local procedure named CHECK which is recursively called. The program computes the number of unique paths that can be taken by a chess rook moving from the lower left corner to the upper right corner of a square board (RANK x RANK). The rook is not allowed to cross its path while traversing the board. The ANSWERs given are the total number of unique paths for a board of order RANK. The numbers below each ANSWER indicate half the total number of paths of length 'm' squares. (Note: the answer for a 7 x 7 board was computed once, requiring a stand-alone computer and eight hours of compute time!)

          H A S P    J O B    L O G

$16.17.26 JOB  537 -- RLGQG187 -- BEGINNING EXEC - INIT 11- CLASS E
$16.19.22 JOB  537 END EXECUTION.
//RLGQG187 JOB RLG$GQ,CLASS=E
***JOBPARM HOLD=OUTPUT
//PL360 EXEC PL360CG
XXPL360CG PROC
XXPL360 EXEC PGM=PL360
***
***      PL360 STEP
***
XXSYSPRINT DD SYSOUT=A
XXSYSPUNCH DD SYSOUT=B
XXSYSGO    DD DSN=&&LOADSET,UNIT=SYSDA,SPACE=(3120,(80,40),,,ROUND),
XX             DCB=(RECFM=FB,BLKSIZE=3120,LRECL=80),DISP=(MOD,PASS)
//PL360.SYSIN DD *
IEF236I ALLOC. FOR RLGQG187 PL360    PL360
IEF237I D32   ALLOCATED TO SYSPRINT
IEF237I D20   ALLOCATED TO SYSPUNCH
IEF237I 517   ALLOCATED TO SYSGO
IEF237I D00   ALLOCATED TO SYSIN
IEF142I - STEP WAS EXECUTED - COND CODE 0000
IEF285I   SYS75275.T103115.RV000.RLGQG187.LOADSET      PASSED
IEF285I   VOL SER NOS= SCR003.
XXGO EXEC PGM=LOADER,PARM='MAP',COND=(0,NE,PL360)
XXSYSLOUT  DD SYSOUT=A
XXSYSPRINT DD SYSOUT=A
XXSYSPUNCH DD SYSOUT=B
XXSYSUDUMP DD SYSOUT=A
XXSYSLIB   DD DSN=SYS2.PL360LIB,DISP=SHR
XXSYSLIN   DD DSN=*.PL360.SYSGO,DISP=(OLD,DELETE)
//
IEF236I ALLOC. FOR RLGQG187 GO       PL360
IEF237I D32   ALLOCATED TO SYSLOUT
IEF237I D34   ALLOCATED TO SYSPRINT
IEF237I D20   ALLOCATED TO SYSPUNCH
IEF237I D35   ALLOCATED TO SYSUDUMP
IEF237I 511   ALLOCATED TO SYSLIB
IEF237I 517   ALLOCATED TO SYSLIN
IEF142I - STEP WAS EXECUTED - COND CODE 0000
IEF285I   SYS2.PL360LIB                                KEPT
IEF285I   VOL SER NOS= SYS16E.
IEF285I   SYS75275.T103115.RV000.RLGQG187.LOADSET      DELETED
IEF285I   VOL SER NOS= SCR003.
IEF375I  JOB /RLGQG187/ START 75275.1617
IEF376I  JOB /RLGQG187/ STOP  75275.1619 CPU   1MIN 07.45SEC
PL360 COMPILATION            PATH FINDER PROGRAM

  .. Reference Number
  :   .. Block Level Number
  :   :  .. Pl360 Program Source
  :   :  :
  :   :  :
0001     BEGIN  |-- FIND NUMBER OF PATHS --|
0002 01     INTEGER RANK = 2;

0003        ARRAY 132 BYTE ANSWER = 132(" ");

0004        EQUATE SQUARES SYN 64,
0005               STAKL SYN SQUARES * 3,

0006               SPACE SYN SQUARES * 5;

0007        ARRAY STAKL INTEGER STACK;

0008        ARRAY SPACE BYTE TABLES;

0009        BYTE FLAGS SYN TABLES(_1),         |-- SYNONYMS used --|

0010             TABLE1 SYN FLAGS(SQUARES),    |-- to allow for --|

0011             TABLE2 SYN TABLE1(SQUARES),   |-- references from --|

0012             TABLE3 SYN TABLE2(SQUARES),   |-- 1 thru SQUARES --|

0013             TABLE4 SYN TABLE3(SQUARES);

0014
0015     |-- CHECK SUBROUTINE, SQUARE PROCESSOR --|
0016
0017     PROCEDURE CHECK (R2);  IF R4 ~= 0 THEN |-- VALID SQUARE --|

0018     BEGIN  IC(R7,FLAGS(R4));  IF R7 = 0 THEN |-- EMPTY --|
0019 02     IF R4 = R9 THEN |-- DESTINATION SQUARE --|
0020        BEGIN R5 := @B5(1);  R8 := 1 + B1(8);  B1(8) := R8;
0021 03     END ELSE  |-- ENTER NEW SQUARE --|
0022 02     BEGIN  STM(R2,R3,B1);
0023 03        R1 := @B1(12);  R3 := R4;
0024           STC(R6,FLAGS(R3));  |-- OCCUPY CURRENT SQUARE --|
0025           IC(R4,TABLE1(R3));  CHECK;  |-- TRY 'UP' --|
0026           IC(R4,TABLE2(R3));  CHECK;  |-- TRY 'RIGHT' --|
0027           IC(R4,TABLE3(R3));  CHECK;  |-- TRY 'DOWN' --|
0028           IC(R4,TABLE4(R3));  CHECK;  |-- TRY 'LEFT' --|
0029           STC(R0,FLAGS(R3));  |-- UNOCCUPY CURRENT SQUARE --|
0030           R1 := R1 - 12;  LM(R2,R3,B1);
0031        END;  |-- RETURN TO CALLING SQUARE OR MAIN CODE --|
0032 02  END;
0033 01
PL360 COMPILATION            PATH FINDER PROGRAM

  .. Reference Number
  :   .. Block Level Number
  :   :  .. Pl360 Program Source
  :   :  :
  :   :  :
0034     |--  MAIN CODE --|
0035
0036        WHILE RANK <= 6 DO
0037        BEGIN  |-- PROCESS PATH FOR CURRENT RANK --|
0038 02        R0 := R0-R0;  R1 := @STACK;  R2 := @TABLES(SPACE);
0039           WHILE R1 < R2 DO  |-- INITIALIZE TABLES --|
0040           BEGIN B1 := R0;  R1 := @B1(4);
0041 03        END;   |-- STORE 'UP' SQUARE NUMBERS --|
0042 02        R10 := RANK;  R9 := R10 * R9;  |-- RANK * RANK --|
0043           R1 := 1;  R2 := R1 + R10;  WHILE R2 <= R9 DO
0044           BEGIN  STC(R2,TABLE1(R1));  R1 := @B1(1);  R2 := @B2(1);
0045 03        END;   |-- STORE 'RIGHT' SQUARE NUMBERS --|
0046 02        R1 := 2;  R2 := 3;  WHILE R2 <= R9 DO
0047           BEGIN  STC(R2,TABLE2(R1));  R1 := @B1(1);  R2 := @B2(1);
0048 03        END;  R3 := R9 - R10;  |-- STORE 'DOWN' SQUARES --|
0049 02        R2 := 2;  R1 := R2 + R10;  WHILE R2 < R3 DO
0050           BEGIN  STC(R2,TABLE3(R1));  R1 := @B1(1);  R2 := @B2(1);
0051 03        END;   |-- STORE 'LEFT' SQUARE NUMBERS --|
0052 02        R2 := 1 + R10;  R1 := @B2(1);  WHILE R2 < R3 DO
0053           BEGIN  STC(R2,TABLE4(R1));  R1 := @B1(1);  R2 := @B2(1);
0054 03        END;   |-- FIX THE EDGES OF THE BOARD --|
0055 02        R1 := R10;  WHILE R1 < R9 DO
0056           BEGIN  STC(R0,TABLE2(R1));  STC(R0,TABLE3(R1));
0057 03           STC(R0,TABLE3(R1+1));  STC(R0,TABLE4(R1+1));
0058              R1 := @B1(R10);
0059           END;  R3 := R0;  R7 := R0;  R5 := R0;
0060 02        R1 := @STACK;  R6 := #FF;  R4 := 1;  CHECK;
0061           R5 := R5 + R5;  R0 := R5;  ANSWER := "ANSWER:  ";
0062           R1 := "0" OR RANK;  STC(R1,ANSWER(8));
0063           R1 := @ANSWER(9);  R2 := 1;  R3 := 9;  VALTOBCD;
0064           R0 := @ANSWER;  WRITE;   R4 := @STACK;  R5 := @TABLES;
0065           R6 := R6-R6;  WHILE R4 < R5 DO
0066           BEGIN  R6 := @B6(1);  R0 := B4(8);  IF R0 ~= 0 THEN
0067 03           BEGIN R1 := @ANSWER(R3);  VALTOBCD;
0068 04              R0 := R6;  R1 := @ANSWER;  VALTOBCD;
0069                 R0 := @ANSWER;  WRITE;
0070              END;  R4 := @B4(12);
0071 03        END;   |-- REPEAT FOR NEXT BOARD SIZE --|
0072 02        PAGE;  R1 := 1 + RANK;  RANK := R1;
0073        END;
0074 01  END.
PL360 COMPILATION            PATH FINDER PROGRAM

SEGMENT 000  NAME = SEGN000    LENGTH = 0510  BASE REG = 13

0048    00000002  40404040  40404040  40404040
0058 TO 00C4      40404040
00C8    40404040  40404040

EXTERNAL SYMBOL DICTIONARY
   SEGN000   ENTRY (SD) AT 0000
SEGMENT 001  NAME = SEGN001    LENGTH = 0240  BASE REG = 15

0000    90ECD00C  18ED58D0  F22C50E0  D00450D0
0010    E008D703  E010E010  47F0F082  12444790
0020    F0804374  D3CF1277  4770F080  19494770
0030    F0464150  50014180  00015A80  10085080
0040    100847F0  F0809023  10004110  100C1834
0050    4263D3CF  4343D40F  4520F01C  4343D44F
0060    4520F01C  4343D48F  4520F01C  4343D4CF
0070    4520F01C  4203D3CF  5B10F220  98231000
0080    07F2D503  D048F224  4730F20A  1B004110
0090    D0D04120  D5101912  47B0F0A8  50001000
00A0    41101004  47F0F096  58A0D048  189A1C89
00B0    41100001  18211A2A  19294730  F0CE4221
00C0    D40F4110  10014120  200147F0  F0B84110
00D0    00024120  00031929  4730F0EC  4221D44F
00E0    41101001  41202001  47F0F0D6  18391B3A
00F0    41200002  18121A1A  192347B0  F10E4221
0100    D48F4110  10014120  200147F0  F0F84120
0110    00011A2A  41102001  192347B0  F12E4221
0120    D4CF4110  10014120  200147F0  F118181A
0130    191947B0  F14E4201  D44F4201  D48F4201
0140    D4904201  D4D0411A  100047F0  F1301830
0150    18701850  4110D0D0  416000FF  41400001
0160    4520F01C  1A551805  D208D04C  F2144110
0170    00F05610  D0484210  D0544110  D0554120
0180    00014130  000958F0  F23005EF  58F0E09C
0190    4100D04C  58F0F234  05EF58F0  E08E4140
01A0    D0D04150  D3D01B66  194547B0  F1F04160
01B0    60015800  40081200  4790F1E8  4113D04C
01C0    58F0F230  05EF58F0  E0621806  4110D04C
01D0    58F0F230  05EF58F0  E0524100  D04C58F0
01E0    F23405EF  58F0E044  4140400C  47F0F1A8
01F0    58F0F238  05EF58F0  E0324110  00015A10
0200    D0485010  D04847F0  F08258D0  D00498EC
0210    D00C07FE  C1D5E2E6  C5D97A40  40000000
0220    0000000C  00000006  00000000  00000000

EXTERNAL SYMBOL DICTIONARY
   SEGN001   ENTRY (SD) AT 0000
   SEGN000   EXTERNAL REFERENCE
PL360 CROSS REFERENCE        PATH FINDER PROGRAM

            43 SYMBOLS,    246 REFERENCES

ANSWER      0003  0061  0062  0063  0064  0067  0068  0069
B1          0020  0020  0022  0023  0030  0040  0040  0044
            0047  0050  0053  0058
B2          0044  0047  0050  0052  0053
B4          0066  0070
B5          0020
B6          0066
CHECK       0017  0025  0026  0027  0028  0060
FLAGS       0009  0010  0018  0024  0029
IC          0018  0025  0026  0027  0028
LM          0030
PAGE        0072
RANK        0002  0036  0042  0062  0072  0072
R0          0029  0038  0038  0038  0040  0056  0056  0057
            0057  0059  0059  0059  0061  0064  0066  0066
            0068  0069
R1          0023  0030  0030  0038  0039  0040  0043  0043
            0044  0044  0046  0047  0047  0049  0050  0050
            0052  0053  0053  0055  0055  0056  0056  0057
            0057  0058  0060  0062  0062  0063  0067  0068
            0072  0072
R10         0042  0042  0043  0048  0049  0052  0055  0058
R2          0017  0022  0030  0038  0039  0043  0043  0044
            0044  0046  0046  0047  0047  0049  0049  0049
            0050  0050  0052  0052  0053  0053  0063
R3          0022  0023  0024  0025  0026  0027  0028  0029
            0030  0048  0049  0052  0059  0063  0067
R4          0017  0018  0019  0023  0025  0026  0027  0028
            0060  0064  0065  0070
R5          0020  0059  0061  0061  0061  0061  0064  0065
R6          0024  0060  0065  0065  0065  0066  0068
R7          0018  0018  0059
R8          0020  0020
R9          0019  0042  0042  0043  0046  0048  0055
SPACE       0006  0008  0038
SQUARES     0004  0005  0006  0010  0011  0012  0013
STACK       0007  0038  0060  0064
STAKL       0005  0007
STC         0024  0029  0044  0047  0050  0053  0056  0056
            0057  0057  0062
STM         0022
TABLES      0008  0009  0038  0064
TABLE1      0010  0011  0025  0044
TABLE2      0011  0012  0026  0047  0056
TABLE3      0012  0013  0027  0050  0056  0057
TABLE4      0013  0028  0053  0057
VALTOBCD    0063  0067  0068
WRITE       0064  0069
                              VS LOADER
OPTIONS USED - PRINT,MAP,LET,CALL,RES,NOTERM,SIZE=163840,NAME=**GO
   NAME  TYPE  ADDR        NAME  TYPE  ADDR        NAME  TYPE  ADDR
SEGN000    SD 170010    SEGN001    SD 170520    $PL360IO*  SD 170760
READ    *  LR 170760    WRITE   *  LR 170800    PAGE    *  LR 170876
PUNCH   *  LR 17087C    PRINT   *  LR 1708B4    NUMEDTR *  SD 170A68
BCDTOVAL*  SD 170BF8    VALN015 *  SD 170F20    VALTOBCD*  SD 170F90
SHELSORT*  SD 171428    BISEARCH*  SD 171510    BCDN013 *  SD 171590
BCDN015 *  SD 1715D8    VALN013 *  SD 1715E8    SHEN013 *  SD 171640
BISN013 *  SD 171688

TOTAL LENGTH     16C0
ENTRY ADDRESS  170520

ANSWER: 2        2
        3        1
ANSWER: 3       12
        5        3
        7        2
        9        1
ANSWER: 4      184
        7       10
        9       18
       11       24
       13       24
       15       16
ANSWER: 5     8512
        9       35
       11      112
       13      255
       15      478
       17      793
       19     1112
       21     1053
       23      366
       25       52
ANSWER: 6  1262816
       11      126
       13      600
       15     1952
       17     5280
       19    12914
       21    29356
       23    60934
       25   108718
       27   150190
       29   140388
       31    85192
       33    30668
       35     5090

The next program compares two data sets to determine where changes were made. The OLD data set is assumed to be the original; the NEW is assumed to be an altered version of OLD but with essentially the same line numbering. Most text editing systems provide the capability of inserting or deleting lines without altering other line numbers. This program was designed for such systems. In this case, the program processes card input with numbering in columns 73-80, or processes either one of two Stanford Wylbur Edit data formats depending on compile time IF's ($IFT or $IFF). The two input data sets may be either both card, both Edit, or one of each type.

The program makes use of a DUMMY data area that acts as an overlay for both AREA1 and AREA2. You will notice that register synonyms are used to make the program more readable. Register I points to AREA1, register J points to AREA2, and register K may point to either area.

Note the use of the CVD and ED functions within the INPUT procedure.

  BEGIN  |-- RECORD COMPARE PROGRAM --|
     FUNCTION REDUCE(6,#0600);  |-- SUBRTACT 1 FROM REGISTER --|
     INTEGER REGISTER I SYN R8,
                      J SYN R9,
                      K SYN R10,
                 RETURN SYN R11;
     DUMMY BASE R0;  |-- WORK AREA DUMMY SECTION --|
        ARRAY 25 INTEGER DCB;
          SHORT INTEGER LRECL SYN DCB(#52);
          BYTE ENDFILE SYN DCB(96);
        INTEGER NXTLINE, ENDLINE;
        ARRAY 2 INTEGER LINENUM;
          SHORT INTEGER LINE1 SYN LINENUM,
                        LINE2 SYN LINE1(2);
        ARRAY 167 BYTE LINE;
        BYTE CARDS;  |-- CARD INPUT FLAG --|
     CLOSE BASE;
  |-- COMPUTE SIZE OF DUMMY SECTION IN INTEGER AMOUNTS --|
     EQUATE DSLEN SYN CARDS(1) - MEM + 3 SHRL 2;
  |-- ASSIGN WORK AREAS --|
     LONG REAL CONWORK;
     ARRAY DSLEN INTEGER AREA1, AREA2;
     BYTE TYPEF = 0;
     ARRAY 134 BYTE RPLMSG = ("-- REPLACE FOLLOWING --",111(" ")),
                    INSMSG = ("-- INSERT FOLLOWING --", 112(" ")),
                    DELMSG = ("-- DELETE FOLLOWING --", 112(" ")),
                    COLMSG = (12("123456789-"),14(" "));
     ARRAY 120 BYTE BLKMSG = 120(" ");
     ARRAY 134 BYTE BLANKS SYN BLKMSG(_14);
  |-- DECLARE INTERNAL PROCEDURES --|

  PROCEDURE INPUT (RETURN);  IF ~ENDFILE(K) THEN
  BEGIN  LINE(K/134) := BLANKS;
     R3 := NXTLINE(K);  IF R3 >= ENDLINE(K) THEN
     BEGIN  R1 := @DCB(K);  GET;  IF ~= THEN
        BEGIN  LINENUM(K) := "9999.999";  GOTO BOTH;
        END;  R3 := R1;  R1 := R1 + LRECL(K);
        NXTLINE(K) := R3;  ENDLINE(K) := R1;
        IF CARDS(K) THEN GOTO CARDIN;  LINENUM(K) := B3;
$IFT    |-- $SET FOR CAMPUS CONVERSION --|
        IF LINE1(K) > 3520 OR LINE2(K) > 9999 THEN
$END
$IFF    |-- NO $SET FOR SLAC CONVERSION --|
        IF LINE1(K) > 3520 OR LINE2(K) > 1525 THEN
$END
        BEGIN  SET(CARDS(K));  GOTO CARDIN;
        END;  R1 := R3 + LINE1(K);  ENDLINE(K) := R1;
        R3 := @B3(2);  |-- READY FOR TEXT --|
     END;  IF CARDS(K) THEN GOTO CARDIN;  LINENUM(K) := B3;
$IFT    |-- $SET FOR CAMPUS CONVERSION --|
     R1 := 1000 * LINE1(K) + LINE2(K);  CVD(R1,CONWORK);
$END
$IFF    |-- NO $SET FOR SLAC CONVERSION --|
     R1 := LINENUM(K);  CVD(R1,CONWORK);
$END
     LINE(K+134) := #202021204B202020X;
     ED(8,LINE(K+133),CONWORK(4));
  |-- ED converts line numbers into strings --|
  |-- from  "   0.000"  through  "9999.999" --|
     LINENUM(K/8) := LINE(K+134);
     R1 := @LINENUM(K+4);  R2 := @B1(3);
     WHILE R2 > R1 AND B2 = "0" DO
     BEGIN  B2 := " ";  REDUCE(R2);
     END;  |-- LINE NUMBER PROCESSED --|
     R5 := R5-R5;  IC(R5,B3(4));  R3 := @B3(5);
     R2 := @LINE(K+2);  R6 := @B2(134);  WHILE R5 > 0 DO
     BEGIN  IC(R1,B3);  REDUCE(R5);  R3 := @B3(1);
        R4 := #F AND R1;  R1 := R1 SHRL 4 AND #F;
        R2 := @B2(R1);  IF R4 > 0 THEN
        BEGIN  R5 := R5-R4;  REDUCE(R4);
           EX(R4,MVC(0,B2,B3));
           R2 := @B2(R4+1);  R3 := @B3(R4+1);
        END;  IF R2 >= R6 THEN |-- TOO MUCH --|
        BEGIN  SET(ENDFILE(K));  GOTO BOTH;
        END;
     END;  GOTO BOTH;
  CARDIN:  |-- CARD IMAGE INPUT --|
     LINENUM(K/8) := B3(72);  LINE(K+2/72) := B3;
     R3 := @B3(80);  |-- UPDATE TO NEXT CARD --|
  BOTH:  NXTLINE(K) := R3;
  END;
  PROCEDURE CHKBLANK (RETURN);
  BEGIN  IF B3(10/131) = BLANKS THEN B3(9) := "I";
  END;

  PROCEDURE START (RETURN);
  BEGIN  RESET(CARDS(K));  ENDLINE(K) := K;
     NXTLINE(K) := K;  R1 := @DCB(K);  R0 := 1;
  END;

  |-- MAIN CODE OF COMPARE PROGRAM --|

     I := @AREA1;  J := @AREA2;
     K := I;  START;  BLANKS := "OLD";  R2 := @BLANKS;  OPEN;
     K := J;  START;  BLANKS := "NEW";  R2 := @BLANKS;  OPEN;
     BLANKS(0/3) := BLANKS(3);
     R0 := @COLMSG;  WRITE;  |-- WRITE HEADING LINE --|
  LOOP:  |-- RECORD INPUT LOOP --|
     RESET(TYPEF);
  X: K := I;  INPUT;
  Y: K := J;
  Z: INPUT;  IF ENDFILE(I) AND ENDFILE(J) THEN GOTO EXIT;
     R2 := @LINENUM(I);  R3 := @LINENUM(J);
     IF B2(0/141) = B3 THEN GOTO LOOP;
     IF B2(0/8) = B3 THEN |-- COMPARE LINE NUMBERS --|
               BEGIN  IF TYPEF ~= 1 THEN
                     BEGIN  R0 := @RPLMSG;  TYPEF := 1;
                     END;  CHKBLANK;  R0 := R3;  WRITE;
                     B2 := "  FOR   ";
                     R0 := R2;  WRITE;
                     GOTO X;
               END;
     IF < THEN BEGIN  IF TYPEF ~= 2 THEN
                     BEGIN  R0 := @DELMSG;  WRITE;  TYPEF := 2;
                     END;  B2(9) := "D";  R0 := R2;  WRITE;
                     K := I;  GOTO Z;
               END;
     COMMENT ELSE;   IF TYPEF ~= 3 THEN
                     BEGIN  R0 := @INSMSG;  WRITE;  TYPEF := 3;
                     END;  CHKBLANK;  R0 := R3;  WRITE;
                     GOTO Y;
  EXIT:  R0 := R0-R0;
     R1 := @DCB(I);  KLOSE;
     R1 := @DCB(J);  KLOSE;
  END.

The next example uses a local procedure to illustrate Newton's method for taking square roots. The formula is:

       R''  =  ((X/R') + R')/2

where R' is an approximation for the root of X, and R'' is the next approximation. When the difference between R' and R'' becomes very small, R'' is the assumed root of X. The first approximation of R' is taken to be X with its exponent cut in half. Thus, the first approximation for the root of 128 would be 8. Likewise, 1/8 would be the approximation for 1/128. (128 = #80)

BEGIN COMMENT -- THIS IS A SAMPLE PROGRAM WHICH
*  MAKES USE OF MOST OF THE FEATURES OF PL360.
*  THE PROGRAM READS THE SIDES OF A RIGHT TRIANGLE,
*  COMPUTES THE HYPOTENUSE, AND WRITES THE RESULT.;

COMMENT -- DECLARE EXTERNAL PROCEDURES, FUNCTIONS,
*  AND VARIABLES FIRST. --;

   PROCEDURE SQRT (R14);  IF F01 > 0L THEN
   |-- THIS PROCEDURE TAKES THE SQUARE ROOT OF THE VALUE IN F01 --|
   BEGIN  LONG REAL TEMPCELL;  |-- TEMPORARY STORAGE --|
          LONG REAL REGISTER DIFF SYN F67,  |-- TEMPORARY --|
          APROX1 SYN F23, APROX2 SYN F45;   |-- REGISTERS --|
      TEMPCELL := F01;  R1 := R1-R1;
   |-- COMPUTE HALF THE ORIGINAL EXPONENT, EXCESS 64 NOTATION --|
      IC(R1,TEMPCELL);  R1 := R1 - #40S SHRA 1 + #40S;
      STC(R1,TEMPCELL);  APROX2 := TEMPCELL;  DIFF := 2L;
      WHILE DIFF > 10'_6L DO  |-- FIND SQRT OF F01 --|
      BEGIN  APROX1 := APROX2;  |-- NEW APPROXIMATION FROM OLD --|
         APROX2 := F01/APROX1 + APROX1 / 2L;
         DIFF := APROX2 - APROX1;  DIFF := ABS DIFF;
      END;  F01 := APROX2;  |-- REPLACE F01 BY SQRT OF F01 --|
   END;

   FUNCTION REDUCE (6,#0600);  |-- SUBTRACT 1 FROM REGISTER --|

   ARRAY 134 BYTE OUTPUT = (
      " HYPOTENUSE =         FOR SIDES OF",100(" "));
   BYTE CARD SYN OUTPUT(35), ANSWER SYN OUTPUT(14);

|-- MAIN CODE --|
   WHILE  R0 := @CARD;  READ;  =  DO  |-- PROCESS INPUT CARD --|
   BEGIN  R1 := @CARD;  R2 := 3;  BCDTOVAL;  |-- SIDE 1 --|
      F67 := F01 * F01;  BCDTOVAL;  |-- SIDE 2 --|
      F01 := F01 * F01 + F67;  SQRT;  |-- TAKE SQUARE ROOT --|
      R1 := @ANSWER;  R3 := 7;  VALTOBCD;
      R0 := @OUTPUT;  WRITE;  |-- HYPOTENUSE --|
   END;
END.

Next is an example of a global procedure program that would be called as a subroutine of some main program by including:

       EXTERNAL PROCEDURE DUMP (R14) BASE R15;  NULL;

in the main program. A call to DUMP requires that registers R1 and R2 be initialized with the starting and ending absolute addresses of the storage region to be dumped. If the storage address in R1 exceeds the address in R2, then no dump is taken. All registers are restored. It is assumed that the main program has a standard 18-integer save area based on R13. Note the use of the UNPK and TR functions.

  GLOBAL PROCEDURE DUMP (R14) BASE R15;
  BEGIN  BYTE C1 SYN B1, C2 SYN B2, C3 SYN B3;
     FUNCTION REDUCE (6,#0600);

  |--- START DUMP HERE ---|
     STM(R14,R12,B13(12));  R14 := R13;
     BEGIN  |--- DATA SEGMENT FOR DUMP PROCEDURE ---|
        SEGMENT BASE R13;
           ARRAY 18 INTEGER B13;
           ARRAY 2 INTEGER CON;
           ARRAY 256 BYTE LINE = (240(" "),"0123456789abcdef");
              BYTE DLINE SYN LINE(8);
           ARRAY 256 BYTE TRTAB = (64(".")," ",
                          9("."),"^.<(+|&",
                          9("."),"!$*);~-/",
                          9("."),",%_>?",
                         10("."),":#@'="".abcdefghi",
                          7("."),"jklmnopqr",
                          8("."),"stuvwxyz",
                         23("."),"ABCDEFGHI",
                          7("."),"JKLMNOPQR",
                          8("."),"STUVWXYZ",
                          6("."),"0123456789",
                          6("."));
        CLOSE BASE;
    COMMENT -- THE FORMAT OF DUMP DATA IS AS FOLLOWS:

     CORE   REL.    HEXADECIMAL FORM OF DATA
       ADDRESS      DUMPED IN INTEGER AMOUNTS

    600540  0000    02c5e2c4  40404040  40400010  40400001
    600550  0010    d7d3c1e8  e2d8d9e2  00000000  40000d80
    600560  0020 TO 003c      40404040
    600580  0040    40404040  40404040  e2c5c7d5  f0f0f0f1

    THE ORIGINAL DATA IS SHOWN AT THE END OF EACH OF THE ABOVE
    LINES WITH UNPRINTABLE CHARACTERS CONVERTED TO PERIODS (.)
    |        SEGN0001|             -- END OF COMMENT;
     |--- MAIN DUMP CODE ---|
         B13(4) := R14;  B14(8) := R13;
         B14(16) := B14(16) XOR B14(16);
         LINE := " ";  LINE(1/132) := LINE;
         R7 := R1;  R6 := R2 - R7;  IF < THEN
         BEGIN  LINE := "REQUEST ABORTED";  R0 := @LINE;
      WRITE;  GOTO EXIT;
         END;  R5 := R7 - 4;  R4 := R4-R4;
         WHILE R4 < R6 DO   |-- DUMP LOOP --|
         BEGIN  CON := R4;  DLINE(8) := 239;
      DLINE(9/40) := DLINE(8);  UNPK(4,4,DLINE,CON);
      DLINE(49) := "|";  DLINE(66) := "|";
      TR(3,DLINE,LINE);  R3 := @B7(R4);  CON := R3;
      UNPK(6,4,LINE,CON);  TR(5,LINE,LINE);  LINE(6) := " ";
      R3 := @B5(R4+4);  IF B3(0/12) = B3(4) THEN
      BEGIN  DLINE(4) := " TO";  UNPK(8,4,DLINE(18),B3);
               DLINE(26) := 239;  DLINE(50/16) := B3;
               TR(15,DLINE(50),TRTAB);
      L1:  R4 := @B4(16);  R3 := @B5(R4);
               IF R4 < R6 AND B3(0/16) = B3(4) THEN GOTO L1;
               IF R4 > R6 THEN R4 := R6;
               R2 := R4 - 4;  CON := R2;
               UNPK(4,4,DLINE(8),CON);  DLINE(12) := 239;
               TR(40,DLINE(8),LINE);  R0 := @LINE;  WRITE;
               DLINE(5/2) := DLINE(4);
      END ELSE
      BEGIN  DLINE(4) := " ";  R9 := R6 - R4;  R4 := @B4(16);
               R2 := @DLINE(8);  IF R9 >= 16 THEN
               BEGIN  DLINE(50/16) := B3;
                  UNPK(8,4,B2,B3);  C2(8) := 239;
                  UNPK(8,4,B2(10),B3(4));  C2(18) := 239;
                  UNPK(8,4,B2(20),B3(8));  C2(28) := 239;
                  UNPK(8,4,B2(30),B3(12));  C2(38) := 239;
                  R2 := @B2(41);  R3 := @B3(16);
               END ELSE
               BEGIN  R1 := @DLINE(50);  C1 := ".";
                  B1(1/15) := B1;
                  WHILE R9 >= 4 DO
                  BEGIN  UNPK(8,4,B2,B3);  C2(8) := 239;
                     B1 := B3;  R1 := @B1(4);
                     R9 := R9 - 4;  R2 := @B2(10);  R3 := @B3(4);
                  END;  WHILE R9 > 0 DO
                  BEGIN  UNPK(2,1,B2,B3);  REDUCE(R9);
                     C1 := C3;  R1 := @B1(1);
                     R2 := @B2(2);  R3 := @B3(1);
                  END;  C2 := 239;
               END;  TR(40,DLINE(8),LINE);
               TR(15,DLINE(50),TRTAB);  R0 := @LINE;  WRITE;
      END;
         END;
      EXIT:  R13 := B13(4);  LM(R14,R12,B13(12));
   END; END.

The following global procedure program is also a subroutine which might be linked with a main program. Note the use of the TRT function.

 .. Program Segment Number
 :    .. Relative Program Address
 :    :     .. Reference Number
 :    :     :   .. Block Level Number
 :    :     :   :   .. PL360 Program Source
 :    :     :   :   :
001 0000  0001      GLOBAL PROCEDURE TRTEST (R14);  BEGIN
014 0000  0002 01   COMMENT THIS ROUTINE TESTS AN INPUT STRING
014 0000  0003      * AGAINST A 256-BYTE SCAN TABLE.
014 0000  0004      * ENTER WITH R1 = @ OF STRING TO BE TESTED.
014 0000  0005      *            R2 = @ OF TABLE.
014 0000  0006      *            R3 = LENGTH OF STRING TO BE TESTED.
014 0000  0007      * EXITS WITH R1 = LENGTH OF SCANNED STRING.
014 0000  0008      *            R2 = SCAN TABLE CHARACTER WHICH
014 0000  0009      *                 STOPPED THE SCAN.
014 0000  0010      *      ALSO, CONDITION CODE SET BASED ON R2;
014 0000  0011         FUNCTION REDUCE(6,#0600);
014 0000  0012         STM(R3,R6,B13(12));  COMMENT SAVE REGISTERS;
014 0004  0013         R4 := R2;  R5 := @B1;  R2 := R2-R2;  R1 := R2;
014 000E  0014         IF R3 > 0 THEN
014 0014  0015         BEGIN  REDUCE(R3);  R6 := R2;
014 0018  0016 02         FOR R3 := R3 STEP _256 UNTIL 256 DO
014 0018  0017            BEGIN  TRT(255,B5,B4);  IF ~= THEN
014 0026  0018 03            BEGIN  R1 := @B1(R6)-R5;  GOTO EXIT;
014 0030  0019 04            END ELSE
014 0030  0020 03            BEGIN  R6 := @B6(256);  R5 := @B5(256);
014 003C  0021 04            END;
014 003C  0022 03         END;  EX(R3,TRT(0,B5,B4));
014 004C  0023 02         IF = THEN R1 := @B5(R3+1);
014 0054  0024            R1 := @B1(R6) - R5;
014 005A  0025         END;
014 005A  0026 01   EXIT: LM(R3,R6,B13(12));  LTR(R2,R2);
014 0060  0027      END.

SEGMENT 014  NAME = TRTEST     LENGTH = 0070  BASE REG = 15

0000    9036D00C  18424150  10001B22  18121233
0010    47D0F05A  06301862  47F0F040  DDFF5000
0020    40004790  F0344116  10001B15  47F0F05A
0030    47F0F03C  41606100  41505100  5A30F068
0040    5930F06C  47A0F01C  4430F062  4770F054
0050    41135001  41161000  1B159836  D00C1222
0060    07FEDD00  50004000  FFFFFF00  00000100

EXTERNAL SYMBOL DICTIONARY
   TRTEST    ENTRY (SD) AT 0000

The following global procedure program makes use of SVC functions and is designed to run as a stand-alone program. The operating system in which it executes is NOT a standard operating system. However, the concepts presented by this program could be applied to standard operating systems.

  GLOBAL PROCEDURE SORTWYL (R14) BASE R10;
  |-- REGISTER CONTENTS UNKNOWN,  ESTABLISH ADDRESSABILITY --|
  BEGIN  BALR(R10,R0);  R15 := 2;  R10 := R10 - R15;
     BEGIN  GLOBAL DATA SORTSPAC BASE R13;
        INTEGER HIGHCORE, PAGETAB, LOCATORS, LOCEND;
        ARRAY 2 SHORT INTEGER CONTROL = (3,0);   |-- TEXT CONTROL --|
        ARRAY 2 INTEGER TEMP = (0,0);  |-- FIRST/LAST LINE RANGES --|
        SHORT INTEGER DELTA = 0;  |-- START COLUMN POSITION --|
        EQUATE LIMITS SYN 10000 SHLL 3;  |-- LINE COUNT LIMIT --|
        EQUATE LL SYN 4,  LLEN SYN LL+1;  |-- LINE NUMBER LENGTHS --|
        ARRAY 3 SHORT INTEGER CLCMD = (#D500,@B14(LLEN),@B15(LLEN));

  DUMMY BASE R2;
     INTEGER LOCATION;
     SHORT INTEGER LENGTH, COUNT;
  CLOSE BASE;

     FUNCTION CR(1,#1900),  REDUCE(6,#0600);

     PROCEDURE TPUT (R9);  |-- SEND MESSAGE TO TERMINAL --|
     BEGIN  R0 := 1;  SVC(246);  R0 := 1;  SVC(242);
     END;

     PROCEDURE WYLBUR (R9);  |-- SEND COMMAND TO EDITOR --|
     BEGIN  R1 := NEG R1;  R0 := R0-R0;  SVC(254);
     END;

  |-- START MAIN CODE,  GETMAIN CORE LIMITS WITH 4095 BYTE MIN. --|
     R0 := 14;  R1 := 4095;  R15 := R15-R15;  SVC(251);
     PAGETAB := R1;  R2 := R0;  R2 := @B1(R2) =: HIGHCORE;
  |-- SENSE THE PARMS FROM THE CALLING COMMAND. --|
     R3 := R3-R3;  R0 := 19;  R15 := 132;  SVC(250);
     IF R1 = 0 THEN GOTO START;  |-- JUMP IF NO PARMS --|
     R4 := PAGETAB;  R5 := R5-R5;  R6 := R5;  WHILE R1 > 0 DO
     BEGIN  REDUCE(R1);  IF B4 >= "0" THEN
        BEGIN  NI(#F,B4);  IC(R6,B4);  R5 := R5 * 10S + R6;
        END;  R4 := @B4(1);
     END;  IF R5 > 0 THEN REDUCE(R5);  DELTA := R5;
     R6 := R5 + CLCMD(2) =: CLCMD(2);  |-- UPDATE DISP FIELDS --|
     R5 := R5 + CLCMD(4) =: CLCMD(4);
  START:  R1 := PAGETAB;  |-- READ THE ACTIVE FILE INTO CORE --|
  LOOP:  R2 := R2 + _8;  LOCATION := R1;
     R0 := 1;  R1 := NEG R1;  R15 := R2 + R1;
     IF R15 <= R3 THEN  |-- NO MORE ROOM --|
     BEGIN  R3 := LIMITS;  GOTO FILLER;
     END;  IF R15 > 4096 THEN R15 := 4096;
  |-- READ A BLOCK OF TEXT INTO CORE. --|
     R14 := @CONTROL;  SVC(247);  R0 := 1;  SVC(242);
     IF ~= OR R14 = 0 THEN GOTO FILLER;
     COUNT := R14;  LENGTH := R1;  TEMP := _1;
     R14 := R14 SHLL 3;  R3 := R3 + R14;
     R1 := R1 + LOCATION;  B1(0/1) := 0;
     R1 := @B1(7) AND _8;  GOTO LOOP;
  FILLER:  |-- ALL TEXT OF ACTIVE FILE HAS BEEN INPUT --|
     R1 := LOCATION =: LOCATORS;  R2 := @B2(8);
     PAGETAB := R2;  R4 := R1 + R3;
     IF R3 >= LIMITS OR R4 > PAGETAB THEN
     BEGIN  LA(R1,"TOO MUCH TEXT TO SORT.");
        R15 := STRING;  GOTO EXIT;  |-- NO SORT --|
     END;  R2 := HIGHCORE;  |-- BUILD LOCATOR TABLE --|
     WHILE  R2 := R2 + _8;  R2 >= PAGETAB  DO
     BEGIN  R3 := LOCATION;  R4 := LENGTH + R3;
        R5 := R5-R5;  WHILE R3 < R4 DO
        BEGIN  IC(R5,B3(LL));  B1(4) := R3;
           R3 := @B3(R5+LLEN);  R8 := R5 - DELTA =: B1;
           R1 := @B1(8);  |-- INCREMENT TO NEXT LOCATOR --|
        END;
     END;  LOCEND := R1;  R2 := LOCATORS;
  |-- SORT THE LOCATOR TABLE DEPENDENT ON TEXT COMPARISONS --|
     R1 := R1 - R2;  R8 := R1;  R7 := 8;  R9 := R2 - R7;
  X1: R1 := R1 SHRL 1 AND _8;
     IF = THEN GOTO X5;
     R2 := R8-R1;  R3 := R7;
  X2: R4 := R3;
  X3: R5 := R9+R4;  R6 := R5+R1;
     R11 := B5;  R12 := B6;
     IF R11 < 0 OR R12 < 0 THEN GOTO CREGS;
     R14 := B5(4);  R15 := B6(4);
     IF R11 > R12 THEN EX(R12,CLCMD)
                  ELSE EX(R11,CLCMD);
     IF ~= THEN GOTO CEQUAL;
  CREGS:   CR(R11,R12);
  CEQUAL:  IF <= THEN GOTO X4;
     XC(7,B6,B5);  XC(7,B5,B6);  XC(7,B6,B5);
     IF R4 <= R1 THEN GOTO X4;
     R4 := R4 - R1;  GOTO X3;
  X4: IF R2 = R3 THEN GOTO X1;
     R3 := R3 + R7;  GOTO X2;
  X5:  |-- SORT FINISHED, NOW CHANGE LINE NUMBERS --|
     R6 := 1000;  R1 := LOCATORS;  WHILE R1 < LOCEND DO
     BEGIN  R3 := B1(4);  TEMP := R6;
        B3(0/LL) := TEMP(4-LL);  R6 := @B6(1000);  R1 := @B1(8);
     END;  LA(R1,"CLEAR TEXT");  R15 := STRING;  WYLBUR;
  |-- RUN THRU PAGE TABLE TO REWRITE BLOCKS OF TEXT --|
     R2 := PAGETAB;  WHILE R2 < HIGHCORE DO
     BEGIN  R1 := NEG LOCATION;  R15 := LENGTH;
        R14 := COUNT;  R0 := 1;  SVC(246);
        R0 := 1;  SVC(242);  R2 := @B2(8);
     END;  LA(R1,"ACTIVE FILE SORTED.");  R15 := STRING;
  EXIT: TPUT;  SVC(253);  END;  END.

This last sample program is one possible answer to the exercise problem given in Chapter 8. If you have not tried the problem, do so before reading the program given here.

$TITLE   PROGRAM WHICH COMPUTES DAY-OF-THE-WEEK FROM DATE
  BEGIN  |-- PROGRAM TO COMPUTE DAY OF THE WEEK --|
     SHORT INTEGER YEAR, DAY, MONTH;
     ARRAY 14 BYTE LIMIT = (31,28,31,30,31,30,31,
                            31,30,31,30,31,31,29),
                  OFFSET = (0,3,3,6,1,4,6,2,5,0,3,5,6,2);
     ARRAY 70 BYTE MONTHNAMES = ( "JAN. ","FEB. ",
        "MARCH","APRIL","MAY  ","JUNE ","JULY ",
        "AUG. ","SEPT.","OCT. ","NOV. ","DEC. ",
        "JAN. ","FEB. ");  |-- LEAP YEAR --|
     ARRAY 63 BYTE DAYNAMES = ( "SUNDAY   ","MONDAY   ",
        "TUESDAY  ","WEDNESDAY","THURSDAY ","FRIDAY   ","SATURDAY ");
     ARRAY 133 BYTE OUTPUT = 133(" ");
     ARRAY 133 BYTE ERRORMSG = ("*** ERROR ***",120(" "));
        BYTE CARD SYN ERRORMSG(14);
     FUNCTION REDUCE(6,#0600);

  |-- MAIN CODE --|

     OUTPUT(19) := ",";  |-- SET COMMA IN OUTPUT LINE --|
  TOP:  R0 := @CARD;  READ;  IF ~= THEN GOTO EXIT;
     R1 := @CARD;  R2 := 1;  R3 := 4;
     WHILE R3 >= 0 DO  |-- EXTRACT VALUES --|
     BEGIN  BCDTOVAL;  YEAR(R3) := R0;  R3 := R3 - 2S;
     END;  IF YEAR > 99 THEN GOTO ERROR;
     R4 := MONTH;  REDUCE(R4);
     IF R4 < 0 OR R4 >= 12S THEN GOTO ERROR;
     R0 := R0-R0;  R1 := YEAR / 4;
     IF R0 = 0 AND R1 ~= 0 AND R4 < 2S THEN R4 := @B4(12);
     R3 := R3-R3;  IC(R3,LIMIT(R4));  R5 := DAY;
     IF R5 > R3 OR R5 <= 0 THEN GOTO ERROR;
     IC(R3,OFFSET(R4));  R2 := R2-R2;
     R3 := R3 + R5 + R1 + YEAR / 7;
     R2 := R2 * 9S;  R4 := R4 * 5S;
     R2 := @DAYNAMES(R2);  R4 := @MONTHNAMES(R4);
     OUTPUT(0/9) := B2;  R0 := R5;  R2 := 1;
     R3 := 5;  R1 := @OUTPUT(14);  VALTOBCD;
     R0 := 1900 + YEAR;  R1 := @OUTPUT(20);  VALTOBCD;
     OUTPUT(11/5) := B4;  R0 := @OUTPUT;
  PUTIT:  WRITE;  GOTO TOP;
  ERROR:  R0 := @ERRORMSG;  GOTO PUTIT;
  EXIT: END.

:A  Appendix A -- Two's Complement Arithmetic

Two's complement, one's complement, ten's complement: what's it all about? To answer that question, let's look at some properties of numbers. If you were asked to write the value 'one million', you would probably write 1,000,000 (with the commas used to group digits); and if you were asked to write the value 'one', you would probably write the single digit 1. As a human being there is no limit to the number of digits you can write to express a value. However, within a computer there generally is a limit to the number of digits which can be used to express a value. For example, let us assume a hypothetical decimal computer where every value is expressed by a 6-digit field. We could then express within this computer values from zero (0) to 999,999. However, the computer requires that every value be exactly six digits, no more, no less. Therefore, the value 'zero' would be expressed as 000000. The value 'one' would be expressed as 000001, etc. Now if you were asked to place the value 'one million' within this computer, you would say, "It can't be done because 'one million' requires seven digits which exceeds the limits for this computer." You would be absolutely correct!

This brings us to the first point: fixed-length values within a computer have specific limits. Values beyond that limit cannot be expressed. In our 6-digit decimal computer, we can express values from 000000 to 999999, a total of one million values. However, we have not taken into consideration negative numbers. So far, everything is positive. Well, in order to allow for negative values, we will have to divide the set of possible values into two parts: one-half million for positive values, and one-half million for negative values. Therefore, the values from 000000 through 499999 could be considered positive, and the values from 500000 through 999999 could be considered negative.

But now the question arises, what is the correspondence between positive and negative numbers? To answer this question, mathematicians long ago discovered the following algorithm:

If any fixed-length value is subtracted from a value that is one larger than the greatest possible fixed-length value, then the right-most fixed-length portion of the result could be considered the negative value.

In our hypothetical 6-digit decimal computer, 1000000 would be the value from which we would subtract any 6-digit value to obtain its negative. Therefore, subtracting 000001 from 1000000 results in 999999. Adding 000001 (positive one) to 999999 (negative one) results in one million again, but if we ignore the excess digits above our limit of six digits, the answer is 000000 (zero). Similarly, subtracting 499999 from one million results in 500001 which we can consider as the negative of 499999. If we subtract 000000 (zero) from one million, the answer is still 000000 (zero). We therefore have a correspondence scheme for positive and negative values, as follows:

  Positive   000000   000001   000002   ...   499998   499999     --
  Negative     --     999999   999998   ...   500002   500001   500000

                    Ten's Complement

Notice that 000000 does not have a negative form, and that 500000 does not have a positive form (subtracting 500000 from one million results in 500000). The above process is known as ten's complement notation. Nine's complement is similar: every value is subtracted from the largest possible value to obtain the negative. That is, every value is subtracted from 999999 in our hypothetical computer to obtain the negative. The result then is:

  Positive   000000   000001   000002   ...   499998   499999
  Negative   999999   999998   999997   ...   500001   500000

                    Nine's Complement

A general practice for finding the nine's complement of any decimal value is to replace each digit of the original value by the nine's complement of that digit (the sum of the original digit and nine's complement digit is 9). The following table shows the nine's complement of each decimal digit.

                0   1   2   3   4   5   6   7   8   9
                9   8   7   6   5   4   3   2   1   0

                       Nine's Complement Table

The rule for finding ten's complement values is: replace the original value by the nine's complement value and then add one.

Now for the IBM System/360 computer, values are expressed in binary digits (0 and 1 only), and a typical fixed-length value is 32 digits (called bits). Just as we used commas to group digits when we wrote 1,000,000; so also, groups of four bits are written as single hexadecimal digits. This simply saves on space for expressing binary values. Thus instead of:

               0011 0111 0001 1111 1010 0110 0000 0000
we write:        3    7    1    F    A    6    0    0

Now the range of possible values is 00000000 through FFFFFFFF, and dividing that range in half as we did with our hypothetical computer results in 00000000 through 7FFFFFF as positive values, and 80000000 through FFFFFFFF as negative values. Two's complement notation is similar to ten's complement, and one's complement is similar to nine's complement. The only difference is that we are using hexadecimal digits instead of decimal digits. The following table shows the one's complement of each hexadecimal digit.

    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    F   E   D   C   B   A   9   8   7   6   5   4   3   2   1   0

                       One's Complement Table

Since the IBM System/360 computer uses two's complement, the rule for finding the negative of any value is: replace the original value by the one's complement value and add one (similar to the ten's complement rule). Thus, the negative of 00000000 is still 00000000, the negative of 00000001 is FFFFFFFF, and so on. (Notice that the negative of FFFFFFFF is 00000001.) Again we have a correspondence scheme for positive and negative values, as follows:

  Positive   00000000   00000001   ...   7FFFFFFF      --
  Negative      --      FFFFFFFF   ...   80000001   80000000

             Two's Complement (Hexadecimal)

One of the problems of a fixed-length system is that when two large positive or negative values are added together, the result may be a value of opposite sign. This is also true when subtracting a negative from a positive or a positive from a negative. Thus, in our decimal system, if we were to add 300000 to 300000 the result would be 600000 which should be interpreted as a negative answer. This is known as the OVERFLOW problem. Digital computer systems take this into account and provide the programmer with a means of checking the result of a fixed-length addition or subtraction. In IBM System/360 computers, the 'condition code' of the machine is set to indicate OVERFLOW whenever fixed-length addition or subtraction yields an answer of opposite sign. To understand what is happening internally, consider all possible fixed-length values as points on a circle with 0 at the top, 80000000 (hexadecimal) at the bottom, negative values on the left side of the circle with -1 next to 0, and positive values on the right side of the circle with +1 next to 0. Addition of positive values moves in a clockwise direction from any point, and subtraction of a positive value moves in a counterclockwise direction. Any time the point at the bottom of the circle between 80000000 and 7FFFFFFF is crossed, an OVERFLOW condition occurs.

:B  Appendix B -- Floating-Point Description

In Appendix A we introduced a hypothetical decimal computer to illustrate two's complement arithmetic. We shall do the same here because decimal arithmetic is easier for us to understand and because the concepts involving floating-point representations and operations within a computer would be the same regardless of which base system we chose.

Before we go any further, let's review some basic mathematics. Any real number can be written in what's called exponential notation: that is, of the form '0.n times 10 to the integer power p'. We shall use an apostrophe character in place of the phrase 'times 10 to the integer power' and simply write 0.n'p . It doesn't matter what base system we are using as long as n, p, and 10 are all of that same base.

For example, we could represent the integer value 'eleven decimal' in different base systems as follows (all comments are in decimal base):

(a)  0.11'2      (decimal)   | (1/10 + 1/100) x 10'2    |
(b)  0.B'1     (hexadecimal) | (11/16) x 16'1           |
(c)  0.13'2      (octal)     | (1/8 + 3/64) x 8'2       |
(d)  0.1011'100  (binary)    | (1/2 + 1/8 + 1/16) x 2'4 |

Notice that the first digit following the radix point was not 0 in any of our representations. In such a case, the number is said to be expressed in normalized form. When the first digit following the radix point is 0, we have an unnormalized form. Thus, using just the decimal base system:

       11.0'0 and 1.1'1  are not proper since the form is not 0.n'p
       0.11'2  is of normalized form
       0.011'3 and 0.0011'4  are of unnormalized form, etc.

As you can see, a floating-point number has two sets of values associated with it. One set represents the significant digits of the fraction (n), and the other specifies the power (p) to which the base must be raised to indicate the proper location of the radix point within the number. Since both n and p are signed quantities, we must devise a method for expressing the four possible combinations of signed n and p in our computer representation.

       0.n'p   0.n'_p   _0.n'p   _0.n'_p

Let's assume a hypothetical 8-digit decimal representation as follows: two digits for p and its sign and the sign of n, which we we shall call the P-field; then an assumed radix point followed by six digits for n, which we shall call the N-field. Pictorially our representation looks like this: PPNNNNNN .

The N-field can represent one million possible fractions from 0.000000 through 0.999999 with the first hundred thousand being of unnormalized form (0.000000 through 0.099999) and the rest being of normalized form (0.100000 through 0.999999).

The P-field can represent one hundred possibilities (00 through 99) but since we need to represent four combinations of signs as well as p, we will have to divide the P-field into fourths. We can take care of the sign of n first by letting 00 through 49 represent positive n's, and 50 through 99 represent negative n's. The P-field for a negative number is simply 50 more than that of the corresponding positive number. All we have left now is p and its sign. To understand what we can do about signed p's, let's work with just positive n's for the moment and look at a scale of normalized numbers written in exponential form arranged in ascending order of magnitude.

        0.n'_p,  0.n'0,   0.n'p
       |--> Ascending order -->|

Notice that the exponent '0 would occur near the center of the scale if
we wanted about as many negative p's as positive p's across the  entire
scale.   Therefore,  we shall arrange our fifty possible P-field values
(00 through 49 for positive n's) so that  p=0  occurs near  the  center
of the range at 25.  In other words, we add 25 to the signed value of p
to  obtain  the  corresponding  P-field  value.   This  makes p=_25 the
smallest possible exponent corresponding to a P-field value of 00,  and
p=24  the largest possible exponent corresponding to a P-field value of
49.  The computer's representation is called 'excess 25 notation' since
its  value  exceeds the value of the true exponent (p) by 25  (Note: 25
is one-fourth of the P-field range).

The computer's representation of a floating-point number is now arranged in such a way that any normalized numbers of like sign may be compared against each other to correctly determine their proper position on the scale shown above. Here are a few examples:

    Number         Exponential form       Computer form

      largest positive     0.999999'24             49999999
        758.5              0.7585'3                28758500
          1.0              0.1'1                   26100000
        one half           0.5'0                   25500000
      smallest positive    0.1'_25                 00100000
         zero              0.0'_25                 00000000
      smallest negative   _0.1'_25                 50100000
      negative one half   _0.5'0                   75500000
         _1.0             _0.1'1                   76100000
      largest negative    _0.99999'24              99999999

The conversion from exponential form (0.n'p) to computer form (PPNNNNNN) is quite simple. First, determine the P-field by taking p and add 25, then if n is negative, add 50 more. Now take the first six digits of n and write them following the P-field to get the final 8-digit computer representation.

We can apply the same technique to hexadecimal machine notation. By allowing two hexadecimal digits for the P-field and either six or 14 hexadecimal digits for the N-field, we have the System/360 forms of REAL and LONG REAL floating-point values. Thus, PPNNNNNN and PPNNNNNNNNNNNNNN represent REAL and LONG REAL values within the computer, with the P's and N's all being hexadecimal digits. P-fields from 00 through 7F indicate positive values, and 80 through FF indicate corresponding negative values. The true exponents range from _64 decimal (PP=00,80) through 00 (PP=40,C0) to 63 decimal (PP=7F,FF), all as powers of 16 decimal (10 hexadecimal).

Conversion of a decimal value in exponential form into the machine's hexadecimal form is not simple. For example, to convert 0.2005125'4 decimal into machine form requires the following steps:

Therefore, 0.2005125'4 decimal becomes 0.7D52'3 hexadecimal which becomes 437D5200 machine form. Conversion from machine form to exponential decimal form requires essentially the same steps in reverse.

:C  Appendix C -- EBCDIC Character Table

                     0 1 2 3 4 5 6 7 8 9 A B C D E F

                 0
                 1
                 2
                 3
                 4                       ^ . < ( + |
                 5   &                   ! $ * ) ; ~
                 6   - /                   , % _ > ?
                 7                     ` : # @ ' = "
                 8     a b c d e f g h i   , < L J K
                 9     j k l m n o p q r   - = M +
                 A   e c s t u v w x y z   > ? [ ' 7
                 B   @ A B C D E F G H I   ; \ ] m ~
                 C     A B C D E F G H I
                 D     J K L M N O P Q R
                 E       S T U V W X Y Z
                 F   0 1 2 3 4 5 6 7 8 9

The above table represents all the normal characters available on an IBM/360. The table is read as follows:

The hexadecimal digits in the first column represent the first digit of the byte created by a character in the table, and the hexadecimal digits across the top are the second digit. Thus, the character "k" is represented by a byte containing the hexadecimal digits 92.

The 'blank' or 'space' character is hexadecimal 40; all other blanks in the table are unprintable characters.

:D  Appendix D -- PL360 Constructs

Three principle postulates were used as guidelines in the design of the language:

The terminology below is used in describing general constructs:

Registers

     Ireg     R0 thru R15          INTEGER
     Rreg     F0, F2, F4, F6       REAL
     Lreg     F01, F23, F45, F67   LONG REAL

Subscript -a indicates the assignment register, as in

     Ireg-a := Expression

Cells

     Bcell    BYTE                 Value X
     Scell    SHORT INTEGER        Value S
     Icell    INTEGER              Value
     Rcell    REAL                 Value R
     Lcell    LONG REAL            Value L

Note: values may replace cells in an expression.

     Ireg-a := Icell       (Icell)
     Ireg-a := #FACE       0000FACE
     Ireg-a := "DROP"      C4D9D6D7
     Ireg-a := _4          FFFFFFFC

Conditions

Cond represents one of: = , ~= , >= , <= , > , < , Number , ~Number

In the following tables, * preceding the CODE indicates the instruction does not change the condition code. CODE is the computer instruction operation code; and MNEMONIC is the assembly language mnemonic corresponding to the operation code.

:D.1  TABLE OF 2-BYTE INSTRUCTIONS

     CODE     MNEMONIC       COMPILER CONSTRUCT

    *05       BALR           Procname       (not local procedure call)
    *07       BCR            END of any PROCEDURE
     10       LPR            Ireg-a := ABS Ireg
     11       LNR            Ireg-a := NEG ABS Ireg
     12       LTR            Ireg Cond 0
     13       LCR            Ireg-a := NEG Ireg
     14       NR             Ireg-a AND Ireg
     16       OR             Ireg-a OR Ireg
     17       XR             Ireg-a XOR Ireg
    *18       LR             Ireg-a := Ireg
                             Ireg-a =: Ireg   (reverse assignment)
                  Note: Ireg-a := Ireg-a generates no instruction.
     19       CR             Ireg-1 Cond Ireg-2
     1A       AR             Ireg-a + Ireg
     1B       SR             Ireg-a - Ireg
    *1C       MR             Ireg-a * Ireg
                  Note: Ireg-a must be odd numbered
    *1D       DR             Ireg-a / Ireg
                  Note: Ireg-a must be odd numbered
     1E       ALR            Ireg-a ++ Ireg
     1F       SLR            Ireg-a -- Ireg
     20       LPDR           Lreg-a := ABS Lreg
     21       LNDR           Lreg-a := NEG ABS Lreg
     22       LTDR           Lreg Cond 0L
     23       LCDR           Lreg-a := NEG Lreg
    *28       LDR            Lreg-a := Lreg
                             Lreg-a =: Lreg   (reverse assignment)
                  Note: Lreg-a := Lreg-a generates no instruction.
     29       CDR            Lreg-1 Cond Lreg-2
     2A       ADR            Lreg-a + Lreg
     2B       SDR            Lreg-a - Lreg
    *2C       MDR            Lreg-a * Lreg
    *2D       DDR            Lreg-a / Lreg
     2E       AWR            Lreg-a ++ Lreg
     2F       SWR            Lreg-a -- Lreg
     30       LPER           Rreg-a := ABS Rreg
     31       LNER           Rreg-a := NEG ABS Rreg
     32       LTER           Rreg Cond 0R
     33       LCER           Rreg-a := NEG Rreg
    *38       LER            Rreg-a := Rreg
                             Rreg-a =: Rreg   (reverse assignment)
                  Note: Rreg-a := Rreg-a generates no instruction.
     39       CER            Rreg-1 Cond Rreg-2
     3A       AER            Rreg-a + Rreg
     3B       SER            Rreg-a - Rreg
    *3C       MER            Rreg-a * Rreg
    *3D       DER            Rreg-a / Rreg
     3E       AUR            Rreg-a ++ Rreg
     3F       SUR            Rreg-a -- Rreg

:D.2  TABLE OF 4-BYTE INSTRUCTIONS

     All these instructions allow indexable cells.

     CODE     MNEMONIC       COMPILER CONSTRUCT

    *40       STH            Scell := Ireg
                             Ireg-a =: Scell  (reverse assignment)
    *41       LA             Ireg-a := @Cell
                             Ireg-a := Ivalue   (0 <= Ivalue < 4096)
     45       BAL            Procname           (local procedure call)
    *47       BC             GOTO, ELSE, Cond
    *48       LH             Ireg-a := Scell
     49       CH             Ireg Cond Scell
     4A       AH             Ireg-a + Scell
     4B       SH             Ireg-a - Scell
    *4C       MH             Ireg-a * Scell
    *50       ST             Icell := Ireg
                             Ireg-a =: Icell  (reverse assignment)
     54       N              Ireg-a AND Icell
     55       CL             Ireg Cond "string"  (4 or less chars only)
     56       O              Ireg-a OR Icell
     57       X              Ireg-a XOR Icell
    *58       L              Ireg-a := Icell
     59       C              Ireg Cond Icell     (not "string")
     5A       A              Ireg-a + Icell
     5B       S              Ireg-a - Icell
    *5C       M              Ireg-a * Icell
                  Note: Ireg-a must be odd numbered
    *5D       D              Ireg-a / Icell
                  Note: Ireg-a must be odd numbered
     5E       AL             Ireg-a ++ Icell
     5F       SL             Ireg-a -- Icell
    *60       STD            Lcell := Lreg
                             Lreg-a =: Lcell  (reverse assignment)
    *68       LD             Lreg-a := Lcell
     69       CD             Lreg Cond Lcell
     6A       AD             Lreg-a + Lcell
     6B       SD             Lreg-a - Lcell
    *6C       MD             Lreg-a * Lcell
    *6D       DD             Lreg-a / Lcell
     6E       AW             Lreg-a ++ Lcell
     6F       SW             Lreg-a -- Lcell
    *70       STE            Rcell := Rreg
                             Rreg-a =: Rcell  (reverse assignment)
    *78       LE             Rreg-a := Rcell
     79       CE             Rreg Cond Rcell
     7A       AE             Rreg-a + Rcell
     7B       SE             Rreg-a - Rcell
    *7C       ME             Rreg-a * Rcell
    *7D       DE             Rreg-a / Rcell
     7E       AU             Rreg-a ++ Rcell
     7F       SU             Rreg-a -- Rcell
    *88       SRL            Ireg-a SHRL Ivalue  or  Ireg
    *89       SLL            Ireg-a SHLL Ivalue  or  Ireg
     8A       SRA            Ireg-a SHRA Ivalue  or  Ireg
     8B       SLA            Ireg-a SHLA Ivalue  or  Ireg

:D.3  TABLE OF PRE-DECLARED FUNCTIONS

This table is in sort order by the function names. The parameters of each function are given in general form. See section 7.1 for a complete description of the parameters. See section 7.3 for descriptions of most of the functions defined in this table.

                  CODE        COMPILER CONSTRUCT

                * 05          BALR (Rx,Ry)
                  D5          CLC (S,CL,CL)
                  95          CLI (S,C)
                * 4F          CVB (R,X)
                * 4E          CVD (R,X)
                  DE          ED (S,C,CL)
                  DF          EDMK (S,C,CL)
                ? 44          EX (R,XL)
                * 43          IC (R,XL)
                * 41          LA (R,XL)
                * 48          LH (R,X)
                * 98          LM (Rx,Ry,C)
                  12          LTR (Rx,Ry)
                * D2          MVC (S,C,CL)
                * 92          MVI (S,C)
                * D1          MVN (S,C,CL)
                * D3          MVZ (S,C,CL)
                  D4          NC (S,C,CL)
                  94          NI (S,C)
                  D6          OC (S,C,CL)
                  96          OI (S,C)
                * F2          PACK (N1,N2,C,CL)
                * 9200        RESET (C)
                * 92FF        SET (C)
                  8F          SLDA (R,CI)
                * 8D          SLDL (R,CI)
                  04          SPM (R)
                  8E          SRDA (R,CI)
                * 8C          SRDL (R,CI)
                * 42          STC (R,X)
                * 40          STH (R,X)
                * 90          STM (Rx,Ry,C)
                * 0A          SVC (S)
                  95FF        TEST (C)
                  91          TM (S,C)
                * DC          TR (S,C,CL)
                  DD          TRT (S,C,CL)
                  9300        TS (C)
                * F3          UNPK (N1,N2,C,CL)
                  D7          XC (S,C,CL)
                  97          XI (S,C)

:D.4  TABLE OF CELLULAR INSTRUCTIONS

             CODE     MNEMONIC       COMPILER CONSTRUCT

        Single Byte Instructions  (4-Byte instruction length)

            *92       MVI            Cell := Value
             94       NI             Cell AND Value
             95       CLI            Cell Cond Value
             96       OI             Cell OR Value
             97       XI             Cell XOR Value

        Multi-Byte Instructions   (6-Byte instruction length)

            *D2       MVC            Cell-1 := Cell-2
             D4       NC             Cell-1 AND Cell-2
             D5       CLC            Cell-1 Cond Cell-2
             D6       OC             Cell-1 OR Cell-2
             D7       XC             Cell-1 XOR Cell-2

Single-byte instructions assume a Value used with a bytecell, or Cell with a length specification of 1. Value may also be a single character "string". Cell must always be an unindexed cell reference.

Cell-2 of multi-byte instructions may be a Value used with a Cell-1 of more than one byte, or a "string" of more than one character. Cell-2 may also be an unindexed cell reference. Cell-1 must always be an unindexed cell reference.

:D.5  Other Constructs of the Language

The following sections show the machine code into which various other constructs of the language are translated. Assembly language mnemonics are used to denote the individual instructions.

1.      IF <condition-1> AND ... AND <condition-n-1> AND
        <condition-n> THEN <true statement> ELSE <false statement>

                     (condition-1)
                     BC  c1,L1
                       ...
                     (condition-n-1)
                     BC  cn-1,L1
                     (condition-n)
                     BC  cn,L1
                     (true statement)
                     B   L2
                L1   (false statement)
                L2   EQU *

ci is determined by the i-th condition, which itself either translates into a compare instruction depending on the types of compared quantities, or has no corresponding instruction if it merely designates a relation or integer value.

2.      IF <condition-1> OR ... OR <condition-n-1> OR
        <condition-n> THEN <true statement> ELSE <false statement>

                     (condition-1)
                     BC  c1,L1
                         ...
                     (condition-n-1)
                     BC  cn-1,L1
                     (condition-n)
                     BC  cn,L2
                L1   (true statement)
                     B   L3
                L2   (false statement)
                L3   EQU *

Example:     IF R1 < R2 THEN R0 := R3 ELSE R0 := R4

                     CR  R1,R2
                     BC  10,L1
                     LR  R0,R3
                     B   L2
                L1   LR  R0,R4
                L2   EQU *
3.      WHILE <condition> DO <statement>

                L1   (condition)
                     BC  cond,L2
                     (statement)
                     B   L1
                L2   EQU *

If the condition is compound, then code sequences similar to those given for the preceding IF statements are used.

4.      FOR <Rn := expression>
        STEP <increment> UNTIL <limit> DO <statement>

                     (Rn := expression)
                     B   L2
                L1   (statement)
                     A   Rn,INC
                L2   C   Rn,LIM
                     BC  cond,L1

Rn is the register specified by the assignment, INC the location where the increment is stored, and LIM the location where the limit is stored. The compare instruction at L2 may be either a C, CH, or CR instruction depending on the type of limit. Moreover, cond depends on the sign of the increment.

5.      CASE <Rn> OF
        BEGIN <statement-1>;
              <statement-2>;
                 ...
              <statement-m>;
        END

                     AR   Rn,Rn
                     LH   Rn,SW(Rn)
                     B    0(Rn,Rp)
                L1   EQU  *-ORIGIN
                     (statement-1)
                     B    LX(Rp,0)
                L2   EQU  *-ORIGIN
                     (statement-2)
                     B    LX(Rp,0)
                     .    .
                     .    .
                     .    .
                Lm   EQU  *-ORIGIN
                     (statement-m)
                     B    LX(Rp,0)
                SW   EQU  *-2
                     DC   Y(L1)
                     DC   Y(L2)
                     .    .
                     .    .
                     .    .
                     DC   Y(Lm)
                LX   EQU  *-ORIGIN

ORIGIN is the address of the beginning of the program segment and register Rp is assumed to contain this address.

6.      PROCEDURE <procname> (Ra);  <statement>

                P    (statement)
                     BR  Ra

It is assumed that P is a label corresponding to <procname>.

7.      <procname>

                     BALR Ra,Rp

                or   BAL  Ra,P

                or   L    Rp,newbase
                     BALR Ra,Rp
                     L    Rp,oldbase

                or   L    Rp,newbase
                     BAL  Ra,P
                     L    Rp,oldbase

It is assumed here that P designates the relative address of the procedure to be called within the program segment in which it is declared, and Ra is the return address register specified in its declaration, and Rp is the program segment's base register. The first two versions of the code are obtained whenever the segment in which the procedure is declared is also the one in which it is invoked. If the procedure call is of the form

       <procname> (Rn)

then the instruction sequences become:

                     BALR Ra,Rp
                     LTR  Rn,Rp
                     BALR Rp,0
                     L    Rp,oldbase

                or   BAL  Ra,P
                     LTR  Rn,Rp
                     BALR Rp,0
                     L    Rp,oldbase

                or   L    Rp,newbase
                     BALR Ra,Rp
                     LTR  Rn,Rp
                     BALR Rp,0
                     L    Rp,oldbase

                or   L    Rp,newbase
                     BAL  Ra,P
                     LTR  Rn,Rp
                     BALR Rp,0
                     L    Rp,oldbase

:E  Appendix E -- Compiler Control Facilities

The compiler accepts instructions inserted anywhere in the sequence of input records. These instructions affect subsequent records. A compiler instruction record is identified by the character '$' in column 1 and an instruction in columns 2-72. To be recognized, the first three characters following the '$' must be UPPER case alphabetics, digits or blanks. Remaining characters in the control word are not checked, but are assumed to be there. Therefore, $title is not recognized, but $TITle is recognized, as would be $TITLE or even $TITxx. Unrecognized control cards are simply ignored.

:E.1  Listing Control

$LIST    Increment List Counter  (initial option:  List Counter = 0).
         Source records are listed if List Counter is not negative.
$NOLIST  Decrement List Counter.
         Source records are not listed if the List Counter is negative.
$PAGE    Start a new page with the next listing record.
$TITLE   Start a new page with the next listing  record,  and  use  the
         contents  of  columns  10 through 62 as the title for that and
         subsequent pages.
$STITLE  This directive provides a subtitle line.   The  subtitle  will
         remain  in  effect  until  the  next  $TITLE  or $STITLE card.
         $STITLE cards may change the subtitle  without  affecting  the
         main  $TITLE.  $STITLE also causes a page eject.  The contents
         of columns 10 through 62 are taken as the subtitle.
$SPACE # This directive allows the user to line space a  listing  by  #
         lines where # is a number from 1 to 99.  If # is blank, then a
         single  line  space  is  assumed.   If  the  number  of  lines
         remaining  on  the  page  is less than #, then a page eject is
         done instead of line spacing.
$EJECT   This directive is equivalent to $PAGE.
$ON      This directive enables the printing  of  all  $-control  cards
         except $TITLE, $STITLE, $EJECT, $PAGE, and $SPACE.
$OFF     This directive disables the printing of all  $-control  cards.
         This is the default condition at the start of compilation.
$BOLD, $NOBold, $UNDerline, $NOUnder
         $BOLD overprints reserved words on impact printers.
         $UNDERLINE will underline reserved words on most printers.

:E.2  Listing Options

$XREF    All subsequent instances  of  identifiers  are  listed  in  an
         alphabetical  cross-reference  listing  together with the line
         numbers at which they are defined or referenced in the  source
         program.   The  cross-reference  listing  follows  the program
         listing if $LIST is in effect at the end of the  program.   If
         there  is  not  enough  free  storage  to  allocate the cross-
         reference tables,  the  $XREF  instruction  is  ignored.   The
         cross-reference  listing  will be single-spaced unless $XREF 2
         is specified to double-space the listing.
$NOXREF  This causes the previous option  to  be  turned  off  (initial
         option).   Any  accumulated  cross-references  will  be listed
         following the program as described above for $XREF.
$0       Print a summary line at the close of each  segment.   This  is
         the initial option, and it applies to all other options.

         The other options make use of the following table:

      a. List the external symbol  dictionary  at  the  close  of  each
         segment.

      b. List the declared identifiers and associated value as each is
         declared.

      c. List the object text in hexadecimal notation at the  close  of
         each segment.
$1 = a
$2 = a+b
$3 = a+b+c
$4 = c
$5 = a+c
$6 = b+c
$7 = b

:E.3  Operating System Control

$OS      Subsequent PL360 programs which are  statements  are  compiled
         with  entry  and  exit instruction sequences conforming to the
         program-calling conventions of an OS environment.  This  is  a
         default   option  when  the  compiler  is  used  with  the  OS
         interface.
$DOS     Subsequent PL360 programs which are  statements  are  compiled
         with entry and exit instruction sequences which conform to the
         program calling conventions of a DOS environment.  This is the
         default  option  when  the  compiler  is  used  with  the  DOS
         interface.

:E.4  Program Base Register Control

$BASE=xx This  directive  must  precede  the  first  non-control  card.
         Program segments following this directive are compiled with xx
         taken as  the  program  base  register.   This  includes  main
         programs,  global procedures, segment procedures, and external
         procedures (which do not specify BASE).   Procedure  calls  to
         such segments automatically set the specified base register to
         the  entry  point  address.   The  decimal  number  xx must be
         between 01 and 15.  Programs which are main blocks must not be
         compiled with base registers 13 or 14.  The initial option  is
         xx=15,  and  all  predeclared  external procedure declarations
         always have base register R15.  It is  recommended  that  this
         compiler directive only be used for programs which make use of
         SVC instructions that do not preserve the contents of register
         R15, the default program base register.

:E.5  Identification

$XYY#    This directive must precede the first non-control  card.   All
         compiler generated segment names will commence with XYY rather
         than  SEG,  and all object deck cards are identified by XYY in
         columns 73 through 75 followed by the letter  N  and a 4-digit
         number.   X  signifies any alphabetic and  Y  any alphanumeric
         characters.

:E.6  Object Deck Control

$GEN     If this directive precedes the first error detected (if  any),
         then  object  decks  are  still  produced  if  any  have  been
         requested.    Otherwise  object  decks  are  suppressed  after
         encountering an error.
$NOGO    Compile, but suppress the GO step.

:E.7  Copy Facility

$COPY ddname
$COPY ddname(member)
         These control cards specify that  a  sequential  data  set  or
         member  of  a  partitioned  data  set is to be copied into the
         compilation.  The compiler temporarily suspends input from the
         standard input medium and continues compilation with the  data
         set    defined    by    the    $COPY   control   card.    When
         end-of-information  is   encountered   on   that   data   set,
         compilation  continues  from  the standard input with the card
         image immediately following the  $COPY  control  card.   Note:
         $COPY is ignored in the data set being copied, i.e., $COPY may
         not nest.  As many $COPY control cards as desired may occur in
         the standard input.  When compiling  under  ORVYL,  ddname  or
         member  is  assumed to be the ORVYL data set name.  An account
         number may follow to  indicate  a  data  set  belonging  to  a
         different account.

:E.8  Conditional Compile Directives

At the start of compilation of each program, an array of flags is reset by the compiler. The following directives use this array. The array flags are specified by individual characters in the directives, and any characters may be used, including blank. Upper and lowercase characters are considered equivalent. The directives must be in uppercase in columns 1 through 4 on the control card.

$SET a     where 'a' is any character in column 6.
           This directive sets the 'a' flag.
$IFT a b   where 'a' is any character in column 6, and 'b' is any char-
$IFF a b   acter in column 8.

           These directives examine the 'a' flag.  If the 'a'  flag  is
           set  for  $IFT,  or  reset for $IFF, this directive takes no
           action and compilation continues normally.

           If the 'a' flag is reset for $IFT,  or  set  for  $IFF,  the
           compiler  skip-reads  source cards until a $END directive is
           encountered  with  its  'b'  character  matching   the   'b'
           character  of  the $IFT or $IFF.  Compilation then continues
           from that point.

           Note: '$IFF a b' is an unconditional skip to '$END b' if 'a'
           is currently set.  '$IFT a b' is an  unconditional  skip  to
           '$END b' if 'a' is currently reset.
$IFJ b     where 'b' is any character in column 6.
           This is an unconditional skip to matching '$END b'.
$END b     where 'b' is any character in column 6.
           This directive terminates $IFT, $IFF or $IFJ directives.
$RESET a   where 'a' is any character in column 8.
           This directive resets the 'a' flag.
Examples of Conditional Compile:

1.  $SET Z
       .
       .
    $IFT Z
       COMMENT Compile this if 'Z' is $SET;
       .
       .
    $END
    $IFF Z
       COMMENT Compile this if 'Z' is not $SET;
       .
       .
    $END

2.  $SET 1
       .
       .
    $IFF 0 X
    $IFF 1 X
    $IFT 2 Q
    $END X
       COMMENT Compile this if '0' or '1' or '2' is $SET;
       .
       .
    $END Q

3.  $SET +
    $SET -
       .
       .
    $IFT +
    $IFT -
       COMMENT Compile this if both '+' and '-' are $SET;
       .
       .
    $IFJ X  Skip over alternative.
    $END
       COMMENT Compile this if '+' and '-' are NOT both $SET;
       .
       .
    $END X

:F  Appendix F -- Compiler Output

:F.1  Compiler Listing Output

If listing is specified, each non-control record is listed as it is read. Source records in which errors are detected are always listed. Six columns of numbers appear at the left of each line. The first pair consists of the current internal program segment number (in decimal) followed by that program segment's relative address (in hexadecimal). The second pair consists of the current internal data segment number (in decimal) followed by that data segment's relative address (in hexadecimal). The fifth number is the reference number of the source record. The final number, the BEGIN/END level count, shows the excess of BEGIN symbols over END symbols at the beginning of the next line following an occurrence of BEGIN/END. This count is only printed when the BEGIN/END level changes. In addition, each page begins with a heading which includes the page number, date, time, and an optional title (see section E.1).

PL360 COMPILATION            TITLE LINE       ORVYL 07/04/76    PAGE   1

 .. Program Segment Number
 :    .. Relative Program Address
 :    :     .. Data Segment Number
 :    :     :    .. Relative Data Address
 :    :     :    :      .. Reference Number
 :    :     :    :      :   .. Block Level Number
 :    :     :    :      :   :   .. PL360 Program Source
 :    :     :    :      :   :   :
001 0000   000 0000   0001      GLOBAL PROCEDURE SORTWYL (R14) BASE R10;
014 0000   000 0000   0002      BEGIN  BALR(R10,R0);  |-- SET BASE --|
014 0002   000 0000   0003 01      R15 := 2;  R10 := R10 - R15;
014 0008   000 0000   0004         BEGIN  GLOBAL DATA SORTSPAC BASE R13;
014 000C   015 0000   0005 02         INTEGER HIGHCORE, PAGETAB,
014 000C   015 0008   0006                    LOCATORS, LOCEND;
014 000C   015 0010   0007            ARRAY 2 SHORT INTEGER CONTROL;
014 000C   015 0014   0008            ARRAY 2 INTEGER TEMP = (0,0);

:F.2  Compiler Object Program Output

The PL360 compiler is designed to be used in conjunction with link/loader programs which resolve symbolic cross-references between the segments of one or more programs. Examples of programs capable of such resolution are the MTS loader, the IBM OS/VS linkage editor or loader, and the IBM DOS linkage editor. The remainder of this section uses the terminology of these programs.

The output of the PL360 compiler is a sequence of object modules. Each object module contains a single control section corresponding to a PL360 segment. It consists of 80-character records in the standard format of external symbol dictionary (ESD), text (TXT), relocation dictionary (RLD), and an end (END).

Every PL360 segment (except a dummy data segment) is associated with an object module in the following fashion:

In all cases, a control section has a single control section name and an entry point name which is identical. In the case of a PL360 program which is a block, a transfer address to the main entry point is provided in the END card of the object module for the implicit segment corresponding to this block. This transfer address is used by a loader to determine where to begin execution.

The task of the linkage editor/loader includes matching global, external, and common declarations, inserting absolute address constants, and completing tables of segment base addresses contained within each control section for a program segment in accordance with the external symbol dictionary and relocation dictionary generated by the compiler for that control section.

For PL360 programs which are blocks, control section names generated by the compiler for SEGMENT declarations are of the form SEGNnnn where nnn is the decimal internal segment number. If the PL360 program is a global procedure, the first three characters of the procedure identifier (extended on the right by NN if necessary) are used in place of the characters 'SEG' in naming control sections for SEGMENT declarations. These naming conventions may be overruled by use of the compiler directive $XYY# (see section E.5).

Each END card of the object module output by the compiler has the name 'PL360' followed by the date and time of compilation.

Additional information about linkage editor/loader processing of object decks is contained in Appendix G.

:G  Appendix G -- Linkage Conventions

Although PL360 was designed for writing logically self-contained programs, it is possible to communicate with separately compiled programs if appropriate linkage and coding conventions are observed. These conventions are summarized below.

:G.1  Calling External Routines from PL360

Addresses which correspond to external symbolic names and which are to be supplied by linkage editing can be specified by the external or common declarations of PL360. Entry to the block containing a data segment declaration causes the specified base register to be loaded with the corresponding address. External names appearing in procedure declarations are assumed to designate entry points to subroutines. In such declarations, the procedure body is normally the statement NULL. The call of the external procedure P2 from the procedure P1 is equivalent to the following 360 Assembler coding:

       USING P1,Rp
       ...
       L     Rc,=V(P2)
       DROP  Rp
       BALR  Ra,Rc
       USING *,Ra
       L     Rp,=A(P1)
       USING P1,Rp
       DROP  Ra

This linkage implies the following restrictions upon the called routine:

Any additional, non-conflicting conventions may be established by the programmer.

If the called procedure (P2) uses Rp to return information to the calling routine (P1), the procedure statement in P1 is usually of the form: P2(Rm), indicating that the return linkage must move the contents of Rp to Rm, thus setting the condition code before re-establishing the base address of P1 in Rp. The equivalent 360 Assembler coding for this type of call differs from that already given only in the last four lines which become:

       LTR   Rm,Rp
       BALR  Rp,0
       USING *,Rp
       L     Rp,=A(P1)
       USING P1,Rp

OS type linkages are facilitated by the fact that if the calling PL360 program is a main block, the first 18 words of the implicit data segment (base register R13) are available for use as a save area (see section 4.2), and by the @@-operator which facilitates the construction of OS-type parameter lists at compile time.

:G.2  Requesting Supervisor Services

SVC instructions are available in PL360 programs through the function statement. It should be noted, however, that in many operating systems the contents of R15 are destroyed by execution of some SVC instructions. In such cases, it is essential that saving and immediately restoring R15 be explicitly programmed. This tedious job of preserving the contents of the program base register can be avoided by using the $BASE compiler instruction (see section E.4), or by explicitly specifying a base register in the procedure heading (see sections 8.2 and 8.3).

:G.3  Calling PL360 Procedures from External Routines

Symbolic names and corresponding addresses to be made known to routines external to the PL360 program are specified by the global and common declarations of PL360. Global names specified in procedure declarations are associated with the corresponding procedure entry point. The external invocation of PL360 procedures must satisfy the following restrictions:

In addition, the following points should be noted:

    (a) the symbolic name of the main program entry point will  normally
        be SEGN001; the symbolic name of the implicit data segment (with
        base register R13) will normally be SEGN000 (see section E.5)
    (b) the return register for SEGN001 will be R14
    (c) at entry to SEGN001,  R13 must contain the address of an 18-word
        save  area, if the $OS option is in effect (see section E.3) and
        R15 must contain the entry address even if a $BASE control  card
        specifies a different base register (see section E.4)
    (d) at exit, all registers are restored from the save area, and  the
        contents  of R15 is set equal to zero (R15=0), if the $OS option
        is in effect (see section E.3)

Consider the following example:

GLOBAL PROCEDURE P1 (R1) BASE R7;      The procedure P2 can  be  entered
   BEGIN GLOBAL DATA D1 BASE R10;      with  the  base register for data
      INTEGER A;                       segment  D1  incorrectly  loaded,
      COMMON PROCEDURE P2 (R2);        since it is  possible  to  bypass
      BEGIN  R0 := A;                  the  entry code of the block con-
      END;                             taining the base declaration.  In
      COMMON PROCEDURE P3 (R2);        procedure P3, however, the exter-
      BEGIN EXTERNAL DATA D1 BASE R10; nal declaration  causes   correct
         R0 := A;                      register loading.    In  general,
      END;                             procedures   which   are   to  be
      R0 := 1 + A;                     entered independently  should  be
   END.                                declared   as  separate  programs
                                       whenever possible.

It should be noted that the registers specified in corresponding global and external procedure declarations must be identical while the registers specified in corresponding global, external, and common data segment declarations may be different.

Also note that when common and external procedures are paired, return registers must be identical and any base register specified in the external declaration must match the base register of the global or segment procedure containing the common procedure declaration. Thus,

       EXTERNAL PROCEDURE P2 (R2) BASE R7; NULL;

would be the proper declaration for P2 in a separately compiled segment linked with the above example.

:G.4  Problems Involving Loading of Object Decks

The order in which object decks are output can be of critical importance at loading time. Object decks for each segment are output by the compiler at the time the segment is closed by CLOSE BASE or END. When multiple segments are ENDed at one time, the segments are output in decending order by assigned segment number (most recently declared segment is output first), except for segments 000 and 001 which are always output with 000 before 001.

When the loader or linkage editor reads the object decks, each END card is examined for a 'transfer address'. If none is found, then the program entry point is assumed to be the start of the first object deck read. If only one 'transfer address' is found, then it defines the program entry point. Any subsequent 'transfer address' definitions are ignored. Since global procedure programs do not define any 'transfer address', it is possible for execution to begin in a data segment! This would most likely cause an abnormal termination. To avoid such a circumstance, you would have to supply a loader or linkage editor control card which specifically defines the program entry point (an ENTRY control card). Any 'transfer address' determined by END cards is overruled by such a control card.

The loaders and linkage editors also have some interesting rules concerning multiply-defined segment or entry point names. If a segment is read whose name matches a segment or entry point name read from a previous object deck, then the new segment is completely bypassed, without an error indication. The following example should serve the demonstrate the concept.

       BEGIN  |-- start of Main program --|
          GLOBAL PROCEDURE A (R1) BASE R8;
          BEGIN  |-- A is known to Main program --|
             GLOBAL PROCEDURE B (R2) BASE R9;
             BEGIN  |-- B is known to A above --|
                COMMON PROCEDURE A (R3);
                BEGIN  |-- A is redefined --|
                END;  |-- end of Common A --|
                  |-- statements of Global B --|
                A;  |-- call to Common A --|
             END;  |-- end of B, segment written --|
               |-- statements of Global A --|
             B;  |-- call to Global B --|
          END;  |-- end of A, segment written --|
            |-- statements of Main program --|
          A;  |-- call to Global A --|
       END.  |-- end of Main, segments written --|

The segments for the program above are output in the following order:

(1)  Segment B with entry points B and A (defined by COMMON).
(2)  Segment A with entry point A, and external reference to B.
(3)  Segment SEGN000 (data segment) with entry point SEGN000.
(4)  Segment SEGN001  (Main  program)  with  entry  point  SEGN001,  and
     external reference to A.  The code generated for the reference to A
     assumes the base and return registers of:

       GLOBAL PROCEDURE A (R1) BASE R8;

When the loader reads these object decks, it will bypass Segment A since Segment B was read first, and B already defined an entry point called A. The external reference to A from SEGN001 will be resolved by the COMMON A entry point, not the GLOBAL A segment. Since the code generated for COMMON A does not match the code generated in SEGN001 for the call to A (different A's), our program will probably terminate abnormally! Yet, there is no indication of error either from the PL360 compiler or from the loader. Clearly, one must be careful not to define a segment or entry point name more than once. GLOBAL, COMMON, and SEGMENT data and procedure declarations create segment or entry point names; but EXTERNAL data and procedure declarations do not create segment or entry point names: they only refer to such names defined elsewhere.

External declarations can also cause problems. For example:

       BEGIN  |-- start Main program --|
          EXTERNAL PROCEDURE OPEN (R14) BASE R15;  NULL;
          GLOBAL PROCEDURE PUT (R5);
          BEGIN  |-- user-defined PUT procedure --|
          END;  |-- end of PUT, segment written --|
            |-- statements of Main program --|
          OPEN;  |-- call to OPEN procedure --|
            |-- other statements of Main program --|
          PUT;  |-- call to user's PUT routine --|
            |-- other statements of Main program --|
       END. |-- end of Main, segments written --|

When these object decks are loaded, an EXTERNAL reference to OPEN must be resolved by the Loader. Normally, this would be no problem since OPEN is defined in the PL360 runtime library. However, OPEN is one of many entry points in a single segment. Another entry point in that same segment is PUT. The loader will NOT load the segment containing the OPEN entry point since that segment also defines a PUT entry point, and PUT has already been defined by the programmer (GLOBAL PROCEDURE PUT). When loading is finished, we have an unresolved external reference for OPEN because a pre-declared external name (PUT) has been redefined by the programmer. Of course, if the programmer supplied an OPEN procedure, then the library would not be referenced, and the external reference could be resolved.

In general, do not define segment or entry point names more than once; and do not define segment or entry point names which match library names unless you truly intend to override the library. These problems involving loading of object decks are not restricted to PL360, but can occur with other languages as well.

:H  Appendix H -- Compiler Error Diagnostics

Errors detected by the compiler are indicated by a message and a vertical bar (|) preceding the point where the error was detected. After about 50 errors, a message is provided, and further diagnostic messages are counted but not listed. The following is a list of error diagnostics and their meanings:

ErrNum Message Meaning

  00     SYNTAX         The source program violates the PL360 syntax.
  01     VAR MIX TYPES  The types of operands in a variable  assignment
                        are incompatible.
  02     FOR PARAMETER  In a FOR clause, the register is not an integer
                        register, or the limit is not a register, cell,
                        or number of the integer types.
  03     REG ASS TYPES  The  types  of  the  operands  in  a   register
                        assignment are incompatible.
  04     BIN OP TYPES   The types  of  operands  of  an  arithmetic  or
                        logical operator are incompatible.
  05     SHIFT OP       A real instead of an integer register or number
                        is specified in a shift operation.
  06     COMPARE TYPES  The types  of  operands  in  a  comparison  are
                        incompatible.
  07     REG TYPE OR #  Either the type or the number of  the  register
                        used is incorrect.
  08     UNDEFINED ID   An undeclared identifier has  been  referenced.
                        The  identifier  is treated as if it were 'R1'.
                        This may generate other errors.
  09     MULT LAB DEF   The same identifier is defined as a label  more
                        than once in the same block.
  10     EXC INI VALUE  The number of initializing values  exceeds  the
                        the number of elements declared in an array, or
                        a  string  attempts  to  initialize  beyond the
                        declared limits of a variable or array.
  11     NOT INDEXABLE  An index register is not allowed for  the  cell
                        designator in this context.
  12     DATA OVERFLOW  The address of the  declared  variable  in  the
                        data segment exceeds 4095.
  13     NO OF ARGS     An incorrect number of arguments is used for  a
                        function.
  14     ILLEGAL CHAR   An illegal character  was  encountered;  it  is
                        skipped.
  15     MULTIPLE ID    The same identifier is declared more than  once
                        in  the  same  block.   This  occurrence of the
                        identifier is ignored.
  16     PROGRAM OFLOW  The current program segment is too  large.   It
                        must be resegmented.
  17     INITIAL OFLOW  The area of initializing data in  the  compiler
                        is  full.   This can usually be circumvented by
                        suitable data segmentation  or  by  re-ordering
                        initialized data within the segment.
  18     ADDRESS OFLOW  The number used  as  index  is  such  that  the
                        resulting  relative  address  is less than 0 or
                        greater than 4095.
  l9     NUMBER OFLOW   The integer number is too large in magnitude.

  20     MISSING .      An end-of-file  is  encountered  before  a  '.'
                        terminating  the program.  The problem may be a
                        missing string quote.
  21     STRING LENGTH  The length of a string is either 0  or  greater
                        than 256.
  22     NULL CASE ST.  A CASE statement did not contain any additional
                        statements.
  23     FUNC DEF NO.   The format number in a function declaration  is
                        illegal.
  24     ILLEGAL PARAM  A parameter is incompatible with  the  specifi-
                        cations of the function.
  25     NUMBER         A number has been used that has an illegal type
                        or value.
  26     SYN MIX        Synonym  declarations  cannot  mix   cell   and
                        register  declarations,  or  T-cell designators
                        have different base registers.
  27     SEG NO OFLOW   The maximum allowed segment  number   has  been
                        exceeded.  The limit is generally set at 255.
  28     ILLEGAL CLOSE  A segment close declaration is encountered when
                        no data segment is open  in  the  corresponding
                        block head.
  29     NO DATA SEG    A  variable  is  declared  with  no  open  data
                        segment.  A dummy data segment is opened.
  30     ILLEGAL INIT   Initialization is specified in  a  common  data
                        segment or replicates an absolute address.
  31     GET MORE CORE  There is insufficient core to compile.

At the end of each program segment, all occurrences of undefined labels are listed with an indication of where they were referenced.

:I  Appendix I -- PL360 Syntactic Grammar

This appendix contains the precedence grammar used to define the PL360 programming language. The grammar is in the form of Bacus-Naur notation where <X> represents 'the syntactic entity X', ::= represents 'is replaced by', and | represents alternative replacements. Thus, <X> ::= <X> <Y> | <Y> is read as follows: "The syntactic entity X is replaced by the syntactic entity X followed by the syntactic entity Y, or the syntactic entity X is replaced by only the syntactic entity Y." This is a recursive grammar in that some syntactic entities are defined in terms of themselves. In such cases, one of the alternatives not involving the syntactic entity being defined must be satisfied before alternatives involving the syntactic entity can be satisfied. In the example given, if <Y> were defined as: <Y> ::= 0 | 1 then <X> would be the definition of a binary number of arbitrary length.

Terms not enclosed in brackets <> are called 'terminal symbols'. Such symbols are to be taken literally (as written). The only special definition in this grammar is that for <CHAR1> where the first alternative would actually be 255 alternatives representing all 256 characters (#00X through #FFX) except for the quote character (").

<ADR OP>     ::= @@
               | @
<ALPHA>      ::= A | B | C | D | E | F | G | H | I | J | K | L | M
               | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
<ALPHA NUM>  ::= <ALPHA>
               | <DIGIT>
<ARITH OP>   ::= --
               | ++
               | -
               | +
               | *
               | /
               | =:
<ASS STEP>   ::= <K REG ASS> STEP <T NUMBER>
<BLOCKBODY>  ::= <BLOCKBODY> <LABEL DEF>
               | <BLOCKBODY> <STATEMENT> ;
               | <BLOCKHEAD>
<BLOCKHEAD>  ::= <BLOCKHEAD> <DECL> ;
               | BEGIN
<BRINDX OP>  ::= GT
               | LE
<BYTE NUM>   ::= <INTEGER> X
<CASE HEAD>  ::= <CASE HEAD> <DECL> ;
               | CASE <K REG> OF BEGIN
<CASE SEQ>   ::= <CASE HEAD>
               | <CASE SEQ> <LABEL DEF>
               | <CASE SEQ> <STATEMENT> ;
<CELL ST>    ::= <CELL ST> <LOG OP> <STRING>
               | <CELL ST> <LOG OP> <T CELL>
               | <CELL ST> <LOG OP> <T NUMBER>
               | <T CELL> := <K REG>
               | <T CELL> := <STRING>
               | <T CELL> := <T CELL>
               | <T CELL> := <T NUMBER>
<CHARS>      ::= <CHARS> <CHAR1>
               | <CHAR1>
<CHAR1>      ::= Any character except "
               | ""
<COMP AOR>   ::= <COMP AOR> <STATEMENT> ;
               | <COMP COND> AND
               | <COMP COND> OR
<COMP COND>  ::= <COMP AOR> <COND>
               | <CONDITION>
<COND DO>    ::= <COMP COND> DO
<COND THEN>  ::= <COMP COND> THEN
<COND>       ::= <CONDITION>
<CONDITION>  ::= <K REG> <BRINDX OP> <K REG>
               | <K REG> <REL OP> <K REG>
               | <K REG> <REL OP> <STRING>
               | <K REG> <REL OP> <T CELL>
               | <K REG> <REL OP> <T NUMBER>
               | <REL OP>
               | <RP> <PRIM COND> )
               | <T CELL>
               | <T CELL> <REL OP> <STRING>
               | <T CELL> <REL OP> <T CELL>
               | <T CELL> <REL OP> <T NUMBER>
               | <T NUMBER>
               | <UNARY REG>
               | ~ <T CELL>
               | ~ <T NUMBER>
               | ~ <UNARY REG>
<DECL>       ::= <DSEG TYPE> BASE <K REG>
               | <EQU SYN2>
               | <FUNC DC7>
               | <PROC HD7> <STATEMENT*>
               | <PROC SYN> <PROC ID>
               | <SYN DC2>
               | <T DECL4>
               | CLOSE BASE
<DIGIT>      ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<DO>         ::= DO
<DSEG TYPE>  ::= COMMON
               | COMMON DATA <ID>
               | DUMMY
               | EXTERNAL DATA <ID>
               | GLOBAL DATA <ID>
               | SEGMENT
<EQU SYN1>   ::= <EQUATE> <ID> SYN
<EQU SYN2>   ::= <EQU SYN1> <K REG>
               | <EQU SYN1> <STRING>
               | <EQU SYN1> <T NUMBER>
               | <EQU SYN1> <UNARY NUM>
               | <EQU SYN2> <ARITH OP> <STRING>
               | <EQU SYN2> <ARITH OP> <T NUMBER>
               | <EQU SYN2> <LOG OP> <STRING>
               | <EQU SYN2> <LOG OP> <T NUMBER>
               | <EQU SYN2> <SHIFT OP> <T NUMBER>
               | <EQU SYN3> <ARITH OP> <T CELL>
<EQU SYN3>   ::= <EQU SYN1> <T CELL>
<EQUATE>     ::= <EQU SYN2> ,
               | EQUATE
               | SHORT EQUATE
<FILL>       ::= <ADR OP> <PROC ID>
               | <ADR OP> <T CELL>
               | <REP LIST2> )
               | <STRING>
               | <T NUMBER>
<FRAC NUM>   ::= <FRAC NUM> <DIGIT>
               | <INTEGER> .
<FORUNTIL>   ::= UNTIL
<FUNC DC1>   ::= <FUNC DC7> ,
               | FUNCTION
<FUNC DC2>   ::= <FUNC DC1> <ID>
<FUNC DC3>   ::= <FUNC DC2> (
<FUNC DC4>   ::= <FUNC DC3> <T NUMBER>
<FUNC DC5>   ::= <FUNC DC4> ,
<FUNC DC6>   ::= <FUNC DC5> <T NUMBER>
<FUNC DC7>   ::= <FUNC DC6> )
<FUNC ID>    ::= <ID>
<FUNC1>      ::= <FUNC2> <FUNC3>
               | <FUNC2> <K REG>
               | <FUNC2> <STRING>
               | <FUNC2> <T CELL>
               | <FUNC2> <T NUMBER>
<FUNC2>      ::= <FUNC ID> (
               | <FUNC1> ,
<FUNC3>      ::= <FUNC1> )
<GLOB HD>    ::= <GLOB HD1>
               | <GLOB HD1> BASE <K REG>
<GLOB HD1>   ::= GLOBAL <PROC HD4>
<GOTO ST*>   ::= GOTO <ID>
<GOTO ST>    ::= <GOTO ST*>
<HEX DIGIT>  ::= <DIGIT> | A | B | C | D | E | F
<HEX VAL>    ::= <HEX VAL> <HEX DIGIT>
               | # <HEX DIGIT>
<ID>         ::= <ID> <ALPHA NUM>
               | <ALPHA>
<IF>         ::= <IF> <STATEMENT> ;
               | IF
<INT NUM>    ::= <HEX VAL>
               | <INTEGER>
<INTEGER>    ::= <INTEGER> <DIGIT>
               | <DIGIT>
<K REG ASS>  ::= <K REG ASS> <ARITH OP> <K REG>
               | <K REG ASS> <ARITH OP> <STRING>
               | <K REG ASS> <ARITH OP> <T CELL>
               | <K REG ASS> <ARITH OP> <T NUMBER>
               | <K REG ASS> <LOG OP> <K REG>
               | <K REG ASS> <LOG OP> <STRING>
               | <K REG ASS> <LOG OP> <T CELL>
               | <K REG ASS> <LOG OP> <T NUMBER>
               | <K REG ASS> <SHIFT OP> <K REG>
               | <K REG ASS> <SHIFT OP> <T NUMBER>
               | <K REG>
               | <K REG> := <ADR OP> <PROC ID>
               | <K REG> := <ADR OP> <T CELL>
               | <K REG> := <K REG>
               | <K REG> := <STRING>
               | <K REG> := <T CELL>
               | <K REG> := <T NUMBER>
               | <K REG> := <UNARY CELL>
               | <K REG> := <UNARY NUM>
               | <K REG> := <UNARY REG>
<K REG>      ::= <ID>
<LABEL DEF>  ::= <ID> :
<LIMIT>      ::= <FORUNTIL> <K REG>
               | <FORUNTIL> <T CELL>
               | <FORUNTIL> <T NUMBER>
<LOG OP>     ::= AND
               | OR
               | XOR
<LONG NUM>   ::= <INT NUM> L
               | <FRAC NUM> ' <SCALE> L
               | <INTEGER> ' <SCALE> L
               | <FRAC NUM> L
<NUMBER>     ::= <LONG NUM>
               | <REAL NUM>
               | <SHORT NUM>
               | <BYTE NUM>
               | <INT NUM>
<PRIM COND>  ::= <COMP COND>
<PROC HD1>   ::= PROCEDURE <ID>
<PROC HD2>   ::= <PROC HD1> (
<PROC HD3>   ::= <PROC HD2> <K REG>
<PROC HD4>   ::= <PROC HD3> )
<PROC HD5>   ::= EXTERNAL <PROC HD4>
               | GLOBAL <PROC HD4>
               | SEGMENT <PROC HD4>
<PROC HD6>   ::= <PROC HD4>
               | <PROC HD5>
               | <PROC HD5> BASE <K REG>
               | COMMON <PROC HD4>
<PROC HD7>   ::= <PROC HD6> ;
<PROC ID>    ::= <ID>
<PROC SYN>   ::= PROCEDURE <ID> SYN
<PROC1>      ::= <PROC ID> (
<PROC2>      ::= <PROC1> <K REG>
<PROGRAM*>   ::= <PROGRAM+> <STATEMENT*>
<PROGRAM+>   ::= .
               | . <GLOB HD> ;
<PROGRAM>    ::= <PROGRAM*> .
<REAL NUM>   ::= <INT NUM> R
               | <FRAC NUM> ' <SCALE>
               | <INTEGER> ' <SCALE>
               | <FRAC NUM>
<REL OP>     ::= =
               | ~=
               | >=
               | <=
               | >
               | <
<REP LIST1>  ::= <REP LIST2> ,
               | <T NUMBER> (
               | (
<REP LIST2>  ::= <REP LIST1> <FILL>
<SCALE>      ::= _ <INTEGER>
               | <INTEGER>
<SHIFT OP>   ::= SHLA
               | SHLL
               | SHRA
               | SHRL
<SHORT NUM>  ::= <INT NUM> S
<REPEAT>     ::= <REPEAT> <LABEL DEF>
               | <REPEAT> <STATEMENT> ;
               | REPEAT
<REPUNTIL>   ::= UNTIL
<RP>         ::= <RP> <STATEMENT> ;
               | (
<SI T TYPE>  ::= BYTE
               | CHARACTER
               | INTEGER
               | LOGICAL
               | LONG REAL
               | REAL
               | SHORT INTEGER
<SIMPLE ST>  ::= <BLOCKBODY> END
               | <CASE SEQ> END
               | <CELL ST>
               | <FUNC ID>
               | <FUNC3>
               | <K REG ASS>
               | <PROC ID>
               | <PROC2> )
               | NULL
<STATEMENT*> ::= <GOTO ST>
               | <STATEMENT+>
<STATEMENT+> ::= <IF> <COND THEN> <GOTO ST>
               | <IF> <COND THEN> <STATEMENT+>
               | <IF> <COND THEN> <TRUE PART> <GOTO ST>
               | <IF> <COND THEN> <TRUE PART> <STATEMENT+>
               | <REPEAT> <REPUNTIL> <PRIM COND>
               | <SIMPLE ST>
               | <WHILE> <COND DO> <STATEMENT*>
               | FOR <ASS STEP> <LIMIT> <DO> <STATEMENT*>
<STATEMENT>  ::= <STATEMENT*>
<STRING>     ::= <HEX VAL> X
               | " <CHARS> "
<SYN DC1>    ::= <SI T TYPE> REGISTER <ID> SYN
               | <SYN DC3> <ID> SYN
               | <T TYPE> <ID> SYN
<SYN DC2>    ::= <SYN DC1> <K REG>
               | <SYN DC1> <T CELL>
               | <SYN DC1> <T NUMBER>
<SYN DC3>    ::= <SYN DC2> ,
<T CELL ID>  ::= <ID>
<T CELL>     ::= <T CELL ID>
               | <T CELL1> )
               | <T CELL2> )
<T CELL1>    ::= <T CELL1> <ARITH OP> <STRING>
               | <T CELL1> <ARITH OP> <T NUMBER>
               | <T CELL2> <ARITH OP> <K REG>
               | <T CELL2> <ARITH OP> <T NUMBER>
               | <T CELL3> <STRING>
               | <T CELL3> <T NUMBER>
<T CELL2>    ::= <T CELL3> <K REG>
<T CELL3>    ::= <T CELL ID> (
<T DECL1>    ::= <T DECL2>
               | <T DECL2> <ID>
               | <T TYPE>
               | <T TYPE> <ID>
<T DECL2>    ::= <T DECL4> ,
<T DECL3>    ::= <T DECL1> =
<T DECL4>    ::= <T DECL1>
               | <T DECL3> <FILL>
<T NUMBER>   ::= <ID>
               | _ <NUMBER>
               | <NUMBER>
<T TYPE>     ::= <SI T TYPE>
               | ARRAY <T NUMBER> <SI T TYPE>
<TRUE PART>  ::= <GOTO ST*> ELSE
               | <SIMPLE ST> ELSE
<UNARY CELL> ::= <UNARY OP> <T CELL>
               | <UNARY OP> <UNARY CELL>
<UNARY NUM>  ::= <UNARY OP> <T NUMBER>
               | <UNARY OP> <UNARY NUM>
<UNARY OP>   ::= ABS
               | DEC
               | HALF
               | NEG
<UNARY REG>  ::= <UNARY OP> <K REG>
               | <UNARY OP> <UNARY REG>
<WHILE>      ::= <WHILE> <STATEMENT> ;
               | WHILE

The remainder of this appendix contains a summary of all the PL360 declarations, statements, and definitions in general form. The heading on each category indicates the section numbers where the category is discussed (cross-reference to table of contents).

Blocks:   3.4
   BEGIN  declarations;  statements;  labels:  END

Data Segment Declarations:   4.1
   COMMON DATA name BASE Rn;
   COMMON BASE Rn;
   SEGMENT BASE Rn;
   GLOBAL DATA name BASE Rn;
   EXTERNAL DATA name BASE Rn;
   DUMMY BASE Rn;
   CLOSE BASE;

Cell Declarations:   4.3
|-- Type = BYTE, SHORT INTEGER, INTEGER, REAL, LONG REAL --|
   Type id1, id2, ... , idn = (initialization);
   ARRAY n Type id1, id2, ... , idn;

Cell Synonym Declarations:   4.5
|-- Type = BYTE, SHORT INTEGER, INTEGER, REAL, LONG REAL --|
   Type id1 SYN cell1, id2 SYN cell2, ... , idn SYN celln;

Register Synonym Declarations:   4.6
|-- Type = INTEGER, REAL, LONG REAL --|
   Type REGISTER id1 SYN reg1, id2 SYN reg2, ... , idn SYN regn;

Integer Value Synonym Declarations:   4.7
   EQUATE id1 SYN exp1, id2 SYN exp2, ... , idn SYN expn;

Register Assignment Statements:   5.1
   register := term1 op term2 ... op termn

Cell Assignment Statements:   5.2
   cell := term1 op term2 ... op termn    |-- unindexed cell --|
   cell := register    |-- cell may be indexed --|

GOTO Statements:   6.1
   GOTO label

Label Definitions:   6.1
   label:

IF Statements:   6.3
   IF conditions THEN statement;
   IF conditions THEN statement ELSE statement;

WHILE Statements:   6.4
   WHILE conditions DO statement;

REPEAT/UNTIL Statements:   6.5
   REPEAT
      statement;  ...  label:  ...
   UNTIL conditions;

FOR Statements:   6.6
   FOR Rn := expression STEP increment UNTIL limit DO statement;

CASE Statements:   6.7
   CASE Rn OF
   BEGIN statement; | 1st subordinate |
         statement; | 2nd subordinate |
         : : : :
         statement; | i-th subordinate |
         : : : :
         statement; | m-th subordinate |
   END

Function Declarations:   7.1
|-- type = 0 - 15,  code = #nnnn --|
   FUNCTION id1(type,code), id2( ... );

Function Statements:   7.2
   id                |-- no parameters --|
   id(p1)            |-- 1 parameter --|
   id(p1,p2)         |-- 2 parameters --|
   id(p1,p2,p3)      |-- 3 parameters --|
   id(p1,p2,p3,p4)   |-- 4 parameters --|

NULL statements:   6.3, 8.3
   NULL

Procedure Declarations:   8.1, 8.2, 8.3, 8.5
   PROCEDURE name (Ra);  statement;          | Simple procedure |
   COMMON PROCEDURE name (Ra);  statement;   | COMMON procedure |
   SEGMENT PROCEDURE name (Ra);  statement;  | SEGMENT procedure |
   SEGMENT PROCEDURE name (Ra) BASE Rp;  statement;
   GLOBAL PROCEDURE name (Ra);  statement;   | GLOBAL procedure |
   GLOBAL PROCEDURE name (Ra) BASE Rp;  statement;
   PROCEDURE name SYN prevname;              | SYNonym procedure |

Procedure Statements:   8.1, 8.2
   name
   name (Rm)

Programs:   Chapter 3 and Chapter 8
   BEGIN  declarations;  statements;  labels:  END.
   GLOBAL PROCEDURE name (Ra);  statement.   | GLOBAL program |
   GLOBAL PROCEDURE name (Ra) BASE Rp;  statement.

:J  Appendix J -- Format of PL360 Programs

During the development of the ALGOL-W compiler, Nicklus Wirth proposed a set of guidelines for producing uniformly readable PL360 programs. These rules with some minor modifications were reprinted in "PL360, A Language for the IBM 360" by Michael Malcolm of the Computer Science Department at Stanford University. I have elected to include these rules here because they have proven helpful and effective in both programming and debugging. These rules are only 'guidelines', not strict rules to be followed under any circumstances.

:J.1  Indentation

(a)  Indent lines contained between BEGIN and END by three spaces:

        BEGIN  ...
           R1 := R2;  ...
           BEGIN  ...
              PAGE;  R0 := @LINE;  ...
           END;  R6 := R5;
        END

(b)  Do not indent after IF, FOR, WHILE clauses, but reserve a separate
     line for the clause, if it is followed by a lengthy statement:

        FOR R1 := 1 STEP 1 UNTIL 100 DO
        BEGIN  ...
           ...
        END;

     However:

        IF R0 = 1 THEN  R1 := R1 + 1;

(c)  In the case of IF-THEN-ELSE, the  statements  following  THEN  and
     ELSE should be shown to be of equal 'importance';  that is:

        IF R0 = 0 THEN  R1 := 1  ELSE  R2 := R1;
     or
        IF R0 = 0 THEN  R1 := R1 * R2 + R3
                  ELSE  R1 := R1 * R3 + R2;
     or
        IF R0 = 0 THEN
        BEGIN  ...
           ...
        END ELSE
        BEGIN  ...
           ...
        END;

(d)  A program usually contains a few  very large  blocks,  each  being
     more  than  a page long.  In such cases, indentation does not make
     sense because the reader cannot see that the page  being  read  is
     related  to  preceding  or  following  pages.  It is preferable to
     accompany the BEGIN and END of such major blocks  with  a  comment
     linking them together with a common name or number.

:J.2  Spacing

Spacing is a powerful tool in grouping things together which should be read together, and to display the structure of a statement or declaration. If spaces are used in the same amount everywhere, they are useless and may as well be omitted with the benefit of saving paper. For example:

       R1 := TEMP / 4 + SIAB9 * C ; TEMP := R1 ;

is as bad as:

       R1:=TEMP/4+SIAB9*C;TEMP:=R1;

The following rules may seem a bit absurd, but have proven useful:

        R1 := X(R2+3) / Y(4) + R1;
     or
        R1 := TEMP / 4 + SIAB9 * C;  TEMP := R1;

     However:

        R1 := R1--R1;  R2 := R2+R3;
        EX(R3,MVC(0,ZAP(8),B5));

:J.3  Choice of Identifiers

:J.4  Comments

:J.5  Miscellaneous

        INTEGER FLAG;   BYTE FULLWORD;
        INTEGER X SYN B3(R2);

     It is hard to realize that the statement:  R1 := X  uses R2 as  an
     index register!

:K  Appendix K -- PL360 Under ORVYL

This Appendix contains a brief narrative description of how one uses the interactive version of PL360 which runs under the ORVYL time-sharing monitor. This version is made possible through a special ORVYL-PL360 interface module written in Assembly Language using the ORVYL macro instructions.

:K.1  Using the PL360 Compiler with ORVYL

This Appendix assumes that the ORVYL system is being used at Stanford where the ORVYL-PL360 compiler is saved as an ORVYL loadfile. To use it, just type:

          ? CALL PL360

You will then receive the message:

          -WELCOME TO PL360
          DECK?

If your account has been activated for ORVYL files, then you can type "YES" and PL360 will respond with:

          FILE NAME?

You should then type the name of an ORVYL file in which PL360 should place the object modules from the subsequent compilation. This file can be either new or old. Appending " SCR" to the file name will cause an old file to be scratched for reuse; otherwise, you will be prompted:

          SCRATCH?

A "NO" response will cause the file naming process to be repeated. The next thing PL360 asks is:

          LISTING?

If you respond "YES", then you will again be asked to supply an ORVYL file to receive the PL360 Compiler list output. PL360 then asks:

          -?

You can now type WYLBUR commands which will be passed to and executed by WYLBUR. You can continue to pass commands to WYLBUR (for example, collect lines, edit lines, use files, copy files, etc.) until your WYLBUR working data set contains the PL360 program(s). You then type "COMPILE" immediately after a -? prompt and PL360 will begin compiling the program(s) contained in your WYLBUR working data set.

Any error messages and the line on which they occur are typed at the terminal as the compilation proceeds. Each time a segment is closed a message is typed at the terminal. When compiling from a WYLBUR working data set, the compiler terminates at the end of the data set and types:

          -LEAVING PL360

You can type in a program directly by responding "COMPILE X" to a -? prompt, where X represents any non-blank character. PL360 responds:

          BEGIN TYPING PL360 PROGRAM
          -?

You can now type in a PL360 program and each line will be compiled as you go. Unfortunately, if you make a mistake, you must start over since the old lines are not saved. Also, leading blanks are stripped from each input line. For these reasons, it is usually best to compile from a WYLBUR working data set. When typing the program in directly, you can leave PL360 at any time by typing "/*" or by simply hitting the ATTN button at the terminal.

As you are leaving PL360, ORVYL core memory is automatically cleared. The WYLBUR working data set is not cleared. If the program you are compiling has numerous errors and you wish to suppress the typing of error messages at the terminal, then simply hit the ATTN button at the terminal (except in response to a prompt). PL360 will then ask:

          DO YOU WANT FURTHER ERROR MESSAGES TYPED?

A "NO" will cause the compilation to continue with no further error messages typed at the terminal. A "YES" will cause compilation to continue as before. In either case, the listing produced in the ORVYL file (if any) will be unaffected.

After leaving PL360, you can retrieve the object deck by typing:

          GET <file name> CARD CLEAR

You can retrieve the listing by typing:

          GET <file name> PRINT CLEAR

The listing has 133-byte records, the first byte of which is a carriage control character. Thus, when the listing is printed offline, the following WYLBUR command should be used:

          PRINT UNN CC

The CC part of the PRINT command causes the first byte to be treated as a carriage control character. The resulting line printer listing looks like a batch PL360 compilation listing. The ORVYL version of PL360 has several advantages: Waiting for the batch queue is completely eliminated. Errors are printed at the terminal, and thus can usually be fixed immediately and another compilation can be made in a minute or two. Paper is saved since listings with errors are seldom listed offline. Finally, the ORVYL version of the runtime library can be used to run and test the program immediately at the terminal. In this way, ORVYL's debugging tools can be used and debugging takes far less time. Most short compilations can be done in about a second or two of ORVYL compute time. This is a significant savings over batch compilations.

:K.2  Input/Output Subroutines for Interactive PL360 Programs

The standard input-output subroutines using the same linkage conventions as the READ and WRITE subroutines described in Section 8.4 are available for input-output operations directly at the terminal when running a PL360 program under the ORVYL monitor. A description of the parameter passing conventions of these subroutines follows:

   READ  The address of a 132 byte input area should be provided  in  R0
         prior   to  calling  READ.   Upon  return,  all  registers  are
         preserved except R15 which contains  the  number  of  non-blank
         characters  typed  by the user (counting imbedded blanks).  All
         details such as error messages  for  illegal  use  of  tabs  or
         waiting  too  long  to  respond  are  taken care of by the READ
         subroutine.  If the attention key is typed  with  no  preceding
         characters, the condition code is set to 2, otherwise it is set
         to 0.

.  WRITE This subroutine works exactly like the subroutine described  in
         Section  8.4;  i.e.,  the  address of a 132 byte output area is
         passed through register R0 and all registers are preserved upon
         return.  The output area is typed at the terminal.

The following discussion assumes that the ORVYL system is being used at Stanford where the ORVYL READ and WRITE subroutines and the library subroutines listed in Section 8.4 are stored in object module form in the ORVYL file ORV.GG.PUB.PL360.RUNLIB. To run a PL360 program in ORVYL, just follow this simple process. First, compile the program. This may be achieved either in batch or with the ORVYL version of the PL360 compiler. The program must be a statement with segment name SEGN001 (cf. Section 4). Place the object module output of the PL360 compiler into an ORVYL file, if not already compiled directly as the DECK output using the ORVYL version of the PL360 compiler. Place in your active file something like this:

          INCLUDE ORV.GG.PUB.PL360.RUNLIB
          INCLUDE <YourObjectFileName>
          ENTRY PL360
          LOADFILE <YourLoadFileName>
          NAME <YourProgamName>

Issue the "LINK ACTIVE" command, and <YourProgramName> will be created in <YourLoadFileName> as a member of that loadfile library.

Your program is now ready to be executed.

          CALL <YourProgramName> LIB <YourLoadFileName>

:L  Appendix L -- Exercise Answers

Chapter 1.

1.  1FFFFFFF

2.  (a)  510030
    (b)   72552
    (c)  536200
    (d)  509950
    (e)     100

3.          AND    OR   XOR
    0F/0F    0F    0F    00
    0F/F0    00    FF    FF
    0F/18    08    1F    17
    F0/F0    F0    F0    00
    F0/18    10    F8    E8
    18/18    18    18    00

4.  (a)  41880000   because  0.5 = 8/16;  so  8.0 + 8/16 = 8.5
    (b)  24.0       because  1x16 + 8 = 24
    (c) 128.0       because  8x16 + 0 = 128

Chapter 2.

1.  (b)  Begins with a digit
    (c)  Contains illegal character (period)
    (d)  Contains lowercase characters
    (g)  Definition of a constant

2.  (b)  Contains illegal characters (commas)
    (d)  Must begin with a digit, such as 0.2L
    (f)  Contains an imbedded blank
    (g)  Definition of an identifier

3.  (a)  STRING  (one byte)
    (c)  STRING  (19 bytes)
    (e)  LONG REAL
    (h)  SHORT INTEGER

Chapter 3.

1.  |01| and |23|
    |15| and |22|
    |18| and |19|

2.  |08| and |10|

3.  (a)  ARRAY, BEGIN, BYTE, COMMENT, DO, END, FUNCTION, SYN, WHILE
    (b)  BCDTOVAL, READ, R0, R1, R2, R3, R5, VALTOBCD, WRITE
    (c)  ANSWER, CARD, INPUT, OUTPUT, REDUCE

Chapter 4.

1.  (a)  ALPHA  line |18|
         BETA   line |07|
         DELTA  line |16|

    (b)  ALPHA - TEST, TOPS, EXTRA
         BETA  - EXAM, FLAGS
         DELTA - LAST

    (c)  group 1 - TEST, EXAM
         group 2 - TEST, EXAM, FLAGS, TOPS
         group 3 - TEST, EXAM
         group 4 - TEST, EXAM, EXTRA, LAST
         group 5 - TEST, EXAM

2.  (a)  LINE  at line |01| takes 133 bytes.
         XX    at line |02| takes 1024 bytes (256*4).
         SIZE  at line |03| takes 4 bytes.
         LIMIT at line |03| takes 4 bytes.
         NSQR  at line |05| takes 2 bytes.

    (b)  Lines |06| and |07|

    (c)  No, because the synonyms are declared within a block which
         ends at line |21|.  All declared quantities within a block
         are forgotten when the block ends.

Chapter 5.

1.  (a)  Can't shift floating-point register.
    (b)  Can't do logical operation with SHORT INTEGER operand.
    (c)  Requires an odd-numbered assignment register.
    (d)  Arithmetic operation invalid in cell assignment.
    (e)  Correct!
    (f)  Monadic and special operators must follow := operator only.
    (g)  Requires an odd-numbered assignment register.
    (h)  Final displacement of B2(_4) is negative.

2.  (e)  In a cell assignment statement, a register operand may
         only follow the := operator, as in (e) of problem 1.

3.  R1 := #0110;    |Notice that the original contents|
    R2 := #1100;    |of R1 and R2 are reversed when we|
    R1 := #1010;    |finish the sequence of statements|

4.  (a)  IJ := I * 16S + J * 4S;   |-- Note that INTEGER constants
         can't be used because IJ is an even-numbered register --|

    (b)  IJ=144 when I=2 and J=4
         IJ=204 when I=3 and J=3
         IJ=260 when I=4 and J=1

    (c)  I=1  and  J=1,  because (1*16+1)*4 = 68

Chapter 6.

1.  (a)  R1 <= R2
    (b)  F01 < 0L
    (c)  X = Y
    (d)  ~OVERFLOW

2.  Yes.
    |2| with |5|
    |3| with |4|
    |4| with |3|

3.  No, all GOTO's refer to the same X at line |2|.

4.  (a)  R1 = 0  (for s1)
    (b)  R1 < 0  (for s2)
    (c)  R1 > 0  (for s3)

5.  (a)  either  R1 > R2  and  R4 > R5
         or      R1 <= R2  and  R2 > R3  and  R4 > R5
    (b)  either  R1 > R2  and  R4 <= R5
         or      R1 <= R2  and  R2 > R3  and  R4 <= R5
    (c)  only    R1 <= R2  and  R2 <= R3

6.  (a)  MID := 14 SHLL 1 + R1;   |-- MID = 28 + R1 --|
    (b)  five times
    (c)  LO + HI SHRA 1  may result in an address which is not an
         even  multiple  of  the  TABLE  size  (four bytes).  For
         example, R1 = #07000 and R2 = #07004 will  yield  #07002
         which must be rounded down to #07000.

Chapter 7.

1.  (a)  PAUSE
    (b)  SETZONE(C)
    (c)  STCK(C)
    (d)  AP(N1,N2,C,CL)

2.  True for all

3.  (a)  "& -*"
    (b)  VALUES(76) := R4   or   X(#5C) := R4
    (c)  #000001346396252CX
    (d)  Yes;  CVB yields #4CE9DD;  UNPK yields "000000000504060E"

Chapter 8.

One possible answer is the last sample program in Chapter 9.

Bibliography

Eve, James. "PL360 Language Extensions." Newcastle upon Tyne, England: University of Newcastle upon Tyne, Computing Laboratory.

"IBM OS/VS Assembler Language." IBM GC33-4010.

"IBM OS/VS JCL Reference Manual." IBM G328-0618.

"IBM System/370 Principles of Operation." IBM GA22-7000.

"IBM System/370 Reference Data Card." IBM GX20-1850.

"IBM System/360 Linkage Editor and Loader Reference Card." IBM GX20-1739.

"IBM System/360 OS Assembler Language." IBM GC28-6514.

"IBM System/360 Principles of Operation." IBM GA22-6821.

"IBM System/360 Reference Data Card." IBM GX20-1703.

Kerningham, Brian, and P. J. Plauger. The Elements of Programming Style. New York: McGraw-Hill, 1974.

Michigan Terminal System (MTS). Vol. 1, rev. ed. Ann Arbor, Mich.: University of Michigan Computation Center, April 1976.

"ORVYL User's Guide." Stanford, Calif.: Stanford University, Center for Information Processing, Academic Computing Services, May 1975.

"PL360 Programming Manual." Newcastle upon Tyne, England: University of Newcastle upon Tyne, Computing Laboratory, 1970.

"PL360 Reference Manual." Stanford, Calif.: Stanford University, Center for Information Processing, Academic Computing Services, July 1975.

Wirth, Nicklus. "PL360, a Programming Language for the 360 Computers." Journal of American Computing Machinery (JACM) 15 (1968): 37. Describes the original PL360 language in Bacus-Naur syntactic form.

Wirth, Nicklus. "Format of PL360 Programs." ALGOL-W project memo. In Michael A. Malcolm, "PL360, a Programming Language for the IBM 360." Stanford, Calif.: Stanford University, Computer Science Department, May 1971. STAN-CS-71-215.


INDEX


$-CONTROL CARDS   :E.2
                  :E.1
ABNORMAL TERMINATIONS   9
                        6.7
                        4.1
ABS   5.1.1
      4.7
ADDRESS ARITHMETIC   5.1.1
                     1.4
ADDRESS, RETURN   8
ADDRESSING ABSOLUTE   :F.2
                      8.2
ADDRESSING, ABSOLUTE   4.3.2
                       4.1
ADDRESSING, RELATIVE   4.4
                       1.5
ALIGNMENT, CELL   5.2.2
                  4.5
                  4.3
AND IN COMPOUND CONDITIONS   6.2.2
AND LOGICAL OPERATOR   5.1.1
                       4.7
                       1.6
ANSWERS TO EXERCISES   :L
APOSTROPHE, USE OF   2.5.2
AREAS   4.1
ARITHMETIC SHIFTS   1.7
ARITHMETIC, ADDRESS   5.1.1
                      1.4
ARITHMETIC, FIXED-POINT   5.1.1.7
                          :A
                          5.1.1
                          4.7
                          1.3
ARITHMETIC, FLOATING-POINT   :B
                             5.1.2
                             1.8
ARITHMETIC, LOGICAL   6.2.1.1
                      5.1.1.7
                      5.1.1.4
ARITHMETIC, OPERATORS   5.1.1
                        4.7
ARRAY DECLARATIONS   4.3.1
ARRAYS   4.3.1
ASSEMBLY LANGUAGE   6.2.1.1
                    5.1.1.1
                    :D
                    7.3
ASSIGNMENT   5
ASSIGNMENT STATEMENTS   5
ASSIGNMENT, CELL   7.3
                   5.2.1
                   5.2
ASSIGNMENT, FLOATING-POINT REGISTER   5.1.2
ASSIGNMENT, INTEGER REGISTER   5.1.1
ASSIGNMENT, REVERSE   5.2.1
                      5.1.2
                      5.1.1
B-FIELD   5.1.1
          4.7
          4.4
          4.3
          1.4
BALR FUNCTION   7.3
BASE REGISTERS, DATA SEGMENT   3.6
BASE REGISTERS, PROGRAM SEGMENT   :E.4
                                  8.2
                                  3.7
BASE-DISPLACEMENTS   5.2.2
                     4.3.2
                     1.4
BCDTOVAL PROCEDURE   8.4
BITS IN BYTES   7.3
                1.1
BLANK CHARACTER   :C
                  2.5.4
BLANK COMMON   :F.2
               4.1
BLOCKS   6.1
         3.4
BOOLEAN LOGIC   6.3
BRANCH FUNCTION   7.4
BRANCHING   8.2
            8.1
            7.3
            6.1
            1.9
BRANCHING, TESTING, AND LOOPS   6
BYTE   1.1
BYTE CELL ASSIGNMENTS   7.3
                        5.2.2
BYTE CELL DECLARATIONS   4.5
                         4.3
BYTE CELL INITIALIZATION   4.3.2
BYTE CONSTANTS   2.5.4
CALLS   8
CARDS, CONTROL   :E.2
                 :E.1
CARDS, INPUT   3.3
               2.5.4
               2
CARDS, OBJECT DECK   :F.2
CASE STATEMENTS   :D.5
                  6.7
CELL ALIGNMENT   5.2.2
                 4.5
                 4.3
CELL ASSIGNMENT   5.2.1
                  5.1.2
                  5.1.1
CELL ASSIGNMENTS   5.2
CELL DECLARATIONS   4.3
CELL INITIALIZATION   4.3.2
CELL REFERENCES   4.4
                  4.3.1
CELL SYNONYMS   4.5
CELLS   1.1
CHARACTER TABLE, EBCDIC   :C
CLC FUNCTION   7.3
CLI FUNCTION   7.3
CLOSE BASE DECLARATIONS   4.1.2
CODES, CONDITION   6.2.1
CODES, FUNCTION   7.1
CODES, INSTRUCTION   :D
                     7.1
                     1.4
COMMENT IDENTIFIER   2.6
COMMENTS   2.6
COMMON DATA DECLARATIONS   :F.2
                           4.1
COMMON PROCEDURES   :G.4
                    :F.2
                    8.1
COMPARE INSTRUCTIONS   6.2.1
COMPILATION   :E.2
              8.2
              3.3
COMPILATION AND EXECUTION   3.3
COMPILER DIAGNOSTICS   :H
COMPILER GENERATED NAMES   3.2
COMPLEMENT ARITHMETIC   :A
COMPOUND CONDITIONS   6.2.2
CONDITION CODE TESTING   :D.5
                         6.2.1
CONDITIONAL BRANCH   6.2.1
                     1.9
CONDITIONAL COMPILE DIRECTIVES   :E.8
CONDITIONS   6.2
CONDITIONS, COMPOUND   6.2.2
CONDITIONS, INVERSE   6.3
CONDITIONS, SIMPLE   6.2.1
CONDITIONS, TABLE OF   5.1.1.3
                       6.2.1
CONSTANTS   2.5
CONSTANTS, FIXED-LENGTH   2.5.1
CONSTANTS, FLOATING-POINT   2.5.2
CONSTANTS, STRING   2.5.4
CONSTRUCTS, LANGUAGE   :D
CONTROL CARDS, COMPILER   :E.2
                          :E.1
CONVENTIONS, LINKAGE   :G.1
CR FUNCTION   7.4
CROSS REFERENCE   :E.2
CVB FUNCTION   7.3
CVD FUNCTION   7.3
D-FIELD   5.1.1
          4.7
          4.4
          4.3
          1.4
DATA SEGMENT DECLARATIONS   4.1
DATA SEGMENTS   4.1
                3.6
DATA SETS   8.4
DEC   5.1.1
      4.7
DEC CONDITION   6.2.3
DECKS, OBJECT   :G.4
                :F.2
                :E.8
                3.3
DECLARATIONS   4
               3.4.2
               3.4.1
DECLARATIONS, ARRAY   4.3.1
DECLARATIONS, CELL   4.3
DECLARATIONS, CELL SYNONYM   4.5
DECLARATIONS, DATA SEGMENT   3.7
DECLARATIONS, EQUATE VALUES   4.7
DECLARATIONS, FUNCTION   7.1
DECLARATIONS, PROCEDURE   8.3
                          8.2
                          8.1
DECLARATIONS, REGISTER SYNONYM   4.6
DECLARATIONS, SAMPLE   4.8
DELIMITERS   2.4
DIAGNOSTICS, COMPILER   :H
DIVISION, FIXED-POINT   5.1.1.6
DOUBLE-WORDS   7.3
               4.3.1
               2.5.2
               1.8
               1.1
DUMMY BASE DECLARATIONS   4.1.1
DUMMY DATA SEGMENTS   4.1.1
EBCDIC TABLE   :C
ED FUNCTION   7.3
ENTRY POINTS   :F.2
               8.2
               8.1
               4.3.2
EQUATE DECLARATIONS   4.7
ERROR DIAGNOSTICS   :H
EVEN-NUMBERED REGISTERS   5.1.1.6
                          7.3
EX FUNCTION   7.3
EXECUTION   :F.2
            9
            8.2
            4.1.1
            3.3
EXERCISES   6.8
            8.7
            7.5
            5.3
            4.9
            3.8
            1.11
            2.7
EXERCISES, ANSWERS TO   :L
EXTERNAL DATA SEGMENTS   :G.4
                         :F.2
                         4.1
EXTERNAL PROCEDURE DECLARATIONS   8.3
EXTERNAL PROCEDURES   8.3
FIXED-LENGTH   :A
               4.3.2
               2.5.3
               1.8
               1.1
FIXED-POINT ARITHMETIC   1.3
FIXED-POINT CONSTANTS   2.5.1
FLOATING-POINT ARITHMETIC   :B
                            1.8
FLOATING-POINT CELL ASSIGNMENTS   5.2.1
                                  5.1.2
FLOATING-POINT CONSTANTS   2.5.2
FLOATING-POINT REGISTER ASSIGNMENTS   5.1.2
FLOATING-POINT REGISTERS   5.1.2
                           2.3
                           1.8
                           1.2
FOR STATEMENTS   :D.5
                 6.6
FUNCTION CODES   7.1
FUNCTION DECLARATIONS   7.1
FUNCTION STATEMENTS   7.2
FUNCTION TYPE TABLE   7.1
FUNCTIONS   7
FUNCTIONS, PRE-DECLARED   :D.3
                          7.3
FUNCTIONS, USER-DEFINED   7.4
GENERATED INSTRUCTIONS   :D
GET PROCEDURE   8.4
GLOBAL DATA SEGMENTS   4.1
GLOBAL PROCEDURE DECLARATIONS   8.2
GLOBAL PROCEDURES   8.2
GOTO LABELS   8.1
              6.1
              3.4
GOTO RESTRICTION   8.2
                   6.1
GOTO STATEMENTS   6.1
                  3.4
GT CONDITION   6.2.3
IC FUNCTION   7.3
IDENTIFIERS   2.1
              2.2
IDENTIFIERS, PRE-DECLARED   8.4
                            7.3
                            4.7
                            4.5
                            2.3
IDENTIFIERS, RESERVED   2.3
IF STATEMENTS   :D.5
                6.3
INITIALIZATION, CELL   4.3.2
INPUT/OUTPUT PROCEDURES   8.4
INSTRUCTION CODES   :D
                    7.1
                    1.4
INSTRUCTIONS AND ADDRESSING   1.4
INSTRUCTIONS, GENERATED   :D
INTEGER ARITHMETIC   :A
                     5.1.1
                     1.3
INTEGER CELL ASSIGNMENTS   5.2.1
                           5.1.1
INTEGER CELL DECLARATIONS   4.5
                            4.3
INTEGER CELL INITIALIZATION   4.3.2
INTEGER CONSTANTS   2.5.1
INTEGER EQUATE VALUES   4.7
INTEGER REGISTER ASSIGNMENTS   5.1.1
INTEGER REGISTER SYNONYMS   4.6
INTEGER REGISTER USAGE   5.1.1.7
INTEGER REGISTERS   5.1
                    2.3
                    1.3
                    1.2
INTEGER VALUE SYNONYMS: EQUATE   4.7
INVERSE CONDITIONS   6.3
KLOSE PROCEDURE   8.4
LA FUNCTION   7.3
LABELED COMMON   :F.2
                 4.1
LABELS, GOTO   8.1
               6.1
               3.4
LANGUAGE CONSTRUCTS   :D
LE CONDITION   6.2.3
LINKAGE CONVENTIONS   :G.1
LISTING CONTROL CARDS   :E.2
                        :E.1
LISTING OPTIONS   :E.2
LISTING OUTPUT FORMAT   :F.2
LITERALS   8.2
           7.2
           3.7
LM FUNCTION   7.3
LOCAL PROCEDURES   8.1
LOGIC TABLES   1.6
LOGIC, BOOLEAN   6.3
LOGICAL ADDITION AND SUBTRACTION   5.1.1.4
LOGICAL ARITHMETIC   6.2.1.1
                     5.1.1.4
LOGICAL OPERATIONS   5.1.1.5
                     1.6
LOGICAL OPERATORS   5.1.1.5
                    5.1.1
                    4.7
                    1.6
LOGICAL SHIFTS   1.7
LOOPS, FOR STATEMENT   6.6
LOOPS, REPEAT/UNTIL STATEMENT   6.5
LOOPS, WHILE STATEMENT   6.4
LTR FUNCTION   7.3
MAIN PROGRAM   8.2
               4.2
               3.7
MAIN PROGRAM BASE REGISTERS   :E.4
MAIN PROGRAM DATA SEGMENTS   4.2
MAIN PROGRAM EXAMPLES   9
MAIN PROGRAMS   3.5
MISCELLANEOUS   8.6
MONADIC OPERATORS   5.1.2
                    5.1.1
                    5.1
                    4.7
MONADIC OPERATORS WITH EQUATE VALUES   4.7
MONADIC OPERATORS WITH REGISTERS   5.1
MULTIPLICATION AND DIVISION   5.1.1.6
MULTIPLICATION, FIXED-POINT   5.1.1.6
MVC FUNCTION   7.3
MVI FUNCTION   7.3
NC FUNCTION   7.3
NEG   5.1.1
      4.7
NEG ABS   5.1.1
          4.7
NESTED BLOCKS   6.1
                3.4.2
NI FUNCTION   7.3
NORMALIZED VALUE   :B
                   5.1.2
                   2.5.2
                   1.8
NULL STATEMENT   8.3
                 6.3
OBJECT DECKS   :G.4
               :F.2
               :E.8
               3.3
OC FUNCTION   7.3
ODD-NUMBERED REGISTERS   5.1.1.6
                         7.3
OI FUNCTION   7.3
OPEN PROCEDURE   8.4
OPERATIONS, FIXED-POINT   5.1.1.6
                          5.1.1
                          1.3
OPERATIONS, FLOATING-POINT   5.1.2
                             1.8
OPERATIONS, LOGICAL   5.1.1.5
                      1.6
OPERATIONS, SHIFT   5.1.1.3
                    5.1.1
                    4.7
                    1.7
OPERATOR RESTRICTIONS AND PRECEDENCE   5.1.1.2
OPERATORS, ARITHMETIC   5.1.1
                        4.7
OPERATORS, LOGICAL   5.1.1.5
                     5.1.1
                     4.7
                     1.6
OPERATORS, MONADIC   5.1.2
                     5.1.1
                     5.1
                     4.7
OPERATORS, RELATIONAL   6.2.1.1
OPERATORS, SHIFT   5.1.1
                   4.7
OR IN COMPOUND CONDITIONS   6.2.2
OR LOGICAL OPERATOR   5.1.1
                      4.7
                      1.6
OTHER FUNCTIONS   7.4
OUTPUT LISTINGS   :F.2
OUTPUT OBJECT DECK   :G.4
                     :F.2
OUTPUT, CONTROL OF   :E.2
                     :E.1
PACK FUNCTION   7.3
PACKED FORMAT OPERANDS   7.3
PAGE PROCEDURE   8.4
PAIRS, EVEN-ODD REGISTER   5.1.1.6
                           7.3
PL360 TEXTBOOK   *
PRE-DECLARED FUNCTIONS   7.3
PRE-DECLARED IDENTIFIERS   8.4
                           7.3
                           4.7
                           4.5
                           2.3
PRE-DECLARED PROCEDURES   8.4
PRECEDENCE, RULES OF   5.1.1.2
                       5
                       4.7
PREFACE   +
PRINT PROCEDURE   8.4
PROCEDURE DECLARATIONS   :D.5
                         8
PROCEDURE STATEMENTS   :D.5
                       8.2
                       8
PROCEDURE SYNONYMS   8.5
PROCEDURES   8
PROCEDURES, COMMON   8.1
PROCEDURES, EXTERNAL   8.3
PROCEDURES, GLOBAL   8.2
PROCEDURES, LOCAL   8.1
PROCEDURES, PRE-DECLARED   8.4
PROCEDURES, RECURSIVE   9
                        8.1
PROCEDURES, SEGMENT   8.2
PROGRAM MAIN   8.2
               4.2
PROGRAM SEGMENTS   8
                   3.7
PROGRAM STATUS WORD   7.3
                      6.2.1
                      1.9
PROGRAM, MAIN   3.5
PUNCH PROCEDURE   8.4
PUT PROCEDURE   8.4
READ PROCEDURE   8.4
REAL CELL ASSIGNMENTS   5.2.1
                        5.1.2
REAL CELL DECLARATIONS   4.5
                         4.3
REAL CELL INITIALIZATION   4.3.2
REAL CONSTANTS   2.5.2
REAL REGISTER ASSIGNMENTS   5.1.2
REAL REGISTER SYNONYMS   4.6
REAL REGISTERS   5.1.2
                 2.3
                 1.8
                 1.2
RECORDS   8.4
RECURSIVE PROCEDURES   9
                       8.1
REDEFINITION   6.1
               3.4.2
REDUCE FUNCTION   7.4
REFERENCES, CELL   4.4
                   4.3.1
REGISTER ASSIGNMENT STATEMENTS   5.1
REGISTER SYNONYM DECLARATIONS   4.6
REGISTER SYNONYMS   4.6
REGISTER-TO-STORAGE   5.2.1
REGISTERS   1.1
            1.2
REGISTERS, BASE   5.1.1.7
                  :E.4
                  8.2
                  5.1.2
                  3.7
                  1.4
REGISTERS, EVEN-NUMBERED   5.1.1.6
                           7.3
REGISTERS, FLOATING-POINT   5.1.2
                            2.3
                            1.8
REGISTERS, INDEX   5.1.1.7
                   5.2.1
                   5.1.2
                   4.4
                   1.4
REGISTERS, INTEGER   5.1
                     2.3
REGISTERS, ODD-NUMBERED   5.1.1.6
                          7.3
REGISTERS, PRE-DECLARED   2.3
REGISTERS, RETURN ADDRESS   8
RELATIONAL OPERATORS   6.2.1.1
RELATIVE ADDRESSING   4.4
                      1.5
REPEAT/UNTIL STATEMENTS   6.5
RESERVED AND PRE-DECLARED IDENTIFIERS   2.3
RESERVED IDENTIFIERS   2.3
RESET FUNCTION   7.3
RESETB FUNCTION   7.4
RESTRICTIONS, EQUATE WITH CELLS   4.7
RESTRICTIONS, GOTO   8.2
                     6.1
RESTRICTIONS, INTEGER MULTIPLE/DIVIDE   5.1.1.6
RESTRICTIONS, SCOPE OF DECLARATIONS   3.4.2
RESTRICTIONS, STORAGE-TO-STORAGE   5.2.2
RETURN ADDRESS   8
REVERSE ASSIGNMENT   5.1.1.1
                     5.2.1
                     5.1.2
                     5.1.1
RULES OF PRECEDENCE   5.1.1.2
                      5
                      4.7
RULES OF SCOPE   6.1
                 3.4.2
RULES, FIXED-LENGTH   2.5.3
RUNTIME LIBRARY   8.4
SAMPLE DECLARATIONS   4.8
SAMPLE PROGRAMS   9
                  8.4
SCOPE, RULES OF   6.1
                  3.4.2
SEGMENT DATA DECLARATIONS   4.1
SEGMENT PROCEDURE DECLARATIONS   8.2
SEGMENTATION   3.1
SEGMENTATION, CONCEPT OF   3.1
SEGMENTS   :F.2
SEGMENTS, DATA   3.6
SEGMENTS, PROGRAM   8
                    3.7
SET FUNCTION   7.3
SETB FUNCTION   7.4
SETUP FUNCTION   7.4
SHIFT OPERATIONS   5.1.1.3
                   1.7
SHIFTING   5.1.1.3
           5.1.1
           4.7
           1.7
SHLA SHIFT OPERATOR   5.1.1
                      4.7
SHLL SHIFT OPERATOR   5.1.1
                      4.7
SHORT EQUATE   4.7
SHORT INTEGER CELL ASSIGNMENTS   5.2.1
                                 5.1.1
SHORT INTEGER CELL DECLARATIONS   4.3
SHORT INTEGER CELL INITIALIZATION   4.3.2
SHORT INTEGER CONSTANTS   2.5.1
SHRA SHIFT OPERATOR   5.1.1
                      4.7
SHRL SHIFT OPERATOR   5.1.1
                      4.7
SIMPLE CONDITIONS   6.2.1
SLDA FUNCTION   7.3
SLDL FUNCTION   7.3
SPECIAL CONDITIONS   6.2.3
SPECIAL SYMBOLS   2.4
SPECIAL SYMBOLS AND DELIMITERS   2.4
SPM FUNCTION   7.3
SRDA FUNCTION   7.3
SRDL FUNCTION   7.3
STATEMENTS   3.4.2
STATEMENTS, BLOCK   3.4
STATEMENTS, CASE   :D.5
                   6.7
STATEMENTS, CELL ASSIGNMENT   5.2.1
STATEMENTS, FOR   :D.5
                  6.6
STATEMENTS, FUNCTION   7.2
STATEMENTS, GOTO   6.1
STATEMENTS, IF   :D.5
                 6.3
STATEMENTS, NULL   8.3
                   6.3
STATEMENTS, PROCEDURE   :D.5
                        8.2
                        8
STATEMENTS, REGISTER ASSIGNMENT   5.1
STATEMENTS, REPEAT/UNTIL   6.5
STATEMENTS, WHILE   :D.5
                    6.4
STATUS WORD, PROGRAM   7.3
                       6.2.1
                       1.9
STC FUNCTION   7.3
STM FUNCTION   7.3
STORAGE   1.1
STORAGE ADDRESSING   4.3.2
                     1.4
STORAGE CELLS   4.3
STORAGE-TO-STORAGE   5.2.2
STRING EQUATE VALUE   4.7
STRINGS   7.2
          5.2.2
          4.3.2
          2.5.4
SUBSCRIPTS   5.1.1.7
             5.2.2
             4.4
SUBTITLES   :E.1
SUMMARY   1.10
SUMMARY OF RULES FOR FIXED-LENGTH VALUES   2.5.3
SVC FUNCTION   7.3
SYMBOLS, SPECIAL   2.4
SYN SYNONYM INDICATOR   4.5
SYNONYM, PROCEDURE   8.5
SYNONYMS   4.5
SYNONYMS, CELL   4.5
SYNONYMS, EQUATE VALUE   4.7
SYNONYMS, REGISTER   4.6
TABLE OF CONDITIONS   5.1.1.3
                      6.2.1
TABLE OF FUNCTION TYPE   7.1
TABLE, EBCDIC   :C
TABLE, LOGIC   1.6
TERMINATIONS, ABNORMAL   9
                         6.7
                         4.1
TERMINATORS   6.3
              5
              3.4
              3.5
TESTING, CONDITION CODE   :D.5
                          6.2.1
THE IBM SYSTEM/360 COMPUTER   1
THE MAIN PROGRAM   3
THE MAIN PROGRAM DATA SEGMENT   4.2
THE PL360 LANGUAGE   2
THE PROGRAM   2.1
TITLES   :F.2
         :E.1
TM FUNCTION   7.3
TR FUNCTION   7.3
TRT FUNCTION   7.3
TYPES, CELL   4.5
              4.3
TYPES, CONSTANT   2.5
TYPES, REGISTER   5.1
                  4.6
UNCONDITIONAL BRANCH   6.2.1.1
                       8.2
                       8.1
                       7.3
                       6.1
                       1.9
UNDEFINED QUANTITIES   :H
                       8.3
                       4.3.2
UNDERFLOW, EXPONENT   7.3
                      5.1.2
UNDERSCORE   2.4
             1.3
UNINDEXED CELLS   :D.5
                  7.2
                  5.2.2
UNNORMALIZED VALUE   :B
                     5.1.2
                     2.5.2
                     1.8
UNPK FUNCTION   7.3
UNSIGNED OPERATIONS   6.2.1.1
                      5.1.1
                      4.7
                      1.6
UPPER/LOWERCASE   :E
                  3.4.1
                  2.2
VALTOBCD PROCEDURE   8.4
VARIABLE-LENGTH   7.2
                  5.2.2
                  4.3.2
                  2.5.4
                  1.6
                  1.1
VARIABLE-LENGTH STRINGS   2.5.4
VERTICAL BAR COMMENTS   2.6
WHILE STATEMENTS   :D.5
                   6.4
WRITE PROCEDURE   8.4
X-FIELD   5.2.1
          5.1.1
          4.7
          4.4
          1.4
XC FUNCTION   7.3
XI FUNCTION   7.3
XOR LOGICAL OPERATOR   5.1.1
                       4.7
                       1.6
XOR'ED FUNCTION FIELDS   7.1
ZONED FORMAT OPERANDS   7.3