/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:f881gen.c 12.0$ */
/* $ACIS:f881gen.c 12.0$ */
/* $Source: /ibm/acis/usr/src/usr.lib/libfp/genfp/RCS/f881gen.c,v $ */

#ifndef lint
static char *rcsid = "$Header:f881gen.c 12.0$";
#endif

#include "fpgen.h"
#include "f881ops.h"
#include "general.h"

/* To debug whole module, compile with -DDEBUG -DALL */
#ifdef ALL
#define DB_ALL			TRUE
#else
#define DB_ALL			FALSE
#endif ALL

/*
 * To debug individual routine, replace DB_ALL by TRUE for that
 * routine and compile the module with -DDEBUG only	
 */
#define DB_DUMP			FALSE		/* dump uif block */
#define DB_MOVEM		DB_ALL		/* debug LOADM and STOREM */
#define DB_FPSCR		DB_ALL		/* debug 881 control reg */

/*
 * The bit pattern of an mc881 'address' is as follows:
 *	1111 1100 00tt ttoo oxxx yyyc cccc ccll
 * where					
 *	tttt:	Transfer Control Field (TCF)
 *		0000	transfer from memory to 881
 *		0111	transfer (to or from) using ZIC length register
 *		1111	transfer from 881 to memory
 *
 *	ooo:	881 operation class (OPCLASS)
 *		000	freg to freg
 *		001	reserved
 *		010	mem to freg
 *		011	freg to mem
 *		100	mem to system control register (SCR)
 *		101	scr to mem
 *		110	move multiple from mem to freg
 *		111	movem freg to mem
 *		
 *	xxx:	Source, could be a freg number or mem operand format
 *		If it is memory operand format, then:
 *		000	Long word integer (1 word)
 *		001	Single precision real (1 word)
 *		010	Extended precision real (not use)
 *		011	Packed decimal real (not use)
 *		100	Word integer (not use)
 *		101	Double precision real (2 words)
 *		110	Byte integer (not use)
 *		111	Undefined
 *
 *	yyy:	Destination, must be a freg
 *		
 *	cccccccc: 881 opcodes			
 *		
 *	ll:	Transfer length
 *		00 -no immediate data (freg op freg)
 *		01 -DMA xfer 1 word (single precision)
 *		10 -DMA xfer 2 words (double precision)
 *		11 -DMA xfer n words (MOVEM, length in ZIC)
 */
#define f881_DMA_ADDR		0xfc000000
#define f881_TCF_M2F		0
#define f881_TCF_F2M		0x003c0000

/* for opclass */
#define f881_F2F		0	
#define f881_M2F		2
#define f881_F2M		3
#define f881_M2SCR		4
#define f881_SCR2M		5
#define f881_MM2FR		6
#define f881_FR2MM		7

/* for len */
#define NO_IMM_DATA		0
#define ONE_WORD		1
#define TWO_WORDS		2
#define N_WORDS			3

#define f881_READ_FPCR		0xfc3ec001
#define f881_WRITE_FPCR		0xfc024001
#define f881_WRITE_ZIC_LENGTH	0xfc1c0003
#define f881_READ_SCR(scr)	(f881_DMA_ADDR | f881_TCF_F2M | \
				(f881_SCR2M << 15) | ((scr) << 12) | ONE_WORD)
#define f881_WRITE_SCR(scr)	(f881_DMA_ADDR | f881_TCF_M2F | \
				(f881_M2SCR << 15) | ((scr) << 12) | ONE_WORD)

#define SET_TCF(opclass)	(((opclass) & 1) ? f881_TCF_F2M : f881_TCF_M2F)
#define f881INST(fopclass,frx,fry,fops)	( \
	f881_DMA_ADDR | SET_TCF(fopclass) | \
	((fopclass) << 15) | ((frx) << 12) | ((fry) << 9) | \
	((fops) << 2) | ((fopclass) ? f881_tbl_ll[(frx)] : 0) )

#ifdef	DEBUG
#define f881inst(fopclass,frx,fry,fops)	(decode_f881(inst.L), \
					f881INST(fopclass,frx,fry,fops))
#else
#define f881inst(fopclass,frx,fry,fops)	f881INST(fopclass,frx,fry,fops)
#endif DEBUG

#define f881_FPIAR		1		/* 881 instruction addr reg */
#define f881_FPSR		2		/* 881 status register */
#define f881_FPCR		4		/* 881 control register */
#define f881_PREC_MASK		0xff3f		/* See mc68881 manual */
#define f881_PREC_EXTENDED	(0 << 6)
#define f881_PREC_SINGLE	(1 << 6)
#define f881_PREC_DOUBLE	(2 << 6)
#define f881_ROUND_MASK		0xffcf
#define f881_ROUND_NEAREST	(0 << 4)
#define f881_ROUND_TOZERO	(1 << 4)
#define f881_ROUND_DOWN		(2 << 4)
#define f881_ROUND_UP		(3 << 4)

#define f881_MOVEM_H		0xfc004003
#define PUSH_OPND		FALSE
#define EA_ONLY			TRUE

#define is_double(reg)		((reg) &0xf0)
#define LONG_WAY
#ifdef LONG_WAY
/*
 * This is the correct way to handle a single precision value
 * left in a floating point register.
 */
#define force_single_prec(gi,resprec,opprec)	( \
		(gi->fp_state->env_prec == SINGLE_PREC) && \
		(resprec == SGLETYPE) && (opprec != SGLETYPE))
#else
/*
 * However, if we leave the value in extended precision, e.g.: more
 * precise than it ought to be, it still works.  Since the 881 can
 * handle extended precision values much faster than single precision
 * values, it is best to do it this way.
 */
#define force_single_prec(gi,resprec,opprec)	FALSE
#endif LONG_WAY

#define check_version(x)	(gi->fp_state->firsttime >= (x))

/*
 * Map the 881 source formats to the number of words
 * to be transfered.
 */
static u_long 	f881_tbl_ll[8] = { 1, 1, 0, 0, 0, 2, 0, 0 };

/*
 * Map the RTFL types (INTTYPE, UINTTYPE, SGLETYPE, DBLETYPE) to
 * the 881 source format
 */
static u_long	f881_tbl_src_format[4] = { 0, 0, 1, 5 };

static u_long	f881_tbl_ops[NUM_OPCODES] = {	OP881_ABS,
						OP881_ACOS,
						OP881_ADD,
						OP881_ASIN,
						OP881_ATAN,
						0,		/* ATAN2 */
						OP881_ATANH,
						OP881_CMP,	/* CMP */
						OP881_CMP,	/* CMPT */
						0,		/* RSVD */
						OP881_COS,
						OP881_COSH,
						OP881_DIV,
						OP881_ETOX,
						OP881_ETOXM1,
						0,		/* LOADM */
						OP881_LOG10,
						OP881_LOGNP1,
						OP881_LOGN,
						OP881_LOG2,
						OP881_MOVE,
						OP881_MUL,
						OP881_NEG,
						OP881_REM,
						OP881_INT,
						OP881_SCALE,
						OP881_SIN,
						OP881_SINH,
						OP881_SQRT,
						0,		/* STOREM */
						OP881_SUB,
						OP881_TAN,
						OP881_TANH,
						0,		/* INIT */
						0,		/* WHICH */
						0,		/* TESTROUND */
						0,		/* SETROUND */
						0,		/* TESTFLAG */
						0,		/* SETFLAG */
						0,		/* TESTTRAP */
						0,		/* SETTRAP */
						0,		/* CLRFLAG */
						0		/* CLRTRAP */
						};


extern void dump();
extern int _num_1s_tbl[];

/*
 * _f881_prec
 * Pick the appropriate precision.
 *
 */
_f881_prec(gi, prec1, prec2)
global_info_type *gi;
int	prec1, prec2;		/* two argument precisions	*/
{
	if (	(prec1 == DBLETYPE) ||
		(prec2 == DBLETYPE) ||
		(gi->fp_state->env_prec != SINGLE_PREC))
		return(DBLETYPE);
	else
		return(SGLETYPE);
} /* end _f881_prec */


/*
 * int _ea_gr ---------------------------------------------------
 *								
 * OpType 0x:  General regs which HOLD RESULT ONLY.  Generate	
 * codes which ALLOCATE room on stack to hold the result	
 * and put the effective address in rx.			
 *								
 * Input values:						
 * rx		= reg number to hold the effective address	
 * rr		= reg number(s) to (later) hold the result	
 * gi->stack_offset = current stack offset			
 *								
 * Return values:						
 * gi->stack_offset	= updated stack offset			
 * newcode		= code generated			
 * int _ea_gr		= number of half-words generated	
 */
static int
_ea_gr(newcode,gi,rx,rr)
u_short			*newcode;
global_info_type	*gi;
int			rx,rr;
{
int			offset, i = 0;

if (is_double(rr)) {			/* 2 words */
	offset = gi->stack_offset - 4;
	gi->stack_offset -= 8;
} else {				/* 1 word */
	offset = gi->stack_offset;
	gi->stack_offset -= 4;
}
newcode[i++] = RTi_CAL(rx,SP);		/* get the addr into rx */
newcode[i++] = offset;
debugf(DB_ALL, printf("\tcal\tr%d,%d(SP)\t# _ea_gr, addr\n", rx, offset));

return (i);
} /* end _ea_gr */


