/********************************************************/
/*							*/
/*		Q/C Compiler Version 3.2		*/
/*			(Part 6)			*/
/*							*/
/*     Copyright (c) 1984 Quality Computer Systems	*/
/*							*/
/*			01/29/84			*/
/********************************************************/

#include "stdio.h"
#include "cstddef.h"
#include "cglbdecl.c"

/* This section contains code generation routines which
 * reflect the architecture of the target machine but
 * which don't generate any assembler mnemonics
 */

/* Generate trace message */
gentrace(inout, funcname)
    char           *funcname;
{
    static          label;
    register        leaving;

    leaving = (inout == '<');
    if (leaving)
	cpush(PREG);		/* save return value */
    else
    {
	dataseg();		/* build trace message */
	label = getlabel();
	linelabel(label);
	defbyte();
	outdec(' ');		/* enter (>) or exit (<) */
	do
	{			/* print function name */
	    outbyte(',');
	    outdec((int) *funcname);
	} while (*funcname++);
	codeseg();
    }
    ot("LD HL,");;		/* load address of message */
    printlabel(label);
    nl();
    ldmemi(inout);		/* enter (>) or exit (<) */
    cpush(PREG);
    ccall("puts");		/* print message */
    cpop(PREG);			/* clear arg off stack */
    if (leaving)
	cpop(PREG);		/* restore return value */
}

/* This tells if two operands are identical */

opequal(op1, op2)
    struct operand *op1, *op2;
{
    if (isaddr(op1) || isvalue(op1))
	return (FALSE);		/* At least one refers to loaded value. */

    return (op1->op_load == op2->op_load &&
	    op1->op_sym == op2->op_sym &&
	    op1->op_val == op2->op_val &&
	    op1->op_type == op2->op_type);

}


/* This copies op1 to op2 */
copyop(op1, op2)
    register struct operand *op1, *op2;
{
    op2->op_load = op1->op_load;
    op2->op_sym = op1->op_sym;
    op2->op_val = op1->op_val;
    op2->op_type = op1->op_type;
    reftype(op2->op_type);
}



/*
 * Do binary operators
 *	Return: CONSTANT if constant expression
 *		FALSE	 if not
 */
binop(lop, op, rop, heirn)
    register struct operand *lop, *rop;
    register        op;
    int             (*heirn) ();

{
    register        oprev, unsignlop;
    struct typeinfo *isptr();

    (*heirn) (rop);
    if (specialcase(lop, op, rop))
	return FALSE;
    else if ((oprev = loadops(lop, rop)) == CONSTANT)
	return CONSTANT;
    unsignlop = (int) isptr(lop) || lop->op_type->t_code == T_UNSIGNED;
    switch (op)
    {
    case '|':
	cor();
	break;
    case '^':
	cxor();
	break;
    case '&':
	cand();
	break;
    case EQ:
	ceq();
	break;
    case NE:
	cne();
	break;
    case LE:
	if (unsignlop)
	    (oprev) ? cuge() : cule();
	else
	    (oprev) ? cge() : cle();
	break;
    case GE:
	if (unsignlop)
	    (oprev) ? cule() : cuge();
	else
	    (oprev) ? cle() : cge();
	break;
    case LT:
	if (unsignlop)
	    (oprev) ? cugt() : cult();
	else
	    (oprev) ? cgt() : clt();
	break;
    case GT:
	if (unsignlop)
	    (oprev) ? cult() : cugt();
	else
	    (oprev) ? clt() : cgt();
	break;
    case '>':
	(unsignlop) ? clsr(oprev) : casr(oprev);
	break;
    case '<':
	casl(oprev);
	break;
    case '%':
    case '/':
	if (oprev)
	    swap_reg();
	if (unsignlop)
	    cudiv();
	else
	    cdiv();
	if (op == '%')
	    swap_reg();
	break;
    }
    return FALSE;
}

