/*                                             
 * 68K/386 32-bit C compiler.
 *
 * copyright (c) 1997, David Lindauer
 * 
 * This compiler is intended for educational use.  It may not be used
 * for profit without the express written consent of the author.
 *
 * It may be freely redistributed, as long as this notice remains intact
 * and either the original sources or derived sources 
 * are distributed along with any executables derived from the originals.
 *
 * The author is not responsible for any damages that may arise from use
 * of this software, either idirect or consequential.
 *
 * V1.85 July 2000
 * David Lindauer, camille@bluegrass.net
 *
 * Credits to Mathew Brandt for original K&R C compiler
 *
 */
#include        <stdio.h>
#include				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include        "gen386.h"
#include 				"diag.h"
extern OCODE *peep_tail, *frame_ins;
extern SYM *currentfunc;
extern int retlab;

#define ABS(x) ( (x) < 0 ? -(x) : (x))
/*
 *      this module contains all of the code generation routines
 *      for evaluating expressions and conditions.
 */
extern int stdinttype,stdunstype,stdintsize, stdldoublesize;
extern int stdaddrsize;
extern int stackadd,stackmod;
extern int prm_largedata;
extern AMODE     push[], pop[];
extern int prm_68020;
extern int regs[3];
extern long nextlabel;
extern long lc_maxauto;
extern int prm_cmangle;
extern int prm_cplusplus;

extern int floatstack_mode;
AMODE freg0[] = { { am_freg, 0 } };
AMODE sreg[] = { { am_dreg,4 } };

extern long bittab[32];

/* little ditty to allocate stack space for floating point
 * conversions and return a pointer to it
 * it will be at the bottom for standard stack frames; top for 
 * using ESP for frames
 */
AMODE *floatconvpos(void)
{
	AMODE *ap1;
	int constv = 4;
	if (prm_cplusplus)
		constv = 12;
	if (!floatstack_mode) {
		if (!lc_maxauto) {
        OCODE * new = xalloc(sizeof(OCODE));
        new->opcode = op_sub;
        new->oper1 = makedreg(ESP);
        new->oper2 = make_immed(constv);
				new->back = frame_ins;
				new->fwd = frame_ins->fwd;
				frame_ins->fwd->back = new;
				frame_ins->fwd = new;
		}
		else {
			frame_ins->oper2->offset->v.i += constv;
		}
		floatstack_mode += constv;
	}
  ap1 = xalloc(sizeof(AMODE));
  ap1->mode = am_indisp;
	ap1->preg = EBP;
  ap1->offset = makenode(en_icon, (char *)-lc_maxauto-constv, 0);
	ap1->length = 4;
  return ap1;
}
int chksize(int lsize, int rsize)
{
	int l,r;
	l = lsize;
	r = rsize;
	if (l < 0) l = - l;
	if (r < 0) r = - r;
  if (rsize == 5) /* 5 is used for bools, which are the smallest type now */
    if (lsize == 5)
      return FALSE;
    else
      return TRUE;
  if (lsize == 5)
    return FALSE;
	return(l > r);
}
AMODE *fstack(void)
{
	AMODE *ap = xalloc(sizeof(AMODE));
	ap->mode = am_freg;
	ap->length = 10;
	ap->preg = 0;
	ap->sreg = 0;
	ap->offset = 0;
	ap->tempflag = TRUE;
	return(ap);
}
AMODE		*make_muldivval(AMODE *ap)
{
			int temp;
			AMODE *ap1 = make_label(queue_muldivval(ap->offset->v.i));
			ap1->mode = am_direct;
			return(ap1);
}
void make_floatconst(AMODE *ap)
{
	int size = ap->length;
	if (isintconst(ap->offset->nodetype)) {
		ap->offset->v.f = ap->offset->v.i;
		ap->offset->nodetype = en_rcon;
		size = 10;
	}
	if (ap->offset->v.f == 1.0) {
		ap->mode = am_fconst;
		ap->preg = fcone;
	}
	else if (ap->offset->v.f == 0.0) {
		ap->mode = am_fconst;
		ap->preg = fczero;
	}
	else {
		AMODE *ap1 = make_label(queue_floatval(ap->offset->v.f,size));
		ap->mode = am_direct;
		ap->length = size;
		ap->offset = ap1->offset;
	}
}
AMODE    *make_label(int lab)
/*
 *      construct a reference node for an internal label number.
 */
{       ENODE    *lnode;
        AMODE    *ap;
        lnode = xalloc(sizeof(ENODE));
        lnode->nodetype = en_labcon;
        lnode->v.i = lab;
        ap = xalloc(sizeof(AMODE));
        ap->mode = am_immed;
        ap->offset = lnode;
        return ap;
}

AMODE *makesegreg(int seg)
{
		AMODE *ap = xalloc(sizeof(AMODE));
		ap->mode = am_seg ;
		ap->seg = seg ;
		return ap;
}
AMODE    *make_immed(long i)
/*
 *      make a node to reference an immediate value i.
 */
{       AMODE    *ap;
        ENODE    *ep;
        ep = xalloc(sizeof(ENODE));
        ep->nodetype = en_icon;
        ep->v.i = i;
        ap = xalloc(sizeof(AMODE));
        ap->mode = am_immed;
        ap->offset = ep;
        return ap;
}
AMODE    *make_immedt(long i, int size)
/*
 *      make a node to reference an immediate value i.
 */
{
				switch (size) {
					case 1:
					case -1:
						i &= 0xff;
						break;
					case 2:
					case -2:
						i &= 0xffff;
						break;
				}
				return make_immed(i);
}

AMODE    *make_offset(ENODE *node)
/*
 *      make a direct reference to a node.
 */
{       AMODE    *ap;
        ap = xalloc(sizeof(AMODE));
        ap->mode = am_direct;
        ap->offset = node;
        return ap;
}
AMODE *make_stack(int number)
{
	AMODE *ap = xalloc(sizeof(AMODE));        
	ENODE *ep = xalloc(sizeof(ENODE));
	ep->nodetype = en_icon;
	ep->v.i = -number;
	ap->mode = am_indisp;
	ap->preg = ESP;
	ap->offset = ep;
	return(ap);
}
AMODE * floatstore(ENODE *node, int novalue)
{
	AMODE *apr;
	int size = natural_size(node);
	apr = gen_expr(node,FALSE,TRUE,size);
	if (isbit(node)) {
		AMODE *ap1 = fstack();
		do_extend(ap1,4,F_DREG | F_VOL);
		bit_store(apr,ap1,node,novalue);
		if (novalue)
			freeop(ap1);
		return ap1;
	}
	else if ( size <= 4) {
		int pushed = FALSE;
		if (regs[0] && apr->preg != 0) {
			pushed = 1;
			gen_push(EAX,am_dreg,0);
		}
		call_library("__ftol");
		if (apr->mode != am_dreg || apr->preg != 0)
			gen_codes(op_mov,size,apr,makedreg(EAX));
		if (pushed)
			gen_pop(EAX,am_dreg,0);
		return apr;
	}
	else if (size <= 4) {
		gen_codef(op_fistp,apr,0);
		return apr;
	}
	else {
		if (novalue)
			gen_codef(op_fstp,apr,0);
		else
			gen_codef(op_fst,apr,0);
		return apr;
	}
}
void floatload(AMODE *ap, int flag)
{
	if (ap->mode == am_fconst) {
						switch (ap->preg) {
							case fczero:
								gen_codef(op_fldz,0,0);
								break;
							case fcone:
								gen_codef(op_fld1,0,0);
								break;
						}
						ap->mode = am_freg;
						ap->preg = 0;
	}
	else
		if (flag && ap->mode != am_freg) {
			if (ap->mode == am_dreg) {
				AMODE *ap1 = floatconvpos();
				do_extend(ap,4,0);
				gen_codes(op_mov,4,ap1,ap);
				gen_codefs(op_fild,4,ap1,0);
			} else
				gen_codef(op_fld,ap,0);
			freeop(ap);
						ap->mode = am_freg;
						ap->preg = 0;
		}
}
int is_memory(AMODE *ap)
{
	switch(ap->mode) {
												case am_indisp: case am_indispscale: 
												case am_direct:
		return TRUE;
	}
	return FALSE;
}
AMODE *bit_load(AMODE *ap,ENODE *node)
{
	do_extend(ap,ap->length,F_DREG | F_VOL);
		if (node->startbit)
			gen_code(op_shr,ap,make_immed(node->startbit));
		gen_code(op_and,ap,make_immed(bittab[node->bits-1]));
		return ap;
}
void bit_store(AMODE *ap2, AMODE *ap1, ENODE *node, int novalue)
{
		AMODE *ap3;
		if (ap1->mode == am_immed) {
			int vb;
			ap3 = make_immed(ap1->offset->v.i & bittab[node->bits-1]);
			if (node->bits == 1)
				gen_codes(op_btr,4,ap2,make_immed(node->startbit));
			else
				gen_code(op_and,ap2,make_immed(~(bittab[node->bits-1]<<node->startbit)));
			if ((vb = single_bit(ap1->offset->v.i)) != -1) {
				gen_codes(op_bts,4,ap2,make_immed(vb+node->startbit));
			}else if (ap3->offset->v.i) {
				ap3->offset->v.i <<= node->startbit;
				gen_codes(op_or,ap2->length,ap2,ap3);
			}
		}
		else {
			do_extend(ap1,ap2->length,F_DREG | F_VOL);
			gen_code(op_and,ap1,make_immed(bittab[node->bits-1]));
			if (!novalue)
				gen_codes(op_push,4,ap1,0);
			if (node->startbit)
				gen_code(op_shl,ap1,make_immed(node->startbit));
			gen_code(op_and,ap2,make_immed(~(bittab[node->bits-1]<<node->startbit)));
			gen_code(op_or,ap2,ap1);
			if (!novalue)
				gen_codes(op_pop,4,ap1,0);
		}
}
void do_extend(AMODE *ap,int osize,int flags)
/*
 *      if isize is not equal to osize then the operand ap will be
 *      loaded into a register (if not already) and if osize is
 *      greater than isize it will be extended to match.
 */
{				AMODE *ap2,*ap1;

				int isize = ap->length;
				if (isize && isize != osize && isize != -osize && ap->mode !=am_immed) {
						if (ap->mode == am_dreg && (!(flags & F_VOL) || ap->tempflag))
							ap2 = ap;
						else {
							freeop(ap);
							ap2 = temp_data();
						}
						if (chksize(isize,osize)) {
							/* moving to a lower type */
							if (isize > 5) {
								if (ap->mode != am_freg)
									floatload(ap,TRUE);
                if (osize <= 5) {
									int pushed = FALSE;
									if (regs[0] && ap2->preg != 0) {
										pushed = 1;
										gen_push(EAX,am_dreg,0);
									}
									call_library("__ftol");
									if (ap2->preg != 0)
										gen_codes(op_mov,4,ap2,makedreg(EAX));
									if (pushed)
										gen_pop(EAX,am_dreg,0);
									ap->mode = ap2->mode;
									ap->preg = ap2->preg;
									ap->tempflag = ap2->tempflag;
								} 	
								else {
									freeop(ap2);
									ap->mode = am_freg;
									ap->preg = 0;
									ap->tempflag = TRUE;
								}
							} 	
							else {
								if (ap->mode != am_dreg || ap->preg != ap2->preg)
									gen_codes(op_mov,isize,ap2,ap);
								ap->mode = ap2->mode;
								ap->preg = ap2->preg;
								ap->tempflag = ap2->tempflag;
							} 	
						}
						else {
							/* moving up in type */
              if (isize > 5) {
								if (ap->mode != am_freg)
									floatload(ap,TRUE);
								freeop(ap2);
								ap->mode = am_freg;
								ap->preg = 0;
								ap->tempflag = TRUE;
							}
							else if (osize > 5) {
								if (isize != 4 && isize != -4) {
									ap1 = floatconvpos();
							  	if (isize < 0)
				 				  	gen_code2(op_movsx,4,isize,ap2,ap);
						  		else
						 	  		gen_code2(op_movzx,4,isize,ap2,ap);
									gen_codes(op_mov,4,ap1,ap2);
									gen_codefs(op_fild,4,ap1,0);
								}
								else
									if (ap->mode == am_direct || ap->mode == am_indisp || ap->mode == am_indispscale)
										gen_codefs(op_fild,4,ap,0);
									else {
										ap1 = floatconvpos();
										gen_codes(op_mov,4,ap1,ap);
										gen_codefs(op_fild,4,ap1,0);
									}
								freeop(ap2);
								ap->mode = am_freg;
								ap->preg = 0;
								ap->tempflag = TRUE;
							}
							else {
								if (isize < 0)
							 	  gen_code2(op_movsx,osize,isize,ap2,ap);
							  else
							 	  gen_code2(op_movzx,osize,isize,ap2,ap);
								ap->mode = am_dreg;
								ap->preg = ap2->preg;
								ap->tempflag = ap2->tempflag;
							}
						}
				}
				if (flags & (F_FREG | F_DREG)) {
					if (flags & F_DREG) {
						if (ap->mode != am_dreg || !ap->tempflag && (flags & F_VOL))
							if (isize == osize || isize == -osize || ap->mode == am_immed) {
								freeop(ap);
								ap2 = temp_data();
								ap2->length = osize;
								gen_code(op_mov,ap2,ap);
								ap->mode = ap2->mode;
								ap->preg = ap2->preg;
								ap->tempflag = ap2->tempflag;
							}
					}
					else if (ap->mode != am_freg) {
						if (ap->mode == am_immed)
							make_floatconst(ap);
						floatload(ap,TRUE);
					}
				}		
        if (osize != isize && osize == 5) {
          int lab = nextlabel++;
					ap2 = temp_data();
					gen_code(op_xor,ap2,ap2);
          gen_code(op_cmp,ap,make_immed(0));
          gen_code(op_jz,make_label(lab),0);
          gen_codes(op_mov,1,ap2,make_immed(1));
          gen_label(lab);
					freeop(ap);
					ap->mode = ap->mode;
					ap->tempflag = ap2->tempflag;
					ap->length = ap2->length;
					ap->preg = ap2->preg;
        }     
        ap->length = osize;
				if (osize == 5)
					ap->length = 1;
}

