	A Guide to AmigaDOS Shared Libraries
Note
	The examples of this article are written in assembler and C and require some knowledge of the languages to fully understand what they are all about. They are written only to illustrate the explanations and are only parts of larger source codes. They may not be accurate and I take no responsibility for the correctness or function of the examples.

	Shared Library Overview

	To be able to learn how to make a shared library, it's important to have the knowledge about what it is all about. In this article I'll take you through all steps, from the most basic ones down to the ones dealing with low level library programming.

Shared Library
	First, an answer to the question: what is a shared library? As the name says, it is a function library shared by several simultaneous tasks and processes. The shared library code is not present in the executable image on disk, but is a separate file. The shared code is not loaded together with the executable. It is loaded into memory only when a program requires it.
	On Amiga, the naming convention says that a shared library should be in lowercase letters with a ".library" ending, and the directory to put them in is "LIBS:".

Link Library
	A link library is not to be mixed up with a shared library. A link library is a function library that is linked into the executable at compile time. A link library becomes a part of an executable image.

ROM Based/Disk Based Libraries
	The AmigaDOS system consists of several shared libraries, whose names you recognize: dos.library, exec.library, graphics.library, only to mention a few. These libraries won't be found in the LIBS: directory, they reside in ROM. Whether in ROM or on disk, shared libraries work and are used the same way.

Memory Usage
	As mentioned, shared libraries are loaded when a program requests, i.e. opens, it. When the program has finished using the library, it closes the library. The library remains in memory even though no process is using it, until the operating system requires the memory it occupies (or is forced to remove itself by a program, such as "avail FLUSH" on the shell prompt in AmigaDOS 2.0 or later).

Other Operating Systems
	Shared libraries are not AmigaDOS specific. Such are also found under UNIX and OS/2, only to mention the most obvious and common.

Advantages
	The reasons why so many systems are using shared libraries are among others: less disk space is used because the shared library code is not included in the executable programs, less memory is used because the shared library code is only loaded once, load time may be reduced because the shared library code may already be in memory when a program wants it, and that programs using shared libraries are very easily updated.


	Calling Shared Library Functions

	We've been looking at what a shared library is, a little about how it works and some of its advantages. Now it's time to see how a library is used and accessed.

Address Library Functions
	To be able to handle library calls, we must know how to call shared library functions. I'll describe it with a small comparsion to standard non-shared functions. The most significant difference is in the way the functions are addressed. A standard function within a program is more or less an address to which the program counter is set when we want to jump to it. A shared library function is on the other hand addressed by adding a number to the address of the library's base.
	When using standard function calls, the compiler or assembler arrange so that e.g. the function "getname" is associated with the paricular static address in memory where the "getname" function starts. If the same "getname" function would be a shared library function, the compiler wouldn't know the actual address of it, but dynamically add a certain number (index) to the library's base address to access it.
	As you see, we must know the index of the function and the library base address to be able to call a shared library function.

Library Base
	To find out the library base of a shared library, you must call OpenLibrary() which will return the library base of the spcified library in register D0. All library bases are found like that except exec.library's, which is found by reading the pointer stored at the absolute address 4.