specialcase(lop, op, rop)
    register struct operand *lop, *rop;
{
    switch (op)
    {
    case '*':
	domult(lop, rop);
	break;
    case '-':
	dosub(lop, rop);
	break;
    case '+':
	doadd(lop, rop);
	break;
    case EQ:
	if (!isconstant(lop) && isconstant(rop))
	{
	    evalp(lop);
	    switch (rop->op_val)
	    {
	    case -1:
		cinc();
		goto case0;
	    case 1:
		cdec();
	    case 0:
	case0:
		cnot();
		break;
	    default:
		if (!evals(rop))
		    generr();
		ceq();
		break;
	    }
	    unreftype(inttype);
	    break;
	}
    default:
	return FALSE;
    }
    return TRUE;
}

/* Add rop to lop */
doadd(lop, rop)
    register struct operand *lop, *rop;
{
    register struct typeinfo *lptrtype, *rptrtype;
    struct typeinfo *isptr();
    register        n;

    if (isconstant(lop) && isconstant(rop))
    {
	lop->op_val += rop->op_val;	/* constant expression */
	unreftype(inttype);
	return;
    }
    lptrtype = isptr(lop);
    rptrtype = isptr(rop);
    if (isconstant(rop))
    {				/* do special cases */
	if (lptrtype)		/* must scale for pointers */
	    n = rop->op_val * basesize(lop);
	else
	    n = rop->op_val;
	if ((lop->op_load & LOADADDR)
	    && (lop->op_load & (CONSTADDR | CONSTOFF)))
	    lop->op_val += n;	/* change offset only */
	else
	{
	    evalp(lop);		/* load lop if necessary */
	    addconst(n);
	}
	unreftype(rop->op_type);
	return;
    }
    if (isconstant(lop))
    {
	if (rptrtype)
	    lop->op_val *= basesize(rop);
	if ((rop->op_load & LOADADDR)
	    && (rop->op_load & (CONSTADDR | CONSTOFF)))
	    ;			/* offset has been changed */
	else
	{
	    evalp(rop);		/* load rop if necessary */
	    addconst(lop->op_val);
	}
	chtype(lop, rop->op_type);	/* now lop is that type */
	lop->op_load = rop->op_load;	/* record loading info */
	lop->op_sym = rop->op_sym;
	unreftype(rop->op_type);
	return;
    }
    if (lptrtype)
    {				/* must scale for pointer addition */
	if (rptrtype)
	    error("Can't add pointers");
	evalp(rop);
	multconst(basesize(lop));
    } else if (rptrtype)
    {
	evalp(lop);
	multconst(basesize(rop));
	chtype(lop, rop->op_type);
    }
    loadops(lop, rop);
    cadd();
}

addto(op, sign, loadflag)
    struct operand *op;
    int             loadflag;	/* TRUE if result should be loaded in preg */
{
    struct operand  aop;
    struct typeinfo *isptr();

    if (!loadflag && (!isptr(op) || basesize(op) == 1))
    {
	prtcomm("Addto could be done without load of operand");
    }
    saveaddr(&aop, op);
    evalp(op);
    increment(op, sign);
    store(&aop, op, TRUE);
    unreftype(aop.op_type);
}

increment(op, sign)
    struct operand *op;
{
    struct typeinfo *isptr();

    if (isptr(op))
	addconst(sign * basesize(op));
    else
	addconst(sign);
}

/* Subtract rop from lop */
dosub(lop, rop)
    register struct operand *lop, *rop;
{
    register struct typeinfo *lptrtype, *rptrtype;
    register        size;
    struct typeinfo *isptr();

    if (isconstant(rop))
    {
	rop->op_val = -rop->op_val;
	doadd(lop, rop);	/* do lop + (-rop) */
	return;
    }
    rptrtype = isptr(rop);
    lptrtype = isptr(lop);
    if (lptrtype && !rptrtype)
    {
	evalp(rop);		/* scale for ptr - scalar */
	multconst(basesize(lop));
    }
    if (loadops(lop, rop))
	swap_reg();		/* operands loaded in reverse order */
    csub();
    if (rptrtype)
    {
	if (!lptrtype)
	    error("Can't subtract pointer from scalar");
	else if (lptrtype != rptrtype)
	    error("Can't subtract unlike pointers");
	else
	{
	    /* it's ptr - ptr so compute occurrences */
	    size = basesize(lop);
	    if (size > 1)
	    {
		swap_reg();	/* put diff in sreg */
		limmed(size, PREG);	/* and divide by size */
		cdiv();
	    }
	    chtype(lop, inttype);	/* now it's just a count */
	}
    }
}

