/*
 * C compiler
 * ==========
 *
 * Copyright 1989, 1990, 1991 Christoph van Wuellen.
 * Credits to Matthew Brandt.
 * All commercial rights reserved.
 *
 * This compiler may be redistributed as long there is no
 * commercial interest. The compiler must not be redistributed
 * without its full sources. This notice must stay intact.
 *
 * History:
 *
 * 1989   starting an 68000 C compiler, starting with material
 *        originally by M. Brandt
 * 1990   68000 C compiler further bug fixes
 *        started i386 port (December)
 * 1991   i386 port finished (January)
 *        further corrections in the front end and in the 68000
 *        code generator.
 *        The next port will be a SPARC port
 *
 *
 * 1995   Ivo Oesch, Started in to write a codegenerator for the 
 *        signalprocessor TMS320C30 (December)
 */

/*
 * this module contains all of the code generation routines for evaluating
 * expressions and conditions.
 */

#include "config.h"

#define AVOID_IMMEDIATE_LABELS

#ifdef TMS320C30

#include "chdr.h"
#include "expr.h"
#include "cglbdec.h"
#include "proto.h"
#include "genc30.h"
#include "outproto.h"

#ifdef MULTIPLE_PROCESSORS
#define PRIVATE static
#undef  g_expression
#undef  g_jtrue
#undef  g_jfalse
#undef  g_stack
#undef  g_switch_table
#undef  g_switch_compare
#undef  g_entry
#undef  g_return
#undef  g_epilogue
#undef  allocate
#undef  g_is_bigendian
#undef	g_is_ascending_stack
#else
#define PRIVATE
#endif /* MULTIPLE_PROCESSORS */

#if defined __STDC__ || defined __cplusplus
#define P_(s) s
#else
#define P_(s) ()
#endif

static ADDRESS *as_fcall        P_((const EXPR *, FLAGS, CHAR *, int));
static ADDRESS *func_result     P_((FLAGS, SIZE, TYP *));
static ADDRESS *g_add           P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *g_aincdec       P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *g_asadd         P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *g_asbitfield    P_((const EXPR *, FLAGS, OPCODE, int));
static ADDRESS *g_asdiv         P_((const EXPR *, FLAGS, BOOL));
static ADDRESS *g_aslogic       P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *g_asmul         P_((const EXPR *, FLAGS));
static ADDRESS *g_asshift       P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *g_assign        P_((const EXPR *, FLAGS));
static ADDRESS *g_cast          P_((ADDRESS *, TYP *, TYP *, FLAGS));
static ADDRESS *g_conditionalload       P_((const EXPR *, ADDRESS *, ADDRESS *, int));
static ADDRESS *g_deref         P_((const EXPR *, TYP *, FLAGS));
static ADDRESS *g_div           P_((const EXPR *, FLAGS, BOOL));
static ADDRESS *g_expr          P_((const EXPR *, FLAGS));
static ADDRESS *g_fcall         P_((const EXPR *, FLAGS));
static ADDRESS *g_fderef        P_((const EXPR *, FLAGS));
static ADDRESS *g_hook          P_((const EXPR *, FLAGS));
static ADDRESS *g_index         P_((const EXPR *));
static ADDRESS *g_logic         P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *g_mul           P_((const EXPR *, FLAGS));
static ADDRESS *g_shift         P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *g_unary         P_((const EXPR *, FLAGS, OPCODE));
static ADDRESS *mk_legal        P_((ADDRESS *, FLAGS, SIZE));
static ADDRESS *mk_ilabel       P_((const EXPR *));

static BOOL g_compare           P_((const EXPR *));
static BOOL tst_iconst          P_((const EXPR *));
static EXPR *copy_iexpr         P_((const EXPR *));
static SIZE g_parms             P_((const EXPR *));
static SIZE push_param          P_((const EXPR *));
static void call_library        P_((const CHAR *));
static void g_immed             P_((OPCODE, long, ADDRESS *));
static void g_rotate            P_((ADDRESS *, int, TYP *, int));
static void g_test              P_((const EXPR *));
static void structassign        P_((ADDRESS *, ADDRESS *, SIZE));
static void g_truejp            P_((const EXPR *,  LABEL));
static void g_falsejp           P_((const EXPR *,  LABEL));

PRIVATE BOOL g_is_bigendian	P_((void));
PRIVATE BOOL g_is_ascending_stack	P_((void));
PRIVATE void g_allocate         P_((CSE *));
PRIVATE void g_entry            P_((SIZE));
PRIVATE void g_epilogue         P_((void));
PRIVATE void g_expression       P_((const EXPR *));
PRIVATE void g_jfalse           P_((const EXPR *, LABEL));
PRIVATE void g_jtrue            P_((const EXPR *, LABEL));
PRIVATE void g_return           P_((const EXPR *, TYP *));
PRIVATE void g_stack            P_((SIZE));
PRIVATE void g_switch_compare   P_((const EXPR *, STMT *));
PRIVATE void g_switch_table     P_((const EXPR *,struct swtab *,unsigned long, unsigned long));

#undef P_


#define isanyreg(AP) ((AP->mode==am_areg)||(AP->mode==am_dreg)||(AP->mode==am_ireg)||(AP->mode==am_sreg))         


typedef struct _itree {
        LABEL   label;
        EXPR *  value;
        struct _itree *less;
        struct _itree *more;
} ITREE;

static  int     regs_used = 0;          /* number of register variable allocated */
static  REGMASK restore_mask;           /* register restore mask */
static  REGMASK floatrestore_mask;      /* register restore mask */
static  SIZE    max_stack_adjust = 0L;  /* largest amount stack is altered */
static  REG     frameptr = FRAMEPTR;

/*
**      The following tables specify the alignment requirements of the
**      basic types depending on the processor type.
*/
static  SIZE    alignments_c30[] = {
        1L,             /* bt_void      */
        1L,             /* bt_char      */
        1L,             /* bt_charu     */
        1L,             /* bt_uchar     */
        1L,             /* bt_schar     */
        1L,             /* bt_short     */
        1L,             /* bt_ushort    */
        1L,             /* bt_int16     */
        1L,             /* bt_uint16    */
        1L,             /* bt_int32     */
        1L,             /* bt_uint32    */
        1L,             /* bt_long      */
        1L,             /* bt_ulong     */
        1L,             /* bt_float     */
        1L,             /* bt_double    */
        1L,             /* bt_longdouble */
        1L,             /* bt_pointer16 */
        1L,             /* bt_pointer32 */
        1L,             /* bt_struct    */
        1L,             /* bt_union     */
        1L,             /* bt_func      */
        1L,             /* bt_bitfield  */
        1L,             /* bt_ubitfield */
        1L              /* bt_ellipsis - used for alignment suitable for all types */
};


/* support routines, define as pointers to be sure to have only */
/* one copy in memory of them, else symsearch does not work */
/* properly sinc it compares pointers and not contents of strings */

static CHAR *psup_fpdiv = SUP_FPDIV;
static CHAR *psup_fprem = SUP_FPREM;
static CHAR *psup_ldiv  = SUP_LDIV; 
static CHAR *psup_lrem  = SUP_LREM; 
static CHAR *psup_uldiv = SUP_ULDIV;
static CHAR *psup_ulrem = SUP_ULREM;



#ifndef MULTIPLE_PROCESSORS
PRIVATE SIZE *g_alignments = &alignments_c30[0];
#endif /* MULTIPLE_PROCESSORS */

#define AL_DEFAULT      (g_alignments[bt_ellipsis])

/*****************************************************************************/

BOOL
is_short_float(const RVAL f)
{
    if ((f>MAX_POS_SHORT_FLOAT)||(f<MIN_NEG_SHORT_FLOAT))
	return (FALSE);
    if (f==0.0)
	return (TRUE);
    if ((f>MAX_NEG_SHORT_FLOAT)&&(f<MIN_POS_SHORT_FLOAT))
	return (FALSE);
    /*
     *  perhaps here should follow a check for the precision
     *  sinc shortfloats only offers a precision of 11 Mantissabits
     *  and sometimes we dont want to loose precision of constants
     *  but in the moment I dont know how to do this check
     *
     */ 
     return (TRUE);

}

/*
 * return true if the node passed can be generated as a short offset.
 */
static BOOL
is_short P1(const EXPR *, ep)
{
    return ep->nodetype == en_icon &&
        (ep->v.i >= -32768 && ep->v.i <= 32767);
}

/*
 * tests if it is a labelfree constant node, that means either en_icon, 
 * or sums or differences of such nodes
 */
static BOOL
tst_iconst P1(const EXPR *, ep)
{
    switch (ep->nodetype) {
      case en_icon:
        return TRUE;
      case en_add:
      case en_sub:
        return tst_iconst(ep->v.p[0]) && tst_iconst( ep->v.p[1]);
#ifndef RELOC_BUG
      case en_cast:
#endif
      case en_uminus:
        return tst_iconst(ep->v.p[0]);
    }
    return FALSE;
}

/*****************************************************************************/

static ADDRESS *
mk_amode P1(AMODE, mode)
{
    ADDRESS   *ap;
    ap = (ADDRESS *) xalloc((size_t) sizeof(ADDRESS));
    ap->mode = mode;
    return ap;
}

static ADDRESS *
mk_expr P2(AMODE, mode, EXPR *, ep)
{
    ADDRESS     *ap;
    ap = mk_amode (mode);
    ap->u.offset = ep;
    return ap;
}

/*
 * make an indirect reference to a node
 */
static ADDRESS *
mk_indirect P2(REG, reg, EXPR*, ep)
{
    ADDRESS *ap;
    ap = mk_expr(am_indx, ep);
    ap->preg = reg;
    return ap;
}

static ADDRESS   *
mk_freg P1(int, r)
/*
 * make an address reference to a floatregister.
 */
{
    ADDRESS   *ap;
    if (r <= REG_R7) {
        ap = mk_amode(am_freg);
        ap->preg = (REG)r;
    } else {
        FATAL((__FILE__,"mk_freg", "illegal register %d", r));
    }             
    return ap;
}
/*
 * make a node to reference an immediate value i.
 */
static ADDRESS   *
mk_immed P1(IVAL, i)
{
    if ((i<32767)||(i>-32768))  
	return mk_expr(am_immed, mk_const(i));
    else   
	return mk_ilabel(mk_const(i));
}

/*
 * construct a reference node for an internal label number.
 */
ADDRESS   *
mk_label P1(LABEL, lab)
{
    return mk_expr(am_direct, mk_lcon(lab));
}

ADDRESS   *
mk_Jumplabel P1(LABEL, lab)
{
    return mk_expr(am_immed, mk_lcon(lab));
}

/*
 * make a direct reference to a node.
 */
static ADDRESS   *
mk_immediatelabel P1(EXPR *, ep)
{
    ADDRESS     *ap;
    ap = mk_amode(am_immed);
    ap->u.offset = ep;
    return ap;
}


/*
 * make a node to reference a line number.
 */
static ADDRESS   *
mk_line P1(LINE, i)
{
    return mk_expr(am_line, mk_const((IVAL)i));
}

/*
 * make a node to reference a source line.
 */
static ADDRESS   *
mk_linetxt P1(CHAR *, s)
{
    EXPR        *ep;
    ep = mk_node(en_str, NIL_EXPR, NIL_EXPR, tp_void);
    ep->v.str = s;
    return mk_expr(am_str, ep);
}

/*
 * generate a direct reference to a string label.
 */
static ADDRESS   *
mk_strlab P1(const CHAR*, s)
{
    EXPR        *ep;
    ep = mk_node(en_nacon, NIL_EXPR, NIL_EXPR, tp_void);
    ep->v.str = s;
    return mk_expr(am_immed, ep);
}

/*
 * make a node to reference an immediate value f.
 */
static ADDRESS   *
mk_immedfloat P1(RVAL, f)
{
    ADDRESS     *ap;
    ap = mk_amode(am_immed);
    ap->u.offset = mk_fcon(&f, tp_double);
    return ap;
}

/*
**      make an address reference to a register.
*/
ADDRESS   *
mk_reg P1(REG, r)
{
    ADDRESS   *ap;
    switch (r) {
      case REG_R0:
      case REG_R1:
      case REG_R2:
      case REG_R3:
      case REG_R4:
      case REG_R5:
      case REG_R6:
      case REG_R7:
        ap = mk_amode(am_dreg);
        break;
      case REG_AR0:
      case REG_AR1:
      case REG_AR2:
      case REG_AR3:
      case REG_AR4:
      case REG_AR5:
      case REG_AR6:
      case REG_AR7:
        ap = mk_amode(am_areg);
        break;
      case REG_IR0:
      case REG_IR1:
        ap = mk_amode(am_ireg);
        break;
      case REG_DP:
      case REG_BK:
      case REG_SP:
      case REG_ST:
      case REG_IE:
      case REG_IF:
      case REG_IOF:
      case REG_RS:
      case REG_RE:
      case REG_RC:
        ap = mk_amode(am_sreg);
        break;
      default:
#if 0
        /* next 2 lines must be removed, for test only */       
        ap = mk_amode(am_sreg);
        break;
#endif
        CANNOT_REACH_HERE();
    }
    ap->preg = r;
    return ap;
}

/*
 * returns addressing mode of form offset(frameptr)
 * size is rounded up to AL_DEFAULT
 */
static ADDRESS   *
mk_scratch P1(SIZE, size)
{
    ADDRESS   *ap;
    SIZE        default_alignment = AL_DEFAULT;

    /* round up the request */
    if (size % default_alignment)
        size += default_alignment - (size % default_alignment);

    /* allocate the storage */
    act_scratch += size;

    /*
     * The next statement could be deferred and put into the
     * routine checkstack(), but this is just safer.
     */
    if (act_scratch > max_scratch)
        max_scratch = act_scratch;

     /* do not add act_scratch, because pointer must point to start of
      * scratch area
      */                                 
    ap = mk_indirect(frameptr, mk_const((lc_auto_max + 1)));
    return ap;
}

/*
 * copy an address mode structure.
 */
ADDRESS   *
copy_addr P2(ADDRESS*, ap, AMODE, mode)
{
    ADDRESS     *newap;
    if (ap) {
        newap = (ADDRESS *) xalloc((size_t) sizeof(ADDRESS));
        *newap = *ap;
        newap->mode = mode;
    } else {
        FATAL ((__FILE__,"copy_addr","ap == 0"));
    }
    return newap;
}

/*
 * Generate a label which points to an integer constant.  This routine
 * ensures that only one copy of the constant is generated.
 */
