[net.sources] UNIX FORTH for the PDP11

lwt1@aplvax.UUCP (06/08/84)

Here is part 1 of the source for FORTH for the PDP-11.
Delete everything thru the "-- cut here --" line, and extract with 'sh':

	sh part1 part2 ... part7

where 'part?' are whatever you've named the files.  Note the copyright
notice at the end of README.  Please let us know how things go.  While
we can't support this software, we'll be posting bug fixes/upgrades to
net.sources as time permits.

VAX-FORTH should be 'forth'-coming {yuk-yuk} within a couple of weeks.

Have fun!



						-Lloyd W. Taylor
						 ... seismo!umcp-cs!aplvax!lwt1
---I will have had been there before, soon---

---------------------------------- cut here ----------------------------------
echo x - README
cat >README <<'+E+O+F'
.TL
Unix-FORTH for the PDP-11
.AU 
John R. Hayes
.AI
Applied Physics Lab
Johns Hopkins University
.ND
.PP
.bp
.PP
.UL Introduction.
FORTH running under unix is now available.  Typing 'forth'
from the terminal will invoke a FORTH process for you.  This memo describes
the unix specific features of this version of FORTH and how to boot the system.
The last section of 
this document deals entirely with unix-FORTH I/O programming.
.PP
Unix-FORTH is a subset of FORTH-83.  The only place that unix-FORTH
and FORTH-83 diverge is in the implementation of I/O.  It seems natural 
that a unix FORTH should take advantage of unix's elegant I/O structure
even at the cost of standardization.  Therefore, unix-FORTH is a process
that reads commands from its standard input and sends results to its standard
output.  If the standard input is the user's terminal, an interactive FORTH
session results.  Or a file of batch commands can be attached to the 
standard input and executed non-interactively.
.PP
A programmer used to typical FORTH systems will immediately note the
absence of FORTH screens.  FORTH screens are inadequate for managing
anything but the smallest programs and arbitrarily constrain software
modules to be sixteen lines long.  Unix-FORTH uses the unix file system and 
programs are created with any text editor.  Therefore, the entire unix 
toolbox is available for operation on FORTH source files.  Unix-FORTH
provides a set of I/O words that are very similar to their unix system-call
counterparts.  The user can have up to fifteen (system dependent) files
open simultaneously.
This, along with unix-FORTH's I/O implementation, allow the use of nested
loads.
.PP
A number of other enhancements are available to the user of unix-FORTH.
Any program resident in the unix file system can be executed from within
FORTH.  For example, to list the files in your current directory on the line
printer, you would type:
.DS L
     " ls | lpr" SYSTEM
.DE
A new subshell can be spawned
without disturbing your current FORTH environment by typing SHELL.  Typing
a ^C will cause FORTH to execute its warm start code.  This allows you
to terminate a program run amok without killing FORTH.  ^D (eof) will 
terminate the FORTH process.
.PP
.UL Bootstrapping.
Booting FORTH consists of two steps.  First, assemble the bootstrap system
with the command:
.DS L
 as -o bootforth  prim.as os.as
.DE
This will generate a FORTH subset system adequate for metacompiling the actual
system.  One potential problem with this step is the use of the PDP-11 extended
instruction set operations DIV and MUL.  If your machine lacks these 
instructions, you will have to code them yourself.  Bootforth is an executable
object file of a small FORTH system.  You might want to test it before going
on.
.PP
The second step consists of using  bootforth to metacompile the actual system.
Type:
.DS L
 bootforth <auto | tee map
.DE
auto is a file containing forth commands to control the metacompilation.
map will contain a memory map of the system useful for debugging.  The new
system will be called newforth.  A good test of the new system is to see if 
it can metacompile itself.
.PP
Three more possible portability problems exist.  The first is in the a.out
format used.  Our version of unix uses:
.DS L
struct	exec {		/* a.out header */
	int     	a_magic;	/* magic number */
	unsigned	a_text; 	/* size of text segment */
	unsigned	a_data; 	/* size of initialized data */
	unsigned	a_bss;  	/* size of unitialized data */
	unsigned	a_syms; 	/* size of symbol table */
	unsigned	a_entry; 	/* entry point */
	unsigned	a_unused;	/* not used */
	unsigned	a_flag; 	/* relocation info stripped */
};

#define	A_MAGIC1	0407       	/* normal */
#define	A_MAGIC2	0410       	/* read-only text */
#define	A_MAGIC3	0411       	/* separated I&D */
#define	A_MAGIC4	0412       	/* mapped read-only text */
.DE
This information is embedded in META1.  The second problem is in the number
of open files per process allowed by the operating system.  The FILEPOS table
is SYS:ASM must have as many entries as open files allowed by your version
of Unix.  There are currently fifteen entries in this table. The final problem
could be in the implementation of system calls.  Our Venix system (similar to
Unix version 7) implements system calls as TRAP instructions and returns an
error flag in the C bit of the condition codes.  If your system behaves
differently, the comments should help you to modify the code appropriately.
.PP
.UL I/O.
The following paragraphs review low-level unix I/O programming.  Some 
previous knowledge is assumed, so you may want to read the low-level I/O
section in "Unix Programming".  Refer to the glossary for an exact description
of how any word behaves.
.PP
Most I/O words use a file descriptor as a parameter instead of the name of 
the file.  A file descriptor is a small non-negative integer that indexs a
unix internal file table.  File descriptors are not the same as the file
pointers used in the C standard I/O library.  The FORTH word READ is typical
in its use of a file descriptor.  The input parameters to READ are the file
descriptor of the file to be read, the address of a receiving buffer, and
the number of bytes to read. READ returns the actual number of bytes read. 
If this is less than the requested number, EOF was encountered or an error
occurred.  The action of WRITE is similar.  All files are accessed sequentially
unless an explicit SEEK command is issued.  The parameters to SEEK are a file
descriptor and a double word file position.
.PP
The OPEN word is used to associate a file name with a file descriptor.  The
parameters to OPEN are the address of a file name text string and a file 
mode.  The string must be null terminated instead of a standard FORTH
counted string.  Unix-FORTH provides some useful words for handling null
terminated strings.  These are described below.  The file mode can be 
0=read-only, 1=write-only, and 2=read-write.  OPEN either returns a file 
descriptor that will be used for accessing the file or returns a -1 indicating
an error of some sort.  Since there are a finite number of file descriptors
per process, the programmer should CLOSE unneeded files to free
file descriptors.  The parameter to CLOSE is a file descriptor.
.PP
To create a new file, the CREAT word is available.  The parameters are the
address of a file name text string and a protection mode bit mask.  The file
is created and opened for writing.  If the file already exists, its length is
truncated to zero.  CREAT returns either a file descriptor or a -1 indicating
an error.
.PP
When the FORTH process is started, three files with file descriptors 0, 1,
and 2 have already been opened.  These correspond to the standard input, 
standard output, and standard error.  FORTH expects commands from the standard
input and types results to the standard output.  The standard error file is
not used by FORTH.  Two CONSTANTS, STDIN and STDOUT with values 0 and 1
respectively are pre-defined in unix-FORTH.
.PP
Unix-FORTH has two words, FEXPECT and FQUERY for line oriented input.
FEXPECT's parameters are a file descriptor, the address of a receive buffer,
and the number of characters to read. FEXPECT reads the requested number of
characters unless a newline or an EOF is encountered and returns the number 
of characters actually read. FEXPECT also converts tabs to blanks.
FQUERY is like FEXPECT expect that FQUERY reads up to 120 characters into 
TIB, the FORTH text input buffer. 
.PP
All FORTH system output goes through the FORTH-83 standard word TYPE.  To
allow FORTH to control redirection of its output, TYPE sends its output
to each file in a table of four file descriptors.  Two words, OUTPUT and
SILENT, are used to edit the table.  Both words use a single file descriptor
as a parameter.  OUTPUT will add the file descriptor to the table if the
table is not already full.  SILENT will remove all instances of its file
descriptor from the table.  As an experiment, try typing:
.DS L
     STDOUT OUTPUT
.DE
.PP
The word FLOAD is used to load FORTH source code.  It's single parameter
is the address of a null terminated string describing the path name of
the desired FORTH file.  There are two words in unix-FORTH for converting
strings in the input stream into null terminated strings. The word " reads
the input stream until a second " is found, moves the string to PAD placing
a null at the end, and returns the address of PAD.  The word "" is a 
compiling version of " to be used inside colon definitions.  The address
of the null terminated string isn't put on the stack until run-time.
Both " and "" are defined in terms of the word STRING.  STRING converts
a counted string to a null terminated string without modifying the counted
string.
.PP
Unix-FORTH maintains a 512 byte block of memory used for buffering the most
recently used read file.  Writing to a file is unbuffered by unix-FORTH.
Due to the read buffering the unix-FORTH file position and the unix maintained
file position can become inconsistent.  This is never a problem with read-only
or write-only files.  However, this can cause loss of data in read-write 
files unless the following simple rule is followed with read-write files.
Always use a SEEK call when switching from reading to writing or from writing
to reading.
.bp
.DS L
Copyright 1984 by The Johns Hopkins University/Applied Physics Lab.
Free non-commercial distribution is *encouraged*, provided that:

	1.  This copyright notice is included in any distribution, and
	2.  You let us know that you're using it.

Please notify:

	Lloyd W. Taylor
	JHU/Applied Physics Lab
	Johns Hopkins Road
	Laurel, MD 20707
	(301) 953-5000

	Usenet:  ... seismo!umcp-cs!aplvax!lwt1