/*
 * int _ea_gr2stk -----------------------------------------------
 *								
 * OpType 0x:  General regs CONTAINING the operand.  Generate	
 * codes which PUSH the operand, rr, onto stack and calculate
 * its address into rx.					
 *								
 * Input values:						
 * rx		= reg number to hold the effective address	
 * rr		= reg number(s) containing the operand		
 * gi->stack_offset = current stack offset			
 *								
 * Return values:						
 * gi->stack_offset	= updated stack offset			
 * newcode		= code generated			
 * int _ea_gr2stk	= number of half-words generated	
 */
static int
_ea_gr2stk(newcode,gi,rx,rr)
u_short			*newcode;
global_info_type	*gi;
int			rx,rr;
{
int			i = 0;

i += _ea_gr(&newcode[i],gi,rx,rr);	/* get effective address into rx */

if (is_double(rr)) {					/* 2 words */
	newcode[i++] = RTi_STS((rr >> 4),0,rx);		/* Hi word */
	newcode[i++] = RTi_STS((rr & 0x0f),1,rx);	/* Lo word */
	debugf(DB_ALL,
	printf("\tsts\tr%d,0(r%d)\t# gr2stk, hi word\n", (rr >> 4), rx));
	debugf(DB_ALL,
	printf("\tsts\tr%d,4(r%d)\t# gr2stk, lo word\n", (rr & 0x0f), rx));
} else {						/* 1 word */
	newcode[i++] = RTi_STS(rr,0,rx);
	debugf(DB_ALL,
	printf("\tsts\tr%d,0(r%d)\t# gr2stk, lo word\n", rr, rx));
}

if (is_scratchg(gi, (rr & 0x0f)))
	fp_free_genr(gi, (rr & 0x0f));
if (is_scratchg(gi, (rr >> 4)))
	fp_free_genr(gi, (rr >> 4));

return (i);
} /* end _ea_gr2stk */


/*
 * int _mv_stk2gr -----------------------------------------------
 *							
 * Move the floating point result stored on the stack, at rx,
 * to the general register(s), rr.			
 *						
 */
static int
_mv_stk2gr(newcode,rx,rr)
u_short			*newcode;
int			rx,rr;
{
int			i = 0;

if (is_double(rr)) {					/* 2 words */
	newcode[i++] = RTi_LS((rr >> 4), 0, rx);	/* get hi word */
	newcode[i++] = RTi_LS((rr & 0x0f), 1, rx);	/* get lo word */
	debugf(DB_ALL,
	printf("\tls\tr%d,0(r%d)\t\t# stk2gr, hi word\n", (rr >> 4), rx));
	debugf(DB_ALL,
	printf("\tls\tr%d,4(r%d)\t\t# stk2gr, lo word\n", (rr & 0x0f), rx));
} else {
	newcode[i++] = RTi_LS(rr, 0, rx);
	debugf(DB_ALL,
	printf("\tls\tr%d,0(r%d)\t\t# stk2gr, 1 word\n", rr, rx));
}

return (i);
} /* end _mv_stk2gr */


/*
 * int _mv_fpscr2gr ---------------------------------------------
 *								
 * Move a floating point system control register, which_scr, to	
 * a general register, rr.  Which_scr could be FPSR or FPCR.	
 *								
 *	FPSR = Status Register, contains exception flags.	
 *	FPCR = Control Register, contains exception traps,	
 *		rounding mode and precision mode.		
 *		(See MC881 manual for more details)		
 *								
 * Upon completion of this function, codes are generated such	
 * that ra = address on stack where scr is stored.  The content	
 * of ra should be preserved so it can later be used by		
 * mv_gr2fpsrc.							
 */
static int
_mv_fpscr2gr(newcode,gi,ra,rr,which_scr)
global_info_type	*gi;
u_short			*newcode;
int			ra,rr,which_scr;
{
int			i = 0;

/* allocate room on stack and get the effective address into ra */
i += _ea_gr(&newcode[i], gi, ra, rr);

/* read which_scr onto stack */
i += _fp_inst_store(&newcode[i], ra, rr, f881_READ_SCR(which_scr));
newcode[i++] = RTi_SYNC;

/* move the value on stack into rr */
i += _mv_stk2gr(&newcode[i], ra, rr);

return (i);
} /* end _mv_fpscr2gr */


/*
 * int _mv_gr2fpscr ---------------------------------------------
 *							
 * Move the value in general register rr to floating point system
 * control register which_scr.			
 *						
 * ra = address on stack where the scr could be stored.
 */
static int
_mv_gr2fpscr(newcode,ra,rr,which_scr)
u_short			*newcode;
int			ra,rr,which_scr;
{
int			i = 0;

newcode[i++] = RTi_STS(rr,0,ra);		/* store scr onto stk */
debugf(DB_ALL, printf("\tsts\tr%d,0(r%d)\t\t# _mv_gr2fpscr\n",rr,ra));

/* move the value on stack into which_scr */
i += _fp_inst_store(&newcode[i], ra, rr, f881_WRITE_SCR(which_scr));

return (i);
} /* end _mv_gr2fpscr */


/*
 * int _set_fpcr ------------------------------------------------
 */

static int
_set_fpcr(newcode,gi,mask,mode)
global_info_type	*gi;
u_short			*newcode;
int			mask,mode;
{
int			i = 0,ra,rr;

	ra = _fp_get_genr(gi);
	rr = _fp_get_genr(gi);

	/* get the address of f881_scr_holder into ra */
	i += _fp_getimm(&newcode[i], ra, (int) &gi->fp_state->f881_scr_holder);

	/* read FPCR into rr */
	i += _fp_inst_store(&newcode[i], ra, rr, f881_READ_SCR(f881_FPCR));
	newcode[i++] = RTi_SYNC;
	newcode[i++] = RTi_LS(rr,0,ra);

	/* set the new mode */
	newcode[i++] = RTi_NILO(rr,rr);
	newcode[i++] = mask;
	newcode[i++] = RTi_OIL(rr,rr);
	newcode[i++] = mode;

	/* write rr back to FPCR */
	newcode[i++] = RTi_INC(ra,8);	/* ra = address of f881_r13_holder */
	newcode[i++] = RTi_STS(rr,0,ra);
	i += _fp_inst_store(&newcode[i], ra, rr, f881_WRITE_SCR(f881_FPCR));

	fp_free_genr(gi,ra);
	fp_free_genr(gi,rr);
	return (i);
} /* end _set_fpcr */


/*
 * int _restore_fpcr --------------------------------------------
 */
static int
_restore_fpcr(newcode,gi)
global_info_type	*gi;
u_short			*newcode;
{
int			i = 0,ra,rr;

	ra = _fp_get_genr(gi);
	rr = _fp_get_genr(gi);

	/* get the address of f881_scr_holder into ra */
	i += _fp_getimm(&newcode[i], ra, (int) &gi->fp_state->f881_scr_holder);

	/* write rr back to FPCR */
	i += _fp_inst_store(&newcode[i], ra, rr, f881_WRITE_SCR(f881_FPCR));

	fp_free_genr(gi,ra);
	fp_free_genr(gi,rr);
	return (i);
} /* end _restore_fpcr */


/*
 * int _f881_wait -----------------------------------------------
 *							
 * Some f881 operations require a long time to execute, for
 * those operations, this loop is added to wait until	
 * its completion.				
 *
 * ri is a scratch register.
 */
static int
_f881_wait(newcode,ri)
u_short			*newcode;
int			ri;
{
int			i = 0;

debugf(DB_ALL, printf("wait:\tcau\tr%d,0xfd00\t\t# _f881_wait\n",ri));
debugf(DB_ALL, printf("\tls\tr%d,0(%d)\t\t# _f881_wait\n",ri,ri));
debugf(DB_ALL, printf("\tsri16\tr%d,0\t\t# _f881_wait\n",ri));
debugf(DB_ALL, printf("\tcli\tr%d,0x0802\t\t# _f881_wait\n",ri));
debugf(DB_ALL, printf("\tjne	wait\n"));

/*
 * Poll response register at address 0xfd000000
 * until the operation is finished.
 */
newcode[i++] = RTi_CAU(ri,r0);
newcode[i++] = 0xfd00;	
newcode[i++] = RTi_LS(ri,0,ri);
newcode[i++] = RTi_SRI16(ri,0);
newcode[i++] = RTi_CLI(ri);
newcode[i++] = 0x0802;
newcode[i++] = RTi_JNE(-6);

return (i);
} /* end _f881_wait */


/*
 * int _f881_truncate -------------------------------------------
 */
static int
_f881_truncate(newcode,ri,fdst)
u_short		*newcode;
int		ri;
u_long		fdst;
{
addr_type	inst;

inst.L = f881inst(f881_F2F,fdst,fdst,OP881_INTRZ);
return (_fp_inst_store(newcode,r0,ri,inst));
} /* end _f881_truncate */


/*
 * int _f881_sub_2to31 ------------------------------------------
 */
