Trailing-Edge - PDP-10 Archives - decuslib20-01 - decus/20-0004/21lisp.tty
There are no other files named 21lisp.tty in the archive.

                               SECTION 21


21.1 Measuring Functions

                        is  an   nlambda  function.   It   executes  the
                        computation timex, and prints out the  number of
                        conses and computation time.  Garbage collection
                        time is subtracted out.

                        _TIME((LOAD (QUOTE PRETTY) (QUOTE PROP]
                        FILE CREATED 7-MAY-71 12:47:14

                        GC: 8
                        582, 10291 FREE WORDS
                        3727 CONSES
                        10.655 SECONDS

                        If timen is greater than 1 (timen=NIL equivalent
                        to timen=1), time executes timex timen number of
                        times and prints out number of conses/timen, and
                        computation time/timen.  This is useful for more
                        accurate measurement on small computations, e.g.

                        _TIME((COPY (QUOTE (A B C))) 10)
                        30/10 = 3 CONSES
                        .055/10 = .0055 SECONDS
                        (A B C)

    Some of the  functions in this  section are TENEX  or implementation
    dependent.  They  may not  be provided  in other  implementations of


                        If timetype is 0, time measures and prints total
                        real time as well as computation time, e.g.

                        _TIME((LOAD (QUOTE PRETTY) (QUOTE PROP)) 1 0]
                        FILE CREATED 7-MAY-71 12:47:14

                        GC: 8
                        582, 10291 FREE WORDS
                        3727 CONSES
                        11.193 SECONDS
                        27.378 SECONDS, REAL TIME

                        If timetyp = 3, time measures and prints garbage
                        collection  time  as well  as  computation time,

                        _TIME((LOAD (QUOTE PRETTY) (QUOTE PROP)) 1 3]
                        FILE CREATED 7-MAY-71 12:47:14

                        GC: 8
                        582, 1091 FREE WORDS
                        3727 CONSES
                        10.597 SECONDS
                        1.487 SECONDS, GARBAGE COLLECTION TIME

                        Another option is timetype=T, in which case time
                        measures and prints the number of pagefaults.

                        The  value  of time  is  the value  of  the last
                        evaluation of timex.

date[]                  obtains date  and time,  returning it  as single
                        string in  format "dd-mm-yy hh:mm:ss",  where dd
                        is  day,  mm is  month,  yy year,  hh  hours, mm
                        minutes, ss seconds, e.g., "14-MAY-71 14:26:08".

idate[d]                d is a time and date string.  Value of  idate is
                        d  converted  to a  number  such that  if  d1 is
                        before  (earlier  than)  d2,  then  idate[d1]  <

    In INTERLISP-10, date  will accept a value  for ac3 as  an argument.
    ac3 can be  used to specify other  formats, e.g., day of  week, time
    zone, etc., as described in TENEX JSYS manual.


clock[n]                for n=0   current value of the time of day clock
                                  i.e.,  number  of  milliseconds  since
                                  last system start up.

                        for n=1   value of  the time  of day  clock when
                                  the  user started  up  this INTERLISP,
                                  i.e., difference between  clock[0] and
                                  clock[1]  is  number  of  milliseconds
                                  (real time)  since this  INTERLISP was

                        for n=2   number of milliseconds of compute time
                                  since user  started up  this INTERLISP
                                  (garbage collection time is subtracted

                        for n=3   number of milliseconds of compute time
                                  spent  in  garbage   collections  (all

dismiss[n]              In   INTERLISP-10,  dismisses   program   for  n
                        milliseconds, during which time program is  in a
                        state similar to an  I/O wait, i.e., it  uses no
                        CPU time.  Can be aborted by control-D, control-
                        E, or control-B.

conscount[n]            conscount[] returns  the number of  conses since
                        INTERLISP started up.   If n is not  NIL, resets
                        conscount to n.

boxcount[type;n]        In  INTERLISP-10,  number  of  boxing operations
                        (see Section 13) since INTERLISP started up.  If
                        type=NIL, returns number of large integer boxes;
                        type=FLOATING,   returns   number   of  floating
                        boxes.    If   n   is   not   NIL,   resets  the
                        corresponding counter to n.

gctrp[]                 number of conses to next GC: 8, i.e.,  number of
                        list words not in use.  Note that an intervening
                        GC  of another  type  could collect  as  well as
                        allocate additional list words.  See Section 3.

    In INTERLISP-10, this number is directly accessible via  the COREVAL

    In  INTERLISP-10, these  counters  are directly  accessible  via the


                        gctrp[n] can be used to cause an  interrupt when
                        value of gctrp[]=n, see Section 10.

pagefaults[]            In  INTERLISP-10,  number of  page  faults since
                        INTERLISP started up.

logout[]                returns   control  to   operating   system.   In
                        INTERLISP-10, a subsequent CONTINUE command will
                        enter  the INTERLISP-10  program, return  NIL as
                        the value  of the call  to logout,  and continue
                        the  computation  exactly  as  if   nothing  had
                        happened,   i.e.,  logout   is   a  programmable
                        control-C.  As with control-C, a REENTER command
                        following a logout will reenter  INTERLISP-10 at
                        the top level.

                        logout[] will not  affect the state of  any open

21.2 Breakdown

Time gives analyses by  computation.  Breakdown is available  to analyze
the breakdown  of computation time  (or any other  measureable quantity)
function by  function.  The  user calls  breakdown giving  it a  list of
functions of interest.  These  functions are modified so that  they keep
track of the "charge" assessed to them.  The function results  gives the
analysis of the  statistic requested as well  as the number of  calls to
each function.  Sample output is shown below.

    FUNCTIONS     TIME     # CALLS   PER CALL     %
    SUPERPRINT    8.261       365     0.023      20
    SUBPRINT      31.910      141     0.226      76
    COMMENT1      1.612       8       0.201      4

    TOTAL         41.783      514     0.081

The procedure  used for  measuring is  such that  if one  function calls

    In INTERLISP-10, if INTERLISP was started as a subsidiary  fork (see
    subsys, page 21.15), control is returned to the higher fork.

    breakdown was written by W. Teitelman.


other and both are 'broken down', then the time (or whatever quantity is
being measured) spent in the inner function is not charged to  the outer
function as well.

To  remove  functions from  those  being monitored,  simply  unbreak the
functions,  thereby  restoring them  to  their original  state.   To add
functions, call breakdown on the new functions.  This will not reset the
counters for any functions not on the new list.  However breakdown[] can
be used for zeroing the counters of all functions being monitored.

To use breakdown for some other statistic, before calling breakdown, set
the variable brkdwntype to the quantity of interest, e.g., TIME, CONSES,
etc.  Whenever  breakdown is called  with brkdwntype not  NIL, breakdown
performs the necessary changes to  its internal state to conform  to the
new analysis.  In particular, if  this is the first time an  analysis is
being run with this statistic, the compiler may be called to compile the
measuring  function.  When  breakdown is  through initializing,  it sets
brkdwntype back to NIL.  Subsequent calls to breakdown will  measure the
new  statistic  until  brkdwntype  is  again  set  and  a  new breakdown
performed.  Sample output is shown below:

    _FLIP((A B C D E F G H C Z) (.. $1 .. #2 ..) (.. #3 ..))
    (A B D E F G H Z)
    MATCH         32          1       32.000     41
    CONSTRUCT     47          1       47.000     59
    TOTAL         79          2       39.500

The value of brkdwntype is  used to search the list brkdwntypes  for the
information  necessary  to   analyze  this  statistic.   The   entry  on
brkdwntypes corresponding to brkdwntype should be of the form (type form
function), where  form computes the  statistic, and  function (optional)
converts  the value  of  form to  some more  interesting  quantity, e.g.

    breakdown  will  not  give  accurate  results  if  a  function being
    measured  is  not  returned from  normally,  e.g.,  a  lower retfrom
    (or error) bypasses it. In this  case, all of the time  (or whatever
    quantity  is  being  measured) between  the  time  that  function is
    entered and  the time  the next function  being measured  is entered
    will be charged to the first function.

    The  measuring  functions  for TIME  and  CONSES  have  already been


(TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000)))   measures  computation
time  and reports  the result  in seconds  instead of  milliseconds.  If
brkdwntype  is  not  defined  on  brkdwntypes,  an  error  is generated.
brkdwntypes  currently contains  entries for  TIME,  CONSES, PAGEFAULTS,

More Accurate Measurement

Occasionally, a  function being analysed  is sufficiently fast  that the
overhead involved in measuring it obscures the actual time spent  in the
function.  If  the user were  using time, he  would specify a  value for
timen greater  than 1  to give  greater accuracy.   A similar  option is
available for  breakdown.  The  user can specify  that a  function(s) be
executed  a  multiple number  of  times for  each  measurement,  and the
average value reported, by including  a number in the list  of functions
given to breakdown, e.g.,  BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP) means
normal  breakdown for  editcom  and edit4f  but executes  (the  body of)
edit4e  and eqp  10 times  each time  they are  called.  Of  course, the
functions so  measured must  not cause any  harmful side  effects, since
they  are executed  more than  once for  each call.   The  printout from
results will look the same  as though each function were run  only once,
except that the measurement will be more accurate.

21.3 Edita

Edita is an editor  for arrays.  However, its most  frequent application
is in editing  compiled functions (which  are also arrays  in INTERLISP-
10), and a great deal of  effort in implementing edita, and most  of its
special features, are in this area.  For example, edita knows the format
and conventions of  INTERLISP-10 compiled code,  and so, in  addition to
decoding  instructions a  la DDT,    edita can  fill in  the appropriate
COREVALS, symbolic  names for index  registers, references  to literals,
linked function calls,  etc.  The following  output shows a  sequence of
instructions in a  compiled function first as  they would be  printed by
DDT, and second by edita.

    For more accurate measurement, the form for TIME in  INTERLISP-10 is
    not (CLOCK 2) but (ASSEMBLE NIL (JSYS 206) (SUB 1 , GCTIM)).

    edita was written by W. Teitelman, and modified by D. C. Lewis. That
    portion  of  edita relating  to  compiled  code may  or  may  not be
    available in implementations of INTERLISP other than INTERLISP-10.

    DDT is one of the  oldest debugging systems still around.  For users
    unfamiliar with it, let us simply say that edita was patterned after
    it because so many people are familiar with it.


    466716/   PUSH 16,LISP&KNIL        3/   PUSH PP,KNIL
    466717/   PUSH 16,LISP&KNIL        4/   PUSH PP,KNIL
    466720/   HRRZ 1,-12(16)           5/   HRRZ 1,-10(PP)
    466721/   CAME 1,LISP&KNIL         6/   CAME 1,KNIL
    466722/   JRST 466724              7/   JRST 9
    466723/   HRRZ 1,@467575           8/   HRRZ 1,@'BRKFILE
    466724/   PUSH 16,1                9/   PUSH PP,1
    466725/   LISP&IOFIL,,467576       10/  PBIND 'BRKZ
    466726/   -3,,-3                   11/  -524291
    466727/   HRRZ 1,-14(16)           12/  HRRZ 1,-12(PP)
    466730/   CAMN 1,467601            13/  CAMN 1,'OK
    466731/   JRST 466734              14/  JRST 17
    466732/   CAME 1,467602            15/  CAME 1,'STOP
    466733/   JRST 466740              16/  JRST 21
    466734/   PUSH 16,467603           17/  PUSH PP,'BREAK1
    466735/   PUSH 16,467604           18/  PUSH PP,'(ERROR!)
    466736/   LISP&FILEN,,467605       19/  CCALL 2,'RETEVAL
    466737/   JRST 467561              20/  JRST 422
    466740/   CAME 1,467606            21/  CAME 1,'GO
    466741/   JRST 466754              22/  JRST 33
    466742/   HRRZ 1,@-12(16)          23/  HRRZ 1,@-10(PP)
    466743/   PUSH 16,1                24/  PUSH PP,1

Therefore, rather  than presenting  edita as an  array editor  with some
extensions for  editing compiled  code, we  prefer to  consider it  as a
facility for editing  compiled code, and point  out that it can  also be
used for editing arbitrary arrays.


To  the  user,  edita  looks  very  much  like  DDT   with  INTERLISP-10
extensions.  It is a function of one argument, the name of  the function
to be  edited.   Individual registers  or cells in  the function  may be
examined by typing their address followed by a slash,   e.g.

                        6/   HRRZ 1,-10(PP)

    Note  that edita  prints  the addresses  of cells  contained  in the
    function relative to the origin of the function.

    An optional  second argument can  be a list  of commands  for edita.
    These are  then executed exactly  as though they  had come  from the

    Underlined characters  were typed  by the user.  edita uses  its own
    read program, so that it  is unnecessary to type a space  before the
    slash or to type a carriage return after the slash.


The slash is really a command to edita to open the indicated register.
Only one register at a time can be open, and only open registers  can be
changed.  To change the contents of a register, the user first opens it,
types  the  new   contents,  and  then   closes  the  register   with  a
carriage-return,   e.g.

                        7/   CAME 1,'^      CAMN1,'^C

If the user closes a  register without specifying the new  contents, the
contents are left unchanged.  Similarly, if an error occurs or  the user
types  control-E, the  open register,  if any,  is closed  without being

Input Protocol

Edita processes all inputs not  recognized as commands in the  same way.
If the input is the name of an instruction (i.e., an atom with a numeric
OPD  property), the  corresponding number  is added  to the  input value
being assembled,    and a  flag is  set which  specifies that  the input
context is that of an instruction.

The  general form  of a  machine instruction  is (opcode  ac , @ address
(index)) as described in Section 18.  Therefore, in instruction context,
edita evaluates all atoms (if the atom has a COREVAL property, the value
of the COREVAL is  used), and then if  the atom corresponds to  an ac,
shifts it left 23 bits and adds it to the input value, otherwise adds it
directly to the input value,  but performs the arithmetic in the  low 18
bits.   Lists  are interpreted  as specifying  index registers,  and the

    edita also converts absolute addresses of cells within  the function
    to relative address on input. Thus, if the definition of  foo begins
    at 85660, typing 6/ is exactly the same as typing 85666/.

    Since  carriage-return has  a special  meaning, edita  indicates the
    balancing of parentheses by typing a space.

    The input value is initially 0.

    i.e., if a "," has not been seen, and the value of the atom  is less
    than 16, and the low 18 bits of the input value are all zero.

    If the  absolute value of  the atom is  greater than  1000000Q, full
    word arithmetic is used. For example, the indirect bit is handled by
    simply binding @ to 20000000Q.


value of car of the list (again COREVALs are permitted) is  shifted left
18 bits.  Examples:

                        PUSH PP, KNIL
                        HRRZ 1,-10(PP)
                        CAME 1, 'GO
                        JRST 33 ORG  

The user can also  specify the address of  a literal via the  ' command,
see page  21.11.  For  example, if  the literal  " UNBROKEN" is  in cell
85672, HRRZ 1,'" UNBROKEN" is equivalent to HRRZ 1, 85672.

When the input context is not  that of an instruction, i.e., no  OPD has
been seen, all inputs are evaluated (the value of an atom with a COREVAL
property is the  COREVAL.) Then numeric values  are simply added  to the
previous input value; non-numeric values become the input value.

The only  exception to the  entire procedure occurs  when a  register is
open that is in the pointer region of the function, i.e., literal table.
In this case,  atomic inputs are not  evaluated.  For example,  the user
can change the  literal FOO to FIE  by simply opening that  register and
then typing FIE followed by carriage-return, e.g.

                        'FOO/     FOO       FIEC

Note that this is equivalent to 'FOO/   FOO   (QUOTE FIE)C

Edita Commands and Variables

C (carriage-return)     If a  register is open  and an input  was typed,
                        store the input in the register and close it.

                        If  a register  is open  and nothing  was typed,
                        close the register without changing it.

                        If a register is  not open and input  was typed,
                        type its value.

    edita  cannot  in  general  know  whether  an  address  field  in an
    instruction that is typed in is relative or absolute. Therefore, the
    user must add ORG, the origin of the function, to the  address field
    himself. Note that edita would print this instruction, JRST  53 ORG,
    as JRST 53.

    Presumably there is only one input in this case.

    If  the register  is  in the  unboxed  region of  the  function, the
    unboxed value is stored in the register.


ORG                     Has  the  value  of  the  address  of  the first
                        instruction in the function.  i.e., loc  of getd
                        of the function.

/                       Opens the register specified by the low  18 bits
                        of the quantity to the left of the /,  and types
                        its  contents.  If  nothing has  been  typed, it
                        uses the last thing typed by edita, e.g.,

                        35/  JRST 53   /    CAME 1,'RETURN      /

                        If  a register  was  open, /  closes  it without
                        changing its contents.

                        After a /  command, edita returns to  that state
                        of no input having been typed.

tab (control-I)         Same as carriage-return, followed by the address
                        of the quantity to the left of the tab, e.g.,

                        35/  JRST 53   tab
                        53/  CAME 1,'RETURN

Note that if a  register was open and  input was typed, tab  will change
the open register before closing it, e.g.,

                        35/  JRST 53   JRST54 tab
                        54/  JRST 70   C
                        35/  JRST 54

. (period)              has  the value  of  the address  of  the current
                        (last) register examined.

line-feed               same    as    carriage-return     followed    by
                        (ADD1 .)/ i.e.   closes  any  open  register and
                        opens the next register.

^                       same as carriage-return followed by (SUB1 .)/

$Q (alt-modeQ)          has  as its  value  the last  quantity  typed by
                        edita e.g.

                        35/  JRST 53   $Q1C
                        ./   JRST 54

LITS                    has as value the (relative) address of the first

BOXED                   same as LITS


$ (dollar)              has as  value the relative  address of  the last
                        literal in the function.

=                       Sets radix to -8  and types the quantity  to the
                        left of the =  sign, i.e., if anything  has been
                        typed, types  the input value,  otherwise, types
                        $Q, e.g.

                        35/  JRST 54   =254000241541Q

                        Following =, radix is restored and edita returns
                        to the no input state.

OK                      leave edita

?                       return  to  "no  input" state.   ?  is  a "weak"
                        control-E, i.e., it negates any input typed, but
                        does not close any registers.

address1, address2/     prints    the  contents  of  registers  address1
                        through address2.   . is  set to  address2 after
                        the completion.

'x                      corresponds  to   the  '   in  LAP.    The  next
                        expression is read, and if it is a small number,
                        the   appropriate   offset  is   added   to  it.
                        Otherwise, the literal table is searched  for x,
                        and the value of 'x is the (absolute) address of
                        that cell.  An error is generated if the literal
                        is not found, i.e.,  ' cannot be used  to create

:atom                   defines atom to an address
                        (1)  the value of $Q if a register is open,
                        (2)  the input if any input was typed, otherwise
                        (3)  the value of ".".
                        For example:

    output goes to file, initially set to T. The user can also  set file
    (while in edita) to the name of a disc file to redirect  the output.
    (The user is  responsible for opening  and closing file.)  Note that
    file only affects output for the address1, address2/ command.

    Only the low  18 bits are used  and converted to a  relative address
    whenever possible.


                        35/  JRST 54   :FOOC
                        FIE/  JRST FOO .=35

Edita  keeps  its symbol  tables  on two  free  variables,  usersyms and
symlst.  Usersyms is a list  of elements of the form  (name . value) and
is used for encoding input, i.e., all variables on usersyms are bound to
their corresponding  values during evaluation  of any  expression inside
edita.  Symlst is a list of  elements of the form (value . name)  and is
used for decoding addresses.  Usersyms is initially NIL, while symlst is
set  to  a list  of  all the  corevals.   Since the  : command  adds the
appropriate information  to both these  two lists, new  definitions will
remain in effect even if the user exits from edita and then  reenters it

Note  that the  user can  effectively define  symbols without  using the
: command by appropriately binding usersyms and/or symlst before calling
edita.   Also, he  can thus  use different  symbol tables  for different

$W (alt-modeW)          search command.

Searching  consists  of comparing  the  object of  the  search  with the
contents of each register, and printing those that match, e.g.,

              8/   HRRZ 1,@'BRKFILE
              23/  HRRZ 1,@-10(PP)
              28/  HRRZ 1,@-12(PP)

The $W command  can be used  to search either  the unboxed portion  of a
function,  i.e., instructions,  or the  pointer region,  i.e., literals,
depending on whether or  not the object of  the search is a  number.  If
any input was typed before the $W, it will be the object of  the search,
otherwise the next expression is read and used as the object.   The user
can  specify  a starting  point  for  the search  by  typing  an address
followed by a "," before calling  $W, e.g., 1, JRST $W.  If  no starting
point  is specified,  the search  will begin  at 0  if the  object  is a
number, otherwise at LITS, the address of the first literal.   After the
search is completed, "." is set to the address of the last register that

    Note  that  inputs typed  before  the $W  will  have  been processed
    according to the input protocol, i.e., evaluated; inputs typed after
    the  $W will  not. Therefore,  the latter  form is  usually  used to
    specify searching the literals, e.g., $W FOO is equivalent to (QUOTE
    FOO) $W.

    Thus  the only  way the  user can  search the  pointer region  for a
    number is to specify the starting point via ",".


If the search is operating in the unboxed portion of the  function, only
those fields  (i.e., instruction, ac,  indirect, index, and  address) of
the object that contain one bits are compared.   For example, HRRZ  @ $W
will find all instances of  HRRZ indirect, regardless of ac,  index, and
address fields.   Similarly, 'PRINT $W  will find all  instructions that
reference the literal PRINT.

If  the search  is operating  in  the pointer  region, a  "match"  is as
defined in the editor.  For example, $W (&) will find all registers that
contain a list consisting of a single expression.

$C (alt-modeC)          like $W except only prints the first match, then
                        prints  the number  of matches  when  the search

Editing Arrays

Edita  is  called to  edit  a function  by  giving it  the  name  of the
function.  Edita can also  be called to edit  an array by giving  it the
array as its first  argument,   in which case the  following differences
are to be noted:

    1.   decoding - The contents of registers in the unboxed  region are
         boxed and printed as numbers, i.e., they are  never interpreted
         as instructions, as when editing a function.

    2.   addressing  convention -  Whereas  0 corresponds  to  the first
         instruction of  a function,  the first element  of an  array by
         convention is element number 1.

    3.   input protocols - If  a register is open, lists  are evaluated,
         atoms  are  not  evaluated  (except  for  $Q  which  is  always
         evaluated).  If no register is open, all inputs  are evaluated,
         and if the value is a number, it is added to the "input value".

    Alternately,  the  user can  specify  his own  mask  by  setting the
    variable mask (while in edita), to the appropriate bit pattern.

    The user may need to establish instruction context for input without
    giving a specific instruction.  For example, suppose the  user wants
    to find all instructions with  ac=1 and index=PP. In this  case, the
    user can give & as a pseudo-instruction, e.g., type & 1, (PP).

    the array  itself, not  a variable  whose value  is an  array, e.g.,
    (EDITA FOO), not EDITA(FOO).


    4.   left  half -  If the  left half  of an  element in  the pointer
         region  of  an array  is  not all  0's  or NIL,  it  is printed
         followed by a ;, e.g.

                   10/  (A B) ; T

         Similarly, if a register is closed, either its left half, right
         half, or both halves can be changed, depending on  the presence
         or absence, and position of the ; e.g.

                   10/  (A B) ; T      B;C                 changes left
                   ./   B ; T          NILC                changes right
                   ./   B ; NIL        A;CC                changes both
                   ./   A ; C

         If ; is used in the unboxed portion of an array, an  error will
         be generated.

The  $W command  will look  at both  halves of  elements in  the pointer
region, and  match if either  half matches.  Note  that $W A ; B  is not

This ends the section on edita.

21.4 Interfork Communication in INTERLISP-10

The functions  described below  permit two  forks (one  or both  of them
INTERLISP-10) to have a  common area of address space  for communication
by providing a means of  assigning a block of storage guaranteed  not to
move during garbage collections.

getblk[n]               Creates a block n  pages in size (512  words per
                        page).  Value is  the address of the  first word
                        in the block, which  is a multiple of  512 since
                        the block will always begin at a  page boundary.
                        If not enough pages are available, generates the
                        error ILLEGAL OR IMPOSSIBLE BLOCK.

Note: the block can be used for storing unboxed numbers only.

To store a number in the block, the following function could be used:


Some boxing and unboxing can be avoided by making this  function compile
open via a substitution macro.


Note: getblk should be used sparingly since several unmovable regions of
memory can make it difficult or impossible for the garbage  collector to
find a contiguous region large enough for expanding array space.

relblk[address;n]       releases a block of storage beginning at address
                        and  extending  for n  pages.   Causes  an error
                        ILLEGAL OR IMPOSSIBLE BLOCK if any of  the range
                        is not a block.  Value is address.

21.5 Subsys

This section describes a function, subsys, which permits the user to run
a  TENEX  subsystem,  such  as SNDMSG,  SRCCOM,  TECO,  or  even another
INTERLISP, from  inside of an  INTERLISP without destroying  the latter.
In particular, SUBSYS(EXEC) will start up a lower exec, which will print
the TENEX herald, followed by @.  The user can then do anything  at this
exec level that he can at the top level, without affecting  his superior
INTERLISP.   For  example, he  can  start another  INTERLISP,  perform a
sysin, run  for a  while, type a  control-C returning  him to  the lower
exec, RESET, do a SNDMSG, etc.   The user exits from the lower  exec via
the command  QUIT, which  will return  control to  subsys in  the higher
INTERLISP.  Thus with subsys, the user need not perform a sysout to save
the state  of his  INTERLISP in order  to use  a TENEX  capability which
would otherwise  clobber the core  image.  Similarly, subsys  provides a
way of checking out a sysout file in a fresh INTERLISP without having to
commandeer another teletype or detach a job.

While subsys can  be used to run  any TENEX subsystem  directly, without
going through  an intervening exec,  this procedure is  not recommended.
The problem is that control-C always returns control to the next highest
exec.  Thus  if the user  is running an  INTERLISP in which  he performs
SUBSYS(LISP), and then types  control-C to the lower  INTERLISP, control
will be  returned to the  exec above the  first INTERLISP.   The natural
REENTER command would  then clear the  lower INTERLISP,   but  any files
opened by  it would remain  open (until the  next @RESET).  If  the user
elects to call  a subsystem directly, he  must therefore know how  it is
normally exited and always exit from it that way.

    subsys was written  by J.W. Goodwin. It  is TENEX dependent  and may
    not  be  available  in  implementations  of  INTERLISP   other  than

    A CONTINUE command however  will return to the  subordinate program,
    i.e., control-C followed by CONTINUE is safe at any level.

    INTERLISP is exited  via the function  logout, TECO via  the command
    ;H, SNDMSG via control-Z, and EXEC via QUIT.


Starting a lower exec does not have this disadvantage, since it can only
be  exited  via QUIT,  i.e.,  the lower  exec  is  effectively "errorset
protected" against control-C.

                        If  file/fork=EXEC,  starts  up  a  lower  exec,
                        otherwise     runs      <SUBSYS>system,     e.g.
                        subsys[SNDMSG],  subsys[TECO] etc.   subsys[] is
                        same as subsys[EXEC].  Control-C  always returns
                        control  to next  higher exec.   Note  that more
                        than one INTERLISP can be stacked, but  there is
                        no backtrace  to help you  figure out  where you

                        incomfile  and  outcomfile  provide  a   way  of
                        specifying   files   for   input   and   output.
                        incomfile can also be a string, in which  case a
                        temporary  file  is  created,  and   the  string
                        printed on it.

                        entrypointflg   may   be   START,   REENTER,  or
                        CONTINUE.   NIL is  equivalent to  START, except
                        when file/fork is a handle (see below)  in which
                        case NIL is equivalent to CONTINUE.

The value of subsys  is a large integer which  is a handle to  the lower
fork. The lower fork is  not reset unless the user specifically  does so
using kfork, described below.   If subsys is given as its first argument
the value of  a previous call to  subsys,  , it continues  the subsystem
run    by    that    call.    For    example,    the    user    can   do
(SETQ SOURCES (SUBSYS TECO)), load up the  TECO with a big  source file,
massage the file, leave TECO with ;H, run INTERLISP for awhile (possibly
including other calls  to subsys) and  then perform (SUBSYS  SOURCES) to
return to TECO,  where he will  find his file  loaded and even  the TECO
pointer position preserved.

Note  that  if  the user  starts  a  lower EXEC,  in  which  he  runs an
INTERLISP, control-C's from the  INTERLISP, then QUIT from the  EXEC, if
he  subsequently continues  this  EXEC with  subsys, he  can  reenter or
continue the INTERLISP.

    The fork  is also  reset when  the handle  is no  longer accessible,
    i.e., when nothing in the  INTERLISP system points to it.  Note that
    the fork is accessible while the handle remains on the history list.

    Must be the exact same large number, i.e., eq. Note that if the user
    neglects to set a  variable to the value  of a call to  subsys, (and
    has performed an intervening call so that subsys[T] will  not work),
    he can still continue this  subsystem by obtaining the value  of the
    call  to subsys  for the  history list  using the  function valueof,
    described in Section 22.


Note  also that  calls to  subsys can  be stacked.   For  example, using
subsys, the user can run  a lower INTERLISP, and within  that INTERLISP,
yet another, etc., and ascend the chain of INTERLISPs using  logout, and
then descend back down again using subsys.

For convenience, subsys[T] continues the last subsystem run.

SNDMSG,  LISP, TECO,  and EXEC,  are all  LISPXMACROS which  perform the
corresponding calls  to subsys.  CONTIN  is a LISPXMACRO  which performs
subsys[T], thereby continuing the last subsys.

kfork[fork]             accepts a value from subsys and kills  it (RESET
                        in  TENEX  terminology).   If   subsys[fork]  is
                        subsequently performed,  an error  is generated.
                        kfork[T] kills all outstanding forks  (from this

21.6 Miscellaneous Tenex Functions in INTERLISP-10

fildir[filegroup]       filegroup  is  a  TENEX  file  group descriptor,
                        i.e., it  can contain  stars.  fildir  returns a
                        list of  the files which  match filegroup,  a la
                        the     TENEX    DIRECTORY     command,    e.g.,
                        (FILDIR (QUOTE *.COM;0)).

loadav[]                returns TENEX current load average as a floating
                        point number  (this number is  the first  of the
                        three printed by the TENEX SYSTAT command).

erstr[ern]              ern is an error number from a JSYS  fail return.
                        ern=NIL means most recent error.   erstr returns
                        the   TENEX   error  diagnostic   as   a  string
                        (from <SYSTEM>ERROR.MNEMONICS).

                        loads (unboxed) values of ac1, ac2, and ac3 into
                        appropriate  accumulaters,  and  executes  TENEX
                        JSYS number  N.  If ac1,  ac2, or ac3=NIL,  0 is
                        used.  Value of jsys is the (boxed)  contents of
                        the accumulator  specified by resultac,  i.e., 1
                        means ac1,  2 means ac2,  and 3 means  ac3, with
                        NIL equivalent to 1.

username[a]             If a=NIL, returns login directory name;  if a=T,

    All of the functions in this section, except for tenex, were written
    by J.W. Goodwin.


                        returns  connected  directory name;  if  a  is a
                        number,   username   returns   the   user   name
                        corresponding  to  that  user  number.   In  all
                        cases, the value is a string.

usernumber[a]           If  a=NIL, returns  login user  number;  if a=T,
                        returns connected user number; if a is a literal
                        atom or string, usernumber returns the number of
                        the corresponding user,  or NIL if no  such user

Note: greeting (see Section 22) sets the variable username to  the login
user name, and firstname to the name used in the greeting.

tenex[str;fileflg]      Starts up a lower exec (without a message) using
                        subsys,  and  then if  fileflg=NIL  unreads str,
                        followed by "QUIT" (using bksysbuf, described in
                        Section 14).  The value of tenex is T if  all of
                        str  is  actually  processed/read  by  the lower
                        exec, NIL if  the user control-C's  and manually
                        QUIT's back to LISP.

                        If  fileflg=T, tenex  passes the  string  as the
                        second argument to subsys, instead  of unreading
                        it.  This has the  advantage that str can  be of
                        any  length, and  also that  typeahead  will not
                        interfere with the call to the lower  exec.  The
                        disadvantage is  that tenex cannot  tell whether
                        the  commands  to  the  lower   exec  terminated
                        successfully,   or  were   aborted.    Thus,  if
                        fileflg=T, the value of tenex is always T.

For example,  listfiles (Section  14) is  implemented using  tenex, with
fileflg=NIL, so listfiles can tell if listings actually  were completed.
The lispxmacro SY, which does a SYSTAT, is implemented as TENEX["SY";T],
so that the user can type ahead.

Manipulating Tenex File Directories from INTERLISP-10

The following  function allows the  user to conveniently  specify and/or
program a variety of directory operations:

                        filegroup is either [1] NIL (which is equivalent
                        to *.*;*); or [2] an atom which can  contain $'s
                        or *'s  (equivalent) which  match any  number of

    directory was written by L.M. Masinter.


                        characters    or   ?'s  which  match   a  single
                        character, or  else [3] filegroup  is a  list of
                        the form  (filegroup + filegroup),  (filegroup -
                        filegroup), or (filegroup *  filegroup),   e.g.,
                        (T$  + $L)  will match  with any  file beginning
                        with T or ending in L, (T$ - *.COM)  matches all
                        files that begin with T and are not .COM files.

                        For  each  file that  matches,  each  command in
                        commands   is   executed   with   the  following

@ fn                    apply fn to the JFN for each file; if fn returns
                        NIL, abort command processing for this file.

P                       print file name.

PAUSE                   wait for user to type any char (good for display
                        if you want to ponder).

PROMPT mess             prompts  with mess;  if user  responds  with No,
                        abort command processing for this file.

SIZE                    print file size.

TRIMTO n                deletes all but n versions of file (n > 0)

OUT file                directs output to file.

COLLECT                 adds file on value list.  In this case the value
                        of  directory   will  be   the  list   of  files

DATE                    prints date the file was last written.

DELETE                  deletes file.

                        The  value of  directory  is NIL  if  no COLLECT
                        command  is  specified,  otherwise  the  list of
                        files "collected".

directory uses  dircommands to correct  spelling, which also  provides a
way of defining abbreviations  and synonyms (see Section 17  on spelling
lists).  Currently the following abbreviations are recognized:

TI                      same as DATE

    not  necessarily trailing  characters,  e.g., F$1  matches  FOO1 and

    OR can be used for +, and AND for *.


DEL                     same as DELETE

DEL?                    same as PROMPT "delete?" DELETE

COLLECT?                same as PROMPT "?" COLLECT

There is also a lispxmacro DIR which calls the function directory:

DIR group commands      calls the function directory with (P . commands)
                        as the command list  and * and * as  the default
                        extension and default version respectively.

For  example, to  DELVER only  those files  which you  ok, do  DIR group

21.7 Printing Reentrant and Circular List Structures

A reentrant list structure is one that contains more than one occurrence
of the same (eq) structure. For example, tconc (Section 6) makes uses of
reentrant list structure so that it does not have to search for  the end
of the list each time it is called.  Thus, if x is a list of 3 elements,
(A B C), being constructed by  tconc, the reentrant list  structure used
by tconc for this purpose is:

                        Figure 21-1


This  structure would  be printed  by print  as ((A B C) C).   Note that
print would produce the same output for the non-reentrant structure:

                        Figure 21-2

In other words,  print does not indicate  the fact that portions  of the
structure in Figure 21-1 are identical.  Similarly, if print  is applied
to a circular list structure (a special type of reentrant  structure) it
will never terminate.

For example, if print is called on the structure:

                        Figure 21-3

it will print  an endless sequence of  left parentheses, and  if applied

                        Figure 21-4

will print a left parenthesis followed by an endless sequence of A's.

The  function  circlprint  described  below  produces  output  that will
exactly  describe  the  structure  of  any  circular  or  reentrant list


structure.   This output may be in either single or double-line formats.
Below  are  a few  examples  of the  expressions  that  circlprint would
produce to describe the structures discussed above.

expression in Figure 21-1:
single-line: ((A B *1* C) {1})
double-line: ((A B C) . {1})

expression in Figure 21-3:
single-line: (*1* {1})
double-line: ({1})

expression in Figure 21-4:
single-line: (*1* A . {1})
double-line: (A . {1})

The more complex structure:

                        Figure 21-5

is printed as follows:
single-line: (*2* (*1* {1} *3* {2} A *4* B . {3}) . {4})
double-line: ( ({1}   {2} A   B . {3}) . {4})
              2 1    3       4

In both formats, the reentrant  nodes in the list structure  are labeled
by numbers.   (A reentrant  node is one  that has  two or  more pointers

    Circlprint and circlmaker were written by P. C. Jackson.


coming into it.) In the single-line format, the label is printed between
asterisks  at  the  beginning  of  the  node  (list  or  tail)  that  it
identifies.  In the double-line  format, the label is printed  below the
beginning of the node it identifies.  An occurrence of a  reentrant node
that has already been identified  is indicated by printing its  label in

                        prints  an   expression  describing   list.   If
                        printflg=NIL,   double-line   format   is  used,
                        otherwise single-line format.   circlprint first
                        calls  circlmark[list;rlknt],  and   then  calls
                        either rlprin1[list] or rlprin2[list], depending
                        on       the       value       of       printflg
                        (T or NIL, respectively).               Finally,
                        rlrestore[list] is  called, which  restores list
                        to its unmarked state.  Value is list.

circlmark[list;rlknt]   marks each reentrant node in list with  a unique
                        number, starting at  rlknt+1 (or 1, if  rlknt is
                        NIL).  Value is (new) rlknt.

                        Marking list physically alters it.  However, the
                        marking  is  performed  undoably.   In addition,
                        list  can  always  be  restored  by specifically
                        calling rlrestore.

rlprin1[list]           prints  an  expression  describing  list  in the
                        single-line  format.  Does  not restore  list to
                        its uncirclmarked  state.  list  must previously
                        have been circlmarked or an error is generated.

rlprin2[list]           same  as  rlprin1,  except  that  the expression
                        describing  list is  printed in  the double-line

rlrestore[list]         physically  restores   list  to   its  original,
                        unmarked state.

Note that the user can mark and print several structures  which together
share  common substructures,  e.g.,  several property  lists,  by making
several calls to circlmark, followed by calls to rlprin1 or rlprin2, and
finally to rlrestore.

circlmaker[list]        list may contain labels and references following
                        the convention  used by circlprint  for printing
                        reentrant  structures  in  single  line  format,
                        e.g.,  (*1* . {1}).   circlmaker   performs  the
                        necessary  rplaca's  and rplacd's  to  make list
                        correspond to the indicated structure.  Value is
                        (altered) list.


circlmaker1[list]       Does  the   work  for  circlmaker.    Uses  free
                        variables labelst and reflst.  labelst is a list
                        of  dotted  pairs  of  labels  and corresponding
                        nodes.   reflst is  a list  of  nodes containing
                        references to  labels not yet  seen.  Circlmaker
                        operates by  initializing labelst and  reflst to
                        NIL, and then calling circlmaker1.  It generates
                        an error if  reflst is not NIL  when circlmaker1
                        returns.  The user can call circlmaker1 directly
                        to  "connect up"  several structures  that share
                        common  substructures,  e.g.,  several  property

21.8 Dumping Unusual Data Structures

The circlprint package is designed primarily for displaying complex list
structures,  i.e.  printing  them  so that  the  user can  look  at them
(although circlmaker can  be used in  conjunction with read  for dumping
and reloading  re-entrant list structures).   Hprint   is a  package for
printing and reading  back in more  general data structures  that cannot
normally  be dumped  and loaded  easily, e.g.,  (possibly  re-entrant or
circular) structures containing user datatypes, arrays, hash  tables, as
well as list structures.   Hprint will correctly print and read  back in
any structure containing any or  all of the above, chasing  all pointers
down to the level of literal atoms, numbers or strings.

Hprint operates  by simulating  the INTERLISP  print routine  for normal
list structures. When it encounters a user datatype (see Section 23), or
an array or hash array, it prints the data contained therein, surrounded
by special characters defined as read-macro characters (see Section 14).
While chasing  the pointers of  a list structure,  it also keeps  a hash
table of  those items it  encounters, and if  any item is  encountered a
second time, another read-macro  character is inserted before  the first
occurrence,    and  all subsequent  occurrences  are printed  as  a back
reference  using  an  appropriate  macro  character.  Thus  the  inverse
function,  hread  merely  calls  the  INTERLISP  read  routine  with the
appropriate readtable, so  that reading time is  only a function  of the
complexity of the structure.


    for  Horrible  PRINT.  The  hprint  package  was  written  by  L. M.

    Hprint  currently   cannot  handle   compiled  code   arrays,  stack
    positions, or arbitrary unboxed numbers.

    by resetting the file pointer using setfileptr.


                        prints x on file.   If uncircular=T, hprint does
                        no checking for  any circularities in x  (but is
                        still useful for dumping arbitrary structures of
                        arrays,  hash  arrays, lists,  user  data types,
                        etc., that do not contain  circularities), which
                        results  in a  large speed  and internal-storage

hread[file]             reads an hprint-ed expression from file.

HORRIBLEVARS            is a prettydef macro for saving and  loading the
                        value  of  "horrible"  variables.   A  prettydef
                        command of the form (HORRIBLEVARS var1 ... varn)
                        will cause appropriate expressions to be written
                        which will restore  the values of vari  ... varn
                        when the file is loaded. The values of  vari ...
                        varn are all  printed by the same  operation, so
                        that they may contain cross references to common

21.9 Typescript Files

A typescript file is a "transcript" of all of the input and output  on a
terminal. The following function enables transcript files for INTERLISP.

                        Opens   filename   and   begins   recording  the
                        typescript.  If appendflg=T, the typescript will
                        be appended to the end of  filename.   dribble[]

    Note: hprint is intended  primarily for output to disk  files, since
    the algorithm depends  on being able to  reset the file  pointer. If
    file is not a disk file  (and uncircular = NIL), a temprary  file is
    opened, x is  hprinted on it,  and then that  file is copied  to the
    final output file.

    dribble was written by D. C.  Lewis.

    dribble also  takes an extra  argument, thawedflg.   If thawedflg=T,
    the file will be opened in "thawed" mode.


                        closes the typescript file.

dribble  processes  a  line  buffer at  a  time.   Thus,  the typescript
produced is  somewhat neater  than that generated  by TELNET  because it
does not show  characters that were  erased via control-A  or control-Q.
Note  that the  typescript file  is not  included in  the list  of files
returned by  openp[], nor will  it be  closed by a  call to  closeall or
closef.  Only dribble[] closes the typescript file.

dribblefile[]           returns name of current typescript file, if any,
                        otherwise NIL.

21.10 Display Terminals

The value of the  variable displaytermflg indicates whether the  user is
running on a display terminal or not.  displaytermflg is used in various
places in  the system,  e.g., prettyprint,  helpsys, etc.,  primarily to
decide how much  information to present to  the user (more on  a display
terminal than on a hard copy terminal.) displaytermflg is initialized to
the  value of  displaytermp[], whenever  INTERLISP is  (re)-entered, and
after returning from a sysout.

displaytermp[]          value is T if user is on a display terminal, NIL
                        otherwise.   In  INTERLISP-10,  displaytermp  is
                        defined to invoke the appropriate jsys  to check
                        the user's terminal type.

    Only  one typescript  file can  be active  at any  one  point; i.e.,
    dribble[file1]  followed by  dribble[file2] will  cause file1  to be