4.3BSD/usr/contrib/icon/docs/tr83-10b.roff

.de Ip
.IP \\$1
.br
..
.nr $1 3
.nr $2 7
.nr $3 0
.NH 3
\*Miconx/start.s\fR
.SH
Overview
.PP
The routine \*Mmstart\fP in \*Mstart.s\fP is used to get Icon started.
When the Icon interpreter is executed,\^ the C routine \*Mmain\fP passes
control to \*Mmstart\fP,\^ and merely serves as a front-end for \*Mmstart\fP.
.SH
Generic Operation
.Ls
.Np
Call the routine \*Minit\fR with the name of the
file to interpret as its argument.
.Np
Make an Icon list out of the command line arguments using
the \*Mllist\fR function.
.Np
Invoke the main procedure of the Icon program.
.Le
.SH
\*Mstart\fP on the VAX
.PP
There is a short main program in \*Miconx/main.c\fP that calls \*Mmstart\fP
with two arguments:
.Ds
.S1
	main(argc, argv)
	int argc;
	char **argv;
	{
		mstart(argc, argv);
	}
.De
.PP
The number of command line arguments is in \*Margc\fP, and \*Margv\fP is a
pointer to an array of pointers to strings representing the arguments.
\*Margv\^[0]\fP is the command used to invoke the interpreter and \*Margv\^[1]\fP
is the name of the file being interpreted.  Additional command line arguments
are passed along to the main procedure of the Icon program.  When
\*Mmstart\fP gets control, \*M4(ap)\fP is the \*Margc\fP value and 
\*M8(ap)\fP is the argv value.
.PP
The first action taken by \*Mmstart\fP is to call \*Minit\fR to initialize the
Icon run-time system.  \*Minit\fR loads the header and code portions of
the interpretable file into memory,\^ so \*Minit\fR needs the
name of the interpretable file.  The word at \*M8(ap)\fP is loaded into
\*Mr9\fP, pointing it at \*Margv\^[0]\fP.  Then the name of the file to interpret
(\*Margv\^[1]\fP), residing at \*M4(r9)\fP, is pushed on the stack as the
argument for \*Minit\fP, which is then called.
.PP
In order to provide conformity with the usual execution environment
of Icon procedures,\^ an expression frame is created for the execution
of the main procedure.  Both the old expression frame pointer and the
old generator frame pointer are set to be 0 in the expression frame
for \*Mmain\fP.  The failure label must point to an interpreter opcode that
will terminate execution of the program.  The opcode 0 is used
for this purpose.  A word of storage,\^ \*Mflab\fR,\^ is declared and
initialized to 0.  The failure label points to \*Mflab\fR.  Thus,\^ if
\*Mmain\fP fails,\^ the interpreter executes the \*Mquit\fR opcode.
.PP
The next task is to push the descriptor for the procedure main
on the stack for later use by \*Minvoke\fR.  The variable
\*M_globals\fR contains the address of the list of global variable
descriptors.
The first global variable descriptor is always the one for the
procedure main; if no main procedure was found when the program
was linked,\^ the descriptor
will be \*M&null\fR.  The value of \*M_globals\fR is loaded into
\*Mr0\fR and the word then referenced by \*Mr0\fR is checked to see if it
is equal to \*MD_PROC\fR.  (The first word of a descriptor for a procedure
is always equal to \*MD_PROC\fR.)
.\"\^[? not sure if we really need to check for D_PROC,\^
.\"I think just checking for ~= 0 will suffice.]
If the word is not equal
to \*MD_PROC\fR,\^ a branch is made to \*Mnomain\fR which generates
the appropriate run-time error.  Otherwise,\^ the descriptor
for \*Mmain\fP is pushed onto the stack.  (The effect of the instruction
\*Mmovq\ (r0),\^\-(sp)\fR
is to move 8 bytes (the size of a quadword)
starting at the address referenced by \*Mr0\fR to the
8 bytes referenced by the \*Msp\fR after subtracting 8 from the
\*Msp\fR.)
.PP
The main procedure is to be invoked with a list consisting of the command
line arguments (if any).  The Icon run-time routine \*Mllist\fR is used to
make the list that is passed to the main procedure.  \*Mllist\fR stores
the descriptor for the list that it creates in the descriptor above its
first argument descriptor,\^ so to accommodate the result,\^ a null descriptor
is pushed on the stack using the \*Mclrq\fR instruction.  Note that
because \*Mllist\fP calls \*Msetbound\fP and \*Mclrbound\fP,\^ it is not
possible to execute all of \*Mstart\fP until \*Mrt/setbound.s\fP has been
completed.
.PP
At the beginning of this routine,\^ \*Mr9\fR was set to point
at the first word of the argument list.
Neither the name of the Icon interpreter nor the name of the interpretable
file is desired in the argument list passed to main,\^ so \*Mr9\fR is
twice incremented by 4 (the size in bytes of a word)
to point it at the first actual program argument.
.PP
The next step is to construct the argument list for \*Mllist\fR.  For
each command line argument,\^ the address of the string and then its
length are pushed on the stack.  The length and address pairs form
descriptors that \*Mllist\fP makes an Icon list from.
\*Mr8\fR is used to count the arguments.
After the addresses and lengths of each argument have been pushed,\^ the
number of arguments is pushed on the stack.
At this point,\^ the stack looks like this:
.Ds
.ft R
.S1
	descriptor for main procedure (2 4-byte words)
	a null descriptor (2 words containing 0)
	address of first argument to Icon program
	length of first argument
	\*(El
	address of last argument to Icon program
	length of last argument
\*Msp\fR \*(ar	number of arguments
.De
.LP
All addresses and lengths are one word in size.  The \*Mcalls\fR
instruction needs to be told how many words are in its argument list.
(This is necessary because when a return is made from the subroutine,\^
the specified number of words are removed from the stack.)  There
are two words for each argument and an additional word for the number
of arguments.  (Do not confuse the argument count for the \*Mllist\fR
subroutine and the argument list size.)
\*Mr8*2+1\fP is calculated in \*Mr8\fP.  This value is used
as the argument list length for the \*Mcalls\fR instruction.
When \*Mllist\fR returns,\^ the arguments are stripped from the
stack and the stack looks like this:
.Ds
.ft R
.S1
	descriptor for main procedure
\*Msp\fR \*(ar	descriptor for list of command line arguments
.De
.LP
Note that the null descriptor pushed earlier received the result of
the \*Mllist\fR function.
.PP
At this point,\^ the main procedure is ready to be invoked.  The descriptor for
the main procedure is \*(a0 and the descriptor for the list of
command line arguments is \*(a1.  Before invoking the main procedure,\^
the procedure frame pointer and the generator frame pointer are cleared.
The main procedure is being invoked with one argument,\^ so a constant
1 is pushed on the stack.  The \*Mcalls\fR instruction is given an
argument of 3 because a word is used for the number of arguments and
two additional words are used for the descriptor for the list of command
line arguments.
.PP
If the main procedure fails,\^ the interpreter will encounter the
0 opcode discussed earlier.  If the main procedure returns (this
usually isn't done),\^ the return will manifest itself as \f3invoke\fR
returning.  If this happens,\^ \*M_c_exit\fR is called with an
argument of 0.  Incidentally,\^ this also happens when the interpreter
hits the 0 opcode.
.PP
There is a block of code labeled \*Mnomain\fR that is executed when
no main procedure is found.  This calls the routine \*Mrunerr\fR to
produce an error message.  The actual call made is \*Mrunerr(117,\^0)\fR.
The number 117 is looked up in a table of run-time errors.  If the second
argument to \*Mrunerr\fR is non-zero,\^ it is interpreted as being the
address of a descriptor and the descriptor is examined to produce
an ``offending value'' to accompany the run-time error.
.PP
The last portion of executable code in \f3start.s\fR is the subroutine
\*M_c_exit\fR.  If the variable \*M_monres\fR is non-zero,\^ it indicates
that profiling is on,\^ and it must be turned off.  This is accomplished
by calling \*M_monitor(0)\fR.  The routine \*M__cleanup\fR is then
called to shut down the i/o system.  Finally,\^ \*M_exit\fR is called
with the argument of \*M_c_exit\fR to terminate execution of the
Icon interpreter.
.PP
There are several data declarations in \*Mstart.s\fR.  The first data
declaration is a \*M.space 60\fR.  This is an accommodation for the
garbage collector.  It
insures that enough of the start of the data section
is used up to force the addresses of other data objects to be
greater than the defined constant \*MMAXTYPE\fR in \f3h/rt.h\fR.
.PP
Some assorted declarations are next. \*Mflab\fR is referenced
by the interpreter if the main procedure fails.  It must be at
least a byte long and contain a 0.  \*M_boundary\fR must be a
word long and contain a 0.  \*M_environ\fR must be a word long;
its contents are unimportant as it is written into at the beginning
of \*Mstart.s\fR.
.PP
The \*M_tended\fR array is also used in conjunction with garbage
collection.  It must declare space for five descriptors (two words
per descriptor) that are initialized to 0.
.\"\^[?]
The label \*M_etended\fR
is used to mark the end of the \*M_tended\fR array.
.ne 1i
.NH 3
\*Mrt/setbound.s\fR
.SH
Overview
.PP
\*Msetbound.s\fP contains code for \*Msetbound\fP and \*Mclrbound\fP.
\*Msetbound\fP sets \*M_boundary\fP under appropriate conditions and
\*Mclrbound\fP clears \*M_boundary\fP under appropriate conditions.
.SH
Generic Operation
.PP
When a call is made from Icon into C,\^ \*M_boundary\fP must be set to point
at the procedure frame on the top of the stack.  C routines that can
be called from Icon have a call to \*Msetbound\fP as their first
operation.  If \*Msetbound\fP is called and \*M_boundary\fP is not
set,\^ that is,\^ \*M_boundary\fP has a value of zero,\^ \*Mboundary\fP is
set to value of the \*Mfp\fP of the calling procedure.
.PP
When a C routine returns to Icon,\^ \*M_boundary\fP must be cleared.
If \*M_boundary\fP has the same value as \*Mfp\fP,\^ the routine was
called from Icon and thus when it returns,\^ it returns to Icon.
At appropriate return points in routines,\^ a call to \*Mclrbound\fP is
made.  If the return takes control back to Icon,\^ \*M_boundary\fP
is cleared.
.SH
\*Msetbound\fP on the VAX
.PP
The value of \*M_boundary\fP is tested.  If \*Mboundary\fP is not set,\^
that is,\^ if it has a value of zero,\^ it is set to the value of the
\*Mfp\fP in the calling procedure.  Because the call to
\*Msetbound\fP creates a procedure frame,\^ the \*Mfp\fP value saved in
the frame is used.  If \*M_boundary\fP is already set,\^ it is not
changed.
.SH
\*Mclrbound\fP on the VAX
.PP
The value of \*M_boundary\fP is compared to the \*Mfp\fP in the
calling procedure.  If the values are the same,\^ the calling procedure
was called from Icon and when it returns it returns to Icon.
If this is the case,\^ \*M_boundary\fP is cleared.
.SH
An Alternative Approach
.PP
If the C system on the target machine uses calls at the beginning and
end of C routines to save and restore registers,\^ it is possible to
modify these routines to set and clear the boundary.  This approach is
used on the PDP-11.
.PP
The file \*Mrt/csv.s\fP contains replacement
routines for \*Mcsv\fP and \*Mcret\fP.  When \*Mcsv\fP is called to
save registers,\^ if \*M_boundary\fP is 0,\^ \*Msp\fP is saved in
\*M_boundary\fP.  This insures that the first call from Icon into C
leaves \*M_boundary\fP at the top of the stack at that point.
When \*Mcret\fP is called to restore registers,\^ if the procedure
frame pointer is equal to \*M_boundary\fP,\^ the return is taking
control back to Icon code and \*M_boundary\fP is cleared.
.PP
Note the resemblance between calling \*Msetbound\fP at the start of a
C routine and having \*Mcsv\fP set \*M_boundary\fP whenever a C routine
is entered.  Similarly,\^ there is a resemblance between calling
\*Mclrbound\fP at appropriate points and having \*Mcret\fP clear
\*M_boundary\fP when necessary.
.PP
The initial implementation of Icon was on the PDP-11 and the modified
\*Mcsv\fP and \*Mcret\fP approach was used.  When the system was
ported to the VAX,\^ the subsumption of \*Mcsv\fP and \*Mcret\fP by the
hardware required that a software approach be taken.  The trade-offs
are marginal if the target machine uses save and restore routines.
Having a distinct \*Msetbound\fP and \*Mclrbound\fP may be easier to
implement,\^ but using modified save and restore routines is
definitely faster.
.PP
If this approach is used,\^ then \*MSetBound\fP and \*MClearBound\fP in
\*Mrt.h\fP should be defined,\^ but given no value.
.NH 3
\*Mlib/invoke.s\fR
.SH
Overview
.LP
\*Minvoke.s\fP handles four specific tasks.  These are
.Ds
.ft R
call a built-in function
call an Icon procedure
create a record
perform mutual evaluation
.De
.LP
Note that all of these operations rise from a source code
expression of the form
.Ds
\*(e0(\*(e1,\^\*(El,\^\*(en)
.De
where each \*(ai is an expression of some type.
Icon has strict left-to-right evaluation and thus,\^ for the preceding
expression,\^ \*(e0 is evaluated first,\^ then \*(e1,\^ and so forth
through \*(en.  As each expression is evaluated,\^ its result
is pushed on the stack.  After the expressions have been evaluated
(and assuming none failed),\^ the stack looks something like
.Ds
.ft R
.S1
	value from \*(e0
	value from \*(e1
	\*(El
	value from \*(ei
	\*(El
\*Msp\fR \*(ar	value from \*(en
.De
Then,\^ \*(e0 is \fIinvoked\fP.
.PP
Recall that stacks are represented as growing downward.  Thus,\^
\*(en is on the ``top'' of the stack.  Also note that each
``value'' on the stack is actually a two-word descriptor.
.\"The various \*(ei may be referred to as \fIarguments\fP.
.SH
Generic Operation
.PP
\*Minvoke\fP is an interpreter opcode that takes a single operand
specifying how many \*(ei are present (\*(e0 is not counted).
\*Minvoke\fP is also called from \*Mstart.s\fP to invoke the \*Mmain\fP
procedure.
.Ls
.Np
\*Minvoke\fP must determine whether it is to call a built-in function,\^
call an Icon procedure,\^ create a record,\^ or perform mutual evaluation.  If
\*(e0 is not something that can be invoked,\^
\*Minvoke\fP notes this as a run-time error.
.Np
If mutual evaluation is to be done,\^ \*Minvoke\fP selects the
value of \*(ei that corresponds to the value of \*(e0.  That is,\^
if \*(e0 is 2,\^ then \*(e2 is selected and returned.  If \*(e0
references a value that is out of range,\^ \*Minvoke\fP fails.
.Np
If an Icon procedure or built-in procedure
is being called,\^ the argument list is adjusted
to conform to the number of arguments that the procedure or function
is expecting.  
This may mean supplying \*M&null\fP for missing values,\^ or discarding
the last few \*(ei.  Some built-in functions take a variable number of
arguments,\^ but all Icon procedures take a fixed number of arguments.
If the desired operation is creation of a record,\^ \*Minvoke\fP treats
this just like invocation of a built-in procedure.  
.Le
.LP
Built-in functions and Icon procedures are handled in very different ways.
If a built-in function is being invoked,\^ it is simply called.  (The
calling sequence is somewhat convoluted on the VAX and is
described later.)
Calling an Icon procedure is more involved; the following actions
are taken.
.Ls
.Np
Each \*(ei in the (adjusted) argument list is dereferenced.
.Np
If \*M&trace\fP has a non-zero value,\^ the function \*Mctrace\fP is
called with appropriate arguments.  \*Mctrace\fP produces output
that includes the name of the procedure being called and the arguments
that are being passed to it.
.Np
The remainder of the procedure frame (partially constructed by the
call itself) is built.  This includes pushing values for \*M_file\fP
and \*M_line\fP on the stack.  \*M_file\fP is a pointer to a string
that names the source file from which the code currently being
executed came.  \*M_line\fP is the number of the source line
that is currently being executed.  A descriptor for
\*M&null\fP is pushed on the
stack for each dynamic local of the procedure.
.Np
The generator frame pointer is cleared (because a new expression
context is being entered).  \*Mipc\fP
is loaded with the entry point of the procedure being called.
Control is then passed back to the interpreter using a jump
instruction.
.Le
.SH
\*Minvoke\fP on the VAX
.PP
On the VAX,\^ immediately after \*Minvoke\fP has been entered,\^ the stack is
.Ds
.ft R
.St
		value from \*(e0
		value from \*(e1
		\*(El
	8	value from \*(en
	4	number of expressions \- 1 (\*Mnargs\fR)
\*Map\fR \*(ar	0	number of words in argument list (\*Mnwords\fR)
	-4	saved \*Mr11\fR
	-8	saved \*Mr10\fR
		\*(El
		saved \*Mr1\fR
		saved \*Mpc\fR
	12	saved \*Mfp\fR
	8	saved \*Map\fR
	4	program status word and register mask
\*Msp\fR,\^ \*Mfp\fR \*(ar	0	0 (condition handler address)
.De
.PP
The first action of \*Minvoke\fP is to set \*M_boundary\fP to the
value of \*Mfp\fP.  This is done because \*Minvoke\fP may be
invoking a built-in procedure.
.PP
The number of arguments with which \*(e0 is to be invoked is contained
in the \fInargs\fP word,\^ which resides at \*M4(ap)\fP.  This value
is frequently used and is put into \*Mr8\fP.
.PP
\*Minvoke\fP makes
frequent use of \*(e0,\^ but its address is not a fixed distance
from any known point.  Rather,\^ the address of \*(e0 must be calculated
using the address of \*(en and the number of arguments.  The VAX
\*Mmovaq\fP instruction makes this calculation easy.  The desired
calculation is
.Ds
r11 = 8 + ap + (r8 * 8)
.De
and is performed by
.Ds
movaq  8(ap)\^[r8],\^r11
.De
\*(e0 may be a
variable and if so,\^ it needs to be dereferenced.
\*Mr11\fP,\^ which contains the address of \*(e0 is pushed on
the stack and \*Mderef\fP is called.  The dereferencing is done
``in place''; the previous value of \*(e0 is replaced with the
dereferenced value.  The dereferenced value is a descriptor
whose first word contains type information and whose second word
(in some cases) contains the address of a data block which holds
the actual value of the object.  Note that \*Mr11\fP points to
the first word of this descriptor.
.PP
Recall that the first task of \*Minvoke\fP is to determine what
\*(e0 is and to act accordingly.  The simplest case is when \*(e0
is a procedure.  That is checked for by comparing \*M0(r11)\fP with
\*MD_PROC\fP.  If \*(e0 is a procedure,\^ a forward jump is made to
\*Mdoinvk\fP.
.PP
It is more interesting if \*(e0 is not a procedure.  The first
alternative investigated is mutual evaluation.  mutual evaluation is similar to a procedure
call,\^ but rather than \*(e0 being a procedure,\^ it is an integer
that selects one of the \*(ei.  The selected \*(ei is the outcome of
the mutual evaluation.  The routine \*Mcvint\fP is used to
try to convert \*(e0 to an integer.  If \*(e0 cannot be converted to
an integer,\^ a forward branch is taken to \*Mtrystr\fP to explore another
possibility.
For mutual evaluation,\^ a non-positive value of \*(e0
is acceptable and is converted to a positive value using the
\*Mcvpos\fP routine.  (Expressions in the argument list are indexed
the same way that characters in a string are indexed.)  If the
position is greater than the number of expressions in the list,\^ that
is,\^ if the reference is out of range,\^ the mutual evaluation fails by calling
the routine \*Mfail\fP.  If the position is in range,\^ the selected
\*(ei must be returned as the result of the invocation (and the
result of the mutual evaluation).  The \*(ei to return is selected by multiplying
the position by 8 (each \*(ei is a descriptor) and subtracting
that from \*Mr11\fP,\^ which points at \*(e0.  The descriptor thus
referenced is copied into the location of \*(e0.  (Recall that \*(e0
is used to receive the result of an operation.)  \*M_boundary\fP
is then cleared and \*Minvoke\fP returns.
.PP
If \*(e0 is not convertible to an integer,\^ conversion to a
procedure is attempted.  (Note that this is an experimental extension
to Icon.)  \*(e0 is first converted to a string
using \*Mcvstr\fP.  If the conversion is successful,\^ the routine
\*Mstrprc\fP is called to see if the string ``names'' a procedure.
The conversion performed by \*Mstrprc\fP is ``in place'',\^ i.e.,\^
\*(e0 becomes a descriptor for a procedure.  If either the
conversion in \*Mcvstr\fP or \*Mstrprc\fP fails,\^ \*(e0 is deemed
to be uninvocable and this is noted by run-time Error 106.
.PP
At this point (the label \*Mdoinvk\fP),\^ \*(e0 is a descriptor to a procedure
to be invoked and \*Mr11\fP points to \*(e0.
.PP
The next operation is to make the number of arguments supplied
conform to the number of arguments that the procedure is expecting.
The number of arguments that a procedure expects is in the fifth
word of its procedure block.  This value for the procedure being
invoked is obtained and placed in \*Mr10\fP.
If the value is negative,\^ the number
of arguments that the procedure expects is variable.  Only built-in
procedures can have a variable number of arguments,\^ so if the
desired argument count is negative,\^ control passes to the label
\*Mbuiltin\fP.
.PP
\*Mr8\fP contains the number of arguments given and \*Mr10\fP contains
the number of arguments desired.  The value of \*Mr10\fP is subtracted
from \*Mr8\fP,\^ leaving \*Mr8\fP with the difference.  If the
number of arguments supplied is the same as the number expected,\^
no adjustment is needed and a forward jump is made to
\*Mdoderef\fP.  Otherwise,\^ the stack must be adjusted.
.PP
First,\^ \fInwords\fR and \fInargs\fR on the stack are adjusted.  Recall
that \fInargs\fR
is used by Icon and is the number of arguments for a procedure.
\fInwords\fR is used by the VAX hardware and is the number of words that
the argument list for a subroutine occupies.  \fInargs\fR resides at
\*M4(ap)\fP and is updated by storing \*Mr10\fP in \*M4(ap)\fP.
\fInwords\fR is trickier because only the low-order byte of the \fInwords\fR word
is to be used for the word count.  The low-order byte of \*Mr10\fP is
stored in \*M0(ap)\fP,\^ doubled,\^ and then incremented by one.  (The
increment by one is to allow for the \fInargs\fR word that is part of
the argument list.)
.PP
The deletion of excess arguments or addition of \*M&null\fP for
missing arguments is accomplished by moving the lower portion of
the stack up or down to overwrite excess arguments or to leave
space for missing arguments.  Consider the following:  A procedure
that expects one argument has been invoked with three arguments.
The stack is
.Ds
.ft R
.St
	128	\*(e0
	120	\*(e1
	112	\*(e2
	104	\*(e3
	100	\*Mnargs\fR (3 at call,\^ 1 after adjustment)
\*Map\fR \*(ar 	96	\*Mnwords\fR (7 at call,\^ 3 after adjustment)
	92	\*Mr11\fR
		\*(El
	48	\*Mr1\fR
	44	\*Mpc\fR
	40	\*Mfp\fR
	36	\*Map\fR
	32	\*Mpsw\fR
\*Msp\fR,\^ \*Mfp\fR \*(ar	28	0
.De
The situation desired is
.Ds
.ft R
	128	\*(e0
	120	\*(e1
	116	\*Mnargs\fR (1)
\*Map\fR \*(ar 	112	\*Mnwords\fR (3)
	108	\*Mr11\fR
		\*(El
	64	\*Mr1\fR
	60	\*Mpc\fR
	56	\*Mfp\fR
	52	\*Map\fR
	48	\*Mpsw\fR
\*Msp\fR,\^ \*Mfp\fR \*(ar	44	0
.De
Note the address field (which has been arbitrarily assigned).
Observe that \*(e0 and \*(e1 are in
the same place,\^ but that the lower portion of the stack has moved up.
Consider what would be desired if the
procedure being invoked requires five arguments and only three are
supplied.  The desired stack configuration is
.Ds
.ft R
.St
	128	\*(e0
	120	\*(e1
	112	\*(e2
	104	\*(e3
	96	\*M&null\fP (\*(e4)
	88	\*M&null\fP (\*(e5)
	84	\*Mnargs\fR (5)
\*Map\fR \*(ar 	80	\*Mnwords\fR (11)
	76	\*Mr11\fR
		\*(El
	32	\*Mr1\fR
	28	\*Mpc\fR
	24	\*Mfp\fR
	20	\*Map\fR
	16	\*Mpsw\fR
\*Msp\fR,\^ \*Mfp\fR \*(ar	12	0
.De
As before,\^ the ``good'' arguments are in the same place,\^ but the
stack has moved down to make room for \*(e4 and \*(e5 and a value
of \*M&null\fP is supplied for them.
.PP
The VAX hardware makes these stack manipulations easy.  Recall that
\*Mr8\fP contains
.Ds
.ft R
number of arguments expected \- number of arguments supplied
.De
Thus,\^ in the first case \*Mr8\fP has a value of 2,\^ while in the second
case \*Mr8\fP is \-2.  The value of \*Mr8\fP is multiplied by
8 to turn the argument count into a byte count.  This byte count
is added to \*Msp\fP,\^ effectively moving it to where it should
be.  Now,\^ a block of memory starting at where \*Mfp\fP points
must be moved to where \*Msp\fP points.  The size of the
block needs to be considered.  It starts at the fp and contains
the condition handler,\^ the old psw,\^ \*Map\fP,\^ \*Mfp\fP and \*Mpc\fP
\(em five words.
Eleven saved registers are included; eleven more words. (A constant,\^
\*MINVREGS\fP,\^ is used to represent the number of saved registers.)
Finally,\^ two words for \fInargs\fR and \fInwords\fR \(em a total of 18
words.  The VAX
instruction that makes the move is
.Ds
movc3  $(INVREGS+7)*4,\^(fp),\^(sp)
.De
which moves 18*4 (=72) bytes from where \*Mfp\fP is pointing to
where \*Msp\fP pointing.  The VAX allows the source and
destination fields to overlap without producing ``curious'' results.
.PP
Some housekeeping must be performed after the move is made.
\*Mfp\fP is set to point to the same word \*Msp\fP points to,\^
and the boundary is set to point to the same place.  \*Map\fP is
adjusted to point to the \fInwords\fR word.
.PP
If arguments were deleted,\^ the adjustment process is done.  If
arguments were added,\^ \*M&null\fP must be supplied as the value
for each argument that was added.  Because the descriptor for
\*M&null\fP consists of two words containing 0,\^ the task amounts to filling
in null bytes in the ``hole'' that was made.  \*Mr8\fP contains
the number of bytes in the ``hole''.  \*Mr8\fP is negative and it
is negated to obtain a positive byte count.  The instruction
.Ds
movc5  $0,\^(r0),\^$0,\^r8,\^(INVREGS+7)*4(sp)
.De
does all the work.  Specifically,\^ this instruction moves bytes of zeroes
starting at \*M(r0)\fP to a field that starts at \*M72(sp)\fP and
extends for \*Mr8\fP bytes.  The third operand of the instruction
is a ``fill character'' that is used in the event that the destination
field is longer than the source field.  In this case,\^ the source
field is 0 bytes long,\^ and a null-byte fill character is moved into
each byte of the destination field.  This is the usual way to zero
out a block of memory on the VAX.
.PP
At this point,\^ the correct number of arguments for the procedure
are on the stack.
.PP
For an Icon procedure,\^ all arguments must be dereferenced before
the procedure is called.  Procedure blocks have a field that tells
how many dynamic local variables the procedure has.  For built-in
procedures this field has a value of \-1.  If a built-in procedure
is to be invoked,\^ control jumps to the label \*Mbuiltin\fP.
If an Icon procedure is to be invoked,\^ but it has no arguments (and
thus they do not need to be dereferenced),\^ a forward jump is made
to \*Mcktrace\fP.
.PP
\*Mr11\fP points to the descriptor for \*(e0.  The address of \*(e1
is used later in \*Minvoke\fP,\^ and its address is calculated using \*Mr11\fP
because it's handy.  \*Mr10\fP contains the number of arguments and
this value is stored in \*Mr5\fP for subsequent use of \*Mr5\fP as a
counter.  The arguments must be dereferenced,\^ this is done by calling
\*Mderef\fP with the address of each argument.  The instruction
.Ds
pushaq  -(r11)
.De
is used to decrement \*Mr11\fP by 8 and then push the value of \*Mr11\fP
on the stack.  Because \*Mr11\fP initially references \*(e0,\^ the first
time through \*Mr11\fP is decremented to point at \*(e1 and
\*Mderef\fP is called with the address of \*(e1.  \*Mr11\fP is backed
down through the expression list,\^ with each \*(ei being dereferenced
in turn.  Note that the \*Msobgeq\fP instruction is used as a loop
controller,\^ decrementing \*Mr5\fP and jumping back to \*Mnxtarg\fP as
long as \*Mr5\fP is not 0.
.PP
At this point,\^ an Icon procedure is being
invoked; it has the correct number of arguments and they
have been dereferenced.
.PP
If tracing is on,\^ (indicated by a non-zero value for \*M_k_trace\fP),\^
a trace message must be produced at this point.
The routine \*Mctrace\fP does all the work.  It needs to be called
with the appropriate arguments.  \*Mctrace\fP requires three
arguments: procedure block address,\^ number of arguments,\^ and the
address of the first argument.  These are pushed on the stack and
\*Mctrace\fP is called.
.PP
The portion of the stack from \*(e0 on down constitutes a partial
procedure frame and it must be completed.  \*M_line\fP and \*M_file\fP
are pushed on the stack.  To complete the frame,\^ the local variables
must be pushed on the stack.  Local variables have an initial value
of \*M&null\fP,\^ and the \*Mmovc5\fP idiom used previously is used
again to zero out the space required for the local variables.
.PP
Because an Icon procedure is being invoked,\^ the boundary is cleared and
\*M_k_level\fP (the \*M&level\fP keyword) is incremented.  The entry
point for a procedure is stored in the third word of the procedure
block.  This value is loaded into \*Mipc\fP.
A new expression context is being entered,\^ and both
\*Mgfp\fP and \*Mefp\fP are cleared using a \*Mclrq\fP instruction.
.PP
Control is passed back to the main loop of the interpreter
by jumping to \*Minterp\fP.
The Icon procedure is now being executed.  The procedure
eventually terminates by use of \*Mpret\fP or \*Mpfail\fP.
.LP
The section of code following \*Mbuiltin:\fP
handles the case where a built-in procedure is to be invoked.
.PP
If nothing is changed,\^ when a built-in function returns,\^ it would
return to the instruction after the call to \*Minvoke\fP.  This
is unsatisfactory,\^ since the boundary needs to be cleared because
of the transition from the C environment to the Icon environment.
Rather than having a call to \*Mclrbound\fP at
the end of each built-in procedure,\^ the boundary is cleared at
a common return point.
.PP
The \*Mpc\fP value that is stored at \*M16(fp)\fP is ``hidden''
at \*M20(fp)\fP where the value of \*Mr1\fP should be saved.
\*M16(fp)\fP is replaced with the address of the forward label
\*Mbprtn\fP.  Thus,\^ when the built-in procedure returns,\^ it goes
right to \*Mbprtn\fP.  Because a C environment is being entered,\^
the boundary is set to the current value of \*Mfp\fP.  The third
word of the procedure block contains the entry point of the built-in
procedure and it is jumped to.  It is important to understand that
the procedure is not called because the call frame has already been
constructed.  Also,\^ the entry point address stored in the procedure
block \fImust\fR be past any instructions that are used to establish
the stack environment for the routine because this environment
should already be on the stack.
.PP
When the built-in procedure returns,\^ it comes to \*Mbprtn\fP.
The boundary is cleared because execution is back in an Icon
environment. The procedure return restores \*Mr1\fP,\^ which contains
the \*Mpc\fP value that \*Minvoke\fP should return to.  Because
the return has already swept off the old frame,\^ \*M0(r1)\fP is
jumped to and \*Minvoke\fP is finished.  Note that the built-in
procedure has left its result in \*(e0,\^ which is left on the
stack.  Thus,\^ the direct result of \*Minvoke\fP is an additional
value on the stack.
.SH
Some Comments on \*Minvoke\fR
.PP
It is important to understand the purpose of \*Minvoke\fP.  Unless
mutual evaluation is being performed,\^
the task of \*Minvoke\fP is to create a procedure frame for an Icon or
built-in procedure and then transfer control to the procedure.
.PP
On the VAX and the PDP-11,\^ the call to \*Minvoke\fP partially
creates the procedure frame.  \*Minvoke\fP then completes the
frame.  For Icon procedures,\^ control eventually passes out of
\*Minvoke\fP and goes back to the interpreter loop.  However,\^
for built-in procedures,\^ after the frame is built,\^ \*Minvoke\fP
\fIbranches into\fP the C code for the procedure.  Thus,\^ it appears
that the built-in procedure was called directly.  This scheme is
facilitated by the fact that entry points of C routines on the
VAX and PDP-11 are a constant distance from the start of the routine.
(The \*MEntryPoint\fP macro in \*Mrt.h\fP specifies the distance.)
On some machines this may not be practical and other schemes may need
to be developed.
.PP
Another point that needs to be addressed is that of argument removal.
When a built-in procedure,\^ Icon procedure,\^ or built-in operation is
performed,\^ the net result almost always is
simply the addition of a descriptor to the stack.  More precisely,\^
the new descriptor is actually a replacement for \*(a0,\^ which
is the descriptor for the procedure being called,\^ or in the case
of a built-in operation is \*M&null\fP.  Recall that arguments are below \*(a0
on the stack.  The \*(a0 word must be on top of the
stack when a procedure or operation is complete.  The VAX does
this via hardware,\^ which manages the stack and removes arguments when
a procedure call is complete.  The PDP-11 does not have such hardware
facilities and thus the arguments must be removed manually.  The
problem is compounded by the fact that for built-in operations,\^
\*Minvoke\fP is never called; rather,\^ the interpreter loop calls the
appropriate subroutine directly.
.PP
The method employed on the PDP-11 uses the \*Mcret\fP routine to
remove arguments.  \*Mcret\fP is called at the end of each C routine
to restore registers.  Icon has a replacement for \*Mcret\fP that
restores registers but also removes arguments when appropriate.
\*Mrt/csv.s\fP contains the replacement routine.  When \*Mcret\fP is
called,\^ if \*Mr5\fP (the \*Mpfp\fP on the PDP-11) is equal to
\*M_boundary\fP,\^ then \*Mcret\fP is returning to Icon code and any
arguments on the stack are removed,\^ leaving \*(a0 on the top of the
stack.
A similar technique may be needed on the target machine.
.PP
\*Mrt/csv.s\fP also contains a replacement for the \*Mcsv\fP routine
which saves registers upon entry to C functions
on the PDP-11.  Both \*Mcsv\fP and \*Mcret\fP also contain code that
is used to set and clear the boundary at appropriate times.  See
the description of \*Mrt/setbound.s\fP for more details.
.NH 3
\*Miconx/interp.s\fR
.SH
Overview
.PP
\*Minterp.s\fP is the main loop for the interpreter.
The execution of an Icon program is stack based.
As the interpreter executes an Icon program,\^ it
fetches instructions and accompanying operands out of the instruction
stream of the interpretable file.  Operands for
interpreter instructions are pushed on the stack and results
accumulate on the stack as operands for other instructions.
In addition to simple incremental and decremental stack changes,\^
the expression evaluation mechanism may cause portions of the
stack to be duplicated and may also cause the top portion of
the stack to be removed.
.SH
Generic Operation
.PP
An Icon program is executed by interpreting the interpretable file
produced by the linker.  The interpretation process itself is fairly
simple.  \*Mipc\fP points at the next
instruction to be executed.  (Recall that the interpretable file is
loaded into memory.)  The opcode of the instruction is fetched
and the corresponding
word in the jump table is taken as the address of a sequence of
instructions that perform the desired operations.  A branch is
taken to the referenced location and the operation is performed.
The operation may require operands; if so,\^ they appear in the
instruction stream following the opcode.  The segment of code that
performs a particular operation is responsible for fetching the
appropriate operands out of the stream.  When the
operation is complete,\^ a jump is taken to the top of the interpreter
loop and the process continues.
.PP
Interpreter operations are of two types.  Operations of the first type
call a routine to perform a task.  Operations of the second type are
executed entirely by the interpreter; no subroutine call is necessary.
.PP
Operations that require a call to be made call routines in the
\*Moperators\fP or \*Mlib\fP directories.  The routine being called
may require one or more arguments.  If arguments are required,\^ they
appear on the stack.  When the routine returns,\^ it removes
any arguments that it was called with from the stack and leaves its
result on the top of the stack.
.PP
To facilitate the calling of
routines,\^ a table known as \*Moptab\fP parallels the jump table.
An opcode \fIn\fP references the \fIn\fPth word of the jump table.
If the operation designated by the opcode requires a call,\^ the
\fIn\fPth word of \*Moptab\fP contains the address of the routine
that should be called.
.PP
The interpreter saves space in its
instruction stream by encoding operand information in some opcodes.
For example,\^ the \*Mline\fP instruction has one operand that
is used to set the value of \*M_line\fP,\^ the current source line
number.  The \*Mlinex\fP instruction is an alternate form of
\*Mline\fP which encodes the line number as the low order bits of the
opcode.  Specifically,\^ the opcodes from 192 to 256 are \*Mlinex\fP
opcodes.  For example,\^ opcode 195 is equivalent to a \*Mline\fP
opcode with an operand of 3.
.SH
Implementing the Interpreter Loop
.PP
\*Minterp.s\fP stands alone among the assembly language files as one that is
well suited to coding in a macro fashion.  Most of the interpreter loop is
written in terms of \fIcpp\fP macros and thus porting it is largely a
matter of writing the macros for the target machine.
.LP
The following \*M#define\fPs must be made.
.Ip \*MOp\ \ \ \ \ \fP
.br
The operand register.  Any general purpose register will do.  The
value of the register need not preserved between instructions; its
lifetime is only from the time that an operand is fetched until the
next opcode is fetched or a routine is called.
.Ip \*MGetOp\fP
This must expand into code that fetches the next operand out of
the instruction stream and places it in the register \*MOp\fP.
Recall that operand size is determined by
the \*M#define\fP for \*MOPNDSIZE\fP in the linker.
On the VAX,\^ \*MGetOp\fP is merely
.Ds
.ta 0.6i +0.6i +0.6i +0.6i
movl	(ipc)+,\^Op
.De
This is because operands are one word long and can begin on any byte
boundary.  If the VAX did not support word fetching from arbitrary
boundaries,\^ it would be necessary to get the bytes from the
instruction stream one at a time and make a word out of them using
boolean operations.  If such were the case,\^ a reasonable alternative
would be to make opcodes one word in size and thus all instruction
stream objects (opcodes,\^ operands,\^ and words),\^ would be of the same
size and lie on word boundaries.
.\".IP \*MGetWord\fP
.\"\*MGetWord\fP is similar to \*MGetOp\fP,\^ but rather than loading the
.\"next operand,\^ it loads the next \fIword\fP from the instruction stream into
.\"the \*MOp\fP register.  Recall that the size of a word is defined by
.\"the \*MWORDSIZE\fP in the linker.  If words are the same size as
.\"operands on the target machine,\^ \*MGetWord\fP should be identical to
.\"\*MGetOp\fP.
.Ip \*MPushOp\fP
Push the \*MOp\fP register on the stack.  The VAX uses
.ta .6i
.Ds
pushl	Op
.De
.Ip \*MPushNull\fP
Push a descriptor for \*M&null\fP on the stack.  That is,\^ push two
words of zeroes.  The VAX \*Mclrq\fP instruction does the trick:
.Ds
clrq	-(sp)
.De
.Ip \*MPush_R(x)\fP,\^\ \*MPush_S(x)\fP,\^\ \*MPush_K(x)\fP
Push the value of \*Mx\fP on the stack.  To accommodate machines with
non-orthogonal instruction sets,\^ \*MPush_R\fP is used to push a register
value,\^ and \*MPush_S\fP is used to push the contents of a storage
location.  \*MPush_K\fP is used to push a constant value.
The VAX uses
.Ds
pushl	x
.De
for both \*MPush_R(x)\fP and \*MPush_S(x)\fP,\^ while
.Ds
pushl	$x
.De
is used for \*MPush_K(x)\fP.
.Ip \*MPushOpSum_R(x)\fP,\^\ \*MPushOpSum_S(x)\fP
\*MPushOpSum_R(x)\fP adds the value
of the register \*Mx\fP to \*MOp\fP and pushes the result on the stack.
\*MPushOpSum_S(x)\fP is similar,\^ adding the value in the memory
location \*Mx\fP to \*MOp\fP and pushing the result.  On the VAX,\^
.Ds
addl3	Op,\^x,\^-(sp)
.De
is used for both.
.Ip \*MNextInst\fP
Branch to the top of the interpreter loop.  The VAX uses
.Ds
jmp	_interp
.De
.Ip \*MCallN(n)\fP
Call the routine corresponding to the current opcode with \*Mn\fP
arguments.  On the VAX,\^ the opcode fetching segment loads \*Mr0\fP
with a byte offset into the jump table.  This same byte offset
references the location in \*Moptab\fP which contains the address
of the routine which corresponds to the current opcode.
\*MCallN(n)\fP expands to
.Ds
pushl	$n
calls	$((n*2)+1),\^*optab(r0)
.De
\*Mpushl $n\fP pushes the number of arguments on the stack.  This
word becomes the \fInargs\fP word of the procedure frame.  The
first of operand of the \*Mcalls\fP instruction is the length of
words in the argument list,\^ since each argument is a two word
descriptor and the \fInargs\fP word occupies another word,\^
\*Mn*2+1\fP is used as the length of the argument list.  The
address contained in the \*Moptab\fP word referenced by \*Mr0\fP is
the routine to call.
.Ip \*MCallNameN(n,\^f)\fP
Call the routine named \*Mf\fP with \*Mn\fP arguments.  This is very
similar to \*MCallN\fP,\^ the only difference being that the routine
to call is explicitly named rather than being implicitly determined
from the opcode value.  The VAX uses
.Ds
pushl	$n
calls	$((n*2)+1),\^f
.De
.Ip \*MBitClear(m)\fP
The constant value \*Mm\fP designates bits in the \*MOp\fP register
to leave on.  All other bits in \*MOp\fP should be turned off.  That
is,\^ the complement of \*Mm\fP is \*MAND\fPed with the contents of
\*MOp\fP and the result is placed in \*MOp\fP.  This is used to
decode opcodes with encoded operands.  The VAX uses
.Ds
bicl2	$0!m,\^Op
.De
.Ip \*MJump(lab)\fP
Branch to the label \*Mlab\fP.  The destination label is close to the
jump,\^ so a short jump of some type may be used.  The VAX uses
.Ds
jbr	lab
.De
.Ip \*MLongJump(lab)\fP
\*MLongJump\fP is like \*MJump\fP with the exception that \*Mlab\fP
may be quite distant.  The VAX uses
.Ds
jmp	lab
.De
.Ip \*MLabel(lab)\fP
Generate a label declaration for \*Mlab\fP.  The VAX uses
.Ds
lab:
.De
.SH
VAX Specific Sections of \*Minterp\fR
.PP
Several sections of \*Minterp\fP are machine specific and must be
coded on a per-machine basis.  The sections in question are explained
on an individual basis:
.Ip \*M_interp\fP
The next opcode is fetched and loaded into \*Mr0\fP.  \*Mmovzbl\fP
moves a byte and zero extends it to a word value.  Because a byte was
fetched,\^ \*Mipc\fP is incremented by 1.  The opcode is saved in
\*MOp\fP in case it contains an encoded operand.  \*Mr0\fP is
multiplied by 4 to turn it into a byte offset.  A jump is made to the
address indexed by \*Mr0\fP in \*Mjumptab\fP to perform the desired
operation.  Eventually,\^ a jump returns control to the label
\*M_interp\fP to fetch and execute the next instruction.
.Ip \*Mop_bscan\fP
A descriptor for \*M_k_subject\fP is pushed on the stack.  Then
the value of \*M_k_pos\fP is pushed,\^ followed by the constant
\*MD_INTEGER\fP.  The routine corresponding to \*Mop_bscan\fP,\^
\*M_bscan\fP,\^ is called with 0 arguments.  (This causes the
descriptors for \*M_k_subject\fP and the value of \*M_k_pos\fP to be
left on the stack.)  When \*M_bscan\fP returns,\^ a branch is made to
\*M_interp\fP.
.Ip \*Mop_ccase\fP
A null descriptor is pushed on the stack.  The word immediately
above the current expression frame is then pushed on the stack.
.Ip \*Mop_chfail\fP
The operand of \*Mchfail\fP is fetched into \*MOp\fP.  \*MOp\fP and
\*Mipc\fP are added together and the result replaces the failure
address in the current expression frame.
.Ip \*Mop_dup\fP
A null descriptor is pushed on the stack.  The value that was on top
of the stack is now at \*M8(sp)\fP,\^ and it is copied to the top of the
stack using a \*Mmovq\fP.
.Ip \*Mop_eret\fP
\*Meret\fP gets the value on top of the stack,\^ removes the current
expression frame and puts the previous top of stack value back on the
top of the stack.  First of all,\^
.Ds
movq	(sp)+,\^r0
.De
moves the descriptor on the top of the stack into the \*Mr0\-r1\fP
register pair and increments the stack pointer by 8.  The \*Mgfp\fP
is loaded with the \*Mgfp\fP value stored in the expression frame
marker.  \*Msp\fP is loaded from \*Mefp\fP,\^ bringing the expression
marker to the top of the stack.  The old \*Mefp\fP value from the
marker is loaded into \*Mefp\fP.  Finally,\^ the value stored in the
\*Mr0\-r1\fP pair is pushed on the stack.
.Ip \*Mop_file\fP
The operand of \*Mfile\fP is loaded into \*MOp\fP.  \*MOp\fP and the
value of \*M_ident\fP are added and the result in placed in
\*M_file\fP.
.Ip \*Mop_goto\fP
The operand is loaded into \*MOp\fP and then added to \*Mipc\fP.
.Ip \*Mop_incres\fP
The eighth word of the co-expression heap block for the current
expression is incremented by one.
.Ip \*Mop_init\fP
This one is tricky.  The \*Minit\fP instruction arises from the
\*Minitial\fP statement in Icon and is used to effect one-time
execution of a segment of code.  The operand of \*Minit\fP is the
address of the first instruction after the segment that is to be
executed once.  The instruction
.Ds
movb	$59,\^-(ipc)
.De
decrements \*Mipc\fP by 1 and then stores the constant 59 in the byte
that \*Mipc\fP references,\^ which is the \*Minit\fP opcode.  The magic
number 59 is the opcode for \*Mgoto\fP,\^ so in effect,\^ the \*Minit\fP
had been made into a goto that skips a section of code.  By adding 5
to \*Mipc\fP,\^ it leaves \*Mipc\fP pointing at the first instruction
of the \*Minitial\fP code.  The constant 5 is derived from the
width of the opcode and associated operand,\^ i.e.,\^
\*MOPSIZE+OPNDSIZE\fP.
.Ip \*Mop_invoke\fP
This section has two entry points: \*Mop_invoke\fP gets control if
\*Minvoke\fP has an operand,\^ and \*Mop_invkx\fP gets control if the
operand is encoded in the opcode.  If an operand is specified,\^ it is
fetched into \*MOp\fP.  If the operand is encoded,\^ \*MBitClear(7)\fP is
used to isolate the operand in \*MOp\fP.  Control converges at
\*Mdoinvoke\fP.  The operand is the number of arguments to
invoke the procedure with and it is pushed on the stack as
the \fInargs\fP word.  (The arguments are already on the stack.)
The \*Mcalls\fP instruction needs the length of the argument list,\^ so
\*MOp\fP is multiplied by 2 and then incremented by 1.  \*Minvoke\fP
is called.
.Ip \*Mop_int\fP
As in \*Mop_invoke\fP,\^ \*Mop_int\fP has a secondary entry point,\^
\*Mop_intx\fP,\^ for operands encoded in the opcode.  At the \*Mop_int\fP
entry point,\^ a \*MWORDSIZE\fP value is fetched out of the instruction
stream and loaded into \*MOp\fP.  At the \*Mop_intx\fP entry point,\^
the \*MOp\fP value is decoded from the operand.  The \*MOp\fP value is
pushed on the stack and is followed by a \*MD_INTEGER\fP word,\^ forming
an integer descriptor.
.Ip \*Mop_line\fP
Like \*Mop_invoke\fP and \*Mop_int\fP,\^ \*Mop_line\fP has a secondary
entry point.  The operand value is obtained and then moved into \*M_line\fP.
.Ip \*Mop_llist\fP
\*Mllist\fP is similar to \*Minvoke\fP in that it has a number of
arguments already on the stack and that the operand specifies the
number.  The operand is fetched into \*MOp\fP and pushed on the
stack to become the \fInargs\fP argument of \*Mllist\fP.  \*MOp\fP is
then multiplied by 2 and incremented by 1 to serve as an argument
list size for \*Mcalls\fP.
.Ip \*Mop_mark\fP
The operand is fetched into \*MOp\fP and \*Mipc\fP is added to it.
\*Mefp\fP is pushed on the stack and the new \*Msp\fP value is put in
\*Mefp\fP.  \*Mgfp\fP is pushed on the stack and cleared.  \*MOp\fP
is pushed on the stack.
.Ip \*Mop_mark0\fP
Like \*Mop_mark\fP,\^ with an implicit operand value of zero.
.Ip \*Mop_pop\fP
The two \*Mtstl\fP instructions serve to add 8 to \*Msp\fP which
removes the top value from the stack.
.Ip \*Mop_sdup\fP
The descriptor on the top of the stack is pushed on the stack,\^
duplicating it.
.Ip \*Mop_unmark\fP
The operand,\^ the number of expression frames to remove from the
stack,\^ is fetched into \*MOp\fP.  \*Mefp\fP is
restored from the current expression frame.  The instruction
.Ds
sobgtr	Op,\^unmkjmp
.De
decrements \*MOp\fP and then branches to \*Mdounmark\fP if \*MOp\fP is not
zero.  This chains through the number of expression frames specified
by the operand.  \*Mgfp\fP is restored from the current expression
marker.  \*Mefp\fP is loaded into \*Msp\fP to move the expression
marker to the top of the stack.  Finally,\^ \*Mefp\fP is restored
from the marker and \*Msp\fP is incremented to remove the last word
of the marker.
.Ip \*Mop_unmk1-7\fP
Similar to \*Munmark\fP,\^ but uses successive restorations of
\*Mefp\fP rather than a loop.
.Ip \*Mop_global\fP
Dual entry points are used to deal with possible operand encoding.
The operand,\^ which is a number of a variable in the global region,\^
is multiplied by 8 to provide a byte offset from the start of
the global region.  The sum of \*MOp\fP and the value of \*Mglobals\fP
is pushed on the stack to provide a descriptor address.  The constant
\*MD_VAR\fP is pushed on the stack to complete the descriptor for the
global variable.
.Ip \*Mop_static\fP
Identical to \*Mop_global\fP except that \*Mstatics\fP is used
instead of \*Mglobals\fP.
.Ip \*Mop_local\fP
The operand value is the number of a local variable for which a
variable descriptor is to be pushed on the stack.  Recall that the
local variables lie below the procedure frame and,\^ on the VAX,\^ the
descriptor for the first one is at \*M\-16(fp)\fP.  \*MOp\fP is
negated.  The instruction
.Ds
pushaq	-16(fp)\^[Op]
.De
performs the calculation
.Ds
-16 + fp + (Op * 8)
.De
which computes the address of the descriptor of the desired variable
and pushes it on the stack.  The variable descriptor is completed by
pushing \*MD_VAR\fP on the stack.
.Ip \*Mop_arg\fP
Like  \*Mop_local\fP,\^ but it uses \*M8(ap)\fP as the base for the
address calculation and the operand value is not negated.
.Ip \*Mquit\ \ \ \ \ \fP
Push a 0 on the stack and call the routine \*M_c_exit\fP to terminate
execution of the Icon program.
.Ip \*Merr\ \ \ \ \ \fP
\*Merr\fP should never be encountered during normal execution.
Reaching it indicates that an invalid opcode was encountered.
It need not do anything more than abort execution.  On the VAX,\^
it calls \*Msprintf\fP to create a string containing the invalid
opcode and the \*Mipc\fP where it was encountered and then calls
\*Msyserr\fP with the string as an argument.
.NH 3
\*Mlib/efail.s\fR
.SH
Overview
.PP
\*Mefail\fP handles the failure of an expression.  When
Icon evaluates an expression,\^ it tries to produce a result from it.
If at some point in the evaluation of an expression the expression
fails,\^ Icon resumes inactive generators in the expression in
an attempt to make the expression succeed. \*Mefail\fP is at the
heart of this activity.
.LP
\*Mefail\fP has three distinct outcomes:
.Ls
.Np
Resumption of the newest inactive generator in the current
expression frame.
.Np
Failure of the current expression with execution continuing
at the failure address contained in the expression marker.
.Np
Failure of the current expression with propagation of failure
to the enclosing expression frame.  This is similar to (2),\^
but occurs when the failure address is 0.  After the current
expression fails,\^ \*Mefail\fP loops back to its entry point
to fail again.
.Le
.PP
\*Mefail\fP is branched to rather than being
called.  This is because it
serves as a ``back-end'' for several failure
actions that may occur during the course of execution:
.Ls
.Np
When a built-in procedure fails,\^ it calls the routine \*Mfail\fP,\^ which
in turn branches to \*Mefail\fP.
.Np
When an Icon procedure fails via the \*Mpfail\fP routine,\^ \*Mpfail\fP
terminates by branching to \*Mefail\fP.
.Np
When the \*Mefail\fP opcode is executed by the interpreter,\^ \*Mefail\fP
is branched to.
.Np
The generator frames built by \*Mesusp\fP and \*Mlsusp\fP use \*Mefail\fP
as a return address.  This is explained in detail later.
.Le
.SH
Generic Operation
.PP
\*Mefail\fP is essentially a simple routine.  There are two separate
paths of execution that \*Mefail\fP may take.  The first is to
resume an inactive generator.  The second is to cause failure of
the expression in lieu of an inactive generator.
.PP
If there is an inactive generator in the current expression frame,\^
it must be resumed.
If the generator is an Icon procedure and tracing is in on,\^
\*Matrace\fP is called with appropriate arguments.  \*M_k_level\fP,\^
\*M_line\fP,\^ and \*M_file\fP are restored from the generator frame.  A return
is performed and the net result is that the stack is restored to
the state that it was in before the suspension that created the
generator.
.PP
If there are no inactive generators that can be resumed,\^ the
expression being evaluated must fail.  This is done by popping
the stack back through the current expression frame and resuming
execution at the point indicated by the failure address in
the expression marker.  This is a two-step process.  The first
is to pop the frame and the second is to resume execution.
When the frame is popped,\^ the expression has failed.  The
failure address in the expression marker is saved before
the frame is popped.  If this address is not zero,\^ execution
is continued by branching to the address.  If the address is
zero,\^ the failure is propagated to the enclosing expression by
branching to efail.
.PP
Zero failure addresses are generated by the ucode instruction
.Ds
mark  L0
.De
Such instructions are used to avoid a special case during code
generation and the only purpose they serve during program execution
is to create an expression marker for the corresponding
\*Munmark\fP instruction to remove.  (\*Mmark\fP and \*Munmark\fP
instructions are paired.)
Thus,\^ whenever \*Mefail\fP pops an expression whose marker has a
zero failure address,\^ \*Mefail\fP causes failure in the enclosing
expression.
.SH
\*Mefail\fP on the VAX
.PP
The first action is to determine if there is an inactive generator
that can be reactivated.  If the generator frame pointer
is non-zero,\^ it points to the newest inactive generator.
Note that whenever a new expression frame is created,\^ the generator
frame pointer is zeroed.  Thus,\^ if \*Mgfp\fP is non-zero,\^ the
generator frame that it points to belongs to a generator in the
current expression frame.
.PP
If an inactive generator is available,\^ it must be reactivated.
First,\^ \*M_boundary\fP is restored from the generator frame.
The stack is popped back to the generator frame by loading
\*Mfp\fP from \*Mgfp\fP.  But,\^ before \*Mfp\fP is loaded,\^
its value is saved in \*Mr0\fP.  \*Mfp\fP now points at
word 0 of the generator frame,\^ but that is a word below the
actual stack frame that it should be pointing at,\^ so \*Mfp\fP is
incremented by 4 using a \*Mtstl\fP.
.LP
There are three types of generators that may be encountered
by \*Mefail\fP.
.Ls
.Np
An Icon procedure that did a \*Msuspend\fP.  In such cases,\^ the
routine \*Mpsusp\fP handled the suspension.
.Np
A built-in procedure that called the C function \*Msuspend()\fP.
.Np
A generator created by an \*Mesusp\fP or \*Mlsusp\fP instruction.
Such generators
arise from source code constructs like \*M\*(x1 |\ \^\*(x2\fP,\^ \*M|\*(xx\fR,\^ and
\*M\*(x1 \e\ \^\*(x2\fR,\^ which are referred to as \fIcontrol regimes\fP.
.Le
.PP
The generators may be treated the same way as far as resumption goes.
However,\^ if an Icon procedure is being resumed,\^ a tracing message must
be generated if \*M_k_trace\fP is not 0.
.PP
If the value of \*M_boundary\fP is not the same as \*Mfp\fP,\^ the
generator is a built-in procedure and tracing is not done.
If the \*Mfp\fP saved in the current frame is the same as the
\*Mfp\fP was upon entry to \*Mefail\fP (the value was saved in
\*Mr0\fP),\^ the generator was made by an \*Mesusp\fP or an \*Mlsusp\fP
and tracing is not done.
.PP
Otherwise,\^ the generator is an Icon procedure,\^ and \*Matrace\fP must
be called.  \*Matrace\fP takes one argument,\^ the address of the
procedure block for the procedure being resumed.  Recall that \*(e0 on the
stack is a descriptor for the procedure block.  The address of
\*(e0 is calculated using
.Ds
&\*(e0 = ap + 8 + (8 * nargs)
.De
The resulting address is used as the single argument for \*Matrace\fP.
.PP
The generator is now ready to be resumed.
\*M_k_level\fP,\^ \*M_line\fP,\^ and \*M_file\fP are restored by popping them
from the generator frame.  If the generator is a built-in procedure,\^
\*M_boundary\fP is cleared.  A return is performed to activate the
generator.
.PP
The return has different effects depending on the type of generator
being resumed.
.PP
If the generator is a built-in procedure,\^ the return
restores the stack to the state it was in before \*Msuspend\fP was
called,\^ and execution proceeds at the point just after \*Msuspend()\fP.
In this case the \*Mpc\fP value being returned to references an instruction
in the built-in procedure.
.PP
If the generator is an Icon procedure,\^ the stack is restored to
the state it was in before the \*Mpsusp\fP ucode instruction
was executed.  The \*Mpc\fP value being returned to references an
instruction in the interpreter loop.  Execution of the program
continues with the interpreter instruction following the \*Mpsusp\fP.
.PP
If the generator is a control regime,\^ the stack is restored to
the state it was in before the \*Mesusp\fP or \*Mlsusp\fP that
created the generator was performed.  The
return \*Mpc\fP points to \*Mefail\fP itself.  Thus,\^ when the
return is done,\^ the stack is cleared,\^ and an \*Mefail\fP is performed.
This has the effect of transferring control to the failure label
in the expression marker of the bounding expression frame.
.\"(The failure label lies at the start of code for the alternative.)
.PP
If there is no generator to reactivate,\^ the expression must fail.
This is handled at the label \*Mnogen\fP.  \*Mefp\fP points to the
expression frame marker.  \*Mipc\fP is loaded
from \*M\-8(efp)\fP which contains the address to go to in the
event that the current expression fails.  (As it has.)
\*Mgfp\fP is restored from the expression marker.
\*Mefp\fP is restored from the marker and the marker is popped off the stack.
.PP
If the failure address in \*Mipc\fP is non-zero,\^ control is passed
back to the interpreter via a branch and execution of the ucode
resumes at the failure address.  If \*Mipc\fP is zero,\^ the expression
failure is transmitted to the surrounding expression frame
by a branch to \*Mefail\fP.  (Recall that a zero failure address
comes from a \*Mmark L0\fP instruction and that a failure that
reaches a \*Mmark L0\fP marker must be propagated to the next
expression marker.)
.NH 3
\*Mlib/pfail.s\fR
.SH
Overview
.LP
\*Mpfail\fP handles the failure of an Icon procedure.  An Icon
procedure can fail by
.Ds
.ft R
executing a \*Mfail\fP expression
executing \*Mreturn \fIexpr\fR when \fIexpr\fR fails
allowing the flow of control to reach the end of a procedure
.De
\*Mpfail\fP is entered via a branch when
the interpreter encounters the \*Mpfail\fP instruction.
.SH
Generic Operation
.PP
The task of \*Mpfail\fP is to signal failure in the expression that
contains the procedure call being evaluated.  This is done
by removing the Icon procedure frame from the stack,\^ restoring
appropriate registers and values,\^ and calling \*Mefail\fP.  The key is that
all \*Mpfail\fP needs to do is to remove the procedure frame
from the stack and from then on things can be handled just like expression
failure.
Thus,\^ \*Mefail\fP does most of the work.
.PP
\*Mpfail\fP calls \*Mftrace\fP to produce a trace message if
tracing is on.  \*Mpfail\fP also decrements \*M_k_level\fP because
a procedure is being exited.
.PP
Note that the procedure frame on the stack is a frame that
was created by \*Minvoke\fP.
.SH
\*Mpfail\fP on the VAX
.PP
After \*M_k_level\fP is decremented,\^ \*M_k_trace\fP is checked to
see if a trace message should be produced.  If tracing is on,\^
\*Mftrace\fP must be called.  \*Mftrace\fP takes one argument,\^
the address of the procedure block for the failing procedure.
\*(e0 is the descriptor for the procedure block,\^ and the address
of \*(e0 is calculated using
.Ds
&\*(e0 = (\fInargs\fP * 8) + 8 + ap
.De
The resulting address is pushed on the stack and \*Mftrace\fP is
called.
.PP
Execution continues at \*Mdofail\fP to remove the procedure frame
from the stack.  The frame cannot be merely popped because it
contains state information that must be restored.  \*M_line\fP and
\*M_file\fP are extracted from the frame.  \*Mefp\fP,\^ \*Mgfp\fP,\^ and
\*Mipc\fP are restored from the frame using addresses
relative to \*Map\fP.  Note that this works because \*Minvoke\fP saves
these registers and their location is known.
.PP
\*Map\fP and \*Mfp\fP are restored from the frame.  When \*Mfp\fP is
restored,\^ it serves to remove the procedure frame (made by
\*Minvoke\fP) from the stack.  At this point,\^ the stack is the
same state it was in before the interpreter performed the
\*Minvoke\fP instruction.  A branch is made to \*Mefail\fP to
cause failure in the enclosing expression.
.NH 2
Testing the Basis
.PP
At this point,\^ enough of the system has been written to run some
very simple Icon
programs.  \*Mtest/hello.icn\fP should be functional
and more complete testing is in order.  Refer to \fITesting the
Basis\fP in \^[5].