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

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

#include "fpgen.h"
#include "general.h"
#include <signal.h>

#define PAGESIZE	2048

extern	int		_num_1s_tbl[];
extern	operations	_emulrtnes;
extern	operations	_fpa1rtnes;
extern	operations	_f881rtnes;
extern	operations	_fpa2rtnes;

static	int	(**codertnes[])() = {
	_emulrtnes,
	_fpa1rtnes,
	_f881rtnes,
	_fpa2rtnes
	};

static	int	RT_instr_len_tbl[] =	{1, 1, 1, 1, 1, 1, 1, 1,
					 2, 1, 1, 1, 2, 2, 1, 1 };

static int  block_sig_mask = 			/* mask of 0x7ffff9f7 */
		  sigmask(SIGHUP)|	/* hangup	*/
		  sigmask(SIGINT)|	/* interrupt  	*/
		  sigmask(SIGQUIT)|	/* quit		*/
		  sigmask(SIGTRAP)|	/* trace trap	*/
		  sigmask(SIGIOT)|	/* IOT instruction */
		  sigmask(SIGEMT)|	/* EMT instruction */
		  sigmask(SIGFPE)|	/* floating point exception */
		  sigmask(SIGKILL)|	/* kill (can not be blocked) */
		  sigmask(SIGSYS)|	/* bad argument in system call */
		  sigmask(SIGPIPE)|	/* write on pipe with no pipe reader */
		  sigmask(SIGALRM)|	/* alarm clock */
		  sigmask(SIGTERM)|	/* software termination from kill */
		  sigmask(SIGURG)|  	/* urgent condition on I/O */
		  sigmask(SIGSTOP)|	/* stop (can not be blocked) */
		  sigmask(SIGTSTP)|	/* stop signal from tty */
		  sigmask(SIGCONT)|	/* continue (can not be blocked) */
		  sigmask(SIGCHLD)|	/* death of child */
		  sigmask(SIGTTIN)|	/* to reader program on background rd.*/
		  sigmask(SIGTTOU)|	/* like ttin for output */
		  sigmask(SIGIO)|	/* I/O possible signal */
		  sigmask(SIGXCPU)|	/* exceeded cpu time limit */
		  sigmask(SIGXFSZ)|	/* exceeded file size */
		  sigmask(SIGVTALRM)|	/* virtual time alarm */
		  sigmask(SIGPROF)|	/* profiling time alarm */
		  sigmask(28)|		/* not defined but block anyway */
		  sigmask(29)|		/* not defined but block anyway */
		  sigmask(30)|		/* not defined but block anyway */
		  sigmask(31);		/* not defined but block anyway */
			

