/*
** Small-C Compiler -- Part 3 -- Expression Analyzer.
** Copyright 1982, 1983, 1985, 1988 J. E. Hendrix
** Copyright 1998 H T Walheim
** All rights reserved.
*/

#include <stdio.h>
#include "cc.h"

#define ST 0			/* is[ST] - symbol table address, else 0 */
#define TI 1			/* is[TI] - type of indirect obj to fetch, else 0 */
#define TA 2			/* is[TA] - type of address, else 0 */
#define TC 3			/* is[TC] - type of constant (INT or UINT), else 0 */
#define CV 4			/* is[CV] - value of constant (+ auxiliary uses) */
#define OP 5			/* is[OP] - code of highest/last binary operator */
#define SA 6			/* is[SA] - stage address of "op 0" code, else 0 */

extern char
*litq, *glbptr, *lptr, ssname[NAMESIZE], quote[2];
extern int
 ch, csp, litlab, litptr, nch, op[16], op2[16], opindex, opsize, *snext;

/***************** lead-in functions *******************/

constexpr(val)
int *val;
{
    int constant;
    int *before, *start;
    setstage(&before, &start);
    expression(&constant, val);
    clearstage(before, 0);	/* scratch generated code */
    if (constant == 0)
	error("must be constant expression");
    return constant;
}

null_is(is)
int is[7];
{
    int i;

    for (i = 0; i < 7; ++i) {
	is[i] = 0;		/* <-- */
    }
}


expression(con, val)
int *con, *val;
{
    int i;
    int is[7];

    null_is(is);

    if (level1(is))
	fetch(is);
    *con = is[TC];
    *val = is[CV];
}

test(label, parens)
int label, parens;
{
    int is[7];
    int *before, *start;

    null_is(is);

    if (parens)
	need("(");
    while (1) {
	setstage(&before, &start);
	if (level1(is))
	    fetch(is);
	if (match(","))
	    clearstage(before, start);
	else
	    break;
    }
    if (parens)
	need(")");
    if (is[TC]) {		/* constant expression */
	clearstage(before, 0);
	if (is[CV])
	    return;
	gen(JMPm, label);
	return;
    }
    if (is[SA]) {		/* stage address of "oper 0" code */
	switch (is[OP]) {	/* operator code */
	case EQ12:
	case LE12u:
	    zerojump(EQ10f, label, is);
	    break;
	case NE12:
	case GT12u:
	    zerojump(NE10f, label, is);
	    break;
	case GT12:
	    zerojump(GT10f, label, is);
	    break;
	case GE12:
	    zerojump(GE10f, label, is);
	    break;
	case GE12u:
	    clearstage(is[SA], 0);
	    break;
	case LT12:
	    zerojump(LT10f, label, is);
	    break;
	case LT12u:
	    zerojump(JMPm, label, is);
	    break;
	case LE12:
	    zerojump(LE10f, label, is);
	    break;
	default:
	    gen(NE10f, label);
	    break;
	}
    } else
	gen(NE10f, label);
    clearstage(before, start);
}

/*
** test primary register against zero and jump if false
*/
zerojump(oper, label, is)
int oper, label, is[];
{
    clearstage(is[SA], 0);	/* purge conventional code */
    gen(oper, label);
}

/***************** precedence levels ******************/
#ifdef __GNUC__
level2();
level3();
level4();
level5();
level6();
level7();
level8();
level9();
level10();
level11();
level12();
level13();
#endif