Unix-FORTH was developed under NASA contract NAS5-27000 for the
Hopkins Ultraviolet Telescope, a March 1986 Space Shuttle mission.  (we
hope to take a peek at Halley's comet!)

Written entirely by Wizard-In-Residence John R. Hayes.

* Unix is a trademark of Bell Labs.
.DE

+E+O+F
echo x - forth.1h
cat >forth.1h <<'+E+O+F'
.TH FORTH 1H
.SH NAME
forth
\- invoke a forth process.
.SH SYNOPSIS
forth
.SH DESCRIPTION
Forth invokes a FORTH-language process.  The process reads commands from the
standard input and sends results to the standard output.  If the standard 
input is a terminal, an interactive forth session results.  This is a subset
of FORTH-83 diverging only in the I/O.
This utility was developed independently from any UNIX or VENIX source code.
.SH "SEE ALSO"
Unix-FORTH for the TEGSE, TCE-T84-34
.SH AUTHORS
J. Hayes
+E+O+F
echo x - format.c
cat >format.c <<'+E+O+F'
/*	
 *	Use:
 *		format [-l num] [-t file] [file file ... ]
 *
 *	This program formats records of arbitrary size and pretty-prints
 *	them.  Records are delimited by '\'.  A title is printed on each
 *	page and the records are separated by a line of dashes.  Records
 *	are prevented from spanning page boundaries.  The -l flag is used
 *	to specify the number of lines per page of your output device.
 *	The default is 63.  The -t flag is used to specify a file that
 *	contains a title that is to be printed on the top of each page.
 */

#include <stdio.h>

#define MAXLINES 15
#define LINELENGTH 120

	char title[10*LINELENGTH]="";	/* default: not title */
	int titlelen=0;

	int linesppage=63;		/* default: 63 lines per page */

main(argc,argv)
int argc;
char *argv[];
{
	char *s;
	FILE *fp;

	while (--argc>0 && **++argv=='-')
		switch (*(*argv+1)){
			case 't':
				argc--; argv++;
				if ((fp=fopen(*argv,"r"))!=NULL){
					s=title;
					while (fgets(s,LINELENGTH,fp)!=NULL){
						s+=strlen(s);
						titlelen++;
					}
					fclose(fp);
				}
				else fprintf(stderr,
                                        "format: can't open %s\n",*argv);
				break;
			case 'l':
				argc--; argv++;
				if (sscanf(*argv,"%d",&linesppage)==0)
					fprintf(stderr,
                                           "format: %s isn't a number\n",*argv);
				break;
			default:
				fprintf(stderr,
				   "format: bad flag %c\n",*(*argv+1));
				break;
		}
		if (argc>0)
			while (argc-- > 0){
				if ((fp=fopen(*argv,"r"))!=NULL){
					format(fp);
					fclose(fp);
				}
				else
					fprintf(stderr,
                                           "format: can't open %s\n",*argv);
				argv++;
			}
		else
			format(stdin);
}

format(input)
FILE *input;
{
	char buf[MAXLINES*LINELENGTH];
	char *bufp=buf;

	int nextline=0;

	while(fgets(bufp,LINELENGTH,input)!=NULL){
		if(*bufp!='\\'){
			nextline++;
			bufp+=strlen(bufp);
		}
		else {
			*bufp='\0';
			printrec(buf,nextline);
			bufp=buf;
			nextline=0;
		}
	}
}

printrec(lines,nlines)
char *lines;
int nlines;
{
	static int linect=1000;			/* absurd number forces
						   title on first page */

	int i;

	if (nlines+1 > linesppage-linect){
		printf("\f%s",title);
		linect=titlelen;
	}
	for (i=1; i<80; i++) putchar('-');
	printf("\n%s",lines);
	linect+=nlines+1;
}
+E+O+F

lwt1@aplvax.UUCP (06/08/84)

Here is part 2 of the source for FORTH for the PDP-11.
Delete everything thru the "-- cut here --" line, and extract with 'sh':

	sh part1 part2 ... part7

where 'part?' are whatever you've named the files.  Note the copyright
notice at the end of README.  Please let us know how things go.  While
we can't support this software, we'll be posting bug fixes/upgrades to
net.sources as time permits.

VAX-FORTH should be 'forth'-coming {yuk-yuk} within a couple of weeks.

Have fun!



						-Lloyd W. Taylor
						 ... seismo!umcp-cs!aplvax!lwt1
---I will have had been there before, soon---

---------------------------------- cut here ----------------------------------
echo x - glossary
cat >glossary <<'+E+O+F'
!			n addr			-

n is stored at addr.
\
!SP			n			-

Parameter stack pointer is set to n.
\
"			-			addr[string]

Generates a null-terminated string.  Used in the form:
	" string"
Copies the input stream to PAD until a second " is found, places a null at
the end of the string, and returns the address of the string.
\
""			-			addr[string]

Compiling word. Generates a null-terminated string at run time. Used as:
	"" string"
Adds a counted string copied from the input stream to the dictionary. At
run-time, converts the string to a null-terminated string and returns address
of string.
\
#			d1l d1h			d2l d2h

The remainder of d1 divided by the value of BASE is converted to an
ASCII character and appended to the pictured output string growing toward
lower memory addresses.  d2 is the quotient and is maintained for further
processing.  Typically used between <# and #>.
\
#>			dl dh			addr +n

Pictured numeric output conversion is ended dropping d.  addr is the
address of the resulting output string and +n is the length of the string.
\
#S			dl dh			0 0

d is converted appending each resultant character onto the pictured
numeric output string until the quotient is zero.  A single zero is added
to the output string if d is zero.  Typically used between <# and #>.
\
'			-			cfa

Used in the form:
	' <name>
cfa is the compilation address of <name>.  A zero is returned if <name> 
could not be found in the dictionary.
\
(FIND)			addr[name] addr[dict]	0 <or> nfa

Searches the vocabulary whose latest dictionary entry nfa is pointed to
by addr[dict] for the counted string pointed to by addr[name].  Returns
the nfa of the word if found, otherwise a zero is returned.  If addr[dict]
is zero, indicating an empty vocabulary, a zero is returned.
\
(LITERAL)		-			word

Pushes the word pointed to by the IAR onto the parameter stack and adds two
to the IAR.
\
*			w1 w2			w3

w3 is the least-significant 16 bits of the arithmetic product of w1 times
w2.
\
*/			n1 n2 n3		quot

n1 is first multiplied by n2 producing an intermediate 32-bit result. quot
is the floor of the quotient of the intermediate 32-bit result divided by
the divisor n3.  The product of n1 times n2 is maintained as an intermediate 
32-bit result for greater precision than the otherwise equivalent sequence:
n1 n2 * n3 / .  An error condition results if the divisor is zero or if the
quotient falls outside the range {-32768..32767}.
\
*/MOD			n2 n3 n3		rem quot

n1 is first multiplied by n2 producing an intermediate 32-bit result.  quot
is the floor of the quotient of the intermediate 32-bit result divided by the
divisor n3.  A 32-bit intermediate product is used as for */ .  rem has the 
same sign as n3 or is zero.  An error condition results if the divisor is 
zero or if the quotient falls outside of the range {-32768..32767}.
\
+			w1 w2			w3

w3 is the arithmetic sum of w1 and w2.
\
+!			w1 addr			-

w1 is added to the contents of addr.
\
+-			n1 n2			n3

Apply the sign of n2 to n1 to obtain n3.  n3 := sign(n2) * n1.
\
+LOOP			n			-

n is added to the loop index.  If the new index was incremented across
the boundary between limit-1 and limit then the loop is terminated and
loop control parameters are discarded.  When the loop is not terminated,
execution continues just after the corresponding DO.
\
,			n			-

ALLOT one word of space at the end of the dictionary and store n in this
space.
\
,WORD			char			-

Compile text from the input stream delimited by char into the dictionary as
a counted string.  The minimum even number of bytes that will hold the text
is ALLOTted.
\
-			w1 w2			w3

w3 is the result of subrtracting w2 from w1.
\
-1			-			-1

CONSTANT that returns -1.
\
-IMM			nfa			cfa n 

Given a name field address, returns the corresponding code field address
and a flag n which is -1 if the word is non-immediate and 1 if the word
is immediate.
\
-ROT			w1 w2 w3		w3 w1 w2

The top three stack entries are rotated, moving the top entry to the
bottom.  -ROT is the converse of ROT.
\
-TRAILING		addr +n1		addr +n2

The character count +n1 of a text string beginning at addr is adjusted to
exclude trailing spaces.  If +n1 is zero, then +n2 is zero.  If the entire
string consists of spaces, then +n2 is zero.
\
.			n			-

The absolute value of n is displayed in a free field format with leading 
minus sign if n is negative.
\
."			-			-

Compiling word used in the form:
	." cccc"
Later execution will display the character cccc up to but not including the
delimiting " (close-quote).  The blank following ." is not part of cccc.
\
.(			-			-

Immediate word used in the form:
	.( cccc)
The characters cccc up to but not including the delimiting ) (closing paren-
thesis) are displayed.  The blank following .( is not part of cccc.
\
.R			n size			-

Attempts to display n right-justified in a field of size characters.
\
/			n1 n2 			n3

n3 is the floor of the quotient of n1 divided by the divisor n2.  An
error condition results if the divisor is zero or if the quotient falls out-
side of the range {-32768..32767}.
\
/MOD			n1 n2			rem quot

rem is the remainder and quot the floor of the quotient of n1 divided by
the divisor n2.  rem has the same sign as n2 or is zero.  An error condition
results if the divisor is zero or if the quotient falls outside of the range
{-32768..32767}.
\
0			-			0

CONSTANT returns zero.
\
0<			n			flag

flag is true if n is less than zero (negative).
\
0=			n			flag

flag is true if n is zero.
\
0>			n			flag

flag is true if n is greater than zero.
\
1			-			1

CONSTANT returns 1.
\
1+			w1			w2

w2 is w1 + 1 (modulo 65536).
\
1-			w1			w2

w2 is w1 - 1 (modulo 65536).
\
2			-			2

CONSTANT returns 2.
\
2!			dl dh addr		-

Store high word dh at addr and store low word dl at addr+2.
\
2*			w1			w2

w2 is the result of shifting w1 left one bit.  A zero is shifted into the
vacated bit position.
\
2+			w1			w2

w2 is w1 + 2 (modulo 65536).
\
2-			w1			w2

w2 is w1 - 2 (modulo 65536).
\
2/			n1			n2

n2 is the result of arithmetically shifting n1 right one bit.  The sign is
included in the shift and remains unchanged.
\
2@			addr			dl dh

dh is contents of addr, dl is contents of addr+2.
\
2DROP			w1 w2			-

w1 and w2 are dropped from the stack.
\
2DUP			w1 w2			w1 w2 w1 w2

w1 and w2 are duplicated on the stack.
\
2VARIABLE		-			-

A defining word used in the form:
	2VARIABLE <name>
A dictionary entry for <name> is created and four bytes are ALLOTted in its
parameter field.  When <name> is later executed, the address of its parameter
field is placed on the stack.
\
:			-			-

A defining word used in the form:
	: <name>  ... ;
Create a definition for <name> in the compilation vocabulary and sets compil-
ation state.  The search order is changed so that the first vocabulary in the
search order is replaced by the compilation vocabulary.  The compilation
vocabulary is unchanged.  The text from the input stream is subsequently
compiled.  The newly created definition for <name> cannot be found in the
dictionary until the corresponding ; or equivalent operation is performed.
\
;			-			-

Compiling word stops compilation of a colon definition, allow the <name> to
be found in the dictionary; sets interpret state; and compiles (;), a word
functionally equivalent to EXIT.
\
<			n1 n2			flag

flag is true if n1 is less than n2.
\
<#			-			-

Initialize pictured numeric output conversion.   The words:
	# #> #S <# HOLD SIGN
can be used to specify the conversion of a double number into an ASCII text
string stored in right-to-left order.
\
<<			-			-

Immediate word to signal the beginning of a case inside the SELlect case
control structure.  See SEL for an example of how to use the case words.
\
<>			n1 n2			flag

flag is true if n1 is not equal to n2.
\
<MARK			-			addr

Used at the destination of a backward branch.  addr is typically only used
by <RESOLVE to compile a branch address.
\
<RESOLVE		addr			-

Used at the source of a backward branch after either BRANCH or ?BRANCH.
Compiles a branch address using addr as the destination address.
\
=			n1 n2			flag

flag is true if n1 equals n2.
\
==>			-			-

Immediate word separates a case structure equality test from the corresponding
case action.  See SEL for an example of how to use the case words.
\
=>			-			-

Immediate word separates a case structure test from the corresponding case
action.  See SEL for an example of how to use the case words.
\
>			n1 n2			flag

flag is true if n1 is greater than n2.
\
>>			-			-

Immediate word ends a case inside a case SEL control structure.  See SEL for
an example of how to use the case words.
\
>>RESOLVE		addr			-

Resolves a list of multiple forward references.  addr points to the first
element of a linked list.  Each link is stored in the address field of an
unresolved BRANCH or ?BRANCH instruction.  >>RESOLVE threads down the 
list pointing the BRANCHes to HERE.  A null list is indicated by addr equal
to zero.
\
>IN			-			addr

VARIABLE that conatains the present character offset within the input stream.
\
>MARK			-			addr

Used at the source of a forward branch.  Typically used after either BRANCH
or ?BRANCH.  Compiles space in ther dictionary for a branch address which will
later by resolved by >RESOLVE.
\
>R			n			-

Transfers n to the return stack.
\
>RESOLVE		addr			-

Used at the destination of a forward branch.  Places a branch address to HERE
in the space left by >MARK.
\
?			addr			-

The contents of addr are displayed in free field format with a leading minus
sign if negative.
\
?BRANCH			flag			-

When used in the form:  COMPILE ?BRANCH  a conditional branch operation is
compiled.  See BRANCH for further details.  When executed, if flag is false
the branch is performed as with BRANCH.  When flag is true execution continues
at the compilation address immediately following the branch address.
\
?DUP			n			n n <or> 0

n is duplicated if it is non-zero.
\
?STACK			-			flag

flag is true if stack has underflowed.
\
@			addr			n

n is the value at addr.
\
@SP			-			addr

addr is the address of the top stack item before @SP was executed.
\
ABORT			-			-

Clears the data stack and performs the function of QUIT.
\
ABORT"			flag 			-

Immediate word used in the form:
	flag ABORT" cccc"
When later executed, if flag is true, the characters cccc, delimited by "
(close-quote), are displayed and ABORT is executed.  If flag is false, the
flag is dropped and execution continues.  The blank following ABORT" is 
not part of cccc.  This word violates the principles of structured program-
ming and its use should be avoided.
\
ABS			n			u

u is the absolute value of n.  If n is -32768 then u is the same value.
\
AGAIN			-			-

Compiling word used in the form:
	BEGIN ... AGAIN
compiles an infinite loop.
\
ALIGN			addr1			addr2

Force word alignment of addr1.
\
ALLOT			w			-

Allocates w bytes in the dictionary.  WARNING: never ALLOT an odd number of 
bytes.
\
AND			n1 n2			n3

n3 is the bit-by-bit logical 'and' of n1 and n2.
\
BASE			-			addr

VARIABLE containing the current numeric conversion radix.
\
BEGIN			-			-

Immediate word marks the start of a word sequence for repetitive execution.
\
BRANCH			-			-

When used in the form:  COMPILE BRANCH  a conditional branch operation is
compiled.  A branch address must be compiled immediately following this
compilation address.  The branch address is typically generated by follow-
ing BRANCH with <RESOLVE, >MARK, or >>RESOLVE.
\
C!			n addr			-

The least significant 8 bits of n are stored into the byte at addr.
\
C@			addr			byte

The byte stored at addr is fetched.
\
CALL			-			-

Compile PDP-11 opcode for JSR iar,*$--- .   This word is typically used in
creating the code field of a dictionary definition.  See the defintions for
:, VARIABLE, and CONSTANT in the source code for an example its use.
\
CFIELD			nfa			cfa

Converts a name field address to the corresponding code field address.
\
CHUCKBUF		-			-

Flush rest of input buffer by moving >IN to the EOL mark.
\
CLOSE			fd			-

Close the Unix file with given file descriptor.
\
CMOVE			addr1 addr2 u		-

Move u bytes beginning at address addr1 to addr2.  The byte at addr1 is moved
first, proceeding toward high memory.  If u is zero, nothing is moved.
\
CMOVE>			addr1 addr2 u		-

Move the u bytes at address addr1 to addr2.  The move begins by moving the
byte at (addr1 + u - 1) to (addr2 + u - 1) and proceeds to successively 
lower addresses.  If u is zero nothing is moved.
\
COMPILE			-			-

Typically used in the form:
	: <name> ... COMPILE <namex> ... ;
When name is executed, COMPILE compiles the execution address of <namex> into
the dictionary.  Execution continues after <namex>.
\
CONSTANT		n			-

A defining word used in the form:
	n CONSTANT <name>
Creates a dictionary entry for <name> so that when <name> is later executed,
n will be left on the stack.
\
CONTXT			-			addr

addr is the address of a variable that points to the dictionary search
vocabulary.  This word is called CONTEXT in FORTH-83.
\
CONVERT			dl1 dh1 addr1		dl2 dh2 addr2

d2 is the result of converting the characters within the text beginning at 
addr1 + 1 into digits, using the value of BASE, and accumulating each into
d1 after multiplying d1 by the value of BASE.  Conversion continues until
an unconvertible character is encountered.  addr2 is the address of the first
unconvertivle character.
\
COUNT			addr			addr+1 n

Assumes a counted string is stored at addr.  Returns n, the byte stored
at addr, and increments addr.
\
CR			-			-

EMITs a linefeed character.
\
CREAT			addr[string] pmode	fd <or> -1

Try to create a file whose name is pointed to be addr with protection bits
pmode.  The file is opened for writing and the file descriptor is returned.
If the file already exists, its length is truncated to zero.  A -1 is returned
in the event of an error.
\
CREATE			-			-

A defining word used in the form:
	CREATE <name>
Creates a dictionary entry for <name>.  After <name> is created, the next
available dictionary location is the first byte of <name>'s parameter field.
When <name> is subsequently executed, the address of <name>'s parameter field
is left on the stack.  CREATE does not allocate space is <name>'s parameter
field.
\
CURRENT			-			addr

addr is the address of a variable pointing to the vocabulary in which new 
word definitions are appended.
\
D+			d1l d1h d2l d2h		dl3 dh3

d3 is is the arithmetic sum of d1 and d2.
\
D+-			d1l d1h n		d2l d2h

d2 is obtained by applying the sign of n to d1.  d2 := sign(n) * d1.
\
D.			dl dh			-

Print the double precision number d in free field format with a leading
minus sign if necessary.
\
D.LZ			dl dh size		-

Print the double precision number d right-justified in a field of size 
characters with leading zeros appended.
\
D.R			dl dh size		-

Print the double precision number d right-justified in a field of size
characters.
\
D<			d1l d1h d2l d2h		flag

flag is true if d1 is less than d2.
\
DABS			d1l d1h			d2l d2h

d2 is the absolute value of the double precision number d1. If d1 is equal
to -2,147,483,647 then d2 has the same value.
\
DECIMAL			-			-

Set the input-output conversion base to ten.
\
DEFINITIONS		-			-

The compilation vocabulary is changed to be the same as the search vocabulary.
\
DEPTH			-			n

N is the number of 16-bit values contained on the parameter stack before
DEPTH was executed.
\
DIGIT			char			n true <or> false

If char represents a valid digit in the current BASE, it is converted to 
the value n and true is returned.  Otherwise false is returned.
\
DNEGATE			d1l d1h			d2l d2h

d2 is the two's complement of of d1.
\
DO			n1 n2			-

Compiling word used in the form:
	DO ... LOOP  or  DO ... +LOOP
Begins a loop which terminates based on control parameters.  The loop index
begins at n2 and terminates based on the limit n1.  See LOOP and +LOOP
for details on how the loop is terminated.  The loop is always executed at
least once.
\
DOES>			-			addr

Defines the run-time action of a word created by the high-level defining
word CREATE.  Used in the form:
	: <namex> ... <create> ... DOES> ... ;
and then
	<namex> <name>
where <create> is CREATE or any user defined word which executes CREATE.
Marks the termination of the defining part of the defining word <namex>
and then begins the definition of the run-time action for words that will
later be defined by <namex>.  When <name> is later executed, the address
of <name>'s parameter field is placed on the stack and then the sequence
of words between DOES> and ; are executed.
\
DP			-			addr

VARIABLE that has the address of the first free byte at the end of the
dictionary.
\
DROP			n			-

n is DROPped from the stack.
\
DUMP			addr n			-

DUMPs n bytes of memory in pretty format starting at addr.
\
DUP			n			n n

n is DUPlicated on the stack.
\
ELSE			-			-

Immediate word used in the form:
	flag IF ... ELSE ... THEN
At run-time ELSE branches to just after the THEN.
\
EMIT			n			-

The least significant 8 bits of n are sent to the standard output.
\
ENDINTERP		-			-

Reset parameter stack pointer and throw away rest of input line.
\
ENDSEL			-			-

Immediate word ends a case control structure.  See SEL for an example
of how to use the case words.
\
EOL			-			char

CONSTANT defined as newline character (linefeed).
\
EXECUTE			cfa			-

The word definition indicated by cfa is executed.
\
EXIT			-			-

When executed inside a colon defintion, returns control to the definition
that passed control to it.  Cannot be used inside a DO ... LOOP.
\
FALSE			-			false

Places false flag (0) on the stack.
\
FEXPECT			fd addr count		actcount

Reads up to count bytes from the file denoted by file descriptor fd into
the buffer at addr.  Tabs are converted to blanks and encountering a line-
feed or and EOF will terminate the read.  The actual number of bytes read
actcount is returned.
\
FLOAD			addr[string]		-

Attempts to open the file indicated by the null-terminated string 'string'
for reading.  If successful, the text in the file is interpreted until an
EOF is encountered.  If the file can't be opened, a message is printed.
\
FQUERY			fd			actcount

FEXPECTs 120 characters from the file denoted by the file descriptor fd.
The text is placed in TIB, the text input buffer.  The actual number of bytes
read is returned.
\
FILL			addr u byte		-

u bytes of memory beginning at addr are set to byte.  No action is taken if
u is zero.
\
FIND			addr1 			addr2 n

addr1 is the address of a counted name string.  Tries to find the name in the
search vocabulary or in the compilation vocabulary.  If the word is not found, 
addr2 is the string address addr1, and n is zero.  If the word is found, addr2
is the compilation address and n is set to one of two non-zero values.  If
the word found has the immediate attribute, n is set to one.  If the word is
non-immediate, n is set to minus one.
\
FORGET			-			-

Used in the form:
	FORGET <name>
If <name> is found is the compilation vocabulary, delete <name> from the dic-
tionary and all words added to the dictionary after <name> regardless of their
vocabulary.  An error message is printed if <name> is not found.
\
FORTH			-			-

The name of the primary vocabulary.  Execution sets the search vocabulary to
FORTH.
\
HEADER			-			-

Create dictionary header in compilation vocabulary for next word in input
stream.  The header contains only the name field and link field.
\
HERE			-			addr

The address of the next available dictionary location.
\
HEX			-			-

Set the input-output conversion base to hex.
\
HLD			-			addr

VARIABLE holds the address of the last character added to the current
pictured numeric output conversion.
\
HOLD			char			-

char is inserted into a pictured numeric output string.  Typically used
between <# and #>.
\
I			-			n

n is a copy of the loop index.  May only be used in the form:
	DO ... I ... LOOP  or  DO ... I ... +LOOP
\
IF			-			-

Immediate word used in the form:
	flag IF ... ELSE ... THEN  or  flag IF ... THEN
If flag is true, the words following IF are executed and the words following
ELSE until just after THEN are skipped.  The ELSE part is optional.  If flag
is false, words from IF through ELSE, or from IF through THEN (when no ELSE
is used), are skipped.
\
INTERPRET		-			-

Interpret the text in the input buffer until an EOL is encountered.
\
J			-			n

n is a copy of the index of the next outer loop.  May only be used within
a nested DO-LOOP.
\
LATEST			-			nfa

Returns name field address of word most recently added to the compilation
vocabulary.
\
LEAVE			-			-

Transfers execution to just beyond the next LOOP or +LOOP.  The loop is
terminated and loop control parameters are discarded.  May only be used in
the form:
	DO ... LEAVE ... LOOP  or  DO ... LEAVE ... +LOOP
Leave may appear within other control structures which are nested within 
the DO-LOOP structure.  More than one LEAVE may appear within a DO-LOOP.
\
LITERAL			n			-

Immediate word typically used in the form:
	[ n ] LITERAL
compiles n as a literal.  At run-time, n will be put on the stack.
\
LOOP			-			-

Increments the DO-LOOP index by one.  If the new index was incremented 
across the boundary between limit-1 and limit, the loop is terminated and
loop control parameters are discarded.  When the loop is not terminated,
execution continues to just after the corresponding DO.
\
M*			n1 n2			dl dh

The signed numbers n1 and n2 and are multiplied to obtain the signed double
precision number d.
\
M/			dl dh divisor		rem quot

Signed mixed mode floored division. d is 32 bits.
\
M/MMOD			dl dh divisor		rem quotl quoth

unsigned mixed mode division.  Dividend and quotient are 32 bits.
\
MAX			n1 n2			n3

n3 is the greater of n1 and n2 according to the operation of >.
\
MIN			n1 n2			n3

n3 is the lesser of n1 and n2 according to the operation of <.
\
MOD			n1 n2			n3

n3 is the remainder after dividing n1 by the divisor n2.  n3 has the same
sign as n2 or is zero.  An error condition results if the divisor is zero
or if the quotient falls outside of the range {-32768..32767}.
\
NEGATE			n1			n2

n2 is the two's complement of n1.
\
NFIELD			cfa			nfa

Convert a code field address to its corresponding name field address.
\
NOT			n1			n2

n2 is the one's complement of n1.
\
NUMBER			addr			n true <or> false

addr points to a counted string.  NUMBER attempts to convert this string
to a number using the current BASE.  The converted number n and a true flag
are returned if successful.  Otherwise a false is returned.  For the con-
version to be successful, there must be a blank at the end of the string.
This is taken care of by WORD.
\
OCTAL			-			-

Set the input-output conversion base to octal.
\
OPEN			addr[string] mode	fd <or> -1

Try to open a file whose name is pointed to by addr with mode attributes.
Returns a file descriptor fd if successful, a -1 otherwise.  string is
a null terminated text string.  File modes are 0=read-only, 1=write-only,
and 2=read-write.
\
OR			n1 n2			n3

n3 is the bit-by-bit inclusive-or of n1 and n2.
\
OUTPUT			fd			-

Add file descriptor to output table used by TYPE if there is room in the table.
If there is no room, the command is ignored.
\
OVER			n1 n2			n1 n2 n1

Duplicates n1 on stack.
\
PAD			-			addr

The lower address of a scratch area used to hold data for intermediate pro-
cessing.  The address or contents of PAD may change and the data lost if the 
address of the next available dictionary location is changed.
\
PICK			n1			n2

n2 is a copy of the n1'th stack item not counting n1 itself.  0 PICK is 
equivalent to DUP, 1 PICK is equivalent to OVER, etc.
\
QUIT			-			-

Sets interpret state, accepts new input from the current input device, and
begins text interpretation.  This word diverges from the FORTH-83 word QUIT
in that it does not reset the return stack.  This may be changed in the 
future.
\
R>			-			n

n is removed from the return stack and transferred to the parameter stack.
\
R@			-			n

n is a copy of the top of the return stack.
\
READ			fd addr count		actcount

READs up to count bytes from the file denoted by file descriptor fd to
the buffer at addr.   actcount is the number of bytes actually read.
If this is not equal to count, the end of file was encountered or an error
occurred.
\
REPEAT			-			-

Immediate word used in the form:
	BEGIN ... flag WHILE ... REPEAT
At run-time, REPEAT continues execution just after the corresponding
BEGIN.
\
ROLL			n			-

The n'th stack value, not counting n itself is first removed and then trans-
ferred to the top of the stack, moving the remaining values into the vacated
position.  2 ROLL is equivalent to ROT.  0 ROLL is a null operation.
\
ROT			n1 n2 n3		n2 n3 n1

The top three stack entries are rotated, bringing the deepest to the top.
\
ROTATE			n1 nbits		n2

ROTATE n1 nbits.  If nbits is greater than zero, n1 is ROTATEd left.  If
nbits is less than zero, n1 is ROTATEd right.  If nbits is zero, nothing
happens.
\
S->D			n			dl dh

Sign extend n into a double precision number.
\
SEEK			fd offsetl offseth	-

Perform random-access seek on file denoted by file descriptor fd. offset
is a double precision number specifying a new file position offset from the
start of the file.
\
SEL			-			-

Immediate case structure word used in the form:
<selector> SEL
	      <<    1      ==> ... >>
	      <<    2      ==> ... >>
	      <<    5      ==> ... >>
	      << OTHERWISE ==> ... >>
           ENDSEL
The constants 1, 2, and 5 are just shown as an example.  Any word that leaves
one item on the stack can be used in the select field.  The action code 
symbolized by ..., can be any thing including another case structure.  The
<selector> is no longer on the stack when the action code begins execution.
The OTHERWISE clause is optional.  If none of the words in the select fields
match the <selector>, the <selector> is dropped by ENDSEL.
\
SHELL			-			-

Spawn a new sub-shell under the forth process.  Typing a ^D will cause control
to return to forth.
\
SIGN			n			-

If n is negative, an ASCII "-" is appended to the pictured numeric output
string.  Typically used between <# and #>.
\
SILENT			fd			-

Remove all instances of fd from the table used by TYPE.
\
SMUDGE			-			-

Toggle smudge bit in name field of word most recently added to the compilation
vocabulary.
\
SP0			-			addr

addr is address of 'top of stack' for an empty stack.  Used for resetting
stack pointer.
\
SPACE			-			-

EMIT an ASCII space.
\
SPACES			n			-

EMIT n ASCII spaces.  Nothing is EMITted if n is negative or zero.
\
STATE			-			addr

VARIABLE has current interpret-compile state.  0=interpret, 1=compile.
\
STDIN			-			0

CONSTANT returns file descriptor of standard input.
\
STDOUT			-			1

CONSTANT returns file descriptor of standard output.
\
STRING			addr[counted string]	addr[unix string]

Converts a counted string to a unix-style null-terminated string.  A copy of
the counted string is moved to PAD so that the conversion does not alter
the original string.
\
SWAP			n1 n2			n2 n1

The top two stack entries are exchanged.
\
SYSTEM			addr[string]		-

Spawns a sub-shell to execute the unix command string pointed to by addr.
The string must be null-terminated.  Typically used in the form:
	" cccc" SYSTEM  or inside a colon definition as:
	"" cccc" SYSTEM
\
TERMINATE		-			-

Terminate the forth process.  Returns 'good' status value.
\
THEN			-			-

Immediate word used in the form:
	flag IF ... ELSE ... THEN  or  flag IF ... THEN
THEN is the point where execution continues after ELSE, or IF when no ELSE
is present.
\
TIB			-			addr

addr is the address of the text input buffer.
\
TOGGLE			addr bits		-

The contents of addr are exclusive-or'ed with bits and the results stored
at addr.
\
TRUE			-			true

Places a true flag (-1) on the stack.
\
TYPE			addr count		-

count bytes of memory beginning at addr are sent to the standard output.
\
U.			u			-

u is displayed as an unsigned number in a free-field format.
\
U.LZ			u size			-

u is displayed as an unsigned number right-justified in a field of size 
characters with leading zeros.
\
U.R			u size			-

u is displayed as an unsigned number right-justified in a field of size
characters.
\
U<			u1 u2			flag

flag is true if the unsigned number u1 is less than the unsigned number u2.
\
U>			u1 u2			flag

flag is true if the unsigned number u1 is greater than the unsigned number n2.
\
U?			addr			-

Display the contents of addr as an unsigned number in free-field format.
\
UM*			n1 n2			ul uh

u is the 32-bit product of the unsigned multiplication of n1 and n2.
\
UM*M			u1l u1h mul		u2l u2h

u2 is the 32-bit product of the unsigned multiplication of u1 and mul.
\
UM/			ul uh div		rem quot

rem and quot are remainder and quotient of unsigned division of 31-bit u
by the unsigned divisor 'div'.
\
UM/MOD			ul uh div		rem quot

'rem' and 'quot' are remainder and quotient of unsigned division of 32-bit 'u'
by the unsigned divisor 'div'.
\
UNTIL			-			-

Immediate word used in the form:
	BEGIN ... flag UNTIL
Marks the end of a BEGIN-UNTIL loop which will terminate based on flag.  If
flag is true, the loop is terminated.  If flag is false, execution continues
just after the corresponding BEGIN.
\
VARIABLE		-			-

A defining word used in the form:
	VARIABLE <name>
A dictionary entry for <name> is created and two bytes are ALLOTted in its
parameter field.  This parameter field is to be used for the contents of the
VARIABLE.   The application is responsible for initializing the contents of 
the VARIABLE.  When <name> is later executed, the address of  its parameter
field is placed on the stack.
\
VOCABULARY		-			-

A defining word used in the form:
	VOCABULARY <name>
A dictionary entry for <name> is created.  Subsequent execution of <name> 
sets the search vocabulary to <name>.  When <name> becomes the compilation
vocabulary, new definitions will be appended to <name>'s list.
\
WHILE			-			-

Immediate word used in the form:
	BEGIN ... flag WHILE ... REPEAT
Selects conditional execution based on flag.  When flag is true, execution
continues just after the WHILE.  When flag is false, execution continues
just after the REPEAT, exiting the control structure.
\
WORD			char			addr

Generates a counted string by non-destructively accepting characters from
the input stream until the delimiting character char is found or the 
input stream is exhausted.  Leading delimiters are ignored.  The entire
character string is stored in memory beginning at addr as a sequence of
bytes.  The string is followed by a blank which is not included in the count.
The first byte of the string is the number of characters {0..255}.  If the
string is longer than 255 characters, the count is unspecified.  If the input
stream is already exhausted as WORD is called, then a zero length character
string will result.
\
WRITE			addr count fd		actcount

count bytes of memory starting at addr are sent to the file denoted by
file descritor fd.  The actual number of bytes written actcount is re-
turned.  If this number does not equal count, an error of some sort has
occurred.
\
WRN			-			addr

VARIABLE that holds the state the warning message enable/disable.  If WRN
is true, the user will be notified if he tries to add a word to the 
dictionary whose name conflicts with a word already in the dictionary.
\
XOR			n1 n2			n3

n3 is the bit-by-bit exclusive-or of n1 and n2.
\
ZEROES			n			-

EMIT n ASCII zeroes.  Nothing is EMITted if n is zero or negative.
\
[			-			-

Immediate word that sets the interpretation state to interpret.
\
[']			-			addr

Immediate word used in the form:
	['] <name>
Compiles the compilation address of <name> as a literal.  At run-time
the cfa of <name> is put on the stack.  If <name> is not found in the
dictionary, a literal zero is compiled.
\
[COMPILE]		-		-

Immediate word used in the form:
	[COMPILE] <name>
Forces compilation of the following word <name>.  This allow compilation of
an immediate word when it would otherwise have been executed.
\
]			-		-

Sets interpretation state to compile.  The text from the input stream is
subsequently compiled.
\
+E+O+F

lwt1@aplvax.UUCP (06/08/84)

Here is part 3 of the source for FORTH for the PDP-11.
Delete everything thru the "-- cut here --" line, and extract with 'sh':

	sh part1 part2 ... part7

where 'part?' are whatever you've named the files.  Note the copyright
notice at the end of README.  Please let us know how things go.  While
we can't support this software, we'll be posting bug fixes/upgrades to
net.sources as time permits.

VAX-FORTH should be 'forth'-coming {yuk-yuk} within a couple of weeks.

Have fun!



						-Lloyd W. Taylor
						 ... seismo!umcp-cs!aplvax!lwt1
---I will have had been there before, soon---

---------------------------------- cut here ----------------------------------
echo x - prim.as
cat >prim.as <<'+E+O+F'
/ Copyright 1984 by The Johns Hopkins University/Applied Physics Lab.
/ Free non-commercial distribution is *encouraged*, provided that:
/ 
/ 	1.  This copyright notice is included in any distribution, and
/ 	2.  You let us know that you're using it.
/ 
/ Please notify:
/ 
/ 	Lloyd W. Taylor
/ 	JHU/Applied Physics Lab
/ 	Johns Hopkins Road
/ 	Laurel, MD 20707
/ 	(301) 953-5000
/ 
/ 	Usenet:  ... seismo!umcp-cs!aplvax!lwt1
/ 
/ 
/ Unix-FORTH was developed under NASA contract NAS5-27000 for the
/ Hopkins Ultraviolet Telescope, a March 1986 Space Shuttle mission.  (we
/ hope to take a peek at Halley's comet!)
/ 
/ Written entirely by Wizard-In-Residence John R. Hayes.
/ 
/ * Unix is a trademark of Bell Labs.
/ 
/
/ FORTH PDP-11 inner interpreter and code primitives
/
iar    =r4
psp    =r5
nl     =012	/ newline
tab    =011	/ tab
EOF    =-1	/ end of file
BLKSIZE=512.	/ disk block size

/	start-up code
	mov $pstack,psp		/ TEST
	mov $dict,DP
	mov $16.,BASE		/ base is hex
	mov $quit-6,INITVOCAB
	mov $quit+4,iar		/ point to high level QUIT code
	jmp *(iar)+


/	parameter stack
	.=.+256.		/ 256 byte stack TEST
pstack:

/	text input buffer
inbuf:	.=.+120.			/ 120 characters


/ (:)   Code for next is thing at bottom of dictionary
	.byte 3; <(:)>
	.byte 0,0		/ end of dictionary
next:	jmp *(iar)+

/    The code for call is compiled in-line for colon definitions.
/
/ call:	jsr iar,*$next
/
/ (;)
	.byte 3; <(;)>
	next-6
return: mov (sp)+,iar
	jmp *(iar)+
/
/    This is tricky code. All words defined by VARIABLE, CONSTANT, or
/ <BUILDS .. DOES> words will have similar code fields. Therefore the
/ code for (VARIABLE), (CONSTANT), and (DOES>) is shown below.
/ Code compiled by VARIABLE will be:
/	jsr iar,*$var

/ (VARIABLE)
	.byte 12; <(VA>
	return-6
var:	mov iar,-(psp)
	mov (sp)+,iar
	jmp *(iar)+

/ (CONSTANT)
	.byte 12; <(CO>
	var-6
con:	mov (iar),-(psp)
	mov (sp)+,iar
	jmp *(iar)+

/ (DOES>)
	.byte 7; <(DO>
	con-6
pdoes:	mov (iar)+,r0
	mov iar,-(psp)
	mov r0,iar
	jmp *(iar)+

/    branching primitives

/ (LITERAL)
	.byte 11; <(LI>
	pdoes-6
lit:	mov (iar)+,-(psp)
	jmp *(iar)+

/ BRANCH
	.byte 6; <BRA>
	lit-6
branch: mov (iar),iar
	jmp *(iar)+

/ ?BRANCH
	.byte 7; <?BR>
	branch-6
zbranch:
	mov (psp)+,r0
	beq branch
	add $2,iar
	jmp *(iar)+

/ EXECUTE
	.byte 7; <EXE>
	zbranch-6
execute:
	jmp *(psp)+

/    FORTH-83 do loops

/ (DO)
	.byte 4; <(DO>
	execute-6
pdo:	mov (psp)+,r1
	mov (psp)+,r0
	add $100000,r0		/ limit' := limit + 8000
	mov r0,-(sp)
	sub r0,r1		/ imit' := init - limit'
	mov r1,-(sp)
	jmp *(iar)+

/ (LOOP)
	.byte 6; <(LO>
	pdo-6
ploop:	inc (sp)
	bvs exitloop
	mov (iar),iar		/ loop back 
	jmp *(iar)+
exitloop:
	add $4,sp		/ pop return stack
	add $2,iar		/ skip loop address
	jmp *(iar)+

/ (+LOOP)
	.byte 7; <(+L>
	ploop-6
pploop: add (psp)+,(sp)
	bvs exitloop
	mov (iar),iar		/ loop back
	jmp *(iar)+

/ I
	.byte 1; <I  >
	pploop-6
i:	mov (sp),r0
	add 2(sp),r0		/ i := i' + limit'
	mov r0,-(psp)
	jmp *(iar)+

/ J
	.byte 1; <J  >
	i-6
j:	mov 4(sp),r0
	add 6(sp),r0
	mov r0,-(psp)
	jmp *(iar)+

/ (LEAVE)
	.byte 7; <(LE>
	j-6
pleave: add $4,sp		/ pop return stack
	mov (iar),iar		/ branch past loop
	jmp *(iar)+

/	basic unix system interface routines

/ buffer for holding indirect system calls
sysbuf:	.byte 0,0		/ trap instruction
	.byte 0,0		/ argument 1
	.byte 0,0		/ argument 2
	.byte 0,0		/ argument 3

/	I/O buffer and control variables
block:	.=.+BLKSIZE; .even
size:	.byte 0,0		/ size in bytes
index:	.byte 0,0		/ current offset into block
fd:	.byte -1,-1		/ file descriptor of file this block belongs to

/	file position table: each slot has a 32 bit file offset. file descriptor
/	is index into table. There are 15 slots.
filepos:
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0
	.byte 0,0,0,0

/	subroutine getc: handles all input and does buffering
/		input: file descriptor in r0
/		output: character or EOF in r0
/		side effects: r0 and r1
getc:	cmp r0,fd		/ is this file in buffer?
	beq 0f			/ if so, do not need to seek
	mov r0,fd		/ save new fd in buffer descriptor
	mov size,index		/ indicate that buffer is empty
	mov $104423,sysbuf	/ move lseek trap instruction to sysbuf
	asl r0; asl r0		/ multiply by 4 to index into table
	mov filepos(r0),sysbuf+2	/ high offset word
	mov filepos+2(r0),sysbuf+4	/ low offset word
	clr sysbuf+6		/ offset from beginning of file
	mov fd,r0		/ file descriptor in r0
	sys 0;sysbuf		/ seek sytem call
	mov fd,r0		/ restore fd since call destroyed r0,r1

0:	mov r2,-(sp)		/ save r2
	mov *$index,r2		/ r2 is index
	cmp r2,*$size
	blt 1f			/ if there is still data in buffer, use it
	sys 3;block;BLKSIZE	/ read up to BLKSIZE bytes
	bcs 2f			/ branch if error
	mov r0,*$size		/ save size of block
	beq 2f			/ branch if eof
	clr r2			/ reset index
1:	movb block(r2),r0	/ get next character
	bic $17400,r0		/ mask off high byte
	inc r2
	mov r2,*$index		/ update index
	mov fd,r2		/ reuse r2 to hold file descriptor
	asl r2; asl r2		/ multiply by 4 to index into table
	add $1,filepos+2(r2)	/ add one to current file position
	adc filepos(r2)
	br 3f
2:	mov $EOF,r0		/ return EOF on error condition
3:	mov (sp)+,r2		/ restore r2
	rts pc

/ OPEN				( addr[string] mode --- fd )
	.byte 4; <OPE>
	pleave-6
open:	mov $104405,sysbuf	/ move trap 5 instruction to indir area
	mov (psp)+,sysbuf+4	/ mode
	mov (psp),sysbuf+2	/ addr[filename]
	sys 0;sysbuf
	bcc 1f
	mov $-1,(psp)		/ error, negative file descriptor returned
	br 2f
1:	mov r0,(psp)		/ return file descriptor
	asl r0; asl r0		/ multiply by 4 to index into table
	clr filepos(r0)		/ initialize file position to zero
	clr filepos+2(r0)
2:	jmp *(iar)+

/ CREAT				( addr[string] pmode --- fd/-1 )
	.byte 5; <CRE>
	open-6
creat:	mov $104410,sysbuf	/ move trap 8 instruction to indir area 
	mov (psp)+,sysbuf+4	/ move mode
	mov (psp),sysbuf+2	/ move address of file name
	sys 0;sysbuf		/ creat system call
	bcc 1f
	mov $-1,(psp)		/ error, negative file descriptor returned 
	br 2f
1:	mov r0,(psp)		/ return file descriptor
	asl r0; asl r0		/ multiply by 4 to index into position table
	clr filepos(r0)		/ initialize file position to zero
	clr filepos+2(r0)
2:	jmp *(iar)+

/ CLOSE				( fd --- )
	.byte 5; <CLO>
	creat-6
close:	mov $104406,sysbuf	/ move trap 6 instruction to indir area
	mov (psp)+,r0		/ file descriptor 
	sys 0;sysbuf
	jmp *(iar)+

/ KEY				( fd --- char/EOF )
	.byte 3; <KEY>
	close-6
key:	mov (psp),r0		/ file descriptor
	jsr pc,getc		/ get next character
	mov r0,(psp)		/ return character
	jmp *(iar)+

/ FEXPECT			( fd addr count --- actcount)
	.byte 7; <FEX>
	key-6
fexpect:
	mov 2(psp),r2		/ buffer address
	mov (psp)+,r3		/ count
	beq 3f			/ do nothing if count is zero
1:	mov 2(psp),r0		/ file descriptor
	jsr pc,getc		/ get next character
	cmp r0,$EOF
	beq 3f			/ leave loop on EOF
	cmpb r0,$tab
	bne 2f
	movb $040,r0		/ change tabs to blanks
2:	movb r0,(r2)+		/ save character
	cmpb r0,$nl
	beq 3f			/ leave loop on newline
	sob r3,1b		/ decrement count and continue if non-zero
3:	sub (psp)+,r2		/ compute actual number of characters read
	mov r2,(psp)		/ return actual number
	jmp *(iar)+

/ READ				( fd addr count --- actcount )  ( like expect )
/				( that tabs are not stripped and newlines are )
/				( not significant.                            )
	.byte 4; <REA>
	fexpect-6
read:	mov 2(psp),r2		/ buffer address
	mov (psp)+,r3		/ count
	beq 3f			/ do nothing if count is zero
1:	mov 2(psp),r0		/ file descriptor
	jsr pc,getc		/ get next character
	cmp r0,$EOF
	beq 3f			/ leave loop on EOF
	movb r0,(r2)+		/ save character
	sob r3,1b		/ decrement count and continue if non-zero
3:	sub (psp)+,r2		/ compute actual number of characters read
	mov r2,(psp)		/ return actual number
	jmp *(iar)+

/ WRITE				( addr count fd --- actcount )
	.byte 5; <WRI>
	read-6
write:	mov $104404,sysbuf	/ move trap 4 instruction to indir area
	mov (psp)+,r0		/ file descriptor
	mov (psp)+,sysbuf+4	/ count
	mov (psp),sysbuf+2	/ address
	sys 0; sysbuf		/ indirect system call
	bcc 1f
	mov $-1,r0		/ error flag
1:	mov r0,(psp)		/ return actual count )
	jmp *(iar)+

/ SEEK				( fd offsetl offseth --- )
	.byte 4; <SEE>
	write-6
seek:	mov 4(psp),r0		/ file descriptor
	cmp r0,fd		/ if seek on currently buffered file
	bne 1f
	mov $-1,fd		/ flag buffer as invalid
1:	asl r0; asl r0		/ multiply by 4 to index into file pos. table
	mov (psp),filepos(r0)	/ high offset into file position table
	mov 2(psp),filepos+2(r0)	/ low offset into file position table
	mov $104423,sysbuf	/ move seek trap instruction to sysbuf
	mov (psp)+,sysbuf+2	/ move high offset
	mov (psp)+,sysbuf+4	/ move low offset
	clr sysbuf+6		/ offset from beginning of file
	mov (psp)+,r0		/ file descriptor in r0
	sys 0;sysbuf		/ seek
	jmp *(iar)+

/ TERMINATE
	.byte 11; <TER>
	seek-6
terminate:
	clr r0			/ return good status
	sys 1
	jmp *(iar)+		/ this should not be executed TEST

/     high level utilities written in assembly language for speed

/ (FIND)                        ( addr[name] addr[vocab] --- 0 <or> nfa )
	.byte 6; <(FI>
	terminate-6
pfind:	mov (psp)+,r0
	beq 3f			/ empty vocabulary?
	mov (psp),r3
	mov (r3)+,r2		/ name ls
	mov (r3),r3		/ name ms
1:	mov (r0),r1
	bic $200,r1		/ clear immediate bit
	cmp r1,r2		/ compare ls
	bne 2f
	cmp 2(r0),r3		/ compare ms
	beq 3f
2:	mov 4(r0),r0		/ next link
	bne 1b			/ zero link?
3:	mov r0,(psp)
	jmp *(iar)+

/ WORD				( del --- addr )
	.byte 4; <WOR>
	pfind-6
word:	mov (psp),r0		/ delimiter
	mov *$IN,r1		/ >IN
	add $inbuf,r1
	mov *$DP,r2		/ HERE
	mov r2,(psp)		/ return HERE
1:	cmpb r0,(r1)+		/ skip delimiters
	beq 1b
	dec r1			/ back up one
	mov r1,r3
2:	cmpb r0,(r3)		/ delimiter?
	beq 3f
	cmpb $nl,(r3)		/ newline?
	beq 3f
	inc r3			/ skip until end of word
	br  2b
3:	sub r1,r3		/ r3 has length
	movb r3,(r2)+		/ save count
	beq 5f			/ skip if eol
4:	movb (r1)+,(r2)+	/ move characters to here
	sob r3,4b
5:	cmpb $nl,(r1)		/ if not newline
	beq 6f
	inc r1			/ skip delimiter
6:	sub $inbuf,r1
	mov r1,*$IN		/ update >IN scanner
	movb $040,(r2)		/ put blank at end of word
	jmp *(iar)+

/     FORTH nucleus primitives

/ !
	.byte 1; <!  >
	word-6
store:	mov (psp)+,r0
	mov (psp)+,(r0)
	jmp *(iar)+

/ !SP
	.byte 3; <!SP>
	store-6
storesp:
	mov (psp),psp
	jmp *(iar)+

/ +
	.byte 1; <+  >
	storesp-6
plus:	add (psp)+,(psp)
	jmp *(iar)+

/ +!
	.byte 2; <+! >
	plus-6
plusstore:
	mov (psp)+,r0
	add (psp)+,(r0)
	jmp *(iar)+

/ -
	.byte 1; <-  >
	plusstore-6
minus:	sub (psp)+,(psp)
	jmp *(iar)+

/ -1
	.byte 2; <-1 >
	minus-6
minusone:
	mov $-1,-(psp)
	jmp *(iar)+

/ 0
	.byte 1; <0  >
	minusone-6
zero:	clr -(psp)
	jmp *(iar)+

/ 0<
	.byte 2; <0< >
	zero-6
zeroless:
	clr r0
	tst (psp)
	bpl 1f
	mov $-1,r0
1:	mov r0,(psp)
	jmp *(iar)+

/ 0=
	.byte 2; <0= >
	zeroless-6
zeroeq: clr r0
	tst (psp)
	bne 1f
	mov $-1,r0
1:	mov r0,(psp)
	jmp *(iar)+

/ 1
	.byte 1; <1  >
	zeroeq-6
one:	mov $1,-(psp)
	jmp *(iar)+

/ 1+
	.byte 2; <1+ >
	one-6
oneplus:
	inc (psp)
	jmp *(iar)+

/ 1-
	.byte 2; <1- >
	oneplus-6
oneminus:
	dec (psp)
	jmp *(iar)+

/ 2
	.byte 1; <2  >
	oneminus-6
two:	mov $2,-(psp)
	jmp *(iar)+

/ 2+
	.byte 2; <2+ >
	two-6
twoplus:
	add $2,(psp)
	jmp *(iar)+

/ 2-
	.byte 2; <2- >
	twoplus-6
twominus:
	sub $2,(psp)
	jmp *(iar)+

/ 2*
	.byte 2; <2* >
	twominus-6
twostar:
	asl (psp)
	jmp *(iar)+

/ 2/
	.byte 2; <2/ >
	twostar-6
twoslash:
	asr (psp)
	jmp *(iar)+

/ <
	.byte 1; <<  >
	twoslash-6
less:	clr r0
	cmp (psp)+,(psp)
	ble 1f
	mov $-1,r0
1:	mov r0,(psp)
	jmp *(iar)+

/ =
	.byte 1; <=  >
	less-6
equal:	clr r0
	cmp (psp)+,(psp)
	bne 1f
	mov $-1,r0
1:	mov r0,(psp)
	jmp *(iar)+

/ >
	.byte 1; <\>  >
	equal-6
greater:
	clr r0
	cmp (psp)+,(psp)
	bge 1f
	mov $-1,r0
1:	mov r0,(psp)
	jmp *(iar)+

/ >R
	.byte 2; <\>R >
	greater-6
tor:	mov (psp)+,-(sp)
	jmp *(iar)+

/ @
	.byte 1; <@  >
	tor-6
at:	mov *(psp),(psp)
	jmp *(iar)+

/ @SP
	.byte 3; <@SP>
	at-6
atsp:	mov psp,r1
	mov r1,-(psp)
	jmp *(iar)+

/ AND
	.byte 3; <AND>
	atsp-6
and:	mov (psp)+,r0
	com r0		/ there is no direct and in PDP-11 assembly lang. 
	bic r0,(psp)
	jmp *(iar)+

/ C!
	.byte 2; <C! >
	and-6
cstore: mov (psp)+,r0
	mov (psp)+,r1
	movb r1,(r0)
	jmp *(iar)+

/ C@
	.byte 2; <C@ >
	cstore-6
cat:	movb *(psp),r0
	bic $177400,r0
	mov r0,(psp)
	jmp *(iar)+

/ CMOVE				( src dest ucount --- )
	.byte 5; <CMO>
	cat-6
cmove:	mov (psp)+,r2
	beq 2f
	mov (psp)+,r0		/ destination
	mov (psp)+,r1		/ source
1:	movb (r1)+,(r0)+
	sob r2,1b
	br 3f
2:	add $4,psp		/ pop two stack args
3:	jmp *(iar)+

/ D+
	.byte 2; <D+ >
	cmove-6
dplus:	mov (psp)+,r0
	add (psp)+,2(psp)
	adc (psp)
	add r0,(psp)
	jmp *(iar)+

/ DNEGATE
	.byte 7; <DNE>
	dplus-6
dnegate:
	com (psp)
	com 2(psp)
	add $1,2(psp)
	adc (psp)
	jmp *(iar)+

/ DROP
	.byte 4; <DRO>
	dnegate-6
drop:	add $2,psp
	jmp *(iar)+

/ DUP
	.byte 3; <DUP>
	drop-6
dup:	mov (psp),-(psp)
	jmp *(iar)+

/ M*
	.byte 2; <M* >
	dup-6
mstar:	mov (psp),r0
	mul 2(psp),r0
	mov r1,2(psp)		/ low result
	mov r0,(psp)		/ high result
	jmp *(iar)+

/ M/
	.byte 2; <M/ >
	mstar-6
mslash: mov (psp)+,r2		/ r2 has divisor
	mov (psp),r0		/ r0 has high dividend 
	mov 2(psp),r1		/ r1 has low dividend 
	mov r2,r3
	xor r0,r3		/ r3 has sign
	div r2,r0		/ divide by r2
	tst r3
	bpl 1f			/ skip if sign is not negative 
	tst r1
	beq 1f			/ skip if remainder is zero
	dec r0			/ subtract one from quotient
	add r2,r1		/ add divisor to remainder
1:	mov r1,2(psp)		/ remainder
	mov r0,(psp)		/ quotient
	jmp *(iar)+

/ NEGATE
	.byte 6; <NEG>
	mslash-6
negate: neg (psp)
	jmp *(iar)+

/ NOT
	.byte 3; <NOT>
	negate-6
not:	com (psp)
	jmp *(iar)+

/ OR
	.byte 2; <OR >
	not-6
or:	bis (psp)+,(psp)
	jmp *(iar)+

/ OVER
	.byte 4; <OVE>
	or-6
over:	mov 2(psp),-(psp)
	jmp *(iar)+

/ R>
	.byte 2; <R\> >
	over-6
fromr:	mov (sp)+,-(psp)
	jmp *(iar)+

/ R@
	.byte 2; <R@ >
	fromr-6
rat:	mov (sp),-(psp)
	jmp *(iar)+

/ ROT
	.byte 3; <ROT>
	rat-6
rot:	mov 4(psp),r0
	mov 2(psp),4(psp)
	mov (psp),2(psp)
	mov r0,(psp)
	jmp *(iar)+

/ ROTATE			( word nbits --- word' )
	.byte 6; <ROT>
	rot-6
rotate:	mov (psp)+,r1		/ loop counter
	bic $0177760,r1		/ mask off all but lower four bits 
	beq 3f
	mov (psp),r0
1:	tst r0			/ test sign bit; clear carry
	bpl 2f
	sec			/ set carry
2:	rol r0			/ rotate
	sob r1,1b
	mov r0,(psp)
3:	jmp *(iar)+

/ SWAP
	.byte 4; <SWA>
	rotate-6
swap:	mov 2(psp),r0
	mov (psp),2(psp)
	mov r0,(psp)
	jmp *(iar)+

/ UM*
	.byte 3; <UM*>
	swap-6
umstar:	clr r0
	mov $20,r1		/ r1 := 16
	mov (psp),r2
	mov 2(psp),r3		/ multiplier
	ror r3			/ get ls bit
1:	bcc 2f
	add r2,r0		/ accumulate
2:	ror r0			/ shift carry into r0
	ror r3			/ shift into r3; get ls bit
	sob r1,1b
	mov r3,2(psp)		/ save ls word
	mov r0,(psp)		/ save ms word
	jmp *(iar)+

/ UM/ 				( dl dh divisor --- rem quot )
/				dividend is 31 bits
	.byte 3; <UM/>
	umstar-6
umslash:
	mov $20,r0		/ 16 bits
	mov (psp)+,r1		/ divisor
	mov (psp),r2		/ ms word
	mov 2(psp),r3		/ ls word
1:	asl r3
	rol r2
	cmp r1,r2
	bhi 2f
	sub r1,r2
	inc r3
2:	sob r0,1b
	mov r2,2(psp)		/ remainder
	mov r3,(psp)		/ quotient
	jmp *(iar)+

/ U<
	.byte 2; <U< >
	umslash-6
uless:	clr r0
	cmp (psp)+,(psp)
	blos 1f
	mov $-1,r0
1:	mov r0,(psp)
	jmp *(iar)+

/ U>
	.byte 2; <U\> >
	uless-6
ugreater:
	clr r0
	cmp (psp)+,(psp)
	bhis 1f
	mov $-1,r0
1:	mov r0,(psp)
	jmp *(iar)+

/ XOR
	.byte 3; <XOR>
	ugreater-6
exor:	mov (psp)+,r0
	xor r0,(psp)
	jmp *(iar)+
+E+O+F

lwt1@aplvax.UUCP (06/08/84)

Here is part 4 of the source for FORTH for the PDP-11.
Delete everything thru the "-- cut here --" line, and extract with 'sh':

	sh part1 part2 ... part7

where 'part?' are whatever you've named the files.  Note the copyright
notice at the end of README.  Please let us know how things go.  While
we can't support this software, we'll be posting bug fixes/upgrades to
net.sources as time permits.

VAX-FORTH should be 'forth'-coming {yuk-yuk} within a couple of weeks.

Have fun!



						-Lloyd W. Taylor
						 ... seismo!umcp-cs!aplvax!lwt1
---I will have had been there before, soon---

---------------------------------- cut here ----------------------------------
echo x - os.as
cat >os.as <<'+E+O+F'
/ 
/    FORTH operating system in assembler format
/

/    System variables and constants
/ 
/    The upper case labels are so that assembly language routines can refer
/ to the values of these variables

/ TIB
	.byte 3; <TIB>
	exor-6
tib:	jsr iar,*$con
	inbuf

/ SP0
	.byte 3; <SP0>
	tib-6
sp0:	jsr iar,*$con
	pstack

/ DP0
	.byte 3; <DP0>
	sp0-6
dp0:	jsr iar,*$con
	dict

/ WRN
	.byte 3; <WRN>
	dp0-6
wrn:	jsr iar,*$var
	.byte -1,-1

/ DP
	.byte 2; <DP >
	wrn-6
dp:	jsr iar,*$var
DP:	.byte 0,0

/ >IN
	.byte 3; <\>IN>
	dp-6
in:	jsr iar,*$var
IN:	.byte 0,0

/ STATE
	.byte 5; <STA>
	in-6
state:	jsr iar,*$var
	.byte 0,0

/ BASE
	.byte 4; <BAS>
	state-6
base:	jsr iar,*$var
BASE:	.byte 0,0

/ INITVOCAB				( intial vocabulary - will be FORTH )
	.byte 11; <INI>
	base-6
initvocab:
	jsr iar,*$var
INITVOCAB: .byte 0,0

/ CONTXT				( context vocabulary )
	.byte 6; <CON>
	initvocab-6
context:
	jsr iar,*$var
	INITVOCAB

/ CURRENT				( current vocabulary )
	.byte 7; <CUR>
	context-6
current:
	jsr iar,*$var
	INITVOCAB

/ CLUE
 	.byte 4; <CLU>
	current-6
clue:	jsr iar,*$var
	.byte 0,0

/ STDIN
	.byte 5; <STD>
	clue-6
stdin:	jsr iar,*$con
	.byte 0,0

/ STDOUT
	.byte 6; <STD>
	stdin-6
stdout:	jsr iar,*$con
	.byte 1,0

/ EOL
	.byte 3; <EOL>
	stdout-6
eol:	jsr iar,*$con
	.byte 12,0

/ TRUE
	.byte 4; <TRU>
	eol-6
true:	jsr iar,*$con
	.byte -1,-1

/ FALSE
	.byte 5; <FAL>
	true-6
false:	jsr iar,*$con
	.byte 0,0

/    Code extensions

/ ?DUP
	.byte 4; <?DU>
	false-6
qdup:	jsr iar,*$next
	dup; zbranch; 1f; dup; 1: return

/ -ROT
	.byte 4; <-RO>
	qdup-6
mrot:	jsr iar,*$next
	rot; rot; return

/ *
	.byte 1; <*  >
	mrot-6
star:	jsr iar,*$next
	umstar; drop; return

/ 2DUP
	.byte 4; <2DU>
	star-6
twodup: jsr iar,*$next
	over; over; return

/ S->D
	.byte 4; <S-\>>
	twodup-6
stod:	jsr iar,*$next
	dup; zeroless; return

/ +-
	.byte 2; <+- >
	stod-6
plusminus:
	jsr iar,*$next
	zeroless; zbranch; 1f; negate; 1: return

/ D+-
	.byte 3; <D+->
	plusminus-6
dplusminus:
 	jsr iar,*$next
	zeroless; zbranch; 1f; dnegate; 1: return

/ ABS
	.byte 3; <ABS>
	dplusminus-6
abs:	jsr iar,*$next
	dup; plusminus; return

/ DABS
	.byte 4; <DAB>
	abs-6
dabs:	jsr iar,*$next
	dup; dplusminus; return

/ 2DROP
	.byte 5; <2DR>
	dabs-6
twodrop:
	jsr iar,*$next
	drop; drop; return

/ UM*M				( ul uh mul --- ul' uh' )
	.byte 4; <UM*>
	twodrop-6
umstarm:
	jsr iar,*$next
	swap; over; umstar; drop; tor; umstar; zero; fromr; dplus; return

/ M/MMOD
	.byte 6; <M/M>
	umstarm-6
mslashmmod:
	jsr iar,*$next
	tor; zero; rat; umslash; fromr; swap; tor; umslash; fromr; return

/ FILL
	.byte 4; <FIL>
	mslashmmod-6
fill:	jsr iar,*$next
	mrot; qdup; zbranch; 2f
		over; plus; swap; pdo; 1: dup; i; cstore; ploop; 1b; branch; 3f
		2: drop
	3: drop; return

/ TOGGLE
	.byte 6; <TOG>
	fill-6
toggle:	jsr iar,*$next
	over; at; exor; swap; store; return

/ <>
	.byte 2; <<\> >
	toggle-6
nequal:	jsr iar,*$next
	equal; not; return

/ MAX
	.byte 3; <MAX>
	nequal-6
max:	jsr iar,*$next
	twodup; less; zbranch; 1f; swap; 1: drop; return

/ HEX
	.byte 3; <HEX>
	max-6
hex:	jsr iar,*$next
	lit; .byte 16.,0; base; store; return

/ DECIMAL
	.byte 7; <DEC>
	hex-6
decimal:
	jsr iar,*$next
	lit; .byte 10.,0; base; store; return

/ OCTAL
	.byte 5; <OCT>
	decimal-6
octal:	jsr iar,*$next
	lit; .byte 8.,0; base; store; return

/ 2!					( n1 n2 addr --- )
	.byte 2; <2! >
	octal-6
twostore:
	jsr iar,*$next
	swap; over; store; twoplus; store; return

/    Compiling words

/ HERE
	.byte 4; <HER>
	twostore-6
here:	jsr iar,*$next
	dp; at; return

/ PAD
	.byte 3; <PAD>
	here-6
pad:	jsr iar,*$next
	here; lit; .byte 80.,0; plus; return

/ LATEST
	.byte 6; <LAT>
	pad-6
latest:	jsr iar,*$next
	current; at; at; return

/ ALLOT
	.byte 5; <ALL>
	latest-6
allot:	jsr iar,*$next
	dp; plusstore; return

/ ,
	.byte 1; <,  >
	allot-6
comma:	jsr iar,*$next
	here; store; two; allot; return

/ IMMEDIATE
	.byte 11; <IMM>
	comma-6
immediate:
	jsr iar,*$next
	latest; lit; .byte 200,0; toggle; return

/ SMUDGE
	.byte 6; <SMU>
	immediate-6
smudge:	jsr iar,*$next
	latest; lit; .byte 100,0; toggle; return

/ COMPILE
	.byte 7; <COM>
	smudge-6
compile:
	jsr iar,*$next
	fromr; dup; at; comma; two; plus; tor; return

/ IF
	.byte 202; <IF >	/ immediate word
	compile-6
if:	jsr iar,*$next
	compile; zbranch; here; two; allot; return

/ THEN
	.byte 204; <THE>
	if-6
then:	jsr iar,*$next
	here; swap; store; return

/ ELSE
	.byte 204; <ELS>
	then-6
else:	jsr iar,*$next
	compile; branch; here; two; allot; here; rot; store; return

/ BEGIN
	.byte 205; <BEG>
	else-6
begin:	jsr iar,*$next
	here; return

/ UNTIL
	.byte 205; <UNT>
	begin-6
until:	jsr iar,*$next
	compile; zbranch; comma; return

/ AGAIN
	.byte 205; <AGA>
	until-6
again:	jsr iar,*$next
	compile; branch; comma; return

/ WHILE
	.byte 205; <WHI>
	again-6
while:	jsr iar,*$next
	compile; zbranch; here; two; allot; return

/ REPEAT
	.byte 206; <REP>
	while-6
repeat:	jsr iar,*$next
	compile; branch; swap; comma; here; swap; store; return

/ DO
	.byte 202; <DO >
	repeat-6
do:	jsr iar,*$next
	compile; pdo; clue; at; zero; clue; store; here; return

/ LOOP
	.byte 204; <LOO>
	do-6
loop:	jsr iar,*$next
	compile; ploop; comma; clue; at; qdup; zbranch; 1f
		here; swap; store
	1: clue; store; return

/ +LOOP
	.byte 205; <+LO>
	loop-6
plusloop:
	jsr iar,*$next
	compile; pploop; comma; clue; at; qdup; zbranch; 1f
		here; swap; store
	1: clue; store; return

/ LEAVE
	.byte 205; <LEA>
	plusloop-6
leave:	jsr iar,*$next
	compile; pleave; here; clue; store; two; allot; return

/ [
	.byte 201; <[  >
	leave-6
lbracket:
	jsr iar,*$next
	zero; state; store; return

/ ]
	.byte 1; <]  >
	lbracket-6
rbracket:
	jsr iar,*$next
	one; state; store; return

/ (
	.byte 201; <(  >
	rbracket-6
paren:	jsr iar,*$next
	lit; .byte 051,0; word; drop; return

/     I/O words

/ TYPE				( addr count --- )
	.byte 4; <TYP>
	paren-6
type:	jsr iar,*$next
	stdout; write; drop; return

/ EMIT				( chr --- )
	.byte 4; <EMI>
	type-6
emit:	jsr iar,*$next
	atsp; one; type; drop; return

/ CR
	.byte 2; <CR >
	emit-6
cr:	jsr iar,*$next
	eol; emit; return

/ FQUERY			( fd --- actcount )
	.byte 6; <FQU>
	cr-6
fquery:	jsr iar,*$next
	zero; in; store;
	tib; lit; .byte 120.,0; fexpect; return

/ COUNT
	.byte 5; <COU>
	fquery-6
count:	jsr iar,*$next
	dup; oneplus; swap; cat; return

/ ALIGN
	.byte 5; <ALI>
	count-6
align:	jsr iar,*$next
	oneplus; twoslash; twostar; return

/ (.")
	.byte 4; <(.">
	align-6
pdotquote:
	jsr iar,*$next
	fromr; count; twodup; type; plus; align; tor; return

/ ,WORD
	.byte 5; <,WO>
	pdotquote-6
commaword:
	jsr iar,*$next
	word; cat; oneplus; align; allot; return

/ ."
	.byte 202; <." >
	commaword-6
dotquote:
	jsr iar,*$next
	compile; pdotquote; lit; .byte 42,0; commaword; return

/ SPACE
	.byte 5; <SPA>
	dotquote-6
space:	jsr iar,*$next
	lit; .byte 40,0; emit; return

/ SPACES
	.byte 6; <SPA>
	space-6
spaces:	jsr iar,*$next
	qdup; zbranch; 2f
		zero; pdo; 1: space; ploop; 1b
	2: return

/ STRING			( adr[counted_string] --- adr[string] )
	.byte 6; <STR>
	spaces-6
string:	jsr iar,*$next
	count; dup; tor; pad; swap; cmove; zero; pad; fromr; plus;
	cstore; pad; return

/ "				( --- adr[string] )
	.byte 1; <"  >
	string-6
quote:	jsr iar,*$next
	lit; .byte 042,0; word; string; return

/ ("")				( --- adr[string] )
	.byte 4; <("">
	quote-6
pdquote:
	jsr iar,*$next
	fromr; dup; count; plus; align; tor; string; return

/ ""
	.byte 202; <"" >
	pdquote-6
dquote:	jsr iar,*$next
	compile; pdquote; lit; .byte 042,0; commaword; return;

/       Defining words

/ CFIELD
	.byte 6; <CFI>
	dquote-6
cfield:	jsr iar,*$next
	lit; .byte 6,0; plus; return

/ NFIELD
	.byte 6; <NFI>
	cfield-6
nfield:	jsr iar,*$next
	lit; .byte 6,0; minus; return

/ -IMM				( nfa --- cfa n )
	.byte 4; <-IM>
	nfield-6
notimm:	jsr iar,*$next
	dup; cfield; minusone; rot; cat; lit; .byte 0200,0; and
	zbranch; 1f; negate; 1: return

/ FIND				( addr[name] --- addr2 n )
	.byte 4; <FIN>
	notimm-6
find:	jsr iar,*$next
	dup; context; at; at; pfind
	qdup; zbranch; 1f; swap; drop; notimm; branch; 3f
	1: dup; latest; pfind
	   qdup; zbranch; 2f; swap; drop; notimm; branch; 3f
	   2: zero
	3: return

/ '
	.byte 1; <'  >
	find-6
tic:	jsr iar,*$next
	here; lit; .byte 4,0; lit; .byte 40,0; fill
	lit; .byte 40,0; word
	find; zeroeq; zbranch; 1f; drop; zero; 1: return

/ HEADER
	.byte 6; <HEA>
	tic-6
header:	jsr iar,*$next
	tic; zbranch; 1f
		wrn; at; zbranch; 1f
			here; count; type
			pdotquote; .byte 15; < isn't unique>; .even; cr
	1: here; lit; .byte 4,0; allot; latest; comma; current; at; store;
	return

/ CALL
	.byte 4; <CAL>
	header-6
call:	jsr iar,*$next
	lit; .byte 037,9; comma; return

/ :
	.byte 1; <:  >
	call-6
colon:	jsr iar,*$next
	current; at; context; store; 
	header; call; compile; next; rbracket; smudge; return

/ ;
	.byte 201; <;  >
	colon-6
semicolon:
	jsr iar,*$next
	compile; return; smudge; zero; state; store; return

/ VARIABLE
	.byte 10; <VAR>
	semicolon-6
variable:
	jsr iar,*$next
	header; call; compile; var; zero; comma; return

/ CONSTANT
	.byte 10; <CON>
	variable-6
constant:
	jsr iar,*$next
	header; call; compile; con; comma; return

/ 2VARIABLE
	.byte 11; <2VA>
	constant-6
twovar:	jsr iar,*$next
	variable; zero; comma; return

/ DOES>
	.byte 5; <DOE>
	twovar-6
does:	jsr iar,*$next
	fromr; latest; cfield; lit; .byte 4,0; plus; store; return

/ CREATE
	.byte 6; <CRE>
	does-6
create:	jsr iar,*$next
	header; call; compile; pdoes; zero; comma; does; return

/ VOCABULARY
	.byte 12; <VOC>
	create-6
vocabulary:
	jsr iar,*$next
	create; here; twoplus; comma; latest; comma
	does; at; context; store; return

/ DEFINITIONS
	.byte 13; <DEF>
	vocabulary-6
definitions:
	jsr iar,*$next
	context; at; current; store; return

/ FORTH					FORTH vocabulary
	.byte 205; <FOR>
	definitions-6
forth:	jsr iar,*$next
	initvocab; context; store; return

/       numeric output words

/ HLD
	.byte 3; <HLD>
	forth-6
hld:	jsr iar,*$var
	.byte 0,0

/ HOLD
	.byte 4; <HOL>
	hld-6
hold:	jsr iar,*$next
	minusone; hld; plusstore; hld; at; cstore; return

/ <#
	.byte 2; <<# >
	hold-6
lnum:	jsr iar,*$next
	pad; hld; store; return

/ #>
	.byte 2; <#\> >
	lnum-6
gnum:	jsr iar,*$next
	twodrop; hld; at; pad; over; minus; return

/ SIGN
	.byte 4; <SIG>
	gnum-6
sign:	jsr iar,*$next
	zeroless; zbranch; 1f; lit; .byte 055,0; hold; 1: return

/ #
	.byte 1; <#  >
	sign-6
num:	jsr iar,*$next
	base; at; mslashmmod; rot; lit; .byte 11,0; over; less
	zbranch; 1f; lit; .byte 7,0; plus; 1:
	lit; .byte 060,0; plus; hold; return

/ #S
	.byte 2; <#S >
	num-6
nums:	jsr iar,*$next
	1: num; twodup; or; zeroeq; zbranch; 1b; return

/ D.R
	.byte 3; <D.R>
	nums-6
ddotr:	jsr iar,*$next
	tor; swap; over; dabs; lnum; nums; rot; sign; gnum;
	fromr; over; minus; zero; max; spaces; type; return

/ ZEROES
	.byte 6; <ZER>
	ddotr-6
zeroes:	jsr iar,*$next
	zero; max; qdup; zbranch; 2f; zero; pdo; 1:
		lit; .byte 060,0; emit; ploop; 1b
	2: return

/ D.LZ
	.byte 4; <D.L>
	zeroes-6
ddotlz:	jsr iar,*$next
	tor; swap; over; dabs; lnum; nums; rot; sign; gnum
	fromr; over; minus; zeroes; type; return

/ D.
	.byte 2; <D. >
	ddotlz-6
ddot:	jsr iar,*$next
	zero; ddotr; space; return

/ .R
	.byte 2; <.R >
	ddot-6
dotr:	jsr iar,*$next
	tor; stod; fromr; ddotr; return

/ .
	.byte 1; <.  >
	dotr-6
dot:	jsr iar,*$next
	stod; ddot; return

/ U.R
	.byte 3; <U.R>
	dot-6
udotr:	jsr iar,*$next
	zero; swap; ddotr; return

/ U.LZ
	.byte 4; <U.L>
	udotr-6
udotlz:	jsr iar,*$next
	zero; swap; ddotlz; return

/	utilities

/ [COMPILE]
	.byte 211; <[CO>
	udotlz-6
bcompile:
	jsr iar,*$next
	tic; comma; return

/ DUMP				( addr bytes --- )
	.byte 4; <DUM>
	bcompile-6
dump:	jsr iar,*$next
	cr; over; plus; swap; pdo; 1:
		i; lit; .byte 4,0; udotlz; pdotquote; .byte 1; <:>; .even
		space
		i; lit; .byte 8,0; plus; i; pdo; 2:
			i; cat; two; udotlz; space; ploop; 2b
		i; lit; .byte 8,0; plus; i; pdo; 3:
			i; cat; dup; lit; .byte 040,0; less; 
			over; lit; .byte 177,0; equal; or
			zbranch; 4f; drop; lit; .byte 056,0; 4:
			emit; ploop; 3b
		cr; lit; .byte 8,0; pploop; 1b
	return

/	operating system support words

/ DIGIT				( char --- n true <or> false )
 	.byte 5; <DIG>
	dump-6
digit:	jsr iar,*$next
	lit; .byte 60,0; minus
	dup; lit; .byte 11,0; greater; over; lit; .byte 21,0; less; and
	zbranch; 1f
		drop; false; branch; 4f
	1:	dup; lit; .byte 11,0; ugreater; zbranch; 2f
			lit; .byte 7,0; minus
		2: dup; base; at; oneminus; ugreater; zbranch; 3f
			drop; false; branch; 4f
		3: 	true
	4: return

/ CONVERT			( dl dh addr1 --- dl' dh' addr2 )
	.byte 7; <CON>
	digit-6
convert:
	jsr iar,*$next
 	tor; 1:
	   fromr; oneplus; dup; tor; cat; digit;
	   zbranch; 2f; tor; base; at; umstarm; fromr; zero; dplus
	branch; 1b
	2: fromr; return

/ NUMBER			( ADDR --- N TRUE <OR> FALSE )
	.byte 6; <NUM>
	convert-6
number:	jsr iar,*$next
	dup; oneplus; cat; lit; .byte 055,0; equal; dup; tor; minus
	zero; zero; rot; convert
	cat; lit; .byte 040,0; equal; zbranch; 1f
	   drop; fromr; plusminus; true; branch; 2f
	   1: twodrop; fromr; drop; false
	2: return

/ ?STACK			( --- T/F )  ( returns true if stack underflow )
	.byte 6; <?ST>
	number-6
qstack:	jsr iar,*$next
	atsp; sp0; greater; return

/ CHUCKBUF                      ( chuck rest of input buffer )
	.byte 10; <CHU>
	qstack-6
chuckbuf:
	jsr iar,*$next
	tib; in; at; plus
		1: dup; cat; eol; nequal; zbranch; 2f; oneplus
		branch; 1b
	2: tib; minus; in; store; return

/ ENDINTERP			( --- )   ( flush reset of input buffer )
	.byte 11; <END>
	chuckbuf-6
endinterp:
	jsr iar,*$next
	sp0; storesp;		/ reset stack pointer
	chuckbuf; return

/ INTERPRET
	.byte 11; <INT>
	endinterp-6
interpret:
	jsr iar,*$next
	1: here; lit; .byte 4,0; lit; .byte 040,0; fill
	lit; .byte 040,0; word; cat; zbranch; 9f
	here; find; qdup; zbranch; 4f
		state; at; plus
		zbranch; 2f; execute; branch; 3f; 2: comma; 3:
		branch; 7f
	4: number; zbranch; 6f
		state; at; zbranch; 5f; compile; lit; comma; 5:
		branch; 7f
		6: here; count; type; pdotquote; .byte 2; < ?>; .even; cr
			endinterp
	7: qstack; zbranch; 8f; pdotquote; .byte 14; < Stack empty>; .even; cr
		endinterp; 8:
	branch; 1b;
	9: return

/ FLOAD				( adr[string] --- )
	.byte 5; <FLO>
	interpret-6
fload:	jsr iar,*$next
	zero; open; dup; zeroless; zbranch; 0f
		drop; pdotquote; .byte 13; < can't open>; .even; cr; branch; 3f
	0: tor
	1: rat; fquery; zbranch; 2f; interpret; branch; 1b
	2: fromr; close; chuckbuf
	3: return

/ QUIT
	.byte 4; <QUI>
	fload-6
quit:	jsr iar,*$next
	zero; state; store; sp0; storesp
	cr; pdotquote; .byte 23.; <unix-FORTH, version 1.0>; .even
	1: cr; stdin; fquery; zbranch; 3f
		interpret
		state; at; zeroeq; zbranch; 2f; pdotquote; .byte 3; < OK>;
		.even
	2: branch; 1b
	3: cr; terminate; return

/	the reset of the dictionary
dict:	.=.+20000.			/ TEST
+E+O+F

lwt1@aplvax.UUCP (06/08/84)

Here is part 5 of the source for FORTH for the PDP-11.
Delete everything thru the "-- cut here --" line, and extract with 'sh':

	sh part1 part2 ... part7

where 'part?' are whatever you've named the files.  Note the copyright
notice at the end of README.  Please let us know how things go.  While
we can't support this software, we'll be posting bug fixes/upgrades to
net.sources as time permits.

VAX-FORTH should be 'forth'-coming {yuk-yuk} within a couple of weeks.

Have fun!



						-Lloyd W. Taylor
						 ... seismo!umcp-cs!aplvax!lwt1
---I will have had been there before, soon---

---------------------------------- cut here ----------------------------------
echo x - METAASM
cat >METAASM <<'+E+O+F'
( FORTH PDP-11 ASSEMBLER ) OCTAL

VARIABLE *OPCODE                  ( VARIABLE POINTS TO LATEST OPCODE )

: CODE                            ( CREATES A CODE DEFINITION )
 HEADER HOST-->META ;

: OPBUILD                         ( OPERANDFIELD --- )   ( ADDS OPERAND FIELD )
                                  ( CONSISTING OF ADDRESSING MODE AND REGISTER)
                                  ( TO LATEST OPCODE.                         )
 *OPCODE FORTH @ DUP HOST @       ( OPERAND OPADDR OPCODE )
 6 ROTATE ROT OR SWAP ! ;

: BYTE                            ( --- )   ( CONVERTS MOST RECENT OPCODE TO )
                                  ( BYTE ADDRESSING. MUST BE USED AT END OF  )
                                  ( ASSEMBLY LANGUAGE LINE.                  )
 *OPCODE FORTH @
 HOST DUP @ 100000 OR SWAP ! ;

: MNEMONIC                        ( OPCODE --- )   ( DEFINING WORD DEFINES )
                                  ( MNEMONIC. WORDS DEFINED WITH MNEMONIC  )
                                  ( COMMA THEIR OPCODES INTO THE DICTION-  )
                                  ( ARY WHEN EXECUTED.                     )
 CREATE FORTH , HOST              ( SAVE OPCODE )
 DOES> HERE *OPCODE FORTH ! @ HOST , ; 
                                  ( SAVE OPCODE ADDRESS; COMMA OPCODE INTO DIC )

( ADDRESSING MODES )

: REG                             ( REG# --- )   ( REGISTER ADDRESSING )
 OPBUILD ;

: )                               ( REG# --- )   ( REGISTER DEFERRED )
 10 OR OPBUILD ;

: )+                              ( REG# --- )   ( AUTO-INCREMENT )
 20 OR OPBUILD ;

: *)+                             ( REG# --- )   ( AUTO-INCREMENT DEFERRED )
 30 OR OPBUILD ;

: -(                              ( REG# --- )   ( AUTO-DECREMENT )
 40 OR OPBUILD ;

: *-(                             ( REG# --- )   ( AUTO-DECREMENT DEFERRED )
 50 OR OPBUILD ;

: X(                              ( OFFSET REG# --- )   ( INDEXED ADDRESSING )
 60 OR OPBUILD , ;

: *X(                             ( OFFSET REG# --- )   ( INDEX DEFERRED )
 70 OR OPBUILD , ;

: $                               ( IMMEDIATE --- )   ( IMMEDIATE )
 27 OPBUILD , ;

: *$                              ( ADDR --- )   ( ABSOLUTE )
 37 OPBUILD , ;

: REL                             ( ADDR --- )   ( RELATIVE )
 67 OPBUILD 
 *OPCODE FORTH @ HOST - 4 - , ;

: *REL                            ( ADDR --- )   ( RELATIVE DEFERRED )
 77 OPBUILD
 *OPCODE FORTH @ HOST - 4 - , ;

: REG-ONLY                        ( REG# --- )   ( FOR REGISTER ONLY INSTRUC- )
                                  ( TIONS SUCH AS MUL OR DIV.                 )
 *OPCODE FORTH @ DUP HOST @       ( REG# OPADDR OPCODE )
 3 ROTATE ROT OR SWAP ! ;

( LOCAL LABELS:  EIGHT LOCAL LABELS ARE ALLOWED NUMBERED FROM 0 TO 7 )
( ONLY ONE FORWARD BRANCH PER LABEL IS ALLOWED.  ANY NUMBER OF BACK- )
( WARD BRANCHES IS ALLOWED.					     )

VARIABLE LTABLE  FORTH 0 , 10 1- 4 * ALLOT HOST
 LTABLE 10 4 * 0 FILL		( LABEL TABLE )

: FWD                           ( LABEL# --- )   ( LEAVE ADDRESS IN TABLE. )
 HERE SWAP 2* 2* LTABLE + 2+ FORTH ! HOST ;

: BACK                          ( LABEL# --- )   ( ADD OFFSET TO PREVIOUSLY  )
				( COMPILED WORD.                             )
 2* 2* LTABLE + FORTH @ HOST HERE - 2/ 377 AND HERE 2- DUP @ ROT OR SWAP ! ;

: L:				( LABEL# --- )   ( RESOLVE FORWARD BRANCHES, )
				( PURGE TABLE, AND ADD CURRENT ADDRESS.      )
 2* 2* LTABLE + DUP 2+ FORTH @ ?DUP IF	( IF LABEL NEEDS RESOLUTION )
 HOST HERE OVER - 2/ 377 AND SWAP 2- DUP @ ROT OR SWAP ! THEN
 0 OVER 2+ FORTH !		( OLD LABEL ADDRESS IS DEFUNCT )
 HOST HERE SWAP FORTH ! HOST ;	( CURRENT ADDRESS )

( MNEMONICS )

050 MNEMONIC CLR      051 MNEMONIC COM     052 MNEMONIC INC
053 MNEMONIC DEC      054 MNEMONIC NEG     057 MNEMONIC TST
060 MNEMONIC ROR      061 MNEMONIC ROL     062 MNEMONIC ASR
063 MNEMONIC ASL      003 MNEMONIC SWB    055 MNEMONIC ADC
056 MNEMONIC SBC      067 MNEMONIC SXT      01 MNEMONIC MOV
 02 MNEMONIC CMP       06 MNEMONIC ADD      16 MNEMONIC SUB
 03 MNEMONIC BIT       04 MNEMONIC BIC      05 MNEMONIC BIS
074 MNEMONIC EXOR     070 MNEMONIC MUL     071 MNEMONIC DIV
001 MNEMONIC JMP      004 MNEMONIC JSR     020 MNEMONIC RTS
261 MNEMONIC SEC
002 MNEMONIC RTI

000400 MNEMONIC BR	001000 MNEMONIC BNE	001400 MNEMONIC BEQ
100000 MNEMONIC BPL	100400 MNEMONIC	BMI	102000 MNEMONIC BVC
102400 MNEMONIC BVS	103000 MNEMONIC BCC	103400 MNEMONIC BCS
002000 MNEMONIC BGE	002400 MNEMONIC BLT	003000 MNEMONIC BGT
003400 MNEMONIC BLE	101000 MNEMONIC BHI	101400 MNEMONIC BLOS
103000 MNEMONIC BHIS	103400 MNEMONIC BLO

( SOB: SUBTRACT ONE AND BRANCH INSTRUCTION )

: SOB				  ( LABEL# REG# --- )
 6 ROTATE 77000 OR
 HERE 2+ ROT 2* 2* LTABLE + FORTH @ HOST - 2/ OR , ;

: TRAP				  ( TRAP# --- )
 104400 + , ;

( MACROS )

4 CONSTANT IAR           5 CONSTANT PSP            6 CONSTANT SP
7 CONSTANT PC

: NEXT                            ( --- )   ( COMPILES CODE FOR NEXT )
 JMP IAR *)+ ;
+E+O+F
echo x - META1
cat >META1 <<'+E+O+F'
( METACOMPILER, PART 1 -- ALLOWS METACOMPILATION OF PRIMITIVES )   HEX
 
: METACOMPILER  ;               ( MARK BEGINNING OF METACOMPILER FOR 'FORGET')

( METACOMPILER DATABASE )

VARIABLE OBJLINK                ( OBJECT SYSTEM VOCABULARY POINTER           )
2VARIABLE WDS                   ( OBJECT SYSTEM HEADER LENGTH IN BYTES       )
VARIABLE W0                     ( BASE OF OBJECT DICTIONARY SPACE            )
VARIABLE 'H                     ( OBJECT SYSTEM DICTIONARY POINTER           )
VARIABLE 'R                     ( OBJECT SYSTEM RAM POINTER                  )
VARIABLE RAMOBJECT              ( TRUE=RAM OBJECT, FALSE=PROM OBJECT         )
VARIABLE METASTATE              ( TRUE=METACOMPILE, FALSE=EXECUTE            )
 0 METASTATE !

VARIABLE METAMP                 ( METACOMPILER MAPPING ENABLE/DISABLE        )
: METAMAP  TRUE METAMP ! ;
: NOMETAMAP  FALSE METAMP ! ;
 
VARIABLE WRNMETA                ( METACOMPILER WARNING ENABLE/DISABLE        )
: METAWARN  TRUE WRNMETA ! ;
: NOMETAWARN  FALSE WRNMETA ! ;

VOCABULARY META IMMEDIATE
VOCABULARY HOST IMMEDIATE     HOST DEFINITIONS
 
: VOCSSAVE              ( --- V1 V2 ) ( SAVE VOCABS ON STACK                 )
	 CONTXT @ CURRENT @ ;
 
: VOCSRESTORE           ( V1 V2 --- ) ( UNDO 'VOCSSAVE'                      )
	 CURRENT ! CONTXT ! ;

: PREVIOUS	( --- N )   ( PRODUCES THE CONTENTS OF THE FIRST WORD OF     )
		( THE PARAMETER FIELD OF THE MOST RECENT DEFINTION IN 	     )
		( VOCABULARY META. IF THIS WAS AN 'EMPLACE' DEFINTION, THE   )
		( VALUE RETURNED WILL BE THE TARGET SYSTEM OPCODE OF THE     )
		( EMPLACE WORD. THIS IS USEFUL FOR IMMEDIATING.              )
	VOCSSAVE
	[COMPILE] META DEFINITIONS
	LATEST CFIELD 6 + @ -ROT
	VOCSRESTORE ;

: FIND          ( ADDR[NAME] --- ADDR2 N ) ( DICTIONARY SEARCH               )
		( RESTRICTED TO VOCABULARY 'META'                            )
	 VOCSSAVE >R >R                 ( SAVE CONTEXT, CURRENT ON RET STACK )
	 [COMPILE] META DEFINITIONS     ( SELECT META VOCABULARY             )
	 FIND                           ( SEARCH DICTIONARY                  )
	 R> R> VOCSRESTORE ;            ( RESTORE CURRENT AND CONTEXT        )
 
: HOST-->META   ( --- ) ( UNLINK LATEST ENTRY IN VOCABULARY 'HOST' AND       )
		( RELINK IT INTO VOCABULARY 'META'.                          )
	 VOCSSAVE                       ( SAVE CONTEXT AND CURRENT ON STACK  )
	 [COMPILE] HOST DEFINITIONS     ( SET CONTEXT AND CURRENT TO 'HOST'  )
	 LATEST DUP 4 + @ CURRENT @ !   ( MOVE BACK 'HOST' VOCAB POINTER     )
	 [COMPILE] META DEFINITIONS     ( SET CONTEXT AND CURRENT TO 'META'  )
	 LATEST @ 4D84 =                ( SET LINK OF FIRST ENTRY IN 'META'  )
	 IF 0 ELSE LATEST               ( [I.E., THE ONE AFTER 'META' ITSELF])
	 THEN OVER 4 + !                ( TO 0, ELSE LINK NORMALLY           )
	 CURRENT @ !                    ( MOVE UP 'META' VOCAB POINTER       )
	 VOCSRESTORE ;                  ( RESTORE OLD CURRENT AND CONTEXT    )
 
: METASMUDGE    ( --- ) ( SMUDGE THE MOST RECENT META DEFINITION             )
	 VOCSSAVE
	 [COMPILE] META DEFINITIONS SMUDGE
	 VOCSRESTORE ;
 
: HERE 'H @ ;   ( --- N ) ( RETURN VALUE OF OBJECT DICTIONARY POINTER        )

: RAMHERE       ( --- N ) ( RETURN VALUE OF OBJECT RAM POINTER               )
         RAMOBJECT @ IF HERE ELSE 'R @ THEN ;

: ALLOT         ( N --- ) ( ALLOT 'N' WORDS OF OBJECT DICTIONARY SPACE       )
	 'H +! ;

: RAMALLOT      ( N --- ) ( ALLOT 'N' WORDS OF OBJECT RAM SPACE              )
	 RAMOBJECT @
	 IF ALLOT
	 ELSE 'R +!
	 THEN ;

: RAM           ( N --- ) ( SET RAMOBJECT FLAG TRUE [RAM], INITIALIZE        )
		( 'H, W0 AND 'R TO N, AND ZERO ENTIRE OBJECT DICTIONARY.     )
		( 'H, W0 AND 'R TO N, OBJLINK TO 0, AND ZERO ENTIRE          )
		( OBJECT DICTIONARY.                                         )
         TRUE RAMOBJECT !
         DUP 'H !  DUP W0 !  'R !  0 OBJLINK ! ;
 
: PROM          ( N --- ) ( SET RAMOBJECT FLAG FALSE [PROM], INITIALIZE      )
		( 'H AND W0 TO N, OBJLINK TO 0, OBJECT DICTIONARY TO 0'S.    )
         FALSE RAMOBJECT !
         DUP 'H !  W0 !  0 OBJLINK ! ;
 
: NOHEAD  0 WDS ! ;     ( --- ) ( MAKE NEXT OBJECT DEFINITION HEADLESS       )
: HEADS  6 6 WDS 2! ;   ( --- ) ( FOLLOWING OBJECT DEFINITIONS HAVE HEADS    )
: NOHEADS  0 0 WDS 2! ; ( --- ) ( FOLLOWING OBJECT DEFINITIONS HEADLESS      )
 
( CODE FOR HANDLING META-COMPILATION RANDOM ACCESS FILES ) DECIMAL

VARIABLE BUFFER 510 FORTH ALLOT HOST
	BUFFER 512 0 FILL

VARIABLE DIRTY                          ( TRUE IF BUFFER IS INCONSISTENT     )
 FALSE DIRTY !				( WITH DISK FILE.                    )
 
VARIABLE IMAGE	       			( HOLDS TARGET ADDRESS THAT COR-     )
 -1 IMAGE !				( RESPONDS TO BUFFER.                )

VARIABLE FILED                          ( FILE DESCRIPTOR OF META OBJECT FILE)

: ?FLUSH				( --- )   ( FLUSH BUFFER IF DIRTY    )
					( FLAG SET.                          )
 DIRTY @ IF
    FILED @ IMAGE @ 0 SEEK              ( SEEK POSITION IN FILE FOR BUFFER   )
    BUFFER 512 FILED @ WRITE DROP	( WRITE BACK TO DISK )
    FALSE DIRTY !			( BUFFER IS CONSISTENT WITH DISK )
 THEN ;

: GET					( ADDR --- )   ( TRIES TO READ 512 )
					( BYTES FROM DISK AT ADDR AND PUTS )
					( INTO BUFFER.  	           )
 BUFFER 512 0 FILL			( ZERO BUFFER )
 DUP IMAGE ! 				( RECORD ADDRESS )
 FILED @ SWAP 0 SEEK			( POSITION FILE READ POINTER )
 FILED @ BUFFER 512 READ DROP ; 	( TRY TO READ 512 BYTES )

HEX
 
: T->R					( ADDR --- ADDR' )   ( TRANSLATES )
					( TARGET ADDRESS IN ADDRESS IN    )
					( BUFFER. DOES BUFFER FLUSHING    )
					( AND READING IF NECESSARY.       )
 10 +					( SKIP A.OUT HEADER )
 DUP 1FF AND SWAP FE00 AND 		( OFFSET 512*BLOCK# )
 DUP IMAGE @ = IF			( IF ALREADY IN RAM )
    DROP				( DO NOTHING )
 ELSE
    ?FLUSH GET				( ELSE GET NEEDED BLOCK )
 THEN BUFFER + ;

: C@					( ADDR --- BYTE )
 T->R C@ ;

: C!					( BYTE ADDR --- )
 T->R C! TRUE DIRTY ! ;

: @					( ADDR --- WORD )
 DUP 1+ C@ 8 ROTATE			( FETCH HIGH BYTE FIRST )
 SWAP C@ OR ;				( THEN FETCH LOW BYTE )

: !					( WORD ADDR --- )
 >R DUP FF AND R@ C!			( STORE LOW BYTE )
 FF00 AND 8 ROTATE R> 1+ C! ;		( STORE HIGH BYTE )

: ,					( WORD --- )
 HERE ! 2 ALLOT ;

: .O		( N --- )   ( PRINT N IN OCTAL WITHOUT CHANGEING BASE.       )
 BASE FORTH @ OCTAL SWAP . BASE ! HOST ;

: EMPLACE       ( --- ) ( LOGS AND CREATES A WORD WHOSE PARAMETER FIELD      )
		( CONTAINS THE TARGET ADDRESS OF THE NEXT CODE FIELD IN THE  )
		( TARGET SPACE. WHEN THE WORD IS EXECUTED, THIS VALUE        )
		( [PRESUMABLY THE OPCODE OF THE 'EMPLACED' WORD] IS          )
		( COMPILED INTO THE OBJECT DICTIONARY.                       )
	 HERE FORTH WDS @ +			( HEADER?		     )
	 FORTH METAMP @
	 IF
	    DUP .O HERE COUNT TYPE CR		( PRINT CFA[OCTAL] AND NAME  )
	 THEN
	 CREATE , DOES> @ HOST , ;
 
: HEADER        ( --- ) ( CREATES AN OBJECT DICTIONARY ENTRY AND A           )
		( CORRESPONDING 'EMPLACE' ENTRY IN THE HOST VOCABULARY.      )
	 WRNMETA FORTH @ HOST                   ( CHECK METAWARNING FLAG     )
	 IF >IN FORTH @                         ( SAVE INPUT POINTER         )
	 HERE 4 20 FILL 20 WORD HOST FIND       ( SEARCH META FOR NEW WORD   )
	   IF FORTH HERE COUNT TYPE             ( PRINT WARNING IF WORD FOUND)
	     SPACE ." isn't unique [Meta]" CR
	   THEN DROP
	   >IN ! HOST                           ( RESTORE INPUT POINTER      )
	 THEN
	 EMPLACE 			        ( CREATE 'EMPLACE' ENTRY     )
	 WDS FORTH @ HOST                       ( TEST FOR OBJ HDR CREATION  )
	 IF HERE FORTH LATEST @ HOST ,          ( OBJECT HEADER, 1ST WORD    )
	   FORTH LATEST 2+ @ HOST ,             ( OBJECT HEADER, 2ND WORD    )
	   OBJLINK FORTH @ HOST ,               ( OBJECT LINK FIELD          )
	   OBJLINK FORTH ! HOST                 ( UPDATE PTR TO OBJECT VOCAB )
	 THEN WDS 2+ FORTH @ WDS ! HOST ;       ( RESET TEMP HEADER LENGTH   )
 
: LABEL
  HERE METAMP FORTH @ IF
    DUP .O					( PRINT ADDRESS OF LABEL )
    >IN @ 					( PEEK AHEAD INTO INPUT STREAM )
    20 WORD COUNT TYPE ."  Label" CR
    >IN !
  THEN 
  CONSTANT HOST ;

: '		( --- CFA <OR> 0 )   ( RETURNS CFA OF TARGET WORD THAT FOLLOWS)
 FORTH HERE 4 20 FILL
 HOST 20 WORD FIND
 IF 6 + FORTH @ HOST
 ELSE DROP 0
 THEN ;
 
: DUMPOBJ       ( ADDR N --- ) ( DUMPS N WORDS OF OBJECT SPACE FROM ADDR     )
         CR OVER + SWAP
	 DO
 	    I 4 U.LZ ." :" SPACE
	    I 8 + I DO
	       I C@ 2 U.LZ SPACE
	    LOOP
	    I 8 + I DO
	       I C@ DUP 20 < OVER 7F = OR
	       IF DROP 2E THEN
	       EMIT
            LOOP
	    CR
	 8 +LOOP ;

( CODE FOR CLEANING UP AFTER A METACOMPILATION )

VARIABLE A.OUT				( A.OUT HEADER )
 FORTH 107 A.OUT ! 0 , 0 , 0 , 0 , 0 , 0 , 1 , HOST

: CLEANUP				( FREE_DICT_SIZE --- )   ( CLEANS UP )
					( AFTER A METACOMPILATION. MAKES     )
					( DISK IMAGE FILE GROW UNTIL IT HAS  )
					( AT LEAST THE FREE_DICT_SIZE ASKED  )
					( FOR. WRITES THE A.OUT HEADER OUT.  )
 HERE + 10 + 200 + FE00 AND		( COMPUTE UPPER LIMIT DISK ADDRESS )
 HERE 10 +				( COMPUTE LOWER LIMIT DISK ADDRESS )
    DO 0 , LOOP				( GROW DICTIONARY )
 ?FLUSH
 HERE A.OUT 2+ FORTH !			( SIZE OBJECT SIZE IN A.OUT )
 FILED @ 0 0 SEEK			( REWIND FILE )
 A.OUT 10 FILED @ WRITE DROP		( WRITE A.OUT HEADER TO DISK )
 FILED @ CLOSE HOST ;

+E+O+F
echo x - META2
cat >META2 <<'+E+O+F'
( METACOMPILER, PART 2 -- ALLOWS METACOMPILATION OF : DEFINITIONS, )   HEX
(                         VARIABLES AND CONSTANTS IN A SINGLE VOCABULARY     )
 
: ]             ( --- ) ( MAIN METACOMPILER INTERPRETATION LOOP              )
         TRUE METASTATE FORTH !
	 BEGIN
	    FORTH >IN @ 20 WORD SWAP >IN !
	    C@ METASTATE @ AND WHILE
	    HERE 4 20 FILL 20 WORD HOST FIND IF 
	       EXECUTE
	    ELSE
	       NUMBER IF
	          META (LITERAL) HOST ,
	       ELSE
	          FORTH HERE COUNT TYPE ."  ? [Meta]" CR ENDINTERP
               THEN
	    THEN
	    ?STACK IF ."  Stack empty [Meta]" CR ENDINTERP THEN
	 REPEAT ; HOST
 
: FLOAD         ( --- ) ( METACOMPILER LOADER; CONTINUES META : DEFINITIONS  )
	 0 OPEN
	 DUP 0< IF
	    DROP ."  can't open" CR
	 ELSE
	    >R BEGIN
	       R@ FQUERY WHILE
	       METASTATE FORTH @ HOST IF
	          ]
	       THEN INTERPRET
	    REPEAT R> CLOSE CHUCKBUF
	 THEN ;
 
( METACOMPILER DIRECTIVES )
 
: (  29 WORD DROP ;   HOST-->META       ( START OF COMMENT                   )
: [                                     ( --- ) ( EXIT METACOMPILER LOOP ']' )
         FORTH FALSE METASTATE ! HOST ;   HOST-->META
: IF  META ?BRANCH  HOST HERE 0 , ;   HOST-->META
: WHILE  META IF HOST ;   HOST-->META
: ELSE  META BRANCH  HOST HERE 0 ,  HERE ROT ! ;   HOST-->META
: THEN  HERE SWAP ! ;   HOST-->META
: DO  META (DO)  FORTH CLUE @ 0 CLUE !  HOST HERE ;   HOST-->META
: LOOP  META (LOOP)  HOST , 
 FORTH CLUE @ ?DUP IF HOST HERE SWAP ! THEN
 FORTH CLUE ! HOST ;   HOST-->META
: +LOOP  META (+LOOP)  HOST ,
 FORTH CLUE @ ?DUP IF HOST HERE SWAP ! THEN
 FORTH CLUE ! HOST ;   HOST-->META
: LEAVE META (LEAVE)  HOST HERE FORTH CLUE ! HOST 0 , ;   HOST-->META
: BEGIN  HERE ;   HOST-->META
: UNTIL  META ?BRANCH  HOST , ;   HOST-->META
: AGAIN  META BRANCH  HOST , ;   HOST-->META
: REPEAT  META BRANCH  HOST SWAP ,  HERE SWAP ! ;   HOST-->META

: ;  META (;)  HOST HOST-->META 
	 FORTH FALSE METASTATE ! HOST ;   HOST-->META
 
( METACOMPILER IMMEDIATOR )
 
: IMMEDIATE       ( --- ) ( TOGGLES IMMEDIATE BIT IN LATEST TARGET HEAD)
	 PREVIOUS NFIELD DUP C@ 80 OR
	 SWAP C! ; 

( DEFINING WORDS )

: CALL		( --- )   ( COMPILE JSR IAR,*$--- INTO TARGET CODE.	     )
	 091F , ;
 
: \CONSTANT     ( N --- ) ( DEFINES THE NEXT INPUT WORD AS A CONSTANT        )
		( 'N' IN THE RESIDENT SYSTEM'S CURRENT VOCABULARY            )
		( WITHOUT MOVING THE INPUT POINTER '>IN'.                    )
         >IN FORTH @  SWAP CONSTANT  >IN ! ;   HOST
 
: CONSTANT
	 DUP \CONSTANT
	 HEADER CALL META (CONSTANT) HOST  ,  HOST-->META ;
 
: :
	 HEADER CALL META (:) HOST ] ;
 
FORTH : VARIABLE        ( --- ) ( CREATES OBJECT VARIABLE INIT'ED TO 0       )
	 RAMOBJECT FORTH @ HOST
 	 IF HERE CFIELD 4 + \CONSTANT 		( RAM VERSION )
	    HEADER CALL META (VARIABLE) HOST 0 , HOST-->META
	 ELSE RAMHERE CONSTANT 2 RAMALLOT	( PROM VERSION )
 	 THEN ;
 
FORTH : 2VARIABLE       ( --- ) ( CREATES OBJECT 2VARIABLE INIT'ED TO 0      )
         VARIABLE
         RAMOBJECT FORTH @ HOST
	 IF 0 ,                                 ( RAM VERSION                )
	 ELSE 2 RAMALLOT                        ( PROM VERSION               )
         THEN ;
+E+O+F

lwt1@aplvax (06/08/84)

Here is part 6 of the source for FORTH for the PDP-11.
Delete everything thru the "-- cut here --" line, and extract with 'sh':

	sh part1 part2 ... part7

where 'part?' are whatever you've named the files.  Note the copyright
notice at the end of README.  Please let us know how things go.  While
we can't support this software, we'll be posting bug fixes/upgrades to
net.sources as time permits.

VAX-FORTH should be 'forth'-coming {yuk-yuk} within a couple of weeks.

Have fun!



						-Lloyd W. Taylor
						 ... seismo!umcp-cs!aplvax!lwt1
---I will have had been there before, soon---

---------------------------------- cut here ----------------------------------
echo x - auto
cat >auto <<'+E+O+F'
( automated meta-compilation file )
" META1" FLOAD
" METAASM" FLOAD
" newforth" -1 CREAT CLOSE
" newforth" 2 OPEN DUP . FORTH FILED !		( object file )
0 WRN ! HOST
0 RAM   HEADS    METAMAP    METAWARN
" SYS:ASM" FLOAD
" META2" FLOAD
" SYS:SRC" FLOAD
DECIMAL 20000 CLEANUP				( allot 20000 byte dictionary )
+E+O+F
echo x - SYS:ASM
cat >SYS:ASM <<'+E+O+F'
( Copyright 1984 by The Johns Hopkins University/Applied Physics Lab.   )
( Free non-commercial distribution is *encouraged*, provided that:      )
(									)
( 	1.  This copyright notice is included in any distribution, and  )
( 	2.  You let us know that you're using it. 			)
(  									)
( Please notify: 							)
(  									)
( 	Lloyd W. Taylor 						)
( 	JHU/Applied Physics Lab 					)
( 	Johns Hopkins Road 						)
( 	Laurel, MD 20707 						)
( 	[301] 953-5000 							)
(  									)
( 	Usenet:  ... seismo!umcp-cs!aplvax!lwt1 			)
(  									)
(  									)
( Unix-FORTH was developed under NASA contract NAS5-27000 for the 	)
( Hopkins Ultraviolet Telescope, a March 1986 Space Shuttle mission.    )
( {We hope to take a peek at Halley's comet!}				)
(  									)
( Written entirely by Wizard-In-Residence John R. Hayes. 		)
(  									)	
( * Unix is a trademark of Bell Labs. 					)

( FORTH ASSEMBLY LANGUAGE SOUCE CODE ) OCTAL

( THIS IS SOURCE CODE TO BE RUN THROUGH THE METACOMPILER - METAASSEMBLER. )
( THEREFORE, THERE ARE DIFFERENCES BETWEEN THIS SOURCE CODE AND SOURCE    )
( CODE TO BE ASSEMBLED IN THE ORDINARY WAY. IN PARTICULAR, THERE IS NO    )
( IMPLICIT OR EXPLICIT SMUDGING.                                          )

 JMP 0 *$                       ( JUMP TO STARTUP; WILL BE BACKPATCHED )

LABEL vector
 MOV 0 $   IAR REG    		( MOVE ABORT TO IAR; WILL BE BACKPATCHED )
 60 TRAP   2 , vector ,
 NEXT

( VARIABLES AND DATA BUFFERS )
LABEL	rsp0 0 ,		( INITIAL VALUE OF RETURN STACK POINTER ) 
LABEL	in 0 ,			( >IN: INPUT PARSER )
LABEL	initvocab 0 ,		( INITIAL FORTH VOCABULARY )
LABEL	dp 0 ,			( END OF DICTIONARY POINTER )

	400 RAMALLOT		( 256 BYTE PARAMETER STACK )
LABEL inbuf
	DECIMAL 120 RAMALLOT 	( 120 BYTES OF INPUT BUFFER )
	OCTAL

( INNER INTERPRETER AND LOW-LEVEL RUN TIME WORDS )

CODE (:)			( CODE FOR NEXT )
 JMP IAR *)+

(    THE CODE FOR CALL IS COMPILED IN-LINE FOR COLON DEFINITIONS. )
(								  )
(    JSR IAR,*$NEXT
(								  )

CODE (;)
 MOV SP )+   IAR REG
 NEXT

(     THIS IS TRICKY CODE. ALL WORDS DEFINED BY VARIABLE, CONSTANT, OR )
( <BUILDS .. DOES> WORDS WILL HAVE SIMILAR CODE FIELDS. THEREFORE, THE )
( CODE FOR [VARIABLE], [CONSTANT], AND [DOES>] IS SHOW BELOW.          )
( EXAMPLE: CODE COMPILED FOR VARIABLE WILL BE:			       )
(     JSR IAR,*$[VARIABLE]					       )

CODE (VARIABLE)
 MOV IAR REG   PSP -(
 MOV SP )+   IAR REG
 NEXT

CODE (CONSTANT)
 MOV IAR )   PSP -(
 MOV SP )+   IAR REG
 NEXT

CODE (DOES>)
 MOV IAR )+   0 REG
 MOV IAR REG   PSP -(
 MOV 0 REG   IAR REG
 NEXT

(     BRANCHING PRIMITIVES )

CODE (LITERAL)
 MOV IAR )+   PSP -(
 NEXT

CODE BRANCH
 MOV IAR )   IAR REG
 NEXT

CODE ?BRANCH
 MOV PSP )+   0 REG
 BNE 1 FWD
 MOV IAR )   IAR REG
 JMP IAR *)+			( NEXT )
1 L: ADD 2 $   IAR REG
 NEXT

CODE EXECUTE
 JMP PSP *)+

(     FORTH-83 DO LOOPS )

CODE (DO)
 MOV PSP )+   1 REG
 MOV PSP )+   0 REG
 ADD 100000 $   0 REG		( LIMIT' := LIMIT + 8000 )
 MOV 0 REG   SP -(
 SUB 0 REG   1 REG		( IINIT' := INIT - LIMIT' )
 MOV 1 REG   SP -(
 NEXT

CODE (LOOP)
 INC SP )
 BVS 1 FWD
 MOV IAR )   IAR REG  		( LOOP BACK )
 JMP IAR *)+			( NEXT )
1 L: ADD 4 $   SP REG		( POP RETURN STACK )
 ADD 2 $   IAR REG		( SKIP LOOP ADDRESS )
 NEXT

CODE (+LOOP)
 ADD PSP )+   SP )
 BVS 1 FWD
 MOV IAR )   IAR REG  		( LOOP BACK )
 JMP IAR *)+ 			( NEXT )
1 L: ADD 4 $   SP REG		( POP RETURN STACK )
 ADD 2 $   IAR REG 		( SKIP LOOP ADDRESS )
 NEXT

CODE I
 MOV SP )   0 REG
 ADD 2 SP X(   0 REG		( I := I' + LIMIT' )
 MOV 0 REG   PSP -(
 NEXT

CODE J
 MOV 4 SP X(   0 REG
 ADD 6 SP X(   0 REG		( J := J' + LIMIT' )
 MOV 0 REG   PSP -(
 NEXT

CODE (LEAVE)
 ADD 4 $   SP REG		( POP RETURN STACK )
 MOV IAR )   IAR REG		( BRANCH PAST LOOP )
 NEXT

(	BASIC UNIX SYSTEM INTERFACE ROUTINES )

( BUFFER FOR HOLDING INDIRECT SYSTEM CALLS )
LABEL SYSBUF    0 ,		( TRAP INSTRUCTION )
		0 ,		( ARGUMENT 1 )
		0 ,		( ARGUMENT 2 )
		0 ,		( ARGUMENT 3 )

(	DATA AND CODE FOR SPAWNING OFF SUBPROCESSES )
HEX
LABEL STATUS	0 ,		( WORD FOR RECEIVING RETURN STATUS OF CHILD )
LABEL NAME	622F , 6E69 , 732F , 68 ,	( "/bin/sh" )
LABEL 0ARG	6873 , 0 ,			( "sh" )
LABEL 1ARG	632D , 0 ,			( "-c" )
LABEL ARGV	0ARG , 1ARG , 0 , 0 ,		( ARGUMENT LIST )
OCTAL

CODE SHELL			( --- )   ( SPAWN OFF INTERACTIVE SUB-SHELL )
 CLR ARGV 2+ *$			( sh WITH NO ARGUMENTS )
0 L: ( SPAWN SUB-PROCESS.  SYSTEM BELOW SHARES THIS CODE )
 2 TRAP				( FORK SYSTEM CALL )
 BR 2 FWD			( BRANCH TO CHILD PROCESS CODE )
 60 TRAP  2 , 1 ,		( IGNORE INTERRUPTS )
 MOV 0 REG   2 REG		( SAVE OLD VECTOR )
 7 TRAP				( WAIT SYSTEM CALL )
 ROR 2 REG
 BCS 1 FWD			( SKIP IF INTERRUPTS WERE IGNORED )
 60 TRAP  2 , vector ,		( ELSE, CATCH INTERRUPTS )
1 L: NEXT			( DONE )
2 L: ( CHILD )			( CHILD PROCESS CODE )
 MOV 104473 $   SYSBUF *$	( EXECE TRAP INSTRUCTION )
 MOV NAME $   SYSBUF 2+ *$	( MOVE NAME POINTER )
 MOV ARGV $   SYSBUF 4 + *$	( MOVE ARGUMENT POINTER )
 MOV rsp0 *$   SYSBUF 6 + *$	( MOVE ENVIRONMENT POINTER )
 0 TRAP	SYSBUF ,		( INDIRECT EXECE SYSTEM CALL )
 1 TRAP				( RETURN TO PARENT )

CODE SYSTEM			( ADDR[STRING] --- )
 MOV 1ARG $   ARGV 2+ *$	( MOVE POINTER TO "-c" TO ARGUMENT LIST )
 MOV PSP )+   ARGV 4 + *$	( MOVE POINTER TO COMMAND STRING TO LIST )
 BR 0 BACK			( BRANCH TO CODE TO SPAWN SUB-SHELL )

(	I/O BUFFER AND CONTROL VARIABLES
LABEL BLOCK	1000 RAMALLOT	( 512 BYTE DISK BUFFER )
LABEL SIZE	0 ,		( SIZE IN BYTES )
LABEL INDEX	0 ,		( CURRENT OFFSET INTO BLOCK )
LABEL FILED	0 ,		( FILE DESCRIPTOR OF FILE THAT OWNS BLOCK )

(	FILE POSITION TABLE: EACH SLOT HAS A 32 BIT FILE OFFSET. FILE )
(	DESCRIPTOR IS OFFSET INTO TABLE. THERE ARE 15 SLOTS.          )
LABEL FILEPOS	0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,
		0 , 0 ,

( SUBROUTINE GETC: HANDLES ALL INPUT AND DOES BUFFERING )
(	INPUT: FILE DESCRIPTOR IN R0                    )
(	OUTPUT: CHARACTER OF EOF IN R0			)
(	SIDE EFFECTS: R0 AND R1 DESTROYED		)
LABEL GETC
 CMP 0 REG   FILED *$		( IS THIS FILE CURRENTLY BUFFERED? )
 BEQ 0 FWD			( IS SO, DO NOT NEED TO TO SEEK )
 MOV 0 REG   FILED *$		( SAVE NEW FD IN BUFFER DESCRIPTOR )
 MOV SIZE *$   INDEX *$		( INDICATE THAT BUFFER IS EMPTY )
 MOV 104423 $   SYSBUF *$	( MOVE LSEEK TRAP INSTRUCTION TO SYSBUF )
 ASL 0 REG   ASL 0 REG		( MULTIPLY BY 4 TO INDEX INTO POSITION TABLE )
 MOV FILEPOS 0 X(   SYSBUF 2+ *$	( HIGH OFFSET WORD )
 MOV FILEPOS 2+ 0 X(   SYSBUF 4 + *$	( LOW OFFSET WORD )
 CLR SYSBUF 6 + *$		( OFFSET FROM BEGINNING OF FILE )
 MOV FILED *$   0 REG		( FILE DESCRIPTOR IN R0 )
 0 TRAP   SYSBUF ,		( LSEEK SYSTEM CALL )
 MOV FILED *$   0 REG		( RESTORE FD SINCE CALL DESTROYED R0, R1 )
0 L: MOV 2 REG   SP -(		( SAVE R2 )
 MOV INDEX *$   2 REG		( R2 IS INDEX )
 CMP 2 REG   SIZE *$
 BLT 1 FWD			( IF THERE IS STILL DATA IN BUFFER, USE IT )
 3 TRAP   BLOCK ,  1000 ,       ( READ UP TO 512 BYTES )
 BCS 2 FWD			( BRANCH IF ERROR )
 MOV 0 REG   SIZE *$		( SAVE SIZE OF BLOCK )
 BEQ 3 FWD			( BRANCH IF EOF )
 CLR 2 REG			( RESET INDEX )
1 L: MOV BLOCK 2 X(   0 REG BYTE
				( GET NEXT CHARACTER )
 BIC 17400 $   0 REG		( MASK OFF HIGH BYTE )
 INC 2 REG
 MOV 2 REG   INDEX *$		( UPDATE INDEX )
 MOV FILED *$   2 REG		( REUSE R2 TO HOLD FILE DESCRIPTOR )
 ASL 2 REG   ASL 2 REG		( MULTIPLY BY 4 TO INDEX INTO POSITION TABLE )
 ADD 1 $   FILEPOS 2+ 2 X(	( ADD ONE TO CURRENT FILE POSITION )
 ADC FILEPOS 2 X(
 BR 4 FWD
2 L: 3 L:
 MOV -1 $   0 REG		( RETURN EOF ON ERROR )
4 L: MOV SP )+   2 REG		( RESTORE R2 )
 RTS PC REG-ONLY 

CODE OPEN			( ADDR[STRING] MODE --- FD )
 MOV 104405 $   SYSBUF *$	( MOVE TRAP 5 INSTRUCTION TO INDIR AREA )
 MOV PSP )+   SYSBUF 4 + *$	( MOVE MODE )
 MOV PSP )   SYSBUF 2+ *$	( MOVE ADDR[STRING] )
 0 TRAP   SYSBUF ,		( OPEN SYSTEM CALL )
 BCC 1 FWD
 MOV -1 $   PSP )		( ERROR, NEGATIVE FILE DESCRIPTOR RETURNED )
 BR 2 FWD
1 L: MOV 0 REG   PSP )		( RETURN FILE DESCRIPTOR )
 ASL 0 REG   ASL 0 REG		( MULTIPLY BY 4 IN INDEX INTO POSITION TABLE )
 CLR FILEPOS 0 X(		( INITIALIZE FILE POSITION TO ZERO )
 CLR FILEPOS 2+ 0 X(
2 L: NEXT

CODE CREAT			( ADDR[STRING] PMODE --- FD )
 MOV 104410 $   SYSBUF *$	( MOVE TRAP 8 INSTRUCTION TO INDIR AREA )
 MOV PSP )+   SYSBUF 4 + *$	( MOVE PMODE )
 MOV PSP )   SYSBUF 2+ *$	( MOVE ADDRESS OF FILE NAME )
 0 TRAP SYSBUF ,		( CREAT SYSTEM CALL )
 BCC 1 FWD
 MOV -1 $   PSP )		( ERROR, NEGATIVE FILE DESCRIPTOR RETURNED )
 BR 2 FWD
1 L: MOV 0 REG   PSP )		( RETURN FILE DESCRIPTOR )
 ASL 0 REG   ASL 0 REG		( MULTIPLY BY 4 TO INDEX INTO POSITION TABLE )
 CLR FILEPOS 0 X(		( INITIALIZE FILE POSITION TO ZERO )
 CLR FILEPOS 2+ 0 X(
2 L: NEXT

CODE CLOSE			( FD --- )
 MOV 104406 $   SYSBUF *$	( MOVE TRAP 6 INSTRUCTION TO INDIR AREA )
 MOV PSP )+   0 REG		( FILE DESCRIPTOR )
 0 TRAP   SYSBUF ,		( CLOSE SYSTEM CALL )
 NEXT

CODE FEXPECT			( FD ADDR COUNT --- ACTCOUNT )
 MOV 2 PSP X(   2 REG		( BUFFER ADDRESS )
 MOV PSP )+   3 REG		( COUNT )
 BEQ 3 FWD			( DO NOTHING IF COUNT IS ZERO )
1 L: MOV 2 PSP X(   0 REG	( FILE DESCRIPTOR )
 JSR PC REG-ONLY   GETC *$	( GET NEXT CHARACTER )
 CMP 0 REG   -1 $		( EOF? )
 BEQ 4 FWD			( LEAVE LOOP ON EOF )
 CMP 0 REG   011 $ BYTE		( TAB ? )
 BNE 2 FWD
 MOV 040 $   0 REG BYTE		( CHANGE TABS TO BLANKS )
2 L: MOV 0 REG   2 )+ BYTE	( SAVE CHARACTER )
 CMP 0 REG   012 $ BYTE		( NEWLINE? )
 BEQ 5 FWD
 1 3 SOB 			( DECREMENT COUNT AND CONTINUE IF NON-ZERO )
3 L: 4 L: 5 L:
 SUB PSP )+   2 REG		( COMPUTE ACTUAL NUMBER OF CHARACTERS READ )
 MOV 2 REG   PSP )		( RETURN ACTUAL NUMBER )
 NEXT

CODE READ			( FD ADDR COUNT --- ACTCOUNT )
 MOV 2 PSP X(   2 REG		( BUFFER ADDRESS )
 MOV PSP )+   3 REG		( COUNT )
 BEQ 2 FWD			( DO NOTHING IF COUNT IS ZERO )
1 L: MOV 2 PSP X(   0 REG	( FILE DESCRIPTOR )
 JSR PC REG-ONLY   GETC *$	( GET NEXT CHARACTER )
 CMP 0 REG   -1 $		( EOF? )
 BEQ 3 FWD			( LEAVE LOOP ON EOF )
 MOV 0 REG   2 )+ BYTE		( SAVE CHARACTER )
 1 3 SOB 			( DECREMENT COUNT AND CONTINUE IF NON-ZERO )
2 L: 3 L:
 SUB PSP )+   2 REG		( COMPUTE ACTUAL NUMBER OF CHARACTERS READ )
 MOV 2 REG   PSP )		( RETURN ACTUAL NUMBER )
 NEXT

CODE WRITE			( ADDR COUNT FD --- ACTCOUNT )
 MOV 104404 $   SYSBUF *$	( MOVE TRAP INSTRUCTION TO INDIR AREA )
 MOV PSP )+   0 REG		( FILE DESCRIPTOR )
 MOV PSP )+   SYSBUF 4 + *$	( COUNT )
 MOV PSP )   SYSBUF 2+ *$	( ADDRESS )
 0 TRAP   SYSBUF ,		( WRITE SYSTEM CALL )
 BCC 1 FWD
 MOV -1 $   0 REG		( ERROR FLAG )
1 L: MOV 0 REG   PSP ) 		( RETURN ACTUAL COUNT )
 NEXT

CODE SEEK			( FD OFFSETL OFFSETH --- )
 MOV 4 PSP X(   0 REG		( FILE DESCRIPTOR )
 CMP 0 REG   FILED *$		( IF SEEK ON CURRENTLY BUFFERED FILE )
 BNE 1 FWD
 MOV -1 $   FILED *$		( FLAG BUFFER AS INVALID )
1 L: ASL 0 REG   ASL 0 REG	( MULTIPLY BY 4 TO INDEX INTO POSITION TABLE )
 MOV PSP )   FILEPOS 0 X(	( HIGH OFFSET INTO FILE POSITION TABLE )
 MOV 2 PSP X(   FILEPOS 2+ 0 X( ( LOW OFFSET INTO FILE POSITION TABLE )
 MOV 104423 $   SYSBUF *$	( MOVE SEEK TRAP INSTRUCTION TO SYSBUF )
 MOV PSP )+   SYSBUF 2+ *$	( MOVE HIGH OFFSET )
 MOV PSP )+   SYSBUF 4 + *$	( MOVE LOW OFFSET )
 CLR SYSBUF 6 + *$		( OFFSET FROM BEGINNING OF FILE )
 MOV PSP )+ 0 REG		( FILE DESCRIPTOR IN R0 )
 0 TRAP   SYSBUF ,		( SEEK SYSTEM CALL )
 NEXT

CODE TERMINATE 			( --- )
 CLR 0 REG			( RETURN GOOD STATUS )
 1 TRAP				( EXIT SYSTEM CALL )
				( SHOULD NOT EXECUTE BEYOND TRAP )

CODE (FIND)			( ADDR[NAME] ADDR[VOCAB] --- 0 <OR> NFA )
 MOV PSP )+   0 REG
 BEQ 3 FWD			( EMPTY VOCABULARY? )
 MOV PSP )   3 REG		( POINTER TO NAME )
 MOV 3 )+   2 REG		( NAME LS )
 MOV 3 )   3 REG		( NAME MS )
1 L: MOV 0 )   1 REG
 BIC 200 $   1 REG		( CLEAR IMMEDIATE BIT )
 CMP 1 REG   2 REG		( COMPARE LS )
 BNE 2 FWD
 CMP 2 0 X( 3 REG		( COMPARE MS )
 BEQ 4 FWD
2 L: MOV 4 0 X(   0 REG		( NEXT LINK )
 BNE 1 BACK			( ZERO LINK? )
3 L: 4 L:
 MOV 0 REG   PSP )
 NEXT

CODE WORD			( DEL --- ADDR )
 MOV PSP )   0 REG		( DELIMITER )
 MOV in *$   1 REG		( >IN )
 ADD inbuf $   1 REG            ( R1 HAS ADDRESS OF NEXT BYTE IN STREAM )
 MOV dp *$   2 REG		( HERE )
 MOV 2 REG   PSP )		( RETURN HERE, ADDRESS OF STRING )
1 L: CMP 0 REG   1 )+ BYTE	( SKIP DELIMITERS )
 BEQ 1 BACK
 DEC 1 REG			( BACK UP ONE )
 MOV 1 REG   3 REG
2 L: CMP 0 REG   3 ) BYTE	( DELIMITER? )
 BEQ 3 FWD
 CMP 012 $   3 ) BYTE		( NEWLINE? )
 BEQ 4 FWD
 INC 3 REG			( SKIP UNTIL END OF WORD )
 BR 2 BACK
3 L: 4 L:
 SUB 1 REG   3 REG		( R3 HAS LENGTH )
 MOV 3 REG 2 )+ BYTE 		( SAVE COUNT )
 BEQ 6 FWD			( SKIP IF EOL, I.E. ZERO LENGTH )
5 L: MOV 1 )+   2 )+ BYTE	( MOVE CHARACTERS TO HERE )
 5 3 SOB
6 L: CMP 012 $   1 ) BYTE	( IF NOT NEWLINE )
 BEQ 7 FWD
 INC 1 REG			( SKIP DELIMITER )
7 L: SUB inbuf $   1 REG        ( >IN IS OFFSET FROM START OF TIB )
 MOV 1 REG   in *$		( UPDATE >IN SCANNER )
 MOV 040 $   2 )+ BYTE		( ADD BLANK TO END OF WORD
 NEXT 
 
(     STACK PRIMITIVES )
 
CODE !				( DATA ADDR --- )
 MOV PSP )+   0 REG
 MOV PSP )+   0 )
 NEXT

CODE !SP			( ADDR --- )   ( SET ADDRESS OF STACK TOP. )
 MOV PSP )   PSP REG
 NEXT

CODE +				( N1 N2 --- N1+N2 )
 ADD PSP )+   PSP )
 NEXT

CODE +!				( DATA ADDR --- )
 MOV PSP )+   0 REG
 ADD PSP )+   0 )
 NEXT

CODE -				( N1 N2 --- N1-N2 )
 SUB PSP )+   PSP )
 NEXT