static int
_f881_sub_2to31(newcode,gi,fdst,ra,ri)
u_short		*newcode;
global_info_type *gi;
int		ra,ri,fdst;
{
int		icode=0;
addr_type	inst;

icode += _fp_getimm(&newcode[icode], ra, (int) &gi->fp_state->hi_2to31);
inst.L = f881inst(f881_M2F, f881_tbl_src_format[DBLETYPE],
		fdst, OP881_SUB);
icode += _fp_inst_store(&newcode[icode],ra,ri,inst);

return(icode);
} /* end _f881_sub_2to31 */


#ifdef DEBUG
/*
 * int decode_f881 ---------------------------------------------
 */
static int
decode_f881(inst)
u_long		inst;
{
int	ll,op,src,dst,opclass;

ll = (inst & 0x3);
inst >>= 2;
op = (inst & 0x7f);
inst >>= 7;
dst = (inst & 0x7);
inst >>= 3;
src = (inst & 0x7);
inst >>= 3;
opclass = (inst & 0x7);
printf("\t# Floating point instr ");
if (opclass == 0) printf("F2F");
else if (opclass == 2) printf("M2F");
else printf("F2M");
printf(" %d word(s),\t",ll);
if (op) printf("0x%x",op);
else printf("FMOVE");
if (opclass) printf("\t%d,fr%d\n",src,dst);
else printf("\tfr%d,fr%d\n",src,dst);
}
#endif DEBUG


/*
 * int _handle_uint_arg ------------------------------------------
 */
static int
_handle_uint_arg(newcode,gi,num,fdst,ra,ri)
u_short			*newcode;
global_info_type	*gi;
int			num, fdst, ra, ri;
{
int			icode=0, tmp, reg;
addr_type		inst;

	/*
	 * Move a double 2^31 value, 41e00000 00000000, stored
	 * at gi->fp_state (fpglue.s) into fdst
	 */
	tmp = (int) &gi->fp_state->hi_2to31;
	icode += _fp_getimm(&newcode[icode], ra, tmp);
	inst.L = f881inst(f881_M2F, f881_tbl_src_format[DBLETYPE],
			fdst, OP881_MOVE);
	icode += _fp_inst_store(&newcode[icode],ra,ri,inst);

	/*
	 * Make ra = &gi->fp_state->scratch_1, _fp_getimm() is NOT
	 * used here because it generates 8 bytes of codes
	 */
	tmp = (int) &gi->fp_state->scratch_1 - tmp;
	if ((tmp & 0xf) == tmp) {	/* 0 <= tmp <= 15 */
		newcode[icode++] = RTi_INC(ra, tmp);
	} else {
		newcode[icode++] = RTi_CAL(ra, ra);
		newcode[icode++] = tmp;
	}

	reg = byteval_of(gi, num);
	
	switch hi_optype_of(gi, num) {
	case GREGTYPE:
		newcode[icode++] = RTi_XIU(ri, reg);
		newcode[icode++] = 0x8000;
		break;
	case IMMEDTYPE:
		tmp = getopnd(gi,num,0) ^ 0x80000000;
		icode += _fp_getimm(&newcode[icode], ri, tmp);
		break;
	case ADDRTYPE:
		tmp = getopnd(gi,num,0);
		icode += _fp_load(&newcode[icode], ri, reg, tmp);
		newcode[icode++] = RTi_XIU(ri, reg);
		newcode[icode++] = 0x8000;
		break;
	default:
		FPABORT(gi, "_handle_uint_arg: invalid type");
	} /* end switch */

	newcode[icode++] = RTi_STS(ri, 0, ra);
	inst.L = f881inst(f881_M2F, f881_tbl_src_format[INTTYPE],
			fdst, OP881_ADD);
	icode += _fp_inst_store(&newcode[icode],ra,ri,inst);

	return (icode);
} /* end _handle_uint_arg */


/*
 * int _handle_uint_res ------------------------------------------
 */
static int
_handle_uint_res(newcode,gi,ra,ri)
u_short			*newcode;
global_info_type	*gi;
int			ra, ri;
{
int			icode=0;
	
	switch hi_optype_of(gi, 0) {
	case GREGTYPE:
		ri = byteval_of(gi, 0);
		newcode[icode++] = RTi_XIU(ri, ri);
		newcode[icode++] = 0x8000;
		break;
	case ADDRTYPE:
		newcode[icode++] = RTi_LS(ri, 0, ra);
		newcode[icode++] = RTi_XIU(ri, ri);
		newcode[icode++] = 0x8000;
		newcode[icode++] = RTi_STS(ri, 0, ra);
		break;
	default:
		FPABORT(gi, "_handle_uint_res: invalid type");
	} /* end switch */

	return (icode);
} /* end _handle_uint_res */


/*
 * int _handle_operand ------------------------------------------
 */
static int
_handle_operand(newcode,gi,num, opclass,fsrc,ra,ea_only_flag)
u_short			*newcode;
global_info_type	*gi;
int			num;
u_long			*opclass,*fsrc;
u_short			*ra;
int			ea_only_flag;
{
int			i=0, reg, offset;

reg = byteval_of(gi, num);

switch hi_optype_of(gi, num) {
case FREGTYPE:
	*fsrc = reg;
	*opclass = f881_F2F;
	return (0);
case GREGTYPE:
	if (ea_only_flag)
		i = _ea_gr(newcode, gi, *ra, reg);
	else
		i = _ea_gr2stk(newcode, gi, *ra, reg);
	break;
case IMMEDTYPE:
	gi->immcode[num] = TRUE;	/* to be _relocated */
	i = _fp_ea_addrtype(newcode, *ra, r0, (u_long) MAGIC_NUMBER);
	break;
case ADDRTYPE:
	offset = getopnd(gi,num,0);
	if (offset)			/* non-0 offset */
		i = _fp_ea_addrtype(newcode, *ra, reg, offset);
	else
		*ra = reg;
	break;
} /* end switch */
*fsrc = f881_tbl_src_format[lo_optype_of(gi, num)];
*opclass = f881_M2F;

return (i);
} /* end _handle_operand */


/*
 * int _f881_monadic_1 ------------------------------------------
 *								
 * Monadic operation with 1 operand ==>  opnd1 = op(opnd1).	
 *								
 * Algorithm:							
 * 								
 * If opnd1 is freg (floating point register)			
 *		freg = op(freg)					
 * If opnd1 is greg (general register)				
 *		stack <- greg					
 *		tmp_freg = op(stack)				
 *		stack <- tmp_freg				
 *		greg <- stack					
 * If opnd1 is address type or immediate value			
 *		calculate effective address			
 *		tmp_freg = op(addr)				
 *		addr <- tmp_freg				
 */
static int
_f881_monadic_1(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		resprec,opprec,
		restype,
		resbyte;
int		icode=0, RTFL_op;
u_short		rtmp,ra,ri;
u_long		fsrc,fdst,opclass;
addr_type	inst;

debugf(DB_DUMP, dump("Enter _f881_monadic_1:",gi->data,gi->data->mysize));

restype = hi_optype_of(gi,0);
resprec = lo_optype_of(gi,0);
resbyte = byteval_of(gi,0);
RTFL_op = gi->data->opcode;
/*
 * _f881_prec expects two argument-precisions, but its
 * logic can be used here if given the same precision twice.
 */
opprec = _f881_prec(gi, resprec, resprec);
if (opprec == SGLETYPE)
	icode += _set_fpcr(&newcode[icode],gi,f881_PREC_MASK,f881_PREC_SINGLE);

if (restype == FREGTYPE) {	/* first opnd is a freg */
	fdst = resbyte;
	inst.L = f881inst(f881_F2F,fdst,fdst,f881_tbl_ops[RTFL_op]);
	icode += _fp_new_inst_store(&newcode[icode],gi,r0,inst);
	if (is_slow_ops(RTFL_op)) {
		ri = _fp_get_genr(gi);
		icode += _f881_wait(&newcode[icode],ri);
		fp_free_genr(gi,ri);
	}
	if (force_single_prec(gi,resprec,opprec)) {
		opprec = SGLETYPE;
		icode += _set_fpcr(&newcode[icode],gi,
				f881_PREC_MASK,f881_PREC_SINGLE);
		inst.L = f881inst(f881_F2F,fdst,fdst,OP881_MOVE);
		icode += _fp_new_inst_store(&newcode[icode],gi,r0,inst);
	}
} else {
	fdst = _fp_get_fltr(gi);		/* get a temporary freg */
	rtmp = ra = _fp_get_genr(gi);	/* gr to hold effective address */
	ri = _fp_get_genr(gi);		/* gr to hold 881 flt instr */

	/* perform the requested operation, tmp_freg = op(opnd1) */
	icode += _handle_operand(&newcode[icode],gi,0,
				&opclass,&fsrc,&rtmp,PUSH_OPND);
	inst.L = f881inst(opclass,fsrc,fdst,f881_tbl_ops[RTFL_op]);
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);

	/*
	 * restype for monadic_1 opeations CAN'T be an int, if it
	 * were, one can just use a non-fp operation
	 */
	if (is_slow_ops(RTFL_op))
		icode += _f881_wait(&newcode[icode],ri);
#ifdef MC881_LOGB
	else if (RTFL_op == FP_LOGB) {
		/* result of logb() is an int in fp format */
		icode += _set_fpcr(&newcode[icode],gi,
				f881_ROUND_MASK,f881_ROUND_DOWN);
		inst.L = f881inst(f881_F2F,fdst,fdst,OP881_INT);
		icode += _fp_inst_store(&newcode[icode],r0,ri,inst);
		icode += _restore_fpcr(&newcode[icode],gi);
	}
#endif MC881_LOGB

	/* the result is now in freg, move the result, opnd1 <- tmp_freg */
	inst.L = f881inst(f881_F2M,fsrc,fdst,OP881_MOVE);
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
	newcode[icode++] = RTi_SYNC;
	if (restype == GREGTYPE)
		icode += _mv_stk2gr(&newcode[icode], rtmp, resbyte);

	fp_free_fltr(gi,fdst);
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
} /* end else */