level1(is)
int is[];
{
    int k, is2[7], is3[2], oper, oper2;

    null_is(is2);
    null_is(is3);

    k = down1(level2, is);
    if (is[TC]) {
	gen(GETd1n, is[CV]);
    }
    if (match("|=")) {
	oper = oper2 = OR12;
    } else if (match("^=")) {
	oper = oper2 = XOR12;
    } else if (match("&=")) {
	oper = oper2 = AND12;
    } else if (match("+=")) {
	oper = oper2 = ADD12;
    } else if (match("-=")) {
	oper = oper2 = SUB12;
    } else if (match("*=")) {
	oper = MUL12;
	oper2 = MUL12u;
    } else if (match("/=")) {
	oper = DIV12;
	oper2 = DIV12u;
    } else if (match("%=")) {
	oper = MOD12;
	oper2 = MOD12u;
    } else if (match(">>=")) {
	oper = oper2 = ASR12;
    } else if (match("<<=")) {
	oper = oper2 = ASL12;
    } else if (match("=")) {
	oper = oper2 = 0;
    } else
	return k;
    /* have an assignment operator */
    if (k == 0) {
	needlval();
	return 0;
    }
    is3[ST] = is[ST];
    is3[TI] = is[TI];
    if (is[TI]) {		/* indirect target */
	if (oper) {		/* ?= */
	    gen(PUSH1, 0);	/* save address */
	    fetch(is);		/* fetch left side */
	}
	down2(oper, oper2, level1, is, is2);	/* parse right side */
	if (oper)
	    gen(POP2, 0);	/* retrieve address */
    } else {			/* direct target */
	if (oper) {		/* ?= */
	    fetch(is);		/* fetch left side */
	    down2(oper, oper2, level1, is, is2);	/* parse right side */
	} else {		/*  = */
	    if (level1(is2))
		fetch(is2);	/* parse right side */
	}
    }
    store(is3);			/* store result */
    return 0;
}

level2(is1)
int is1[];
{
    int is2[7], is3[7], k, flab, endlab, *before, *after;

    null_is(is2);
    null_is(is3);

    k = down1(level3, is1);	/* expression 1 */
    if (match("?") == 0)
	return k;
    dropout(k, NE10f, flab = getlabel(), is1);
    if (down1(level2, is2))
	fetch(is2);		/* expression 2 */
    else if (is2[TC]) {
	gen(GETd1n, is2[CV]);
    }
    need(":");
    gen(JMPm, endlab = getlabel());
    gen(LABm, flab);
    if (down1(level2, is3))
	fetch(is3);		/* expression 3 */
    else if (is3[TC]) {
	gen(GETd1n, is3[CV]);
    }
    gen(LABm, endlab);

    is1[TC] = is1[CV] = 0;
    if (is2[TC] && is3[TC]) {	/* expr1 ? const2 : const3 */
	is1[TA] = is1[TI] = is1[SA] = 0;
    } else if (is3[TC]) {	/* expr1 ? var2 : const3 */
	is1[TA] = is2[TA];
	is1[TI] = is2[TI];
	is1[SA] = is2[SA];
    } else if ((is2[TC])	/* expr1 ? const2 : var3 */
	       ||(is2[TA] == is3[TA])) {	/* expr1 ? same2 : same3 */
	is1[TA] = is3[TA];
	is1[TI] = is3[TI];
	is1[SA] = is3[SA];
    } else
	error("mismatched expressions");
    return 0;
}

level3(is)
int is[];
{
    return skim("||", EQ10f, 1, 0, level4, is);
}

level4(is)
int is[];
{
    return skim("&&", NE10f, 0, 1, level5, is);
}

level5(is)
int is[];
{
    return down("|", 0, level6, is);
}

level6(is)
int is[];
{
    return down("^", 1, level7, is);
}

level7(is)
int is[];
{
    return down("&", 2, level8, is);
}

level8(is)
int is[];
{
    return down("== !=", 3, level9, is);
}

level9(is)
int is[];
{
    return down("<= >= < >", 5, level10, is);
}

level10(is)
int is[];
{
    return down(">> <<", 9, level11, is);
}

level11(is)
int is[];
{
    return down("+ -", 11, level12, is);
}

level12(is)
int is[];
{
    return down("* / %", 13, level13, is);
}