/* Multiply lop by rop */
domult(lop, rop)
    register struct operand *lop, *rop;
{
    if (isconstant(lop) && isconstant(rop))
	lop->op_val *= rop->op_val;	/* constant expression */
    else if (isconstant(rop))
    {
	evalp(lop);
	multconst(rop->op_val);	/* chk for special cases */
    } else if (isconstant(lop))
    {
	evalp(rop);
	multconst(lop->op_val);
	chtype(lop, rop->op_type);	/* record what we did */
	lop->op_load = EXPRESSION;
    } else
    {
	loadops(lop, rop);
	cmult();
	return;
    }
    unreftype(rop->op_type);
}

/* Multiply primary reg by 'size' */
multconst(size)
{
    switch (size)
    {
    case 64:
	doublereg();
    case 32:
	doublereg();
    case 16:
	doublereg();
    case 8:
	doublereg();
    case 4:
	doublereg();
    case 2:
	doublereg();
    case 1:
	return;
    default:
	limmed(size, SREG);
	cmult();
	return;
    }
}

/* Do a function call */
callfunction(sym)
    register struct st *sym;	/* symbol table entry (or NULL) */
{
    char           *rindex();
    register        nargs;
    register struct typeinfo *type;
    struct operand  op;
    register char  *p, *q;

    nargs = 0;
    blanks();
    if (sym == NULL ||		/* if we're calling address in preg or */
	pregflag)		/* preg contains something... */
	cpush(PREG);		/* save it */
    while (!streq(lptr, ")"))
    {				/* process arguments */
	if (endst())
	    break;		/* premature end of function call */
	pregflag = FALSE;	/* start new expression */
	heir2(&op);		/* get arg, but no comma ops */
	type = op.op_type;
	if (type->t_code == T_STRUCT || type->t_code == T_UNION)
	{
	    error("Can't pass structures or unions");
	    op.op_load &= ~LOADVALUE;	/* chg to ptr ref */
	    op.op_load |= LOADADDR;
	}
	evalp(&op);
	if (sym == NULL)	/* calling addr on top of stk */
	    swapstk();		/* keep addr on top of stack */
	cpush(PREG);		/* push addr or arg in preg */
	++nargs;
	unreftype(type);	/* remove type table ref */
	if (!matchc(','))	/* any more arguments? */
	    break;
    }
    needpunc(')');
    if (sym)
    {
	p = sym->st_name;	/* special case printf/scanf */
	if ((q = rindex(p, 'p')) && strcmp(q, "printf") == 0 ||
	    (q = rindex(p, 's')) && strcmp(q, "scanf") == 0)
	{
	    limmed(nargs, PREG);/* add argument count */
	    cpush(PREG);
	    ++nargs;		/* one more arg must be cleared */
	}
	ccall(sym->st_name);	/* call named function */
    } else
	callstk();		/* else call address on top of stack */
    pregflag = TRUE;
    clearstk(2 * nargs);
    testflag = TESTED;		/* return value is tested by function */
}

/* Copy an argument into a register */
argtoreg(p, regno)
    register struct st *p;
{
    struct operand  aop, op;

    initop(&op, p, p->st_type, LOADVALUE);
    copyop(&op, &aop);
    pregflag = FALSE;
    evalp(&op);			/* copy arg into preg */

    p->st_sc = SC_REG;
    p->st_info = regno;
/*    aop.op_load |= LOADADDR; */

    store(&aop, &op, FALSE);
    unreftype(op.op_type);
    unreftype(aop.op_type);
}



/* Load operands in the proper registers
 *	Return: CONSTANT if both operands are constant
 *		TRUE	if operands are loaded in
 *			reverse of normal order
 *		FALSE	otherwise
 */
