Breaking the 64KB Limit Using a DLL

Breaking the 64 KB limit is an important topic, and because of the internal representation of files in the AMS as well as the size limit of memory blocks, it is possible only by breaking the program up into multiple files.

If you get close to this limit, the first thing to do is optimizing the program. Ways to do this are those described in Advanced Options of GCC4TI, using functions from the AMS instead of those from the GCC4TI Library, and, last but not least, hand-optimizing the code. If and only if you are still close to the limit, you should think about the possibility to use multiple files. At first, try storing as much data as possible (sprites, maps, etc.) in external files (using functions from vat.h). If this still does not help, the only possibility left is to split the code into multiple files.

This means that you need to use some form of dynamic link libraries. These are very well-known for kernel programs, but kernel-mode libraries are not something we want to encourage, seeing that they have led to a huge mess of versioning problems and incompatiblities. For information about how to create them, see the section How to make kernel-based programs. The necessary definitions for libraries for kernel-less programs can be found in the dll.h header file.

Now that we have explained why you might need to use external libraries, we specifically want to discourage their use for anything else. External (dynamic) libraries have been used quite often in the past to automate common tasks, like file access, graphics, compression, etc. This is not what we want to happen, otherwise there will be a different libraries for the same tasks, with a lot of incompatiblities between libraries and even between different versions of the same library. GCC4TI provides the possibility to use static libraries (also known as function archives) for this purpose. In the IDE, you need to select the creation of a function archive in the project options; when using the command line compiler, you need to use the '-ar' switch. When using function archives, only the files which are needed are really included in the final program.

There is an exception to the rule that you should not create a dynamic library to make it available to others: If the library itself is very large, and a program will typically use a lot of its functions at the same time, client programs are likely to exceed the file size limit if it is provided as a static library. In this case, using a dynamic library really is the only possibility. An example is the FAT Library by the TI-Chess Team.

Hopefully, we have talked you out of creating a dynamic library if there is any other reasonable option. Now we can explain how to create a dynamic library in nostub (kernel-less) mode. For detailed explanations of all directives, take a look at the dll.h header file. The "Custom DLL" example demonstrates the creation of a very small library (note that the library file must have the name "mydll" on the calculator):

// Custom DLL example; must be named "mydll"
// Note: You should not use DLLs under normal circumstances; see section
// "How to break the 64 KB limit using a DLL" for more information.

#define USE_TI89

#include <tigcclib.h>

DLL_INTERFACE

char MessageInDLL[]="Hello!\n";
long GlobalVarInDLL;
void HelloFromDLL(void);
int SumFromDLL(int,int);

DLL_ID 372377271
DLL_VERSION 2,12
DLL_EXPORTS HelloFromDLL,SumFromDLL,MessageInDLL,&GlobalVarInDLL

DLL_IMPLEMENTATION

void HelloFromDLL(void)
{
  printf ("Hello from DLL!\n");
  printf ("Global variable is %ld\n", GlobalVarInDLL);
}

int SumFromDLL(int a, int b)
{
  return (a + b);
}

The order of the definitions is very important; read more about this in DLL_INTERFACE. The exported symbols are HelloFromDLL, SumFromDLL, MessageInDLL, and GlobalVarInDLL; they are assigned index numbers from 0 to 3.

A client program which uses this DLL could look like the following example, called "Custom DLL Test":

#define USE_TI89

#include <tigcclib.h>

#define HelloFromDLL _DLL_call(void,(void),0)
#define SumFromDLL _DLL_call_attr(int,(int,int),__attribute__((stkparm)),1)
#define MessageInDLL _DLL_reference(const char,2)
#define GlobalVarInDLL _DLL_glbvar(long,3)

void _main(void)
{
  if (LoadDLL ("mydll", 372377271, 2, 11) != DLL_OK) 
    {
      DlgMessage ("ERROR", "Error loading DLL!", BT_OK, BT_NONE);
      return;
    }
  clrscr ();
  GlobalVarInDLL = 1234567;
  HelloFromDLL ();
  printf ("Sum from DLL: 2+3=%d\n", SumFromDLL (2, 3));
  printf ("Message from DLL: %s\n", MessageInDLL);
  ngetchx ();  
  UnloadDLL ();
}

This program simply demonstrates all possible ways to import symbols from the DLL. See LoadDLL and the corresponding identifiers for more information.

A program which wants to use DLLs has to be executing from the so-called "ghost address space". This means that the program either has to be started through an external launcher which calls enter_ghost_space (which is always the case if it is exe-packed), or EXECUTE_IN_GHOST_SPACE needs to be defined at the beginning of the program. You will will want to exepack the program anyway if it is large enough. In the IDE, this can be done through a check box in the project options; when using the command line compiler, you need to use the '-pack' switch.

Note that this example is an exception to the rules above. It breaks two rules: First, it does not even come close to the 64 KB limit. Second, there is a variable in the DLL that could be put in another file: MessageInDLL. This program should remain the only exception; it only exists because a small program is best to teach. Imagine trying to learn how to use a DLL from a program that pushed the 64 KB limit (several thousand lines of code)!

Certain pseudo-constants in compat.h depend on initialization code, which is not present at all in DLLs. Using definitions like NO_CALC_DETECT helps for calculator-dependent definitions, but not all parts of the GCC4TI Library are available (for example, defining RETURN_VALUE makes absolutely no sense).

Finally, we need to write a few sentences about DLL version information. The major version number needs to be changed if and only if two DLLs are incompatible with each other. Adding new functions does not make DLLs incompatible; rearranging them does, for example. Usually, you do not ever need to change the major version number. Increasing the minor version number means that the DLL is still compatible with existing programs; the client program can specify a minimum minor version number to indicate that it depends on the new functionality.

See also: dll.h


Return to the main index