if (opprec == SGLETYPE)
	icode += _restore_fpcr(&newcode[icode],gi);

debugf(DB_DUMP, dump("Exit _f881_monadic_1:",&newcode[0],icode * 2));

return (icode);
} /* end _f881_monadic_1 */


/*
 * int _f881_monadic_2 ------------------------------------------
 *								
 * Monadic operation with 2 operands ==>  opnd1 = op(opnd2).	
 *								
 * Algorithm:							
 * 								
 * If opnd1 is freg						
 *		handle opnd2					
 *		freg = op(opnd2)				
 * Else								
 *		get a tmp_freg
 *		handle opnd2					
 *		tmp_freg = op(opnd2)				
 *		handle opnd1					
 *		opnd1 <- tmp_freg
 */
static int
_f881_monadic_2(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		argprec,resprec,opprec,
		restype,
		resbyte;
int		icode=0,fr_avail;
u_short		rtmp,ra,ri;
u_long		fsrc,fdst,opclass;
addr_type	inst;

debugf(DB_DUMP, dump("Enter _f881_monadic_2:",gi->data,gi->data->mysize));

restype = hi_optype_of(gi,0);
resprec = lo_optype_of(gi,0);
resbyte = byteval_of(gi,0);

argprec = lo_optype_of(gi,1);

fr_avail = (restype == FREGTYPE);
if (fr_avail)
	fdst = resbyte;			/* first opnd is a freg */
else
	fdst = _fp_get_fltr(gi);		/* get a tmp freg */

if (gi->data->opcode == FP_MOVE) {	/* precision conversion only */
	opprec = DBLETYPE;
	if (fr_avail && force_single_prec(gi,resprec,opprec))
		opprec = SGLETYPE;
} else
	opprec = _f881_prec(gi, argprec, argprec);
if (opprec == SGLETYPE)
	icode += _set_fpcr(&newcode[icode],gi,f881_PREC_MASK,f881_PREC_SINGLE);

rtmp = ra = _fp_get_genr(gi);		/* to hold the effective addr */
ri = _fp_get_genr(gi);			/* to hold 881 flt instr */

/* perform the requested floating point operation, freg = op(arg) */
if (is_uint(argprec) && check_version(VERSION_UNSIGNED)) {
	opclass = f881_F2F;
	fsrc = fdst;
	icode += _handle_uint_arg(&newcode[icode],gi,1,fdst,rtmp,ri);
} else {
	icode += _handle_operand(&newcode[icode],gi,1,&opclass,&fsrc,&rtmp,PUSH_OPND);
}
if ((gi->data->opcode != FP_MOVE) || !is_uint(argprec) ||
			!check_version(VERSION_UNSIGNED)) {
	inst.L = f881inst(opclass,fsrc,fdst,f881_tbl_ops[gi->data->opcode]);
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
}
if (is_slow_ops(gi->data->opcode))
	icode += _f881_wait(&newcode[icode],ri);

/* the result is now in fdst */
if (fr_avail) {
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
	if (force_single_prec(gi,resprec,opprec)) {
		opprec = SGLETYPE;
		icode += _set_fpcr(&newcode[icode],gi,
				f881_PREC_MASK,f881_PREC_SINGLE);
		inst.L = f881inst(f881_F2F,fdst,fdst,OP881_MOVE);
		icode += _fp_new_inst_store(&newcode[icode],gi,r0,inst);
	}
} else {			/* need to move opnd1 <- freg */
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
		icode += _f881_sub_2to31(&newcode[icode],gi,fdst,ra,ri);
	if is_int(resprec)	/* result is int or unsigned int? */
		icode += _f881_truncate(&newcode[icode],ri,fdst);
	rtmp = ra;
	icode += _handle_operand(&newcode[icode],gi,0,
				&opclass,&fsrc,&rtmp,EA_ONLY);
	inst.L = f881inst(f881_F2M,fsrc,fdst,OP881_MOVE);
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
	newcode[icode++] = RTi_SYNC;
	if (restype == GREGTYPE)
		icode += _mv_stk2gr(&newcode[icode], rtmp, resbyte);
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
		icode += _handle_uint_res(&newcode[icode],gi,rtmp,ri);
	fp_free_fltr(gi,fdst);
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
} /* end if */

if (opprec == SGLETYPE)
	icode += _restore_fpcr(&newcode[icode],gi);

debugf(DB_DUMP, dump("Exit _f881_monadic_2:",&newcode[0],icode * 2));

return (icode);
} /* end _f881_monadic_2 */


/*
 * int _f881_dyadic_2 -------------------------------------------
 *								
 * Dyadic operation with 2 operands ==>  opnd1 = opnd1 op opnd2.
 *								
 * Algorithm:							
 * 								
 * If opnd1 is freg						
 *		handle opnd2					
 *		freg = op(opnd2)				
 * Else								
 *		handle opnd1					
 *		tmp_freg <- opnd1				
 *		handle opnd2					
 *		tmp_freg = tmp_freg op opnd2			
 *		opnd1 <- tmp_freg				
 *								
 */
static int
_f881_dyadic_2(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		argprec,resprec,opprec,
		restype,
		resbyte;
int		icode=0,fr_avail;
u_short		saved_ra,ra,rtmp,ri;
u_long		saved_fsrc,fsrc,fdst,opclass;
addr_type	inst;
int		offset, need_to_free_saved_ra=FALSE;

debugf(DB_DUMP, dump("Enter _f881_dyadic_2:",gi->data,gi->data->mysize));

restype = hi_optype_of(gi,0);
resprec = lo_optype_of(gi,0);
resbyte = byteval_of(gi,0);

argprec = lo_optype_of(gi,1);

opprec = _f881_prec(gi, resprec, argprec);
if (opprec == SGLETYPE)
	icode += _set_fpcr(&newcode[icode],gi,f881_PREC_MASK,f881_PREC_SINGLE);

rtmp = ra = _fp_get_genr(gi);	/* to hold the effective address */
ri = _fp_get_genr(gi);		/* to hold 881 flt instr */
fr_avail = (restype == FREGTYPE);
if (fr_avail)				/* first opnd is a freg */
	fdst = resbyte;
else {
	/* move the 1st operand into a temp freg which holds the result */
	fdst = _fp_get_fltr(gi);		/* get a tmp_freg */
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED)) {
		icode += _handle_uint_arg(&newcode[icode],gi,0,fdst,rtmp,ri);
		saved_fsrc = f881_tbl_src_format[restype];
		if (restype == ADDRTYPE) {
			offset = getopnd(gi, 0, 0);
			if (offset)
				icode += _fp_ea_addrtype(&newcode[icode],
						rtmp, resbyte, offset);
			else
				rtmp = resbyte;
		}
	} else {
		icode += _handle_operand(&newcode[icode],gi,0,
				&opclass,&fsrc,&rtmp,PUSH_OPND);
		inst.L = f881inst(opclass,fsrc,fdst,OP881_MOVE);
		icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
		saved_fsrc = fsrc;
	}
	saved_ra = rtmp;			/* save the effective address */
	if (saved_ra == ra) {
		need_to_free_saved_ra = TRUE;
		ra = _fp_get_genr(gi);
	}
} /* end else */

/* perform the requested floating point operation, freg = freg op opnd2 */
rtmp = ra; /* rtmp could have been changed by _handle_operand, re-load it */
if (is_uint(argprec) && check_version(VERSION_UNSIGNED)) {
	opclass = f881_F2F;
	fsrc = _fp_get_fltr(gi);		/* get a floating point reg */
	icode += _handle_uint_arg(&newcode[icode],gi,1,fsrc,rtmp,ri);
	fp_free_fltr(gi,fsrc);
} else {
	icode += _handle_operand(&newcode[icode],gi,1,&opclass,&fsrc,&rtmp,PUSH_OPND);
}
inst.L = f881inst(opclass,fsrc,fdst,f881_tbl_ops[gi->data->opcode]);
icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
if (is_slow_ops(gi->data->opcode))
	icode += _f881_wait(&newcode[icode],ri);

