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

#include <errno.h>
#include <stdio.h>
#include "as.h"

/*
 *	Construct a floating point number
 */
#ifdef FRMBUG
void as_atof(numbuf, radix, ovfp, result)
	Bignum	*result;
#else
Bignum as_atof(numbuf, radix, ovfp)
#endif
	char	*numbuf;
	int	radix;
	Ovf	*ovfp;
{
	Bignum	number;
	extern	int	errno;
	double	atof();

	number = Znumber;
	errno = 0;
	switch(radix){
	case TYPF:
	case TYPD:
		number.num_tag = TYPD;
		*ovfp = 0;
		number.num_num.numFd_float.Fd_value = atof(numbuf);
		break;
	case TYPH:
		number = bigatof(numbuf, radix);
		break;
	}
	if (errno == ERANGE && passno == 2){
		yywarning("Floating conversion over/underflowed\n");
	}
#ifdef FRMBUG
	*result = number;
	return;
#else
	return(number);
#endif
}

/*
 *	Convert a string to an integer.
 */

#ifdef FRMBUG
void as_atoi_frm(ccp, radix, ovfp, result)
	Bignum *result;
#else FRMBUG
Bignum as_atoi(ccp, radix, ovfp)
#endif FRMBUG
	reg	char	*ccp;		/* character cp */
		int	radix;
		Ovf	*ovfp;
{
	reg	chptr	bcp;
		chptr	tcp;
	reg	int	i;
		int	val;
		Bignum	n_n;
		Bignum	t_n;
		int	sign;
		Ovf	ovf;

	ovf = 0;
	sign = 0;
	/*
	 *	Step through the string one byte at a time until
	 *	ccp points to something other than 0, +, or -.
	 */
	for (; *ccp; ccp++){
		switch(*ccp){
		case '0':
		case '+':	continue;
		case '-':	sign ^= 1;
				continue;
		}
		break;
	}

	n_n = Znumber;			/* zero out n_n */
	t_n = Znumber;			/* zero out t_n */
					/* calling numclear for bcp and tcp
					   should be therefore redundant */
	bcp = CH_FIELD(n_n); (void)numclear(bcp);
	tcp = CH_FIELD(t_n); (void)numclear(tcp);
	/*
	 *	Step through the string byte by byte.
	 */
	for (; *ccp; ccp++){
		switch(*ccp){
		case '8': case '9':
			if (radix < 10)
				goto done;
			/*FALLTHROUGH*/
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7':
			val = *ccp - '0';
			break;
		case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
			if (radix < 16)
				goto done;
			val = *ccp - 'A' + 10;
			break;
		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
			if (radix < 16)
				goto done;
			val = *ccp - 'a' + 10;
			break;
		default:
			goto done;
		}
		switch(radix){
		case 8:
		/*
		 *	For an octal number, multiply the previous total by 8
		 *	(shift left 3).
		 */
			ovf |= numshift(3, bcp, bcp);
			break;
		case 16:
		/*
		 *	For a hexadecimal number, multiply the previous total
		 *	by 16 (shift left 4).
		 */
			ovf |= numshift(4, bcp, bcp);
			break;
		case 10:
		/*
		 *	For a decimal number, multiply the previous total by
		 *	10 (shift left 1, then shift left 3).
		 */
			ovf |= numshift(1, tcp, bcp);
			ovf |= numshift(3, bcp, bcp);
			ovf |= numaddv(bcp, tcp, bcp);
			break;
		}
		ovf |= numaddd(bcp, bcp, val);
	}
  done: ;
	ovf |= posovf(bcp);
	if (sign){
		if (ovf & OVF_MAXINT) {
			ovf &= ~(OVF_MAXINT | OVF_POSOVF);
		} else {
			ovf |= numnegate(bcp, bcp);
		}
	}
	/*
	 *	Find the first null chunk (the first chunk of 32 bits
	 *	which is zero).
	 */
	val = sign ? -1 : 0;
	for (i = 0; i < CH_N; i++){
		if (bcp[i] == val)
			break;
	}
	/*
	 *	The first line of the tagtab table gives the smallest
	 *	unit into which the number could be stored if a chunk
	 *	is 8 bits long (i.e. 1 byte per chunk).
	 *
	 *	The second line of the tagtab table gives the smallest
	 * 	unit into which the number could be stored if a chunk
	 *	is 16 bits long (i.e. 2 bytes per chunk).
	 *
	 *	The third line is zero because no one has considered
	 *	having 24 bit chunks.
	 *
	 *	The fourth line of the tagtab table gives the smallest
	 *	unit into which the number could be stored if a chunk
	 *	is 32 bits long (i.e. 4 bytes per chunk).
	 */
	{
		static u_char tagtab[4][8] = {
		{	TYPB,
			TYPS,
			TYPL, TYPL,
			TYPU, TYPU, TYPU, TYPU},
		{	TYPS,
			TYPL,
			TYPU, TYPU},
		{   0   },
		{	TYPL,
			TYPU }
		};
		/*
		 *	Since i points to the null chunk, subtract one
		 *	so that it points to the last non null chunk.
		 */
		i -= 1;
		if (i < 0)
			i = 0;
		n_n.num_tag = tagtab[BPC][i];
		assert(n_n.num_tag != 0, "Botch width computation");
	}
	*ovfp = ovf;
#ifdef FRMBUG
	*result = n_n;
	return;
#else FRMBUG
	return(n_n);
#endif FRMBUG
}