int     isshort(ENODE *node)
/*
 *      return true if the node passed can be generated as a short
 *      offset.
 */
{       return (isintconst(node->nodetype) &&
                (node->v.i >= -32768L && node->v.i <= 32767L));
}

int     isbyte(ENODE *node)
/*
 *      return true if the node passed can be evaluated as a byte
 *      offset.
 */
{       return isintconst(node->nodetype) &&
                (-128 <= node->v.i && node->v.i <= 127);
}

static int depth(ENODE *node)
{
				int a,b;
				if (!node)
					return 0;
        switch( node->nodetype )
                {
								case en_cl:
								case en_cul:
								case en_cp:
                case en_cbool:
								case en_cub:
								case en_cb:
								case en_cuw:
                case en_cw:
								case en_cd:
								case en_cld:
								case en_cf:
								case en_bits:
								case en_l_ref:
                case en_ul_ref:
                case en_ub_ref:
                case en_bool_ref:
                case en_b_ref:
                case en_uw_ref:
                case en_w_ref:
								case en_longdoubleref:
								case en_doubleref:
								case en_floatref:
												return 1 + depth(node->v.p[0]);
                case en_uminus: 
                case en_ainc:   case en_adec: 
								case en_moveblock: case en_stackblock:
												return 1+depth(node->v.p[0]);
                case en_icon:
								case en_lcon: 
								case en_lucon: 
								case en_iucon: 
								case en_ccon:                 
								case en_rcon:
								case en_lrcon:
								case en_fcon:
								case en_absacon:
								case en_trapcall:
                case en_labcon:
                case en_nacon:  case en_autocon:  case en_autoreg:
                case en_napccon: case en_nalabcon:
								case en_tempref:
								case en_regref:
												return 1;
                case en_not:    case en_compl:
												return 1+depth(node->v.p[0]);
                case en_eq:     case en_ne:
                case en_lt:     case en_le:
                case en_gt:     case en_ge:
								case en_ugt: case en_uge: case en_ult: case en_ule:
                case en_land:   case en_lor:
								case en_div: case en_udiv: case en_pdiv: case en_mod: case en_umod:
								case en_assign: case en_refassign:
								case en_asuminus:
                case en_add:    case en_sub: case en_addstruc:
								case en_umul:		case en_pmul:
                case en_mul:		case en_and:
                case en_or:     case en_xor:
								case en_asalsh: case en_asarsh: case en_alsh: case en_arsh:
                case en_lsh:    case en_rsh:
                case en_asadd:  case en_assub:
                case en_asmul:  case en_asdiv:
                case en_asmod:  case en_asand:
								case en_asumod: case en_asudiv: case en_asumul:
                case en_asor:   case en_aslsh:  case en_asxor:
                case en_asrsh:
												a = depth(node->v.p[0]) ;
												b = depth(node->v.p[1]);
												if (a > b)
													return 1 + a;
												return 1 + b;
                case en_void:   case en_cond:
												return 1 + depth(node->v.p[1]);
								case en_sfcall: case en_sfcallb: case en_scallblock:
								case en_pfcall: case en_pfcallb: 
								case en_fcall: case en_intcall: case en_callblock: case en_fcallb:
								case en_pcallblock: case en_thiscall:
												return 1;
                default:
                        DIAG("error in depth routine.");
												return 1;
                }
}
	
void noids(AMODE *ap)
{
	if (ap->mode == am_indispscale)
		do_extend(ap,ap->length,F_DREG | F_VOL);
}
AMODE *indx_data(AMODE *ap)
{
	AMODE *ap3;
	freeop(ap);
	ap3 = temp_data();
		gen_codes(op_lea,4,ap3,ap);
		ap3->mode = am_indisp;
		ap3->offset = makenode(en_icon,0,0);
		return ap3;
}
AMODE *doindex(ENODE *node, enum e_node type)
{
				AMODE *ap, *ap3;
				ENODE node2;
				int scale;
				switch (node->nodetype) {
					case en_icon:
						ap = gen_expr(node,FALSE,TRUE,4);
						break;
					case en_lsh:
						if ((scale = node->v.p[1]->v.i) < 4 && scale) {
							ap = gen_expr(node->v.p[0],FALSE,TRUE,4);
							if (ap->mode != am_immed)
								do_extend(ap,4,F_DREG);
							if (ap->mode == am_immed) {
								while (--scale)
									ap->offset->v.i <<=1;
							}
							else {
								do_extend(ap,4,0);
								ap->mode = am_indispscale;
								ap->sreg = ap->preg;
								ap->preg = -1;
								ap->scale = scale;
								ap->offset = makenode(en_icon,0,0);
							}
							break;
						}
					default:
						node2.v.p[0] = node;
						node2.nodetype = type;
						ap = gen_deref(&node2,4);
						switch (ap->mode) {
							default:
								break;
							case am_indispscale:
								if (ap->sreg >=0 && ap->preg >= 0) {
									ap = indx_data(ap);
								}
						}
						
						break;
				}
				return ap;
}
AMODE    *gen_index(ENODE *node)
/*
 *      generate code to evaluate an index node (^+) and return
 *      the addressing mode of the result. This routine takes no
 *      flags since it always returns either am_ind or am_indx.
 */
{       AMODE    *ap1,*ap2, *ap, *ap3;
				ENODE node2;

				int a = depth(node->v.p[1]) - depth(node->v.p[0]);
				if (a <= 2 ) {
					ap1 = doindex(node->v.p[0],node->nodetype);
					ap2 = doindex(node->v.p[1],node->nodetype);
				}
				else {
					ap2 = doindex(node->v.p[1],node->nodetype);
					ap1 = doindex(node->v.p[0],node->nodetype);
				}
				switch(ap1->mode) {
					case am_dreg:
						switch (ap2->mode) {
							case am_dreg:
								do_extend(ap1,4,0);
								ap1->sreg = ap2->preg;
								ap1->scale = 0;
								ap1->mode = am_indispscale;
								ap1->offset = makenode(en_icon,(char *)0,0);
								return ap1;
							case am_immed:
							case am_direct:
								ap2->preg = ap1->preg;
								ap2->mode = am_indisp;
								return ap2;	
							case am_indisp:
								ap2->sreg = ap2->preg;
								ap2->preg = ap1->preg;
								ap2->mode = am_indispscale;
								ap2->offset = ap1->offset;
								ap2->scale = 0;
								return ap2;
							case am_indispscale:
								if (ap2->preg == -1) {
									ap2->preg = ap1->preg;
									return ap2;
								}
								ap = indx_data(ap2);
								ap->sreg = ap1->preg;
								ap->mode = am_indispscale;
								ap->scale = 0;
								ap->offset = makenode(en_icon,0,0);
								return ap;
						}
						break;
					case am_direct:
					case am_immed:
						switch (ap2->mode) {
							case am_dreg:
								ap2->mode = am_indisp;
								ap2->offset = ap1->offset;
								return ap2;
							case am_immed:
							case am_direct:
								if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
									ap1->offset->v.i += ap2->offset->v.i;
								else
									ap1->offset = makenode(en_add,ap1->offset,ap2->offset);
							  ap1->mode = am_direct;
								return ap1;
							case am_indisp:
							case am_indispscale:
								if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
									ap2->offset->v.i += ap1->offset->v.i;
								else
									ap2->offset = makenode(en_add,ap1->offset,ap2->offset);
								return ap2;
						}
						break;
					case am_indisp:
						switch (ap2->mode) {
							case am_dreg:
								ap1->mode = am_indispscale;
								ap1->sreg = ap2->preg;
								ap1->scale = 0;
								return ap1;
							case am_immed:
							case am_direct:
								if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
									ap1->offset->v.i += ap2->offset->v.i;
								else
									ap1->offset = makenode(en_add,ap1->offset,ap2->offset);
								return ap1;	
							case am_indisp:
								ap1->mode = am_indispscale;
								ap1->sreg = ap2->preg;
								ap1->scale = 0;
								if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
									ap1->offset->v.i += ap2->offset->v.i;
								else
									ap1->offset = makenode(en_add,ap1->offset,ap2->offset);
								return ap1;
							case am_indispscale:
								if (ap2->preg == -1) {
									ap2->preg = ap1->preg;
									if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
										ap2->offset->v.i += ap1->offset->v.i;
									else
										ap2->offset = makenode(en_add,ap1->offset,ap2->offset);
									return ap2;
								}
								ap = indx_data(ap2);
								ap1->sreg = ap->preg;
								ap1->scale = 0;
								ap1->mode = am_indispscale;
								if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
									ap1->offset->v.i += ap2->offset->v.i;
								else
									ap1->offset = makenode(en_add,ap1->offset,ap2->offset);
								return ap1;
						}
						break;
					case am_indispscale:
						switch (ap2->mode) {
							case am_dreg:
								if (ap1->preg == -1) {
									ap1->preg = ap2->preg;
									return ap1;
								}
								ap = indx_data(ap1);
								ap->sreg = ap2->preg;
								ap->scale = 0;
								ap->offset = ap1->offset;
								return ap;
							case am_immed:
							case am_direct:
								if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
									ap1->offset->v.i += ap2->offset->v.i;
								else
									ap1->offset = makenode(en_add,ap1->offset,ap2->offset);
								return ap1;
							case am_indisp:
								if (ap1->preg == -1) {
									ap1->preg = ap2->preg;
									if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
										ap1->offset->v.i += ap2->offset->v.i;
									else
										ap1->offset = makenode(en_add,ap1->offset,ap2->offset);
									return ap1;
								}
								ap = indx_data(ap1);
								ap->sreg = ap2->preg;
								ap->scale = 0;
								ap->mode = am_indispscale;
								if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
									ap->offset->v.i = ap2->offset->v.i +ap1->offset->v.i;
								else
									ap->offset = makenode(en_add,ap1->offset,ap2->offset);
								return ap;
							case am_indispscale:
								if (ap1->preg == -1 && ap2->preg == -1) {
									if (ap1->scale == 0) {
										ap2->preg = ap1->sreg;
										if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
											ap2->offset->v.i += ap1->offset->v.i;
										else
											ap2->offset = makenode(en_add,ap1->offset,ap2->offset);
										return ap2;
									} else if (ap2->scale == 0) {
										ap1->preg = ap2->sreg;
										if (ap1->offset->nodetype == en_icon && ap2->offset->nodetype == en_icon)
											ap1->offset->v.i += ap2->offset->v.i;
										else
											ap1->offset = makenode(en_add,ap1->offset,ap2->offset);
										return ap1;
									}
								}
								if (ap1->preg == -1) {
									ap = indx_data(ap2);
									ap1->preg = ap->preg;
									return ap1;
								}
								else if (ap2->preg == -1) {
									ap = indx_data(ap1);
									ap2->preg = ap->preg;
									return ap2;
								}
								ap = indx_data(ap1);
								ap1 = indx_data(ap2);
								ap->mode = am_indispscale;
								ap->sreg = ap1->preg;
								ap->scale = 0;
								ap->offset = makenode(en_icon,0,0);
								return ap;
						}
						break;           
				}
				DIAG("invalid index conversion");
				return 0;
}

AMODE    *gen_deref(ENODE *node, int size)
/*
 *      return the addressing mode of a dereferenced node.
 */
{       AMODE    *ap1;
        int             siz1;
        switch( node->nodetype )        /* get load size */
                {
                case en_bool_ref:
                case en_ub_ref:
                        siz1 = 1;
                        break;
                case en_b_ref:
                        siz1 = -1;
                        break;
                case en_uw_ref:
                        siz1 = 2;
                        break;
                case en_w_ref:
                        siz1 = -2;
                        break;
                case en_l_ref:
												siz1 = -4;
												break;
								case en_ul_ref:
                        siz1 = 4;
                        break;
								case en_floatref:
												siz1 = 6;
												break;
								case en_doubleref:
												siz1 = 8;
												break;
								case en_longdoubleref:
												siz1 = 10;
												break;
								default:
												siz1 = 4;
                }
        if( node->v.p[0]->nodetype == en_add || node->v.p[0]->nodetype == en_addstruc)
                {
                	ap1 = gen_index(node->v.p[0]); 
									ap1->length = siz1;
                	return ap1;
								}
				else if( node->v.p[0]->nodetype == en_autocon  || node->v.p[0]->nodetype == en_autoreg)
                {
								SYM *sp = node->v.p[0]->v.sp;
								int i = sp->value.i;
                ap1 = xalloc(sizeof(AMODE));
                ap1->mode = am_indisp;
			          ap1->preg = EBP;
#ifdef CPLUSPLUS
								if ((currentfunc->value.classdata.cppflags & PF_MEMBER) &&
											!(currentfunc->value.classdata.cppflags & PF_STATIC))
									i += 4;
#endif
      			    ap1->offset = makenode(en_icon,(char *)i,0);
								ap1->length = siz1;
                return ap1;
                }
        else if( node->v.p[0]->nodetype == en_nacon || node->v.p[0]->nodetype == en_napccon)
                {
                ap1 = xalloc(sizeof(AMODE));
                ap1->offset = makenode(node->v.p[0]->nodetype,(void *)node->v.p[0]->v.sp,0);
                ap1->mode = am_direct;
								ap1->length = siz1;
                return ap1;
                }
        else if( node->v.p[0]->nodetype == en_nalabcon)
                {
                ap1 = xalloc(sizeof(AMODE));
                ap1->offset = makenode(node->v.p[0]->nodetype,(char *)node->v.p[0]->v.i,0);
                ap1->mode = am_direct;
								ap1->length = siz1;
                return ap1;
                }
        else if( node->v.p[0]->nodetype == en_labcon)
                {
                ap1 = xalloc(sizeof(AMODE));
                ap1->mode = am_direct;
                ap1->offset = makenode(node->v.p[0]->nodetype,(char *)node->v.p[0]->v.i,0);
								ap1->length = siz1;
                return ap1;
                }
				else if (node->v.p[0]->nodetype == en_absacon) {
								ap1 = xalloc(sizeof(AMODE));
								ap1->mode = am_direct;
								ap1->offset = makenode(en_absacon,(char *)node->v.p[0]->v.sp->value.i,0);
								ap1->length = siz1;
                return ap1;

				}
				else if (node->v.p[0]->nodetype == en_regref) {
        	ap1 = gen_expr(node->v.p[0],FALSE,TRUE,siz1);
					return ap1;
				}
        ap1 = gen_expr(node->v.p[0],FALSE,TRUE,siz1); /* generate address */
				if (ap1->mode != am_immed) {
					do_extend(ap1,4,F_DREG);
				}
				ap1->length = siz1;
        if( ap1->mode == am_dreg )
                {
                ap1->mode = am_indisp;
								ap1->offset = makenode(en_icon,0,0);
                return ap1;
                }
				else if (ap1->mode == am_stackedtemp) {
                ap1->mode = am_stackedtempaddr;
                return ap1;
				}
        ap1->mode = am_direct;
        return ap1;
}