loadops(lop, rop)
    register struct operand *lop, *rop;
{
    register        oprev;

    oprev = FALSE;		/* record order operands are loaded */
    if (isconstant(lop) && isconstant(rop))
    {
	unreftype(inttype);
	return CONSTANT;
    } else if (isvalue(lop))
    {
	if (isvalue(rop))
	{
	    /* Lop is on stack, rop in PREG */
	    prtcomm("Loadops V V");
	    cpop(SREG);		/* retrieve value of lop in sreg */

	} else if (isaddr(rop))
	{
	    /* Value of lop on stack, addr of rop in preg */
	    prtcomm("Loadops V A");
	    indirect(rop);	/* Load value of rop */
	    cpop(SREG);		/* retrieve value of lop in sreg */

	} else
	{
	    /* Value of lop in preg, nothing loaded for rop */
	    prtcomm("Loadops V O");
	    if (evals(rop))	/* Try to put value of rop in sreg */
		oprev = TRUE;	/* If successful, note operands are reversed */
	    else
	    {
		pritosec();	/* Otherwise, put preg in sreg */
		evalp(rop);	/* Load value of rop into preg */
	    }

	}
    } else if (isaddr(lop))
    {
	if (isvalue(rop))
	{
	    /* Addr of lop on stack, value of rop in preg */
	    prtcomm("Loadops A V");
	    pritosec();		/* Put value of rop into sreg */
	    cpop(PREG);		/* Get addr of lop into preg */
	    indirect(lop);	/* Get value of lop into preg */
	    oprev = TRUE;	/* Note that operands are in wrong order */

	} else if (isaddr(rop))
	{
	    /* Address of lop on stack, address of rop in preg */
	    prtcomm("Loadops A A");
	    if (!sindirect(rop))/* Try to get value of rop into sreg */
	    {
		indirect(rop);	/* Otherwise, load value into preg */
		pritosec();	/* Then move to sreg */
	    }
	    cpop(PREG);		/* Get address of lop into preg */
	    indirect(lop);	/* Get value of lop into preg */
	    oprev = TRUE;	/* Note that operands are in wrong order */
	} else
	{
	    /* Address of lop in preg, nothing loaded for rop */
	    prtcomm("Loadops A O");
	    if (sindirect(lop))	/* Try to load value of lop into sreg */
		evalp(rop);	/* If success, load value of rop into preg */
	    else
	    {
		indirect(lop);	/* If failure, load value of lop into preg */
		if (evals(rop))	/* Try loading rop into sreg */
		    oprev = TRUE;	/* If successful, note ops are
					 * reversed */
		else
		{
		    pritosec();	/* If not, move lop into sreg */
		    evalp(rop);	/* Load value of rop into preg */
		}
	    }
	}

    } else
    {
	if (isvalue(rop))
	{
	    /* Nothing loaded for lop, value of rop in preg */
	    prtcomm("Loadops O V");
	    if (!evals(lop))	/* Try to load value of lop into sreg */
	    {
		pritosec();	/* Put value of rop into sreg */
		evalp(lop);	/* Get value of lop into preg */
		oprev = TRUE;	/* Note that operands are in wrong order */
	    }
	} else if (isaddr(rop))
	{
	    /* Nothing loaded for lop, address of rop in preg */
	    prtcomm("Loadops O A");
	    if (evals(lop))	/* Try to load value of lop into sreg */
		indirect(rop);	/* If that works, load value of rop into preg */
	    else
	    {
		if (!sindirect(rop))	/* If fails, try to load rop into
					 * sreg */
		{
		    indirect(rop);	/* If that fails, load rop into preg */
		    pritosec();	/* Then move to sreg */
		}
		evalp(lop);	/* Then get lop into preg */
		oprev = TRUE;	/* Note that operands are in wrong order */
	    }

	} else
	{
	    /* Nothing loaded for lop, nor for rop */
	    prtcomm("Loadops O O");

	    if (opequal(lop, rop))
	    {
		prtcomm("Loadops copied identical ops");
		evalp(lop);
		copy_reg();
	    } else if (evals(lop))	/* Try to put lop into sreg */
		evalp(rop);	/* If that works, put rop into preg */
	    else if (evals(rop))/* If that fails, try to put rop into sreg */
	    {
		evalp(lop);	/* If we got rop into sreg, put lop into preg */
		oprev = TRUE;	/* Note that operands are in wrong order */
	    } else
	    {
		evalp(lop);	/* If neither op could be loaded into sreg: */
		pritosec();	/* Load lop into preg, move it to sreg */
		evalp(rop);	/* Load rop in preg */
	    }

	}
    }
    settype(lop, rop);		/* set type of expression */
    return oprev;
}

