/* Library.c
 *	Routines required in all shared libraries.
 *	Copyright 1986 by James M Synge.
 */

#include "exec/types.h"
#include "exec/libraries.h"
#include "ExtLibrary.h"
#include "libraries/dos.h"

/* Declare the name of the library	*/

char LibraryName[] = "task.library";
char LibraryId[] =
	"task.library V1.0 (25 May 1987)\015\012";

#define LIB_VERSION	1
#define LIB_REVISION	2

/*
 * This short assembler section is included here so that
 * there is just one small place where these numbers need to
 * be replaced, instead of in two different files (i.e. here
 * and LibHead.asm).
 */

#asm
	PUBLIC	LibVersion
	PUBLIC	LibRevision
LibVersion	EQU	1	; Shared with libhead via
LibRevision	EQU	2	; the PUBLIC statement
#endasm

/*
 * If the rt_Flags field of the RomTag (struct Resident) has
 * the AUTOINIT bit set, then the rt_Init field must point
 * to a block such the following LibInitBlock.  This block
 * contains four of the five parameters which OpenLibrary()
 * will pass to MakeLibrary.  (The fifth parameter is the
 * SegList.)  If the AUTOINIT bit is not set, then the
 * rt_Init field must point to a routine which will perform
 * the equivalent initialization.
 *
 * When the library is being auto-initialized, a block of
 * memory is allocated by MakeLibrary().  Its size is
 * determined by the length of FunctionList (in Functions.c)
 * and an entry, lib_sizeof_ExtLibrary, in the structure
 * pointed to by rt_Init.  That entry is the size in bytes
 * of the struct ExtLibrary to be allocated automatically,
 * before calling the routine whose address is in
 * lib_InitCode.  So, this value must be known before
 * linking.
 *
 * This can be done by expressing the LibInitBlock structure
 * in C so that the compiler can determine the size for us.
 * This is a drastic improvement over figuring it out by
 * hand!
 */

extern long *FunctionList[];
void _LibInitCode();

struct {

	long lib_sizeof_ExtLibrary; /* Bytes to allocate  */
	APTR lib_FunctionList;	    /* Ptr to func list   */
	long lib_InitStruct;	    /* see initializers.i */
	APTR lib_InitCode;	    /* Ptr to asm routine */

} LibInitBlock = {

	sizeof( struct ExtLibrary ),
	(APTR) FunctionList,
	0L,			    /* No struct init	  */
	(APTR) _LibInitCode

};

/* The assembly routine _LibInitCode calls LibraryInit() to
 * complete the initialization of the library.
 */
struct ExtLibrary *
LibraryInit( LibBasePtr )

struct ExtLibrary *LibBasePtr;
{
	/* These initializations are done here because it is
	 * difficult to build an initializer structure with
	 * Aztec Assembler 3.2.  The argument, the library
	 * pointer, is pushed onto the stack by the
	 * assembler routine _LibInitCode.
	 */

	LibBasePtr -> el_Node.ln_Type	= NT_LIBRARY;
	LibBasePtr -> el_Node.ln_Name	= LibraryName;
	LibBasePtr -> el_Version	= LIB_VERSION;
	LibBasePtr -> el_Revision	= LIB_REVISION;
	LibBasePtr -> el_IdString	= (APTR) LibraryId;

	/* Now set up the initial values for my libraries'
	 * variables.
	 */

	/* NONE IN THIS EXAMPLE!!! */

	return LibBasePtr;	/* This is the value which
				 * will be returned to
				 * MakeLibrary()
				 */
}

