BLISS-11 PROGRAMMERS MANUAL SOFTWARE SUPPORT CATEGORY The software described in this document is not supported by Digital Equipment Corporation. It is currently available upon request from the Computer Science Department, Carnegie-Mellon University, Pittsburgh, PI. 15213, subject to their own distribution and support procedures. First printing, December 1972 Second printing, March 1973 Third printing, April 1974 (revised) Copyright (C) 1972, 1973, 1974 by Digital Equipment Corporation This document is for information purposes and is subject to change without notice. The following are registered trademarks of Digital Equipment Corporation: Digital (logo) PDP-11 DEC Comtex-11 DECtape RSTS-11 RSX-I1 Unibus ACKNOWLEDGEMENT --------------- Many persons have made significant contributions to the design and development of the BLISS-11 language and its compiler. The cooperation of faculty, staff, and graduate students at Carnegie-Mellon University is possible in part because of a Department of Computer Science orientation that pursues research and advanced development efforts of relevance to the total community, public and private. Digital Equipment Corporation acknowledges the considerable work performed by the members of the Department of Computer Science in bringing BLISS-11 into being. Prime contributors are: Jerry L. Apperson Charles M. Geschke Steven O. Hobbs Richard K. Johnsson Paul J. Knueven Bruce W. Leverett Charles B. Weinstock David S. Wile William A. Wulf Department of Computer Science Carnegie-Mellon University Pittsburgh, Pennsylvania Ronald F. Brander Mario R. Pellegrini Digital Equipment Corporation Maynard, Massachusetts TABLE OF CONTENTS INTRODUCTION 1.0 LANGUAGE DEFINITION 2 1.1 BLISS - An Expression Language 2 1.2 Expressions 2 1.3 Simple Expressions 3 1.3.1 Order of Expression Evaluation 1.4 Names 10 1.5 The "Contents of" Operator 10 1.6 Data Representation 11 1.7 Comments 12 1.8 Modules 12 1.9 Blocks 13 1.10 Literals 13 1.11 Pointers to Literals 15 1.12 Position and Size Modifier 16 1.13 Interlude 23 1.14 Control Expressions 23 1.15 Conditional Expressions 23 1.16 Loop Expressions 24 1.17 Escape Expressions 26 1.18 Choice Expressions 27 1.18.1 CASE Expression 1.18.2 SELECT Expression 1.18.3 Compile-time Constant Conditional and Choice Expressions 1.19 Coroutine Expressions [not implemented] 30 1.20 Declarations 30 1.21 Storage (An Introduction) 31 1.22 Routines 32 1.22.1 GLOBAL Routines 1.22.2 EXTERNAL and FORWARD Routine Declarations 1.22.3 Calling Sequence Declarations 1.22.4 Predefined Calling Sequences 1.22.5 Calling Sequences for Variable Routine Names 1.23 Data Structures (An Introduction) 38 1.24 Data Structures (Actual Syntax) 41 1.24.1 Structures 1.24.2 Structure Accesses for Variable Segment Names 1.24.3 Memory Allocation 1.24.4 MAP Declaration 1.24.5 BIND Declaration 1.25 LABEL Declaration 46 1.26 UNDECLARE Declaration 46 1.27 ENABLE Declaration 46 1.28 Macros 47 1.28.1 Syntax For Macro Declarations 1.28.2 Macro Forms and Special Functions 1.28.3 Macro Forms and Examples 1.28.4 Default Separators 1.28.5 Special Functions 1.29 CSECT and PSECT Declarations 55 1.30 REQUIRE Declaration 55 1.31 SWITCHES Declaration 56 SPECIAL LANGUAGE FEATURES 57 Special Routines 57 2.1.1 TRAP, EMT and IOT 2.1.2 HALT, RESET and WAIT 2.1.3 SWAB 2.2 INLINE - Machine Language in BLISS-11 58 3.0 SYSTEM FEATURES 59 3.1 Command Syntax 59 3.2 Compiler Options 59 3.3 Error Reporting 63 4.0 RUN TIME REPRESENTATION OF PROGRAMS 64 4.1 Introduction To Gelling Sequences 64 4.2 BLISS-11 Internal Linkage 64 4.3 BLISS-11 With Arguments Passed In Registers 65 4.4 FORTRAN Linkage [not implemented] 65 4.5 INTERRUPT Linkage 66 4.6 EMT/TRAP Linkage 67 4.7 Access To Variables 67 4.8 Coroutine Creation end Calls 67 5.0 EXAMPLES 68 Appendix A - BLISS-11 Syntax 72 Appendix B - Description of Non-Terminals of BLISS-11 BNF 76 Appendix C - Reserved Names 84 Appendix D - Predefined Identifiers 85 Appendix E - RADIX 50 (RAD5O) 86 Appendix F - Error Messages 87 INDEX 89 Too little do we realize what a hindrance a language of antiquated structure is. Such a language does not help, but actually prevents correct analysis through the semantic habits and structural implications embodied in it. Alfred Korzybski Science and Sanity INTRODUCTION BLISS-11 is a programming language for the PDP-11. It is specifically intended to be used for implementing "System Software". As such it differs from other languages in several significant ways. 1 Higher level languages derive their suitability for a particular problem area - FORTRAN/ALGOL for mathematics, SNOBOL for strings, GPSS for simulations, etc., - because they provide to the user a vocabulary and set of conventions appropriate to that problem area. We view "Implementation Languages" in a similar way - as application languages where the application is a particular brand of hardware. As such, an implementation language must reflect the capabilities and architecture of its machine, and not block or frustrate the programmer's use of these capabilities. 2 Input/output is not a part of BLISS-11. I/O can be done directly in the language much as an assembly program might, or through subroutine calls. 3 Every attempt has been made to give the user explicit control over the code his program generates, while providing maximum convenience otherwise. 4 There are no explicit or implicit data modes other than the 16 bit binary word. Data modes are essentially user defined via the STRUCTURE* mechanism which allows the user to define an algorithm for data access. Clearly BLISS-11 is a close relative of BLISS-10 for the DECsystem-10 and shares much of that language's philosophy toward treatment of identifiers, absence of GOTO statements, importance of structures, etc. The interested reader may find it useful to examine the differences between the two languages and how these are a reflection of the differences in the machines themselves. ---------- *Words in uppercase represent reserved words in BLISS-11 (terminal symbols in the language), but in programs the compiler treats upper and lower case letters as equivalent and thus recognizes names and reserved words in either case. ---------- 1.0 LANGUAGE DEFINITION 1.1 BLISS - An Expression Language The programming language BLISS enables programmers (persons who converse in a programming language) to construct text (programs) which evoke computations to transform input into a desired result. Programs written in BLISS consist of declarations (which establish structure) and expressions sequenced to compute results. Expressions in BLISS can assume remarkably complex forms built up from elementary forms; but regardless of their complexity every expression computes a value. This notion, that a BLISS program consists solely of declarations and expressions, and that these expressions can become arbitrarily complex yet compute a single value during the execution of the program, represents a key concept the reader must understand to properly construct BLISS programs and fully exploit the language's power. The concept of a statement prevalent in many programming languages has little meaning in BLISS. In reading this manual the reader should strive to master the implications and meaning of the statement: 'BLISS is an expression language'. 1.2 Expressions Every executable form in the BLISS language (that is, every form except the declarations) computes a value. Thus all commands are expressions. In the syntax description E is used as an abbreviation for expression. E ::= smpl-expr* / cntrl-expr Note: Section 1.3 lists all the simple expressions (smpl-expr) in BLISS-11. This list appears with full knowledge that the reader does not yet know the semantics of many of the operators. In doing so we have presumed on the sophistication of our readers and depend on them to fill in the gaps usually provided in documents with a tutorial intent. The discussion of control expressions (cntrl-expr) will appear in sections 1.14-1.19 after discussing a sufficient number of the components making up simple expressions (block, literal, name, etc.) to permit the construction of some simple programs. ---------- *This manual will use a form of syntax specification similar to BNF, differing from it by its absence of angle brackets <> to enclose non-terminals in the language. We shall indicate non-terminals by using lower case characters. Appendix B contains definitions of non-terminals used throughout the manual. ----------- 1.3 Simple Expressions The semantics of simple expressions (smpl-expr) is most easily described in terms of the relative precedence of a set of operators, but readers should also refer to the BNF-like description at the end of this section. The syntax definition implies that binding must take place in the precedence order given below. The precedence number used should be viewed as an ordinal, so that 1 means first and 2 second in precedence. In the following table the letter E has been used to denote an actual expression of the appropriate syntactic type, see Appendix A. The basic building block of simple expressions is the primary. The primaries in BLISS-11 are: Blocks (including compound and parenthesized expressions) Routine Calls Structure Accesses Names Literals Each primary is itself a simple expression. Blocks, routine calls and structure accesses can be built up out of other expressions. Other simple expressions are built up by applying operators to primaries and to simple expressions that have already been formed. Operators are applied in precedence order. In most cases, operators of equal precedence are applied in left to right order. The operators dot, unary plus, unary minus, NOT, and the store operators (= and <--) are applied in right to left order. (However, see discussion of side effects in Section 1.3.1) Precedence Example(s) Semantics 1 (E0;E1;...En) A block. This includes the important subcases of compound expression and parenthesized expression. The component expressions are evaluated from left to right and the final value is that of the last component expression. Note the absence of a trailing semicolon. If such a semicolon appears then En is the empty expression and has the value zero. 1 E0(E1,E2,£££,En) A routine call, see 1.23. 1 name[E1,E2,...,En] A structure access, see 1.24. 1 name A pointer, see 1.4. 1 13, #46, "A" A literal. Value of the converted literal, see 1.10. Note: All precedence 1 items are primaries. 2 .E The "contents of" E. Value pointed £E at by E, possibly modified by position and size E1 and E2. (E1 and E2 must evaluate to compile time constants*.) 3 -E Unary minus. The negative of E. 3 +E Unary plus. E. 4 E1^E2** E1 shifted arithmetically by EP bits. Shift to left if Ep is positive, to right if negative. 4 E1 ROT E2 Rotate E1 by E2 bits. Rotate left if E2 is positive, right if negative. This operator rotates 17 bits.*** 5 E1*E2 Product of E's. 5 E1/E2 E1 divided by E2. E1 DIV E2 5 E1 MOD E2 E1 module E2. 6 E1+E2 Sum of E's. 6 E1-E2 Difference between E1 and E2. 7 E1 MAX E2 The greater of E1, E2£ 7 E1 MIN E2 The lesser of E1, E2. Note: All arithmetic is carried out module 2^16 with a residue of -2^15 All arithmetic is integer. Note: For all relational operators (precedence=8), the resultant value is 1 if the relation holds and O if it does not. (See Section 1.6) ---------- *see section 1.18.3 for a detailed discussion of compile-time constants and their use. **The characters up-arrow, and caret are treated identically, as are back-arrow and underscore. ***The ROT operator is defined by the PDP-I1 ROR and ROL machine language instructions; thus the carry bit is included in the quantity rotated. ---------- 8 E1 EQL E2 E1 "equal to" E2. 8 E1 NEQ E2 E1 "not equal to" E2. 8 E1 LSS E2 E1 "less than" E2. 8 E1 LEQ E2 E1 "less than or equal to" E2. 8 E1 GTR E2 E1 "greater than" E2. 8 E1 GEQ E2 E1 "greater than or equal to" E2. Note: The following relational operators are unsigned versions of the above; i.e. they treat E1 and E2 as 16 bit unsigned integers. 8 E1 EQLU E2 8 E1 NEQU E2 8 E1 LSSU E2 8 E1 LEQU E2 8 E1 GTRU E2 8 E1 GEQU E2 9 NOT E Bitwise "complement" of E. 10 E1 AND E2 Bitwise "and" of E's. 11 E1 OR E2 Bitwise "inclusive or" of E's. 12 E1 XOR E2 Bitwise "exclusive or" of. E's. 12 E1 EQV E2 Bitwise "equivalence" of E's. 13 E1 = E2 Store E2 in E1. The value of this E1=E2 expression is identical to that of E2, but as a side effect this value is stored into the partial word pointed to by E1. Assignments are executed from right to left: thus E1 = E2 = E3 is equivalent to E1 = (E2 = E3). Note: Position and size (E3 and E4) are associated with the store operator in this case and they modify the effect of the store (see Section 1.12). The reader should refer to a PDP-11 Processor Handbook for complete definition of the arithmetic operators under various special input value conditions. The syntax for BLISS-11 simple expressions is as follows: smpl-expr ::= P12 / P12=E / pt pos-size = E P12 ::= P11 / P12 XOR P11 / P12 EQV P11 P11 ::= P10 / P11 OR P10 P10 ::= P9 / P10 AND P9 P9 ::= P8 / NOT P9 P8 ::= P7 / P8 rel-op P7 P7 ::= P6 / P7+P6 / P7-P6 P6 ::= P5 / P6*PS / P6 DIV P5 / P6/P5 / P6 MOD P5 P5 ::= P4 / P5^P4 / P5 ROT P4 P4 ::= P3 / +P4 / -P4 P3 ::= P2 /.P3 P2 ::= P1 /.P2 pos-size pi ::= literal/ name / strc-access / block / routn-call pos-site ::= position ::= compile-time-expr size ::= compile-time-expr rel-op ::= EQL / NEQ / LSS / LEQ / GTR / GEQ / EQLU / NEQU / LSSU / LEQU / GTRU / GEQU strc-access ::= name[expr-lst] / name[alloc-unit expr-lst;expr-lst] routn-call ::= Pl(expr-lst) / P1( ) The following are legal (if somewhat unusual) BLISS-11 expressions. .A<0,16><0,8>=.B NOT NOT .A ..(.A<0,1 6>)<0,8> A(.X,1,.Y)(.M,.N) 1.3.1 Order of Expression Evaluation* BLISS-11 has two potential side-effect producing operations: 1. The assignment operator, and 2. Routine calls (which can produce side effects by internally contained assignments and routine calls.) The order of expression evaluation is only partly specified in BLISS-11. Whenever the order is not fully specified, the compiler is free to complete the specification in any way it chooses. A dangerous side-effect occurrs if the result of expression evaluation depends upon the order in which the expression was evaluated. This can only happen if the same variable appears more than once in the expression (explicitly or implicitly) and is altered at least once during expression evaluation. For example, the expression (R=2; £R*(R=3)) may evaluate to 6 or 9 depending an which of the expressions .R or (R=3) is evaluated first. Problems of this type are usually resolved by establishing order-of-evaluation rules. To describe these rules in BLISS-11, we must consider five different kinds of orderings: Lexical order (the ordering of program text); Order imposed by operator precedence and parentheses; The order (if any) observed with respect to sub-expressions of commutative operators (e.g. +, *, AND, etc.); The 'essential' order observed with respect to expressions which produce 'side-effects' (specifically, assignments and routine calls). The evaluation rules for expressions whose values may be uniquely determined without evaluating the entire expression. BLISS-11 applies rules to these cases as follows: Case 1 The lexical order is determined by the programmer. Case 2 The effect of operator precedence (hierarchy) and parentheses (grouping), subject to the definitions in Cases 3 and 4 below, will be observed. ---------- *This section logically belongs here, but contains examples that presume knowledge of other sections, in particular 1.4 and 1.5; on a first pass the reader may find it useful to read these sections before proceeding with 1.3.1 ---------- Case 3 Mathematically commutative operators are assumed to have computational commutativity as well. Thus E1+E2 may compile as E1+E2, or E2+E1. Case 4 Side-effects are accounted for only at certain prescribed "mark points" in an expression. Thus, for example, a ";" within a compound expression is a "mark point", and all side-effect producing operations lexically to the left of the ";" within the enclosing compound are completed prior to evaluation of expressions to the right of the ";". The following "mark points" are currently defined in BLISS-11: - ";" in a compound expression - ")" in a routine call parameter list - The end of each expression component in a loop expression, conditional expression or choice expression - ":" in an ENABLE declaration. Case 5 Expression evaluation occurs only to the point necessary to uniquely determine the expression value. The compiler may make other re-arrangements but they are guaranteed to observe the rules stated above. These rules permit more code re-arrangement than typically found in higher-level languages. There are two reasons for this: 1. The compiler can produce considerably better code if allowed more freedom, and 2. The rules tend to eliminate the use of programming techniques containing hidden side-effects since the programmer himself cannot predict the outcome when employing such techniques. Programs with hidden side-effects are often difficult to understand and modify. The elimination of expressions which produce unpredictable results does require some diligence from the programmer, but the circumstances which give rise to potentially ambiguous results occur rarely in practice, are easily recognized, and their elimination represents good programming practice. Two contexts can produce unwanted side-effects, unpredictable results, or both. Context 1 occurs in the following situations: 1. An assignment expression is nested in a larger expression that uses the contents of the variable assigned to in the inner expression. 2. A routine is called which alters the contents of a variable which is used elsewhere in the same expression as the routine call. The side effects caused by routine calls can arise from assignment to GLOBAL variables or indirect assignments. Examples of Context 1 <1> X=.X*(X=2) BLISS-11 can freely decide which of the two components in the simple expression .X*(X=2) it will evaluate first since * is a communtative operator. The outcome clearly depends on the order of evaluation and the programmer must consider it unpredictable. <2> X=G(.X)+F(X)+.X Here F(X), passed a pointer to X, may change X and if it does the value of entire expression depends on the order of evaluation. Since the + operator is commutative, this construct must be viewed by the programmer as having an unpredictable outcome. The cautions are simple. Suspect any expressions which 1. Contain store operations on variables used elsewhere in the expression. 2. Contain routine calls containing un-dotted names of variables appearing elsewhere in the expression. Context 2 occurs when a side-effect producing sub-expression may not be needed to determine the value of the overall expression. Examples of Context 2 <1> X=.A GTR .B AND (C=5) LSS F(B) BLISS-11 will perform expression evaluation only to the point at which it can uniquely determine a result. In the above expression both the order-of-evaluation and the outcome can affect the result. If, for example, .A is in fact less than .B, and if the compiled code evaluated this result first, then F(B), which may change B, is not executed, thus eliminating a possibly assumed side-effect. On the other hand, if the boolean (C=5) LSS F(B) is executed first, and if F(B) changes B then the outcome of the test .A GTR .B is clearly affected, possibly unpredictably. <2> X=O*(Y=1) Here X will always equal zero, but depending on order of evaluation, Y may remain unchanged (since the value of X is always uniquely determined here) or set equal to 1. The caution simply exhorts the programmer to beware of expressions whose value can be uniquely determined by the evaluation of a sub-expression contained within it. Failure to do so may result in an assumed side-effect not occurring. 1.4 Names Syntactically an identifier, or name, is composed of a sequence of letters and/or digits, the first of which must be a letter or the character $. Identifiers may be of arbitrary length, however, only the first ten characters are treated as significant. Since other PDP-11 support software only recognizes six characters, care must be taken with EXTERNAL and GLOBAL identifiers. Certain names, such as BEGIN, EQV, etc., are reserved as delimiters (See Appendix C). Semantically the occurrence of a name is exactly equivalent to the occurrence of a pointer to the named item. The term "pointer" will take on special connotation later with respect to contiguous sub-fields within a word; however, for the present discussion the term may be equated with "address". This interpretation of name is uniform throughout the language and there is no distinction between left and right hand values. Note that a name belongs to the class of simple expressions, has a position in the precedence hierarchy, and computes a value, namely, the address associated with the name. Some identifiers are initially declared in the BLISS-11 compiler. These identifiers, given in Appendix D, may be redefined by the programmer. They are not reserved words. 1.5 The "Contents of" Operator Since a name always evaluates to an address, and typically a programmer wants to manipulate the datum stored at the address, the language must provide some notation for accessing the contents of memory; in BLISS-11 this takes the form of the "contents of", or "dot" operator signified by a period "." prefixing an expression. The operator "." is a unary operator used to designate the contents of the location named by its operand. That location may be in main memory or one of the registers. Thus if "X" is the name of a word of memory, then ".X" names its contents, and "..X" names the contents of the word pointed at by the contents of location X. Similarly, if R is a register name, then ".R" designates the contents of that register. To further illustrate this basic BLISS-11 concept consider the assignment expression A=.B In this expression we have two names, A and B, and two operators . and =. To determine the value of this expression we use the precedence rules of section 1.3. First we derive values for the names (highest precedence). This yields the address of A and the address of B. Then we apply the dot operator to the value of B; thus, we derive the contents of B as the value of .B. Then we apply the store operator, =, to the two values thus computed. The store operator causes the value on the right hand side to be stored into the location pointed to by the value on the left hand side. Algorithmically, A=.B becomes 1. Derive value of A (a pointer) 2. Derive value of B (a pointer) 3. Apply dot to value derived in 2. (contents of B becomes new value) 4. Apply store operator to values derived in 1 and 3. (store contents of B into address specified by A) This simple example illustrates the semantics of the operators = and . and how simple expressions build more complex expressions. To carry complexity a step further consider the construction D=(A=.B) which represents a valid BLISS-11 construction. To determine the semantics view it as two expressions, D and (A=.B), connected by the store operator. The value of an expression involving the store operator takes on the value of the right hand side, so, (A=.B) has a value equal to the contents of B. Thus the entire expression results in storing the contents of B into A and D. Similarly, A=B=C=D=O will initialize the four variables, A, B, C, and D to O. (Store operators are left associative). To understand BLISS the reader must master the concept that every BLISS expression computes a value. Note: BLISS-11 recognizes two characters for the store operator, <-- and =, This manual, however, uses = exclusively to represent the store operator. 1.6 Data Representation All word values are normally treated as two's-complement signed integers. Certain operators, e.g. dot and the unsigned relational operators, treat the word as an unsigned integer value. All quantities which are specified by position and size modifiers (see Section 1.12) and which are less than a full 16-bit word are treated as unsigned integers. In particular byte quantities are normally treated as unsigned integers ranging between O and 255 in value. All Boolean tests (in conditions and loop expressions) depend on the low-order bit of the word or field being tested. The relational operators generate a full word 0 or 1 as their value, when required to yield a value. Zero represents falsity, and one represents truth. All bitwise operations (AND, OR, NOT, XOR, EQV) treat their operands as bit strings. 1.7 Comments comment ::= ! string-no-eol-sym eol-sym/ % string-no-pct % Comments may be enclosed between the symbol ! and the end of the line on which the ! appears or between paired % symbols. However, a ! may appear in the quoted string of a literal, or between two % symbols, without being considered the beginning of a comment. Likewise, a % enclosed within quotes will be considered part of a string. A % contained between an ! and the end of the line will also be ignored. Paired - % comments may extend over several lines. Examples: ! Start a comment % Hurrah for Karamazov! % ! the GNP declined 3% 1.8 Modules A module is a program element which may be compiled independently of other elements and subsequently loaded with them to form a complete program. module ::= E / MODULE module-head E ELUDOM module-head ::= name ( mdle-parms ) = / name = mdle-parms ::= mdle-parm / mdle-parm , mdle-parms mdle-parm ::= See Section 3.2 for list of options A module may request access to variables and routines declared in other modules by declaring their names in EXTERNAL declarations. A module permits general use of its variables and ROUTINES in other modules by means of GLOBAL declarations. These lines of communication between modules are completed by the linker prior to execution. A complete program consists of a set of compiled modules linked by the loader. The mdle-parms of a module definition are used to control the compilation. See Section 3.2 for a descriptive list of available options and their defaults. Example: MODULE START(MAIN,NOLIST)= BEGIN ... END ELUDOM 1.9 Blocks A block consists of an arbitrary number of declarations followed by an arbitrary number of expressions all separated by semicolons and enclosed in a matching BEGIN-END or "(" - ")" pair. block ::= BEGIN blockbody END / ( blockbody ) blockbody ::= decls ; exprs / exprs decls ::= decl / decl ; decls exprs ::= E / label : exprs / E; exprs / empty* Compound expressions (i.e. blocks with no declarations; decls is empty) form an important subclass of blocks. The block indicates the lexical scope of the names declared at its head. The lexical scope of a name declared in a given block is that entire block with the exception of inner blocks containing an explicit re-declaration of the same name, or loop-expressions (see Section 1.16) which use the name as a loop control variable. Names of variables and ROUTINES declared as GLOBAL follow this same rule. In addition such variables and routines may be referenced by other modules in scopes where they are declared EXTERNAL. .10 Literals The basic data element of BLISS-11 is the 16 bit word. Literals are normally converted to a single word, but may in fact end up being truncated to fewer bits if the use of the literal requires less than 16 bits. ---------- *The string will mean that the construct in question has the empty string as an alternative; i.e. a valid option is to omit it. ---------- literal ::= string / number / plit string ::= string-type quoted-string string-type ::= ASCII / ASCIZ / RADIX50 / RAD50 / empty (=> ASCII) quoted-string ::= "strng" / 'strng' number ::= decimal / octal decimal ::= digit / digit decimal octal ::= #oits oits ::= oit / oit oits oit ::= 0 / 1 / 2 / 3 / 4 / 5 / 6 / 7 digit ::= 0 / 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / 9 strng ::= A sequence of characters Numbers (unsigned integers) are converted to binary modulo the size of the data required in the context. This implies truncation for any number which exceeds the precision of the data size. No warning is given in these cases. Octal constants are indicated by a sharp sign, #. String literals may be used to specify constants coded in ASCII or RAD50 code. The ASCII string-type converts the quoted-string following the keyword ASCII into ASCII characters. ASCIZ creates an ASCII string which has an ending byte equal to zero. BLISS-11 will allocate one byte per character in increasing memory address order. RADIX50 (or equivalently RAD50) packs three characters into 16 bits. The quoted-string following RADIX50 may only contain characters from the set ,,<$>,<.>, and . See Appendix E for the definition of PDP-11 RADIX50 code. If no string-type is given then BLISS-11 assumes ASCII. Within paired double quotes, single quotes may appear freely and within single quotes, double quotes may appear freely. The appearance of two successive occurrences of the bracketing pair of quotes produces a single instance of the character; hence "'A" and '''A' both produce the word containing 'A. All quoted-strings (when used as a literal) must fit into a single 16 bit word. Strings of characters which exceed one word in length may be created in plits (see Section 1.11). In strngs ? is used as an "escape" character. The single character immediately following a ? is interpreted as follows: ?O null ?1 rubout ?? ? ?A = CONTROL-A ... ?Z = CONTROL-Z ?SHIFT-K = CONTROL-SHIFT-K , etc. Thus ?I is interpreted as a tab character and ?J is interpreted as a line-feed character. 1.11 Painters to Literals A "plit" is a pointer to a literal word whose contents are specified at compile time and established at load time; e.g., PLIT 3 is a pointer to a word whose contents will be set to 3 at load time. plit ::= PLIT plitarg / UPLIT plitarg plitarg ::= load-time-expr / string / tuple tuple ::= (tuple-item-lst) tuple-item-lst ::= tuple-item / tuple-item , tuple-item-lst tuple-item ::= load-time-expr / string / dup-fctr : plitarg dup-fctr ::= compile-time-expr PLIT (3)+4 has two parses: PLIT load-time-expr, and PLIT tuple + expr The latter choice is used. Hence, PLIT (3)+4 is the same as (PLIT 3)+4. Note that PLIT (3)+4 yields a value that may have little or no meaning. PLIT (3) produces a pointer to a memory location containing a 3. Adding 4 to this pointer may not produce the intended value. Load time expressions must evaluate to a constant at load time end thus include compile time expressions and expressions which contain only literals and addresses known at load time. A plit may point to a contiguously stored sequence of literals strings and nested lists of literals are also allowed. The value of PLIT (3,5,7,9) is a pointer to 4 contiguous words containing 3, 5, 7 and 9 respectively. A long string (more than 2 characters) is also a valid argument to a plit: PLIT "THIS ALLOCATES 12 WORDS" allocates 12 words of ASCII characters (one character per byte). The length (in words) of a plit is stored in the word preceding the data. A UPLIT (i.e. uncounted plit) does not have a length word allocated with it. The arguments to plits need only be constant at load time. Plits are themselves literals, so nesting of plits is allowed. The following example uses two declarations, EXTERNAL and BIND. EXTERNAL declares that the names following it exist in an independently compiled module. BIND establishes an equivalence between the variable name on the left of the equal sign, =, and the expression on the right, such that when the variable name appears in the program the compiler substitutes the value of the expression for the variable name. The use of the equal sign does not indicate a store operation in the BIND context, merely association between the name and the expression (see section 1.24.3 for details on EXTERNAL and 1.24.5 for details on BIND) Examples: EXTERNAL A,B,C: BIND Y=PLIT (A, PLIT (B,C), PLIT 3, "A LONG STRING", 5+9*3); is such that: .Y[O]* = A (i.e. the address A); .Y[1] = the address of PLIT(B,C); ..Y[1] = B; .(.Y[1]+2) = C; .Y[2] = the address of PLIT 3; ..Y[2] = 3; .Y[3] = "A "; .Y[4] = "LO"; .Y[5] = "NG"; .Y[9] = "G "; .Y[10] = 32; In addition, any argument to a plit can be replicated by specifying the number of times it is to be repeated; e.g. PLIT (7:3) produces a pointer to 7 contiguous words, each of which contains the value 3. A replication factor of zero results in no allocation. Duplicated plits are allocated once. Identical plits are not pooled; hence, BIND X = PLIT (3: PLIT A, PLIT A, 2: (2,3)); is such that: ..X[O] = ..X[1] = ..X[2] = ..X[3] = A .X[O] = .X[1] = .X12] <> .X[3] .X[4] = .X[6] = 2 .X[5] = .X[7] = 3 .X[-1] = 8 1.12 Position and Size Modifier The BLISS-11 language provides the following notation as a modifier to either the "contents of" or assignment operators: ---------- *Consider this notation as array addressing with Y[O] pointing to the first word of the array, Y[1] pointing to the second word, etc. ---------- pos-size ::= position ::= compile-time-expr site ::= compile-time-expr The position and size together name a field which begins "position" bits from the right hand side of the named byte and extending "size" bits to the left. Thus .X<0,8> designates the contents of the byte at location X and .X<0,16> designates the contents of the word at location X (X must be a word address in this case). Figure 1.12a shows these relationships. Figure 1.12a Note that the size may not exceed 16 bits and the field may not cross word boundaries. Any position or size specification which violates this rule causes a warning message to be issued. Such erroneous position specifications are used module 16. Erroneous site specificatons are reduced to the greatest value which does not cause a word boundary to be crossed. Examples: (use Figure 1.12b for memory layout) X=.Y<0,8> ! Store low order byte of .Y into ! low order byte pointed to by X; ! clears high order byte of X. X=.Y<8,8> ! Store high order byte of .Y into ! low order byte of X; clears ! high order byte of X. X=.(Y+1)<0,8> ! Same result as previous example. ! Stare the byte at address Y+1 into the ! low order byte of X. Clear the high ! order byte of X. X<8,2>=1 ! Set bit 8 of X to 1 and bit 9 ! to O. The low order byte of X ! unmodified and Bits 10-15 are ! also unmodified. In other words, ! bits 8 and 9 of X are set using ! the low order 2 bits of the ! literal 1. Figure 1.12b To understand the previous examples we need to discuss the semantics of , which has two valid contexts: as a modifier of the store operator (= or <-), or as a modifier of the dot operator. Note that X (an undotted name) is only legal on the left side of store in which case is associated with the store operator. Every dot or store operation carries a default position-size modifier if the programmer has not established one explicitly. In the absence of declarations specifying otherwise* BLISS-11 uses a default of <0,16> on every store and dot operator. Thus X=.Y means the same as X<0,16>=.Y<0,16> where normal precedence rules hold in the evaluation of the expression. Evaluation proceeds as follows: Y Yields a pointer to Y .Y<0,16> Yields full word contents of Y X Yields a pointer <0,16>= Stores fullword contents of Y into fullword at X To emphasize, note that since dot has higher precedence than store the semantics of .X<0,8>=.Y is equivalent to (.X<0,8>)=.Y<0,16> and differs from (.X)<0,8>=.Y In the first case the position-size modifier is associated with the contents operator (".") while in the second case it is associated with the assignment operator. The first case results in the full word at Y being stored into the address pointed to by the contents of the low order byte of X. The second case results in storing the low order byte at Y into the low order byte pointed to by the fullword contents of X. ---------- *Section 1.24.3 discusses declarations that can alter the default to be <0,8>. ---------- The meaning of position size modifiers (PSM) can be defined by the following rules. 1. The left hand side of a store always computes the location where storage is to take place. (Call this address L) 2. Any PSM associated with a dot an the left hand side is used only for calculation of that location. i.e. it specifies the field whose contents help specify L. 3. A PSM associated with a dot on the right-hand side is used to extract the contents of a field at the location specified by the right hand side. This field may be considered as being stored right justified in a temporary, T, with the remainder of T cleared. 4. The size parameter of the store operator's PSM is used to extract a field from the right hand side result. The position parameter of the store's PSM determines the position (in location L) where this field is to be stored. All other bits at location L are left unchanged. Examples: The following examples of BLISS-11 source text and the corresponding algorithmic decomposition should help the reader extract some of the implications of the preceding sections. The discussions will become less prolix as the pattern becomes obvious. BLISS-11 Semantics -------- --------- A=B; 1. Store address B into word A A=.B; 1. Store contents of word B into word A A<0,8>=.B; 1. Derive A (a pointer) 2. Derive B (a pointer) 3. Modify store operator to store a byte 4. Store low order byte of word B into the byte location pointed to by A. A<0,8>=.B<0,8>; 1. Same result as previous example. A=.B<0,8>; 1. Derive B (a pointer) 2. Modify dot operator to access low order byte 3. Derive .B (low order byte of the word B) 4. Derive A (a pointer) 5. Apply store (full word implied so low order contents of B stored into low order byte of A; high order byte cleared) (.A)<0,8>=.B<8,8>; 1. Derive .B (contents of B) 2. Apply modifier to select high order byte of B 3. Derive .A (contents of A) 4. Apply <0,8> modifier to store operator 5. Store high order byte of B into the byte pointed to by value derived in 3 (contents of A) Note: Parentheses establish precedence so that the store is modified rather than the dot. .A<0,8>=.B; 1. Derive .B (contents of B) 2. Derive .A (contents of A) 3. Apply <0,8> to contents derived in 2 (extract low order byte) 4. Store contents of B in location pointed to by low order byte of A. (.A)<0,8>=.B 1. Derive .B (contents of B) 2. Derive .A (contents of A), an address 3. Apply <0,8> to store 4. Store low order byte of B into byte pointed to by contents of A. Note: Compare this example with the previous one. A=..B 1. Derive .B (contents of B) 2. Derive contents of location pointed to by .B 3. Store result of 2 into A A=..B<0,8> 1. Derive B 2. Apply <0,8> to contents of B (extract low order byte) 3. Apply dot to result derived in 2 this uses the low order byte of B as a pointer and extracts the contents at the "pointed-to" location 4. Store the contents derived in 3 into A. Note: associates with either a dot or a store. Precedence determines order. In this example <0,8> associates with the dot nearest the B, precedence playing no role here. See the next example for contrast where parentheses do force precedence. A=.(.B)<0,8> 1. Derive .B 2. Derive ..B (contents of location pointed to by contents of B 3. Apply <0,8> to contents derived in 2 thus extracting low order byte. 4. Store low order byte derived in 3 into A; clear high order byte of A (remember <0,16> applies to store by default. A=.(B+1)<0,8> 1. Derive 8+1 (the address of the high order byte of B) 2. Apply dot to B+1 (derive contents of the word which begins at B+1) 3. Apply <0,8> to the two bytes beginning at B+1 extracting the low order byte of the logical word beginning at B+1. (Here logical means any two consecutive bytes in memory as apposed to hardware words which consist of two consecutive bytes beginning on word boundaries.) 4. Store result of 3 into A; clear high order byte of A. Figure 1.12c should help the reader understand this example Figure 1.12c A=.B<8,8> 1. This is equivalent to previous example if B is on word boundary. A<0,8>=.(B+1)<0,8> 1. Derive .(8+1)<0,8> (the high order byte of the word B; refer to previous example.) 2. Derive A 3. Apply <0,8> to store placing high order byte of contents of 8 into low order byte of A; high order byte of A remains unaltered. Note : The expression A=.(B+1) will default to a <0,16> modifier on B+1. This may violate position-size restrictions if B is on a word boundary. BLISS-11 does not make compile-time or runtime checks for valid addressing. An attempt to address a word on an odd boundary will produce a PDP-11 hardware error trap. (A+.C)<0,8>= 1. Derive .B .B<0,8>+1 2. Apply <0,8> to contents of B deriving low order byte. 3. Add 1 to low order byte derived in 2 creating a full word result 4. Derive A (a pointer) 5. Derive .C (contents of C) 6. Add value derived in 4 to value derived in 5 7. Apply <0,8> to store and store the low order byte of the value derived in 3 into the low order byte of the location derived in 6. A<2,12>=.B 1. Store low order 12 bits of .B into bits 2 thru 13 of A. The other bits in A are unchanged. A<2,12>=.B<0,14> 1. Same as previous example A<2,12>=.B<0,10> 1. Store low order 10 bits of .8 into bits 2 thru 11 of A. Clear bits 12 and 13 of A. The other bits of A are unchanged. A<2,12>=.B<2,12> 1. Store bits 2 thru 13 of .B into bits 2 thru 13 of A A<2,12>=.B<2,14> 1. Same as previous example A<2,12>=.B<2,10> 1. Store bits 2 thru 11 of .B into bits 2 thru 11 of A. Clear bits 12 and 13 of A. 1.13 Interlude In Section 1.2 we began at the basic building blocks of BLISS-11 programs namely, expressions, but interrupted it exposing the reader to names, the dot and store operators, modules, blocks, data representation, literals, pointers to literals (plits), and position-size modifiers. We now return to the discussion of expressions. It may prove useful for the reader to review Sections 1.1 and 1.2 before proceeding. 1.14 Control Expressions The control expressions provide the mechanism needed for controlling the execution sequence of a program. BLISS-11 has five forms: cntrl-expr ::= cndtl-expr / loop-expr / choice-expr / escp-exprl / cortn-expr 1.15 Conditional Expressions cndtl-expr ::= IF E1 THEN E2 ELSE E3 E1 is computed and the resulting value is tested. If it is true, then E2 is evaluated to provide the value of the conditional expression, otherwise E3 is evaluated. cndtl-expr ::= IF E1 THEN E2 This form is equivalent to the IF-THEN-ELSE form with O replacing E3. However, it does introduce the "dangling else" ambiguity. This is resolved by matching each ELSE to the most recent unmatched THEN as the conditional expression is scanned from left to right. Thus the following expressions are equivalent: IF E1 THEN IF E2 THEN E3 ELSE E4 IF E1 THEN (IF E2 THEN E3 ELSE E4) IF E1 THEN (IF E2 THEN E3 ELSE E4) ELSE O Examples: <1>* IF .X<8,1> THEN J = .K ELSE J=.L; <2> J = (IF .X<8,1> THEN .K ELSE .L); This example has the same effect as the previous one. However, In this one the value of the IF control expression is used in a store context. BLISS requires that control expressions be enclosed entirely in parentheses when used in contexts which make use of their values. Thus the expression X=IF E1 THEN E2 ELSE .A+.B will produce a syntax error since BLISS does not know which of the two possible meanings X=(IF E1 THEN E2 ELSE .A)+.B or X=(IF E1 THEN E2 ELSE .A+.B) is intended. Parenthesizing is required. <3> IF .L THEN BEGIN ... END ELSE BEGIN ... END Notice that the use of blocks permits an arbitrarily large computation to take place after a THEN or ELSE. <4> POS = .POS + (IF .CHAR EQL #11 %tab% THEN 8 ELSE 1); 1.16 Loop Expressions The value of each of the six loop expressions is -1, except when an EXlTLOOP or LEAVE is used (see Section 1.17). loop-expr ::= WHILE El DO E2 E1 is computed and the resulting value is tested. If it is true, then E2 is computed and the complete loop-expr is recomputed; if it is false, then the loop-expr evaluation is complete. loop-expr ::= UNTIL E3 DO E2 This form is equivalent to the WHILE-DO form except the E1 is replaced by NOT(E3). loop-expr ::= DO E2 WHILE E1 The expressions E2, E1 are computed in that sequence. The value resulting from E1 is tested: if it is true, then the complete loop-expr is recomputed: if it is false, then the loop-expression evaluation is complete. ---------- *The single integer values in angle brackets simply number the examples, and do not belong to the syntax of the language. ---------- loop-expr ::= DO E2 UNTIL E3 This form is equivalent to the DO-WHILE form except that E1 is replaced by NOT(E3). loop-expr ::= INCR name FROM E1 TO E2 BY E3 DO E4 The name is implicitly declared to be a LOCAL* variable for the scope of the loop. This implicit declaration supersedes any previous declaration for that name for the duration of the loop expression. It may itself be temporarily superseded by a declaration in an inner block of the loop or in a contained loop-expr. The expression E1 is computed and stored in name. The expressions E2 and E3 are computed and stored in unnamed local memory which for explanation purposes we shall name U2 and U3. Any of the phrases FROM E1, TO E2, or BY E3 may be omitted--in which case default values of E1=0, E2=00, E3=l are supplied. The following loop-expr is then executed: BEGIN LOCAL NAME,U2,U3; NAME=E1; U2=E2; U3=E3; UNTIL .NAME GTR .U2 DO (E4; NAME=.NAME+.U3) END The last form of a loop-expr is: loop-expr ::= DECR name FROM E1 TO E2 BY E3 DO E4 This is equivalent to the INCR-FROM-TO-BY-DO form except that the loop is equivalent to BEGIN LOCAL NAME,U2,U3; NAME=E1; U2=E2; U3=E3; UNTIL .NAME LSS .U2 DO (E4; NAME=.NAME-.U3) END Caution: BLISS-11 uses signed relationals in the loop expressions. This can cause unexpected results if any of the loop variables represent addresses rather than numerical values. If any of the FROM, TO, or BY phrases are omitted from a DECR expression, default values of E1=0, E2=00 and E3=1 are supplied. Notice that in both forms the end condition is tested before the loop, hence the loop is potentially executed zero or more times. ---------- *Section 1.24.3 discusses the LOCAL declaration. ---------- Examples: <1> ! Following algorithm will place the address of the ! last item of a linked list into the variable ! LINK. Figure 1.15a shows the initial storage layout. LINKP.LISTHEAD; ! Link now points to first item WHILE ..LINK NEQ O DO LINK=..LINK; ! Chain down list Figure 1.15a <2> ! Add up the first n integers SUM=O; INCR J FROM 1 TO .N DO SUM=.SUM+.J 1.17 Escape Expressions The escape expressions permit control to leave its current environment. There are five forms: esc-expr ::= LEAVE label WITH E / LEAVE label / EXITLOOP E / RETURN E / SIGNAL E Any expression may be labeled by preceding it with the label name and a colon. Within a labeled expression, control may be caused to leave the expression and yield the given value as the value of the expression. All labels must be predeclared in a label-decl (see Section 1.25). The LEAVE expression must occur within the control scope of the label named. The same label may be defined only once within the lexical scope of its declaration. If the WITH E is missing then WITH O is assumed. EXITLOOP is a special case which exits the scope of the innermost loop control scope which contains it. The value becomes the value of the exited loop. RETURN exits the routine body and passes the given value as the value of the routine. SIGNAL provides controlled exit within a nested environment. We will discuss the details of SIGNAL in the context of the ENABLE declaration. Examples: <1> ! Find INDEX of first space in line of 80 characters ! INDEX is -1 if none found because the value of an ! exhausted loop-expr equals -1 INDEX=(INCR J FROM O TO 79 DO IF .(LINE+.J)<0,8> EQL " " THEN EXITLOOP .J) <2> ! How to escape to next iteration in a loop INCR J FROM 1 TO 100 DO L2:BEGIN ... IF .condition THEN LEAVE L2; ! The LEAVE exits the compound which ! constitutes the loop body. This causes ! the termination of the current iteration and ! the beginning of the next. ... END <3> ! Find first zero element of a 2-D array L3: INCR I FROM 1 TO .IMAX DO INCR J FROM 1 10 .JMAX DO IF .ARRAY[.I,.J] EQL 0 THEN (II=.I; JJ=.J; LEAVE L3); ! This example results in complete loop termination ! since the label, L3, labels the INCR I... ! Contrast this with preceding example. 1.18 Choice Expressions There are two varieties of choice expression - CASE expressions and SELECT expressions. 1.18.1 CASE expression choice-expr::= CASE E1 OF SET exprs TES Note that the syntax of exprs implies that it consists of a set of E's and empty expressions separated by semi-colons. "Empty expressions" are equivalent to O. The expression E1 is evaluated and used to select the E-th expression of the following set to execute. (The expressions are indexed starting at zero.) The zero-th expression is executed if E1 equals 0, the first if E1 equals 1, etc. The value of the CASE expression is undefined if E1 is less than zero or greater then the number of expressions. In such a case unpredictable results will occur at runtime since no range checking is performed on E1. The delimiters SET and TES (SET spelled backwards)* bound the exprs within the CASE expression. Examples: <1> ! Collect a FORTRAN identifier ! TYPE(C) is a routine with the value ! O if .C is a numeral ! 1 if .C is a letter ! 2 if .C is a space or tab ! 3 otherwise ! NXTCHAR is a routine which returns the next input character ! ID is the location where the identifier is collected ! COUNT contains the length of the identifier ! The WHILE loop acts as an infinite loop since 1 ! is always true. The computation exits this loop via the ! EXITLOOP in case 3. COUNT=0; WHILE 1 DO CASE TYPE(CHAR=NXTCHAR()) OF SET !O-Numeral ((ID+.COUNT)<0,8>=.CHAR; COUNT=.COUNT+1); !1-Letter ((ID+.COUNT)<0,8>=.CHAR; COUNT=.COUNT+1); !2-ignorable ; ! 3 - ID is complete EXITLOOP TES; ---------- *BLISS-11 uses the convention of forward-reverse spelling of keyword delimiters in every case except BEGIN-END. ---------- 1.18.2 SELECT expression choice-expr::= SELECT E OF NSET a-expr-set TESN a-expr-set a-E / a-E ; a-expr-set a-E E: E / OTHERWISE : El ALWAYS : E This form is somewhat similar to the CASE expression except that the expressions in the a-expr-set are not thought of as being sequentially numbered--instead each expression in the a-expr-set is tagged with an "activation" expression. Suppose we have the following select expression: SELECT E1 OF NSET E4: E5; E6: E7; E8: E9; E10: E11 TESN The execution proceeds as follows: El is evaluated. Then E4 is evaluated and if it equals E1, E5 is evaluated. Then E6 is evaluated and if it equals E1, E7 is evaluated, and so on for E8 and E10. The value of the entire expression is that of the last one to actually be executed. If none are executed the value is -1. In place of one of the activation expressions, E4, E6, etc., one of the two reserved words OTHERWISE or ALWAYS may be used, e.g. ALWAYS: E9. The expression following an OTHERWISE will be executed just in the case that none of the preceding selection criteria were satisfied. The expression following an ALWAYS will always be executed regardless of the value of E1. In the following example Z=(SELECT .X OF NSET 1: E1; .A+.B: E2; OTHERWISE: E3; 36: E4; ALWAYS: E5; 94: E6 TESN) the execution of E1, E2, E4, E6 depend on .X having the value 1, .A+.B, 36, and 94 respectively. The execution of E3 occurs if .X did not equal 1 or .A+.8. The execution of E5 always occurs. Note that although OTHERWISE and ALWAYS may replace any selection expression, it makes no sense to use more than one OTHERWISE or to use an OTHERWISE after an ALWAYS since in these cases the latter OTHERWISEs can have no effect. 1.18.3 Compile-time Constant Conditional and Choice-Expressions Some BLISS-11 contexts require that expressions evaluate to constants at compile time. For example the declaration OWN X[E] requires that E be completely determined at compile time. In particular we may write without generating an error OWN X[IF E1 THEN E2 ELSE E3] if we can guarantee that the conditional expression will reduce to a compile time constant. BLISS-11 permits conditional expressions and CASE expressions to be used in this manner, but not SELECT expressions. Use of constant conditional and CASE expressions can prove quite useful in effecting conditional compilations and as a technique for the achievement of algorithm and data independence. 1.19 Coroutine Expressions Coroutine expressions have not been implemented. 1.20 Declarations Any number of declarations may occur at the beginning of a block. The block defines the scope of the declarations (see Section 1.9). Most declarations introduce names and/or provide information about names. Other declarations are used for such things as listing control, inclusion of text from other source files and control over certain code optimizations. The declarations are the following: decl ::= routn-decl / ext-fwd-decl / link-decl / strc-decl / alloc-decl / bind-decl / map-decl / label-decl / un-decl / enable-decl / macro-decl / csect-decl / requ-decl / swtch-decl The remainder of Chapter 1 will describe these declarations in detail. 1.21 Storage (An Introduction) Before proceeding with a detailed discussion of the name-introducing declarations we shall give an intuitive overview of their effect. A BLISS-11 program operates with and on a number of storage "segments". A storage segment consists of a fixed and finite number of words or bytes, each of which is composed of a fixed and finite number of bits (16 and 8 for the PDP-11). In practice a segment generally contains either program or data, and if the latter, it is generally integer numbers, characters, or pointers to other data. To a BLISS-11 program, however, a segment merely contains a pattern of bits. Segments are introduced into a BLISS-11 program by declarations, called allocation declarations (alloc-decl) or routine declarations (routn-decl); for example: GLOBAL G OWN X,Y[5],Z LOCAL P[100] REGISTER R1,R2 ROUTINE F(A,B) -- .A^.B GLOBAL ROUTINE INC(X) = .X+1 STACKLOCAL S1,S2 Each of these declarations introduces one or more segments and binds the identifiers mentioned (e.g. G, X, V, etc.) to the address of the associated segment. (The ROUTINE declarations also initialize the segments named "F" and "INC" to the appropriate machine code.) The data segments introduced by these declarations contain one or more words, where the number of words may be specified (as in LOCAL P[100]), or defaulted to one (as in GLOBAL G). The identifiers introduced by a declaration are defined only within the scope of the block in which the declaration is made, with one exception - namely, GLOBAL identifiers are made available to other, separately compiled modules. Segments created by OWN, GLOBAL, and ROUTINE declarations are created only once and are preserved for the duration of the execution of a program. Segments created by LOCAL and REGISTER declarations are created at the time of block entry and are preserved only for the duration of the execution of that block. REGISTER segments differ from LOCAL segments in that they must be allocated from the machine's general purpose (fast) registers. Re-entry of a block before it is exited (by recursive routine calls, for example) results in the dynamic allocation of LOCAL and REGISTER segments on each incarnation of the block. The compiler may choose to allocate LOCAL variables in registers. The STACKLOCAL declaration is identical to LOCAL with the exception that STACKLOCAL's are explicitly prohibited from being allocated in registers. They are always allocated on the runtime stack. There are three additional declarations whose effect is to associate identifiers with segments, but which do not actually create segments; examples are: EXTERNAL S FORWARD RTN BIND Y2=Y+2, PA=P+.A An EXTERNAL declaration specifies that an identifier represents a segment named by the same identifier and declared GLOBAL in another, separately compiled module. A FORWARD declaration indicates that an identifier is associated with a routine which will be declared lexically later in the module. The BIND declaration associates an identifier with the segment whose address is given by the value of an expression at the time the block is entered. At least potentially the value of this expression may not be calculable until run time - as in 'PA = P+.A' above. 1.22 Routines routn-decl ::= glbl-decl ROUTINE routn-specs glbl-decl ::= GLOBAL / empty routn-specs ::= routn-spec / routn-spec , routn-specs routn-spec ::= link-id name(name-lst)=E / link-id name=E link-id ::= link-name / empty (=> BLISS) link-name ::= name The ROUTINE declaration (routn-decl) defines the name to be that of a potentially recursive and re-entrant routine whose value is the expression E and which is called using the linkage mechanism (calling sequence) specified by link-id. If link-id is empty then the default link-name BLISS is used. The name-ist defines the names of the routine's formal parameters. The scope of these names is the body of the routine, the expression E. However, the scope does not include the bodies of any routines defined within E (i.e. up-level addressing is not permitted). The syntax of a routine call is routn-call ::= Pl(expr-lst) / P1() P1 ::= literal / name / strc-access / block / routn-call P1 is a primary expression, and should evaluate, either at compile time or runtime, to a name which has been declared as a ROUTINE. The expr-lst at the call site (routn-call) is used to supply actual values of the parameters for the invocation. All parameters are call-by-value; but notice that call-by-reference is achieved by simply presenting addresses at the call site. Parentheses are required at the call site even for a routine with no formal parameters since the name by itself is a pointer to the routine, not a request for a call. To eliminate possible misunderstanding on the BLISS-11 parameter passing mechanism for those readers familiar with other higher level languages, and to make the process explicit for others, we shall examine it in more detail. BLISS routines have one local variable set aside for each formal parameter declared. These local variables may be on the stack or in registers depending on the link-name associated with the routine (see Section 1.22.3). When a call on the routine occurs, an evaluation of the actual parameters takes place, and the values are stored in the corresponding local variables. Because every expression in BLISS-11 computes a value, and because the value thus computed in a routine call establishes the contents of the formal parameter, we say that all BLISS-11 calls occur as calls-by-value. Examples: <1> ! Calculate the sum of O, 1,..., .N ROUTINE ARITHSUM(N)=(.N*(.N+1 ))/2 Call: Y=ARITHSUM(10) The call mechanism evaluates the actual parameter and deposits it in the local variable, N, in the called routine, then invokes the routine. The routine operates on the passed value; note the formal, N, has the dot operator applied. Y=ARITHSUM(A) In the above call A, as a value, is the address A and the call mechanism deposits this value into the local variable reserved for the formal N. The value returned equals the arithmetic sum of the address treated as an integer--hardly the outcome intended. To achieve the desired result requires the call: Y=ARITHSUM(.A) This places the contents of A into the location of the formal N. This example amplifies the strict call-by-value parameter passing in BLISS-11. Specifically, we have a routine with a list of formal names and for each formal name a location. At the call site each actual parameter is evaluated and the contents of the corresponding formal receives the value; the caller, using the requirements of the routines, sets up the call to effect the proper initialization of the formals. Figure 1.22a shows storage locations for the above two calls Figure 1.22a <2> The occurrence of a primary expression followed by a parameter list enclosed in parentheses triggers a function call; an identifier by itself, even one declared as the name of a routine, merely denotes an address. The value of a routine call results from the execution of the body of the routine. Consider: BEGIN GLOBAL X,Y,Z; ROUTINE F(A,B)=(.A=.B); Y=5; Z=F; (.Z)(X,.Y); .X END This block has 5 as its value. Z=F stores the address of F into Z but does not trigger a call. The expression (.Z)(X,.Y) is actually a routine call which results in storing the contents of Y into X. A routine call need not explicitly name a routine by its associated identifier, only that the primary expression evaluate to a routine segment which can properly accept the arguments. 1.22.1 GLOBAL Routines A routine name is like an OWN name in that its scope is limited to the block in which it is declared and its associated storage segment is already created before block entry. The prefix GLOBAL makes the routine name available for reference by modules which are loaded with the module containing the routine declaration. The other modules gain access to the name by means of an EXTERNAL declaration. A GLOBAL routine is prohibited from accessing register variables declared outside of the routine itself unless the register is mentioned in a RESERVE module parameter (see Section 3.4). 1.22.2 EXTERNAL and FORWARD routine declarations ext-fwd-decl ::= EXTERNAL mlid-lst / FORWARD mlid-lst mlid-lst ::= mlid-elmt / mlid-elmt , mlid-lst mlid-elmt ::= link-id id-part id-part ::= name / name : id-part Normally a routine must be declared lexically before any reference to the routine name. The EXTERNAL and FORWARD declarations are used to make exceptions to this rule. The EXTERNAL declaration allows the name of a routine declared in an other module to be made available for reference within the scope of the declaration. A routine with the same name must be declared GLOBAL in some module which is loaded with the module containing the EXTERNAL declaration. Note that the EXTERNAL declaration may also take the form of an alloc-decl (see Section 1.24.3). The FORWARD declaration permits the name of a routine declared later in the current lexical scope to be made available for reference prior to its actual declaration. This allieviates the necessity of arranging routine definitions in any particular order. It also makes it possible for two routines to call each other. Both of these declarations allow a linkage specification to be associated with the routine names. All names in the id-part of a mlid-elmt use the link-id appearing in the mlid-elmt. As in routine declarations an empty link-id causes the default link-name, BLISS, to be used. 1.22.3 Calling Sequence Declarations There are two motivations for allowing some degree of programmer control over routine calling sequences. First, it is desirable that it be relatively simple to interface BLISS-11 routines with the PDP-11 hardware calling sequences (e.g. for interrupts, EMT's, etc.) and with programs written in programming languages using calling sequences other than the standard BLISS sequence. Second, since a generalized calling sequence cannot be the most efficient in all cases, user-defined calling sequences provide an opportunity for the programmer to gain additional efficiency if it is necessary. A calling, sequence consists of the following five components: 1) the call instruction(s) 2) parameter location 3) state saving and restoring (e.g. register contents) 4) routine value conventions 5) the return instruction(s). BLISS-11 recognizes several calling sequence types. Each specifies a particular set of conventions for the areas listed above. The programmer may create calling sequences of his own by using the LINKAGE declaration. The only calling sequence component which may be changed is the location of parameters. It is hoped that this together with the variety of predefined linkage types will meet the interfacing and efficiency requirements mentioned previously. LINKAGE declarations have the following syntax: link-decl ::= LINKAGE link-specs link-specs ::= link-spec /link-spec , link-specs link-spec ::= name - link-typ / name = link-typ ( link-call-ist ) link-typ ::= BLISS / INTERRUPT / EMT / TRAP / IOT / FORTRAN / link-name link-call-ist ::= link-arg /link-arg , link-call-lst link-arg ::= STACK / REGISTER=num num ::= 0 / 1 / 2 / 3 / 4 / 5 A LINKAGE declaration defines the name to specify a calling sequence which has the basic characteristics of the link-typ appearing in the declaration. The link-call-lst, if specified, describes the locations to be used to hold the positionally corresponding actual parameters in a call on a routine which is invoked using the newly defined linkage name. STACK means the parameter is placed on the runtime stack in memory. REGISTER=num means it is loaded into the general register specified by num. Once a linkage name has been defined it may be used as the link-typ in subsequent LINKAGE declarations. In this case it specifies that the basic link-typ to be used is the link-typ associated with the link-name in its own declaration. A link-name is associated with a routine by specifying it in the declaration of the routine name (see Section 1.22). 1.22.4 Predefined Calling Sequences There are six predefined calling sequence types, BLISS, INTERRUPT, EMT, TRAP, IOT and FORTRAN. Of these only BLISS and INTERRUPT are currently implemented. The details of the run-time representation and characteristics of these calling sequences may be found in Sections 4.1 to 4.6. The BLISS calling sequence is the standard calling sequence used in BLISS-11. The link-name BLISS is the default link-name in all routine declarations so normally it need not be explicitly specified. This default can be changed merely by using a LINKAGE declaration to define a new calling sequence with the name BLISS. The predefined BLISS linkage specifies that a routine returns a value and that all actual arguments are to be placed on the stack. New calling sequences with link-typ of BLISS may specify that certain arguments are to be put into registers. For example, the declarations LINKAGE NONSTD=BLISS(REGISTER=1,REGISTER=2,STACK) ROUTINE NONSTD LINKUP(FOREWARD,BACKWARD,HOME)=E1 will result in a calling sequence that will place the actual argument for FOREWARD in R1, the actual for BACKWARD in R2, and the actual for HOME on the stack, then transfer to LINKUP using the characteristics of the BLISS calling sequence. If a ROUTINE declaration contains more formals than its corresponding LINKAGE declaration, then the actuals corresponding to these formals are placed on the stack. The INTERRUPT linkage declares that a routine will be entered as a result of a hardware interrupt or trap. Explicit calls are not allowed to invoke a routine which is declared to use the INTERRUPT linkage. Thus INTERRUPT routines do not return a value. Note that using this linkage name only declares a routine for servicing interrupts. The user must separately arrange to establish the routine name and desired status word in the appropriate interrupt vector. An INTERRUPT routine may not have any explicitly declared formal parameters. However it has two implicitly declared formal parameters. OLDPC and OLDPS, which may be used to access the Program Counter and Processor Status saved on the stack by the interrupt hardware. 1.22.5 Calling Sequences for Variable Routine Names When a linkage name is itself used syntactically as a routine name in a routine call expression, the first argument is evaluated at run-time and interpreted to designate the routine entry point to be called. The remaining arguments are passed to the named routine in the manner required by the linkage definition. Example: FORTRAN(.(TRANSFERVECTOR+.I),PAR1 ,PAR2) ! this call generates a FORTRAN linkage to a routine ! located at .(TRANSFERVECTOR+.I) and passes ! two parameters. 1.23 Data Structures (An Introduction) Two principles were followed in the design of the data structure facility of BLISS: 1. The user must be able to specify the accessing algorithm for elements of a structure. 2. The representational specification and the specification of algorithms which operate on the information represented must be separated in such a way that either can be be modified without affecting the other. The definition of a class of structures, that is, of an accessing algorithm to be associated with certain specific data structures, may be made by a declaration of somewhat the following form: STRUCTURE name[strc-frml-lst]=E Particular storage segments may then be associated with a structure, that is with an accessing algorithm, by specifying the structure name in the declaration of the storage. Consider the following example: <1> BEGIN STRUCTURE ARY2[1,J]=(.ARY2+(.I-1)*2*10+(.J-1)*2)<0, 16>; OWN ARY2 X[100]; OWN ARY2 Y[100]; OWN ARY2 2[100]; ... X[.A,.B] = .Y[.B,.A]; ... END; In this example we introduce a very simple structure, ARY2, for two-dimensional (10x10) word arrays*, declare three segments of 100 words each with names X, Y, and Z bound to them, and associate the structure ARY2 with these names. The syntactic forms X[E1,EZ] and Y[E3,E4] are valid within this block and denote evaluation of the accessing algorithm defined by the ARY2-structure declaration (with an appropriate substitution of actual for formal parameters). ---------- *Remember that on the PDP-11 memory is byte-addressable, Words are addressed by the even addresses; hence the multiplications by 2 in the definition of ARY2. ---------- Although they are not implemented in this way, for purposes of exposition one may think of the STRUCTURE declaration as defining a routine with one more formal parameter than is explicitly mentioned. The value of this hypothetical routine is the address of the data structure element which is accessed by the call to the routine. For example, the STRUCTURE declaration in the previous example STRUCTURE ARY2[I,J] = (.ARY2+(.1-I)*2*10+(.J-l)*2); conceptually is identical to a ROUTINE declaration ROUTINE ARY2(FO,F1,F2) = ( .FO+(.F1-1)*2* 10+(.F2-l)*2); The expressions X[.A,.B] and Y[.B,.A] correspond to calls on this routine; i.e. to ARY2(X,.A,.B) and ARY2(Y,.B,.A). In a STRUCTURE declaration there is an implicit, un-named formal parameter, the name (address) of the storage segment to be accessed. The name of the structure itself is used to denote this zero-th parameter. This convention maintains the positional correspondence of actuals and formals. Thus, in the example above, .ARY2 denotes the value of the name of the particular segment being referenced, and X[.A,.B] is equivalent to: (X+(.A-1)*2*10+(.B-1)*2) The value of this expression is a pointer to the designated element of the segment named by X. Remember, routines have locations assigned to each of its formals. To continue the analogy three locations ARY2, I, J, would exist, and on call invocation, ARY2 would contain the value of X, I the value of .A, and J the value of .B as shown in figure 1.23a. Figure 1.23a In the following example the STRUCTURE facility and BIND declaration have been used in the computation of a matrix product. Each element of the product Z is the vector product of a row of X and a column of Y. In the inner block the names XR and YC are bound to pointers to the base of a row of X and column of Y respectively. These identifiers are then associated with structures which allow one-dimensional access. BEGIN STRUCTURE ARY2[1,J] - (.ARY2+(.1-1 )*2*10+(.J-1)*2), ROW[I] = (.ROW+(.I-1)*2), COL[J] = (.COL+(.J-l)*2*10); OWN ARY2 X[100]:Y[100]:Z[100]; ... INCR I FROM 1 TO 10 DO ! Loop over rows BEGIN BIND ROW XR=X[.I,1]:ZR=Z[.I,1]; INCR J FROM 1 TO 10 DO ! Loop over columns BEGIN LOCAL T; BIND COL YC=Y[I,.J]; T = 0; INCR K FROM 1 TO 10 DO T = .T+.XR[.K]*.YC[.K]; ZR[.J] - .T; END; END; ... END ! The XR BIND establishes a row pointer in X ! The YR BIND establishes a column pointer in Y ! The ZR BIND establishes a row pointer in Z ! T accumulates the vector multiply for a given row and column ! ZR[.J] establishes a unique element in Z; ZR via the BIND selects ! the row and .J the element within the row. Figure 1.23b ! shows memory at the beginning of the calculation for Z(2,3) Figure 1.23b 1.24 Data Structures (Actual Syntax) The example declarations in the preceding section are valid BLISS syntax; however, they do not reflect the complete power of the declarative facilities. The following sections are definitive presentations of the actual syntax and semantics of these declarations 1.24.1 Structures strc-decl ::= STRUCTURE name strc-fml-lst = strc-size E strc-fml-lst ::= [name-lst] / empty strc-size ::= [E] / empty strc-access ::= name[expr-lst] / name[alloc-unit expr-lst;expr-lst] expr-lst ::= E / E,expr-lst / empty Structure declarations serve to define a class of data structures by defining an explicit access algorithm, E, to be used in accessing instances of that structure. The access algorithm introduced by such a declaration is given a name which may be used as the structure name (strc-id) in an allocation declaration, BIND declaration or MAP declaration. In this way the access algorithm is associated with a storage segment. The strc-size specifies an allocation algorithm. It is an expression which calculates the number of bytes of storage which are allocated to the segment referenced by a name associated with the structure in an allocation declaration. The expression must evaluate, after substitution for formal arguments, to a compile-time constant when the allocation declaration is made. The names in the structure formal list (strc-fml-lst) are formal parameter identifiers which are used in two distinct ways: 1. Dotted occurences of the formal names in E positionally correlate with the values of expr-lst elements at the site of a structure access. These are referred to as access formals and access actuals respectively. 2. Undotted occurrences of the formal names positionally correlate with the values of the expr-lst elements in a size-part at the site of the declaration which associated the variable name with the structure class (see Section 1.24.3). These are referred to as incarnation formals and incarnation actuals respectively. They may occur in both strc-size and in E. In addition to the explicit formal names, the structure name in dotted form is used as an access formal to denote the'name of the specific segment being accessed (that is, to denote the pointer to the base of the segment). The access algorithm, E, on the right of the equal sign in a STRUCTURE declaration is just another BLISS-11 expression once it is invoked as a structure access. On the PDP-11 both bytes and words can be addressed. Proper access to byte and word data structure elements depends on the computation in the access algorithm. The keyword BYTES provides a means of specifying data structures whose basic units may be either bytes or words depending on the allocation declaration of the storage. In the strc-size or E of a STRUCTURE declaration the name BYTES has a value of 1 or 2 depending on the size indicated in the alloc-unit portion of a declaration referencing the structure (see Section 1.24.3). An alloc-unit of BYTE gives it a value 1, WORD makes it 2. Thus BYTES is the number of bytes in the basic allocation unit of the storage segment. Whether the user cares to use the BYTES keyword depends on the exact specification of a structure and the amount of allocation unit independence desired, but he must always be aware of the fact that a structure access usually produces an address which selects a data item and he is responsible for generating the correct address. Consider the following declarations: STRUCTURE VECTOR[I] = [I*BYTES] (.VECTOR+.I*BYTES); WORD OWN VECTOR X[100]; The incarnation actual 100 replaces the undotted occurrences of I in the STRUCTURE declaration. Since the allocation unit is WORD the special keyword BYTES is replaced by 2. After making these substitutions the strc-size expression gives the value 200, so 200 bytes (100 words) are allocated as an OWN storage segment with the name X. A structure access such as .X[10] supplies an access actual, 10, which replaces occurrences of .I and a segment name, X, which replaces .VECTOR. The access expands to .(X+10*2)<0,8*2> which specifies the contents of the 10-th element (where the first word is the zero-th element) of the word vector X. This example shows the interaction of a simple allocation declaration with a syntactically complete structure declaration; it remains now to discuss the complete syntax of allocation declarations. The VECTOR structure defined in the last example is the default structure used in any structure access in which no other structure name has been indicated. The default may be altered by using a STRUCTURE declaration to redefine the algorithm identified by VECTOR. 1.24.2 Structure Accesses for Variable Segment Names A structure access of the following form strc-name[alloc-unit E, expr-lst ; expr-lst) may be used to treat the expression E as the name of a storage segment and to perform an access as specified by the access algorithm associated with the strc-name. The first and second expr-lst's are used as the incarnation actuals and access actuals respectively. The alloc-unit is used to give a value to BYTES. 1.24.3 Memory Allocation Informally, an allocation declaration looks like the following: OWN empty LOCAL BYTE GLOBAL alloc-decl-arg-list WORD EXTERNAL REGISTER STACKLOCAL where alloc-decl-arg-list is essentially a list of names to be declared. The BNF description is the following: alloc-decl ::= alloc-unit init-type msid-lst1 / alloc-unit REGISTER msid-lst2 / alloc-unit other-type msid-lst3 alloc-unit ::= WORD / BYTE / empty (=> WORD) init-type ::= OWN / GLOBAL other-type ::= LOCAL / EXTERNAL / STACKLOCAL msid-lstn ::= msid-elmt, / msid-elmtn, msid-lst, msid-elmtn ::= link-id strc-id id-groups, / link-id strc-id id-part / link-id strc-id id-groupsn : id-part id-groupsn ::= id-group, / id-groupn : id-groups, id-groupl ::= id-part size-part init-part / id-part size-part / id-part init-part id-groupn ::= id-part size-part reg-part / id-part size-part / id-part reg-part id-groupa ::= id-part size-part id-part ::= name / name : id-part size-part ::= [ expr-lst ] init-part ::= = plitarg reg-part ::= = oit strc-id ::= strc-name / empty (=> VECTOR) strc-name ::= name link-id ::= link-name / empty (=> BLISS) link-name ::= name An allocation declaration introduces names, each of which has a scope consisting of the block in which the declaration occurs minus any inner blocks containing an explicit redeclaration of the sam name. however, no up-level addressing is permitted for names declared as LOCAL, REGISTER or STACKLOCAL. By this it is meant that the scope of such names does not include the bodies of any routines declared within the block where the name is declared. Certain problems may arise with valid redeclarations of OWN names. For a discussion of and solution for the problem see the explanation of the UNAMES switch in Section 3.2. REGISTER, LOCAL, and STACKLOCAL declarations cause allocation of storage at each block entry (including recursive ones), and corresponding de-allocation on block exit. Storage for OWN and GLOBAL declarations is allocated once (before execution begins) and remains allocated during the entire execution of the program. EXTERNAL declarations do not allocate storage, but cause a reference to be made to storage declared with the same name in a GLOBAL declaration of another module. Space for allocation is taken from main memory for OWN and GLOBAL declarations, and from the machine's high speed registers for REGISTER declarations. Space for allocation of LOCAL declarations will be taken from either the registers or main memory depending upon the availability of registers and the use of the LOCAL. STACKLOCALS are always allocated in main memory. If present, alloc-unit specifies what unit memory size is to be allocated. If this is not present then WORD is assumed. An msid-lstn specifies one or more identifiers which are to be declared as the type of storage indicated. Each msid-elmt, contains a linkage name, a structure name, a list of identifiers and possibly some other information. If the linkage name is omitted then the name BLISS is used by default. Similarly the name VECTOR is used if the structure name is omitted. All of the identifiers appearing in the msid-elmt, have the given or defaulted linkage and structure names associated with them. Within an msid-elmt, the identifiers may be divided into id-groupn's. Each id-group, is followed by some additional information which is applied to all names in the id-part of the id-groupn. Three kinds of additional information may occur. These are the syntactic units size-part, init-part and reg-part. Each will be discussed in detail. A size-part specifies a list of incarnation actuals to be used in the structure definitions associated with the identifiers. This may be specified in any alloc-decl. However, in REGISTER declarations the allocation size which is computed by the strc-size expression in the structure definition and which typically is a function of the incarnation actuals must be one word. If no size-part is indicated for an identifier its incarnation actuals default to 1. An init-part may be included in a GLOBAL or OWN declaration. It supplies a series of data words which are used as initial values of the associated storage segment. Note that in the plitarg specifies words (never bytes) of data regardless of the allocation unit appearing in the declaration. This initialization is performed only once. A reg-part can be used in a REGISTER declaration to force a name to be allocated to a specific register. The oit following the equal sign is the number of the general register which is to be assigned to the identifiers in the id-part of the id-groupn containing the reg-part. The first example in section 1.23 of a two-dimensional array might now be written: BEGIN STRUCTURE ARY2[1,J]= [I*J*BYTES] (.ARY2+.(1-1 )*BYTES*J)+(.J-1)*BYTES)<0,8*BYTES>; OWN ARY2 X:Y:Z[10,10]; ... X[.A,.B] = .Y[.B,.A]; ... END; Notice that the declaration OWN A,X[100] allocates a single-ward OWN variable A and a 100-word OWN vector X because the defaults make it equivalent to WORD OWN VECTOR A[1], VECTOR X[100] where VECTOR is as defined in Section 1.24.1 (unless it has been redefined by a STRUCTURE declaration). 1.24.4 MAP Declaration map-decl ::= MAP msid-lst3 The MAP declaration is syntactically and semantically similar to an allocation declaration except that no new storage or identifiers are introduced. The purpose of the MAP declaration is to permit redefinition of the structure and incarnation actuals associated with an identifier (or set of identifiers) for the scope of the block in which the MAP declaration occurs. This is the only way to associate a structure (other than VECTOR) with a formal parameter or a control variable of an INCR or DECR loop. 1.24.5 BIND Declaration bind-decl ::= BIND msid-lst4 msid-elmt4 ::= link-id strc-id id-groups4 id-groups4 ::= id-group4 / id-group4 : id-groups4 id-group4 ::= id-part size-part bind-part / id-part bind-part bind-part ::= = E A BIND declaration introduces a new set of names whose scope is the block in which the BIND declaration occurs, and binds the value of these names to the value of the associated expressions at the time that the block is entered. Note that these expressions need not be constant at compile time. 1.25 LABEL Declaration label-decl ::= LABEL name-lst Labels are used solely in conjunction with the LEAVE expression. Each name to be used as a label must be so declared in some block containing that usage. See the description of the UNAMES switch for a discussion of a potential problem with label names. 1.26 UNDECLARE Declaration un-decl ::= UNDECLARE name-ist The identifiers in the name-lst become undefined within the scope of the declaration. This can be used to prevent accidental access to variable names defined in outer blocks. 1.27 ENABLE Declaration enable-decl ::= ENABLE a-expr-set ELBANE The syntax of a-expr-set is identical to that of the body of a SELECT expression including OTHERWISE and ALWAYS. ENABLE serves to dynamically declare an interest in handling a set of conditions that may occur in the dynamic scope of the declaration, where the occurrence of one of the conditions is signalled by the subsequent execution of a SIGNAL expression. In particular, when a SIGNAL expression, say 'SIGNAL e', is executed the context of the most recently executed ENABLE declaration is immediately entered and the value of e is compared to each of the selection expressions (preceding the colons). If none of the selection criteria is satisfied, the next embracing ENABLE context is entered, etc., until some selection criterion is satisfied. When a selection criterion is satisfied the associated expression (following the colon) is executed - unless explicit subsequent control sequencing is specified in this expression (e.g. another 'escape' expression) control will pass out of the block in which the ENABLE declaration occurs. The value of the block is the value of the expression in the ENABLE which was executed. The value of the global variable SIGVAL contains the value of the argument of the most recently executed SIGNAL. This may be of use inside the body of an ENABLE. ENABLE declarations can have adverse effect on variables contained in registers. The source code within the ENABLE declaration and following the block containing the ENABLE must not rely on the contents of REGISTER or LOCAL variables or register parameters whose scope contains an ENABLE. If a LOCAL variable is required it should be declared using the STACKLOCAL declaration. These problems are a deficiency in the implementation of ENABLE and should not be considered a permanent feature of the language. 1.28 Macros In order to facilitate program readability and modifiability, a macro system is embedded in BLISS. The system allows nested macro definition as well as iterative and recursive forms of evaluation. 1.28.1 Syntax for Macro Declarations macro-decl ::= MACRO dfn-lst dfn-lst ::= dfn / dfn ,dfn-lst dfn ::= name fxd-parms itrd-parms = body $ fxd-parms ::= (name-lst)/ empty itrd-parms ::= [] / [name-lst] / empty The essential function of the macro system is to replace the macro name and its actual parameter list (wherever the name occurs within the scope of the macro definintion in the program) by its body, with actual parameters substituted for formals. The body is considered to be a string of "atoms"--names, literals and delimiters--and is therefore independent of editing symbols--blanks, CR, LF, and BLISS comments. Atoms are the smallest recognizable syntactic elements in the BLISS-11 language. The format of the macro call is simply the macro. name possibly followed by the bracketed actual parameter list. The brackets must be one of the pairs: (), [], <>, and the actual parameters must be separated by commas. The actual parameters may be arbitrary strings of atoms; however, occurrences of the brackets: (), [], <>, and components of a parameter must be properly balanced and nested. All macros in actual parameter lists are expanded before formal/actual binding; i.e. BLISS-11 expands macros from the inside out. It is possible to trace the expansion of macros through the use of the EXPAND switch. See Section 3.2 for a discussion of this facility. Macros effect a text substitution process. The actual parameters in the call serve as the "text values" for formal parameters in the definition and, on call, the definition serves as a template for a text substitution process resulting in a macro expansion. The result of the macro expansion depends on the form of the macro, the context of the call, and any "special functions" used within the macro definition. 1.28.2 Macro Forms and Special Functions BLISS has six macro forms and seven special functions: Macro Forms Special Functions Substitute SREMAINING* Simple $LENGTH Pass $COUNT Recursive $OUOTE Iterated $UNQUOTE Fixed Iterated $STRING* $NAME* 1.28.3 Macro Forms and Examples <1> Substitute Syntax: MACRO name=body$ Call Format: name Replacement Algorithm: The body replaces the macro name. Example: Definitions: MACRO HBYTE=8,8$, ! position-size of high-order byte LBYTE=0,8$, ! position-size of low-order byte LINK=2,0,16$, ! specify a data field TAB=OUTPUT(#11)$; ! call to output routine ---------- *These special functions have not yet been implemented. ---------- Calls: A=.B; ! move a byte PTR=.PTR[LINK]; ! follow a linked list TAB; ! output a tab character In expanding the above calls a one-for-one substitution occurs between the name and the body, so A=.B expands to A<0,8>=.B<8,8> <2> Simple Syntax: MACRO name(frml1,...,frmln)=body$ Call Format: name(actul1,...,actuln)* Replacement Algorithm: Occurrences of the formals (frml1...) in body are replaced by the actuals (actul1...) at the call site. After the replacement the expanded body replaces the call. Unspecified actuals default to empty. The algorithm ignores extra actuals, but issues a warning message. Example: Definition: ! Use this macro to call an output routine ! to print an error message MACRO OUTERR(NUM,MSG)= OUTSTRING(PLIT($STRING('ERR',NUM,':',MSG))$ This macro uses the special function $STRING. The string values of the $STRINC argument list are concatenated to form a string (a single atom). Call: OUTERR(4,' Invalid Date') expands to: OUTSTRING(PLIT('ERR4: Invalid Data')) <3> Pass Syntax: MACRO name[]=body$ ---------- *Macro calls may use any of the bracketing pairs (), <>, or []; all the examples will use (). ---------- Call Format: name(actul1,...,actuln) Replacement Algorithm: The body replaces the call only if a non-empty actual-parameter list is specified; otherwise, the macro is replaced by the empty atom. If an actual-parameter list is specified, the special function $REMAINING may be used in the body to stand for the actual-parameter list with outer brackets removed and the "default separator" replacing the commas. $REMAINING and default separators are discussed in more detail in later sections. Example: The example in the discussion of the recursive macro form will cover the pass form as well as recursive. <4> Recursive Declaration Syntax: MACRO name(frml1,...,frmln)[]=body$ Call Syntax: name(actul1,...,actuln,actuln+1,...,actulm) Replacement Algorithm: If the call has not specified at least n actuals then the empty atom replaces the call; otherwise the macro is expanded with actul1,...,actuln replacing frml1,...,frmln in the body. The special function SREMAINING may be used in the body to denote the remaining actuals actuln+1,...,actulm. Note: The body may have a recursive call within it, but this is not necessary. Example: Definition: MACRO COND(BOOL,EXP)[] = IF BOOL THEN EXP EL($REMAINING) COND($REMAINING)$; MACRO EL[]=ELSE$; The macro, COND, has the recursive form, and the macro, EL, the pass form. Note that a call on COND appears in the body of COND. Call: The COND macro requires two actuals to replace the formals BOOL and EXP, but can accept any number of pairs of actuals. A call such as COND(E1,E2,...,E2n-1,E2n) results in BLISS code which evaluates E1, E3, ... , E2n-l until one of them, Ei , is true; then Ei+1 is evaluated. Consider the call COND(.B,A=.X,.X OR .Y,FCT(.M)) This will expand to IF .B THEN A=.X EL(.X OR .Y,FCT(.M)) COND(.X OR .Y,FCT(.M)) Since the argument list passed to EL is not empty it is replaced by its body. Next the recursive call to COND is made. At this point the expanded text looks like the following: IF .B THEN A=.X ELSE IF .X OR .Y THEN FCT(.M) EL() COND() The argument list in the call to EL is empty so the call just disappears. Similarly the next recursive call to COND has an empty argument list and yields no text. The expansion is finished and the result is IF .B THEN A=.X ELSE IF .X OR .Y THEN FCT(.M) <5> Iterated Declaration Syntax: MACRO name[frml1,...,frmln]=body$ Call Syntax: name(actul1 1,...,actul I,actul21,-..,actul2,...,acfu),l,...,acf Replacement Algorithm: The macro is treated as if it were a simple macro and called m times. The first time the formals are bound to actul11,...,actul1n, the second time to actul21,...,actul2n, etc. Default separators are placed between the text produced by the iterations of the macro expansion. Note that expansion stops when less than n actual parameters remain to be bound to the formals. (Thus the expansion is not exactly like a simple macro since unspecified actuals do not default to empty.) Inside the body the special function $COUNT denotes the integer which gives the number of iterations completed up to the point where it appears. $REMAINING may be used to denote the actuals which have not been bound to formals so far in the expansion process. <6> Fixed Iterated Declaration Syntax: MACRO name(fixdl,...,fixd,)[frml1,...,frmln]=body$ Call Syntax: name(actul1,...,actulm,actulm+1,...actulm+k) Replacement Algorithm: The fixed formals, fixd1,...,fixdm, are bound to the first m actual parameters, then the macro behaves exactly as if it were an iterated macro with formals frml1,...,frmln, and actuals actulm+1,...,actulm+k. Example: Definition: MACRO INITLOCLVEC (ARYNAME)[]= LOCAL ARYNAME[$LENGTH-1]; LOAD (ARYNAME, $REMAINING)$; MACRO LOAD(BASE)[VALUE]=BASE[$COUNT]IVALUE$; The special function $LENGTH has a value equal to the number of actuals passed in the current macro call. Call: INITLOCLVEC(CAT,4,3,7,0,1 ) expands to LOCAL CAT[5]; CAT[O]=4; CAT[I]-3; CAT[2]=7; CAT[3]=0; CAT[4]=1 The initial expansion results in LOCAL CAT[6-1]; LOAD(CAT,4,3,7,0,1) The expansion of LOAD iterates as follows - $COUNT=O BASE=CAT VALUE=4 produces CAT[0]=4 - The default separator is emitted - $COUNT=1 BASE=CAT VALUE=3 produces CAT[0]=3 - The default separator is emitted - etc. 1.28.4 Default Separators Because punctuation plays a critical role in BLISS syntax, the iterated macro forms could produce incorrect BLISS source text if the proper punctuation atoms did not occur between the text produced by the various iterations. To establish proper punctuation BLISS-11 outputs punctuation atoms, called default separators, based on the context of the macro call. The following list describes possible BLISS contexts and the default separators associated with them; in addition certain contexts also generate brackets, and these are described where they apply. 1. Preceding Context: a. The separator "," b. The declarators EXTERNAL, BIND, MACRO, ROUTINE, etc. c. The bracket "<" d. The bracket "(" used as parameter list bracket for routine or macro call a. PLIT ( f. PLIT g. An expression or name Separator Generated: "," Brackets Generated: For cases f and g generate "(" and ")" 2. Preceding Context: a. The separator "; b. BEGIN c. "(" used as open bracket of a block d. SET or NSET e. An OF following a CASE or SELECT Separator Generated: ";" Brackets Generated: After OF following CASE generate a SET, TES pair. After OF following SELECT generate a NSET, TESN pair. 3. Preceding Context: a. Structure or linkage name in a declaration Separator Generated: ":" 1.28.5 Special Functions $REMAINING Within the body of a pass, recursive, iterated or fixed iterated macro this special function denotes the string consisting of all those actual parameters not yet bound to a formal parameter. The commas in the actual parameter list are replaced by the default separator implied by the context of the use of $REMAINING. $LENGTH This special function may appear in the body of any form of macro. It denotes the integer which is the number of actual parameters passed to the current iteration or invocation of a macro. $COUNT Inside the body of a recursive, iterated or fixed iterated macro the value of $COUNT is the integer which specifies the recursion depth or iteration count for an expansion of the body. The recursion depth is the number of partially complete expansions of the macro which cannot be completed until the current call to the macro is complete. The iteration count for an iterated or fixed iterated macro is the number of iterations of the macro completed so far. $QUOTE The single atom following this special function is its argument. It causes this argument to be used as it appears rather than attaching any special meaning to it. For example, if the argument is a name it is not associated with any meaning it may have as the result of a previous declaration. A $QUOTEd "," in a macro argument list will act as just another atom in the current argument rather than indicating the end of the argument. A $QUOTEd "$" may appear within a MACRO declaration without terminating the declaration. Note that this allows nested macro definition. Similarly special functions may be $QUOTEd to allow their use in nested macro definitions. $UNQUOTE The single atom following $UNQUOTE is its argument. It forces the argument to take on the current meaning that is associated with it. This is useful only in the case of names appearing within macro declarations. Normally a name inside a macro declaration is automatically $QUOTEd, i.e. it is not associated with the meaning given the name in some other declaration. When macro expansion occurs a name atom is associated with any declaration of the name which is in effect at the site of the macro call. Using $UNQUOTE within a macro declaration forces a name to be associated with any declaration of the name which is in effect at the site of the macro definition. $STRING( ... ) The string values of the items in the argument list are concatenated to form a single atom which is a string. $NAME( ... ) As in $STRING the string values of the arguments are concatenated to form a single atom. However, this atom is a name rather than a string. Some typical uses of this special function might be to create new names from macro parameters or to construct names acceptable to the loader but not to BLISS (e.g. the name S.FDB). 1.29 CSECT and PSECT Declarations csect-decl ::= CSECT name / PSECT name / CSECT keys / PSECT keys keys ::= key / key , keys key ::= keyword = quoted-string keyword ::= CODE / DEBUG / GLOBAL / OWN / PLIT A csect-decl may occur only in the outermost block of a module. When CSECT is used, the assembler directive .CSECT is generated for the control section. When PSECT is used, .PSECT is generated. The quoted string may contain both a control section name and attributes acceptable to the loader. Examples: PSECT ABCD; generates .PSECT ABCD.C ;code .PSECT ABCD.D ;debug .PSECT ABCD.G ;globals .PSECT ABCD.O ;owns .PSECT ABCD.P ;plits MODULE AP ... PSECT CODE=' ',OWN='$OWN',GLOBAL='GLOBLS,GBL'; generates .PSECT ;code .PSECT A.D ;debug .PSECT $OWN ;owns .PSECT GLOBLS,GBL ;globals .PSECT A.P ;plits 1.30 REQUIRE Declaration It is often convenient to maintain files of declarations or code which may be used in more than one module. The REQUIRE declaration implements a way to specify this in a BLISS-11 program. requ-decl ::= REQUIRE filespec filespec ::= device filenamespec device ::= name: / empty (=> DSK:) filenamespec ::= filename / filename[ppnspec] filename ::= name / name.name ppnspec ::= octal,octal / CMU-ppn* / empty (=> ppn of job) The REQUIRE declaration instructs the compiler to read the text of the file named by the filespec as if that text had been copied into the source file immediately following the semicolon which follows the declaration. A REQUIRE file may in turn REQUIRE another file to a total depth of eleven REOUIREd files. ---------- *This is a special form of ppn acceptable on the PDP-10 systems at Carnegie-Mellon University. ---------- The defaults for omitted parts of a filespec are as follows: device is defaulted to DSK, ppnspec is defaulted to the project-programmer number of the job which is running the compiler. The following points should be noted: 1) Ppn's such as [27,15] must be entered as octal constants, i.e. [#27,#15]. 2) Everything between the semicolon which follows a REQUIRE declaration and the next carriage return will be ignored. 3) Only the first six (six, three) characters of the name given for a device (file, extension) will be used. These names should refer to existing devices and files. 4) The ppnspec should refer to a valid account number on the system being used. 5) The result of a REQUIRE inside a macro is unpredictable. 1.31 SWITCHES Declaration swtch-decl ::= SWITCHES swtch-lst swtch-lst ::= swtch / swtch , swtch-lst The SWITCHES declaration allows the user to set various switches which control the compiler options. The effect of a SWITCHES declaration is limited to the scope of the block in which the declaration is made. The allowed forms of swtch are given in tabular form in section 3.2. 2.0 SPECIAL LANGUAGE FEATURES The previous chapter described the basic features of the BLISS language. In this chapter we describe additional features which are highly machine and implementation dependent. The reader should consult a PDP-11 Processor Handbook for full descriptions of specific PDP-11 machine instructions mentioned in the following sections. 2.1 Special Routines A number of features have been added to the basic BLISS-11 language which allow greater access to the PDP-11 hardware features. These features have the syntactic form of routine calls and are thus referred to as "special routine calls". Code for special routines is always generated in line. 2.1.1 TRAP, EMT and IOT spcl-rout ::= emt-call / trap-call / lot-call emt-call ::= EMT(compile-time-expr ,expr-lst) trap-call ::= TRAP(compile-time-expr , expr-lst) lot-call ::= I0T(expr-lst) An EMT or TRAP special routine call is syntactically a routine call with at least one parameter. The first (and possibly only) parameter must evaluate at compile-time to a value in the range O to 255 inclusive. This value is incorporated into the low byte of the EMT/TRAP instruction itself. The remaining arguments, (if any) are evaluated and pushed onto the stack prior to the EMT/TRAP instruction. The arguments are removed from the stack following return from the EMT/TRAP. An IOT special routine call is similar to an EMT/TRAP special routine call except that the integer parameter is not used and the instruction generated is IOT. 2.1.2 HALT, RESET and WAIT spcl-rout ::= HALT() / RESET() / WAIT() Each of these parameterless special routine calls causes the corresponding PDP-11 machine instruction of the same name to be emitted. Care should be taken to ensure that the instruction occurs in the desired place. 2.1.3 SWAB spcl-rout ::= SWAB(E) Not implemented yet. 2.2 INLINE - Machine Language in BLISS-11 inline ::= INLINE(string) INLINE is a simple escape mechanism to allow the programmer to embed machine language code in his BLISS-11 program. Its use is both machine and context dependent and extreme caution is recommended. It is intended primarily as a means to continue program development while a compiler bug or language deficiency is being handled. The text string is saved intact for inclusion in the compiled assembly source. When the assembly source is generated, a carriage-return/line-feed is appended to the string. A warning message is given for each use of INLINE. 3.0 SYSTEM FEATURES 3.1 Command Syntax The general format of the initial command to BLISS-11 is: LSTDEV:FILE.EXT=SORCDEV:FILE.EXT,...,SORCDEV:FILE.EXT The LSTDEV:FILE.EXT may be omitted with the implication that the file is not to be generated. The .EXT may be omitted on any of the file specifications and following defaults will be assumed: Listing File: P11 Source File: Bll As with other DEC system programs, switches of the form /X, X=A,B,...,Z may be placed anywhere in a command string, and *XY..Z)=/X/Y.../Z. The listing file is properly formatted for immediate assembly. 3.2 Compiler Options The actions of the compiler with respect to a program may be controlled by specifications located in: 1) the initial command string typed by the user, 2) the module head, or 3) a SWITCHES declaration. The remainder of this section gives a list of compiler options and the associated command switch and/or source language constructs which specify these options. Many of the options are two-valued switches; that is, they may be either activated or deactivated. These cases are described by showing both settings of the option. The affirmative or activated setting is given first. Normally the function described will be that of the affirmative setting. An asterisk (') indicates the initial setting of such options. Not all options may be specified in all places. If a particular option cannot be manipulated in one of the three possible locations a "--" will appear as the entry corresponding to the location. Keep in mind that a SWITCHES declaration is in effect only for the block in which it appears. Upon leaving a block options revert to their previous settings. Command Switch: /D, /-D* Module Head: -- SWITCHES: -- Function: Produce symbol table and linkages for SIX12 debugging package. Command Switch: /E, /-E* Module Head: EXPAND, NOEXPAND SWITCHES: EXPAND, NOEXPAND Function: Activate tracing of macro expansions. Command Switch: /F*, /-F Module Head: FINAL, NOFINAL SWITCHES: FINAL, NOFINAL Function: Activate final peephole optimization. On the PDP-11 external devices are made available by including their control registers in the address space. These addresses appear to be ordinary memory locations. However, accessing such locations may cause unexpected events to occur. For example, setting a bit (in an I/O device register) may cause the contents of another location (the device buffer) to change; or, changing a single location (a segment register) may have drastic effects on the values at many other locations. The peephole optimization phase of the compiler cannot detect these "volatile" locations and performs optimizations assuming no such unusual events occur. This may result in the generation of incorrect object code. Peephole optimization may be suppressed by using this switch. The SWITCHES specification may occur only in the outermost block of a routine or module. Command Switch: /H, /-H* Module Head: -- SWITCHES: -- Function: Do special things for CMUs HYDRA operating system. Command Switch: /L*, /-L Module Head: LIST, NOLIST SWITCHES: LIST, NOLIST Function: Activate listing of source code. Command Switch: /N, /-N* Module Head: NOERRS, ERRS SWITCHES: NOERRS, ERRS Function: Suppress printing of warning and error messages on user's input terminal. Command Switch: /O*, /-O Module Head: OPTIMIZE, NOOPTIMIZE SWITCHES: OPTIMIZE, NOOPTIMIZE Function: NOOPTIMIZE may be used to disable all code motion optimizations and common sub-expression elimination across mark points (see Section 1.3.1), except that expressions common to all branches of a condtl-expr or case-expr may be computed before the branch point when possible. The compiler assumes (approximately) that at each mark point a null expression is executed which modifies every variable. Command Switch: /P, /-P* Module Head: PIC, NOPIC SWITCHES: PIC, NOPIC Function: Generate position independent code. The SWITCHES specification may occur only in the outermost block of a routine or module. Command Switch: /Q*, /-Q Module Head: SAFE, UNSAFE SWITCHES: SAFE, UNSAFE Function: Because of the possibility of computed addresses in a BLISS program, it is not always possible for the compiler to determine whether certain optimizations are "safe" across mark points (see Section 1.3.1). For example, in the expression BEGIN EXTERNAL A,B,C,D,E,F; A=.E+.F; .C=.D; B=.E+.F; END the assignment .C=.D may or may not affect the variables E and F, and the expression .E+.F may or may not have to computed twice. When the Q switch is on the compiler assumes that such optimizations are safe, i.e. in the above example .E+.F is computed only once. Note that variables declared as REGISTER cannot be affected by such assignments since the PDP-11 registers are not in the address space. Hence, if E and F are declared registers the optimization is automatically safe. Command Switch: /S, /-S* Module Head: -- SWITCHES: -- Function: Print each routine name and its size on the user's input terminal when the routine is finished compiling. Command Switch: /U, /-U* Module Head: UNAMES, NOUNAMES SWITCHES: UNAMES, NOUNAMES Function: Generate unique names for OWN variables, routine names and labels. The object file output by the compiler is a source file for a PDP-11 assembler. OWN variables, routines and labels are referred to in this file by their original source names. However, the BLISS language allows, through the use of block structure, the declaration of many such variables or names which have the same name but which indicate different items. Since the assembler does not provide any block structure the only way to prevent such names from referring to the same location is to generate unique names for each separate item. These names are of the form U$n where n is an integer. Command Switch: /X Module Head: SYNTAX SWITCHES: SYNTAX Function: Only perform a syntax check of the input. Do not generate code. This may be useful during the early stage of program development since a syntax check can be done considerably faster than a full compilation. Command Switch: /Z, /-Z* Module Head: ZIP, UNZIP SWITCHES: ZIP, UNZIP Function: Make some space-time tradeoffs in favor of time. The only decision currently affected by this switch is register saving and restoring. /Z forces this to be done inline rather than by the normal way of calling a routine. The SWITCHES form of specification may occur only in the outermost block of a routine or module. Command Switch: -- Module Head: IDENT = quoted-string SWITCHES: -- Function: Cause the assembler directive ".IDENT" to be generated with /quoted-string/ as its argument. Command Switch: -- Module Head: MAIN(E) or MAIN SWITCHES: -- Function: Declares that the module contains the starting point to be used by the loader as the loaded program's starting address. If E is specified it causes a stack of E words to be allocated from address #400 to #400+E and intializes the SP register to point to it. Command Switch: -- Module Head: RESERVE(expr-lst) SWITCHES: -- Function: Reserves the registers specified by the values in expr-lst. This prevents the compiler from generating code which uses these registers unless the program explicitly references them; i.e. by declaring a specific register such as REGISTER A=2. Command Switch: -- Module Head: STACK SWITCHES: -- Function: Equivalent to MAIN(#400). 3.3 Error Reporting Compiler error messages are of the form: ;TYPE#NNN ........2......3.......1 L1:NNN1 L2:NNN2 L3:NNN3 ;MESSAGE where TYPE is either WARN for an informational or warning message not fatal to the compilation, or ERR for a fatal error. Syntax-only processing continues after a fatal error message. NNN is the error number. MESSAGE is the error or warning message text. The integers 1, 2, 3 and line indexes L1, L2, L3 and line numbers following the line indexes provide three items of information about the location of the error: 1 and L1:NNN1 give the point in the text where the error was detected. 2 and L2:NNN2 give the beginning of the opening of the current control scope in which the error is detected. 3 and L3:NNN3 give the end of the last control scope successfully completed prior to the error. If any of the integers occur at the same position, then priority is given as they are listed above. Example: ; 0003 X=(B=.C; .Y .Z); ; ERR #006 .......3.2...1 L1:0003 L2:0003 L3:0003 ; IMPROPERLY FORMED ARITHMETIC EXPRESSION 4.0 RUN TIME REPRESENTATION OF PROGRAMS 4.1 Introduction To Calling Sequences In order to make fullest possible use of BLISS-11 it is important to understand the run-time environment in which BLISS-11 programs execute. The address space is occupied by various types of information: 1. Programs 2. Constants 3. Static variable areas (GLOBALS and OWNS) 4. Run-time Stack Programs are 'pure' - they do not modify themselves. Programs and constant areas can be placed in contiguous read only memory. Static variable storage and stack areas must be placed in read/write memory. BLISS-11 provides a number of linkage mechanisms for passing control to or from programs written using other language processors and to interface with the PDP-11 interrupt structure. The cases include: 1. BLISS - standard internal calling sequence 2. FORTRAN - FORTRAN compatible, out-of-line calling sequence convention. 3. INTERRUPT - hardware initiated routine calls via hardware traps 4. EMT, TRAP or IOT - software initiated call to interrupt routine These are discussed in turn. 4.2 BLISS-11 Internal Linkage The first type of routine linkage discussed is used by BLISS-11 itself. The basic mechanism is simply to push the arguments onto the stack, call the routine via register 7 (the PC), and on return the calling program deletes the arguments from the stack. The prototype call is thus: PUSH ARG1 PUSH ARG2 ... PUSH ARGn JSR PC,SUB ADD #2*N,SP Note the following about this linkage: 1. The called routine must access both formals and locals via offsets from the stack pointer (SP, Register 6). 2. The calling sequence itself does not provide a reasonable means to determine the number of arguments passed. Thus if a variable number of arguments. are to be used, the last argument must be the number of arguments to enable the called routine to do the appropriate address arithmetic. 3. The call is pure and reentrant. 4. Return is via "RTS PC". 5. The value of the called routine is returned in register zero, the previous contents of this register are last. Further, the called routine is responsible for saving and restoring any registers that are used during the execution. 4.3 BLISS-11 With Arguments Passed In Registers The calling sequence for placing an argument in a register consists simply of moving the value to that register instead of onto the stack. Manipulating register contents for code efficiency is the responsibility of the compiler. A routine which is called with an argument in a register generates code substantially as if the formal parameter had actually been declared as a register. 4.4 FORTRAN Linkage [Not implemented] The FORTRAN linkage is a mechanism compatible with the PDP-11 FORTRAN Linkage mechanism as set forth in DEC PDP-11 FORTRAN manual, DEC-11-OXFMA-A-D. In particular, the out-of-line convention is used. This call consists of: ... JSR PC,OUTLIN ... OUTLIN:JSR R1,$SAV5 ;SAVE R1 - R5 WHICH MAY BE ;CHANCED BY FORTRAN ROUTINE MOV #ARGLIST,R5 ;SET PARAMETER LIST POINTER JSR PC,SUB RTS PC ARGLIST:.BYTE N,0 .WORD ARG1 ... .WORD ARGN At the call site, the appropriate values are stored into the impure area beginning at ARGLIST. Arguments which are constant at compile time may be set up at compile time rather than at run time. Note that, as with FORTRAN compiled routines: 1. Formals are accessed relative to the argument pointer (R5, register 5). 2. The number of arguments is available as the value of the expression ".(.R5)<0,8>" 3. Return is via "RTS PC". The FORTRAN standard call sequence makes available at run-time the number of arguments actually presented to a routine. This number may be obtained as the value of the expression. .(.R5)<0,8> Note that only the low order byte is accessed. 4.5 INTERRUPT Linkage The INTERRUPT designation is applicable only to a routine (callee) and never to a call - that is, interrupt routines are intended to respond directly to hardware interrupts. The requirements for the called routine are as follows: 1. All register preservation and restoration is the responsibility of the called routine. 2. Termination of the routine is via an RTI instruction. 3. Every INTERRUPT routine has two implicit arguments: OLDPC and OLDPS which may be used to access the program counter and processor status pushed onto the stack by the interrupt hardware. No explicit arguments (formal parameters) are permitted. 4.6 EMT/TRAP Linkage (No Specification yet.) 4.7 Access to Variables This section briefly indicates the mechanisms by which generated code accesses various types of variables (formals, OWNS, and GLOBALS, LOCALS; etc.). The exact addressing scheme used by the compiler in any particuiar case is highly dependent upon the context; however, the following material should aid in understanding the overall strategy. 1. OWN and GLOBAL variables are accessed directly. They are accessed by absolute addressing or by PC-relative addressing unless the PIC switch is on. In this case PC-relative addressing is used. 2. The compiler will attempt to allocate 'simple' LOCAL variables to registers whenever possible and more efficient to do so. Several LOCAL variables, REGISTER variables, and temporary results may be allocated to ocupy the same physical location if their "lifetimes" do not overlap. 3. Local variables and Formal parameters of the current routine are accessed with respect to the SP-register. Access to these locations depends on the action of the stack pointer and is not the same at every point in a routine. 4.8 Coroutine Creation and Calls (No specification yet.) 5.0 EXAMPLES MODULE LISTPKG= BEGIN MACRO NOVALUE=.VREG$; ! ITEM DEFINITION STRUCTURE ITEM[I,J]= CASE .I OF SET (.ITEM); (..ITEM+2*.J); (...ITEM+2*.J); (.(..ITEM+2)+2*.J); TES; MACRO BASE=O,O$, RLINK=1,0$, LLINK=1,1$, SIZE=1,2$, DATA(I)=1,(I)+3$, NXTRLINK=2,0$, NXTLLINK=2,1$, NXTSIZE=2,2$, NXTDATA(I)=2,(I)+3$, PRVRLINK=3,0$, PRVLLINK=3,1$, PRVSIZE=3,2$, PAVDATA(I)=3,(I)+3$; FORWARD GET; MACRO INITITEM(ITM,SZ)= BEGIN BIND ITEM I=ITM; I[RLINK]=I[LLINK]=.I[BASE]; I[SIZE]=SZ END$; GLOBAL ROUTINE COPYITEM(FRM,T00)= BEGIN MAP ITEM FRM:TOO; DECR I FROM (.FRM[SIZE] MIN .TOOCSIZE])-3 TO 0 DO TOO[DATA(.I)]=.FRM[DATA(.I)]; .TOO[BASE] END; GLOBAL ROUTINE NEWCOPY(ITM)= BEGIN MAP ITEM ITM; COPYITEM(.ITM[BASE],GET(.ITM[SIZE])) END; ! LIST MANIPULATION GLOBAL ROUTINE LINK(ITM,AFTER)= BEGIN MAP ITEM ITM:AFTER; ITM[LLINK]=.AFTER[BASE]; ITM[RLINK]=.AFTER[RLINK]; AFTER[NXTLLINK]=AFTER[RLINK]= .ITM[BASE] END; GLOBAL ROUTINE DELINK(ITM)= BEGIN MAP ITEM ITM; ITM[PRVRLINK]=.ITM[RLINK]; ITM[NXTLLINK]=.lTM[LLINK]; TM[RLINK]=ITM[LLINK]=.lTM[BASE] END; GLOBAL ROUTINE EMPTY(ITM)= BEGIN MAP ITEM ITM; .ITM[BASE] EQL .ITM[RLINK] END; MACRO FORALL(L,I,BODY)= BEGIN LOCAL ITEM I; BIND ITEM Z=(L); I[BASE]=.Z[BASE]; WHILE (I[BASE]=.I[RLINK]) NEQ .Z[BASE] DO (BODY) END$; ! STORAGE ALLOCATION BIND K=13; STRUCTURE VECTOR2[l]=(.VECTOR2+4*.I); OWN VECTOR2 FREE[K], ITEM MEM[ITK], VECTOR TTN[K+1]= ( 1 ,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192), VECTOR MASK[K+1]= (0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191); MACRO BUDDIES(A,B,N)= (((A) XOR (8)) EQL .TTN[N])$, BASEOF(A,N)= ((A) AND NOT(.TTN[N]))$, INCLUDES(A,B,N)= (((A) XOR (B)) LSS .TTN[N])$; MACRO TESTFREE(INDX,FRM,TOO,ID,CONDITION)= BEGIN INCR INDX FROM (FRM) TO (TOO) DO FORALL(FREE[.INDX],ID,(IF (CONDITION) THEN RETURN -1)); END$; ROUTINE COLLAPSE(A,N)= BEGIN MAP ITEM A; FORALL(FREE[.N],TN, (IF BUDDIES(( .TN[BASE]),( .A[BASE]),.N) THEN BEGIN DELINK( .TN[BASE]); COLLAPSE(BASEOF((.TN[BASE]),.N), .N+1); RETURN NOVALUE END)); LINK(.A[BASE], FREE[.N]); NOVALUE END; ROUTINE LOG2(N)= BEGIN LOCAL I,T; I=0; IF (T=.N) NEQ O THEN UNTIL (I=.l+l) GTR K DO IF (T=.T/2) EQL O THEN EXITLOOP .I END; GLOBAL ROUTINE GET(N)= BEGIN LOCAL T; IF .N LSS O THEN RETURN -1; IF (N=LOG2(.N)) LSS O THEN RETURN -1; IF NOT EMPTY(FREE[.N]) THEN DELINK(.FREE[.N]) ELSE IF (T=GET(.N+1)) GEQ O THEN (LINK(INITITEM( .T+.TTN[.N],.N),FREE[.N]); INITITEM(.T,.N)) END; GLOBAL ROUTINE RELEASE(A)= BEGIN MAP ITEM A; LOCAL N; N=.A[SIZE]; IF .N LSS O THEN RETURN -1; IF (N=LOG2(.N)) LSS O THEN RETURN -1; TESTFREE(I,O,.N-1 ,T,(INCLUDES( .T,.A,.I))); TESTFREE(I,.N,.N,T,.T EQL .A); TESTFREE(I,.N+1 ,K,T,(INCLUDES(.A,.T,.I))); COLLAPSE(.A,.N); O END; END ELUDOM Appendix A - BLISS-11 Syntax module ::= E / MODULE module-head E ELUDOM module-head ::= name ( mdle-parms ) = / name = mdle-parms ::= mdle-parm / mdle-parm mdle-parms mdle-parm ::= LIST / NOLIST /ERRS / NOERRS / OPTIMIZE / NOOPTIMIZE / SYNTAX / PIC / NOPIC / EXPAND / NOEXPAND / FINAL / NOFINAL / UNAMES / NOUNAMES / SAFE / UNSAFE / ZIP / UNZIP / MAIN(E) / MAIN / RESERVE(expr-lst) / STACK / IDENT=quoted-string block ::= BEGIN blockbody END / ( blockbody ) blockbody ::= decls ; exprs / exprs decls ::= decl / decl ; decls exprs ::= E / label : exprs / E; exprs / empty expr-lst ::= E / E, expr-lst / empty comment ::= ! string-no-eel-sym eel-sym / % string-no-pet % E ::= smpl-expr / cntrl-expr smpl-expr ::= P12 / P12=E / P1 pos-size = E P12 ::= P11 / P12 XOR P11 / P12 EQV P11 P11 ::= PIO / P11 OR P10 P10 ::= P9 / P10 AND P9 P9 ::= P8 / NOT P9 P8 ::= P7 / P8 rel-op P7 P7 ::= P6 / P7+P6 / P7-P6 P6 ::= P5 / P6*P5 / P6/P5 / P6 DIV P5 / P6 MOD P5 P5 ::= P4 / P5^P4 / P5 ROT P4 P4 ::= P3 / +P4 / -P4 P3 ::= P2 / .P3 P2 ::= P1 / .P2 pos-size P1 ::= literal/ name / strc-access / block / routn-call pos-size ::= position ::= compile-time-expr size ::= compile-time-expr strc-access ::= name[expr-lst] / name[alloc-unit expr-lst;expr-lst] routn-call ::= Pl(expr-lst)/ P1( ) rel-op ::= EQL / NEQ / LSS / LEQ / GTR / GEQ / EQLU / NEQU / LSSU / LEQU / GTRU / GEQU literal ::= string / number / plit string ::= string-type quoted-string string-type ::= ASCII / ASCIZ / RADIX50 / RAD50 / empty (=> ASCII) quoted-string ::= "strng" / 'strng' number ::= decimal / octal decimal ::= digit / digit decimal octal ::= #oits oits ::= oit / oit oits oit ::= 0 / 1 / 2 / 3 / 4 / 5 / 6 / 7 digit ::= 0 / 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / 9 plit ::= PLIT plitarg / UPLIT plitarg plitarg ::= load-time-expr / string / tuple tuple ::= (tuple-item-lst) tuple-item-lst ::= tuple-item / tuple-item , tuple-item-lst tuple-item ::= load-time-expr / string / dup-fctr : plitarg dup-fctr ::= compile-time-expr name ::= $ namer / letter namer namer ::= letter / digit / namer letter / namer digit letter ::= A/B/C/.../Z/a/b/c/.../z cntrl-expr ::= cndtl-erpr / loop-expr / choice-expr / escp-expr / cortn-eupr cndtl-expr ::= IF E1 THEN E2 ELSE E3 / IF E1 THEN E2 loop-expr ::= WHILE E1 DO E2 / UNTIL E1 DO E2 / DO E1 WHILE Ep / DO E1 UNTIL E2 / INCR name FROM E1 TO E2 BY E3 DO E4 / DECR name FROM E1 TO E2 BY E3 DO E4 esc-expr ::= LEAVE label WITH E / LEAVE label / EXITLOOP E / RETURN E / SIGNAL E label ::= name choice-expr ::= CASE E OF SET exprs TES / SELECT E OF NSET a-expr-set TESN a-expr-set ::= a-E / a-E ; a-expr-set a-E ::= E : E / OTHERWISE : E / ALWAYS : E decl ::= routn-decl / ext-fwd-decl / link-decl / strc-decl / alloc-decl / bind-decl / map-decl / label-decl / un-decl / enable-decl / macro-decl / csect-decl / requ-decl / swtch-decl routn-decl ::= glbl-decl ROUTINE routn-specs glbl-decl ::= GLOBAL / empty routn-specs ::= routn-spec / routn-spec , routn-specs routn-spec ::= link-id name(name-lst)=E / link-id name=E ext-fwd-decl ::= EXTERNAL mlid-lst / FORWARD mlid-lst mlid-lst ::= mlid-elmt / mlid-elmt , mlid-lst mlid-elmt ::= link-name id-part link-decl ::= LINKAGE link-specs link-specs ::= link-spec / link-spec , link-specs link-spec ::= name = link-typ / name = link-typ (link-call-lst) link-typ ::= BLISS / INTERRUPT / EMT / TRAP / IOT / FORTRAN / link-name link-call-lst ::= link-arg / link-arg , link-call-lst link-arg ::= STACK / REGISTER=num num ::= 0 / 1 / 2 / 3 / 4 / 5 strc-decl ::= STRUCTURE name strc-fml-lst = strc-size E strc-fml-lst ::= [name-lst] / empty strc-size ::= [E] / empty alloc-decl ::= alloc-unit init-type msid-lst1 / alloc-unit REGISTER msid-lst2 / alloc-unit other-type msid-lst3 alloc-unit ::= WORD / BYTE / empty (=> WORD) init-type ::= OWN / GLOBAL other-type ::= LOCAL / EXTERNAL / STACKLOCAL msid-lstn ::= msid-elmtn / msid-elmtn, msid-lstn msid-elmtn ::= link-id strc-id id-groupsn / link-id strc-id id-part / link-id strc-id id-groupsn: id-part id-groupsn ::= id-groupn / id-groupn: id-groupsn id-group1 ::= id-part size-part init-part / id-part size-part / id-part init-part id-group2 ::= id-part size-part reg-part / id-part size-part / id-part reg-part id-group3 ::= id-part size-part id-part ::= name / name : id-part size-part ::= [ expr-lst ] init-part ::= = plitarg reg-part ::= = oit strc-id ::= strc-name / empty (=> VECTOR) strc-name ::= name link-id ::= link-name / empty (=> BLISS) link-name ::= name map-decl ::= MAP msid-lst3 bind-decl ::= BIND msid-lst4 id-group4 ::= id-part size-part bind-part / id-part bind-part bind-part ::= = E label-decl ::= LABEL name-ist un-decl ::= UNDECLARE name-ist enable-decl ::= ENABLE a-expr-set ELBANE macro-decl ::= MACRO dfn-lst dfn-lst ::= dfn / dfn , dfn-lst dfn ::= name fxd-parms itrd-parms = body $ fxd-parms ::= (name-lst) / empty itrd-parms ::= [] / [name-lst] / empty name-ist ::= name / name , name-ist csect-decl ::= CSECT name / PSECT name / CSECT keys / PSECT keys keys ::= key / key , keys key ::= keyword = quoted-string keyword ::= CODE / DEBUG / GLOBAL / OWN / PLIT requ-decl ::= REQUIRE filespec filespec ::= device filenamespec device ::= name: / empty (=> DSK:) filenamespec ::= filename / filename[ppnspec] filename ::= name / name.name ppnspec ::= octal , octal / CMU-ppn / empty (=> ppn of job) swtch-decl ::= SWITCHES swtch-lst swtch-lst ::= swtch / swtch swtch-lst swtch ::= LIST / NOLIST ERRS / NOERRS / OPTIMIZE / NOOPTIMIZE / EXPAND / NOEXPAND / SAFE / UNSAFE / ZIP / UNZIP / FINAL / NOFINAL /PIC / NOPIC / UNAMES / NOUNAMES spcl-rout ::= emt-call / trap-call / iot-call / HALT() / RESET() / WAIT() / SWAB(E) emt-call ::= EMT(compile-time-expr , expr-lst) trap-call ::= TRAP(compile-time-expr , expr-lst) lot-call ::= IOT(expr-lst) inline ::= INLINE(quoted-string) empty ::= The empty string strng ::= A sequence of characters compile-time-expr::= Expression with constant value at compile time load-time-expr ::= Expression with constant value at load time eel-sym ::= Character which indicates the end of a source line string-no-eol-sym::= A strng which does not contain an eel-sym string-no-pet ::= A strng which does not contain a per-cent sign Appendix B - Description of Non-Terminals of BLISS-11 BNF The non-terminal mnemonics appear in the same order as they are defined in Appendix A. module An independently compilable and linkable entity. module-head The beginning of a module consisting of a module name and parameters which affect the type of module produced. mdle-parms A list of module parameters separated by commas. mdle-parm A single module parameter. Module parameters specify various compilation options. block An explicit grouping of declarations and/or expressions. A block defines the lexical scope of declarations. blockbody A series of declarations and/or expressions separated by semi-colons. decls A series of declarations separated by semi-colons. exprs A series of optionally labeled expressions separated by semicolons. expr-lst A list of expressions separated by commas. comment Source text ignored by the compiler. E An expression. smpl-expr A simple expression; i.e. an expression containing no unbracketed control expressions. Pn n has some integer value between 1 and 12. Language elements which compute a single value. These consist of literals, names, structure names, routine calls, and blocks or larger expressions built up from these. Pi with larger i are built up from Pj's with j; LINKAGE BLISS = % PREDEFINED %, EMT = % PREDEFINED %, FORTRAN = % PREDEFINED %, HYDRA = % PREDEFINED %, IHYDRA = % PREDEFINED %, INTERRUPT = % PREDEFINED %, IOT = % PREDEFINED %, TRAP = % PREDEFINED %, BLISSREG1 = BLISS(REGISTER=1), BLISSREG2 = BLISS(REGISTER=1 ,REGISTER=2), ... BLISSREGS = BLISS(REGISTER=1,.. .,REGISTER=5); REGISTER RO:VREG=0, R1=1, R2=2, R3=3, R4=4, RS=5, SP=6, PC=7; ! Out-of-line routines for various operators EXTERNAL BLISS MUL:DIVR:MODR:ROTATE:SHIFT:EXCHJ; ! Out-of-line routines to save and restore registers EXTERNAL %SPECIAL LINKAGE% $SAV2:$SAV3:$SAV4:$SAV5; ! Entry points in the SIX12 debugging package EXTERNAL BLISS INIT612:SIX12:ESIX12; EXTERNAL % SPECIAL LINKAGE % XSIX12 ! Out-of-line routines to handle SIGNAL and ENABLE EXTERNAL % SPECIAL LINKAGE % $SIGNL:$SIGN1 :$ENABL; ! Global data used by SIGNAL and ENABLE EXTERNAL SIGVAL,SIGREG; Appendix E - RADIX50(RAD50) The following codes show the internal storage conventions for RADIX50. All numeric values in the table are octal. FIRST CHAR SECOND CHAR THIRD CHAR A 003100 000050 000001 B 006200 000120 000002 C 011300 000170 000003 D 014400 000240 000004 E 017500 000310 000005 F 022600 000360 000006 G 025700 000430 000007 H 031000 000500 000010 I 034100 000550 000011 J 037200 000620 000012 K 042300 000670 000013 L 045400 000740 000014 M 050500 001010 000015 N 053600 001060 000016 O 056700 001130 000017 P 062000 001200 000020 Q 065100 001250 000021 R 070200 001320 000022 S 073300 001370 000023 T 076400 001440 000024 U 101500 001510 000025 V 104600 001560 000026 W 107700 001630 000027 X 113000 001700 000030 Y 116100 001750 000031 Z 121200 002020 000032 $ 124300 002070 000033 . 127400 002140 000034 132500 002210 000035 (UNUSED) O 135600 002260 000036 1 140700 002330 000037 2 144000 002400 000040 3 147100 002450 000041 4 152200 002520 000042 5 155300 002570 000043 6 160400 002640 000044 7 163500 002710 000045 8 166600 002760 000046 9 171700 003030 000047 Appendix F - Error Messages ID Message O UNDECLARED IDENTIFIER 1 MISPLACED DECLARATION 2 EXPRESSION IN WRONG CONTEXT 3 BEGIN...) AND (...END ARE ILLEGAL 4 OPERATOR IN ILLEGAL CONTEXT 5 CONTROL EXPRESSION MUST BE PARENTHESIZED 6 IMPROPERLY FORMED ARITHMETIC EXPRESSION 7 IMPROPERLY FORMED ARITHMETIC EXPRESSION 8 MISSING THEN IN IF EXPRESSION 9 MISSING DO AFTER WHILE OR UNTIL 10 MISSING WHILE OR UNTIL AFTER DO 11 IMPROPERLY DEFINED CONTROL VARIABLE 12 MISSING DO AFTER INCR OR DECR 13 PARAMETER LIST NOT CLOSED PROPERLY 14 IMPROPER SELECTION LIST 1N CASE 15 MISSING SET AFTER CASE 16 MISSING TES AFTER SET 17 IMPROPER SELECTION LIST IN SELECT 18 MISSING NSET AFTER SELECT 19 COLON MISSING IN SELECT EXPRESSION 20 MISSING TESN AFTER NSET 21 ADDRESS ARITHMETIC INVOLVING LOCAL 22 ILLEGAL OCCURRENCE OF POINTER EXPRESSION 23 INVALID POSITION EXPRESSION IN POINTER 24 INVALID SIZE EXPRESSION 1N POINTER 25 POINTER EXPRESSION CROSSES WORD BOUNDARY 26 POSITION SPECIFIED IS GREATER THAN 16 27 BAD SYNTAX IN INLINE 28 INLINE ARGUMENT MUST BE STRING 29 CAVEAT EMPTOR! (USE OF INLINE) 30 MISSING COLON IN ENABLE 31 MISSING ELBANE AFTER ENABLE 32 MORE THAN ONE ENABLE IN BLOCK 33 MISSING OPEN PARENTHESIS IN EXCHJ 34 MISSING COMMA IN EXCHJ 35 MISSING CLOSE PARENTHESIS IN EXCKI 39 NON-ADDRESSABLE SYMBOL USED AS EXPRESSION 40 LABEL NOT USED ON THIS EXPRESSION 41 LABEL NOT YET ENCOUNTERED 42 MUST LEAVE TO LABEL 46 REGISTER NOT AVAILABLE FOR RESERVATION 47 TOO MANY CLOSE BRACKETS OR MISSING OPEN BRACKET 48 ERROR IN MODULE HEAD; SCAN RESTARTED AT FIRST BEGIN 49 INVALID SWITCH SPECIFIED 50 SYMBOL ALREADY DECLARED IN THIS BLOCK 51 SYNTAX ERROR IN SWITCH SPECIFICATION 52 EXPRESSION MUST BE LITERAL 53 UNDECLARED STRUCTURE 54 INVALID STRUCTURE NAME 55 MISSING EQUAL SIGN 56 DELIMITER MUST BE COMMA OR SEMICOLON 57 MORE THAN 1 EXPRESSION IN PARENTHESIS 58 ILLEGAL SYMBOL BEFORE DECLARATOR NAME 59 REGISTER NOT AVAILABLE 60 MISSING BRACKET AFTER SIZE FIELD 61 MISSING SEMICOLON AFTER DECLARATION 62 MODULE DECLARATION INSIDE MODULE BODY 63 SIZE FIELD ILLEGAL IN LABEL DECLARATION 64 REGISTER TO BE ALLOCATED ALREADY IN USE 65 MISSING EQUAL 1N ROUTINE DECLARATION 66 MISSING OPERATOR 67 NO DECLARATION FOLLOWING BYTE 68 INVALID USE OF POINTER 69 STRUCTURE NAME MUST BE DOTTED IN ITS BODY 70 MUST NOT DOT A FORMAL IN SIZE EXPRESSION 71 MISSING CLOSING BRACKET IN PARAMETER LIST 72 SYMBOL OR LITERAL AFTER CLOSE BRACKET 73 EQUAL SIGN MISSING IN STRUCTURE OR MACRO 74 MISSING ACTUAL PARAMETER LIST 75 MISSING ACTUAL PARAMETER 76 EXTRA ACTUAL PARAMETERS IN STRUCTURE ACCESS 77 MISSING CLOSING BRACKET IN STRUCTURE ACCESS 78 MISSING SEMICOLON IN STRUCTURE ACCESS 79 MISSING ACTUAL PARAMETER LIST DELIMITER 80 INVALID ESCAPE CHARACTER 81 MISSING RIGHT QUOTE 82 MISSING CLOSE PARENTHESIS IN PLIT 83 PLIT DUPLICATION FACTOR NOT LITERAL 84 PLIT ARGUMENT NOT LITERAL AT LOAD TIME 85 ILLEGAL USE OF LONG STRING 86 ROUTINE DECLARED FORWARD IS NOT DEFINED 87 SIZE OF INITIAL VALUE SPECIFICATION EXCEEDS DECLARED SIZE 88 STRING FUNCTION REQUIRES STRING ARGUMENT 89 CSECT DECLARATION ERROR - IGNORED 90 MISSING EQUAL IN LINKAGE DECLARATION 91 NO LINKAGE TYPE SPECIFIED 92 TOO MANY PARAMETERS IN LINKAGE DECLARATION 93 MISSING COMMA IN LINKAGE DECLARATION 94 PARAMETER TYPE NOT STACK OR REGISTER 95 INVALID REGISTER NUMBER 96 CANNOT MAP TRAP LINKAGE TYPE 97 ILLEGAL CHARACTER IN RADIX 50 STRING 100 ATTEMPT TO DIVIDE BY ZERO 101 MISSING SYMBOL IN DECLARATION 103 ILLEGAL UP-LEVEL ADDRESSING 502 REQUIRE FILE NOT FOUND 503 INVALID PPN FORMAT IN REQUIRE 504 REQUIRE DEVICE ERROR 505 REQUIRE DECLARATIONS NESTED MORE THAN 11 LEVELS 511 NOT IMPLEMENTED YET