perm filename MCMAN4.S78[206,LSP]1 blob sn#345122 filedate 1978-03-31 generic text, type T, neo UTF8
**DRAFT**                         The Compiler                         **DRAFT**

                             Part 4 - The Compiler

                               Table of Contents

1.      Peculiarities of the Compiler  . . . . . . . . . . . . . . . . . . . 4-1
1.1     Variables  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-1
1.2     In-line (or Open) Coding . . . . . . . . . . . . . . . . . . . . . . 4-3
1.3     Function Calling . . . . . . . . . . . . . . . . . . . . . . . . . . 4-5
1.4     Input to the Compiler  . . . . . . . . . . . . . . . . . . . . . . . 4-7
1.5     Output of the Compiler . . . . . . . . . . . . . . . . . . . . . . . 4-8
1.6     Functions Connected with the Compiler  . . . . . . . . . . . . . . .4-10

2.      Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-11

3.      Running Compiled Functions . . . . . . . . . . . . . . . . . . . . .4-17

4.      Running the Compiler . . . . . . . . . . . . . . . . . . . . . . . .4-19

5.      The Lisp Assembly Program, LAP . . . . . . . . . . . . . . . . . . .4-25
5.1     LAP on the pdp-10  . . . . . . . . . . . . . . . . . . . . . . . . .4-25
5.1.1   The LAP Function . . . . . . . . . . . . . . . . . . . . . . . . . .4-25
5.1.2   Valid LAP Code Forms . . . . . . . . . . . . . . . . . . . . . . . .4-27
5.1.3   LAP Syllables  . . . . . . . . . . . . . . . . . . . . . . . . . . .4-31
5.1.4   Differences Between lap and faslap . . . . . . . . . . . . . . . . .4-35
5.2     LAP on Multics . . . . . . . . . . . . . . . . . . . . . . . . . . .4-36
5.2.1   LAP Words  . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-36
5.2.2   LAP Instructions . . . . . . . . . . . . . . . . . . . . . . . . . .4-37
5.2.3   LAP Operands . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-39
5.2.4   LAP Expressions  . . . . . . . . . . . . . . . . . . . . . . . . . .4-40
5.2.5   Using LAP  . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-41

6.      Calling Programs Written in Other Languages  . . . . . . . . . . . .4-43
6.1     The defpl1 declaration . . . . . . . . . . . . . . . . . . . . . . .4-43
6.2     Producing fasloadable files with the Midas Assembler  . . . . . . . 4-48

February 7, 1977                                                        Page 4-1
**DRAFT**                                                              **DRAFT**

1.  Peculiarities of the Compiler

   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;
functions which modify themselves.  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; therefore  erroneous compiled programs can damage  the LISP
environment and  can cause strange  errors to occur  - be  forewarned.  However,
most "normal" programs are compilable.

   Some operations  are compiled in  such a way  that they will  behave somewhat
differently than 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 page 4-11.

1.1  Variables

   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 has no associated
atomic symbol; thus it can only be referred to from the function  that possesses
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.

February 7, 1977                                                        Page 4-1
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

   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.  Storage is assigned and 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  is 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
this rule, i.e. it is somewhere used as a "free variable," the variable  must be
declared special.  The necessary special declaration of such a variable provides
a convenient place to put  a comment explaining and and defending  its non-local
usage.   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.

Page 4-2                             ∪4-1.1                     February 7, 1977
**DRAFT**                Peculiarities of the Compiler                 **DRAFT**

1.2  In-line (or Open) Coding

   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 to perform 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, then 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

   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 two  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.  The second solution  is to
write a form such as (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 page 4-11.

February 7, 1977                     ∪4-1.2                             Page 4-3
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

   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.

Page 4-4                             ∪4-1.2                     February 7, 1977
**DRAFT**                Peculiarities of the Compiler                 **DRAFT**

1.3  Function Calling

   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
proclaiming 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 now 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  part 3.5.) 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

February 7, 1977                     ∪4-1.3                             Page 4-5
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

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

   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 page 4-17.

