/*
	Copyright 1983
	Alcyon Corporation
	8716 Production Ave.
	San Diego, Ca.  92121

	@(#)main.c	1.7	12/28/83
*/

char *version = "@(#)main.c	1.7 12/28/83";

/**
 *	ALCYON C Compiler for the Motorola 68000 - Code Generator
 *
 *	Called from c68:
 *
 *		c168 icode link asm
 *
 *	icode:		parsed intermediate code with some assembly code
 *              preceded by left parens.
 *
 *	link:		contains the procedure link and movem instructions.
 *
 *	asm:	    output assembler code for as68.
 *
 *	The basic structure of the code generator is as follows:
 *
 *	main				- main routine
 *		readicode		- code generation driven by intermediate code
 *
**/

#include "cgen.h"
#include "cskel.h"

char *opap;
short gflag;
short nextlabel	= 10000;
char null[] = "";
short lflag = 1;
char source[PATHSIZE] = "";
#ifdef VERSADOS 
int inerr;
#endif

char *readtree();
char *readsym();

/* main - main routine, handles arguments and files*/
main(argc,argv)						/* returns - none*/
int argc;							/* arg count*/
char **argv;						/* arg pointers*/
{
	register char *q, *calledby;

	calledby = *argv++;
#ifdef	CPM
	calledby = "c168";
#endif
	if( argc < 4 )
		usage(calledby);
	if( fopen(*argv,&ibuf,0) < 0 )	/* 3rd arg for versados */
		ferror("can't open %s",*argv);
	if( fopen(*++argv,&lbuf,0) < 0)
		ferror("can't open %s",*argv);
	if( fcreat(*++argv,&obuf,0) < 0 )
		ferror("can't create %s",*argv);

	for( argc -= 4; argc--; ) {
		q = *++argv;
		if( *q++ != '-' )
			usage(calledby);
		while( 1 ) {
			switch( *q++ ) {

			case 'a':		/* [vlh] 4.2, alter ego of the '-L' flag */
				lflag = 0;
				continue;

			case 'f':
				fflag++;
				continue;

			case 'g':	/* [vlh] 4.2 generate line labels for cdb */
				gflag++;
				continue;
			
			case 'D':
			case 'd':			/*sw*/
				dflag++;
				continue;
#ifdef DEBUG
			case 'c':
				cflag++;
				continue;

			case 'e':
				eflag++;
				continue;

			case 'm':
				mflag++;
				continue;

			case 'o':
				oflag++;
				continue;
#endif
			case 'L':	/* [vlh] 4.2, OBSOLETE */
			case 'l':	/*sw*/
				lflag++;
				continue;

			case 'T':	/* [vlh] 4.2 generates code for the 68010 */
			case 't':	/*sw*/
				m68010++;
				continue;

			case '\0':
				break;

			default:
#ifndef VERSADOS
				usage(calledby);
#else
				break;	
#endif
			}
			break;
		}
	}

	readicode();
	myfflush(&obuf);
	exit(errcnt!=0);
}

/* readicode - read intermediate code and dispatch output*/
/*		This copies assembler lines beginning with '(' to assembler*/
/*		output and builds trees starting with '.' line.*/
readicode()								/*returns - none*/
{
	register short c;
	register struct tnode *tp;

	while( (c=getc(&ibuf)) > 0 ) {
		switch(c) {

		case '.':
			lineno = readshort();
			readfid();
			opap = exprarea;
			if( tp = readtree() ) {
				PUTEXPR(cflag,"readicode",tp);
				switch( tp->t_op ) {

				case INIT:
					outinit(tp->t_left);
					break;

				case CFORREG:
					outcforreg(tp->t_left);
					break;

				case IFGOTO:
					outifgoto(tp->t_left,tp->t_type,tp->t_su);
					break;

				default:
					outexpr(tp);
					break;
				}
			}
			else
				outline();
			break;

		case '(':
			while( (c=getc(&ibuf)) != '\n' )
				putchar(c);
			putchar(c);
			break;

		case '%':		/* [vlh] 4.2 */
			while( (c=getc(&ibuf)) != '\n' )
				;	/* skip over carriage return */
			while( (c=getc(&lbuf)) != '%' && c != -1)
				putchar(c);
			if (c == -1)
				ferror("early termination of link file");
			break;

		default:
			error("intermediate code error %c,%d",c,c);
			break;
		}
	}
}

