/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 *
 */

#include        "ass00.h"
#include        "assex.h"
#include        "ip_spec.h"

#ifndef NORCSID
static char rcs_id[] = "$Header$" ;
static char rcs_ip[] = RCS_IP ;
#endif

short           opt_line ;      /* max_line_no - # lines removed from end
				   after perfoming exc's.
				   Used to estimate the distance in # of
				   instructions.
				*/
/*
** Determine the exact instruction length & format where possible, and the
** the upper and lower limits otherwise. Enter limits in labeltable
*/
pass_3()
{
	register line_t *lnp, *rev_lnp;
	line_t   *tmp_lnp;
	locl_t   *lbp;
	int      min_l, max_l, min_bytes;
	short    last_line ;
	short    hol_err_line ;
	register insno ;

	pass = 3;
	opt_line= line_num ; hol_err_line=0 ;
	min_bytes = max_bytes = 0; rev_lnp= lnp_cast 0 ;
	for (lnp = pstate.s_fline ; lnp ; opt_line--, line_num-- ) {
		pstate.s_fline= lnp;
		insno = ctrunc(lnp->instr_num);
		switch( insno ) {
		case sp_fpseu :
			last_line = line_num ;
			line_num = lnp->ad.ad_ln.ln_first ;
			opt_line -= lnp->ad.ad_ln.ln_extra ;
			lnp->ad.ad_ln.ln_first= last_line ;
			break ;
		case sp_ilb1 :
			lbp = lnp->ad.ad_lp;
			lbp->l_defined = SEEN;
			lbp->l_min = min_bytes;
			lbp->l_max = max_bytes;
			break ;
		default:
		if ( lnp->type1==CONST && (em_flag[insno]&EM_PAR)==PAR_G ) {
			if (holbase != 0) {
				if (lnp->ad.ad_i >= holsize) {
						hol_err_line= line_num ;
				}
				lnp->ad.ad_i += holbase;
			}
		} else
		if ( lnp->type1>=VALLOW && (em_flag[insno]&EM_PAR)==PAR_G ) {
			if (holbase != 0) {
				pstate.s_fline= lnp->l_next ;
				newline(CONST) ;
				pstate.s_fline->instr_num= insno ;
				pstate.s_fline->ad.ad_i=
					VAL1(lnp->type1)+holbase ;
				freearea((area_t)lnp,
					(unsigned)linesize[VALLOW]) ;
				lnp= pstate.s_fline ;
				if ( VAL1(lnp->type1) >= holsize) {
					hol_err_line= line_num ;
				}
			}
		}
		if ( !valid(lnp) ) fatal("Invalid operand") ;

		determine_props(lnp, &min_l, &max_l);
		min_bytes += min_l; max_bytes += max_l;
		break ;
		}
		tmp_lnp= lnp->l_next ;
		lnp->l_next= rev_lnp ; rev_lnp= lnp ;
		lnp= tmp_lnp ;
	}
	pstate.s_fline= rev_lnp ;
	if ( hol_err_line ) {
		line_num= hol_err_line ;
		werror("address exceeds holsize") ;
	}
}


/*
** Determine the format that should be used for each instruction,
** depending on its offsets
*/

determine_props(lnp, min_len, max_len)
	line_t *lnp;
	int    *min_len, *max_len;
{
	cons_t  val ;
	register int insno ;
	register char *f_off, *l_off ;
	char defined ;

	insno=ctrunc(lnp->instr_num) ;
	val=parval(lnp,&defined) ;
	if ( !defined ) {
		switch(em_flag[insno]&EM_PAR) {
		case PAR_NO:
		case PAR_W:
			f_off = findnop(insno) ;
			break ;
		case PAR_G:
			/* We want the maximum address that is a multiple
			   of the wordsize.
			   Assumption: there is no shortie for
				intr max_word_multiple
			     where intr is a instruction allowing parameters
			     that are not a word multiple (PAR_G).
			*/
			f_off = findfit(insno, maxadr&(~(wordsize-1))) ;
			break ;
		case PAR_B:
			f_off = findfit(insno, (cons_t)0) ;
			l_off = findfit(insno, val ) ;
			if ( f_off != l_off ) {
				*min_len=oplength(*f_off) ;
				*max_len=oplength(*l_off) ;
				lnp->opoff = NO_OFF ;
				return ;
			}
			break ;
		}
	} else {
		f_off = findfit(insno,val) ;
	}
	lnp->opoff = f_off ;
	*min_len = *max_len = oplength(*f_off) ;
}

char *findfit(instr,val) int instr ; cons_t val ; {
	register char *currc,*endc ;
	int found, flags, number ;
	char *opc ;

	endc = opindex[instr+1] ;
	for ( currc=opindex[instr], found=0 ;
		!found && currc<endc ; currc++ ) {
		opc = currc ;
		flags=ctrunc(*currc++) ;
		switch ( flags&OPTYPE ) {
		case OPNO :
			continue ;
		case OPMINI :
		case OPSHORT :
			number=ctrunc(*++currc) ;
		}
		found = opfit(flags, number, val, em_flag[instr]&EM_PAR ) ;
	}
	if ( !found ) fatal("Cannot find interpreter opcode") ;
	return opc ;
}

char *findnop(instr) int instr ; {
	register char *currc,*endc ;

	endc = opindex[instr+1] ;
	for ( currc=opindex[instr] ; currc<endc ; currc++ ) {
		switch ( ctrunc(*currc)&OPTYPE ) {
		case OPNO :
			return currc ;
		case OPSHORT :
		case OPMINI :
			currc++ ;
		}
		currc++ ;
	}
	fatal("Cannot find interpreter opcode") ;
	/* NOTREACHED */
}