Ovf posovf(src)
	reg	chptr	src;
{
	reg	int	i;
	Ovf	overflow = 0;

	if (src[HOC] & SIGNBIT)
		overflow = OVF_POSOVF;
	if (src[HOC] == SIGNBIT){
		for (i = HOC - 1; i >= 0; --i){
			if (src[i] != 0)
				return(overflow);
		}
		overflow |= OVF_MAXINT;
	}
	return(overflow);
}

/*
 *	check if the number is clear
 */
int isclear(dst)
	reg	chptr	dst;
{
	return(!isunequal(dst, CH_FIELD(Znumber)));
}

int isunequal(src1, src2)
	reg	chptr	src1, src2;
{
	reg	int	i;

	i = CH_N;
	do{
		if (*src1++ != *src2++)
			return(i);
	}while(--i);
	return(0);
}

Ovf numclear(dst)
	reg	chptr	dst;
{
	reg	int	i;
	i = CH_N;
	do{
		*dst++ = 0;
	}while(--i);
	return(0);
}

Ovf numshift(n, dst, src)
		int	n;
	reg	chptr	dst, src;
{
	reg	int	i;
	reg	u_int	carryi, carryo;
	reg	u_int	mask;
	reg	u_int	value;

	i = CH_N;
	if (n == 0){
		do{
			*dst++ = *src++;
		} while(--i);
		return(0);
	}

	carryi = 0;
	mask = ONES(n);

	if (n > 0){
		do{
			value = *src++;
			carryo = (value >> (CH_BITS - n)) & mask; 
			value <<= n;
			value &= ~mask;
			*dst++ = value | carryi;
			carryi = carryo;
		} while (--i);
		return(carryi ? OVF_LSHIFT : 0);
	} else {
		n = -n;
		src += CH_N;
		dst += CH_N;
		do{
			value = *--src;
			carryo = value & mask; 
			value >>= n;
			value &= ONES(CH_BITS - n);
			*--dst = value | carryi;
			carryi = carryo << (CH_BITS - n);
		} while (--i);
		return(carryi ? OVF_LSHIFT : 0);
	}
}

Ovf numaddd(dst, src1, val)
	chptr	dst, src1;
	int	val;
{
	static	Bignum	work;

/*	work.num_uchar[0] = val;	*/
	(CH_FIELD(work))[0] = val;	/* chunk size independent. */
	return (numaddv(dst, src1, CH_FIELD(work)));
}

Ovf numaddv(dst, src1, src2)
	reg	chptr	dst, src1, src2;
{
	reg	int	i;
	reg	int	carry;
	reg	u_int	A,B,value;

	carry = 0;
	i = CH_N;
	do{
		A = *src1++;
		B = *src2++;
		value = A + B + carry;
		*dst++ = value;
		carry = 0;
		if (value < A || value < B)
			carry = 1;
	} while (--i);
	return(carry ? OVF_ADDV : 0);
}

Ovf numnegate(dst, src)
	chptr	dst, src;
{
	Ovf	ovf;

	ovf = num1comp(dst, src) ;
	ovf |= numaddd(dst, dst, 1);
	return(ovf);
}

Ovf num1comp(dst, src)
	reg	chptr	dst, src;
{
	reg	int	i;
	i = CH_N;
	do{
		*dst++ = ~ *src++;
	}while (--i);
	return(0);
}

/*
 *	Determine if floating point numbers are
 *	capable of being represented as a one byte immediate literal constant
 *	If it is, then stuff the value into *valuep.
 *	argtype is how the instruction will interpret the number.
 */