/* readtree - recursive intermediate code tree read*/
char *
readtree()						/* returns ptr to expression tree*/
{
	register short op, type, sc;
	register struct tnode *tp, *rtp;
	char sym[SSIZE];

	if( (op=readshort()) <= 0 )
		return(0);
	type = readshort();
	switch( op ) {

	case SYMBOL:
		if( (sc=readshort()) == EXTERNAL )
			tp = cenalloc(type,sc,readsym(sym));
		else
			tp = snalloc(type,sc,(long)readshort(),0,0);
		break;

	case CINT:
		tp = cnalloc(type,readshort());
		break;

	case CLONG:
		tp = lcnalloc(type,readlong());	/* [vlh] 4.1 was two readshort's */
		break;

	case CFLOAT:	/* [vlh] 3.4 */
		tp = fpcnalloc(type,readlong());	/* [vlh] 4.1 was two readshort's */
		break;

	case IFGOTO:
	case BFIELD:
		sc = readshort();
		if( tp = readtree() )
			tp = tnalloc(op,type,sc,0,tp,null);
		break;

	default:
		if( BINOP(op) ) {
			if( !(tp=readtree()) )
				return(0);
			if( !(rtp=readtree()) )
				return(0);
			tp = tnalloc(op,type,0,0,tp,rtp);
		}
		else if( tp = readtree() )
			tp = tnalloc(op,type,0,0,tp,null);
		break;
	}
	return(tp);
}

/* readfid - read source filename out of intermediate file */
readfid()
{
	register char *p;

	p = &source[0];
	while( (*p = getc(&ibuf)) != '\n') 
		p++;
	*p = 0;
}

/* readshort - reads an integer value from intermediate code*/
short
readshort()
{
	register short c;
	register short i;

	i = 0;
	while(1) {
		switch( c = getc(&ibuf) ) {

		case '.':
		case '\n': 
			return(i);

		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			i <<= 4;
			i += (c-'0');
			break;

		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
			i <<= 4;
			i += (c-('a'-10));
			break;

		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
			i <<= 4;
			i += (c-('A'-10));
			break;

		default:
			error("intermediate code error - %c,%d",c,c);
		}
	}
}

/* readlong - reads a long value from intermediate code*/
long
readlong()		/* [vlh] 4.1 */
{
	long l;
	register unsigned short w1, w2;
	register short c, onedot;

	w2 = 0; onedot = 0;
	while(1) {
		switch( c = getc(&ibuf) ) {

		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			w2 <<= 4;
			w2 += (c-'0');
			break;

		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
			w2 <<= 4;
			w2 += (c-('a'-10));
			break;

		case 'A':			/*sw Hex in upper case as well...*/
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
			w2 <<= 4;
			w2 += (c-('A'-10));
			break;

		case '.':
			if (!onedot++) {
				w1 = w2;
				w2 = 0;
				continue;
			}
		case '\n': 
			if (onedot) {
				l.hiword = w1;
				l.loword = w2;
				return(l);
			}
		default:
			error("intermediate code error - %c,%d",c,c);
		}
	}
}

/* readsym - read a symbol from intermediate code*/
char *readsym(sym)
char *sym;
{
	register short i, c;
	register char *s;

	for( i = SSIZE, s = sym; (c=getc(&ibuf)) != '\n'; )
		if( --i >= 0 )
			*s++ = c;
	if( i > 0 )
		*s = '\0';
	return(sym);
}

/* error - output an error message*/
error(s,x1,x2,x3,x4,x5,x6)
char *s;
int x1, x2, x3, x4, x5, x6;
{
	errcnt++;
#ifdef REGULUS
	if( lineno != 0 )
		printf((char *)STDERR,"\"%s\", ** %d: ",source,lineno);
	printf((char *)STDERR,s,x1,x2,x3,x4,x5,x6);
	cputc('\n',STDERR);
#endif
#ifdef	VERSADOS
	inerr = 1;
	if( lineno != 0 )
		printf("\"%s\", ** %d: ",source,lineno);
	printf(s,x1,x2,x3,x4,x5,x6);
	printf("\n");
	inerr = 0;
#endif
#ifdef	CPM
	if( lineno != 0 )
		fprintf(stderr,"\"%s\", ** %d: ",source,lineno);
	fprintf(stderr,s,x1,x2,x3,x4,x5,x6);
	fprintf(stderr,"\n");
#endif
#ifdef	WHITESM
	if( lineno != 0 )
		fprintf(stderr,"\"%s\", ** %d: ",source,lineno);
	fprintf(stderr,s,x1,x2,x3,x4,x5,x6);
	fprintf(stderr,"\n");
#endif
#ifdef	UNIX
	if( lineno != 0 )
		fprintf(stderr,"\"%s\", ** %d: ",source,lineno);
	fprintf(stderr,s,x1,x2,x3,x4,x5,x6);
	fprintf(stderr,"\n");
#endif
}