level13(is)
int is[];
{
    int k;
    char *ptr;
    if (match("++")) {		/* ++lval */
	if (level13(is) == 0) {
	    needlval();
	    return 0;
	}
	step(rINC1, is, 0);
	return 0;
    } else if (match("--")) {	/* --lval */
	if (level13(is) == 0) {
	    needlval();
	    return 0;
	}
	step(rDEC1, is, 0);
	return 0;
    } else if (match("~")) {	/* ~ */
	if (level13(is))
	    fetch(is);
	gen(COM1, 0);
	is[CV] = ~is[CV];
	return (is[SA] = 0);
    } else if (match("!")) {	/* ! */
	if (level13(is))
	    fetch(is);
	gen(LNEG1, 0);
	is[CV] = !is[CV];
	return (is[SA] = 0);
    } else if (match("-")) {	/* unary - */
	if (level13(is))
	    fetch(is);
	gen(ANEG1, 0);
	is[CV] = -is[CV];
	return (is[SA] = 0);
    } else if (match("*")) {	/* unary * */
	if (level13(is))
	    fetch(is);
	if (ptr = is[ST])
	    is[TI] = ptr[TYPE];
	else
	    is[TI] = INT;
	is[SA] =		/* no (op 0) stage address */
	    is[TA] =		/* not an address */
	    is[TC] = 0;		/* not a constant */
	is[CV] = 1;		/* omit fetch() on func call */
	return 1;
    } else if (amatch("sizeof", 6)) {	/* sizeof() */
	int sz, p;
	char *ptr, sname[NAMESIZE];
	if (match("("))
	    p = 1;
	else
	    p = 0;
	sz = 0;
	if (amatch("unsigned", 8))
	    sz = INTSIZE;
	if (amatch("int", 3))
	    sz = INTSIZE;
	else if (amatch("char", 4))
	    sz = 1;
	if (sz) {
	    if (match("*"))
		sz = PTRSIZE;
	} else if (symname(sname)
		   && ((ptr = findloc(sname)) || (ptr = findglb(sname)))
		   && ptr[IDENT] != FUNCTION && ptr[IDENT] != LABEL)
	    sz = getint(ptr + SIZE, INTSIZE);
	else if (sz == 0)
	    error("must be object or type");
	if (p)
	    need(")");
	is[TC] = INT;
	is[CV] = sz;
	is[TA] = is[TI] = is[ST] = 0;
	return 0;
    } else if (match("&")) {	/* unary & */
	if (level13(is) == 0) {
	    error("illegal address");
	    return 0;
	}
	ptr = is[ST];
	is[TA] = ptr[TYPE];
	if (is[TI])
	    return 0;
	gen(POINT1m, ptr);
	is[TI] = ptr[TYPE];
	return 0;
    } else {
	k = level14(is);
	if (match("++")) {	/* lval++ */
	    if (k == 0) {
		needlval();
		return 0;
	    }
	    step(rINC1, is, rDEC1);
	    return 0;
	} else if (match("--")) {	/* lval-- */
	    if (k == 0) {
		needlval();
		return 0;
	    }
	    step(rDEC1, is, rINC1);
	    return 0;
	} else
	    return k;
    }
}

level14(is)
int *is;
{
    int k, consta, val;
    char *ptr, *before, *start;
    k = primary(is);
    ptr = is[ST];
    blanks();
    if (ch == '[' || ch == '(') {
	int is2[7];		/* allocate only if needed */

	null_is(is2);

	while (1) {
	    if (match("[")) {	/* [subscript] */
		if (ptr == 0) {
		    error("can't subscript");
		    skip();
		    need("]");
		    return 0;
		}
		if (is[TA]) {
		    if (k)
			fetch(is);
		} else {
		    error("can't subscript");
		    k = 0;
		}
		setstage(&before, &start);
		is2[TC] = 0;
		down2(0, 0, level1, is2, is2);
		need("]");
		if (is2[TC]) {
		    clearstage(before, 0);
		    if (is2[CV]) {	/* only add if non-zero */
			if (ptr[TYPE] >> 2 == BPD) {
			    gen(GETd2n, is2[CV] << LBPD);
			} else if (ptr[TYPE] >> 2 == BPW) {
			    gen(GETd2n, is2[CV] << LBPW);
			} else {
			    gen(GETd2n, is2[CV]);
			}
			gen(ADD12, 0);
		    }
		} else {
		    if (ptr[TYPE] >> 2 == BPD) {
			gen(DBL1, 0);
			gen(DBL1, 0);
		    } else if (ptr[TYPE] >> 2 == BPW) {
			gen(DBL1, 0);
		    }
		    gen(ADD12, 0);
		}
		is[TA] = 0;
		is[TI] = ptr[TYPE];
		k = 1;
	    } else if (match("(")) {	/* function(...) */
		if (ptr == 0)
		    callfunc(0);
		else if (ptr[IDENT] != FUNCTION) {
		    if (k && !is[CV])
			fetch(is);
		    callfunc(0);
		} else
		    callfunc(ptr);
		k = is[ST] = is[TC] = is[CV] = 0;
	    } else
		return k;
	}
    }
    if (ptr && ptr[IDENT] == FUNCTION) {
	gen(POINT1m, ptr);
	is[ST] = 0;
	return 0;
    }
    return k;
}

