
/*
 *  EXP.C
 *
 *  Handle expression evaluation and addressing mode decode.
 *
 *  NOTE! If you use the string field in an expression you must clear
 *  the SYM_MACRO and SYM_STRING bits in the flags before calling
 *  freesymbollist()!
 */

#include "asm.h"

#define UNION	0

#if UNION		/* warning: ANSI disallows cast to union type */
typedef void (unop)(long v1, int f1);
typedef void (binop)(long v1, long v2, int f1, int f2);

union unibin {
    unop *unary;
    binop *binary;
};

typedef union unibin opfunc_t;
#define _unary	.unary
#define _binary .binary
#else			/* warning: Calling functions without prototype */

typedef void (*opfunc_t)();
#define _unary
#define _binary

#endif

void stackarg(long val, int flags);

void doop(opfunc_t, int pri);
void evaltop(void);
void	op_mult(long v1, long v2, int f1, int f2),
	op_div(long v1, long v2, int f1, int f2),
	op_mod(long v1, long v2, int f1, int f2),
	op_add(long v1, long v2, int f1, int f2),
	op_sub(long v1, long v2, int f1, int f2),
	op_shiftleft(long v1, long v2, int f1, int f2),
	op_shiftright(long v1, long v2, int f1, int f2),
	op_greater(long v1, long v2, int f1, int f2),
	op_greatereq(long v1, long v2, int f1, int f2),
	op_smaller(long v1, long v2, int f1, int f2),
	op_smallereq(long v1, long v2, int f1, int f2),
	op_eqeq(long v1, long v2, int f1, int f2),
	op_noteq(long v1, long v2, int f1, int f2),
	op_andand(long v1, long v2, int f1, int f2),
	op_oror(long v1, long v2, int f1, int f2),
	op_xor(long v1, long v2, int f1, int f2),
	op_and(long v1, long v2, int f1, int f2),
	op_or(long v1, long v2, int f1, int f2),
	op_question(long v1, long v2, int f1, int f2);

void	op_takelsb(long v1, int f1),
	op_takemsb(long v1, int f1),
	op_negate(long v1, int f1),
	op_invert(long v1, int f1),
	op_not(long v1, int f1);


char *pushsymbol(char *str);
char *pushstr(char *str);
char *pushbin(char *str);
char *pushoct(char *str);
char *pushdec(char *str);
char *pushhex(char *str);
char *pushchar(char *str);

int alphanum(int c);

/*
 *  evaluate an expression.  Figure out the addressing mode:
 *
 *		implied
 *    #val	immediate
 *    val	zero page or absolute
 *    val,x	zero,x or absolute,x
 *    val,y	zero,y or absolute,y
 *    (val)	indirect
 *    (val,x)	zero indirect x
 *    (val),y	zero indirect y
 *
 *    exp, exp,.. LIST of expressions
 *
 *  an absolute may be returned as zero page
 *  a relative may be returned as zero page or absolute
 *
 *  unary:  - ~ ! < >
 *  binary: (^)(* / %)(+ -)(>> <<)(& |)(`)(&& ||)(== != < > <= >=)
 *
 *  values: symbol, octal, decimal, $hex, %binary, 'c "str"
 *
 */

#define MAXOPS	    32
#define MAXARGS     64

ubyte Argflags[MAXARGS];
long  Argstack[MAXARGS];
char *Argstring[MAXARGS];
int Oppri[MAXOPS];
opfunc_t Opdis[MAXOPS];

int	Argi, Opi, Lastwasop;
int	Argibase, Opibase;