int slitflt(number, argtype, valuep)
	Bignum	number;		/* number presented */
	int	argtype;	/* what the instruction expects */
	int	*valuep;
{
#define	EXPPREC 3
#define	MANTPREC 3

		int	mask;
	reg	int	i;
		Bignum	unpacked;
		Ovf	ovf;

	*valuep = 0;
	if (!ty_float[argtype])
		return(0);
	unpacked = bignumunpack(number, &ovf);
	assert(ovf == 0, "overflow in unpacking floating #!?");
	if (unpacked.num_sign)
		return(0);
	if (unpacked.num_exponent < 0)
		return(0);
	if (unpacked.num_exponent > ONES(EXPPREC))
		return(0);
	for (i = 0; i < HOC; i++){
		if (CH_FIELD(unpacked)[i])
			return(0);
	}
	if ((CH_FIELD(unpacked)[HOC]) & ONES(CH_BITS - MANTPREC))
		return(0);
	*valuep = (unpacked.num_exponent & ONES(EXPPREC)) << MANTPREC;
	mask = (CH_FIELD(unpacked)[HOC]) >> (CH_BITS - MANTPREC);
	mask &= ONES(MANTPREC);
	*valuep |= mask;
	*valuep &= ONES(MANTPREC + EXPPREC);
	return(1);
}

#ifndef STANDALONE
/*
 *	Output a big number to txtfil
 *	Called only when passno == 2
 *
 *	The conversion specifies the width of the number to be written out.
 *	The width is supplied from either an initialized data directive
 *	(for example .float, .double), or from the operand size
 *	defined by an operator.
 *	If the number is of type dlong (double long),
 *	we just write it out; this allows one to specify bit
 *	patterns for floating point numbers.
 *	If the number is one of the floating types and the conversion
 *	is not the same type, then we complain, but do the conversion anyway.
 *	The conversion is strict.
 */
bignumwrite(number, toconv)
	Bignum	number;
	int	toconv;		/* one of TYP[U FDH] */
{
	reg	u_int	*bp;

	if (passno != 2)
		return;

	bp = &number.num_uint[0];
	switch(number.num_tag){
	case TYPB:
	case TYPS:
	case TYPL:
	case TYPU:
		number = intconvert(number, toconv);
		break;
	default:
		number = floatconvert(number, toconv);
		break;
	}
#ifndef	ONVAX
	bwrite((char *)bp, ty_nbyte[toconv], txtfil);
#else
	if (toconv == TYPF || toconv == TYPD ) {
		double	iflt;
		cvtfloat(bp, &iflt);
		bwrite(&iflt, ty_nbyte[toconv], txtfil);
		return;
	} else
	bwriteflip((char *)bp, ty_nbyte[toconv], txtfil);
#endif
}
#endif STANDALONE

#ifdef	ONVAX
struct ffloat {
unsigned int fract1: 7,
	exp: 8,
	sign:1,
	fract2: 16;
unsigned int	fract3: 16,
		fract4: 16;
};


union vaxfloat
{
double d;
struct ffloat ff;
char bytes[8];
};

struct ifloat
{
unsigned int
	exp:7,
	sign:1,
	fract1:24;
unsigned int fract2:32;
};

union ibmfloat
{
struct ifloat ff;
char bytes[8];
double d;
};

#define EXP_EXCESS_VAX 128
#define EXP_EXCESS_IBM 64
#define FLOAT_BYTES	8

cvtfloat(in,out) union vaxfloat *in; union ibmfloat *out;
{
/*
 * do byte and bit manipulations to convert floating point on vax
 * to ibm format
 */
register char *temp = out->bytes+1;
register int exp = in->ff.exp - EXP_EXCESS_VAX;
register int i, shift;

if (in->d == 0.0)
	{
	for (i=0; i<FLOAT_BYTES; ++i)
		out->bytes[i] = 0;
	return;
	}
temp[0] = 0200 | in->ff.fract1;		/* put back hidden bit */
for (i=2,shift=0; i<FLOAT_BYTES; ++i, shift= 2-shift)
	temp[i-shift] = in->bytes[i];
while (exp&03)
	{
	++exp;
	bitright(temp,FLOAT_BYTES-1);
	}
out->ff.exp = (exp>>2)+EXP_EXCESS_IBM;
out->ff.sign = in->ff.sign;
}

bitright(ptr,ptrl) unsigned char *ptr;
{
register int carry, n;

for (carry=0; --ptrl >= 0; carry = n, ++ptr)
	{
	n = *ptr&01;
	*ptr = (carry << 7) | (*ptr >> 1);
	}
return(carry);
}
#endif 