primary(is)
int *is;
{
    char *ptr, sname[NAMESIZE];
    int k;
    if (match("(")) {		/* (subexpression) */
	do
	    k = level1(is);
	while (match(","));
	need(")");
	return k;
    }
    putint(0, is, 7 << LBPW);	/* clear "is" array */
    if (symname(sname)) {	/* is legal symbol */
	if (ptr = findloc(sname)) {	/* is local */
	    if (ptr[IDENT] == LABEL) {
		experr();
		return 0;
	    }
	    gen(POINT1s, getint(ptr + OFFSET, INTSIZE));
	    is[ST] = ptr;
	    is[TI] = ptr[TYPE];
	    if (ptr[IDENT] == ARRAY) {
		is[TA] = ptr[TYPE];
		return 0;
	    }
	    if (ptr[IDENT] == POINTER) {
		is[TI] = UINT;
		is[TA] = ptr[TYPE];
	    }
	    return 1;
	}
	if (ptr = findglb(sname)) {	/* is global */
	    is[ST] = ptr;
	    if (ptr[IDENT] != FUNCTION) {
		if (ptr[IDENT] == ARRAY) {
		    gen(POINT1m, ptr);
		    is[TI] = is[TA] = ptr[TYPE];
		    return 0;
		}
		if (ptr[IDENT] == POINTER)
		    is[TA] = ptr[TYPE];
		return 1;
	    }
	} else
	    is[ST] = addsym(sname, FUNCTION, INT, 0, 0, &glbptr, AUTOEXT);
	return 0;
    }
    if (constant(is) == 0)
	experr();
    return 0;
}

experr()
{
    error("invalid expression");
    gen(GETd1n, 0);
    skip();
}

/* attempt at right to left - do it later */
#ifdef LATER
pushargs(ptr, nargs)
char *ptr;
int nargs;
{
    if (streq(lptr, ")") != 0)
	return;

    if (endst())
	return;

    if (ptr) {
	expression(&consta, &val);
	gen(PUSH1, 0);
    } else {
	gen(PUSH1, 0);
	expression(&consta, &val);
	gen(SWAP1s, 0);		/* don't push addr */
    }
    nargs = nargs + INTSIZE;	/* count args*BPW */

    if (match(",") == 0)
	break;

}
#endif

callfunc(ptr)
char *ptr;			/* symbol table entry or 0 */
{
    int nargs, consta, val;

    nargs = 0;
    blanks();			/* already saw open paren */

    while (streq(lptr, ")") == 0) {
	if (endst())
	    break;
	if (ptr) {
	    expression(&consta, &val);
	    gen(PUSH1, 0);
	} else {
	    gen(PUSH1, 0);
	    expression(&consta, &val);
	    gen(SWAP1s, 0);	/* don't push addr */
	}
	nargs = nargs + INTSIZE;	/* count args*BPW */

	if (match(",") == 0)
	    break;
    }
    need(")");
    if (ptr && (streq(ptr + NAME, "CCARGC") == 0))	/* accessing ... like va_args */
	gen(ARGCNTn, nargs >> LBPD);	/* to get start of frame */

    if (ptr)
	gen(CALLm, ptr);
    else
	gen(CALL1, 0);
    gen(ADDSP, csp + nargs);
}