static ADDRESS   *
mk_ilabel P1(const EXPR *, ep)
{
    ITREE       *p, *q;
    int          local_global = global_flag;
    LABEL        lab;
    EXPRTYPE     type; 

    static ITREE *iitree    = NULL; /* Tree for integer constants */
    static ITREE *ilabtree  = NULL; /* Tree for label   constants */
    static ITREE *inatree   = NULL; /* Tree for na      constants */
    static ITREE *iexprtree = NULL; /* Tree for expr    constants */

    lab = nextlabel++;
     
    type = ep->nodetype;
    if (tst_iconst(ep)) {
        type = en_icon;
        FATAL((__FILE__,"mk_ilabel", "icon not supportet now"));
    }                          
    switch (type) {
      case en_icon:
        p = iitree;
        break;
      case en_nacon:
        p = inatree;
        break;
      case en_labcon:
        p = ilabtree;
        break;
      case en_add:
      case en_sub:
      case en_cast:
      case en_uminus:
        p = iexprtree;
        break;
      default:  
        FATAL((__FILE__,"mk_ilabel", "illegal nodetype %d", type));
    }
         /* in the moment we do not try to build a tree */
    for (q = p; p ; p = p->more) {
        if (is_equalnode(p->value, ep)) {
            return mk_label(p->label);
        }
        q = p;
    }
    global_flag = 1;
    p = (ITREE *) xalloc ((int)sizeof(ITREE));
    global_flag = local_global;
    p->label = lab;
    p->value = copy_iexpr(ep);
    p->less = p->more = NULL;
    if (q == NULL) {
        switch (type) {
          case en_icon:
            iitree = p;
            break;
          case en_nacon:
            inatree = p;
            break;
          case en_labcon:
            ilabtree = p;
            break;
          case en_add:
          case en_sub:
          case en_cast:
          case en_uminus:
            iexprtree = p;
            break;
        }
    } /*else if (q->value < val)*/
      /*  q->less = p;          */
    else
        q->more = p;
    put_kseg(alignment(tp_double));
    put_label(lab);
    put_pointer(ep);
    return mk_label(lab);
}

/*****************************************************************************/

static EXPR *
copy_iexpr P1(const EXPR *, ep)
{
    EXPR *newep;
    if (ep == NIL_EXPR)
        return NIL_EXPR;
    newep = copynode(ep);
                 
    switch (ep->nodetype) {
      case en_icon:
      case en_autocon:
      case en_labcon:
      case en_nacon:
        return newep;
      case en_add:
      case en_sub:
        newep->v.p[0] = copy_iexpr(ep->v.p[0]);
        newep->v.p[1] = copy_iexpr(ep->v.p[1]);
        return newep;
      case en_cast:
      case en_uminus:
        newep->v.p[0] = copy_iexpr(ep->v.p[0]);
        return newep;
      default:
        FATAL((__FILE__,"copy_iexpr", "illegal nodetype %d", ep->nodetype));
        return NIL_EXPR;
    }
}

/*
 * generate the mask address structure.
 */
static void
push_registers P2(REGMASK, mask, REGMASK, floatmask)
{
    REG reg;
    for (reg=REG_R0; reg<MAX_REG; reg++) {              
        /* Check which registers are set in mask */
        if ((mask & (1<<reg))!= 0) {    
            g_code(op_push, OP_INT, mk_reg(reg), NIL_ADDRESS);
        }
        if ((floatmask & (1<<reg))!= 0) {    
            g_code(op_pushf, OP_FLOAT, mk_reg(reg), NIL_ADDRESS);
        }
    }
}

/*
 * generate the mask address structure.
 */
static void
pop_registers P2(REGMASK, mask, REGMASK, floatmask)
{
    REG reg;
    for (reg=MAX_REG-1; reg>= REG_R0; reg--) {              
        /* Check which registers are set in mask */
        if ((floatmask & (1<<reg))!= 0) {    
            g_code(op_popf, OP_FLOAT, mk_reg(reg), NIL_ADDRESS);
        }
        if ((mask & (1<<reg))!= 0) {    
            g_code(op_pop, OP_INT, mk_reg(reg), NIL_ADDRESS);
        }
    }
}


/*
 * generate a call to a library routine.
 * it is assumed that lib_name won''t be clobbered
 */
static void
call_library P1(const CHAR*, lib_name)
{
    SYM         *sp;
    sp = internal_symbol(lib_name, tp_void);
    symbol_used(sp);
    g_code(op_call, OP_INT, mk_strlab(sp->name), NIL_ADDRESS);
}

/*
 * mk_legal will coerce the addressing mode in ap1 into a mode that is
 * satisfactory for the flag word.
 */
static ADDRESS   *
mk_legal P3(ADDRESS *, ap, FLAGS, flags, SIZE, size)
{
    ADDRESS     *ap2, *ap3;
    OPCODE       OpSto, OpLd;    

    if (flags & F_NOVALUE) {
        freeop(ap);
        return NIL_ADDRESS;
    }
    switch (size) {
      case OP_INT:
        OpSto = op_sti;
        OpLd = op_ldi;
        break;                               
      case OP_FLOAT:
        OpSto = op_stf;
        OpLd = op_ldf;
        break;                               
    }    

    switch (ap->mode) {
      case am_immed:
        if (flags & F_IMMED)
            return ap;  /* mode ok */
        break;
        case am_areg:
          if (flags & F_AREG && (!(flags & F_VOL) || is_temporary_register(ap->preg)))
              return ap;
          break;
        case am_dreg:
        case am_freg:
          if (flags & F_DFREG && (!(flags & F_VOL) || is_temporary_register(ap->preg)))
              return ap;
          break;
        case am_ind:
        case am_indx:
        case am_indx2:
        case am_direct:
        case am_adec:     
        case am_ainc:
          if (flags & F_MEM)
              return ap;
          break;
    }
    if (flags & (F_XREG != 0)) {
        /* decide, which mode is better */
        if (is_free_data() && (flags & F_DREG)) {
            freeop(ap);         /* maybe we can use it... */
            ap2 = data_register(F_DREG);  /* allocate to dreg */
            g_code(op_ldi, OP_INT, ap, ap2);
            return ap2;
        }
        if (is_free_addr() && (flags & F_AREG)) {
            freeop(ap);         /* maybe we can use it... */
            ap2 = address_register();  /* allocate to dreg */
            g_code(op_ldi, OP_INT, ap, ap2);
            return ap2;
        }
        if (is_free_ireg() && (flags & F_IREG)) {
            freeop(ap);
            ap2 = index_register();
            g_code(op_ldi, OP_INT, ap, ap2);
            return ap2;
        }

    }
    if (flags & F_DREG) {
        freeop(ap);             /* maybe we can use it... */
        ap2 = data_register(F_DREG);      /* allocate to dreg */
        g_code(op_ldi, OP_INT, ap, ap2);
        return ap2;
    }
    if (flags & F_FREG) {
        freeop(ap);             /* maybe we can use it... */
        ap2 = data_register(F_FREG);      /* allocate to dreg */
        g_code(op_ldf, OP_FLOAT, ap, ap2);
        return ap2;
    }
    if (flags & F_AREG) {
        freeop(ap);
        ap2 = address_register();
        g_code(op_ldi, OP_INT, ap, ap2);
        return ap2;
    }
    if (flags & F_IREG) {
        freeop(ap);
        ap2 = index_register();
        g_code(op_ldi, OP_INT, ap, ap2);
        return ap2;
    }
    if (flags & F_MEM) {
        if (ap->mode == am_immed) {
            /* we can not store immediates directly in memory */                  
            freeop(ap);
            ap3 = data_register((size==OP_INT) ? F_DREG : F_FREG);
            g_code(OpLd, size, ap, ap3);
            ap = ap3;                                              
        }                                               
        freeop(ap);
        ap2 = mk_scratch(1L);
        /* copy value into memory */
        g_code(OpSto, size, ap, ap2);
        return ap2;
    }
    FATAL((__FILE__,"mk_legal",""));
    /*NOTREACHED*/
}

/*****************************************************************************/

/*
 * add a conditional branch instruction to the peep list.
 */
static void
g_cbranch P2(OPCODE, op, LABEL, labno)
{
    sync_stack();
    g_code(op, OP_INT, mk_Jumplabel(labno), NIL_ADDRESS);
}

/*
 * add a branch instruction to the peep list.
 */
PRIVATE void
g_branch P1(LABEL, labno)
{
    g_cbranch(op_br, labno);
}

/*
 * add a compiler generated label to the peep list.
 */
PRIVATE void
g_label P1(LABEL, labno)
{
    sync_stack();
    g_code(op_label, OP_INT, mk_label(labno), NIL_ADDRESS);
}

#ifdef DEBUGOPT
/*
 * add a source line number to the peep list.
 */
PRIVATE void
g_line P2(LINE, line, CHAR *, linetxt)
{
    g_code(op_line, OP_INT, mk_line(line), mk_linetxt(linetxt));
}
#endif /*DEBUGOPT*/

/*
 * adjust the stack by "bytes" bytes.
 */
PRIVATE void
g_stack P1(SIZE, bytes)
{
    if (bytes != 0L) {
        /* adjust stack pointer */
        g_code(op_subi, OP_INT, mk_immed(bytes), mk_reg(STACKPTR));
        stack_offset -= bytes;
        if (max_stack_adjust < bytes)
            max_stack_adjust = bytes;
    }
}

/*
 * Generate an instruction which takes an immediate option with optimal
 * (for space) instruction(s).
 */
static void
g_immed P3(OPCODE, op, long, i, ADDRESS *, ap)
{
    if (i >= -32768 && i <= 32767) {    
        g_code(op, OP_INT, mk_immed(i), ap);
        if (!(isanyreg(ap))/*||(ap->mode==am_freg)*/)
            FATAL((__FILE__,"g_immed","immed-to-memory"));   
    } else {                
        /* bigger constants we must build by hand */
        /* in future it could be solved through storing constants */
        /* in datasegment and fetching them from there */                 
        ADDRESS *ap1 = temporary_register(F_XREG);
        g_code(op_ldi, OP_INT, mk_immed((i>>16)&0xffff), ap1);
        g_code(op_lsh, OP_INT, mk_immed(16), ap1);  
        g_code(op_or,  OP_INT, mk_immed(i&0xffff), ap1);       
        g_code(op,     OP_INT, ap1, ap);
        freeop(ap1);
    } 
}

/*----------------------------------------------------------------------------*/


/*
 * generate code to evaluate an index node and return the addressing
 * mode of the result.
 */
static ADDRESS   *
g_index P1(const EXPR *, ep)
{
    ADDRESS     *ap1, *ap2;
    EXPR *      ep0 = ep->v.p[0];
    EXPR *      ep1 = ep->v.p[1];
    /*
    **  Try and ensure that we evaluate address registers first ....
    **  this leads to better code.
    */
    if (ep1->nodetype == en_register && is_address_register(ep1->v.r)) {
        ep0 = ep->v.p[1];
        ep1 = ep->v.p[0];
    }

    if (ep1->nodetype == en_register && ep0->nodetype == en_register) {
        if (is_address_register(ep0->v.r)) {
            /* first node is address register */
            ap1 = g_expr(ep->v.p[0], F_AREG);
            ap1 = copy_addr(ap1, am_indx2);
            ap2 = g_expr(ep->v.p[1], F_IREG );
            ap1->sreg = ap2->preg;
            ap1->u.offset = mk_const(0L);
            return ap1;
        } else if (is_address_register(ep1->v.r)) {
            /* second node is address register */
            ap1 = g_expr(ep->v.p[1], F_AREG);
            ap1 = copy_addr(ap1, am_indx2);     /* (ARx,IRx) */
            ap2 = g_expr(ep->v.p[0], F_IREG);
            ap1->sreg = ap2->preg;
            ap1->u.offset = mk_const(0L);
            return ap1;
        }
    }

    /*
    **   The general case (no register)
    */

    ap1 = g_expr(ep0, (FLAGS)(F_AREG | F_IMMED));
    switch (ap1->mode) {
      case am_areg:
        ap2 = g_expr(ep1, (FLAGS)(F_IREG | F_IMMED));
        validate(ap1);
        break;
      case am_immed:
        ap2 = ap1;
        ap1 = g_expr(ep1, (FLAGS)(F_AREG | F_IMMED));
        validate(ap2);
        break;
      default:
        CANNOT_REACH_HERE();
    }
    /*
    **  possible combinations:
    ** 
    **          F_IMMED + F_IMMED
    **          F_AREG  + F_IMMED
    **          F_AREG  + F_IREG
    **
    **  watch out for:
    **          register(addr) + address_register
    **          register(addr) + data_register
    */

    if (ap1->mode == am_areg) {
        if (!is_temporary_register(ap1->preg)) {
            /* ap1 = tempref address register */
            ap1 = copy_addr(ap1, ap1->mode);
            switch (ap2->mode) {
              case am_ireg:
                /* 0(ARx,IRy) */
                ap1->mode = am_indx2;
                ap1->sreg = ap2->preg;
                ap1->deep = ap2->deep;
                ap1->u.offset = mk_const(0L);
                return ap1;
              case am_immed:
                if (!is_short(ap2->u.offset))
                    /* we want to add to ap1 later... */
                    /* so copy it in scratchregister */                      
                    ap1 = mk_legal(ap1, (FLAGS)(F_AREG | F_VOL), OP_INT);
                break;
            }
        }

        /*
        **      watch out for:
        **               address_register + register(data)
        */

        if  (!is_temporary_register(ap1->preg) && ap2->mode == am_ireg) {
            ap1 = copy_addr(ap1, am_indx2);
            ap1->sreg = ap2->preg;
            ap1->deep = ap2->deep;
            ap1->u.offset = mk_const(0L);
            return ap1;
        }
    }

    if (ap2->mode == am_immed) {
        if (ap1->mode == am_immed) {
            ap1 = copy_addr(ap1, am_direct);
            ap1->u.offset = mk_add(ap1->u.offset, ap2->u.offset);
            return ap1;
        }
        if (is_short(ap2->u.offset)) {
            ap1 = mk_legal(ap1, F_AREG, OP_INT);
            ap1 = copy_addr(ap1, am_indx);
            ap1->u.offset = ap2->u.offset;
            return ap1;
        }
    }
    
    /* ap1 is volatile ... */
    g_code(op_addi, OP_INT, ap2, ap1);/* add left to address reg */
    ap1 = copy_addr(ap1, am_ind);
    freeop(ap2);                /* release any temps in ap2 */
    return ap1;                 /* return indirect */
}

/*
 * return the addressing mode of a dereferenced node.
 */