/* the result is now in fdst */
if (fr_avail) {
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
	if (force_single_prec(gi,resprec,opprec)) {
		opprec = SGLETYPE;
		icode += _set_fpcr(&newcode[icode],gi,
				f881_PREC_MASK,f881_PREC_SINGLE);
		inst.L = f881inst(f881_F2F,fdst,fdst,OP881_MOVE);
		icode += _fp_new_inst_store(&newcode[icode],gi,r0,inst);
	}
} else {			/* need to move opnd1 <- freg */
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
		icode += _f881_sub_2to31(&newcode[icode],gi,fdst,ra,ri);
	if is_int(resprec)	/* result is int or unsigned int */
		icode += _f881_truncate(&newcode[icode],ri,fdst);
	inst.L = f881inst(f881_F2M,saved_fsrc,fdst,OP881_MOVE);
	icode += _fp_inst_store(&newcode[icode],saved_ra,ri,inst);
	newcode[icode++] = RTi_SYNC;
	if (restype == GREGTYPE)
		icode += _mv_stk2gr(&newcode[icode], saved_ra, resbyte);
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
		icode += _handle_uint_res(&newcode[icode],gi,saved_ra,ri);
	if (need_to_free_saved_ra)
		fp_free_genr(gi,saved_ra);
	fp_free_fltr(gi,fdst);
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
} /* end if */

if (opprec == SGLETYPE)
	icode += _restore_fpcr(&newcode[icode],gi);

debugf(DB_DUMP, dump("Exit _f881_dyadic_2:",&newcode[0],icode * 2));

return (icode);
} /* end _f881_dyadic_2 */


/*
 * int _f881_dyadic_3 -------------------------------------------
 *								
 * Dyadic operation with 3 operands ==>  opnd1 = opnd2 op opnd3.
 *								
 * Algorithm:							
 * 								
 * If opnd1 is freg						
 *		handle opnd2					
 *		freg <- opnd2					
 *		handle opnd3					
 *		freg = freg op opnd3				
 * Else								
 *		handle opnd2					
 *		tmp_freg <- opnd2				
 *		handle opnd3					
 *		tmp_freg = tmp_freg op opnd3			
 *		handle opnd1					
 *		opnd1 <- tmp_freg				
 *								
 */
#define pfd(z)          printf("z =     0x%.8x  %d\n",z,z)

static int
_f881_dyadic_3(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		arg1prec,arg2prec,resprec,opprec,
		arg2type,restype,
		arg2byte,resbyte;
int		icode=0,fr_avail,fr_conflit;
u_short		rtmp,ra,ri;
u_long		fsrc,fdst,opclass;
addr_type	inst;

debugf(DB_DUMP, dump("Enter _f881_dyadic_3:",gi->data,gi->data->mysize));

restype = hi_optype_of(gi,0);
resprec = lo_optype_of(gi,0);
resbyte = byteval_of(gi,0);

arg1prec = lo_optype_of(gi,1);

arg2type = hi_optype_of(gi,2);
arg2prec = lo_optype_of(gi,2);
arg2byte = byteval_of(gi,2);

opprec = _f881_prec(gi, arg1prec, arg2prec);
if (opprec == SGLETYPE)
	icode += _set_fpcr(&newcode[icode],gi,f881_PREC_MASK,f881_PREC_SINGLE);

rtmp = ra = _fp_get_genr(gi);	/* to hold the effective address */
ri = _fp_get_genr(gi);		/* to hold 881 flt instr */
fr_avail = (restype == FREGTYPE);

if (fr_avail) {
	fr_conflit = ((arg2type == FREGTYPE) && (arg2byte == resbyte));
	if (fr_conflit)			/* we have op	frx,opnd,frx */
		fdst = _fp_get_fltr(gi);	/* get a floating point reg */
	else
		fdst = resbyte;
} else {
	fdst = _fp_get_fltr(gi);		/* get a floating point reg */
	fr_conflit = FALSE;
}

/* move the 2nd operand into fdst, which holds the result */
if (is_uint(arg1prec) && check_version(VERSION_UNSIGNED)) {
	icode += _handle_uint_arg(&newcode[icode],gi,1,fdst,rtmp,ri);
} else {
	icode += _handle_operand(&newcode[icode],gi,1,&opclass,&fsrc,&rtmp,PUSH_OPND);
	inst.L = f881inst(opclass,fsrc,fdst,OP881_MOVE);
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
}

/* perform the requested floating point operation, freg = freg op opnd3 */
rtmp = ra;
if (is_uint(arg2prec) && check_version(VERSION_UNSIGNED)) {
	opclass = f881_F2F;
	fsrc = _fp_get_fltr(gi);		/* get a floating point reg */
	icode += _handle_uint_arg(&newcode[icode],gi,2,fsrc,rtmp,ri);
	fp_free_fltr(gi,fsrc);
} else {
	icode += _handle_operand(&newcode[icode],gi,2,&opclass,&fsrc,&rtmp,PUSH_OPND);
}
inst.L = f881inst(opclass,fsrc,fdst,f881_tbl_ops[gi->data->opcode]);
icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
if (is_slow_ops(gi->data->opcode))
	icode += _f881_wait(&newcode[icode],ri);

/* the result is now in fdst */
if (fr_conflit) {
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
	if (force_single_prec(gi,resprec,opprec)) {
		opprec = SGLETYPE;
		icode += _set_fpcr(&newcode[icode],gi,
				f881_PREC_MASK,f881_PREC_SINGLE);
	}
	fsrc = fdst;
	fdst = resbyte;
	inst.L = f881inst(f881_F2F,fsrc,fdst,OP881_MOVE);
	icode += _fp_new_inst_store(&newcode[icode],gi,r0,inst);
	fp_free_fltr(gi,fdst);
} else if (fr_avail) {
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
	if (force_single_prec(gi,resprec,opprec)) {
		opprec = SGLETYPE;
		icode += _set_fpcr(&newcode[icode],gi,
				f881_PREC_MASK,f881_PREC_SINGLE);
		inst.L = f881inst(f881_F2F,fdst,fdst,OP881_MOVE);
		icode += _fp_new_inst_store(&newcode[icode],gi,r0,inst);
	}
} else {			/* need to move opnd1 <- freg */
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
		icode += _f881_sub_2to31(&newcode[icode],gi,fdst,ra,ri);
	if is_int(resprec)	/* result is int or unsigned int */
		icode += _f881_truncate(&newcode[icode],ri,fdst);
	rtmp = ra;
	icode += _handle_operand(&newcode[icode],gi,0,
				&opclass,&fsrc,&rtmp,EA_ONLY);
	inst.L = f881inst(f881_F2M,fsrc,fdst,OP881_MOVE);
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
	newcode[icode++] = RTi_SYNC;
	if (restype == GREGTYPE)
		icode += _mv_stk2gr(&newcode[icode], rtmp, resbyte);
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
		icode += _handle_uint_res(&newcode[icode],gi,rtmp,ri);
	fp_free_fltr(gi,fdst);
	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
} /* end if */

if (opprec == SGLETYPE)
	icode += _restore_fpcr(&newcode[icode],gi);

debugf(DB_DUMP, dump("Exit _f881_dyadic_3:",&newcode[0],icode * 2));

return (icode);
} /* end _f881_dyadic_3 */


/*
 * int _f881_monadic --------------------------------------------
 */
static int
_f881_monadic(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
if (gi->data->numopnds == 1)
	return(_f881_monadic_1(newcode,gi));
else
	return(_f881_monadic_2(newcode,gi));
} /* end _f881_monadic */


/*
 * int _f881_dyadic ---------------------------------------------
 */
static int
_f881_dyadic(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
if (gi->data->numopnds == 2)
	return(_f881_dyadic_2(newcode,gi));
else
	return(_f881_dyadic_3(newcode,gi));
} /* end _f881_dyadic */


/*
 * int _move_fr2opnd --------------------------------------------
 *								
 * Move a freg to a memory operand.  This operation is mainly	
 * used for precision conversion.				
 *	opnd1 = memory operand	<--	opnd2 = freg		
 * When converting from float/double to integer, truncation is	
 * used (tmp_freg = trunc(freg);  opnd1 <- tmp_freg).		
 *								
 */