void get_size(AMODE *ap1, AMODE *ap2,int size)
{
	if (size > 5)
		size = 4;
	if (size) {
		if (ap1->mode == am_immed && ap1->length <= 4)
			ap1->length = size;
		if (ap2->mode == am_immed && ap2->length <= 4)
			ap2->length = size;
	}
	if (chksize(ap2->length,ap1->length)) {
		do_extend(ap1,ap2->length,0);
	}
	else {
		do_extend(ap2,ap1->length,0);
	}
	
}
void small_size(AMODE *ap1, AMODE *ap2, int size)
{
	
	if (ap1->length <= 4 && ap2->length <= 4 && chksize(ap2->length, ap1->length))
		ap2->length = ap1->length;
	else
		get_size(ap1,ap2,size);
}
void resolve_binary(ENODE *node,AMODE **ap1, AMODE **ap2, int size)
{
  	*ap2 = gen_expr(node->v.p[1],FALSE,FALSE, size);
		noids(*ap2);
  	*ap1 = gen_expr(node->v.p[0],FALSE,FALSE, size);
		get_size(*ap1,*ap2,size);
}
static int isbit(ENODE *node)
{
	return node->nodetype == en_bits;
}
static int as_args(ENODE *node,AMODE **apr,AMODE **ap1,AMODE **ap2, int size)
{
	int rv = isbit(node->v.p[0]);
	*ap2 = gen_expr(node->v.p[1],FALSE,FALSE,size);
	noids(ap2);
	*apr = gen_expr(node->v.p[0],FALSE,TRUE,size);
	if (rv)
		*ap1 = gen_expr(node->v.p[0],FALSE,FALSE,size);
	else
		*ap1 = copy_addr(*apr);
	return rv;
}
static int prefer(ENODE *node,AMODE **ap2, AMODE **ap3, int op, int dosizing, int size)
{                   
	AMODE *ap2x, *ap3x;
	int rv = 0;
	int a = depth(node->v.p[1]) - depth(node->v.p[0]);
	if (a <= 2 ) {
  	ap2x = gen_expr(node->v.p[0],FALSE,FALSE,size);
		noids(ap2x);
  	ap3x = gen_expr(node->v.p[1],FALSE,FALSE,size);
		if (dosizing)
			get_size(ap2x,ap3x,size);
	}
	else {
  	ap3x = gen_expr(node->v.p[1],FALSE,FALSE,size);
		noids(ap3x);
  	ap2x = gen_expr(node->v.p[0],FALSE,FALSE,size);
		if (dosizing)
			get_size(ap2x,ap3x,size);
	}
	*ap3 = ap3x;
	*ap2 = ap2x;
	if (ap2x->mode != am_dreg || !ap2x->tempflag) {
		if (op != op_sub && op != op_div) {
		  if (ap3x->mode == am_dreg && ap3x->tempflag) {
				*ap3 = ap2x;
				*ap2 = ap3x;
				rv = !rv;
			}
			else
				if (ap2x->mode == am_immed) {
					*ap3 = ap2x;
					*ap2 = ap3x;
					rv = !rv;
			}
		}
	}		
	return rv;
}
AMODE    *gen_unary(ENODE *node,int op, int fop, int size)
/*
 *      generate code to evaluate a unary minus or complement.
 */
{       AMODE    *ap;
				int xchg;
        ap = gen_expr(node->v.p[0],FALSE,FALSE,size);
				if (natural_size(node) > 5) {
					do_extend(ap,10,F_FREG | F_VOL);
					gen_code(fop,0,0);
					ap = fstack();
				}
				else {
				  do_extend(ap,ap->length,F_DREG | F_VOL);
          gen_code(op,ap,0);
				}
        return ap;
}
AMODE *gen_fbinary(ENODE *node,int fop, int fopp, int fopr,int foppr,int size)
{
		AMODE *ap1,*ap2;
		if (fop == op_fdiv || fop == op_fsub) {
  		ap1 = gen_expr(node->v.p[0],FALSE,FALSE,size);
		  do_extend(ap1,10,F_FREG);
  		ap2 = gen_expr(node->v.p[1],FALSE,FALSE,size);
		}
		else {
			prefer(node,&ap1,&ap2,fop,FALSE,size);
		}
		if (ap1->mode == am_immed)
			make_floatconst(ap1);
		if (ap2->mode == am_immed)
			make_floatconst(ap2);
		floatload(ap1,FALSE);
    floatload(ap2,FALSE);
		if (ap1->mode != am_freg && ap2->mode != am_freg)
			do_extend(ap1,10,F_FREG | F_VOL);
		if (ap1->mode == am_freg) {
			if (ap2->mode == am_freg) {
				gen_codef(fopp,makefreg(1),0);             	
			}
			else if (ap2->length == 10) {
				do_extend(ap2,10,F_FREG);
				gen_codef(fopp,makefreg(1),0);             	
			}
			else if (ap2->mode == am_dreg) {
				do_extend(ap2,10,F_FREG);
				gen_codef(fopp,makefreg(1),0);             	
			}
			else if (ap2->length < 6) {
				do_extend(ap2,10,F_FREG);
				gen_codef(fopp,makefreg(1),0);             	
			}
			else
				gen_code(fop,ap2,0);
		} else if (ap2->mode == am_freg) {
			if (ap1->length == 10) {
				do_extend(ap1,10,F_FREG);
				gen_codef(foppr,makefreg(1),0);             	
			}
			else if (ap1->mode == am_dreg) {
				do_extend(ap1,10,F_FREG);
				gen_codef(foppr,makefreg(1),0);             	
			}
			else if (ap1->length < 6) {
				do_extend(ap1,10,F_FREG);
				gen_codef(fopp,makefreg(1),0);             	
			}
			else
				gen_code(fopr,ap1,0);
		} 
		ap1 = fstack();
		return ap1;
}

AMODE    *gen_xbin(ENODE *node,int op,int op2, int size)
/*
 *      generate code to evaluate a restricted binary node and return 
 *      the addressing mode of the result.
 *		  restriction is: integers only.  We also use this for add,sub,mul
 */
{       AMODE    *ap1, *ap2, *ap3;
				prefer(node,&ap1,&ap2,op,TRUE,size);
				if (op == op_sub) {
					do_extend(ap1,ap1->length,F_DREG | F_VOL);
				}
				else {
					if ((ap1->mode != am_dreg) || !ap1->tempflag) {
						if (ap2->mode == am_dreg && ap2->tempflag) {
							ap3 = ap1;
							ap1 = ap2;
							ap2 = ap3;
						}
						else {
							int vb;
							do_extend(ap1,ap1->length,F_DREG | F_VOL);
							if (ap2->mode == am_immed) {
								if (op == op_and && (vb = single_bit(~ap2->offset->v.i)) != -1) {
									gen_codes(op2,4,ap1,make_immed(vb));                        	
									return ap1;
								}
								else if ((op == op_or || op == op_xor ) && (vb = single_bit(ap2->offset->v.i))!= -1) {
									gen_codes(op2,4,ap1,make_immed(vb));
									return ap1;
								}
							}
						}
					}
				}
     		gen_code(op,ap1,ap2);
				freeop(ap2);
				return(ap1);
}
AMODE    *gen_binary(ENODE *node,int op, int fopp, int fop, int foppr, int fopr, int size)
/*
 *      generate code to evaluate a binary node and return 
 *      the addressing mode of the result.
 */
{
				if (natural_size(node) > 5)
					return gen_fbinary(node,fop,fopp,fopr,foppr, size);
				else 
					return gen_xbin(node,op,0,size);
}
void doshift(AMODE *ap1, AMODE *ap2, int op)
{       AMODE   *ecx = makedreg(ECX);
				freeop(ap2);
				if (ap2->mode == am_immed) {
					gen_codes2(op,1,ap1,ap2);
				}
				else
					if (ap2->mode == am_dreg) {
						if (ap2->preg != ECX) {
							if (regs[1]) {
								gen_codes(op_xchg,4,ap2,ecx);
		 						if (equal_address(ap1,ap2))
									gen_codes(op,1,ecx,ecx);
								else
									gen_code2(op,ap1->length,1,ap2,ecx);
								if (!regs[ap2->preg] && ap1->mode == am_dreg && ap1->tempflag) {
									freeop(ap1);
									regs[ap2->preg]++;
									ap1->preg = ap2->preg;
									ap1->tempflag = ap2->tempflag;
									return;
									
								}
								else	
									gen_codes(op_xchg,4,ap2,ecx);
							}
							else {
								gen_codes(op_mov,4,ecx,ap2);
		 						if (equal_address(ap1,ap2))
									gen_codes(op,1,ecx,ecx);
								else
									gen_codes2(op,1,ap1,ecx);
							}
						}
						else
							gen_codes2(op,1,ap1,ecx);
					}
					else {
							if (regs[1])
								gen_push(ECX,am_dreg,0);
							gen_codes(op_mov,1,ecx,ap2);
					  	gen_codes2(op,1,ap1,ecx);
							if (regs[1])
								gen_pop(ECX,am_dreg,0);
					}
					freeop(ap2);
}
AMODE    *gen_shift(ENODE *node, int op,int size)
/*
 *      generate code to evaluate a shift node and return the
 *      address mode of the result.
 */
{       AMODE    *ap1, *ap2;

        ap1 = gen_expr(node->v.p[0],FALSE,FALSE,size);
				noids(ap1);
        ap2 = gen_expr(node->v.p[1],FALSE,FALSE,size);
				do_extend(ap1,4,F_DREG | F_VOL);
				doshift(ap1,ap2,op);
        return ap1;
}
void divintoeax(AMODE *ap1, int size, int op, int modflag)
{
	AMODE *eax = makedreg(EAX), *edx = makedreg(EDX), *ecx = makedreg(ECX);
	AMODE *ah = makedreg(4);
	int pushdx = FALSE, pushcx = FALSE;
			if ((ap1->mode == am_dreg || ap1->mode == am_indisp || ap1->mode == am_indispscale) && ap1->preg == EDX
					|| ap1->mode == am_indispscale && ap1->sreg == EDX) {
				if (size > 1) {
					if (regs[1]) {
						gen_push(ECX,am_dreg,1);
						pushcx = 1;
					}
					gen_codes(op_mov,size,makedreg(ECX),ap1);
				}
				if (op == op_idiv)
					gen_code(op_cdq,0,0);
				else
					gen_codes(op_sub,4,edx,edx);
				gen_codes(op,size,ecx,0);
				if (pushcx)
					gen_pop(ECX,am_dreg,0);
			}
			else {
				if (regs[2] && size > 1) {
					gen_push(EDX,am_dreg,1);
					pushdx = 1;
				}
				if (op == op_idiv)
					gen_code(op_cdq,0,0);
				else
					gen_codes(op_sub,4,edx,edx);
				gen_codes(op,size,ap1,0);
			}
eaxjoin:
			ap1->mode = am_dreg;
			ap1->preg = EAX;
			ap1->tempflag = TRUE;
			ap1->length = size;
			if (modflag)
				if (size == 1) {
					gen_codes(op_mov,1,eax,ah);
					regs[0]++;
				}
				else if (pushdx) {
					gen_code(op_mov,eax,edx);
					regs[0]++;
				}
				else {
					regs[2]++;
					ap1->preg = EDX;
				}
			else
				regs[0]++;
			if (pushdx)
				gen_pop(EDX,am_dreg,0);
}
void dodiv(AMODE *ap1, AMODE *ap2, int op,int modflag)
{
	AMODE *eax = makedreg(EAX), *edx = makedreg(EDX), *ecx = makedreg(ECX);
	AMODE *ah = makedreg(4);
	int size = ap1->length,pushax = FALSE;
	ah->length = 1;
	if (size < 0)
		size = -size;
	if (ap2->mode == am_immed) {
		ap2 = make_muldivval(ap2);
	}
	/* divide by self */
	if (ap2->mode == am_dreg && ap1->preg == ap2->preg) {
		if (modflag)
			gen_codes(op_sub,4,ap2,ap2);
		else
			gen_codes(op_mov,4,ap2,make_immed(1));
		return;
	}
	freeop(ap1);
	freeop(ap2);
	switch (ap1->preg) {
		case ESI:
		case EDI:
		case EBX:
			/* only gets here if /= with source reg */
		case EDX:
		case ECX:
			if (ap2->mode == am_dreg && ap2->preg == EAX) {
				gen_codes(op_xchg,4,ap2,ap1);
				divintoeax(ap1,size,op,modflag);
				break;
			}
			if (regs[0]) {
				gen_push(EAX,am_dreg,1);
				pushax = TRUE;
			}
			gen_codes(op_xchg,4,eax,ap1);
			if (ap2->mode == am_indisp && ap2->preg == EAX)
				ap2->preg = ap1->preg;
			else if (ap2->mode == am_indispscale) {
				if (ap2->preg == EAX)
					ap2->preg = ap1->preg;
				if (ap2->sreg == EAX)
					ap2->sreg = ap1->preg;
			}
			divintoeax(ap2,size,op,modflag);
			if (pushax && ap2->preg == EAX)
				gen_codes(op_mov,4,ap1,eax);
			else {
				ap1->preg = ap2->preg;
				ap1->length = ap2->length;
				ap1->mode = ap2->mode;
			}
			if (pushax)
				gen_pop(EAX,am_dreg,0);
			break;
		case EAX:
			/* case for eax/val */
			divintoeax(ap2,size,op,modflag);
			ap1->preg = ap2->preg;
			ap1->length = ap2->length;
			ap1->mode = ap2->mode;
			break;
	}
}
AMODE    *gen_modiv(ENODE *node, int op, int fopp, int fop, int foppr, int fopr, int modflag,int size)
/*
 *      generate code to evaluate a mod operator or a divide
 *      operator. these operations are done on only long
 *      divisors and word dividends so that the 68000 div
 *      instruction can be used.
 */
{       AMODE    *ap1, *ap2;
				if (natural_size(node) > 5) {
					return gen_fbinary(node,fop,fopp,fopr,foppr,size);
				}
				resolve_binary(node,&ap1,&ap2,size);
				do_extend(ap1,ap1->length,F_DREG | F_VOL);
				dodiv(ap1,ap2,op,modflag);
        return ap1;
					
}
AMODE * gen_pdiv(ENODE *node,int size)
{
				return gen_modiv(node, op_div,op_fdivp,op_fdiv,op_fdivrp, op_fdivr,FALSE,size);
}			

