/* lkarea.c */

/*
 * (C) Copyright 1989-2000
 * All Rights Reserved
 *
 * Alan R. Baldwin
 * 721 Berkeley St.
 * Kent, Ohio  44240
 */

#include <stdio.h>
#include <string.h>

#ifdef WIN32
#include <stdlib.h>
#else
#include <alloc.h>
#endif

#include "aslink.h"

/*)Module	lkarea.c
 *
 *	The module lkarea.c contains the functions which
 *	create and link together all area definitions read
 *	from the .rel file(s).
 *
 *	lkarea.c contains the following functions:
 *		VOID	lnkarea()
 *		VOID	lnksect()
 *		VOID	lkparea()
 *		VOID	newarea()
 *
 *	lkarea.c contains no global variables.
 */

/*)Function	VOID	newarea()
 * 
 *	The function newarea() creates and/or modifies area
 *	and areax structures for each A directive read from
 *	the .rel file(s).  The function lkparea() is called
 *	to find tha area structure associated with this name.
 *	If the area does not yet exist then a new area
 *	structure is created and linked to any existing
 *	linked area structures. The area flags are copied
 *	into the area flag variable.  For each occurence of
 *	an A directive an areax structure is created and
 *	linked to the areax structures associated with this
 *	area.  The size of this area section is placed into
 *	the areax structure.  The flag value for all subsequent
 *	area definitions for the same area are compared and
 *	flagged as an error if they are not identical.
 *	The areax structure created for every occurence of
 *	an A directive is loaded with a pointer to the base
 *	area structure and a pointer to the associated
 *	head structure.  And finally, a pointer to this
 *	areax structure is loaded into the list of areax
 *	structures in the head structure.  Refer to lkdata.c
 *	for details of the structures and their linkage.
 *
 *	local variables:
 *		areax **halp		pointer to an array of pointers
 *		int	i		counter, loop variable, value
 *		char	id[]		id string
 *		int	narea		number of areas in this head structure
 *		areax *	taxp		pointer to an areax structure
 *					to areax structures
 *
 *	global variables:
 *		area	*ap		Pointer to the current
 *				 	area structure
 *		areax	*axp		Pointer to the current
 *				 	areax structure
 *		head	*hp		Pointer to the current
 *				 	head structure
 *		int	lkerr		error flag
 *
 *	functions called:
 *		addr_t	eval()		lkeval.c
 *		VOID	exit()		c_library
 *		int	fprintf()	c_library
 *		VOID	getid()		lklex.c
 *		VOID	lkparea()	lkarea.c
 *		VOID	skip()		lklex.c
 *
 *	side effects:
 *		The area and areax structures are created and
 *		linked with the appropriate head structures.
 *		Failure to allocate area or areax structure
 *		space will terminate the linker.  Other internal
 *		errors most likely caused by corrupted .rel
 *		files will also terminate the linker.
 */

/*
 * Create an area entry.
 *
 * A xxxxxx size nnnn flags mm
 *   |           |          |
 *   |           |          `--  ap->a_flag
 *   |           `------------- axp->a_size
 *   `-------------------------  ap->a_id
 *
 */
VOID
newarea()
{
	register int i, narea;
	struct areax *taxp;
	struct areax **halp;
	char id[NCPS];

	/*
	 * Create Area entry
	 */
	getid(id, -1);
	lkparea(id);
	/*
	 * Evaluate area size
	 */
	skip(-1);
	axp->a_size = eval();
	/*
	 * Evaluate flags
	 */
	skip(-1);
	i = 0;
	taxp = ap->a_axp;
	while (taxp->a_axp) {
		++i;
		taxp = taxp->a_axp;
	}
	if (i == 0) {
		ap->a_flag = eval();
	} else {
		i = eval();
		if (i && (ap->a_flag != i)) {
		    fprintf(stderr, "Conflicting flags in area %s\n", id);
		    lkerr++;
		}
	}
	/*
	 * Place pointer in header area list
	 */
	if (headp == NULL) {
		fprintf(stderr, "No header defined\n");
		lkexit(ER_FATAL);
	}
	narea = hp->h_narea;
	halp = hp->a_list;
	for (i=0; i < narea ;++i) {
		if (halp[i] == NULL) {
			halp[i] = taxp;
			return;
		}
	}
	fprintf(stderr, "Header area list overflow\n");
	lkexit(ER_FATAL);
}