static int
_pick_support(fp_state)
state_type	*fp_state;
{
int	fpprecval, fpaval, use_state;
struct floatstate	*fs;

int			fs_length;
struct floatstate	fs_mask, fs_state;


	if (fp_state->env_prec == 0)
		fpprecval = DEFAULT_PREC;
	else
		switch(*((char *)fp_state->env_prec)) {
			case 'f':	fpprecval = FAST_PREC;
					break;
			case 's':	fpprecval = SINGLE_PREC;
					break;
			case 'd':	fpprecval = DOUBLE_PREC;
					break;
			case 'p':	fpprecval = PRECISE_PREC;
					break;
			default:	fpprecval = DEFAULT_PREC;
					break;
		}
	if (fp_state->env_fpa == 0)
		fpaval = 0;
	else
		switch(*((char *)fp_state->env_fpa)) {
			case 'a':	fpaval = FLOAT_AFPA;
					break;
			case 'm':	fpaval = FLOAT_MC881;
					break;
			case 'f':	fpaval = FLOAT_FPA;
					break;
			case 'e':	fpaval = FLOAT_EMUL;
					break;
			default:	fpaval = 0;
					break;
		}

	fp_state->env_prec = fpprecval;
	fp_state->env_fpa = fpaval;

	fs = &(fp_state->floatstate);
#ifndef FORCE
	if (fs->process_state != 0)
		use_state = fs->process_state;
	else {
#endif !FORCE
		use_state = fs->hardware_state;
		if (use_state & fp_state->env_fpa)
			use_state = fp_state->env_fpa;
#ifndef FORCE
	}
	if (*fp_state->compat) {
		use_state &= ~(FLOAT_MC881);
	}
#endif !FORCE

	if (use_state & FLOAT_AFPA) {
		return(FPA_FPA2);
	}
	if (use_state & FLOAT_MC881) {
		if (	(fp_state->env_prec == SINGLE_PREC) &&
			(fp_state->firsttime < VERSION_881NAN))
				fp_state->env_prec = DOUBLE_PREC;
		return(FPA_68881);
	}
	if (use_state & FLOAT_FPA) {
		return(FPA_FPA1);
	}

	/* When in user mode, tell the kernel and adb that emul is choosen */
	if ((!iskernel()) && (fp_state->firsttime >= VERSION_881NAN)) {
		bzero((char *)&fs_mask, sizeof fs_mask);
		fs_state = fs_mask;
		fs_mask.process_state = FLOAT_EMUL;
		fs_state.process_state = FLOAT_EMUL;
		fs_length = sizeof fs_mask;
		if (fp_state->fpsetfloatstate(&fs_mask, &fs_state, &fs_length) == -1)		{
			fp_state->fpabort("fpgen: setfloatstate");
			exit(1);
		}
	}

	return(FPA_EMUL);
}

