/*
** libinit.c
** 21st May 2000
** David McMinn
** The basic library functions: init, open, close, expunge, extra
*/


#include <exec/types.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <exec/execbase.h>
#include <exec/resident.h>
#include <exec/initializers.h>

#include <powerpc/powerpc.h>
#include <powerup/proto/ppc.h>
#include <proto/exec.h>

#include "defs.h"
#include "semaphores.h"
#include "libinit.h"


struct ExecBase    *SysBase = NULL;        /* pointer to exec.library */
SEGLISTPTR         sl = NULL;              /* pointer to the seglist of our library */
struct PPCBase     *PowerPCBase = NULL;    /* powerpc.library base */
struct Library     *PPCLibBase = NULL;    /* ppc.library base */

static VOID     free_library_base( struct Library *lib_base, SEGLISTPTR seglist );

/*
** InitLib()
**
** Called by MakeResident() after library is LoadSeg'd
**
	INPUTS
		struct myLibBase *base (in D0)  - Pointer to our library base
		SEGLISTPTR seglist (in A0)      - Pointer to our seglist
		struct ExecBase *execbase       - Pointer to exec.library base

	RESULT
		Pointer to our library base if everything was OK, or NULL for failure
*/
struct myLibBase * SAVEDS ASM   InitLib(REG(d0) struct myLibBase *base, REG(a0) SEGLISTPTR seglist, REG(a6) struct ExecBase *execbase)
{
	struct myLibBase *myLibBase = base;                 /* take backup copy of library base as d0 is a scratch register */

	SysBase = execbase;                                 /* store pointer to exec.library base */
	sl = seglist;                                       /* and also store our seglist pointer */
	myLibBase->lb_pluginID = 0x504C5547;                /* store 'PLUG' in library base to identify this as a Scalos plugin */

	/* try to open ppc.library */
	PPCLibBase = myLibBase->lb_PowerUPBase = (struct Library *)OpenLibrary("ppc.library", 0L);
	if(myLibBase->lb_Semaphore = CreateSemaphore())
	{
			return(myLibBase);
	}

	free_library_base((struct Library *)myLibBase, sl); /* otherwise call function to free everything */
	return(NULL);                                       /* and return NULL for failure */
}


/*
** OpenLib() - called on each OpenLibrary()

	INPUTS
		struct myLibBase *base (in A6) - Pointer to our library base

	RESULT
		Pointer to our library base if everything went OK, NULL for failure
*/
struct myLibBase * SAVEDS ASM   OpenLib(REG(a6) struct myLibBase *base)
{
	struct myLibBase *myLibBase = base;                 /* take working copy of our libbase pointer */

	ObtainSemaphore(myLibBase->lb_Semaphore);           /* Try to get exclusive access to the library base */
	myLibBase->lb_LibNode.lib_OpenCnt++;                /* Increase open count */
	myLibBase->lb_LibNode.lib_Flags &= ~LIBF_DELEXP;    /* Clear the delayed expunge flag */
	ReleaseSemaphore(myLibBase->lb_Semaphore);
	return(myLibBase);
}


/*
** CloseLib() - called on every CloseLibrary()

	INPUTS
		struct myLibBase *base (in A6) - pointer to our library base

	RESULT
		Pointer to our seglist if the library is OK to be freed from memory
		or NULL if it should be left alone
*/
SEGLISTPTR SAVEDS ASM           CloseLib(REG(a6) struct myLibBase *base)
{
	struct myLibBase *myLibBase = base;                     /* copy of the pointer to our library base */

	ObtainSemaphore(myLibBase->lb_Semaphore);               /* try to get exclusive access to the library base */
	myLibBase->lb_LibNode.lib_OpenCnt--;                    /* decrease number of tiems the library has been opened */
	if(!myLibBase->lb_LibNode.lib_OpenCnt)                  /* if no-one else has opened this library */
	{
		if(myLibBase->lb_LibNode.lib_Flags & LIBF_DELEXP)   /* and the delayed expunge flag is set */
		{
			ReleaseSemaphore(myLibBase->lb_Semaphore);
			return(ExpungeLib(myLibBase));                  /* try to Expunge the library and return */
		}
	}
	ReleaseSemaphore(myLibBase->lb_Semaphore);
	return(NULL);                                           /* Return NULL, so don't free library from memory */
}


/*
** ExpungeLib() - Called when library should be freed from memory

	INPUTS
		struct myLibBase *base (in A6) - pointer to our library base

	RESULT
		Pointer to our seglist if this library should be freed from memory
		or NULL if the library should not be freed from memory
*/
SEGLISTPTR SAVEDS ASM           ExpungeLib(REG(a6) struct myLibBase *base)
{
	struct myLibBase    *myLibBase = base;                  /* take copy of pointer to our library base */
	SEGLISTPTR          seglist;                            /* copy of the pointer to our seglist */

	ObtainSemaphore(myLibBase->lb_Semaphore);               /* Get exclusive access to our library base */
	if(!myLibBase->lb_LibNode.lib_OpenCnt)                  /* if no-one has this library open */
	{
		ReleaseSemaphore(myLibBase->lb_Semaphore);          /* release access to library base */
		FreeSemaphore(myLibBase->lb_Semaphore);             /* free semaphore used to grant access to library base */

		/* Close all the libraries we have opened */
		//if(myLibBase->lb_PowerPCBase)
		//	CloseLibrary((struct Library *)myLibBase->lb_PowerPCBase);

		if(myLibBase->lb_PowerUPBase)
			CloseLibrary(myLibBase->lb_PowerUPBase);

		Remove((struct Node *)myLibBase);                   /* remove our library from the list of libraries */
		seglist = sl;                                       /* take copy of the seglist pointer (the next function sets it to NULL) */

		/* Call function to free memory used by library */
		free_library_base((struct Library *) myLibBase, seglist);

		return(seglist);                                    /* return pointer to seglist, tell OS to remove it from memory */
	}
	myLibBase->lb_LibNode.lib_Flags |= LIBF_DELEXP;         /* else, set the delayed expunge flag */
	ReleaseSemaphore(myLibBase->lb_Semaphore);              /* release access to library base */
	return(NULL);                                           /* return NULL (tell OS not to remove us from memory */
}


/*
** ExtFuncLib() - Extra library function, must return NULL
*/
ULONG SAVEDS ASM                ExtFuncLib(void)
{
	return(0);
}


/*
** free_library_base() - frees the memory taken up by the library base

	INPUTS
		struct Library *lib_base    - pointer to the library base to free
		SEGLISTPTR seglist          - pointer to our seglist
*/
static VOID             free_library_base( struct Library *lib_base, SEGLISTPTR seglist )
{
	ULONG negsize;                      /* the size of memory that the library base extends to below the libbase pointer */
	ULONG possize;                      /* memory used by the library base above the libbase pointer */
	ULONG fullsize;                     /* the total size of the library base (the previous two added up) */
	UBYTE *negptr = (UBYTE *) lib_base; /* pointer to the start of memory to be freed */

	negsize  = lib_base->lib_NegSize;   /* get negative size of library base */
	possize  = lib_base->lib_PosSize;   /* and positive size */
	fullsize = negsize + possize;       /* add them to get the total size of the libbase */
	negptr  -= negsize;                 /* alter the pointer to point to the start of the memory to be freed */

	FreeMem( negptr, fullsize );        /* free the library base memory */
}