Page 4-6                             ∪4-1.3                     February 7, 1977
**DRAFT**                Peculiarities of the Compiler                 **DRAFT**

1.4  Input to the Compiler

   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, 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  defun or  defprop.   If the
function 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 random form  is anything read  from the input file  that is not  a function
definition  or a  declare.  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.

   A declare  is a  form whose  car is  the atom  declare.  See   for how  it is
processed by the compiler.  It is ignored by the interpreter because there is an
fsubr called declare in Maclisp which does nothing.

   Note that if a form is read from the input file and its car has  been defined
as a macro, 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.

February 7, 1977                     ∪4-1.4                             Page 4-7
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

1.5  Output of the Compiler

   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, create "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 part
6.5 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 builds 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 one file has to go on the same obarray.

Page 4-8                             ∪4-1.5                     February 7, 1977
**DRAFT**                Peculiarities of the Compiler                 **DRAFT**

   The coutput  function usually does  not have to  be used, since  the compiler
coutputs  any form  it reads  from  the input  file that  does not  look  like a
declaration or a function definition.  It is provided for the benefit of certain
hairy macros.

February 7, 1977                     ∪4-1.5                             Page 4-9
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

1.6  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.

        (declare ((lambda (↑w) (princ "Now compiling fubar"))

%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.

     Note:  this function presently exists only in the Multics implementation.

     See also the nouuo switch, part 3.5.

Page 4-10                            ∪4-1.6                     February 7, 1977
**DRAFT**                         Declarations                         **DRAFT**

2.  Declarations

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

   A  declare is  a  list whose  first element  is  the atom  declare  and whose
remaining elements  are forms  called "declarations."  The compiler  processes a
declare 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  or  at  read-in  time  if  the  file  was interpreted.
Arbitrarily complex compile-time processing  may be achieved by  the combination
of declarations and macros.

   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.

February 7, 1977                     ∪4-2.                             Page 4-11
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

(*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.   See the
     note under *expr.

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

(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 (f00 fixnum) y).

(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.

Page 4-12                            ∪4-2.                      February 7, 1977
**DRAFT**                         Declarations                         **DRAFT**

(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  so  (setq  fixsw  t)  is  an  equivalent
     declaration to (fixsw t).

(setq special t)
     Causes all variables to be 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

(macros t)
     Causes macro definitions to be retained at run time.

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

(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.  A reasonable convention would be to use the name of
     a file as the  genprefix within that file.   If you don't give  a genprefix
     declaration, the compiler uses !g or something like that.

February 7, 1977                     ∪4-2.                             Page 4-13
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

(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) ...)) can be used.  It 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  store's 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  rather than  by  an atomic

(arith (type1 fcn1 fcn2 ...) (type2 fcn21 fcn22 ... ) ... )
     Is  used  to declare  a  general arithmetic  function  such as  plus  to be
     replaced by a one-type arithmetic function such as +.  fcn1, fcn2, etc. are
     the  functions to  be replaced.   type1, etc.  is the  type of  function to
     replace them with: fixnum means replace them with the corresponding fixnum-
     only functions, e.g. replace plus by +.  flonum means replace them with the
     corresponding flonum-only functions, e.g. replace plus by +$.  notype means
     turn off a previous arith declaration for these functions.

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

(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

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

Page 4-14                            ∪4-2.                      February 7, 1977
**DRAFT**                         Declarations                         **DRAFT**

(noargs t)
     Causes the compiler not to output information as to the number of arguments
     each function compiled takes; this provides some saving of memory  space in
     non-Bibop pdp-10 implementations.

(noargs nil)
     Causes the compiler to output number of arguments information.  This is the

(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.

February 7, 1977                     ∪4-2.                             Page 4-15
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

   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 part 4.6 for details.

Page 4-16                            ∪4-2.                      February 7, 1977
**DRAFT**                  Running Compiled Functions                  **DRAFT**

3.  Running Compiled Functions

   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
on part 3.4.

The  following   function  is   at  present  available   only  in   the  Multics

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.

                         (fasload foo fasl dsk macsym)

The following function only exists in the Multics implementation.

February 7, 1977                     ∪4-3.                             Page 4-17
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

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.

            (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


     See  part 6.6  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.

Page 4-18                            ∪4-3.                      February 7, 1977
**DRAFT**                     Running the Compiler                     **DRAFT**

4.  Running the Compiler

                         in the Multics implementation

   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>

reads the file "dir>" 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

     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.

February 7, 1977                     ∪4-4.                             Page 4-19
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

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

     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

-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

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

-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

-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.

                        in the ITS pdp-10 implementation

   The ITS compiler is presently in an anomalous state.  There are two versions,
COMPLR and NCOMPLR.   NCOMPLR contains the fast-arithmetic  facilities described

Page 4-20                            ∪4-4.                      February 7, 1977
**DRAFT**                     Running the Compiler                     **DRAFT**

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.

   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.

February 7, 1977                     ∪4-4.                             Page 4-21
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

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
V       (nfunvarsf1)  Equivalent to (declare (nfunvars t)); disallows functional
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.

Page 4-22                            ∪4-4.                      February 7, 1977
**DRAFT**                     Running the Compiler                     **DRAFT**

   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.

   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.

February 7, 1977                     ∪4-4.                             Page 4-23
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

Page 4-24                            ∪4-4.                      February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

5.  The Lisp Assembly Program, LAP

   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.

5.1  LAP on the pdp-10

   The lisp compiler for the pdp-10 implementation does not output binary object
files  directly;  rather, it  outputs  a series  of  S-expressions  denoting the
machine-language instructions of the compiled function.  There are  two programs
which accept  such S-expressions  and convert them  to binary  machine language,
called lap  and faslap.  (Historical  note:  the word  "lap" dates back  to 7090
LISP, and is derived from the phrase "Lisp Assembly Program".) lap is an in-core
assembler; it reads in the  S-expressions (hereafter referred to as  "lap code")
and deposits the  resulting binary instructions in  the binary program  space of
the current lisp environment.   faslap, on the other  hand, takes a file  of lap
code  and produces  a binary  file  suitable for  use with  fasload.   faslap is
normally part  of the  pdp-10 lisp  compiler, but  in some  implementations with
limited memory it  can be a separate  program.  Both assemblers will  accept the
same  lap  code, except  for  certain peculiar  conditions.   This  section will
describe the lap function and lap code; differences between lap and  faslap will
be treated in a special section.

5.1.1  The LAP Function

   lap is an fsubr which is executed primarily for its side effect -  loading in
a binary program.   It accepts a  series of S-expressions  similar in form  to a
program  written  in MIDAS  or  MACRO-10.  It  is  not intended  to  be  a fancy
assembler: it  does not  have conditional assembly,  macros, or  complex literal
generation features.   It does,  however, contain sufficient  power to  load the

February 7, 1977                     ∪4-5.                             Page 4-25
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

output of the compiler, plus enough extra features that  simple machine-language
functions may be hand-coded in it.  (See the section on conventions  for writing
lap code by hand on part 6.6.  The major operational differences between lap and
MIDAS or MACRO-10 are that (1)  lap is one-pass, while the others take  two, (2)
lap  uses  the function  read  to input  lap  code, while  the  others  are more
efficient, and (3) lap assembles  directly into the lisp environment,  while the
others produce a binary object file (note that faslap differs only in  the first
two respects).

   The lap  function is  an fsubr  which expects  to get  two atomic  symbols as
arguments; the first is the name of the function to be assembled, and the second
is the type (i.e.  the property under which it  is to be stored on  the property
list.) Thus

                                (lap quux subr)

would assemble a subr called quux.  When invoked, lap repeatedly calls  the read
function,  operating  on  the  S-expressions  thus  obtained,  until  a  nil  is
encountered, at which time the assembly ends.  Some messages may be  printed out
as this happens.  If the assembly completed successfully, the variable  bporg is
updated to  reflect the new  size of binary  program space, and  the appropriate
property is placed on the property list of the specified atomic symbol.

   Normally, lap does not reside in the initial lisp system, though  the initial
system does contain several specialized functions for use by lap.   Instead, lap
has an autoload property of (lap fasl com) on ITS and (lap fasl sys) on the DEC-
10 system.   Thus, if  one simply  reads in  a file  of lap  code lap  will load
automatically and assemble the functions.

   Here is an  example of some  lap code which  corresponds roughly to  the lisp
function memq:

          (ARGS FUNNY-MEMQ (NIL . 2))
      MEMBEG  (JUMPE B MEMEND)   ;result nil if arg 2 nil
              (HLRZ T 0 B)       ;else look at car of arg 2
              (CAIN T 0 A)
               (JRST 0 MEMEND)   ;win if same as arg 1
              (HRRZ B 0 B)       ;else take cdr of arg 2
              (JRST 0 MEMBEG)    ; and try again
      MEMEND  (MOVEI A 0 B)      ;return arg 2
              (POPJ P)           ;exit from function

Page 4-26                           ∪4-5.1.1                    February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

   Note that this is  greatly different in style  from 7090 lap, which  took the
entire program as one argument, and  a symbol table as the other.   The drawback
with the 7090  method is that the  entire program must be  read in before  it is
assembled; this  can require prohibitively  large amounts of  memory, especially
for the lap output from compilation  of a large lisp function.  The  method used
by pdp-10 lap is much  more reasonable in practical situations (e.g.  reading in
lap code from a file).

   lap does not use an a-list  for its symbol table, either.  Rather,  the value
of the  symbol is  stored on  the property  list under  the sym  property.  Thus
(defprop ztesch  43 sym) would  make the  symbol ztesch known  to lap,  with the
value 43.  lap has a number of symbols initially defined, including the names of
all the  accumulators, and  the addresses  of some  useful routines  internal to
lisp.   It also  uses  the function  getmidasop on  a  symbol if  the  symbol is
otherwise  undefined  to  determine  whether it  is  a  pdp-10  instruction (the
getmidasop function contains a concise table of all pdp-10 instructions and most
monitor calls, as well as names of UUO's used internally by lisp).  In  this way
lap can  recognize all  standard instruction mnemonics  without defining  400 or
more sym properties.  If lisp's symbol table has been loaded into DDT,  then lap
will ask DDT about the values of symbols as a last resort.  In this manner hand-
coded lap code may refer to  any location internal to lisp (with  an appropriate
amount of caution, of course).

   When lap terminates, it returns as its value a list of the new value of bporg
and the entry  point(s) of the function  defined (hand-coded functions  may have
more than one entry point).  If any symbols were undefined or  multiply defined,
they  will be  printed  out first.   It  is generally  a  good idea  to  let lap
terminate naturally,  rather than quitting  out of it,  since it hacks  the lisp
environment in various peculiar ways.

5.1.2  Valid LAP Code Forms

   lap acts on the S-expressions it reads as follows:

     Terminate assembly and return.   Any literals generated are  assembled into
     memory  at  the  end  of the  function,  temporary  symbol  definitions are
     flushed,  and (gctwa  t) is  evaluated.  The  nil should  be followed  by a
     space,  because  carriage  return  is not  always  an  atom  separator.  In
     particular, the compiler depends on there being a space there.

February 7, 1977                    ∪4-5.1.1                           Page 4-27
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

atomic symbol

     Assign to the atomic symbol  (non-nil, of course) a temporary  sym property
     equal to the address of the next word to be assembled into.  Thus  one uses
     atomic symbols as location tags.  If (symbols t) is in effect lap will pass
     the value of this symbol to DDT if lisp's symbol table has been loaded.

(defsym atom1 value1 ... atomn valuen)

     For each i define  atomi with a sym  property of (eval valuei).   No binary
     words are generated,  and these symbols are  not passed to DDT.   Note that
     this performs a lisp evaluation, not a lap evaluation!

(entry name type)  or  (entry name)

     Defines the atomic  symbol name to  be a function  of type type  with entry
     point at the  current location.  If type  is not specified, it  defaults to
     the second argument to  lap, i.e. the type  of the principal entry  to this
     function.  No  binary words are  generated.  This form  is not used  by the
     output of the lisp compiler, and  is provided only as an aid to  writers of
     hand-coded  lap code.   It allows  several functions  to share  symbols and
     storage areas.

(args atom args-prop)

     If the assembly  terminates successfully, then  atom gets args-prop  as its
     args  property.   atom  must  be  an  entry  point  of  the  function being
     assembled.  No binary words  are generated.  The pdp-10 lisp  compiler will
     output this declaration in  each function compiled unless  (declare (noargs
     t)) or the N switch  is given.  faslap requires that each  args declaration
     follow the corresponding entry point.

(comment ...)

     This form is totally ignored.  Of course, since the lisp reader is  used to
     input lap code, semicolon comments may be used as well.

Page 4-28                           ∪4-5.1.2                    February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

(eval form1 ... formn)

     Applies  the function  eval to  each  form in  turn.  No  binary  words are
     generated.  This is  useful for such things  as (eval (setq ibase  23.)) or

(symbols t-or-nil)

     Controls the  lap feature which  tries to pass  symbolic location  names to
     DDT.  Furthermore, if the symbols pseudo-operation occurs anywhere within a
     given lap  function, the names  and locations of  the entry points  of that
     function will also be passed to DDT.  No binary words are  generated.  Note
     that there  is possibility for  confusion here because  lap will  accept as
     tags atomic symbols with names of any length, while MIDAS and  DDT truncate
     tags to six characters.  Thus quuxbar and quuxbaz are two different symbols
     to  lap, but  will interfere  with  each other  when passed  to  DDT.  When
     symbols are passed to DDT,  the names are truncated to six  characters, and
     any non-squoze character (a character other  than A-Z, 0-9, ., $, or  %) is
     assumed to be a dot.  Since  dots must be slashified to be read  into lisp,
     the  standard  convention  is  to  use  *  (the  lisp  compiler  uses  this
     convention).   Thus  one would  write  (JSP  D *LCALL)  instead  of  (JSP D

(ddtsyms atom1 atom2 ... atomn)

     Ordinarily lap asks ddt for the values of any symbols which  are undefined.
     If this declaration appears, lap will get the values of atom1 through atomn
     from ddt, and any other undefined symbols will be considered as errors.

(block fixnum)

     Assembles a block fixnum words long, containing zeros.

(ascii S-expression)

     explodec's  the S-expression  and  assembles the  characters  obtained into
     successive words, five per word, in ASCII code.

February 7, 1977                    ∪4-5.1.2                           Page 4-29
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

(sixbit S-expression)

     Similar to ascii, but characters are assembled six per word in  sixbit code
     (ascii characters between 40 and 137 are represented as 0 to 77).

(squoze atom) or (squoze fixnum atom)

     Produces a word  of squoze code, which  is a left-justified  radix-50 code,
     from the  first six characters  of the  print name of  atom.  If  fixnum is
     present, then it is divided by 4 and the low four bits of the quoutient are
     added into the high four  bits of the squoze value (the  MIDAS convention).
     The squoze  word generated is  in ITS format,  which is different  from DEC

(begin temp-syms-list form)

     The form is evaluated (as a  lisp form, not a lap expression).   The result
     should be a list of valid LAP code forms, which will be assembled one  at a
     time.   They may  use symbols  which appear  in the  temp-syms-list.  These
     symbols  will have  a local  scope confined  to the  begin, i.e.  their sym
     properties will be saved and restored.  This provides a primitive macro and
     conditional assembly facility.

any other list

     Assembles a  single word,  which is assumed  to be  an instruction  of some
     kind.  First, if the list contains the atomic symbol @, it is  deleted from
     the  list  and saved.   Then  the first  four  components of  the  list are
     processed in order.  These  must all be lap "syllables"  (described below).
     If the length of the list  is less then four, missing elements  are assumed
     to  be zero.   If the  length  of the  list is  more than  four,  the extra
     elements are ignored.  The four elements of the list are assumed to  be, in
     order, the operation code, accumulator, address, and index fields of a pdp-
     10  instruction.  These  are  evaluated by  the lap  syllable  evaluator to
     obtain four numbers, which are then added together after being  modified as

Page 4-30                           ∪4-5.1.2                    February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

                opcode no change
                accumulator shift left 23. places
                address clear left half
                index swap halves

     Finally,  if the  atom @  had been  present, the  octal number  20000000 is
     logically or'ed into the result, thus turning on the indirection bit.  Note
     that neither the accumulator nor the index field is truncated to four bits.
     This has many useful applications; see, for example, the description of the
     specbind routine below.

   There  is  a  fairly  strong  similarity  between  code  written  in  lap and
equivalent code written in MIDAS or MACRO-10.  The essential difference  is that
lap processes assembly fields in order from left to right in order  to determine
which field  is which.   One pitfall to  avoid is  writing such  instructions as
(JRST FOO) or (SETZM FOO) when one intends rather (JRST 0 FOO) or (SETZM 0 FOO).
Another difference to  remember is that  lap uses the  lisp reader to  input lap
code; thus  one must remember  to put spaces  around an @,  and that  one cannot
write (JRST 0 FOO+3) unless  FOO+3 really is a tag!  (For  arithmetic operations
within assembly fields,  see the description of  lap syllables below.) If  it is
desired to make lap code look more like the standard assembly languages, one may
use the  fact that comma  is like a  space to lisp,  and that  extra parentheses
don't hurt, and write (MOVE A,TABLE(D)) instead of (MOVE A TABLE D).  Be sure to
remember that  the index  field is  in the left  half because  it is  the fourth
component, not because it is in parentheses.

5.1.3  LAP Syllables

   Each  of the  four components  of  assembly words  are evaluated  by  the lap
evaluator to  produce numeric  quantities; these  are then  combined to  form an
assembly word.  Note that @ is treated specially and is not a  component.  Forms
to be evaluated by the lap evaluator are called lap syllables.  Valid  forms for
lap syllables are as follows:

a number

        Fixnums  evaluate  to  themselves,  and  may  be  operated  upon  by lap
     arithmetic operations.  Flonums also evaluate to themselves, but arithmetic

February 7, 1977                    ∪4-5.1.2                           Page 4-31
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

     operations  on them  will not  work.   Flonums should  not be  used  in the
     address field, because the left  halves will be truncated off;  they should
     be  used only  in the  opcode or  index fields  (the latter  is  useful for
     writing (FADRI 7 0 3.0) or something like that).


        same as (quote nil).


        Evaluates to the address of the word into which the  current instruction
     will be assembled.  Equivalent to . in MIDAS and MACRO-10 (however, see the
     note below about literals).

an atomic symbol

        Any atomic  symbol other than  @, *, and  nil evaluates to  its assembly
     symbol value.  That is,  if the symbol has a  sym property, then it  is the
     value of  that property;  otherwise, the value  returned by  the getmidasop
     function if  non-nil; otherwise,  the value  which DDT  assigns to  it.  An
     error occurs if no value can be found for a symbol.

(quote S-expression)

        Protects  S-expression from  garbage  collection, and  evaluates  to the
     address of the S-expression.  Thus (MOVEI A '(A B)) puts the address of the
     S-expression  (A  B)  into accumulator  A.   Warning:  faslap  permits this
     syllable only in the address field.

(function S-expression)

        Same as  (quote2 S-expression),  but emphasizes  that S-expression  is a
     function.  Thus one might write (CALL 2 (FUNCTION CONS)).

Page 4-32                           ∪4-5.1.3                    February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

(special atom)

        Evaluates to the  address of the value  cell of the atomic  symbol atom.
     If atom does not have a value cell, one is created for it first.  Thus, for

                (MOVE A (SPECIAL QUUX))
                (MOVEM A (SPECIAL ZTESCH))

     Accomplishes the  equivalent of  (SETQ ZTESCH  QUUX).  faslap  permits this
     syllable only in the address field.

(array atom)

        Evaluates to  the address  of the  TTSAR of  the array  which is  on the
     property list of atom.  If atom is not yet an array, a dummy array property
     is created.   This is  of use  for open-coded  array accessing.   The TTSAR
     contains  a pointer  to the  array which  has an  index field  of  TT.  The
     garbage  collector  knows  to  alter this  pointer  whenever  the  array is
     relocated.  Typically one operates  on an array by putting  the appropriate
     index in TT and then indirecting through the TTSAR.

(ascii S-expression)

        Evaluates to a 36-bit quantity consisting of the ascii representation of
     the first five explodec'd characters of S-expression.  Note that  the ascii
     pseudo-op  may generate  several binary  words as  a lap  form, but  only a
     single-word quantity as a syllable.

(sixbit S-expression)

        Like ascii,  but uses  the first  six characters  and produces  a sixbit
     representation quantity.

(squoze atom) or (squoze fixnum atom)

        (ITS only) Similar to  the same form as a  lap form: produces a  word of
     squoze code as its value.

February 7, 1977                    ∪4-5.1.3                           Page 4-33
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

(+ lapsyl1 lapsyl2 ... lapsyln)

        Adds together the values  of the lap syllables lapsyl1  through lapsyln.
     (Thus note that lap syllables are defined recursively.) This allows  one to
     write such things as (JRST 0 (+ FOO 3)).

(- lapsyl)

        Evaluates to the negative of the value of lapsyl.

(- lapsyl1 lapsyl2 ... lapsyln)

        Subtracts the values of  the lap syllables lapsyl2 through  lapsyln from
     the value of the lap syllable lapsyl1.

(lapsyl1 lapsyl2 ... lapsyln)

        Same as (+ lapsyl1 lapsyl2 ... lapsyln).


        Evaluates to the value of lapsyl.  It most definitely does  not evaluate
     to  the swapped-halves  value of  lapsyl, as  some might  think!   When one
     writes (MOVE A,FOO(B)), the  value of B gets  swapped because it is  in the
     index field, and not because it is in parentheses.

(% lap assembly word)

        Generates a literal; i.e. the  lap assembly word is saved  and assembled
     at the end of  the function.  The value of  the syllable is the  address of
     this remotely generated word.  lap assembly word must be an instruction, or
     one of  the ascii, sixbit,  or block pseudo-ops.   (The block  pseudo-op is
     relatively  useless here.)  Thus,  for example,  (MOVEI T  (%  SIXBIT LONG-
     MESSAGE!))  is  perfectly valid.   Note:   *  in a  literal  refers  to the
     location of the literal, not of the referencing instruction.  Thus (JUMPE A
     (% AOJA T  (+ * 1))) will  not do what you  might expect from  using MIDAS.
     Finally, faslap permits literals only in the address field.

Page 4-34                           ∪4-5.1.3                    February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

5.1.4  Differences Between lap and faslap

   Much effort has been  made to keep lap  and faslap compatible.  There  are of
necessity, however,  some differences.   faslap reads the  lap code  at assembly
time, and not at load time,  which means that read macro characters  and obarray
hackery may not  happen at the  right time.  faslap  and fasload cooperate  in a
scheme to gain  speed by calling  the function intern  only once on  each atomic
symbol needed by a file of functions; faslap creates a table of such symbols and
passes them when  encountered into the binary  file.  This means  that switching
obarrays in the middle of a fasload file will lose.

   There  are also  some  internal differences  due  to the  different  modes of
operation.  As an in-core assembler, lap does not need to worry  about questions
relating to relocatability.   faslap, however, does not  know where in  memory a
binary file will be loaded, and thus must produce relocatable binary code.  This
implies that faslap must  distinguish between relocatable and  absolute symbols.
This is done by using  non-numeric sym properties for relocatable  symbols;  the
user who hand-codes lap code and  expects to look at sym properties  at assembly
time should be aware of this.

   faslap furthermore does  not know into what  version of lisp the  binary file
will be loaded.  This  poses a problem because  compiled code needs to  refer to
routines and locations  internal to lisp, such  as FLOAT1 and ERSETUP.   This is
solved by the so-called globalsym  convention; these labels, which for  lap have
numeric sym properties,  in faslap have  non-numeric sym properties,  and direct
faslap to output directions to fasload to find the correct value of a symbol for
the lisp being loaded into.  For most purposes such symbols should be treated as
a funny kind of relocatable symbol.

   faslap imposes some restrictions on the use of certain  constructs.  Multiple
and negative relocatability is  not permitted.  Relocatable symbols,  the quote,
function, special, and sar0 constructs,  and literals are permitted only  in the
address field of an instruction.

February 7, 1977                    ∪4-5.1.4                           Page 4-35
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

5.2  LAP on Multics

   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.

5.2.1  LAP Words

   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

(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.

Page 4-36                            ∪4-5.2                     February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

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

(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.
      See page 4-42.

(get-linkage)  loads the  lb  register with  a  pointer to  the  Multics linkage
      section.  The external operand (refer  to page 4-40) 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.

5.2.2  LAP Instructions

   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  page 4-40.) 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.

February 7, 1977                    ∪4-5.2.1                           Page 4-37
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

   The allowed formats for ordinary instructions are:

              (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,

              (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

Page 4-38                           ∪4-5.2.2                    February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

              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 part 6.6 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.

5.2.3  LAP Operands

   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:

February 7, 1977                    ∪4-5.2.2                           Page 4-39
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

           (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 part 6.6.

(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.   One calls a  function by
      pushing the arguments onto the  ap stack, then doing the  call instruction
      indirect through the function-link created by the function  operand.  Upon
      return, the arguments have been popped and the result is in the aq.

(function ap|n type nargs) refers to  a computed function, located in a  cell in
      the  stack.  This  is used  instead of  simply tspbp  ap|-n,* so  that the
      interpreter can get invoked if it is not a compiled function.

(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

5.2.4  LAP Expressions

   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

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.

Page 4-40                           ∪4-5.2.3                    February 7, 1977
**DRAFT**                The Lisp Assembly Program, LAP                **DRAFT**

* 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.

5.2.5  Using LAP

   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:

February 7, 1977                    ∪4-5.2.4                           Page 4-41
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

(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.

Page 4-42                           ∪4-5.2.5                    February 7, 1977
**DRAFT**         Calling Programs Written in Other Languages          **DRAFT**

6.  Calling Programs Written in Other Languages

6.1  The defpl1 declaration

   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

   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(*).'

February 7, 1977                     ∪4-6.                             Page 4-43
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

   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

   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

Page 4-44                            ∪4-6.1                     February 7, 1977
**DRAFT**         Calling Programs Written in Other Languages          **DRAFT**

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

   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

   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

February 7, 1977                     ∪4-6.1                            Page 4-45
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

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

     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

     update ignore       an update argument whose returned value is ignored.

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

   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

         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

   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)

Page 4-46                            ∪4-6.1                     February 7, 1977
**DRAFT**         Calling Programs Written in Other Languages          **DRAFT**

   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 page 2-85).

   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

February 7, 1977                     ∪4-6.1                            Page 4-47
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

6.2  Producing fasloadable files with the Midas Assembler

   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

.ENTRY function type args

Page 4-48                            ∪4-6.2                     February 7, 1977
**DRAFT**         Calling Programs Written in Other Languages          **DRAFT**

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

        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

              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.


        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)

February 7, 1977                     ∪4-6.2                            Page 4-49
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

.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.


   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

Page 4-50                            ∪4-6.2                     February 7, 1977
**DRAFT**         Calling Programs Written in Other Languages          **DRAFT**

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


  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

  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.
             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

  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

February 7, 1977                     ∪4-6.2                            Page 4-51
**DRAFT**                   Maclisp Reference Manual                   **DRAFT**

     `.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

  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.

Page 4-52                            ∪4-6.2                     February 7, 1977