/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char sccsid[] = "@(#)var.c	5.2 (Berkeley) 7/26/85";
#endif not lint

#include "whoami.h"
#include "0.h"
#include "objfmt.h"
#include "align.h"
#include "iorec.h"
#ifdef PC
#   include	"pc.h"
#endif PC
#include "tmps.h"
#include "tree_ty.h"

/*
 * Declare variables of a var part.  DPOFF1 is
 * the local variable storage for all prog/proc/func
 * modules aside from the block mark.  The total size
 * of all the local variables is entered into the
 * size array.
 */
/*ARGSUSED*/
varbeg( lineofyvar , r )
    int	lineofyvar;
{
    static bool	var_order = FALSE;
    static bool	var_seen = FALSE;

/* this allows for multiple declaration
 * parts except when the "standard"
 * option has been specified.
 * If routine segment is being compiled,
 * do level one processing.
 */

#ifndef PI1
	if (!progseen)
		level1();
	line = lineofyvar;
	if ( parts[ cbn ] & RPRT ) {
	    if ( opt( 's' ) ) {
		standard();
		error("Variable declarations should precede routine declarations");
	    } else {
		if ( !var_order ) {
		    var_order = TRUE;
		    warning();
		    error("Variable declarations should precede routine declarations");
		}
	    }
	}
	if ( parts[ cbn ] & VPRT ) {
	    if ( opt( 's' ) ) {
		standard();
		error("All variables should be declared in one var part");
	    } else {
		if ( !var_seen ) {
		    var_seen = TRUE;
		    warning();
		    error("All variables should be declared in one var part");
		}
	    }
	}
	parts[ cbn ] |= VPRT;
#endif
    /*
     *  #ifndef PI0
     *      sizes[cbn].om_max = sizes[cbn].curtmps.om_off = -DPOFF1;
     *  #endif
     */
	forechain = NIL;
#ifdef PI0
	send(REVVBEG);
#endif
}

var(vline, vidl, vtype)
#ifdef PI0
	int vline;
	struct tnode *vidl, *vtype;
{
	register struct nl *np;
	register struct tnode *vl;

	np = gtype(vtype);
	line = vline;
	/* why is this here? */
	for (vl = vidl; vl != TR_NIL; vl = vl->list_node.next) {
		}
	}
	send(REVVAR, vline, vidl, vtype);
}
#else
	int vline;
	register struct tnode *vidl;
	struct tnode *vtype;
{
	register struct nl *np;
	register struct om *op;
	long w;
	int o2;
#ifdef PC
	struct nl	*vp;
#endif

	np = gtype(vtype);
	line = vline;
	w = lwidth(np);
	op = &sizes[cbn];
	for (; vidl != TR_NIL; vidl = vidl->list_node.next) {
#		ifdef OBJ
		    op->curtmps.om_off =
			roundup((int)(op->curtmps.om_off-w), (long)align(np));
		    o2 = op -> curtmps.om_off;
#		endif OBJ
#		ifdef PC
		    if ( cbn == 1 ) {
				/*
				 * global variables are not accessed off the fp
				 * but rather by their names.
				 */
			    o2 = 0;
		    } else {
				/*
				 * locals are aligned, too.
				 */
			    op->curtmps.om_off =
				roundup((int)(op->curtmps.om_off - w),
				(long)align(np));
			    o2 = op -> curtmps.om_off;
		    }
#		endif PC
#		ifdef PC
		vp = enter(defnl((char *) vidl->list_node.list, VAR, np, o2));
#		else
		(void) enter(defnl((char *) vidl->list_node.list, VAR, np, o2));
#		endif
		if ( np != NLNIL && (np -> nl_flags & NFILES) ) {
		    dfiles[ cbn ] = TRUE;
		}
#		ifdef PC
		    if ( cbn == 1 ) {
			putprintf( "	.data" , 0 );
			aligndot(align(np));
			putprintf( "	.comm	" , 1 );
			putprintf( EXTFORMAT , 1 , (int) vidl->list_node.list );
			putprintf( ",%d" , 0 , (int) w );
			putprintf( "	.text" , 0 );
			stabgvar( vp , w , line );
			vp -> extra_flags |= NGLOBAL;
		    } else {
			vp -> extra_flags |= NLOCAL;
		    }
#		endif PC
	}
#	ifdef PTREE
	    {
		pPointer	*Vars;
		pPointer	Var = VarDecl( ovidl , vtype );

		pSeize( PorFHeader[ nesting ] );
		Vars = &( pDEF( PorFHeader[ nesting ] ).PorFVars );
		*Vars = ListAppend( *Vars , Var );
		pRelease( PorFHeader[ nesting ] );
	    }
#	endif
}
#endif

varend()
{

	foredecl();
#ifndef PI0
	sizes[cbn].om_max = sizes[cbn].curtmps.om_off;
#else
	send(REVVEND);
#endif
}

/*
 * Evening
 */
long
leven(w)
	register long w;
{
	if (w < 0)
		return (w & 0xfffffffe);
	return ((w+1) & 0xfffffffe);
}

#ifndef PC
int
even(w)
	register int w;
{
	return leven((long)w);
}
#endif

/*
 * Find the width of a type in bytes.
 */
width(np)
	struct nl *np;
{

	return (lwidth(np));
}

long
lwidth(np)
	struct nl *np;
{
	register struct nl *p;