SYMBOL *
eval(char *str, int wantmode)
{
    register SYMBOL *base, *cur;
    int oldargibase = Argibase;
    int oldopibase = Opibase;
    int scr;

    Argibase = Argi;
    Opibase = Opi;
    Lastwasop = 1;
    base = cur = allocsymbol();

    while (*str) {
	if (Xdebug)
	    printf("char '%c'\n", *str);
	switch(*str) {
	case ' ':
	case '\n':
	    ++str;
	    break;
	case '~':
	    if (Lastwasop)
		doop((opfunc_t)op_invert, 128);
	    else
		asmerr(0,0);
	    ++str;
	    break;
	case '*':
#if OlafStar
	    if (Lastwasop) {
		pushsymbol(".");
	    } else
#endif
		doop((opfunc_t)op_mult, 20);
	    ++str;
	    break;
	case '/':
	    doop((opfunc_t)op_div, 20);
	    ++str;
	    break;
	case '%':
	    if (Lastwasop) {
		str = (char *)pushbin(str+1);
	    } else {
		doop((opfunc_t)op_mod, 20);
		++str;
	    }
	    break;
	case '?':   /*  10      */
	    doop((opfunc_t)op_question, 10);
	    ++str;
	    break;
	case '+':   /*  19      */
	    doop((opfunc_t)op_add, 19);
	    ++str;
	    break;
	case '-':   /*  19: -   (or - unary)        */
	    if (Lastwasop) {
		doop((opfunc_t)op_negate, 128);
	    } else {
		doop((opfunc_t)op_sub, 19);
	    }
	    ++str;
	    break;
	case '>':   /*  18: >> <<  17: > >= <= <    */
	    if (Lastwasop) {
#if OlafLt
		if (SwapLessMore)
		    doop((opfunc_t)op_takelsb, 128);
		else
		    doop((opfunc_t)op_takemsb, 128);
#else
		doop((opfunc_t)op_takelsb, 128);  /* SHOULD BE op_takemsb */
#endif
		++str;
		break;
	    }
	    if (str[1] == '>') {
		doop((opfunc_t)op_shiftright, 18);
		++str;
	    } else if (str[1] == '=') {
		doop((opfunc_t)op_greatereq, 17);
		++str;
	    } else {
		doop((opfunc_t)op_greater, 17);
	    }
	    ++str;
	    break;
	case '<':
	    if (Lastwasop) {
#if OlafLt
		if (SwapLessMore)
		    doop((opfunc_t)op_takemsb, 128);
		else
		    doop((opfunc_t)op_takelsb, 128);
#else
		doop((opfunc_t)op_takemsb, 128);
#endif
		++str;
		break;
	    }
	    if (str[1] == '<') {
		doop((opfunc_t)op_shiftleft, 18);
		++str;
	    } else if (str[1] == '=') {
		doop((opfunc_t)op_smallereq, 17);
		++str;
	    } else {
		doop((opfunc_t)op_smaller, 17);
	    }
	    ++str;
	    break;
	case '=':   /*  16: ==  (= same as ==)      */
	    if (str[1] == '=')
		++str;
	    doop((opfunc_t)op_eqeq, 16);
	    ++str;
	    break;
	case '!':   /*  16: !=                      */
	    if (Lastwasop) {
		doop((opfunc_t)op_not, 128);
	    } else {
		doop((opfunc_t)op_noteq, 16);
		++str;
	    }
	    ++str;
	    break;
	case '&':   /*  15: &   12: &&              */
	    if (str[1] == '&') {
		doop((opfunc_t)op_andand, 12);
		++str;
	    } else {
		doop((opfunc_t)op_and, 15);
	    }
	    ++str;
	    break;
	case '^':   /*  14: ^                       */
	    doop((opfunc_t)op_xor, 14);
	    ++str;
	    break;
	case '|':   /*  13: |   11: ||              */
	    if (str[1] == '|') {
		doop((opfunc_t)op_oror, 11);
		++str;
	    } else {
		doop((opfunc_t)op_or, 13);
	    }
	    ++str;
	    break;
	case '[':   /*  eventually an argument      */
	bra:
	    if (Opi == MAXOPS)
		puts("too many ops");
	    else
		Oppri[Opi++] = 0;
	    ++str;
	    break;
	case ']':
	ket:
	    while(Opi != Opibase && Oppri[Opi-1])
		evaltop();
	    if (Opi != Opibase)
		--Opi;
	    ++str;
	    if (Argi == Argibase) {
		puts("']' error, no arg on stack");
		break;
	    }
	    if (*str == 'd') {  /*  STRING CONVERSION   */
		char buf[32];
		++str;
		if (Argflags[Argi-1] == 0) {
		    sprintf(buf,"%ld",Argstack[Argi-1]);
		    Argstring[Argi-1] = strcpy(malloc(strlen(buf)+1),buf);
		}
	    }
	    break;
	case '#':
	    cur->addrmode = AM_IMM8;
	    ++str;
	    break;
	case '(':
#if OlafBraKet
	    if (!wantmode)
		goto bra;
#endif
	    cur->addrmode = AM_INDWORD;
	    ++str;
	    break;
	case ')':
#if OlafBraKet
	    if (!wantmode)
		goto ket;
#endif
	    if (cur->addrmode == AM_INDWORD &&
		    str[1] == ',' && (str[2]|0x20) == 'y') {
		cur->addrmode = AM_INDBYTEY;
		str += 2;
	    }
	    ++str;
	    break;
	case ',':
	    while(Opi != Opibase)
		evaltop();
	    Lastwasop = 1;
	    scr = str[1]|0x20;	  /* to lower case */
	    if (cur->addrmode == AM_INDWORD && scr == 'x' && !alphanum(str[2])) {
		cur->addrmode = AM_INDBYTEX;
		++str;
	    } else if (scr == 'x' && !alphanum(str[2])) {
		cur->addrmode = AM_0X;
		++str;
	    } else if (scr == 'y' && !alphanum(str[2])) {
		cur->addrmode = AM_0Y;
		++str;
	    } else {
		register SYMBOL *new = allocsymbol();
		cur->next = new;
		--Argi;
		if (Argi < Argibase)
		    asmerr(0,0);
		if (Argi > Argibase)
		    asmerr(0,0);
		cur->value = Argstack[Argi];
		cur->flags = Argflags[Argi];
		if ((cur->string = (void *)Argstring[Argi]) != NULL) {
		    cur->flags |= SYM_STRING;
		    if (Xdebug)
			printf("STRING: %s\n", cur->string);
		}
		cur = new;
	    }
	    ++str;
	    break;
	case '$':
	    str = pushhex(str+1);
	    break;
	case '\'':
	    str = pushchar(str+1);
	    break;
	case '\"':
	    str = pushstr(str+1);
	    break;
	default:
#if OlafDol
	    {
		char *dol = str;
		while (*dol >= '0' && *dol <= '9')
		    dol++;
		if (*dol == '$') {
		    str = pushsymbol(str);
		    break;
		}
	    }
#endif
	    if (*str == '0')
		str = pushoct(str);
	    else {
		if (*str > '0' && *str <= '9')
		    str = pushdec(str);
		else
		    str = pushsymbol(str);
	    }
	    break;
	}
    }
    while(Opi != Opibase)
	evaltop();
    if (Argi != Argibase) {
	--Argi;
	cur->value = Argstack[Argi];
	cur->flags = Argflags[Argi];
	if ((cur->string = (void *)Argstring[Argi]) != NULL) {
	    cur->flags |= SYM_STRING;
	    if (Xdebug)
		printf("STRING: %s\n", cur->string);
	}
	if (base->addrmode == 0)
	    base->addrmode = AM_BYTEADR;
    }
    if (Argi != Argibase || Opi != Opibase)
	asmerr(0,0);
    Argi = Argibase;
    Opi  = Opibase;
    Argibase = oldargibase;
    Opibase = oldopibase;
    return(base);
}

