Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!utgpu!water!watmath!clyde!rutgers!ames!amdcad!decwrl!sqm.dec.com!jmsynge From: jmsynge@sqm.dec.com.UUCP Newsgroups: comp.sys.amiga Subject: A shared library (mostly) in C Message-ID: <10090@decwrl.DEC.COM> Date: Fri, 29-May-87 03:43:28 EDT Article-I.D.: decwrl.10090 Posted: Fri May 29 03:43:28 1987 Date-Received: Sat, 30-May-87 10:21:31 EDT Sender: daemon@decwrl.DEC.COM Organization: Digital Equipment Corporation Lines: 1174 Here is the source for a shared library which is written in C with a little bit of assembly glue. It is very heavily commented, so it should be easy for others to produce shared libraries by simply editing this version. The library, task.library, contains versions of CreateTask and DeleteTask. I developed this library after having examined every example that Amiga produced and reading and re-reading the RKM. I have copyrighted this code because it is not simply a paraphrase of previous works, nor a simple rearrangement. It contains substantial addtions which significantly improve the library. I'm not a lawyer, so I don't know all the regulations about copyrights, but I hope I've stated the case correctly. This code may be redistributed for non-commercial purposes. If you wish to sell this library for a profit, contact me. My purpose is to spread the knowledge of how shared libraries work and how to build them. Have fun gang! James Synge USENET: {decvax, ucbvax, allegra}!decwrl!sqm.dec.com!jmsynge ARPAnet: jmsynge%sqm.DEC@decwrl.DEC.COM USmail: 12 Newcastle Dr, #12, Nashua, NH, 03060 #include "Ken Olsen can speak for Digital, not me!" # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # LibHead.asm # Library.c # ExtLibrary.h # Task_Routines.c # FunctionList.c # Interface.asm # Protect.i # 2Tasks.c # task_lib.asm # Makefile # This archive created: Wed May 27 23:46:09 1987 echo shar: extracting LibHead.asm cat << \SHAR_EOF > LibHead.asm ;; LibHead.asm ;; Copyright 1986, James M Synge ;; ;; This file contains the data structures and an ;; initialization routine. ;; far code ; Absolute addresses please. far data ; Me too! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; To protect against the library accidentally being invoked ;; as a program, we return an error value. This code must ;; be the first thing in the library. cseg ; Code segment moveq.l #20,d0 ; AmigaDOS Fatal Error rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Declaration Section: ;; ;; Imported Global Variables and Constants: dseg ; Data segment public _LibraryName ; char * LibraryName public _LibraryId ; char * LibraryId public LibVersion ; Current version number. ; Will be checked by ; OpenLibrary() against the ; requested version number. public _LibInitBlock ; Defined in Library.c public _SysBase ; Library Base Pointer for ; exec.library ;; Exported Global Variables: global _LibraryBase,4 ; Address of this library. global _LibSegList,4 ; Address of first entry in ; this library's seg list. ;; Imported Functions: cseg public _LibraryInit ; This is the routine which ; handles most of the ; details of preparing the ; library, except for regs. ;; Exported Functions: public __LibInitCode ; Referenced by LibInitBlock ; in Library.c public _regA6 ; Returns register A6. ;; Constants: ;; (These should be set in the Amiga supplied include files ;; but these aren't available for Manx 3.2a) RTC_MATCHWORD EQU $4afc ; An illegal instruction RTF_AUTOINIT EQU (1 << 7) NT_LIBRARY EQU 9 ; As defined in exec/nodes.i ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Resident (RomTag) Structure ;; This structure must be in the first hunk (AmigaDOS Hunk) ;; of the library. Defined in "exec/resident.h" RomTag: DC.W RTC_MATCHWORD ; This value is an illegal ; instruction. It is used ; to mark the beginning of a ; rom tag. The next ; longword confirms it. DC.L RomTag ; The RomTag points to ; itself as confirmation ; that this is a RomTag. DC.L EndMarker ; The address of a location ; after this structure, but ; still in the same hunk. ; The simplest solution is ; to place the label right ; after this structure. DC.B RTF_AUTOINIT ; Auto-initialize flag, see ; Library.c for description DC.B LibVersion ; Library version, will be ; checked against the value ; requested in the ; OpenLibrary() call. DC.B NT_LIBRARY ; This is the Resident ; struct of a library. DC.B 0 ; Execution priority. ; Not used in libraries. DC.L _LibraryName ; Pointer to library name. DC.L _LibraryId ; Pointer to full library id DC.L _LibInitBlock ; Pointer to initialization ; descriptor block or code. ; See Library.c ;; End of the RomTag EndMarker ; Simplest place for this marker ; See Interface.asm for a complete explanation of: AztecBugList: reg a4/a6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; LibInitCode, is called from within MakeLibrary(), after ;; all the memory has been allocated, the jump table has ;; been constructed, and the InitStruct() call has been ;; made. It is identified as the initialization code by ;; LibInitBlock in Library.c. LibraryInit() returns the ;; value (in D0 as per the calling standard) which is then ;; returned to MakeLibrary(). If it is zero, the library ;; wouldn't be added to the exec's library list and thus ;; cann't be opened. __LibInitCode: move.l a6,_SysBase ; Store away these three ; registers which tell us move.l d0,_LibraryBase ; we are and where the rest move.l a0,_LibSegList ; of the world can be found. ; Forgive me, Oh Lord, for I have sinned... movem.l AztecBugList,-(sp) ; Now call the C routine LibraryInit() with the ; address of our Library struct as its only argument. move.l d0,-(sp) ; Push LibraryBase jsr _LibraryInit ; Call addq.l #4,sp ; Pop LibraryBase ; Restore the registers movem.l (sp)+,AztecBugList rts ; Now return to our caller. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This trite little routine is included so that those ;; library specific routines which wish to access the struct ;; Library may do so by calling regA6() in order to get the ;; value of the Library Base Pointer. ;; Example Code Fragment: ;; ;; struct Library *lib, *regA6(); ;; lib = regA6(); ;; return(lib -> lib_IdString); ;; ;; An alternate (and perhaps safer method) is to use the ;; _LibraryBase global variable which is setup by the ;; __LibInitCode routine. public _regA6 _regA6: move.l a6,d0 rts ds.w 0 ; Align to a word boundary END SHAR_EOF if test 4746 -ne "`wc -c LibHead.asm`" then echo shar: error transmitting LibHead.asm '(should have been 4746 characters)' fi echo shar: extracting Library.c cat << \SHAR_EOF > Library.c /* 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); } SHAR_EOF if test 7036 -ne "`wc -c Library.c`" then echo shar: error transmitting Library.c '(should have been 7036 characters)' fi echo shar: extracting ExtLibrary.h cat << \SHAR_EOF > ExtLibrary.h /* library.h James M Synge 16-Apr-1987 */ /* This struct definition is designed to enable easy * extention of the Library structure with a particular * library's variables. */ #ifndef EXEC_TYPES_H #include "exec/types.h" #endif #ifndef EXEC_NODES_H #include "exec/nodes.h" #endif #ifndef EXEC_LIBRARIES_H #include "exec/libraries.h" #endif struct ExtLibrary { /* First the standard Library */ struct Library el_Lib; /* And now any library specific variables */ /* Example: These show the form of the names * such variables might have. */ /* long el_var1; */ /* long el_var2; */ /* long el_var3; */ }; /* These accessor #define's are simple conveniences. */ #define el_Node el_Lib.lib_Node #define el_Flags el_Lib.lib_Flags #define el_pad el_Lib.lib_pad #define el_NegSize el_Lib.lib_NegSize #define el_PosSize el_Lib.lib_PosSize #define el_Version el_Lib.lib_Version #define el_Revision el_Lib.lib_Revision #define el_IdString el_Lib.lib_IdString #define el_Sum el_Lib.lib_Sum #define el_OpenCnt el_Lib.lib_OpenCnt SHAR_EOF if test 1038 -ne "`wc -c ExtLibrary.h`" then echo shar: error transmitting ExtLibrary.h '(should have been 1038 characters)' fi echo shar: extracting Task_Routines.c cat << \SHAR_EOF > Task_Routines.c /* Task_Routines.c Copyright 1987 by James M Synge */ /* This file contains CreateTask() and DeleteTask(), * versions of the routines of the same names in the ROM * Kernel Manual. */ #include "exec/types.h" #include "exec/ports.h" #include "exec/tasks.h" #include "exec/memory.h" struct Task * CreateTask(Task_Name, Priority, Startup_Routine, Stack_Size) char *Task_Name; int Priority; void (* Startup_Routine) (); int Stack_Size; { /* A pointer to the child task's task structure. */ register struct Task *Child; register APTR Child_Stack; /* A pointer to it's stack */ register APTR AllocMem(); /* First allocate a stack for the task. */ Child_Stack = AllocMem(Stack_Size, MEMF_CLEAR); if (Child_Stack == 0) { /* Couldn't allocate a stack! And we cann't print an * error message because this routine could be called * by a task or a process which doesn't have a window. */ return(NULL); }; /* Now allocate a Task structure. */ Child = (struct Task *) AllocMem(sizeof(struct Task), MEMF_CLEAR | MEMF_PUBLIC); if (Child == NULL) { FreeMem(Child_Stack, Stack_Size); return(NULL); }; /* Now initialize the task structure as per the RKM. */ Child->tc_SPLower = Child_Stack; Child->tc_SPReg = Child->tc_SPUpper = (APTR)( (ULONG)Child_Stack + (ULONG)Stack_Size); Child->tc_Node.ln_Type = NT_TASK; Child->tc_Node.ln_Pri = Priority; Child->tc_Node.ln_Name = Task_Name; AddTask(Child, Startup_Routine, 0L); return(Child); } /* DeleteTask is written in a style which will allow it to * be called by any task, including the task being deleted. * Note that it can not delete an AmigaDOS Process. */ void DeleteTask(Child) register struct Task *Child; { register ULONG Stack_Size; if (Child == NULL) return; /* Free up the stack and the Task structure. This is * done inside a Forbid() / Permit() so that no other * task can do the same. */ Forbid(); if (Child->tc_Node.ln_Type != NT_TASK) { /* OOPS! It ain't a Task! */ Permit(); return; } Stack_Size = (ULONG)(Child->tc_SPUpper) - (ULONG)(Child->tc_SPLower); FreeMem(Child->tc_SPLower, Stack_Size); FreeMem(Child, sizeof(struct Task)); /* Now remove the task. This will not return if it is * removing the current task. */ RemTask( Child ); /* If we removed another task, then we continue on: * so call Permit() and return. */ Permit(); return; } SHAR_EOF if test 2545 -ne "`wc -c Task_Routines.c`" then echo shar: error transmitting Task_Routines.c '(should have been 2545 characters)' fi echo shar: extracting FunctionList.c cat << \SHAR_EOF > FunctionList.c /* FunctionList.c James M Synge 16-Apr-1987 */ /* This file contains the function table whose address is * passed to MakeLibrary. This table is used to create the * jump table which will immediately proceed the library * structure in memory. Notice that the first three entries * are the assembly language interludes in the file * LibHead.ASM. */ /* Required routines: */ long _Library_Open(); /* Called by OpenLibrary(). */ long _Library_Close(); /* Called by CloseLibrary(). */ long _Library_Expunge();/* Called by memory allocator. */ long Library_Reserved(); /* Must return 0L. */ /* Public, library specific routines: */ long _CreateTask(); long _DeleteTask(); long (* FunctionList[])() = { _Library_Open, _Library_Close, _Library_Expunge, Library_Reserved, _CreateTask, _DeleteTask, -1L /* Marks the end of the list. */ }; SHAR_EOF if test 861 -ne "`wc -c FunctionList.c`" then echo shar: error transmitting FunctionList.c '(should have been 861 characters)' fi echo shar: extracting Interface.asm cat << \SHAR_EOF > Interface.asm ;; Interface.ASM ;; Copyright 1986, James M Synge ;; ;; This file contains the assembly language interfaces which ;; allow the C routines to be called from any language which ;; can push arguments on to the stack. far code far data ;; Imported Functions: public _Library_Open public _Library_Close public _Library_Expunge public _CreateTask public _DeleteTask ;; Exported Functions: public __Library_Open public __Library_Close public __Library_Expunge public __CreateTask public __DeleteTask ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Due to the fact that the Aztec C68K compiler does not use ;; the same register allocation as the rest of the system, ;; it tromps on some of the registers which it is supposed ;; to preserve. It is designed to be the top level, making ;; calls to the rest of the system and NOT being called by ;; the OS. ;; ;; Using version 3.4a of the compiler, I find it necessary ;; to save A6. Version 3.2a also stepped on D2 and D3. I ;; have not yet seen any use of register A4, even though I'm ;; using the +R option on the CC command line. Nonetheless, ;; I'll be cautious, and save A4. ;; ;; So? So I've written an interlude for each of the ;; routines which the OS will be calling. These interludes ;; know where the arguments are in the registers, and push ;; them onto the stack where they are useful. ;; ;; And after these interludes are the library specific ;; interlude routines. These are designed to have their ;; arguments on the stack, as if called by a C routine. AztecBugList: reg a4/a6 AztecBugSize: equ 2 ; 2 registers ; AztecBugList_3_2a: reg d2/d3/a4/a6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; These are the interlude routines for Library_Open, ;; Library_Close and Library_Expunge. They enable these C ;; routines to receive the arguments which are in registers. __Library_Open: movem.l AztecBugList,-(sp) move.l d0,-(sp) ; Push the version move.l a6,-(sp) ; and the lib base jsr _Library_Open addq.l #8,sp ; Pop them movem.l (sp)+,AztecBugList rts __Library_Close: movem.l AztecBugList,-(sp) move.l a6,-(sp) ; Push lib base jsr _Library_Close addq.l #4,sp ; Pop it movem.l (sp)+,AztecBugList rts __Library_Expunge: movem.l AztecBugList,-(sp) move.l a6,-(sp) ; Push lib base jsr _Library_Expunge addq.l #4,sp ; Pop it movem.l (sp)+,AztecBugList rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Library specific interface routines. These first push ;; certain registers to the stack to protect them, then push ;; the arguments from earlier on the stack. The only reason ;; this is done is so that we can protect two of the ;; registers. Otherwise the need for these silly routines ;; would totally disappear. ;; ;; In order to make it easy for those not familiar with ;; 68000 assembly language to add their own routines to a ;; library, and the corresponding interface routines, I've ;; written a macro which can be invoked to generate all the ;; code to protect the registers and move the arguments. ;; The macro, protect, is defined in the file Protect.i, but ;; I don't recommend reading it until after you get a good ;; feeling for the way macro's and assembly work. ;; ;; It is very easy to use protect. The syntax should be: ;; ;; label PROTECT.L address,n ;; ;; where label is the name of the routine you are creating, ;; for example __CreateTask below; address is the name of a ;; C routine (with the underscore prepended), just like ;; _CreateTask below; and finally n is the number of ;; longword arguments the routine takes. No support is ;; supplied for variable numbers of arguments. Nor support ;; for arguments which are not longwords. include protect.i ; CreateTask(Task_Name, Startup_Routine, Cleanup_Routine, ; Priority, Stack_Size) __CreateTask protect.l _CreateTask,5 ; DeleteTask( Child ) __DeleteTask: protect.l _DeleteTask,1 ; Now that was pretty painless, wasn't it. Macros are great ds.w 0 END SHAR_EOF if test 4032 -ne "`wc -c Interface.asm`" then echo shar: error transmitting Interface.asm '(should have been 4032 characters)' fi echo shar: extracting Protect.i cat << \SHAR_EOF > Protect.i ;; Protect.i Copyright 1987 by James M Synge ;; ;; An assembly macro for protecting registers. ;; Not for the weak of heart. macro protect if NARG <> 2 fail ; Must specify the number of args! mexit endc iflt \2 fail ; Negative number of args! mexit endc ; Until a bug fix arrives for the assembler so that the \0 ; macro works, we must assume the argument size is 4 bytes. \@elementsize set 4 \@bytes set \@elementsize * \2 \@offset set \@bytes + (AztecBugSize * 4) movem.l AztecBugList,-(sp) ; Save some regs ; Push the argument(s). We'll use D0 as our scratch ; register since it is free for that use according ; to the register convention. ifne \2 ; If there are any arguments moveq #\2,d0 ; Number of arguments to move \@loop move.l \@offset(sp),-(sp) ; Push an arg dbeq d0,\@loop ; Loop again? endc jsr \1 ; Call the C routine ifne \2 ; Pop any arguments ifge 8 - \@bytes ; Can we use addq.l? addq.l #\@bytes,sp ; Yup! else lea \@bytes(sp),sp ; Otherwise use lea endc endc movem.l (sp)+,AztecBugList ; Restore regs rts ; And return endm ; End of the protect macro SHAR_EOF if test 1120 -ne "`wc -c Protect.i`" then echo shar: error transmitting Protect.i '(should have been 1120 characters)' fi echo shar: extracting 2Tasks.c cat << \SHAR_EOF > 2Tasks.c /* 2Tasks.C James M Synge, May 18, 1987 */ /* Include files */ #include "exec/types.h" #include "exec/nodes.h" #include "exec/lists.h" #include "exec/tasks.h" #include "exec/libraries.h" #include "exec/ports.h" /* Other declarations */ struct Library *TaskBase, /* Library Base Pointer. */ *OpenLibrary(); struct MsgPort *CreatePort(), *FindPort(); struct Message *WaitPort(), *GetMsg(); #define TASK_LIBRARY "task.library" #define TASK_VERSION 1L #define CHILD_PORT "2Tasks.Child.Port" #define CHILD_TASK "2Tasks.Child.Name" main() { struct Message message, *msg; struct MsgPort *ParentPort, *ChildPort; struct Task *ChildTask, *CreateTask(); void ChildMain(); int seconds; /* First things first: Open the task library: */ printf("Openning %s\n", TASK_LIBRARY); TaskBase = OpenLibrary(TASK_LIBRARY, TASK_VERSION); if (TaskBase == 0L) { printf("Unable to open %s\n", TASK_LIBRARY); exit(10); } /* Create a nameless MsgPort where we can receive * the reply to a message. */ if ((ParentPort = CreatePort( 0L, 0L )) == 0L) { printf("Unable to create a MsgPort!\n"); CloseLibrary( TaskBase ); exit(10); } /* Now create the child task. */ printf("Creating the child task.\n"); ChildTask = CreateTask( CHILD_TASK, /* Name of the task. */ 1L, /* Higher priority. */ ChildMain, /* Its main routine. */ 4096L); /* Stack Size. */ if (ChildTask == 0L) { printf("Unable to create child task!\n"); DeletePort( ParentPort ); CloseLibrary( TaskBase ); exit(10); } /* Find the child's message port. */ for(seconds = 0; seconds < 60; seconds++) { printf("Find the child's MsgPort\n"); if (ChildPort = FindPort( CHILD_PORT )) break; Delay(50); /* Wait a second! */ } if (ChildPort == 0L) { printf("Unable to find child MsgPort!\n"); DeleteTask( ChildTask ); DeletePort( ParentPort ); CloseLibrary( TaskBase ); exit(10); } /* Send the child a message. */ message.mn_Node.ln_Type = NT_MESSAGE; message.mn_ReplyPort = ParentPort; message.mn_Length = 0; printf("Sending the message.\n"); PutMsg( ChildPort, &message ); /* Wait for the child to respond. */ WaitPort( ParentPort ); /* Get the message. */ msg = GetMsg( ParentPort ); printf("Got the reply. All done.\n"); /* And now delete the MsgPort we used. */ DeletePort( ParentPort ); /* Finally, close the library. */ CloseLibrary( TaskBase ); exit(0); } /* Notice that the child doesn't open any libraries, * including exec.library and Task.Library whose * routines it uses. It does this because it will * operate solely during the life of the parent, when we * know the libraries will be open. This is not kosher in * general, but Commodore has produced examples doing this, * and I know there aren't any problems in this case. * * It's also particularly difficult to close a library * after using DeleteTask() on yourself! */ void ChildMain() { struct Message *msg; struct MsgPort *ChildPort; /* To allow this to be a small code/small data model * task, we must make sure register A4 contains the * correct value. We do so by calling the Aztec C * routine geta4() which computes the value. */ geta4(); /* Create a MsgPort where we can receive a message * from the parent task. */ if ((ChildPort = CreatePort( CHILD_PORT, 0L )) == 0) DeleteTask( FindTask( 0L )); /* Now wait for the message. */ WaitPort( ChildPort ); /* Fetch it from the port... */ msg = GetMsg( ChildPort ); /* ... and reply to it. Do so inside a Forbid() / * Permit() pair so there'll be time to delete the * message port. Note that the Permit() call is * not included because it will never be called; * instead, DeleteTask() is called so that we * delete the current task: ourselves! */ Forbid(); ReplyMsg( msg ); DeletePort( ChildPort ); /* Now take the leap of death. */ DeleteTask( FindTask( 0L )); /* That's all she wrote! */ } SHAR_EOF if test 3939 -ne "`wc -c 2Tasks.c`" then echo shar: error transmitting 2Tasks.c '(should have been 3939 characters)' fi echo shar: extracting task_lib.asm cat << \SHAR_EOF > task_lib.asm ; task_lib.asm James M Synge 25-May-1987 ; ; Note that these glue routines do not save register A6. ; This is because they are designed for use with Aztec C ; which does not seem to expect A6 to remain unchanged ; during a subroutine call. dseg ; TaskBase = OpenLibrary("Task.Library",0); public _TaskBase cseg public _CreateTask _CreateTask move.l _TaskBase,a6 jmp _LVOCreateTask(a6) public _DeleteTask _DeleteTask move.l _TaskBase,a6 jmp _LVODeleteTask(a6) _LVODeleteTask equ -36 _LVOCreateTask equ -30 end SHAR_EOF if test 526 -ne "`wc -c task_lib.asm`" then echo shar: error transmitting task_lib.asm '(should have been 526 characters)' fi echo shar: extracting Makefile cat << \SHAR_EOF > Makefile # makefile James M Synge 26-May-1987 # # Used to build the shared library (task.library) and the # interface library (task.lib). Note that the shared # library is like a complete program in that it is linked, # while the interface library is just an object file which # can be included on some other programs linkage line. # # Example: ln prog.o -ltask -lc # # This example places task.lib earlier in the link than # c.lib. Therefore, any routine in task.lib of the same # name as a routine in c.lib will be loaded instead of the # routine in c.lib. This is useful because it allows us to # replace any routine in c.lib. # # Note that these FLAGS macros are only used for making the # shared library, not the test program nor the interface # library. CFLAGS = +BCDLP AFLAGS = -CD LIBOBJ = LibHead.o Library.o Task_Routines.o \ FunctionList.o Interface.o ALL = LibHead.asm Library.c ExtLibrary.h Task_Routines.c FunctionList.c \ Interface.asm Protect.i 2tasks.c task_lib.asm Makefile all: task.library task.lib 2Tasks task.library : $(LIBOBJ) ln -o task.library $(LIBOBJ) -lcl32 # You may wish to add a command like this: # copy task.library LIBS: LibHead.o : LibHead.asm Interface.o : Interface.asm Protect.i Library.o : ExtLibrary.h task.lib : task_lib.asm as $(AFLAGS) -o task.lib task_lib.asm # You may wish to add a command which moves the file # to the directory where you keep c.lib, etc. 2Tasks : 2Tasks.o task.lib ln 2Tasks.o -ltask -lc 2Tasks.o : 2Tasks.c cc 2Tasks.c ram:sharfile : $(ALL) shar > ram:sharfile -vc $(ALL) SHAR_EOF if test 1553 -ne "`wc -c Makefile`" then echo shar: error transmitting Makefile '(should have been 1553 characters)' fi # End of shell archive exit 0