/*
 * generate a multiply using the 386 extended IMUL.
 * rather than push EDX, we just use the extended form when EDX
 * is in use since that does not modify edx
 * at LEAST one of the args to this must be a DREG!!!!!!
 *
 * note:  we don't care what size or the sign of a multiply, as long as
 * things have been properly extended to matching sizes...
 *
 */
int domul(AMODE *ap1, AMODE *ap2, int op)
{
	int size = ap1->length;
	if (equal_address(ap1,ap2)) {
		gen_codes(op_imul,4,ap1,ap1);
	}
	else {
		if (ap2->mode == am_dreg)
			gen_codes(op_imul,4,ap1,ap2);
		else {
			if (size == -1 || size == 1) {
			/* note: for character multiplies this will do a WORD SIZED MULTIPLY
       * which is ok as long as we weren't at a segment boundary.  It may
       * be a little slower, but, I opted for this rather than use up another
       * temp register...
       */
				gen_codes(op_imul,2,ap1,ap2);
			}
			else
				gen_code(op_imul,ap1,ap2);
		}
	}
	freeop(ap2);
	if (chksize(4,ap1->length))
		ap1->length = 2 * ap1->length;
	return 0;
}
AMODE * gen_pmul(ENODE *node,int size)
{
				return gen_mul(node,op_mul, op_fmulp,op_fmul, op_fmulp, op_fmul,size);
}			
AMODE    *gen_mul(ENODE *node, int op,int fopp,int fop, int foppr, int fopr, int size)
/*
 *      generate code to evaluate a multiply node. both operands
 *      are treated as words and the result is long and is always
 *      in a register so that the 68000 mul instruction can be used.
 */
{       AMODE    *ap1, *ap2;
				if (natural_size(node) > 5) {
					return gen_fbinary(node,op_fmul, op_fmulp, op_fmul, op_fmulp, natural_size(node));
				}
				prefer(node,&ap1,&ap2,op,TRUE,size);
				do_extend(ap1,ap1->length,F_DREG | F_VOL);
				domul(ap1,ap2,op);
				freeop(ap2);
        return ap1;
					
}
AMODE    *gen_hook(ENODE *node,int size)
/*
 *      generate code to evaluate a condition operator node (?:)
 */
{       AMODE    *ap1, *ap2;
        int             false_label, end_label;
				int sizl,sizr,xsiz;
				sizr = natural_size(node->v.p[1]->v.p[1]);
				sizl = natural_size(node->v.p[1]->v.p[0]);
				sizl = sizl < 0 ? - sizl : sizl;
				sizr = sizr < 0 ? - sizr : sizr;
				if (sizl < sizr)
					xsiz = sizr;
				else 
					xsiz = sizl;
        false_label = nextlabel++;
        end_label = nextlabel++;
        falsejp(node->v.p[0],false_label);
        node = node->v.p[1];
        ap1 = gen_expr(node->v.p[0],FALSE,FALSE,size);
				do_extend(ap1,xsiz,F_DREG | F_VOL);
				freeop(ap1);
        gen_codes(op_jmp,0,make_label(end_label),0);
        gen_label(false_label);
        ap2 = gen_expr(node->v.p[1],FALSE,FALSE,size);
				do_extend(ap2,xsiz,F_DREG | F_VOL);
        if( !equal_address(ap1,ap2) )
                {
								/* if both are floating they won't get here */
								if (ap1->mode == am_freg) {
									do_extend(ap2,10,F_FREG);
								}
								else {
									gen_code(op_mov,ap1,ap2);
									freeop(ap2);
									ap2->mode = am_dreg;
									ap2->preg = ap1->preg;
									regs[ap2->preg]++;
                }
				}
        gen_label(end_label);
        return ap2;
}

AMODE    *gen_asunary(ENODE *node,int novalue,int op, int fop, int size)
/*
 *      generate code to evaluate a unary minus or complement of an address
 */
{       AMODE    *ap;
				int nsize = natural_size(node->v.p[0]);
        ap = gen_expr(node->v.p[0],novalue,TRUE,nsize);
				if (ap->length > 5) {
					floatload(ap,TRUE);
					gen_codef(fop,0,0);
					return floatstore(node->v.p[0],novalue);
				}
				else {
        	gen_code(op,ap,0);
				}
        return ap;
}
AMODE    *gen_asadd(ENODE *node,int novalue,int op,int fopp,int fop, int foppr, int fopr,int size)
/*
 *      generate a plus equal or a minus equal node.
 */
{       AMODE    *ap1, *ap2, *apr;
				int bits;
				int nsize = natural_size(node->v.p[0]);
				if (natural_size(node) > 5) {
					ap1 = gen_fbinary(node,fop,fopp,fopr,foppr,nsize);
					return floatstore(node->v.p[0],novalue);
				}
				bits = as_args(node,&apr,&ap1,&ap2,nsize);
				small_size(ap1,ap2,nsize);
				if (!bits) {
					if (ap1->mode != am_dreg && ap2->mode != am_dreg && ap2->mode != am_immed)
						do_extend(ap2,ap1->length,am_dreg);
					gen_code(op,apr,ap2);
					freeop(ap1);
					freeop(ap2);
					return apr;
				}
				else {
					gen_code(op,ap1,ap2);
					bit_store(apr,ap1,node->v.p[0],novalue);
					freeop(ap2);
					freeop(apr);
					return ap1;
				}
}

AMODE    *gen_aslogic(ENODE *node, int novalue, int op, int op2, int size)
/*
 *      generate a and equal or a or equal node.
 */
{       AMODE    *ap1, *ap2, *apr, *ap3;
				int bits = isbit(node->v.p[0]);
				int nsize = natural_size(node->v.p[0]);
				if (bits) {
					ap2 = gen_expr(node->v.p[1],FALSE,FALSE,nsize);
					noids(ap2);
					apr = gen_expr(node->v.p[0],FALSE,FALSE,nsize);
					if (ap2->mode == am_immed) {
						ENODE *node1 = node->v.p[0];
						int vb;
						ap2->offset->v.i &= bittab[node1->bits-1];
						ap3 = make_immed(ap2->offset->v.i << node1->startbit);
						if (op == op_and) {
							ap3->offset->v.i |= ~(bittab[node1->bits-1] << node1->startbit);
							ap3->offset->v.i = ~ap3->offset->v.i;
							vb = single_bit(ap3->offset->v.i);
							if (vb != -1)
								gen_codes(op_btr,4,apr,make_immed(vb));
							else {
								ap3->offset->v.i = ~ap3->offset->v.i;
								gen_code(op_and,apr,ap3);
							}
						}
						else {
							vb = single_bit(ap3->offset->v.i);
							if (vb != -1)
								gen_codes(op2,4,apr,make_immed(vb));
							else
								gen_code(op,apr,ap3);
						}
						return ap2;
					}
					else {
						do_extend(ap2,ap2->length,F_DREG | F_VOL);
						gen_code(op_and,ap2,make_immed(bittab[node->v.p[0]->bits-1]));
						if (chksize(ap2->length,apr->length))
							ap2->length = apr->length;
						if (!novalue)
							gen_codes(op_push,4,ap2,0);
						if (node->v.p[0]->startbit)
							gen_code(op_shl,ap2,make_immed(node->v.p[0]->startbit));
						if (op == op_and)
							gen_code(op_or,ap2,make_immed(~(bittab[node->v.p[0]->bits-1] << node->v.p[0]->startbit)));
						gen_code(op,apr,ap2);
						freeop(apr);
						if (!novalue)
							gen_codes(op_pop,4,ap2,0);
						else freeop(ap2);
						return ap2;
					}
				}
				else {
					int vb;
					as_args(node,&apr,&ap1, &ap2,nsize);
					small_size(ap1,ap2,nsize);
					if (ap1->mode != am_dreg && ap2->mode != am_dreg && ap2->mode != am_immed)
						do_extend(ap2,ap2->length,F_DREG | F_VOL);
					if (ap2->mode == am_immed) {
						if (op == op_and && (vb = single_bit(~ap2->offset->v.i))!= -1) {
							gen_codes(op2,4,ap1,make_immed(vb));                        	
							freeop(ap1);
							return apr;
						}
						else if ((vb = single_bit(ap2->offset->v.i)) != -1){
							gen_codes(op2,4,ap1,make_immed(vb));
							freeop(ap1);
							return apr;
						}
					}
        	gen_code(op,apr,ap2);
					freeop(ap1);
					freeop(ap2);
        	return apr;
				}
}

AMODE *gen_asshift(ENODE *node, int novalue,int op, int size)
/*
 *      generate shift equals operators.
 */
{       AMODE    *ap1, *ap2, *apr;
				int nsize = natural_size(node->v.p[0]);
				int bits = as_args(node,&apr,&ap1,&ap2,nsize);
				
				if (op == op_sal || op == op_shl)
					small_size(ap1,ap2,nsize);
				else
					get_size(ap1,ap2,nsize);
				if (!bits) {
					doshift(apr,ap2,op);
					return apr;
				}
				else {
					do_extend(ap1,4,0);
					doshift(ap1,ap2,op);
					bit_store(apr,ap1,node->v.p[0],novalue);
					freeop(apr);
					return ap1;
				}
}

AMODE    *gen_asmul(ENODE *node, int novalue,int op, int foppr, int fopr, int fopp, int fop,int size)
/*
 *      generate a *= node.
 */
{       AMODE    *ap1, *ap2, *apr;
				int bits;
				int nsize = natural_size(node->v.p[0]);
				if (natural_size(node) > 5) {
					ap1 = gen_fbinary(node,fop,fopp,fopr,foppr,nsize);
					return floatstore(node->v.p[0],novalue);
				}
				bits = as_args(node,&apr,&ap1,&ap2,nsize);
				small_size(ap1,ap2,nsize);
				if (!bits) {
					if (ap1->mode != am_dreg) {
						if (ap2->mode != am_dreg) 
							do_extend(ap2,ap2->length,F_DREG | F_VOL);
						domul(ap2,ap1,op);
						freeop(ap1);
						gen_code(op_mov,apr,ap2);
						if (novalue)
							freeop(ap2);
						return ap2;
					}
					else {
						domul(ap1,ap2,op);
						freeop(ap2);
						if (!equal_address(ap1,apr)) {
							gen_code(op_mov, apr,ap1);
							if (novalue)
								freeop(ap1);
							return ap1;
						}
						freeop(ap1);
					}
					return apr;
				}
				else {
					domul(ap1,ap2,op);
					bit_store(apr,ap1,node->v.p[0],novalue);
					freeop(ap2);
					freeop(apr);
					return ap1;
				}
}

