Making TIGCC Programs

Making Kernel-Less (NoStub) Programs

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:

#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


Making Kernel-Based Programs

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:

See also: doors.h


Advanced Options of TIGCC

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

SAVE_SCREEN

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.

OPTIMIZE_ROM_CALLS

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.

KERNEL_FORMAT_ROM_CALLS

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

MLINK_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

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

USE_FLINE_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

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

USE_4_BYTE_FLINE_JUMPS

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

KERNEL_FORMAT_RELOCS

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

MLINK_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

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

MERGE_BSS

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

KERNEL_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

MLINK_FORMAT_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

COMPRESSED_FORMAT_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

OMIT_BSS_INIT

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.

KERNEL_FORMAT_DATA_VAR

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

MLINK_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

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

OPTIMIZE_CALC_CONSTS

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_....

NO_CALC_DETECT

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.

EXECUTE_IN_GHOST_SPACE

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.

STRICT_POINTERS

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

SET_FILE_IN_USE_BIT

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

UNOFFICIAL_OS_SUPPORT

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

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

_GENERIC_ARCHIVE

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.

Linker Optimization Facilities

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.

GCC Optimization Switches

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:

GNU Assembler Optimization Switches

The following GNU assembler switches can possibly generate better code:


Program Comments

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.

COMMENT_STRING

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.

COMMENT_PROGRAM_NAME

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).

COMMENT_VERSION_STRING

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

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

COMMENT_AUTHORS

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.

COMMENT_BW_ICON

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

COMMENT_GRAY_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 Information

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).

INCOMPAT_CREATES_HANDLES

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.

INCOMPAT_USES_TRAPS

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

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

INCOMPAT_USES_EV_HOOK

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.

INCOMPAT_NEEDS_ALL_STACK

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.


Defining a Minimum AMS Version

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.


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:

Manually Obtaining the Address of a ROM Function

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

Wrapping a ROM Function in tigcc.a

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.

Wrapping a ROM Function with a Macro

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.

Reimplementing a ROM Function in tigcc.a

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.

Reimplementing a ROM Function as a Macro

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.


Return to the main index