int
alphanum(int c)
{
    return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
	    (c >= '0' && c <= '9'));
}

void
evaltop(void)
{
    if (Xdebug)
	printf("evaltop @(A,O) %d %d\n", Argi, Opi);
    if (Opi <= Opibase) {
	asmerr(0,0);
	Opi = Opibase;
	return;
    }
    --Opi;
    if (Oppri[Opi] == 128) {
	if (Argi < Argibase + 1) {
	    asmerr(0,0);
	    Argi = Argibase;
	    return;
	}
	--Argi;
	(*Opdis[Opi]_unary)(Argstack[Argi], Argflags[Argi]);
    } else {
	if (Argi < Argibase + 2) {
	    asmerr(0,0);
	    Argi = Argibase;
	    return;
	}
	Argi -= 2;
	(*Opdis[Opi]_binary)(Argstack[Argi], Argstack[Argi+1],
			     Argflags[Argi], Argflags[Argi+1]);
    }
}

void
stackarg(long val, int flags)
{
    char *str = NULL;

    if (Xdebug)
	printf("stackarg %ld (@%d)\n", val, Argi);
    Lastwasop = 0;
    if (flags & SYM_STRING) {
	unsigned char *ptr = (unsigned char *)str = (char *)val;
	char *new;
	int len;
	val = len = 0;
	while (*ptr && *ptr != '\"') {
	    val = (val << 8) | *ptr;
	    ++ptr;
	    ++len;
	}
	new = malloc(len + 1);
	memcpy(new, str, len);
	new[len] = 0;
	flags &= ~SYM_STRING;
	str = new;
    }
    Argstack[Argi] = val;
    Argstring[Argi] = str;
    Argflags[Argi] = flags;
    if (++Argi == MAXARGS) {
	puts("stackarg: maxargs stacked");
	Argi = Argibase;
    }
    while (Opi != Opibase && Oppri[Opi-1] == 128)
	evaltop();
}