/*
** true if is2's operand should be doubled
*/
fdouble(oper, is1, is2)
int oper, is1[], is2[];
{
    //////////////////////////////////////////////////////
    // This is a 'magic' function, its usage and function
    // are not so obvious
    //
    // Purpose:  when indexing an address we must know
    // what we are pointing at so that the indexsize is
    // proper, e,g,
    // charptr++,  should multiply index by a 1
    // shortptr++, should multiply index by a 2
    // intptr++,   should multiply index by a 4
    //
    // Algorithm:
    // IF
    // operation is ADD12 or SUB12
    // AND
    // is1 is an address (pointer, array or via & operator
    // AND
    // is2 is NOT an address (pointer, array or via & operator
    // THEN 
    // return the multiplication factor based on s1 (or true)
    // ELSE
    // return 0 (or false)
    //
    // Usage: The return value is used as a 'boolean'
    // for nonconstant values, indicating that runtime code
    // is necessary to do the necessary multiplication
    // For contant value the return value is used to do a compile-time
    // multiplication (shift actually)
    //

    if ((oper == ADD12 || oper == SUB12)
	&& (is2[TA] == 0)) {
	switch (is1[TA] >> 2) {
	default:
	case 1:		// char
	    return (0);
	case 2:		// short
	    return (1);
	case 4:		// int
	    return (2);
	}
    }

    return (0);
}


step(oper, is, oper2)
int oper, is[], oper2;
{
    fetch(is);
    gen(oper, is[TA] ? (is[TA] >> 2) : 1);
    store(is);
    if (oper2)
	gen(oper2, is[TA] ? (is[TA] >> 2) : 1);
}

store(is)
int is[];
{
    char *ptr;

    if (is[TI]) {		/* putstk */
	if (is[TI] >> 2 == 1) {
	    gen(PUTbp1, 0);
	} else if (is[TI] >> 2 == 2) {
	    gen(PUTwp1, 0);
	} else {
	    gen(PUTdp1, 0);
	}
    } else {			/* putmem */
	ptr = is[ST];
	if (ptr[IDENT] == POINTER) {
	    gen(PUTdm1, ptr);
	} else if (ptr[TYPE] >> 2 == 1) {
	    gen(PUTbm1, ptr);
	} else if (ptr[TYPE] >> 2 == BPW) {
	    gen(PUTwm1, ptr);
	} else {
	    gen(PUTdm1, ptr);
	}
    }
}

fetch(is)
int is[];
{
    char *ptr;

    ptr = is[ST];
    if (is[TI]) {		/* indirect */
	if (is[TI] >> 2 == BPD) {	/* pointer to DWORD */
	    gen(GETd1p, 0);
	} else if (is[TI] >> 2 == BPW) {	/* pointer to WORD */
	    gen(GETw1p, 0);
	} else {
	    if (ptr[TYPE] & UNSIGNED)
		gen(GETb1pu, 0);
	    else
		gen(GETb1p, 0);
	}
    } else {			/* direct */

	if (ptr[IDENT] == POINTER) {
	    gen(GETd1m, ptr);
	} else if (ptr[TYPE] >> 2 == BPD) {
	    gen(GETd1m, ptr);
	} else if (ptr[TYPE] >> 2 == BPW) {
	    gen(GETw1m, ptr);
	} else {
	    if (ptr[TYPE] & UNSIGNED)
		gen(GETb1mu, ptr);
	    else
		gen(GETb1m, ptr);
	}
    }
}

constant(is)
int is[];
{
    int offset;

    if (is[TC] = number(is + CV)) {
	gen(GETd1n, is[CV]);
    } else if (is[TC] = chrcon(is + CV)) {
	gen(GETd1n, is[CV]);
    } else if (string(&offset)) {
	gen(POINT1l, offset);
    } else {
	return 0;
    }
    return 1;
}

number(value)
int *value;
{
    int k, minus;
    k = minus = 0;
    while (1) {
	if (match("+"));
	else if (match("-"))
	    minus = 1;
	else
	    break;
    }
    if (isdigit(ch) == 0)
	return 0;
    if (ch == '0') {
	while (ch == '0')
	    inbyte();
	if (toupper(ch) == 'X') {
	    inbyte();
	    while (isxdigit(ch)) {
		if (isdigit(ch))
		    k = k * 16 + (inbyte() - '0');
		else
		    k = k * 16 + 10 + (toupper(inbyte()) - 'A');
	    }
	} else
	    while (ch >= '0' && ch <= '7')
		k = k * 8 + (inbyte() - '0');
    } else
	while (isdigit(ch))
	    k = k * 10 + (inbyte() - '0');
    if (minus) {
	*value = -k;
	return (INT);
    }
    if ((*value = k) < 0)
	return (UINT);
    else
	return (INT);
}