AMODE    *gen_asmodiv(ENODE *node, int novalue, int op, int fopp, int fop, int foppr, int fopr, int modflag, int size)
/*
 *      generate /= and %= nodes.
 */
{       AMODE    *ap1, *ap2, *apr;
				int bits;
				int nsize = natural_size(node->v.p[0]);
				if (natural_size(node) > 5) {
					ap1 = gen_fbinary(node,fop,fopp,fopr,foppr,nsize);
					return floatstore(node->v.p[0],novalue);
				}
				bits = as_args(node,&apr,&ap1,&ap2,nsize);
				get_size(ap1,ap2,nsize);
				if (!bits) {
					if (ap1->mode != am_dreg)
						do_extend(ap1,ap1->length,F_DREG | F_VOL);
					dodiv(ap1,ap2,op,modflag);
					if (chksize(ap1->length,apr->length))
						ap1->length = apr->length;
					if (!equal_address(ap1,apr))
						gen_code(op_mov,apr,ap1);
					freeop(apr);
					return ap1;
				}
				else {
					dodiv(ap1,ap2,op,modflag);
					bit_store(apr,ap1,node->v.p[0],novalue);
					freeop(apr);
					return ap1;
				}
}
void mov1(AMODE *dst, AMODE *src, int size)
{
	AMODE *intermed = temp_data();
	gen_codes(op_mov,size,intermed,src);
	gen_codes(op_mov,size,dst,intermed);
	freeop(intermed);
	freeop(src);
	freeop(dst);
	
}
void mov2(AMODE *dst, AMODE *src, int size1,int size2)
{
	AMODE *intermed = temp_data(),*dst1,*src1;
	gen_codes(op_mov,size1,intermed,src);
	gen_codes(op_mov,size1,dst,intermed);
	src->offset = makenode(en_add,src->offset,makenode(en_icon,(char *)size1,0));
	dst->offset = makenode(en_add,dst->offset,makenode(en_icon,(char *)size1,0));
	gen_codes(op_mov,size2,intermed,src);
	gen_codes(op_mov,size2,dst,intermed);
	freeop(intermed);
	freeop(src);
	freeop(dst);
}
void mov3(AMODE *dst, AMODE *src, int size1,int size2,int size3)
{
	AMODE *intermed = temp_data(),*dst1,*src1;
	gen_codes(op_mov,size1,intermed,src);
	gen_codes(op_mov,size1,dst,intermed);
	src->offset = makenode(en_add,src->offset,makenode(en_icon,(char *)size1,0));
	dst->offset = makenode(en_add,dst->offset,makenode(en_icon,(char *)size1,0));
	gen_codes(op_mov,size2,intermed,src);
	gen_codes(op_mov,size2,dst,intermed);
	src->offset = makenode(en_add,src->offset,makenode(en_icon,(char *)size2,0));
	dst->offset = makenode(en_add,dst->offset,makenode(en_icon,(char *)size2,0));
	gen_codes(op_mov,size3,intermed,src);
	gen_codes(op_mov,size3,dst,intermed);
	freeop(intermed);
	freeop(src);
	freeop(dst);
}
AMODE *amode_moveblock(AMODE *ap1, AMODE *ap2, int size)
{
	int t = size & 3,q=size >> 2;
	AMODE *ap3;

	switch(size) {
		case 1:
		case 2:
		case 4:
			mov1(ap1,ap2,size);
			break;
		case 3:
			mov2(ap1,ap2,2,1);
			break;
		case 5:
			mov2(ap1,ap2,4,1);
			break;
		case 6:
			mov2(ap1,ap2,4,2);
			break;
		case 7:
			mov3(ap1,ap2,4,2,1);
			break;
		case 8:
			mov2(ap1,ap2,4,4);
			break;
		case 9:
			mov3(ap1,ap2,4,4,1);
			break;
		case 10:
			mov3(ap1,ap2,4,4,2);
			break;
		case 12:
			mov3(ap1,ap2,4,4,4);
			break;
		default:
			if (ap1->mode == am_indisp && ap1->preg == ESI ||
					(ap1->mode == am_indispscale && (ap1->preg == ESI || ap2->preg == ESI)) ||
					(ap1->mode == am_dreg && ap1->preg == ESI)) {
				ap3 = temp_data();
				gen_code(op_lea,ap3,ap1);
				ap1 = ap3;
				freeop(ap1);
			}
			gen_push(ESI,am_dreg,0);                                  
			gen_push(EDI,am_dreg,0);                                  
			freeop(ap2);
			freeop(ap1);
			if (regs[1])                                              
				gen_push(ECX,am_dreg,0);                                
			if (ap2->mode == am_dreg)
				gen_codes(op_mov,4,makedreg(ESI),ap2);		                  
			else
				gen_codes(op_lea,4,makedreg(ESI),ap2);		                  
			if (ap1->mode == am_dreg)
				gen_codes(op_mov,4,makedreg(EDI),ap1);                     
			else
				gen_codes(op_lea,4,makedreg(EDI),ap1);                     
			gen_code(op_cld,0,0);
			if (q) {
				gen_codes(op_mov,4,makedreg(ECX),make_immed(q));  
				gen_code(op_rep,0,0);                                   
				gen_code(op_movsd,0,0);
			}
			if (t & 2)
				gen_code(op_movsw,0,0);
			if (t & 1)
				gen_code(op_movsb,0,0);
			if (regs[1])                                              
				gen_pop(ECX,am_dreg,0);
			gen_pop(EDI,am_dreg,0);
			gen_pop(ESI,am_dreg,0);
			break;
	}
	freeop(ap2);
	freeop(ap1);
}
AMODE *gen_moveblock(ENODE *node)      
{                                                           
	AMODE *ap1, *ap2;
	ENODE ep1 ;
	if (!node->size)                                          
		return(0);
	ep1.nodetype = en_l_ref ;
	ep1.cflags = 0;
	ep1.v.p[0] = node->v.p[1] ;
	ap2 = gen_expr(&ep1,FALSE,TRUE,0);                     
	noids(ap2);
	ep1.nodetype = en_l_ref ;
	ep1.cflags = 0;
	ep1.v.p[0] = node->v.p[0] ;
	ap1 = gen_expr(&ep1,FALSE,TRUE,0); 
	return amode_moveblock(ap1,ap2,node->size);
}
AMODE    *gen_assign(ENODE *node, int novalue,int size)
/*
 *      generate code for an assignment node. if the size of the
 *      assignment destination is larger than the size passed then
 *      everything below this node will be evaluated with the
 *      assignment size.
 */
{       AMODE    *ap2, *apr;
				int bits;
				int nsize = natural_size(node->v.p[0]);
				ap2 = gen_expr(node->v.p[1],FALSE,FALSE,nsize);
				noids(ap2);
				apr = gen_expr(node->v.p[0],FALSE,TRUE,nsize);
				if (ap2->length > 5) {
					do_extend(ap2,10,F_FREG);
					return floatstore(node->v.p[0],novalue);
				}
				small_size(apr,ap2,nsize);
				bits = isbit(node->v.p[0]);
				if (!equal_address(apr,ap2)	) {
					if (!bits) {
						if (apr->length > 5) {
							do_extend(ap2,apr->length,F_FREG);
							return floatstore(node->v.p[0],novalue);
						}
						else {
							if (apr->mode != am_dreg && ap2->mode != am_dreg && ap2->mode != am_immed)
								do_extend(ap2,apr->length,F_DREG);
							else
								do_extend(ap2,apr->length,0);
						
							gen_codes(op_mov,apr->length,apr,ap2);
							if (!novalue && (ap2->mode == am_dreg || ap2->mode == am_immed)) {
								freeop(apr);
								return ap2;
							}
							freeop(ap2);
							return apr;
						}
					}
					else {
						bit_store(apr,ap2,node->v.p[0],novalue);
						freeop(apr);
						return ap2;
					}
				}
				return apr;
}
AMODE    *gen_refassign(ENODE *node, int novalue, int size)
/*
 *      generate code for an assignment node. if the size of the
 *      assignment destination is larger than the size passed then
 *      everything below this node will be evaluated with the
 *      assignment size.
 */
{       AMODE    *ap1, *ap2, *apr,*ap4;
				int nsize = natural_size(node->v.p[0]);
				int bits = as_args(node,&apr,&ap1,&ap2,nsize);
				if (natural_size(node->v.p[0]) > 5) {
					do_extend(ap2,ap1->length,0);
					floatload(ap2,TRUE);
					ap2 = floatstore(node->v.p[0],novalue);
					ap4 = temp_data();
					gen_code(op_lea,ap4,ap2);
					return ap4;
				}
				if (!equal_address(apr,ap2)	) {
					if (!bits) {
						if (apr->length > 5) {
							do_extend(ap2,apr->length,F_FREG);
							return floatstore(node->v.p[0],novalue);
						}
						do_extend(ap2,ap1->length,0);
						gen_code(op_mov,apr,ap2);
						ap4 = temp_data();
						gen_code(op_lea,ap4,apr);
						freeop(ap1);
						freeop(ap2);
						freeop(apr);
						return ap4;
					}
					else {
						bit_store(apr,ap2,node->v.p[0],novalue);
						freeop(ap1);
						freeop(apr);
						return ap2;
					}
				}
				freeop(ap1);
				freeop(ap2);
				return apr;
}

AMODE    *gen_aincdec(ENODE *node, int novalue, int op, int size)
/*
 *      generate an auto increment or decrement node. op should be
 *      either op_add (for increment) or op_sub (for decrement).
 */
{       AMODE    *ap1,*ap2,*ap3;
				int pushed = FALSE,sz;
				int nsize = natural_size(node->v.p[0]);
				ap2 = make_immed((long)node->v.p[1]);
				ap1 = gen_expr(node->v.p[0],FALSE,FALSE,nsize);
				sz = ap1->length;
				if (sz > 5) {
					floatload(ap1,TRUE);
					make_floatconst(ap2);
					floatload(ap2,TRUE);
					if (novalue)
						gen_codef(op_faddp,0,0);
					else
						gen_codef(op_fadd,makefreg(1),0);
					floatstore(node->v.p[0],TRUE);
					freeop(ap1);
					ap3 = fstack();
				}
				else if (isbit(node->v.p[0])) {
					ap3 = gen_expr(node->v.p[0],FALSE,FALSE,size);
					gen_code(op,ap3,ap2);
					bit_store(ap1,ap3,node->v.p[0],novalue);
					freeop(ap1);
					if (novalue)
						freeop(ap3);
				}
				else {
					freeop(ap1);
					if (!novalue) {
						int reg = next_dreg();
/*FIXME*/
						if (ap1->mode == am_indisp && ap1->preg == reg ||
								ap1->mode == am_indispscale && (ap1->preg == reg || ap1->sreg == reg)) {
							gen_codes(op_push,4,ap1,0);
							pushed = TRUE;
						}
						else {
							ap3 = temp_data();
							gen_codes(op_mov,sz,ap3,ap1);
						}
					}
					gen_code(op,ap1,ap2);
					if (pushed) {
						ap3 = temp_data();
						gen_codes(op_pop,4,ap3,0);
					}
				}
				if (!novalue)
					ap3->length = sz;
				return ap3;
}

