 /* $Header:bignum2.c 12.0$ */
 /* $ACIS:bignum2.c 12.0$ */
 /* $Source: /ibm/acis/usr/src/bin/as_ca/RCS/bignum2.c,v $ */
#ifndef lint
static char rcsid[] = "$Header:bignum2.c 12.0$";
#endif
/* $Source: /ibm/acis/usr/src/bin/as_ca/RCS/bignum2.c,v $ */
/*
 *	Copyright (c) 1982 Regents of the University of California
 */
#ifndef lint
static char sccsid[] = "@(#)bignum2.c 4.3 2/14/82";
#endif not lint

#include <stdio.h>
#include "as.h"
Bignum	Znumber;			/* zero reference */
#define	MINEXP		-32768		/* never generate; reserved for id 0 */

Bignum intconvert(number, convto)
	Bignum	number;
	int	convto;
{
	reg	int	i;
	if (number.num_tag == convto)
		return(number);
	if (ty_nbyte[number.num_tag] > ty_nbyte[convto] && (passno == 2)){
		yywarning("Conversion between %s and %s looses significance",
			ty_string[number.num_tag],
			ty_string[convto]);
	}
	for (i = ty_nbyte[convto]; i < ty_nbyte[TYPU]; i++)
		number.num_uchar[i] = 0;
	return(number);
}

#define	CONV(src, dst)	(((src) << TYPLG) + (dst))

Bignum floatconvert(number, convto)
	Bignum	number;
	int	convto;
{
	reg	u_int	*bp;		/* r11 */
		int	loss = 0;
		int	gain = 0;
		int	mixs = 0;
		Ovf	ovf;

	if (number.num_tag == convto)
		return(number);
	bp = &number.num_uint[0];
#ifdef lint
	*bp = *bp;
#endif lint

	switch(CONV(number.num_tag, convto)){

	case CONV(TYPF, TYPD):	 /* asm("cvtfd (r11), (r11)"); */ break;
	/* case CONV(TYPF, TYPH):	mixs++; break; */

	case CONV(TYPD, TYPF):	/* asm("cvtdf (r11), (r11)");*/ break;
	/* case CONV(TYPD, TYPH):	mixs++; break; */

	/* case CONV(TYPH, TYPF):	mixs++; break;
	case CONV(TYPH, TYPD):	mixs++; break; */
	default:	panic("Bad floating point conversion?");
	}
	if ((gain || mixs || loss) && (passno == 2)){
		yywarning("Converting from %s to %s: %s ",
			ty_string[number.num_tag],
			ty_string[convto],
			gain ? "gains significance" :
			(loss ? "looses significance" : "mixs exponent formats")
		);
	}
	if (mixs){
		number = bignumconvert(number, convto, &ovf);
		if (ovf && passno == 2){
			yywarning("Floating conversion over/underflowed\n");
		}
	} else {
		number.num_tag = convto;
	}
	return(number);
}

/* 
 *	Convert a big number between various representations
 */
Bignum bignumconvert(number, toconv, ovfp)
	Bignum	number;
	int	toconv;
	Ovf	*ovfp;
{
	int	tag;

	*ovfp = 0;
	tag = number.num_tag;
	if (tag == toconv)
		return(number);
	if (tag == TYPUNPACKED){
		return(bignumpack(number, toconv, ovfp));
	}
	if (toconv == TYPUNPACKED){
		return(bignumunpack(number, ovfp));
	}
	return(bignumpack(bignumunpack(number, ovfp), toconv, ovfp));
}

