#ifndef NORCSID
static char rcsid[] = "$Header$";
#endif

#include "assert.h"
#include "param.h"
#include "tables.h"
#include "types.h"
#include <cgg_cg.h>
#include "data.h"
#include "result.h"
#include "glosym.h"
#include "extern.h"

/*
 * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands.
 *
 *          This product is part of the Amsterdam Compiler Kit.
 *
 * Permission to use, sell, duplicate or disclose this software must be
 * obtained in writing. Requests for such permissions may be sent to
 *
 *      Dr. Andrew S. Tanenbaum
 *      Wiskundig Seminarium
 *      Vrije Universiteit
 *      Postbox 7161
 *      1007 MC Amsterdam
 *      The Netherlands
 *
 * Author: Hans van Staveren
 */

#define LLEAF 01
#define LDEF  02
#define RLEAF 04
#define RDEF  010
#define LLDEF LLEAF|LDEF
#define RLDEF RLEAF|RDEF

char opdesc[] = {
	0,                      /* EX_TOKFIELD */
	0,                      /* EX_ARG */
	0,                      /* EX_CON */
	0,                      /* EX_ALLREG */
	LLDEF|RLDEF,            /* EX_SAMESIGN */
	LLDEF|RLDEF,            /* EX_SFIT */
	LLDEF|RLDEF,            /* EX_UFIT */
	0,                      /* EX_ROM */
	LLDEF|RLDEF,            /* EX_NCPEQ */
	LLDEF|RLDEF,            /* EX_SCPEQ */
	LLDEF|RLDEF,            /* EX_RCPEQ */
	LLDEF|RLDEF,            /* EX_NCPNE */
	LLDEF|RLDEF,            /* EX_SCPNE */
	LLDEF|RLDEF,            /* EX_RCPNE */
	LLDEF|RLDEF,            /* EX_NCPGT */
	LLDEF|RLDEF,            /* EX_NCPGE */
	LLDEF|RLDEF,            /* EX_NCPLT */
	LLDEF|RLDEF,            /* EX_NCPLE */
	LLDEF,                  /* EX_OR2 */
	LLDEF,                  /* EX_AND2 */
	LLDEF|RLDEF,            /* EX_PLUS */
	LLDEF|RLDEF,            /* EX_CAT */
	LLDEF|RLDEF,            /* EX_MINUS */
	LLDEF|RLDEF,            /* EX_TIMES */
	LLDEF|RLDEF,            /* EX_DIVIDE */
	LLDEF|RLDEF,            /* EX_MOD */
	LLDEF|RLDEF,            /* EX_LSHIFT */
	LLDEF|RLDEF,            /* EX_RSHIFT */
	LLDEF,                  /* EX_NOT */
	LLDEF,                  /* EX_COMP */
	0,                      /* EX_COST */
	0,                      /* EX_STRING */
	LLEAF,                  /* EX_DEFINED */
	0,                      /* EX_SUBREG */
	LLDEF,                  /* EX_TOSTRING */
	LLDEF,                  /* EX_UMINUS */
	0,                      /* EX_REG */
	0,			/* EX_LOWW */
	0,			/* EX_HIGHW */
	LLDEF,			/* EX_INREG */
	LLDEF,			/* EX_REGVAR */
};

string salloc(),strcpy(),strcat();

string mycat(s1,s2) register string s1,s2; {
	register string s;

	if (s1==0) return(s2);
	if (s2==0) return(s1);
	s=salloc(strlen(s1)+strlen(s2)+1);
	strcpy(s,s1);
	strcat(s,"+");
	strcat(s,s2);
	return(s);
}

string mystrcpy(s) register string s; {
	register string r;

	r=salloc(strlen(s));
	strcpy(r,s);
	return(r);
}

char digstr[21][15];

string tostring(n) register word n; {
	char buf[25];

	if (n>=-20 && n<=20 && (n&1)==0) {
		if (digstr[(n>>1)+10][0]==0)
			sprintf(digstr[(n>>1)+10],WRD_FMT,n);
		return(digstr[(n>>1)+10]);
	}
	sprintf(buf,WRD_FMT,n);
	return(mystrcpy(buf));
}

result_t undefres= {EV_UNDEF};