int push_param(ENODE *ep, int size)
/*
 *      push the operand expression onto the stack.
 */
{       AMODE    *ap, *ap2;
				int rv;
				int nsize = natural_size(ep);
				switch (ep->nodetype) {
								case en_absacon:
												ep->v.i = ep->v.sp->value.i;
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_immed;
                        ap->offset = ep;     /* use as constant node */
												ap->length = rv = 4;
                        gen_code(op_push,ap,0);
												rv = 4;
												break;
                case en_napccon:
                case en_nacon:
												ep->v.p[0] = ep->v.sp;
                case en_labcon:
                case en_nalabcon:
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_immed;
                        ap->offset = ep;     /* use as constant node */
												ap->length = rv = 4;
                        gen_code(op_push,ap,0);
												gen_code(op_void,0,0);
												break;
								case en_cf:
								case en_floatref:
												ap = gen_expr(ep,FALSE,TRUE,4);
												do_extend(ap,10,F_FREG);
												gen_codes(op_sub,4,makedreg(ESP),make_immed(4));
												ap2 = make_stack(0);
												ap2->length = 6;
												gen_codef(op_fstp,ap2,0);
												gen_codef(op_fwait,0,0);
												gen_code(op_void,0,0);
												rv = 4;
												break;
								case en_cd:
								case en_doubleref:
												ap = gen_expr(ep,FALSE,TRUE,8);
												do_extend(ap,10,F_FREG);
												gen_codes(op_sub,4,makedreg(ESP),make_immed(8));
												ap2 = make_stack(0);
												ap2->length = 8;
												gen_codef(op_fstp,ap2,0);
												gen_codef(op_fwait,0,0);
												gen_code(op_void,0,0);
												rv = 8;
												break;
								case en_cld:
								case en_longdoubleref:
												ap = gen_expr(ep,FALSE,TRUE,10);
												do_extend(ap,10,F_FREG);
												gen_codes(op_sub,4,makedreg(ESP),make_immed(12));
												ap2 = make_stack(0);
												ap2->length = 10;
												gen_codef(op_fstp,ap2,0);
												gen_codef(op_fwait,0,0);
												gen_code(op_void,0,0);
												rv = 12;
												break;
								case en_rcon: 
												size = 8;
												goto floatconst;
								case en_fcon:
												size = 6;
												goto floatconst;
								case en_lrcon:
												size = 10;
floatconst:
												rv = size;
												if (rv == 6) rv= 4;
												if (rv == 10) rv= 12;
												ap = xalloc(sizeof(AMODE));
												ap->offset = ep;
												ap->length = size;
												ap->mode = am_immed;
												make_floatconst(ap);
												floatload(ap,TRUE);
												gen_codes(op_sub,4,makedreg(ESP),make_immed(rv));
												ap2 = make_stack(0);
												ap2->length = size;
												gen_codef(op_fstp,ap2,0);
												gen_codef(op_fwait,0,0);
												break;
								default:
      			  					ap = gen_expr(ep,FALSE,TRUE,size);
												if (size <=4) {
													do_extend(ap,4,0);
													gen_code(op_push,ap,0);
													rv = 4;
												}
												else {
													floatload(ap,FALSE);
													rv = 8;
													if (size == 6) rv= 4;
													if (size == 10) rv= 12;
													gen_codes(op_sub,4,makedreg(ESP),make_immed(rv));
													ap2 = make_stack(0);
													ap2->length = size;
													gen_codef(op_fstp,ap2,0);
													gen_codef(op_fwait,0,0);
												}
												break;
				}
				gen_code(op_void,0,0);
        freeop(ap);
	return(rv);
}
int push_stackblock(ENODE *ep)
{
	AMODE *ap;
	SYM *sp;
	int x;
	int sz = (ep->size + stackadd) &stackmod;
	if (!sz)
		return(0);
	if (sz > 24) {
				gen_codes(op_sub,4,makedreg(ESP),make_immed(sz));
				gen_codes(op_push,4,makedreg(ESI),0);
				gen_codes(op_push,4,makedreg(EDI),0);
				switch (ep->nodetype) {
                case en_napccon:
                case en_nacon:
												ep->v.p[0] = ep->v.sp;
                case en_nalabcon:
                case en_labcon:
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_direct;
                        ap->offset = ep;     /* use as constant node */
                        gen_codes(op_lea,4,makedreg(ESI),ap);
												break;
								case en_absacon:
												ep->v.i = ep->v.sp->value.i;
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_direct;
                        ap->offset = ep;     /* use as constant node */
                        gen_codes(op_lea,4,makedreg(ESI),ap);
												break;
								case en_autocon:
												sp = ep->v.p[0] = ep->v.sp;
												x = sp->value.i;
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_indisp;
			          				ap->preg = EBP;
      			    				ap->offset = makenode(en_icon,(char *)x,0);
												gen_codes(op_lea,4,makedreg(ESI),ap);
												break;
								default:
      			  					ap = gen_expr(ep,F_DREG | F_MEM | F_IMMED,TRUE,4);
												gen_codes(op_lea,4,makedreg(ESI),ap);
												break;
				}
				gen_codes(op_lea,4,makedreg(EDI),make_stack(-8));
				freeop(ap);
				if (regs[1])
					gen_push(ECX,am_dreg,0);
				gen_code(op_cld,0,0);
				gen_codes(op_mov,4,makedreg(ECX),make_immed(sz>>2));  
				gen_code(op_rep,0,0);                                   
				gen_code(op_movsd,0,0);
				if (regs[1])
					gen_pop(ECX,am_dreg,0);
				gen_codes(op_pop,4,makedreg(EDI),0);
				gen_codes(op_pop,4,makedreg(ESI),0);
				gen_code(op_void,0,0);
	} else {
				int i = ((sz +3)/4)*4;
				switch (ep->nodetype) {
                case en_napccon:
                case en_nacon:
												ep->v.p[0] = ep->v.sp;
                case en_nalabcon:
                case en_labcon:
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_direct;
                        ap->offset = ep;     /* use as constant node */
												break;
								case en_absacon:
												ep->v.i = ep->v.sp->value.i;
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_direct;
                        ap->offset = ep;     /* use as constant node */
												break;
								case en_autocon:
												sp = ep->v.p[0] = ep->v.sp;
												x = sp->value.i;
                        ap = xalloc(sizeof(AMODE));
                        ap->mode = am_indisp;
			          				ap->preg = EBP;
      			    				ap->offset = makenode(en_icon,(char *)x,0);
												break;
								default:
      			  					ap = gen_expr(ep,F_DREG | F_MEM | F_IMMED,FALSE,4);
												break;
				}
				while (i > 0) {
					AMODE *ap1 = xalloc(sizeof(AMODE));
					memcpy(ap1,ap,sizeof(AMODE));
					ap1->offset = makenode(en_addstruc,ap->offset,makenode(en_icon,(char *)(i-4),0));
					gen_code(op_push,ap1,0);
					i = i - 4;
				}
				
	}
	return(sz);
}

int     gen_parms(ENODE *plist)
/*
 *      push a list of parameters onto the stack and return the
 *      size of parameters pushed.
 */
{       int     i;
        i = 0;
        while( plist != 0 )
                {         	
								if (plist->nodetype == en_stackblock)
									i+=push_stackblock(plist->v.p[0]);
								else
                	i+=push_param(plist->v.p[0],natural_size(plist->v.p[0]));
                plist = plist->v.p[1];
                }
        return i;
}

AMODE    *gen_tcall(ENODE *node, int novalue)
/*
 *      generate a trap call node and return the address mode
 *      of the result.
 */
{       AMODE    *ap, *result;
        int             i,siz1;
				int regax,regdx,regcx;
				regax = regs[0]; regcx = regs[1]; regdx = regs[2];
				if (regax)
					gen_push(EAX,am_dreg,0);
				if (regcx)
					gen_push(ECX,am_dreg,0);
				if (regdx)
					gen_push(EDX,am_dreg,0);
				regs[0] = regs[1] = regs[2] = 0;
				siz1 = node->v.p[0]->v.i;
				gen_code(op_int,make_immed((int)node->v.p[0]->v.p[0]),0);
				regs[0] = regax; regs[1] = regcx; regs[2] = regdx;
				if (!novalue) {
	        result = temp_data();
					result->length = siz1;
    	    if(result->preg != EAX)
      	    gen_codes(op_mov,siz1,result,makedreg(EAX));
				}
				if (regdx)
					gen_pop(EDX,am_dreg,0);
				if (regcx)
					gen_pop(ECX,am_dreg,0);
				if (regax)
					gen_pop(EAX,am_dreg,0);
        return result;
}
#ifdef CPLUSPLUS
AMODE *inlinecall(ENODE *node)
{
      ENODE *nameref = node; 
      SYM *sp;
      int size;
      if (nameref->nodetype == en_thiscall)
        nameref = nameref->v.p[1];
      nameref = nameref->v.p[1]->v.p[0];
      if (nameref->nodetype == en_nacon || nameref->nodetype == en_napccon) {  
        sp = nameref->v.sp;
				if (sp && (sp->value.classdata.cppflags & PF_INLINE)) {
					int oldretlab = retlab;
					AMODE *ap;
					SYM *oldcurfunc = currentfunc;
					currentfunc = sp;
					retlab = -1;
					genstmt(sp->value.classdata.inlinefunc->stmt);
					genreturn(0,3);
									size = currentfunc->tp->btp->size;
									if (size > 5) {
										ap = makefreg(0);
									}
            	    else {
											ap = makedreg(EAX);
									}
					currentfunc = oldcurfunc;
					retlab = oldretlab;
					return ap;
				}
      }
      return 0;
}
#endif
AMODE    *gen_fcall(ENODE *node, int novalue)
/*
 *      generate a function call node and return the address mode
 *      of the result.
 */
{       AMODE    *ap, *result;
				ENODE *pushthis = 0;
        int             i=0,siz1;
				int regax,regdx,regcx;
#ifdef CPLUSPLUS
				if (ap = inlinecall(node))
					return ap;
#endif
				if (node->nodetype != en_callblock && node->nodetype != en_scallblock)
					if (ap = HandleIntrins(node, novalue))
						return ap;
				regax = regs[0]; regcx = regs[1]; regdx = regs[2];
				if (regax)
					gen_push(EAX,am_dreg,0);
				if (regcx)
					gen_push(ECX,am_dreg,0);
				if (regdx)
					gen_push(EDX,am_dreg,0);
				regs[0] = regs[1] = regs[2] = 0;
#ifdef CPLUSPLUS
				if (node->nodetype == en_thiscall) {
					pushthis = node->v.p[0];
					node = node->v.p[1];
					i = 4;
				}
#endif
				if (node->nodetype == en_callblock || node->nodetype == en_scallblock) {
					i = i + gen_parms(node->v.p[1]->v.p[1]->v.p[1]->v.p[0]);
      		ap = gen_expr(node->v.p[0],FALSE,TRUE,4);
					gen_codes(op_push,4,ap,0);
					i+=4;
					node = node->v.p[1];
					freeop(ap);
					siz1 = 4;
				}
				else {
	        i = i + gen_parms(node->v.p[1]->v.p[1]->v.p[0]);    /* generate parameters */
					siz1 = node->v.p[0]->v.i;
				}
				if (node->nodetype == en_intcall) {
					AMODE *ap2 = xalloc(sizeof(AMODE));
					ap2->mode = am_seg;
					ap2->seg = e_cs;
					gen_code(op_pushfd,0,0);
					gen_code(op_push,ap2,0);
				}
				else if (pushthis) {
					AMODE *ap2 = gen_expr(pushthis,FALSE,TRUE,4);
					gen_codes(op_push,4,ap2,0);
					freeop(ap2);
				}
									
   	    if( node->v.p[1]->v.p[0]->nodetype == en_nacon || node->v.p[1]->v.p[0]->nodetype == en_napccon ) {
								SYM *sp= node->v.p[1]->v.p[0]->v.sp;
								if (sp->inreg) {
									ap = makedreg(sp->value.i);
								}
								else {
									node->v.p[1]->v.p[0]->v.p[0] = sp;
     	          	ap = make_offset(node->v.p[1]->v.p[0]);
									ap->mode = am_immed;
								}
								if (sp->farproc)
									gen_code(op_push,makesegreg(CS),0);
         	      gen_codes(op_call,0,ap,0);
				}
 	      else
   	            {
     	          ap = gen_expr(node->v.p[1]->v.p[0],FALSE,FALSE,4);
       	        freeop(ap);
									
         	      gen_codes(op_call,4,ap,0);
                }
				if (node->nodetype == en_fcall || node->nodetype == en_fcallb || node->nodetype == en_callblock)
            if( i != 0 ) {
								if (i == 4)
									gen_codes(op_pop,4,makedreg(ECX),0);
								else
                	 gen_code(op_add,makedreg(ESP),make_immed(i));
				}
				regs[0] = regax; regs[1] = regcx; regs[2] = regdx;
				if (siz1 > 5) {
						result = fstack();
				}
				else {
          result = temp_data();
					result->length = siz1;
        	if( result->preg != EAX)
                gen_codes(op_mov,siz1,result,makedreg(EAX));
				}
				if (regdx)
					gen_pop(EDX,am_dreg,0);
				if (regcx)
					gen_pop(ECX,am_dreg,0);
				if (regax)
					gen_pop(EAX,am_dreg,0);
        return result;
}
AMODE    *gen_pfcall(ENODE *node, int novalue)
/*
 *      generate a function call node to a pascal function
 *			and return the address mode of the result.
 */
{       AMODE    *ap, *result;
        int             i=0,siz1;
				ENODE * invnode = 0,*anode;
				ENODE *pushthis = 0;
				int regax,regdx,regcx;
#ifdef CPLUSPLUS
				if (ap = inlinecall(node))
					return ap;
#endif
				
				/* invert the parameter list */
				if (node->nodetype == en_pcallblock || node->nodetype == en_scallblock)
					anode = node->v.p[1]->v.p[1]->v.p[1]->v.p[0];
				else
					anode = node->v.p[1]->v.p[1]->v.p[0];
				while (anode) {
					invnode = makenode(anode->nodetype,anode->v.p[0],invnode);
					anode = anode->v.p[1];
				}
				regax = regs[0]; regcx = regs[1]; regdx = regs[2];
				if (regax)
					gen_push(EAX,am_dreg,0);
				if (regcx)
					gen_push(ECX,am_dreg,0);
				if (regdx)
					gen_push(EDX,am_dreg,0);
				regs[0] = regs[1] = regs[2] = 0;
#ifdef CPLUSPLUS
				if (node->nodetype == en_thiscall) {
					pushthis = node->v.p[0];
					node = node->v.p[1];
					i = 4;
				}
#endif
				if (pushthis) {
					AMODE *ap2 = gen_expr(pushthis,FALSE,TRUE,4);
					gen_codes(op_push,4,ap2,0);
					freeop(ap2);
				}
				if (node->nodetype == en_pcallblock || node->nodetype == en_scallblock) {
      		ap = gen_expr(node->v.p[0],FALSE,FALSE,4);
					gen_codes(op_push,4,ap,0);
					freeop(ap);
					i= i +4;
					i += gen_parms(invnode);
					node = node->v.p[1];
					siz1 = 4;
				}
				else {
	        i = i + gen_parms(invnode);    /* generate parameters */
					siz1 = node->v.p[0]->v.i;
				}
									
   	    if( node->v.p[1]->v.p[0]->nodetype == en_nacon || node->v.p[1]->v.p[0]->nodetype == en_napccon ) {
								SYM *sp= node->v.p[1]->v.p[0]->v.sp;
								if (sp->inreg) {
									ap = makedreg(sp->value.i);
								}
								else {
									node->v.p[1]->v.p[0]->v.p[0] = sp;
     	          	ap = make_offset(node->v.p[1]->v.p[0]);
									ap->mode = am_immed;
								}
								if (sp->farproc)
									gen_code(op_push,makesegreg(CS),0);
         	      gen_codes(op_call,0,ap,0);
				}
 	      else
   	            {
     	          ap = gen_expr(node->v.p[1]->v.p[0],FALSE,FALSE,4);
       	        freeop(ap);
									
         	      gen_codes(op_call,4,ap,0);
                }
				regs[0] = regax; regs[1] = regcx; regs[2] = regdx;
				if (siz1 > 5) {
						result = fstack();
				}
				else {
          result = temp_data();
					result->length = siz1;
        	if( result->preg != EAX)
                gen_codes(op_mov,siz1,result,makedreg(EAX));
				}
				if (regdx)
					gen_pop(EDX,am_dreg,0);
				if (regcx)
					gen_pop(ECX,am_dreg,0);
				if (regax)
					gen_pop(EAX,am_dreg,0);
        return result;
}
AMODE    *gen_expr(ENODE *node, int novalue, int adronly, int size)
/*
 *      general expression evaluation. returns the addressing mode
 *      of the result.
 */
{
				AMODE    *ap1, *ap2;
        int             lab0, lab1,i;
				SYM *sp ;
        if( node == 0 )
                {
                DIAG("null node in gen_expr.");
                return 0;
                }
        switch( node->nodetype )
                {
								case en_bits:
												ap1 = gen_expr(node->v.p[0],novalue,adronly,size);
												if (!adronly)
													ap1 = bit_load(ap1,node);
												return ap1;
                case en_cbool:
								case en_cb: 
								case en_cub:
								case en_cw: 
								case en_cuw:
								case en_cl: 
								case en_cul:
								case en_cf: 
								case en_cd: 
								case en_cld: 
								case en_cp:
												ap1 = gen_expr(node->v.p[0],novalue,adronly,natural_size(node));
												if (!adronly)
                					do_extend(ap1,natural_size(node),0);
												return ap1;
                case en_napccon:
                case en_nacon:
												node->v.p[0] = node->v.sp;
                case en_nalabcon:
                case en_labcon:
                        ap1 = temp_data();
                        ap2 = xalloc(sizeof(AMODE));
                        ap2->mode = am_direct;
                        ap2->offset = node;     /* use as constant node */
                        gen_codes(op_lea,4,ap1,ap2);
                        return ap1;             /* return reg */
								case en_rcon: 
								case en_lrcon: 
								case en_fcon:
                        ap1 = xalloc(sizeof(AMODE));
                        ap1->mode = am_immed;
                        ap1->offset = node;
												ap1->length = natural_size(node);
												make_floatconst(ap1);
                        return ap1;
                case en_icon:
								case en_lcon: case en_lucon: case en_iucon: 
								case en_ccon:
                        ap1 = xalloc(sizeof(AMODE));
                        ap1->mode = am_immed;
                        ap1->offset = node;
												ap1->length = natural_size(node);
                        return ap1;
								case en_absacon:
												node->v.i = node->v.sp->value.i;
                        ap1 = temp_data();
                        ap2 = xalloc(sizeof(AMODE));
                        ap2->mode = am_direct;
                        ap2->offset = node;     /* use as constant node */
                        gen_codes(op_lea,4,ap1,ap2);
                        return ap1;             /* return reg */
                case en_autocon:
                case en_autoreg:
                        ap1 = temp_data();
                        ap2 = xalloc(sizeof(AMODE));
				                ap2->mode = am_indisp;
			  				        	ap2->preg = EBP;
													sp = node->v.sp;
													i = sp->value.i;
#ifdef CPLUSPLUS
												if ((currentfunc->value.classdata.cppflags & PF_MEMBER) &&
															!(currentfunc->value.classdata.cppflags & PF_STATIC))
													i += 4;
#endif
      			    				ap2->offset = makenode(en_icon,(char *)i,0);
                        gen_codes(op_lea,4,ap1,ap2);
                        return ap1;             /* return reg */
                case en_bool_ref:
                case en_b_ref:
                case en_w_ref:
                case en_ub_ref:
                case en_uw_ref:
                case en_l_ref:
                case en_ul_ref:
								case en_floatref:
								case en_doubleref:
								case en_longdoubleref:
                        return gen_deref(node,4);
                case en_tempref:
                case en_regref:
                        ap1 = xalloc(sizeof(AMODE));
												ap1->tempflag = 0;
                        if( (node->v.i & 0xff) < 16 )
                                {
                                ap1->mode = am_dreg;
                                ap1->preg = node->v.i & 0xff;
																ap1->tempflag = 1;
                                }
                        else
													if ((node->v.i &0xff) < 32)
                                {
                                ap1->mode = am_dreg;
                                ap1->preg = (node->v.i & 0xff)- 12;
                                }
													else
                                {
                                ap1->mode = am_freg;
                                ap1->preg = (node->v.i & 0xff) - 32;
                                }
                        ap1->tempflag = 0;      /* not a temporary */
												ap1->length = node->v.i >> 8;
                        return ap1;
								case en_asuminus:
                        return gen_asunary(node,novalue,op_neg, op_fchs,size);
                case en_uminus:
                        return gen_unary(node,op_neg, op_fchs,size);
                case en_compl:
                        return gen_unary(node,op_not, op_not,size);
                case en_add:
								case en_addstruc:
                        return gen_binary(node,op_add,op_faddp,op_fadd, op_faddp, op_fadd,size);
                case en_sub:
                        return gen_binary(node,op_sub,op_fsubp,op_fsub,op_fsubrp, op_fsubr,size);
                case en_and:
                        return gen_xbin(node,op_and,op_btr,size);
                case en_or:
                        return gen_xbin(node,op_or,op_bts,size);
				case en_xor:
						return gen_xbin(node,op_xor,op_btc,size);
								case en_pmul:
												return gen_pmul(node,size);
								case en_pdiv:
												return gen_pdiv(node,size);
                case en_mul:
                        return gen_mul(node,op_imul,op_fmulp,op_fmul,op_fmulp,op_fmul,size);
                case en_umul:
                        return gen_mul(node,op_mul,op_fmulp,op_fmul,op_fmulp, op_fmul,size);
                case en_div:
                        return gen_modiv(node,op_idiv,op_fdivp,op_fdiv,op_fdivrp, op_fdivr, 0,size);
                case en_udiv:
                        return gen_modiv(node,op_div,op_fdivp,op_fdiv,op_fdivrp, op_fdivr, 0,size);
                case en_mod:
                        return gen_modiv(node,op_idiv,op_fdivp,op_fdiv,op_fdivrp,op_fdivr, 1,size);
                case en_umod:
                        return gen_modiv(node,op_div,op_fdivp,op_fdiv,op_fdivrp, op_fdivr, 1,size);
                case en_alsh:
                        return gen_shift(node,op_sal,size);
                case en_arsh:
                        return gen_shift(node,op_sar,size);
                case en_lsh:
                        return gen_shift(node,op_shl,size);
                case en_rsh:
                        return gen_shift(node,op_shr,size);
                case en_asadd:
                        return gen_asadd(node,novalue,op_add,op_faddp,op_fadd,op_faddp,op_fadd,size);
                case en_assub:
                        return gen_asadd(node,novalue,op_sub,op_fsubp,op_fsub,op_fsubrp,op_fsubr,size);
                case en_asand:
                        return gen_aslogic(node,novalue,op_and,op_btr,size);
                case en_asor:
                        return gen_aslogic(node,novalue,op_or,op_bts,size);
                case en_asxor:
                        return gen_aslogic(node,novalue,op_xor,op_btc,size);
                case en_aslsh:
                        return gen_asshift(node,novalue,op_shl,size);
                case en_asrsh:
                        return gen_asshift(node,novalue,op_shr,size);
                case en_asalsh:
                        return gen_asshift(node,novalue,op_sal,size);
                case en_asarsh:
                        return gen_asshift(node,novalue,op_sar,size);
                case en_asmul:
                        return gen_asmul(node, novalue,op_imul,op_fmulp,op_fmul, op_fmulp, op_fmul,size);
                case en_asumul:
                        return gen_asmul(node,novalue,op_mul,op_fmulp,op_fmul,op_fmulp, op_fmul,size);
                case en_asdiv:
                        return gen_asmodiv(node,novalue,op_idiv,op_fdivp,op_fdiv,op_fdivrp, op_fdivr, FALSE,size);
                case en_asudiv:
                        return gen_asmodiv(node,novalue,op_div,op_fdivp,op_fdiv,op_fdivrp, op_fdivr, FALSE,size);
                case en_asmod:
                        return gen_asmodiv(node,novalue,op_idiv,op_fdivp,op_fdiv,op_fdivrp, op_fdivr, TRUE,size);
                case en_asumod:
                        return gen_asmodiv(node,novalue,op_div,op_fdivp,op_fdiv,op_fdivrp, op_fdivr, TRUE,size);
                case en_assign:
                        return gen_assign(node,novalue,size);
                case en_refassign:
                        return gen_refassign(node,novalue,size);
                case en_moveblock:
                        return gen_moveblock(node);
                case en_ainc:
                        return gen_aincdec(node,novalue,op_add,size);
                case en_adec:
                        return gen_aincdec(node,novalue,op_sub,size);
                case en_land:   case en_lor:
                case en_eq:     case en_ne:
                case en_lt:     case en_le:
                case en_gt:     case en_ge:
                case en_ult:    case en_ule:
                case en_ugt:    case en_uge:
                case en_not:
												return gen_relat(node);
                case en_cond:
                        return gen_hook(node,size);
                case en_void:
                        gen_void(node->v.p[0]);
                        return gen_expr(node->v.p[1],TRUE,FALSE,natural_size(node));
								case en_pfcall: case en_pfcallb:  case en_pcallblock:
												return gen_pfcall(node,novalue);
								case en_sfcall: case en_sfcallb:  case en_scallblock:
                case en_fcall:  case en_callblock: case en_fcallb:
								case en_intcall: case en_thiscall:
                        return gen_fcall(node,novalue);
                case en_trapcall:
												return gen_tcall(node, novalue);
                default:
                        DIAG("uncoded node in gen_expr.");
                        return 0;
                }
}
AMODE	   *gen_void(ENODE *node)
{
	gen_expr(node,TRUE,FALSE,natural_size(node));
	gen_code(op_void,0,0);
	return 0;
}

