/*
 *      Simple Minded Assembler Program
 */

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

/*
 *      Error Numbers
 */

#define DD      1               /* doubly defined symbol */
#define LN      2               /* too long a name */
#define LO      3               /* too large an octal constant */
#define XP      4               /* cross page reference (pass2) */
#define SX      5               /* some obscure syntax error */
#define UNDEF   6               /* undefined symbol (pass2) */

/*
 *      Symbol Types
 */

#define LABEL8  0
#define MRI     1
#define OPR     2
#define IOT     3
#define INDIR   4

/*
 *      Input Tokens
 */

#define LABELS          0
#define COMMENT         1
#define OCTAL           2
#define ORIGIN          3
#define ENDING          4
#define IDENTIFIER      5
#define NIL             6
#define ERRORS          7

/*
 *      Detailed Input Tokens (Pass2)
 */

#define XLAB8           0
#define XCOMMENT        1
#define XOCTAL          2
#define XORIGIN         3
#define XENDING         4
#define XNIL            5
#define XERRORS         6
#define XVARIABLE       7
#define XMRIOPCODE      8
#define XIOTOPCODE      9
#define XOPROPCODE      10
#define XINDIRECTBIT    11

/*
 *      Lexical States
 */

#define START   0
#define IDENT   1
#define INOCT   2
#define DONE    3

/*
 *      Symbol Table Entry
 */

typedef struct entry    entry;
struct entry    {
	char    name[8];
	int     stype;
	int     value;
};

/*
 *      Global Variables
 */

int     error;          /* flag for fatal error */
int     ended;          /* flag for end of source file */
char    line[128];      /* source input line */
int     numchars;       /* number of characters in line */
char    *posn;          /* where we are on line */
entry   symtab[512];    /* symbol table */
int     numsym;         /* number of defined symbols */
int     where;          /* location counter */
FILE    *source;        /* source file */
FILE    *object;        /* object file */
FILE    *symbols;       /* standard symbol file */
char    newsym[8];      /* most recent identifier */
int     newval;         /* most recent number */
int     printval;
int     passno;         /* pass number */
int     listing;        /* flag to say if want listing */
int     argextra;       /* count of command line args used as flags, rather than file names. */
int     errpass = 0;        /* pass on which error report (if any) last generated */

errs(errno, passno)
int     errno;
int     passno;
{
	char    *p;
	error++;
	if (errpass != passno)     {
		errpass = passno;
		printf("\n\nAssembly error detected in Pass %d\n", passno);
	}
	printf("\n\nError occurred in or about the line:\n");
	printf("%s\n", line);

	for (p = line; p < posn; p++)
		putchar(' ');

	printf("^\n");

	for (p = line; p < posn; p++)
		putchar(' ');

	printf("|\n");
	printf("\n\nError Type: ");

	switch (errno)  {

	case XP:
		printf("Cross Page Reference\n");
		break;

	case DD:
		printf("doubly defined symbol, %s\n", newsym);
		break;

	case LN:
		printf("too long a name, %s\n", newsym);
		break;

	case LO:
		printf("too large an octal constant\n");
		break;

	case SX:
		printf("syntax error\n");
		break;

	case UNDEF:
		printf("undefined symbol, %s\n", newsym);
		break;

	}
	skipline();
}

andsymtab()
{
	int     i;
	entry   *e;

	fprintf(object, "%d\n", numsym + 1);

	for (e = symtab, i = 0; i <= numsym; e++, i++)
		fprintf(object, "%-8s %d %04o\n", e -> name, e -> stype, e -> value);

	fprintf(object, "$\n");
}

char    *pxtyp[] =      {
	"label",
	"mri",
	"opr",
	"iot",
	"indirect"
};

