-*-Text-*-

This file describes the LISP compiler, COMPLR.

It could use more work; many nodes are too long, many others refer
only to the Multics implementation, and some are completely
incomprehensible.

This file put into INFO format by David Eppstein <KRONJ at MC>.

File: LISPC,		Node: Top,		Up: (DIR)

   LISP programs can be compiled into machine code.  This
representation of a program is more compact than the interpreted
list-structure representation, and it can be executed much more
quickly.  However, a price must be paid for these benefits.  It is not
as easy to intervene in the execution of compiled programs as it is
with interpreted programs.  Thus most LISP programs should not be
compiled until after they have been debugged.

   In addition, not all LISP programs can be compiled.  There are
certain things which can be done with the interpreter that cannot be
effectively compiled.  These include indiscriminate use of the
functions eval and apply, especially with pdl-pointer arguments;
"nonlocal" use of the go and return functions; and functions which
modify their own code.  Also, there are a number of functions which
detect illegal arguments when they are called interpretively but not
when a call to them is compiled; erroneous compiled programs can
therefore damage the LISP environment and can cause strange errors to
occur -- be forewarned.  However, most "normal" programs are
compilable.

* Menu:

* Peculiarities::
* Declare::
* Running Functions::
* Running the Compiler::
* The Lisp Assembly Program: LAP
* Internal Implementation Details: Details
* Other Languages::

Node: Peculiarities,		Up: Top,	Next: Declare

   Some operations are compiled in such a way that they will behave
somewhat differently from the way they did when they were interpreted.
It is sometimes necessary to make a "declaration" in order to obtain
the desired behavior.  This is explained on .

* Menu:

* Vars::
* InLine Coding::
* Function Calling::
* Input::
* Output::
* Functions::

Node: Vars,	Up: Peculiarities,	Next: InLine Coding

   In the interpreter "variables" are implemented as atomic symbols
which possess "shallow-bound" value cells.  The continual manipulation
of value cells would decrease the efficiency of compiled code, so the
compiler defines two types of variables: "special variables" and
"local variables."  Special variables are identical to variables in
the interpreter.

   Local variables are more like the variables in commonly-used
algebraic programming languages such as Algol or PL/I.  A local
variable no longer has an associated atomic symbol; thus it can only
be referred to from the text of the same function that bound it.  The
compiler creates local variables for prog- variables, do-variables,
and lambda-variables, unless directed otherwise.  The compiled code
stores local variables in machine registers or in locations within a
stack.

   The principal difference between local variables and special
variables is in the way a binding of a variable is compiled.  (A
binding has to be compiled when a prog-, do-, or lambda-expression is
compiled, and for the entry to a function which has lambda-variables
to be bound to its arguments.) If the variable to be bound has been
declared to be special, the binding is compiled as code to imitate the
way the interpreter binds variables: the value of the atomic symbol is
saved and a new value is stored into its value cell.  If the variable
to be bound has not been declared special, the binding is compiled as
the declaration of a new local variable.  Code is generated to store
the value to which the variable is to be bound into the register or
stack-location assigned to the new local variable.  This runs
considerably faster than a special binding.

   Although a special variable is associated with an atomic symbol
which has the name of the variable, the name of a local variable
appears only in the input file - in compiled code there is no
connection between local variables and atomic symbols.  Because this
is so, a local variable in one function may not be used as a "free
variable" in another function since there is no way for the location
of the variable to be communicated between the two functions.

   When the usage of a variable in a program to be compiled does not
conform to these rules, i.e. it is somewhere used as a "free
variable," the variable must be declared special.  There are two
common cases in which this occurs.  One is where a "global" variable
is being used, i.e. a variable which is setq'ed by many functions but
is never bound.  The other is where two functions cooperate, one
binding a variable and then calling the other one which uses that
variable as a free variable.

   Certain variables built into the LISP system, such as IBASE and
ERRSET, are automatically assumed by the compiler to be special unless
declared otherwise.  This exception to the general rule is predicated
on the assumption that the when the user sets, for example, the value
of IBASE, he intends to affect the operation of the READ function.

Node: InLine Coding, Up: Peculiarities, Next: Function Calling, Previous: Vars

   Another difference between the compiler and the interpreter is
"in-line coding," also called "open coding." When a form such as (and
(foo x) (bar)) is evaluated by the interpreter, the built-in function
and is called and it performs the desired operation.  But to compile
this form as a call to the function and with list-structured arguments
derived from (foo x) and (bar) would negate much of the advantage of
compiling.  Instead the compiler recognizes and as part of the LISP
language and compiles machine code to carry out the intent of (and
(foo x) (bar)) without actually calling the and function.  This code
might look like:

                pick up value of variable x
                call function foo
                is the result nil?
                if yes, the value of the and is nil
                if no, call the function bar
                the result of the and is what bar returned.

This "in-line coding" is done for all special forms (cond, prog, and,
errset, setq, etc.); thus compiled code will usually not call any of
the built-in fsubrs.

   Another difference between the compiler and the interpreter has to
do with arithmetic operations.  Most computers on which MACLISP is
implemented have special instructions for performing all the common
arithmetic operations.  The MACLISP compiler contains a "number
compiler" feature which allows the LISP arithmetic functions to be
"in-line coded" using these instructions.

   A problem arises here because of the generality of the MACLISP
arithmetic functions, such as plus, which are equally at home with
fixnums, flonums, and bignums.  Most present-day computers are not
this versatile in their arithmetic instructions, which would preclude
open-coding of plus.  There are several ways out of this problem.  One
is to use the special purpose functions which only work with one kind
of number.  For example, if you are using plus but actually you are
only working with fixnums, use + instead.  The compiler can compile (+
a b c) to use the machine's fixnum-addition instruction.  A second
solution is to write (plus a b (foo c)) but tell the compiler that the
values of the variables a and b, and the result of the function foo
can never be anything but fixnums.  This is done by means of the
"number declarations" which are described on .  A third way is to use
the FIXSW and FLOSW declarations.  Note that when interpreted (plus a
b (foo c)) can legitimately produce a bignum even though all three
numbers added are fixnums, but the open-compiled code will not check
for overflow and will simply lose high-order bits in such cases.  This
is true no matter how you cause the expression to be open-coded.

   Another problem that can arise in connection with the in-line
coding of arithmetic operations is that the LISP representation of
numbers and the machine representation of numbers may not be the same.
Of course, this depends on the particular implementation.  If these
two representations are different, the compiler would store variables
which were local and declared to be numeric-only in the machine form
rather than the LISP form.  This could result in compilation of poor
code which frequently converts number representations and in various
other problems.  Compilers which have this problem provide a (closed
t) declaration which inhibits open coding of arithmetic operations.

Node: Function Calling,	Up: Peculiarities, Next: Input,	Previous: InLine Coding

   Another property of compiled code that should be understood is the
way functions are called.  In the interpreter function calling
consists of searching the property list of the called function for a
functional property (if it is an atomic symbol) and then recursively
evaluating the body of the function if it is an expr, or transferring
control to the function if it is a subr.  In compiled code function
calling is designed according to the belief that most of the functions
called by compiled code will be machine executable, i.e. "subrs":
other compiled functions, or built-in functions, and only infrequently
will compiled code call an interpreted function.  Therefore a calling
mechanism is used which provides for efficient transfer between
machine-executable functions without constant searching of property
lists.  This mechanism is called the "uuo link" mechanism for
historical reasons.

   When a compiled function is first loaded into the environment, it
has a uuo link for each function it will call.  This uuo link contains
information indicating that it is "unsnapped" and giving the name of
the function to be called, which is an atomic symbol.  The first time
a call is made through such a uuo link, the fact that it is
"unsnapped" is recognized and a special linking routine is entered.
This routine searches the property list of the function to be called,
looking for a functional property (or an autoload property) in just
the same way as the interpreter would.  If the function turns out to
be an expr, or is undefined, the interpreter is used to apply the
function and the result is given back to the compiled code.  The link
is left "unsnapped" so that every time this function is called the
interpreter will be invoked to interpret its definition.  If, however,
the function being called is machine executable (a subr), the link is
"snapped."  Exactly what this means is implementation dependent but
the effect is that from then on whenever a call is made through this
uuo link, control will be transferred directly to the called function
using the subroutine-calling instruction of the machine, and neither
the linking routine nor the interpreter will be called.

   There is a flag which can be set so that links will not be snapped
even if they go to a function which is machine executable.  This flag
is the value of the atomic symbol nouuo.  (See .) There is also a
function, (sstatus uuolinks), which unsnaps all the links in the
environment.  These facilities are used in circumstances such as when
a compiled function is redefined, or compiled code is being traced or
otherwise debugged.

   In the pdp-10 implementation a uuo link is implemented as an
instruction which is executed when a call is to be made through the
link.  An "unsnapped" link consists of a special instruction, "UUO",
which causes the LISP linking routine in the interpreter to be called.
The address field of the uuo points to the atomic symbol which names
the function to be called.  The operation code and accumulator fields
indicate the type of call and number of arguments.  When the link is
snapped the UUO instruction is replaced with a "PUSHJ" instruction,
which is the machine instruction for calling subroutines.

   In the Multics implementation, a uuo link is implemented as a
pointer.  To call through this link a "tspbp" instruction indirect
through the pointer is used.  An unsnapped link points at the linking
subroutine and various fields in the pointer, left unused by the
machine, indicate the type of call, number of arguments, and the
atomic symbol which names the function.  When the link is snapped the
pointer is changed to point at the first instruction of the called
function.

   Before a function can be used it must be made known in the LISP
environment.  Interpreted functions are made known simply by putting a
functional property on the property list of the atomic symbol which
names the function.  This is usually done using the built-in function
defun.  Compiled functions must be made known by a more complex
mechanism known as "loading," because of the complexity of the support
mechanisms needed to make compiled functions execute efficiently.  In
some dialects of LISP the compiler automatically makes the compiled
functions known, but in MACLISP the compiler creates a file in the
file system of the host operating system, and this file has to be
loaded before the compiled function can be called.  In the pdp-10
implementation this file is called a "fasl file."  In the Multics
implementation it is called an "object segment."  Loading is described
in detail on .

Node: Input,   Up: Peculiarities,   Next: Output,   Previous: Function Calling

   The input to the compiler consists of an ascii file containing a
number of S-expressions.  The format of this file is such that it
could be read into a LISP environment using a function such as load or
uread, and then the functions defined in this file would be executed
interpretively.

   When a file is compiled, the compiler reads successive
S-expressions from the file and processes them.  Each is classified as
a function definition, a "declare-form", a "macro-form", or a "random
form" according to what type of object it is and according to its car
if it is a list.

   A function definition is a form whose car is one of the atoms
defun, defprop.  When the compiler encounters a function definition if
it defines a macro the macro is defined for use at compile time.  If
it defines an expr or a fexpr, the compiler translates the definition
from LISP to machine code and outputs it into the "fasl file" or
"object segment" which is the output from the compiler.  If it defines
some other property, it is treated as a random form.

   A macro form is any form whose car has previously been defined to
be a macro.  When a macro form is read from the input file, the
compiler will apply the macro and then process the result as if it had
been read from the input file.  Thus if foo is a macro which expands
(foo a b c) into (defun a ...), the resulting function definition will
be compiled.

   A declare-form is a form whose car is the atom declare.  It is