int     natural_size(ENODE *node)
/*
 *      return the natural evaluation size of a node.
 */
{       int     siz0, siz1;
        if( node == 0 )
                return 0;
        switch( node->nodetype )
                {
								case en_bits:
												return stdintsize;
                case en_icon:
								case en_lcon:
												return -4; 
								case en_lucon: 
                case en_iucon:
												return 4;
								case en_ccon:                 
												return 4;
								case en_rcon:
								case en_doubleref:
												return 8;
								case en_lrcon:
								case en_longdoubleref:
												return 10;
								case en_fcon:
								case en_floatref:
												return 6;
								case en_trapcall:
                case en_labcon:
                case en_nacon:  case en_autocon:  case en_autoreg:
                case en_napccon:  case en_absacon: case en_nalabcon:
												return stdaddrsize;
								case en_l_ref:
								case en_cl:
												return -4;
								case en_thiscall:
												return natural_size(node->v.p[0]);
								case en_scallblock: case en_sfcallb: case en_sfcall:
								case en_pfcall: case en_pfcallb: /* ignore pascal style now */
								case en_pcallblock:
								case en_fcall: case en_callblock: case en_fcallb:
								case en_intcall:
												return natural_size(node->v.p[1]);
								case en_tempref:
								case en_regref:
												return node->v.i >> 8;
                case en_ul_ref:
								case en_cul:
                        return 4;
								case en_cp:
												return stdaddrsize;
                case en_bool_ref: case en_cbool:
                        return 5;
                case en_ub_ref:
								case en_cub:
												return 1;
                case en_b_ref:
								case en_cb:
                        return -1;
                case en_uw_ref:
								case en_cuw:
												return 2;
                case en_cw:
                case en_w_ref:
                        return -2;
								case en_cd:
												return 8;
								case en_cld:
												return 10;
								case en_cf:
												return 6;
                case en_not:    case en_compl:
                case en_uminus: case en_assign: case en_refassign:
                case en_ainc:   case en_adec: case en_asuminus:
								case en_moveblock: case en_stackblock:
                        return natural_size(node->v.p[0]);
                case en_add:    case en_sub: case en_addstruc:
								case en_umul:		case en_udiv:	case en_umod: case en_pmul:
                case en_mul:    case en_div:  case en_pdiv:
                case en_mod:    case en_and:
                case en_or:     case en_xor:
								case en_asalsh: case en_asarsh: case en_alsh: case en_arsh:
                case en_lsh:    case en_rsh:
                case en_eq:     case en_ne:
                case en_lt:     case en_le:
                case en_gt:     case en_ge:
								case en_ugt: case en_uge: case en_ult: case en_ule:
                case en_land:   case en_lor:
                case en_asadd:  case en_assub:
                case en_asmul:  case en_asdiv:
                case en_asmod:  case en_asand:
								case en_asumod: case en_asudiv: case en_asumul:
                case en_asor:   case en_aslsh:  case en_asxor:
                case en_asrsh:
                        siz0 = natural_size(node->v.p[0]);
                        siz1 = natural_size(node->v.p[1]);
                        if( chksize(siz1,siz0))
                                return siz1;
                        else
                                return siz0;
                case en_void:   case en_cond:
                        return natural_size(node->v.p[1]);
                default:
                        DIAG("natural size error.");
                        break;
                }
        return 0;
}

/*
 * subroutine evaluates a node determining how to test it for
 * non-zero
 */