Bignum bignumunpack(Packed, ovfp)
	Bignum	Packed;
		Ovf	*ovfp;
{
		Bignum	Mantissa;
		Bignum	Enumber;
	reg	int	i;
		int	j;
		int	k;
	reg	struct	ty_bigdesc	*p;
	reg	chptr	packed;
	reg	chptr	mantissa;
	reg	chptr	enumber;
		u_short	exponent;
		int	sign;
		int	mask;

	p = &ty_bigdesc[Packed.num_tag];

	*ovfp = 0;
	Mantissa = Znumber;
	sign = 0;
	exponent = 0;
	mantissa = CH_FIELD(Mantissa);
	enumber = CH_FIELD(Enumber);
	packed = CH_FIELD(Packed);

	if (isclear(packed)){
		Mantissa.num_tag = TYPUNPACKED;
		Mantissa.num_exponent = MINEXP;
		return(Mantissa);
	}
	/*
	 *	map the packed number into the mantissa, using
	 *	the unpacking map
	 */
	mapnumber(mantissa, packed, 16, p->b_upmmap);
	/*
	 *	perform the mantissa shifting.
	 *	This may appear to overflow; all that is lost
	 *	is low order bits of the exponent.
	 */
	(void)numshift(p->b_mlshift, mantissa, mantissa);
	/*
	 *	handle sign and exponent
	 */
	switch(Packed.num_tag){
	case TYPB:
	case TYPS:
	case TYPL:
	case TYPU:
		sign = 0;
		exponent = p->b_eexcess;
		if (mantissa[HOC] & SIGNBIT){
			sign = -1;
			*ovfp |= numnegate(mantissa, mantissa);
		}
		/*
		 *	Normalize the packed by left shifting,
		 *	adjusting the exponent as we go.
		 *	Do a binary weighted left shift for some speed.
		 */
		k = 0;
		for (j = 4; j >= 0; --j){
			i = 1 << j;	/* 16, 8, 4, 2, 1 */
			while(1){
				if (k >= p->b_msigbits)
					break;
				mask = ONES(i) << (CH_BITS - i);
				if (mantissa[HOC] & mask)
					break;
				(void)numshift(i, mantissa, mantissa);
				k += i;
				exponent -= i;
			}
		}
		assert(mantissa[HOC] & SIGNBIT, "integer <<ing");
		/*
		 *	now, kick the most significant bit off the top
		 */
		(void)numshift(1, mantissa, mantissa);
		break;
	default:
		/*
		 *	map the exponent into the local area.
		 */
		Enumber = Znumber;
		mapnumber(enumber, packed, 2, p->b_upemap);
		/*
		 *	Extract the exponent, and get rid
		 *	of the sign bit
		 */
		exponent = Enumber.num_ushort[0] & ONES(15);
		/*
		 *	shift the exponent, and get rid of high order
		 *	trash
		 */
		exponent >>= p->b_ershift;
		exponent &= ONES(p->b_esigbits);
		/*
		 *	un excess the exponent
		 */
		exponent -= p->b_eexcess;
		/*
		 *	extract and extend the sign bit
		 */
		sign = (Enumber.num_ushort[0] & ~ONES(15)) ? -1 : 0;
	}
	/*
	 *	Assemble the pieces, and return the number
	 */
	Mantissa.num_tag = TYPUNPACKED;
	Mantissa.num_sign = sign;
	Mantissa.num_exponent = exponent;
	return(Mantissa);
}