fpgen(data, fp_state, usr_gprs, cc)
uif_type *data;
state_type *fp_state;
u_long *usr_gprs;		/* area where fpglue saved caller's gprs     */
u_long *cc;			/* addr. of save area for caller's cond. code*/
{
int opcode = 1, codelen;
unsigned short	newcode[MAXCODELEN];
uif_type        copied_data;
unsigned short	*dataptr;
global_info_type	glob_info;
char	*new_space;
int prev_sig_mask;		/* current signal mask will be saved here   */

	/*
	 *	copy data block first, so if gen rtnes
	 *	change anything it will be okay, or if we are
	 *	interrupted and data gets changed...
	 */

	copied_data = *data;               
	 
	/*
	 *	Verify that universal instruction is valid.  If not
	 *	the universal instruction has already been converted 
	 *	to machine instructions, so return and execute them.
	 */

	if (data->saver15 != RT_short(RT_CAS,r15,r0)){
		return(1);		/* code has already been generated   */
	}


	if (fp_state->firsttime < VERSION) 		/* garbage */
		fp_state->fpabort("fpgen: fpglue version mismatch");
	else if (fp_state->firsttime == VERSION) {	/* first time thru */
#ifndef FORCE
		if (!iskernel())	/* block signals, only in user mode */
			prev_sig_mask = fp_state->fpblock(block_sig_mask);
#endif !FORCE
		if (fp_state->firsttime) {
			fp_state->firsttime += ((int) fp_state->fp_MachRegs + 1);
			if (fp_state->firsttime >= VERSION_UNSIGNED) {
				/* constants to support unsigned int */
				fp_state->hi_2to31 = 0x41e00000;
				fp_state->lo_2to31 = 0x00000000;
				fp_state->f_2to31  = 0x4f000000;
			}
			fp_state->fptype = _pick_support(fp_state);
			_init_global_info(&copied_data, fp_state, &glob_info,
				usr_gprs, cc);
			(codertnes[fp_state->fptype])[FP_INIT](&glob_info);
		}
		else
			_init_global_info(&copied_data, fp_state, &glob_info,
				usr_gprs, cc);
#ifndef FORCE
		if (!iskernel())	/* enable signals, only in user mode */
			fp_state->fpblock(prev_sig_mask);
#endif !FORCE
	}
	else						/* been here before */
		_init_global_info(&copied_data, fp_state, &glob_info,
				usr_gprs, cc);

	opcode = copied_data.opcode;
	if ((codertnes[fp_state->fptype])[opcode] == NULL) {
		if (_opinfo[opcode].alt_required) {
			codelen = _gen_alternate(newcode, &glob_info,
						(u_long)data);
		}
		else
		if (opcode == FP_WHICH) {
			codelen = _gen_which(newcode, &glob_info);
			codelen = _push_and_pop(newcode, codelen, &glob_info,
						(u_long)data);
		}
		/*
		 * Error!  No function available.  What should we do?
		 * For now, just return.
		 */
		else
			codelen = 0;
	}
	else {
		if (_opinfo[opcode].alt_required)
			copied_data.numopnds -= 1;
		codelen = 
		    (codertnes[fp_state->fptype])[opcode](newcode, &glob_info);
		if (codelen > 0) {
			codelen = _push_and_pop(newcode, codelen, &glob_info,
						(u_long)data);
			codelen = _put_imm_vals(newcode, codelen, &glob_info);
		}
	}

	dataptr = (unsigned short *)data;
	/*
	 * For re-entrancy, put signal-block here.
	 */
#ifndef FORCE
	if (!iskernel() && (fp_state->fptype != FPA_EMUL))
		/* block signals, only in user mode and NOT using emul */
		prev_sig_mask = fp_state->fpblock(block_sig_mask);
#endif !FORCE
	if (codelen*2 > copied_data.mysize) {
		if ((new_space = fp_state->fpalloc(codelen*2)) == NULL) {
			fp_state->fpabort("fpgen: No space from fpalloc()");
		}
#ifdef ERROR
		int	i;
		printf("OH NO!  %d code is bigger than %d data block!\n",
			codelen*2, copied_data.mysize);
		for (i=0; i<(copied_data.mysize/2); i++)
			printf("%0.x ",dataptr[i]);
		printf("\n");
		printf("new_space 0x%.8x\n",(int) new_space);
#endif ERROR
		bcopy(newcode, new_space, codelen*2);
		_relocate(new_space, codelen, &glob_info, (u_long)new_space);
		codelen = _fp_getimm(newcode, r0, (u_long)new_space);
		newcode[codelen++] = RT_short(RT_BR, 0, r0);
	}
	else if (codelen > 0)
		_relocate(newcode, codelen, &glob_info, (u_long)data);
#ifdef DEBUG
	for (i=0; i<codelen; i++)
	{
		dataptr[i] = newcode[i];
		printf("%x ",newcode[i]);
		printf("\n");
	}
#else !DEBUG
	if (codelen > 0)
		bcopy(newcode, dataptr, codelen*2);
#endif DEBUG
	/*
	 * For re-entrancy, put signal-un-block here.
	 */
#ifndef FORCE
	if (!iskernel() && (fp_state->fptype != FPA_EMUL))
		/* enable signals, only in user mode and NOT using emul */
		fp_state->fpblock(prev_sig_mask);
#endif !FORCE
	return(codelen==0);
}