printsymtab()
{
	int     i,j;
	entry   *e;

	printf("\nSymbol Table:\n\n");
	printf("%8s  %10s  %6s    %8s  %10s  %6s\n\n", "Name", "Type", "Value", "Name", "Type", "Value");

	j = 1;
	for (e = symtab, i = 0; i <= numsym; e++, i++)
	if(e -> stype == LABEL8) {
		printf("%8s  ", e -> name);
		printf("%10s  ", pxtyp[e -> stype]);
		printf("  %04o", e -> value);

		j++;
		if (j % 2)
			putchar('\n');
		else
			printf("    ");
	}
}

tab(tabpos)
int     tabpos;
{
	int     i;

	for (i = 0; i < tabpos; i++)
		putchar(' ');
}

dumpline()
{

	printf("%s\n", line);
}

skipline()
{

	while (*posn)
		posn++;
}

getline()
{
	int     ch;
	int     i;

	numchars = 0;
	posn = line;

	while ((ch = getc(source)) != '\n' && ch != EOF)        {
		numchars++;
		*posn++ = (isupper(ch) ? tolower(ch) : ch);
	}
	if(ch==EOF) {
		fprintf(stderr,"unexpected end of file\n");
		exit(1);
		}

	*posn++ = ' ';

	for (i = numchars; i < 127; i++)
		*posn++ = 0;

	posn = line;
	numchars += 2;
}

nextsym()
{
	int     state;
	int     ch;
	int     octdigit;
	int     alphabetic;
	int     numeric;
	int     dollars;
	int     comma;
	int     star;
	int     slash;
	int     space;
	int     endln;
	int     readtype;
	int     octval;
	int     symlength;
	int     errno;
	int     i;
	char    symbuf[8];

	state = START;
	octval = 0;
	symlength = 0;

	for (i = 0; i < 8; i++)
		symbuf[i] = 0;

	while (state != DONE)   {
		if (posn - line > numchars)
			getline();

		ch = *posn++;

		alphabetic = isalpha(ch);
		numeric = isdigit(ch);
		octdigit = (ch >= '0' && ch <= '7');
		dollars = (ch == '$');
		star = (ch == '*');
		slash = (ch == '/');
		space = (ch == ' ' || ch == '\t');
		endln = (ch == 0);
		comma = (ch == ',');

		switch (state)  {

		case START:
			if (star)       {
				readtype = ORIGIN;
				state = INOCT;
			}
			else if (slash) {
				readtype = COMMENT;
				state = DONE;
			}
			else if (dollars)       {
				readtype = ENDING;
				state = DONE;
			}
			else if (alphabetic)    {
				readtype = IDENTIFIER;
				state = IDENT;
				symbuf[symlength++] = ch;
			}
			else if (octdigit)      {
				readtype = OCTAL;
				state = INOCT;
				octval = (ch - '0');
			}
			else if (endln) {
				readtype = NIL;
				state = DONE;
			}
			else if (!space)        {
				errno = SX;
				readtype = ERRORS;
				state = DONE;
			}

			continue;

		case INOCT:
			if (octdigit)   {
				if (octval >= 512)      {
					errno = LO;
					readtype = ERRORS;
					state = DONE;
				}
				else
					octval = (octval << 3) + (ch - '0');
			}
			else if (space || endln)
				state = DONE;
			else    {
				errno = SX;
				readtype = ERRORS;
			}

			continue;

		case IDENT:
			if (alphabetic || numeric)      {
				if (symlength < 6)
					symbuf[symlength++] = ch;

				if (symlength > 6)      {
					strcpy(newsym, symbuf);
					errno = LN;
					readtype = ERRORS;
					state = DONE;
				}
			}
			else if (space)
				state = DONE;
			else if (comma) {
				state = DONE;
				readtype = LABELS;
			}

			continue;
		}
	}

	if (readtype == ERRORS)
		errs(errno, passno);

	if (readtype == LABELS || readtype == IDENTIFIER)
		strcpy(newsym, symbuf);

	if (readtype == ORIGIN || readtype == OCTAL)
		newval = octval;

	return (readtype);
}