static int defcond(ENODE *node) 
{
  AMODE *ap1;
	int rv = 0;
	if (natural_size(node) > 5) {
  	ap1 = gen_expr(node,FALSE,FALSE,4);
	 	do_extend(ap1,10,F_FREG | F_VOL);
		gen_codef(op_fldz,0,0);
		gen_codef(op_fcompp,0,0);
		if (regs[0])
			gen_push(EAX,am_dreg,0);
	  gen_codefs(op_fstsw,2,makedreg(EAX),0);
		gen_codef(op_fwait,0,0);
		gen_code(op_sahf,0,0);
		if (regs[0])
			gen_pop(EAX,am_dreg,0);
	}
	else {
		if (isbit(node)) {
  		ap1 = gen_expr(node,FALSE,TRUE,0);
			if (node->bits == 1) {
				rv = 1;
				gen_codes(op_bt,4,ap1,make_immed(node->startbit));
			} else
				gen_code(op_test,ap1,make_immed(bittab[node->bits-1]<<node->startbit));
		}
		else {
  		ap1 = gen_expr(node,FALSE,FALSE,0);
			gen_code(op_cmp,ap1,make_immed(0));
		}
	}
  freeop(ap1);
	return rv;
}
static AMODE *truerelat(ENODE *node)
{       AMODE    *ap1;
        if( node == 0 )
                return 0;
        switch( node->nodetype )
                {
                case en_eq:
                        ap1 = gen_compare(node,op_sete,op_sete,op_sete,op_sete,0);
                        break;
                case en_ne:
                        ap1 = gen_compare(node,op_setne,op_setne,op_setne,op_setne,0);
                        break;
                case en_lt:
                        ap1 = gen_compare(node,op_setl,op_setge,op_seta,op_setbe,0);
                        break;
                case en_le:
                        ap1 = gen_compare(node,op_setle,op_setg,op_setnc,op_setb,0);
                        break;
                case en_gt:
                        ap1 = gen_compare(node,op_setg,op_setle,op_setb,op_setnc,0);
                        break;
                case en_ge:
                        ap1 = gen_compare(node,op_setge,op_setl,op_setbe,op_seta,0);
                        break;
                case en_ult:
                        ap1 = gen_compare(node,op_setb,op_setnc,op_seta,op_setbe,0);
                        break;
                case en_ule:
                        ap1 = gen_compare(node,op_setbe,op_seta,op_setnc,op_setb,0);
                        break;
                case en_ugt:
                        ap1 = gen_compare(node,op_seta,op_setbe,op_setb,op_setnc,0);
                        break;
                case en_uge:
                        ap1 = gen_compare(node,op_setnc,op_setb,op_setbe,op_seta,0);
                        break;
                case en_not:
												if (isintconst(node->nodetype)) {
													ap1 = gen_expr(node,FALSE,FALSE,0);
													ap1->offset->v.i = !ap1->offset->v.i;
													break;
												}
												if (defcond(node->v.p[0])) {
													ap1 = temp_data();
													gen_codes(op_setnc,1,ap1,0);
												} else {
													ap1 = temp_data();
													gen_codes(op_sete,1,ap1,0);
												} 
                        break;
                default:
												DIAG("True-relat error");
                        break;
                }
  ap1->length = 1;
	return ap1;
}
static int complex_relat(ENODE *node)
{
				if (!node)
					return 0;
        switch( node->nodetype )
                {
								case en_bits:
								case en_l_ref:
								case en_cl:
                case en_ul_ref:
								case en_cul:
								case en_cp:
                case en_ub_ref:
								case en_cub:
                case en_bool_ref: case en_cbool:
                case en_b_ref:
                case en_cb:
                case en_uw_ref:
								case en_cuw:
                case en_cw:
                case en_w_ref:
								case en_cd:
								case en_cld:
								case en_cf:
                case en_uminus: 
                case en_ainc:   case en_adec: 
								case en_moveblock: case en_stackblock:
												return complex_relat(node->v.p[0]);
                case en_icon:
								case en_lcon: 
								case en_lucon: 
								case en_iucon: 
								case en_ccon:                 
								case en_rcon:
								case en_doubleref:
								case en_lrcon:
								case en_longdoubleref:
								case en_fcon:
								case en_floatref:
								case en_absacon:
								case en_trapcall:
                case en_labcon:
                case en_nacon:  case en_autocon:  case en_autoreg:
                case en_napccon: case en_nalabcon:
								case en_tempref:
								case en_regref:
												return 0;
                case en_eq:     case en_ne:
                case en_lt:     case en_le:
                case en_gt:     case en_ge:
								case en_ugt: case en_uge: case en_ult: case en_ule:
                case en_land:   case en_lor:
                case en_not:    case en_compl:
												return 1;
								case en_div: case en_udiv: case en_pdiv: case en_mod: case en_umod:
								case en_assign: case en_refassign:
								case en_asuminus:
                case en_add:    case en_sub: case en_addstruc:
								case en_umul:		case en_pmul:
                case en_mul:		case en_and:
                case en_or:     case en_xor:
								case en_asalsh: case en_asarsh: case en_alsh: case en_arsh:
                case en_lsh:    case en_rsh:
                case en_asadd:  case en_assub:
                case en_asmul:  case en_asdiv:
                case en_asmod:  case en_asand:
								case en_asumod: case en_asudiv: case en_asumul:
                case en_asor:   case en_aslsh:  case en_asxor:
                case en_asrsh:
												return complex_relat(node->v.p[0]) || complex_relat(node->v.p[1]);
                case en_void:   case en_cond:
												return complex_relat(node->v.p[1]);
								case en_sfcall: case en_sfcallb: case en_scallblock:
								case en_pfcall: case en_pfcallb: case en_thiscall:
								case en_fcall: case en_intcall: case en_callblock: case en_fcallb:
								case en_pcallblock:
												return 0;
                default:
                        DIAG("error in complex_relat routine.");
												return 1;
                }
}
AMODE *gen_relat(ENODE *node)
{
	long lab1;
	AMODE *ap1;
	int size = natural_size(node);
	if (size > 5)
		size = 4;
	if (node->nodetype != en_land && node->nodetype != en_lor &&
			!complex_relat(node->v.p[0]) && !complex_relat(node->v.p[1])) {
		ap1 = truerelat(node);
	}
	else {
	  lab1 = nextlabel++;
	  truejp(node,lab1);
	  ap1 = temp_data();
		if (ap1->mode != am_dreg) {
			DIAG("gen_relat: No free temp regs");
		}
		else {
	  	gen_codes(op_sub,4,ap1,ap1);
gen_comment("; TEST EAX,IMMED (branches around next four bytes of code)\n");
			gen_codes(op_genword,0,make_immed(0xa9),0);
			gen_label(lab1);
			gen_codes(op_sub,4,ap1,ap1);
			gen_codes(op_inc,4,ap1,0);
			gen_codes(op_nop,0,0,0);
			ap1->length = size;
		}
	}
  return ap1;
}
AMODE *gen_compare(ENODE *node, int btype1, int btype2, int fbtype1, int fbtype2,int label)
/*
 *      generate code to do a comparison of the two operands of
 *      node.
 */
{       AMODE    *ap1, *ap2;
				int btype = btype1;
				/* can't use PREFER here because evaluation ordering is 
				 * strict... we won't be using sets unless we have already
				 * qualified this as a very short sequence that can be fully
	       * evaluated
				 */
				if (natural_size(node) > 5) {
					resolve_binary(node,&ap1,&ap2,natural_size(node));
					do_extend(ap1,10,F_FREG | F_VOL);
					do_extend(ap2,10,F_FREG | F_VOL);
					gen_codef(op_fcompp,0,0);
					if (regs[0])
						gen_push(EAX,am_dreg,0);
					gen_codefs(op_fstsw,2,makedreg(EAX),0);
					gen_codef(op_fwait,0,0);
					gen_code(op_sahf,0,0);
					if (regs[0])
						gen_pop(EAX,am_dreg,0);
					freeop(ap1);
					freeop(ap2);
					if (label) {
							gen_branch(fbtype1,make_label(label));
					}
					else {
							ap1 = make_stack(0);
							ap1->length = 1;
							gen_code(fbtype1,ap1,0);
					}
					return ap1;

				}
				if ((isbit(node->v.p[0]) || isbit(node->v.p[1])) && (btype1 == op_je || btype1 == op_jne || btype1 == op_sete || btype1 == op_setne)) {
					ENODE *xnode;
					if (isbit(node->v.p[0])) {
  					ap2 = gen_expr(node->v.p[1],FALSE,FALSE,4);
						noids(ap2);
  					ap1 = gen_expr(xnode = node->v.p[0],FALSE,TRUE,4);
					} else {
  					ap2 = gen_expr(node->v.p[0],FALSE,FALSE,4);
						noids(ap2);
  					ap1 = gen_expr(xnode = node->v.p[1],FALSE,TRUE,4);
					}
					if (ap2->mode == am_immed) {
						if (xnode->bits == 1) {
							if (!(ap2->offset->v.i & 1)) {
								gen_codes(op_bt,4,ap1,make_immed(xnode->startbit));
								switch (btype1) {
									case op_je:
										btype = op_jnc;
										break;
									case op_jne:
										btype = op_jb;
										break;
									case op_sete:
										btype = op_setnc;
										break;
									case op_setne:
										btype = op_setb;
										break;
								}
							} else {
								gen_codes(op_bt,4,ap1,make_immed(xnode->startbit));
								switch (btype1) {
									case op_je:
										btype = op_jb;
										break;
									case op_jne:
										btype = op_jnc;
										break;
									case op_sete:
										btype = op_setb;
										break;
									case op_setne:
										btype = op_setnc;
										break;
								}
							}
						} 
						else goto join;
					}
					else {
join:
						ap1=bit_load(ap1,xnode);
						goto join2;
					}
				}
				else {
					resolve_binary(node,&ap1,&ap2,0);
join2:
					if (is_memory(ap1) && is_memory(ap2)) {
						do_extend(ap1,ap2->length,F_DREG | F_VOL);
						gen_code(op_cmp,ap1,ap2);
					}
					else {
						if (ap1->mode == am_immed) {
							btype = btype2;
							do_extend(ap2,4,F_DREG);
							gen_code(op_cmp,ap2,ap1);
						}
						else
							gen_code(op_cmp,ap1,ap2);
					}
				}
				freeop(ap1);
				freeop(ap2);
				if (label) {
						gen_branch(btype,make_label(label));
				}
				else {
						ap1 = temp_data();
						ap1->length = 1;
						gen_code(btype,ap1,0);
				}
				return ap1;
}

void truejp(ENODE *node, int label)
/*
 *      generate a jump to label if the node passed evaluates to
 *      a true condition.
 */
{
        int             lab0;
        if( node == 0 )
                return;
        switch( node->nodetype )
                {
                case en_eq:
                        gen_compare(node,op_je,op_je,op_je,op_je,label);
                        break;
                case en_ne:
                        gen_compare(node,op_jne,op_jne,op_jne,op_jne,label);
                        break;
                case en_lt:
                        gen_compare(node,op_jl,op_jge,op_ja,op_jbe,label);
                        break;
                case en_le:
                        gen_compare(node,op_jle,op_jg,op_jnc,op_jb,label);
                        break;
                case en_gt:
                        gen_compare(node,op_jg,op_jle,op_jb,op_jnc,label);
                        break;
                case en_ge:
                        gen_compare(node,op_jge,op_jl,op_jbe,op_ja,label);
                        break;
                case en_ult:
                        gen_compare(node,op_jb,op_jnc,op_ja,op_jbe,label);
                        break;
                case en_ule:
                        gen_compare(node,op_jbe,op_ja,op_jnc,op_jb,label);
                        break;
                case en_ugt:
                        gen_compare(node,op_ja,op_jbe,op_jb,op_jnc,label);
                        break;
                case en_uge:
                        gen_compare(node,op_jnc,op_jb,op_jbe,op_ja,label);
                        break;
                case en_land:
                        lab0 = nextlabel++;
                        falsejp(node->v.p[0],lab0);
                        truejp(node->v.p[1],label);
                        gen_label(lab0);
                        break;
                case en_lor:
                        truejp(node->v.p[0],label);
                        truejp(node->v.p[1],label);
                        break;
                case en_not:
                        falsejp(node->v.p[0],label);
                        break;
                default:
												if (isintconst(node->nodetype)) {
													if (node->v.i != 0) 
                        		gen_code(op_jmp,make_label(label),0);
													break;
												}
			 									if (defcond(node))
                        	gen_branch(op_jc,make_label(label));
												else
                        	gen_branch(op_jne,make_label(label));
                        break;
                }
}

void falsejp(ENODE *node, int label)
/*
 *      generate code to execute a jump to label if the expression
 *      passed is false.
 */
{       
        int             lab0;
        if( node == 0 )
                return;
        switch( node->nodetype )
                {
                case en_eq:
                        gen_compare(node,op_jne,op_jne,op_jne,op_jne,label);
                        break;
                case en_ne:
                        gen_compare(node,op_je,op_je,op_je,op_je,label);
                        break;
                case en_lt:
                        gen_compare(node,op_jge,op_jl,op_jbe,op_ja,label);
                        break;
                case en_le:
                        gen_compare(node,op_jg,op_jle,op_jb,op_jnc,label);
                        break;
                case en_gt:
                        gen_compare(node,op_jle,op_jg,op_jnc,op_jb,label);
                        break;
                case en_ge:
                        gen_compare(node,op_jl,op_jge,op_ja,op_jbe,label);
                        break;
                case en_ult:
                        gen_compare(node,op_jnc,op_jb,op_jbe,op_ja,label);
                        break;
                case en_ule:
                        gen_compare(node,op_ja,op_jbe,op_jb,op_jnc,label);
                        break;
                case en_ugt:
                        gen_compare(node,op_jbe,op_ja,op_jnc,op_jb,label);
                        break;
                case en_uge:
                        gen_compare(node,op_jb,op_jnc,op_ja,op_jbe,label);
                        break;
                case en_land:
                        falsejp(node->v.p[0],label);
                        falsejp(node->v.p[1],label);
                        break;
                case en_lor:
                        lab0 = nextlabel++;
                        truejp(node->v.p[0],lab0);
                        falsejp(node->v.p[1],label);
                        gen_label(lab0);
                        break;
                case en_not:
                        truejp(node->v.p[0],label);
                        break;
                default:
												if (isintconst(node->nodetype)) {
													if (node->v.i == 0) 
                        		gen_code(op_jmp,make_label(label),0);
													break;
												}
												if (defcond(node))
                        	gen_branch(op_jnc,make_label(label));
												else
                        	gen_branch(op_je,make_label(label));
                        break;
                }
}