static int
_move_fr2opnd(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		resprec;
int		icode=0,fr_avail,offset;
u_short		rtmp,ra,ri;
u_long		fsrc,fdst;
addr_type	inst;

debugf(DB_DUMP, dump("Enter _move_fr2opnd:",gi->data,gi->data->mysize));

resprec = lo_optype_of(gi,0);
ra = _fp_get_genr(gi);		/* to hold the effective address */
ri = _fp_get_genr(gi);		/* to hold 881 flt instr */
if is_int(resprec) {		/* result is int or unsigned int */
				/* truncation is needed */
	fsrc = byteval_of(gi,1);		/* 2nd opnd is a freg */
	fr_avail = is_scratchf(gi,fsrc);
	if (fr_avail)
		fdst = fsrc;
	else
		fdst = _fp_get_fltr(gi);
	inst.L = f881inst(f881_F2F,fsrc,fdst,OP881_INTRZ);
	icode += _fp_inst_store(&newcode[icode],r0,ri,inst);
	if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
		icode += _f881_sub_2to31(&newcode[icode],gi,fdst,ra,ri);
} else {
	fr_avail = TRUE;
	fdst = byteval_of(gi,1);		/* 2nd opnd is a freg */
}
fsrc = f881_tbl_src_format[lo_optype_of(gi, 0)];
inst.L = f881inst(f881_F2M,fsrc,fdst,OP881_MOVE);
rtmp = byteval_of(gi,0);
if (hi_optype_of(gi, 0) == GREGTYPE) {
	icode += _ea_gr(&newcode[icode], gi, ra, rtmp);
	icode += _fp_inst_store(&newcode[icode],ra,ri,inst);
	newcode[icode++] = RTi_SYNC;
	icode += _mv_stk2gr(&newcode[icode], ra, rtmp);
} else { /* ADDRTYPE */
	offset = getopnd(gi,0,0);
	if (offset) {				/* non-0 offset */
		icode += _fp_ea_addrtype(&newcode[icode], ra, rtmp, offset);
		rtmp = ra;
	}
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
	newcode[icode++] = RTi_SYNC;
}
if (is_uint(resprec) && check_version(VERSION_UNSIGNED))
	icode += _handle_uint_res(&newcode[icode],gi,rtmp,ri);

debugf(DB_DUMP, dump("Exit _move_fr2opnd:",&newcode[0],icode * 2));

if (!fr_avail)
	fp_free_fltr(gi,fdst);
fp_free_genr(gi,ra);
fp_free_genr(gi,ri);
return (icode);
} /* end _move_fr2opnd */


/*
 * int _f881_cmp_all --------------------------------------------
 *							
 * Handle FP_CMP (eq, ne) and FP_CMPT (lt, le, gt, ge)
 * operations.  This function generates codes to do a floating
 * point compare and then translate the 881 condition codes to
 * RT condition codes					
 */
static int
_f881_cmp_all(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		icode=0,fr_avail,tmp;
u_short		ra,ri,rtmp;
u_long		fsrc,fdst,opclass;
addr_type	inst;

debugf(DB_DUMP, dump("Enter _f881_cmp_all:",gi->data,gi->data->mysize));

rtmp = ra = _fp_get_genr(gi);	/* to hold the effective address */
ri = _fp_get_genr(gi);		/* to hold 881 flt instr */
fr_avail = (hi_optype_of(gi, 0) == FREGTYPE);
if (fr_avail)				/* first opnd is a freg */
	fdst = byteval_of(gi,0);
else {
	/* move the 1st operand into a temp freg */
	fdst = _fp_get_fltr(gi);		/* get a floating point reg */
	if (is_uint(lo_optype_of(gi, 0)) && check_version(VERSION_UNSIGNED)) {
		icode += _handle_uint_arg(&newcode[icode],gi,0,fdst,rtmp,ri);
	} else {
		icode += _handle_operand(&newcode[icode],gi,0,
				&opclass,&fsrc,&rtmp,PUSH_OPND);
		inst.L = f881inst(opclass,fsrc,fdst,OP881_MOVE);
		icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
	}
} /* end else */

/* perform the floating point COMPARE operation */
rtmp = ra;
if (is_uint(lo_optype_of(gi, 1)) && check_version(VERSION_UNSIGNED)) {
	opclass = f881_F2F;
	fsrc = _fp_get_fltr(gi);		/* get a floating point reg */
	icode += _handle_uint_arg(&newcode[icode],gi,1,fsrc,rtmp,ri);
	fp_free_fltr(gi,fsrc);
} else {
	icode += _handle_operand(&newcode[icode],gi,1,&opclass,&fsrc,&rtmp,PUSH_OPND);
}
inst.L = f881inst(opclass,fsrc,fdst,OP881_CMP);
icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);

/* now get the f881's condition codes in FPSR into ri */
icode += _mv_fpscr2gr(&newcode[icode], gi, ra, ri, f881_FPSR);

/*
 * the f881's condition codes (N,Z,I,NAN) are now in ri, translate
 * (N,Z) into RT's condition codes (LT,EQ,GT) using ra as a temp reg.
 * Notice that the I code is not used.  The NAN code only applied to
 * CMPT.
 *
 * Here are the bits lay out:
 *		MSB                                            LSB
 * mc881 (SR)	xxxx N Z I NAN xxxx xxxx xxxx xxxx x x  x  x  xxxx
 * RT (SR)	xxxx x x x x   xxxx xxxx xxxx xxxx x LT EQ GT xxxx
 */
if (gi->data->opcode == FP_CMP) {
	newcode[icode++] = RTi_NIUZ(ra,ri);	/* isolate f881 Z bit */
	newcode[icode++] = 0x0400;
	newcode[icode++] = RTi_SRI16(ra,5);	/* line up with RT EQ bit */
} else if (gi->data->opcode == FP_CMPT) {	/* tx N,Z into LT,EQ,GT */
	newcode[icode++] = RTi_NIUZ(ra,ri);	/* isolate f881 NAN bit */
	newcode[icode++] = 0x0100;
	if (!check_version(VERSION_881NAN))
		newcode[icode++] = RTi_JEQ(3);		/* jump to not NAN */
	else {
		newcode[icode++] = RTi_JEQ(13);		/* jump to not NAN */

		/* handle NAN */
		tmp = (int) &gi->fp_state->f881_scr_holder;
		newcode[icode++] = RTi_CAL16(ra,r0);	/* get address of */
		newcode[icode++] = (tmp & 0xffff);	/* f881_scr_holder */
		newcode[icode++] = RTi_OIU(ra,ra);	/* ra		*/
		newcode[icode++] = (tmp >> 16);
		newcode[icode++] = RTi_STM(r13,ra);
		newcode[icode++] = 8;
		newcode[icode++] = RTi_LS(r15,1,ra);	/* call f881_cmp_NAN_code */
		newcode[icode++] = RTi_BALRX(r15,r15);
		newcode[icode++] = RTi_OIL(r13,ri);	/* set IOP flag */
		newcode[icode++] = 0x2080;		/* in f881_FPSR */
	}
	newcode[icode++] = RTi_LIS(ra,0);	/* set RT LT,EQ,GT to 0	*/
	newcode[icode++] = RTi_J(8);		/* jump to MTS SCR15,ra	*/

	/* handle not NAN */
	newcode[icode++] = RTi_SRI16(ri,10);	/* right justify N,Z */
	newcode[icode++] = RTi_CAU(ra,0);
	newcode[icode++] = 0x2000;		/* 001 in high order bits */
	newcode[icode++] = RTi_SL(ra,ri);	/* NZ = 11 translate to 0 */
	newcode[icode++] = RTi_SRI16(ra,9);	/* line up wit LT,EQ,LT */
	newcode[icode++] = RTi_MTTBIL(ri,15);	/* f881 Z bit to testbit */
	newcode[icode++] = RTi_MFTBIL(ra,10);	/* testbit to EQ */
}
newcode[icode++] = RTi_MTS(SCR15,ra);

debugf(DB_DUMP, dump("Exit _f881_cmp_all:",&newcode[0],icode * 2));

if (!fr_avail)
	fp_free_fltr(gi,fdst);
fp_free_genr(gi,ra);
fp_free_genr(gi,ri);
return (icode);
} /* end _f881_cmp_all */


/*
 * int _f881_movem ----------------------------------------------
 */
static int
_f881_movem(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
	int		icode=0, offset;
	int		reg_mask,num_word,rtmp,ra,ri,opclass;
	addr_type	inst;

	debugf(DB_DUMP && DB_MOVEM,
		dump("Enter _f881_movem:",gi->data,gi->data->mysize));

	reg_mask = byteval_of(gi,0);
	if (reg_mask == 0) {		/* input error !! */
		debugf(DB_MOVEM, printf("fr mask = 0 !!!\n"));
		return (icode);
	}
	ra = _fp_get_genr(gi);			/* to hold effective address */
	ri = _fp_get_genr(gi);			/* to hold 881 flt instr */

	/* set the ZIC length register to num_word */
	num_word = _num_1s_tbl[reg_mask] * 3;	/* extended prec, 3 wrd each */
	icode += _fp_getimm(&newcode[icode], ra, num_word);
	icode += _fp_inst_store(&newcode[icode], ra ,ri, f881_WRITE_ZIC_LENGTH);

	/* set up the effective address, the word offset is 1 */
	rtmp = byteval_of(gi,1);
	offset = gi->data->opr.operand[1].UL;	/* can't use getopnd here */
	if (offset) {				/* non-0 offset */
		icode += _fp_ea_addrtype(&newcode[icode], ra, rtmp, offset);
		rtmp = ra;
	}

	/* do the FMOVEM instruction */
	opclass = ((gi->data->opcode == FP_LOADM) ? f881_MM2FR : f881_FR2MM);
	inst.L = (f881_MOVEM_H | SET_TCF(opclass) | (opclass << 15) |
		  (reg_mask << 2));
	icode += _fp_inst_store(&newcode[icode],rtmp,ri,inst);
	newcode[icode++] = RTi_SYNC;
	debugf(DB_DUMP && DB_MOVEM,
		dump("Exit _f881_movem:",&newcode[0],icode * 2));

	fp_free_genr(gi,ra);
	fp_free_genr(gi,ri);
	return (icode);
} /* end _f881_movem */


