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

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

#include <machine/fp.h>
#include <ieee.h>
#include "fpemul.h"
#include "fpgen.h"
#include "general.h"
#include "emulfuns.h"

#define FPM (*fp_regs)          /* Define Floating Point Machine registers  */
#define GPM (*gpr_regs)		/* Define General Purpose Machine registers */
/*
 * This array contains pointers to the emulator double format functions.
 */
static FP_DOUBLE
    (*emul_d_tbl_ops[NUM_OPCODES])() = {	_FPabd,
						NULL,		/* ACOS */
						_FPadd,
						NULL,		/* ASIN */
						NULL,		/* ATAN */
						NULL,		/* ATAN2 */
						NULL,		/* ATANH */
						_FPcmd,		/* CMP */
						_FPcmd,		/* CMPT */
						NULL,		/* CMPB */
						NULL,		/* COS */
						NULL,		/* COSH */
						_FPdvd,
						NULL,		/* EXP */
						NULL,		/* EXPM1 */
						NULL,		/* LOADM */
						NULL,		/* LOG10 */
						NULL,		/* LOG1P */
						NULL,		/* LOG */
						NULL,		/* LOGB */
						_FPcpd,
						_FPmld,
						_FPngd,
						NULL,		/* REM */
						NULL,		/* RINT */
						NULL,		/* SCALB */
						NULL,		/* SIN */
						NULL,		/* SINH */
						NULL,		/* SQRT */
						NULL,		/* STOREM */
						_FPsbd,
						NULL,		/* TAN */
						NULL,		/* TANH */
						NULL,		/* INIT */
						NULL,		/* WHICH */
						NULL,		/* SETROUND */
						NULL,		/* TESTROUND */
						NULL,		/* SETFLAG */
						NULL,		/* TESTFLAG */
						NULL,		/* SETTRAP */
						NULL,  		/* TESTTRAP */
						NULL,           /* CLRFLAG  */
						NULL };		/* CLRTRAP  */



/*
 * This array contains pointers to the emulator single format functions.
 */
static u_long 
(*emul_s_tbl_ops[NUM_OPCODES])() = {	_FPabf,
						NULL,		/* ACOS */
						_FPadf,
						NULL,		/* ASIN */
						NULL,		/* ATAN */
						NULL,		/* ATAN2 */
						NULL,		/* ATANH */
						_FPcmf,		/* CMP */
						_FPcmf,		/* CMPT */
						NULL,		/* CMPB */
						NULL,		/* COS */
						NULL,		/* COSH */
						_FPdvf,
						NULL,		/* EXP */
						NULL,		/* EXPM1 */
						NULL,		/* LOADM */
						NULL,		/* LOG10 */
						NULL,		/* LOG1P */
						NULL,		/* LOG */
						NULL,		/* LOGB */
						_FPcpf,
						_FPmlf,
						_FPngf,
						NULL,		/* REM */
						NULL,		/* RINT */
						NULL,		/* SCALB */
						NULL,		/* SIN */
						NULL,		/* SINH */
						NULL,		/* SQRT */
						NULL,		/* STOREM */
						_FPsbf,
						NULL,		/* TAN */
						NULL,		/* TANH */
						NULL,		/* INIT */
						NULL,		/* WHICH */
						NULL,		/* SETROUND */
						NULL,		/* TESTROUND */
						NULL,		/* SETFLAG */
						NULL,		/* TESTFLAG */
						NULL,		/* SETTRAP */
						NULL,  		/* TESTTRAP */
						NULL,           /* CLRFLAG  */
						NULL };		/* CLRTRAP  */


/*
 * _emul_op2freg     
 *									       
 * This function will store the operand specified by "opnum" into the          
 * floating point register "freg" in the percision specified by "outprec".     
 *									       
 * Inputs:								       
 * *gi      - points to general info. structure				       
 * opnum    - operand number (0, 1, and 2 are valid for most instructions)     
 * freg	    - fp reg number  (0 - 7 are valid fp reg numbers)		       
 * outprec  - requested precision (1 - single, 2 - double)		       
 *									       
 *
 */
