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

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

#include "fpgen.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

#ifdef DEBUG
/*
 * Debug aid.  Print a short msgs, dump "len" number of bytes
 * in hex starting at addr
 */
void
dump(msgs,addr,len)
char *msgs,*addr;
int len;
{
	int row,col;

	printf("\n%s",msgs);
	for (row = 0; row < (len >> 4); row++) {	/* number of rows */
		printf("\n%8X:",addr);
		for (col = 0; col < 16; col++) printf(" %.2X",addr[col]);
		addr += 16;  
	}
	if (len % 16) {					/* partial row */
		printf("\n%8X:",addr);
		for (col = 0; col < (len % 16); col++) printf(" %.2X",addr[col]);
	}
	printf("\n");
} /* end dump */
#endif DEBUG


/*
 * int _fp_get_genr
 *
 * This procedure is called whenever a GENERAL reg is needed.
 * When the reg is no longer needed, fp_free_genr should be
 * called to insure that the reg can be re-used.	
 *
 * Input values:					
 * gi->avail_genr 	= 1's bits indicate available regs
 * gi->pushable_genr	= 1's bits indicate pushable regs
 *							
 * Return values:					
 * gi->pushed_genr	= 1's bits indicate the pushed regs
 * int _fp_get_genr	= allocated register number	
 */
int
_fp_get_genr(gi)
global_info_type	*gi;
{
int			grnum;

if (gi->avail_genr) {				/* free reg available */
	grnum = 16 - ffs(gi->avail_genr);	/* grasp one */
	S_clrbit(gi->avail_genr,grnum);		/* mark it as used */
	return (grnum);
}
grnum = 16 - ffs(gi->pushable_genr);		/* need to push to get one */
S_clrbit(gi->pushable_genr,grnum);
S_setbit(gi->pushed_genr,grnum);		/* indicate it was pushed */
return (grnum);
} /* end _fp_get_genr */


/*
 * int _fp_get_fltr
 *
 * This procedure is called whenever a FLOATING reg is needed.
 * When the reg is no longer needed, fp_free_fltr should be
 * called to insure that the reg can be re-used.	
 *
 * Input values:				
 * gi->avail_fltr 	= 1's bits indicate available regs
 * gi->pushable_fltr	= 1's bits indicate pushable regs
 *
 * Return values:					
 * gi->pushed_fltr	= 1's bits indicate the pushed regs
 * int _fp_get_fltr	= allocated register number	
 */
int
_fp_get_fltr(gi)
global_info_type	*gi;
{
int			frnum;

if (gi->avail_fltr) {				/* free reg available */
	frnum = 8 - ffs(gi->avail_fltr);	/* grasp one */
	C_clrbit(gi->avail_fltr,frnum);		/* mark it as used */
	return (frnum);
}
frnum = 8 - ffs(gi->pushable_fltr);		/* need to push to get one */
C_clrbit(gi->pushable_fltr,frnum);
C_setbit(gi->pushed_fltr,frnum);		/* indicate it was pushed */
return (frnum);
} /* end _fp_get_fltr */


/*
 * fp_free_genr, fp_free_fltr
 *						
 * Macros.  They are defined in general.h		
 *						
 */


/*
 * int _fp_ea_addrtype
 *
 * Generates codes which compute rx <-- rr + offset.  To handle
 * the following operand types:				
 *
 * OpType 2x:  Immediate operand.  The operand will be appended
 *	to the end of newcode.  A funny offset is passed to 
 *	this routine.  The real offset is pluged in later on.
 *	No base reg (rr=0).				
 *
 * OpType 3x:  Address type operand (base reg + offset).
 *
 * Input values:				
 * rx		= reg number to hold the effective address
 * rr		= base reg				
 * offset	= a non-zero offset (funny offset = 0xaabbccdd)
 *		if offset=0, the codes still work.  However,
 *		a redundant "cal rx,0(rr)" is generated.
 *
 * Return values:				
 * newcode	= code generated	
 * int _fp_ea_addrtype = number of half-words generated	
 */