findposn()
{
	int     top;
	int     bottom;
	int     sposn;
	int     found;

	top = numsym;
	bottom = 0;
	found = 0;

	while (!found)  {
		sposn = (top - bottom) / 2 + bottom;

		if (strcmp(newsym, symtab[sposn].name) == 0)
			found++;
		else if (strcmp(newsym, symtab[sposn].name) > 0)
			bottom = ++sposn;
		else
			top = sposn - 1;

		found = (found || (bottom > top));
	}

	return (sposn);
}

xnextsym()
{
	int     temp;
	int     sympos;
	int     xtemp;

	temp = nextsym();

	switch (temp)   {

	case LABELS:
		xtemp = XLAB8;
		break;

	case COMMENT:
		xtemp = XCOMMENT;
		break;

	case OCTAL:
		xtemp = XOCTAL;
		break;

	case ORIGIN:
		xtemp = XORIGIN;
		break;

	case ENDING:
		xtemp = XENDING;
		break;

	case NIL:
		xtemp = XNIL;
		break;

	case ERRORS:
		xtemp = XERRORS;
		break;

	case IDENTIFIER:
		sympos = findposn();
		if (strcmp(symtab[sympos].name, newsym) != 0)   {
			xtemp = XERRORS;
			errs(UNDEF, passno);
		}
		else    {
			switch (symtab[sympos].stype)   {

			case LABEL8:
				xtemp = XVARIABLE;
				break;

			case MRI:
				xtemp = XMRIOPCODE;
				break;

			case IOT:
				xtemp = XIOTOPCODE;
				break;

			case OPR:
				xtemp = XOPROPCODE;
				break;

			case INDIR:
				xtemp = XINDIRECTBIT;
				break;

			}

			newval = symtab[sympos].value;
		}

		break;
	}

	return (xtemp);
}

insertsymbol()
{
	int     sposn;
	int     n;

	sposn = findposn();

	if (strcmp(newsym, symtab[sposn].name) == 0)
		errs(DD, passno);
	else    {
		n = numsym;

		while (n >= sposn)      {
			symtab[n + 1] = symtab[n];
			n--;
		}

		strcpy(symtab[sposn].name, newsym);
		symtab[sposn].stype = LABEL8;
		symtab[sposn].value = where;
		numsym++;
	}
}

pass1(argc, argv)
int     argc;
char    **argv;
{

	if ((source = fopen(argv[1+argextra], "r")) == NULL)     {
		fprintf(stderr, "assemble: Cannot open %s\n", argv[1+argextra]);
		exit(1);
	}

	where = 0;
	ended = 0;
	numchars = 0;
	passno = 1;
	posn = line;

	while (!ended)       {
		switch (nextsym())      {

		case COMMENT:
			skipline();
			break;

		case ORIGIN:
			where = newval;
			break;

		case LABELS:
			insertsymbol();
			break;

		case ENDING:
			ended++;
			break;

		case OCTAL:
		case IDENTIFIER:
			where++;
			skipline();
			break;

		}
	}

	if(listing) printf("\n\n");

	if ( listing)    {
		printf("SMAP    Pass 1, Symbol table listing:\n\n");
		printsymtab();
		printf("\n\n");
	}
}

writecode(cval, next)
int     cval;
int     next;
{

	if(!error)fprintf(object, " %04o\n", cval);
	if(listing) {

		printf("%04o", where);
		tab(4);
		printf("%04o", cval);
		tab(8);
		dumpline();
	}
	where++;
	if (!(next == XCOMMENT || next == XNIL))
		errs(SX, passno);
	else
		skipline();
}

iots()
{
	int     next;

	printval = newval;
	next = xnextsym();

	while (next == XOCTAL)  {
		printval |= newval;
		next = xnextsym();
	}

	writecode(printval, next);
}