CODE -1				( --- -1 )
 MOV -1 $   PSP -(
 NEXT

CODE 0				( --- 0 )
 CLR PSP -(
 NEXT

CODE 0<				( N --- T/F )
 CLR 0 REG
 TST PSP )
 BPL 1 FWD
 MOV -1 $   0 REG
1 L: MOV 0 REG   PSP )
 NEXT

CODE 0=				( N --- T/F )
 CLR 0 REG
 TST PSP )
 BNE 1 FWD
 MOV -1 $   0 REG
1 L: MOV 0 REG   PSP )
 NEXT

CODE 1				( --- 1 )
 MOV 1 $   PSP -(
 NEXT

CODE 1+				( N --- N+1 )
 INC PSP )
 NEXT

CODE 1-				( N --- N-1 )
 DEC PSP )
 NEXT

CODE 2				( --- 2 )
 MOV 2 $   PSP -(
 NEXT

CODE 2+				( N --- N+2 )
 ADD 2 $   PSP )
 NEXT

CODE 2-				( N --- N-2 )
 SUB 2 $   PSP )
 NEXT

CODE 2*				( N --- 2*N )
 ASL PSP )
 NEXT

CODE 2/				( N --- N/2 )
 ASR PSP )
 NEXT

CODE <				( N1 N2 --- T/F )
 CLR 0 REG
 CMP PSP )+   PSP )
 BLE 1 FWD
 MOV -1 $   0 REG