void
doop(opfunc_t func, int pri)
{
    if (Xdebug)
	puts("doop");
    Lastwasop = 1;
    if (Opi == Opibase || pri == 128) {
	if (Xdebug)
	    printf("doop @ %d unary\n", Opi);
	Opdis[Opi] = func;
	Oppri[Opi] = pri;
	++Opi;
	return;
    }
    while (Opi != Opibase && Oppri[Opi-1] && pri <= Oppri[Opi-1])
	evaltop();
    if (Xdebug)
	printf("doop @ %d\n", Opi);
    Opdis[Opi] = func;
    Oppri[Opi] = pri;
    ++Opi;
    if (Opi == MAXOPS) {
	puts("doop: too many operators");
	Opi = Opibase;
    }
    return;
}

void
op_takelsb(long v1, int f1)
{
    stackarg(v1 & 0xFFL, f1);
}

void
op_takemsb(long v1, int f1)
{
    stackarg((v1 >> 8) & 0xFF, f1);
}

void
op_negate(long v1, int f1)
{
    stackarg(-v1, f1);
}

void
op_invert(long v1, int f1)
{
    stackarg(~v1, f1);
}

void
op_not(long v1, int f1)
{
    stackarg(!v1, f1);
}

void
op_mult(long v1, long v2, int f1, int f2)
{
    stackarg(v1 * v2, f1|f2);
}

void
op_div(long v1, long v2, int f1, int f2)
{
    if (f1|f2) {
	stackarg(0L, f1|f2);
	return;
    }
    if (v2 == 0) {
	puts("division by zero");
	stackarg(0L, 0);
    } else {
	stackarg(v1 / v2, 0);
    }
}

void
op_mod(long v1, long v2, int f1, int f2)
{
    if (f1|f2) {
	stackarg(0L, f1|f2);
	return;
    }
    if (v2 == 0)
	stackarg(v1, 0);
    else
	stackarg(v1 % v2, 0);
}

void
op_question(long v1, long v2, int f1, int f2)
{
    if (f1)
       stackarg(0L, f1);
    else
	stackarg((long)((v1) ? v2 : 0), ((v1) ? f2 : 0));
}

void
op_add(long v1, long v2, int f1, int f2)
{
    stackarg(v1 + v2, f1|f2);
}

void
op_sub(long v1, long v2, int f1, int f2)
{
    stackarg(v1 - v2, f1|f2);
}

void
op_shiftright(long v1, long v2, int f1, int f2)
{
    if (f1|f2)
	stackarg(0L, f1|f2);
    else
	stackarg((long)(v1 >> v2), 0);
}

void
op_shiftleft(long v1, long v2, int f1, int f2)
{
    if (f1|f2)
	stackarg(0L, f1|f2);
    else
	stackarg((long)(v1 << v2), 0);
}

void
op_greater(long v1, long v2, int f1, int f2)
{
    stackarg((long)(v1 > v2), f1|f2);
}

void
op_greatereq(long v1, long v2, int f1, int f2)
{
    stackarg((long)(v1 >= v2), f1|f2);
}

void
op_smaller(long v1, long v2, int f1, int f2)
{
    stackarg((long)(v1 < v2), f1|f2);
}

void
op_smallereq(long v1, long v2, int f1, int f2)
{
    stackarg((long)(v1 <= v2), f1|f2);
}