int
_fp_ea_addrtype(newcode,rx,rr,offset)
u_short		*newcode;
int		rx,rr;
addr_type	offset;
{
int		i = 0;

if (small_pos_num(offset.L) || small_neg_num(offset.L)) {
	/* -(2^16) < x < 2^16 - 1 */
	newcode[i++] = RT_short(RT_CAL,rx,rr);
	newcode[i++] = offset.sh.LO;	/* cal	rx,offset.sh.LO(rr) */
	debugf(DB_ALL, printf("\tcal\tr%d,0x%x(r%d)\t# addrtype, cal only\n",
				rx,offset.sh.LO,rr));
} else {
	newcode[i++] = RT_short(RT_CAU,rx,rr);
	newcode[i++] = offset.sh.HI + S_sign_of(offset.sh.LO);
	/* cau	rx,(offset.sh.HI + sign of offset.sh.LO)(rr) */
	debugf(DB_ALL, printf("\tcau\tr%d,0x%x(r%d)\t# addrtype\n",
			rx,offset.sh.HI + S_sign_of(offset.sh.LO),rr));
	if (offset.sh.LO) {
		newcode[i++] = RT_short(RT_CAL,rx,rx);
		newcode[i++] = offset.sh.LO;	/* cal	rx,offset.sh.LO(rx) */
		debugf(DB_ALL, printf("\tcal\tr%d,0x%x(r%d)\t# addrtype\n",
				rx,offset.sh.LO,rx));
	} /* end offset.sh.LO */
} /* end else */

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


/*
 * int _fp_inst_store
 *
 * Generate (store-type) floating point instruction.	
 *
 * Input values:				
 * rx		= general register with store-value
 * ry		= to hold the fp inst		
 * inst		= the instruction itself
 *
 * Return values:		
 * newcode	= code generated
 * int _fp_inst_store= number of half-words generated	
 */
int
_fp_inst_store(newcode,rx,ry,inst)
u_short		*newcode;
int		rx,ry;
addr_type	inst;
{
int	i = 0;

debugf(DB_ALL, printf("\tcau\tr%d,0x%x\t# _fp_inst_store\n",
		ry, inst.sh.HI + S_sign_of(inst.sh.LO)));
debugf(DB_ALL, printf("\tst\tr%d,0x%x(r%d)\t# _fp_inst_store\n",
		rx, inst.sh.LO,ry));
newcode[i++] = RT_short(RT_CAU,ry,0);	/* cau ry,inst.sh.HI */
newcode[i++] = inst.sh.HI + S_sign_of(inst.sh.LO);
newcode[i++] = RT_short(RT_ST,rx,ry);	/* st rx,inst.sh.LO(ry) */
newcode[i++] = inst.sh.LO;

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


int
_fp_new_inst_store(newcode,gi,rx,inst)
u_short		*newcode;
global_info_type	*gi;
int		rx;
addr_type	inst;
{
int	i = 0,ry;

ry = _fp_get_genr(gi);
debugf(DB_ALL, printf("\tcau\tr%d,0x%x\t# _fp_inst_store\n",
		ry, inst.sh.HI + S_sign_of(inst.sh.LO)));
debugf(DB_ALL, printf("\tst\tr%d,0x%x(r%d)\t# _fp_inst_store\n",
		rx, inst.sh.LO,ry));
newcode[i++] = RT_short(RT_CAU,ry,0);	/* cau ry,inst.sh.HI */
newcode[i++] = inst.sh.HI + S_sign_of(inst.sh.LO);
newcode[i++] = RT_short(RT_ST,rx,ry);	/* st rx,inst.sh.LO(ry) */
newcode[i++] = inst.sh.LO;
fp_free_genr(gi,ry);

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


/*
 * int _fp_inst_load
 *
 * Generate (load-type) floating point instruction.	
 *
 * Input values:				
 * rx		= general register will get load-value
 * ry		= to hold the fp inst		
 * inst		= the instruction itself
 *
 * Return values:		
 * newcode	= code generated
 * int _fp_inst_load= number of half-words generated	
 */
int
_fp_inst_load(newcode,rx,ry,inst)
u_short		*newcode;
int		rx,ry;
addr_type	inst;
{
int	i = 0;

debugf(DB_ALL, printf("\tcau\tr%d,0x%x\t# _fp_inst_load\n",
			ry, inst.sh.HI + S_sign_of(inst.sh.LO)));
debugf(DB_ALL, printf("\tl\tr%d,0x%x(r%d)\t# _fp_inst_load\n",
			rx, inst.sh.LO,ry));
newcode[i++] = RT_short(RT_CAU,ry,0);	/* cau ry,inst.sh.HI */
newcode[i++] = inst.sh.HI + S_sign_of(inst.sh.LO);
newcode[i++] = RT_short(RT_L,rx,ry);	/* l rx,inst.sh.LO(ry) */
newcode[i++] = inst.sh.LO;

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


int
_fp_new_inst_load(newcode,gi,rx,inst)
u_short		*newcode;
global_info_type	*gi;
int		rx;
addr_type	inst;
{
int	i = 0,ry;

ry = _fp_get_genr(gi);
debugf(DB_ALL, printf("\tcau\tr%d,0x%x\t# _fp_inst_load\n",
			ry, inst.sh.HI + S_sign_of(inst.sh.LO)));
debugf(DB_ALL, printf("\tl\tr%d,0x%x(r%d)\t# _fp_inst_load\n",
			rx, inst.sh.LO,ry));
newcode[i++] = RT_short(RT_CAU,ry,0);	/* cau ry,inst.sh.HI */
newcode[i++] = inst.sh.HI + S_sign_of(inst.sh.LO);
newcode[i++] = RT_short(RT_L,rx,ry);	/* l rx,inst.sh.LO(ry) */
newcode[i++] = inst.sh.LO;
fp_free_genr(gi,ry);

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


/*
 * _fp_load:
 *
 * Generate code which loads one word from memory into a register.
 */
int
_fp_load(newcode, rx, basereg, offset)
u_short		*newcode;
int		rx, basereg;
addr_type	offset;
{
int		i=0;

	if ((offset.L & 0x0000003c) == offset.L) {
		newcode[i++] = RT_short(RT_LS | (offset.sh.LO<<6), rx, basereg);
		debugf(DB_ALL, printf("\tls\tr%d,0x%x(r%d)\t# _fp_load\n",
						rx,offset.sh.LO,basereg));
	} else if (small_pos_num(offset.L) || /* -(2^16) < x < 2^16 -1 */
		   small_neg_num(offset.L)) {
		newcode[i++] = RT_short(RT_L,rx,basereg);
		newcode[i++] = offset.sh.LO;
		debugf(DB_ALL, printf("\tl\tr%d,0x%x(r%d)\t# _fp_load\n",
						rx,offset.sh.LO,basereg));
	} else {
		newcode[i++] = RT_short(RT_CAU,rx,basereg);
		newcode[i++] = offset.sh.HI + S_sign_of(offset.sh.LO);
		debugf(DB_ALL, printf("\tcau\tr%d,0x%x(r%d)\t# _fp_load\n",
			rx,offset.sh.HI + S_sign_of(offset.sh.LO),basereg));
		if (offset.sh.LO) {
			newcode[i++] = RT_short(RT_L,rx,rx);
			newcode[i++] = offset.sh.LO;
			debugf(DB_ALL, printf("\tl\tr%d,0x%x(r%d)\t# _fp_load\n",rx,offset.sh.LO,rx));
		} else {
			newcode[i++] = RT_short(RT_LS,rx,rx);
			debugf(DB_ALL, printf("\tls\tr%d,0(r%d)\t# _fp_load\n",
						rx,rx));
		}
	} /* end else */

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


/*
 * _fp_store:
 *
 * Generate code which stores a register into one word of memory.
 */
int
_fp_store(newcode, gi, rx, basereg, offset)
u_short			*newcode;
global_info_type	*gi;
int			rx, basereg;
addr_type		offset;
{
int			i=0, ry;

	if ((offset.L & 0x0000003c) == offset.L) {
		newcode[i++] = RT_short(RT_STS | (offset.sh.LO<<6),rx,basereg);
		debugf(DB_ALL, printf("\tsts\tr%d,0x%x(r%d)\t# _fp_store\n",
					rx,offset.sh.LO,basereg));
	} else if (small_pos_num(offset.L) || small_neg_num(offset.L)) {
		/* -(2^16) < x < 2^16 -1 */
		newcode[i++] = RT_short(RT_ST,rx,basereg);
		newcode[i++] = offset.sh.LO;
		debugf(DB_ALL, printf("\tst\tr%d,0x%x(r%d)\t# _fp_store\n",
					rx,offset.sh.LO,basereg));
	} else {
		ry = _fp_get_genr(gi);
		newcode[i++] = RT_short(RT_CAU,ry,basereg);
		newcode[i++] = offset.sh.HI + S_sign_of(offset.sh.LO);
		debugf(DB_ALL, printf("\tcau\tr%d,0x%x(r%d)\t# _fp_store\n",
			ry,offset.sh.HI + S_sign_of(offset.sh.LO),basereg));
		if (offset.sh.LO) {
			newcode[i++] = RT_short(RT_ST,rx,ry);
			newcode[i++] = offset.sh.LO;
			debugf(DB_ALL, printf("\tst\tr%d,0x%x(r%d)\t# _fp_store\n", rx,offset.sh.LO,ry));
		} else {
			newcode[i++] = RT_short(RT_STS,rx,ry);
			debugf(DB_ALL, printf("\tsts\tr%d,0(r%d)\t# _fp_store\n",
						rx,ry));
		}
		fp_free_genr(gi, ry);
	} /* end else */

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


/*
 * _fp_getimm:
 *
 * Generate code which creates one word of immediate value in a register.
 */
int
_fp_getimm(newcode, rx, value)
u_short		*newcode;
int		rx;
addr_type	value;
{
int		i=0;

	if ((value.L & 0xf) == value.L) {	/* 0 <= value <= 15 */
		newcode[i++] = RT_short(RT_LIS,rx,(value.L & 0xf));
		debugf(DB_ALL, printf("\tlis\tr%d,0x%x\t# _fp_getimm\n",
					rx,value.sh.LO));
	} else if (small_neg_num(value.L)) {	/* -(2^16) <= value */
		newcode[i++] = RT_short(RT_CAL,rx,0);
		newcode[i++] = value.sh.LO;
		debugf(DB_ALL, printf("\tcal\tr%d,0x%x\t# _fp_getimm\n",
					rx,value.sh.LO));
	} else if (value.sh.LO) {
		debugf(DB_ALL, printf("\tcal16\tr%d,0x%x\t# _fp_getimm\n",
					rx,value.sh.LO));
		newcode[i++] = RT_short(RT_CAL16,rx,0);
		newcode[i++] = value.sh.LO;
		if (value.sh.HI) {
			newcode[i++] = RT_short(RT_OIU,rx,rx);
			newcode[i++] = value.sh.HI;
			debugf(DB_ALL, printf("\toiu\tr%d,0x%x(r%d)\t# _fp_getimm\n",rx,value.sh.HI,rx));
		}
	} else {				/* value = 0xXXXX0000 */
		newcode[i++] = RT_short(RT_CAU,rx,0);
		newcode[i++] = value.sh.HI;
		debugf(DB_ALL, printf("\tcau\tr%d,0x%x(r0)\t# _fp_getimm\n",
					rx,value.sh.HI));
	}

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


/*
 * opnd2greg:
 *
 * Generate code which gets an operand, opnum, into a general
 * register, rx.  Hilo indicates hi word or lo word of a double
 * operand.
 */
int
_fp_opnd2greg(newcode, gi, rx, opnum, hilo)
u_short			newcode[];
global_info_type	*gi;
int			*rx, opnum, hilo;
{
addr_type		addr;
int			breg, i;

	switch hi_optype_of(gi, opnum) {
		case ADDRTYPE:
			addr.L = getopnd(gi, opnum, 0) + 4*hilo;
			breg = byteval_of(gi, opnum);
			i = _fp_load(newcode, *rx, breg, addr);
			break;
		case IMMEDTYPE:
			addr.L = getopnd(gi, opnum, hilo);
			i = _fp_getimm(newcode, *rx, addr);
			break;
		case GREGTYPE:
			i = 0;
			if (lo_optype_of(gi, opnum) == DBLETYPE)
				if (hilo)
					*rx = (byteval_of(gi, opnum) & 0x0f);
				else
					*rx = (byteval_of(gi, opnum) >> 4);
			else
				*rx = (byteval_of(gi, opnum) & 0x0f);
			break;
		default:
			break;
	}

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


/* 
 * table for number of 1 bits in a value between 0-255;
 */
int	_num_1s_tbl[256] =	{
		0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
		4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 
		};

