/* subsegs.c - subsegments -
   Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
   
   This file is part of GAS, the GNU Assembler.
   
   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.
   
   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/*
 * Segments & sub-segments.
 */

#include "as.h"

#include "subsegs.h"
#include "obstack.h"

#ifdef MANY_SEGMENTS
segment_info_type segment_info[SEG_MAXIMUM_ORDINAL];

frchainS*	frchain_root,
    *	frchain_now;

#else
frchainS*	frchain_root,
    *	frchain_now,	/* Commented in "subsegs.h". */
    *	data0_frchainP,
 * bss0_frchainP;

#endif
char * const /* in: segT   out: char* */
    seg_name[] = {
	    "absolute",
#ifdef MANY_SEGMENTS
	    "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9",
#else
	    "text",
	    "data",
	    "bss",
#endif
	    "unknown",
	    "absent",
	    "pass1",
	    "ASSEMBLER-INTERNAL-LOGIC-ERROR!",
	    "bignum/flonum",
	    "difference",
	    "debug",
	    "transfert vector preload",
	    "transfert vector postload",
	    "register",
	    "",
    }; /* Used by error reporters, dumpers etc. */


void
    subsegs_begin()
{
	/* Check table(s) seg_name[], seg_N_TYPE[] is in correct order */
#ifdef MANY_SEGMENTS
#else
	know( SEG_ABSOLUTE	==  0 );
	know( SEG_TEXT    	==  1 );
	know( SEG_DATA    	==  2 );
	know( SEG_BSS     	==  3 );
	know( SEG_UNKNOWN 	==  4 );
	know( SEG_ABSENT    	==  5 );
	know( SEG_PASS1	==  6 );
	know( SEG_GOOF	==  7 );
	know( SEG_BIG		==  8 );
	know( SEG_DIFFERENCE	==  9 );
	know( SEG_DEBUG	== 10 );
	know( SEG_NTV		== 11 );
	know( SEG_PTV		== 12 );
	know( SEG_REGISTER	== 13 );
	know( SEG_MAXIMUM_ORDINAL == SEG_REGISTER );
	/*  know( segment_name (SEG_MAXIMUM_ORDINAL + 1) [0] == 0 );*/
#endif
	
	obstack_begin( &frags, 5000);
	frchain_root = NULL;
	frchain_now  = NULL;		/* Warn new_subseg() that we are booting. */
	/* Fake up 1st frag. */
	/* It won't be used=> is ok if obstack... */
	/* pads the end of it for alignment. */
	frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG);
	memset(frag_now,  SIZEOF_STRUCT_FRAG, 0); 
	/* This 1st frag will not be in any frchain. */
	/* We simply give subseg_new somewhere to scribble. */
	now_subseg = 42;		/* Lie for 1st call to subseg_new. */
#ifdef MANY_SEGMENTS
	{
		int i;
		for (i = SEG_E0; i < SEG_UNKNOWN; i++) {
			subseg_new(i, 0);
			segment_info[i].frchainP = frchain_now;
		}
	}
#else
	subseg_new (SEG_DATA, 0);	/* .data 0 */
	data0_frchainP = frchain_now;

	subseg_new (SEG_BSS, 0);
	bss0_frchainP = frchain_now;

#endif
	
}

/*
 *			subseg_change()
 *
 * Change the subsegment we are in, BUT DO NOT MAKE A NEW FRAG for the
 * subsegment. If we are already in the correct subsegment, change nothing.
 * This is used eg as a worker for subseg_new [which does make a new frag_now]
 * and for changing segments after we have read the source. We construct eg
 * fixSs even after the source file is read, so we do have to keep the
 * segment context correct.
 */
void
    subseg_change (seg, subseg)
register segT	seg;
register int	subseg;
{
	now_seg	 = seg;
	now_subseg = subseg;
#ifdef MANY_SEGMENTS
	seg_fix_rootP = & segment_info[seg].fix_root;
	seg_fix_tailP = & segment_info[seg].fix_tail;
#else
	if (seg == SEG_DATA)
	    {
		    seg_fix_rootP = & data_fix_root;
		    seg_fix_tailP = & data_fix_tail;
	    }
	else if (seg == SEG_TEXT)
	    {
		    seg_fix_rootP = & text_fix_root;
		    seg_fix_tailP = & text_fix_tail;
	    }
	else {
		    know (seg == SEG_BSS);
		    seg_fix_rootP = & bss_fix_root;
		    seg_fix_tailP = & bss_fix_tail;
	    }
#endif
}

/*
 *			subseg_new()
 *
 * If you attempt to change to the current subsegment, nothing happens.
 *
 * In:	segT, subsegT code for new subsegment.
 *	frag_now -> incomplete frag for current subsegment.
 *	If frag_now==NULL, then there is no old, incomplete frag, so
 *	the old frag is not closed off.
 *
 * Out:	now_subseg, now_seg updated.
 *	Frchain_now points to the (possibly new) struct frchain for this
 *	sub-segment.
 *	Frchain_root updated if needed.
 */

void
    subseg_new (seg, subseg)	/* begin assembly for a new sub-segment */
