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

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

#include "fpgen.h"
#include "general.h"
#include "fpa1ops.h"

/*
#define NO_DMA(gi)	TRUE
*/

extern u_long *_fromprec[];
extern u_long _fpas_ops_tbl[];

extern _fpa1_monadic();
extern _fpa1_dyadic();
extern _fpa1_loadm();
extern _fpa1_move();
extern _fpa1_storem();
extern _fpa1_setround();
extern _fpa1_testround();
extern _fpa1_setflag();
extern _fpa1_testflag();
extern _fpa1_settrap();
extern _fpa1_testtrap();
extern _fpa1_clrflag();
extern _fpa1_clrtrap();

/*
 * _fpas_prec
 * Pick the appropriate precision.
 *
 */
_fpas_prec(prec1, prec2, mode)
int	prec1, prec2;		/* two argument precisions	*/
int	mode;
{
	/*
	 *  Default or single mode, go as fast as possible...
	 *	(but if mixed precision, choose widest!)
	 */
	if ((mode == FAST_PREC) || (mode == SINGLE_PREC))
		if ((prec1 == DBLETYPE) || (prec2 == DBLETYPE))
			return(DBLETYPE);
		else
			return(SGLETYPE);
	/*
	 *  Precise or double mode, do in double ...
	 */
	else
		return(DBLETYPE);
} /* end _fpas_prec */


/*
 * _fpas_freg2freg
 *
 * Copies from one freg to another (fr1 <- fr2),
 * doing any necessary conversion.
 */
_fpas_freg2freg(newcode, gi, fr1, prec1, fr2, prec2)
u_short	*newcode;
global_info_type *gi;
int	fr1, prec1, fr2, prec2;
{
u_long	instr, conversion;

	if (USING_FPA1(gi)) {
		if (fr1 == 7)
		return(_fpa1_freg2seven(newcode, gi, prec1, fr2, prec2));

		if (fr2 == 7)
		return(_fpa1_seven2freg(newcode, gi, fr1, prec1, prec2));
	}

	conversion = _fromprec[prec2][prec1];
	if (conversion)
		instr = fpa1inst(conversion, NO_IMM_DATA, (fr1*2), (fr2*2));
	else if (prec1 == DBLETYPE)
		instr = fpa1inst(OPFPA1_COPL, NO_IMM_DATA, (fr1*2), (fr2*2));
	else
		instr = fpa1inst(OPFPA1_COPS, NO_IMM_DATA, (fr1*2), (fr2*2));

	return(_fp_new_inst_store(newcode, gi, r0, instr));
} /* end _fpas_freg2freg */


/*
 * _fpa2_freg2op
 *
 * Read the floating point register "freg" into the result (operand #1).
 * Its (operand 1) type can only be GREGTYPE or ADDRTYPE.
 *
 */