/* Move preg to sreg, mark preg free */

pritosec()
{
    swap_reg();
    pregflag = FALSE;
}


/* Evaluate the op into preg */
evalp(op)
    register struct operand *op;
{
    register        load_info;

    if (isvalue(op))
	return;
    if (isaddr(op))
    {
	indirect(op);
	op->op_load = EXPRESSION;
	pregflag = TRUE;
	return;

    }
    savepreg();			/* save preg if it's in use */
    load_info = op->op_load;
    if (load_info & (CONSTANT | ASGCONST))	/* is it a constant? */
	limmed(op->op_val, PREG);
    else if (load_info & LOADADDR)	/* do we want the address... */
	getaddr(op, PREG);
    else if (load_info & LOADVALUE)	/* or the value of op? */
	getvalue(op);
    op->op_load = EXPRESSION;	/* record that op is loaded */
}


/* Take op and put its address in aop.  This will result in the
    generation of code only if op is an address. */

saveaddr(aop, op)
    register struct operand *aop, *op;
{
    struct typeinfo *ptrto();

    if (!islval(op))
	generr();

    copyop(op, aop);

    /* If aop does not already represent the address of something, make it. */
    if (!isaddr(op))
    {
/*	chtype(aop, ptrto(aop->op_type));   */
	aop->op_load &= ~(LVALUE | LOADVALUE);
	if (aop->op_sym != NULL)
	{			/* if not already loaded... */
	/*    aop->op_load |= LOADADDR;	/* need to load address */
	/*    setconstaddr(aop);	/* check for constant address */
	}
	prtcomm("Saveaddr doesn't push operand address");
    } else
    {
	cpush(PREG);
	pregflag = TRUE;	/* if addr of op already in preg, save it */
	prtcomm("Saveaddr pushes operand address");
    }
}


/* Evaluate the op into sreg */
evals(op)
    register struct operand *op;
{
    int             load_info;

    if (isloaded(op) || isaddr(op))
    {
	generr();
	return (FALSE);
    }
    load_info = op->op_load;
    if (load_info & (CONSTANT | ASGCONST))	/* is it a constant? */
	limmed(op->op_val, SREG);
    else if (load_info & LOADADDR)
    {				/* do we want the address... */
	if (!getaddr(op, SREG))
	    return (FALSE);
    } else if (load_info & LOADVALUE)
    {				/* or the value of op? */
	if (!sgetvalue(op))
	    return (FALSE);
    }
    op->op_load = EXPRESSION;	/* record that op is loaded */
    return (TRUE);
}


/* Load value of operand into preg, where the operand describes a variable */
getvalue(op)
    register struct operand *op;
{
    register struct st *sym;

    sym = op->op_sym;
    if (peepflag == PREGSYM && strcmp(sym->st_name, peepsym) == 0
	&& op->op_val == peepoffset && op->op_type->t_size == peeplen)
    {
	prtcomm("Load of preg optimized away");
	return;			/* sym is already in preg */
    }
    if (sym->st_sc == SC_AUTO)
	autoload(op, PREG);
    else if (sym->st_sc == SC_REG)
	regload(op, PREG);
    else
	getmem(op, PREG);
}

/* Load value of operand into sreg */
sgetvalue(op)
    register struct operand *op;
{
    register struct st *sym;
    int             success;

    sym = op->op_sym;

    if (sym->st_sc == SC_AUTO)
	success = autoload(op, SREG);
    else if (sym->st_sc == SC_REG)
	success = regload(op, SREG);
    else
	success = getmem(op, SREG);

    return (success);
}