ignored by the interpreter because there is an fsubr called declare in
MACLISP which does nothing.

   A progn form is any form which starts out as (progn 'compile...
The compiler processes each of the remaining elements of the progn
form as if it had been encountered at top level in the file.  Progn
forms are useful for macros (and read macro characters) which are to
expand into a function definition and a declare form.  For example,
one might define a macro defloat such that (defloat f (a b) ...)
expanded into I don't know pub well enough.  Note that the (progn
'compile ...) will be processed correctly by the interpreter also.

   Forms which are calls to the functions include or %include are
simply evaluated when they are encountered.  This causes the contents
of the specified file to be included in the compilation at that point,
just as when interpreting such a form would cause the contents of the
file to be loaded at that point.  See the description of %include,
below.

   A random form is anything read from the input file that is not one
of the special types of forms described above.  It is simply copied
into the output file of the compiler in such a way that when that file
is loaded it will be evaluated.

Node: Output,	Up: Peculiarities,	Next: Functions,	Previous: Input

   The output of the compiler normally consists of error and warning
messages on the terminal, and a file of machine code which can be
loaded into a lisp environment with load or fasload.  In the pdp-10
implementation it is also possible to get a "lap file." This is a file
which contains machine code in symbolic form.

   In the Multics implementation the compiler produces a standard
object segment with a translator name of "lisp" and a symbol section
which contains the information used by load to define functions, set
up constants needed by the compiled code, set up "uuo links", etc.

   When the object segment is "loaded", it is not copied into the lisp
environment.  Instead a "linkage block" is set up in the environment
and initialized according to directives in the segment's symbol
section.  This block includes the reference name of the object segment
and a pointer to it.  Thus compiled code is automatically shared
between multiple users in the Multics implementation.  However, list
structure constants used by the compiled code can never be shared.

   In the pdp-10 implementation the output of the compiler is a "fasl
file."  This file begins with a header identifying it as a fasl file
and indicating what version of lisp it was produced for.  (This is
used to detect incompatibilities.)  The rest of the file consists of a
series of directives to load and relocate code words, set up
list-structure constants, reference value cells of symbols, evaluate
random S-expressions, etc.

   fasload operates by reading through the file, storing code in
lisp's binary program space, and generating the necessary LISP objects
for constants used by the compiled code.  Normally none of this is
shared between users, but see for information on how to make it pure
and shared.

   There is a function defined in the compiler, coutput, which can be
used to put a random S-expression into the output file.  When the file
is loaded, this S-expression will be evaluated.  This can be used to
print the version number of the program, initialize its data base,
etc.  It cannot be used to fool around with obarrays because of the
way the loader handles atomic symbols.  For efficiency, it creates a
table of all the atoms needed by the file being loaded, and creates
and interns them all just once.  This makes loading much faster, but
means that everything in a file has to go on the same obarray.

   The coutput function usually does not have to be used, since the
compiler coutputs any "random form" it reads from the input file.  It
is provided for the benefit of certain classes of hairy macros.

Node: Functions,	Up: Peculiarities,	Previous: Output

Functions Connected with the Compiler:


declare             FSUBR

        In the interpreter, declare is like comment.  In the compiler,
     the arguments are evaluated at compile time.  This is used to
     make declarations, to gobble up input needed only in the
     interpreter, or to print messages at compile time.

           Examples:

           (declare (special x y) (*fexpr f00))

           (declare (read)) ;in compiler, gobble next S-expression.
           (something-needed-only-in-the-interpreter)

           (declare (princ '|Now compiling foobar|))


%include            FSUBR

        (%include name) is used to cause an "include file" to be
     included in the input to the compiler.  It works in the
     interpreter also, causing the specified file to be inpush'ed.
     name may be a string or an atomic symbol.  The translator search
     rules are used.

        In the PDP-10 implementation, this function is called include
     (no "%").  name may also be a namelist.


           (defun include fexpr (x)
                  (prog (file)
                        (setq file (open (car x)))
                        (eoffn file '+internal-include-eoffn)
                        (inpush file)))

           (defun +internal-include-eoffn (f v) nil)

Node: Declare,  Up: Top,  Next: Running Functions,  Previous: Peculiarities

   It is often necessary to supply information to the compiler beyond
the definition of a function with defun, in order to compile the
function, although the definition is all that the interpreter needs in
order to interpret the function.  This information can be supplied
through declarations.

   A declare form is a list whose first element is the atom declare
and whose remaining elements are forms called "declarations."  The
compiler processes a declare form by evaluating each of the
declarations, at compile time.  Usually the declarations call on one
of the declaration functions which the compiler provides.  These are
described below.  However, it is permissible for a declaration to be
any evaluable form, and it is permissible for a declaration to read
from the input file by using the read function.  This may be used to
prevent the compiler from seeing certain portions of the input which
are only needed when a program is run interpretively.  Prefixing a
form in the input file with (declare (eval (read))) would cause it to
be evaluated at compile time if the file was compiled, but evaluated
at read-in time if the file was interpreted.  Arbitrarily complex
compile-time processing may be achieved by the combination of
declarations and macros.

   A few declarations may be used locally.  If a declare form occurs
as the first form of a lambda-, prog- or do- body, it is processed by
the compiler before the compilation of the rest of the body, but is
effective only during the compilation of that body.  The declarations
which may be used locally are special, fixnum, flonum, and notype, and
they may apply only to variables bound in that lambda, prog or do, or
to global variables.

      Example:

      (defun factorial (n)
             (declare (fixnum n))
             (do ((m n (- m 1))
                  (a 1 (* m a)))
                 ((= m 0) a)
                 (declare (fixnum m a))))

   The remainder of this section describes the declaration functions
provided by the compiler.  Note that if a declaration function
described below is of the form (foo t), its effect can be reversed by
using the form (foo nil).

(special var1 var2 ... )
     Declares var1, var2, etc. to be special variables.

(unspecial var1 var2 ... )
     Declares var1, var2, etc. to be local variables.

(*expr fcn1 fcn2 ... )
     Declares that fcn1, fcn2, etc.  are expr- or subr-type functions
     that will be called.  This declaration is generally supplied by
     default by the compiler, but in some peculiar circumstances it is
     required to tell the compiler what is going on when the same
     symbol is used as both a function and a variable.  It is good
     practice to put *expr, *lexpr, and *fexpr declarations for all
     the functions defined in a file near the beginning of that file.

(*lexpr fcn1 fcn2 ... )
     Declares fcn1, fcn2, etc. to be lexpr- or lsubr-type functions
     that will be called.  This declaration is required for
     non-builtin functions unless the functions are defined in the
     file being compiled and are not referenced by any functions that
     are defined before they are.

(*fexpr fcn1 fcn2 ... )
     Declares fcn1, fcn2, etc. to be fexpr- or fsubr-type functions
     that will be called.  This declaration is required for
     non-builtin functions unless the functions are defined in the
     file being compiled and are not referenced by any functions that
     are defined before they are.

(**array arr1 arr2 ... )
     Declares arr1, arr2, etc.  to be arrays that will be referred to.
     This declaration is obsolete and is being phased out.  Use array*
     instead.  See the note under *expr.

(array* (type arr1 n1 arr2 n2 ... ) ... )
     Is used to declare arrays arr1, arr2, etc.  type may be fixnum,
     flonum, or notype; it indicates what type of objects will be
     contained in the arrays.  n1, n2, etc.  are the number of
     dimensions in arr1, arr2, etc.  respectively.  The extended form
     (array* (type (arr1 dim1.1 dim1.2 ...  dim1.n) ...))  is
     preferred if the dimensions are known at compile-time.  The
     dimensions declared must be either fixnums, or nil or ?, which
     indicate a dimension not known at compile time.  If dimensions
     are declared, the compiler can generate faster code.

     The array* declaration causes the compiler to generate in-line
     code for accesses of and stores into the arrays declared.  This
     code is somewhat faster than the usual subroutine-call array
     accessing.  The compiler will also generate in-line code if the
     arraycall function is used; in this case the array must be named
     by an array-pointer value rather than by an atomic symbol.  Note
     that arrays declared by array* are arrays of fixed name, not
     variables whose values are array pointers.

(fixnum var1 var2 ... )
     Declares var1, var2, etc. to be variables whose values will
     always be fixnums.

(fixnum (fcn type1 type2 ... ) ... )
     Declares fcn to be a function which always returns a fixnum
     result.  Also the types of the arguments may be declared as
     type1, type2, etc.  An argument type may be fixnum, meaning the
     argument must be a fixnum; flonum, meaning the argument must be a
     flonum; or notype, meaning the argument may be of any type.

     The two types of fixnum declarations may be intermixed, for
     example (fixnum x (foo fixnum) y).  Note, however, that
     functions, unlike variables, may not be declared locally.

(flonum var1 var2 ... (fcn type1 ... ) ... )
     Is the same as the fixnum declaration except the variables or
     function- results are declared to always be flonums.

(notype var1 var2 ... (fcn type1 ... ) ... )
     Is the same as the fixnum declaration except the variables or
     function- results are declared not to be of any specific type.

(fixsw t)
     Causes the compiler to assume that all arithmetic is to be done
     with fixnums exclusively, except that obviously functions such as
     +$ and cos will still use flonums.

(fixsw nil)
     Turns off the above.

(flosw t)
     Causes the compiler to assume that all arithmetic is to be done
     with flonums exclusively, except that obviously functions such as
     + and rot will still use fixnums.

(flosw nil)
     Turns off the above.

     fixsw and flosw are variables; hence, (setq fixsw t) is
     equivalent as a declaration to (fixsw t).

(setq special t)
     Causes all variables to be special.  Note: (special t) does not
     do the same thing!.
[Annotation by REM, 1979 Sept 13 -- That is nonsense.  The correct declaration
 is (setq specials t).  Note the plural form!  With two differences it should
 be obvious that this declaration makes everything special whereas (special t)
 merely makes T special.]

(setq nfunvars t)
     Causes the compiler to disallow functional variables.  All
     symbols in function position in a form are assumed to have a
     functional property at run time.  The case of a symbol whose
     value is a functional form is disallowed.

(macros t)
     Causes macro definitions to be retained at run time.  Normally,
     macro definitions in files being compiled are used for
     compilation purposes only.  This declaration causes them to be
     written into the output file as well, so that loading it will
     define the macros at run time.

(macros nil)
     Causes macros to be used only at compile time.  This is the
     default choice.

(genprefix foo)
     Causes auxiliary functions generated by the compiler (for
     instance when function is used) to be named foon, where n is a
     number incremented by 1 each time such a function is generated.
     The genprefix declaration is used when several separately
     compiled files are to be loaded together, in order to avoid name
     clashes.  In the PDP-10 implementation, the genprefix is
     initialized as a function of the directory and first name of the
     main input file.  On ITS, generated names are thus of the form
     "USER;FOO-43", while on TOPS-10 they are of the form
     "[10,7]FOO-43".


   The following declarations are useful only in the pdp-10
implementation; however, the Multics implementation will accept them
and ignore those which are irrelevant.

(mapex t)
     In the pdp-10 implementation, causes all map-type functions to be
     open- coded as do loops.  (This is always done in the Multics
     implementation.)  The resulting code is somewhat larger than
     otherwise, but also somewhat faster.

(mapex nil)
     Causes map-type functions to actually be called.  This is the default.

(messioc chars)
     Causes an (ioc chars) to be done just before printing out each
     error message.  In this way one may direct error messages to the
     LAP file instead of to the terminal on the pdp-10.

     The default messioc is vr which puts the messages in both places.

(muzzled t)
     Prevents the pdp-10 fast-arithmetic compiler from printing out a
     message every time closed compilation of arithmetic is forced.

(muzzled nil)
     Causes the compiler to print a message when closed-compilation is
     forced.  This is the default.

(symbols t)
     Causes the compiler to output LAP directives so that the LAP
     assembler will attempt to pass assembly symbols to DDT for
     debugging purposes.

(symbols nil)
     Does not generate debugging symbols.  This is the default.

(closed t)
     Causes arithmetic operations to be close-compiled, that is, the
     function + will generate in-line code but the function plus will
     not in any circumstances.  This declaration is necessary if you
     apply plus to two fixnums and want a bignum result if the
     operation overflows.

(closed nil)
     Causes the compiler to produce code that assumes overflow will
     not occur, which may give incorrect results in the above case.
     When the compiler can determine, by declaration or implication,
     that all of the operands to an arithmetic function are fixnums
     (or flonums), it will generate code to use the hardware fixnum
     (or flonum) instructions.  This is the default state.


   This declaration only exists in the Multics implementation.

(defpl1 ...)
     Defines an interfacing function which may be used to call
     programs written in other languages, such as PL/I.  See for
     details.

Node: Running Functions, Up: Top, Next: Running the Compiler, Previous: Declare

   After a file of functions has been compiled, those functions can be
loaded into an environment and then used.  They can be loaded either
by using the load or fasload functions described below, or by using
the autoload feature described in section 12.4.4.


The following function is at present available only in the Multics
implementation.


load                SUBR 1 arg

        (load x), where x is a file specification acceptable by openi,
     i.e. a namestring or a namelist, causes the specified file to be
     loaded into the environment.  The file may be either a source
     file or a compiled file (called a "fasl" file in the ITS
     implementation and an object segment in the Multics
     implementation.) load determines which type of file it is and
     acts accordingly.  A source file is loaded by openi'ing and
     inpush'ing it.  A read-eval loop is then executed until the end
     of the file is reached.  An object file is loaded by reading it,
     defining functions as directed by specifications inserted in the
     file by the compiler.


fasload             FSUBR

        fasload takes the same arguments as uread.  It causes a file
     of compiled functions, called a "fasl" file in some
     implementations, to be loaded in.  Example:

                    (fasload foo fasl dsk macsym)


The following function only exists in the Multics implementation.


defsubr             LSUBR 3 to 7 args

        defsubr is the function used to define new machine code
     functions.  It defines various types of functions, depending on
     its arguments.  The way to define a subr written in PL/I is

                (defsubr "segname" "entryname" nargs)

     which defines segname$entryname as a subr expecting nargs
     arguments.  The value returned is a pointer which can be
     putprop'ed under the subr property or the fsubr property.  The
     way to define an lsubr written in PL/I is

        (defsubr "segname" "entryname" nargs2*1000+nargs1 -2)

     which defines segname$entryname as an lsubr allowing from nargs1
     to nargs2 arguments.  The 1000 is octal.  The value returned
     should be putprop'ed under the lsubr property.

           Examples:
               (putprop 'mysubr (defsubr "myfuns" "mysubr" 1) 'subr)
               (putprop 'myfsubr (defsubr "myfuns" "myfsubr" 0) 'fsubr)
               (putprop 'mylsubr
                   (defsubr "myfuns" "mylsubr" 2001 -2) 'lsubr)

        A function defined in this way receives its arguments and
     returns its value on the marked pdl, which may be accessed
     through the external static pointer

                     lisp_static_vars_$stack_ptr

     See for details on how to access the arguments, and on the
     internal format of LISP data.  lisp_static_vars_$nil and
     lisp_static_vars_$t_atom are fixed bin(71) external static; they
     contain nil and t.

Node: Running the Compiler, Up: Top, Next: LAP, Previous: Running Functions

* Menu:

* The Multics Compiler::
* The ITS Compiler::

Node: The Multics Compiler,		Up: Running the Compiler

   The compiler is invoked by the lisp_compiler command to Multics.
This command can be abbreviated lcp.  The arguments to the command are
the pathname of the input file and options.  The compiler appends
".lisp" to the given pathname unless it is preceded by the -pathname
or -pn option.  The output object segment is created in the working
directory with a name which is the first component of the name of the
input file.  For example, the command

                           lcp dir>foo.bar

reads the file "dir>foo.bar.lisp" and produces an object segment named
"foo" in the working directory.

   Usually no options need be supplied, since there are defaults.  The
options available are:

-pathname -pn -p
     Causes the following argument to be taken as the exact pathname
     of the input file, even if it begins with a minus sign.  ".lisp"
     will not be appended.

-eval
     Causes the following argument to be evaluated by LISP.  For example,
              lisp_compiler foo -eval "(special x y z)"

-time -times -tm
     As each function is compiled, its name and the time taken to
     compile it will be typed out.

-total_time -total -tt
     At the end of the compilation, metering information will be typed
     out.

-nowarn -nw
     Suppresses the typing of warning messages.  Error messages of a
     severity greater than "warning" will still be typed.

-macros -mc
     Equivalent to the (macros t) declaration: Causes macro
     definitions to be retained at run time.

-all_special
     Causes all variables to be made special.  Equivalent to the (setq
     special t) declaration.

-genprefix -gnp -gp
     Takes the following argument as the prefix for names of auxiliary
     functions automatically generated by the compiler.  Equivalent to
     the genprefix declaration.

-check -ck
     Causes only the first pass of the compiler to be run.  The input
     file is checked for errors but no code is generated and no object
     segment is produced.

-ioc
     If the following argument is x, (ioc x) is evaluated.  The main
     use for this "-ioc d" which turns on garbage-collection messages
     during compilation.

-list -ls
     Causes a listing file to be created in the working directory,
     containing a copy of the source file and a table of functions
     defined and referenced.  If the object segment is named "name",
     the listing file will be named "name.list".

-long -lg
     Causes the listing file to also contain an assembly language
     listing, with commentary, of the generated code.

-no_compile -ncp
     Causes the compiler not to attempt to compile the file.  Instead
     the input file is simply treated as being composed entirely of
     random forms.  It is digested into a form which can be processed
     quickly by the load function.

Node: The ITS Compiler,		Up: Running the Compiler

   The ITS compiler is presently in an anomalous state.  There are two
versions, COMPLR and NCOMPLR.  NCOMPLR contains the fast-arithmetic
facilities described here.  COMPLR is an older version which will soon
go away.  At that time, NCOMPLR will be renamed to COMPLR.  This
documentation uses the name COMPLR to refer to what is now NCOMPLR, so
it is presently inaccurate but will become accurate in the future.

   Invoke the compiler with the :COMPLR command.  The compiler will
announce itself, print an underscore or backarrow, and accept a
command line, which should be of the standard form

               <output file> _ <input file> (switches)

The file specifications should be standard ITS file names, e.g.
DEV:DIRNAM;FNAME1 FNAME2.  If it is necessary to get a "funny"
character such as _ into the file name, it may be quoted with a slash.

   The compiler normally processes a file of LISP functions and
produces a so- called "LAP file", containing S-expressions denoting
pdp-10 machine-language instructions, suitable for use with LAP (the
Lisp Assembly Program).  However, one may direct the compiler instead
to produce a binary object file, called a "FASL FILE", suitable for
use with the fasload function or the autoload feature.  A third option
is to process a previously generated file of LAP code to produce a
FASL file.  This is especially useful in the case where
special-purpose functions have been hand-coded in LAP.

   If one specifies only an input file name, say FOO BAR, then by
default the name of a generated LAP file will be FOO LAP, and of a
FASL file, FOO FASL.


   COMPLR will accept a "Job Command Line" if desired; simply type

                      :COMPLR <command line><cr>

In this mode COMPLR will automatically proceed itself and run without
the TTY, and kill itself when done.

* Menu:

* Exiting to LISP::
* Switches::

Node: Exiting to LISP,   Up: The ITS Compiler,   Next: Switches

   It may be desirable to execute some LISP functions in the compiler
before actually compiling a file.  Typing ctrl/G will cause the
compiler to announce itself and then type an asterisk; you will then
be at lisp's top level.  To make the compiler accept a command line,
say (maklap) or type ctrl/^.  One useful function for debugging and
snooping around is cl; (cl foo) will compile the function foo, which
should be defined in the compiler's lisp environment, and print LAP
code onto whatever device(s) are open for output.

Node: Switches,   Up: The ITS Compiler,   Previous: Exiting to LISP

   The various modes of operation of the compiler may be controlled by
specifying various switches, which are single letters, inside
parentheses at the end of the command line.  A switch may be turned
off by preceding the switch letter with a minus sign.  Extraneous or
invalid switches are ignored.  Initially all switches are off (the use
of minus sign described above is provided in case the compiler is used
for several files in succession).

   The most commonly-used switch setting is "(FK)", which causes a
FASL file to be produced.

   Most of the switches correspond to values of atomic symbols within
the compiler.  These are noted in parentheses.

   The switches are:

A     (assemble) The specified input file contains LAP code which is
      to be made into a binary FASL file.

D     Disown.  Causes the compiler to disown itself after it has started
      running.  This is the safest way to disown a COMPLR, because the
      compiler will know that it can't try to get any information from
      DDT.

F     (fasl) Accept a file of LISP functions, produce a LAP file,
      and then assemble the LAP file into a FASL file.  This is
      probably the most useful mode.  With the K switch the LAP file
      is not actually produced at all; the lap code is sent directly
      to faslap as the compiler generates it.

K     (nolap) Kill LAP file.  Delete the LAP file after assembly.
      Usually used in conjunction with the F switch.

Mo(in)(macros) Equivalent to (declare (macros t)).  Causes macro
      definitions to be defined at run time as well as at compile
      time.

N     (noargs) No args properties.  Equivalent to (declare (noargs t)).
      Normally the compiler outputs information in the LAP code
      as to how many arguments each function requires, so that args
      properties may be created on the appropriate atomic symbols at
      load time.  In some implementations these properties occupy a
      significant amount of list space; thus it may be desirable to
      eliminate these properties.

S     (special) Equivalent to (declare (setq special t)).  Causes
      all variables to be considered special.

T     (ttynotes) Causes the compiler to print a note on the user's
      terminal as each function is compiled or assembled.  This switch
      is normally off so that a COMPLR may be proceeded and allowed to
      run without the TTY.  In any case error messages will be printed
      out on the terminal.

Uo(in)(unfaslcomments) Useful only in conjunction with the F or
      A switch.  Causes the assembler to output comment messages into
      a file whose second file name is UNFASL.  (Actually, this file
      is always created, and error comments will be directed into this
      file also if messioc so specifies; but the file is immediately
      deleted if it contains nothing significant.)  These comment
      messages describe the size of each function assembled, and give
      other random information also.

V     (nfunvarsf1) Equivalent to (declare (nfunvars t)); disallows
      functional variables.

W     (muzzled) (i.e.  Whisper).  Equivalent to (declare (muzzled t)).
      Prevents the fast-arithmetic compiler from printing out a
      message when closed compilation of arithmetic is forced.

X     (mapex) Equivalent to (declare (mapex t)).  Causes all
      map-type functions to be open-coded as do loops.  The resulting
      code is somewhat larger, but also somewhat faster.

Z     (symbols) Equivalent to (declare (symbols t)).  Causes the
      compiler to output a special directive in the LAP code so that
      the LAP assembler will attempt to pass assembly symbols to DDT
      for debugging purposes.  Primarily of use to machine language
      hackers.

Node: LAP,    Up: Top,    Next: Details,     Previous: Running the Compiler

   MACLISP includes a facility by which machine-language programs can
be defined as LISP functions.  This can be used to gain direct access
to the hardware or the operating system, and may also be used by the
compiler.

   The Lisp Assembly Program translates S-expressions which resemble
the native assembly language of the host machine into machine
language, and sets things up so that machine language coding can be
called by LISP programs in the same way that built-in "subrs" are
called.

* Menu:

* LAP on the pdp-10::
* LAP on Multics::
* LAP Words::
* LAP Instructions::
* LAP Operands::
* LAP Expressions::
* Using LAP::

Node: LAP on the pdp-10,	Up: LAP,	Next: LAP Words


   ;;;tty hacking fcns ;;;uncertain about syscall 'scml effects

   (declare (special tv-main tv-echo tv-size tv-ycor) (genprefix tty))

   (defun tv-screen num
      (do ((mode (or (= num 0) (arg 1)))
	   (echoareasize (cond ((< num 2) 10)
			       (t  (arg 2)))))
       nil (setq tv-ycor (- 443. (* 12. echoareasize)))
           (or (boundp 'tv-size)
	       (setq tv-main (open '((tty)) '(tty out ascii))
		     tv-echo (open '((tty)) '(tty out ascii echo))
		     tv-size (car (status ttysize t))))
           (cond ((equal mode 'splitscreen)
		  (open t '(tty out ascii))
		  (sstatus ttycons t tv-echo)
		  (pagel t (- tv-size echoareasize))
		  (endpagefn t 'tty-endpagefn)
		  (syscall 0 'scml tv-echo echoareasize))
		 ((equal mode 'smallscreen)
		  (open t '(tty out echo ascii))
		  (sstatus ttycons t t)
		  (pagel t echoareasize)
		  (syscall 0 'scml tv-echo echoareasize))
		 (t
		  (setq echoareasize 0)
		  (open t '(tty out ascii))
		  (sstatus ttycons t t)
		  (pagel t tv-size)
		  (syscall 0 'scml tv-echo 0))))
      (cursorpos 'c tv-main)
      (cursorpos 't t)
      'done)

   (declare (special **more** more-flush))

   (setq **more** '##more## more-flush nil)

   (defun tty-endpagefn (file)      ;Might lambda  bind outfiles, infile
      (prog (^q ^r ^w infile outfiles  echofiles)
	    (cond (**more**
		   (princ **more**) ;^q, ^r, etc.
		   ((lambda (ifile) ;the princ's should have file args
			    ((lambda (ch)
				     (and (or (= ch 32.)
					      (= ch 127.))
					  (tyi ifile))
				     (cond ((and more-flush
						 (not (= ch 32.)))
					    (princ more-flush)
					    (throw nil clever-more))
					   (t
					    (cursorpos nil 0 file)
					    (cursorpos 'l file)
					    (cursorpos 't file))))
			     (tyipeek nil ifile)))
		    (cond ((equal file t) t)                 
			  (t (or (status ttycons file) t))) ))
		  (more-flush (throw nil clever-more)))))

   (macrodef catchmore (moremsg flushmsg . body)
	     (catch ((lambda (**more** more-flush) . body)
		     moremsg flushmsg)
		    clever-more))

Node: LAP on Multics,	Up: LAP,	Next: LAP Words


   A LAP program begins with the form

                         (lap fn type nargs)

This defines the function fn, which is of type type (subr, lsubr, or
fsubr.)  nargs is the number of arguments expected by the function.
In the case of an lsubr, this is nine bits of the maximum number of
arguments followed by nine bits of the minimum number of arguments.

   Following this form is a series of "LAP words," terminated by nil.

Node: LAP Words, Up: LAP, Next: LAP Instructions, Previous: LAP on the pdp-10

   A LAP program consists of a sequence of LAP words, or statements.
Usually a LAP word generates one word of object code, but some LAP
words are pseudo-ops which generate no code, and some lap words
generate many words of code.

   The allowed formats for LAP words are as follows:

A number.  This generates a word whose contents is that number.
      Octal numbers which LISP would normally treat as bignums because
      the high order bit is on but the number is not negative, such as
      400000710120, are handled properly.  A flonum is also allowed,
      and a word containing the machine representation of that flonum
      will be generated.

An atomic symbol.  As in prog, this defines a label or tag at
      the current location.

(entry fn type nargs).  This defines an additional entry point.
      The arguments are the same as in the lap header line.

(comment ...) is ignored.

(eval form1 form2 ...) evaluates the forms (as lisp forms, not
      lap expressions) but does not do anything with the results.

(defsym sym1 val1 sym2 val2 ...).  This defines values for
      symbols.  The values are evaluated as LISP forms, not as LAP
      expressions.

(equ sym1 val1 sym2 val2 ...).  This is similar to defsym except
      that the values are evaluated as LAP expressions.  (See for the
      details of LAP expressions.)

(block n) generates n words of zeroes.  It is unclear how useful
      this is, since the code generated by LAP goes into a read-only
      object segment.

(ascii some text) explodec's the text and generates a string of
      the corresponding ascii characters.  This is not a LISP string,
      just the characters themselves.  If the number of characters is
      not a multiple of four, the last word is filled out with zeroes
      (null characters).

(bind symbol value) generates a binding word for use with the binding
      operator.

(get-linkage) loads the lb register with a pointer to the
      Multics linkage section.  The external operand (refer to ) may
      be used to refer to external data and procedures once the lb is
      loaded.  lb is used instead of lp because LISP uses lp
      internally.

A list whose car is a LISP macro will be expanded.  This
      provides LAP with the primitive makings of a macro facility.
      The result of the macro should be a list of LAP words, or nil.

Anything else will be assembled as an instruction.  The next
      section describes the format of instructions.

Node: LAP Instructions, Up: LAP, Next: LAP Operands, Previous: LAP Words

   Instructions have essentially the same format as in the ALM
assembler, with the following exceptions: Since LAP words are lists,
instructions are enclosed in parentheses.  Comments must be introduced
by semicolon.  Index-register tags must be in the form "x7" rather
than just "7." This is because in LAP tag fields are general
expressions and are not evaluated specially.  By default numbers are
octal, but a trailing point indicates decimal (as in LISP.)
Arithmetic expressions are written differently.  (See .) The rpt, rpd,
and rpl instructions are not supported.  The format of literals is
different from that used by ALM.  The ALM pseudo-ops, particularly
vfd, are not present, but could be simulated using macros.  ALM's
format for external references is not used.  The use of spaces and
commas is freer than in ALM.  Vertical bar may be used freely since in
the LAP reader it is a single character object.

   The allowed formats for ordinary instructions are:

              (opcode)
              (opcode operand)
              (opcode operand tag)
              (opcode pointer|operand)
              (opcode pointer|operand tag)

   For instructions such as "epp" which need a register operand:

              (opcode register operand)
              (opcode register operand tag)
              (opcode register pointer|operand)
              (opcode register pointer|operand tag)

Note that LAP treats comma and space identically, and use of commas in
the above formats can make them more like ALM.  Also, ALM lacks an
opcode for spri in the second format above, because the symbol spri is
already used for a different instruction.  In LAP, use sprip.

   EIS instructions and descriptors are written in the same format as
with ALM, e.g.

              (mlr (pr,rl),(pr,x6),fill(040))
              (desc4ls bp|-1(3),46,3)

The various fields are all general LAP expressions, except that the
words pr, id, and rl are special-cased.

   The tag field in an instruction may be any tag known to the
machine.  It may also be the special value $.  The following are
equivalent:

              (tnz frob,$)
              (tnz (- frob *),ic)
        or in ALM,  tnz frob-*,ic

   The pointer register names which appear in opcodes, in register
fields in the second format of instructions, and in "pointer|" fields
may be chosen from among

              0, 1, 2, 3, 4, 5, 6, 7

              ap, ab, bp, bb, lp, lb, sp, sb

              ms, op, tp, cp, lp, rp, sp, sb, us

us is a pseudo pointer register which points at the unmarked stack.
It actually consists of a combination of ab and x7, and therefore
cannot be used in EIS instructions.  See for the standard usage of the
pointer registers.

   The operand field in an instruction may take on a number of forms,
which are described in the next section.

Node: LAP Operands, Up: LAP, Next: LAP Expressions, Previous: LAP Instructions

   A LAP instruction may have either an ordinary ALM-type operand, a
literal, or a special lispish operand.  In general, the latter do not
allow tags.

   The allowed operand formats are:

A number, a symbol, or any LAP expression.  This is an ordinary
      ALM-type operand.  The symbol * represents the current location,
      as in ALM.

(% code) or (%% code).  These operands are literals.  The
      contained code is assembled at the end of the program and the
      instruction refers to that address.  If %% is used, the literal
      is placed on a double-word boundary.  The code may be either a
      single LAP word or several; since tags are disallowed in
      literals, if the first item in a literal is an atomic symbol,
      then the literal is taken to be a single word.  Otherwise it is
      a list of words.  Note that inside a literal the value of * is
      the location of the instruction that referenced the literal, not
      the location of the literal.  Examples:

           (ana (% 000777777777))

           (eraq (%% -2 777777000000))

           (eppbp (% ascii Now is the time))

           (tnz (% (eax1 1,x1)
                   (tze frob)
                   (tra (+ * 1)) ))        ;return to loc after tnz

(quote S-expression) refers to a LISP constant.

(special var) refers to the value cell of a special variable (an
      atomic symbol).

(array name type ndims) refers to an array.  This is intended to
      be used with the xec instruction for in-line accessing of
      arrays.  See .

(function name type nargs) refers to a LISP (or LAP) function.
      It is intended to be used with the call (tspbp) instruction.  If
      type is lsubr, an "eax5 -2*nargs" instruction should precede the
      call.  [[[MORE]]]

(function ap|n type nargs) refers to a computed function,
      located in a cell in the stack.  [[[MORE]]]

(external "seg$ent") refers to an external item.  No tag or
      offset may be used.  Use the (get-linkage) pseudo-instruction to
      make the external item addressable.

Node: LAP Expressions,  Up: LAP,  Next: Using LAP,  Previous: LAP Operands

   LAP expressions are used as operands of instructions, tag fields,
EIS length fields, and in general wherever a numeric value is needed.
The allowed formats are:

A symbol.  Somewhere the symbol must be defined, by use of
      defsym, equ, a tag, or the symbol may be one whose definition is
      built into LAP.  [[[ THESE ARE LISTED SOMEWHERE OR OTHER ]]]

* has the value of the current location.

A number.  This has the value of the machine representation of that
      number.

(+ lap-expr1 lap-expr2 ...) is the sum of the values of the
      lap-expressions.  The + may be omitted.  If the list is empty,
      the value is zero.

(- lap-expr1 lap-expr2 ...)  subtracts the values of the
      expressions lap-expr2, ..., lap-exprn from the value of the
      expression lap-expr1.  However,

(- lap-expr) is the negative of the value of the expression lap-expr.

(symb arg1 arg2 ...), where symb is a LISP macro, expands the
      macro and uses the result as an operand.

Node: Using LAP,	Up: LAP,	Previous: LAP Expressions

   The LAP assembler may be used in either of two ways: as a
translator which is invoked from Multics command level to assemble a
file of LAP programs and produce an object segment which can be loaded
into lisp; or as a lisp function which will read a lap program from
the current input source and assemble it into the lisp environment.
In the pdp-10 implementation these are called "faslap" and "lap"
respectively, but in the Multics implementation they are the same
thing.  If lisp tries to evaluate a form such as (lap foobar subr 2),
then lap will be automatically loaded into the environment and it will
read in and assemble until nil is encountered.  This mode should be
used with caution since loading lap defines a lot of functions which
might conflict with names already in use.

   The more common way of using lap is as a Multics command:

                          lap name -options-

reads the file name.lap and produces an object segment called name in
the working directory.  This segment may then be loaded into the lisp
environment with the load function.  This is similar to the operation
of the lisp_compiler command.  [[[ ??? WHAT ARE THE OPTIONS ??? ]]]

   LAP reads forms from the source file and processes them as follows:

(lap function type nargs) introduces a LAP program.  The
      assembler reads and assembles until nil, then returns to this
      scan.

(declare ...) is the same as in the compiler.  It can be used to
      cause things to happen at compile time.

(%include name) causes an include file name.incl.lap to be read
      in the same way as the main file.

Macro definitions (with defun or defprop) are evaluated as they are seen.

A form whose car is a macro is expanded and re-processed.

   [[[[[[[[ ********** Need to discuss: operators available to lap.
		       + other internal cruft.  How to use macros.

Node: Details,	Up: Top,  Next: Other Languages,  Previous: LAP

   This section describes the internal machine-level details of the
various implementations.  An understanding of this material is not
necessary in order to use lisp, but it is helpful in writing LAP code
and in understanding the output of the compiler.

* Menu:

* The pdp-10 Implementation: pdp-10
* The Multics Implementation: Multics
* Conventions::
* Routines used by the Compiler: Internal
* User Routines::
* Representation::
* Environment, Stacks, Registers: Environ
* Calling::
* Operators::

Node: pdp-10, Up: Details, Next: Conventions

Node: Conventions, Up: Details, Next: Internal, Previous: pdp-10

   This section briefly describes some of the internal conventions of
pdp-10 lisp, and contains enough information for a person who knows
pdp-10 machine language to understand the output of the compiler, and
possibly to write simple lap functions for use with lisp.  However,
the information within this section is subject to change.  Whenever
any location within lisp is referred to symbolically in this section,
that symbol is predefined to lap and may be used by any lap program
even if DDT does not have lisp's symbols loaded.

   The names of the accumulators and their uses are, briefly:

   0 nil      atom header of the atomic symbol nil
   1 A        first argument to a function; value of function
   2 B        second argument
   3 C        third argument
   4 AR1      fourth argument
   5 AR2A     fifth argument
   6 T        negative of the number of args to an lsubr; temp
   7 TT       super-temporary; value from numeric function
  10 D        semi-temporary; arithmetic
  11 R        semi-temporary; arithmetic
  12 F        semi-temporary; arithmetic
  13 FREEAC   unused, except saved/used/restored by gc
  14 P        regular pushdown list (pdl) pointer
  15 FLP      flonum pdl pointer
  16 FXP      fixnum pdl pointer
  17 SP       special (variable bindings) pdl pointer


   In general, S-expressions should be manipulated in the five
argument accumulators; the contents of these are protected by the
garbage collector.  Random arithmetic should not be done in them; this
might accidentally generate the address of something the garbage
collector should not protect.  Arguments to subrs are passed through
these five accumulators, and the value of a function is returned in
accumulator A.  The single argument to an fsubr is likewise passed
through accumulator A.

   It is generally assumed that when an argument is passed or a value
returned through these five accumulators that that the left half will
be zero, while the right half will contain a pointer to an
S-expression.  Much code depends on the left half being zero; in
particular, tests for nil (which is the zero pointer) use JUMPE
instructions, which require that the left half be zero so that the
test of the right half will be valid.  In general, then, instructions
like HRRZ and HLRZ should be used to fetch items into these
accumulators.

   S-expressions are represented in such a way that if a pointer to a
dotted pair is in, say, accumulator A, then

                             (HLRZ B 0 A)

will get, as a pointer, the car of the S-expression and put it in
accumulator B, and

                             (HRRZ B 0 A)

will get the cdr.  If the S-expression whose address is in A is a
fixnum or flonum, then

                            (MOVE TT 0 A)

will get the machine representation of the number and put it in
accumulator TT.

   Accumulators T through F may be used as scratch registers, in
general.  When an lsubr is called, however, the negative of the number
of arguments is passed in accumulator T.  Many useful internal
routines are called by JSP T,FOO, and the argument or value is
commonly passed in TT.  Functions compiled by the fast- arithmetic
compiler return their values in TT.  TT is also used in connection
with array accessing.

   FREEAC is presently unused by the lisp system, except for the
garbage collector, which, however, saves and restores it.  This fact
should not be taken as permanent; it is mentioned primarily because it
can be useful for debugging purposes.  One day soon this accumulator
will be renamed BAR and used as a base address register for
relocatable binary programs.

   The pdp-10 lisp system uses four pushdown lists, or stacks.  The
regular and special pdls, whose pointers are in P and SP, are marked
from by the garbage collector; thus an S-expression is "safe" from gc
if pushed on either of these pdls.  (Only the right half of each pdl
slot is marked from; the left half may contain garbage.)  The special
pdl is used to hold variable bindings, and its contents are highly
structured.  The user should not use SP except through the routines
SPECBIND and UNBIND, described below.  P may be used for any purpose,
provided that totally random things are not put into the right halves
of pdl slots (the same restriction as for argument accumulators).  The
fixnum and flonum pdls (pointers in FXP and FLP) are used primarily by
compiled code produced by the fast-arithmetic compiler, and their
contents are not affected by gc in any way.  If it is desired to save
random quantities on a stack, the fixnum pdl should be used if
possible.

   The standard function calling convention in pdp-10 lisp requires
that functions be effectively called via a (PUSHJ P function) and exit
via (POPJ P).  The arguments to subrs and fsubrs are as described
above.  Lsubrs take their arguments on the regular pdl (where they are
safe from gc), and T has minus the number of arguments.  The return
address is also on the pdl, under the arguments.  This usually
requires code of this sort:

                  (PUSH P (% 0 0 G0475))
                  (PUSH P A)
                  (PUSH P '(funny list))
                  (MOVNI T 2)
                  (JRST 0 FOO-LSUBR)
          G0475   --- lsubr returns to here ---

That is, the return address must be pushed ahead of time.  It is the
responsibility of the called lsubr to remove its arguments from the
pdl and return with a POPJ.

   Interfacing between compiled code and the interpreter is
accomplished via a large set of UUO instructions.  All of them work in
the same fashion: the effective address must be the address of an
S-expression which is the function to be invoked.  The arguments to
this function are passed in the manner described above, and the
accumulator field describes which argument passing convention has been
used (hopefully the same as that required by the called function): 0-5
means a call to a subr with that many arguments, 16 means a call to an
lsubr, and 17 means a call to an fsubr.  Thus the function CONS might
be called with the UUO (CALL 2 (FUNCTION CONS)).

   There are several variants on this basic UUO type.  One variant is
the JRST vs. PUSHJ mode; sometimes instead of writing a PUSHJ to a
function one wants to write a JRST for efficiency.  To see why,
consider that

                  (PUSHJ P FOO)
                  (POPJ P)

is in effect equivalent to

                  (JRST 0 FOO).

This kind of UUO is also useful for calling lsubrs (see the example
above).

   A second variant is the "clobberable" vs.  the "unclobberable" UUO.
If certain conditions are met, it is possible for the UUO handler to
replace the invoking UUO by the equivalent PUSHJ or JRST, so that next
time the same code is used it will call the desired function directly.
In some cases, however, it is not desirable for the UUO to be so
clobbered, for example if the function to be invoked is an argument in
an accumulator, and is to be invoked via something like (CALL 1 0 A).
A UUO may therefore specify that it may never be clobbered.

   A third option is used by code compiled by the fast arithmetic
compiler.  It is undesirable for a function which returns a number to
do a "number cons" in order to return the number as an S-expression if
the number will only be converted back to a machine number and used in
more open-coded arithmetic.  (It is undesirable because number
consing, like ordinary consing, eventually causes garbage collection,
an expensive process.) Thus a UUO may specify that it wants only a
machine number as a result; this is to be returned in accumulator TT,
rather than a lisp number in A.

   The mnemonics for all these UUOs are summarized here:

                         clobberable         unclobberable
                         PUSHJ     JRST      PUSHJ     JRST
      standard result    CALL      JCALL     CALLF     JCALLF
      numeric result     NCALL     NJCALL    NCALLF    NJCALF

   Thus the example of an lsubr call above would actually be written:

                  (PUSH P (% 0 0 G0475))
                  (PUSH P A)
                  (PUSH P '(FUNNY LIST))
                  (MOVNI T 2)
                  (JCALL 16 (FUNCTION FOO-LSUBR))
          G0475

   Functions produced by the fast arithmetic compiler follow a
convention so that NCALLs will work properly: If a function is to be
NCALL'ed, and returns a fixnum, the first instruction of the function
should be (PUSH P (% 0 0 FIX1)); if it returns a flonum, the first
instruction should be (PUSH P (% 0 0 FLOAT1)).  (For a description of
the FIX1 and FLOAT1 routines, see below.) If the function is NCALL'ed,
the function is entered at the second instruction, i.e.  after the
PUSH.  The appropriate machine number is returned in accumulator TT,
as expected by the caller.  If, on the other hand, the function is
simply CALL'ed, then it is entered at the normal entry point, and the
address of FIX1 or FLOAT1 goes on the stack.  When the function exits,
it will transfer to FIX1 or FLOAT1, which will convert the machine
number to a lisp number and then return to the original caller.

   Some other UUO's besides the CALL UUO's are useful to compiled code
and hand-coded lap.  The STRT (STRing Typeout) UUO is quite useful
for printing out constant strings of characters.  The effective
address of the STRT UUO must be the first of several words of sixbit
characters.  Several characters in the string have special
significance:

^ Complement the 100 bit of the character before printing it.
     (This occurs after 40 has been added to convert it to ascii.)
     Thus ^M in the sixbit string causes a carriage return to be
     printed.  Similarly, ^4 is a lower case t.

!    Terminate typeout.

#    Quote the next character.  This is used to get #, ^, and ! into a string.

   Thus, for example, to print the message "YOU LOSE!" in lap code,
preceded and followed by a carriage return, say

                (STRT 0 (SIXBIT /^MYOU/ LOSE#/!/^M/!))

(The slashes are necessary because lap will read this using the lisp
reader!)

   The LERR (Lisp ERRor) UUO takes a string like the ones STRT takes,
and signals an uncorrectable error, with the string as the error
message.  Because the error is uncorrectable, control never returns to
after the LERR; it is like a JRST to the error handler.

   The LER3 UUO is similar to LERR, but also takes an S-expression in
accumulator A; this expression should be followed by the string which
constitute the error message.

   The ERINT UUO is used to signal correctable errors.  It too takes a
string argument and an S-expression in A.  The accumulator field of
the ERINT UUO indicates the type of error:

            0     undef-fnctn
            1     unbnd-vrbl
            2     wrng-type-arg
            3     unseen-go-tag
            4     wrng-no-args
            5     gc-lossage (ordinarily used only by gc)
            6     fail-act
            7     io-lossage

   The S-expression becomes the argument to the error interrupt
handler for the given type of error (in the case of types 0 to 3, the
error handler automatically applies the function ncons to this object
before passing it as the argument).  If the handler returns a
corrected value (e.g. the user in a standard error break used the
return function) then this new value is passed back in A and control
returns to the instruction after the ERINT.

   A typical piece of lap code to use this might be:

          (LAP FOO SUBR)
                  (PUSH P A)
          TEST    (JSP T FXNV2)      ;get numeric value in d
                  (TRNE D 3)         ;want a multiple of 4
                  (JRST 0 LOSE)
                  . . .
                  (POPJ P)
          LOSE    (EXCH A B)         ;get bad arg in a
                  (ERINT 2 (% SIXBIT NOT A MULTIPLE OF 4))
                  (EXCH A B)         ;switch back again
                  (JRST 0 TEST)      ;go try again
          NIL


   UUO's never change the values in any accumulators except ERINT,
which may return a new value in A, and the various CALL UUO's, which
may clobber everything if they have to invoke eval to link to an
interpreted function.  CALL UUO's save all accumulators when linking
from one compiled or handcoded function to another.  This implies that
the called function will get whatever was placed in accumulators T
through F as well as A through AR2A.  It does not imply, however, that
any accumulators will have been preserved by the time the called
function has returned to the caller.

Node: Internal, Up: Details, Next: User Routines, Previous: Conventions

   Compiled code requires a certain set of support routines.  The
names and addresses of these routines are predefined to lap.  It
should not be assumed that a given routine saves any accumulators
unless it is specifically described as doing so.  They are briefly
described here:

(JSP T SPECBIND)
     This routine handles the binding of special variables.  The call
     is followed by one or more specifications of the form (type where
     (special atom)), where type is either 7_41 or 0.  The value of
     the atomic symbol atom, which is in the word pointed to by the
     effective address of the argument, is saved on the special pdl,
     and a new value is placed in the value cell, as specified by type
     and where.  If both type and where are zero, the new value is
     nil.  If type is zero, then where is the number of an accumulator
     containing the new value.  If type is 7_41, then the new value is
     in the regular pdl slot addressed by subtracting where from the
     current contents of accumulator P; where may be any number less
     than 2000 octal.  (This is a case where not truncating the
     accumulator field of a lap instruction to four bits is very
     useful.)  Any number of specifications may follow the call to
     SPECBIND; the end of the call is determined by the fact that a
     valid pdp-10 instruction within lisp cannot be zero in the first
     nine bits or ones in the first three.  All the values pushed in a
     single call form a single bind block; this fact is used by the
     UNBIND routine.  SPECBIND destroys the contents of accumulator R.


(JSP T (SPECBIND -1))
     This is an alternate entry to SPECBIND, which has the additional
     effect of passing all new values through the routine PDLNMK (see
     below) before placing them in the value cells.  It is used by
     code compiled by the fast- arithmetic compiler.
     [[[ HAS THIS VANISHED OR SOMETHING? ]]]

(PUSHJ P UNBIND)
     Pops one bind block off the special pdl, thus restoring the old
     values of the atoms whose values were formerly saved.  Example:
     the following lisp code and lap code are roughly equivalent:

             ((LAMBDA (SPECVAR) (ZORCH)) 'BARF)

                    (MOVEI B (QUOTE BARF))
                    (JSP T SPECBIND)
                    (0 B (SPECIAL SPECVAR))
                    (CALL 0 (FUNCTION ZORCH))
                    (PUSHJ P UNBIND)

     UNBIND does not destroy any accumulators.


(JSP T PDLNMK)
     "Pdl number make".  This routine examines the S-expression in
     accumulator A, and if it is a pdl number it replaces it with a
     freshly number-consed copy.  Used by code produced by the
     fast-arithmetic compiler.  Does not destroy any other
     accumulators, even TT.

(JRST 0 PDLNKJ)
     Equivalent to

                    (JSP T PDLNMK)
                    (POPJ P)


(JSP T FXCONS)
     Takes a machine fixnum in accumulator TT and returns an
     equivalent S- expression number in accumulator A.  The value in
     TT is not preserved.  No other accumulators are disturbed.
     Another name for FXCONS is FIX1A; they are entirely equivalent.
     Note that lisp fixnums are represented in such a way that the
     address in A will point to a word containing what was in TT.


(JSP T FLCONS)
     Similar to FXCONS, but takes a floating-point machine number in
     TT, and returns a lisp flonum in A.

(JSP T FXNV1)
     Verifies that the S-expression in accumulator A is a fixnum; if
     it is not, a correctable wrng-type-arg error is signaled.  If it
     does contain a fixnum, or if the error break eventually returns a
     fixnum, then it returns with the equivalent machine fixnum in
     accumulator TT.  This routine is useful primarily for the error
     checking; if it is already known that A contains a lisp fixnum,
     the instruction (MOVE TT 0 A) serves just as well.  Such
     knowledge, for example, can be derived from declarations by the
     fast-arithmetic compiler.


(JSP T FXNV2)
(JSP T FXNV3)
(JSP T FXNV4)
     Similar to FXNV1, but take arguments and return machine fixnums in
     different accumulators:

       FXNV2     B   -> D
       FXNV3     C   -> R
       FXNV4     AR1 -> F

     There is no FXNV5 - you must move an argument in AR2A into some
     other accumulator first.


(JSP T IFIX)
     Takes a machine flonum in TT and converts it to a (truncated)
     machine fixnum, returned in TT.  Destroys accumulator D.


(JSP T IFLOAT)
     Takes a machine fixnum in TT and converts it to a machine flonum,
     returned in TT.  Does not destroy any other accumulators.

(JRST 0 FIX1)
(JRST 0 FIX2)
(JRST 0 FLOAT1)
(JRST 0 FLOAT2)
     These are convenient exits to the following code internal to the lisp
     system:

       FIX2    (JSP T IFIX)
       FIX1    (JSP T FXCONS)
               (POPJ P)
       FLOAT2  (JSP T IFLOAT)
       FLOAT1  (JSP T FLCONS)
               (POPJ P)


(JSP T FLTSKP)
     Verifies that the S-expression in A is a fixnum or flonum; if it
     is not, a wrng- type-arg error is signaled.  If it is, then the
     machine number is returned in accumulator TT; moreover, the
     return skips if it is a flonum.  Example: here is a simplified
     version of the sub1 function which does not accept bignums:

       (LAP SUB1NOBIG SUBR)
       (ARGS SUB1NOBIG (NIL . 1))
               (JSP T FLTSKP)
               (SOJA TT FIX1)
               (FSBRI TT 0 1.0)
               (JRST 0 FLOAT1)
       NIL


(JSP T (NPUSH -n))
     This routine pushes n nil's onto the regular pdl; i.e.  it is
     equivalent to writing (PUSH P (% 0 0 NIL)) n times.  n must be
     between 1 and 20 octal.  Note the minus sign in the above: to
     push 4 nil's one writes (JSP T (NPUSH -4)).  This routine is used
     greatly by compiled code to create pdl slots for local variables.

(JSP T (0PUSH -n))
     Similar to NPUSH, but pushes zeros onto the fixnum pdl.  n must
     be between 1 and 10 octal.  Used by code produced by the
     fast-arithmetic compiler.

(JSP T (0*0PUSH -n))
     Similar to NPUSH, but pushes zeros onto the flonum pdl.  n must
     be between 1 and 10 octal.  Used by code produced by the
     fast-arithmetic compiler.

(JSP D *LCALL)
     This routine is called by user lsubrs produced by the lisp
     compiler.  It accounts for the number of arguments, and saves
     some information so that the arg and setarg functions can find
     the arguments.  After the user lsubr has been executed it takes
     care of popping the arguments off the pdl and returning to the
     caller.


(JSP D (*LCALL -1))
     Used by user lsubrs declared to be of type fixnum.  It performs
     the same setup as *LCALL, but also sets up a number-consing
     return by doing (PUSH P (% 0 0 FIX1)).  *LCALL skip-returns; the
     following instruction is always (JSP D *LCALL).

(JSP D (*LCALL -2))
     Is like F3(*LCALL -1), but is for flonum-type lsubrs.


(PUSHJ P IOGBND)
     Used by compiled code to perform the iog function.  Equivalent to the
     code

               (JSP T SPECBIND)
               (0 0 (SPECIAL ^W))
               (0 0 (SPECIAL ^Q))
               (0 0 (SPECIAL ^R))
               (0 0 (SPECIAL ^B))
               (0 0 (SPECIAL ^N))


(JSP T (*MAP -n))
     Used by compiled code to call the various mapping functions in
     the common case where there are two arguments.  The function
     should be in B, and the list in A.  (This is backwards from the
     standard order!) n determines which mapping function as follows:

        1   maplist   3   map       5   mapcon
        2   mapcar    4   mapc      6   mapcan


(JSP T *SET)
     Used for compiling calls to the function set.  Accumulator A
     should have the value (second argument to set), while AR1 should
     have the atomic symbol which is to get the value (first argument
     to set).

(JSP T *STORE)
     Used for compiling calls to the function store.  (The conventions
     for this routine are undergoing some change, and thus are not
     described here.)

(PUSHJ P *UDT)
     Used by compiled code for handling undefined computed go tags in
     compiled progs.  The tag is in accumulator A.  It handles the
     case where the tag is really a fixnum; and if not, signals a
     correctable error and possibly returns with a corrected tag in A.

(JSP TT ERSETUP)
     Used for compiling calls to the function ERRSET.  Accumulator A
     has the second argument to ERRSET, and B has the address to go to
     if an error occurs.  This routine pushes various things onto the
     regular pdl.

(JRST 0 ERUNDO)
     If all the code compiled for the first argument to an errset runs
     without error, it must go to ERUNDO to undo the errset, i.e. to
     pop the things off the pdl which ERSETUP pushed.  Control is
     returned to the address given in B when ERSETUP was called.

(JSP T GOBRK)
     Used by compiled code when a go is done within an errset (yech!).
     It is similar to ERUNDO, but returns to the instruction following
     the (JSP T GOBRK), rather than to the place specified to ERSETUP.

(JSP TT (ERSETUP -1))
     Used to compile calls to the function catch, which internally is
     similar to errset.  Accumulator A contains the second argument to
     catch (the catch tag), and B the return address which is used if
     a throw is done.

(JRST 0 (ERUNDO -1))
     Just as ERUNDO undoes an errset, so ERUNDO-1 undoes a catch.

(JSP T (GOBRK -1))
     Similar to GOBRK, but breaks out of a catch rather than an
     errset.  This is what a throw compiles into.

ARGLOC
     This is not a routine but a variable, which contains the address
     of the pdl slot just below the arguments to the most recently
     called lexpr or user lsubr, or zero if none has been called. Thus
     the call (ARG 2) may be coded in lap roughly as:

               (MOVE T ARGLOC)
               (ADDI T 2)
               (HRRZ A 0 T)

     This is one of the variables set up by *LCALL.

ARGNUM
     This, like ARGLOC, is a variable.  It contains the number of
     arguments to the most recent lexpr or user lsubr call, as a lisp
     number.  (accessing ARGNUM indirectly will of course fetch the
     machine number.)  Thus one might write a function:

       (LAP ARGN-2 SUBR)
       (ARGS ARGN-2 (NIL . 0))
           (MOVE TT @ ARGNUM)     ;get number of args
           (CAIGE TT 3)           ;need at least 3
           (LERR 0 (% SIXBIT LESS THAN 3 ARGS))
           (ADD TT ARGLOC)        ;fetch the last
           (HRRZ A -2 TT)         ; arg but 2
           (POPJ P)
       NIL

Node: User Routines, Up: Details, Next: Representation, Previous: Internal

   There are some routines internal to pdp-10 lisp which are not used
by code produced by the compiler, but which may be of use to those
writing functions in lap.  Unless specified otherwise, the symbols for
these routines are also predefined to lap.

(PUSHJ P PRINTA)
     This routine is the internal lisp print function.  It does not
     actually perform any output, but merely supplies a stream of
     characters.  It is called with the S-expression to be printed in
     accumulator A, and the address of a routine in R.  The sign bit
     of R controls the use of slashes: zero means produce characters
     like prin1 and explode would, one means like princ and explodec.
     PRINTA will generate characters and pass them one at a time to
     the routine specified in R by placing the ascii code in
     accumulator A and doing a (PUSHJ P 0 R).  (This violates the rule
     about putting non-S- expressions in gc-protected accumulators,
     but for numbers less than about 2000 octal this is guaranteed to
     be a safe procedure anyway.)  The routine may do anything it
     wants to with the character, but must avoid destroying the
     contents of accumulators B, C, TT, and R, which are assumed by
     PRINTA to be safe.  On the other hand, AR1 and AR2A are not
     altered by PRINTA and may be used to communicate over successive
     calls to the routine; e.g. they may hold byte pointers, etc.
     (Again, a violation of the rule, but this is all right as long as
     they point to "safe" places, like pdl slots or binary code.) When
     PRINTA is done it will return to the instruction after the PUSHJ
     to it.  The contents of accumulator A are not preserved.
     Example: Here is a funny version of flatc which only counts
     capital letters.

            (LAP ALPHLATC SUBR)
            (ARGS ALPHLATC (NIL . 1))
                (PUSH P (% 0 0 FIX1))   ;it's NCALLable!
                (PUSH FXP (% 0))        ;counter
                (MOVEI AR2A 0 FXP)      ;remember where it is
                (HRROI R COUNT)         ;princ style
                (PUSHJ P PRINTA)
                (POP FXP TT)            ;pop count
                (POPJ P)
        COUNT   (CAIGE A 101)           ;only count capital
                (POPJ P)                ; letters
                (CAIG A 132)
                (AOS 0 0 AR2A)
                (POPJ P)
            NIL


(PUSHJ P GETCOR)
     This symbol is not known to lap; it is intended primarily for
     systems programmers on ITS who need large blocks of core for
     special I/O devices; however, it also exists in dec-10 lisp.  It
     is called with the number of 1K blocks of core desired in TT.
     Lisp allocates a single block of core that large and returns the
     address of the first word of the block in TT.  It may destroy
     several other accumulators in the process.  Lisp may or may not
     actually cause the core to exist; it merely allocates address
     space and promises not to use it for anything else.  The caller
     should do the appropriate .CBLK calls on ITS to cause the core to
     exist.  (On dec-10 lisp will cause the core to exist, for the
     present.)


INHIBIT
     This is a variable which, if non-zero, specifies that (a) user
     interrupts may not be processed, but must be delayed, and (b)
     lisp may not relocate any arrays when garbage collecting (it may
     if the array functions are called, however).  This is used
     primarily by the lisp system; the nointerrupt function is usually
     sufficient for users.  When INHIBIT is reset to zero the routine
     INTREL should be called, to check for any delayed interrupts
     which may be pending.  Note that INHIBIT does not prevent
     uncorrectable errors and control G or control X quits.  Thus, it
     is preferable to the nointerrupt function when it is desired to
     inhibit user interrupts but not quits (such situations are rare
     except in lap code).  The standard usage of this switch is:

                    (PUSH FXP INHIBIT)
                    (SETOM 0 INHIBIT)
                    ... process with user interrupts inhibited ...
                    (PUSHJ P INTREL)

     Note that INTREL will do a (POP FXP INHIBIT).


NOQUIT
     This switch inhibits all interrupts and quits.  The left half is
     for use by the garbage collector, and only the garbage collector!
     The right half may be used by user programs by using (HLLOS 0
     NOQUIT) to turn it on, and (HLLZS 0 NOQUIT) to turn it back off.
     After turning it back off the routine CHECKI should be called to
     check for any delayed interrupts or quits.  Thus the standard
     usage is:

                    (HLLOS 0 NOQUIT)
                    ... process with NOQUIT non-zero ...
                    (HLLZS 0 NOQUIT)
                    (PUSHJ P CHECKI)

     This is somewhat less useful than the user nointerrupt function,
     but was implemented first.  Note that the routine INTREL
     described above under INHIBIT is equivalent to

                    (POP FXP INHIBIT)
                    (JRST 0 CHECKI)

     and thus if for some reason one wants to pop the old value of
     INHIBIT oneself, CHECKI may be used instead of INTREL.  CHECKI
     preserves all accumulators.


(PUSHJ P UINITA)
     This routine sets things up for opening a file, old I/O style.
     It takes a file name list (name1 name2 dev user) in accumulator
     A, and on ITS a mode in the right half of TT.  If the file name
     list is short the default file names are applied as for the uread
     function.  In the dec-10 implementation, the device name is
     placed at location UTIN, and the ppn in USN (the latter tag is
     not known to lap; beware!); the file names are returned in T and
     TT.  In the ITS implementation, the mode, device, and file names
     are placed in a three-word block suitable for .OPEN at location
     UTIN, and the lisp's sname is set to the appropriate user name.
     The contents of accumulator A are preserved.  UINITA also does
     the equivalent of

                    (PUSH FXP INHIBIT)
                    (SETOM 0 INHIBIT)

     thus locking out user interrupts, on the theory that some I/O
     operation will take place which should not be interrupted.  It is
     up to the caller subsequently to unlock interrupts, e.g. by doing
     (JRST 0 INTREL).  Example: on ITS, these functions provide a
     (relatively inefficient) method for binary input (I/O channel 17,
     presently unused in pdp-10 lisp, is usurped; beware, for this
     fact will change!):

            (LAP BINOPEN FSUBR)
                (MOVEI T 4)           ;image unit input
                (PUSHJ P UINITA)      ;set up
                (*OPEN 17 UTIN)       ;try to open it
                (LER3 0 (% SIXBIT BIN FILE NOT FOUND))
                (JRST 0 INTREL)           ;must unlock interrupts
            (ENTRY BINGET SUBR)
            (ARGS BINGET (NIL . 0))
                (PUSH P (% 0 0 FIX1)) ;NCALLable!
                (*IOT 17 TT)          ;input a binary word
                (POPJ P)              ;return as a fixnum
            (ENTRY BINCLOSE SUBR)
            (ARGS BINCLOSE (NIL . 0))
                (*CLOSE 17)           ;close the channel
                (POPJ P)
            NIL

Node: Multics,		Up: Details,		Next: Conventions

   [[[ OVERVIEW ]]]

Node: Representation, Up: Details, Next: Environ, Previous: User Routines

   Like most LISPs, Multics MACLISP represents LISP values as pointers
to objects.  If two values are eq, the two pointers point to the same
identical object.

   A pointer is a two-word entity.  It consists of an "indirect to
segment pair," which is the principal pointer or indirect-word form
provided by the hardware, with some additional bits which specify the
data-type of the object pointed to.  These bits are arranged so that
all common type testing can be done in a single instruction.

   Numerical values are represented somewhat differently.  Since the
pointer is big enough to hold a machine fixnum or a machine flonum,
these LISP data types are not represented as objects in their own
right.  Instead, the machine number is stored directly in the pointer.
A special code is also stored in the pointer which makes the hardware
refuse to use it as a true pointer, i.e. any attempt to indirect
through a number-pointer will cause a fault.

   The arrangement of bits in an ordinary pointer is:

      --------------------------------------
      |   segment num  |ring|  type  | tag |
      --------------------------------------
      |    word num    |         0         |
      --------------------------------------

segment num and word num together make up the address of the object
pointed to.  tag contains a magic value which informs the hardware
that this is a pointer.  ring contains a ring-validation level which
is of no concern to lisp.

   The arrangement of bits in a number "pointer" is:

      --------------------------------------
      |          0          |  type  | tag |
      --------------------------------------
      |           machine number           |
      --------------------------------------

Here tag is set to a different magic value, which informs the hardware
that this is not really a pointer.

   The bits in the type field are:

     Fixed     this pointer contains a fixnum.

     Float     this pointer contains a flonum.

     Atsym     this pointer points at an atomic symbol.

     String    this pointer points at a string.

     Subr      this pointer points at an entry point to a
	       machine-executable function.

     Bignum    this pointer points at a bignum.

     System_Subr this pointer points at a subr built in to MACLISP.
	       The Subr bit is also on.

     Array     this pointer points at the header of a lisp array.
	       The Subr bit is also on, because arrays are also
	       functions.

     File      this pointer points at a file-object.

     We can see that if any bit is on, the value is atomic.  If no
	       bits  are on, i.e. the type field is zero, this is a
	       pointer to a cons.

   Now that we know what pointers look like, it is necessary to
discuss what the objects they point at look like.  [OR, Having
discussed the representation of lisp values, now we will discuss the
representation of lisp objects.  ]

   A cons consists of two values (or pointers), first the car and then
the cdr.

   The representation of fixnums and flonums has been discussed above.

   An atomic symbol consists of a structure which contains two
pointers, the length of the symbol's pname, and the pname itself.
This structure is thus variable in length, since there is no limit on
the length of a pname.  The two pointers are the symbol's value cell
and its property-list cell.  The value cell contains either all zero
bits, if the symbol has no value, or the symbol's lisp value.  Since
the value cell is the first item in an atomic symbol, the value of a
symbol can be referenced very quickly, by indirection.  The
property-list cell contains the list of indicators and properties that
have been placed on the symbol.  This list is nil when the symbol is
first created.  The atomic symbol nil is an exception, however.  Its
property-list cell is always nil in order to ensure that taking the
cdr of nil always returns nil.  The actual property list is kept
elsewhere.

   A string is represented as a word containing its length, in
characters, followed by the characters themselves, in the machine's
string format of four 9-bit characters per word.

   A bignum is represented as a header word, with the sign in the left
half and the size in the right half.  The sign is 18 0-bits for a
positive number, or 18 1-bits for a negative number.  The size is the
number of words (machine fixnums) required to represent the number.
These words immediately follow the header word.  Each word contains 35
significant bits.  The least significant word is first.

   The representation of a subr is a header word followed by the first
instruction of the subr.  Normally this instruction calls a small
subroutine which saves the information about the caller that has to be
saved [???] and then transfers to the subr proper.  This subroutine
can be system-supplied or generated by the compiler.  In the case of
some simple subrs, for instance eq, the subr-object transfers directly
to the subr proper.

   The header word contains in its left half a specification of the
number of arguments required, and in its right half further
information.  In the case of a compiled lisp subr, this is the
relative address within the object segment of the machine code for the
subr.  It is used by the linkage subroutine.  The right half is not
used in a system subr.  In the case of a subr (fixed number of
arguments), the left half is the number of arguments required.  In the
case of an lsubr, it is the maximum number of arguments, in 9 bits,
then the minimum number of arguments in the next 9 bits.

   The representation of an array is a header block which is pointed
at by the lisp value.  This header block describes the array and
points in turn to the actual array of values or machine numbers.

   The representation of a file is a large structure which contains
all the information required to access the file.  This includes both
information needed by lisp, such as the eoffn, and information needed
to access the file in the outside world, such as a pointer to and
pathname of a segment.

Node: Environ,  Up: Details,  Next: Calling,  Previous: Representation

   The lisp environment is contained in several segments of storage.
First there are the object segments.  These are read-only segments
containing, for the most part, executable machine code.  There is one
object segment for the lisp system itself, another for the compiler,
and another for library programs such as grind.  When a user's lisp
functions are compiled, the compiler produces another object segment.

   There are "lisp.lists" segments, which contain list structure and
other lisp-object representations.  These segments are maintained by
the garbage collector.

   There are "lisp.static" [???] segments.  These contain data not
managed by garbage collection, principally the representations of
subrs (but not the machine code itself), the representations of arrays
(but not the element of the arrays), and the representations of
file-objects.

   In addition there are two "lisp.stack" segments.  One is called the
marked stack, or marked pdl, and the other the unmarked stack, or
unmarked pdl.  The marked pdl is "marked" by the garbage collector,
hence used to store lisp values temporarily.  The unmarked pdl is
unknown to the garbage collector, hence used to store machine values
such as miscellaneous numbers, subroutine return addresses, etc.  The
two pdls are often pushed and popped in unison.  For instance, when a
function is entered it saves its return address and its caller's
linkage pointer (see below) on the unmarked pdl, and pushes some
temporary working space onto the marked pdl.  Its arguments are also
conveyed on the marked pdl; they are pushed on by the caller.

   At the base of the segment containing the unmarked pdl is a fixed
area known as the "stack header."  It is stored there only to make it
very easy to address.  The stack header contains a variety of
information which is frequently referenced both by the lisp system and
by compiled lisp functions.  Its contents include:

   Pointers to internal information stored elsewhere.

   Data used to remember the last array reference for the sake of the
store function.

   The values t and nil.  (That is, pointers to the atomic symbols t
and nil, with their Atsym type-bits turned on.)

   The in_pl1_code flag, which says whether execution is currently in
the lisp environment or the PL/I environment (see below).

   Pointers to "operators," which are subroutines to perform
operations very frequently required by compiled code.  These exist
because they can have more efficient calling sequences than the full
general lisp calling sequence.  Many operators perform operations
which are performed by special forms in interpreted code.  (Other
special forms are compiled directly into machine instructions.)  The
table of pointers to operators is required so that compiled code
doesn't have to be "linked" to the operators when it is load'ed.  This
would be time- consuming and might prevent sharing of compiled object
segments.  The operators are described in more detail below.

   [Registers]

Node: Calling,  Up: Details,  Next: Operators,  Previous: Environ

   [ TO BE SUPPLIED ]

Node: Operators,	Up: Details,		Previous: Calling

   [ EXPAND FROM THIS LIST ]

   bind unbind errset1 errset2 unerrset call catch1 catch2 uncatch
iogbind unseen go tag throw1 throw2 signp return err cons ncons xcons
begin_list append_list terminate_list compare link

Node: Other Languages,		Up: Top,	Previous: Details

Calling programs written in other languages.

* Menu:

* The defpl1 declaration: defpl1
* Producing fasloadable files with the Midas Assembler: Midas

Node: defpl1,	Up: Other Languages,	Next: Midas

   The Multics lisp compiler provides a feature by which you can
compile a lisp subr which represents, in the lisp environment, a
subroutine in the outside world which has a PL/I-compatible calling
sequence.  The Multics Fortran, PL/I, and Basic compilers use this
calling sequence.  The BCPL compiler uses it for "main" routines.
Most Multics system entries can be called from lisp through defpl1.

   When the lisp subr is applied, the subroutine will be called with
arguments derived from the arguments given to the lisp subr.  Results
returned by the subroutine may be passed back to lisp either as the
return value of the lisp subr, or by setq'ing an atomic symbol.

   Because lisp and PL/I use different data types, a correspondence
between the types must be set up:

   Numbers.  `fixed binary' with a precision not more than 35.
corresponds to the lisp fixnum.  `float binary' with a precision of
not more than 27.  corresponds to the lisp flonum.  Nonzero scale
factors, complex numbers, decimal or pictured numbers, and large
precisions are not supported.

   Bit strings.  A bit string of up to 36.  bits corresponds to a lisp
fixnum.  The bits are stored left-justified in the fixnum; thus in the
case of bit(1) the fixnum is zero for "0"b and negative for "1"b.
Note that because of the left- justification many bit strings map into
"illegal" fixnums which cannot be typed in as octal numbers.  Typing
in the corresponding digits would produce a bignum.  The lsh function
or the "_" number-modifier character can be useful for inputting these
fixnums.  These bit strings work as either `aligned' or `unaligned.'
Bit strings longer than 36. bits are not supported.

   Character strings.  Lisp character strings and PL/I character
strings correspond directly.  For input arguments, lisp will also
automatically convert an atomic symbol to a character string by taking
its pname, as usual.  Usually the PL/I argument will be declared
`char(*).'

   Varying Character Strings.  Varying character strings are somewhat
special.  Lisp will take whatever string argument you supplied (the
null string if it is a `return' argument) and create a varying string
of the length you declared, initialized with the string you supplied.
Thus usually its current length will be less than its maximum length.
This varying string will be passed to the PL/I subroutine.  When the
subroutine returns, whatever it leaves in the varying string will be
made back into a lisp string and returned (if it is an `update' or
`return' argument.)  This procedure is necessary because lisp strings
may not vary in length.  Note that you must declare the length of the
string to lisp; `char(*) varying' is illegal.  However, the PL/I
subroutine may declare it `char(*) varying' since a descriptor is
passed.

   Pointers.  Both packed and unpacked pointers are supported.  These
are both represented in lisp as fixnums in packed pointer format, that
is 2 octal digits of bit offset, 4 octal digits of segment number, and
6 octal digits of word offset.  The null pointer is 007777000001
octal.  It is not possible to reference, within lisp, what a pointer
points at.  Because of the packed pointer representation, ring numbers
in pointers are not supported.  If you declare the PL/I subroutine to
take unpacked pointers, which is the default, lisp will do the
conversion between packed and unpacked representations.

   Raw lisp objects.  A PL/I subroutine which knows about lisp may be
passed (or return) raw lisp objects.  In PL/I these should be declared
`fixed bin(71)' and then the based overlays declared in sundry lisp
include files should be used.  See section 14.6.

   Arrays.  Arrays of any number of dimensions may be passed.  The
arrays can only contain numbers or raw lisp objects however.  Usually
you would pass a lisp fixnum (or flonum) array and in PL/I declare it
`dimension(*,*) fixed bin(35)' (or float bin(27).)  In the dimension
attribute put as many stars as there are dimensions.  Proper matching
of types and dimensions will be checked at run time.

   There are certain pitfalls associated with arrays.  Arrays with
more than 15 dimensions may tend to lose, due to the format of PL/I
array argument descriptors.  Arrays as return or update arguments
(defined below) are not supported.  However, the lisp array is passed
by reference, so if the PL/I subroutine stores into elements of the
array the appropriate thing will happen.  If you are calling a Fortran
program, you need to be aware that Fortran reverses the order of the
subscripts of multidimensional arrays.

   Because lisp passes arguments by value, while PL/I passes arguments
by reference, it is necessary to pay attention to whether an argument
is input to the PL/I subroutine, output from (returned by) the PL/I
subroutine, or both (updated by the PL/I subroutine.)  `Output from'
includes both arguments that are stored into and values returned by a
return statement.  If the PL/I subroutine has a `returns' attribute,
this is considered to be an extra argument stuck on the end of the
argument list.  Note that PL/I `returns(char(*))',
`returns(dimension(*) fixed bin)', and similar constructs are not
supported because they use a non-standard calling sequence.

   Input arguments to the PL/I subroutine are derived from arguments
to the lisp subr which represents it according to the data type
transformations described above.

   Return arguments from the PL/I subroutine are passed back to lisp
according to the user's declaration; they may be ignored, setq'ed onto
an atomic symbol, or passed back as the value of the lisp subr.  If
more than one is passed back in the latter way, they are consed up
into a list.  If there are none, nil is returned.

   Update arguments are a combination of the two types described
above.  They are derived from the arguments to the lisp subr, and they
are also passed back like return arguments.

   Now the detailed syntax of the `defpl1' feature will be described.
It is invoked by using the defpl1 declaration in the lisp compiler, in
a form generally as follows (note that nothing in this "form" is
evaluated):

     (declare (defpl1 lisp-name external-name arg-dcl-1
                      arg-dcl-2 ... arg-dcl-n ))

lisp-name is an atomic symbol, which will be defined as a subr when
the output of the compilation is loaded.  This subr will take as many
arguments as the PL/I subroutine has input and update arguments.

   external-name is a string which is the name of the subroutine to be
called, as it would be written in PL/I.  If it is "", the pname of
lisp-name will be used so that you need not type the same thing twice.

   arg-dcl-1 through arg-dcl-n are lists.  Each one gives the
attributes of one of the arguments to the PL/I subroutine.  First you
must give attributes describing whether it is an input, update, or
return argument.  These are:

     <no attribute given>an input argument

     return              a return argument, passed back as the value
			 of the subr.

     return ignore       a return argument which is ignored.

     return (setq var)   a return argument to which the atomic symbol
			 var is setq'ed.  var should be declared special.

     update              an update argument, passed back as the value of
                         the subr.

     update ignore       an update argument whose returned value is ignored.

     update (setq var)   an update argument whose returned value var is
			 setq'ed to.

   Next you specify the data type attributes, in a form quite similar
to the way you would in PL/I.  (But don't forget that the declaration
of each argument is enclosed in its own pair of parentheses, instead
of being separated from the others with commas.)  The following
keywords are recognized for data type attributes:

         fixed       float       binary      bin
         bit         pointer     ptr         packed-ptr
         packed-pointer          character   char aligned
         unaligned   lisp        array       varying

   Note that `packed-pointer' is used rather than `pointer unaligned,'
and `array' is used rather than `dimension.'  `lisp' means a raw lisp
object.  Precisions, array extents, and string lengths are specified
as parenthesized numbers or asterisks, just as in PL/I.  Note that
unless you declare otherwise to the compiler or put a decimal point,
these numbers will be interpreted as octal.

   Here is an example, although not of a very useful case:

     (declare (defpl1 hcs_$initiate "" (char(*)) (char(*))
               (char(*)) (fixed bin(1)) (fixed bin(2))
               (return pointer) (return (setq code) fixed bin(35.))))

   If this was compiled and loaded into lisp, you could type

     (hcs_$initiate ">system_control_1" "whotab" "" 0 0)

and lisp would reply with a number such as 356000000, and code would
have been setq'ed to 0, presumably.  The "whotab" could then be
accessed via an external array (see chapter 9.)

   It is important to note that the defpl1 declaration is not known to
the interpreter.  A defpl1-defined function may be called by
interpreted lisp code, but the source of the defpl1 declaration must
nevertheless be compiled and loaded into the lisp environment before
it can be used.  For this reason, it is a good idea to keep defpl1's
and defun's in separate files.  The defpl1's may be placed in an
include file which is %include'd by the other file when it is
compiled, and may also be compiled separately when the interpreter is
to be used.

Node: Midas,	Up: Other Languages,	Previous: defpl1

   Midas can assemble FASL files that can be loaded by LISP in the
same manner as compiler output.  This mode is entered by the .FASL
pseudo-op, which must appear at the beginning of the file before any
storage words.

   After .FASL has been seen, the assembly becomes a two pass
relocatable assembly.  However, certain restrictions and "changes of
interpretation" apply.

   Global symbols (declared as usual with " or .GLOBAL) are
permissible.  However, since the output is to be loaded with fasload
using DDT's symbol table instead of STINK, there are quite a few
differences in detail.

   For symbols defined within the current assembly, the only effect of
being declared GLOBAL is that the GLOBAL information is passed on to
fasload when the symbol table is written at the end of pass 2.  This
in combination with the symbols switch in fasload determines whether
the symbol gets loaded into DDT's symbol table.  If symbols is nil, no
symbols will be loaded; if the value of symbols is the atomic symbol
symbols, only globals will be loaded; and if symbols is t, all symbols
(local and global) will be loaded.  Once the symbol is loaded (or
not), the information as to its GLOBALness is lost and, of course,
makes no further difference. The initial state when LISP is loaded is
nil.

   GLOBAL symbols not defined in the current assembly are also legal,
but there are additional restrictions as to where in a storage word
they may appear and what masking may be specified (as compared to a
normal relocatable assembly).  Briefly, they may appear in a storage
word as a full word, a right half, a left half, or an accumulator.
They may be negated, but can not be operated on with any other
operator.  Error printouts will be produced if they appear elsewhere.
When the symbol is encountered by fasload, DDT's symbol table is
consulted.  If it is not defined at that time, fasload will try to
find a sym property on the atomic symbol with the same name.

   Any sort of global parameter assignment or location assignment is
forbidden.  .LOP, .LVAL1, .LVAL2, etc are not available.


   The following pseudo-ops are available to facilitate the
communication between MIDAS assembled programs and LISP (particularly
with regard to list structure).

.ENTRY function type args

        Note that the arguments to this pseudo-op are separated by
     spaces, not commas.

        function is an atom and is taken as the name of a function
     beginning at the current location.  type should be one of SUBR,
     FSUBR, or LSUBR, and has the obvious interpretation.  args is a
     numeric-valued field which is passed through to fasload and used
     to construct the args property of the function.  If it is zero,
     no args property is created.  Otherwise it is considered to be a
     halfword divided into two 9-bit bytes, each of which is converted
     as follows:

              byte      result
              0         nil
              777       777
        otherwise       n n-1

     These two items are then cons'ed and form the args property.

The following pseudo-ops may appear in constants.

.ATOM atom

        Followed by a LISP atom in "MIDAS" format (see below). May
     only appear in right half (or entire word) of a storage word.
     Assembles into a pointer to the atom.

.SPECIAL atom

        Similar to .ATOM but assembles into a pointer to the (special)
     value cell of the specified atom.

.FUNCT atom

        Similar to .ATOM, but invokes special action by fasload in
     case the pure switch is on.  Normally used in function calls.
     Briefly, if fasload is going to purify the function it is
     loading, it must "snap the links" first.  If .FUNCT is used, the
     location will be examined by fasload and the link snapped if
     possible before purification.

           Typical usage:

              CALL 2,.FUNCT EQUAL     ;calls equal as a function of 2 args
                                      ; note: the CALL is not defined
                                      ; or treated specially by MIDAS.
                                      ; (but see .FASL DEFS below)

.ARRAY atom

        Similar to .ATOM, but assembles into a pointer to the array SAR.

.SX S-expression

        Similar to .ATOM, but handles a LISP S-expression. (See below).

.SXEVA S-expression

        Reads S-expression.  This S-expression is evaluated (for
     effect presumably) at fasload time.  The resulting value is
     thrown away.  Does not form part of storage word.

.SXE S-expression

        Similar to .SX but the S-expression is evaluated at fasload
     time.  The resulting value is assembled into the storage word.


   The MIDAS "LISP READER"

   By a conspiracy between MIDAS and fasload, a version of the LISP
reader is available.  However, due to historical reasons (mostly, i.e.
the fasload format was originally intended only to deal with COMPLR
type output), there are a number of "glitches" (see below for list).
These will probably tend to go away in the fullness of time.

a) numeric ATOM

   The first character of a LISP atom is examined specially. If it is
a # or &, the atom is declared to be numeric and either fixed (#) or
floating (&).  Midas then proceeds to input a normal numeric field
(terminated, note, by either space or comma).  This value is then
"stored" in the appropriate "space" (fixnum space or flonum space).

b) other ATOMs (also known as PNAME atoms or (LISP) SYMBOLS)

   If the first character of the atom is not # or &, the atom is a
"PNAME" atom.  / becomes a single character quote character as in
LISP.  The atom may be indefinitely long.  The atom will be terminated
by an unquoted space, carriage return, tab, (, ), or semicolon.
Unquoted linefeeds are ignored and do not become part of the atom.
The character that terminates the atom is "used up" unless it is a (
or ).  Note that period is a legal constituent of a atom and does not
terminate it or act specially.

c) lists

   Lists work normally, but note following caution relative to dot
notation: .  does not terminate atoms.  Thus, to invoke dot notation,
the dot must be left delimited by a space, tab, parenthesis, or other
character that does terminate atoms.

Glitches:

       1) Restriction on pass dependent list structure -- In any list
     reading operation, no new atoms not previously encountered may be
     encountered for the first time on pass 2. However, this
     restriction does not apply to atom- only reading operations
     (.ATOM, .SPECI, .FUNCT etc).

       2) Single quote for quoting does not exist (no other macro
     characters exist either.)

       3) Numbers must be flagged as above always.

             MOVEI A,.ATOM 123       ;LOSES - gives pointer
                                     ; to PNAME type atom
                                     ; with PNAME 123. it is
                                     ; not numeric.
     use:
             MOVEI A,.ATOM #123      ;wins

       4) No provision exists to reference "GLOBALSYMS" in fasload.
     This mostly means only that DDT must be present to load a MIDAS
     assembled FASL file.  (Some simple COMPLR and LAP FASL files can
     successfully be fasloaded by, for example, a disowned LISP
     running without a DDT.)

       5) LOC is illegal in a FASL assembly.  BLOCK of a
     non-relocatable quantity is ok.

       6) Currently, symbol loading is very slow.  Thus use (symbols
     nil), (the initial state) unless symbols are necessary.

       7) Midas does not know about any LISP symbols or UUOs
     specially.  You should `.INSRT SYS:.FASL DEFS'.  This file
     contains definitions of symbols for all LISP accumulators and
     UUOs, .GLOBAL declarations for all GLOBALSYMS, and definitions
     for some internal LISP macros such as LOCKI and UNLOCKI.  This
     file is guaranteed to be up to date since the assembly of LISP
     itself uses it.

       8) .ATOM "should" be a special case of .SX . However, it is
     handled separately because of the following "reasons": a) The
     previously noted restriction on pass dependent LISTS.  b) Midas
     can do constants optimization on atoms appearing in constants (on
     both pass one and pass two) but not on LISTS. Therefore, each
     list is guaranteed to take a separate word in the constants area
     even if it is identical to some other list which also appears in
     a constant.  c) Each list takes an additional entry in fasload's
     "atom" table.  This is a temporary table that is flushed after
     the fasloading is complete.  Of course, .SX still works for atoms
     modulo the above noted restrictions and inefficencies.