	p = np;
	if (p == NIL)
		return (0);
loop:
	switch (p->class) {
		default:
			panic("wclass");
		case TYPE:
			switch (nloff(p)) {
				case TNIL:
					return (2);
				case TSTR:
				case TSET:
					panic("width");
				default:
					p = p->type;
					goto loop;
			}
		case ARRAY:
			return (aryconst(p, 0));
		case PTR:
			return ( sizeof ( int * ) );
		case FILET:
			return ( sizeof(struct iorec) + lwidth( p -> type ) );
		case CRANGE:
			p = p->type;
			goto loop;
		case RANGE:
			if (p->type == nl+TDOUBLE)
#ifdef DEBUG
				return (hp21mx ? 4 : 8);
#else
				return (8);
#endif
		case SCAL:
			return (bytes(p->range[0], p->range[1]));
		case SET:
			setran(p->type);
			/*
			 * Sets are some multiple of longs
			 */
			return roundup((int)((set.uprbp >> 3) + 1),
				(long)(sizeof(long)));
		case STR:
		case RECORD:
			return ( p->value[NL_OFFS] );
	}
}

    /*
     *	round up x to a multiple of y
     *	for computing offsets of aligned things.
     *	y had better be positive.
     *	rounding is in the direction of x.
     */
long
roundup( x , y )
    int			x;
    register long	y;
    {
	
	if ( y == 0 ) {
	    return x;
	}
	if ( x >= 0 ) {
		return ( ( ( x + ( y - 1 ) ) / y ) * y );
	} else {
		return ( ( ( x - ( y - 1 ) ) / y ) * y );
	}
    }

    /*
     *	alignment of an object using the c alignment scheme
     */
int
align( np )
    struct nl	*np;
    {
	register struct nl *p;
	long elementalign;

	p = np;
	if ( p == NIL ) {
	    return 0;
	}
alignit:
	switch ( p -> class ) {
	    default:
		    panic( "align" );
	    case TYPE:
		    switch ( nloff( p ) ) {
			case TNIL:
				return A_POINT;
			case TSTR:
				return A_STRUCT;
			case TSET:
				return A_SET;
			default:
				p = p -> type;
				goto alignit;
		    }
	    case ARRAY:
			/*
			 * arrays are structures, since they can get
			 * assigned form/to as structure assignments.
			 * preserve internal alignment if it is greater.
			 */
		    elementalign = align(p -> type);
		    return elementalign > A_STRUCT ? elementalign : A_STRUCT;
	    case PTR:
		    return A_POINT;
	    case FILET:
		    return A_FILET;
	    case CRANGE:
	    case RANGE:
		    if ( p -> type == nl+TDOUBLE ) {
			return A_DOUBLE;
		    }
		    /* else, fall through */
	    case SCAL:
		    switch ( bytes( p -> range[0] , p -> range[1] ) ) {
			case 4:
			    return A_LONG;
			case 2:
			    return A_SHORT;
			case 1:
			    return A_CHAR;
			default:
			    panic( "align: scal" );
		    }
	    case SET:
		    return A_SET;
	    case STR:
			/*
			 * arrays of chars are structs
			 */
		    return A_STRUCT;
	    case RECORD:
			/*
			 * the alignment of a record is in its align_info field
			 * why don't we use this for the rest of the namelist?
			 */
		    return p -> align_info;
	}
    }

#ifdef PC
    /*
     *	output an alignment pseudo-op.
     */
aligndot(alignment)
    int	alignment;
#ifdef vax
{
    switch (alignment) {
	case 1:
	    return;
	case 2:
	    putprintf("	.align 1", 0);
	    return;
	default:
	case 4:
	    putprintf("	.align 2", 0);
	    return;
    }
}
#endif vax
#ifdef mc68000
{
    switch (alignment) {
	case 1:
	    return;
	default:
	    putprintf("	.even", 0);
	    return;
    }
}
#endif mc68000
#endif PC
    
/*
 * Return the width of an element
 * of a n time subscripted np.
 */
long aryconst(np, n)
	struct nl *np;
	int n;
{
	register struct nl *p;
	long s, d;

	if ((p = np) == NIL)
		return (NIL);
	if (p->class != ARRAY)
		panic("ary");
	/*
	 * If it is a conformant array, we cannot find the width from
	 * the type.
	 */
	if (p->chain->class == CRANGE)
		return (NIL);
	s = lwidth(p->type);
	/*
	 * Arrays of anything but characters are word aligned.
	 */
	if (s & 1)
		if (s != 1)
			s++;
	/*
	 * Skip the first n subscripts
	 */
	while (n >= 0) {
		p = p->chain;
		n--;
	}
	/*
	 * Sum across remaining subscripts.
	 */
	while (p != NIL) {
		if (p->class != RANGE && p->class != SCAL)
			panic("aryran");
		d = p->range[1] - p->range[0] + 1;
		s *= d;
		p = p->chain;
	}
	return (s);
}

/*
 * Find the lower bound of a set, and also its size in bits.
 */
setran(q)
	struct nl *q;
{
	register lb, ub;
	register struct nl *p;

	p = q;
	if (p == NIL)
		return;
	lb = p->range[0];
	ub = p->range[1];
	if (p->class != RANGE && p->class != SCAL)
		panic("setran");
	set.lwrb = lb;
	/* set.(upperbound prime) = number of bits - 1; */
	set.uprbp = ub-lb;
}

/*
 * Return the number of bytes required to hold an arithmetic quantity
 */
bytes(lb, ub)
	long lb, ub;
{

#ifndef DEBUG
	if (lb < -32768 || ub > 32767)
		return (4);
	else if (lb < -128 || ub > 127)
		return (2);
#else
	if (!hp21mx && (lb < -32768 || ub > 32767))
		return (4);
	if (lb < -128 || ub > 127)
		return (2);
#endif
	else
		return (1);
}