/*
 * int _f881_testround ------------------------------------------
 */
static int
_f881_testround(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		icode = 0,ra,rr;

debugf(DB_DUMP, dump("Enter _f881_testround:",gi->data,gi->data->mysize));

ra = _fp_get_genr(gi);			/* to hold the effective address */
rr = byteval_of(gi, 0);			/* to hold f881_FPCR */

/* generate code to move the 881 FPCR into rr */
icode += _mv_fpscr2gr(&newcode[icode], gi, ra, rr, f881_FPCR);

/*
 * Content of rr:
 *	xxxx xxxx xxxx xxxx xxxx xxxx xxRR xxxx
 * where RR is the current rounding mode.
 *
 * Clear un-wanted bits to the right of rounding mode by shifting
 * right until the 2 rightmost bits = f881 rounding mode.
 *
 * Clear un-wanted bits to the left of rounding mode by shifting
 * left until the 2 leftmost bits = f881 rounding mode.
 *
 * Translate 881 rounding mode to enum type ROUNDDIR rounding mode:
 *
 *			f881	ROUNDDIR
 *	To nearest	00	00
 *	Toward zero	01	11
 *	Downward	10	10
 *	Upward		11	01
 *
 * a twoc will do the job.
 *
 * Finally, shift right until the 2 rightmost bit = ROUNDDIR
 * rounding mode.
 */
newcode[icode++] = RTi_SRI(rr,4);
newcode[icode++] = RTi_SLI16(rr,14);
newcode[icode++] = RTi_TWOC(rr,rr);
newcode[icode++] = RTi_SRI16(rr,14);

debugf(DB_FPSCR, printf("\tsri\tr%d,4\t# _f881_testround\n",rr));
debugf(DB_FPSCR, printf("\tsli16\tr%d,14\t# _f881_testround\n",rr));
debugf(DB_FPSCR, printf("\ttwoc\tr%d,r%d\t# _f881_testround\n",rr,rr));
debugf(DB_FPSCR, printf("\tsri16\tr%d,14\t# _f881_testround\n",rr));
debugf(DB_DUMP, dump("Exit _f881_testround:",&newcode[0],icode * 2));

fp_free_genr(gi,ra);
return (icode);
} /* end _f881_testround */


/*
 * int _f881_setround -------------------------------------------
 */
static int
_f881_setround(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int		icode = 0,rr,ra,ri;

debugf(DB_DUMP, dump("Enter _f881_setround:",gi->data,gi->data->mysize));

ra = _fp_get_genr(gi);		/* to hold the effective address */
ri = _fp_get_genr(gi);		/* to hold 881 flt instr */
rr = byteval_of(gi, 0);		/* holding the ROUNDDIR rounding mode  */

/* Move f881_FPCR into ri */
icode += _mv_fpscr2gr(&newcode[icode], gi, ra, ri, f881_FPCR);

/* Clear current rounding mode in ri */
newcode[icode++] = RTi_NILO(ri,ri);
newcode[icode++] = 0xffcf;
debugf(DB_FPSCR, printf("\tnilo\tr%d,0xffcf(r%d)\t# _f881_setround\n",ri,ri));

/*
 * Translate the ROUNDDIR rounding mode in rr to the f881
 * rounding mode and plug it into ri.
 */
newcode[icode++] = RTi_SLI16(rr,14);
newcode[icode++] = RTi_TWOC(rr,rr);
newcode[icode++] = RTi_SRI16(rr,10);
newcode[icode++] = RTi_O(ri,rr);
newcode[icode++] = RTi_CAS(rr,ri,r0);		/* DEBUG */

debugf(DB_FPSCR, printf("\tsli16\tr%d,14\t# _f881_setround\n",rr));
debugf(DB_FPSCR, printf("\ttwoc\tr%d,r%d\t# _f881_setround\n",rr,rr));
debugf(DB_FPSCR, printf("\tsri16\tr%d,10\t# _f881_setround\n",rr));
debugf(DB_FPSCR, printf("\to\tr%d,r%d\t# _f881_setround\n",ri,rr));
debugf(DB_FPSCR, printf("\tcas\tr%d,r%d\t# _f881_setround\n",rr,ri));

/* Move ri back to f881_FPCR */
icode += _mv_gr2fpscr(&newcode[icode], ra, ri, f881_FPCR);

debugf(DB_DUMP, dump("Exit _f881_setround:",&newcode[0],icode * 2));

fp_free_genr(gi,ra);
fp_free_genr(gi,ri);

return (icode);
} /* end _f881_setround */


/*
 * int _f881_test_flag_trap -------------------------------------
 */
static int
_f881_test_flag_trap(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int			icode = 0,which_scr,delta,ri,rr;

debugf(DB_DUMP, dump("Enter _f881_test_flag_trap:",gi->data,gi->data->mysize));

ri = _fp_get_genr(gi);
rr = byteval_of(gi, 0);		/* to hold value of flag or trap */

/*
 * Here are the bits lay out (IBM bit convention):
 * lower half	0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 * mc881 (CR)	     inv ov un dz iex				(trap)
 * mc881 (SR)			      inv ov un dz iex		(flag)
 * ui bits				       iex dz ov un inv	
 */
if (gi->data->opcode == FP_TESTFLAG) {
	which_scr = f881_FPSR;		/* FPSR = current flag status */
	delta = 6;			/* bit position delta between */
					/*	FPCR & FPSR	*/
} else { /* FP_TESTTRAP */
	which_scr = f881_FPCR;		/* FPCR = current trap setting */
	delta = 0;
}

/*
 * generate codes to move the FP system control register
 * into ri; rr is used as a scratch register here.
 */
icode += _mv_fpscr2gr(&newcode[icode], gi, rr, ri, which_scr);

/* now tx the 881 exception bits (ri) to the ui exception bits (rr) */
newcode[icode++] = RTi_NILZ(rr,ri);
newcode[icode++] = (0x1800 >> delta);		/* get 881 ov, un */
newcode[icode++] = RTi_SRI(rr,10 - delta);	/* tx to ui ov, un */
newcode[icode++] = RTi_MTTBIL(ri,2 + delta);	/* get 881 inv */
newcode[icode++] = RTi_MFTBIL(rr,15);		/* tx to ui inv */
newcode[icode++] = RTi_MTTBIL(ri,5 + delta);	/* get 881 dz */
newcode[icode++] = RTi_MFTBIL(rr,12);		/* tx to ui dz */
newcode[icode++] = RTi_MTTBIL(ri,6 + delta);	/* get 881 iex */
newcode[icode++] = RTi_MFTBIL(rr,11);		/* tx to ui iex */

debugf(DB_FPSCR, printf("\tnilz\tr%d,r%d,0x%x\t# _f881_test_flag_trap ov, un\n", rr, ri, 0x1800 >> delta));
debugf(DB_FPSCR, printf("\tsri\tr%d,%d\t# _f881_test_flag_trap ov, un\n", rr, 10 - delta));
debugf(DB_FPSCR, printf("\tmttbil\tr%d,%d\t# _f881_test_flag_trap inv\n", ri, 2 + delta));
debugf(DB_FPSCR, printf("\tmftbil\tr%d,15\t# _f881_test_flag_trap inv\n", rr));
debugf(DB_FPSCR, printf("\tmttbil\tr%d,%d\t# _f881_test_flag_trap dz\n", ri, 5 + delta));
debugf(DB_FPSCR, printf("\tmftbil\tr%d,12\t# _f881_test_flag_trap dz\n", rr));
debugf(DB_FPSCR, printf("\tmttbil\tr%d,%d\t# _f881_test_flag_trap iex\n", ri, 6 + delta));
debugf(DB_FPSCR, printf("\tmftbil\tr%d,11\t# _f881_test_flag_trap iex\n", rr));
debugf(DB_DUMP, dump("Exit _f881_test_flag_trap:",&newcode[0],icode * 2));

fp_free_genr(gi,ri);

return (icode);
} /* end _f881_test_flag_trap */


/*
 * int _f881_set_clr --------------------------------------------
 */