void
op_eqeq(long v1, long v2, int f1, int f2)
{
    stackarg((long)(v1 == v2), f1|f2);
}

void
op_noteq(long v1, long v2, int f1, int f2)
{
    stackarg((long)(v1 != v2), f1|f2);
}

void
op_andand(long v1, long v2, int f1, int f2)
{
    if ((!f1 && !v1) || (!f2 && !v2)) {
	stackarg(0L, 0);
	return;
    }
    stackarg(1L, f1|f2);
}

void
op_oror(long v1, long v2, int f1, int f2)
{
    if ((!f1 && v1) || (!f2 && v2)) {
	stackarg(1L, 0);
	return;
    }
    stackarg(0L, f1|f2);
}

void
op_xor(long v1, long v2, int f1, int f2)
{
    stackarg(v1^v2, f1|f2);
}

void
op_and(long v1, long v2, int f1, int f2)
{
    stackarg(v1&v2, f1|f2);
}

void
op_or(long v1, long v2, int f1, int f2)
{
    stackarg(v1|v2, f1|f2);
}

char *
pushchar(char *str)
{
    if (*str) {
	stackarg((long)*str, 0);
	++str;
    } else {
	stackarg((long)' ', 0);
    }
    return str;
}

char *
pushhex(char *str)
{
    register long val = 0;
    for (;; ++str) {
	if (*str >= '0' && *str <= '9') {
	    val = (val << 4) + (*str - '0');
	    continue;
	}
	if ((*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F')) {
	    val = (val << 4) + ((*str&0x1F) + 9);
	    continue;
	}
	break;
    }
    stackarg(val, 0);
    return str;
}

char *
pushoct(char *str)
{
    register long val = 0;
    while (*str >= '0' && *str <= '7') {
	val = (val << 3) + (*str - '0');
	++str;
    }
    stackarg(val, 0);
    return str;
}

char *
pushdec(char *str)
{
    register long val = 0;
    while (*str >= '0' && *str <= '9') {
	val = (val * 10) + (*str - '0');
	++str;
    }
    stackarg(val, 0);
    return str;
}

char *
pushbin(char *str)
{
    register long val = 0;
    while (*str == '0' || *str == '1') {
	val = (val << 1) | (*str - '0');
	++str;
    }
    stackarg(val, 0);
    return str;
}

char *
pushstr(char *str)
{
    stackarg((long)str, SYM_STRING);	/* warning: pointer to integer */
    while (*str && *str != '\"')
	++str;
    if (*str == '\"')
	++str;
    return str;
}

char *
pushsymbol(char *str)
{
    register SYMBOL *sym;
    register char *ptr;
    ubyte macro = 0;

    for (ptr = str;
	*ptr == '_' ||
	*ptr == '.' ||
	(*ptr >= 'a' && *ptr <= 'z') ||
	(*ptr >= 'A' && *ptr <= 'Z') ||
	(*ptr >= '0' && *ptr <= '9');
	++ptr
	);
    if (ptr == str) {
	asmerr(9,0);
	printf("char = '%c' %d (-1: %d)\n", *str, *str, *(str-1));
	if (F_listfile)
	    fprintf(FI_listfile, "char = '%c' code %d\n", *str, *str);
	return str+1;
    }
#if OlafDol
    if (*ptr == '$')
	ptr++;
#endif
    if ((sym = findsymbol(str, ptr - str)) != NULL) {
	if (sym->flags & SYM_UNKNOWN)
	    ++Redo_eval;
	if (sym->flags & SYM_MACRO) {
	    macro = 1;
	    sym = eval(sym->string, 0);
	}
	if (sym->flags & SYM_STRING)
	    stackarg((long)sym->string, SYM_STRING);	/* warning:ptr->int */
	else
	    stackarg(sym->value, sym->flags & SYM_UNKNOWN);
	sym->flags |= SYM_REF|SYM_MASREF;
	if (macro)
	    freesymbollist(sym);
    } else {
	stackarg(0L, SYM_UNKNOWN);
	sym = createsymbol(str, ptr - str);
	sym->flags = SYM_REF|SYM_MASREF|SYM_UNKNOWN;
	++Redo_eval;
    }
    return(ptr);
}

