[comp.sys.amiga] Working example of an Amiga library for Aztec C

dillon@CORY.BERKELEY.EDU (Matt Dillon) (10/04/87)

	Now could somebody post a completely working Exec Device Driver
	and DOS Device Driver?

					-Matt

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	Makefile
#	README
#	hlib.asm
#	test.c
#	testlib.c
# This archive created: Sat Oct  3 21:01:34 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'Makefile'" '(356 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \!Funky!Stuff! > 'Makefile'




all: lib test

lib:
    cc +BCDL +p +Ivd0:include/symbols.m testlib.c -o ram:testlib.o
    ln +Q ram:testlib.o -lcl32
    copy ram:testlib libs:testlib.library
    delete ram:testlib ram:testlib.o

test:
    cc +L +Ivd0:include/symbols.m test.c -o ram:test.o
    as hlib.asm -o ram:hlib.o
    ln +Q ram:test.o ram:hlib.o -lc32
    delete ram:test.o



!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'README'" '(2508 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \!Funky!Stuff! > 'README'


	Working Library Skeleton for AZTEC C.

	By Matthew Dillon

	Placed in Public Domain


	This is an example library for Aztec C.  It will not work with
Lattice C due to extensive Manxisms.   This library does not use the 
auto-init method and thus provides a contrast with the RKM example.  Note
especially the compile time options required (in the Makefile).  Here are
the reasons:

	+B	No reference to startup code.

	+CD     Large code and data model.  Alternately you can setup A4
		on every entry point and use the small code and data model.

	+L      32 bit integers.  My preference.. not required when 
		designing your own libraries, of course.

	+p	compatibility option... Aztec automagically saves D2 and D3
		on function calls.  Otherwise we would have to do it by hand
		on every entry point.


LINK LIBRARY:

	The routines in this example expect arguments on the stack.   You 
	will note the link library is incredibly simple ... the assembly 
	to make a library call is only two instructions.  Note that since the
	arguments are expected on the stack, the link library routines cannot
	save anything on the stack.  A0-A1 D0-D1 are scratch however and I
	simply use A0.

	Thus this library is optimized for C->C calls.  You want your library
	to follow the AMIGA STANDARD, which is that only A0-A1 D0-D1 may be
	trashed.  The only register Aztec C calls will not automatically
	save is A6 and this must be done WITHIN your actual library routines
	(see testlib.c).

REMEMBER:

	Remember that you are running in the context of the calling task
	inside a library call, and that the DOS library is not automatically
	openned for you as it is in C programs.  Remember that if you use
	other libraries you must open them yourself, setting the proper
	global variable xxxxBase so the linked-in-library your run-time
	library uses, if any, knows what to do.  E.G. the first thing my
	library does in the Init routine is to setup SysBase.

FILES:

	Makefile	

		For making the library itself.  I make reference to a
		precompiled symbol table which is all the AMIGA includes
		(none of the Aztec includes) */*.h . You will have to either
		generate such a symbol table or simply figure out which
		#include's you need to make.

	hlib.asm

		Example 'link' library... what you need to link with your
		C programs to be able to call library functions after openning
		the library.

	testlib.c

		The source to the library itself.

	test.c

		The source to the test program.  Must be linked with hlib.o


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'hlib.asm'" '(216 characters)'
if test -f 'hlib.asm'
then
	echo shar: "will not over-write existing file 'hlib.asm'"
else
cat << \!Funky!Stuff! > 'hlib.asm'


	    ;	TestBase

	    FAR code
	    FAR data

	    public  _TestBase
	    public  _Sub
	    public  _Add

_Sub:	    move.l  _TestBase,A0
	    jmp     -36(A0)

_Add:	    move.l  _TestBase,A0
	    jmp     -30(A0)



!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'test.c'" '(329 characters)'
if test -f 'test.c'
then
	echo shar: "will not over-write existing file 'test.c'"
else
cat << \!Funky!Stuff! > 'test.c'

long TestBase;

extern long OpenLibrary();
extern long Add(), Sub();

main()
{
    TestBase = OpenLibrary("testlib.library",0);
    if (TestBase) {
	printf("Open OK %08xl\n", TestBase);
	printf("ADD 1 -> %ld (should be 2)\n", Add(1L));
	CloseLibrary(TestBase);
	puts("Close OK");
    } else {
	puts("Unable to Open");
    }
}


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'testlib.c'" '(4850 characters)'
if test -f 'testlib.c'
then
	echo shar: "will not over-write existing file 'testlib.c'"
else
cat << \!Funky!Stuff! > 'testlib.c'

/*
 *  TESTLIB.C
 *
 *  Compile with +BCDL
 *	    +B	No startup reference
 *	    +C	Large code
 *	    +D	Large data
 *	    +L	32 bit Integers
 */

