static char sccs_id[] = "@(#)actions_util.c	1.1	10/31/94";
/*
 * This file contains actions associated with the parse rules in "parse.y".
 *
 * Also see "actions_*.c".
 */

#include <stdio.h>
#include <values.h>
#include <floatingpoint.h>
#include "structs.h"
#include "y.tab.h"
#include "bitmacros.h"
#include "errors.h"
#include "actions.h"
#include "instructions.h"
#include "globals.h"
#include "lex.h"	/* for curr_flp */
#include "build_instr.h"
#include "segments.h"
#include "obj.h"
#include "registers.h"
#include "opt_unique_label.h"

/*---------------------------------------------------------------------------*/
/*	Utility routines for actions...
/*---------------------------------------------------------------------------*/

void
chk_absolute(vp)
	register struct value *vp;
{
	if (VALUE_IS_ABSOLUTE(vp))
	{
		/* all is OK; the value is an absolute number		*/
		/* (i.e. no relocatable symbol is associated with it)	*/
	}
	else
	{
		/* not an absolute value!  take the approp. error actions */
		abs_error(vp);
	}
}

Bool
chk_redefn(symp)   /* returns TRUE if symbol is being erroneously re-defined */
	register struct symbol *symp;
{
	if ( ((symp->s_attr & (SA_DEFN_SEEN|SA_LOCAL)) == SA_DEFN_SEEN)
#ifdef EXCL_COMMON_DEFINE
	     || ( BIT_IS_ON(symp->s_attr, SA_COMMON) )
#else
		/* #else:  previous ".common" definitions are ignored,
		 * and will be overridden by "real" definitions.
		 */
#endif
	   )
	{
		/* The symbol is previously defined, but not a local symbol
		 * (local symbols can be re-defined many times);
		 * OR it has been declared to be a COMMON symbol and
		 * should never actually BE defined.
		 */

		/* attempted redefinition of a user label */
		error(ERR_REDEFN, input_filename, input_line_no,
				symp->s_symname);

		/* Let's try to give the user some more information about what
		 * he/she did wrong, if possible.  If the label being redefined
		 * looks just like one generated by the peephole optimizer, it
		 * is quite likely that this input file was ALREADY optimized,
		 * via "as -O -S".  If this is the case, suggest that to the
		 * user.
		 */
		if ( (optimization_level > 0) &&
		     is_unique_optimizer_label(symp->s_symname)
		   )
		{
			error(ERR_REDEFN_OPT, input_filename, input_line_no);
		}

		return TRUE;
	}
	else	return FALSE;
}

void
abs_error(vp)
	register struct value *vp;
{
	/* not an absolute value:  [1] complain and [2] fix it	*/
	error(ERR_ABSREQD, input_filename, input_line_no);
	vp->v_symp  = NULL;
	vp->v_value = 0;
}


static unsigned long int
get_reg_group(regno)
	register Regno regno;
{
	switch ( regno & ~(MASK(5)) )
	{
	case _GPREGS:	return RM_GP;
	case _FPREGS:	return RM_FP;
	case _CPREGS:	return RM_CP;
	case _MISCREGS:	return RM_misc(RAW_REGNO(regno));
	default:	return RM_OTHER;
	}
}


Regno
chk_regno(regno, valid_regs)
	register Regno	  regno;
	unsigned long int valid_regs;
{
	if ( (get_reg_group(regno) & valid_regs) == 0 )
	{
		/* invalid register specified */
		error(ERR_INV_REG, input_filename, input_line_no);
		regno = ((Regno)0);
	}

	return regno;
}


void
define_symbol(symp, segment, value)
	register struct symbol *symp;
	SEGMENT  segment;
	long int value;
{
	/* define a symbol */

	/* Mark that this symbol has been actually defined, i.e. had its
	 * final value assigned to it.
	 */
	SET_BIT(symp->s_attr, SA_DEFINED);

#ifdef EXCL_COMMON_DEFINE
#else
	/* #else: override any previous ".common" definition
	 */
	CLR_BIT(symp->s_attr, SA_COMMON);
#endif
	symp->s_segment = segment;
	symp->s_value   = value;

	switch ( *(symp->s_symname) )
	{
	case '&':
		/* Never write internal '&' (internal beginning-of-segment)
		 * symbols to the object file.
		 */
		symp->s_attr |= SA_NOWRITE;
		break;
	case 'L':
		/* This is a local label (begins with 'L').
		 * If local labels aren't to be kept in the object file,
		 * then mark it not to (ever) be written to the object file.
		 */
		if (!curr_flp->fl_options.opt_L_lbls)
		{
			symp->s_attr |= SA_NOWRITE;
			break;
		}
		else  ;/* fall through */
	default:
		/* Do nothing now, but later write the symbol's definition
		 * to the object file.
		 */
		break;
	}
}