chrcon(value)
int *value;
{
    int k;

    k = 0;
    if (match("'") == 0)
	return 0;
    while (ch != '\'')
	k = (k << 8) + (litchar() & 255);
    gch();
    *value = k;
    return (INT);
}

string(offset)
int *offset;
{
    char c;
    if (match(quote) == 0)
	return 0;
    *offset = litptr;
    while (ch != '"') {
	if (ch == 0)
	    break;
	stowlit(litchar(), 1);
    }
    gch();
    litq[litptr++] = 0;
    return 1;
}

stowlit(value, size)
int value, size;
{
    if ((litptr + size) >= LITMAX) {
	error("literal queue overflow");
	exit(ERRCODE);
    }
    putint(value, litq + litptr, size);
    litptr += size;
}

litchar()
{
    int i, oct;
    if (ch != '\\' || nch == 0)
	return gch();
    gch();
    switch (ch) {
    case 'n':
	gch();
	return NEWLINE;
    case 't':
	gch();
	return 9;		/* HT */
    case 'b':
	gch();
	return 8;		/* BS */
    case 'f':
	gch();
	return 12;		/* FF */
    }
    i = 3;
    oct = 0;
    while ((i--) > 0 && ch >= '0' && ch <= '7')
	oct = (oct << 3) + gch() - '0';
    if (i == 2)
	return gch();
    else
	return oct;
}

/***************** pipeline functions ******************/

/*
** skim over terms adjoining || and && operators
*/
skim(opstr, tcode, dropval, endval, level, is)
char *opstr;
int tcode, dropval, endval, (*level) (), is[];
{
    int k, droplab, endlab;
    droplab = 0;
    while (1) {
	k = down1(level, is);
	if (nextop(opstr)) {
	    bump(opsize);
	    if (droplab == 0)
		droplab = getlabel();
	    dropout(k, tcode, droplab, is);
	} else if (droplab) {
	    dropout(k, tcode, droplab, is);
	    gen(GETd1n, endval);
	    gen(JMPm, endlab = getlabel());
	    gen(LABm, droplab);
	    gen(GETd1n, dropval);
	    gen(LABm, endlab);
	    is[TI] = is[TA] = is[TC] = is[CV] = is[SA] = 0;
	    return 0;
	} else
	    return k;
    }
}

/*
** test for early dropout from || or && sequences
*/
dropout(k, tcode, exit1, is)
int k, tcode, exit1, is[];
{
    if (k)
	fetch(is);
    else if (is[TC]) {
	gen(GETd1n, is[CV]);
    }
    gen(tcode, exit1);		/* jumps on false */
}

/*
** drop to a lower level
*/
down(opstr, opoff, level, is)
char *opstr;
int opoff, (*level) (), is[];
{
    int k;
    k = down1(level, is);
    if (nextop(opstr) == 0)
	return k;
    if (k)
	fetch(is);
    while (1) {
	if (nextop(opstr)) {
	    int is2[7];		/* allocate only if needed */

	    null_is(is2);

	    bump(opsize);
	    opindex += opoff;
	    down2(op[opindex], op2[opindex], level, is, is2);
	} else
	    return 0;
    }
}

/*
** unary drop to a lower level
*/
down1(level, is)
int (*level) (), is[];
{
    int k, *before, *start;
    setstage(&before, &start);
    k = (*level) (is);
    if (is[TC])
	clearstage(before, 0);	/* load constant later */
    return k;
}

