PL/M-80 Language Summary

PL/M-80 is a programming language for i8080 systems.  It is based most
notable on PL/I.  It has the type of block structure and scope rules
most programmers now expect despite the fact it is a fairly small

The one thing that may "trip-up" may Pascal programmers is that PL/M
(and its PL/I big brother) use semicolon as a terminator, not as a
statement separator.  Semicolons mark the end of every statement.

The remainder of this file summarizes the PL/M-80 language and its
features.  It is only a summary; no attempt is made to provide a
complete and unambiguous description.

PL/M Character Set
Alphabetics:       A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Numerics:          0 1 2 3 4 5 6 7 8 9
Specials:          $ = . / ( ) + - ' * , < > : ;  and space

All other characters are unrecognized by PL/M in the sense that they
are regarded as equivalent to the space character.

PL/M Identifiers
Identifiers may be from 1 to 31 characters in length.  An alphabetic
must be the first character in an identifer name; the remainder may
be alphabetic or numeric.  In addition, dollar signs may be imbedded
within a variable name to improve readability.  They are ignored by
PL/M.  (The identifiers LINE$COUNT and LINECOUNT are interpreted
as identical names.)

The following are all reserved words, and may not be used as
identifier names:

  ADDRESS        DATA           EOF            LABEL          PROCEDURE
  AND            DECLARE        GO             LITERALLY      RETURN
  BASED          DISABLE        GOTO           MINUS          THEN
  BY             DO             HALT           MOD            TO
  BYTE           ELSE           IF             NOT            WHILE
  CALL           ENABLE         INITIAL        OR             XOR
  CASE           END            INTERRUPT      PLUS

PL/M Data Types
There are two data types in PL/M.  The data type BYTE refers to
8-bit data;  ADDRESS, to 16.  It is also possible to construct
arrays of either type and pointers to either type.

PL/M Constants
Numeric constants may be expressed as binary, octal, decimal, and
hexadecimal numbers.  The radix for the number is specified by a
letter appended to the number: B for binary, O and Q for octal,
D for decimal, and H for hexadecimal.  If the letter suffix is
omitted, the number is treated as decimal.  Hexadecimal constants
must begin with a numeric to avoid confusion with identifier names.
As with identifiers, dollar signs may be imbedded in numeric constants
to improve readability.  However a number is expressed, it must be
representable in 16 bits.

Character string constants are enclosed in apostrophes.  An apostrophe
within the string must be doubled.  Character strings are represented
using 7-bit ASCII codes.  Character strings constants of length 1 are
treated as BYTE values; length 2 as ADDRESS values.  Longer strings
are only useful with the "dot" operator.

PL/M Expressions
There are seven arithmetic operators in PL/M.  All perform unsigned
arithmetic operations on either BYTE or ADDRESS values.

     +     Binary addition operator.
     -     Binary subtraction operator, or unary negation.
     PLUS  Binary addition-with-carry operator.
     MINUS Binary subtraction-with-carry operator.
     *     Binary multiplication operator.
     /     Binary division operator.
     MOD   Binary remainder operator.

Multiply and divide always produce ADDRESS results.  The others
produce BYTE results if both operands are BYTE values; ADDRESS,

There are four boolean operators in PL/M.  All perform either 8-bit
or 16-bit boolean operations of their operands.

     NOT   Unary complement operator.
     AND   Binary conjunction operator.
     OR    Binary disjunction operator.
     XOR   Binary exclusive-disjunction operator.

The operators produce BYTE results if both operands are BYTE values.
If at least one is of type ADDRESS, the other is extended with
high-order zeroes if necessary, and the result is type ADDRESS.

There are six relational operators.  All return a true/false result
with 0FFH representing "true" and 00H, "false".

     <     Binary less-than operator.
     <=    Binary less-than-or-equal operator.
     =     Binary equal operator.
     >=    Binary greater-than-or-equal operator.
     >     Binary greater-than operator.
     <>    Binary not-equal operator.

There is one other PL/M operator, the so-called "dot" operator.  It
is a unary operator that returns the memory address of its operand.
The operator may be used in the following forms:

     .(constant, ...)

The construction " .(08H, 'Message', 0DH) " might best be considered
as the address of a nine-element BYTE array.

Expression evaluation obeys operator precedence unless modified by
parenthesis.  The following lists the operators in order of precedence:

     Highest:   .
                *  /  MOD
                +  -  PLUS  MINUS
                <  <=  =  =>  >  <>
     Lowest:    OR  XOR

PL/M Executable Statements
            /* Not really an executable statement, but... */
            variable = expression ;
     -or-   variable, variable, ... = expression ;

Imbedded assignment.  (May be used within an expression.)
            (variable := expression)

Do-End.  (Simple statement grouping.)
               statement; ...;

Do-While.  (Loop while rightmost bit of expression = 1.)
            DO WHILE expression;
               statement; ...;

Iterative Do.
            DO variable = expression1 to expression2;
               statement; ...;

Do-Case.  (Execute i-th statement, numbered from 0.)
            DO CASE expression;

            IF expression THEN statement;

            IF expression THEN statement; ELSE statement;

Go To.  (GO TO and GOTO are synonomous.)
            GO TO label;
     -or-   GO TO number;
     -or-   GO TO variable;