Bignum bignumpack(Unpacked, toconv, ovfp)
	Bignum	Unpacked;
	int	toconv;
	Ovf	*ovfp;
{
	Bignum	Back;
	Bignum	Enumber;
	Bignum	Temp;

		short	exponent;
		char	sign;
	reg	struct	ty_bigdesc	*p;
	reg	chptr	back;
	reg	chptr	enumber;
	reg	chptr	temp;
	reg	chptr	unpacked;

		int	i,j;

	if (Unpacked.num_tag != TYPUNPACKED)
		panic("Argument to bignumpack is not unpacked");

	*ovfp = 0;
	Back = Znumber;
	Temp = Znumber;
	Back.num_tag = toconv;

	back = CH_FIELD(Back);
	temp = CH_FIELD(Temp);
	enumber = CH_FIELD(Enumber);
	unpacked = CH_FIELD(Unpacked);
	p = &ty_bigdesc[toconv];

	exponent = Unpacked.num_exponent;
	sign = Unpacked.num_sign;
	if (exponent == MINEXP)
		return(Back);	/* identically zero */

	switch(toconv){
	case TYPB:
	case TYPS:
	case TYPL:
	case TYPU:
		/*
		 *	Put back in the assumed high order fraction
		 *	bit that is always a 1.
		 */
		(void)numshift(-1, temp, unpacked);
		temp[HOC] |= SIGNBIT;
		if (exponent > p->b_eexcess){
			/*
			 *	Construct the largest positive integer
			 */
			(void)numclear(temp);
			(void)num1comp(temp, temp);
			temp[HOC] &= ~SIGNBIT;
			sign = sign;
			*ovfp |= OVF_OVERFLOW;
		} else
		if (exponent <= 0){
			/*
			 *	chop the temp; underflow to integer 0
			 */
			(void)numclear(temp);
			sign = 0;
			*ovfp |= OVF_UNDERFLOW;
		} else {
			/*
			 *	denormalize the temp.
			 *	This will again chop, by shifting
			 *	bits off the right end into oblivion.
			 */
			for (j = 4; j >= 0; --j){
				i = 1 << j;	/* 16, 8, 4, 2, 1 */
				while(exponent + i <= p->b_eexcess){
					numshift(-i, temp, temp);
					exponent += i;
				}
			}
		}
		/*
		 *	negate the temp if the sign is set
		 */
		if (sign)
			*ovfp |= numnegate(temp, temp);
		/*
		 *	Stuff the temp number into the return area
		 */
		mapnumber(back, temp, 16, p->b_pmmap);
		return(Back);
	default:
		/*
		 *	Shift the mantissa to the right, filling in zeroes on
		 *	the left.  This aligns the least significant bit
		 *	on the bottom of a byte, something that upround
		 *	will use.
		 *	Put the result into a temporary.
		 *	Even though the shift may be zero, there
		 *	is still a copy involved here.
		 */
		(void)numshift(-(p->b_mlshift), temp, unpacked);
		/*
		 *	Perform the rounding by adding in 0.5 ulp's
		 */
		exponent = upround(&Temp, p, exponent);
		/*
		 *	Do a range check on the exponent, in preparation
		 *	to stuffing it in.
		 */
		if ((short)(exponent + p->b_eexcess) == 0){
			/*
			 *	Sorry, no gradual underflow on the
			 *	VAX.  Chop this beasty totally to zero
			 */
			goto zeroret;
		} else
		if ((short)(exponent + p->b_eexcess) < 0){
			/*
			 *	True underflow will happen;
			 *	Chop everything to positive zero
			 */
		  zeroret:
			(void)numclear(temp);
			exponent = 0;
			sign = 0;	/* avoid reserved operand! */
			*ovfp |= OVF_UNDERFLOW;
		} else
		if ((unsigned)(exponent + p->b_eexcess)
		    >= (unsigned)(1 << p->b_esigbits)){
			/*
			 *	Construct the largest magnitude possible
			 *	floating point unpacked: 0.{1}111111111
			 */
			(void)numclear(temp);
			(void)num1comp(temp, temp);
			exponent = ONES(p->b_esigbits);
			sign = sign;
			*ovfp |= OVF_OVERFLOW;
		} else {
			/*
			 *	The exponent will fit.
			 *	Bias it up, and the common code will stuff it.
			 */
			exponent += p->b_eexcess;
		}
		exponent <<= p->b_ershift;
		/*
		 *	mask out trash for the sign, and put in the sign.
		 */
		exponent &= ONES(15);
		if (sign)
			exponent |= ~ONES(15);
		Enumber.num_ushort[0] = exponent;
		/*
		 *	Map the unpacked exponent into the value going back
		 */
		mapnumber(back, enumber, 2, p->b_pemap);
		/*
		 *	Stuff the unpacked mantissa into the return area
		 */
		mapnumber(back, temp, 16, p->b_pmmap);
		return(Back);
	}
	/*NOTREACHED*/
}

mapnumber(chp1, chp2, nbytes, themap)
		chptr	chp1, chp2;
		int	nbytes;
		char	*themap;
{
	reg	int	i;
	reg	u_char	*p1, *p2;

	p1 = (u_char *)chp1;
	p2 = (u_char *)chp2;
	for (i = 0; i < nbytes; i++){
		switch(themap[i]){
		case NOTAKE:
			break;
		default:
			p1[themap[i]] |= p2[i];
			break;
		}
	}
}

