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