1 L: MOV 0 REG   PSP )
 NEXT

CODE =				( N1 N2 --- T/F )
 CLR 0 REG
 CMP PSP )+ PSP )
 BNE 1 FWD
 MOV -1 $   0 REG
1 L: MOV 0 REG   PSP )
 NEXT

CODE >				( N1 N2 --- T/F )
 CLR 0 REG
 CMP PSP )+ PSP )
 BGE 1 FWD
 MOV -1 $   0 REG
1 L: MOV 0 REG   PSP )
 NEXT

CODE >R				( N1 --- )
 MOV PSP )+ SP -(
 NEXT

CODE @				( ADDR --- DATA )
 MOV 0 PSP *X(   PSP )
 NEXT

CODE @SP			( --- ADDR )   ( RETURN STACK POINTER )
 MOV PSP REG   0 REG
 MOV 0 REG   PSP -(
 NEXT

CODE AND			( N1 N2 --- N1 & N2 )
 MOV PSP )+   0 REG
 COM 0 REG
 BIC 0 REG   PSP )
 NEXT

CODE C!				( BYTE ADDR --- )
 MOV PSP )+   0 REG
 MOV PSP )+   1 REG
 MOV 1 REG   0 )   BYTE
 NEXT

CODE C@				( ADDR --- BYTE )
 MOV 0 PSP *X(   0 REG   BYTE
 BIC 177400 $   0 REG
 MOV 0 REG   PSP )
 NEXT