static int
_fpa2_freg2op(newcode, gi, freg, inprec)
u_short newcode[];
global_info_type *gi;
int freg, inprec;
{
int	i=0, opndtype, opndprec, rx=(-1), breg,
	newfreg, free_newfreg=FALSE;
u_long	offset, instr, conversion;

	opndtype = hi_optype_of(gi, 0);
	if (opndtype == GREGTYPE)
		return(_fpa1_freg2op(newcode, gi, freg, inprec));
	if (opndtype != ADDRTYPE) {		/* something is REALLY wrong! */
		gi->fp_state->fpabort("fpa2gen: _fpa2_freg2op,opndtype");
	}
	opndprec = lo_optype_of(gi, 0);
	conversion = _fromprec[inprec][opndprec];
	if (conversion) {
		/*
		 * Need a floating register to do the conversion.
		 * If freg is marked scratch, we can just use that,
		 * otherwise, we need a new one.
		 */
		if (is_scratchf(gi,freg))
			newfreg = freg;
		else {
			newfreg = _fp_get_fltr(gi);
			free_newfreg = TRUE;
		}
		instr = fpa1inst(conversion,NO_IMM_DATA,(newfreg*2),(freg*2));
		i += _fp_new_inst_store(&newcode[i], gi, r0, instr);
		freg = newfreg;
	} /* end if conversion */

	offset = getopnd(gi, 0, 0);
	breg = byteval_of(gi, 0);
	if (offset) {	/* non-zero offset */
		rx = _fp_get_genr(gi);
		i += _fp_ea_addrtype(&newcode[i], rx, breg, offset);
		breg = rx;
	}
	instr = fpa2DMAread(freg, length_of(opndprec));
	i += _fp_new_inst_store(&newcode[i], gi, breg, instr);
	newcode[i++] = RTi_SYNC;
	if (rx != (-1)) fp_free_genr(gi, rx);
	if (free_newfreg) fp_free_fltr(gi, newfreg);

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


/*
 * _massage_operand
 * Computes the effective address of operand opnum into rx.
 */
static int
_massage_operand(newcode,gi,opndnum,rx,freg)
u_short			newcode[];
global_info_type	*gi;
int			opndnum,*rx,freg;
{
int	breg,offset;

	switch (hi_optype_of(gi, opndnum)) {
	case GREGTYPE:
		*rx = byteval_of(gi, opndnum);
		if (lo_optype_of(gi,opndnum) == DBLETYPE) {
			*rx >>= 4;
			return(_fpa1_storelo(newcode, gi, opndnum, freg));
		} else
			return (0);
	case IMMEDTYPE:
		gi->immcode[opndnum] = 1;	/* where hi offset of the */
						/* imm val should go */
		*rx = _fp_get_genr(gi);
		return(_fp_ea_addrtype(newcode, *rx, r0, (u_long) MAGIC_NUMBER));
	case ADDRTYPE:
		offset = getopnd(gi, opndnum, 0);
		breg = byteval_of(gi, opndnum);
		if (offset) {	/* non-zero offset */
			*rx = _fp_get_genr(gi);
			return(_fp_ea_addrtype(newcode, *rx, breg, offset));
		} else {
			*rx = breg;
			return(0);
		}
	default:
		gi->fp_state->fpabort("fpa2gen: _massage_operand,opndtype");
	} /* end switch */
} /* end _massage_operand */


/*
 * _fpa2_op2freg
 *
 * Store the operand specified by "opnum" into the floating point
 * register "freg".
 * Operand type can be GREGTYPE, ADDRTYPE or IMMEDTYPE
 *
 */
static int
_fpa2_op2freg(newcode, gi, opndnum, freg, outprec)
u_short newcode[];
global_info_type *gi;
int opndnum, freg, outprec;
{
int	opndprec, opndtype, rx, i=0, len;
u_long	instr, conversion;

	opndtype = hi_optype_of(gi, opndnum);
	if (opndtype == GREGTYPE)
		return(_fpa1_op2freg(newcode, gi, opndnum, freg, outprec));
	opndprec = lo_optype_of(gi, opndnum);
	conversion = _fromprec[opndprec][outprec];

	i += _massage_operand(&newcode[i], gi, opndnum, &rx, freg);
	len = length_of(opndprec);
	if (conversion)
		instr = fpa2inst(conversion, DMA_WRITE, IMM2OP1,
					(freg*2), (freg*2), len);
	else
		instr = fpa2DMAwrite(freg,len);
	i += _fp_new_inst_store(&newcode[i], gi, rx, instr);
	if (rx != byteval_of(gi, opndnum)) fp_free_genr(gi, rx);

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


/*
 * _fpa2_monadic
 *
 * Performs a monadic operation on 1 or 2 operands.
 */
static int
_fpa2_monadic(newcode, gi)
u_short			newcode[];
global_info_type	*gi;
{
int			rx, i=0, opprec,
			res, restype, resprec,
			arg, argtype, argprec,
			freg1=-1, freg2=-1, free1=FALSE;
u_long			instr;
uif_type		*data = gi->data;

	if (NO_DMA(gi)) return(_fpa1_monadic(newcode, gi));

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

	res = 0;
	restype = hi_optype_of(gi, res);
	resprec = lo_optype_of(gi, res);
	if (data->numopnds == 1) {
		arg = 0;
		argtype = restype;
		argprec = resprec;
	} else {
		arg = 1;
		argtype = hi_optype_of(gi, arg);
		argprec = lo_optype_of(gi, arg);
	}

	/*
	 * _fpas_prec expects two argument-precisions, but its
	 * logic can be used here if given the same precision twice.
	 */
	opprec = _fpas_prec(argprec, argprec, gi->fp_state->env_prec);

	/*
	 * set up work fregs.  Must have at least one.
	 */
	if (restype == FREGTYPE) {
		freg1 = byteval_of(gi, res);
		if (argtype == FREGTYPE) freg2 = byteval_of(gi, arg);
	} else if (argtype == FREGTYPE) {
		freg2 = byteval_of(gi, arg);
		if (is_scratchf(gi,freg2))
			freg1 = freg2;
		else {
			freg1 = _fp_get_fltr(gi);
			free1 = TRUE;
		}
	} else {
		freg1 = _fp_get_fltr(gi);
		free1 = TRUE;
	}

	/*
	 * Does it need conversion ?
	 */
	if (argprec != opprec) {
		if (argtype == FREGTYPE)
			i += _fpas_freg2freg(&newcode[i], gi,
					freg1, opprec, freg2, argprec);
		else {
			i += _fpa2_op2freg(&newcode[i], gi, arg, freg1, opprec);
			argtype = FREGTYPE;
		}
		freg2 = freg1;
		/*
		 * At this point argtype == FREGTYPE, argprec should be
		 * the same as opprec.  However, no need to set argprec
		 * because it is not used from here on.
		 */
	}

	/*
	 * Now do the instruction.
	 */
	switch (argtype) {
	case FREGTYPE:
		instr = fpa1inst (fpas_opcode(data->opcode,opprec),
					NO_IMM_DATA, (freg1*2), (freg2*2));
		i += _fp_new_inst_store(&newcode[i], gi, r0, instr);
		break;
	case GREGTYPE:
		i += _massage_operand(&newcode[i], gi, arg, &rx, freg1);
		instr = fpa1inst (fpas_opcode(data->opcode,opprec),
					IMM2OP1, (freg1*2), (freg1*2));
		i += _fp_new_inst_store(&newcode[i], gi, rx, instr);
		break;
	case ADDRTYPE:
	case IMMEDTYPE:
		i += _massage_operand(&newcode[i], gi, arg, &rx, freg1);
		instr = fpa2inst (fpas_opcode(data->opcode,opprec),
					DMA_WRITE, IMM2OP1,
					(freg1*2), (freg1*2),
					length_of(opprec));
		i += _fp_new_inst_store(&newcode[i], gi, rx, instr);
		if (rx != byteval_of(gi, arg)) fp_free_genr(gi, rx);
		break;
	default:
		gi->fp_state->fpabort("fpa2gen: _fpa2_monadic,argtype");
	} /* end switch */

	/*
	 * The result is now in freg1.  Do I need to move it anywhere?
	 */
	if (restype != FREGTYPE)
		i += _fpa2_freg2op(&newcode[i], gi, freg1, opprec);
	else if (resprec != opprec)
		i += _fpas_freg2freg(&newcode[i], gi,
				freg1, resprec, freg1, opprec);

	debugf(DB_DUMP, dump("Exit _fpa2_monadic:", newcode, i * 2));
	
	if (free1) fp_free_fltr(gi, freg1);
	return(i);
} /* end _fpa2_monadic */


/*
 * _fpa2_dyadic
 *
 * Performs a dyadic operation on 2 or 3 operands.
 */
static int
_fpa2_dyadic(newcode, gi)
u_short		newcode[];
global_info_type	*gi;
{
int	rx, i=0, numopnds, opprec,
	arg1, arg2, res,
	arg1type, arg2type, restype,
	arg1prec, arg2prec, resprec,
	arg1byte, arg2byte, resbyte,
	freg1, freg2, newfreg,
	free1=FALSE, free2=FALSE;
u_long	instr;
uif_type *data = gi->data;

	if (NO_DMA(gi)) return(_fpa1_dyadic(newcode, gi));

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

	numopnds = data->numopnds;
	if (numopnds == 2) {
		res = 0;
		arg1 = 0;
		arg2 = 1;
	} else {
		res = 0;
		arg1 = 1;
		arg2 = 2;
	}

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

	arg1type = hi_optype_of(gi, arg1);
	arg1prec = lo_optype_of(gi, arg1);
	arg1byte = byteval_of(gi, arg1);

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

	opprec = _fpas_prec(arg1prec, arg2prec, gi->fp_state->env_prec);

	/* We need to have 2 fregs to perform a dyadic operation */
	if (restype == FREGTYPE)
		freg1 = resbyte;
	else if (arg1type == FREGTYPE)
		freg1 = arg1byte;
	else {
		freg1 = _fp_get_fltr(gi);
		free1 = TRUE;
	}
	if (arg2type == FREGTYPE)
		freg2 = arg2byte;
	else {
		freg2 = _fp_get_fltr(gi);
		free2 = TRUE;
	}

	/*
	 * if numopnds==3, first operand is where to place the result,
	 * and the next two are the actual arguments.  Set EVERYTHING
	 * up so that it "looks like" the case where we have only 2
	 * operands, and then let the code fall through to that.
	 *
	 * EVERYTHING:
	 * 1.  The arg1, arg2 are two operands to the dyadic operation.
	 * 2.  We have 2 floating registers at hand: freg1, freg2;
	 *     freg1 is where the result will go.
	 * 3.  If arg1type==FREGTYPE ==> freg1==(arg1) though arg1prec
	 *	may or may not equal opprec.  Otherwise, freg1 is just
	 *	a plain scratch register.
	 * 4.  Condition (3) also applied to arg2.
	 *
	 */
	if (numopnds == 3) {
		if (restype == FREGTYPE) {
			if ((arg2type == FREGTYPE) && (arg2byte == resbyte)) {
				/*
				 * Potential freg conflit.
				 * Here we have:
				 * op	fregX,opnd,fregX
				 */
				if (is_commutative(data->opcode) &&
				    (arg2prec == resprec)) {
					/*
					 * Here we have:
					 * ADD/MUL	fregX,opnd,fregX
					 * No conflit.  This is a commutative
					 * operation, arg2 is redundant.
					 */
					arg2 = arg1;
					arg2type = arg1type;
					arg2prec = arg1prec;
					arg2byte = arg1byte;

					arg1 = res;
					arg1type = restype;
					arg1prec = resprec;
					arg1byte = resbyte;

					if (arg2type == FREGTYPE)
						freg2 = arg2byte;
					else {
						freg2 = _fp_get_fltr(gi);
						free2 = TRUE;
					}
				} else {
					/* freg conflit */
					if (arg1type != FREGTYPE) {
						freg1 = _fp_get_fltr(gi);
						free1 = TRUE;
						i += _fpa2_op2freg(&newcode[i],
							gi,arg1,freg1,opprec);
						arg1type = FREGTYPE;
						arg1prec = opprec;
					} else if (is_scratchf(gi,arg1byte)) {
						freg1 = arg1byte;
					} else if (arg1byte != freg1) {
						freg1 = _fp_get_fltr(gi);
						free1 = TRUE;
						i += _fpas_freg2freg(&newcode[i],
							gi, freg1, opprec, 
							arg1byte, arg1prec);
						arg1prec = opprec;
					}
				} /* end freg conflit */
			} else {
				/* no potential freg conflit */
				if (arg1type == FREGTYPE)
					i += _fpas_freg2freg(&newcode[i], gi,
							freg1, opprec, 
							arg1byte, arg1prec);
				else
					i += _fpa2_op2freg(&newcode[i], gi, 
							arg1, freg1, opprec);
				arg1 = res;
				arg1type = FREGTYPE;
				arg1prec = opprec;
			} /* end no potential conflit */
		} else if ((arg1type == FREGTYPE) &&
			   !(is_scratchf(gi,arg1byte))) {
				/*
				 * Oopp! can't use freg1
				 */
				freg1 = _fp_get_fltr(gi);
				free1 = TRUE;
				i += _fpas_freg2freg(&newcode[i], gi,
					freg1, opprec, arg1byte, arg1prec);
				arg1prec = opprec;
		} /* end restype!=FREGREG */
	} /* end numopnds==3 */

	/*
	 * Now convert all arguments to the proper precisions.
	 * arg1 is easy, because it just goes into freg1.
	 * arg2 could go into freg2, unless freg2 can not be used
	 *	as scratch.  In that case, we need to get another
	 *	fp scratch register.
	 */
	if (arg1prec != opprec) {
		if (arg1type != FREGTYPE) {
			i += _fpa2_op2freg(&newcode[i], gi, arg1, freg1, opprec);
			arg1type = FREGTYPE;
		} else {
			i += _fpas_freg2freg(&newcode[i], gi, 
					freg1, opprec, freg1, arg1prec);
			if ((arg2type == FREGTYPE) && (freg2 == freg1))
				arg2prec = opprec;
		}
	} /* end converting arg1 */

	if (arg2prec != opprec) {
		if (gi->data->opcode == FP_SCALB) {
			instr = FPA1WTFR(freg2*2);
			i += _fp_new_inst_store(&newcode[i], gi, arg2byte, instr);
			arg2type = FREGTYPE;
		} else if (arg2type != FREGTYPE) {
			i += _fpa2_op2freg(&newcode[i], gi, arg2, freg2, opprec);
			arg2type = FREGTYPE;
		} else if (is_scratchf(gi,freg2)) {
			i += _fpas_freg2freg(&newcode[i], gi,
					freg2, opprec, freg2, arg2prec);
		} else { /* need a scratch */
			newfreg = _fp_get_fltr(gi);
			free2 = TRUE;
			i += _fpas_freg2freg(&newcode[i], gi,
					newfreg, opprec, freg2, arg2prec);
			freg2 = newfreg;
		} /* end need a scratch */
	} /* end converting arg2 */

	/*
	 * Now do the instruction
	 */
	if (arg1type != FREGTYPE) {
		if (arg2type != FREGTYPE)
			i += _fpa2_op2freg(&newcode[i], gi, arg2, freg2, opprec);

		i += _massage_operand(&newcode[i], gi, arg1, &rx, freg1);
		if (arg1type == GREGTYPE)
			instr = fpa1inst (fpas_opcode(data->opcode,opprec),
					IMM2OP2, (freg1*2), (freg2*2));
		else
			instr = fpa2inst (fpas_opcode(data->opcode,opprec),
					DMA_WRITE, IMM2OP2,
					(freg1*2), (freg2*2),
					length_of(opprec));
		i += _fp_new_inst_store(&newcode[i], gi, rx, instr);
		if ((arg1type != GREGTYPE) && (rx != arg1byte))
			fp_free_genr(gi, rx);

	} else if (arg2type != FREGTYPE) {
		i += _massage_operand(&newcode[i], gi, arg2, &rx, freg2);
		if (arg2type == GREGTYPE)
			instr = fpa1inst (fpas_opcode(data->opcode,opprec),
					IMM2OP1, (freg1*2), (freg2*2));
		else
			instr = fpa2inst (fpas_opcode(data->opcode,opprec),
					DMA_WRITE, IMM2OP1,
					(freg1*2), (freg2*2),
					length_of(opprec));
		i += _fp_new_inst_store(&newcode[i], gi, rx, instr);
		if ((arg2type != GREGTYPE) && (rx != arg2byte))
			fp_free_genr(gi, rx);

	} else { /* both are FREGTYPE */
		instr = fpa1inst (fpas_opcode(data->opcode,opprec),
					NO_IMM_DATA, (freg1*2), (freg2*2));
		i += _fp_new_inst_store(&newcode[i], gi, r0, instr);
	} /* end both are FREGTYPE */

	/*
	 * The result is in freg1.  Do I need to move it anywhere?
	 */
	if (restype != FREGTYPE)
		i += _fpa2_freg2op(&newcode[i], gi, freg1, opprec);
	else if ((resprec != opprec) || (resbyte != freg1))
		i += _fpas_freg2freg(&newcode[i], gi,
			resbyte, resprec, freg1, opprec);

	if (free1) fp_free_fltr(gi, freg1);
	if (free2) fp_free_fltr(gi, freg2);

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


static int
_fpa2_init(gi)	/* FP_INIT */
global_info_type	*gi;
{
#ifdef FORCE
	u_long	ul;
#endif FORCE

	gi->fp_state->fp_MachRegs  = (u_long *) findfpm();
#ifdef FORCE
	ul = (u_long) findfpm();
	printf("findfpm = %.8x %d\n",ul,ul);
#endif FORCE
	return;
}


static int
_fpa2_move(newcode,gi)		/* FP_MOVE */
u_short			newcode[];
global_info_type	*gi;
{
int			intype, outtype, icode=0, freg;

	if (NO_DMA(gi)) return(_fpa1_move(newcode,gi));

	debugf(DB_ALL, dump("Enter _fpa2_move:",gi->data,gi->data->mysize));

	outtype = hi_optype_of(gi, 0);
	intype = hi_optype_of(gi, 1);

	if (outtype == FREGTYPE) {
		/*
		 * Target type is an freg, do moves TO freg
		 */
		if (intype == FREGTYPE)
			icode += _fpas_freg2freg(&newcode[icode], gi,
				byteval_of(gi, 0), lo_optype_of(gi, 0),
				byteval_of(gi, 1), lo_optype_of(gi, 1));
		else
			icode += _fpa2_op2freg(&newcode[icode], gi,
				1, byteval_of(gi, 0), lo_optype_of(gi, 0));

	} else if (intype == FREGTYPE)
		/*
		 * Source type is an freg, do moves FROM freg
		 */
		icode += _fpa2_freg2op(&newcode[icode], gi,
			byteval_of(gi, 1), lo_optype_of(gi, 1));

	else {
		/*
		 * None is freg, do moves TO a scratch freg,
		 * and then back out into target.
		 */
		freg = _fp_get_fltr(gi);
		icode += _fpa2_op2freg(&newcode[icode], gi,
				1, freg, lo_optype_of(gi, 0));
		icode += _fpa2_freg2op(&newcode[icode], gi,
				freg, lo_optype_of(gi, 0));
		fp_free_fltr(gi, freg);
	}

	debugf(DB_ALL, dump("Exit _fpa2_move:",newcode,icode * 2));

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


static int
_fpa2_storem(newcode,gi)		/* FP_STOREM */
u_short			newcode[];
global_info_type	*gi;
{
int		rx, basereg, icode=0,
		mask, current_freg, first_savef, last_savef;
u_long		offset, instr;

	if (NO_DMA(gi) || (gi->pushing_fltr_flag == MAGIC_NUMBER))
		return(_fpa1_storem(newcode,gi));

	/*
	 * First operand is mask of fregs to be stored.
	 * However, the fpa2 DMA only works on consecutive
	 * fregs.  So locate the first & last freg in mask.
	 * and storem everything in [first_savef,last_savef].
	 */
	mask = byteval_of(gi,0);
	if (mask == 0) return (0);
	current_freg = freg_num(FIRST_SAVEF);
	first_savef = FIRST_SAVEF;
	while ((mask & current_freg) == 0) {
		first_savef++;
		current_freg >>= 1;
	}
	current_freg = freg_num(LAST_SAVEF);
	last_savef = LAST_SAVEF;
	while ((mask & current_freg) == 0) {
		last_savef--;
		current_freg <<= 1;
	}

	/* write ZIC length register */
	rx = _fp_get_genr(gi);
	icode += _fp_getimm(&newcode[icode], rx, 2*(1+last_savef-first_savef));
	icode += _fp_new_inst_store(&newcode[icode], gi, rx, FPA2_WRITE_ZIC_LENGTH);

	/*
	 * Second operand is address of savearea.
	 */
	basereg = byteval_of(gi, 1);
	offset = gi->data->opr.operand[1].UL + (first_savef-FIRST_SAVEF)*8;
		/*
		 * getopnd(gi,1,0) CAN'T be used here because
		 * the 2nd operand's type is "ff", not ADDRTYPE,
		 * so it is not parsed properly by _init_global_info().
		 */
	if (offset) {
		icode += _fp_ea_addrtype(&newcode[icode], rx, basereg, offset);
		basereg = rx;
	}
	instr = fpa2DMAread(first_savef,N_WORDS);
	icode += _fp_new_inst_store(&newcode[icode], gi, basereg, instr);

	fp_free_genr(gi, rx);

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


static int
_fpa2_loadm(newcode,gi)		/* FP_LOADM */
u_short			newcode[];
global_info_type	*gi;
{
int			rx, breg, basereg, icode=0, i,
			mask, current_freg, hole;
u_long			offset, instr;

	if (NO_DMA(gi)) return(_fpa1_loadm(newcode,gi));
	/*
	 * First operand is mask of fregs to be restored.
	 */
	mask = byteval_of(gi, 0);
	if (mask == 0) return (0);
	/*
	 * Second operand is address of savearea.
	 */
	basereg = byteval_of(gi, 1);
	offset = gi->data->opr.operand[1].UL;
		/*
		 * getopnd(gi,1,0) CAN'T be used here because
		 * the 2nd operand's type is "ff", not ADDRTYPE,
		 * so it is not parsed properly by _init_global_info().
		 */
	if (gi->pushing_fltr_flag == MAGIC_NUMBER)
		hole = 0;
	else
		hole = 8;

	rx = _fp_get_genr(gi);
	current_freg = freg_num(FIRST_SAVEF);
	for (i=FIRST_SAVEF; i <= LAST_SAVEF; i++) {
		/*
		 * For each bit set (left-to-right), read the value from
		 * the save area and store it into the corresponding
		 * floating point register (doubles always).
		 */
		if (mask & current_freg) {
			if (offset) {
				breg = rx;
				icode += _fp_ea_addrtype(&newcode[icode], rx,
						basereg, offset);
			} else
				breg = basereg;
			instr = fpa2DMAwrite(i,TWO_WORDS);
			icode += _fp_new_inst_store(&newcode[icode], gi,
						breg, instr);
			offset += (8 - hole);	/* one of these is +0 */
		}
		offset += hole;		/* one of these is +0 */
		current_freg >>= 1;	/* advance to next freg */
	} /* end for */

	fp_free_genr(gi, rx);

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


#define SUPPORT
#define RELEASE

operations	_fpa2rtnes =
	{ /*** FPA2 DEFINITIONS ***/
	    _fpa2_monadic,	/* FP_ABS */
	    NULL,		/* FP_ACOS */
	    _fpa2_dyadic,	/* FP_ADD */
	    NULL,		/* FP_ASIN */
#ifdef RELEASE
	    _fpa2_monadic,	/* FP_ATAN 	new, release plan */
	    _fpa2_dyadic,	/* FP_ATAN2	new, release plan */
#else
	    NULL,		/* FP_ATAN */
	    NULL,		/* FP_ATAN2 */
#endif RELEASE
	    NULL,		/* FP_ATANH */
	    _fpa1_dyadic,	/* FP_CMP */
	    _fpa1_dyadic,	/* FP_CMPT */
	    NULL,		/* FP_RSVD */
#ifdef RELEASE
	    _fpa2_monadic,	/* FP_COS	new, release plan */
#else
	    NULL,		/* FP_COS */
#endif RELEASE
	    NULL,		/* FP_COSH */
	    _fpa2_dyadic,	/* FP_DIV */
#ifdef RELEASE
	    _fpa2_monadic,	/* FP_EXP	new, release plan */
#else
	    NULL,		/* FP_EXP */
#endif RELEASE
	    NULL,		/* FP_EXPM1 */
	    _fpa2_loadm,		/* FP_LOADM */
#ifdef RELEASE
	    _fpa2_monadic,	/* FP_LOG10	new, release plan */
#else
	    NULL,		/* FP_LOG10 */
#endif RELEASE
	    NULL,		/* FP_LOG1P */
#ifdef RELEASE
	    _fpa2_monadic,	/* FP_LOG	new, release plan */
	    _fpa2_monadic,	/* FP_LOGB	new, release plan */
#else
	    NULL,		/* FP_LOG */
	    NULL,		/* FP_LOGB */
#endif RELEASE
	    _fpa2_move,		/* FP_MOVE */
	    _fpa2_dyadic,	/* FP_MUL */
	    _fpa2_monadic,	/* FP_NEG */
#ifdef SUPPORT
	    _fpa2_dyadic,	/* FP_REM	new, supported */
#else
	    NULL,		/* FP_REM	fix up on inexact */
#endif SUPPORT
	    _fpa2_monadic,	/* FP_RINT	new, supported */
	    			/* FP_RINT	NEEDS fix up on NAN */
#ifdef RELEASE
	    _fpa2_dyadic,	/* FP_SCALB	new, release plan */
#else
	    NULL,		/* FP_SCALB */
#endif RELEASE
#ifdef RELEASE
	    _fpa2_monadic,	/* FP_SIN	new, release plan */
#else
	    NULL,		/* FP_SIN */
#endif RELEASE
	    NULL,		/* FP_SINH */
#ifdef SUPPORT
	    _fpa2_monadic,	/* FP_SQRT	new, supported */
#else
	    NULL,		/* FP_SQRT	fix up on denorm */
#endif SUPPORT
	    _fpa2_storem,	/* FP_STOREM */
	    _fpa2_dyadic,	/* FP_SUB */
#ifdef RELEASE
	    _fpa2_monadic,	/* FP_TAN	new, release plan */
#else
	    NULL,		/* FP_TAN */
#endif RELEASE
	    NULL,		/* FP_TANH */
	    _fpa2_init,		/* FP_INIT */
	    NULL,		/* FP_WHICH */
	    _fpa1_setround,	/* FP_SETROUND */
	    _fpa1_testround,	/* FP_TESTROUND */
	    _fpa1_setflag,	/* FP_SETFLAG */
	    _fpa1_testflag,	/* FP_TESTFLAG */
	    _fpa1_settrap,	/* FP_SETTRAP */
	    _fpa1_testtrap,	/* FP_TESTTRAP */
	    _fpa1_clrflag,	/* FP_CLRFLAG */
	    _fpa1_clrtrap	/* FP_CLRTRAP */
	};