/* warning - output a warning message*/
warning(s,x1,x2,x3,x4,x5,x6)
char *s;
int x1, x2, x3, x4, x5, x6;
{
#ifdef REGULUS
	if( lineno != 0 )
		printf((char *)STDERR,"\"%s\", ** %d: (warning) ",source,lineno);
	else
		printf((char *)STDERR,"(warning) ");
	printf((char *)STDERR,s,x1,x2,x3,x4,x5,x6);
	cputc('\n',STDERR);
#endif
#ifdef VERSADOS
	inerr = 1;
	if( lineno != 0 )
		printf("\"%s\", ** %d: (warning) ",source,lineno);
	else
		printf("(warning) ");
	printf(s,x1,x2,x3,x4,x5,x6);
	printf("\n");
	inerr = 0;
#endif
#ifdef CPM
	if( lineno != 0 )
		fprintf(stderr,"\"%s\", ** %d: (warning) ",source,lineno);
	else
		fprintf(stderr,"(warning) ");
	fprintf(stderr,s,x1,x2,x3,x4,x5,x6);
	fprintf(stderr,"\n");
#endif
#ifdef WHITESM
	if( lineno != 0 )
		fprintf(stderr,"\"%s\", ** %d: (warning) ",source,lineno);
	else
		fprintf(stderr,"(warning) ");
	fprintf(stderr,s,x1,x2,x3,x4,x5,x6);
	fprintf(stderr,"\n");
#endif
#ifdef UNIX
	if( lineno != 0 )
		fprintf(stderr,"\"%s\", ** %d: (warning) ",source,lineno);
	else
		fprintf(stderr,"(warning) ");
	fprintf(stderr,s,x1,x2,x3,x4,x5,x6);
	fprintf(stderr,"\n");
#endif
}

/* ferror - output error message and die*/
ferror(s,x1,x2,x3,x4,x5,x6)
char *s;
int x1, x2, x3, x4, x5, x6;
{
	error(s,x1,x2,x3,x4,x5,x6);
	exit(1);
}

/* tnalloc - allocate binary expression tree node*/
/*	returns ptr to node made.*/
char *tnalloc(op,type,info,dummy,left,right)
int op;						/* operator*/
int type;					/* resultant node type*/
int info;					/* info field*/
int dummy;					/* dummy field - used to match pass1 args*/
struct tnode *left;			/* left sub-tree*/
struct tnode *right;		/* righst sub-tree*/
{
	register struct tnode *tp;

	tp = talloc(sizeof(*tp));
	tp->t_op = op;
	tp->t_type = type;
	tp->t_su = info;			/* info for bit-field & condbr's*/
	tp->t_left = left;
	tp->t_right = right;
	return(tp);
}

/* cnalloc - allocate constant expression tree node*/
char *cnalloc(type,value)	/* returns pointer to node alloced*/
int type;						/* type of constant*/
int value;						/* value of constant*/
{
	register struct conode *cp;

	cp = talloc(sizeof(*cp));
	cp->t_op = CINT;
	cp->t_type = type;
	cp->t_value = value;
	return(cp);
}

/* lcnalloc - allocate constant expression tree node*/
char *lcnalloc(type,value)	/* returns pointer to node alloced*/
int type;						/* type of constant*/
long value;						/* value of constant*/
{
	register struct lconode *cp;

	cp = talloc(sizeof(*cp));
	cp->t_op = CLONG;
	cp->t_type = type;
	cp->t_lvalue = value;
	return(cp);
}

/* fpcnalloc - allocate constant expression tree node*/
char *fpcnalloc(type,value)	/* returns pointer to node alloced*/
int type;						/* type of constant*/
long value;						/* value of constant*/
{								/* [vlh] 3.4 */
	register struct lconode *cp;

	cp = talloc(sizeof(*cp));
	cp->t_op = CFLOAT;
	cp->t_type = type;
	cp->t_lvalue = value;
	return(cp);
}