static int
_f881_set_clr(newcode,gi)
u_short			newcode[];
global_info_type	*gi;
{
int			icode = 0,which_scr,delta;
int			rr,ra,ri,rtmp;

debugf(DB_DUMP, dump("Enter _f881_set_clr:",gi->data,gi->data->mysize));

ra = _fp_get_genr(gi);		/* to hold the effective address */
ri = _fp_get_genr(gi);		/* to hold 881 flt instr */
rtmp = _fp_get_genr(gi);		/* to hold 881 exception bits */
rr = byteval_of(gi, 0);		/* holding ui exception bits  */

/*
 * Here are the bits lay out (IBM bit convention):
 * lower half	0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 * mc881 (CR)	     inv ov un dz iex				(trap)
 * mc881 (SR)			      inv ov un dz iex		(flag)
 * ui bits				       iex dz ov un inv	
 */
if ((gi->data->opcode == FP_SETFLAG) || (gi->data->opcode == FP_CLRFLAG)) {
	which_scr = f881_FPSR;		/* FPSR = current flag status */
	delta = 6;			/* bit position delta between */
					/*	FPCR & FPSR	*/
} else { /* FP_SETTRAP, FP_CLRTRAP */
	which_scr = f881_FPCR;		/* FPCR = current trap setting */
	delta = 0;
}

/* tx the ui exception bits (rr) to the 881 exception bits (rtmp) */
newcode[icode++] = RTi_NILZ(rtmp,rr);
newcode[icode++] = 0x0006;			/* get ui ov, un */
newcode[icode++] = RTi_SLI(rtmp,10 - delta);	/* tx to 881 ov, un */
newcode[icode++] = RTi_MTTBIL(rr,15);		/* get ui inv */
newcode[icode++] = RTi_MFTBIL(rtmp,2 + delta);	/* tx to 881 inv */
newcode[icode++] = RTi_MTTBIL(rr,12);		/* get ui dz */
newcode[icode++] = RTi_MFTBIL(rtmp,5 + delta);	/* tx to 881 dz */
newcode[icode++] = RTi_MTTBIL(rr,11);		/* get ui iex */
newcode[icode++] = RTi_MFTBIL(rtmp,6 + delta);	/* tx to 881 iex */

debugf(DB_FPSCR, printf("\tnilz\tr%d,r%d,0x0006\t# _f881_set_clr ov, un\n", rtmp, rr));
debugf(DB_FPSCR, printf("\tsli\tr%d,%d\t# _f881_set_clr ov, un\n", rtmp, 10 - delta));
debugf(DB_FPSCR, printf("\tmttbil\tr%d,15\t# _f881_set_clr inv\n", rr));
debugf(DB_FPSCR, printf("\tmftbil\tr%d,%d\t# _f881_set_clr inv\n", rtmp, 2 + delta));
debugf(DB_FPSCR, printf("\tmttbil\tr%d,12\t# _f881_set_clr dz\n", rr));
debugf(DB_FPSCR, printf("\tmftbil\tr%d,%d\t# _f881_set_clr dz\n", rtmp, 5 + delta));
debugf(DB_FPSCR, printf("\tmttbil\tr%d,11\t# _f881_set_clr iex\n", rr));
debugf(DB_FPSCR, printf("\tmftbil\tr%d,%d\t# _f881_set_clr iex\n", rtmp, 6 + delta));

/*
 * generate codes to move the FP system control register into ri.
 * After this block, ri = current 881 exception bits.
 * ra = effective address where the updated exception bits
 * should be stored.  Therefore ra CAN NOT be clobbered.
 */
icode += _mv_fpscr2gr(&newcode[icode], gi, ra, ri, which_scr);

/* now plug the new setting in rtmp into ri */
if ((gi->data->opcode == FP_SETFLAG) || (gi->data->opcode == FP_SETTRAP)) {
	newcode[icode++] = RTi_O(ri,rtmp);
	debugf(DB_FPSCR, printf("\to\tr%d,r%d\t# _f881_set_clr set\n", ri, rtmp));
} else { /* FP_CLRFLAG, FP_CLRTRAP */
	newcode[icode++] = RTi_ONEC(rtmp,rtmp);
	newcode[icode++] = RTi_N(ri,rtmp);
	debugf(DB_FPSCR, printf("\tonec\tr%d,r%d\t# _f881_set_clr clr\n",rtmp,rtmp));
	debugf(DB_FPSCR, printf("\tn\tr%d,r%d\t# _f881_set_clr clr\n", ri, rtmp));
}

newcode[icode++] = RTi_CAS(rr,ri,r0);		/* DEBUG */
debugf(DB_FPSCR, printf("\tcas\tr%d,r%d\t# _f881_set_clr\n", rr, ri));

/* and move it back to the f881_FPCR */
icode += _mv_gr2fpscr(&newcode[icode], ra, ri, which_scr);

debugf(DB_DUMP, dump("Exit _f881_set_clr:",&newcode[0],icode * 2));

fp_free_genr(gi,ra);
fp_free_genr(gi,ri);
fp_free_genr(gi,rtmp);

return (icode);
} /* end _f881_set_clr */


/*
 * int _f881_commutative ----------------------------------------
 */
static int
_f881_commutative(newcode,gi)	/* FP_ADD, FP_MUL */
u_short			newcode[];
global_info_type	*gi;
{
if (gi->data->numopnds == 2)
	return(_f881_dyadic_2(newcode,gi));
else if (	(hi_optype_of(gi,0) != FREGTYPE) ||	
		(hi_optype_of(gi,2) != FREGTYPE) ||
		(byteval_of(gi,0) != byteval_of(gi,2)) )
	return(_f881_dyadic_3(newcode,gi));
else {					/* ADD/MUL	frx opnd frx */
	gi->data->numopnds -= 1;	/* ignore the third opnd */
	return(_f881_dyadic_2(newcode,gi));
}
}

static int
_f881_move(newcode,gi)	/* FP_MOVE */
u_short			newcode[];
global_info_type	*gi;
{
if (	(hi_optype_of(gi,0) != FREGTYPE) &&		/* move fr2opnd */
	(hi_optype_of(gi,1) == FREGTYPE) )
	return (_move_fr2opnd(newcode,gi));
else
	return (_f881_monadic_2(newcode,gi));
}

#ifdef DEBUG
#define debugf(cond,stmt)       if (cond) stmt
#define DB_FPSCR		TRUE
#endif DEBUG

static int
_f881_init(gi)						/* FP_INIT */
global_info_type	*gi;
{
volatile int		i = 0;

	debugf(DB_FPSCR, printf("Enter _f881_init.\n"));
	debugf(DB_FPSCR, printf("prec = %d\n",gi->fp_state->env_prec));

	if ((gi->fp_state->env_prec == DOUBLE_PREC) ||
	    (gi->fp_state->env_prec == SINGLE_PREC)) {
		*(int *)f881_READ_SCR(f881_FPCR) = (int) &i;
		*(int *)f881_WRITE_ZIC_LENGTH = 0;		/* sync */
		debugf(DB_FPSCR, printf("current FPCR = %d\n",i));

		/* clear current prec, set new precision to double */
		i = ((i & f881_PREC_MASK) | f881_PREC_DOUBLE);
		i = i;						/* sync */
		*(int *)f881_WRITE_SCR(f881_FPCR) = (int) &i;
		debugf(DB_FPSCR, printf("new FPCR = %d\n",i));
	}

	return(0);
} /* end _f881_init */

operations	_f881rtnes =
	{ /*** MC68881 DEFINITIONS ***/
	    _f881_monadic,		/* FP_ABS */
	    _f881_monadic,		/* FP_ACOS */
	    _f881_commutative,		/* FP_ADD */
	    _f881_monadic,		/* FP_ASIN */
	    _f881_monadic,		/* FP_ATAN */
	    NULL,			/* FP_ATAN2, alt function */
	    _f881_monadic,		/* FP_ATANH */
	    _f881_cmp_all,		/* FP_CMP */
	    _f881_cmp_all,		/* FP_CMPT */
	    NULL,			/* FP_RSVD, reserved */
	    _f881_monadic,		/* FP_COS */
	    _f881_monadic,		/* FP_COSH */
	    _f881_dyadic,		/* FP_DIV */
	    _f881_monadic,		/* FP_EXP */
	    _f881_monadic,		/* FP_EXPM1 */
	    _f881_movem,			/* FP_LOADM */
	    _f881_monadic,		/* FP_LOG10 */
	    _f881_monadic,		/* FP_LOG1P */
	    _f881_monadic,		/* FP_LOG */
#ifdef MC881_LOGB
	    _f881_monadic,		/* FP_LOGB */
#else
	    NULL,		/* FP_LOGB */
#endif MC881_LOGB
	    _f881_move,			/* FP_MOVE */
	    _f881_commutative,		/* FP_MUL */
	    _f881_monadic,		/* FP_NEG */
	    _f881_dyadic,		/* FP_REM */
	    _f881_monadic,		/* FP_RINT */
	    _f881_dyadic,		/* FP_SCALB */
	    _f881_monadic,		/* FP_SIN */
	    _f881_monadic,		/* FP_SINH */
	    _f881_monadic,		/* FP_SQRT */
	    _f881_movem,			/* FP_STOREM */
	    _f881_dyadic,		/* FP_SUB */
	    _f881_monadic,		/* FP_TAN */
	    _f881_monadic,		/* FP_TANH */
	    _f881_init,			/* FP_INIT */
	    NULL,			/* FP_WHICH, in fpgen.c */
	    _f881_setround,		/* FP_SETROUND */
	    _f881_testround,		/* FP_TESTROUND */
	    _f881_set_clr,		/* FP_SETFLAG */
	    _f881_test_flag_trap,	/* FP_TESTFLAG */
	    _f881_set_clr,		/* FP_SETTRAP */
	    _f881_test_flag_trap,	/* FP_TESTTRAP */
	    _f881_set_clr,		/* FP_CLRFLAG */
	    _f881_set_clr		/* FP_CLRTRAP */
	};