/* Store operand op into address in aop */
/* Retain its value in preg if keep is true */
store(aop, op, keep)
    register struct operand *aop, *op;
    int             keep;
{

    register unsigned value;
    register struct st *sym;
    register        offset;
    int             size;

    sym = aop->op_sym;
    size = aop->op_type->t_size;

    /* Look for optimizations */

    if (!keep && isconstant(op))
    {
	/* if we don't need to keep the value and we're assigning a constant
	 * try to optimize to an immediate load */

	if (sym == NULL)
	{
	    /* address of aop is in preg */
	    value = aop->op_val = op->op_val;
	    if (size == 1)
	    {
		ldmemi(value & 0xff);
	    } else
	    {
		ot("LDMIW\t");
		outdec(value);
		nl();
	    }
	    aop->op_load = (ASGCONST | LOADVALUE);
	    pregflag = FALSE;	/* preg no longer in use */
	    return;
	} else if (sym->st_sc == SC_REG)
	{
	    value = aop->op_val = op->op_val;
	    if (size == 1)
		generr();
	    else
	    {
		if (sym->st_info == 1)
		    limmed(value, RREG1);	/* Store constant to BC */
		else
		    limmed(value, RREG2);	/* Store constant to BC */
	    }
	    aop->op_load = (ASGCONST | LOADVALUE);
	    return;

	} else if (size == 1 && sym->st_sc == SC_AUTO)
	{
	    offset = sym->st_info + aop->op_val;
	    value = aop->op_val = op->op_val;

	    /* For automatic bytes, load immediate */
	    if (offset < 127 && offset > -127)
	    {
		ot("LD\t");
		xreg(offset);
		outbyte(',');
		outdec(value & 0xFF);
	    } else
	    {
		ot("LDXIB\t");
		outdec(offset);
		outbyte(',');
		outdec(value);
	    }

	    prtname(sym);	/* print name as comment */

	    aop->op_load = (ASGCONST | LOADVALUE);
	    return;
	}
    }

    if (size > 2) {
	if (sym == NULL && op->op_sym == NULL) /* Both addresses were loaded */
	    cpop(SREG);	
	else if (sym == NULL) {   /* LHS was loaded */
	    pritosec();
	    getaddr(op, PREG);
	}
	else if (op->op_sym == NULL) {   /* RHS was loaded */
	    if (!getaddr(aop, SREG)) {
		pritosec();
		getaddr(aop, PREG);
		swap_reg();
	    }
	}
	else {		/* Neither was loaded */
	    if (pregflag)
		savepreg();
	    if (!getaddr(aop, SREG)) {
		getaddr(aop, PREG);
		pritosec();
	    }
	    getaddr(op, PREG);
	}

	if (keep)
	    cpush(SREG);
	ol("PUSH\tBC");
	ot("LD\tBC,");
	outdec(size);
	nl();
	ol("LDIR");
	ol("POP\tBC");
	if (keep)
	    cpop(PREG);

    } else {
	evalp(op);
	if (aop->op_sym == NULL)	/* store at address on stack */
	    putstk(aop->op_type->t_size, keep);
	else
	    putmem(aop);		/* store in memory */
	op->op_load = EXPRESSION;	/* keep op from being loaded */
	aop->op_load = EXPRESSION;
    }
}

/* Load specified object whose address is in preg indirect into preg */
indirect(op)
    register struct operand *op;

{
    if (!isaddr(op))
	generr();

    if (op->op_type->t_size == 1)
    {
	ol("LD\tA,(HL)");
	ol("EXTS");
        testflag = REGL;		/* remember that L is in A */
    } else
    {
	ol("LDHLM");
    }
    op->op_load = EXPRESSION;
    pregflag = TRUE;
}

/* Load specified object size indirect thru preg into sreg */
sindirect(op)
    register struct operand *op;
{
    if (op->op_type->t_size == 1)
	return (FALSE);		/* Can't do because of sign extension */
    else
	ol("LDDEM");

    op->op_load = EXPRESSION;
    pregflag = FALSE;
    return (TRUE);
}

/* Add constant amt to primary reg */
addconst(amt)
{
    switch (amt)
    {
    case 3:
	cinc();
    case 2:
	cinc();
    case 1:
	cinc();
    case 0:
	return;
    case -3:
	cdec();
    case -2:
	cdec();
    case -1:
	cdec();
	return;
    default:
	limmed(amt, SREG);
	cadd();
	return;
    }
}

/* Subtract preg from sreg */
csub()
{
    swap_reg();
    ol("SUBDE");
}