Index
	Whenever you want to call a function in a shared library you (or the compiler) have to know the index to add to the library's base address.
	E.g., to call OpenLibrary() you must know the index of the function and the library base itself (OpenLibrary() is an exec.library function and we know that exec.library's base address is found at address 4). A call to OpenLibrary() could look like this in assembler:

	MOVE.L	SysBase,a6	; SysBase is the name of
			; exec.library's base pointer
	: >>> Parameters left out in this example <<<
	JSR	-552(a6)	; We'll jump straight into the jump
			; table at the certain index. The index
			; is -552 in this case

Parameters
	Ok, we know how to call a library function and we know that we must call OpenLibrary() to get a library's base address. To inform e.g OpenLibrary() which library we want to open, we must send it some parameters. The documentation tells us that OpenLibrary() wants the library name in A1 and the lowest acceptable version in D0. Parameters to the library functions are always stored in registers. See the library reference documentation for closer information exactly which registers.

	This example opens a library with the name at libName with version 33 or higher:

	INCLUDE "exec/funcdef.i"	* _LVO macro constructs
	INCLUDE "exec/exec_lib.i"	* exec function index
	VERSION equ 33

	MOVE.L	SysBase,a6		; exec library base
	LEA.L	libName,a1		; library name
	MOVE.L	#VERSION,d0	; lowest usable version
	JSR	_LVOOpenLibrary(a6)	; OpenLibrary()

Access Libraries
	The operating system provides facilities for the creation, use and access of shared libraries. The functions that let the programmer construct and access libraries are of different levels to give different possibilities. Low level function where you can change every single parameter and more high level functions that do a lot without the programmers exact specification.

	I'll describe the functions of the highest level that also are the most frequently used:

- OpenLibrary()
	Gains access to a named library of a given version number. Always open libraries with the lowest version which includes the functions you need. To open intuition.library for 2.0+ (version 36) only, try something like:

	#include <proto/exec.h>
	#define LIB_VERSION 36
	struct ExecBase *SysBase;
	struct IntuitionBase *IntuitionBase;
	void main(void)
	{
	  /*
	   * The SysBase should be in order to perform this.
	   * (Using any C startup module will do this for you.)
	   */
	  IntuitionBase=(struct IntuitionBase *)
	    OpenLibrary("intuition.library", LIB_VERSION);
	  if(!IntuitionBase) {
	    printf("Couldn't open intuition version %d+\n", LIB_VERSION);
	    exit(10);
	  }
	  /*
	   * The program using intuition.library V36+ follows here!
	   */
	}

	AmigaDOS file names are not case sensitive, but Exec lists are. If the library name is specified in a different case than it exists on disk, unexpected results may occur.
  
- CloseLibrary()
	Concludes access to a library. Whenever your program has finished using the functions of a shared library, there should be a call to CloseLibrary() for every call to OpenLibrary(). Simply like this:

	CloseLibrary((struct Library *)IntuitionBase);

- RemLibrary()
	Calls the Expunge() function of the specified library. If the library isn't open, it will delete itself from memory. This is not typically called by user code.

	 /* Attempts to flush the named library out of memory. */
	 #include <exec/types.h>
	 #include <exec/execbase.h>

	 void FlushLibrary(STRPTR name)
	 {
	   struct Library *result;

	   Forbid();
	   if(result=(struct Library *)FindName(&SysBase->LibList,name))
	      RemLibrary(result);
	   Permit();
	 }

With these three functions in mind, we'll continue.

Return Code
	The return code of a shared library function call is always received in a register. (Today, I don't think there is a single function not using D0 for that purpose.)

Glue Code
	The parameter storage in registers is not that comfortable in all occasions and many compilers (in all kinds of programming languages) don't even have the ability to store parameters in (pre-decided) registers. Then, glue code is required. Glue code (also known as "stub functions" or simply "stubs") is simply a set of functions that you can call instead of the shared library functions. The stub function reads the parameters from the stack and stores them in registers and then calls the shared library function. That makes the use of the glue code functions identical to other functions. Glue code is compiled into a kind of object file, using the suffix ".lib", and is stored in LIB: (not to be mixed up with LIBS: where the shared libraries are stored). All stub functions for the standard AmigaDOS libraries are found in the "amiga.lib" file that comes with most compilers.

C and Register Parameters
	C language compilers are in general using the stack to pass parameters between functions, but to be able to use shared libraries smoothly, several compilers offer ways to force parameters in registers and automatically use the right library base and function index.
	The two largest commercial C compilers on Amiga, SAS/C and Aztec C, both provide such solutions by special pragma instructions. A pragma instruction is a line starting with "#pragma", which is a compiler instruction keyword, followed by the compiler specific text.
Such a pragma defines the function, which library base it needs and in which registers the parameters must be stored. By using such pragmas you don't have to call or link any glue code within your program.
	The GNU C compiler, which is a freely distributable C and C++ compiler, has a very complicated way to solve this problem. It declares and uses inlined functions that use GNU's own __asm() instruction to set the proper registers to the right values.

	SAS/C pragmas are built-up like this:

	#pragma <kind of call> <lib base> <name> <index> <registers>

	which means:

 #pragma
	Compiler instruction keyword.

 <kind of call>
	Which kind of library call this pragma should generate. There are three different ones:
	`libcall' makes a standard library call
	`tagcall' makes a standard library call where the last parameter points to a taglist
	`syscall' makes a call to exec.library

 <lib base>
	The library base name to use. Not specified for `syscall' calls. Example: "DiskfontBase" (The name of diskfont.library's library base.)

 <name>
	Function name identifier. Example: "MyFunction".

 <index>
	Function index of the library. A hexadecimal, positive number (which is turned negative by the compiler when it generates the indexed library call). Example: "1A" (The first library function index of all normal libraries.)

 <registers>
	Register/parameter information in a special format, a sequence of hexadecimal numbers. Reading from the right, each digit has the following meaning:
	1. Number of parameters.
	2. Result code register (0-6 means register D0-D6 and 8-9, A-E means register A0-A6)
	3+. The parameter registers, read from the left (!). The numbers are associated with the same registers as in paragraph 2 above.

	Example:

	#pragma libcall SysBase OpenLibrary 228 0902

	When using this information, a compiled result uses SysBase, the index and the parameters in registers just as we did in the assembler examples above. C language usage:

	#include <proto/exec.h>	/* This includes the pragmas too */
	#define libName "foobar.library"
	#define VERSION 33
	OpenLibrary(libName, VERSION);

	(Generate pragma files with the SAS/C utility `fd2pragma' which uses a function descriptor file as source of information. Read about function descriptor files further on.)

Indexing Effects
	Compilers of different programming languages often create machine language instructions that address data indexed by a 16-bit register, instead of straight 32-bit addressing, to increase execution speed and decrease the code size.

	Some libraries might request or offer a "callback function", a function supplied by you in the form of a function pointer that might get called from inside the library. A call from within a library may not have that index register set properly and therefore you must set it before you can access any data that requires that register!

	In SAS/C, this is simply done by defining the function like:

	void __saveds callback( void );

	if using DICE, __saveds must be replaced with __geta4.

	(In the SAS and Aztec compilers, it can also be done by calling geta4() first in the callback function.)

	From version 36-37 some of the AmigaDOS system libraries feature hook abilities, which is a kind of callback function. They are also called from inside the library and then of course also demand loading of the index register the same way.

Registers
	Library functions should preserve the a2-a7 and d2-d7 registers. The rest must be stored in a safe place and then brought back after the library call if you want to be sure of their contents.


	Parts of an AmigaDOS Shared Library Image

	If we were content with only using shared libraries, we would have enough information by now to use all kinds of library calls.
	Only scratching the surface isn't enough if we want to create someting by ourselves. We must instead start digging into detailed information. How is a shared library constructed? Of which parts? How do you combine those parts to make your own shared library?
	First we take a look at the parts of a shared library. A shared library does not look the same when compiled/assembled as when loaded into memory and added to the system's list. That is because when the library is loaded/added it is also modified and initialized in a few ways to make the system able to use it. But let's not hesitate.

	A shared library image is built up by a few different parts:
	- Code preventing execution
	- ROMTag structure with sub data:
	- Init table
	- Function pointer table
	- Data table
	- Init routine
	- Functions

Prevent Execution
	The first thing the disk image contains is a piece of code that prevent users from trying to execute the library as an executable file. That piece of code should preferably return an error code to the calling environment (that most possibly is a shell).

	Example:

	MOVEQ	#-1,d0
	RTS

ROMTag Structure
	Coming up next is a ROMTag structure. ROMTags are used to link system resident modules together. The ROMTag looks like:

	(found in <exec/resident.h>)

	struct Resident {
	  UWORD rt_MatchWord;
	  struct Resident *rt_MatchTag;
	  APTR  rt_EndSkip;
	  UBYTE rt_Flags;
	  UBYTE rt_Version;
	  UBYTE rt_Type;
	  BYTE  rt_Pri;
	  char  *rt_Name;
	  char  *rt_IdString;
	  APTR  rt_Init;
	};

  rt_MatchWord -	Used by exec to find this structure when it is about to link us into the ROMTag list. This must contain RTC_MATCHWORD (the hexadecimal number 4AFC, which is a MC68000 "ILLEGAL" instruction).
  rt_MatchTag -	This must contain a pointer to this struct.
  rt_EndSkip -	Pointer to end of library init code.
  rt_Flags -	RTF_AUTOINIT informs exec that the structures rt_Init member points to an init table.
  rt_Version -	Library version number
  rt_Type -	Should contain NT_LIBRARY (found in <exec/nodes.h>), which informs exec about the fact that this is a shared library image.
  rt_Pri -	Initialization priority. 0 (zero) is perfectly ok.
  rt_Name -	Pointer to the zero terminated library name.
  rt_IdString -	Standard name/version/date ID string. Example: "myown.library 1.0 (01.04.93)"
  rt_Init -	This data points to an init table if RTF_AUTOINIT is set in structure member rt_Flags.

	As you can see, this structure requires some more information stored. You must have the library name and a standard ID string stored, and the last structure member should point to a "init table".

Init Table
	The init table is a table of four longwords. I try to visualize them in a structure like this:

	(A struct of this kind is not found in any standard include file, this is written by me.)

	struct InitTable {
	  ULONG it_LibBaseSize;
	  APTR  it_FuncTable;
	  ULONG *it_DataTable;
	  APTR  it_InitRoutine;
	};

  it_LibBaseSize -	Size of your library base structure. In common situations it is no point in using anything else but a straight struct Library as library base. It must not be smaller than that!
  it_FuncTable -	This should contain a pointer to an array of function pointers.
  it_DataTable -	Pointer to a data table in exec/InitStruct format for initialization of the Library base structure.
  in_InitRoutine -	Pointer to a library initialization routine or NULL.

	Once again we have a structure that needs more data. The function pointer table, the data table and the init routine is left.

Function Pointer Table
	This should be a table of function pointers to the different functions in the library. They can be specified in two ways:

  1)	By setting the first word in the list to -1, you specify that the table is a list with 16-bit addresses relative to the start of the list. End the table with a -1 word.

  2)	By storing absolute 32-bit pointers to the functions and ending with a -1 longword.

	My examples will use the second way.

	The pointers should point to the functions of the library. All libraries should still have a few standard functions used by exec and must not be left out. The first four entries are dedicated to such functions.

	The list must look like:

	- Open()	- Open library routine.
	- Close()	- Close library routine.
	- Expunge()	- Delete library from memory routine.
	- Extfunc()	- Reserved for future expansion.
	- own1()	- Our first function
	- own2()	- Our second function
	- ...	- The rest of our functions
	- -1	- End of table

	How to program such functions is discussed further on. Let's continue, we have the data table and the init routine left to look at.

Data Table
	The data table is used to initialize the library base structure when it's linked into the system list of shared libraries. The table is in the so called "exec/InitStruct" format. A data table is controlling a number of different initializing methods. In our case we just use a number of offsets (relative to the library base) and their initialization values.

	#include <exec/libraries.i>
	#include <exec/initializers.i>
	#include <exec/nodes.i>

	INITBYTE	LN_TYPE,NT_LIBRARY
		; Init type: Library.
	INITLONG	LN_NAME,LibName
		; Init name of the library
	INITBYTE	LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
		; Set the flags that tells exec we have changed
		: the library and that we allow checksumming.
	INITWORD	LIB_VERSION,VERSION
		; Init version
	INITWORD	LIB_REVISION,REVISION
		; Init revision
	INITLONG	LIB_IDSTRING,IDString
		; Init IDString
	DC.L 0	; End of InitStruct() command table

	If you have a larger library base than a Library struct, you might want to add more initialize entries to this table. The only thing left now to complete our ROMTag structure is the init routine.

Init Routine
	This routine gets called after the library has been allocated by exec. The library base pointer is in D0, the segment list is in A0 and SysBase in A6. This function must return the library base in D0 to be linked into the library list. If this initialization function fails, the library memory must be manually deallocated, then NULL returned in D0.

	Deallocate library memory by using something like:

	move.l	d0,a5
	moveq	#0,d0
	move.l	a5,a1
	move.w	LIB_NEGSIZE(a5),d0

	sub.l	d0,a1
	add.w	LIB_POSSIZE(a5),d0

	jsr	_LVOFreeMem(a6)

	The segment list, that we receive in A0, should be stored somewhere for later access. We'll need it when the library is to be removed from memory. Note that this routine will be called only once for every time the library is being loaded into memory. That makes it perfectly ok to store the segment list simply like:

	LEA	anywhere(pc),a1
	MOVE.L	a0,(a1)
	RTS

	anywhere: DC.L 0

	A nice way to store this data is to extend the library base structure to hold the segment list pointer too.

This was the last of the initialization part. The ROMTag structure is complete. Left in the library are the functions that it should contain.

Functions
	As mentioned before, there are four required functions that should be in all shared libraries. The rest of the functions are up to you to decide, design and make sure they receive proper data. How to code the functions and what to think of when doing so, is discussed in a chapter below.


	Libraries in the System

	We know what shared libraries are and we are familiar with all data stored in the library image. We know what functions to use when we want to access libraries and we know how to call library functions. What about low level information? What is done in the system when we call OpenLibrary()? How can I check if library already is loaded and which version number that library has? How can I patch a function of an already loaded library?

Library Opening Details
	When a single OpenLibrary() is called, a lot of things happen:

 1.	Exec checks the already loaded libraries to see if the requested library is there. If it is, go to step 6.

 2.	If the library name is specified without path, it is searched for in ROM, LIBS: and then current directory, otherwise simply in the specified path. The first directory that holds a library with the name it searches for, will be the one it loads from. If the library wasn't found, return NULL. If the library was found anywhere else but in ROM, it's LoadSeg()'ed into memory. ROM libraries are already accessible.

 3.	Exec scans the library for the 4AFC word with a following 32-bit pointer back to it. That word is the beginning of a ROMTag structure!

 4.	InitResident() is called, which hopefully finds the RTF_AUTOINIT flag set in the rt_Init member of the ROMTag structure and therefore calls MakeLibrary() which performs:

	Memory is allocated to fit a jump table and the library base structure. The size of the library base structure is found in the first longword of the data table. The jump table is created by a call to MakeFunctions() and is placed just before the library base in memory. The size of the allocation can be read in the library base structure (lib_NegSize + lib_PosSize).

	The library base structure is initialized using the data table list and an InitStruct() call.

	The init routine is called with the library base pointer in D0, SysBase in A6 and the segment list pointer in A0. If NULL is returned, the entire OpenLibrary() fails and returns NULL.

	Observe that any kind of failure in InitResident() means that the library is never added to the system.

 5.	AddLibrary() adds the library to the system list, making it available to programs. The checksum of the library entries will be calculated.

 6.	The OpenLibrary() call's version number parameter is checked against the version number of the library base (lib_Version). If the requested number is higher than the library version, OpenLibrary() fails and returns NULL.

 7.	The open function of the library is called. If that fails NULL is returned, otherwise the library base is returned in D0.

	If the same library exists in LIBS: with one version and in current directory with a later version, OpenLibrary() will always go for the one that it finds first. In this case that is the library in LIBS:. If that library has a too low version number, OpenLibrary() fails.

	As you can see, OpenLibrary() is a rather high level function. By using the other mentioned functions you can add a library to the system without going the way I describe in this article. But that wouldn't make it a standard shared library.

Library List
	Exec keeps track of all libraries that are opened. We can take part of exec's library list information by studying the linked list starting at SysBase->LibList. That pointer points to a `struct List', whose `struct Node' pointers point to the `struct Library' of all libraries that are currently in memory. This sounds more difficult than it is. Take a look at this small example.

	To find a certain library name in the library list, we can write:

	struct Library *findlib(char *name)
	{
	  struct Library *lib;
	  Forbid();
	  lib = (struct Library *)FindName( SysBase->LibList, name );
	  Permit();
	  return( lib );
	}

Patching Libraries
	All libraries that are opened get a jump table created. That means that even ROM based libraries get a jump table in RAM. When using functions in any library, we always go through that jump table which consists of nothing but a number of JMP #ADDRESS. As you understand, these jumps are supposed to jump into the library to perform whatever they are to perform. By changing an entry in that jump table, we can make a certain library call to call our own function instead of the original! But to change an entry is more than just stor ing in the list (since there are checksums and things that have to be correct). The correct way to do it, is to use SetFunction(), which can make one of those JMPs jump to our own code.

	To replace OpenLibrary() with our own function, we can do it like:

	#include <exec/types.h>
	#include <exec/protos.h>

	int OurOpenLibrary(char *, int);
	void patch(void)
	{
	APTR oldfunc;

	oldfunc = SetFunction((struct Library *)SysBase,
			   -552,
			   (APTR)OurOpenLibrary );

	/*
	 * Now, all following calls to OpenLibrary() will
	 * call our own function instead.
	 */

	/*
	 * To swap back, we simply use SetFunction()
	 * again. We really should be careful before
	 * doing so, because someone else might have
	 * patched the function after us, and if we
	 * simply restore our original we would ruin
	 * that patch!
	 */

	SetFunction( SysBase, -552, oldfunc );
	}	

	int __asm OurOpenLibrary(register __a1 libName,
		                register __d0 version)
	{
	/*
	 * Code our own library opener. Do remember that
	 * our index register is not initialized now, and
	 * if you want it, make sure you can restore the
	 * previous value before returning from this
	 * function. We don't want to crash any programs,
	 * do we?
	 */

	 /* Preserve used registers! */

	}

	Patching libraries are often used when creating debugging tools (such as the well-known `Mungwall' which patches AllocMem and FreeMem, `Snoopdos' which hangs on to most of dos.library's functions and others) and for programs that enhances or somehow changes the functionality of a function system wide (such as `Explodewindows' which patches OpenWindow() and beautifies window openings, `RTpatch' and `reqchange' which patches different requester calls to bring up reqtools.library requesters instead).

NOTE: SetFunction() cannot be used on non-standard libraries like dos.library! If you want to patch dos.library, you must manually Forbid(), preserve all 6 original bytes of the jump table entry, SumLibrary() (to evaluate the new checksum) and then Permit().


	Programming

Functions
	Shared libraries must be programmed by someone. Until now, you've learned how to control, play around and change already existing libraries. Now, we'll check out more of what there is to know to be able to program a library. The ROMTag initializing is of course required when programming a library, but the biggest part and the part that really makes the library, is still the functions.

	You're not restricted to anything when it comes to the function of the routines you want to put in a shared library. What must be thought of when creating functions for a shared library using a compiler, is that there is no main function and no startup modules,  and therefore no one of the symbols declared in those modules will be declared if you don't do it yourself.

	There are always four functions required that have to be in every library. They are Open(), Close(), Expunge() and Extfunc() and are called by exec when the library is to be opened, closed and removed from memory (the fourth is reserved for future use). Exec turns off task switching while executing these routines (via Forbid), so we should make them not take too long. (When using SAS/C these functions won't be necessary to code, see the "Compiling" and "Linking" chapters.)

- Open()	- (Library base:a6, version:d0)
	This routine is called by exec when OpenLibrary() (or more correct InitResident()) is called. Open should return the library pointer in D0 if the open was successful. If the open fails, NULL should be returned. It might fail in cases where we allocate memory on each open, or if the library only can be open once at a time...
	Example:

	; Increase the library's open counter
	addq.w	#1,LIB_OPENCNT(a6)

	; Switch off delayed expunge
	bclr	#LIBB_DELEXP,LIB_FLAGS(a6)

	; Return library base
	move.l	a6,d0
	rts

- Close()	- (Library base:a6)
	This routine is called by exec when CloseLibrary() is called. If the library is no longer open and there is a delayed expunge, then Expunge! Otherwise Close should return NULL.
	Example:

	; Decrease the library's open counter
	subq.w	#1,LIB_OPENCNT(a6)

	; If there is anyone still open, return
	bne.s	retlabel

	; Is there a delayed expunge waiting?
	btst	#LIBB_DELEXP,LIB_FLAGS(a6)
	beq.s	retlabel

	; Do the expunge!
	bsr	Expunge

	retlabel:
	; set the return value
	moveq	#0,d0

	rts

- Expunge()	- (Library base:a6)
	This routine is called by exec when RemLibrary() is called, or from Close when there was a delayed expunge. If the library is no longer open then Expunge should Remove() itself from the library list, FreeMem() the InitResident()'s allocation and return the segment list (which was given to the Init routine). Otherwise Expunge should set the delayed expunge flag and return NULL.

	Because Expunge might be called from the memory allocator, it may NEVER Wait() or otherwise take long time to complete.

	Example:

	; Is the library still open?
	tst.w	LIB_OPENCNT(a6)
	beq	notopen

	; It is still open. set the delayed expunge flag
	; and return zero
	bset	#LIBB_DELEXP,LIB_FLAGS(a6)
	moveq	#0,d0
	rts

	notopen: ; Get rid of us!

	movem.l	d2/a5/a6,-(sp)	; save some registers
	move.l	a6,a5

	; Store our segment list in d2
	lea	anywhere(pc),a6
	move.l	(a6),d2

	move.l	4,a6	; get SysBase

	; Unlink from library list
	move.l	a5,a1
	jsr	_LVORemove(a6)	; This removes our
				; node from the list

	; Free our memory
	moveq	#0,d0
	move.l	a5,a1
	move.w	LIB_NEGSIZE(a5),d0 ; jump table size

	sub.l	d0,a1
	add.w	LIB_POSSIZE(a5),d0 ; the size of the rest
				   ; of the library.

	jsr	_LVOFreeMem(a6)

	; Return the segment list
	move.l	d2,d0

	movem.l	(sp)+,d2/a5/a6	; Get back the registers
	rts

- Extfunc()	- (we don't know about any registers!)
	This routine is reserved for future use and should return 0 in register D0.

	Example:

	MOVEQ #0,d0
	RTS


Function Descriptor File
	To easily use the SAS/C options for creating a shared library, a standard AmigaDOS function descriptor file is required. It describes the functions in a library like:

	##base _OwnBase
	##bias 30
	OwnFunction(a)(A0D2)
	OwnFoobar(a)(D1A3)
	##end

	Where:

	##base -	The library base identifier

	##bias -	Index base position. The first function specified will use this index, which should be positive (turned negative later by the compiler) and in all normal cases starts on the first free jump table entry: 30.

	functionname(a)(registers) -
		Describes in which registers the function received its parameters in. The registers should be written without any spaces between them as in the example above.

	##end -	end of function descriptor file

Glue Code
	Glue code is written to be called with the parameters on the stack instead of the registers as it should. The glue functions should pick parameters from the stack and assign to the proper registers.

	Example:

	MOVE.L	a1,-(sp) ; Store register A1 on stack
	MOCE.L	a6,-(sp) ; Store register A6 on stack
	MOVE.L	12(sp),a1 ; Get first argument from stack
	MOVE.L	16(sp),d0 ; Get second argument from stack
	MOVE.L	4,a6 ; Get SysBase in A6.
	jsr	_LVOOpenLibrary(a6) ; Call OpenLibrary()
		; Now d0 contains the result code from the
		; library call
	MOVE.L	(sp)+,a6 ; Restore A6 from stack
	MOVE.L	(sp)+,a1 ; Restore A1 from stack
	rts

Compiling
	Things to think of when compiling library code:

 *	Always make the function called from another process (the outside) a "__saveds" function as the index register has to be properly initialized before continuing. __saveds should be replaced with __geta4 when using Dice and an initial Geta4() call when using Manx.

 *	Whether to use global symbols unique or shared by every task. SAS/C features easy changing between these two, but other compilers might have trouble creating unique global variables for each library open.

 *	Options when compiling a library may include some of the following. (These are the SAS/C options, but all compilers of today offer simlar functionalities.):

	LIBCODE		Forces all index addressing to use the library base pointer (a6) instead of the standard a4.

	NOSTANDARDIO	Do not use any of the C standard io functions such as printf() or fprintf(stderr, ...) since they rely on global symbols declared and initialized in the startup module.

	OPTIMIZE		Optimize the output code.

Linking
Linking a library often causes many problems, at least it has done so for me. You must remember that no compiler startup symbols will exist unless you declare them! Things like stack expansions can't be made to work, and routines like fopen() and others are using startup module symbols (which can be declared by us though).

	With the symbols in mind, we continue! All the talk about the library initializer structures is no problem of a SAS/C programmer's mind. By including the following flags in your `slink' line, all such problems are solved:

* LIBPREFIX <prefix>
	Default is `_' (underscore). This is the prefix added to the functions specified in the function descriptor file to match the symbols of the object file(s).

* LIBFD <function desc file>
	Tells where the function descriptor file is.

* FROM lib:libent.o lib:libinit.o
	Two nice object files holding code that we would have to code by ourselves otherwise. If you are using global variables in your code, "libinit.o" will make all currently open sessions of the library access the same, shared, variable. By using "libinitr.o" all globals will be copied at the library open, thus each open library has its own global variables.

* LIBID
	Sets the IdString of the library

* LIBVERSION <number>
	Sets the version number of the library

* LIBREVISION <number>
	Sets the revision number of the library

Debugging
	Using SAS/C, shared libraries can be run time debugged (including variable checking, break-pointing and so on) just like any other program using the "step into reslib" option in `cpr'. Break any library function by writing "b myown.library:foobar" (where foobar is the name of the function we want cpr to stop in when we enter) on the command prompt of `cpr'. When creating debug code, remember to debug the library that exists in the same directory as the code does, or specify the compiler flag SOURCEIS= and the name of your source file.

Hints
	I have been programming and developing shared libraries for some time by now, and there are a few things to pay certain attention to when dealing with this stuff.

  - Flush before retry
	Libraries don't go away simply because you close them, you know that. If you run your library once, close it and recompile it with a few changes, there will still be the older version remaining in memory that will be opened. When debugging libraries, always make sure that your library isn't already in memory before debugging a new version!

	I made a small program that resets the open counter and then RemLibrary() a named library. It is not at all a nice thing to do, but there really is a problem when you open your library and something crashes before you have had the chance to close it. There is no "nice" way of removing such a library from memory!

  - Globals
	By using the SAS/C object files libinit.o or libinitr.o you can make your global variables to be shared by all processes or unique for each OpenLibrary() call. If you want to mix the two versions or create something different, I advise you to code the library initial code by yourself.

  - Stack usage
	When your library is called and runs, it uses the same stack as the caller. If the caller has a very small stack, so do you. Built-in stack check routines are not available since they need irreplaceable symbols. For advanced users, allocating and using an own stack while the library is running could be the only and best way to solve a problem like this.

  - Symbols
	I've written it earlier and I do it again: high level language functions often uses symbols initialized and declared in the startup modules. Declare them by yourself if possible or avoid using such functions!

  - Register preservation
	I think it's a good habit to always preserve all registers (except for D0 that holds the return code) when your library routines are called. Remember that your library code index register is un-initialized when called from the library opener.


	Appendix

  A. VERSION NUMBERS AND SHARED LIBRARIES

	Commodore has introduced a general standard for shared library version numbering. The libversion number is the number of the library version. The librevision number is expected to be a counter from 0 and upwards, without any kind of preceding zero. This makes the first library version 1.0 and such as 1.9 is followed by 1.10, 1.11 and so on all the way to the maximum, of the same version, 1.65535.

	Failing in the version number check of a library opening leaves the library in memory. For example, when you want to open "myown.library", it's loaded into memory. If the version number check fails and you get a NULL in return, "myown.library" will still remain loaded.

	There are utilities which automatically updates a source file with the version number, revision number and the IDstring on every invoke. `bumprev' is one.

  B. FURTHER READING

    * Amiga ROM Kernel Reference Manual: Libraries, 3rd edition.

    * Amiga ROM Kernel Reference Manual: Includes & Autodocs, 3rd edition.


  C. LIBRARY SOURCE EXAMPLES

	Makefile A
	==========

	# This makefile uses the standard way of making a
	# shared library with SAS/C. Using the already
	# created object files SAS supports us with.

	CC      = sc
	CHEADER = myown.h
	CSOURCE = myown.c
	OBJ     = myown.o
	LIBRARY = myown.library
	FLAGS   = STRINGMERGE NOSTKCHK NOSTANDARDIO\
		  DATA=NEAR NOVERSION LIBCODE\
		  OPTIMIZE

	$(LIBRARY): $(OBJ)
	      slink with <<
		LIBFD myown.fd
		to $(LIBRARY)
		FROM lib:libent.o lib:libinit.o $(OBJ)
		noicons
		SD SC
		libid "myown.library 2.1 (18.04.93)"
		libversion 2 librevision 1
	<
	copy $(LIBRARY) LIBS: CLONE # Copy library to LIBS:

	$(OBJ): $(CSOURCE) $(CHEADER)
		$(CC) $(FLAGS) $*.c

	Makefile B
	==========
	# This makefile compiles everything and uses no
	# pre-compiled files.
	# Easy changed to fit DICE, Aztec or other
	# compilers.

	CC      = sc
	CHEADER = myown.h
	CSOURCE = myown.c
	ASOURCE = myownass.a
	OBJS    = myown.o myownass.o
	LIBRARY = myown.library
	FLAGS   = STRINGMERGE NOSTKCHK NOSTANDARDIO\
		  DATA=NEAR NOVERSION LIBCODE\
		  OPTIMIZE
	ASM     = asm
	ASMFLAGS= -iINCLUDE:

	$(LIBRARY): $(OBJS)
	      slink to $(LIBRARY) FROM $(OBJS) noicons SD SC
	      copy $(LIBRARY) LIBS: CLONE

	myown.o: $(CSOURCE) $(CHEADER)
		$(CC) $(FLAGS) $*.c

	myownass.o: $(ASOURCE)
		$(ASM) $(ASMFLAGS) $*.a


	myownass.a
	===========

	************************************************
	*
	* myown.library assembler source code
	*
	************************************************
	* Author: Daniel Stenberg
	************************************************

	   SECTION	code

	   NOLIST
	   INCLUDE "exec/types.i"
	   INCLUDE "exec/initializers.i"
	   INCLUDE "exec/libraries.i"
	   INCLUDE "exec/lists.i"
	   INCLUDE "exec/alerts.i"
	   INCLUDE "exec/resident.i"
	   INCLUDE "libraries/dos.i"

	   LIST

	   XDEF InitTable
	   XDEF Open
	   XDEF Close
	   XDEF Expunge
	   XDEF LibName

	   XREF _SysBase

	   XREF _LVOOpenLibrary
	   XREF _LVOCloseLibrary
	   XREF _LVOAlert
	   XREF _LVOFreeMem
	   XREF _LVORemove
	
	   XREF _Min
	   XREF _Abs
	
	   ; Prevent library execution:
	
	Prevent:
	   MOVEQ #-1,d0
	   rts
	
	;-----------------------------------------------
	; The romtag structure is next:
	;-----------------------------------------------
	
	MYPRI EQU 0	; priority zero...

	VERSION EQU 2	; version 2

	REVISION EQU 1	; revision 1

	RomTag:
			;STRUCTURE RT,0
	   DC.W RTC_MATCHWORD ; UWORD RT_MATCHWORD
	   DC.L RomTag	; APTR  RT_MATCHTAG
	   DC.L EndCode	; APTR  RT_ENDSKIP
	   DC.B RTF_AUTOINIT	; UBYTE RT_FLAGS
	   DC.B VERSION	; UBYTE RT_VERSION
	   DC.B NT_LIBRARY	; UBYTE RT_TYPE
	   DC.B MYPRI	; BYTE  RT_PRI
	   DC.L LibName	; APTR  RT_NAME
	   DC.L IDString	; APTR  RT_IDSTRING
	   DC.L InitTable	; APTR  RT_INIT
	
	; the name of our library
	LibName:
	  DC.B 'myown.library',0
	
	; standard name/version/date ID string
	IDString:
	   DC.B 'myown.library 2.1 (01.04.93)',13,10,0
	
	; force word alignment
	   DS.W 0
	
	; The init table
	InitTable:
	   DC.L LIB_SIZEOF  ; size of library base data,
			    ; sizeof(struct Library)
	   DC.L funcTable   ; pointer function pointer
			    ; table below
	   DC.L dataTable   ; pointer to the library data
			    ; initializer table
	   DC.L initRoutine ; routine to run
	
	funcTable:
	
	   ;----- standard system routines
	   dc.l Open
	   dc.l Close
	   dc.l Expunge
	   dc.l Extfunc
	
	   ;----- our library functions
	   ;The function names get those `_' in the
	   ;beginning when compiling in C.
	   dc.l _Min
	   dc.l _Abs
	
	   ;----- function table end marker
	   dc.l -1
	
	
	; The data table initializers static data structs.
	dataTable:
	   INITBYTE	LN_TYPE,NT_LIBRARY
	   INITLONG	LN_NAME,LibName
	   INITBYTE	LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
	   INITWORD	LIB_VERSION,VERSION
	   INITWORD	LIB_REVISION,REVISION
	   INITLONG	LIB_IDSTRING,IDString
	   DC.L 0
	
	; The init routine.
	initRoutine:	; (segment list:a0)
	   move.l a5,-(sp)	 ; save a5
	   lea    seglist(pc),a5 ; get address of our
				 ; seglist storage
	   move.l a0,(a5)	 ; store segment list
				 ; pointer
	   move.l (sp)+,a5	 ; restore previous a5
	   move.l #0,d0		 ; return zero
	   rts
	
	seglist:
	   DC.L 0

	;---------------------------------------------
	; The four required functions:
	;---------------------------------------------
	
	Open:	; (libptr:A6, version:D0)
	
	  ; Increase the library's open counter
	  addq.w	#1,LIB_OPENCNT(a6)

	  ; Switch off delayed expunge
	  bclr	#LIBB_DELEXP,LIB_FLAGS(a6)

	  ; Return library base
	  move.l	a6,d0
	  rts
	
	Close:	; (libptr:a6)
	
	  ; set the return value
	  moveq	#0,d0

	  ; Decrease the library's open counter
	  subq.w	#1,LIB_OPENCNT(a6)

	  ; If there is anyone still open, return
	  bne.s	retlabel

	  ; Is there a delayed expunge waiting?
	  btst	#LIBB_DELEXP,LIB_FLAGS(a6)
	  beq.s	retlabel

	  ; Do the expunge!
	  bsr	Expunge

	  retlabel:
	  rts
	
	Expunge:	; (libptr:a6)
	
	  ; Is the library still open?
	  tst.w	LIB_OPENCNT(a6)
	  beq	notopen

	  ; It is still open. set the delayed expunge flag
	  ; and return zero
	  bset	#LIBB_DELEXP,LIB_FLAGS(a6)
	  moveq	#0,d0
	  rts		; return

	  notopen: ; Get rid of us!

	  movem.l	d2/a5/a6,-(sp) ; save some registers
	  move.l	a6,a5

	  ; Store our segment list in d2
	  lea	seglist(pc),a6
	  move.l	(a6),d2

	  move.l	4,a6	; get SysBase

	  ; Unlink from library list
	  move.l	a5,a1
	  jsr	_LVORemove(a6) ; This removes our node
			 ; from the list

	  ; Free our memory
	  moveq	 #0,d0
	  move.l	a5,a1
	  move.w	LIB_NEGSIZE(a5),d0

	  sub.l	d0,a1
	  add.w	LIB_POSSIZE(a5),d0

	  jsr	_LVOFreeMem(a6) ; This frees the memory
	  ; we occupied

	  ; Return the segment list
	  move.l	d2,d0

	  movem.l	(sp)+,d2/a5/a6 ; Get back the registers
	  rts
	
	Extfunc:	; should return zero
	   moveq	#0,d0
	   rts
	
	   ; EndCode is a marker that show the end of our
	   ; code.
	EndCode:
	   END


	myown.h
	=======

	/***********************************************
	*
	* myown.library header file
	*
	************************************************
	* Author: Daniel Stenberg
	***********************************************/

	/* Library function prototypes */

	int Min(int, int); /* return minimum value */
	int Abs(int);      /* return absolute value */


	myown.c
	=======

	/***********************************************
	*
	* myown.library functions source code
	*
	************************************************
	* Author: Daniel Stenberg
	***********************************************/

	/*
	 * Use the __asm, __register and the __XX registers to
	 * force parameters into certain registers in SAS/C,

	 *
	 * Use __XX registers to force register parameters using DICE.
	 */
	int __asm Min(register __d0 int a,
		      register __d1 int b)
	{
	  int c = a < b ? a : b;

	  return (c);
	}

	int __asm Abs(register __d0 int a)
	{
	  int c = a < 0 ? -a : a;

	  return (c);
	}

	myown.fd
	========

	##base _MyBase
	##bias 30
	Min(a)(D0D1)
	Abs(a)(D0)
	##end


	myown_pragmas.h
	===============

	/* SAS/C pragmas */

	#pragma libcall MyBase Min 1E 1002 /* d0 and d1 */
	#pragma libcall MyBase Abs 24 001  /* only d0 */


	uselibrary.c
	============

	#include "myown_pragmas.h" /* if using SAS/C or Aztec C */
	#include "myown.h"
	struct Library *MyBase=NULL;

	void main(void)
	{
	  int min, abs;
	  MyBase=OpenLibrary("myown.library", 2);

	  if(MyBase) {
	    min = Min( 3, 2 ); /* library Min() function */
	    abs = Abs( -12 );  /* library Abs() function */

	    CloseLibrary( MyBase );
	  } else
	    printf("Couldn't open myown.library!\n");
	}