result_t compute(node) register node_p node; {
	result_t leaf1,leaf2,result;
	register token_p tp;
	int desc;
	long mask,tmp;
	int i,tmpreg;
	glosym_p gp;

	desc=opdesc[node->ex_operator];
	if (desc&LLEAF) {
		leaf1 = compute(&enodes[node->ex_lnode]);
		if (desc&LDEF && leaf1.e_typ==EV_UNDEF)
			return(undefres);
	}
	if (desc&RLEAF) {
		leaf2 = compute(&enodes[node->ex_rnode]);
		if (desc&RDEF && leaf2.e_typ==EV_UNDEF)
			return(undefres);
	}
	result.e_typ=EV_INT;
	switch(node->ex_operator) {
	default:        assert(FALSE);
	case EX_TOKFIELD:
		if (node->ex_lnode!=0)
			tp = &fakestack[stackheight-node->ex_lnode];
		else
			tp = curtoken;
		switch(result.e_typ = tokens[tp->t_token].t_type[node->ex_rnode-1]) {
		default:
			assert(FALSE);
		case EV_INT:
			result.e_v.e_con = tp->t_att[node->ex_rnode-1].aw;
			break;
		case EV_ADDR:
			result.e_v.e_addr = tp->t_att[node->ex_rnode-1].aa;
			break;
		case EV_REG:
			result.e_v.e_reg = tp->t_att[node->ex_rnode-1].ar;
			break;
		}
		return(result);
	case EX_ARG:
		return(dollar[node->ex_lnode-1]);
	case EX_CON:
		result.e_typ = EV_INT;
		result.e_v.e_con = ((long) node->ex_rnode << 16) | ((long)node->ex_lnode&0xffff);
		return(result);
	case EX_REG:
		result.e_typ = EV_REG;
		result.e_v.e_reg = node->ex_lnode;
		return(result);
	case EX_ALLREG:
		result.e_typ = EV_REG;
		result.e_v.e_reg = allreg[node->ex_lnode-1];
#if MAXMEMBERS!=0
		if (node->ex_rnode!=0)
			result.e_v.e_reg = machregs[result.e_v.e_reg].
				r_members[node->ex_rnode-1];
#endif
		return(result);
	case EX_SAMESIGN:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_typ = EV_INT;
		if (leaf1.e_v.e_con>=0)
			result.e_v.e_con= leaf2.e_v.e_con>=0;
		else
			result.e_v.e_con= leaf2.e_v.e_con<0;
		return(result);
	case EX_SFIT:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		mask = 0xFFFFFFFFL;
		for (i=0;i<leaf2.e_v.e_con-1;i++)
			mask &= ~(1<<i);
		tmp = leaf1.e_v.e_con&mask;
		result.e_v.e_con = tmp==0||tmp==mask;
		return(result);
	case EX_UFIT:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		mask = 0xFFFFFFFFL;
		for (i=0;i<leaf2.e_v.e_con;i++)
			mask &= ~(1<<i);
		result.e_v.e_con = (leaf1.e_v.e_con&mask)==0;
		return(result);
	case EX_ROM:
		assert(node->ex_rnode>=0 &&node->ex_rnode<MAXROM);
		leaf2=dollar[node->ex_lnode];
		if (leaf2.e_typ != EV_ADDR)
			return(undefres);
		if (leaf2.e_v.e_addr.ea_off!=0)
			return(undefres);
		gp = lookglo(leaf2.e_v.e_addr.ea_str);
		if (gp == (glosym_p) 0)
			return(undefres);
		if ((gp->gl_rom[MAXROM]&(1<<node->ex_rnode))==0)
			return(undefres);
		result.e_v.e_con = gp->gl_rom[node->ex_rnode];
		return(result);
	case EX_LOWW:
		result.e_v.e_con = saveemp[node->ex_lnode].em_u.em_loper&0xFFFF;
		return(result);
	case EX_HIGHW:
		result.e_v.e_con = saveemp[node->ex_lnode].em_u.em_loper>>16;
		return(result);
	case EX_NCPEQ:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con==leaf2.e_v.e_con;
		return(result);
	case EX_SCPEQ:
	assert(leaf1.e_typ == EV_ADDR && leaf2.e_typ == EV_ADDR);
		result.e_v.e_con =
		    (strcmp(leaf1.e_v.e_addr.ea_str,leaf2.e_v.e_addr.ea_str)==0 &&
		    leaf1.e_v.e_addr.ea_off==leaf2.e_v.e_addr.ea_off);
		return(result);
	case EX_RCPEQ:
	assert(leaf1.e_typ == EV_REG && leaf2.e_typ == EV_REG);
		result.e_v.e_con = leaf1.e_v.e_reg==leaf2.e_v.e_reg;
		return(result);
	case EX_NCPNE:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con!=leaf2.e_v.e_con;
		return(result);
	case EX_SCPNE:
	assert(leaf1.e_typ == EV_ADDR && leaf2.e_typ == EV_ADDR);
		result.e_v.e_con =
		    !(strcmp(leaf1.e_v.e_addr.ea_str,leaf2.e_v.e_addr.ea_str)==0 &&
		    leaf1.e_v.e_addr.ea_off==leaf2.e_v.e_addr.ea_off);
		return(result);
	case EX_RCPNE:
	assert(leaf1.e_typ == EV_REG && leaf2.e_typ == EV_REG);
		result.e_v.e_con = leaf1.e_v.e_reg!=leaf2.e_v.e_reg;
		return(result);
	case EX_NCPGT:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con>leaf2.e_v.e_con;
		return(result);
	case EX_NCPGE:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con>=leaf2.e_v.e_con;
		return(result);
	case EX_NCPLT:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con<leaf2.e_v.e_con;
		return(result);
	case EX_NCPLE:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con<=leaf2.e_v.e_con;
		return(result);
	case EX_OR2:
	assert(leaf1.e_typ == EV_INT);
		if (leaf1.e_v.e_con==0)
			return(compute(&enodes[node->ex_rnode]));
		return(leaf1);
	case EX_AND2:
	assert(leaf1.e_typ == EV_INT);
		if (leaf1.e_v.e_con!=0)
			return(compute(&enodes[node->ex_rnode]));
		return(leaf1);
	case EX_PLUS:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con=leaf1.e_v.e_con+leaf2.e_v.e_con;
		return(result);
	case EX_CAT:
	assert(leaf1.e_typ == EV_ADDR && leaf2.e_typ == EV_ADDR);
		result.e_typ = EV_ADDR;
		result.e_v.e_addr.ea_str = mycat(leaf1.e_v.e_addr.ea_str,leaf2.e_v.e_addr.ea_str);
		result.e_v.e_addr.ea_off = leaf1.e_v.e_addr.ea_off+leaf2.e_v.e_addr.ea_off;
		return(result);
	case EX_MINUS:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con - leaf2.e_v.e_con;
		return(result);
	case EX_TIMES:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con * leaf2.e_v.e_con;
		return(result);
	case EX_DIVIDE:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con / leaf2.e_v.e_con;
		return(result);
	case EX_MOD:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con % leaf2.e_v.e_con;
		return(result);
	case EX_LSHIFT:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con << leaf2.e_v.e_con;
		return(result);
	case EX_RSHIFT:
	assert(leaf1.e_typ == EV_INT && leaf2.e_typ == EV_INT);
		result.e_v.e_con = leaf1.e_v.e_con >> leaf2.e_v.e_con;
		return(result);
	case EX_NOT:
	assert(leaf1.e_typ == EV_INT);
		result.e_v.e_con = !leaf1.e_v.e_con;
		return(result);
	case EX_COMP:
	assert(leaf1.e_typ == EV_INT);
		result.e_v.e_con = ~leaf1.e_v.e_con;
		return(result);
	case EX_STRING:
		result.e_typ = EV_ADDR;
		result.e_v.e_addr.ea_str = codestrings[node->ex_lnode];
		result.e_v.e_addr.ea_off = 0;
		return(result);
	case EX_DEFINED:
		result.e_v.e_con=leaf1.e_typ!=EV_UNDEF;
		return(result);
	case EX_SUBREG:
		result.e_typ = EV_REG;
		tp= &fakestack[stackheight-node->ex_lnode];
		assert(tp->t_token == -1);
		tmpreg= tp->t_att[0].ar;
#if MAXMEMBERS!=0
		if (node->ex_rnode)
			tmpreg=machregs[tmpreg].r_members[node->ex_rnode-1];
#endif
		result.e_v.e_reg=tmpreg;
		return(result);
	case EX_TOSTRING:
	assert(leaf1.e_typ == EV_INT);
		result.e_typ = EV_ADDR;
		result.e_v.e_addr.ea_str = "";
		result.e_v.e_addr.ea_off = leaf1.e_v.e_con;
		return(result);
#ifdef REGVARS
	case EX_INREG:
	assert(leaf1.e_typ == EV_INT);
		result.e_v.e_con = isregtyp((long) leaf1.e_v.e_con);
		return(result);
	case EX_REGVAR:
	assert(leaf1.e_typ == EV_INT);
		i = isregvar((long) leaf1.e_v.e_con);
		if (i<=0) 
			return(undefres);
		result.e_typ = EV_REG;
		result.e_v.e_reg=i;
		return(result);
#endif
	case EX_UMINUS:
	assert(leaf1.e_typ == EV_INT);
		result.e_v.e_con = -leaf1.e_v.e_con;
		return(result);
	}
}