int 
_emul_op2freg(gi, opnum, freg, outprec)
global_info_type *gi;
int opnum, freg, outprec;
{
int	opndprec, opndtype; 		/* precision and type of operand      */
int	hi_reg, reg; 			/* high & low half of double reg pair */

struct regs { u_long reg[16];} *gpr_regs;    /* general purpose regs          */

DOUBLE *store;				/* address of operand if in storage   */

u_long offset; 				/* offset to a address type operand   */

FP_MACH *fp_regs;			/* emulator's floating point regs.    */

	/* Set pointers to floatint point and general purpose regs            */

        fp_regs  =  (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	gpr_regs = (struct regs *) (gi->fp_RegSave);

	/* Get information on operand from universal floating point instr.    */

	opndtype = hi_optype_of(gi, opnum);
	opndprec = lo_optype_of(gi, opnum);
	reg = (byteval_of(gi, opnum) & 0x0f);
	hi_reg = (byteval_of(gi, opnum) >> 4);
 
	switch (opndtype)
	{
	case ADDRTYPE:
		/* 
		 * Operands which are of ADDR type are addressed using a
		 * value in a general register and a 32 bit offset from
		 * operands optional field.  Since floating point regs.
		 * can contain only doubles or floats, all integers will
		 * be converted to either float of double then stored in
		 * the floating point register.
		 */
		offset = getopnd(gi, opnum, 0);
		if (reg != 0) {
			store = (DOUBLE *) GPM.reg[reg];
			store = (DOUBLE *) ((int) store + offset); 
		}
		else
			store = (DOUBLE *) (offset);
		switch (opndprec)
		{
		case DBLETYPE:
			FPM.dreg[freg] = (*store);
			break;
		case SGLETYPE:	
			FPM.dreg[freg].dfracth = (*store).dfracth;   
			break;
		case INTTYPE:
		case UINTTYPE:
			if (outprec == DBLETYPE)
				_FPi2d(freg,(*store).dfracth);    
			else
				_FPi2f(freg,(*store).dfracth);    
			break;
		default:
			break;
		}
		break;
	case FREGTYPE:
		/*
		 * This will be an floating point reg. to floating point reg.
		 * move.  This move will also convert to the correct precision.
		 */
		_emul_freg2freg(gi,freg,outprec,reg,opndprec);
		break;
	case GREGTYPE:
		/*
		 * Operands of this type are found in general registers
		 * (the "glue code's" saved copy of the users gpr's).
		 * Here too the integers are first converted to float
		 * or double and the stored in the floating point reg.
		 */
		switch (opndprec)
		{
		case DBLETYPE:
			FPM.dreg[freg].dfracth = GPM.reg[hi_reg];
			FPM.dreg[freg].dfractl = GPM.reg[reg];
			break;
		case SGLETYPE:	
			FPM.dreg[freg].dfracth = GPM.reg[reg];
			break;
		case INTTYPE:
		case UINTTYPE:
			if (outprec == DBLETYPE)
				_FPi2d(freg,GPM.reg[reg]);
			else
				_FPi2f(freg,GPM.reg[reg]);
			break;
		default:
			break;
		}
		break;
	case IMMEDTYPE:
		/*
		 * Immediate operands are contain within the universal
		 * floating point instruction.  Immediate integers are
		 * also converted to float or double then stored in the 
		 * floating point register.
		 */ 
		switch (opndprec)
		{
		case DBLETYPE:
			FPM.dreg[freg].dfracth = getopnd(gi,opnum,0);
			FPM.dreg[freg].dfractl = getopnd(gi,opnum,1);
			break;
		case SGLETYPE:	
			FPM.dreg[freg].dfracth = getopnd(gi,opnum,0);
			break;
		case INTTYPE:
		case UINTTYPE:
			if (outprec == DBLETYPE)
				_FPi2d(freg, (getopnd(gi,opnum,0)));
			else
				_FPi2f(freg, getopnd(gi,opnum,0));
			break;
		default:
			break;
		}
		break;
	default:
		break;
	}
	if (opndtype == FREGTYPE || opndprec == outprec || 
	    opndprec == INTTYPE  || opndprec == UINTTYPE)
	;	
		/**********************************************
		 * NULL IF
		 **********************************************
		 */
		/*
		 * These operands have already been converted to
		 * the required precision.
		 */
	else
	{
		/*
		 * Now convert the value of the floating point reg to	
		 * the precision of the operation.
		 */
		if (outprec == DBLETYPE)
			_FPf2d(freg);
		else
			_FPd2f(freg);
	}
	return;

}	/* end of _emul_op2freg						*/
/*
 * _emul_freg2op                                                 
 * 									       
 * Place the content of floating point reg "freg" in the result operand,       
 * specified by "opnum".  The precision of the result is specified in the      
 * universal instruction.						       
 * 									       
 * Inputs:								       
 * *gi      - points to general info. structure				       
 * freg	    - fp reg number  (0 - 7 are valid fp reg numbers)		       
 * fregprec - precision of freg (0 - int, 1 - single, 2 - double)	       
 * opnum    - operand number (0, 1, and 2 are valid for most instructions)     
 * 									       
 */
static int 
_emul_freg2op(gi, freg, fregprec, opnum)
global_info_type *gi;
int freg, opnum, fregprec;
{
int	 opndprec, opndtype; 	/* The precision and type of the operand      */
int	 hi_reg, reg; 		/* The high & low regs. of double reg. pair   */

struct regs { u_long reg[16]; } *gpr_regs;   /* general purpose regs          */

DOUBLE *store;          	/* The addr. of an operand in storage         */

u_long offset, intval; 		/* The offset to the address type operand     */

FP_MACH *fp_regs;	        /* emulator's floating point regs.            */

	/* Set pointers to floating point and general purpose registers       */

        fp_regs  = (FP_MACH* ) (gi->fp_state->fp_MachRegs);                
	gpr_regs = (struct regs *) (gi->fp_RegSave);

	/* Get info. on operand from universal floating point instruction.    */

	opndtype = hi_optype_of(gi, opnum);
	opndprec = lo_optype_of(gi, opnum);
	reg = (byteval_of(gi, opnum) & 0x0f);
	hi_reg = (byteval_of(gi, opnum) >> 4);
 
	if (fregprec != opndprec)
	{
		/*
		 * Convert the value of the floating point reg to	
		 * the precision of the result.   
		 */
		if (opndprec == DBLETYPE)
			_FPf2d(freg);
		else if (opndprec == SGLETYPE)
			_FPd2f(freg);
		else if ((opndprec == INTTYPE || opndprec == UINTTYPE)
                          && fregprec == DBLETYPE)
			intval = _FPtrd(freg);
		else 
			intval = _FPtrf(freg);
	}
	switch (opndtype)
	{
	case ADDRTYPE:
		/* 
		 * Operands which are of ADDR type are addressed using a
		 * value in a general register and a 32 bit offset from
		 * operands optional field.
		 */
		offset = getopnd(gi, opnum, 0);
		if (reg != 0) {
			store = (DOUBLE *) GPM.reg[reg];
			store = (DOUBLE *) ((int) store + offset); 
		}
		else
			store = (DOUBLE *) (offset);
		switch (opndprec)
		{
		case DBLETYPE:
  			(*store) = FPM.dreg[freg] ;
			break;
		case SGLETYPE:	
			(*store).dfracth = FPM.dreg[freg].dfracth ;   
			break;
		case INTTYPE:
		case UINTTYPE:
			(*store).dfracth = intval;    
			break;
		default:
			break;
		}
		break;
	case FREGTYPE:
		/*
		 * Operands of this type are found in floating point registers.
		 */
		_emul_freg2freg(gi,reg,opndprec,freg,opndprec);
		break;
	case GREGTYPE:
		/*
		 * Operands of this type are found in general registers
		 * (really the "glue code's" save area for caller's gpr's).
		 */
		switch (opndprec)
		{
		case DBLETYPE:
 			GPM.reg[hi_reg] = FPM.dreg[freg].dfracth;
 			GPM.reg[reg] = FPM.dreg[freg].dfractl;
			break;
		case SGLETYPE:	
 			GPM.reg[reg] = FPM.dreg[freg].dfracth ;
			break;
		case INTTYPE:
		case UINTTYPE:
			GPM.reg[reg] = intval;
			break;
		default:
			break;
		}
		break;
	case IMMEDTYPE:
		/*
		 * This case is an error result can not be immediate.
		 */
		break;
	default:
		break;
	}
	return;
}	/* end of _emul_freg2op						*/

/*
 * _emul_freg2freg     
 * 								      
 * Copies from one freg to another (fr1 <- fr2) and then performs any 
 * conversion, required to put fr1 into the required precision. This  
 * function will not convert the target to integer, only double or    
 * single.     							      
 *
 * Inputs:                                                            
 * *gi      - points to general info. structure                       
 * fr1 	    - fp reg number  (0 - 7 are valid fp reg numbers)         
 * prec1    - precision of freg (1 - single, 2 - double (default))    
 * fr2 	    - fp reg number  (0 - 7 are valid fp reg numbers)         
 * prec2    - precision of freg (0 - int, 1 - single, 2 - double)     
 *
 */
static int 
_emul_freg2freg(gi, fr1, prec1, fr2, prec2)
global_info_type *gi;
int	fr1, prec1, fr2, prec2;
{
FP_MACH *fp_regs;		/* emulator's floating point regs.            */

	/* Set pointer to emulator's floating point registers                 */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                

	/* First, copy register 2 to register 1;			      */

	if (fr1 != fr2)
	{
		FPM.dreg[fr1] = FPM.dreg[fr2];
	}

	/* then, convert register 1 to the correct precision 		      */

	if (prec1 != prec2)
	{
		if (prec1 == SGLETYPE)
		
			_FPd2f(fr1);
		else
			_FPf2d(fr1);
	}
	return;
}	/* end of _emul_freg2freg			*/

/*
 * _emul_monadic              
 *								      
 * Perform all emulator monadic functions. The monadic functions      
 * supported by the emulator are:                                     
 * FP_ABS         						      
 * FP_NEG							      
 *
 * Inputs:                                                            
 * *gi      - points to general info. structure                       
 *         
 */

_emul_monadic(newcode,gi)
u_short		newcode[];
global_info_type	*gi;
{
struct regs { DOUBLE dreg[8]; } reg_save;  /* save area for fp regs          */
   
int	numopnds, 			   /* the number of operand          */
	arg1, arg1type, arg1prec, /* index to, type of,& precision of arg.1  */
	res,				   /* index to result operand        */
	opprec,				   /* precision of operation         */
	freg1;				   /* floating point register number */

uif_type *data;                            /* Universal Floating point Instr.*/

FP_MACH *fp_regs;			   /* emulator's floating point regs.*/

	/* Set pointers to the emulator's floating point regs. and to the
	 * universal floating point instruction.
	 */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	data = gi->data;

	/* Get general information about this operation			*/

	numopnds = data->numopnds;

	if (numopnds == 1) 
	{
		res = 0;
		arg1 = 0;
	}
	else 
	{
		res = 0;
		arg1 = 1;
	}

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


	/*
	 * The operation will be preformed in double if arg1 is a double;
	 * else it will be performed in single.
	 */
	opprec = _fpas_prec(arg1prec,arg1prec,gi->fp_state->env_prec);

	if (arg1type != FREGTYPE)
	{
		/*	
		 * If arg1 is not a floating point register
		 * then get a register and move arg1 into it.
		 */
		freg1 = _fp_get_fltr(gi);
		reg_save.dreg[freg1] = FPM.dreg[freg1] ;
		_emul_op2freg(gi,arg1,freg1,opprec);
	}
	else
	{
		/* 
		 * Otherwise, arg1 is already in a float reg.
		 * Arg1 must also be of the same precision as the
		 * operation to be performed.
		 */
		if (arg1 == res)
		{
			freg1 = byteval_of(gi, arg1);
			if (arg1prec != opprec)
				_emul_freg2freg(gi,freg1,opprec,freg1,arg1prec); 
		}
		else
		{
		        freg1 = _fp_get_fltr(gi);
		        reg_save.dreg[freg1] = FPM.dreg[freg1] ;
		        _emul_freg2freg(gi,freg1,opprec,byteval_of(gi,arg1),                                            arg1prec); 
		}
	}
			           
	/*
	 * If operation precision is double use a double format function;
	 * else use a single format function.
	 */

	if (opprec == DBLETYPE)
		(emul_d_tbl_ops[data->opcode])(freg1);
	else
		(emul_s_tbl_ops[data->opcode])(freg1);

	/* Move result of operation to result argument			*/
	_emul_freg2op(gi,freg1,opprec,res);

	/* Restore any "pushed" floating point registers		*/
	_emul_rst_fltr(gi,&reg_save);

	return(0) ;
}	/* end of _emul_monadic						*/

/*
 * _emul_dyadic     
 *								    
 * Perform all emulator dyadic functions.  The emulator supports    
 * the following dyadic functions:    				    
 * FP_ADD							    
 * FP_DIV							    
 * FP_MUL							    
 * FP_SUB						            
 *          
 * Inputs:							    
 * *gi      - points to general info. structure			    
 *         
 */

int
_emul_dyadic(newcode,gi)
u_short		newcode[];
global_info_type	*gi;
{
struct regs { DOUBLE dreg[8]; } reg_save;  	/* fp reg save area          */
   
int	numopnds,         	   	/* the number of operands in the op. */
	arg1, arg2, res,		/* index to arg1, arg2, & result     */
	arg1type, arg2type, restype,    /* type of arg1, arg2, & result      */
	arg1prec, arg2prec, opprec,     /* precision of arg1, arg2, & result */
	freg1, freg2;			/* floating point register numbers   */

uif_type	*data;			/* Universal Floating point Instr.   */

FP_MACH *fp_regs;	                /* emulator's floating point regs.   */

	/* Set pointers to emulator's floating point res. & UFI              */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	data = gi->data;

	/* Get general information about the operation			     */

	numopnds = data->numopnds;

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

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

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

	restype = hi_optype_of(gi, res);

	/*
	 * The operation will be preformed in the longest precision of
	 * of arg1 or arg2 or single.   
	 */
	opprec = _fpas_prec(arg1prec,arg2prec,gi->fp_state->env_prec);

	if (numopnds == 2)
	{
		if (arg1type != FREGTYPE)
		{
			/*	
			 * If arg1 is not a floating point register
			 * then get a register and move arg1 into it.
			 */
			freg1 = _fp_get_fltr(gi);
			reg_save.dreg[freg1] = FPM.dreg[freg1] ;
			_emul_op2freg(gi,arg1,freg1,opprec);
		}
		else
		{
			/* 
			 * Otherwise, arg1 is already in a float reg.
			 * Arg1 must also be of the same precision as the
			 * operation to be performed.
			 */
			freg1 = byteval_of(gi, arg1);
			if (arg1prec != opprec)
			{
				_emul_freg2freg(gi,freg1,opprec,freg1,arg1prec); 
				if ((arg2type == FREGTYPE) &&
				    (freg1 == byteval_of(gi,arg2)))
					arg2prec = opprec;
					/*
					 * To handle ADD S:fr0,S:fr0 with
					 * FP_PRECISION=double
					 */
			}
		}
	}			
	else 
	/*
	 *  For three operand dyadic functions, get a floating point reg. for
	 *  the result, if needed, and then move arg1 into it.
	 */
	{
		if (restype != FREGTYPE || arg2type == FREGTYPE && 
		    (byteval_of(gi,res)) == (byteval_of(gi,arg2)))
			
		{
			/*	
			 * If result is not a floating point register
			 * then get a register.
			 */
			freg1 = _fp_get_fltr(gi);
			reg_save.dreg[freg1] = FPM.dreg[freg1] ;
		}
		else
		{
			/* 
			 * Otherwise, result is already in a float reg.
			 */
			freg1 = byteval_of(gi, res);
		}
		_emul_op2freg(gi,arg1,freg1,opprec);
	}			

	if ((arg2type != FREGTYPE) || (arg2prec != opprec))
	{
		/*	
		 * If arg2 is not a floating point register
		 * then get a register and move arg2 into it.
		 */
		freg2 = _fp_get_fltr(gi);
		reg_save.dreg[freg2] = FPM.dreg[freg2] ;
		_emul_op2freg(gi,arg2,freg2,opprec);
	}
	else
	{
		/* 
		 * Otherwise, arg2 is already in a float reg.
		 * Arg2 must be the same precision as the operation.
		 */
		freg2 = byteval_of(gi, arg2);
		if (arg2prec != opprec)
			_emul_freg2freg(gi,freg2,opprec,freg2,arg2prec); 
	}

	/*
	 * If operation precision is double use a double format function;
	 * else use a single format function.
	 */

	if (opprec == DBLETYPE)
		(emul_d_tbl_ops[data->opcode])(freg1, freg2);
	else
		(emul_s_tbl_ops[data->opcode])(freg1, freg2);

	/* Move result of operation to result argument			*/
	_emul_freg2op(gi,freg1,opprec,res);

	/* Restore any "pushed" floating point registers		*/
	_emul_rst_fltr(gi,&reg_save);

	return(0) ;
}	/* end of _emul_dyadic						*/
/*
 * _emul_moveop          
 *									 
 * Move the content of the second operand, in the percision of the first 
 * operand, into the first operand.					 
 *									 
 *     
 * Inputs:								 
 * *gi      - points to general info. structure				 
 *     
 */
int
_emul_moveop(newcode,gi)

u_short		newcode[];
global_info_type	*gi;
{
int	intype, 		/* the type of the source operand	  */
	inprec,outprec,opprec,  /* precision of source,target,& operation */
	arg1,arg2,		/* index to target and source		  */
	freg;			/* floating point register number	  */

struct regs { DOUBLE dreg[8]; } reg_save;  /* save area for fp regs       */
FP_MACH *fp_regs;	        /* emulator's floating point regs.        */

	/* Set pointer to the emulator's floating point registers         */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                

	/* Get general information about operation			  */

	arg1 = 0;
	arg2 = 1;

	outprec = lo_optype_of(gi, arg1);

	intype = hi_optype_of(gi, arg2);
	inprec = lo_optype_of(gi, arg2);

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

	if (intype != FREGTYPE || inprec != opprec || inprec != outprec)
	{
		/*	
		 * If arg2 is not a floating point register or not the
		 * required precision; then get a reg. and move arg2 into it.
		 */
		freg = _fp_get_fltr(gi);
		reg_save.dreg[freg] = FPM.dreg[freg] ;
		_emul_op2freg(gi,arg2,freg,opprec);
	}
	else
	{
		/* 
		 * Otherwise, arg2 is already in a float reg.
		 */
		freg = byteval_of(gi, arg2);
	}
			           
	/* Move result of operation to argument 1     			*/
	_emul_freg2op(gi,freg,opprec,arg1);

	/* Restore any "pushed" floating point registers		*/
	_emul_rst_fltr(gi,&reg_save);

	return(0) ;
}	/* end of _emul_moveop						*/

/*
 *     _emul_compare     
 *									      
 * This function will perform both compare and compare with trap.	      
 * Operand 1 will be compared to operand 2.  The result of the compare will   
 * be stored in the general info. structure.  The glue code will transfer     
 * the compare result from the generla info. structure to the condition code, 
 * just before returning to the caller.					      
 *									      
 *
 * Inputs:								      
 * *gi      - points to general info. structure				      
 *
 */

/* Defines for values found in emulator status reg. compare result field.    */
#define	CMP_LT 0 				/* Emulator status: op1<op2  */
#define	CMP_EQ 1 				/* Emulator status: op1=op2  */
#define	CMP_GT 2 				/* Emulator status: op1>op2  */
#define	CMP_UNORDERED 3 			/* Emulator status: reserved */

/* Defines for values found in RT condition status register.                 */
#define	CS_LT 0x40				/* Condition status: op1<op2 */
#define	CS_EQ 0x20				/* Condition status: op1=op2 */
#define	CS_GT 0x10 				/* Condition status: op1>op2 */

int
_emul_compare(newcode,gi)

u_short		newcode[];
global_info_type	*gi;
{
struct regs { DOUBLE dreg[8]; } reg_save;       /* fp reg save area          */

FP_MACH *fp_regs;				/* emulator's fp registers   */
   
int	arg1, arg2, 			/* index to arg.1 and arg.2	     */
	arg1type, arg2type,     	/* type or arg1, arg2, and operation */
	arg1prec, arg2prec, opprec,	/* precision of arg1,arg2,& operation*/
	freg1, freg2;			/* floating point register numbers   */
uif_type	*data;

	/* Set pointer to emulator's fp registers and UFI		     */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	data = gi->data;

	/* Get general information about the operation			     */

	arg1 = 0;
	arg2 = 1;

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

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

	/*
	 * The operation will be preformed in the longest precision of
	 * of arg1 or arg2.             
	 */
	opprec = _fpas_prec(arg1prec,arg2prec,gi->fp_state->env_prec);

	if ((arg1type != FREGTYPE) || (arg1prec != opprec))
	{
		/*	
		 * If arg1 is not a floating point register
		 * or not the correct precision
		 * then get a register and move arg1 into it.
		 */
		freg1 = _fp_get_fltr(gi);
		reg_save.dreg[freg1] = FPM.dreg[freg1] ;
		_emul_op2freg(gi,arg1,freg1,opprec);
	}
	else
	{
		/* 
		 * Otherwise, arg1 is already in a float reg. and arg1 
		 * is the same precision as the operation to be performed.
		 */
		freg1 = byteval_of(gi, arg1);
	}

	if ((arg2type != FREGTYPE) || (arg2prec != opprec))
	{
		/*	
		 * If arg2 is not a floating point register
		 * or not the correct precision
		 * then get a register and move arg2 into it.
		 */
		freg2 = _fp_get_fltr(gi);
		reg_save.dreg[freg2] = FPM.dreg[freg2] ;
		_emul_op2freg(gi,arg2,freg2,opprec);
	}
	else
	{
		/* 
		 * Otherwise, arg2 is already in a float reg. and arg2
		 * is the same precision as the operation to be performed.
		 */
		freg2 = byteval_of(gi, arg2);
	}

	/* If this is a compare with trap, then set the trap bit	*/
	if (data->opcode == FP_CMPT)
		freg1 |= ExceptOnUnordered ;

	/*
	 * If operation precision is double use a double format function;
	 * else use a single format function.
	 */

	if (opprec == DBLETYPE)
		(emul_d_tbl_ops[data->opcode])(freg1, freg2);
	else
		(emul_s_tbl_ops[data->opcode])(freg1, freg2);


	/* Save condition code, so it can be restored by glue code.
	 */

	*(gi->emul_cstat) = 0;

	switch (FPM.status.cmp_rslt)
	{
	case CMP_LT:   
		/* 
		   Set emulator condition state to op1 < op2.
		 */
		*(gi->emul_cstat) = CS_LT;  
		break;

	case CMP_EQ:     
		/* 
		   Set emulator condition state to op1 = op2.
		 */
		*(gi->emul_cstat) = CS_EQ;
		break;
	
	case CMP_GT:
		/* 
		   Set emulator condition state to op1 > op2.
		 */
		*(gi->emul_cstat) = CS_GT;  
		break;

	default:
		break;
	}

	/* Restore any "pushed" floating point registers		*/
	_emul_rst_fltr(gi,&reg_save);

	return(0) ;
}	/* end of _emul_compare						*/

/* 
 * _emul_rst_fltr     
 *									
 * This function is called to restore all "pushed" floating point	
 * register.  As, indicated by the mask in gi->pushed_fltr        	
 *									
 * Input values:							
 * gi->pushed_fltr	 1 bits indicate the pushed regs    		
 *									
 * Side effects:							
 * gi->avail_fltr 	 1 bits indicate available regs		
 * gi->pushable_fltr	 1 bits indicate a pushable reg		
 * gi->pushed_fltr	 0 bits indicate regs no longer pushed		
 *									
 * Return values:        0   
 */

static int  
_emul_rst_fltr(gi,reg_save)
global_info_type	*gi;

struct regs { DOUBLE dreg[8]; } *reg_save;   	/* FP_DOUBLE regs 0-7         */

{
int	freg; 			/* floating point register number             */

FP_MACH *fp_regs;	        /* emulator's floating point regs.	      */

	/* Set pointer to emulator's floating point registers	       	      */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                

	/* restore all pushed registers					      */

	while (gi->pushed_fltr)		/* While there are pushed regs        */
	{
		freg = 8 - ffs(gi->pushed_fltr);	/* pick one           */

	 	FPM.dreg[freg] = (*reg_save).dreg[freg]; /* restore reg   */

		C_clrbit(gi->pushed_fltr,freg);		/* mark it not pushed */
		C_setbit(gi->avail_fltr,freg);		/* it's availiable    */
		C_setbit(gi->pushable_fltr,freg);	/* and pushable       */
        }
	return ;
} /* end _emul_rst_fltr */

/*
 * _emul_ldm_stm     
 *									       
 * Perform a load or store multiple (defined by data->opcode).  Operand 1      
 * 'byte' contains a mask of floating point registers to be loaded/stored.     
 * Operand 2 will be the address of the area to store into, when data->opcode  
 * g FP_STOREM, or loaded from, when data->opcode  FP_LOADM.		       
 *									       
 * Inputs:								       
 * *gi      - points to general info. structure				       
 *
 */
int 
_emul_ldm_stm(newcode,gi)

u_short		newcode[];
global_info_type *gi;

{

int	opnum, 				/* operand number		      */
	greg, 				/* gpr register number		      */
	freg,				/* floating point register number     */
	i;   				/* index                              */


u_char	mask;				/* mask of regs. to be loaded/stored  */

u_long	offset; 			/* offset to storage source or target */

DOUBLE	(*store);                       /* storage pointer 		      */

uif_type *data;				/* Universal Floating point Instr.    */

struct regs { u_long reg[16];} *gpr_regs;    /* general purpose regs	      */

FP_MACH *fp_regs;			/* emulator's floating point regs.    */

	/*Set pointers to emulator's fp regs. and to gp regs. and UFI         */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	gpr_regs = (struct regs *) (gi->fp_RegSave);
	data = gi->data;

	opnum = 1 ;	/* second operand is the storage address	*/

	/*
	 * Operands 2 must be the address of a storage area.  This address is
	 *  the value in a general register added to a 32 bit offset from
	 * operands optional field.
	 */

	greg = (byteval_of(gi, opnum) & 0x0f);
	offset = gi->data->opr.operand[1].UL;
  

	if (greg !=0) 
	{
		store = (DOUBLE *) GPM.reg[greg];
		store = (DOUBLE *) ((int) store + offset); 
	}
	else
	{
		store = (DOUBLE *) (offset);
	}


	opnum = 0 ;	/* The first operand is the mask	      */

	mask = byteval_of(gi, opnum);

	/*
	 * load/store each register specified by the mask
	 *
	 */
	
	i=0;
	while (mask)		
	{
		freg = 8 - ffs(mask);
		/*
	 	* If storem source is floating point regs and target is user
	 	* data area.  If loadm source is user data area and target is
	 	* floating point regs.
	 	*/
		if (data->opcode == FP_STOREM)
		{
			*(store + i) = FPM.dreg[freg];
		}
		else 
		{
			FPM.dreg[freg] = *(store + i);
		}
		C_clrbit(mask,freg);
		i++;
	}
		
	return(0);
}	/* end of _emul_ldm_stm						*/
  
/*                    
 *     _emul_set_round           
 *									
 * This function will set the floating point rounding mode as specified 
 * by the first operand of the universal floating point instruction.    
 *								        
 * Input values:						 	
 * *gi      - points to general info. structure				
 *									
 * Return values:	 0                         			
 *      
 */
int 
_emul_set_round(newcode,gi)

u_short		newcode[];
global_info_type *gi;

{
int 	opnum;			/* index to operand			*/
int	mode; 			/* value of rounding mode		*/

FP_MACH *fp_regs;		/* emulator's floating point regs.	*/

	/* Set the pointer to the emulator's fp regs.			*/
        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	
	opnum = 0 ;	/* first operand is the rounding mode  		*/

	/*
	 * The operand for this function must be address of an integer, a      
	 * general register containing an integer, or an integer immediate
	 * value in the operands optional field.
  	 */

	mode = _emul_get_int(gi,opnum);
 
	switch (mode)
	{
	case TONEAREST:
		/* 
		   Set rounding mode to "round to nearest"
		 */
		FPM.status.rnd_mode = FP_NEAR;
		break;

	case TOWARDZERO:
		/* 
		   Set rounding mode to "round toward zero"
		 */
		FPM.status.rnd_mode = FP_ZERO;
		break;
	
	case UPWARD:   
		/* 
		   Set rounding mode to "round toward + infinity"
		 */
		FPM.status.rnd_mode = FP_UP;  
		break;

	case DOWNWARD:  
		/* 
		   Set rounding mode to "round toward - infinity"
		 */
		FPM.status.rnd_mode = FP_DOWN;
		break;
	

	default:
		break;
	}
  

		
	return(0);
}	/* end of _emul_set_round				*/
  
/*                  
 *     _emul_test_round     
 *									      
 * This function will store the value of the floating rounding mode in the    
 * location specified by the first operand of the universal floating point    
 * instruction.								      
 *									      
 *
 * Input values:							      
 * *gi      - points to general info. structure			              
 *									      
 * Return values:	 0                         			      
 *
 */
int 
_emul_test_round(newcode,gi)

u_short		newcode[];
global_info_type *gi;

{
int	opnum, opndtype;		/* operand's type and index          */
int	greg;				/* general purpose reg. number       */
int 	mode; 				/* value of the rounding mode        */

u_long	offset; 			/* offset if ADDRTYPE operand        */

DOUBLE 	(*store);			/* storage addr. if ADDRTYPE operand */

struct regs { u_long reg[16]; } *gpr_regs;    /* general purpose regs        */

FP_MACH *fp_regs;			/* emulator's floating point regs.   */

	/* Set pointers to emulator's fp regs and to gpr regs.               */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	gpr_regs = (struct regs *) (gi->fp_RegSave);

	opnum = 0 ;	/* The rounding mode will be stored in the first oper.*/

	/* Convert emulator rounding mode to RTFL standard value	      */

	switch (FPM.status.rnd_mode)
	{
	case FP_NEAR:  
		/* 
		   Set rounding mode to "round to nearest"
		 */
		mode = TONEAREST;
		break;

	case FP_ZERO:
		/* 
		   Set rounding mode to "round toward zero"
		 */
		mode = TOWARDZERO;
		break;
	
	case FP_UP:   
		/* 
		   Set rounding mode to "round toward + infinity"
		 */
		mode = UPWARD;
		break;

	case FP_DOWN:   
		/* 
		   Set rounding mode to "round toward - infinity"
		 */
		mode = DOWNWARD;
		break;
	

	default:
		break;
	}
	/*
	 * The operand for this function must be address of an integer, or a   
	 * general register containing an integer.
  	 */


	opndtype = hi_optype_of(gi, opnum);
	greg = (byteval_of(gi, opnum) & 0x0f);
 
	switch (opndtype)
	{
	case ADDRTYPE:
		/* 
		 * Operands which are of ADDR type are addressed using a
		 * value in a general register and a 32 bit offset from
		 * operands optional field.
		 */
		offset = getopnd(gi, opnum, 0);
		if (greg != 0) {
			store = (DOUBLE *) GPM.reg[greg];
			store = (DOUBLE *) ((int) store + offset); 
		}
		else
			store = (DOUBLE *) (offset);
		             
		/* Mode must be an integer				*/ 
 		(*store).dfracth = mode;
		break;

	case GREGTYPE:
		/*
		 * Operands of this type are found in general registers.
		 */
 		GPM.reg[greg] = mode;
		break;

	default:
		break;
	}
  
  

		
	return(0);
}	/* end of _emul_test_round				*/
  
/*               
 *     _emul_get_int      
 *									
 * This function will return the integer value of a universal floating  
 * point instruction operand.                                           
 *									
 * Input values:							
 * *gi      - points to general info. structure			        
 * opnum    - index to requested operand				
 *									
 * Return values:	 integer value of operand  			
 *      
 */

static int 
_emul_get_int(gi,opnum)

global_info_type *gi;

int	opnum;

{

int	greg;					/* gpr reg number	*/
int 	value; 					/* integer vlaue	*/
int	opndtype;				/* type of operand      */

u_long	offset; 				/* offset to operand    */

DOUBLE 	(*store);				/* address of operand   */

struct regs { u_long reg[16]; } *gpr_regs;      /* general purpose regs */

	/* Set pointer to general purpose registers			*/
	gpr_regs = (struct regs *) (gi->fp_RegSave);

	/*
	 * The operand for this function must be address of an integer, a      
	 * general register containing an integer, or an integer immediate
	 * value in the operands optional field.
  	 */


	opndtype = hi_optype_of(gi, opnum);
	greg = (byteval_of(gi, opnum) & 0x0f);
 
	switch (opndtype)
	{
	case ADDRTYPE:
		/* 
		 * Operands which are of ADDR type are addressed using a
		 * value in a general register and a 32 bit offset from
		 * operands optional field.
		 */
		offset = getopnd(gi, opnum, 0);
		if (greg != 0) {
			store = (DOUBLE *) GPM.reg[greg];
			store = (DOUBLE *) ((int) store + offset); 
		}
		else
			store = (DOUBLE *) (offset);
		             
		/* Mode must be an integer				*/ 
		value = (*store).dfracth;    
		break;

	case GREGTYPE:
		/*
		 * Operands of this type are found in general registers.
		 */
		value = GPM.reg[greg];
		break;

	case IMMEDTYPE:
		/*
		 * Immediate operands are contain within the universal
		 * floating point instruction.
		 */ 
		value = getopnd(gi,opnum,0);

	default:
		break;
	}
  
	return(value);
}	/* end of _emul_get_int  				*/

/*                   
 * _emul_set_clr_flag     
 *								               
 * This function will, if gi->data->opcode  FP_SETFLAG, set the floating point
 * flag bits as specified by the first operand of the universal floating point 
 * instruction. If gi->data->opcode  FP_CLRFLAG, the flag bits specified by   
 * the first operand will be cleared.					       
 *									       
 * Input values:							       
 * *gi      - points to general info. structure				       
 *									       
 * Return values:	 0                         			       
 *
 */
int 
_emul_set_clr_flag(newcode,gi)

u_short		newcode[];
global_info_type *gi;

{
int	opnum;				/* index to operand in UFI	*/
int	flag; 				/* value of flag		*/
int	SetOnOrOff;			/*set to 1 for set 0 for clear 	*/

uif_type *data;				/* pointer to UFI data          */

FP_MACH *fp_regs;	/* emulator's floating point regs.		*/

	/* Set pointers to fp regs. and to UFI data			*/
        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	data = gi->data;

	opnum = 0 ;	/* first operand is the rounding mode  		*/
	
	/* set flag on if FP_SETFLAG; else, set flag off for FP_CLRFLAG */
	SetOnOrOff = (data->opcode == FP_SETFLAG);

	/*
	 * The operand for this function must be address of an integer, a      
	 * general register containing an integer, or an integer immediate
	 * value in the operands optional field.
  	 */

	flag = _emul_get_int(gi,opnum);
 

	if (flag & FPINVALID) 	/* set invalid op. flag                    */
		FPM.status.io_flag = SetOnOrOff;

	if (flag & FPUNDERFLOW)	/* set underflow flag                      */
		FPM.status.uf_flag = SetOnOrOff;

	if (flag & FPOVERFLOW)	/* set overflow flag                       */
		FPM.status.of_flag = SetOnOrOff;

	if (flag & FPDIVBYZERO)	/* set divide by zero flag                 */
		FPM.status.dz_flag = SetOnOrOff;

	if (flag & FPINEXACT)  	/* set inexact flag                        */
		FPM.status.ir_flag = SetOnOrOff;
		
	return(0);
}	/* end of _emul_set_clr_flag	*/
  
/*                  
 *     _emul_set_clr_trap     
 *								               
 * This function will, if gi->data->opcode  FP_SETTRAP, set the floating point
 * trap bits as specified by the first operand of the universal floating point 
 * instruction. If gi->data->opcode  FP_CLRTRAP, the trap bits specified by   
 * the first operand will be cleared.				               
 *    									       
 * Input values:						               
 * *gi      - points to general info. structure				       
 *								               
 * Return values:	 0                         			       
 *
 */
int 
_emul_set_clr_trap(newcode,gi)

u_short		newcode[];
global_info_type *gi;

{

int 	opnum;				/* index to operand in UFI      */  
int	trap; 			        /* trap mask                    */
int	SetOnOrOff;			/*set to 1 for set 0 for clear 	*/

uif_type *data;				/* Universal Fp Instruction     */

FP_MACH *fp_regs;			/* emulator's fp regs.		*/

	/* Set pointers to emulators fp regs and to the UFI             */
        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	data = gi->data;

	opnum = 0 ;	/* first operand is the trap mask      		*/
	
	/* set flag on if FP_SETTRAP; else, set flag off for FP_CLRTRAP */
	SetOnOrOff = (data->opcode == FP_SETTRAP);

	/*
	 * The operand for this function must be address of an integer, a      
	 * general register containing an integer, or an integer immediate
	 * value in the operands optional field.
  	 */

	trap = _emul_get_int(gi,opnum);
 
	/* Convert from RTFL standard to emulator values		  */
	if (trap & FPINVALID) 	/* set invalid op. xpt                    */
		FPM.status.io_xpt = SetOnOrOff;

	if (trap & FPUNDERFLOW)	/* set underflow xpt                      */
		FPM.status.uf_xpt = SetOnOrOff;

	if (trap & FPOVERFLOW)	/* set overflow xpt                       */
		FPM.status.of_xpt = SetOnOrOff;
	if (trap & FPDIVBYZERO)	/* set divide by zero xpt                 */
		FPM.status.dz_xpt = SetOnOrOff;

	if (trap & FPINEXACT)  	/* set inexact xpt                        */
		FPM.status.ir_xpt = SetOnOrOff;
		
	return(0);
}		/* end of _emul_set_clr_trap	*/
				

/* 
 * _emul_test_flag     
 *									      
 * This function will store the value of the floating point flag bits in the  
 * location specified by the first operand of the universal floating point    
 * instruction.								      
 *								              
 * Input values:						              
 * *gi      - points to general info. structure                               
 *									      
 * Return values:	 0                         		              
 *
 */
int 
_emul_test_flag(newcode,gi)

u_short		newcode[];
global_info_type *gi;

{

int	opnum, opndtype;			/* operand index and type     */
int	greg;					/* general reg. number        */
int 	flag; 					/* value of flag 	      */

u_long	offset; 				/* offset of ADDRTYPE operand */

DOUBLE 	(*store);				/* addr. of ADDRTYPE operand  */

struct regs { u_long reg[16]; } *gpr_regs;      /* general purpose regs       */

FP_MACH *fp_regs;				/* emulator's fp regs.	      */

	/* Set fp pointer and gpr pointer				      */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	gpr_regs = (struct regs *) (gi->fp_RegSave);

	opnum = 0 ;	/* The fp flag bits are stored in first operand       */

	/* Convert flag to RTFL standard				      */

	flag =(FPM.status.io_flag ? FPINVALID : 0) | 
	      (FPM.status.uf_flag ? FPUNDERFLOW : 0) | 
	      (FPM.status.of_flag ? FPOVERFLOW : 0) | 
	      (FPM.status.dz_flag ? FPDIVBYZERO : 0) | 
	      (FPM.status.ir_flag ? FPINEXACT : 0); 
 
	/*
	 * The operand for this function must be address of an integer, or a   
	 * general register containing an integer.
  	 */

	opndtype = hi_optype_of(gi, opnum);
	greg = (byteval_of(gi, opnum) & 0x0f);
 
	switch (opndtype)
	{
	case ADDRTYPE:
		/* 
		 * Operands which are of ADDR type are addressed using a
		 * value in a general register and a 32 bit offset from
		 * operands optional field.
		 */
		offset = getopnd(gi, opnum, 0);
		if (greg != 0) {
			store = (DOUBLE *) GPM.reg[greg];
			store = (DOUBLE *) ((int) store + offset); 
		}
		else
			store = (DOUBLE *) (offset);
		             
		/* Mode must be an integer				*/ 
 		(*store).dfracth = flag;
		break;

	case GREGTYPE:
		/*
		 * Operands of this type are found in general registers.
		 */
 		GPM.reg[greg] = flag;
		break;

	default:
		break;
	}
  
  

		
	return(0);
}	/* end of _emul_test_flag 				*/

/*                
 * _emul_test_trap      
 *									     
 * This function will store the value of the floating point trap bits in the 
 * location specified by the first operand of the universal floating point   
 * instruction.								     
 *								             
 * Input values:						             
 * *gi      - points to general info. structure				     
 *									     
 * Return values:	 0                         			     
 * 
 */
int 
_emul_test_trap(newcode,gi)

u_short		newcode[];
global_info_type *gi;

{

int	opnum,opndtype;		/* operand index and type		*/
int	greg;			/* gpr register number			*/
int 	trap; 			/* value of trap			*/

u_long	offset; 		/* storage operand offset		*/

DOUBLE 	(*store);		/* address of storage operand           */

struct regs { u_long reg[16]; } *gpr_regs;    /* general purpose regs   */

FP_MACH *fp_regs;	/* emulator's floating point regs.		*/

	/* Set pointers to fp regs and gpr regs                         */

        fp_regs  = (FP_MACH *) (gi->fp_state->fp_MachRegs);                
	gpr_regs = (struct regs *) (gi->fp_RegSave);

	opnum = 0 ;	/* The fp trap bits are set in the first operand*/

	/* Convert trap from emulator status to RTFL standard		 */

	trap =(FPM.status.io_xpt ? FPINVALID : 0) | 
	      (FPM.status.uf_xpt ? FPUNDERFLOW : 0) | 
	      (FPM.status.of_xpt ? FPOVERFLOW : 0) | 
	      (FPM.status.dz_xpt ? FPDIVBYZERO : 0) | 
	      (FPM.status.ir_xpt ? FPINEXACT : 0); 
 
	/*
	 * The operand for this function must be address of an integer, or a   
	 * general register containing an integer.
  	 */

	opndtype = hi_optype_of(gi, opnum);
	greg = (byteval_of(gi, opnum) & 0x0f);
 
	switch (opndtype)
	{
	case ADDRTYPE:
		/* 
		 * Operands which are of ADDR type are addressed using a
		 * value in a general register and a 32 bit offset from
		 * operands optional field.
		 */
		offset = getopnd(gi, opnum, 0);
		if (greg != 0) {
			store = (DOUBLE *) GPM.reg[greg];
			store = (DOUBLE *) ((int) store + offset); 
		}
		else
			store = (DOUBLE *) (offset);
		             
		/* Mode must be an integer				*/ 
 		(*store).dfracth = trap;
		break;

	case GREGTYPE:
		/*
		 * Operands of this type are found in general registers.
		 */
 		GPM.reg[greg] = trap;
		break;

	default:
		break;
	}
  
	return(0);
}	/* end of _emul_test_trap 				*/

/*               
 *     _emul_init_mach         
 *									  
 * This function is called to establish adddressability to the emulator's 
 * floating point registers, statatus register, and excpetion register.   
 * register.                                                              
 *								          
 * Inputs:								  
 * *gi      - points to general info. structure				  
 *									  
 * Return values:	 0                         			  
 *    
 */
_emul_init_mach(gi)

global_info_type *gi;

{
	gi->fp_state->fp_MachRegs  = (u_long *) findfpm();

	return(0);
}	/* end of _emul_init_mach    	*/

operations	
_emulrtnes =
	{ /*** EMULATOR DEFINITIONS ***/
	    _emul_monadic,	/* FP_ABS */
	    NULL,	/* FP_ACOS */
	    _emul_dyadic,	/* FP_ADD */
	    NULL,	/* FP_ASIN */
	    NULL,	/* FP_ATAN */
	    NULL,	/* FP_ATAN2 */
	    NULL,	/* FP_ATANH */
	    _emul_compare,	/* FP_CMP */
	    _emul_compare,	/* FP_CMPT */
	    NULL,     	/* FP_CMPB */
	    NULL,	/* FP_COS */
	    NULL,	/* FP_COSH */
	    _emul_dyadic,	/* FP_DIV */
	    NULL,	/* FP_EXP */
	    NULL,	/* FP_EXPM1 */
	    _emul_ldm_stm,	/* FP_LOADM */
	    NULL,	/* FP_LOG10 */
	    NULL,	/* FP_LOG1P */
	    NULL,	/* FP_LOG */
	    NULL,	/* FP_LOGB */
	    _emul_moveop,	/* FP_MOVE */
	    _emul_dyadic,	/* FP_MUL */
	    _emul_monadic,	/* FP_NEG */
	    NULL,	/* FP_REM */
	    NULL,	/* FP_RINT */
	    NULL,	/* FP_SCALB */
	    NULL,	/* FP_SIN */
	    NULL,	/* FP_SINH */
	    NULL,	/* FP_SQRT */
	    _emul_ldm_stm,	/* FP_STOREM */
	    _emul_dyadic,	/* FP_SUB */
	    NULL,	/* FP_TAN */
	    NULL,	/* FP_TANH */
	    _emul_init_mach,	/* FP_INIT */
	    NULL,	/* FP_WHICH */
	    _emul_set_round,	/* FP_SETROUND */
	    _emul_test_round,	/* FP_TESTROUND */
	    _emul_set_clr_flag,	/* FP_SETFLAG */
	    _emul_test_flag,	/* FP_TESTFLAG */
	    _emul_set_clr_trap,	/* FP_SETTRAP */
	    _emul_test_trap,	/* FP_TESTTRAP */
	    _emul_set_clr_flag,	/* FP_CLRFLAG */
	    _emul_set_clr_trap	/* FP_CLRTRAP */
	};
