NoStub programs are programs which don't need any kernel like DoorsOS, Universal OS, TeOS, PlusShell etc. for execution. This does not imply that they do not work with a kernel installed. Starting from release 2.2 of TIGCCLIB, TIGCC produces NoStub programs by default (there is no need to include nostub.h before including any other header files from this library as in releases of TIGCCLIB prior 2.2; this will be explained in more details below). As this release of the library is highly ANSI-compatible, the first example will be the classic Kernighan & Ritchie "Hello world" example (called "Hello World 1", slightly modified to include clearing the screen and waiting for the keypress):
#define SAVE_SCREEN // this directive forces saving/restoring the screen #define USE_TI89 // produce all types of files #define USE_TI92PLUS #define USE_V200 #include <stdio.h> // standard ANSI C input/output support #include <kbd.h> // keyboard handling support, needed for ngetchx void _main(void) // main entry point is function _main { clrscr (); // clear the screen and reset print position printf ("Hello world!"); // do you know what this is? ngetchx (); // wait for a keypress }
Note that the main entry point is _main
, not main
as defined in Kernighan & Ritchie. The reason for it is the compatibility with
kernels, which propose that the main program entry
point is called _main
. Also note that _main
should be a function
with return type void
(not int
as usual), because you can
not return a status code to the operating system.
To compile this program, you can use the
TIGCC Integrated Environment, or you can type from the command line
(if the file is named "hello.c"):
tigcc -O2 hello.c
Always include the switch '-O2'. It will turn on optimization
(click here to see much more about compiler command line options).
As you can see, defining macros named USE_TI89
,
USE_TI92PLUS
(or USE_TI92P
), and USE_V200
will generate
.89z (for the TI-89) or .9xz (for the TI-92 Plus and V200) executable files.
Only the calculators which you define this way are supported; if you only
define USE_TI89
, then only the TI-89 will be supported. Defining
only one calculator model may generate shorter and faster code. See
compat.h for more info.
The old way of defining global variables called _ti89
and _ti92plus
is still supported, but it is not recommended to use it any more, since no
optimization of the above kind can be performed. In the IDE, you will find
replacements for these macros in the project options.
The directive SAVE_SCREEN
forces saving the screen contents before
execution and restoring them after execution of the program.
Users which are familiar with previous releases of TIGCCLIB may ask what
has happened to the nostub.h header file. Starting from release 2.2
the use of any header file from TIGCCLIB will automatically include
the nostub.h header file, except if the global preprocessor
symbol USE_KERNEL
is defined, or if the doors.h
is included at the beginning (see section
How to make a kernel-based program for more info). This way you need
to include nostub.h
manually only if you don't include any
other header file from TIGCCLIB, which is extremely unlikely. To retain compatibility
with programs created with previous versions of the library, including
nostub.h explicitly will not cause any damage.
The example given above works, but it is better to use TIOS-specific functions than ANSI C console
functions. First, they are more flexible, and second, they are built-in in the OS,
so using them will usually produce much shorter code. For example, the use of the
printf function will increase the size of your program
by about 200 bytes, because printf is an advanced
high-level function (not present in the TIOS) which may do much more than writing "Hello
world" on the screen (like formatted output, screen scrolling, etc.). So, to write
just "Hello world" on the screen, it is a better idea to use particular TIOS functions
like DrawStr to do the same thing: your program will
be much shorter and more efficient (although less "standard"). This idea is illustrated
in the following program ("Hello World 2"), which uses only TIOS functions, and which does
the following:
saves the contents of the screen;
clears the screen;
displays the "Hello world!" message using the large font;
draws a border around the message;
waits for a keypress;
restores the original contents of the screen before returning.
#define SAVE_SCREEN #define USE_TI89 #define USE_TI92PLUS #define USE_V200 #include <graph.h> #include <kbd.h> void _main(void) { static WIN_RECT rect = {0, 0, 100, 14}; ClrScr (); FontSetSys (F_8x10); DrawStr (3, 3, "Hello world!", A_NORMAL); DrawClipRect (&rect, ScrRect, A_NORMAL); ngetchx (); }
Note that SAVE_SCREEN
is a new directive introduced in
TIGCCLIB 2.0. In previous releases of TIGCCLIB you had to save and restore the
screen manually, like in the following example ("Hello World 3"):
#define USE_TI89 #define USE_TI92PLUS #define USE_V200 #include <graph.h> #include <kbd.h> void _main(void) { LCD_BUFFER buffer; static WIN_RECT rect = {0, 0, 100, 14}; LCD_save (buffer); ClrScr (); FontSetSys (F_8x10); DrawStr (3, 3, "Hello world!", A_NORMAL); DrawClipRect (&rect, ScrRect, A_NORMAL); ngetchx (); LCD_restore (buffer); }
See graph.h and kbd.h for
detailed descriptions of the used functions and data types. Be aware of the slight difference between
ClrScr defined in graph.h and
clrscr defined in stdio.h.
The SAVE_SCREEN
directive does exactly the same thing as in the previous
example, but the task is automatized (i.e. you need not to explicitely declare the save buffer
and to call functions for saving and restoring screen manually). If you are not happy with such
allocation (the buffer is allocated on the stack, reducing its size by 3840 bytes), don't use
SAVE_SCREEN
directive and perform saving and restoring the screen
using an other method (for example, using dynamic memory allocation). Note that the
total stack size on TI is limited to 16384 bytes.
The disadvantage of NoStub programs is that they are somewhat longer
than kernel programs if the program contains many ROM calls (but in the above
example, the NoStub version is shorter than the kernel version), and they can not call
routines from external files (often called "libraries") whithout special support
code. Any other features supported with this library of header files work in both
kernel and NoStub mode, so if the difference in the program size is not enormous,
and if no external libraries are needed, NoStub mode is highly recommended. Note that
routines defined in these header files contain most of routines which are seen
in various external libraries, sometimes maybe with a different name and syntax. I
think that making a program which is independent of external library files is a
big advantage!
If you make a NoStub program, you must also know that the exit point defined
by the function _exit
has no meaning in NoStub programs.
See also: nostub.h
To produce a kernel-based program, i.e. a program which will need PreOS,
DoorsOS, UniversalOS, TeOS, etc. for executing, simply define the global
preprocessor symbol USE_KERNEL
at the begining of your program,
before including any other header files from the library. Alternatively, the
old method (including the doors.h header file
before including any other header files from the library) still works. In
fact, if USE_KERNEL
is defined, including any header file from
the library will include doors.h
automatically (if it is not
included explicitely before). So, explicitely including doors.h
is really necessary only if your program does not include any other header
file from the library (which is extremely unlikely).
Here is a Kernighan & Ritchie "Hello world" example (kernel version, called "Hello World 4"),
which works exactly like the example given in the previous section
(Making Kernel-Less Programs):
#define USE_KERNEL // include kernel support #define USE_TI89 // produce all types of files #define USE_TI92PLUS #define USE_V200 #include <stdio.h> // standard ANSI C input/output support #include <kbd.h> // keyboard handling support, needed for ngetchx void _main(void) // main entry point is function _main { clrscr (); // clear the screen and reset print position printf ("Hello world!"); // do you know what this is? ngetchx (); // wait for a keypress }
Note that the directive SAVE_SCREEN
is not included like in the NoStub version.
DoorsOS saves and restores the screen content by default, so no extra intervention
is necessary.
Of course, it is better again to use TIOS specific functions than ANSI C console
functions, so an extended "Hello world" TI-specific example (kernel-based
version), which does the same as in the NoStub case, follows (called "Hello World 5"):
#define USE_KERNEL #define USE_TI89 #define USE_TI92PLUS #define USE_V200 #include <graph.h> #include <kbd.h> void _main(void) { static WIN_RECT rect = {0, 0, 100, 14}; ClrScr (); FontSetSys (F_8x10); DrawStr (3, 3, "Hello world!", A_NORMAL); DrawClipRect (&rect, ScrRect, A_NORMAL); ngetchx (); }
See graph.h and kbd.h for
detailed descriptions of the used functions and data types. Be aware of the slight difference between
ClrScr defined in graph.h and
clrscr defined in stdio.h.
To compile this program, you can use the
TIGCC Integrated Environment, or you can type from the command line
(if the file is named "hello.c"):
tigcc -O2 hello.c
Always include the '-O2' or '-Os' switch: it will turn on optimization
(click here to see much more about compiler command line options).
See also the notes given with the example in the previous section.
If you make programs spread in more than one file, you must define
USE_KERNEL
(or include doors.h) in each file!
The advantage of kernel-based programs lies in the fact that they are
often (but not always) shorter than NoStub programs if the
program contains a lot of ROM calls. Kernel-based programs can also call
routines from external files (often called "libraries") without special
library support inside the program. Any other features supported
with this library of header files work in both kernel and
NoStub mode, so if the difference in the program size is not
too large, and if no external libraries are needed, NoStub mode
is recommended. Note that routines defined in these header files
contain most of the routines which are seen in various external
libraries, sometimes maybe with different name and syntax.
Additionally, if you want to make your program even smaller but only want to support
new versions of the PreOS kernel, you can define USE_PREOS_COMPRESSED_TABLES
.
This will create compressed versions of the relocation tables, which are not
available in older kernels. If you try to run the program with an old kernel
installed, you will get an error message.
If you make a kernel-based program, knowing the following facts may be useful:
You can produce an external library file by defining the global symbol
_library
, i.e. by putting int _library
in your program. If the name of your library is for example MyLib,
all symbols with names like MyLib__nnnn will be exported as public. To make
life easier, use the #define
directive. For example, if you
want to export function named MyFunc, you can, for example, put the directive
#define MyFunc MyLib__0005
at the beginning of
your library. The same directive must be included in the program which wants
to use the function MyFunc from the library MyLib. Also, the prototype
of the imported function also needs to be defined.
Kernel-based programs may have an exit point. The function with name
_exit
will be executed if and only if the program is terminated
in an abnormal way, for example if there was an error, or if the user pressed
STO+ON.
See also: doors.h
There are several ways to optimize programs further, and enable special support for some not so common situations:
See also: default.h, MIN_AMS, RETURN_VALUE, ENABLE_ERROR_RETURN
If you put
#define SAVE_SCREEN
at the beginning of your program, the screen will be saved automatically at
startup and restored when the program terminates. This takes
LCD_SIZE bytes on the stack.
Note: Kernels always save the screen automatically, so this directive
has no effect in kernel mode.
In nostub mode, if you put
#define OPTIMIZE_ROM_CALLS
at the beginning of the program, the compiler will reserve one processor register
(a5) to keep the base address of TIOS jump table in it. This will make all calls
to TIOS routines smaller and faster. The disadvantage of this method lies in the
fact that the a5 register is fully reserved during the execution of the program, so
the compiler will have one register less for internal manipulations. The consequence
is that it sometimes can produce somewhat longer code when compiling a particular
expression or statement. Generally, the use of OPTIMIZE_ROM_CALLS
usually
leads to shorter and faster code, but this does not always need to be true. It is best
to try it and see how it performs in a particular program.
If you define OPTIMIZE_ROM_CALLS
in one file, you must also define it in
every file that uses a function from this file.
Note: Avoid OPTIMIZE_ROM_CALLS
in a program which may change its flow
asynchronously in an unpredictable way (an event-driven program, for example).
Such programs are mainly all programs which use some kind of callback function (like
programs which use OSVRegisterTimer, complex
dialogs with callback functions, the function vcbprintf etc.).
There may also be some problems with floating point arithmetic.
In nostub mode, if you put
#define KERNEL_FORMAT_ROM_CALLS
at the beginning of the program, ROM calls will be stored and relocated in
the same manner as kernel programs do it. This does not mean that your
program will need a kernel to run. The kernel format for ROM calls is very
efficient if the same ROM calls are used at many different places, but may
waste space otherwise. You need to see for yourself whether using it makes
the program smaller.
This feature uses the linker-specific symbol
__ld_insert_kernel_rom_calls.
See also: MLINK_FORMAT_ROM_CALLS, COMPRESSED_FORMAT_ROM_CALLS
In nostub mode, if you put
#define MLINK_FORMAT_ROM_CALLS
at the beginning of the program, ROM calls will be stored in the program in a
compressed format.
This format for the relocation entries (first used in Johan Eilert's mlink
linker) is designed to balance the size of the relocs against the size of the
decompression routine. The relocation entries take only slightly more space
than in the compressed format, but the decoding code is much smaller.
Moreover, as for the compressed format, the decoding code only has to be
inserted into the program once if multiple MLINK_FORMAT_...
features are used. In most cases, this should be the optimal format to use for
relocation entries, but for programs with very few or very many relocations,
the kernel or compressed formats (respectively) are more efficient.
This feature uses the linker-specific symbol
__ld_insert_mlink_rom_calls.
See also: KERNEL_FORMAT_ROM_CALLS, COMPRESSED_FORMAT_ROM_CALLS
In nostub mode, if you put
#define COMPRESSED_FORMAT_ROM_CALLS
at the beginning of the program, ROM calls will be stored in the program in a
compressed format.
Relocation entries in this format take the smallest space that is reasonable.
The relocation code for the compressed format is rather large, but part of it
only has to be inserted into the program once if multiple
COMPRESSED_FORMAT_...
features are used.
This feature uses the linker-specific symbol
__ld_insert_compressed_rom_calls.
See also: KERNEL_FORMAT_ROM_CALLS, MLINK_FORMAT_ROM_CALLS
AMS 2.04 and higher support a method of calling ROM calls in a way that is shorter than any other way, but very slow. If you want to use this method, write
#define USE_FLINE_ROM_CALLS
at the beginning of the program. However, you need to define a minimum AMS version
for your program if you want to use this. You can bypass this, assuming that the user
has installed an emulator for Line 1111 ROM calls, by defining USE_FLINE_EMULATOR
.
Alternatively, you can even define USE_INTERNAL_FLINE_EMULATOR
to make the program install its own handler for Line 1111 ROM calls.
Note: The Line 1111 handler of the AMS does not support calling TIOS
functions from interrupts. If you want to use your own interrupt handlers for
something, you will have to define USE_INTERNAL_FLINE_EMULATOR
.
See also: USE_FLINE_JUMPS
AMS 2.04 and higher support a method of jumping to other places in the code in a way that is shorter than any other way, but very slow. If you want to use this method, write
#define USE_FLINE_JUMPS
at the beginning of the program. However, you need to define a minimum AMS version
for your program if you want to use this. You can bypass this, assuming that the user
has installed an emulator for Line 1111 jumps, by defining USE_FLINE_EMULATOR
.
Alternatively, you can even define USE_INTERNAL_FLINE_EMULATOR
to make the program install its own handler for Line 1111 jumps.
Note: The Line 1111 handler of the AMS does not support Line 1111
jumps from interrupts. If you want to use your own interrupt handlers for
something, you will have to define USE_INTERNAL_FLINE_EMULATOR
,
which includes special support for such jumps automatically if
DEFINE_INT_HANDLER is used.
This feature uses the linker-specific symbol
__ld_use_fline_jumps.
See also: USE_4_BYTE_FLINE_JUMPS, USE_FLINE_ROM_CALLS
Normal F-Line jumps take 6 bytes, but if they are not pc-relative but relative to the beginning of the program, they can fit into 4 bytes. To activate 4-byte F-Line jumps, put
#define USE_4_BYTE_FLINE_JUMPS
at the beginning of the program. However, only the internal F-Line emulator
supports such jumps, so you need to define
USE_INTERNAL_FLINE_EMULATOR
as well, to make the program install
its own handler for Line 1111 jumps.
Note: Since 4-byte F-Line jumps use codes that are otherwise used for
ROM calls, this might break applications that are called from the program, if
any. However, this is very unlikely, as the two ROM calls used are not
defined yet.
This feature uses the linker-specific symbol
__ld_use_4byte_fline_jumps.
See also: USE_FLINE_JUMPS, USE_FLINE_ROM_CALLS
In nostub mode, if you put
#define KERNEL_FORMAT_RELOCS
at the beginning of the program, relocation entries will be stored and
relocated in the same manner as kernel programs do it. This does not
mean that your program will need a kernel to run. The kernel format for
relocation information is always smaller than the native TIOS format, but the
relocation code will take up a few bytes. You need to see for yourself
whether using it makes the program smaller.
This feature uses the linker-specific symbol
__ld_insert_kernel_relocs.
See also: MLINK_FORMAT_RELOCS, COMPRESSED_FORMAT_RELOCS
In nostub mode, if you put
#define MLINK_FORMAT_RELOCS
at the beginning of the program, relocation entries will be stored in the
program in a compressed format.
This format for the relocation entries (first used in Johan Eilert's mlink
linker) is designed to balance the size of the relocs against the size of the
decompression routine. The relocation entries take only slightly more space
than in the compressed format, but the decoding code is much smaller.
Moreover, as for the compressed format, the decoding code only has to be
inserted into the program once if multiple MLINK_FORMAT_...
features are used. In most cases, this should be the optimal format to use for
relocation entries, but for programs with very few or very many relocations,
the kernel or compressed formats (respectively) are more efficient.
This feature uses the linker-specific symbol
__ld_insert_mlink_relocs.
See also: KERNEL_FORMAT_RELOCS, COMPRESSED_FORMAT_RELOCS
In nostub mode, if you put
#define COMPRESSED_FORMAT_RELOCS
at the beginning of the program, relocation entries will be stored in the
program in a compressed format.
Relocation entries in this format take the smallest space that is reasonable.
The relocation code for the compressed format is rather large, but part of it
only has to be inserted into the program once if multiple
COMPRESSED_FORMAT_...
features are used.
This feature uses the linker-specific symbol
__ld_insert_compressed_relocs.
See also: KERNEL_FORMAT_RELOCS, MLINK_FORMAT_RELOCS
In nostub mode, if you do not want the BSS section (which holds all uninitialized global variables) to be created dynamically, you can write
#define MERGE_BSS
at the beginning of all files. Unlike the '-mno-bss' compiler switch, this ensures that the variables are initialized to zero.
See also: KERNEL_FORMAT_BSS, MLINK_FORMAT_BSS, COMPRESSED_FORMAT_BSS
In nostub mode, if you put
#define KERNEL_FORMAT_BSS
at the beginning of the program, a BSS section will be created in the same
manner as kernel programs do it. A BSS section holds uninitialized global
variables. This does not mean that your program will need a kernel to
run. Currently, this is the default.
This feature uses the linker-specific symbol
__ld_insert_kernel_bss_refs.
See also: MLINK_FORMAT_BSS, COMPRESSED_FORMAT_BSS, MERGE_BSS
In nostub mode, if you put
#define MLINK_FORMAT_BSS
at the beginning of the program, a BSS section will be created dynamically at
run time, and the references into this section will be stored in the program
in a compressed format. A BSS section holds uninitialized global variables.
This format for the relocation entries (first used in Johan Eilert's mlink
linker) is designed to balance the size of the relocs against the size of the
decompression routine. The relocation entries take only slightly more space
than in the compressed format, but the decoding code is much smaller.
Moreover, as for the compressed format, the decoding code only has to be
inserted into the program once if multiple MLINK_FORMAT_...
features are used. In most cases, this should be the optimal format to use for
relocation entries, but for programs with very few or very many relocations,
the kernel or compressed formats (respectively) are more efficient.
This feature uses the linker-specific symbol
__ld_insert_mlink_bss_refs.
See also: KERNEL_FORMAT_BSS, COMPRESSED_FORMAT_BSS, MERGE_BSS
In nostub mode, if you put
#define COMPRESSED_FORMAT_BSS
at the beginning of the program, a BSS section will be created dynamically at
run time, and the references into this section will be stored in the program
in a compressed format. A BSS section holds uninitialized global variables.
Relocation entries in this format take the smallest space that is reasonable.
The relocation code for the compressed format is rather large, but part of it
only has to be inserted into the program once if multiple
COMPRESSED_FORMAT_...
features are used.
This feature uses the linker-specific symbol
__ld_insert_compressed_bss_refs.
See also: KERNEL_FORMAT_BSS, MLINK_FORMAT_BSS, MERGE_BSS
Writing
#define OMIT_BSS_INIT
at the beginning of a file specifies that uninitialized global variables defined in this file do not need to be set to zero at program startup. If this is done for all files, the BSS initialization is skipped.
In nostub mode, if you put
#define KERNEL_FORMAT_DATA_VAR
at the beginning of the program, and the data section is put into an external
data variable, references into the data variable will be stored in the same
manner as kernel programs store references. This does not mean that
your program will need a kernel to run. Currently, this is the default.
This feature uses the linker-specific symbol
__ld_insert_kernel_data_refs.
See also: MLINK_FORMAT_DATA_VAR, COMPRESSED_FORMAT_DATA_VAR
In nostub mode, if you put
#define MLINK_FORMAT_DATA_VAR
at the beginning of the program, and the data section is put into an external
data variable, references into the data variable will be stored in the
program in a compressed format.
This format for the relocation entries (first used in Johan Eilert's mlink
linker) is designed to balance the size of the relocs against the size of the
decompression routine. The relocation entries take only slightly more space
than in the compressed format, but the decoding code is much smaller.
Moreover, as for the compressed format, the decoding code only has to be
inserted into the program once if multiple MLINK_FORMAT_...
features are used. In most cases, this should be the optimal format to use for
relocation entries, but for programs with very few or very many relocations,
the kernel or compressed formats (respectively) are more efficient.
This feature uses the linker-specific symbol
__ld_insert_mlink_data_refs.
See also: KERNEL_FORMAT_DATA_VAR, COMPRESSED_FORMAT_DATA_VAR
In nostub mode, if you put
#define COMPRESSED_FORMAT_DATA_VAR
at the beginning of the program, and the data section is put into an external
data variable, references into the data variable will be stored in the
program in a compressed format.
Relocation entries in this format take the smallest space that is reasonable.
The relocation code for the compressed format is rather large, but part of it
only has to be inserted into the program once if multiple
COMPRESSED_FORMAT_...
features are used.
This feature uses the linker-specific symbol
__ld_insert_compressed_data_refs.
See also: KERNEL_FORMAT_DATA_VAR, MLINK_FORMAT_DATA_VAR
If you are compiling a program for more than one calculator, you can put
#define OPTIMIZE_CALC_CONSTS
at the beginning of the program to optimize code speed and size. This will
affect PSEUDO_CONST_CALC and
everything derived from it - mainly pseudo-constants from
compat.h, but you can also define your own
pseudo-constants very easily.
Defining this does have one drawback: Each of the executable files produced
by the linker can only be run on the calculator it was linked for and cannot
be transferred from one type of calculator to another. A check to make sure
the program is not run on a wrong calculator is inserted automatically,
unless NO_CALC_DETECT
is defined.
This feature uses the linker-specific symbol
__ld_calc_const_....
By default, code to detect the calculator model is inserted at the start of the program. This is used mainly to optimize the pseudo-constants defined in compat.h if more than one calculator has to be supported. However, it is also used to prevent the program from being run on an unsupported calculator. You can turn this check off by writing
#define NO_CALC_DETECT
Note that the detection is turned off automatically if it is not needed.
Sometimes, on hardware version 2, it is necessary to execute a program in the so-called "ghost space", which is the area of addresses above 0x40000. If you need to know more about this, read the launcher FAQ entry. If you put
#define EXECUTE_IN_GHOST_SPACE
at the beginning of your program, on hardware version 2 (and 1, for
backwards compatibility reasons), the program will automatically be
executed in the ghost space. This causes an overhead of about 200 bytes.
More precisely, it will relocate itself in the ghost space (by
applying EX_patch to itself with the
start address increased by 0x40000) before entering the ghost space.
Everything will be performed before anything else. Therefore, there
are no limitations introduced by this directive on hardware versions 1 and 2,
whereas the older
enter_ghost_space function
was very limited and hard to use.
EXECUTE_IN_GHOST_SPACE
allows the user to simply call one
program from another, without worrying about the AMS protections which
usually prevent this.
Unfortunately, on hardware version 3 (TI-89 Titanium), it is not possible
anymore to bypass the execution protection this way. EXECUTE_IN_GHOST_SPACE
detects hardware version 3 and requires a FlashROM patch to be present,
refusing the execution otherwise. The name might be changed in future versions
of TIGCC to reflect this. Therefore, you need to know that you should
not blindly add 0x40000 to an address, because this does not work at
all on the TI-89 Titanium. Instead, use HW_VERSION==2?0x40000:0
or HW_VERSION<=2?0x40000:0
as appropriate.
If functions from the TIGCC Library take a pointer to an unsigned integer as an argument, you can usually pass a signed integer as well. However, this may not be desirable, as the function may write values to it which the turn out to be negative. The same is true the other way. To prevent this, use
#define STRICT_POINTERS
With this definition, the program's in use (a.k.a. hidden) bit is set while the program is running. Normally, this isn't necessary; however, the event dispatching loop deletes twin symbols unless their in-use bit is set. In many cases, programs which might be archived will not call the event dispatching loop directly. However, dialog boxes (including the catalog) do call the event dispatching loop. The result: If an archived program uses dialog boxes (including the catalog), then the program's twin symbol will get deleted. Thus, the area of memory the program occupies gets freed, and the program will almost certainly crash. Setting this bit is handled automatically now, but you can still set it manually by defining this at the beginning of a file:
#define SET_FILE_IN_USE_BIT
Normally, programs written in TIGCC are ensured to be compatible only with the "AMS" operating system that is installed on TI-89, TI-92 Plus or V200 calculators by default. Running them on unofficial operating systems might produce an error message or crash the calculator. If you write
#define UNOFFICIAL_OS_SUPPORT
at the top of each source file, most of the hacks that require the original
"AMS" software to be installed are turned off, generating error messages at
compile time instead. Defining a higher
MIN_AMS
value fixes the
compile-time errors, but this might also prevent the program from being run
on certain unofficial operating systems.
Of course, this definition does not guarantee that any particular
unofficial operating system can run the program.
See also: REJECT_UNOFFICIAL_OS
If your program uses a hack that is likely to fail in unofficial operating systems, you should write
#define REJECT_UNOFFICIAL_OS
at the top of at least one source file. This checks against all known unofficial operating systems (currently only a yet unreleased system called "PedroM"), before actually executing the program. However, do not abuse this feature to reject unofficial operating systems because you do not like the idea of unofficial operating systems, or because you know that your program does not work on a particular system. Valid uses of this feature are rare.
See also: UNOFFICIAL_OS_SUPPORT
If you are writing a function archive (also known as a static library), you will probably want to write:
#define _GENERIC_ARCHIVE
at the top of every C file that includes a header file from the TIGCC
Library. It tells the TIGCC Library that you are creating a function archive
(static library) which you want programs to be able to use no matter what
their compiler, TIGCCLIB, linker and output format settings are. It disables
all optimizations which rely on startup code, and it makes the library use a
kernel-independent ROM call mechanism. It also disables outputting of any
linker control symbols which force a specific output format.
All static libraries should use this option unless they are created for a
very specific program (which implies you are probably better off linking the
files directly into your project). However, some special features of the
TIGCC Library may not be available.
The TIGCC linker provides a lot of settings to
control the optimization of binary code. When using the
IDE, you can set these options in the "Linking"
tab of the project settings. When using the command-line compiler, you can
use the linker switches.
To make full use of section reordering, you should pass
'-ffunction-sections' and '-fdata-sections' to the compiler.
The following GCC switches can possibly generate better code. However, some of them can also increase code size, decrease speed, or even produce invalid code if they are used incorrectly:
'-O1', '-O2', '-O3', '-Os'
'-fomit-frame-pointer'
'-mregparm'
'-freg-relative-reg'
'-fzero-initialized-in-bss'
'-mno-bss'
The following GNU assembler switches can possibly generate better code:
'-l'
'--short-jumps'
Program comments are data supplied by the author of a program, to provide additional information for users. Some or all of the information may be ignored depending on the type of program or library you are creating. Currently only nostub programs can handle the full range of information; kernel programs are restricted to the simple comment string. For Nostub DLLs, the information is not ignored (so it will waste space in the DLL), although it should be.
To define a comment string, write
#define COMMENT_STRING "comments"
at the top of your source code. If supported by the target format, this will include the text inside the quotes as a comment string. It can be arbitrary text, but as a general rule, it should fit into the status line of a TI-89.
To define a name for your program, write
#define COMMENT_PROGRAM_NAME "name"
at the top of your source code. If supported by the target format, this will store the text inside the quotes as a program name. The name can be longer than eight characters, but it should not include any additional information such as a version number or author name(s).
To include user-visible version information in the program, write
#define COMMENT_VERSION_STRING "version"
at the top of your source code. If supported by the target format, this will
include the text inside the quotes as a version string. It can have an
arbitrary format, but it should be recognizable as a program version, for
example major.minor
.
See also: COMMENT_VERSION_NUMBER
To include structured version information in the program, write
#define COMMENT_VERSION_NUMBER major, minor, revision, subrevision
at the top of your source code. If supported by the target format, this will include some or all of the numbers (which must be between 0 and 255) inside the program. Structured version information enables other programs such as shells to do version comparison and checks.
See also: COMMENT_VERSION_STRING
To include the names of the author(s) of your program, write
#define COMMENT_AUTHORS "author(s)"
at the top of your source code. If supported by the target format, this will store the text inside the quotes as author information. The name can be arbitrarily long, but don't include any information other than the names of one or more authors, authoring teams or entities. You should also keep in mind that these names take up place in your program and that they are intended to be displayed by calculator shells with limited screen space, so consider using a team name rather than a long list of individuals.
You can include a 16x16 pixel icon in your program, for display in shells. To do this, write
#define COMMENT_BW_ICON {line0, line1, ..., line15}
at the top of your source code. Each of the lines is specified as a 16 bit number, with the most significant bit being the leftmost pixel. If you use binary numbers to define the numbers, you can easily see what the icon will look like. The icon is defined in the same format as an ICON structure.
See also: COMMENT_GRAY_ICON, ICON
If you have an icon in your program (see
COMMENT_BW_ICON
), you can
enhance it using grayscale. To do this, write
#define COMMENT_GRAY_ICON dark, light
at the top of your source code, where dark and light are
the dark and light plane parts of the icon as defined in
COMMENT_BW_ICON
. For the
dark plane part, you might actually want to use
COMMENT_BW_ICON
.
See also: COMMENT_BW_ICON, gray.h
Incompatibility data can be used to specify that running the program outside of the usual calculator home screen environment may be problematic. Not all program formats permit storing incompatibility data; in particular, currently only the nostub format does. However, in a usual program, you will never have to set any of these incompatibility flags; most of them only apply to TSRs (programs that hook into the operating system and stay in memory after termination).
If your program allocates memory which may not be freed automatically after exiting the program, and which does not belong to a VAT symbol or home screen item, you should write
#define INCOMPAT_CREATES_HANDLES
at the top of at least one source file, before including any header file from the TIGCC Library. This especially refers to TSRs that allocate memory. If a shell or another tool freed the memory, the calculator would crash sooner or later. This directive should prevent such tools from automatically freeing memory allocated by the program.
If your program is a TSR that hooks a trap (see SetIntVec), you should write
#define INCOMPAT_USES_TRAPS
at the top of at least one source file, before including any header file from the TIGCC Library. If this directive is not used, a shell might automatically restore the hooked trap, rendering the TSR useless. Or it might even just free other resources allocated by the program, causing a crash.
See also: INCOMPAT_USES_VECTORS
If your program is a TSR that hooks an interrupt or exception (see SetIntVec) or makes any other change to the interrupt vector table other than traps, you should write
#define INCOMPAT_USES_VECTORS
at the top of at least one source file, before including any header file from the TIGCC Library. If this directive is not used, a shell might automatically restore the hooked interrupt, rendering the TSR useless. Or it might even just free other resources allocated by the program, causing a crash.
See also: INCOMPAT_USES_TRAPS
If your program is a TSR that hooks events (see EV_hook), you should write
#define INCOMPAT_USES_EV_HOOK
at the top of at least one source file, before including any header file from the TIGCC Library. If this directive is not used, a shell might automatically remove the event hook, rendering the TSR useless. Or it might even just free other resources allocated by the program, causing a crash.
If your program needs the entire stack (or almost the entire stack) to be free, you should write
#define INCOMPAT_NEEDS_ALL_STACK
at the top of at least one source file, before including any header file from the TIGCC Library. This will prevent shells that fill up part of the stack from executing the program.
In some cases it might be desirable to use features which are only present in the latest versions of the AMS. In this case, you need to define the minimum version the program will run under explicitly, like this:
#define MIN_AMS 200
MIN_AMS
has to be a 3-digit decimal number, which is obtained by
multiplying the AMS version with 100 (or omitting the dot, if the version has
the format "x.xx").
If you do not specifiy a sufficient minimum AMS version, the functions and
constants which are not available will not even be defined. Note that
MIN_AMS
defaults to 101, which means AMS 1.01 on the TI-92 Plus,
and AMS 1.00 on the TI-89. These two versions are in fact the same.
You can bypass the AMS check which is performed at startup by defining
NO_AMS_CHECK
.
To find out about the different ways we try to overcome AMS dependencies,
read the next section,
Overcoming AMS Dependencies.
Many AMS dependencies result from the unavailability of specific ROM calls in the operating system. The AMS maintains a table of pointers of functions, the jump table (see __jmp_tbl in default.h). In early AMS releases this jump table was very incomplete, and it even changed at some places. Nevertheless, we try to maintain compatibility as much as possible. There are five types of workarounds for AMS dependencies:
If a function is not in the jump table (at least in early versions of the
AMS), there are often ways to obtain its address manually through some very
dirty hacks. If possible, these hacks are really only used in those AMS
releases where the jump table entry is missing, so the hacks are usually
safe. The only exception is when the function is not listed in any jump
table. If this is the case, the documentation will contain the type
"Function (ROM Call)", but without an index.
If this is not the case, you can usually gain speed and decrease the program
size by defining a suitable minimum AMS version.
See also: _rom_call_hack
Sometimes a ROM function exists as a normal ROM call in the jump table, but
for some reason it is not usable in a direct way. This means that it has to
be implemented either using macros or using a wrapper function in
tigcc.a
.
This does not have to be related to an AMS dependency; it may also be
because of a bug in the AMS, or because of some missing feature.
Sometimes a ROM function exists as a normal ROM call in the jump table, but
for some reason it is not usable in a direct way. This means that it has to
be implemented either using macros or using a wrapper function in
tigcc.a
. Using a macro prevents you from taking the address of
the function and therefore from using it as a callback function.
This does not have to be related to an AMS dependency, although it is in the
special case of XR_stringPtr.
In fact, all functions returning
float
values are
implemented in this way, since the TIOS method of returning floats is strange
to say the least.
Sometimes it is hard or tedious to obtain the address of an AMS-dependent function in a nonstandard way. In this case, the function may be reimplemented in tigcc.a. Often the user-defined minimum AMS version is used to determine whether this implementation or the real ROM function should be used.
Sometimes it is hard or tedious to obtain the address of an AMS-dependent function in a nonstandard way. In this case, the function may be implemented as a macro which works in all (or almost all) AMS versions. Often the user-defined minimum AMS version is used to determine whether this macro definition or the real ROM function should be used. Like every macro definition, this prevents you from taking the address of the function and therefore from using it as a callback function.