register segT	seg; /* SEG_DATA or SEG_TEXT */
register subsegT	subseg;
{
	long tmp;		/* JF for obstack alignment hacking */
#ifndef MANY_SEGMENTS
	know(seg == SEG_DATA || seg == SEG_TEXT || seg == SEG_BSS);
#endif
#ifdef OBJ_AOUT
/* If -R specifed, always put stuff into the data section */
	if (flagseen['R']) 
	{
	  if (seg == SEG_DATA) 
	  {
	    subseg += 1000;
	    seg = SEG_TEXT;
	  }
	}
#endif
	if (seg != now_seg || subseg != now_subseg)
	    {				/* we just changed sub-segments */
		    register	frchainS *	frcP;	/* crawl frchain chain */
		    register	frchainS**	lastPP;	/* address of last pointer */
		    frchainS *	newP;	/* address of new frchain */
		    register fragS *		former_last_fragP;
		    register fragS *		new_fragP;
		    
		    if (frag_now)		/* If not bootstrapping. */
			{
				frag_now -> fr_fix = obstack_next_free(& frags) - frag_now -> fr_literal;
				frag_wane(frag_now);	/* Close off any frag in old subseg. */
			}
		    /*
		     * It would be nice to keep an obstack for each subsegment, if we swap
		     * subsegments a lot. Hence we would have much fewer frag_wanes().
		     */
		    {
			    
			    obstack_finish( &frags);
			    /*
			     * If we don't do the above, the next object we put on obstack frags
			     * will appear to start at the fr_literal of the current frag.
			     * Also, above ensures that the next object will begin on a
			     * address that is aligned correctly for the engine that runs
			     * this program.
			     */
		    }
		    subseg_change (seg, (int)subseg);
		    /*
		     * Attempt to find or make a frchain for that sub seg.
		     * Crawl along chain of frchainSs, begins @ frchain_root.
		     * If we need to make a frchainS, link it into correct
		     * position of chain rooted in frchain_root.
		     */
		    for (frcP = * (lastPP = & frchain_root);
			 frcP
			 && (int)(frcP -> frch_seg) <= (int)seg;
			 frcP = * ( lastPP = & frcP -> frch_next)
			 )
			{
				if (   (int)(frcP -> frch_seg) == (int)seg
				    && frcP -> frch_subseg >= subseg)
				    {
					    break;
				    }
			}
		    /*
		     * frcP:		Address of the 1st frchainS in correct segment with
		     *		frch_subseg >= subseg.
		     *		We want to either use this frchainS, or we want
		     *		to insert a new frchainS just before it.
		     *
		     *		If frcP==NULL, then we are at the end of the chain
		     *		of frchainS-s. A NULL frcP means we fell off the end
		     *		of the chain looking for a
		     *		frch_subseg >= subseg, so we
		     *		must make a new frchainS.
		     *
		     *		If we ever maintain a pointer to
		     *		the last frchainS in the chain, we change that pointer
		     *		ONLY when frcP==NULL.
		     *
		     * lastPP:	Address of the pointer with value frcP;
		     *		Never NULL.
		     *		May point to frchain_root.
		     *
		     */
		    if (   ! frcP
			|| (   (int)(frcP -> frch_seg) > (int)seg
			    || frcP->frch_subseg > subseg)) /* Kinky logic only works with 2 segments. */
			{
				/*
				 * This should be the only code that creates a frchainS.
				 */
				newP=(frchainS *)obstack_alloc(&frags,sizeof(frchainS));
				memset(newP, sizeof(frchainS), 0); 
				/* This begines on a good boundary */
				/* because a obstack_done() preceeded  it. */
				/* It implies an obstack_done(), so we */
				/* expect the next object allocated to */
				/* begin on a correct boundary. */
				*lastPP = newP;
				newP -> frch_next = frcP; /* perhaps NULL */
				(frcP = newP) -> frch_subseg		= subseg;
				newP  -> frch_seg		= seg;
				newP  -> frch_last	 	= NULL;
			}
		    /*
		     * Here with frcP ->ing to the frchainS for subseg.
		     */
		    frchain_now = frcP;
		    /*
		     * Make a fresh frag for the subsegment.
		     */
		    /* We expect this to happen on a correct */
		    /* boundary since it was proceeded by a */
		    /* obstack_done(). */
		    tmp=obstack_alignment_mask(&frags);	/* JF disable alignment */
		    obstack_alignment_mask(&frags)=0;
		    frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG);
		    obstack_alignment_mask(&frags)=tmp;
		    /* know( frags . obstack_c_next_free == frag_now -> fr_literal ); */
		    /* But we want any more chars to come */
		    /* immediately after the structure we just made. */
		    new_fragP = frag_now;
		    new_fragP -> fr_next = NULL;
		    /*
		     * Append new frag to current frchain.
		     */
		    former_last_fragP = frcP -> frch_last;
		    if (former_last_fragP)
			{
				know( former_last_fragP -> fr_next == NULL );
				know( frchain_now -> frch_root );
				former_last_fragP -> fr_next = new_fragP;
			}
		    else
			{
				frcP -> frch_root = new_fragP;
			}
		    frcP -> frch_last = new_fragP;
	    }				/* if (changing subsegments) */
}				/* subseg_new() */

/*
 * Local Variables:
 * comment-column: 0
 * fill-column: 131
 * End:
 */

/* end of subsegs.c */