static int
_init_global_info(data, fp_state, gi, usr_gprs, cc)
uif_type		*data;
state_type		*fp_state;
global_info_type	*gi;
u_long *usr_gprs;		/* area where fpglue saved caller's gprs     */
u_long *cc;			/* addr. of save area for caller's cond. code*/
{
int	i,iopnd;

	gi->data = data;		/* set pointer to data block	*/
	gi->fp_state = fp_state;	/* set pointer to fp hardware state */

	gi->fp_RegSave = usr_gprs; 	/* set pointer to user's gprs  */
	gi->emul_cstat = cc;       	/* set pointer to saved emul cc */

	/*
	 * NOTE: stack_offset of -4 is reserved for the case where
	 * we don't have enough room to restore generated code, and
	 * must branch-and-link somewhere else.  So we start here at -8.
	 */
	gi->stack_offset = -8;		/* set stack offset, safe to store */

	gi->avail_genr = (data->scratchg & USABLE_REGS);
	gi->pushable_genr = USABLE_REGS;

	/*
	 * The following should be 0xff for all but fpa1,
	 * which should be only 0xfe, since fpa1 doesn't 
	 * have a real fr7
	 */
	if (fp_state->fptype == FPA_FPA1) {
		data->scratchf &= 0xfe;
		gi->avail_fltr = data->scratchf;
		gi->pushable_fltr = 0xfe;
	} else {
		gi->avail_fltr = data->scratchf & 0xff;
		gi->pushable_fltr = 0xff;
	}

	iopnd = ((data->numopnds + 1) >> 1);
	/*
	 * parse the UI as much as possible
	 */
	for (i = 0; i < data->numopnds; i++) {
		int	reg = byteval_of(gi, i);
		int	lo_type = lo_optype_of(gi, i);

		gi->wordopnd[i] = 0;	/* assume no word opnd */
		gi->immcode[i] = 0;	/* where in the gen'd code should the */
					/* offset of imm value be plugged in */
		switch hi_optype_of(gi, i) {
			case FREGTYPE:
				C_clrbit(gi->pushable_fltr, reg);
				C_clrbit(gi->avail_fltr, reg);
				break;
			case GREGTYPE:
				S_clrbit(gi->pushable_genr, reg & 0x0f);
				S_clrbit(gi->avail_genr, reg & 0x0f);
				if (lo_type == DBLETYPE) {
					S_clrbit(gi->pushable_genr, reg >> 4);
					S_clrbit(gi->avail_genr, reg >> 4);
				}
				break;
			case ADDRTYPE:
				S_clrbit(gi->pushable_genr, reg);
				S_clrbit(gi->avail_genr, reg);
				gi->wordopnd[i] = iopnd++;
				break;
			case IMMEDTYPE:
				if ((lo_type == UINTTYPE) &&
				    ((gi->data->opr.operand[iopnd].UL & 0x80000000) == 0)) {
					gi->data->opr.opt[i].ch.typenum &= 0xf0;
					gi->data->opr.opt[i].ch.typenum |= INTTYPE;
				}
				gi->wordopnd[i] = iopnd++;	
						/* at least one word for imm */
				if (lo_type == DBLETYPE) 
					iopnd++;	/* 1 more for dble */
				break;
			case HISPECTYPE:
				S_clrbit(gi->pushable_genr, reg & 0x0f);
				S_clrbit(gi->avail_genr, reg & 0x0f);
				break;
		} /* end switch */
	} /* end for */

	gi->pushable_genr &= ~gi->avail_genr;
	gi->pushed_genr = 0;		/* none is pushed so far */
	gi->pushed_fltr = 0;		/* none is pushed so far */
	gi->pushed_genr_count = 0;	/* none is pushed so far */
	gi->pushed_fltr_count = 0;	/* none is pushed so far */
	gi->pushing_fltr_flag = 0;

#ifdef DEBUG
printf("avail_genr= %.8x, pushable_genr= %.8x\n",gi->avail_genr,gi->pushable_genr);
printf("avail_fltr= %.8x, pushable_fltr= %.8x\n",gi->avail_fltr,gi->pushable_fltr);
#endif DEBUG

} /* end _init_global_info */

#ifdef FORCE
debug_brx_page(rtfl_blk_addr, brx_page, sub_inst_page)
int	rtfl_blk_addr, brx_page, sub_inst_page;
{
	printf("debug_brx_page: rtfl_blk_addr = 0x%.8x\n", rtfl_blk_addr);
	printf("debug_brx_page: brx_page = 0x%.8x\n", brx_page);
	printf("debug_brx_page: sub_inst_page = 0x%.8x\n\n", sub_inst_page);
}
#endif FORCE