/*)Function	VOID	lkparea(id)
 *
 *		char *	id		pointer to the area name string
 *
 *	The function lkparea() searches the linked area structures
 *	for a name match.  If the name is not found then an area
 *	structure is created.  An areax structure is created and
 *	appended to the areax structures linked to the area structure.
 *	The associated base area and head structure pointers are
 *	loaded into the areax structure.
 *
 *	local variables:
 *		area *	tap		pointer to an area structure
 *		areax *	taxp		pointer to an areax structure
 *
 *	global variables:
 *		area	*ap		Pointer to the current
 *				 	area structure
 *		area	*areap		The pointer to the first
 *				 	area structure of a linked list
 *		areax	*axp		Pointer to the current
 *				 	areax structure
 *
 *	functions called:
 *		VOID *	new()		lksym()
 *		char *	strsto()	lksym.c
 *		int	symeq()		lksym.c
 *
 *	side effects:
 *		Area and/or areax structures are created.
 *		Failure to allocate space for created structures
 *		will terminate the linker.
 */

VOID
lkparea(id)
char *id;
{
	register struct area *tap;
	register struct areax *taxp;

	ap = areap;
	axp = (struct areax *) new (sizeof(struct areax));
	while (ap) {
		if (symeq(id, ap->a_id, 0)) {
			taxp = ap->a_axp;
			while (taxp->a_axp)
				taxp = taxp->a_axp;
			taxp->a_axp = axp;
			axp->a_bap = ap;
			axp->a_bhp = hp;
			return;
		}
		ap = ap->a_ap;
	}
	ap = (struct area *) new (sizeof(struct area));
	if (areap == NULL) {
		areap = ap;
	} else {
		tap = areap;
		while (tap->a_ap)
			tap = tap->a_ap;
		tap->a_ap = ap;
	}
	ap->a_axp = axp;
	axp->a_bap = ap;
	axp->a_bhp = hp;
	ap->a_id = strsto(id);
}

/*)Function	VOID	lnkarea()
 *
 *	The function lnkarea() resolves all area addresses.
 *	The function evaluates each area structure (and all
 *	the associated areax structures) in sequence.  The
 *	linking process supports four (4) possible area types:
 *
 *	ABS/OVR	-	All sections (each individual areax
 *			section) starts at the identical base
 *			area address overlaying all other
 *			areax sections for this area.  The
 *			size of the area is largest of the area
 *			sections.
 *
 *	ABS/CON -	All sections (each individual areax
 *			section) are concatenated with the
 *			first section starting at the base
 *			area address.  The size of the area
 *			is the sum of the section sizes.
 *
 *		NOTE:	Multiple absolute (ABS) areas are
 *			never concatenated with each other,
 *			thus absolute area A and absolute area
 *			B will overlay each other if they begin
 *			at the same location (the default is
 *			always address 0 for absolute areas).
 *
 *	REL/OVR	-	All sections (each individual areax
 *			section) starts at the identical base
 *			area address overlaying all other
 *			areax sections for this area.  The
 *			size of the area is largest of the area
 *			sections.
 *
 *	REL/CON -	All sections (each individual areax
 *			section) are concatenated with the
 *			first section starting at the base
 *			area address.  The size of the area
 *			is the sum of the section sizes.
 *
 *		NOTE:	Relocatable (REL) areas are always concatenated
 *			with each other, thus relocatable area B
 *			(defined after area A) will follow
 *			relocatable area A independent of the
 *			starting address of area A.  Within a
 *			specific area each areax section may be
 *			overlayed or concatenated with other
 *			areax sections.
 *
 *
 *	If a base address for an area is specified then the
 *	area will start at that address.  Any relocatable
 *	areas defined subsequently will be concatenated to the
 *	previous relocatable area if it does not have a base
 *	address specified.
 *
 *	The names s_<areaname> and l_<areaname> are created to
 *	define the starting address and length of each area.
 *
 *	local variables:
 *		addr_t	rloc		;current relocation address
 *		char	temp[]		;temporary string
 *		struct symbol	*sp	;symbol structure
 *
 *	global variables:
 *		area	*ap		Pointer to the current
 *				 	area structure
 *		area	*areap		The pointer to the first
 *				 	area structure of a linked list
 *
 *	functions called:
 *		int	fprintf()	c_library
 *		VOID	lnksect()	lkarea.c
 *		symbol *lkpsym()	lksysm.c
 *		char *	strncpy()	c_library
 *		int	symeq()		lksysm.c
 *
 *	side effects:
 *		All area and areax addresses and sizes are
 *		determined and saved in their respective
 *		structures.
 */