The first form causes a GOTO the statement prefixed with 'label:'.
The latter two forms cause a GOTO an absolute storage location.

Disable interrupts.

Enable interrupts.

PL/M Variable Declarations
Identifiers are defined with the DECLARE statement.  The following
are typical forms for the DECLARE statement.

     Single identifier:      DECLARE identifier type;
     Group of identifiers:   DECLARE (identifier, ...) type;
     Array:                  DECLARE identifier (constant) type;
     Multiple:               DECLARE id type, id type, ...;

Array subscripts start at 0.  Thus, DECLARE A(10) BYTE; defines the
array of elements A(0)...A(9).

Declared variables may have initial values specified by including
the INITIAL attribute after the type on the DECLARE statement:

     DECLARE A(10) BYTE INITIAL(10,11,12,13,14,15,16,17,18,19);

Variables declared with the INITIAL attribute are preset at program
load time.  They are not reset at procedure invocation or anywhere
else.  The INITIAL attribute may specify fewer values then would
be needed for the declared variables.

A DATA attribute is available for declaring storage constants.  No
type or array sizes are specified; BYTE is assumed and the array
size is implicitly determined from the DATA value.  The values of
identifiers declared as DATA must not be changed during program

     DECLARE GREETINGS DATA ('Hello, world.');

PL/M also supports a limited macro facility.  Identifiers may be
declared with the LITERALLY attribute.  The literal value is
substituted in the program source text where ever the identifier is

      . . .

     Variables may be declared as BASED, as in

             A BASED A$PTR BYTE;

In this example, the memory location associated with variable A is
determined by the address stored in variable A$PTR.

Labels are declared using LABEL for the type.  An identifier so
declared should also appear before an executable statement, separated
from the statement by a colon.  (It is often not strictly necessary
to declare all labels.  An implicit DECLARE results when an otherwise
undeclared label is encountered in the program.  That is,


is equivalent to


However, due to scope rules, a earlier reference to the label (in a
GOTO statement) may be flagged in error, because the implicit DECLARE
is physically latter in the program.

PL/M Procedure Declarations
Procedures must be defined before they are used.  This declaration
form is:

     identifier: PROCEDURE (arg, ...) type;
        statement; ...;
     END identifier;

The 'identifier' (which appears in two places) specifies the name for
the procedure.  If no result is returned, the 'type' is omitted from
the PROCEDURE statement.

Return from a procedure is implicit after the last statement of the
procedure, although no value is returned in this case.  Return may be
explicitly specified with the RETURN statement:

     No value:     RETURN ;
     Value:        RETURN expression ;

Procedures may be declared with the special type INTERRUPT followed
by a number in the range 0 through 7.  Such a procedure will be used
as an interrupt handler for the corresponding RST instruction.
Interrupts are re-enabled on return from an interrupt procedure.

Procedures may not be recursive.  Procedures are invoked either with
the CALL statement, or within an expression.

     Stand-alone:            CALL identifier (arg, ...);
     Within expressions:     identifier (arg, ...)

Built-in Procedures
     Returns a BYTE value from the I/O port specified by 'number'.

OUTPUT(number) = expression;
     Sends the BYTE value of 'expression' to the I/O port specified
     by 'number'.

     Returns the number of elements in the array 'identifier'.

     Returns the highest subscript for array 'identifier'.  Note that
     LAST = LENGTH - 1.

     Returns the low-order byte of 'expression'.

     Returns the high-order byte of 'expression'.

     Returns an ADDRESS value equivalent to 'expression'.  High-order
     zeroes are used to pad BYTE expressions.

ROL(expr1, expr2)  and  ROR(expr1, expr2)
     Returns the value of 'expr1' rotated left/right the number of bits
     specified by 'expr2'.  Both expressions must be BYTE values.  The
     value of 'expr2' must not be zero.

SCL(expr1, expr2)  and  SCR(expr1, expr2)
     Returns the value of 'expr1' rotated left/right the number of bits
     specified by 'expr2'.  The carry flag participates in the rotate.
     'expr2' must be a BYTE value; 'expr1' may be BYTE or ADDRESS.  The
     value returned is of the same type as 'expr1'.  The value of
     'expr2' must not be zero.

SHL(expr1, expr2)  and  SHR(expr1, expr2)
     Returns the value of 'expr1' shifted left/right the number of bits
     specified by 'expr2'.  The last bit shifted out ends up in the
     carry flag.  'expr2' must be a BYTE value; 'expr1' may be BYTE or
     ADDRESS.  The value returned is of the same type as 'expr1'.  The
     value of 'expr2' must not be zero.

CALL TIME(expression)
     The expression is evaluated as a BYTE value.  The TIME procedure
     delays 100 microseconds times the value.  (Timing is based on
     instruction execution times for the standard i8080 cpu.)

DEC(expr1 + expr2)  and  DEC(expr1 PLUS expr2)
     The two expressions must be unsubscripted variables, constants,
     or expressions that represent BCD values.  The DEC function does
     the necessary decimal adjustment to produce the BCD result from
     the addition.

Pre-defined Variables
     The values of these variables reflect the current values of the
     cpu flags.

     The MEMORY variable is assigned the to the first memory location
     following the PL/M program.  It is useful for determining where
     free memory begins.

     The STACKPTR variable's value reflects the current value of the
     SP register.  The variable may be assigned a new value to alter
     the stack register.