#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	fact.c
#	gen.c
#	gen.h
#	makefile
#	small.g
#	sym.c
#	sym.h
#	tsam.c
#	tsam.doc
# This archive created: Fri Mar 23 17:49:13 1990
# By:	CARP Research Group ()
export PATH; PATH=/bin:$PATH
if test -f 'fact.c'
then
	echo shar: will not over-write existing file "'fact.c'"
else
cat << \SHAR_EOF > 'fact.c'
fact(n)
{
	if ( n ) return n*fact(n + -1);
	else return 1;
}

main(a)
{
	int f;

	f = fact(5);
	return f;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'gen.c'
then
	echo shar: will not over-write existing file "'gen.c'"
else
cat << \SHAR_EOF > 'gen.c'
#include <stdio.h>
#include "gen.h"
#include "sym.h"
#include "dlgdef.h"

/* C o d e  G e n e r a t o r */

char *calloc();

char *howTo[] =
{
	"%s:\n",				/* Label */
	"\tPUSH\t0\n",			/* Local */
	"%s:\n\tWORD\t0\n",		/* Global */
	"\tRET\n",				/* Return */
	"\tADD\n",				/* Add */
	"\tMUL\n",				/* Mult */
	NULL,					/* Ref */
	"\tNEG\n",				/* Neg */
	"\tPUSH\t%s\n",			/* Push */
	"\tAFP\n",				/* Afp */
	"\tIND\n",				/* Ind */
	"\tCALL\n",				/* Call */
	"\tPOP\n",				/* Pop */
	"\tJUMP\t%s\n",			/* Jump */
	"\tJUMPZ\t%s\n",		/* JumpZ */
	"\tTEST\n"				/* Test */
};

gen(action, ptr, type)
int action;
Sym *ptr;
int type;
{
	/* V a r i a b l e  R e f e r e n c e ? */
	if ( action == Ref )
	{
		if ( ptr == NULL ) {Cerror("Hmmm...gen() fatal\n"); exit(-1);}
		/* F u n c t i o n ? */
		if ( ptr->executable )
		{
			gen(Push, ptr->symbol);
			return;
		}
		if ( ptr->level == GLOBAL )
		{
			gen(Push, ptr->symbol);
			if ( type == Value ) gen(Ind);
		}
		else
		{
			gen(Push, str(ptr->offset));
			gen(Afp);
			if ( type == Value ) gen(Ind);
		}
		return;
	}
	
	printf(howTo[action], ptr);
}

char *
NewLabel()
{
	static char label[LabelSize+1];
	static int num = 0;
	
	sprintf(label, "_L%03d", ++num);
	return( label );
}

char *
str(i)
int i;
{
	static char buf[LabelSize+1];

	sprintf(buf, "%d", i);
	return(buf);
}

Cerror(err, a1, a2, a3)
char *err, *a1, *a2, *a3;
{
	fprintf(stderr, "stdin, line %d: ", lex_line);
	fprintf(stderr, err, a1, a2, a3);
}

SHAR_EOF
fi # end of overwriting check
if test -f 'gen.h'
then
	echo shar: will not over-write existing file "'gen.h'"
else
cat << \SHAR_EOF > 'gen.h'
/* C o d e  G e n  D e f s */

#define Label	0
#define Local	1
#define Global	2
#define Return	3
#define	Add		4
#define Mult	5
#define Ref		6
#define Neg		7
#define Push	8
#define Afp		9
#define Ind		10
#define Call	11
#define Pop		12
#define Jump	13
#define JumpZ	14
#define Test	15

/* Note that locals start at -1 and move more negative */
/* Parameter is always at + 2 */

#define ParmOffset	2
#define LocalStart	(-1)
#define Addr	0
#define Value	1

#define HTsize		501
#define STRsize		500
#define LabelSize	10

char *NewLabel(), *str();
SHAR_EOF
fi # end of overwriting check
if test -f 'makefile'
then
	echo shar: will not over-write existing file "'makefile'"
else
cat << \SHAR_EOF > 'makefile'
SRC=small.c ssup.c gen.c sym.c err.c
OBJ=small.o ssup.o gen.o sym.o err.o
CFLAGS=-DUNIX -I../h

small : $(OBJ)
	cc -o C $(OBJ)
small.c : small.g
	antlr small.g
ssup.c : parser.dlg small.g
	dlg -C2 parser.dlg ssup.c
small.o : small.c

ssup.o : ssup.c

gen.o : gen.c

sym.o : sym.c

err.o : err.c

SHAR_EOF
fi # end of overwriting check
if test -f 'small.g'
then
	echo shar: will not over-write existing file "'small.g'"
else
cat << \SHAR_EOF > 'small.g'
#attrib <<#include "sym.h">>

<<
#include "gen.h"

Sym *CurSym;
int CurVal;

main()
{
	aSymInit(HTsize, STRsize);
	ANTLR(smallC, stdin);
}

/*
 * Convert a lexical object into an attribute
 * In this case, the we told the lexical analyzer to do a lot.  aCreate()
 * doesn't have to do much.
 */
aCreate(attr)
Attrib *attr;
{
	switch ( Token )
	{
		case WORD :
		case FUNC :
		case VAR :
			attr->sym = CurSym;
			break;
		case Number :
			attr->ival = CurVal;
			break;
	}
}
>>

#token "[\t\ ]"		<< LexSkip(); >>
#token "\n"			<< lex_line++; LexSkip(); >>

smallC	:	<<Sym *globals=NULL; gen(Jump, "main"); aSymScope(&globals);>>
			decls[{$1.ival=GLOBAL;}]
			( func <<aSymScope(&globals);>> )*
			"@"
			<<aSymRmScope(&globals);>>
		;

decls	:	(	<< int offset=LocalStart; >>
				"int"
				(	WORD <<	aSymAdd($1.sym->symbol, $1.sym);
							$1.sym->token = VAR;
							$1.sym->level = $$.ival;
							$1.sym->offset = offset--;
							if ( $$.ival==LOCAL ) gen(Local);
							else gen(Global, $1.sym->symbol); >>
				|	VAR  <<	if ( $1.sym->level == $$.ival ) {
								Cerror("redecl of '%s' ignored\n",
										$1.sym->symbol);
							}
							else {
								aSymAdd($1.sym->symbol, $1.sym);
								$1.sym->token = VAR;
								$1.sym->level = LOCAL;
								$1.sym->offset = offset--;
								gen(Local);
							}
							>>
				)
				";"
			)*
		;

func	:	<<Sym *locals=NULL;>>
			WORD		<< gen(Label, $1.sym->symbol);
						   aSymAdd($1.sym->symbol, $1.sym);
						   $1.sym->token = FUNC;
						   $1.sym->level = GLOBAL;
						   $1.sym->executable = 1; >>
			<<aSymScope(&locals);>>
			"\(" WORD	<< aSymAdd($3.sym->symbol, $3.sym);
						   $3.sym->token = VAR;
						   $3.sym->level = LOCAL;
						   $3.sym->offset = ParmOffset; >>
			"\)"	
			body
			<<aSymRmScope(&locals); gen(Push,str(0)); gen(Return);>>
		;

body	:	"\{" decls[{$2.ival=LOCAL;}] slist "\}" ;

slist	:	( stat )* ;

stat	:	<<char L1[LabelSize], L2[LabelSize];>>
			"if" expr
				<<
					strcpy(L1,NewLabel());
					gen(Test);
					gen(JumpZ, L1);
				>>
			stat
				<<
					strcpy(L2,NewLabel());
					gen(Jump, L2);
					gen(Label,L1);
				>>
			{ "else" stat }
			<<gen(Label, L2);>>
		|   "while"
				<<
					strcpy(L1,NewLabel());
					gen(Label,L1);
				>>
			expr
				<<
					strcpy(L2,NewLabel());
					gen(Test);
					gen(JumpZ, L2);
				>>
			stat
				<<
					gen(Jump, L1);
					gen(Label,L2);
				>>
		|   "return" expr ";"	<<gen(Return);>>
		|   (	VAR  <<gen(Ref,$1.sym,Addr);>> 
			|	WORD <<Cerror("undefined variable '%s'\n", $1.sym->symbol);>>
			)
			"=" expr ";"	<<gen(Pop);>>
		|   "\{" (stat)* "\}"
		|   ";"
		;
expr	:	term ( "\+" term <<gen(Add);>> )* ;
term	:	fact ( "\*" fact <<gen(Mult);>> )* ;
fact	:	FUNC <<gen(Ref,$1.sym,Value);>> "\(" expr "\)" <<gen(Call);>>
		|	VAR  <<gen(Ref,$1.sym,Value);>> 
		|   "\-" fact		<<gen(Neg);>>
		|   Number			<<gen(Push, str($1.ival));>>
		|   "\(" expr "\)"
		;

#token Number "[0-9]+"		<< {extern int CurVal; CurVal = atoi(LexText);} >>
#token WORD "[a-z] [a-z]*"
				<<
				{
					/* Assume that two WORDs do not appear adjacently */
					register Sym *p;
					extern Sym *CurSym;
					extern int CurVal;

					p = aSymGet(LexText);
					if ( p == NULL ) p = aSymNew(LexText);
					else Token = p->token;
					CurSym = p;
				}
				>>
SHAR_EOF
fi # end of overwriting check
if test -f 'sym.c'
then
	echo shar: will not over-write existing file "'sym.c'"
else
cat << \SHAR_EOF > 'sym.c'
/*
 * Simple symbol table manager using coalesced chaining to resolve collisions
 *
 * Doubly-linked lists are used for fast removal of entries.
 *
 * 'sym.h' must have a definition for typedef "Sym".  Sym must include at
 * minimum the following fields:
 *
 *		...
 *		char *symbol;
 *		struct ... *next, *prev, **head, *scope;
 *		...
 *
 * 'head' is &(table[hash(itself)]).
 * The hash table is not resizable at run-time.
 * The scope field is used to link all symbols of a current scope together.
 * Scope() sets the current scope (linked list) to add symbols to.
 * Any number of scopes can be handled.  The user passes the address of
 * a pointer to a symbol table
 * entry (INITIALIZED TO NULL first time).
 *
 * Available Functions:
 *
 *	aSymInit(s1,s2)	-- Create hash table with size s1, string table size s2.
 *	aSymDone(s1,s2)	-- Free hash and string table created with aSymInit().
 *	aSymAdd(key,rec)-- Add 'rec' with key 'key' to the symbol table.
 *	aSymGet(key)	-- Return pointer to last record entered under 'key'
 *			   Else return NULL
 *	aSymDel(p)	-- Unlink the entry associated with p.  This does
 *			   NOT free 'p' and DOES NOT remove it from a scope
 *			   list.  If it was a part of your intermediate code
 *			   tree or another structure.  It will still be there.
 *			   It is only removed from the symbol table.
 *	aSymScope(sc)	-- Specifies that everything added to the symbol
 *			   table with aSymAdd() is added to the list (scope)
 *			   'sc'.  'sc' is of 'Sym **sc' type and must be
 *			   initialized to NULL before trying to add anything
 *			   to it (passing it to aSymScope()).  Scopes can be
 *			   switched at any time and merely links a set of
 *			   symbol table entries.  If a NULL pointer is
 *                         passed, the current scope is returned.
 *	aSymRmScope(sc)	-- Remove (aSymDel()) all elements of scope 'sc'
 *			   from the symbol table.  The entries are NOT
 *			   free()'d.  A pointer to the first
 *			   element in the "scope" is returned.  The user
 *			   can then manipulate the list as he/she chooses
 *			   (such as freeing them all). NOTE that this
 *			   function sets your scope pointer to NULL,
 *			   but returns a pointer to the list for you to use.
 *	aSymStat()	-- Print out the symbol table and some relevant stats.
 *	aSymNew(key)	-- Create a new record with calloc() of type Sym.
 *			   Add 'key' to the string table and make the new
 *			   records 'symbol' pointer point to it.
 *	aSymSaveStr(s)	-- Add s to the string table and return a pointer
 *			   to it.  Very fast routine allocation routine
 *			   and does not require strlen() nor calloc().
 *
 * Example:
 *
 *	#include <stdio.h>
 *	#include "sym.h"
 *
 *	main()
 *	{
 *	    Sym *scope1, *scope2, *a, *p;
 *	
 *	    aSymInit(101, 100);
 *	
 *	    a = aSymNew("Apple");	aSymAdd(a->symbol, a);	-- No scope
 *	    aSymScope( &scope1 );	-- enter scope 1
 *	    a = aSymNew("Plum");	aSymAdd(a->symbol, a);
 *	    aSymScope( &scope2 );	-- enter scope 2
 *	    a = aSymNew("Truck");	aSymAdd(a->symbol, a);
 *	
 *    	    p = aSymGet("Plum");
 *    	    if ( p == NULL ) fprintf(stderr, "Hmmm...Can't find 'Plum'\n");
 *	
 *    	    p = aSymRmScope(&scope1)
 *    	    for (; p!=NULL; p=p->scope) {printf("Scope1:  %s\n", p->symbol);}
 *    	    p = aSymRmScope(&scope2)
 *    	    for (; p!=NULL; p=p->scope) {printf("Scope2:  %s\n", p->symbol);}
 *     }
 *
 * Terence Parr
 * Purdue University
 * February 1990
 */

#include <stdio.h>
#include "sym.h"

char *calloc();

#define StrSame		0

static Sym **CurScope = NULL;
static unsigned size = 0;
static Sym **table=NULL;
static char *strings;
static char *strp;
static int strsize = 0;

aSymInit(sz, strs)
int sz, strs;
{
	if ( sz <= 0 || strs <= 0 ) return;
	table = (Sym **) calloc(sz, sizeof(Sym *));
	if ( table == NULL )
	{
		fprintf(stderr, "Cannot allocate table of size %d\n", sz);
		exit(1);
	}
	strings = (char *) calloc(strs, sizeof(char));
	if ( strings == NULL )
	{
		fprintf(stderr, "Cannot allocate string table of size %d\n", strs);
		exit(1);
	}
	size = sz;
	strsize = strs;
	strp = strings;
}

aSymDone()
{
	if ( table != NULL ) free( table );
	if ( strings != NULL ) free( strings );
}
aSymAdd(key, rec)
char *key;
register Sym *rec;
{
	register h=0;
	register char *p=key;
	
	while ( *p != '\0' ) h = (h<<1) + *p++;
	h %= size;
	
	if ( CurScope != NULL ) {rec->scope = *CurScope; *CurScope = rec;}
	rec->next = table[h];			/* Add to doubly-linked list */
	rec->prev = NULL;
	if ( rec->next != NULL ) (rec->next)->prev = rec;
	table[h] = rec;
	rec->head = &(table[h]);
}

Sym *
aSymGet(key)
char *key;
{
	register h=0;
	register char *p=key;
	register Sym *q;
	
	while ( *p != '\0' ) h = (h<<1) + *p++;
	h %= size;
	
	for (q = table[h]; q != NULL; q = q->next)
	{
		if ( strcmp(key, q->symbol) == StrSame ) return( q );
	}
	return( NULL );
}

/*
 * Unlink p from the symbol table.  Hopefully, it's actually in the
 * symbol table.
 *
 * If p is not part of a bucket chain of the symbol table, bad things
 * will happen.
 *
 * Will do nothing if all list pointers are NULL
 */
aSymDel(p)
register Sym *p;
{
	if ( p == NULL ) {fprintf(stderr, "aSymDel(NULL)\n"); exit(1);}
	if ( p->prev == NULL )	/* Head of list */
	{
		register Sym **t = p->head;
		
		if ( t == NULL ) return;	/* not part of symbol table */
		(*t) = p->next;
		if ( (*t) != NULL ) (*t)->prev = NULL;
	}
	else
	{
		(p->prev)->next = p->next;
		if ( p->next != NULL ) (p->next)->prev = p->prev;
	}
	p->next = p->prev = NULL;	/* not part of symbol table anymore */
	p->head = NULL;
}

/* S c o p e  S t u f f */

/* Set current scope to 'scope'; return current scope if 'scope' == NULL */
Sym **
aSymScope(scope)
Sym **scope;
{
	if ( scope == NULL ) return( CurScope );
	CurScope = scope;
	return( scope );
}

/* Remove a scope described by 'scope'.  Return pointer to 1st element in scope */
Sym *
aSymRmScope(scope)
register Sym **scope;
{
	register Sym *p;
	Sym *start;

	if ( scope == NULL ) return(NULL);
	start = p = *scope;
	for (; p != NULL; p=p->scope) {aSymDel( p );}
	*scope = NULL;
	return( start );
}

aSymStat()
{
	static unsigned short count[20];
	int i,n=0,low=0, hi=0;
	register Sym **p;
	float avg=0.0;
	
	for (i=0; i<20; i++) count[i] = 0;
	for (p=table; p<&(table[size]); p++)
	{
		register Sym *q = *p;
		int len;
		
		if ( q != NULL && low==0 ) low = p-table;
		len = 0;
		if ( q != NULL ) fprintf(stderr, "[%d]", p-table);
		while ( q != NULL )
		{
			len++;
			n++;
			fprintf(stderr, " %s", q->symbol);
			q = q->next;
			if ( q == NULL ) fprintf(stderr, "\n");
		}
		count[len]++;
		if ( *p != NULL ) hi = p-table;
	}

	fprintf(stderr, "Storing %d recs used %d hash positions out of %d\n",
					n, size-count[0], size);
	fprintf(stderr, "%f %% utilization\n",
					((float)(size-count[0]))/((float)size));
	for (i=0; i<20; i++)
	{
		if ( count[i] != 0 )
		{
			avg += (((float)(i*count[i]))/((float)n)) * i;
			fprintf(stderr, "Bucket len %d == %d (%f %% of recs)\n",
							i, count[i], ((float)(i*count[i]))/((float)n));
		}
	}
	fprintf(stderr, "Avg bucket length %f\n", avg);
	fprintf(stderr, "Range of hash function: %d..%d\n", low, hi);
}

/*
 * Given a string, this function allocates and returns a pointer to a
 * symbol table record whose "symbol" pointer is reset to a position
 * in the string table.
 */
Sym *
aSymNew(text)
char *text;
{
	Sym *p;
	char *aSymSaveStr();
	
	if ( (p = (Sym *) calloc(1,sizeof(Sym))) == 0 )
	{
		fprintf(stderr,"Out of memory\n");
		exit(1);
	}
	p->symbol = aSymSaveStr(text);
	
	return(p);
}

/* Add a string to the string table and return a pointer to it.
 * Bump the pointer into the string table to next avail position.
 */
char *
aSymSaveStr(s)
register char *s;
{
	register char *start=strp;

	while ( *s != '\0' ) { *strp++ = *s++; }
	*strp++ = '\0';

	return( start );
}
SHAR_EOF
fi # end of overwriting check
if test -f 'sym.h'
then
	echo shar: will not over-write existing file "'sym.h'"
else
cat << \SHAR_EOF > 'sym.h'

typedef struct symrec {
			char *symbol;
			struct symrec *next, *prev, **head, *scope;
			int token;
			int level;
			int offset;
			int executable;
		} Sym, *SymPtr;

/* SmallC attribute stuff */

typedef union {
			Sym *sym;
			int ival;
		} Attrib;

#define GLOBAL	0		/* Use .ival field of Attrib to inherit context */
#define LOCAL	1

Sym *aSymGet();
Sym *aSymNew();
Sym *aSymRmScope();
SHAR_EOF
fi # end of overwriting check
if test -f 'tsam.c'
then
	echo shar: will not over-write existing file "'tsam.c'"
else
cat << \SHAR_EOF > 'tsam.c'
/*      TSAM.C

	Trivial Stack Assembler & Machine emulator.

	This is NOT well-written code. However, many of you have
	expressed an interest in the operation of the assembler-
	emulator, so here it is.

	The Assembler is always invoked to "load" the program to
	be executed. It is a conventional two-pass assembler, but
	it does not have a separate lexical analysis section, and
	the data structures are intertwined with those of the
	emulator. The symbol table is linear, mainly because the
	linear arrangement makes the table easy to use during the
	emulation phase -- then it is used to map addresses into
	symbolic names, rather than the reverse. Each line in the
	listing is generated by re-constructing the machine codes
	which were generated directly into the mem[] array of the
	emulator.

	The emulator is an obvious implementation of the definitions
	from the project #3 description. Machine errors are detected
	and cause "traps," which stop execution and print an error
	message. For every instruction, there is a function which is
	used to execute that instruction. The only tricky thing is
	the set-up before running the user program -- we must fake
	having had an Operating System call the user program and
	we must recognize a valid return to the OS.

	Fall 1983, by Hank Dietz
*/

#include        <stdio.h>
#include        <ctype.h>

/*      Machine Code Equivalents (and symbol values) */
#define ADD     1
#define SUB     2
#define MUL     3
#define NEG     4
#define TEST    5
#define POP     6
#define PUSH    7
#define AFP     8
#define ASP     9
#define IND     10
#define JUMP    11
#define JUMPZ   12
#define CALL    13
#define RET     14
#define WORD    15
#define SET     16
#define EQU     17

/*      Assembler data structures */
#define SETTYP  0x80
#define EQUTYP  0x40
#define LABELTYP 0x20
#define OPTYP   0x10
#define TRUE    1
#define FALSE   0

char    linebuf[257];      /* input line buffer */
char    *next;            /* pointer to next char in linebuf */

int     undeflag;              /* undefined var flag */
int     makelst;                /* MAKE a LiSTing file? */
FILE    *fin;              /* File INput stream */
int     pass;              /* which PASS, 1 or 2 */
int     lc, tlc;
int     casewild;              /* WILDcard between upper & lower */

#define MAXLEV  16            /* MAXimum LEVel of errors per line */
int     errlev;          /* ERRor LEVel counter */
char    *errf[MAXLEV];    /* ERRor Formats */
int     erra[MAXLEV];      /* ERRor Arguments */

#define MAXSYMS 256
#define MAXCHAR 4096

char    mempool[MAXCHAR];
char    *memnext;

char    *symtxt[MAXSYMS];
int     symtyp[MAXSYMS];
int     symval[MAXSYMS];
int     symnext;

/*      Machine emulator data structures */
#define MEMSIZE (8*1024)        /* must be a power of two */
#define OS      -1            /* for fp & pc, marks OS return */

int     fp;          /* Frame Pointer */
int     sp;          /* Stack Pointer */
int     pc;          /* Program Counter */
int     zero;      /* Zero Flag */
int     mem[MEMSIZE];   /* main MEMory */
char    *trap;    /* instruction trap message */
int     trace;    /* Trace instructions as they are executed */
int     dbug;

main(argc, argv)
int argc;
char **argv;
{
	char *p, *filenam;
	register int i, runit, arg, limit, negate;

	if (argc < 2) {
		printf("Usage:\n");
		printf("tsam [+-a] [+-c] [+l#] [+-r] [+-t] <filename> <arg>\n");
		printf("\nDefault  Meaning of option\n");
		printf("+a       make Assembly listing\n");
		printf("+c       case wildcarded\n");
		printf("+l10000  execution Limit is 10000\n");
		printf("+r       Run the program\n");
		printf("+t       Trace execution\n");
		leave(1);
		}

	dbug = FALSE;
	casewild = TRUE;
	arg = 0;
	limit = 10000;
	runit = TRUE;
	trace = TRUE;
	makelst = TRUE;
	filenam = 0;
	i = 1;
	while (i < argc) {
		p = argv[i++];
		if ((*p == '-') || (*p == '+')) {
			/* set the option(s) */
			do {
				switch (upcase(*p)) {
				case '-': negate = TRUE; break;
				case '+': negate = FALSE; break;
				case '$': dbug = (!negate); break;
				case 'A': makelst = (!negate); break;
				case 'C': casewild = (!negate); break;
				case 'L': ++p; limit = num(&p); --p; break;
				case 'R': runit = (!negate); break;
				case 'T': trace = (!negate); break;
				default:  printf("tsam: bad option %s\n", p);
					  leave(2);
					}
				}
				while (*(++p));
			}
		else {
			/* either filename or arg */
			if (filenam == 0) {
				filenam = p;
				}
			else {
				arg = atoi(p);
				}
			}
		}

	if (filenam == 0) {
		printf("tsam: no file name given\n");
		leave(3);
		}
	else {
		/* process the file */
		if (runit) minit();
		ainit(); assemble(filenam);
		if (runit) emulate(arg, limit);
		}
	}

ainit()
{
	/* Assembler INIT routine */
if (dbug) printf("ainit()\n");

	memnext = &(mempool[0]);
	symnext = 0;

	enter("ADD", OPTYP, ADD);
	enter("SUB", OPTYP, SUB);
	enter("MUL", OPTYP, MUL);
	enter("NEG", OPTYP, NEG);
	enter("TEST", OPTYP, TEST);
	enter("POP", OPTYP, POP);
	enter("PUSH", OPTYP, PUSH);
	enter("AFP", OPTYP, AFP);
	enter("ASP", OPTYP, ASP);
	enter("IND", OPTYP, IND);
	enter("JUMP", OPTYP, JUMP);
	enter("JUMPZ", OPTYP, JUMPZ);
	enter("CALL", OPTYP, CALL);
	enter("RET", OPTYP, RET);
	enter("WORD", OPTYP, WORD);
	enter("SET", OPTYP, SET);
	enter("EQU", OPTYP, EQU);
	}

findtxt(s)
char *s;
{
	register int i;
if (dbug) printf("findtxt(%s)\n", s);

	for (i=0; i<symnext; ++i) {
		/* if the string matches this table entry */
		if (ancmp(symtxt[i], s)) return(i);
		}
	return(-1);
	}

enter(s, t, v)
char *s;
int t, v;
{
	register int i;
if (dbug) printf("enter(%s, %d, %d)\n", s, t, v);

	if ((i = findtxt(s)) != -1) {
		/* Matches an existing entry? */
		if ((t != symtyp[i]) ||
		    ((t != SETTYP) && (v != symval[i]))) {
			/* Nope. Multiple definition. */
			err("%s is multiply defined", s);
			}
		else {
			/* A set op, change the value */
			symval[i] = v;
			}
		}
	else {
		/* Make a new entry */
		symtxt[i = (symnext++)] = memnext;
		symtyp[i] = t;
		symval[i] = v;
		while (isalnu(*s)) {
			*(memnext++) = *(s++);
			}
		*(memnext++) = 0;
		}
	}

ancmp(p, q)
char *p, *q;
{
if (dbug) printf("ancmp(%s, %s)\n", p, q);
	while (*p) {
		if (casewild) {
			if (upcase(*(p++)) != upcase(*(q++))) return(0);
			}
		else {
			if (*(p++) != *(q++)) return(0);
			}
		}
	return(!isalnu(*q));
	}

assemble(fname)
char *fname;
{
	/* Assemble fname */
if (dbug) printf("assemble(%s)\n", fname);

	if ((fin = fopen(fname, "r")) == NULL) {
		printf("tsam: cannot open %s\n", fname);
		leave(4);
		}

	do_pass(1);

	fseek(fin, 0L, 0);      /* rewind the file */
	do_pass(2);

	fclose(fin);
	}

do_pass(p)
int p;
{
if (dbug) printf("do_pass(%d)\n", p);
	trap = 0;
	pass = p;
	lc = 0;
	while (getl()) {
		tlc = lc;
		do_line();
		do_list();
		}
	}

getl()
{
	int i;
if (dbug) printf("getl()\n");

	next = &(linebuf[0]);
	i = getc(fin);
	while ((i != '\n') && (i != -1)) {
		*next = i;
		if (++next > &(linebuf[256])) {
			printf("tsam: input line too long\n");
			leave(5);
			}
		i = getc(fin);
		}
	*next = 0;	    /* null terminate */
	next = &(linebuf[0]);
	return(i != -1);
	}

do_line()
{
	register int i, lab;
if (dbug) printf("do_line()\n");

	errlev = 0;
	lab = 0;	/* Begin assuming no label */
	eatspace();
	if (isal2(*next)) {
		/* Is this an op? */
		if (((i = findtxt(next)) == -1) ||
		    (symtyp[i] != OPTYP)) {
			/* Nope. It must be a label. */
			lab = next;
			/* Move past it */
			while (isalnu(*next)) ++next;
			eatspace();
			/* Optional ':' after label */
			if (*next == ':') ++next;
			eatspace();
			}
		/* Now we ought to be looking at an op */
		if (((i = findtxt(next)) != -1) &&
		    (symtyp[i] == OPTYP)) {
			/* Yup. Move past it. */
			while (isalnu(*next)) ++next;
			eatspace();
			/* And do the operation */
			do_op(lab, i);
			}
		else {
			if (i != -1) {
				/* We are looking at something wrong */
				err("%s is not a valid opcode", symtxt[i]);
				}
			/* set the label, if there was one */
			if (lab) {
				enter(lab, LABELTYP, tlc);
				}
			}

		/* Hopefully, we now see the end of the line. */
		}

	/* No more pertinent info on the line, junk the rest */
	junk();
	}

eatspace()
{
if (dbug) printf("eatspace()\n");
	while (isspace(*next)) ++next;
	}

junk()
{
if (dbug) printf("junk()\n");
	eatspace();
	switch (*next) {
	case ';':
	case 0:  break;
	default:	err("'%s' ignored", next);
		}
	}

do_op(lab, i)
char *lab;
int i;
{
if (dbug) printf("do_op(%s, %d)\n", lab, i);
	switch (symval[i]) {
	case ADD:       case SUB:       case MUL:
	case NEG:       case TEST:      case POP:
	case AFP:       case ASP:       case IND:
	case CALL:      case RET:
		/* Opcode has form: OP */
		genw(symval[i]);
		break;
	case PUSH:      case JUMP:      case JUMPZ:
		/* Opcode has form: OP expression */
		genw(symval[i]);
		genw(expr());
		break;
	case WORD:
		/* Generate a word of data */
		genw(expr());
		break;
	case EQU:
		/* Equate label to data */
		if (!lab) {
			err("EQU missing label");
			}
		else {
			i = expr();
			if (!undeflag) {
				enter(lab, EQUTYP, i);
				}
			}
		return;
	case SET:
		/* Set label to data */
		if (!lab) {
			err("SET missing label");
			}
		else {
			i = expr();
			if (!undeflag) {
				enter(lab, SETTYP, i);
				}
			}
		return;
	default:
		/* Cannot possibly happen. */
		err("do_op(%s) fails", symtxt[i]);
		}

	/* Set label to tlc (it was a label, not EQU or SET) */
	if (lab) {
		enter(lab, LABELTYP, tlc);
		}
	}

expr()
{
if (dbug) printf("expr()\n");
	undeflag = 0;
	eatspace();
	return(orop());
	}

orop()
{
	int i;
if (dbug) printf("orop()\n");

	i = mulops();
	do {
		switch (*next) {
		case '|':       ++next; eatspace(); i |= andop(); break;
		default:	return(i);
			}
		eatspace();
		}
		while (TRUE);
	}

andop()
{
	int i;
if (dbug) printf("andop()\n");

	i = addops();
	do {
		switch (*next) {
		case '&':       ++next; eatspace(); i &= addops(); break;
		default:	return(i);
			}
		eatspace();
		}
		while (TRUE);
	}

addops()
{
	int i;
if (dbug) printf("addops()\n");

	i = mulops();
	do {
		switch (*next) {
		case '+':       ++next; eatspace(); i += mulops(); break;
		case '-':       ++next; eatspace(); i -= mulops(); break;
		default:	return(i);
			}
		eatspace();
		}
		while (TRUE);
	}

mulops()
{
	int i;
if (dbug) printf("mulops()\n");

	i = primary();
	do {
		switch (*next) {
		case '*':       ++next; eatspace(); i *= primary(); break;
		case '/':       ++next; eatspace(); i /= primary(); break;
		case '%':       ++next; eatspace(); i %= primary(); break;
		default:	return(i);
			}
		eatspace();
		}
		while (TRUE);
	}

primary()
{
	register int i;
if (dbug) printf("primary()\n");

	switch (*next) {
	case '(':
		++next; eatspace();
		i = addops();
		eatspace();
		if (*next != ')') {
			err("missing ')' assumed");
			}
		else {
			++next; eatspace();
			}
		return(i);
	case '-':
		++next; eatspace();
		return( -primary() );
	case '~':
	case '!':
		++next; eatspace();
		return( ~primary() );
		}
	if (isdigit(*next)) {
		i = 0;
		while (isdigit(*next)) {
			i = (i * 10) + (*next) - '0';
			++next;
			}
		return(i);
		}
	if (isal2(*next)) {
		i = findtxt(next);
		/* Move past it */
		while (isalnu(*next)) ++next;
		eatspace();
		if (i == -1) {
			/* Not defined. */
			undeflag = TRUE;
			return(0);
			}
		/* Is this a valid type */
		if (symtyp[i] == OPTYP) {
			/* Nope. Cannot use operator as operand. */
			err("cannot use %s as an operand", symtxt[i]);
			return(0);
			}
		/* Return the value of the symbol */
		return(symval[i]);
		}
	}

err(f, a)
char *f;
int a;
{
	/* Stack error messages for output after line */
if (dbug) printf("err(%s, %d)\n", f, a);

	if (errlev < MAXLEV) {
		errf[errlev] = f;
		erra[errlev++] = a;
		}
	}

do_list()
{
	register int i;
if (dbug) printf("do_list()\n");

	/* Really do it? */
	if ((pass == 2) && makelst) {
		/* First, print the listing prefix */
		if (tlc < lc) {
			printf("%6d: ", tlc);
			}
		else printf("\t");
		if (tlc < lc) {
			printf("%6d  ", fetch(tlc++));
			}
		else printf("\t");
		if (tlc < lc) {
			printf("%6d  ", fetch(tlc++));
			}
		else printf("\t");
		printf("%s\n", &(linebuf[0]));

		/* Then print any errors which are buffered */
		for (i=0; i<errlev; ++i) {
			printf("******: \t\t");
			printf(errf[i], erra[i]);
			printf("\n");
			}
		errlev = 0;
		}
	}

minit()
{
	register int i;
if (dbug) printf("minit()\n");

	for (i=0; i<MEMSIZE; ++i) mem[i] = 0;
	}

emulate(a, l)
int a;
register int l;
{
if (dbug) printf("emulate(%d, %d)\n", a, l);
	osinit(a);      /* set-up OS with a */

	if (l < 1) l = 1;
	if (l > 10000) l = 10000;
	run(l);  /* run up to l instructions */
	}

osinit(arg)
int arg;
{
	/* Fake OS set-up of the program. Pass arg as the
	   argument value.
	*/
if (dbug) printf("osinit(%d)\n", arg);

	printf("Argument: %d\n", arg);
	trap = 0;
	fp = OS;
	sp = MEMSIZE;
	pc = 0;
	store(--sp, 0);
	store(--sp, arg);
	store(--sp, OS);	/* pc of OS */
	store(--sp, OS);	/* fp of OS */
	fp = sp;
	}

num(pp)
char **pp;
{
	register char *q;
	register int i;
if (dbug) printf("num(%s)\n", *pp);

	q = *pp;
	i = 0;
	while (isdigit(*q)) {
		i = (i * 10) + (*(q++) - '0');
		}
	*pp = q;
	return(i);
	}

run(lim)
register int lim;
{
	/* Run program until trap or lim */
	register int count;
if (dbug) printf("run(%d)\n", lim);

	trap = 0;
	count = 0;
	while ((!trap) && (lim > ++count) && (pc != OS)) {
		step();
		}
	if ((pc == OS) && (fp == OS)) {
		printf("Normal termination.\n");
		printf("%d Instruction%sexecuted.\n", count,
		       ((count == 1) ? " " : "s "));
		printf("Returned value: %d\n", fetch(sp));
		}
	else {
		printf("%d Instruction%sexecuted.\n", count,
		       ((count == 1) ? " " : "s "));
		if (trap) {
			printf("Trap at %d: %s\n", pc, trap);
			}
		tb();      /* TraceBack */
		}
	}

step()
{
	/* Execute a single step */
	register int tpc, tsp, tfp;
if (dbug) printf("step()\n");

	/* save pc (in case intruction causes a trap) */
	tpc = pc;
	tsp = sp;
	tfp = fp;

	/* trace if need be */
	if (trace) traceop();

	switch (fetch(pc++)) {
	case ADD:       add(); break;
	case SUB:       sub(); break;
	case MUL:       mul(); break;
	case NEG:       neg(); break;
	case TEST:      test(); break;
	case POP:       pop(); break;
	case PUSH:      push(); break;
	case AFP:       afp(); break;
	case ASP:       asp(); break;
	case IND:       ind(); break;
	case JUMP:      jump(); break;
	case JUMPZ:     jumpz(); break;
	case CALL:      call(); break;
	case RET:       ret(); break;
	default:	trap = "Illegal instruction";
		}

	/* back-up if we had a trap */
	if (trap) {
		pc = tpc;
		sp = tsp;
		fp = tfp;
		}
	}

add()
{
if (dbug) printf("add()\n");
	store(sp + 1, fetch(sp + 1) + fetch(sp));
	++sp;
	}

sub()
{
if (dbug) printf("sub()\n");
	store(sp + 1, fetch(sp + 1) - fetch(sp));
	++sp;
	}

mul()
{
if (dbug) printf("mul()\n");
	store(sp + 1, fetch(sp + 1) * fetch(sp));
	++sp;
	}

neg()
{
if (dbug) printf("neg()\n");
	store(sp, 0 - fetch(sp));
	}

test()
{
if (dbug) printf("test()\n");
	zero = !fetch(sp++);
	}

pop()
{
if (dbug) printf("pop()\n");
	store(fetch(sp + 1), fetch(sp));
	sp += 2;
	}

push()
{
	register int t;
if (dbug) printf("push()\n");

	t = fetch(pc++);
	store(--sp, t);
	}

afp()
{
if (dbug) printf("afp()\n");
	store(sp, fetch(sp) + fp);
	}

asp()
{
if (dbug) printf("asp()\n");
	store(sp, fetch(sp) + sp);
	}

ind()
{
if (dbug) printf("ind()\n");
	store(sp, fetch( fetch(sp) ));
	}

jump()
{
if (dbug) printf("jump()\n");
	pc = fetch(pc);
	}

jumpz()
{
	register int t;
if (dbug) printf("jumpz()\n");

	t = fetch(pc++);
	if (zero) pc = t;
	}

call()
{
if (dbug) printf("call()\n");
	store(--sp, pc);
	store(--sp, fp);
	fp = sp;
	pc = fetch(fp + 3);
	}

ret()
{
	register int t;
if (dbug) printf("ret()\n");

	store(fp + 3, fetch(sp));
	t = fetch(fp);
	pc = fetch(fp + 1);
	sp = fp + 3;
	fp = t;
	}

fetch(addr)
int addr;
{
if (dbug) printf("fetch(%d)\n", addr);
	if (!trap) {
		if (addr & (~(MEMSIZE-1))) {
			trap = "Bad memory read address";
			return(0);
			}
		return( mem[addr] );
		}
	}

store(addr, val)
int addr, val;
{
if (dbug) printf("store(%d, %d)\n", addr, val);
	if (!trap) {
		if (addr & (~(MEMSIZE-1))) {
			trap = "Bad memory write address";
			}
		else {
			mem[addr] = val;
			}
		}
	}

traceop()
{
if (dbug) printf("traceop()\n");
	printf("FP=%6d; SP=%6d; SP[0]=%6d; PC=%6d: ",
	       fp, sp, fetch(sp), pc);
	printop(pc);
	printf("\n");
	}

printop(p)
int p;
{
	register char *ttrap;
if (dbug) printf("printop(%d)\n", p);

	ttrap = trap;
	switch (fetch(p)) {
	case ADD:       printf("ADD"); break;
	case SUB:       printf("SUB"); break;
	case MUL:       printf("MUL"); break;
	case NEG:       printf("NEG"); break;
	case TEST:      printf("TEST"); break;
	case POP:       printf("POP"); break;
	case PUSH:      printf("PUSH %d", fetch(p+1)); break;
	case AFP:       printf("AFP"); break;
	case ASP:       printf("ASP"); break;
	case IND:       printf("IND"); break;
	case JUMP:      printf("JUMP "); printlab(fetch(p+1)); break;
	case JUMPZ:     printf("JUMPZ "); printlab(fetch(p+1)); break;
	case CALL:      printf("CALL"); break;
	case RET:       printf("RET"); break;
	default:	printf("?WORD %d", fetch(p)); break;
		}
	trap = ttrap;
	}

printlab(addr)
int addr;
{
	/* Print label name (if we know it) */
	register int i;
if (dbug) printf("printlab(%d)\n", addr);

	if ((i = findval(LABELTYP, addr)) != -1) {
		printf("%s", symtxt[i]);
		}
	else {
		printf("%d", addr);
		}
	}

findval(t, v)
int t, v;
{
	register int i;
if (dbug) printf("findval(%d, %d)\n", t, v);

	for (i=0; i<symnext; ++i) {
		/* if the type-mask permits this table entry
		   and the value matches, we found it.
		*/
		if ((t & symtyp[i]) && (v == symval[i])) return(i);
		}
	return(-1);
	}

tb()
{
	register int i;
	register cnt;
if (dbug) printf("tb()\n");

	i = fp;
	trap = 0;
	printf("TraceBack:\n");
	cnt = 10;
	while (cnt-- && (!trap) && (i != OS)) {
		printlab(fetch(i + 3));
		printf("(%d)\n", fetch(i + 2));
		i = fetch(i);
		}
	if (trap) printf("Stack frame not valid.\n");
	}

leave(n)
{
if (dbug) printf("leave(%d)\n", n);
	if (n > 1) printf("tsam: aborting\n");
	exit(n);
	}

isalnu(c)
int c;
{
	return(isdigit(c) || isal2(c));
	}

isal2(c)
int c;
{
	return(((c >= 'A') && (c <= 'Z')) ||
	       ((c >= 'a') && (c <= 'z')) ||
	       (c == '_'));
	}

genw(d)
int d;
{
if (dbug) printf("genw(%d)\n", d);
	store(lc, d);
	++lc;
	}

upcase(c)
int c;
{
	return(islower(c) ? toupper(c) : c);
	}
SHAR_EOF
fi # end of overwriting check
if test -f 'tsam.doc'
then
	echo shar: will not over-write existing file "'tsam.doc'"
else
cat << \SHAR_EOF > 'tsam.doc'
Re: The assembler/emulator

        The Trivial Stack Machine (TSM) Assembler/eMulator is
now available for your testing pleasure.

        The program is named:

/cs3/cs641ix/hdietz/tsam

        And is used by:

1.  Compile your compiler: "cc proj3.c".

2.  Use it to compile your test program: "a.out <test.c >test.tsm".
    If you wish, the two factorial programs are avaliable as tests
    in the files: "/cs3/cs641ix/hdietz/rfact.c" for the recursive
    version and "/cs3/cs641ix/hdietz/fact.c" for the non-recursive.

3.  Run tsam to produce an assembly listing:

/cs3/cs641ix/hdietz/tsam -r test.tsm

4.  Run tsam to emulate (without trace):

/cs3/cs641ix/hdietz/tsam -at test.tsm 35

    Where 35 is set-up as the argument to main().


The options to the emulator are . . .
Usage:
tsam [+-a] [+-c] [+l#] [+-r] [+-t] <filename> <arg>

Default  Meaning of option
+a       make Assembly listing
+c       case wildcarded
+l10000  execution Limit is 10000
+r       Run the program
+t       Trace execution

        Send me mail if you find any bugs (I do not know of
any at this time). The source program for tsam, written in C,
is available in "/cs3/cs641ix/hdietz/tsam.c" -- it is not good
code, but many of you expressed curiosity.

                                        -hdietz
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
