SECTION 21
1
MISCELLANEOUS
21.1 Measuring Functions
time[timex;timen;timetyp]
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
PRETTYFNS
PRETTYVARS
3727 CONSES
10.655 SECONDS
PRETTY
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)
------------------------------------------------------------------------
1
Some of the functions in this section are TENEX or implementation
dependent. They may not be provided in other implementations of
INTERLISP.
21.1
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
PRETTYFNS
PRETTYVARS
3727 CONSES
11.193 SECONDS
27.378 SECONDS, REAL TIME
PRETTY
If timetyp = 3, time measures and prints garbage
collection time as well as computation time,
e.g.
_TIME((LOAD (QUOTE PRETTY) (QUOTE PROP)) 1 3]
FILE CREATED 7-MAY-71 12:47:14
GC: 8
582, 1091 FREE WORDS
PRETTYFNS
PRETTYVARS
3727 CONSES
10.597 SECONDS
1.487 SECONDS, GARBAGE COLLECTION TIME
PRETTY
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.
2
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] <
idate[d2].
------------------------------------------------------------------------
2
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.
21.2
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
started.
for n=2 number of milliseconds of compute time
since user started up this INTERLISP
(garbage collection time is subtracted
off).
for n=3 number of milliseconds of compute time
spent in garbage collections (all
3
types).
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
4
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.
------------------------------------------------------------------------
3
In INTERLISP-10, this number is directly accessible via the COREVAL
GCTIM.
4
In INTERLISP-10, these counters are directly accessible via the
COREVALs IBOXCN and FBOXCN.
21.3
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.
5
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
files.
6
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.
_BREAKDOWN(SUPERPRINT SUBPRINT COMMENT1)
(SUPERPRINT SUBPRINT COMMENT1)
_PRETTYDEF((SUPERPRINT) FOO)
FOO.;3
_RESULTS()
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
NIL
The procedure used for measuring is such that if one function calls
------------------------------------------------------------------------
5
In INTERLISP-10, if INTERLISP was started as a subsidiary fork (see
subsys, page 21.15), control is returned to the higher fork.
6
breakdown was written by W. Teitelman.
21.4
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
7
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
8
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:
_SET(BRKDWNTYPE CONSES)
CONSES
_BREAKDOWN(MATCH CONSTRUCT)
(MATCH CONSTRUCT)
_FLIP((A B C D E F G H C Z) (.. $1 .. #2 ..) (.. #3 ..))
(A B D E F G H Z)
_RESULTS()
FUNCTIONS CONSES # CALLS PER CALL %
MATCH 32 1 32.000 41
CONSTRUCT 47 1 47.000 59
TOTAL 79 2 39.500
NIL
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.
------------------------------------------------------------------------
7
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.
8
The measuring functions for TIME and CONSES have already been
compiled.
21.5
9
(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,
BOXES, and FBOXES.
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.
10
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
11
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.
------------------------------------------------------------------------
9
For more accurate measurement, the form for TIME in INTERLISP-10 is
not (CLOCK 2) but (ASSEMBLE NIL (JSYS 206) (SUB 1 , GCTIM)).
10
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.
11
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.
21.6
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
12
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.
Overview
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
13
to be edited. Individual registers or cells in the function may be
14
examined by typing their address followed by a slash, e.g.
6/ HRRZ 1,-10(PP)
------------------------------------------------------------------------
12
Note that edita prints the addresses of cells contained in the
function relative to the origin of the function.
13
An optional second argument can be a list of commands for edita.
These are then executed exactly as though they had come from the
teletype.
14
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.
21.7
15
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
16
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
changed.
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
17
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
18
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
19
bits. Lists are interpreted as specifying index registers, and the
------------------------------------------------------------------------
15
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/.
16
Since carriage-return has a special meaning, edita indicates the
balancing of parentheses by typing a space.
17
The input value is initially 0.
18
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.
19
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.
21.8
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
20
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
21
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,
22
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.
------------------------------------------------------------------------
20
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.
21
Presumably there is only one input in this case.
22
If the register is in the unboxed region of the function, the
unboxed value is stored in the register.
21.9
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 /
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
literal.
BOXED same as LITS
21.10
$ (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
JRST54=254000000066Q
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.
23
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
literals.
: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
24
(3) the value of ".".
For example:
------------------------------------------------------------------------
23
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.
24
Only the low 18 bits are used and converted to a relative address
whenever possible.
21.11
35/ JRST 54 :FOOC
:FIEC
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
later.
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
applications.
$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.,
HRRZ@$WC
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,
25
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
26
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
matched.
------------------------------------------------------------------------
25
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.
26
Thus the only way the user can search the pointer region for a
number is to specify the starting point via ",".
21.12
If the search is operating in the unboxed portion of the function, only
those fields (i.e., instruction, ac, indirect, index, and address) of
27
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
28
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
finishes.
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
29
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".
------------------------------------------------------------------------
27
Alternately, the user can specify his own mask by setting the
variable mask (while in edita), to the appropriate bit pattern.
28
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).
29
the array itself, not a variable whose value is an array, e.g.,
(EDITA FOO), not EDITA(FOO).
21.13
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
allowed.
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:
[SETBLOCK (LAMBDA (START N X) (CLOSER (IPLUS (LOC START) N) X]
Some boxing and unboxing can be avoided by making this function compile
open via a substitution macro.
21.14
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.
30
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
31
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
32
normally exited and always exit from it that way.
------------------------------------------------------------------------
30
subsys was written by J.W. Goodwin. It is TENEX dependent and may
not be available in implementations of INTERLISP other than
INTERLISP-10.
31
A CONTINUE command however will return to the subordinate program,
i.e., control-C followed by CONTINUE is safe at any level.
32
INTERLISP is exited via the function logout, TECO via the command
;H, SNDMSG via control-Z, and EXEC via QUIT.
21.15
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.
subsys[file/fork;incomfile;outcomfile;entrypointflg]
If file/fork=EXEC, starts up a lower exec,
otherwise runs 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
are.
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
33
using kfork, described below. If subsys is given as its first argument
34
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.
------------------------------------------------------------------------
33
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.
34
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.
21.16
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
INTERLISP).
35
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 ERROR.MNEMONICS).
jsys[n;ac1;ac2;ac3;resultac]
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,
------------------------------------------------------------------------
35
All of the functions in this section, except for tenex, were written
by J.W. Goodwin.
21.17
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
exists.
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:
36
directory[filegroup;commands;defaultext;defaultvers]
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
------------------------------------------------------------------------
36
directory was written by L.M. Masinter.
21.18
37
characters or ?'s which match a single
character, or else [3] filegroup is a list of
the form (filegroup + filegroup), (filegroup -
38
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
interpretation:
@ 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
collected.
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
------------------------------------------------------------------------
37
not necessarily trailing characters, e.g., F$1 matches FOO1 and
FIE1.
38
OR can be used for +, and AND for *.
21.19
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
PROMPT "?" TRIMTO 1.
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
21.20
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
to:
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
21.21
39
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})
1
expression in Figure 21-3:
single-line: (*1* {1})
double-line: ({1})
1
expression in Figure 21-4:
single-line: (*1* A . {1})
double-line: (A . {1})
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
------------------------------------------------------------------------
39
Circlprint and circlmaker were written by P. C. Jackson.
21.22
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
brackets.
circlprint[list;printflg;rlknt]
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
format.
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.
21.23
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
lists.
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
40
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
41
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
42
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.
hprint[x;file;uncircular]
------------------------------------------------------------------------
40
for Horrible PRINT. The hprint package was written by L. M.
Masinter.
41
Hprint currently cannot handle compiled code arrays, stack
positions, or arbitrary unboxed numbers.
42
by resetting the file pointer using setfileptr.
21.24
43
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
advantage.
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
structures.
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.
44
dribble[filename;appendflg]
Opens filename and begins recording the
typescript. If appendflg=T, the typescript will
45
be appended to the end of filename. dribble[]
------------------------------------------------------------------------
43
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.
44
dribble was written by D. C. Lewis.
45
dribble also takes an extra argument, thawedflg. If thawedflg=T,
the file will be opened in "thawed" mode.
21.25
46
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.
------------------------------------------------------------------------
46
Only one typescript file can be active at any one point; i.e.,
dribble[file1] followed by dribble[file2] will cause file1 to be
closed.
21.26