static int
_gen_alternate(newcode, gi, rtfl_blk_addr)
u_short newcode[];
global_info_type *gi;
u_long	rtfl_blk_addr;
{
int	rx, icode = 0, avail;
int	brx_page, sub_inst_page;
u_char	numopnds;
u_long	altaddr;
uif_type *data = gi->data;

#ifdef DEBUG
	printf("generating alternate.\n");
#endif DEBUG

/* FIRST, should check that all operands are okay:
 *	op1 should be r2, r3
 *	(optional) op2 should be r4, r5
 *	(optional) result should be r2, r3
 *	opN should be address (type 0xff, r0, non-zero offset)
 *	If something isn't right, should call fpabort().
 *	(Or, could add smarter code to put arguments in proper
 *	registers and generate the call anyway.)
 */

	/*
	 * Get a general register for address-generation.
	 * If no available registers, use any one (not 0, 1, 15),
	 * saving and restoring it in the mq.
	 */
	avail = gi->avail_genr;
	if (avail)
		rx = _fp_get_genr(gi);
	else {
		rx = 6;		/* could pick just about any one */
		newcode[icode++] = RT_short(RT_MTS, MQ, rx);
	}
	numopnds = data->numopnds;
	altaddr = getopnd(gi, numopnds-1, 0);
	icode += _fp_ea_addrtype(&newcode[icode], rx, 0, altaddr);	/* dp into rx */
	newcode[icode++] = RT_short(RT_CAS, rx, 0);		/* dp into r0 */
	newcode[icode++] = RT_short(RT_LS, rx, rx);	/* real addr into rx  */

	if (!avail) {
		brx_page = (rtfl_blk_addr + icode*2) / PAGESIZE;
		sub_inst_page = (rtfl_blk_addr + icode*2 + 4) / PAGESIZE;
		if (brx_page != sub_inst_page) {
			newcode[icode++] = RT_short(RT_CAS, rx, rx);
			newcode[icode++] = RT_short(RT_CAS, rx, rx);
#ifdef FORCE
			printf("\n\nCall from gen_alternate:\n");
			debug_brx_page(rtfl_blk_addr, brx_page, sub_inst_page);
#endif FORCE
		}
		newcode[icode++] = RT_short(RT_BRX, 0, rx);
		newcode[icode++] = RT_short(RT_MFS, MQ, rx);
	}
	else
		newcode[icode++] = RT_short(RT_BR, 0, rx);

#ifdef DEBUG
	printf("alternate: altaddr = %08.x, rx = %d\n",altaddr,rx);
	printf("returning %d from alternate gen'r.\n",icode);
#endif DEBUG

	return(icode);
}

static int
_gen_which(newcode, gi)
u_short newcode[];
global_info_type *gi;
{
int	greg, i=0, which;

	switch(gi->fp_state->fptype) {
		case	FPA_EMUL:	which = FLOAT_EMUL;
					break;
		case	FPA_FPA1:	which = FLOAT_FPA;
					break;
		case	FPA_68881:	which = FLOAT_MC881;
					break;
		case	FPA_FPA2:	which = FLOAT_AFPA;
					break;
	}
	greg = byteval_of(gi, 0);

	newcode[i++] = RT_short(RT_LIS,greg,which);
#ifdef DEBUG
printf("\tlis\tr%d,0x%x\t# _gen_which\n",greg,which);
#endif DEBUG
	return(i);
}