operate()
{
	int     next;

	printval = newval;
	next = xnextsym();

	while (next == XOCTAL || next == XOPROPCODE)    {
		printval |= newval;
		next = xnextsym();
	}

	writecode(printval, next);
}

memrefs()
{
	int     next;
	int     temp;
	int     pcp;
	int     dp;

	printval = newval;
	next = xnextsym();

	if (next == XINDIRECTBIT)       {
		printval |= newval;
		next = xnextsym();
	}

	if (!(next == XOCTAL || next == XVARIABLE))
		errs(SX, passno);
	else    {
		if (newval < 128)       {
			printval |= newval;
			writecode(printval, xnextsym());
		}
		else    {
			pcp = where & 07600;
			dp = newval & 07600;

			if (pcp != dp)
				errs(XP, passno);
			else    {
				temp = newval & 0177;
				temp |= 128;
				printval |= temp;
				writecode(printval, xnextsym());
			}
		}
	}
}

pass2(argc, argv)
int     argc;
char    **argv;
{
	char    *s;

	rewind(source);
	ended = 0;
	numchars = 0;
	posn = line;
	passno = 2;

	if (argc < (3+argextra))
		s = "object";
	else
		s = argv[2+argextra];

	if ((object = fopen(s, "w")) == NULL)   {
		fprintf(stderr, "assemble: Cannot create %s\n", s);
		exit(1);
	}

	if(listing) printf("\n\nSMAP    Pass 2 Assembly Listing.\n\n\n");

	while (!ended )       {
		printval = 0;

		switch (xnextsym())     {

		case XINDIRECTBIT:
			errs(SX,passno);
			continue;

		case XCOMMENT:
			if(listing) {
				tab(20);
				dumpline();
			}
			skipline();
			continue;

		case XORIGIN:
			if(!error)fprintf(object, "*%04o\n", newval);
			where = newval;
			if(listing) {
				tab(20);
				dumpline();
			}
			if (xnextsym() != XNIL)
				errs(SX, passno);

			continue;

		case XOCTAL:
		case XVARIABLE:
			writecode(newval, xnextsym());
			continue;

		case XMRIOPCODE:
			memrefs();
			continue;

		case XIOTOPCODE:
			iots();
			continue;

		case XOPROPCODE:
			operate();
			continue;

		case XENDING:
			ended++;
			continue;

		}
	}

	if(!error)fprintf(object, "$\n");

	if (error)
		unlink(s);
	else
		andsymtab();
}

initialise()
{
	int     i;
	int     j;

	error = 0;

	if ((symbols = fopen("/usr/pub/cs/211/source/symbols", "r")) == NULL)  {
		fprintf(stderr, "assemble: Cannot open symbols file\n");
		exit(1);
	}

	fgets(line, 128, symbols);
	sscanf(line, "%d", &numsym);

	for (i = 0; i < numsym; i++)
		fscanf(symbols, "%s %d %o", symtab[i].name, &symtab[i].stype, &symtab[i].value);

	numsym--;
	fclose(symbols);
}

main(argc, argv)
int     argc;
char    **argv;
{

	if (argc < 2)   {
		fprintf(stderr, "Usage: assemble [-l] source [object]\n");
		exit(1);
	}
	listing = 0;
	argextra = 0;
	if (argv[1][0] == '-')
	{
		argextra = 1;
		if((argc>2) && (argv[1][1] == 'l')) listing = 1;
		else {
			fprintf(stderr,"illegal option given to assemble\n");
			exit(1);
		}
	}
	initialise();
	zeroline();
	pass1(argc, argv);
	zeroline();

	if (error)   printf("\n\nAssembly errors in Pass 1\nError reports from Pass 2 may not be valid\n");
	pass2(argc, argv);

	if (!error)
		printf("\n\n\tassembled ok\n\n");
}

zeroline()
{
	char    *p;
	int     i;

	for (p = line, i = 0; i < 128; i++)
		*p++ = 0;
}