static ADDRESS *
g_deref P3(const EXPR *, ep, TYP *, tp, FLAGS, flags)
{
    ADDRESS     *ap1;
    /*
     * If a reference to a struct/union is required, return a pointer to the
     * struct instead
     */
    if (tp->type == bt_struct || tp->type == bt_union) {
        return g_expr(ep, F_ALL);
    }
    switch (ep->nodetype) {
      case en_add:
        return g_index(ep);
      case en_autocon:
        if (ep->v.i >= -255 && ep->v.i < 255) {
            ap1 = mk_amode(am_indx);
            ap1->preg = frameptr;    /* frame pointer */
            ap1->u.offset = mk_const(ep->v.i);
        } else if (ep->v.i >= -0x8000L && ep->v.i < 0x7fffL) {
            ap1 = address_register();
            g_code(op_ldi, OP_INT, mk_immed(ep->v.i), ap1);
            g_code(op_addi,OP_INT, mk_reg(frameptr), ap1);
            ap1 = copy_addr(ap1, am_ind);
        } else {
            /* nasty, we have to build big constants this way */                        
            ap1 = address_register();
            g_code(op_ldi, OP_INT, mk_immed(((long) ep->v.i>>16)&0xffffL), ap1);
            g_code(op_lsh, OP_INT, mk_immed(16), ap1);
            g_code(op_or,  OP_INT, mk_immed(((long) ep->v.i)&0xffffL), ap1);                                                
            g_code(op_addi,OP_INT, mk_reg(frameptr), ap1);
            ap1 = copy_addr(ap1, am_ind);
        }
        return ap1;
      case en_ainc:
        /* special TMS320C30 instructions */
         if ( /*(size == 1 || size ==2 || size ==4)*/
         /*   && ep->v.p[1]->v.i == size &&*/
               ep->v.p[1]->v.i < 255   /* if size less than max autoincrement */      
            && ep->v.p[0]->nodetype == en_register
            && is_address_register(ep->v.p[0]->v.r)
            && !(flags & F_USES)) {
            /* (An)+ */
            ap1 = mk_amode(am_ainc);
            ap1->preg = ep->v.p[0]->v.r;
            ap1->u.offset = mk_const((long) ep->v.p[1]->v.i);
            return ap1;
        }
        break;    
      case en_adec:
        /* special TMS320C30 instructions */
        if (  /*(size ==1 || size ==2 || size ==4)*/
          /*  && ep->v.p[1]->v.i == size &&*/
               ep->v.p[1]->v.i < 255 /* if size less than max autoincrement */         
            && ep->v.p[0]->nodetype == en_register
            && is_address_register(ep->v.p[0]->v.r)
            && !(flags & F_USES)) {
            /* (An)- */
            ap1 = mk_amode(am_adec);
            ap1->preg = ep->v.p[0]->v.r;
            ap1->u.offset = mk_const((long) ep->v.p[1]->v.i);
            return ap1;
        }
        break;    
    }
    /*
     * F_DIRECT Supresses to make a doubleindirection for
     * immedietes, sinc it will be converted in an direct-reference       
     */                  
    ap1 = g_expr(ep, (FLAGS)(F_AREG | F_IMMED | F_DIRECT));       /* generate address */
    if (ap1->mode == am_areg) {
        return copy_addr(ap1, am_ind);
    }
    return copy_addr(ap1, am_direct);
}

/*
 * get a bitfield value
 */
static ADDRESS *
g_fderef P2(const EXPR *, ep, FLAGS, flags)
{
    ADDRESS     *ap;

    ap = g_deref(ep->v.p[0], ep->etp, (FLAGS)(F_ALL));
    ap = mk_legal(ap, (FLAGS)(F_XREG | F_VOL), OP_INT);
    g_rotate(ap, (int)ep->v.bit.offset, ep->etp, ep->v.bit.width);
    return mk_legal(ap, flags, OP_INT);
}

/*============================================================================*/

/*
 * generate code to evaluate a unary minus or complement. float: unary minus
 * calls a library function
 */
static ADDRESS *
g_unary P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    ADDRESS     *ap;
    switch (ep->etp->type) {
      case bt_uchar:
      case bt_schar:
      case bt_char:
      case bt_charu:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_pointer32:
        ap = g_expr(ep->v.p[0], (FLAGS)(F_XREG | F_VOL));
        g_code(op, OP_INT, ap, ap);
        return mk_legal(ap, flags, OP_INT);
      case bt_float:
      case bt_double:
      case bt_longdouble:
        if (op == op_negi) {
            ap = g_expr(ep->v.p[0], (FLAGS)(F_FREG | F_VOL));
            g_code(op_negf, OP_FLOAT, ap, ap);
            return mk_legal(ap, flags, OP_FLOAT);
        }
        break;
      default:
        FATAL((__FILE__,"g_unary","illegal type %d or operation %d",ep->etp->type, op));
        break;
    }
    return NIL_ADDRESS;
}

/*============================================================================*/

/*
 * generate an auto increment or decrement node. op should be either op_add
 * (for increment) or op_sub (for decrement).
 */
static ADDRESS *
g_aincdec P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    ADDRESS     *ap1, *ap2, *ap3;
    switch (ep->etp->type) {
      case bt_uchar:
      case bt_schar:
      case bt_char:
      case bt_charu:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_pointer32:
        if (ep->v.p[0]->nodetype == en_fieldref)
            return g_asbitfield(ep, flags, op, TRUE);
        if (flags & F_NOVALUE) {/* dont need result */
            ap1 = g_expr(ep->v.p[0], (FLAGS)(F_ALL));
            /* if ap1 is in register we can modify it directly */         
            if (isanyreg(ap1)) {
                g_code(op,     OP_INT, mk_immed(ep->v.p[1]->v.i), ap1);
            } else {
                /* else we must get ap1 in a register, modify it and store it again */                    
                ap2 = temporary_register(F_XREG);
                g_code(op_ldi, OP_INT, ap1, ap2);
                g_code(op,     OP_INT, mk_immed(ep->v.p[1]->v.i), ap2);
                g_code(op_sti, OP_INT, ap2, ap1);
                freeop(ap2);               
            }                   
            return mk_legal(ap1, flags, OP_INT);
        }
        ap1 = temporary_register(flags);
        ap2 = g_expr(ep->v.p[0], (FLAGS)(F_ALL | F_USES));
        validate(ap1);
        g_code(op_ldi, OP_INT, ap2, ap1);
        /* if ap2 is in register we can modify it directly */     
        if (isanyreg(ap2)) {
            g_code(op,     OP_INT, mk_immed(ep->v.p[1]->v.i), ap2);
        } else {
            /* else we must get ap2 in a register, modify it and store it again */                        
            ap3 = temporary_register(F_XREG);
            g_code(op_ldi, OP_INT, ap1, ap3);
            g_code(op,     OP_INT, mk_immed(ep->v.p[1]->v.i), ap3);
            g_code(op_sti, OP_INT, ap3, ap2);
            freeop(ap3);           
        }                       
        freeop(ap2);
        return mk_legal(ap1, flags, OP_INT);
      case bt_float:
      case bt_double:
      case bt_longdouble:
        if (flags & F_NOVALUE) {/* dont need result */
            ap1 = g_expr(ep->v.p[0], F_ALL);
            /* if ap1 is in register we can modify it directly */         
            if (ap1->mode == am_freg) {
                g_code(op,     OP_FLOAT, mk_immedfloat((double) ep->v.p[1]->v.f), ap1);
            } else {
                /* else we must get ap1 in a register, modify it and store it again */                    
                ap2 = temporary_register(F_FREG);
                g_code(op_ldf, OP_FLOAT, ap1, ap2);
                g_code(op,     OP_FLOAT, mk_immedfloat((double) ep->v.p[1]->v.f), ap2);
                g_code(op_stf, OP_FLOAT, ap2, ap1);
                freeop(ap2);               
            }                   
            return mk_legal(ap1, flags, OP_FLOAT);
        }
        ap1 = temporary_register(F_FREG);
        ap2 = g_expr(ep->v.p[0], (FLAGS)(F_ALL | F_USES));
        validate(ap1);
        g_code(op_ldf, OP_FLOAT, ap2, ap1);
        /* if ap2 is in register we can modify it directly */     
        if (ap2->mode == am_freg) {
            g_code(op,     OP_FLOAT, mk_immedfloat((double) ep->v.p[1]->v.f), ap2);
        } else {
            /* else we must get ap2 in a register, modify it and store it again */                        
            ap3 = temporary_register(F_FREG);
            g_code(op_ldf, OP_FLOAT, ap1, ap3);
            g_code(op,     OP_FLOAT, mk_immedfloat((double) ep->v.p[1]->v.f), ap3);
            g_code(op_stf, OP_FLOAT, ap3, ap2);
            freeop(ap3);           
        }                       
        freeop(ap2);
        return mk_legal(ap1, flags, OP_FLOAT);
      default:
        FATAL((__FILE__,"g_aincdec","illegal type %d or float", ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*============================================================================*/

/*
 * generate code to evaluate a binary node and return the addressing mode of
 * the result.
 */
static ADDRESS *
g_add P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    ADDRESS     *ap1, *ap2;
    switch (ep->etp->type) {
      case bt_uchar:
      case bt_schar:
      case bt_char:
      case bt_charu:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_pointer32:
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_VOL | (flags & (F_XREG))));
        ap2 = g_expr(ep->v.p[1], F_ALL);
        validate(ap1);          /* in case push occurred */
        g_code(op, OP_INT, ap2, ap1);
        freeop(ap2);
        return mk_legal(ap1, flags, OP_INT);
      case bt_longdouble:
      case bt_double:
      case bt_float:
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_VOL | (flags & (F_FREG))));
        ap2 = g_expr(ep->v.p[1], F_ALL);
        validate(ap1);          /* in case push occurred */
        g_code((op==op_addi ? op_addf : op_subf), OP_FLOAT, ap2, ap1);
        freeop(ap2);
        return mk_legal(ap1, flags, OP_FLOAT);
      default:
        FATAL((__FILE__,"g_add","illegal type %d", ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*
 * generate a plus equal or a minus equal node.
 */
static ADDRESS *
g_asadd P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    FLAGS        flagx;
    ADDRESS     *ap1, *ap2, *ap3;
    switch (ep->etp->type) {
      case bt_char:
      case bt_charu:
      case bt_schar:
      case bt_uchar:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_pointer32:
        if (ep->v.p[0]->nodetype == en_fieldref)
            return g_asbitfield(ep, flags, op, 0);
        if (flags & F_NOVALUE)
            flagx = F_ALL;
        else
            flagx = (FLAGS)(F_ALL | F_USES);
        ap1 = g_expr(ep->v.p[0], flagx);

        if (isanyreg(ap1)) {
            ap2 = g_expr(ep->v.p[1],(FLAGS)(F_ALL));
            validate(ap1);
            g_code(op,     OP_INT, ap2, ap1);
        } else {                 
            ap2 = g_expr(ep->v.p[1], (FLAGS)(F_ALL&~F_FREG));
            validate(ap1);
            ap3 = temporary_register(F_XREG);
            g_code(op_ldi, OP_INT, ap1, ap3);       
            g_code(op,     OP_INT, ap2, ap3);
            g_code(op_sti, OP_INT, ap3, ap1);
            freeop(ap3);       
        }
        freeop(ap2);
        return mk_legal(ap1, flags, OP_INT);
      case bt_float:
      case bt_double:
      case bt_longdouble:
        op = (op==op_addi) ? op_addf : op_subf;     
        flagx = (flags & F_NOVALUE) ? F_ALL : (FLAGS)(F_ALL | F_USES);
        ap1 = g_expr(ep->v.p[0], flagx);
        if (ap1->mode == am_freg) {
            ap2 = g_expr(ep->v.p[1], (FLAGS)(F_FREG | F_MEM | F_IMMED));
            validate(ap1);
            g_code(op,     OP_FLOAT, ap2, ap1);
        } else {                 
            ap2 = g_expr(ep->v.p[1], (FLAGS)(F_FREG | F_MEM | F_IMMED));
            validate(ap1);
            ap3 = temporary_register(F_FREG);
            g_code(op_ldf, OP_FLOAT, ap1, ap3);       
            g_code(op,     OP_FLOAT, ap2, ap3);
            g_code(op_stf, OP_FLOAT, ap3, ap1);
            freeop(ap3);     
        }
        freeop(ap2);
        return mk_legal(ap1, flags, OP_FLOAT);
      default:
        FATAL((__FILE__,"asadd","illegal type %d",ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*============================================================================*/

/*
 * generate code to evaluate a restricted binary node and return the
 * addressing mode of the result.
 */
static ADDRESS *
g_logic P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    ADDRESS     *ap1, *ap2;
    /* try to deliver the result where it is needed */   
    if ((flags & F_XREG) != F_NONE) {            
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_VOL | (flags & F_XREG)));
    } else {            
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_VOL | F_XREG));
    }
    ap2 = g_expr(ep->v.p[1], F_ALL);
    validate(ap1);              /* in case push occurred */
    g_code(op, OP_INT, ap2, ap1);
    freeop(ap2);
    return mk_legal(ap1, flags, OP_INT);
}

/*
 * generate a &= or a |= node.
 */
static ADDRESS *
g_aslogic P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    FLAGS        flagx;
    ADDRESS     *ap1, *ap2;
    if (ep->v.p[0]->nodetype == en_fieldref)
        return g_asbitfield(ep, flags, op, 0);
    flagx = (flags & F_NOVALUE) ? F_ALL : (FLAGS)(F_ALL | F_USES);
    ap1 = g_expr(ep->v.p[0], flagx);
    ap2 = g_expr(ep->v.p[1], F_XREG);
    validate(ap1);
    if (isanyreg(ap1)) {
        g_code(op,     OP_INT, ap2, ap1);
    } else {
        ap2 = mk_legal(ap2, (FLAGS)(F_VOL | F_XREG), OP_INT);                                
        g_code(op,     OP_INT, ap1, ap2);
        g_code(op_sti, OP_INT, ap2, ap1);   
    }
    freeop(ap2);
    return mk_legal(ap1, flags, OP_INT);
}

/*============================================================================*/

/*
 * generate code to evaluate a shift node and return the address mode of the
 * result.
 * Note: right shifts will have already been converted to negative left
 * shifts.
 */
static ADDRESS *
g_shift P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    ADDRESS     *ap1, *ap2;
  
    ap1 = g_expr(ep->v.p[0], (FLAGS)(F_XREG | F_VOL));
    ap2 = g_expr(ep->v.p[1], F_ALL);

    validate(ap1);
    g_code(op, OP_INT, ap2, ap1);
    freeop(ap2);

    return mk_legal(ap1, flags, OP_INT);
}

/*
 * generate shift equals operators.
 */