/* talloc - allocate expression tree area*/
char *talloc(size)				/* returns pointer to area alloced*/
int size;						/* number of bytes to alloc*/
{
	register char *p;

	p = opap;
	if( p + size >= &exprarea[EXPSIZE] )
		ferror("expression too complex");
	opap = p + size;
	return(p);
}

/* symcopy - copy symbol*/
symcopy(sym1,sym2)					/* returns - none*/
char *sym1;						/* from symbol*/
char *sym2;						/* to symbol*/
{
	register char *p, *q;
	register short i;

	for( p = sym1, q = sym2, i = SSIZE; --i >= 0; )
		*q++ = (*p ? *p++ : '\0');
}

/* usage - output usage message*/
usage(calledby,num)
char *calledby;
int num;
{
#ifdef DEBUG
	ferror("usage: %s icode link asm [-DTacemov]",calledby);
#else
	ferror("usage: %s icode link asm [-Tav]",calledby);
#endif
}

/* cputc - put a character to a file descriptor (used by error) */
cputc(c, fn)
char c;
int fn;
{
#ifdef VERSADOS
	versaputchar(c);
#else
	if (fn == STDERR)
		write(STDERR, &c, 1);
	else
		putchar(c);
#endif
}

/**
 * putchar - special version
 *		This allows the use of printf for error messages, debugging
 *		output and normal output.
**/
putchar(c)							/* returns - none*/
char c;								/* character to output*/
{
#ifdef VERSADOS
	if (inerr) {
		versaputchar(c);
		return;
	}
#endif
	if( dflag > 1 )
		write(1,&c,1);			/*to standard output*/
	putc(c,&obuf);				/*put to assembler file*/
}

#ifdef VERSADOS

#define STDOUT 1

struct iob versfout = { STDOUT, BSIZE, &versfout.cbuf[0]};

versaputchar(c)
char c;
{
	if (c == '\n') {	/* end of line */
		if (versaflush())	/* write one line */
			return(-1);
		return(c);
	}

	/* buffered output */
	if (versfout.cc <= 0) {
		versfout.cp = &(versfout.cbuf[0]);
		if (write(versfout.fd,versfout.cp,BSIZE) != BSIZE)
			return(-1);
		versfout.cc = BSIZE;
	}
	*(versfout.cp)++ = c;
	versfout.cc--;
	return(c);
}

versaflush()
{
	register short size, fildes;

	if ((size = (BSIZE - versfout.cc)) == 0)
		return(0);
	versfout.cc = BSIZE;
	versfout.cp = &(versfout.cbuf[0]);
	fildes = (versfout.fd <= STDERR) ? 6 : versfout.fd;
	if (write(fildes,versfout.cp,size) < 0)
		return(-1);
	return(0);
}
#else
#	ifdef VAX11
	getc(ibuf)
	struct iob *ibuf;
	{
		if (ibuf->cc <= 0) {
			ibuf->cp = &(ibuf->cbuf[0]);
			ibuf->cc = read(ibuf->fd,ibuf->cp,BSIZE);
		}
		if (ibuf->cc <= 0)
			return(-1);
		ibuf->cc--;
		return((int)(*(ibuf->cp)++)&0xff);
	}

	fopen(fname,ibuf)
	char *fname;
	register struct iob *ibuf;
	{
		ibuf->cc = 0;   /* no chars */
		ibuf->fd = open(fname,0);
		return(ibuf->fd);
	}
#	endif
#endif

myfflush(mybuf)
register struct iob *mybuf;
{
	register i;

	i = BSIZE - mybuf->cc;
	mybuf->cc = BSIZE;
	mybuf->cp = &(mybuf->cbuf[0]);
	if (write(mybuf->fd,mybuf->cp,i) != i)
		return(-1);
	return(0);
}

#ifdef CPM
printf(string,a,b,c,d,e,f,g)
char *string;
int a,b,c,d,e,f,g;
{
	char area[256];
	register char *p;

	sprintf(area,string,a,b,c,d,e,f,g);
	for(p = &area[0]; *p ; p++ )
		putchar(*p);
}
#endif

#ifdef WHITESM
printf(string,a,b,c,d,e,f,g)
char *string;
int a,b,c,d,e,f,g;
{
	char area[256];
	register char *p;

	sprintf(area,string,a,b,c,d,e,f,g);
	for(p = &area[0]; *p ; p++ )
		putchar(*p);
}
#endif