/*
** binary drop to a lower level
*/
down2(oper, oper2, level, is, is2)
int oper, oper2, (*level) (), is[], is2[];
{
    int *before, *start;
    char *ptr;
    int value;
    setstage(&before, &start);
    is[SA] = 0;			/* not "... op 0" syntax */
    if (is[TC]) {		/* consant op unknown */
	if (down1(level, is2))
	    fetch(is2);
	if (is[CV] == 0)
	    is[SA] = snext;
	gen(GETw2n, is[CV] << fdouble(oper, is2, is));
    } else {			/* variable op unknown */
	gen(PUSH1, 0);		/* at start in the buffer */
	if (down1(level, is2))
	    fetch(is2);
	if (is2[TC]) {		/* variable op constant */
	    if (is2[CV] == 0)
		is[SA] = start;
	    csp += BPD;		/* adjust stack and */
	    clearstage(before, 0);	/* discard the PUSH */
	    if (oper == ADD12) {	/* commutative */
		gen(GETd2n, is2[CV] << fdouble(oper, is, is2));
	    } else {		/* non-commutative */
		gen(MOVE21, 0);
		gen(GETd1n, is2[CV] << fdouble(oper, is, is2));
	    }
	} else {		/* variable op variable */
	    gen(POP2, 0);
	    if (value = fdouble(oper, is, is2)) {
		gen(DBL1, 0);	// index size 2
		if (value > 1)
		    gen(DBL1, 0);	// .. or even 4
	    }
	    if (value = fdouble(oper, is2, is)) {
		gen(DBL2, 0);
		if (value > 1)
		    gen(DBL2, 0);
	    }
	}
    }
    if (oper) {
	if (nosign(is) || nosign(is2))
	    oper = oper2;
	if (is[TC] = is[TC] & is2[TC]) {	/* constant result */
	    is[CV] = calc(is[CV], oper, is2[CV]);
	    clearstage(before, 0);
	    if (is2[TC] == UINT)
		is[TC] = UINT;
	} else {		/* variable result */
	    gen(oper, 0);
	    if (oper == SUB12 && is[TA] >> 2 == BPW && is2[TA] >> 2 == BPW) {	/* difference of two word addresses */
		gen(SWAP12, 0);
		gen(GETw1n, 1);
		gen(ASR12, 0);	/* div by 2 */
	    }
	    is[OP] = oper;	/* identify the operator */
	}
	if (oper == SUB12 || oper == ADD12) {
	    if (is[TA] && is2[TA])	/*  addr +/- addr */
		is[TA] = 0;
	    else if (is2[TA]) {	/* value +/- addr */
		is[ST] = is2[ST];
		is[TI] = is2[TI];
		is[TA] = is2[TA];
	    }
	}
	if (is[ST] == 0 || ((ptr = is2[ST]) && (ptr[TYPE] & UNSIGNED)))
	    is[ST] = is2[ST];
    }
}

/*
** unsigned operand?
*/
nosign(is)
int is[];
{
    char *ptr;
    if (is[TA]
	|| is[TC] == UINT || ((ptr = is[ST]) && (ptr[TYPE] & UNSIGNED))
	)
	return 1;
    return 0;
}

/*
** calcualte signed constant result
*/
calc(left, oper, right)
int left, oper, right;
{
    switch (oper) {
    case ADD12:
	return (left + right);
    case SUB12:
	return (left - right);
    case MUL12:
	return (left * right);
    case DIV12:
	return (left / right);
    case MOD12:
	return (left % right);
    case EQ12:
	return (left == right);
    case NE12:
	return (left != right);
    case LE12:
	return (left <= right);
    case GE12:
	return (left >= right);
    case LT12:
	return (left < right);
    case GT12:
	return (left > right);
    case AND12:
	return (left & right);
    case OR12:
	return (left | right);
    case XOR12:
	return (left ^ right);
    case ASR12:
	return (left >> right);
    case ASL12:
	return (left << right);
    }
    return (calc2(left, oper, right));
}

/*
** calcualte unsigned constant result
*/
calc2(left, oper, right)
unsigned left, right;
int oper;
{
    switch (oper) {
    case MUL12u:
	return (left * right);
    case DIV12u:
	return (left / right);
    case MOD12u:
	return (left % right);
    case LE12u:
	return (left <= right);
    case GE12u:
	return (left >= right);
    case LT12u:
	return (left < right);
    case GT12u:
	return (left > right);
    }
    return (0);
}