int opfit(flag,number,val,i_flag)
int i_flag,flag,number ; cons_t val ; {
	/* Number is invalid if flag does not contain MINI or SHORT */
	switch ( flag&OPRANGE ) {
	case OP_POS :
		if ( val<0 ) return 0 ;
		break ;
	case OP_NEG :
		if ( val>=0 ) return 0 ;
		break ;
	}
	if ( flag&OPWORD ) {
		if ( val%wordsize ) return 0 ;
		val /= wordsize ;
	}
	if ( flag&OPNZ ) {
		if ( val==0 ) return 0 ;
		val-- ;
	}
	switch ( flag&OPTYPE ) {
	case OPMINI :
		if ( val<0 ) val = -1-val ;
		return val>=0 && val<number ;
	case OPSHORT :
		if ( val<0 ) val = -1-val ;
		return val>=0 && val<number*256 ;
	case OP16U:
		return val>=0 && val<=65535L &&
		       ( i_flag!=PAR_G || val<=maxadr ) ;
	case OP16 :
		return val>= -32768 && val<=32767 ;
	case OP32 :
		return TRUE ;
	default :
		fatal("illegal OPTYPE value") ;
		/* NOTREACHED */
	}
}

int oplength(flag) int flag ; {
	int cnt ;

	cnt=1 ;
	if ( flag&OPESC ) cnt++ ;
	switch( flag&OPTYPE ) {
	case OPNO    :
	case OPMINI  : break ;
	case OP8     :
	case OPSHORT : cnt++ ; break ;
	case OP16U:
	case OP16    : cnt+=2 ; break ;
	case OP32    : cnt+=5 ; break ;
	case OP64    : cnt+=9 ; break ;
	}
	return cnt ;
}

/*
** return estimation of value of parameter
*/
cons_t parval(lnp,defined)
	line_t *lnp;
	char *defined;
{
	register int    type;
	register locl_t *lbp;
	register glob_t *gbp;
	cons_t   offs ;

	*defined = TRUE ;
	type = lnp->type1;
	switch(type) {
		default: if ( type>=VALLOW && type<=VALHIGH )
				 return VAL1(type) ;
			 error("bad type during parval");
			 break;
		case CONST:
			return(lnp->ad.ad_i);
		case GLOSYM:
		case GLOOFF:
			if ( type!=GLOOFF) {
				gbp = lnp->ad.ad_gp;
				offs= 0 ;
			} else {
				gbp =lnp->ad.ad_df.df_gp ;
				offs=lnp->ad.ad_df.df_i ;
			}
			if(gbp->g_status&DEF)
				return(gbp->g_val.g_addr+offs);
			else {
				*defined = FALSE ;
				return offs ;
			}
		case LOCSYM:
			lbp = lnp->ad.ad_lp;
			switch(pass) {
			default:error("bad pass in parval");
			case 3:
				*defined = FALSE;
				switch(lbp->l_defined) {
				default : fatal("Illegal local label") ;
				case NO :
					error("Undefined local label") ;
					lbp->l_defined= NOTPRESENT ;
				case NOTPRESENT:
					return max_bytes;
				case SEEN :
					return max_bytes - lbp->l_min ;
				case YES :
					/* l_min contains line_num
					   adjusted for exc's.
					*/
			return (lbp->l_min - opt_line -1 ) * maxinsl ;
				}
			case 4: if(lbp->l_defined == YES)
					return(lbp->l_min-prog_size-maxinsl);
				return max_bytes - lbp->l_max- prog_size;
			case 5: if (lbp->l_defined == YES )
					return lbp->l_min ;
				*defined = FALSE ;
				break ;
			}
			break;
		case MISSING:
			*defined = FALSE ;
			break;
		case PROCNAME:
			return(lnp->ad.ad_pp->p_num);
	}
	return(0);
}
int valid(lnp) register line_t *lnp ; {
	cons_t val ;
	char type ;

	type = lnp->type1 ;
	if ( type>=VALLOW && type<=VALHIGH ) {
		val= VAL1(type) ;
		type= CONST ;
	} else if ( type==CONST ) val = lnp->ad.ad_i ;
	switch ( em_flag[ctrunc(lnp->instr_num)]&EM_PAR ) {
	case PAR_NO:
		return type==MISSING ;
	case PAR_C:
		if ( type!=CONST ) return FALSE;
		if ( val>maxint && val<=maxunsig ) {
			lnp->ad.ad_i = val -maxunsig -1 ;
		}
		return TRUE ;
	case PAR_D:
		if ( type!=CONST ) return FALSE;
		if ( val>maxdint && val<=maxdunsig ) {
			lnp->ad.ad_i = val -maxdunsig -1 ;
		}
		return TRUE ;
	case PAR_L:
	case PAR_F:
		return type==CONST ;
	case PAR_N:
		return type==CONST && val>=0 ;
	case PAR_G:
		return type==CONST || type==GLOSYM || type==GLOOFF ;
	case PAR_W:
		if ( type==MISSING ) return TRUE ;
	case PAR_S:
		return type==CONST && val>0 && val%wordsize==0 ;
	case PAR_Z:
		return type==CONST && val>=0 && val%wordsize==0 ;
	case PAR_O:
		return type==CONST && val>=0 &&
		    ( val >= wordsize ? val%wordsize : wordsize%val ) == 0 ;
	case PAR_P:
		return type==PROCNAME ;
	case PAR_B:
		return type==LOCSYM ;
	case PAR_R:
		return type==CONST && val>=0 && val<=3 ;
	default:
		fatal("Unknown parameter type") ;
		/* NOTREACHED */
	}
}