This package is now obsolete

This package is obsolete as it has been replaced by the new library mechanism in the rexdk. It is here only for reference purposes. The code remains available under the GPL in case it is useful.

DYNLINK for Rex 6000 (V0.3)

This is a package which provides an easy method for an addin to call routines in another addin. This simplifies splitting code across multiple addins or creating a shareable library of utility functions.

In addition to exporting functions, addins can export other addresses (e.g. of text or pictures) or 16-bit or 32-bit values (e.g. constants).

The exporting addin is called the server library and the importing addin is called the client.

Design

The core function is to maintain a database indexed by library name and entry point name and containing the bank address and virtual address (assuming the bank is mapped at 0x8000) of the exported function/address. Routines are provided to add new entries to the database and to read values from the database.

There are some additional capabilities designed to make life easier:

Limitations

The package has been designed to work with the Rexdx kit (and has only been tested with that kit).

There is little the package can do to detect that someone has deleted an addin which has registered. In that case, clients of that library may attempt to call into random data/code!

This is still in development and future versions are likely to change the database format and, possibly, the API. If you are making serious use of this package please let me know so I can let you know of planned changes.

Documentation

To be written. Read the comments in the code.

Clients and servers must both include dynlink.h and link with dynlink.lib. dynlink.lib should be put in ../../lib/clibs and linked using -ldynlink.

Server libraries

Server libraries must export functions or values using the routines:

Functions exported using dl_export_function must be called by the client using dl_library_call. They can have any prototype and return value. Any additional arguments to dl_library_call beyond the libname and func will be passed to the called routine. The return value wil become the return value of dl_library_call.

If the server library is exporting a number of functions it may be more efficient (in code size) to use the _fatal forms of the routines (e.g. dl_export_function_fatal). These versions never return an error (so the return value does not have to be checked) but will display an error message on the users screen and terminate the add-in if an error occurs. The _fatal versions are used automatically if DLFATAL is defined when the dynlink.h file is included. However, note that using these routines means your add-in will also include some additional dl__ routines and DsPrintf and some routines that requires. If you are not already using DsPrintf in your add-in the additional code may be more than the error checking code required for the standard versions of the export routines.

In order to keep code space requirements to a minimum, server libraries can call the dl_export... routines with no additional preparation. However, if code space in the server library permits, three additional routines can be called in order to increase robustness.

None of these calls are required but, if space permits, they make the linking process more reliable.

Unfortunately there is little the dynlink package can do about servers which are deleted without removing their registry entries. If the user deletes the server library addin, the client will retrieve stale information and will probably end up calling into random parts of Flash RAM.

Clients

The basic client routines are:

dl_library_call allows the client to call a function in a library (exported using dl_export_function), passing in a list of parameters and receiving a 16-bit or 32-bit returned value, without worrying about addresses, banks, FarCalls, etc.

NOTE: long (32-bit) parameters must be passed to dl_library_call as two 16-bit parameters, high word first. For example, if an exported routine was declared as:
int test_routine(long value);
It would be called using:
dl_library_call("LIBRARY","TEST_ROUTINE", (unsigned short)(value>>16), (unsigned short)(value));

If a library routine is to be called many times, dl_library_lookup allows the lookup to happen once and then dl_library_call_fast can be used to call the routine without looking it up again.

Like the server case, robustness is improved by having the client call dl_version_check before calling any other routines. This is not required but it can detect version mismatch problems.

Unfortunately there is little the dynlink package can do about servers which are deleted without removing their registry entries. If the user deletes the server library addin, the client will retrieve stale information and will probably end up calling into random parts of Flash RAM.

Stub routines and wrappers

When creating a library, it is often convenient to provide your caller with a library of stub routines which they call (with a documented API) amd which hide the calls to dl_library_call. This is not required and is purely optional.

One advantage of providing stub routines is that they can deal with any differences in the environment of the caller and the library. For example, when passing addresses to routines in a server library you must make sure either that the addresses are in RAM (e.g. on the stack) or you must pass the bank and offset so that the server routine can map the original address. The stub routine can hide that mechanism from the caller.

Stub routines are often used with wrappers. A wrapper is a function that is exported instead of the desired function and which then calls the desired function. For example, a wrapper may map the bank and offset provided by a stub routine (as in the example above) and then pass the mapped address to the desired function.

By convention, if a wrapper is being used, so that the exported routine does not have the normal calling interface, the exported symbol should be prefixed with a dot. This helps avoid callers calling the routine directly (not via the stub) and not handling the API differences.

You can see examples of stubs, wrappers and the leading dot in the GRCLIB shareable library.

Source files

Copyright (C) 2001, 2002 Graham R. Cobb. The package is distributed under the GPL (see the copyright notices and the COPYING file).

DYNLINK For Rex 6000 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

DYNLINK For Rex 6000 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

When the ZIPfile is expanded, it creates the library itself and several informational files in the current directory and sources in four subdirectories. These four subdirectories are intended to be placed within the Projects directory of the Rexdk environment and are explained in some more detail below.

The actual library consists of two files:

dynlink

The package itself:

DLmaint

Dynlink database maintenance utility. Can be used to create the database, examine the database and delete the database.

The directory contains the source files, the makefile, the .rex file for the addin and an addin1.out file for the emulator.

DLStest

Sample server library addin.

The directory contains the source files, the makefile, the .rex file for the addin and an addin1.out file for the emulator.

DLCtest

Sample client addin -- calls a routine exported by DLStest.

The directory contains the source files, the makefile, the .rex file for the addin and an addin1.out file for the emulator.

Example

To see a real example of using the DYNLINK package, see grclib-so, in the GRCLIB package.

This demonstrates use of the client APIs (in stub routines) and the server APIs in a server library addin. It also shows how to create a server library that can be told to de-register itself when the user wants to delete it or move it (e.g. using Adder).

Changes

Since V0.2

DYNLINK now includes its own FarCall mechanism. This is re-entrant and only uses a single 16-bit word of "global" RAM storage (it uses the far_ret word defined in the RexDK package). The rest of the FarCall data is held on the stack.

The built-in FarCall mechanism now permits arbitrary argument lists and 32-bit results to be supported.

dl__library_call, dl__farcall, dl__farret and the helper routine have been rewritten in assembler.

User-written helper routines are no longer required and no longer supported.

The following routines are now obsolete and will be removed in a later release:

Server library initialisation can be simplified by using the new dl_export_..._fatal routines. These will automatically display an error message and exit the add-in if an error occurs, removing the need for the caller to check the status of each export call. However, this is at the cost of possibly increased code size as these routines use DsPrintf and other Rex routines.

Since V0.1

Helper routines (since obsoleted).

Example (GRCLIB).


Return to Graham's Rex page

This page has been accessed Access counter times.

Graham Cobb