#define	UPSHIFT	2
/*
 *	round in 1/2 ulp in the number, possibly modifying
 *	the binary exponent if there was total carry out.
 *	Return the modified exponent
 */
int upround(numberp, p, exponent)
	reg	Bignum	*numberp;
	reg	struct	ty_bigdesc	*p;
		int	exponent;
{
	reg	u_char	*bytep;
		int	nbytes;
		int	byteindex;
		int	hofractionbit;
		int	ovffractionbit;
	reg	int	ovfbitindex;
	reg	chptr	number;
	static	Bignum	ulp;

	/*
	 *	Find out the byte index of the byte containing the ulp
	 */
	number = CH_FIELD(numberp[0]);
	bytep = numberp->num_uchar;

	nbytes = (p->b_msigbits - 1) + p->b_mlshift;
	assert((nbytes % 8) == 0, "mantissa sig bits");
	nbytes /= 8;
	byteindex = 15 - nbytes;
	assert(byteindex >= 0, "ulp in outer space");
	/*
	 *	Shift the number to the right by two places,
	 *	so that we can do full arithmetic without overflowing
	 *	to the left.
	 */
	numshift(-UPSHIFT, number, number);
	/*
	 *	Construct the missing high order fraction bit
	 */
	ovfbitindex = 8 - (p->b_mlshift + UPSHIFT);
	assert(ovfbitindex >= 0, "Shifted byte 15 into byte 14");
	hofractionbit = (0x01 << ovfbitindex);
	ovffractionbit = (0x02 << ovfbitindex);
	bytep[15] |= hofractionbit;
	/*
	 *	construct the unit in the last place, and it
	 *	to the fraction
	 */
	ulp.num_uchar[byteindex] |= (0x80 >> UPSHIFT);
	numaddv(number, number, CH_FIELD(ulp));
	ulp.num_uchar[byteindex] &= ~(0x80 >> UPSHIFT);
	/*
	 *	Check if there was an overflow,
	 *	and adjust by shifting.
	 *	Also, bring the number back into canonical
	 *	unpacked form by left shifting by two to undeo
	 *	what we did before.
	 */
	if (bytep[15] & ovffractionbit){
		exponent += 1;
		numshift(UPSHIFT - 1, number, number);
	} else {
		numshift(UPSHIFT, number, number);
	}
	/*
	 *	Clear off trash in the unused bits of the high
	 *	order byte of the number
	 */
	bytep[15] &= ONES(8 - p->b_mlshift);
	return(exponent);
}
#ifdef DEBUG
bignumprint(number)
	Bignum	number;
{
	printf("Bignum: %s (exp: %d, sign: %d) 0x%08x%08x%08x%08x",
		ty_string[number.num_tag],
		number.num_exponent,
		number.num_sign,
		number.num_uint[3],
		number.num_uint[2],
		number.num_uint[1],
		number.num_uint[0]);
	switch(number.num_tag){
	case TYPB:
	case TYPS:
	case TYPL:
	case TYPU:
	case TYPUNPACKED:
		break;
	case TYPF:
		printf(" == %10.8e", number.num_num.numFf_float.Ff_value);
		break;
	case TYPD:
		printf(" == %20.17e", number.num_num.numFd_float.Fd_value);
		break;
	case TYPH:
		break;
	}
}

numprintovf(ovf)
	Ovf	ovf;
{
	int	i;
	static struct	ovftab{
		Ovf	which;
		char	*print;
	} ovftab[] = {
		OVF_POSOVF,	"posovf",
		OVF_MAXINT,	"maxint",
		OVF_ADDV,	"addv",
		OVF_LSHIFT,	"lshift",
		OVF_F,		"F float",
		OVF_D,		"D float",
		OVF_H,		"H float",
		OVF_OVERFLOW,	"cvt overflow",
		OVF_UNDERFLOW,	"cvt underflow",
		0,		0
	};
	for(i = 0; ovftab[i].which; i++){
		if (ovf & ovftab[i].which)
			printf("Overflow(%s) ", ovftab[i].print);
	}
}
#endif DEBUG