static int
_push_and_pop(newcode, len, gi, rtfl_blk_addr)
u_short newcode[];
int len;
global_info_type *gi;
u_long	rtfl_blk_addr;
{
u_short	tempcode[MAXCODELEN], storem_code[MAXCODELEN/2], popcode[MAXCODELEN/2];
int	i, storem_len=0, pushlen=0, poplen=0;
int	last_instr_len, shortinstr1, shortinstr2;
int	tmp_1_4r = 1;
uif_type	ui_lm_stm, *savedata;
int	brx_page, sub_inst_page;

	if ((len == 1) && (newcode[0] == RT_short(RT_BR,0,15)))	/* TEMPORARY! */
		return(len);

	if ((gi->pushed_fltr) || (gi->pushed_genr)) {
		bcopy(newcode, tempcode, len*2);
		if (gi->pushed_fltr) {
			/*
			 * Reserve space on the stack for pushing
			 * floating point registers, then set up a
			 * fake universal instruction for store-multiple.
			 * Reserve 3 words (12 bytes) for each register;
			 * note that stack_offset already points to an
			 * available word on the stack, so subtract 1 word.
			 */
			gi->stack_offset -= (12*_num_1s_tbl[gi->pushed_fltr] -4);
			ui_lm_stm.opcode = FP_STOREM;
			ui_lm_stm.numopnds = 2;
			ui_lm_stm.opr.opt[0].ch.typenum = SPECTYPE;
			ui_lm_stm.opr.opt[0].ch.regnum = gi->pushed_fltr;
			ui_lm_stm.opr.opt[1].ch.typenum = SPECTYPE;
			ui_lm_stm.opr.opt[1].ch.regnum = SP;
			ui_lm_stm.opr.operand[1].I = gi->stack_offset;

			/*
			 * clear the bits in avail_genr for all operand
			 * values; they may have been made available by
			 * the code generation, but are not available
			 * for pushing/popping use!
			 */
			for (i=0; i < gi->data->numopnds; i++) {
				if (hi_optype_of(gi, i) == ADDRTYPE)
					S_clrbit(gi->avail_genr, 
						byteval_of(gi, i) & 0x0f);
				else
				if (hi_optype_of(gi, i) == GREGTYPE) {
					S_clrbit(gi->avail_genr, 
						byteval_of(gi, i) & 0x0f);
					if (lo_optype_of(gi, i) == DBLETYPE)
						S_clrbit(gi->avail_genr,
							byteval_of(gi, i) >> 4);
				}
			}

			/*
			 * With the "fake" universal instruction, call the
			 * storem and loadm routines.  Since _init_global_info
			 * is not called for this "fake" ui block, we need to
			 * set up certain fields in gi that will be used by
			 * storem and loadm.
			 */
			savedata = gi->data;
			gi->data = &ui_lm_stm;
			gi->pushing_fltr_flag = MAGIC_NUMBER;
			storem_len = 
				(codertnes[gi->fp_state->fptype])[FP_STOREM](
					storem_code, gi);
			ui_lm_stm.opcode = FP_LOADM;
			poplen = 
				(codertnes[gi->fp_state->fptype])[FP_LOADM](
					popcode, gi);
			gi->data = savedata;
			gi->stack_offset -= 4;	/* stack's next available word*/
		}
		/*
		 * Push and pop the general registers.
		 */
		if (gi->pushed_genr) {
			pushlen = _push_greg(newcode, gi);
			poplen += _pop_greg(&popcode[poplen], gi);
		}

		/*
		 * Now put all of the pieces together.
		 */
		bcopy(storem_code, &newcode[pushlen], storem_len*2);
		pushlen += storem_len;
		bcopy(tempcode, &newcode[pushlen], len*2);
		len += pushlen;
		bcopy(popcode, &newcode[len], poplen*2);
		len += poplen;

		/*
		 * need to adjust where the imm offset should be plugged
		 * in.  The 0th element CAN be imm (as in CMPs).
		 */
		for (i=0; i < gi->data->numopnds; i++)
			if (gi->immcode[i])
				gi->immcode[i] += pushlen;
	}

	i = 0;
	while (i < len) {
		last_instr_len = RT_instr_len_tbl[(newcode[i] & IMASK) >> 12];
		i += last_instr_len;
	}

	shortinstr1 = newcode[len - last_instr_len] & OPMASK;
	shortinstr2 = shortinstr1 & IMASK;
	brx_page = (rtfl_blk_addr + len*2 - 4) / PAGESIZE;
	sub_inst_page = (rtfl_blk_addr + len*2) / PAGESIZE;
#ifdef FORCE
	if (brx_page != sub_inst_page) {
		printf("\n\nCall from push_and_pop:\n");
		debug_brx_page(rtfl_blk_addr, brx_page, sub_inst_page);
	}
#endif FORCE
	if ( (shortinstr2 == RT_JUMP) || (shortinstr2 == (tmp_1_4r << 15)) ||
	     ((shortinstr2 == RT_BRREG) && (shortinstr1 != RT_LHS)) ||
	     (brx_page != sub_inst_page) )
		newcode[len] = RT_short(RT_BR, 0, r15);
	else {
		/* where "brx r15" goes */
		newcode[len] = newcode[len - 1];
		if (last_instr_len == 1)	/* short instruction? */
			newcode[len - 1] = RT_BRXr15;
		else {
			newcode[len - 1] = newcode[len - 2];
			newcode[len - 2] = RT_BRXr15;
		}
	}

	return(++len);
}


