home *** CD-ROM | disk | FTP | other *** search
-
- Amiga shared libraries under SAS/C - the easy way
- =================================================
-
- Questions, comments, etc. can be directed to:
-
- David Jones
- 6730 Tooney Drive
- Orleans, Ont.
- K1C 6R4
- CANADA
-
- dej@qpoint.amiga.ocunix.on.ca
- David_Jones@p8,f109.n163.z1.fidonet.org
- David Jones @ Fidonet#1:163/109.8
-
-
- IMPORTANT NOTICE:
- ----------------
-
- The files in this archive are released under the terms and conditions
- of the Free Software Foundation's General Library Public License.
- Release 2 (June 1991) or any later version shall apply.
- This license is included in this archive under the name "gpll.doc".
- Read the terms and conditions of this license before using this archive!
-
- In particular, there is NO WARRANTY of any kind associated with this
- software.
-
-
- The problem:
- -----------
-
- You want to create a library that can be opened by OpenLibrary - an
- Amiga shared library. You've looked in the RKMs and you investigated
- the ability of the SAS compiler to make libraries. And you've pulled
- your hair out. And screamed. And...
-
- Stop. This is what you've been looking for. It's foolproof. The routines
- in this file have been designed to be easy to interface to. The library
- code isn't the most compact or the most efficient, but the process is
- PAINLESS.
-
- Read on and I guarantee you'll have a library easier than you've ever
- had it before. NO knowledge of assembly language is required, although
- it helps for debugging. If you have any problems, feel free to vent your
- frustrations to the mail addresses above.
-
- One final thing: ignore everything about library generation in the
- SAS manual unless I say otherwise. It's a jungle in there.
-
-
- Before you start:
- ----------------
-
- You will need the following in addition to the files in this archive:
-
- SAS/C compiler for AmigaDOS, Version 5.x
- A68K, by Charlie Gibbs
-
- I would have liked to use the SAS assembler, but it is my opinion that
- it is broken. So is A68K, but at least it's usable. The SAS assembler
- isn't.
-
-
- Some hints when writing your code:
- ---------------------------------
-
- Try to write the code as a linker library first. As long as it's
- incorporated into the body of programs that call it, CPR can list
- its source and you can easily debug it. Once it's resident in the
- Exec library list, it can be VERY difficult to do source level debugging.
- Get your functions working BEFORE making it into a shared library.
-
- Remember that you're not writing an application; you're writing a library.
- Don't call any of the SAS I/O or memory functions unless you know what
- you are doing. They depend on globals that are in the standard SAS/C
- startup code but are not in the library code, and for good reason.
- Libraries aren't processes. If the linker complains about globals that
- you've never heard of, then look for calls to SAS library functions.
-
- By the same token, be kind to the OS. Do not assume that you're a
- process free to call DOS functions unless you specifically want your
- library to be callable from processes. Do not call ANY functions that may
- directly or indirectly do a Wait() inside the init and expunge functions
- (discussed later).
-
-
- Now, to create the library:
- --------------------------
-
- The files in this archive constitute the support required to create
- shared libraries. Here is the contents of the archive:
-
- dejmac.i
- exec.offsets
- libhdr.a
- These files are assembler files that create the library interface
- code. You do not need to change any of these.
-
- libinf.a
- This is the file that you will need to change. It gives ALL of
- the information about the library. Although it's assembly, I
- will take you through it step by step.
- makefile
- This is the makefile for LMK. It gives the necessary compiler
- flags and link files.
-
- demo.fd
- demo.library
- demotest
- demotest.c
- demo_pragmas.h
- squareme.c
- These files form a demonstration library.
-
-
- Designing your library interface - register selection
- -----------------------------------------------------
-
- Assuming that you have the routines for your library already written
- (you DO have a linker version, don't you?), you will now have to choose
- 68000 microprocessor registers for the arguments in your functions.
- That's the way the Amiga libraries work - in registers.
-
- If you know 68000 assembler well, then go ahead and assign registers to
- your arguments. If you don't, read on.
-
- The 68000 has two sets of registers: address registers and data registers.
- The address registers are named A0 through A7 and the data registers are
- D0 to D7. As a general rule, data registers D0-D5 and address registers
- A0-A3 are good choices for library arguments. To assign registers to
- your functions, follow these rules.
-
- 1. Assign registers to your arguments starting from the lowest numbered
- registers and working up.
-
- 2. If the argument is a pointer to something, then use an address register.
- It is best not to use address registers for BPTRs.
-
- 3. If the argument is a byte, word or longword, then use a data register.
- Single-precision floating point numbers require one data register.
- Double-precision numbers cannot be passed directly, as far as I can
- determine. Pass them by reference (using pointers).
-
- 4. If you exceed the recommended register allocation (more than six
- data arguments and/or more than four pointer arguments) then maybe
- you should write less complex functions! You CAN use data registers
- D6 and D7 and you CAN use A4 and A5 if you are running SAS/C 5.10
- or better.
-
- 5. Be sure your compiler is passing what you think it's passing!
- Take arithmetic promotion into account.
-
-
- For example, let's say I have the function
-
- int foo(struct bar *a, int b, char *c, int e, int f)
-
- I would assign registers as follows:
-
- argument register
- ------------------
- a A0
- b D0
- c A1
- e D1
- f D2
-
-
- Preparing the prototype and .fd files
- -------------------------------------
-
- It is a good idea to prepare prototype files for your functions.
- Prototypes allow the compiler to do type checking for you. If you
- aren't using prototypes, try it. You'll probably like it.
-
- When it comes to libraries, you have to be careful with prototypes.
- There are two ways that you can call a function in your library
- from within your library: through the library register interface
- (through A6) and directly, as a normal C program would. It is best for
- you to call your function through the library.
-
- Note that you may have functions in your library code that are not
- directly callable from a client of your library. These functions can
- be prototyped in the normal way.
-
- For functions callable from outside the library, prototype them like
- you normally would. Do NOT put any register designations in the
- prototypes. For examples on how to do library prototypes, examine
- your include files from Commodore.
-
- The next file you will need to prepare is the .fd file. The .fd file
- is what the fd2pragma program uses to construct the pragmas that the
- SAS/C compiler uses to interface directly to the library. In addition,
- the .fd file is the Amiga standard now for defining library functions.
- If you include the .fd file in your library release, then users of any
- compiler system that reads .fd files can use your library. It's so
- easy to prepare a .fd file that I strongly recommend you do so.
-
- As an example of an .fd file, see "demo.fd". You can also use the
- .fd files that come on the 1.3 Extras disk if you have them.
-
- Any line in a .fd file that begins with '*' is a comment.
-
- The first line in the .fd file begins with '##base'. This line tells
- the system what the name of your library base variable is. The
- system libraries all have standard base variable names, such as SysBase,
- DOSBase, GfxBase, etc. Pick a name for your library's base and put
- it on this line. I have '_DemoBase'. Note that the base name should
- start with an underscore, as your C compiler prepends an underscore
- to all variable names.
-
- The next line in the file is '##bias 30'. Don't change this unless you
- know what you are doing.
-
- What follows is a list of the functions in the library in the format
-
- Name(arg,arg,arg)(reg/reg/reg)
-
- where:
-
- Name is the name of the function.
- arg is a name for the argument. This name is never used, it is
- only for documentation purposes.
- reg is the 68000 machine register that you assigned to the
- argument.
-
- Notes:
-
- The value a function returns (if any) is not given in the .fd file.
- Argument descriptors are separated with commas.
- Register names are separated with slashes or commas. To determine
- whether a slash or a comma is required, do the following:
-
- "Compare" the registers numerically. Any address register is
- "greater" than any data register. For example, D5 > D2, A3 > A1,
- and A0 > D7.
-
- If the register on the right is greater than the register on the
- left, then use a slash. Otherwise, use a comma. See the example
- below.
-
- Register names are in uppercase (A0, not a0).
- Don't use any spaces on the line.
-
- As an example, here's our foo function from above:
-
- foo(a,b,d,e,f)(A0,D0/A1,D1/D2)
-
-
- The last line in the .fd file should be '##end'.
-
- To test your .fd file, try using fd2pragma. Just give the command line:
-
- fd2pragma <fd file> <pragma file>
-
- If there's a serious problem with your .fd file, fd2pragma will tell
- you about it.
-
-
- Preparing your source code
- --------------------------
-
- I suggest you create a C source file to handle the interface to the
- Exec library system. The file squareme.c is a working example.
-
- Although the library code opens Exec and Dos for you, you must still
- declare the library bases. This is different from application programming
- (where the bases are declared in the startup code). In addition, you
- must declare LibBase as a pointer to a Library. LibBase is used by
- the interface code so don't change its name.
-
- If your library calls itself, then also declare your base variable,
- using the same name you used in the .fd file (but without the
- underscore).
-
- In addition to the functions in your library, you will have to write
- the following functions. Declare them EXACTLY as you see here. The
- file squareme.c contains examples of these functions. Failure to follow
- these instructions or any that follow could lead to aggravating bugs.
-
- struct Library *__saveds LibInit(void)
-
- This function is called when your library is first loaded by the
- system. All of the Amiga library housekeeping stuff is done by
- my startup code. What this function is for is if you have to
- initialize things before the library is used. For example, if you
- maintain a linked list of something, then NewList() it here.
-
- What this function MUST do is return the library base if your
- initialization performed successfully. The library base is stored
- in LibBase, which is set up correctly before this function is called.
- If you call your own library from within itself, then you should
- set up your library base to the value of LibBase. In my example,
- I have set up DemoBase to the value of LibBase. Then I return LibBase.
-
-
- struct Library *__saveds LibOpen(void)
-
- This function is called when the library is opened by someone. All
- Amiga housekeeping is already done, so all this function does is
- perform any process-specific initialization. For example, you might
- want to record the process structures of your openers so that you
- can maintain a separate data area for each.
-
- What this function MUST do is return the library base if any
- initialization completed successfully. See my example.
-
-
- void __saveds LibClose(void)
-
- This function is called when someone closes your library. Again,
- all Amiga housekeeping is done for you. This function is used to
- clean up after anything you set up in LibOpen.
-
- This function doesn't have to do anything if it doesn't want to!
- All it has to do is return! But it DOES have to be in the file,
- even if all it does is return.
-
-
- BOOL __saveds LibExpunge(void)
-
- This function is called when Exec wants to remove you from the system.
- My startup code will free the resources associated with the library
- base. All you have to do is free the stuff you allocated in
- LibInit.
-
- This version of LibExpunge allows you to abort the expunge if
- necessary. For example, some libraries may maintain data that must
- be flushed to disk before an expunge. In this case, you may not
- want to expunge, even if Exec thinks you can. In this case, return
- FALSE from this function. If you can expunge, return TRUE.
-
- Again, if there is nothing to free, this function is allowed to
- simply return TRUE!
-
-
- Notes:
-
- All these functions are declared using __saveds. THIS IS ESSENTIAL!
-
-
- *** IMPORTANT NOTICE RE: FORBID IN LIBRARIES ***
-
- Exec performs a Forbid() before calling the Init, Open, Close or Expunge
- functions. However, the Operating Systems Development Group at Commodore
- has indicated that it has never been guarenteed that a library will not
- break a Forbid() in open or close. The exception to this rule is the ROM
- library set - any ROM library that existed in 1.3 or earlier will not break
- a Forbid().
-
- This means that you cannot open or close a non-ROM library (ARexx included)
- if you do not want to break Forbid() yourself. In particular, since
- Expunge is never allowed to break a Forbid, you must not close a non-ROM
- library inside Expunge.
-
- Breaking a Forbid inside a library's Open or Close functions requires
- careful mutual exclusion management. The library controller included
- in this release of saslib performs such management for you.
-
- Whether you break Forbid or not is up to you, but be aware of it if your
- code depends on Forbid! Saslib doesn't care. The end result is that
- operations which were previously forbidden, such as flushing a queue
- out to disk, no longer are.
-
-
-
- Now to prepare the source code itself. You will have to code your
- functions to use assembly language interfaces. Assuming that you
- already have these functions written and that register assignment has
- been done, this is easy:
-
- 1. Prepend a 'LIB' to the name of every function callable from outside
- the library. If you call functions from within your library, this
- will prevent problems associated with having assembler registers in
- the prototypes and pragmas at the same time. If you don't know
- what I'm talking about, then you are lucky. As an example, I have
- named the SquareMe function 'LIBSquareMe'.
-
- 2. Immediately after the return type of your function, but before
- its name, put the keywords '__saveds __asm' exactly as you see them
- here. See my example for details.
-
- 3. Before each argument in the function header, put the keywords
- 'register __xx' where xx is replaced with the register name you
- assigned earlier to that argument. Note that the letter in the
- register name MUST be in lowercase.
-
- Here's our foo function as an example:
-
- int __saveds __asm foo(register __a0 struct bar *a, register __d0 int b,
- register __a1 char *c,
- register __d1 int e, register __d2 int f)
-
- Repeat the above for each function callable from a client of your
- library. You do not need to change private functions called from
- within your library.
-
-
- Preparing libinf.a
- ------------------
-
- lininf.a is the file that describes your library. Yes, it's in assembler,
- but you don't need to know assembly to modify it. Just do the following:
-
- 1. On the line with '_LibName', replace 'demo.library' with the name
- you want your library to be opened with.
-
- 2. On the line with '_LibID', replace the ID string with a string
- identifying your library. The format is
-
- nameof.library major.minor (dd.mm.yy)
-
- See the example.
-
- 3. On the LibVersion and LibRevision lines, place version and revision
- numbers. For example, if you want your library to be version 4.2,
- set LibVersion to 4 and LibRevision to 2. If you don't know
- what version you want, use 1.0.
-
- 4. Where it says 'put pointers to your functions here', do just that.
- Create a line that says 'DEF <name>' where name is the name of
- your function, not including the 'LIB'. IMPORTANT!!!! The names of your
- functions must appear here IN THE SAME ORDER as they appear in the
- .fd file!
-
-
- Preparing the makefile
- ----------------------
-
- Now on to the makefile. I have defined lines for the compiler and
- assembler. You should replace the path to the assembler 'Bin:a68k/a68k'
- with what it is on your machine. If you know how to use the assembler,
- then you can tweak the command line arguments but they work fine as
- they are.
-
- For the compiler, I have disabled stack checking. This is important.
- Your library will be called from many processes each with its own stack.
- Which stack do you expect to check? Other than this, you can define
- most other C flags any way you want.
-
- The make instrucions for demo.library link the object file (squareme.o)
- to libhdr.o and libinf.o IN THAT ORDER. Replace squareme.o with the
- name(s) of the object files you will be producing. Leave the other
- object file names as they are. If you have any linker libraries you
- would like to use, feel free to use them.
-
- The dependency for squareme.o can be anything you want. Just be sure
- to have stack checking turned off.
-
- The dependencies for libhdr.o and libinf.o should be left as they are.
-
- Finally, you should make an entry for your pragma file. Use fd2pragma
- to generate the pragma file from the .fd file. Putting this in the
- makefile ensures that the pragma file is kept up to date if you ever
- change the .fd file.
-
- Of course, you should change all references to demo.library to whatever
- you are calling your library.
-
- That's it!
-
-
- And now the fun part
- --------------------
-
- Do an LMK. Ideally, everything should compile, assemble and link just
- fine. If it does not, check your files over for small errors. If you
- still have problems, write me.
-
- The result will be a library file that you can copy to libs:. Try
- it out! If you have xoper or a similar utility, use it to verify that
- opening, closing and expunging work correctly.
-
- Now, who said it was hard to make an Amiga library?
-
-
- ARexx function libraries
- ------------------------
-
- ARexx allows you to add functions to the language through the use of
- libraries. Details are in the documentation that comes with pre-2.0
- versions of ARexx and in the Programmer's Guide to ARexx, published
- by Commodore.
-
- You can use saslib to create ARexx function libraries. To do so:
-
- 1. Remove the semicolon from the INCLUDE_REXX line at the bottom of
- libinf.a.
-
- 2. Declare your query function in C like this:
-
- ULONG __saveds __asm LIBRexxQuery(register __a0 struct RexxMsg *rm,
- register __a1 char **rv)
-
- 3. Be sure to reserve an offset for the query function in the dispatch
- table. Usually, the query function resides at offset -30, which
- corresponds to the first entry in the table.
-