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

	@(#)util.c	1.7	   12/15/83
*/

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

char null[];
char *opname[];

/* xnalloc - allocate address-indexed node*/
char *xnalloc(type,ar,off,xr,xt)	/* returns ptr to node alloced*/
int type;						/* data type*/
int ar;							/* address register*/
long off;						/* 8-bit offset*/
int xr;							/* index register*/
int xt;							/* index register type*/
{
	register struct indexnode *xp;

	xp = talloc(sizeof(*xp));
	xp->t_op = SYMBOL;
	xp->t_type = type;
	xp->t_sc = INDEXED;
	xp->t_reg = ar;
	xp->t_su = SU_ADDR;
	xp->t_offset = off;
	xp->t_xreg = xr;
	xp->t_xtype = xt;
	return(xp);
}

/* tcopy - expression tree copy*/
char *tcopy(tp,autof)					/* returns ptr to copied tree*/
struct tnode *tp;
int autof;								/* {A_DOPRE,A_DOPOST} */
{
	register char *p;
	register short op;

	op = tp->t_op;
	switch( op ) {

	case SYMBOL:
		if( tp->t_sc == EXTERNAL || tp->t_sc == EXTOFF )
			p = cenalloc(tp->t_type,tp->t_sc,tp->t_symbol);
		else {
			p = snalloc(tp->t_type,tp->t_sc,tp->t_offset,0,0);
			p->t_label = tp->t_label;
		}
		p->t_offset = tp->t_offset;
		p->t_reg = tp->t_reg;
		return(p);

	case CINT:
		return(cnalloc(tp->t_type,tp->t_value));

	case CLONG:
		return(lcnalloc(tp->t_type,tp->t_lvalue));

	case CFLOAT:		/*[vlh] 3.4 */
		return(fpcnalloc(tp->t_type,tp->t_lvalue));

	case DCLONG:
		p = lcnalloc(tp->t_type,tp->t_lvalue);
		p->t_op = DCLONG;
		return(p);

	case AUTOINC:		/*[mac] 4.2*/
	case AUTODEC:
		p = snalloc(tp->t_type,AUTO,0L,0,0);
		if( op == AUTODEC ) {
			if( (autof&A_DOPRE) != 0 )
				p->t_op = AUTODEC;
		}
		else if( op == AUTOINC ) {
			if( (autof&A_DOPOST) != 0 )
				p->t_op = AUTOINC;
		}
		p->t_reg = tp->t_reg;
		return(p);

	case POSTINC:
	case POSTDEC:
		if( (autof&A_DOPOST) == 0 )
			return(tcopy(tp->t_left,autof));
		goto copyop;
	case PREINC:
	case PREDEC:
		if( (autof&A_DOPRE) == 0 )
			return(tcopy(tp->t_left,autof));
copyop:
	default:
		if( ISASGOP(op) ) {
			if( (autof&A_DOPRE) == 0 )
				return(tcopy(tp->t_left,autof));
			autof |= A_DOPOST;		/*We'll come this way only once*/
		}
		p = tnalloc(op,tp->t_type,0,0,null,null);
		if( BINOP(op) )
			p->t_right = tcopy(tp->t_right,autof);
		p->t_left = tcopy(tp->t_left,autof);
		return(p);
	}
}

/* outaexpr - output address expression*/
outaexpr(tp,flags)				/* returns - none*/
struct tnode *tp;				/* pointer to tree*/
int flags;						/* flags (IMMED,LOFFSET,...)*/
{
	register short reg, lab;
	register long off;
	long l;

	if( tp->t_op == ADDR ) {
		tp = tp->t_left;
		putchar('#');
	}
	off = tp->t_offset;
	reg = tp->t_reg;
	lab = tp->t_label;
	switch( tp->t_op ) {
	
		case AUTOINC:
			if( (flags&A_DOPOST) != 0 )
				printf("(R%d)+",reg);
			else
				printf("(R%d)",reg);
			break;
		
		case AUTODEC:
			if( (flags&A_DOPRE) != 0 )
				printf("-(R%d)",reg);
			else
				printf("(R%d)",reg);
			break;
	
		case CINT:
			if( (flags&A_DOIMMED) != 0 )
				putchar('#');
			printf("%d",tp->t_value);
			break;
	
		case DCLONG:
		case CLONG:
		case CFLOAT:		/*[vlh] 3.4 */
			if( (flags&A_DOIMMED) != 0 )
				putchar('#');
			outlval(tp->t_lvalue);
			break;
	
		case SYMBOL:
			if( off ) {
				switch( tp->t_sc ) {
		
				default:
					printf("%ld+",off);
					break;

				case REGOFF:
					printf("%ld",off);
				case CINDR:
				case CLINDR:
				case CFINDR:	/* [vlh] 3.4 */
				case INDEXED:
					break;

				case REGISTER:
					error("invalid register expression");
					break;
				}
			}
			switch( tp->t_sc ) {
	
				case REGISTER:
					printf("R%d",reg);
					break;
		
				case REGOFF:
					printf("(R%d)",reg);
					break;
		
				case EXTERNAL:
					printf("_%.8s",tp->t_symbol);
					break;
		
				case EXTOFF:
					printf("_%.8s(R%d)",tp->t_symbol,reg);
					break;
		
				case STATIC:
					printf("L%d",lab);
					break;
		
				case STATOFF:
					printf("L%d(R%d)",lab,reg);
					break;

				case INDEXED:
					printf("%ld(R",off);	/* [vlh] 4.3, long offset */
					printf("%d,R%d",reg,tp->t_xreg);
					outatype(tp->t_xtype);
					putchar(')');
					break;

				case CINDR:
					printf("%ld",off);
					break;
		/*
		 * the following will work on: PDP-11, 68000, IBM-360, VAX, etc.
		 * it will not work on word machines or on machines where either
		 * longs two ints or two shorts.
		 */
				case CLINDR:
				case CFINDR:	/* [vlh] 3.4 */
					/*l.hiword = (short)tp->t_offset; l.loword = tp->t_ssp;*/
					l = tp->t_offset;	/* [vlh] 4.3, changed to long !!! */
					outlval(l);
					break;
		
				default:
					error("invalid storage class %d\n",tp->t_sc);
					break;
			}
			break;

		default:
			error("invalid operator %s\n",opname[tp->t_op]);
			break;
	}
}

/* outlval - output long value*/
/*		This is a big pain because the PDP-11 doesn't do long divides*/
/*		in hardware.*/
outlval(lval)
long lval;
{
	char digs[8];
	register short i, c;

	i = 0;
	do {
		digs[i++] = lval & 0xf;
		lval >>= 4;
		lval &= 0xfffffff;
	} while ( lval );
	putchar('$');
	while( --i >= 0 ) {
		c = digs[i];
		putchar(c>=10?c+('a'-10):c+'0');
	}
}

/* outtype - output 68000 type (null, .b, .l) depending on data type*/
outtype(type)
int type;
{
	if( ISFLOAT(type) || LONGORPTR(type) )
		printf(".l");
	else if( type == CHAR )
		printf(".b");
}

/* outatype - output address type (.l or null) depending on data type*/
outatype(type)
int type;
{
	if( LONGORPTR(type) || ISFLOAT(type) )
		printf(".l");
}

/* outextend - output register extension to long depending on type*/
outextend(tp,type,reg)						/* returns - none*/
struct tnode *tp;							/* tree to convert from*/
int type;								        /* type to convert to*/
int reg;								        /* register to convert*/
{
	if( (ISDREG(reg)) && !(LONGORPTR(tp->t_type)) && (LONGORPTR(type)) ) {
		if( UNSIGN(tp->t_type) )
			OUTUEXT(reg);
		else
			OUTEXT(reg);
	}
}

/* outrr - output register to register instruction*/
outrr(ins,r1,r2,tp)
char *ins;
int r1;
int r2;
struct tnode *tp;
{
	printf("%s",ins);
	if( ISAREG(r1) || ISAREG(r2) )
		outatype(tp->t_type);
	else
		outtype(tp->t_type);
	printf(" R%d,R%d\n",r1,r2);
}

/* outmovr - output "move[type] R1,R2" instruction*/
outmovr(r1,r2,tp)
int r1;
int r2;
struct tnode *tp;
{
	if( r1 != r2 )
		outrr("move",r1,r2,tp);
}

/* outcmpm - output "cmpm[type] (R1)+,(R2)+"*/
outcmpm(tp)
struct tnode *tp;
{
	register int lreg, rreg;

	if (!m68010) {	/* 68000 version cmpm ok !!! */
		printf("cmpm");
		outtype(tp->t_left->t_type);
		printf(" (R%d)+,(R%d)+\n",tp->t_left->t_reg,tp->t_right->t_reg);
	}
	else	/* generating code for 68010, micro code is bad for cmpm */
		fakecmpm(tp->t_left->t_type,tp->t_left->t_reg,tp->t_right->t_reg);
}

/* fakecmpm - output 68010 fake cmpm for bad microcode [vlh] 4.2 */
fakecmpm(type,lreg,rreg)
int type,lreg,rreg;
{
	printf("move");
	outtype(type);
	printf(" (R%d),R0\n",rreg);
	printf("cmp");
	outtype(type);
	printf(" (R%d),R0\n",lreg);
	if( ISFLOAT(type) || LONGORPTR(type) )
		printf("addq #4,R%d\naddq #4,R%d\n",lreg,rreg);
	else if( type == CHAR )
		printf("addq #1,R%d\naddq #1,R%d\n",lreg,rreg);
	else
		printf("addq #2,R%d\naddq #2,R%d\n",lreg,rreg);
}

/* outcreg - output reference to compiler temp register*/
outcreg(reg)
int reg;
{
	if( (DREG(reg)) > HICREG )
		error("expression too complex");
	printf("R%d",reg);
}

/* outcmp0 - output a compare with 0, special for address register*/
outcmp0(reg,tp)
int reg;
struct tnode *tp;
{
	if( ISAREG(reg) ) {
		printf("cmp");
		outatype(tp->t_type);
		printf(" #0,R%d\n",reg);
	}
	else {
		printf("tst");
		outtype(tp->t_type);
		printf(" R%d\n",reg);
	}
}

/* outrpush - output "move[type] R1,[-](sp)"*/
outrpush(reg,tp,pflag)
int reg;
struct tnode *tp;
int pflag;
{
	printf("move");
	outatype(tp->t_type);
	printf(" R%d,%s\n",reg,pflag?"-(sp)":"(sp)");
}

outdbra(dir,op,ltp,rtp,lab)
int dir, op, lab;
struct tnode *ltp, *rtp;
{
	if (!( (dir!=0 && op==NEQUALS) || (dir==0 && op==EQUALS)))
		return(0);
	if(!(ltp->t_op==PREDEC && rtp->t_op==CINT && rtp->t_value == -1 &&
			ISDREG(ltp->t_left->t_reg) && ltp->t_left->t_type==INT &&
			ltp->t_left->t_sc == REGISTER))
		return(0);
	
	printf("dbra R%d,L%d\n",ltp->t_left->t_reg,lab);
	return(1);
}

/* cenalloc - code generator external node allocation*/
/*		This may be coalesced into enalloc in parser.*/
char *cenalloc(type,sc,sym)		/* returns ptr to node alloced*/
int type;						/* type of symbol*/
int sc;							/* storage class*/
char *sym;						/* symbol name*/
{
	register struct extnode *ep;

	ep = talloc(sizeof(*ep));
	ep->t_op = SYMBOL;
	ep->t_type = type;
	ep->t_sc = sc;
	ep->t_su = 0;
	ep->t_offset = 0;
	symcopy(sym,ep->t_symbol);
	return(ep);
}

/*popstack - clear off the stack after a call if necessary */
popstack(nb)
{
	if (nb > 0 && nb <= 8)
		printf("addq.l #%d,sp\n",nb);
	else if (nb > 0)
		printf("adda.l #%d,sp\n",nb);
}