/* Multiply preg by sreg */
cmult()
{
    ol("MULTDE");
}

/* Divide (signed) sreg by preg */
/*	(quotient in preg, remainder in sreg) */
cdiv()
{
    callib("div");
}

/* Divide (unsigned) sreg by preg */
cudiv()
{
    callib("udiv");
}

/* Inclusive 'or' preg and sreg */
cor()
{
    callib("o");
    testflag = REGH;		/* remember that H is in A */
}

/* Exclusive 'or' preg and sreg */
cxor()
{
    callib("x");
    testflag = REGH;		/* remember that H is in A */
}

/* 'and' preg and sreg */
cand()
{
    callib("a");
    testflag = REGH;		/* remember that H is in A */
}

/* Arithmetic right shift sreg number of times in preg */
casr(oprev)
{
    callib(oprev ? "asr1" : "asr");
}

/* Arithmetic left shift sreg number of times in preg */
casl(oprev)
{
    callib(oprev ? "asl1" : "asl");
}

/* Logical right shift sreg number of times in preg */
clsr(oprev)
{
    callib(oprev ? "lsr1" : "lsr");
}

/* two's complement of preg */
cneg()
{
    ol("NEGHL");
    testdone = TRUE;
}

/* one's complement of preg */
ccom()
{
    callib("com");
    testflag = REGL;		/* remember that L is in A */
}

/* Logical not of preg. Set HL and Z flag to indicate TRUE/FALSE */
cnot()
{
    callib("n");
    testdone = TRUE;
}

/*
 * The following are the conditional operators.
 * They compare sreg to preg and set preg = 1 if
 * the condition is true and 0 if not. The Z
 * flag is reset if TRUE and set if FALSE.
 */
/* Test for equal */
ceq()
{
    callib("e");
    testdone = TRUE;
}

/* Test for not equal */
cne()
{
    callib("ne");
    testdone = TRUE;
}

/* Test for less than (signed) */
clt()
{
    callib("lt");
    testdone = TRUE;
}

/* Test for less than or equal (signed) */
cle()
{
    callib("le");
    testdone = TRUE;
}

/* Test for greater than (signed) */
cgt()
{
    callib("gt");
    testdone = TRUE;
}

/* Test for greater than or equal (signed) */
cge()
{
    callib("ge");
    testdone = TRUE;
}

/* Test for less than (unsigned) */
cult()
{
    callib("ult");
    testdone = TRUE;
}

/* Test for less than or equal (unsigned) */
cule()
{
    callib("ule");
    testdone = TRUE;
}

/* Test for greater than (unsigned) */
cugt()
{
    callib("ugt");
    testdone = TRUE;
}

/* Test for greater than or equal (unsigned) */
cuge()
{
    callib("uge");
    testdone = TRUE;
}

/* Save preg if it's in use */
savepreg()
{
    if (pregflag)
	cpush(PREG);
    else
	pregflag = TRUE;
}


/* Print a name so it won't upset the assembler */
outname(name)
    char           *name;
{
    register        c;
    register char  *s;

    s = name;
    while (c = *s++)
	outbyte((c == '_') ? ASMULINE : c);
    if (*name != ASMRTS)	/* don't change library function names */
	outbyte(ASMRTS);
}

/* Print specified number as label */
printlabel(label)
{
    outbyte(ASMRTS);
    outdec(label);
}

/* Generate a compiler label in the peephole buffer */
peeplabel(label)
{
    static char     s[7] = {ASMRTS};
    char           *itob();

    itob(label, s + 1, 10);
    loadpeep(s);
    loadpeep("\n");
}

/* Load pattern into peephole optimization buffer */
loadpeep(s)
    char           *s;
{
    strcat(peepbuf, s);
}

/* Dump the contents of the peephole buffer */
dumpeep()
{
    peepflag = FALSE;
    if (*peepbuf)
	outstr(peepbuf);
    clearpeep();
}

/* Clear the peephole pattern buffer */
clearpeep()
{
    peepflag = FALSE;
    *peepbuf = '\0';
}


generr()
{
    error("Panic: Code generation error");
}


/* end of CC6.C */