/* int _push_greg 
 *
 * At least one reg need to pushed (gi->pushed_genr != 0).
 * Exam the gi->pushed_genr mask, generate codes to push those
 * regs on the stack.  Set gi->pushed_genr back in reversed
 * order (leftmost bit = r15) as expected by _pop_greg function.
 *
 * Input values:
 * gi->pushed_genr = 1's bits indicate the pushed regs
 *			(leftmost bit = r0)
 *
 * Returned values:
 * gi->pushed_genr = 1's bits indicate the pushed regs
 *			(leftmost bit = r15)
 * newcode 	= the code generated
 * int _push_greg= number of half-words generated
 */
static int
_push_greg(newcode, gi, alt)
u_short		*newcode;
global_info_type *gi;
int	alt;
{
u_short		gr,regmask;
int		i = 0;

#ifdef PUSHPOP
printf("Enter _push_greg:pushed_genr = 0x%x pushed_fltr = 0x%x\n",
			gi->pushed_genr,gi->pushed_fltr);
#endif PUSHPOP

	regmask = gi->pushed_genr;
	gi->pushed_genr = 0;				/* clear it out */
	do {
		gr = 16 - ffs(regmask);			/* take a pushed reg */
					/* set it back in reversed order */
		S_setbit(gi->pushed_genr,15 - gr); 
		gi->pushed_genr_count++;
		if (gi->pushed_genr_count == 1) {		/* use r0 */
#ifdef DEBUG
printf("\tmr\tr0,r%d\t# _push_greg\n",gr);
#endif DEBUG
			newcode[i++] = (RT_CAS | (gr << 4));
		} else if (gi->pushed_genr_count == 2) {	/* use mq */
#ifdef DEBUG
printf("\tmts\tmq,r%d\t# _push_greg\n",gr);
#endif DEBUG
			newcode[i++] = RT_short(RT_MTS,MQ,gr);
		} else {					/* use stack */
#ifdef DEBUG
printf("\tst\tr%d,%d(SP)\t# _push_greg\n",gr,gi->stack_offset);
#endif DEBUG
			newcode[i++] = RT_short(RT_ST,gr,SP);
			newcode[i++] = gi->stack_offset;
			gi->stack_offset -= 4;
		} /* end else */
	} while ( S_clrbit(regmask,gr) );		/* more to push ? */

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


/* int _pop_greg
 *
 * 1 or more regs were pushed on the stack.  Generate code to
 * pop them.
 *
 * Input values:
 * gi->pushed_genr = 1's bits indicate the pushed regs
 *			(leftmost bit = r15)
 *
 * Return values:
 * newcode	= code generated
 * int _pop_greg	= number of half-words generated
 */
static int
_pop_greg(newcode, gi, alt)
u_short		*newcode;
global_info_type *gi;
int	alt;
{
int		j,i = 0;
u_short		gr,regmask;

	regmask = gi->pushed_genr;
	for (j = 0; j < gi->pushed_genr_count; j++) {
		gr = ffs(regmask) - 1;
		if (j == (gi->pushed_genr_count - 1)) {
			newcode[i++] = (RT_CAS | (gr << 8));
#ifdef DEBUG
printf("\tmr\tr%d,r0\t# _pop_greg\n",gr);
#endif DEBUG
		} else if (j == (gi->pushed_genr_count - 2)) {
			newcode[i++] = RT_short(RT_MFS,MQ,gr);
#ifdef DEBUG
printf("\tmfs\tmq,r%d\t# _pop_greg\n",gr);
#endif DEBUG
		} else {
			gi->stack_offset += 4;
			newcode[i++] = RT_short(RT_L,gr,SP);
			newcode[i++] = gi->stack_offset;
#ifdef DEBUG
printf("\tl\tr%d,%d(SP)\t# _pop_greg\n",gr,gi->stack_offset);
#endif DEBUG
		}
		S_clrbit(regmask,15 - gr);
	} /* end for */
	return (i);
} /* end _pop_greg */


static int
_put_imm_vals(newcode, len, gi)
u_short newcode[];
int len;
global_info_type *gi;
{
int i;
addr_type word_opnd;

	for (i=0; i < gi->data->numopnds; i++)	/* 0th element CAN be imm */
		if (gi->immcode[i]) {		/* need to get the imm opnd */
			if (len & 1) len++;	/* align on word boundary */
			word_opnd.L = getopnd(gi,i,0);
			newcode[len++] = word_opnd.sh.HI;
			newcode[len++] = word_opnd.sh.LO;
			if (lo_optype_of(gi, i) == DBLETYPE) {
				word_opnd.L = getopnd(gi,i,1);
				newcode[len++] = word_opnd.sh.HI;
				newcode[len++] = word_opnd.sh.LO;
		}
	}
	return(len);
} /* end _put_imm_vals() */


#ifdef OLD

static int
_relocate(newcode, len, gi, abs_addr)
u_short newcode[];
int len;
global_info_type *gi;
u_long abs_addr;
{
int i;
addr_type imm_abs_addr;

	imm_abs_addr.L = abs_addr + (len*2);	/* start from end of block */
	for (i = gi->data->numopnds - 1; i >= 0; i--) {	/* count backward */
						/* 0th element CAN be imm */
		if (gi->immcode[i]) {		/* need to adjust imm offset */
			imm_abs_addr.L -= 4;
			if (lo_optype_of(gi, i) == DBLETYPE)
				imm_abs_addr.L -= 4;
			newcode[gi->immcode[i]] =
			    imm_abs_addr.sh.HI + S_sign_of(imm_abs_addr.sh.LO);
			newcode[gi->immcode[i] + 2] = imm_abs_addr.sh.LO;
		} /* end if */
	} /* end for */
} /* end _relocate() */

#else
#define MAGIC_SHORT	(u_short) 0xdfdf

static int
_relocate(newcode, len, gi, abs_addr)
u_short newcode[];
int len;
global_info_type *gi;
u_long abs_addr;
{
int i,j,k;
addr_type imm_abs_addr;

	imm_abs_addr.L = abs_addr + (len*2);	/* start from end of block */
	for (i = gi->data->numopnds - 1; i >= 0; i--) {	/* count backward */
						/* 0th element CAN be imm */
		if (gi->immcode[i]) {		/* need to adjust imm offset */
			imm_abs_addr.L -= 4;
			if (lo_optype_of(gi, i) == DBLETYPE)
				imm_abs_addr.L -= 4;
			j = len;
			k = 0;
			while ((k == 0) && (j > 0))
				if ((newcode[j] == MAGIC_SHORT) &&
				    (newcode[j-2] == MAGIC_SHORT))
					k = j - 2;
				else
					j--;
			newcode[k] =
			    imm_abs_addr.sh.HI + S_sign_of(imm_abs_addr.sh.LO);
			newcode[k + 2] = imm_abs_addr.sh.LO;
		} /* end if */
	} /* end for */
} /* end _relocate() */

#endif OLD