#ifdef OLD_EXTENDED
     typedef struct { extended x; } extended_kludge;
#    define LONGREAL extended_kludge
#    define DECIMAL_TO_LONGREAL decimal_to_extended
#else
#    define LONGREAL quadruple
#    define DECIMAL_TO_LONGREAL decimal_to_quadruple
#endif

void
convert_to_fp(fpstring, len, fval)
	char *fpstring;
	register int   len;
	register U32BITS fval[4];
{

	decimal_mode    mr;
	decimal_record  dr;
	fp_exception_field_type fs;
	enum decimal_string_form form;
	char           *pechar;
	union {
		float           f;
		double          d;
		LONGREAL        r;
		U32BITS         u[4];
	}               kluge;
	int             i, imax;

	string_to_decimal(&fpstring, MAXINT, 0, &dr, &form, &pechar);
	mr.rd = fp_nearest;
	switch (len) {
	case 1:
		decimal_to_single(&(kluge.f), &mr, &dr, &fs);
		imax = 1;
		break;
	case 2:
		decimal_to_double(&(kluge.d), &mr, &dr, &fs);
		imax = 2;
		break;
	case 4:
		DECIMAL_TO_LONGREAL(&(kluge.r), &mr, &dr, &fs);
		imax = 4;
		break;
	}
	for (i = 0; i < imax; i++)
		fval[i] = kluge.u[i];
	for (; i < 4; i++)
		fval[i] = 0;
}


static void
ps_common_setsymbol(v1p, v2p, segment)	/* called only by ps_common() */
	register struct value *v1p;
	register struct value *v2p;
	register SEGMENT segment;
{
	/* the given symbol length must not be a relocatable qty.	*/
	chk_absolute(v2p);

#ifdef SUN3OBJ
	/* value of common symbol is its size */
	v1p->v_symp->s_value = v2p->v_value;
#endif

#ifdef INTERIM_OBJ_FMT
	if (v2p->v_value == 0)
	{
		/* a COMMON of length zero: make it look like it used to
		 * in the old Sun-3 format -- i.e. an UNDEF.
		 */
		v1p->v_symp->s_segment = ASSEG_UNKNOWN;
		SET_BIT(v1p->v_symp->s_attr, SA_GLOBAL);
	}
	else
	{
#endif
		/* make it a FORTRAN-style COMMON symbol in the
		 * appropriate segment.
		 */
		SET_BITS(v1p->v_symp->s_attr, (SA_COMMON|SA_GLOBAL));

		v1p->v_symp->s_segment = segment;
#ifdef INTERIM_OBJ_FMT
	}
#endif
}


void
ps_common(v1p, v2p, segname)		/* "common" pseudo */
	register struct value *v1p;
	register struct value *v2p;
	register char *segname;
{
	if ( (v1p == NULL) || (v2p == NULL) )
	{
		internal_error("ps_common(): NULL vp");
	}
	
#ifdef SUN3OBJ
	{
		if ( ! sun3_seg_name_ok(segname) )
		{
			error(ERR_SEGNAME, input_filename, input_line_no,
					   segname);
			segname = "bss";
		}
	}
#endif

	if ( (VALUE_IS_ABSOLUTE(v1p) || (v1p->v_value != 0))  ||
	     (!VALUE_IS_ABSOLUTE(v2p) || VALUE_IS_QUOTED_STRING(v2p))
	   )
	{
		/* 1st arg was a constant or expression, not just a symbol,
		 * or 2nd operand was not just a constant (or constant
		 * expression) or was a string.
		 */
		error(ERR_SYNTAX, input_filename,input_line_no);
	}
	else
	{
		if ( !BIT_IS_ON(v1p->v_symp->s_attr, SA_DEFN_SEEN) )
		{
			/* segment name should be "bss" (or, equivalently
			 * for now, "data") or "sdata".
			 */

			if ( (strcmp(segname, "bss" ) == 0) ||
			     (strcmp(segname, "data") == 0) )
			{
				ps_common_setsymbol(v1p, v2p, ASSEG_DCOMMON);
			}
#ifdef INTERIM_OBJ_FMT
			else if (strcmp(segname, "sdata") == 0)
			{
				ps_common_setsymbol(v1p, v2p, ASSEG_SDCOMMON);
			}
#endif /*INTERIM_OBJ_FMT*/
			else	error(ERR_VAL_INVALID, input_filename,
				      input_line_no, segname);
		}
	}
}