/*
 * Resolve all area addresses.
 */
VOID
lnkarea()
{
	register int rloc;
	char temp[NCPS+2];
	struct sym *sp;

	rloc = 0;
	ap = areap;
	while (ap) {
		if (ap->a_flag&A_ABS) {
			/*
			 * Absolute sections
			 */
			lnksect(ap);
		} else {
			/*
			 * Relocatable sections
			 */
			if (ap->a_bset == 0)
				ap->a_addr = rloc;
			lnksect(ap);
			rloc = ap->a_addr + ap->a_size;
		}

		/*
		 * Create symbols called:
		 *	s_<areaname>	the start address of the area
		 *	l_<areaname>	the length of the area
		 */

		if (! symeq(ap->a_id, _abs_, 0)) {
			strcpy(temp+2, ap->a_id);
			*(temp+1) = '_';

			*temp = 's';
			sp = lkpsym(temp, 1);
			sp->s_addr = ap->a_addr;
			sp->s_axp = NULL;
			sp->s_type |= S_DEF;

			*temp = 'l';
			sp = lkpsym(temp, 1);
			sp->s_addr = ap->a_size;
			sp->s_axp = NULL;
			sp->s_type |= S_DEF;
		}
		ap = ap->a_ap;
	}
}

/*)Function	VOID	lnksect()
 *
 *		area *	tap		pointer to an area structure
 *
 *	The function lnksect() is the function called by
 *	lnkarea() to resolve the areax addresses.  Refer
 *	to the function lnkarea() for more detail. Pageing
 *	boundary and length errors will be reported by this
 *	function.
 *
 *	local variables:
 *		addr_t	size		size of area
 *		addr_t	addr		address of area
 *		areax *	taxp		pointer to an areax structure
 *
 *	global variables:
 *		int	lkerr		error flag
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		All area and areax addresses and sizes area determined
 *		and linked into the structures.
 */

VOID
lnksect(tap)
register struct area *tap;
{
	register addr_t size, addr;
	register struct areax *taxp;

	size = 0;
	addr = tap->a_addr;
	if ((tap->a_flag&A_PAG) && (addr & 0xFF)) {
	    fprintf(stderr,
		"\n?ASlink-Warning-Paged Area %s Boundary Error\n",
		tap->a_id);
	    lkerr++;
	}
	taxp = tap->a_axp;
	if (tap->a_flag&A_OVR) {
		/*
		 * Overlayed sections
		 */
		while (taxp) {
			taxp->a_addr = addr;
			if (taxp->a_size > size)
				size = taxp->a_size;
			taxp = taxp->a_axp;
		}
	} else {
		/*
		 * Concatenated sections
		 */
		while (taxp) {
			taxp->a_addr = addr;
			addr += taxp->a_size;
			size += taxp->a_size;
			taxp = taxp->a_axp;
		}
	}
	tap->a_size = size;
	if ((tap->a_flag&A_PAG) && (size > 256)) {
	    fprintf(stderr,
		"\n?ASlink-Warning-Paged Area %s Length Error\n",
		tap->a_id);
	    lkerr++;
	}
}