#asm
	;   RLIB.ASM
	;
	;   Run-time library tag

	    FAR     code
	    FAR     data

	    public  _CInit
	    public  _LibOpen
	    public  _LibClose
	    public  _LibExpunge

Start:	    clr.l   D0
	    rts

InitDesc:   dc.w    $4AFC	;RTC_MATCHWORD
	    dc.l    InitDesc	;Pointer to beginning
	    dc.l    EndCode	;Note sure it matters 
	    dc.b    0		;flags (NO RTF_AUTOINIT)
	    dc.b    0		;version
	    dc.b    9		;NT_LIBRARY
	    dc.b    0		;priority (doesn't matter)
	    dc.l    _Libname	;Name of library
	    dc.l    _Libid	;ID string (note CR-LF at end)
	    dc.l    Init	;Pointer to init routine

_Libname:   dc.b    "testlib.library",0
_Libid:     dc.b    "testlib.library 1.0 (02 Oct 1987)",13,10,0
EndCode:

Init:	    move.l  A6,-(sp)	;Must save A6
	    move.l  A0,-(sp)	;Segment list
	    jsr     _CInit
	    addq.l  #4,sp
	    move.l  (sp)+,A6
	    rts

cifc	    macro		
	    move.l  D0,-(sp)	;Make a C call and save A6 to boot
	    move.l  A6,-(sp)
	    jsr     \1
	    move.l  (sp)+,A6
	    addq.l  #4,sp
	    rts
	    endm

_ALibOpen:	cifc	_LibOpen
_ALibClose:	cifc	_LibClose
_ALibExpunge:	cifc	_LibExpunge

#endasm

extern char Libname[1];
extern char Libid[1];

extern long ALibExpunge(),ALibClose(),ALibOpen(),ALibReserved();
extern long Lib_Add(), Lib_Sub();

/*
 *	Function Entries consist of a JMP instruction followed by an
 *	absolute vector.   I *do* admit to cheating here... there is
 *	an Exec call to integrate a library with a function list.  I
 *	do it all with a single static declaration.  In fact, the proper
 *	way to do it would be with MakeLibrary() especially considering
 * 	that C-A has reserved one of the library vectors.
 */

struct FuncList {
    UWORD jmp;
    long (*func)();
};

typedef struct Library LIB;
typedef struct FuncList FLIST;

struct {
    FLIST Funcs[6];
    LIB Lib;
} WLib = {
    0x4EF9,  Lib_Sub,
    0x4EF9,  Lib_Add,
    0x4EF9,  NULL	,	/*	reserved by C-A?	*/
    0x4EF9,  ALibExpunge,
    0x4EF9,  ALibClose	,
    0x4EF9,  ALibOpen	,
    {
	{ NULL,NULL,NT_LIBRARY,0,Libname },
	LIBF_CHANGED|LIBF_SUMUSED,0,sizeof(WLib.Funcs),sizeof(WLib.Lib),
	0,0,(APTR)Libid,0,0
    }
};

long Seglist;			/*	Save the DOS seglist	*/

/*
 *    The Initialization routine is given only a seglist pointer.  Since
 *    we are NOT AUTOINIT we must construct and add the library ourselves
 *    and return either NULL or the library pointer.  Exec has Forbid()
 *    for us during the call.
 */

LIB *
CInit(segment)
{
    extern long SysBase;
    SysBase = *(long *)4;
    Seglist = segment;
    AddLibrary(&WLib.Lib);
    return(&WLib.Lib);
}

/*
 *    Open is given the library pointer and the version request.  Either
 *    return the library pointer or NULL.  Remove the DELAYED-EXPUNGE flag.
 *    Exec has Forbid() for us during the call.
 */

LIB *
LibOpen(lib,version)
LIB *lib;
{
    ++lib->lib_OpenCnt;
    lib->lib_Flags &= ~LIBF_DELEXP;
    return(lib);
}

/*
 *    Close is given the library pointer and the version request.  Be sure
 *    not to decrement the open count if already zero.  If the open count
 *    is or becomes zero AND there is a LIBF_DELEXP, we expunge the library
 *    and return the seglist.  Otherwise we return NULL.
 *
 *    Exec has Forbid() for us during the call.
 */

LibClose(lib)
LIB *lib;
{
    if (lib->lib_OpenCnt) {
	--lib->lib_OpenCnt;
	return(NULL);
    }
    if (lib->lib_Flags & LIBF_DELEXP)
	return(LibExpunge(lib));
    return(NULL);
}

/*
 *    We expunge the library and return the Seglist ONLY if the open count
 *    is zero.  If the open count is not zero we set the DELAYED-EXPUNGE
 *    flag and return NULL.
 *
 *    Exec has Forbid() for us during the call.  NOTE ALSO that Expunge
 *    might be called from the memory allocator and thus we CANNOT DO A
 *    Wait() or otherwise take a long time to complete (straight from RKM).
 */

LibExpunge(lib)
LIB *lib;
{
    if (lib->lib_OpenCnt) {
	lib->lib_Flags |= LIBF_DELEXP;
	return(NULL);
    }
    Remove(lib);
    return(Seglist);
}

/*
 *  The Library routines themselves.  Note that we must also save A6 if
 *  any library routine makes a library call to another library.  These
 *  examples do not and thus you can remove the #asm statements in them if
 *  you wish... just DON'T FORGET! 
 *
 *  The reason A6 must be saved is that Aztec library interface routines
 *  (e.g. FindTask(), OpenWindow(), etc....) trash A6 on purpose to make
 *  the interface routine faster.
 */

Lib_Add(n)
{
#asm
    move.l A6,-(sp)
#endasm

    ++n; /* do stuff here */

#asm
    move.l (sp)+,A6
#endasm
    return(n);
}

Lib_Sub(n)
{
#asm
    move.l A6,-(sp)
#endasm

    --n; /* do stuff here */

#asm
    move.l (sp)+,A6
#endasm
    return(n);
}


!Funky!Stuff!
fi  # end of overwriting check
exit 0
#	End of shell archive

bart@amiga.UUCP (Barry A. Whitebook) (10/07/87)

[eat this line -- please!]

this is amiga!bart.  thanks to matt dillon for posting an example library.
however, as he pointed out in his comments to the code, the system approved
way of creating a library to add to the system is with the MakeLibrary call.

why should YOU follow the convention of using the MakeLibrary call? well,
if you are ROMCODE (or want to look like romcode, or be romcode eventually)
the example as posted won't work.

why would you like to look like romcode? well, for one thing there will
eventually (real soon now) (actually a binary was posted recently to usenet)
be a way to protect your ram-based code from reboot so that YOUR library
is just like any other rom-based library as far as the system is concerned.

taking the extra time and exercising the appropriate brain cells NOW will save
you time later (i have got sample code to clean up and post... but currently
it is at a lower priority than my "system tasks")

bart
-- 
     //----------------------------------------------------- ----------\\
    //|  Bart Whitebook, Manager of Amiga ROM Software      | {|V|)))  |\\
      |  Comodore-Amiga, Inc.                               | ()^()-)))|
      |  16795 Lark Avenue, Suite #106, Los Gatos, CA 95030 |  /_   ?))|
      |  UUCP: pyramid!oliveb!amiga!bart                    | { _ } )\ |
      |  BIX:  amigabart                                    | \   //   | 
    \\|_____________________________________________________|__\//____ |//
     \\                                                                //