static ADDRESS *
g_asshift P3(const EXPR *, ep, FLAGS, flags, OPCODE, op)
{
    FLAGS        flagx;
    ADDRESS     *ap1, *ap2, *ap3;
    switch (ep->etp->type) {
      case bt_uchar:
      case bt_schar:
      case bt_char:
      case bt_charu:
      case bt_ushort:
      case bt_short:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_ulong:
      case bt_long:
        if (ep->v.p[0]->nodetype == en_fieldref)
            return g_asbitfield(ep, flags, op, FALSE);
        flagx = (flags & F_NOVALUE) ? F_ALL : (FLAGS)(F_ALL | F_USES);
        ap1 = g_expr(ep->v.p[0], flagx);
        ap2 = g_expr(ep->v.p[1], (FLAGS)(F_ALL&~F_FREG));
        validate(ap1);
        if (isanyreg(ap1)) {
            g_code(op, OP_INT, ap2, ap1);
        } else {
            ap3 = temporary_register(F_XREG);
            g_code(op_ldi, OP_INT, ap1, ap3);
            g_code(op, OP_INT, ap2, ap3);
            g_code(op_sti, OP_INT, ap3, ap1);
            freeop(ap3);
        }
        freeop(ap2);
        return mk_legal(ap1, flags, OP_INT);
      default:
        FATAL((__FILE__,"g_asshift","illegal type %d", ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*============================================================================*/

/*
 * generate code to evaluate a divide operator
 */
static ADDRESS *
g_div P3(const EXPR *, ep, FLAGS, flags, BOOL, mod)
{
    ADDRESS *ap1, *ap2;          
    switch (ep->etp->type) {
      case bt_char:
      case bt_charu:
      case bt_schar:
      case bt_uchar:
      case bt_short:
      case bt_int16:
      case bt_ushort:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
        temp_inv();
        ap1 = g_expr(ep->v.p[0], F_ALL);
        ap2 = g_expr(ep->v.p[1], F_ALL);
        validate(ap1);
        freeop(ap2);
        freeop(ap1);                      
        if (ap2->preg == REG_R0) {
            /* bad luck, parameter is using space for ap1 */
            if (ap1->preg == REG_R1) {
                /* its worse, a2 is in r0 and a1 is in r1 */
                g_code(op_ldi, OP_INT, ap2, mk_reg(REG_AR0));
                g_code(op_ldi, OP_INT, ap1, mk_reg(REG_R0));                  
                g_code(op_ldi, OP_INT, mk_reg(REG_AR0), mk_reg(REG_R0));
            } else {
                g_code(op_ldi, OP_INT, ap2, mk_reg(REG_R1));
                g_code(op_ldi, OP_INT, ap1, mk_reg(REG_R0));                  
            }                                                             
        } else {
            g_code(op_ldi, OP_INT, ap1, mk_reg(REG_R0));            
            g_code(op_ldi, OP_INT, ap2, mk_reg(REG_R1));
        }                                                               
        call_library(mod ? psup_lrem : psup_ldiv);
        return func_result(flags, 0L, ep->etp);
      case bt_float:
      case bt_double:
      case bt_longdouble:
        temp_inv();
        ap1 = g_expr(ep->v.p[0], F_ALL);
        ap2 = g_expr(ep->v.p[1], F_ALL);
        validate(ap1);
        freeop(ap2);
        freeop(ap1);                      
        if (ap2->preg == REG_R0) {
            /* bad luck, parameter is using space for ap1 */
            if (ap1->preg == REG_R1) {
                /* its worse, a2 is in r0 and a1 is in r1 */
                /* could be solved with parallel addressing */                      
                g_code(op_ldf, OP_INT, ap2, mk_reg(REG_R2));
                g_code(op_ldf, OP_INT, ap1, mk_reg(REG_R0));                  
                g_code(op_ldf, OP_INT, mk_reg(REG_R2), mk_reg(REG_R0));
            } else {
                g_code(op_ldf, OP_INT, ap2, mk_reg(REG_R1));
                g_code(op_ldf, OP_INT, ap1, mk_reg(REG_R0));                  
            }                                                             
        } else {
            g_code(op_ldf, OP_INT, ap1, mk_reg(REG_R0));            
            g_code(op_ldf, OP_INT, ap2, mk_reg(REG_R1));
        }                                                               
        call_library(mod ? psup_lrem : psup_fpdiv);
        return func_result(flags, 0L, ep->etp);
      default:
        FATAL((__FILE__,"g_div","%d: illegal type %d", mod, ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*
 * generate /= and %= nodes.
 */
static ADDRESS *
g_asdiv P3(const EXPR *, ep, FLAGS, flags, BOOL, mod)
{
    switch (ep->etp->type) {
      case bt_char:
      case bt_schar:
      case bt_charu:
      case bt_uchar:
      case bt_short:
      case bt_int16:
      case bt_ushort:
      case bt_uint16:
      case bt_int32:
      case bt_long:
        if (mod) {
            return as_fcall(ep, flags, psup_ulrem, OP_INT);
        } else {
            return as_fcall(ep, flags, psup_uldiv, OP_INT);
        }
      case bt_uint32:
      case bt_ulong:
      case bt_pointer32:
        if (mod) {
            return as_fcall(ep, flags, psup_ulrem, OP_INT);
        } else {
            return as_fcall(ep, flags, psup_uldiv, OP_INT);
        }
      case bt_float:
      case bt_double:
      case bt_longdouble:
        if (mod) {
            return as_fcall(ep, flags, psup_fprem, OP_FLOAT);
        } else {
            return as_fcall(ep, flags, psup_fpdiv, OP_FLOAT);
        }
      default:
        FATAL((__FILE__,"asdiv","%d: illegal type %d", mod, ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*============================================================================*/

static ADDRESS *
g_mul P2(const EXPR *, ep, FLAGS, flags)
{
    ADDRESS     *ap1, *ap2;
    switch (ep->etp->type) {
      case bt_char:
      case bt_schar:
      case bt_charu:
      case bt_uchar:
      case bt_short:
      case bt_int16:
      case bt_ushort:
      case bt_uint16:
      case bt_uint32:
      case bt_ulong:
      case bt_pointer32:
      case bt_long:
      case bt_int32:
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_XREG | F_VOL));
        ap2 = g_expr(ep->v.p[1], F_ALL );
        validate(ap1);
        g_code(op_mpyi, OP_INT, ap2, ap1);
        freeop(ap2);
        return mk_legal(ap1, flags, OP_INT);
      case bt_float:
      case bt_double:
      case bt_longdouble:
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_FREG | F_VOL));
        ap2 = g_expr(ep->v.p[1], F_ALL );
        validate(ap1);
        g_code(op_mpyf, OP_FLOAT, ap2, ap1);
        freeop(ap2);
        return mk_legal(ap1, flags, OP_INT);
      default:
        FATAL((__FILE__,"g_mul","illegal type %d",ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*
 * generate a *= node.
 */
static ADDRESS *
g_asmul P2(const EXPR *, ep, FLAGS, flags)
{
    ADDRESS     *ap1, *ap2, *ap3;
    switch (ep->etp->type) {
      case bt_char:
      case bt_schar:
      case bt_charu:
      case bt_uchar:
      case bt_short:
      case bt_int16:
      case bt_ushort:
      case bt_uint16:
      case bt_int32:
      case bt_long:
      case bt_uint32:
      case bt_ulong:
      case bt_pointer32:
        if (ep->v.p[0]->nodetype == en_fieldref)
            return g_asbitfield(ep, flags, op_mpyi, FALSE);
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_ALL | F_USES));
        ap2 = g_expr(ep->v.p[1], (FLAGS)(F_ALL&~F_FREG));
        if (isanyreg(ap1)) {
            validate(ap1);
            g_code(op_mpyi, OP_INT, ap2, ap1);
            freeop(ap2);
        } else {
            ap3 = temporary_register(F_XREG);
            validate(ap1);
            g_code(op_ldi,  OP_INT, ap1, ap3);
            g_code(op_mpyi, OP_INT, ap2, ap3);
            g_code(op_sti,  OP_INT, ap3, ap1);
            freeop(ap3);
            freeop(ap2);
        }
        return mk_legal(ap1, flags, OP_INT);
      case bt_float:
      case bt_double:
      case bt_longdouble:
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_FREG | F_MEM | F_IMMED | F_USES));
        ap2 = g_expr(ep->v.p[1], (FLAGS)(F_FREG | F_MEM | F_IMMED));
        if ((ap1->mode != am_freg)) {
            ap3 = data_register(F_FREG);
            validate(ap1);
            g_code(op_ldf,  OP_FLOAT, ap1, ap3);
            g_code(op_mpyf, OP_FLOAT, ap2, ap3);
            g_code(op_stf,  OP_FLOAT, ap3, ap1);
            freeop(ap3);
            freeop(ap2);
        } else {
            validate(ap1);
            g_code(op_mpyf, OP_FLOAT, ap2, ap1);
            freeop(ap2);
        }
        return mk_legal(ap1, flags, OP_FLOAT);
      default:
        FATAL((__FILE__,"asmul","illegal type %d",ep->etp->type));
        break;
    }
    return NIL_ADDRESS;
}

/*============================================================================*/

/*
 * generate code to evaluate a condition operator node (?:)
 */
static ADDRESS *
g_hook P2(const EXPR *, ep, FLAGS, flags)
{
    ADDRESS     *ap1, *ap2;
    EXPRTYPE     typ1, typ2; 
    FLAGS        flagx;
    BOOL         result_is_void = FALSE;
    int          type = OP_INT;  
    SIZE         offset;
    LABEL        false_label = nextlabel++;
    LABEL        end_label = nextlabel++;

    switch (ep->etp->type) {
      case bt_void:
        result_is_void = TRUE;
        flagx = (FLAGS)(F_ALL | F_NOVALUE);
        break;
      case bt_float:
      case bt_double:
      case bt_longdouble:
        flagx = (FLAGS)(F_FREG | F_VOL);
        type = OP_FLOAT;
        break;            
      default:
        flagx = (FLAGS)(((flags & (F_DREG | F_AREG)) == F_AREG) ?
                    F_AREG | F_VOL : F_DREG | F_VOL);
        break;
    }

    typ1 = ep->v.p[1]->v.p[0]->nodetype;
    typ2 = ep->v.p[1]->v.p[1]->nodetype;
    if (  (  (typ1 == en_icon)||(typ1 == en_autocon)
           ||(typ1 == en_fcon)||(typ1 == en_register)) 
        &&(  (typ2 == en_icon)||(typ2 == en_autocon)
           ||(typ2 == en_fcon)||(typ2 == en_register))
        &&(ep->v.p[0]->nodetype != en_land)
        &&(ep->v.p[0]->nodetype != en_lor)) {             
        
        /* Sequence of ap1 and ap2 is important, validate is used in */
        /* routine g_conditionalload                                 */
  
        ap1 = g_expr(ep->v.p[1]->v.p[0], F_ALL);
        ap2 = g_expr(ep->v.p[1]->v.p[1], F_ALL);
        if (  ((ap1->mode == am_dreg)&&is_temporary_register(ap1->preg))
            ||((ap2->mode == am_dreg)&&is_temporary_register(ap2->preg))) {
            FATAL((__FILE__,"g_hook","INCONSISTENCY Dreg"));
        }                                 
        ap2 = g_conditionalload(ep->v.p[0],ap1, ap2, type);
    } else {
        temp_inv(); /* I do not think I can avoid that */
        offset = stack_offset;
        stack_offset = 0L;

        /* all scratch registers are void */

        g_falsejp(ep->v.p[0], false_label);
        ep = ep->v.p[1];

        /* all scratch registers are void */

        ap1 = g_expr(ep->v.p[0], flagx);
        freeop(ap1);

        /* all scratch registers are void */

        g_branch(end_label);
        g_label(false_label);

        ap2 = g_expr(ep->v.p[1], flagx);
        if (!result_is_void && !is_equal_address(ap1,ap2))
            FATAL((__FILE__,"g_hook","INCONSISTENCY"));

        g_label(end_label);

        g_stack(stack_offset);
        stack_offset = offset;
    }           
    return mk_legal(ap2, flags, type);
}

/*
 * rotate a bitfield into the required position (assumes ap is in a register)
 * Offset determines direction of shift, >= 0 bitfield is isolated and shiftet 
 * to left, signum is maintained through combination lsh <<, ash >>
 *
 * Offset < 0 Bitfield is shifted to right, extrabits are not masked out 
 */
static void
g_rotate P4(ADDRESS *, ap, int, offset, TYP *, tp, int, width)
{
    int          w;
    ADDRESS     *ap1;

    if (offset >= 0) {
        switch (tp->type) {
          case bt_int32:
          case bt_int16:
            /* signed bitfield */
            w = (int)(32-offset-width);
            if (w!=0) {
                ap1 = mk_immed((long)w);
                g_code(op_lsh, OP_INT, ap1, ap);
                freeop(ap1);
            }        
            w = (int)(32-width);
            if (w!=0) {
                ap1 = mk_immed((long)-w);
                g_code(op_ash, OP_INT, ap1, ap);
                freeop(ap1);
            }        
            break;

          default:
            /* unsigned bitfield */
            /* shift has same costs as and, thanks barrelshifter */
            w = (int)(32-offset-width);
            if (w!=0) {
                ap1 = mk_immed((long)w);
                g_code(op_lsh, OP_INT, ap1, ap);
                freeop(ap1);
            }        
            w = (int)(32-width);
            if (w!=0) {
                ap1 = mk_immed((long)-w);
                g_code(op_lsh, OP_INT, ap1, ap);
                freeop(ap1);
            }        
            break;
        }
    } else {
        ap1 = mk_immed((long)w);
        g_code(op_lsh, OP_INT, ap1, ap);
        freeop(ap1);
    }
}


/*
 * generate the code for assign operators in bitfield
 * Swap determines 1 by divide if modulo or div is wished,
 * by ++/-- if posttype or pre-type (returns modified or standardvalue)
 */
static ADDRESS *
g_asbitfield P4(const EXPR *, ep, FLAGS, flags, OPCODE, op, BOOL, swap)
{
    ADDRESS     *ap1, *ap2, *ap3;
    EXPR        *ep1, *lnode = ep->v.p[0];
    int         width = (int)lnode->v.bit.width;
    int         offset = (int)lnode->v.bit.offset;
    UVAL        mask;

    /* Evaluate the address of the LHS */
    ep1 = mk_ref(lnode->v.p[0], tp_pointer);
    ap2 = g_expr(ep1, F_MEM);

    /* Now get the value of the LHS, rotate and mask out unwanted bits */
    ap1 = temporary_register(F_XREG);
    g_code(op_ldi, OP_INT, ap2, ap1);
    g_rotate (ap1, offset, lnode->etp, width);

    /* evaluate the RHS */
    ap3 = g_expr(ep->v.p[1], (FLAGS)(F_XREG | F_IMMED));
    validate(ap1);
    validate(ap2);

    /* now do the operation, masking the result back to the required size */
    switch (op) {
      case op_divs:
      case op_divu:
        FATAL((__FILE__,"g_asbitfield", "div is illegal"));
        break;
      default:
        g_code(op, OP_INT, ap3, ap1);
        break;
    }
    freeop(ap3);
    ap3 = temporary_register(F_XREG);    
    if (width < 16) {            
        mask = bitmask(width);
        g_code(op_ldi, OP_INT, mk_immed(mask), ap3);
    } else {
        g_code(op_ldi, OP_INT, mk_immed(0), ap3);
        g_code(op_not, OP_INT, ap3, NIL_ADDRESS);
        g_code(op_lsh, OP_INT, mk_immed(-(32-width)), ap3);                            
    }                            
    g_code(op_and,  OP_INT, ap3, ap1);
    if (offset != 0) {
        g_code(op_lsh,  OP_INT, mk_immed(offset), ap1);     
        g_code(op_lsh,  OP_INT, mk_immed(offset), ap3);
    }    
    g_code(op_andn, OP_INT, ap3, ap2);
    g_code(op_or, OP_INT, ap1, ap2);
    
    freeop(ap3);
    freeop(ap1);
    freeop(ap2);
         
    /* return a result */
    ap2 = temporary_register(F_XREG);
    g_code(op_ldi, OP_INT, ap1, ap2);
    ap1 = ap2;

    if ((flags & F_NOVALUE) == F_NONE) {
        /* result value needed */
        if (offset != 0) { 
            g_code(op_lsh,  OP_INT, mk_immed(-offset), ap1);
        }  
        if (swap) {
            /* post increment/decrement restore original value */
            switch (op) {
              case op_addi:
                op = op_subi;
                g_code (op, OP_INT, mk_immed(1L), ap1);
                g_immed(op_and, mask, ap1);
                break;
              case op_subi:
                op = op_addi;
                g_code (op, OP_INT, mk_immed(1L), ap1);
                g_immed(op_and, mask, ap1);
                break;
            }
        }
    }
    return mk_legal(ap1, flags, OP_INT);
}


/*
 * assign structure from ap1 to ap2
 * ap1, ap2 are scratch address registers
 */
static void
structassign P3(ADDRESS *, ap1, ADDRESS *, ap2, SIZE, size)
{
    SIZE         loop;
    ADDRESS     *ap3;
    SIZE         i;

    loop = size;
    ap1 = copy_addr(ap1, am_ainc);
    ap1->u.offset = mk_const(1);   
    ap2 = copy_addr(ap2, am_ainc);
    ap2->u.offset = mk_const(1);   
    ap3 = data_register(F_DREG);

    /* Fetch first word to init prallelmode */                           
    g_code(op_ldi, OP_INT, ap1, ap3); 
        
    /* Short loops we do manually */     
    if (loop <= 3) {
        for (i = 1; i <= loop-1; i++) {         
            g_code_parallel(op_ldi_sti, OP_INT, ap1, ap3, NIL_ADDRESS, 
                                                ap3, ap2, NIL_ADDRESS);
        }                           
    } else {
        /* check if we have to save the blockrepeatregisters */   
#if 0
        save_blockrepeat();               /* for dbra */
#endif
        if (loop <= 65535) {    /* single loop */
            g_code(op_rpts, OP_INT, mk_immed(loop-2), NIL_ADDRESS);
            g_code_parallel(op_ldi_sti, OP_INT, ap1, ap3, NIL_ADDRESS, 
                                                ap3, ap2, NIL_ADDRESS);
        } else {                /* extended loop */
            g_immed(op_rpts, loop-2, NIL_ADDRESS);
            g_code_parallel(op_ldi_sti, OP_INT, ap1, ap3, NIL_ADDRESS, 
                                                ap3, ap2, NIL_ADDRESS);
        }
#if 0
        restore_blockrepeat();    
#endif
    }
    /* finish the last cycle */  
    g_code(op_sti, OP_INT, ap3, ap2);
    freeop(ap3);                 
}

/*
 * generate code for an assignment node.
 */
static ADDRESS *
g_assign P2(const EXPR *, ep, FLAGS, flags)
{
    FLAGS        flagx;
    ADDRESS     *ap1, *ap2, *ap3, *ap4; 
    EXPR        *ep1;
    SIZE         size = ep->etp->size;
    SIZE         type = OP_INT;
    UVAL         mask;
    
    flagx = (flags & F_NOVALUE) ? F_ALL : (FLAGS)(F_ALL | F_USES);
    switch (ep->etp->type) {
      case bt_struct:
      case bt_union:
        /*
         * Other parts of this module return a pointer to a struct in a register,
         * not the struct itself
         */
        ap1 = g_expr(ep->v.p[1], (FLAGS)(F_AREG | F_VOL));
        ap2 = g_expr(ep->v.p[0], (FLAGS)(F_AREG | F_VOL));
        validate(ap1);

        /* hacky: save ap1 if needed later, structassign destroys it */
        if (flags & F_NOVALUE) {
            /* no need to save any registers */
            structassign(ap1, ap2, ep->etp->size);
            freeop(ap2);
            freeop(ap1);
            return 0;
        }
        ap3 = address_register();
        g_code(op_ldi, OP_INT, ap1, ap3);
        structassign(ap3, ap2, size);
        freeop(ap3);
        freeop(ap2);
        validate(ap1);
        return mk_legal(ap1, flags, OP_INT);
      default:
        switch (size) {
          case 1:
          case 2:
          case 4:
            switch (ep->v.p[0]->nodetype) {
              case en_fieldref:
                /*
                 * Field assignment
                 */
                /* get the value */
                mask = bitmask(ep->v.p[0]->v.bit.width);
                ap1 = g_expr(ep->v.p[1], (FLAGS)(F_IMMED | F_XREG | F_VOL));
                if (ap1->mode == am_immed) {
                    ap1->u.offset->v.i &= mask;
                    ap3 = mk_immed(ap1->u.offset->v.i << ep->v.p[0]->v.bit.offset);
                } else {
                    g_immed(op_and, mask, ap1);
                    if (flags & F_NOVALUE) {
                        ap3 = ap1;
                    } else {
                        /* result value needed */
                        ap3 = temporary_register(F_XREG);
                        g_code(op_ldi, OP_INT, ap1, ap3);
                    }
                    if (ep->v.p[0]->v.bit.offset != 0) {
                        g_code(op_lsh, OP_INT, mk_immed(ep->v.p[0]->v.bit.offset), ap3);
                    }
                }
                mask <<= ep->v.p[0]->v.bit.offset;
                ep1 = mk_ref(ep->v.p[0]->v.p[0], tp_pointer);
                ap2 = g_expr(ep1, F_MEM);
                validate(ap3);
                ap4 = temporary_register(F_XREG);
                g_code(op_ldi, OP_INT, ap2, ap4);
                g_immed(op_andn, mask, ap4);
                g_code(op_or, OP_INT, ap3, ap4);
                g_code(op_sti, OP_INT, ap4, ap2);
                freeop(ap4);     
                freeop(ap2);
                if (!(flags & F_NOVALUE)) {
                    freeop(ap3);
                    validate(ap1);
                }
                return mk_legal(ap1, flags, OP_INT);

            /*
             * we want to pass the right hand side as the expression value.
             * This can''t be done if the left side is a register variable 
             * on which the right hand side addressing mode depends. But if
             * the left side IS a register variable, it is desirable to pass
             * the left side, so no problem.
             */
              case en_register:
                /* pass the left side as expr. value */
                ap1 = g_expr(ep->v.p[0], flagx);
                ap2 = g_expr(ep->v.p[1], F_ALL);
                validate(ap1);
                switch (ap1->mode) {
                  case am_areg: case am_dreg:
                  case am_ireg: case am_sreg:
                    g_code(op_ldi, OP_INT, ap2, ap1);
                    break;
                  case am_freg:
                    g_code(op_ldf, OP_FLOAT, ap2, ap1);
                    type = OP_FLOAT;                              
                    break;
                  default:
                    switch (ap2->mode) {
                      case am_areg: case am_dreg:
                      case am_ireg: case am_sreg:
                        g_code(op_sti, OP_INT, ap2, ap1);
                        break;
                      case am_freg:
                        g_code(op_stf, OP_FLOAT, ap2, ap1);
                        type = OP_FLOAT;                                  
                        break;
                      default:
                        /* we can not store memory to memory directly*/
                        ap3 = temporary_register(F_XREG);
                        g_code(op_ldi, OP_INT, ap2, ap3);
                        g_code(op_sti, OP_INT, ap3, ap1);
                        freeop(ap3);
                        break;                                  
                    }
                    break;                                      
                }               
                freeop(ap2);
                return mk_legal(ap1, flags, OP_INT);
              default:
                /* pass the right side as expr. value */
                /* normally, this is more efficient */
                ap1 = g_expr(ep->v.p[1], flagx);
                ap2 = g_expr(ep->v.p[0], F_ALL);
                validate(ap1);
                switch (ap2->mode) {
                  case am_areg:
                  case am_dreg:
                  case am_ireg:
                  case am_sreg:
                    g_code(op_ldi, OP_INT, ap1, ap2);
                    break;
                  case am_freg:
                    g_code(op_ldf, OP_FLOAT, ap1, ap2);
                    type = OP_FLOAT;                              
                    break;
                  default:
                    switch (ap1->mode) {
                      case am_areg:
                      case am_dreg:
                      case am_ireg:
                      case am_sreg:
                        g_code(op_sti, OP_INT, ap1, ap2);
                        break;
                      case am_freg:
                        type = OP_FLOAT;                                  
                        g_code(op_stf, OP_FLOAT, ap1, ap2);
                        break;
                      default:
                        /* we can not store memory to memory directly*/
                        ap3 = temporary_register(F_XREG);
                        g_code(op_ldi, OP_INT, ap1, ap3);
                        g_code(op_sti, OP_INT, ap3, ap2);
                        freeop(ap3);
                        break;                                  
                    }
                    break;                                      
                }               
                freeop(ap2);
                return mk_legal(ap1, flags, type);
            }
          default:
            FATAL((__FILE__,"g_assign", "size = %ld", size));
        }
    }
    return NIL_ADDRESS;
}

/*
 * push the operand expression onto the stack. return the number of bytes
 * pushed
 */
static SIZE
push_param P1(const EXPR *, ep)
{
    ADDRESS     *ap, *ap1;
    SIZE         size = ep->etp->size;

    /* pushing of structures and unions */
    switch (ep->etp->type) {
      case bt_struct:
      case bt_union:
        if (is_lvalue(ep)) {
            ep = ep->v.p[0];
        }
        /* all other cases return a pointer to the struct anyway */
        /* allocate stack space */
        ap1 = address_register();
        g_code(op_ldi,  OP_INT, mk_reg(STACKPTR), ap1);
/*
 * Add 1 to pointer sinc sp points allways to top-of-stack 
 * next free location on stack is sp-1                     
 */
        g_code(op_addi, OP_INT, mk_immed(1), ap1);

        g_code(op_addi, OP_INT, mk_immed(size), mk_reg(STACKPTR));
        /*
         * F_VOL was missing in the following line --
         * it took a hard-core debugging session to find this error
         */
        ap = g_expr(ep, (FLAGS)(F_AREG | F_VOL));
        validate(ap1);
        /* now, copy it on stack - the same as structassign */
        structassign(ap, ap1, size);
        freeop(ap);
        freeop(ap1);
        break;
      case bt_float:
      case bt_double:
      case bt_longdouble:                               
        ap = g_expr(ep, F_FREG);
        g_code(op_pushf, OP_INT, ap, NIL_ADDRESS);
        freeop(ap);
        break;
      default:
        ap = g_expr(ep, F_XREG);
        g_code(op_push, OP_INT, ap, NIL_ADDRESS);
        freeop(ap);
        break;
    }
    return size;
}

/*
 * push a list of parameters onto the stack and return the number of
 * bytes that they occupy on the stack.
 */
static SIZE
g_parms P1(const EXPR *, plist)
{
    SIZE         size;
    is_parameter++;
    for (size = 0; plist != 0; plist = plist->v.p[1]) {
        size += push_param(plist->v.p[0]);
    }
    is_parameter--;
    return size;
}

/*
* saves a function call result in D0 it is assumed that flags contain
* either F_DREG or F_AREG return value is the addressing mode of the
* result bytes is the number of bytes to pop off the stack
*
* This routine does not use mk_legal and takes care of the stuff itself.
*/
static ADDRESS *
func_result P3(FLAGS, flags, SIZE, bytes, TYP*, tp)
{
    ADDRESS     *ap;
    stack_offset += bytes;
    if (is_parameter)
        g_stack(bytes);
    if (flags & F_NOVALUE)
        return NIL_ADDRESS;
    switch (tp->type) {
      case bt_float:
      case bt_double:
      case bt_longdouble:
        if (flags & F_FREG) {
          ap = data_register(F_FREG);             
          g_code(op_ldf, OP_FLOAT, mk_freg(RESULT), ap); 
        }
        else {
          FATAL((__FILE__,"func_result","illegal addressing mode"));
        }
        break;   
      default:
        if (flags & F_XREG) {
          ap = temporary_register (flags & F_XREG);
          g_code(op_ldi, OP_INT, mk_reg(RESULT), ap);                                 
        }
        else {
          FATAL((__FILE__,"func_result","illegal addressing mode"));
        }
        break;   
    }
    return ap; 
}

/* assignment operations with library calls */
static ADDRESS *
as_fcall P4(const EXPR *, ep, FLAGS, flags, CHAR *, libnam1, int, type)
{
/*
 * example: libnam1 = ".ldiv"
 */
    ADDRESS     *ap1, *ap2, *ap3, *ap4;
    EXPR        *lnode;
    UVAL         mask;
    int          width, offset;
    OPCODE       OpLd;    

    OpLd  = (type==OP_INT) ? op_ldi : op_ldf;
         
    temp_inv();
    switch (ep->v.p[0]->nodetype) {
      case en_register:
        ap2 = g_expr(ep->v.p[1], F_ALL);
                  
        /* ap1 cannot be destroyed, no problem */
        ap1 = g_expr(ep->v.p[0], F_XREG);
        validate(ap2);
        freeop(ap2);
        /* no problem at all, ap1 can't be in R1 (tempref!)*/     
        g_code(OpLd, type, ap2, mk_reg(REG_R1));                          
        g_code(OpLd, type, ap1, mk_reg(REG_R0));                          
        call_library(libnam1);
        /* ap1 is always valid and not equal to RESULT */
        if (type == OP_INT) {             
            g_code(op_ldi, OP_INT, mk_reg(RESULT), ap1);
        } else {
            g_code(op_ldf, OP_FLOAT, mk_freg(RESULT), ap1);
        }                                 
        break;
      case en_fieldref:
        lnode = ep->v.p[0];
        width = (int)lnode->v.bit.width;
        offset = (int)lnode->v.bit.offset;

        /* evaluate the address of the LHS */
        ap3 = g_deref(lnode->v.p[0], lnode->etp, F_MEM);
        freeop(ap3);
        switch (ap3->mode) {
          case am_indx:  
            ap3->mode = am_areg;
            ap2 = ap3;
            if (!is_temporary_register(ap3->preg)) {
                /* is tempref register, dont overwrite */
                ap2 = address_register();
                g_code(op_ldi, OP_INT, ap3, ap2);
            }
            g_immed(op_addi, ap3->u.offset->v.i, ap2);
            break;                     
          case am_indx2:
            ap3->mode = am_areg;
            ap2 = ap3;
            if (!is_temporary_register(ap3->preg)) {
                /* is tempref register, dont overwrite */
                ap2 = address_register();
                g_code(op_ldi, OP_INT, ap3, ap2);
            }
            g_code(op_addi, OP_INT, mk_reg(ap3->sreg), ap2);
            break;
          case am_ind:
            ap3->mode = am_areg;
            ap2 = ap3;
            break;                                   
          case am_direct:
            ap3 = copy_addr(ap3, am_direct);
            ap2 = ap3;
            break;
            default:
            FATAL((__FILE__,"as_fcall","illegal addressing mode"));
        }

        /* and save this address on the stack so that it can be used later */
        if (ap3->mode != am_direct) {
            /* a constant address must not be saved, it will not change */
            /* during an functionscall :-) */
            g_code(op_push, OP_INT, ap2, NIL_ADDRESS);
            ap2 = copy_addr(ap2, am_ind);
        }
        if (ap2 != ap3) {
            freeop(ap2);
        }
        /* Now get the value of the LHS */
        ap1 = temporary_register(F_XREG);
        g_code(op_ldi, OP_INT, ap2, ap1);

        /* evaluate the RHS and push it onto the stack */
        ap2 = g_expr(ep->v.p[1], F_XREG);
        validate(ap1);
        /* rotate to position, mask out unwanted bits */
        g_rotate (ap1, offset, lnode->etp, width);
        freeop(ap2);
        /* do not freeop(ap1), we use it later to store result in it */
        /* so it doesn't matter if it is overwritten due functinscall*/   

        if (ap2->preg == REG_R0) {
            /* bad luck, parameter is using space for ap1 */
            if (ap1->preg == REG_R1) {
                /* its worse, a2 is in r0 and a1 is in r1 */
                /* could be solved with parallel addressing */                      
                g_code(op_ldi, OP_INT, ap2, mk_reg(REG_R2));
                g_code(op_ldi, OP_INT, ap1, mk_reg(REG_R0));                  
                g_code(op_ldi, OP_INT, mk_reg(REG_R2), mk_reg(REG_R0));               
            } else {
                g_code(op_ldi, OP_INT, ap2, mk_reg(REG_R1));
                g_code(op_ldi, OP_INT, ap1, mk_reg(REG_R0));                  
            }                                                             
        } else {
            g_code(op_ldi, OP_INT, ap1, mk_reg(REG_R0));            
            g_code(op_ldi, OP_INT, ap2, mk_reg(REG_R1));
        }                                                               
        /* now do the operation, masking the result back to the required size */
        call_library(libnam1);

        /* ap1 points still to an register from above */
        g_code(op_ldi, OP_INT, mk_reg(RESULT), ap1);
        mask = bitmask(width);
        g_immed(op_and, mask, ap1);
        if (flags & F_NOVALUE) {
            ap4 = ap1;
        } else {
            /* result value needed */
            ap4 = temporary_register(F_XREG);
            g_code(op_ldi, OP_INT, ap1, ap4);
        }

        /* restore ap3 (ap3 is not used since push, so information*/
        /* contained in it is still valid, only registers are */
        /* no more valid) */
        if (ap3->mode != am_direct) {
            ap3 = address_register();
            g_code(op_pop, OP_INT, ap3, NIL_ADDRESS);
            ap3 = copy_addr(ap3, am_ind);
        }
        ap2 = temporary_register(F_XREG);         
        g_code(op_ldi, OP_INT, ap3, ap2);
        /* rotate result back into position, and store */
        mask <<= offset;
        if (offset != 0) {
            g_code(op_lsh, OP_INT, mk_immed(offset), ap4);
        }
        g_immed(op_andn, mask, ap2);
        g_code(op_or, OP_INT, ap4, ap2);
        g_code(op_sti, OP_INT, ap2, ap3);      
        freeop(ap2);      
        freeop(ap3);
        if (!(flags & F_NOVALUE)) {
            freeop(ap4);
            validate(ap1);
        }
        return mk_legal(ap1, flags, OP_INT);
      default:
        ap2 = g_expr(ep->v.p[1], F_ALL);
        ap1 = g_expr(ep->v.p[0], (FLAGS)(F_ALL | F_USES));
        freeop(ap1);
        switch (ap1->mode) {
          case am_indx:  
            ap1->mode = am_areg;
            ap3 = ap1;
            if (!is_temporary_register(ap1->preg)) {
                /* is tempref register, dont overwrite */
                ap3 = address_register();
                g_code(op_ldi, OP_INT, ap1, ap3);
                freeop(ap3);
            }
            g_immed(op_addi, ap1->u.offset->v.i, ap3);
            break;                     
          case am_indx2:
            ap1->mode = am_areg;
            ap3 = ap1;
            if (!is_temporary_register(ap1->preg)) {
                /* is tempref register, dont overwrite */
                ap3 = address_register();
                g_code(op_ldi, OP_INT, ap1, ap3);
                freeop(ap3);
            }
            g_code(op_addi, OP_INT, mk_reg(ap1->sreg), ap3);
            break;
          case am_ind:
            ap1->mode = am_areg;
            ap3 = ap1;
            break;                                   
          case am_direct:
            ap1 = copy_addr(ap1, am_direct);
            ap3 = ap1;
            break;
          default:
            FATAL((__FILE__,"as_fcall","illegal addressing mode"));
        }
        /* and save this address on the stack so that it can be used later */
        if (ap3->mode != am_direct) {
            /* a constant address must not be saved, it will not change */
            /* during a function call :-) */
            g_code(op_push, OP_INT, ap3, NIL_ADDRESS);
            ap3 = copy_addr(ap3, am_ind);
        }
        validate (ap2);
        freeop(ap2);
          
        /* ap3 is an addressregister, so we cont overwrite it */
        g_code(OpLd, type, ap2, mk_reg(REG_R1));
        g_code(OpLd, type, ap3, mk_reg(REG_R0));                  
        
        /* now do the operation, masking the result back to the required size */
        call_library(libnam1);


        /* restore ap1 */
        if (ap1->mode != am_direct) {
            ap1 = address_register();
            g_code(op_pop, OP_INT, ap1, NIL_ADDRESS);
            ap1 = copy_addr(ap1, am_ind);
        } 
        if (type == OP_INT) {             
            g_code(op_sti, OP_INT, mk_reg(RESULT), ap1);
        } else {
            g_code(op_stf, OP_FLOAT, mk_freg(RESULT), ap1);
        }                                 
        freeop(ap1);      
        break;
    }
    if (flags & F_NOVALUE)
        return NIL_ADDRESS;

    ap1 = temporary_register(flags);
    if (type == OP_INT) {                 
        g_code(op_ldi, OP_INT, mk_reg(RESULT), ap1);
    } else {
        g_code(op_ldf, OP_FLOAT, mk_freg(RESULT), ap1);
    }                             
    return mk_legal(ap1, flags, type);
}

/*
 * generate a function call node and return the address mode of the result.
 */
static ADDRESS *
g_fcall P2(const EXPR *, ep, FLAGS, flags)
{
    ADDRESS     *ap, *ap2;
    EXPR        *ep0 = ep->v.p[0];
    SIZE         size;

    if (!is_parameter && ep->nodetype != en_call) {
        switch (stackopt_option) {

          default:
          case 0:
            /*
            **  no stack optimisation
            */
            g_stack(stack_offset);
            break;

          case 1:
            /*
            **  "Safe" stack optimisation.  Perform a stack optimisation
            **  unless:
            **  1.  The function call is via a variable
            **  2.  The function starts with an underscore character
            **  3.  The alloca() routine is called
            */
            if ((ep0->nodetype != en_nacon) ||
                (ep0->v.str[0] == (CHAR)'_') ||
                (ep0->v.str == alloca_name)) {
                g_stack(stack_offset);
            }
            break ;

          case 2:
            /*
            **  "Forced" stack optimisation.   This will not suppress
            **  the optimisation on encountering calls to functions
            **  whose names begin with underscore.
            */
            if ((ep0->nodetype != en_nacon) ||
                (ep0->v.str == alloca_name)) {
                g_stack(stack_offset);
            }
            break ;

          case 3:
            /*
            **  "Forced" stack optimisation.   This will not suppress
            **  the optimisation on encountering calls to functions
            **  whose names begin with underscore or via a function
            **  variable.
            */
            if ((ep0->nodetype == en_nacon) &&
                (ep0->v.str == alloca_name)) {
                g_stack(stack_offset);
            }
            break ;
        }
    }

    /* push any used addr&data temps */
    temp_inv();
    size = g_parms(ep->v.p[1]);       /* generate parameters */
    /*
     * for functions returning a structure or a union, push a pointer to the
     * return value as additional argument The scratch space will be
     * allocated in the stack frame of the calling function.
     */
    switch (ep->etp->type) {
      case bt_struct:
      case bt_union:
        ap = mk_scratch(ep->etp->size);
        ap = copy_addr(ap, am_areg);
        if (ap->u.offset->v.i != 0L) {
            freeop(ap);                   
            ap2 = temporary_register(F_XREG);
            g_code(op_ldi, OP_INT, ap, ap2);
            g_immed(op_addi, (long)ap->u.offset->v.i, ap2);
            ap = ap2;
        }                                       
        g_code(op_push, OP_INT, ap, NIL_ADDRESS);
        size += tp_pointer->size;
        freeop(ap);
    }
    /* call the function */
    switch (ep0->nodetype) {
      case en_nacon:
      case en_labcon:
        ap = mk_immediatelabel(ep0);
        break;
      default:
        ap = g_expr(ep0, F_XREG);
        freeop(ap);
        break;
    }
    if (ap->mode == am_immed) {
        g_code(op_call, OP_INT, ap, NIL_ADDRESS);
    } else {                                     
        g_code(op_callu, OP_INT, ap, NIL_ADDRESS);
    }           
    return func_result(flags, size, ep->etp);
}

/*
 * generates code for a en_cast node
 *
 */
static ADDRESS *
g_cast P4(ADDRESS *, ap, TYP *, tp1, TYP *, tp2, FLAGS, flags)
{
    ADDRESS     *ap1;

    if (flags & F_NOVALUE) {
        freeop(ap);
        return NIL_ADDRESS;
    }

    if (tp1->type == tp2->type) {
        /*
         * this can happen in with the g_xmul stuff, where a cast from
         * (u)short to long now casts from (u)short to (u)short for an 68000
         * mulu or muls instruction.
         * It is save to cut things short then.
         * It should not happen with types other than (u)short, but
         * it does not harm either.
         */
        if (tp1->type == bt_short || tp1->type == bt_ushort ||
            tp1->type == bt_int16 || tp1->type == bt_uint16)
            return mk_legal(ap, flags, OP_INT);
        else
            eprintf("DEBUG: g_cast: tp1->type == tp2->type\n");
    }

    switch (tp2->type) {
        /* switch: type to cast to */
      case bt_char:
      case bt_charu:
      case bt_schar:
      case bt_uchar:
      case bt_ushort:
      case bt_short:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_pointer32:
        switch (tp1->type) {
          case bt_charu:
          case bt_uchar:
          case bt_char:
          case bt_schar:
          case bt_ushort:
          case bt_uint16:
          case bt_short:
          case bt_int16:
          case bt_int32:
          case bt_uint32:
          case bt_long:
          case bt_ulong:
          case bt_pointer32:
            return mk_legal(ap, flags, OP_INT);
          case bt_float:
          case bt_double:
          case bt_longdouble:
            freeop(ap);
            ap1 = temporary_register(F_XREG);
            g_code(op_fix, OP_INT, ap, ap1);                                         
            ap = ap1;
            return mk_legal(ap, flags, OP_INT);
          default:
            break;
        }
        break;
      case bt_float:
      case bt_double:
      case bt_longdouble:
        switch (tp1->type) {
          case bt_char:
          case bt_charu:
          case bt_schar:
          case bt_uchar:
          case bt_short:
          case bt_ushort:
          case bt_int16:
          case bt_uint16:
          case bt_int32:
          case bt_uint32:
          case bt_long:
          case bt_ulong:
          case bt_pointer32:
            freeop(ap);
            ap1 = data_register(F_FREG);
            g_code(op_float, OP_FLOAT, ap, ap1);
            ap = ap1;
            return mk_legal(ap, flags, OP_FLOAT);
          case bt_float:
          case bt_double:
          case bt_longdouble:
            return mk_legal(ap, flags, OP_FLOAT);
          default:
            break;
        }
        break;
      default:
        break;
    }
    FATAL((__FILE__,"g_cast","illegal combination type1=%d, type2=%d", tp1->type, tp2->type));
    /* NOTREACHED */
}

#ifdef ASM
static ADDRESS *
g_asm P1(const EXPR*, ep)
{
    ADDRESS *ap = mk_expr(am_str, copynode(ep));
    g_code(op_asm, OP_INT, ap, NIL_ADDRESS);
    return NIL_ADDRESS;
}
#endif /* ASM */

/*
 * general expression evaluation. returns the addressing mode of the result.
 */
static ADDRESS *
g_expr P2(const EXPR *, ep, FLAGS, flags)
{
    ADDRESS     *ap1;
    LABEL        lab0, lab1;
    SIZE         size;
    TYP *        tp = ep->etp;
    OPCODE       op;
    if (ep == NIL_EXPR) {
        FATAL((__FILE__,"g_expr","ep == 0"));
    }
    if (tst_const(ep)) {
        /* check if node contains no label */ 
#ifdef AVOID_IMMEDIATE_LABELS                 
        /*
         * F_DIRECT is set if immediate will be converted to direct
         * so we can avoid non necessary doubleindirection              
         */                                      
        if ((flags & F_DIRECT)||tst_iconst(ep)) {     
#endif                          
        /*
         *  am_immediate is only allowed if: 
         *  -Value is short and will not be used as direct (we dont
         *   know content of dp, so directaccess is forbidden)
         *  
         *  -Value is a label or an expression containing a label
         *   and will later be used for directacces (it is assumed
         *   that dp points to the page of labels)         
         *                    
         */

          if (  (is_short(ep)&&((flags & F_DIRECT)==0))
              ||((flags & F_DIRECT)&&(!tst_iconst(ep)))) { 
            ap1 = mk_expr(am_immed, copynode(ep));
          } else {
            if (flags & (F_XREG))  
              ap1 = temporary_register(flags);
            else
              ap1 = temporary_register(F_XREG);
            g_immed(op_ldi, (long)ep->v.i, ap1);    
          }    
          return mk_legal(ap1, flags, OP_INT);
#ifdef AVOID_IMMEDIATE_LABELS                    
        } else {
            ap1 = mk_ilabel(ep);                       
            return mk_legal(ap1, flags, OP_INT);
        }           
#endif                        
                                                 
    }
    size = is_floating_type(tp) ? OP_FLOAT : OP_INT;

    switch (ep->nodetype) {
      case en_autocon:
        ap1 = address_register();
        g_code(op_ldi, OP_INT, mk_immed((long) ep->v.i), ap1);
        g_code(op_addi, OP_INT, mk_reg(frameptr), ap1);
        return mk_legal(ap1, flags, size);
      case en_ref:
        /*
         * g_deref uses flags and size only to test F_USES
         *
         * If the result is not used, autoincrement addressing
         * modes are wrong!
         */
        if (flags & F_NOVALUE) {
            ap1 = g_deref(ep->v.p[0], tp, (FLAGS)(flags | F_USES));
        } else {
            ap1 = g_deref(ep->v.p[0], tp, flags);
        }
        if (is_structure(tp) || is_array_type(tp)) {
            return mk_legal(ap1, flags, OP_INT);
        } else {
            return mk_legal(ap1, flags, size);
        }
      case en_fieldref:
        return g_fderef(ep, flags);
      case en_register:
        ap1 = mk_reg(ep->v.r & (~FLOAT_REG));
        if ((ep->v.r & FLOAT_REG) != 0) {
            ap1->mode = am_freg;
        }
        return mk_legal(ap1, flags, size);
      case en_uminus:
        return g_unary(ep, flags, op_negi);
      case en_compl:
        return g_unary(ep, flags, op_not);
      case en_add:
        return g_add(ep, flags, op_addi);
      case en_sub:
        return g_add(ep, flags, op_subi);
      case en_and:
        return g_logic(ep, flags, op_and);
      case en_or:
        return g_logic(ep, flags, op_or);
      case en_xor:
        return g_logic(ep, flags, op_xor);
      case en_mul:
        return g_mul(ep, flags);
      case en_div:
        return g_div(ep, flags, 0);
      case en_mod:
        return g_div(ep, flags, 1);
      case en_lsh:
        switch (tp->type) {
          case bt_uchar:
          case bt_ushort:
          case bt_uint16:
          case bt_uint32:
          case bt_ulong:
            return g_shift(ep, flags, op_lsh);
          default:
            return g_shift(ep, flags, op_ash);
        }
      case en_asadd:
        return g_asadd(ep, flags, op_addi);
      case en_assub:
        return g_asadd(ep, flags, op_subi);
      case en_asand:
        return g_aslogic(ep, flags, op_and);
      case en_asor:
        return g_aslogic(ep, flags, op_or);
      case en_asxor:
        return g_aslogic(ep, flags, op_xor);
      case en_aslsh:
        switch (tp->type) {
          case bt_uchar:
          case bt_ushort:
          case bt_uint16:
          case bt_uint32:
          case bt_ulong:
            return g_asshift(ep, flags, op_lsh);
          default:
            return g_asshift(ep, flags, op_ash);
        }
      case en_asmul:
        return g_asmul(ep, flags);
      case en_asdiv:
        return g_asdiv(ep, flags, FALSE);
      case en_asmod:
        return g_asdiv(ep, flags, TRUE);
      case en_assign:
        return g_assign(ep, flags);
      case en_ainc:
        return g_aincdec(ep, flags, op_addi);
      case en_adec:
        return g_aincdec(ep, flags, op_subi);
      case en_eq:
        VOIDCAST g_compare(ep);
        op = op_ldieq;
        goto cont1;
      case en_ne:
        VOIDCAST g_compare(ep);
        op = op_ldine;
        goto cont1;
      case en_lt:
        op = g_compare(ep) ? op_ldilo : op_ldilt;
        goto cont1;
      case en_le:
        op = g_compare(ep) ? op_ldils : op_ldile;
        goto cont1;
      case en_gt:
        op = g_compare(ep) ? op_ldihi : op_ldigt;
        goto cont1;
      case en_ge:
        op = g_compare(ep) ? op_ldihs : op_ldige;
        goto cont1;
      case en_test:
        g_test(ep->v.p[0]);
        op = op_ldine;
        goto cont1;
      case en_not:
        g_test(ep->v.p[0]);
        op = op_ldieq;
cont1:
        ap1 = temporary_register(flags);
        g_code(op_ldiu, OP_INT, mk_immed(0L), ap1);
        g_code(op,      OP_INT, mk_immed(1L), ap1);
        return mk_legal(ap1, flags, size);
      case en_land:
      case en_lor:
        lab0 = nextlabel++;
        lab1 = nextlabel++;
        g_falsejp(ep, lab0);
        ap1 = temporary_register(F_XREG);
        g_code(op_ldi, OP_INT, mk_immed(1L), ap1);
        g_branch(lab1);
        g_label(lab0);
        g_code(op_ldi, OP_INT, mk_immed(0L), ap1);
        g_label(lab1);
        return mk_legal(ap1, flags, size);
      case en_cond:
        return g_hook(ep, flags);
      case en_comma:
        freeop(g_expr(ep->v.p[0], (FLAGS)(F_ALL | F_NOVALUE)));
        return g_expr(ep->v.p[1], flags);
      case en_fcall:
        return g_fcall(ep, flags);
      case en_cast:
        return g_cast(g_expr(ep->v.p[0], (FLAGS)(F_ALL | F_USES)),
                      ep->v.p[0]->etp, ep->etp, flags);
      case en_deref:
        /*
         * The cases where this node occurs are handled automatically:
         * g_assign and g_fcall return a pointer to a structure rather than a
         * structure.
         */
        return g_expr(ep->v.p[0], flags);
#ifdef ASM
      case en_str:
        return g_asm(ep);
#endif /* ASM */

      case en_fcon:
        ap1 = mk_expr(am_immed, copynode(ep));
        return mk_legal(ap1, flags, OP_FLOAT);
      default:
        FATAL((__FILE__,"g_expr","uncoded node %d",ep->nodetype));
        /* NOTREACHED */
    }
}

PRIVATE void
g_expression P1(const EXPR*, ep)
{
    initstack();
    if (ep != NIL_EXPR)
        VOIDCAST g_expr(ep, (FLAGS)(F_ALL | F_NOVALUE));
    checkstack();
}


/*
 * generate code to do a comparison of the two operands of node. returns 1 if
 * it was an unsigned comparison
 */
static BOOL
g_compare P1(const EXPR *, ep)
{
    ADDRESS     *ap1, *ap2;
    switch (ep->v.p[0]->etp->type) {
      case bt_uchar:
      case bt_schar:
      case bt_char:
      case bt_charu:
      case bt_ushort:
      case bt_short:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_pointer32:
        ap2 = g_expr(ep->v.p[1], F_ALL);
        ap1 = g_expr(ep->v.p[0], F_XREG );
        validate(ap2);
        g_code(op_cmpi, OP_INT, ap2, ap1);
        freeop(ap1);
        freeop(ap2);
        switch (ep->v.p[0]->etp->type) {
          case bt_char:
          case bt_schar:
          case bt_short:
          case bt_int16:
          case bt_int32:
          case bt_long:
            return FALSE;
        }
        return TRUE;
      case bt_float:
      case bt_double:
      case bt_longdouble:
        ap2 = g_expr(ep->v.p[1], F_ALL);
        ap1 = g_expr(ep->v.p[0], F_FREG);
        validate(ap2);
        g_code(op_cmpf, OP_FLOAT, ap2, ap1);
        freeop(ap1);
        freeop(ap2);
        return FALSE;
    }
    FATAL((__FILE__,"g_compare","illegal type %d",ep->v.p[0]->etp->type));
    /* NOTREACHED */
}


/*
 * Test the expression and set the condition codes accordingly
 */
static void
g_test P1(const EXPR *, ep)
{
    ADDRESS     *ap;
    switch(ep->etp->type) {
      case bt_float:
      case bt_double:
      case bt_longdouble:
        ap = g_expr(ep, F_FREG);
        g_code(op_cmpf, OP_FLOAT, mk_immedfloat(0.0), ap);
        freeop(ap);
        /* note that the condition codes are tested,
           not the return value */
        break;
      default:
        ap = g_expr(ep, (FLAGS)(F_ALL & ~F_IMMED));
        /* tstb allows only [reg | ind | indx2] <-> [reg | ind | indx2] */        
        if ((ap->mode == am_direct)||(ap->mode == am_indx)) {
            ap = mk_legal(ap, F_XREG, OP_INT);
        }                
        if ((ap->mode == am_ind)||(ap->mode == am_indx2)) {
          g_code(op_tstb3, OP_INT, ap, ap); 
        } 
        else {             
          g_code(op_tstb, OP_INT, ap, ap); 
        } 
        freeop(ap);
    }
}

/*
 * generate a jump to label if the node passed evaluates to a true condition.
 */
static void
g_truejp P2(const EXPR *, ep, LABEL, label)
{
    LABEL        lab0;
    OPCODE       op;
    if (ep == NIL_EXPR)
        FATAL((__FILE__,"g_truejp","ep == 0"));
    if (ep->nodetype == en_icon) {
        if (ep->v.i)
            g_branch(label);
        return;
    }
    switch (ep->nodetype) {
      case en_eq:
        VOIDCAST g_compare(ep);
        op = op_beq;
        g_cbranch(op, label);
        break;
      case en_ne:
        VOIDCAST g_compare(ep);
        op = op_bne;
        g_cbranch(op, label);
        break;
      case en_lt:
        op = g_compare(ep) ? op_blo : op_blt;
        g_cbranch(op, label);
        break;
      case en_le:
        op = g_compare(ep) ? op_bls : op_ble;
        g_cbranch(op, label);
        break;
      case en_gt:
        op = g_compare(ep) ? op_bhi : op_bgt;
        g_cbranch(op, label);
        break;
      case en_ge:
        op = g_compare(ep) ? op_bhs : op_bge;
        g_cbranch(op, label);
        break;
      case en_land:
        lab0 = nextlabel++;
        g_falsejp(ep->v.p[0], lab0);
        g_truejp(ep->v.p[1], label);
        g_label(lab0);
        break;
      case en_lor:
        g_truejp(ep->v.p[0], label);
        g_truejp(ep->v.p[1], label);
        break;
      case en_not:
        g_test(ep->v.p[0]);
        g_cbranch(op_beq, label);
        break;
      case en_test:
        g_test(ep->v.p[0]);
        g_cbranch(op_bne, label);
        break;
      case en_call:     /* library routine which sets the flags */
        freeop(g_expr(ep, F_ALL));
        g_cbranch(op_bne, label);
        break;
      default:
        g_test(ep);
        g_cbranch(op_bne, label);
        break;
    }
}

/*
 * generate code to execute a jump to label if the expression passed is
 * false.
 */
static void
g_falsejp P2(const EXPR *, ep, LABEL, label)
{
    LABEL        lab0;
    OPCODE       op;
    if (ep == NIL_EXPR)
        FATAL((__FILE__,"g_falsejp","ep == 0"));
    if (ep->nodetype == en_icon) {
        if (!ep->v.i)
            g_branch(label);
        return;
    }
    switch (ep->nodetype) {
      case en_eq:
        VOIDCAST g_compare(ep);
        op = op_bne;
        g_cbranch(op, label);
        break;
      case en_ne:
        VOIDCAST g_compare(ep);
        op = op_beq;
        g_cbranch(op, label);
        break;
      case en_lt:
        op = g_compare(ep) ? op_bhs : op_bge;
        g_cbranch(op, label);
        break;
      case en_le:
        op = g_compare(ep) ? op_bhi : op_bgt;
        g_cbranch(op, label);
        break;
      case en_gt:
        op = g_compare(ep) ? op_bls : op_ble;
        g_cbranch(op, label);
        break;
      case en_ge:
        op = g_compare(ep) ? op_blo : op_blt;
        g_cbranch(op, label);
        break;
      case en_land:
        g_falsejp(ep->v.p[0], label);
        g_falsejp(ep->v.p[1], label);
        break;
      case en_lor:
        lab0 = nextlabel++;
        g_truejp(ep->v.p[0], lab0);
        g_falsejp(ep->v.p[1], label);
        g_label(lab0);
        break;
      case en_not:
        g_test(ep->v.p[0]);
        g_cbranch(op_bne, label);
        break;
      case en_test:
        g_test(ep->v.p[0]);
        g_cbranch(op_beq, label);
        break;
      case en_call:     /* library routine which sets the flags */
        freeop(g_expr(ep, F_ALL));
        g_cbranch(op_beq, label);
        break;
      default:
        g_test(ep);
        g_cbranch(op_beq, label);
        break;
    }
}

/*
 * generate a conditional load.
 */
PRIVATE ADDRESS*
g_conditionalload P4(const EXPR *, ep, ADDRESS *, truesrc, ADDRESS *, falsesrc, int, type)
{
    OPCODE       op, op2;
    ADDRESS     *ap1;
                 
    if (ep == NIL_EXPR)
        FATAL((__FILE__,"g_conditionalload","ep == 0"));
    if (ep->nodetype == en_icon) {
        op =  (type==OP_INT) ? op_ldi : op_ldf;        
        ap1 = (type==OP_INT) ? temporary_register(F_XREG) : data_register(F_FREG);
        freeop(truesrc);          
        freeop(falsesrc);         
        if (ep->v.i) 
            g_code(op, type, truesrc, ap1);
        else                    
            g_code(op, type, falsesrc, ap1);
        return (ap1);
    }
    switch (ep->nodetype) {
      case en_eq:
        VOIDCAST g_compare(ep);
        op  = (type==OP_INT) ? op_ldieq : op_ldfeq;   
        op2 = (type==OP_INT) ? op_ldine : op_ldfne; 
        goto common;
                            
      case en_ne:
        VOIDCAST g_compare(ep);
        op  = (type==OP_INT) ? op_ldine : op_ldfne;   
        op2 = (type==OP_INT) ? op_ldieq : op_ldfeq;   
        goto common;
                            
      case en_lt:
        if (g_compare(ep)) {
            op  = (type==OP_INT) ? op_ldilo : op_ldflo;         
            op2 = (type==OP_INT) ? op_ldihs : op_ldfhs;       
        } else {                   
            op  = (type==OP_INT) ? op_ldilt : op_ldflt;         
            op2 = (type==OP_INT) ? op_ldige : op_ldfge;       
        }                
        goto common;
                            
      case en_le:
        if (g_compare(ep)) {
            op  = (type==OP_INT) ? op_ldils : op_ldfls;         
            op2 = (type==OP_INT) ? op_ldihi : op_ldfhi;       
        } else {                   
            op  = (type==OP_INT) ? op_ldile : op_ldfle;         
            op2 = (type==OP_INT) ? op_ldigt : op_ldfgt;       
        }                
        goto common;
                            
      case en_gt:
        if (g_compare(ep)) {
            op  = (type==OP_INT) ? op_ldihi : op_ldfhi;       
            op2 = (type==OP_INT) ? op_ldils : op_ldfls;         
        } else {                   
            op  = (type==OP_INT) ? op_ldigt : op_ldfgt;       
            op2 = (type==OP_INT) ? op_ldile : op_ldfle;         
        }                
        goto common;
                            
      case en_ge:
        if (g_compare(ep)) {
            op  = (type==OP_INT) ? op_ldihs : op_ldfhs;       
            op2 = (type==OP_INT) ? op_ldilo : op_ldflo;         
        } else {                   
            op  = (type==OP_INT) ? op_ldige : op_ldfge;       
            op2 = (type==OP_INT) ? op_ldilt : op_ldflt;         
        }       
                  
common: validate (truesrc);
        validate (falsesrc);    
        ap1 = (type==OP_INT) ? temporary_register(F_XREG) : data_register(F_FREG);
        freeop(truesrc);          
        freeop(falsesrc);         
        g_code(op,  type, truesrc, ap1);
        g_code(op2, type, falsesrc, ap1);
        break;
                  
      case en_land:
      case en_lor:
        FATAL((__FILE__,"g_conditionalload","en_land/en_lor"));
        break;
      case en_not:
        ap1 = g_conditionalload(ep->v.p[1], falsesrc, truesrc, type);
        break;
      default:
        g_test(ep);
        validate (truesrc);
        validate (falsesrc);    
        freeop(truesrc);          
        freeop(falsesrc);         
        ap1 = (type==OP_INT) ? temporary_register(F_XREG) : data_register(F_FREG);
        op  = (type==OP_INT) ? op_ldieq : op_ldfeq;   
        op2 = (type==OP_INT) ? op_ldine : op_ldfne;   
        g_code(op,  type, truesrc, ap1);
        g_code(op2, type, falsesrc, ap1);
        break;
    }
    return (ap1);        
}

PRIVATE void
g_jtrue P2(const EXPR*, ep, LABEL, label)
{
    initstack();
    g_truejp(ep, label);
    checkstack();
}

PRIVATE void
g_jfalse P2(const EXPR*, ep, LABEL, label)
{
    initstack();
    g_falsejp(ep, label);
    checkstack();
}

PRIVATE void
g_switch_table P4(const EXPR *, ep, struct swtab*, sw, UVAL, min_caselabel, UVAL, max_caselabel)
{
    ADDRESS     *ap, *ap1, *ap2;

    initstack();
    ap = g_expr(ep, (FLAGS)(F_XREG | F_VOL));
    /*
     * move the interval
     */
    if (min_caselabel != 0) {
        g_code(op_subi, OP_INT, mk_immed(min_caselabel), ap);
        g_code(op_cmpi, OP_INT, mk_immed(max_caselabel - min_caselabel), ap);
    } else {
        g_code(op_cmpi, OP_INT, mk_immed(max_caselabel-min_caselabel), ap);
    }
    g_cbranch(op_bhi, sw->deflab);
    ap1 = address_register();
#ifdef AVOID_IMMEDIATE_LABELS                    
    g_code(op_ldi, OP_INT, mk_ilabel(mk_lcon(sw->tablab)), ap1);
#else         
    g_code(op_ldi, OP_INT, mk_label(sw->tablab), ap1);
#endif                        
    /* jump table contains 4 byte address of case branches */
    g_code(op_addi, OP_INT, ap, ap1);
    freeop(ap1);
    ap1 = copy_addr(ap1, am_ind);
    ap2 = address_register();
    g_code(op_ldi, OP_INT, ap1, ap2);
    sync_stack();
    g_code(op_bu, OP_INT, ap2, NIL_ADDRESS);
    freeop(ap2);
    freeop(ap);
}

/*
 * Generate the body of a switch statement by comparing each case value
 * in turn.   The comparision is infact done by using subtraction as this
 * actually generates more efficient code (and would work best if the
 * labels were sorted!)
 */
PRIVATE void
g_switch_compare P2(const EXPR *, ep, STMT *, stmt)
{
    unsigned long min_value;
    ADDRESS     *ap;
    initstack();
    ap = g_expr(ep, (FLAGS)(F_XREG | F_VOL));
    for (min_value = 0; stmt != 0; stmt = stmt->s1) {
        if (stmt->stype != st_default) {
            g_code(op_subi, OP_INT, mk_immed((long) stmt->v2.i-min_value), ap);
            min_value = stmt->v2.i;
            stmt->v2.l = nextlabel++;
            g_cbranch(op_beq, stmt->v2.l);
        }
    }
    freeop(ap);
}

PRIVATE void
g_entry P1(SIZE, frame_size)
{
#ifdef PROBES
    if (probe_option) {
        SIZE     size = frame_size + max_stack_adjust + 32;
        ADDRESS *ap2, *ap = mk_reg(STACKPTR);
        if (size < 32768l) {
            ap->mode = am_indx;
            ap->u.offset = mk_const (-size);
            g_code(op_tst, OP_INT, ap, NIL_ADDRESS);
        } else {
            ap2 = data_register(F_DREG);
            ap->mode = am_indx2;
            ap->sreg = ap->preg;
            ap->u.offset = mk_const (0L);
            g_code(op_ldi, OP_INT, mk_immed(-size), ap2);
            g_code(op_tst, OP_INT, ap, NIL_ADDRESS);
            freeop(ap2);
        }
    }
#endif /* PROBES */
#ifdef STACK_CHECK
    if (stackcheck_option) {
        SYM     *sp = internal_symbol(SUP_STACKCHECK, NIL_TYP);
        symbol_used(sp);
        g_code(op_push, OP_INT, mk_immed(frame_size + max_stack_adjust), NIL_ADDRESS);
        g_code(op_call, OP_INT, mk_strlab(sp->name), NIL_ADDRESS);
    }
#endif /* STACK_CHECK */
    if (frame_size < 32768L) {
        g_code(op_push, OP_INT, mk_reg(frameptr), NIL_ADDRESS);
        g_code(op_ldi,  OP_INT, mk_reg(STACKPTR), mk_reg(frameptr));
        if (frame_size != 0L) {                 
            g_code(op_addi, OP_INT, mk_immed(frame_size), mk_reg(STACKPTR));
        }
    } else {
        FATAL((__FILE__,"g_entry","framesize > 32768"));
    }

    max_stack_adjust = 0L;
}

PRIVATE void
g_return P2(const EXPR *, stmtexp, TYP *, tp)
{
    EXPR        *ep, *ep1;
    ADDRESS     *ap;
    initstack();
    switch (tp->type) {
      case bt_struct:
      case bt_union:
        uses_structassign = TRUE;
        /* assign structure */
        ep = mk_autocon((SIZE)-2L); 
        ep = mk_ref(ep, tp_pointer);
        ep1 = mk_ref(ep, tp);
        ep1 = mk_node(en_assign, ep1, copynode(stmtexp), tp);
        VOIDCAST g_expr(ep1, (FLAGS)(F_ALL | F_NOVALUE));
        ap = g_expr(ep, F_ALL);
        g_code(op_ldi, OP_INT, ap, mk_reg(RESULT));
        freeop(ap);
        break;

      case bt_longdouble:
      case bt_double:
      case bt_float:
        ap = g_expr(stmtexp, F_ALL);
        g_code(op_ldf, OP_FLOAT, ap, mk_freg(RESULT));
        freeop(ap);
        break;

      case bt_char:
      case bt_charu:
      case bt_uchar:
      case bt_schar:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_pointer32:
        ap = g_expr(stmtexp, F_ALL);
        g_code(op_ldi, OP_INT, ap, mk_reg(RESULT));
        freeop(ap);
        break;
      default:
        FATAL( (__FILE__,"g_return","illegal type %d",tp->type));
    }
}

PRIVATE void
g_epilogue P0(void)
{
    ADDRESS     *ap, *ap2;
    SIZE         stackoffset;

    if (regs_used > 0) {
        if (is_leaf_function) {         
            pop_registers(restore_mask, floatrestore_mask);
        } else {
            if (lc_auto > lc_auto_max)
                lc_auto_max = lc_auto;
            stackoffset = lc_auto_max + max_scratch + regs_used;
            ap = mk_reg(STACKPTR);
            ap2 = mk_reg(REG_AR0);
            g_code(op_ldi,   OP_INT, mk_reg(frameptr), ap2);
            g_immed(op_addi,  stackoffset, ap2);
            g_code(op_ldi,   OP_INT, ap2, ap);
            pop_registers(restore_mask, floatrestore_mask);
        }
    }
    g_code(op_ldi, OP_INT, mk_reg(frameptr), mk_reg(STACKPTR));
    g_code(op_pop, OP_INT, mk_reg(frameptr), NIL_ADDRESS);
    g_code(op_retsu, OP_INT, NIL_ADDRESS, NIL_ADDRESS);
}

/*
 * allocate will allocate registers for the expressions that have a high
 * enough desirability.
 */
PRIVATE void
g_allocate P1(CSE *, olist)
{
    CSE         *csp;
    REG          datareg = REG_R7;
    REG          addreg = (REG)((int)frameptr - 1);
    REGMASK      mask = (REGMASK)0;
    REGMASK      floatmask = (REGMASK)0;  

    regs_used = 0;
    for (csp = olist; csp != NIL_CSE; csp = csp->next) {
        /*
         * If reg_option is not true, the 'desire' value must be at least
         * 5000, which I hope can only be achieved by the 'register' attribute
         */
        if (desire(csp) < 3 || (!reg_option && desire(csp) < 5000)||(is_function_type(csp->exp->etp)))
            csp->reg = NO_REG;
        else if (csp->duses > (csp->uses / (unsigned)3) && !is_temporary_register(addreg)
            /*
             * integer constants may have different types
             */
                 && csp->exp->nodetype != en_icon
            /*
             * the types which are fine in address registers
             * allow only 32-bit integral and signed 16-bit integral types
             */
                 && (csp->exp->etp->type == bt_short ||
                     csp->exp->etp->type == bt_int16 ||
                     csp->exp->etp->type == bt_int32 ||
                     csp->exp->etp->type == bt_uint32 ||
                     csp->exp->etp->type == bt_long ||
                     csp->exp->etp->type == bt_ulong ||
                     csp->exp->etp->type == bt_pointer32)) {
            csp->reg = addreg--;
        } else if (  !is_temporary_register(datareg)
                   &&!is_short(csp->exp)) {
            /*
             * the types which are fine in data registers:
             * allow all types 
             */
            /* Floatnumber need some special handling */
            if(  csp->exp->etp->type == bt_float
               ||csp->exp->etp->type == bt_double
               ||csp->exp->etp->type == bt_longdouble) {                                        
                /*  numbers with bit FLOAT_REG set indicate floatingregisters */
                floatmask |= (1 << datareg);
                csp->reg = FLOAT_REG|datareg--;
                /* regs used must be incremented twice for float    */
                /* sinc pushed floatregisters need 2 words on stack */                                           
                regs_used++;
            } else {                                        
                csp->reg = datareg--;
           }
        } else {
            csp->reg = NO_REG;
        }
        if (csp->reg != NO_REG) {
            regs_used++;
            mask |= (1 << ((~FLOAT_REG)&(csp->reg)));
        }
    }
    if (is_temporary_register(datareg)) {
        /* maybe we can allocate some of the address registers */
        for (csp = olist; csp != 0 && !is_temporary_register(addreg); csp = csp->next) {
            if (csp->reg != NO_REG ||   /* already allocated to a register */
                desire(csp) < 3 ||      /* not desirable in a register */
                (!reg_option && desire(csp) < 5000)
                ||(is_function_type(csp->exp->etp)))
                continue;
            if ((csp->exp->nodetype != en_icon)
                 && (csp->exp->etp->type == bt_short ||
                     csp->exp->etp->type == bt_int16 ||
                     csp->exp->etp->type == bt_int32 ||
                     csp->exp->etp->type == bt_uint32 ||
                     csp->exp->etp->type == bt_long ||
                     csp->exp->etp->type == bt_ulong ||
                     csp->exp->etp->type == bt_pointer32)) {
                csp->reg = addreg--;
                regs_used++;
                mask |= (1 << csp->reg);
            }
        }
    }
    if (mask != 0) {
        push_registers(mask, floatmask);     
    }
    restore_mask = mask;
    floatrestore_mask = floatmask;       
}

PRIVATE void
g_preload P1(CSE *, olist)
{
    CSE *csp;
    EXPR *ep;
    ADDRESS *ap, *ap2;
    for (csp = olist; csp != NIL_CSE; csp = csp->next) {
        if (csp->reg != NO_REG) {       /* see if preload needed */
            ep = csp->exp;
            if (!is_lvalue(ep) || (ep->v.p[0]->v.i < 0)) {
                initstack();
                ap = g_expr(ep, F_ALL);
                if ((csp->reg & FLOAT_REG) != 0) {                                                
                    ap2 = mk_freg((int)((~FLOAT_REG)&(csp->reg)));
                    g_code(op_ldf, OP_FLOAT, ap, ap2);
                } else {                                                  
                    ap2 = mk_reg(csp->reg);
                    g_code(op_ldi, OP_INT, ap, ap2);
                }                               
                freeop(ap);
            }
        }
    }
}


PRIVATE void
g_flush P1(SYM*, sp)
{
    put_literals();
    if (sp) {
        put_cseg(alignment(sp->tp));
        put_name(sp);
    }
    flush_peep();
}

PRIVATE void
g_auto_align P0(void)
{
    SIZE        default_alignment = AL_DEFAULT;

    if (lc_auto_max % default_alignment != 0L)
        lc_auto_max += default_alignment - (lc_auto_max % default_alignment);
}

PRIVATE BOOL
g_is_bigendian P0(void)
{
    return TRUE;
}

/*
 *  Defines if the systemstack is growing from low to high addresses (TRUE)
 *  or if it is going down from high to low addresses (more usual) (FALSE)
 *
 */

PRIVATE BOOL
g_is_ascending_stack P0(void)
{
    return TRUE;
}
       

/*
**      This routine does any code generator specific transformations
**      on the expression tree.
**
**      For example it can replace operator nodes with calls to runtime
**      routines.   This allows the global optimiser to perform optimisations
**      on such calls which wouldn't be possible if the calls were
**      generated in the code generator routines themselves.
*/

PRIVATE EXPR *
g_transform P1(EXPR *,ep)
{
    TYP *tp;
    if (ep == NIL_EXPR)
        return ep;
    tp = ep->etp;
    switch (ep->nodetype) {
      case en_fcon:
        if (!is_short_float(ep->v.f)) {
            ep = mk_lcon(mk_flabel(&(ep->v.f), tp));
            ep = mk_ref(ep, tp);
        }
        return ep;
      case en_icon:
      case en_nacon:
      case en_labcon:
      case en_autocon:
      case en_sym:
      case en_register:
      case en_size:
        break;
      case en_eq:
      case en_ne:
      case en_lt:
      case en_le:
      case en_gt:
      case en_ge:
      case en_add:
      case en_sub:
      case en_and:
      case en_or:
      case en_xor:
      case en_land:
      case en_lor:
      case en_cond:
      case en_comma:
      case en_list:
      case en_asadd:
      case en_assub:
      case en_asor:
      case en_asxor:
      case en_asand:
      case en_fcall:
      case en_call:
      case en_assign:
      case en_ainc:
      case en_adec:
      case en_aslsh:
      case en_asmul:
      case en_asdiv:
      case en_asmod:
      case en_mul:
      case en_div:
      case en_mod:
      case en_lsh:
        ep->v.p[1] = g_transform (ep->v.p[1]);
        /*FALLTHRU*/
      case en_uminus:
      case en_test:
      case en_not:
      case en_compl:
      case en_deref:
      case en_cast:
      case en_ref:
      case en_fieldref:
        ep->v.p[0] = g_transform (ep->v.p[0]);
        break;
      case en_rsh:
        ep->v.p[0] = g_transform (ep->v.p[0]);
        ep->v.p[1] = g_transform (ep->v.p[1]);
        ep->v.p[1] = mk_node(en_uminus, ep->v.p[1], NIL_EXPR, ep->v.p[1]->etp);
        ep->nodetype = en_lsh;
        return ep;
      case en_asrsh:
        ep->v.p[0] = g_transform (ep->v.p[0]);
        ep->v.p[1] = g_transform (ep->v.p[1]);
        ep->v.p[1] = mk_node(en_uminus, ep->v.p[1], NIL_EXPR, ep->v.p[1]->etp);
        ep->nodetype = en_aslsh;
        return ep;
      default:
        CANNOT_REACH_HERE();
        break;
    }
    return ep;
}

/*
**	This routine is called after the global optimizer has done it's
**	work re-organizing the expression tree.  This allows a code
**	generator to make code generator specific changes to the expression
**	tree which will result in better code generation.
*/

PRIVATE EXPR *
g_order P1(EXPR *, ep)
{
    return ep;
}

PRIVATE void
g_initialize P0(void)
{
    tp_short->size =
    tp_ushort->size =
    tp_int->size =
    tp_uint->size = 
    tp_long->size =
    tp_ulong->size =
    tp_float->size = 
    tp_double->size =
    tp_longdouble->size = 1L;
}

#ifdef MULTIPLE_PROCESSORS
struct genfuncs mcc30_funcs  = {
    g_expression,
    g_jtrue,
    g_jfalse,
    g_stack,
    g_switch_table,
    g_switch_compare,
    g_entry,
    g_return,
    g_epilogue,
    g_label,
    g_branch,
#ifdef DEBUGOPT
    g_line,
#endif /*DEBUGOPT*/
    g_allocate,
    g_preload,
    g_flush,
    g_auto_align,
    g_is_bigendian,
    g_is_ascending_stack,
    g_order,
    g_transform,
    g_initialize,
    &alignments_c30[0]
};
#endif /* MULTIPLE_PROCESSORS */
#endif /* MC680X0 */