/*
 * The required routines Open, Close and Expunge are called
 * not by other C routines, but rather by Exec.  This means
 * that their arguments are in registers instead of on the
 * stack.
 *
 * There are three straight forward options at this point:
 *
 *   1)	Use the variable LibraryBase (see LibHead.asm) which
 *	should contain the same value as that in A6.
 *
 *   2)	Immediately call the routine regA6() in LibHead.asm
 *	which will return the value in A6 (Should be the
 *	Library Base Pointer).  This requires that we can be
 *	certain that A6 hasn't been wiped out already by the
 *	compiler generated setup code.
 *
 *   3)	Place an intermediate assembly routine between exec
 *	and each C routine which moves the arguments,
 *	including the Library Base Pointer, from the
 *	registers to the stack.
 *
 * The last method seems the best, so we'll use it.
 */

struct ExtLibrary *
Library_Open( LibBasePtr, Version )

struct ExtLibrary *LibBasePtr;	/* Was in A6 */
long Version;			/* Was in D0 */
{
	Forbid();

	/* Note that another open has occured. */
	LibBasePtr->el_OpenCnt ++;

	/* Since we know that there is at least one user of
	 * this library, we'll ignore any previous call to
	 * Library_Expunge.
	 */
	 
	LibBasePtr->el_Flags &= ~LIBF_DELEXP;

	Permit();

	return LibBasePtr;
}

/* When the memory allocator in exec.library is looking for
 * more memory, it will try to get rid of the memory used by
 * libraries, devices and fonts.  To do this with a library,
 * it calls Library_Expunge, the third of the required
 * routines.  If Library_Expunge returns zero, then the
 * memory allocator looks elsewhere.  If it returns non-zero
 * then the value must be a pointer to an AmigaDOS SegList.
 * This will be placed on the free list by the memory
 * allocator.  It is the responsibility of the library to
 * free the jump table and Library structure.
 */

extern BPTR LibSegList;  /* Allocated in LibHead.asm */

BPTR
Library_Expunge( LibBasePtr )

struct ExtLibrary * LibBasePtr;	/* Was in A6 */
{
	unsigned long size, JumpTableBase;

	/* The memory allocator runs inside a Forbid() /
	 * Permit() pair.  To protect against being called
	 * by somebody else (i.e. Library_Close).
	 */

	Forbid ();

	if (LibBasePtr->el_OpenCnt > 0) {
		/* Still open, so note the expunge for
		 * later use in Library_Close.
		 */
		LibBasePtr->el_Flags |= LIBF_DELEXP;
		Permit ();
		return 0;
	}

	/* Remove the library from the exec library list
	 * so that nobody tries to allocate the library.
	 */

	Remove( LibBasePtr );

	/* Now free up the jump table and library structure.
	 * We add the sizes of the two (lib_NegSize is the
	 * size of the jump table, and lib_PosSize is the
	 * size of struct ExtLibrary), then free the block
	 * starting at the base of the jump table.
	 */

	size = (long)(LibBasePtr->el_NegSize)
	     + (long)(LibBasePtr->el_PosSize);

	if ( size ) {
		JumpTableBase = (long) LibBasePtr -
			(long) (LibBasePtr->el_NegSize);

		FreeMem( JumpTableBase, size );
	}

	Permit();

	return LibSegList;
}

/* When the exec routine CloseLibrary() is called, it calls
 * the required Close() routine of the identified library.
 * That routine is implemented here by Library_Close.  It
 * decrements the open count.  If it is zero, and if the
 * delayed expunge flags is set, then Library_Expunge() is
 * called.  Just as in the case of the Expunge routine, if
 * a non-zero value is returned, it must be a BPTR to an
 * AmigaDOS SegList.
 */

BPTR
Library_Close( LibBasePtr )

struct ExtLibrary * LibBasePtr;	/* Was in A6 */
{
	Forbid();	/* Have to be cautious when there's
			 * not much documentation. */
	LibBasePtr->el_OpenCnt -- ;	/* Decrement */
	Permit();
	
	if (LibBasePtr->el_OpenCnt == 0)
	    if ((LibBasePtr->el_Flags & LIBF_DELEXP) != 0)
		return Library_Expunge();

	return 0;	/* No expunge. */
}

/* Now for the big routine!
 */
long Library_Reserved()
{
	return(0L);
}