CODE CMOVE			( SRC DEST UCOUNT --- )
 MOV PSP )+   2 REG
 BEQ 2 FWD			( DO NOTHING IF LENGTH ZERO )
 MOV PSP )+   0 REG		( DESTINATION )
 MOV PSP )+   1 REG		( SOURCE )
1 L: MOV 1 )+   0 )+ BYTE	( MOVE BYTE )
 1 2 SOB
 BR 3 FWD
2 L: ADD 4 $ PSP REG		( POP TWO STACK ARGS )
3 L: NEXT

CODE D+				( D1L D1H D2L D2H --- [D1+D2]L [D1+D2]H )
 MOV PSP )+   0 REG
 ADD PSP )+   2 PSP X(
 ADC PSP )
 ADD 0 REG   PSP )
 NEXT

CODE D<				( D1L D1H D2L D2H --- T/F )
 CLR 0 REG
 CMP PSP )+   2 PSP X(
 BLT 2 FWD
 BNE 1 FWD
 CMP PSP )   4 PSP X(
 BLE 3 FWD
1 L: MOV -1 $   0 REG
2 L: 3 L:
 ADD 4 $   PSP REG
 MOV 0 REG   PSP )
 NEXT

CODE DNEGATE			( D1L D1H --- [-D1]L [-D1]H )
 COM PSP )
 COM 2 PSP X(
 ADD 1 $   2 PSP X(
 ADC PSP )
 NEXT

CODE DROP			( N --- )
 ADD 2 $   PSP REG
 NEXT

CODE DUP			( N --- N N )
 MOV PSP )   PSP -(
 NEXT

CODE M* 			( S1 S2 --- [S1*S2]L [S1*S2]H )
 MOV PSP )   0 REG
 MUL 0 REG-ONLY   2 PSP X(
 MOV 1 REG   2 PSP X(		( LOW RESULT )
 MOV 0 REG   PSP )		( HIGH RESULT )
 NEXT

CODE M/ 			( SDL SDH DIVISOR --- SREM SQUOT )
 MOV PSP )+   2 REG		( R2 HAS DIVISOR )
 MOV PSP )   0 REG		( R0 HAS HIGH DIVIDEND )
 MOV 2 PSP X(   1 REG		( R1 HAS LOW DIVIDEND )
 MOV 2 REG   3 REG
 EXOR 0 REG-ONLY   3 REG	( R3 HAS SIGN )
 DIV 0 REG-ONLY   2 REG		( DIVIDE BY R2 )
 TST 3 REG
 BPL 1 FWD			( BRANCH IF SIGN IS NOT NEGATIVE )
 TST 1 REG
 BEQ 2 FWD			( BRANCH IF REMAINDER IS ZERO )
 DEC 0 REG			( SUBTRACT ONE FROM QUOTIENT )
 ADD

lwt1@aplvax (06/08/84)

Here is part 7 of the source for FORTH for the PDP-11.
Delete everything thru the "-- cut here --" line, and extract with 'sh':

	sh part1 part2 ... part7

where 'part?' are whatever you've named the files.  Note the copyright
notice at the end of README.  Please let us know how things go.  While
we can't support this software, we'll be posting bug fixes/upgrades to
net.sources as time permits.

VAX-FORTH should be 'forth'-coming {yuk-yuk} within a couple of weeks.

Have fun!



						-Lloyd W. Taylor
						 ... seismo!umcp-cs!aplvax!lwt1
---I will have had been there before, soon---

---------------------------------- cut here ----------------------------------
echo x - SYS:SRC
cat >SYS:SRC <<'+E+O+F'
( HIGH LEVEL FORTH DEFINITIONS ) HEX

( SYSTEM CONSTANTS AND VARIABLES )

inbuf     CONSTANT TIB 			( START OF TEXT INPUT BUFFER )
inbuf     CONSTANT SP0			( TOP OF PARAMETER STACK AREA )
dp        CONSTANT DP			( CURRENT DICTIONARY POINTER )
in        CONSTANT >IN			( TEXT SCANNER )
initvocab CONSTANT INITVOCAB		( INITIAL FORTH VOCABULARY )
	  VARIABLE WRN			( ENABLE 'NOT UNIQUE' WARNINGS )
	  VARIABLE STATE                ( INTERPRETATION STATE )
	  VARIABLE BASE			( BASE HEX )
	  VARIABLE CURRENT		( CURRENT VOCABULARY )
	  VARIABLE CONTXT		( CONTEXT VOCABULARY )
          VARIABLE CLUE			( USED FOR COMPILING LEAVE )

0         CONSTANT STDIN		( STANDARD INPUT FILE DESCRIPTOR )
1 	  CONSTANT STDOUT		( STANDARD OUTPUT FILE DESCRIPTOR )
0A	  CONSTANT EOL			( END OF LINE )
-1 	  CONSTANT TRUE			( TRUE )
0	  CONSTANT FALSE		( FALSE )

( CODE EXTENSIONS: THESE ARE LOW LEVEL WORDS THAT MAY BE CANDIDATES )
( FOR REWRITING AS CODE DEFINTIONS.                                 )

: ?DUP   DUP IF DUP THEN ;		( N --- N N <OR> 0 )

: -ROT   ROT ROT ;			( N1 N2 N3 --- N3 N1 N2 )

: *   UM* DROP ;			( N1 N2 --- N1*N2 ) ( SIGNED MULTIPLY )
  
: 2DUP   OVER OVER ;			( N1 N2 --- N1 N2 N1 N2 )

: S->D   DUP 0< ;			( N1 --- DL DH )   ( SIGN EXTEND )

: +-   0< IF NEGATE THEN ;		( N1 N2 --- SIGN[N2]*N1 )

: D+-   0< IF DNEGATE THEN ;		( D1L D1H N1 --- D2L D2H )

: ABS   DUP +- ;			( N --- |N| )

: DABS   DUP D+- ;			( D --- |D| )

: 2DROP   DROP DROP ;			( N1 N2 --- )

: 0>    0 > ;				( N --- T/F )
 
: MAX   2DUP < IF SWAP THEN DROP ;	( N1 N2 --- MAX[N1,N2] )

: MIN   2DUP > IF SWAP THEN DROP ;	( N1 N2 --- MIN[N1,N2] )

: <>   = NOT ;				( N1 N2 --- T/F )

( UNSIGNED MULTIPLCATION AND DIVISITON OPERATORS )

: UM*M					( UL UH MUL --- UL' UH' )
 SWAP OVER UM* DROP >R UM* 0 R> D+ ;

: M/MMOD				( DL DH DIV --- REM QUOTL QUOTH )
 >R 0 R@ UM/ R> SWAP >R UM/ R> ;

: UM/MOD				( DL DH DIV --- REM QUOT )
 M/MMOD DROP ;

( SIGNED MULTIPLICATION AND DIVISION OPERATORS )
 
: /MOD					( N1 DIV --- REM QUOT )
 >R S->D R> M/ ;

: /					( N DIV --- DIVIDEND )
 /MOD SWAP DROP ;

: MOD					( N DIV --- MOD )
 /MOD DROP ;

: */MOD					( N MUL DIV --- REM QUOT )
 >R M* R> M/ ;

: */					( N MUL DIV --- QUOT )
 */MOD SWAP DROP ;

: DEPTH                                 ( --- N )   ( RETURN DEPTH OF STACK )
					( IN WORDS NOT COUNTING N.          )
 @SP SP0 SWAP - 2/ ;

: PICK					( N1 --- N2 )   ( N2 IS A COPY OF THE )
					( N1TH STACK ITEM NOT COUNTING N1.    )
					( 0 PICK IS EQUIVALENT TO DUP.	      )
 2* @SP + 2+ @ ;

: FILL					( ADDR N BYTE --- )
 SWAP ?DUP IF
    >R OVER C!
    DUP 1+ R> 1- CMOVE
 ELSE 2DROP
 THEN ;

: CMOVE>				( ADDR1 ADDR2 U --- )   ( MOVE U BYTES )
					( FROM ADDR1 TO ADDR2. STARTS MOVING   )
					( HIGH ADDRESSED CHARACTERS FIRST.     )
 ?DUP IF
    DUP >R + 1- SWAP DUP R> + 1-
    DO I C@ OVER C! 1- -1 +LOOP
 ELSE DROP
 THEN DROP ;

: ROLL					( <'N' VALUES> N --- <'N' VALUES> )
					( THE NTH STACK ITEM NOT COUNTING )
					( N ITSELF IS TRANSFERRED TO THE  )
					( TOP OF THE STACK, MOVING THE RE-)
					( MAINING VALUES INTO THE VACATED )
					( POSITION. 0 ROLL IS A NOP.      )
 DUP >R PICK
 @SP DUP 2+ R> 1+ 2* CMOVE> DROP ;

: TOGGLE				( ADDR BITS --- )    ( TOGGLE THE IN- )
					( DICATED BITS AT ADDR.               )
 OVER @ XOR SWAP ! ;

: 2!					( DL DH ADDR --- )   ( M[ADDR]<--DH, )
					( M[ADDR+2]<--DL.                    )
 SWAP OVER ! 2+ ! ;

: 2@					( ADDR --- DL DH )   ( DH<--M[ADDR], )
					( DL<--M[ADDR+2].		     )
 DUP 2+ @ SWAP @ ;

: HEX   10 BASE ! ;			( SET BASE TO HEX )
: DECIMAL   A BASE ! ;			( SET BASE TO DECIMAL )
: OCTAL   8 BASE ! ;			( SET BASE TO OCTAL )

( COMPILING WORDS )

: HERE   DP @ ;				( --- ADDR )

: PAD   HERE 50 + ;			( --- ADDR )

: LATEST   CURRENT @ @ ;		( --- ADDR )   ( RETURNS ADDR OF MOST )
					( RECENTLY COMPILED NAME FIELD.       )

: ALLOT   DP +! ;			( BYTECOUNT --- )   ( ALLOT DICTIONARY )

: ,   HERE ! 2 ALLOT ;			( WORD --- )   ( ADD TO DICTIONARY )

: IMMEDIATE   LATEST 80 TOGGLE ;	( --- )   ( MAKE MOST RECENTLY COM- )
					( PILED WORD IMMEDIATE.             )

: SMUDGE   LATEST 40 TOGGLE ;		( --- )   ( SMUDGE MOST  RECENTLY )
					( COMPILED WORD.                  )

: COMPILE
 R> DUP @ , 2 + >R ;

: <MARK					( --- ADDR )   ( USED AS DESTINATION )
					( OF BACKWARD BRANCH.                )
 HERE ;

: <RESOLVE				( ADDR --- )   ( RESOLVE BACKWARD )
					( BRANCH.		          )
 , ;

: >MARK					( --- ADDR )   ( SOURCE OF FORWARD )
					( BRANCH.			   )
 HERE 2 ALLOT ;

: >RESOLVE				( ADDR --- )   ( RESOLVE FORWARD )
					( BRANCH.			 )
 HERE SWAP ! ;

: >>RESOLVE				( OLDLINK --- )   ( RESOLVE A CHAIN )
					( OF FORWARD BRANCHES.		    )
 HERE SWAP BEGIN
    DUP WHILE
    OVER SWAP DUP @ -ROT !
 REPEAT 2DROP ;

: IF					( --- ADDR )
 COMPILE ?BRANCH >MARK ; IMMEDIATE METASMUDGE

: THEN					( ADDR --- )
 >RESOLVE ; IMMEDIATE METASMUDGE

: ELSE					( ADDR --- ADDR' )
 COMPILE BRANCH >MARK
 SWAP >RESOLVE ; IMMEDIATE METASMUDGE

: BEGIN					( --- ADDR )
 <MARK ; IMMEDIATE METASMUDGE

: UNTIL					( ADDR --- )
 COMPILE ?BRANCH <RESOLVE ; IMMEDIATE METASMUDGE

: AGAIN					( ADDR --- )
 COMPILE BRANCH <RESOLVE ; IMMEDIATE METASMUDGE

: WHILE					( --- ADDR )
 COMPILE ?BRANCH >MARK ; IMMEDIATE METASMUDGE

: REPEAT				( ADDR1 ADDR2 --- )
 COMPILE BRANCH SWAP <RESOLVE >RESOLVE ; IMMEDIATE METASMUDGE

: SEL
 0 ; IMMEDIATE METASMUDGE

: << 					( OLDLINK --- OLDLINK )
 COMPILE DUP ; IMMEDIATE METASMUDGE

: =>					( --- IFADDR )
 COMPILE ?BRANCH >MARK 
 COMPILE DROP ; IMMEDIATE METASMUDGE

: ==>					( --- IFADDR )
 COMPILE =
 COMPILE ?BRANCH >MARK
 COMPILE DROP ; IMMEDIATE METASMUDGE

: >>					( OLDLINK IFADDR --- NEWLINK )
 COMPILE BRANCH SWAP ,
 >RESOLVE 
 HERE 2- ; IMMEDIATE METASMUDGE 

: ENDSEL				( OLDLINK --- )
 COMPILE DROP >>RESOLVE ; IMMEDIATE METASMUDGE

( THE CODE WORDS [DO], [LOOP], AND [+LOOP] IMPLEMENT FORTH-83 DO..LOOPS. )
( [LEAVE] IS A FORTH-83 LEAVE. CLUE IS USED TO IMPLEMENT LEAVE. 	 )

: DO					( --- CLUE HERE )
 COMPILE (DO) CLUE @ 0 CLUE ! <MARK ; IMMEDIATE METASMUDGE

: LOOP					( CLUE HERE --- )
 COMPILE (LOOP) <RESOLVE
 CLUE @ >>RESOLVE
 CLUE ! ; IMMEDIATE METASMUDGE

: +LOOP					( CLUE HERE --- )
 COMPILE (+LOOP) <RESOLVE
 CLUE @ >>RESOLVE
 CLUE ! ; IMMEDIATE METASMUDGE

: LEAVE					( --- )
 COMPILE (LEAVE) HERE CLUE @ , CLUE ! ; IMMEDIATE METASMUDGE

: EXIT					( --- )   ( EXIT THE CURRENT )
					( COLON DEFINTION. CAN'T BE  )
					( USED INSIDE A LOOP.        )
 R> DROP ;

: [   0 STATE ! ; IMMEDIATE METASMUDGE
: ]   1 STATE ! ;

: (   29 WORD DROP ; IMMEDIATE METASMUDGE

( I/O WORDS: MOST OF THE I/O IS WRITTEN IN ASSEMBLY LANGUAGE )

VARIABLE OUTTABLE                       ( TABLE OF FILE DESCRIPTORS USED  )
					( BY TYPE.			  )
 STDOUT OUTTABLE ! 0 , 0 , 0 ,		( ZERO INDICATES NO FILE )

: FOREACHOUTPUT				( --- ADDR2 ADDR1 )   ( RETURNS UPPER)
					( AND LOWER ADDRESSES OF OUTPUT TABLE)
					( IN FORMAT SUITABLE FOR DO.	     )
 OUTTABLE 8 + OUTTABLE ;

: OUTPUT				( FD --- )   ( ADD THE FILE DESCRIP- )
					( TOR TO THE OUTPUT TABLE IF THERE IS)
					( ROOM.				     )
 FOREACHOUTPUT DO
    I @ 0= IF DUP I ! LEAVE THEN
 2 +LOOP DROP ;

: SILENT				( FD --- )   ( DELETE THE FILE DES- )
					( CRIPTOR FROM THE OUTPUT TABLE.    )
 FOREACHOUTPUT DO
    DUP I @ = IF 0 I ! THEN
 2 +LOOP DROP ;

: TYPE					( ADDR COUNT --- )   ( SEND COUNT )
					( BYTES TO EACH FILE IN THE OUTPUT)
					( TABLE.			  )
 FOREACHOUTPUT DO
    I @ ?DUP IF >R 2DUP R> WRITE DROP THEN
 2 +LOOP 2DROP ;

: EMIT					( CHAR --- )   ( SEND CHARACTER TO )
					( STDOUT.			   )
 @SP 1 TYPE DROP ;

: CR					( --- )   ( SEND NEWLINE CHARACTER )
 EOL EMIT ;

: FQUERY				( FD --- ACTCOUNT )   ( READ ONE   )
					( LINE, UP TO 120 CHARACTERS, FROM )
					( INDICATED FILE. ACTCOUNT IS      )
					( ACTUAL NUMBER OF CHARACTERS READ.)
					( WILL BE ZERO ON END OF FILE.     )
0 >IN ! TIB 78 FEXPECT ;

: COUNT					( ADDR --- ADDR+1 LEN )
 DUP 1+ SWAP C@ ;

: ALIGN					( ADDR --- ADDR' )   ( FORCE WORD )
					( ALIGNMENT OF AN ADDRESS.        )
 1+ 2/ 2* ;

: ,WORD					( DEL --- )   ( ADD TEXT DELIMITED BY )
					( DEL INTO DICTIONARY. 		      )
 WORD C@ 1+ ALIGN ALLOT ;

: (.")					( --- )
 R> COUNT 2DUP TYPE + ALIGN >R ;

: ."
 COMPILE (.") 22 ,WORD ; IMMEDIATE METASMUDGE

FORTH : ."
	 META (.") FORTH
	 22 WORD DUP COUNT + ALIGN
	 SWAP DO
	    I @ HOST , 
	 2 +LOOP ; HOST-->META

: SPACE					( --- )   ( EMIT SPACE )
 20 EMIT ;

: SPACES 				( COUNT --- )
 0 MAX ?DUP IF 0 DO SPACE LOOP THEN ;

: -TRAILING				( ADDR N1 --- ADDR N2 )   ( THE CHAR- )
					( ACTER COUNT OF A STRING BEGINNING   )
					( AT ADDR IS ADJUSTED TO REMOVE TRAIL-)
					( ING BLANKS. IF N1 IS ZERO, THEN N2  )
					( IS ZERO. IF THE ENTIRE STRING CON-  )
					( SISTS OF SPACES, THEN N2 IS ZERO.   )
 DUP IF
    DUP 0 DO
       2DUP + 1- C@ 20 - IF LEAVE ELSE 1- THEN
    LOOP
 THEN ;

: STRING				( ADDR[COUNTED_STRING] ---           )
					(		    ADDR[UNIX_STRING )
 COUNT DUP >R PAD SWAP CMOVE 0 PAD R> + C! PAD ; 

: "					( --- ADDR[STRING] )
 22 WORD STRING ;

: ("")					( --- ADDR[STRING] )
 R> DUP COUNT + ALIGN >R STRING ;

: ""
 COMPILE ("") 22 ,WORD ; IMMEDIATE METASMUDGE

( DEFINING WORDS )

: CFIELD				( NFA --- CFA )
 6 + ;

: NFIELD				( CFA --- NFA )
 6 - ;

: -IMM					( NFA --- CFA N )   ( GIVEN A NAME )
					( FIELD ADDRESS, CONVERTS TO CODE  )
					( FIELD ADDRESS AND RETURNS A FLAG )
					( N WHICH IS -1 IF THE WORD IS NON-)
					( IMMEDIATE AND 1 IF THE WORD IS   )
					( IMMEDIATE.			   )
 DUP CFIELD -1 ROT C@ 80 AND IF NEGATE THEN ;

: FIND					( ADDR[NAME] --- ADDR2 N )   ( TRIES )
					( TO FIND NAME IN THE DICTIONARY.    )
					( ADDR2 IS ADDR[NAME] AND N IS 0 IF  )
					( NOT FOUND. IF THE NAME IS FOUND,   )
					( ADDR2 IS THE CFA. N IS -1 IF THE   )
					( WORD IS NON-IMMEDIATE AND 1 IF IT  )
					( IS IMMEDIATE.			     )
 DUP CONTXT @ @ (FIND)			( LOOKUP IN CONTEXT VOCABULARY )
 ?DUP IF 				( ADDR[NAME] NFA )
    SWAP DROP -IMM
 ELSE
    DUP LATEST (FIND)			( LOOKUP IN CURRENT VOCABULARY )
    ?DUP IF
       SWAP DROP -IMM
    ELSE
       0				( NOT FOUND )
    THEN
 THEN ;

: '					( --- 0 <> CFA )   ( MOVES NEXT )
					( WORD IN INPUT STREAM TO HERE  )
					( AND LOOKS UP IN CONTEXT AND   )
					( CURRENT VOCABULARIES. RETURNS )
					( CFA IF FOUND, ZERO OTHERWISE. )
 HERE 4 20 FILL				( BLANK HERE AREA )
 20 WORD FIND 0= IF DROP 0 THEN ;

: HEADER				( --- )   ( CREATE DICTIONARY )
					( HEADER FOR NEXT WORD IN     )
					( INPUT STREAM.    	      )
 ' IF
    WRN @ IF
       HERE COUNT TYPE ."  isn't unique" CR
    THEN
 THEN
 HERE 4 ALLOT LATEST , CURRENT @ ! ;

: CALL					( --- )   ( COMPILE OPCODE FOR )
					( JSR IAR,*$---		       )
 091F , ;

: :
 CURRENT @ CONTXT !			( SET CONTEXT TO CURRENT )
 HEADER CALL COMPILE (:) ] SMUDGE ;

: ;
 COMPILE (;) SMUDGE 0 STATE ! ; IMMEDIATE METASMUDGE

: VARIABLE
 HEADER CALL COMPILE (VARIABLE) 0 , ;

: CONSTANT
 HEADER CALL COMPILE (CONSTANT) , ;

: 2VARIABLE
 VARIABLE 0 , ;

: DOES>
 R> LATEST CFIELD 4 + ! ;

: CREATE
 HEADER CALL COMPILE (DOES>) 0 , DOES> ;

: VOCABULARY
 CREATE HERE 2+ , LATEST ,
 DOES> @ CONTXT ! ;

: DEFINITIONS
 CONTXT @ CURRENT ! ;

: FORTH
 INITVOCAB CONTXT ! ; IMMEDIATE

( FORMATTED OUTPUT ) 

VARIABLE HLD

: HOLD					( CHAR --- )  ( ADD CHARACTER TO )
					( FRONT OF STRING POINTED TO BY  )
					( HLD. 			         )
 -1 HLD +! HLD @ C! ;

: <#					( --- )
 PAD HLD ! ;

: #>					( DL DH --- ADDR COUNT )
 2DROP HLD @ PAD OVER - ;

: SIGN					( SIGN --- )
 0< IF 2D HOLD THEN ;

: # 					( DL DH --- DL' DH' )
 BASE @ M/MMOD ROT 9 OVER < IF 7 + THEN
 30 + HOLD ;

: #S					( DL DH --- 0 0 )
 BEGIN # 2DUP OR 0= UNTIL ;

: D.R					( DL DH FILEDSIZE --- )
 >R SWAP OVER DABS <# #S ROT SIGN #>
 R> OVER - SPACES TYPE ;

: ZEROES				( N --- )   ( EMIT N ZEROES )
 0 MAX ?DUP IF 0 DO 30 EMIT LOOP THEN ;

: D.LZ					( DL DH FIELDSIZE --- )
 >R SWAP OVER DABS <# #S ROT SIGN #>
 R> OVER - ZEROES TYPE ;

: D.					( DL DH --- )
 0 D.R SPACE ;

: .R   >R S->D R> D.R ;			( N FIELDSIZE --- )

: .					( N --- )
 S->D D. ;

: U.R   0 SWAP D.R ;			( N FIELDSIZE --- )

: U.LZ   0 SWAP D.LZ ;			( N FIELDSIZE --- )

: U.   0 D. ;				( N --- )

: ?   @ . ;				( ADDR --- )

: U?   @ U. ;				( ADDR --- )

( UTILITIES )

: [COMPILE]
 ' , ; IMMEDIATE METASMUDGE

: [']
 ' COMPILE (LITERAL) , ; IMMEDIATE METASMUDGE

: LITERAL
 COMPILE (LITERAL) , ; IMMEDIATE METASMUDGE

: .(
 29 WORD COUNT TYPE CR ; IMMEDIATE METASMUDGE

: DUMP
 CR 
 FFFF 0 <# #S #> SWAP DROP -ROT
 FF   0 <# #S #> SWAP DROP -ROT
 OVER + SWAP DO
    I 2 PICK U.LZ ." :" SPACE
    I 8 + I DO
       I C@ OVER U.LZ SPACE
    LOOP 4 SPACES
    I 8 + I DO
       I C@ DUP 20 < OVER 7E > OR
       IF DROP 2E THEN
       EMIT
    LOOP
 CR 8 +LOOP 2DROP ;

: FORGET				( --- )   ( DELETE THE NEXT WORD    )
					( IN THE INPUT STREAM FROM THE COM- )
					( PILATION VOCABULARY.		    )
 HERE 4 20 FILL
 20 WORD LATEST (FIND) ?DUP
 IF DUP DP ! 4 + @ CURRENT @ !
 ELSE HERE COUNT TYPE ."  ?" CR
 THEN ;

( OPERATING SYSTEM SUPPORT WORDS )

: DIGIT					( CHR --- N TRUE <OR> FALSE )
 30 -
 DUP 9 > OVER 11 < AND IF
    DROP FALSE
 ELSE
    DUP 9 U> IF 7 - THEN
    DUP BASE @ 1- U> IF 
       DROP FALSE
    ELSE
       TRUE
    THEN
 THEN ;

: CONVERT				( DL DH ADDR1 --- DL' DH' ADDR2 )
 					( CONVERT CHARACTERS TO NUMBERS )
					( STARTING AT ADDR1 ACCUMULATING)
					( IN D. ADDR2 IS THE ADDRESS OF )
					( THE FIRST UNCONVERTIBLE CHAR. )
 >R BEGIN
    R> 1+ DUP >R C@ DIGIT		( TRY TO CONVERT NEXT DIGIT )
    WHILE >R BASE @ UM*M R> 0 D+
 REPEAT R> ;

: NUMBER				( ADDR --- N TRUE <OR> FALSE )
 DUP 1+ C@ 2D = DUP >R -		( SAVE SIGN ON RETURN STACK )
 0 0 ROT CONVERT
 C@ 20 = IF 				( IF SUCCESSFUL )
    DROP R> +- TRUE			( TRUNCATE, APPLY SIGN, RETURN TRUE )
 ELSE
    2DROP R> DROP FALSE			( ELSE RETURN FALSE )
 THEN ;

: ?STACK				( --- T/F )   ( RETURNS TRUE )
					( ON STACK UNDERFLOW.        )
 @SP SP0 > ;

: CHUCKBUF				( --- )   ( FLUSH REST OF INPUT LINE )
 TIB >IN @ + BEGIN
    DUP C@ EOL <>
    WHILE 1+
 REPEAT TIB - >IN ! ;

: ENDINTERP				( --- )   ( RESET STACK POINTER AND )
					( FLUSH REST OF INPUT LINE.         )
 SP0 !SP CHUCKBUF ;

: INTERPRET				( --- )
 BEGIN
    HERE 4 20 FILL
    20 WORD C@ WHILE			( WHILE NOT AT END OF LINE )
    HERE FIND ?DUP IF
       STATE @ + IF EXECUTE ELSE , THEN
    ELSE
       NUMBER IF
          STATE @ IF
             COMPILE (LITERAL) ,
          THEN
       ELSE
          HERE COUNT TYPE ."  ?" CR ENDINTERP
       THEN
    THEN
    ?STACK IF
       ."  Stack empty" CR ENDINTERP
    THEN
 REPEAT ;

: FLOAD					( ADDR[UNIX_STRING] --- )
 0 OPEN
 DUP 0< IF
    DROP ." can't open" CR
 ELSE
    >R BEGIN R@ FQUERY WHILE INTERPRET REPEAT
    R> CLOSE CHUCKBUF
 THEN ;

: QUIT					( --- )
 RESET 0 STATE !			( RESET RETURN STACK; INTERPRET STATE )
 BEGIN
    CR STDIN FQUERY WHILE
    INTERPRET STATE @ 0= IF ."  OK" THEN
 REPEAT CR TERMINATE ;

: ABORT					( --- )
 SP0 !SP QUIT ;

: ABORT"				( T/F --- )  ( PRINTS MESSAGE AND )
					( ABORTS IF FLAG IS TRUE.         )
 COMPILE ?BRANCH >MARK
 COMPILE (.") 22 ,WORD COMPILE ABORT
 >RESOLVE ; IMMEDIATE METASMUDGE

( INITIALIZATION CODE AND STARTUP CODE )

 ' ABORT 4 + vector 2+ !        ( BACKPATCH INTERRUPT ROUTINE )
 HERE 2 !                       ( BACKPATCH STARTING JUMP )

 MOV inbuf $   PSP REG          ( INITIALIZE PSP )
 30 TRAP  2 , 1 ,		( IGNORE INTERRUPT SIGNALS )
 ROR 0 REG
 BCS 1 FWD                      ( SKIP IF INTERRUPTS ARE ALREADY IGNORED )
 30 TRAP  2 , vector ,          ( CATCH INTERRUPTS )
1 L: MOV SP )+   0 REG		( R0 HAS ARGUMENT COUNT )
 ASL 0 REG			( R0 HAS BYTE COUNT )
 ADD 0 REG   SP REG		( POP ARGUMENTS )
 TST SP )+			( POP NULL POINTER; SP NOW HAS ENVIRONMENT )
				( POINTER USED BY EXEC CALLS               )
 MOV SP REG   rsp0 *$           ( SAVE RETURN STACK POINTER FOR USE BY QUIT )
				( AND EXEC CALL                             )
 MOV HERE 4 + $   IAR REG       ( TRICKY; IAR POINTS TO HIGH LEVEL STARTUP  )
 NEXT				( EXECUTE FORTH )


( HIGH LEVEL STARTUP CODE )

] HEX   TRUE WRN !   0 CLUE !
 FORTH DEFINITIONS
 CR ." unix-FORTH, version 2.1"
 ABORT
[

( INITILIZE VARIABLES AT COMPILE TIME )

HERE DP !				( INITIAL DP )
OBJLINK FORTH @ HOST initvocab !	( INITIAL VOCABULARY )
+E+O+F