
/*
	Copyright 1981
	Alcyon Corporation
	8474 Commerce Av.
	San Diego, Ca.  92121
*/

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

/* 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*/
int 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)							/* returns ptr to copied tree*/
struct tnode *tp;
{
	register char *p;

	switch( tp->t_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 DCLONG:
		p = lcnalloc(tp->t_type,tp->t_lvalue);
		p->t_op = DCLONG;
		return(p);

	default:
		if( binop(tp->t_op) )
			return(tnalloc(tp->t_op,tp->t_type,0,0,tcopy(tp->t_left),
					tcopy(tp->t_right)));
		if( unaryop(tp->t_op) )
			return(tnalloc(tp->t_op,tp->t_type,0,0,tcopy(tp->t_left),
					null));
		error("tcopy op=%d",tp->t_op);
		return(tp);
	}
}

/* outaexpr - output address expression*/
outaexpr(tp,flags)						/* returns - none*/
struct tnode *tp;						/* pointer to tree*/
int flags;								/* flags (IMMED,LOFFSET,...)*/
{
	register int off, reg, lab;
	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:
		printf("(R%d)+",reg);
		break;
	
	case AUTODEC:
		printf("-(R%d)",reg);
		break;
	
	case CINT:
		if( flags & IMMED )
			putchar('#');
		printf("%d",tp->t_value);
		break;
	
	case DCLONG:
	case CLONG:
		if( flags & IMMED )
			putchar('#');
		outlval(tp->t_lvalue);
		break;
	
	case SYMBOL:
		if( off ) {
			switch( tp->t_sc ) {
	
			default:
				printf("%d+",off);
				break;

			case REGOFF:
				printf("%d",off);
			case CINDR:
			case CLINDR:
			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("%d(R%d,R%d",off,reg,tp->t_xreg);
			outatype(tp->t_xtype);
			putchar(')');
			break;

		case CINDR:
			printf("%d",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:
			l.hiword = tp->t_offset;
			l.loword = tp->t_ssp;
			outlval(l);
			break;
	
		default:
			error("invalid storage class %d\n",tp->t_sc);
			break;
		}
		break;

	default:
		error("invalid operator %d\n",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 int 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( 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) )
		printf(".l");
}

/* outext - output register sign extension*/
outext(reg)
int reg;
{
	printf("ext.l R%d\n",reg);
}

/* outuext - output unsigned to long register extension*/
outuext(reg)
int reg;
{
	printf("swap R%d\nclr R%d\nswap R%d\n",reg,reg,reg);
}

/* 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) == 0 && longorptr(type) ) {
		if( unsign(tp->t_type) )
			outuext(reg);
		else
			outext(reg);
	}
}

/* outswap - output swap register instruction*/
outswap(reg)
int reg;
{
	printf("swap R%d\n",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);
}

/* outaddr - output "add[type] R1,R2" instruction*/
outaddr(r1,r2,tp)
int r1;
int r2;
struct tnode *tp;
{
	outrr("add",r1,r2,tp);
}

/* outcmpm - output "cmpm[type] (R1)+,(R2)+"*/
outcmpm(tp)
struct tnode *tp;
{
	printf("cmpm");
	outtype(tp->t_left->t_type);
	printf(" (R%d)+,(R%d)+\n",tp->t_left->t_reg,tp->t_right->t_reg);
}

/* 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,%c(sp)\n",reg,pflag?'-':'\0');
}

/* popstack - output instruction to pop stack desired number of bytes*/
/*		Makes use of special case instructions to save a word.*/
popstack(nb)							/* returns - none*/
int nb;									/* number of bytes to pop*/
{
	if( nb > 0 )
		printf("add #%d,sp\n",nb);
}

/* 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);
}

/* outccsave - output instruction to move cc's to register.*/
outccsave(reg)
int reg;
{
	printf("move sr,R%d\n",reg);
}

/* outccrestore - output instruction to restore cc's from register*/
outccrestore(reg)
int reg;
{
	printf("move R%d,ccr\n",reg);
}
