Frequently Asked Questions

The FAQ list is organized into several categories. In each category, you will see a list of commonly asked questions in short form. Please click on a question that interests you to get the full form of the question and the related answer. Note, however, that some questions/answers are related to each other, and some answers are logical consequences of previous ones. So, after reading the answer, it is recommended to browse through surrounding quesion/answer pairs too, to get some more information.

Also, please visit the TICT & TIGCC/GCC4TI Programming Message Board and the GCC4TI message board. A lot of interesting topics about TIGCC/GCC4TI programming are discussed there.


The C Language


TI-Basic and C


Graphics and Display


Assembly and C


Strings


Event-driven Programming, Menus, and Throwing Errors


TI Variables and the Variable Allocation Table (VAT)


Memory, C Variables, and Pointers


Input: Keyboard and Link


Miscellaneous


Where can I learn more about C?

Q: Is there any examples available on the net from which it is possible to learn something more...
A: The best solution would be if I wrote a tutorial. Unfortunately, I have not enough time to write tutorials, small examples, etc. Some examples come with TIGCC starting from release 0.6, but this is probably not enough. I know that some people work on making a tutorial, but as far as I know, all of them are in a very early state. I suggest first learning C language (not C++) on some big computer (PC, Mac, etc.). A very good list of generic C language tutorials may be found on the TI-Chess Team Home Page. After learning the basics of C, reading the documentation of GCC4TILIB will be enough to learn how to apply this to TI programming. In addition, try looking at my programs like creversi, cblaster and scott (scott is in the advint.zip archive); all of them are in the TI-89 assembly games directory on ticalc.org. Note that every day the number of TI programs written in C increases, so now there are lots of open-source C programs which can be found on ticalc.org.

Will you implement C++?

Q: I learn C++ in a school so it will be good if I can program my TI-89 in C++ (not in ordinary C). Do you plan to implement C++ on TI-89?
A: No. Although C++ is more powerful language than C, it is not a language which is good for TI calculators. It is not efficient enough to be good for a calculator. The code generated by C++ is less efficient than code generated by ordinary C, and it is too bloated. So, even if somebody made C++ compiler for TI, I don't recommend using any C++ extensions (like classes, and especially streaming), except if you like programs like

cout << "Hello world";

which produces 5 Kb long code...

I get weird error messages where I don't see errors.

Q: The compiler sometimes report strange error messages on places where I really can not see any errors. For example, the compiler reports the error

parse error before 'void'

but the statement on which the error is reported was

int cexp;

I am really confused. First, I don't see any errors here, and second, I can't see any 'void' keywords here!

A: Yes, such problems may really be the source of big frustrations. There is nothing wrong with above statement, but note that 'cexp' is a function defined in timath.h header file, and you can not use this name as a name of a variable. Now you can say why you got such strange error message? See, the most of functions in GCC4TILIB are translated by the preprocessor into constructions which perform indirect function calls through a TIOS jump table. In other words, 'cexp' will be replaced by the preprocessor into the indirect function call constructor whenever it is used. So, the innocent statement like

int cexp;

will be converted into

int (*(void(**)(float,float,float*,float*))(*(long*)0xC8+1316));

which is a syntax error. And the error is just before 'void'.

I can not do anything against such hidden errors. Whenever you encounter strange errors without obvious reasons, check whether you used reserved library name for your identifier. The chance of making such errors is much smaller if you include only the necessary header files than if you include the general header file tigcclib.h.

Why do I get "Undefined Reference to..." errors?

Q: When I tried to compile my program, the linker reports to me

Undefined reference to ...

although my program seems correct. What's a problem?

A: Either something is wrong with your program (more probably you used a function which is not defined elsewhere, maybe due to typing error), or you used something which is not yet implemented yet in GCC4TI (which is probably true if you got an undefined reference to a symbol which begins which the double underscore '__'). For example, although GCC4TI supports very long (64-bit) integers ('long long' type, which is a GNU C extension), the support for multiplying and dividing double longs is not supported yet. For example, if you try to divide two double-long numbers, you will get an undefined reference to '__udivdi3'. Sorry, there is no simple help for this. You must live without 64-bit division for now. It may be implemented in the future.

How can I make a floating point comparison function for use with qsort?

Q: How I can define a comparison function for sorting an array of floats using qsort function:
A: Here is a simple example (called "Sort Floats"):

// Sort a list of floating point values

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Comparison Function
CALLBACK short flt_comp(const void *a, const void *b)
{
  return fcmp (*(const float*)a, *(const float*)b);
}

// Main Function
void _main(void)
{
  float list[5] = {2.5, 3.18, 1.42, 4.0, 3.25};
  int i;
  clrscr ();
  qsort (list, 5, sizeof (float), flt_comp);
  for (i = 0; i < 5; i++)
    printf ("%f\n", list[i]);
  ngetchx ();
}

Why do I get the message "invalid lvalue in unary '&'"?

Q: Well, I need to know more about lvalues and GNU C initializers and cast constructors. Especially, I need to know why I sometimes get an error message "invalid lvalue in unary '&'"...
A: Click here to see everything about GNU C extensions. But if you are lazy to read too much, I will be more concrete here. First, in GNU C (GCC4TI is GNU C), initializers may be non-constants. For example,

int a = 3, b = 4, c = 5, d = 6;
int array[4] = {a, b, c, d};

is quite legal. That's why

int a = 3, b = 4, c = 5, d = 6;
SCR_RECT myScr = {{b + a, b - a, d + c, d - c}};

is quite legal too. Second, GNU C has one very nice extension in addition to ordinary C: cast constructors. This is a method for constructing structures, arrays, unions etc. "on fly" by using a typecasting of an initializer to an appropriate data type, for example

(SCR_RECT){{10, 10, 50, 50}}

So, you can use

SCR_RECT myScr;
...
myScr = (SCR_RECT){{10, 10, 50, 50}};

which is impossible in ordinary C (ANSI C). You can even use

myScr = (SCR_RECT){{a, b, c, d}};

where a,b,c,d are expressions. Well, but what is now the problem? See, C has two type of objects: lvalues and non-lvalues. lvalues are objects which may appear on the left size of an assignment. For example, a variable is an lvalue and a constant is not an lvalue, because 'x=5' is legal and '5=x' (or '5=3') is not legal. Not only variables are lvalues; for example, dereferenced pointers are also lvalues, so this is legal for example (store 100 at address 0x4c00):

*(char*)0x4c00 = 100;

So, '*(char*)0x4c00' is an lvalue. Now, about the problem. In GNU C, cast constructors are lvalues only if the initializer is completely constant. I.e. '(SCR_RECT){{10,10,50,50}}' is an lvalue, but '(SCR_RECT){{a,b,c,d}}' is not. As C language accepts unary '&' operator (i.e. "address of") only on lvalue objects, this means that, for example,

&(SCR_RECT){{10, 10, 50, 50}}

is legal, but

&(SCR_RECT){{a, b, c, d}}

is not! This is the real cause of the problem!!!

What you can do if you need an address of non-constant cast constructor? You need to declare an auxilary variable. For example, declare one SCR_RECT variable, say myScr,

SCR_RECT myScr;

and instead of

ScrRectFill (&(SCR_RECT){{a, b, c, d}}, ScrRect, A_XOR);

use:

myScr = (SCR_RECT){{a, b, c, d}};
ScrRectFill (&myScr, ScrRect, A_XOR);

Note that '&myScr' is legal, because 'myScr' is an lvalue (it is an ordinary variable). I hope that this helps a lot understanding of cast constructors and lvalues.

Can I use # (indirection) like in TI-Basic?

Q: I wonder how I can simulate the indirection operator ('#') from TI-Basic in C programs...
A: Nothing equivalent to indirection ('#') exists in any compiling language (like C), since the variable names do not appear in the compiled program. You need to make up your mind to avoid this operator. For indirect references to variables in C, you can use pointers. However, usually you can re-express the code using multiple if-else statements or arrays. Don't be afraid, C will process it 1000 times faster than TI-Basic processes indirections.

I need the C equivalent of the when() function from TI-Basic.

Q: I need the C equivalent of the 'when()' function from TI-Basic.
A: This is simple:

when (condition, true_val, false_val)

is translated to C as

condition ? true_val : false_val

For example,

sign = x >= 0 ? 1 : -1;

Happy?

I tried to use sizeof to get the size of an object, but it returned zero!

Q: When I tried to use the sizeof operator to determine the exact size of some objects, I got zero as the result. What is wrong?
A: You probably tried something like

printf ("%d", sizeof (something));

The ANSI standard proposes that the sizeof operator returns a value of type size_t, which is in fact long integer in this implementation. So, the result is pushed on the stack as a long integer, but the format specifier "%d" expects an ordinary integer, so it pulls from the stack just one word, which is zero in this case. You need to write

printf ("%ld", sizeof (something));

Alternatively, you can use a typecast to convert the result to a short integer

printf ("%d", (short) sizeof (something));

assuming that no object would be longer that 32767 bytes.

Can I use sizeof to determine the exact size of functions?

Q: Is it possible to determine the size of the function using the sizeof operator?
A: No. In "normal" programs you should never know this information. ANSI C even does not propose what is 'sizeof(function)', and such construction will be rejected by the most of C compilers. GNU C (like GCC4TI is) uses extended pointer arithmetic on such way that 'sizeof(function)' is always 1. If you are a dirty hacker (as I am), and if you really need to determine the number of bytes occupied by function, I used the following method:

void MyFunction(void)
{
  // The function body...
}
void End_Marker(void);
asm("End_Marker:");
...
...
num_of_bytes = (char*)End_Marker - (char*)MyFunction;

Note however that this method is not absolutely reliable, because it depends of the ordering of functions in the program. But, the compiler is free to change the order of functions if such reorganization may lead to a better code.


RETURN_VALUE doesn't work on AMS 2.xx!

Q: I have problems with programs which return values to the TI-Basic using RETURN_VALUE directive!
A: Yes, this is a problem with AMS 2.xx. For some strange reasons AMS 2.xx does not allow ASM programs to be part of expressions any more, i.e. if 'xyz' is an ASM program, 'xyz(3,2)+5' or 'xyz(3,2)->a' is not legal in AMS 2.xx. Fortunately, there is a solution. Read what I wrote about this problem in the section How to return values to the TI-Basic.

How can I display a PIC variable created in TI-Basic?

Q: Is it hard to display a PIC variable created from TI-Basic in C programs?
A: No, it is easy. It is enough to locate the variable (using SymFind for example), and to display its content using BitmapPut (its contents are just as expected by BitmapPut function). To be more concrete, look at the following function:

short show_picvar (SYM_STR SymName, short x, short y, short Attr)
{
  SYM_ENTRY *sym_entry = SymFindPtr (SymName, 0);
  if (!sym_entry) return FALSE;
  if (peek (HToESI (sym_entry->handle)) != PIC_TAG) return FALSE;
  BitmapPut (x, y, HeapDeref (sym_entry->handle) + 2, ScrRect, Attr);
  return TRUE;
}

The usage of this function is straightforward, for example:

show_picvar (SYMSTR ("testpic"), 30, 30, A_NORMAL);

assuming that "testpic" is the name of the wanted PIC variable. This function returns TRUE if the operation was successful, else returns FALSE (i.e. the picvar does not exist, or it is not a PIC variable).

How can I execute TI-Basic programs or statements from C?

Q: Is there any way to execute a file (i.e. another ASM or TI-Basic program) from a C program?
A: There are a lot of methods for doing this. The most obvious method to do this is usage of a function like this one:

void progrun(const char *name)
{
  char fname[25];
  HANDLE h;
  strcpy (fname, name);
  strcat (fname, "()");
  push_parse_text (fname);
  h = HS_popEStack ();
  TRY
    NG_execute (h, FALSE);
  FINALLY
    HeapFree (h);
  ENDFINAL
}

The usage of it is straightforward, for example:

progrun ("testprog");

Note that the program you call may throw errors. If you understand this function, you can easily expand it to accept arguments, etc. Principally, using NG_execute you can execute any particular sequence of TI-Basic statements.

See also: How can I create a program that is bigger than 24K and works on AMS 2.xx?

Can I write a program that returns a value to TI-Basic?

Q: Is it possible to write a program which will return a value to TI-Basic, i.e. which acts like functions in TI-Basic?

This is easy, was implemented a long time ago, and is described on the starting page of the documentation.

Why is 'a=GetIntArg (top_estack);' wrong?

Q: What is wrong in doing

a = GetIntArg (top_estack);

It seems that it works fine, but you always use an auxilary variable...

A: It works fine sometimes, but not always. See, GetIntArg is a function-looking macro, with changes the value of its actual argument. So, if you write

a = GetIntArg (top_estack);

you will also change the value of TIOS system variable top_estack, and I am not sure that you really want this. So, I strictly recommend using an auxilary variable, like in the following example:

ESI argptr = top_estack;
...
a = GetIntArg (argptr);

Using this method, you will avoid unexpected changes of top_estack.

How can I get a value from a TI-Basic matrix element?

Q: I have a matrix on the top of the expression stack which is produced as a result of calculation, and I don't know how do I put a matrix element located at [i,j] into result (values of 'i' and 'j' may vary)? I have read infos about estack.h, but I couldn't find the answer...
A: There is a lot of methods. I suggest the following one:

ESI ptr;
int result;
int i = 1;                             // Just an example
int j = 2;
push_parse_text ("[[11,12][21,22]]");  // An example matrix
ptr = locate_element (i,j);
result = GetIntArg (ptr);              // (assumed that elements are ints)

where 'locate_element' is an user-written function, which may be implemented as follows:

ESI locate_element (short m, short n)
{
  short i;
  ESI ptr = top_estack-1;
  for (i = 0; i < m-1; i++) ptr = next_expression_index (ptr);
  ptr--;
  for (i = 0; i < n-1; i++) ptr = next_expression_index (ptr);
  return ptr;
}

You can use it as-is, but it will be much better if you can understand how it works.

How can I make my C function return itself?

Q: Suppose I'm writing an ASM function 'foo(n)' which accepts an argument (a string, for example). I want to make 'foo(n)' return 'foo(n)' (the call itself) when 'n' is of type "VAR" (i.e. if nothing has been assigned to 'n' yet)...
A: This is quite easy, if you know in advance the name of the program. Suppose, that your program name is "example". Here is the demo (called "Function Returning Itself") which will return 'example(n)' if you type 'example(n)' if 'n' is a variable, and which will return the string "blabla" (for example) if the argument is something else:

// A function returning itself

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define RETURN_VALUE          // Return pushed expression
#define MIN_AMS 101           // Compile for AMS 1.01 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  ESI argptr = top_estack;
  if (GetArgType (argptr) <= VAR_Q_TAG)  // it means that arg is a variable
                                         // see Tags to see why...
    push_expr_quantum (SYMSTR ("example"), USERFUNC_TAG);
  else
    {
      while (ESTACK (top_estack) != END_TAG)
        top_estack = next_expression_index (top_estack);
      top_estack--;
      push_string (SYMSTR ("blabla"));
    }
}

Note that this solution is not ideal: if you rename the program name to something else, the function will still return 'example(n)'. It is possible to determine the real name of the program in the run time, but this is very awkward.


How can I display a number variable on the screen (like an int)?

Q: How can I convert an integer or a float to a string?
A: It's easy. Use sprintf. For example:

char string1[50];
char string2[50];
short int var1;
float var2;
...
sprintf (string1, "%d", var1);
sprintf (string2, "%f", var2);

That's why there is no need for functions like itoa and ftoa in opposite to atoi and atof.

Do you plan on implementing 7-level grayscale?

Q: Do you plan to implement 7-level grayscale mode?
A: So far no, because nobody yet implemented 7-level grayscale which works stable on both HW1 and HW2 calculators. I don't want to implement features which works only on HW1 calculators.

How can I use sprites?

Q: Do you know how I can use sprites with GCC4TI ?
A: Another common question. The answer depends on the size of the sprite. If your sprite is not wider than 32 pixels (which is likely), then you can now use the sprites.h header file. This header file defines fast functions which work with sprites (including masked sprites). The only limitation is that the sprite must not be wider than 32 pixels (the height is not limited).

What to do if you want sprites wider than 32 pixels, and don't want to use kernel-based programming? The answer depends on what the usage of this sprite will be. If you don't need fast action, the built-in function BitmapPut may be good enough. If you need a very fast sprite routine, you may be better off using the ExtGraph library by the TI-Chess Team.

I don't understand how to define sprites!

Q: I can't understand how sprites are defined; I looked in many program sources, and every sprite definition looks for me as an array of random hex numbers!?
A: Well, suppose that you want to make a sprite which is a filled circle. Make a grid on the paper, and make a sprite shape by filling grid squares. Then, replace each filled square with 1 and each blank square with 0. In above example, it may look like:

00111000
01111100
11111110
11111110
01111100
00111000

Then, produce rows as a set of binary numbers, and convert them to hex. For example:

00111000 binary = 38 hex
01111100 binary = 7C hex

etc. These hex numbers describe the sprite, i.e. the sprite definition should be

unsigned char sprite [] = {0x38, 0x7C, ...};

assuming that Sprite8 will be used. That's all...

Note: GCC4TI also supports binary numbers (0b...).

How do you use sprite functions from sprites.h with grayscale?

Q: I would like to know if it is possible to use the sprite functions in sprites.h with grayscale. I have tried using the Sprite16 function with grayscale, and none of the sprites I intended to be in grayscale appeared. I tried to use DrawIcon and the grayscale worked just fine. But DrawIcon is too slow... Can Sprite16 and Sprite32 handle grayscale sprites.
A: Very common question in a recent time. See, you probably tried to use GraySetAMSPlane. Sprite16 have a parameter for drawing plane, so it is not sensitive to GraySetAMSPlane: you need to give the plane as an explicite parameter. In fact, you need to have two different sprite planes and to draw a sprite twice, passing two different planes as a parameter. For example,

static unsigned short light_definition [] = {...};
static unsigned short dark_definition [] = {...};
...
Sprite16 (x, y, height, light_definition, GrayGetPlane (LIGHT_PLANE), A_XOR);
Sprite16 (x, y, height, dark_definition, GrayGetPlane (DARK_PLANE), A_XOR);

In other words, sprite routines can handle grayscale sprites, but not natively, meaning you have to take your grayscale sprite, split it into two layers, and draw each one separately on its own plane - the routine does not handle these by itself. As suggested by Scott Noveck, it is possible to make a function of your own to handle this. Assume that your grayscale sprites follow the "standard" format seen in the most of ASM games, with the dark plane data followed immediately by the light plane data. This routine will call Sprite16 twice - once for each plane:

void GraySprite16 (short x, short y, short h, unsigned short *spr, short mode)
{
  Sprite16 (x, y, h, *spr, GetPlane (LIGHT_PLANE), mode);
  Sprite16 (x, y, h, *spr + h, GetPlane (DARK_PLANE), mode);
}

Don't be afraid about calling GrayGetPlane each time: it is not a waste of time. Its implementation is smart: when the input is a constant, it will simply evaluate to a memory address that contains the pointer; when it is variable, it expands to a simple macro.

How does BitmapGet work?

Q: Can you give me info on how BitmapGet works. I've tried everything that I know and I still get protected memory errors. The manual just doesn`t give anything solid to base a few lines of code on.
A: You probably didn't allocate enough space to store a bitmap. The simplest way to do so is given in this example (called "Bitmap Test"):

// Retrieve and store a bitmap

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization

#define MIN_AMS 100           // Compile for AMS 1.00 or higher

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  SCR_RECT full_screen = {{0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1}};
  char buffer [BITMAP_HDR_SIZE + LCD_WIDTH*LCD_HEIGHT/8]; // or 2004 for a TI-89 and 3844 for a TI-92+/V200 if you like it more
  BitmapGet (&full_screen, buffer);          // store screen in buffer
  clrscr ();
  printf ("Press any key to\nrestore screen...");
  ngetchx ();
  BitmapPut (0, 0, buffer, &full_screen, A_REPLACE);
  ngetchx ();
}

Note that this is just an example: for saving/restoring the whole screen, the functions LCD_save and LCD_restore are much more efficient! Moreover, buffer will probably be allocated using malloc in a more realictic example.

How can I create a virtual screen?

Q: How I can change the port for the screen memory to draw to it, and then copy it back to the regular address?
A: Do this:

void *virtual = malloc (LCD_SIZE);  // Allocate the buffer
...
if (!virtual) ... // do some error handling - not enough memory!
PortSet (virtual, 239, 127); // redirect drawing routines to buffer

or, even simpler, virtual screen may be simply in any local variable which is enough long:

char virtual[3840];
...
PortSet (virtual, 239, 127);

Note that, in this case, virtual memory will be in fact somewhere on the stack. There is nothing bad in this, but keep in mind that the total amount of the stack is 16K, so don't put TOO MANY data (like big arrays etc.) on the stack (i.e. in local variables). If you really need to handle a lot of data, use malloc instead.

After this, do any drawing you want - it will be redirected to the virtual screen. To copy this to the regular screen (i.e. to display it) do this:

memcpy (LCD_MEM, virtual, LCD_SIZE);

or even simpler (this is the same):

LCD_restore (buffer);

And, don't forget to do PortRestore before end of the program, else TIOS will be fooled after returning to TI-Basic!

How can I set up a SCR_RECT structure?

Q: How I can setup properly a SCR_RECT structure? For example, the FillTriangle function requires the parameter of SCR_RECT type. How to put coordinates into it, so that I can change the clipping area for the screen?
A: If all coordinates of clip area are known in advance (for example 5, 5, 90, 70), do this:

FillTriangle (10, 10, 10, 50, 50, 50, &(SCR_RECT){{5, 5, 90, 70}}, A_NORMAL);

or, using "standard" C (i.e. without GNU extensions):

SCR_RECT area = {{5, 5, 90, 70}};  // somewhere in the declaration part
...
FillTriangle (10, 10, 10, 50, 50, 50, &area, A_NORMAL);

Note that double braces are necessary because SCR_RECT is an union.

If coordinates are not known in advance, for examples if they are in integer variables a, b, c and d, you can do this:

SCR_RECT area;
...
area.xy.x0 = a;
area.xy.y0 = b;
area.xy.x1 = c;
area.xy.y1 = d;
FillTriangle (10, 10, 10, 50, 50, 50, &area, A_NORMAL);

or, much simpler, using GNU C extensions:

FillTriangle (10, 10, 10, 50, 50, 50, &(SCR_RECT){{a, b, c, d}}, A_NORMAL);

How can I make a WINDOW structure?

Q: I would like to make my own window but I don't understand the lot of fields in the WINDOW structure, like 'TaskID' and others!
A: You do not need to fill up the WINDOW structure manually: function WinOpen will fill everything automatically. See the next question for examples of usage.

How do I use functions from the wingraph.h file?

Q: I need some examples of how to use the functions in the wingraph.h header file. Especially, I am confused with static and/or dynamic data allocation. I tried to use alloc.h to create necessary structures, but something was wrong...
A: I will give five very similar examples how to do the same thing. From the first and fifth example you will see that you need not to use alloc.h at all, and in the second, third and fourth example, you will see what you need to do if you want to use dynamic allocation (i.e. alloc.h) anyway. Maybe this is not so obvious from my documnetation, but flags in WinOpen must be ORed, so they must be "separated" by "|", not by commas.

Example 1: Using wingraph.h without dynamic allocation (called "Window 1"):

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  WINDOW wind;
  WIN_RECT winr = {20, 20, 80, 50};
  WinOpen (&wind, &winr, WF_SAVE_SCR | WF_TTY);
  WinActivate (&wind);
  WinFont (&wind, F_6x8);
  WinStr (&wind, "hello everyone");
  ngetchx ();
  WinClose (&wind);
}

Example 2: Window is allocated dynamically (called "Window 2"):

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  WINDOW *wind = HeapAllocPtr (sizeof (WINDOW));
  WIN_RECT winr = {20, 20, 80, 50};
  WinOpen (wind, &winr, WF_SAVE_SCR | WF_TTY);
  WinActivate (wind);
  WinFont (wind, F_6x8);
  WinStr (wind, "hello everyone");
  ngetchx ();
  WinClose (wind);
  HeapFreePtr(wind);
}

Note that synonyms for HeapAllocPtr and HeapFreePtr are malloc and free (like in ANSI C).

Example 3: Both "window" and "rect" are allocated dynamically (called "Window 3"):

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  WINDOW *wind = HeapAllocPtr (sizeof (WINDOW));
  WIN_RECT *winr = HeapAllocPtr (sizeof (WIN_RECT));
  winr->x0 = 20; winr->y0 = 20;
  winr->x1 = 80; winr->y1 = 50;
  WinOpen (wind, winr, WF_SAVE_SCR | WF_TTY);
  WinActivate (wind);
  WinFont (wind, F_6x8);
  WinStr (wind, "hello everyone");
  ngetchx ();
  WinClose (wind);
  HeapFreePtr (wind);
  HeapFreePtr (winr);
}

Example 4: How to use MakeWinRect to avoid "winr" (called "Window 4"):

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  WINDOW *wind = HeapAllocPtr (sizeof (WINDOW));
  WinOpen (wind, MakeWinRect (20, 20, 80, 50), WF_SAVE_SCR | WF_TTY);
  WinActivate (wind);
  WinFont (wind, F_6x8);
  WinStr (wind, "hello everyone");
  ngetchx ();
  WinClose (wind);
  HeapFreePtr (wind);
}

Example 5: This is what I do in my programs (called "Window 5"):

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 100 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  WINDOW wind;
  WinOpen (&wind, &(WIN_RECT) {20, 20, 80, 50}, WF_SAVE_SCR | WF_TTY);
  WinActivate (&wind);
  WinFont(&wind, F_6x8);
  WinStr (&wind, "hello everyone");
  ngetchx ();
  WinClose (&wind);
}

Don't forget to close a window before exiting. If you forget to do so, the TI may crash later, when window manager tries to refresh a still active window which ceased to exist due to end of the program!

In general, I prefer static allocation instead of dynamic. It is good if you know in advance how many open windows you have in the program (this is often a case on TI). Dynamic allocation is the only method if you don't know in advance how many open windows you need (then, you can keep them in linked list). I don't think that there is a lot of use for this on the TI. :-)

How do you use DrawIcon?

Q: I don't understand how to use DrawIcon. Do I need to use pICON?
A: If you want to draw an icon at 50,50 for example, do:

ICON i = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
...
DrawIcon (50, 50, &i, A_NORMAL);

Of course, change 1, 2, 3... to the real definition of the icon.

pICON is necessary only for using with icons which are dynamically allocated (forget it if you are not familiar with dynamic structures in C), like in:

pICON p = malloc (32);
...
// Here is a code which fill up the icon structure
...
DrawIcon (50, 50, p, A_REPLACE);

Look operator "&" in first example, it is omitted in second example. In fact, "&" converts a variable of ICON type to pICON type (in general, it converts any type to corresponding pointer type).

I need a really fast line-drawing routine...

Q: I want to make a program which draw a lot of lines (for example which does some kind of 3D graphic). But, it seems that DrawLine function is not too fast for my purposes. What I can do?
A: Yes, TIOS drawing routines are not championes in speed. Note that really fast line drawing function may be written only in ASM. But don't underestimate the power of C: even in pure C it is possible to make better (read: faster) line drawing routine than TIOS routine. As a programming exercise, I started with one trivial line drawing routine, and tried to optimize it. What I got is the routine which is in general 3-4 times faster than TIOS DrawLine routine. In some special cases, TIOS routine may be faster (for example, TIOS routine handles special cases when the line is horizontal or vertical), but in general case (slope lines) my routine is much better. Maybe (and probably) it may be speed-optimized yet; this is a challenge for you. Here is the routine (example "Draw Line"):

// Example of a fast line-drawing routine

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Draws a line from (x1,y2) to (x2,y2).
void DrawLineFast(short x1, short y1, short x2, short y2)
{
  short x = x1, y = y1;
  short dx = abs (x2 - x1), dy = abs (y2 - y1);
  short ystep = (y1 < y2) ? 1 : -1, pystep = 30 * ystep;
  short mov = dx ? 0 : -1;
  unsigned char *ptr = (char*)LCD_MEM + 30 * y + (x >> 3);
  short mask = 1 << (~x & 7);
  if (x1 < x2)
    while (x != x2 || y != y2)
      {
        *ptr |= mask;
        if (mov < 0) y += ystep, ptr += pystep, mov += dx;
        else
          {
            mov -= dy;
            if (++x & 7) mask >>= 1;
            else ptr++, mask = 0x80;
          }
      }
  else
    while (x != x2 || y != y2)
      {
        *ptr |= mask;
        if (mov < 0) y += ystep, ptr += pystep, mov += dx;
        else
          {
            mov -= dy;
            if (x-- & 7) mask <<= 1;
            else ptr--, mask = 1;
          }
      }
}

// Main Function
void _main(void)
{
  DrawLineFast (10, 10, 60, 70);
  ngetchx ();
}

If you need a line erasing or line inverting routine, replace '*ptr |= mask' with '*ptr &= ~mask' or '*ptr ^= mask' respectively.

I can't reset printf to start at the top of the screen!

Q: When I'm using printf function, I'm not abble to reset the scrolling: after using this function, and after clearing the screen, the text is still displayed at the bottom of the screen, even if the calculator was turned off between two use of the program... How can I reset the scrolling?
A: As I know, I warned in the documentation that ClrScr and clrscr are not the same: the second one resets the print position too, so you need to use it. Anyway, you can always use MoveTo to reset print position to (0,0) which effective resets the scrolling.

How can you disable the 2nd and alpha indicators at the bottom of the screen?

Q: How do you make that the status line is not visible on the screen so that the status indicators do not mess up your graphics?
A: The most general method (I use this) is: disable auto-int 1 using OSSetSR function (status line indicators are updated from this interrupt). Note that in this case you need to read the keyboard using the _rowread function, because ngetchx etc. are based on auto-int 1. I used this method in my program cblaster (you can pick it from ticalc). Anyway, if you use _rowread, you should also disable auto-int 5 as well.

When I disable interrupts, grayscale doesn't work!

Q: I'm writing a game in C, but I encountered the following problem: when I disable interrupts, the grayscale doesn't work...
A: Of course, because grayscale is based on interrupts. I.e. you must not disable them in grayscale programs. So, what to do? You probably want to disable auto-int 1 to avoid screwing up the status line. Instead of DISABLING auto-int 1, you may REDIRECT it to nothing. New header file intr.h provides very elegant methods for doing this. It is enough to do this:

INT_HANDLER save_int_1;
...
save_int_1 = GetIntVec (AUTO_INT_1);
SetIntVec (AUTO_INT_1, DUMMY_HANDLER);   // redirect auto-int 1 to "nothing"
// enable grayscale
// do your code
// disable grayscale
SetIntVec (AUTO_INT_1, save_int_1);

While I was fiddling with interrupt handlers...

Q: I'm just fiddling around with interrupt handlers and don't understand why the following code won't work:

void myInterruptHandler(void)
{
  asm ("movem.l %d0-%d7/%a0-%a6,-(%sp)");
  // do something here...
  asm ("movem.l (%sp)+,%d0-%d7/%a0-%a6; rte");
}
 

A: There are two reasons for this. First, the sequence "do something here" surely uses some local variables. The compiler will put them in registers during the optimization, and it will generate code for saving them on the stack at the beginning of the procedure, before the execution of any statements in the function body. So, before executing

asm("movem.l %d0-%d7/%a0-%a6,-(%sp)");

something will be pushed on the stack. Read this as "open brace '{' is not only the marker, it also generates an entry procedure code". So, when "rte" is encountered, the return address on the stack is trashed. Second, even if the procedure does not use any local vars, the entry code for the procedure is usually

link #something,%a6

and the stack is again not suitable for executing "rte". Before release 2.2 of TIGCCLIB, the only universal and correct method for defining interrupt handlers was is to define a pure assembly auxilary procedure as follows:

void InterruptHandler(void);  // Just a prototype
asm ("myInterruptHandler:
      move.w #0x2700,%sr
      movem.l %d0-%d7/%a0-%a6,-(%sp)
      jbsr MainHandler
      movem.l (%sp)+,%d0-%d7/%a0-%a6
      rte");

void MainHandler(void)
{
  // Put everything you want here...
}

To make life easier, starting from release 2.2, I introduced a new language extension called DEFINE_INT_HANDLER in the intr.h header file. Now, it is enough to write

DEFINE_INT_HANDLER (myInterruptHandler)
{
  // Put everything you want here...
}


How can I use a kernel-based libraries in C?

Q: Is there any method to use a kernel-based library with GCC4TI (such as ZipLib) and how to call a routine that uses something other than the stack in the routine for arguments passing?
A: Good and common question. When an assembly routine uses register passing, the solution is not so obvious. I will give a concrete example. Look at ZipLib, function compress. Quoting from the PreOS documentation:

; compress ()
; Function: compress data
; Input: A0   = Pointer to uncompressed data
;        A1   = Pointer to where the compressed data
;               should be stored
;        D0.W = Length of datas which will be compressed
; ziplib::compress equ ziplib@0004

So, how to interface this with GCC4TI? There is a lot of solutions. One solution is to use an interface function which accepts parameters via stack then to use embeded assembler to call library function:

void compress (void *src, void *dest, unsigned short len)
{
  asm ("move.l (%a6,8),%a0
        move.l (%a6,12),%a1
        move.w (%a6,16),%d0
        jsr ziplib__0004");
}

This works, but maybe it is awkward to know where the parameters are stored on the stack. GNU C has some extensions for interfacing with assembler, so the following solution is more elegant:

void compress (void *src, void *dest, unsigned short len)
{
  asm ("move.l %0,%%a0" :: "g"(src));
  asm ("move.l %0,%%a1" :: "g"(dest));
  asm ("move.w %0,%%d0" :: "g"(len));
  asm ("jsr ziplib__0004");
}

If you don't understand this (which is probably the case if you are not familiar with GNU C extensions), accept this as as-is template.

Both solutions have two bad points: first, usage of stack is awkward if you want very fast calling. Second, this interface function will always be inserted in the code (even if not called in the program), so it is not suitable for making universal header files. As GNU C allows interfacing arbitrary C expressions with assembler, and allows building smart safe macros using so-called "statement expressions", the following solution avoids both the stack and "embedding" problem:

#define compress(src, dest, len) \
  ({ asm ("move.l %0,%%a0" :: "g"(src)); \
     asm ("move.l %0,%%a1" :: "g"(dest)); \
     asm ("move.w %0,%%d0" :: "g"(len)); \
     asm ("jsr ziplib__0004"); })

In any case, you can call function (or macro) "compress" using, for example,

compress (LCD_MEM, buffer, LCD_SIZE);

etc.

The third solution has one drawback: the impossibility of compile-time checking of parameters type. This can be solved using the following construction (don't be afraid by introducing extra variables; the compiler will remove them during the optimization, and it will produce the same code as in previous example, but with type-checking):

#define compress(src, dest, len) \
  ({ void *__src = (src), *__dest = (dest); \
     unsigned long __len = (len); \
     asm ("move.l %0,%%a0" :: "g"(__src)); \
     asm ("move.l %0,%%a1" :: "g"(__dest)); \
     asm ("move.w %0,%%d0" :: "g"(__len)); \
     asm ("jsr ziplib__0004"); })

There is also fifth, nearly "ideal" solution, using GNU cast constructors which constructs a function during the compilation (look how functions in stdio.h are implemented, etc.). But, I will not present this here, because you will not understand anything if you are not familiar with cast constructors.

Note: TIGCC v0.94 and later (which GCC4TI is) support explicit register specification.

How can I communicate between ASM and C code?

Q: Maybe I am quite stupid, but I cannot catch anything about communicating information between the C code and the assembly code... I'm not really familiar with mixing high-level languages with assembly language; all I really know is that the asm keyword allows one to include assembly instructions in C source code. But how would would you write an assembly function that C code could utilize?
A: This is explained in detail, see section about Extended Asm.


How do you use strcat properly?

Q: Whenever I used strcat in my program, it crashes!?
A: See, you probably want to ask what is wrong in the following code:

printf (strcat ("Hello ", "World!"));

strcat appends the second argument to the first argument and returns the augmented first argument, but it does not allocate any extra space for doing this task. So, if you do

strcat ("Hello ", "World!");

string "World!" will be copied over bytes which follows immidiately after bytes "Hello " in the memory (whatever is there), which will nearly surely cause a crash (because there is probably a part of code or other data there). So, strcat may be used only when the first argument is enough long buffer, like in:

char buffer[50];
strcpy (buffer, "Hello ");
strcat (buffer, "World!");

In other words, C language does not support dynamic string manipulations which is present in some other languages like Basic, Turbo Pascal, etc.

How can I write an efficient number to string routine?

Q: I am just curious how I can write my own and efficient IntToStr routine...
A: Process the number starting from the end (i.e. from the least significant digit instead from the most significant digit), like in the following example, which is probably the most optimal code:

char *IntToStr (unsigned long an_integer)
{
  static char result [] = "          \0";    // 10 spaces and \0
  char *ptr = result + 10;
  while (an_integer)
    {
      *ptr-- = an_integer % 10 + '0';
      an_integer/=10;
    }
  return ptr;
}

Note that 'static' before char in the first line is essential: without it, the variable 'result' will be allocated of the stack, so it will not live too long after this function returns. Returning any pointers which points to structures allocated on the stack is extremely dangerous (it is not only dangerous; it is almost completely nonsense, except if you performs some really nasty and bizzare hacks). The another solution (but less elegant) is to make 'result' global (i.e. to define it out of the function).

Why can't I use a backslash (e.g. "main\var") in my strings?

Q: When I type "main\var", the compiler gives me an error. Help!
A: This is a common problem among beginning programmers. The backslash (\) is used as an escape character in the C language. Because of this, you need to modify your string to read like this: "main\\var". Now, compiler will interpret the \\ as a single backslash, which is what you want.

If you want to lean more about escape sequences, look at the information on strings.


If my program throws an error, I can't start it again!

Q: If my program stops execution by throwing an error, I cannot execute it again without retransmitting it from the computer or another calculator. Namely, when I try to execute it again, the calculator reports "Invalid program reference". Why, and what I can do to prevent such behaviour?
A: See Returning Errors.

Can dialogs have popup menus with submenus?

Q: The popup menu given in the example for menus.h seems to work fine on its own, but not when used in a dialog: if you select anything after "Option 2" the text in the selector is the wrong one. Can't dialog popups handle sub-menus?
A: I am afraid that popups in dialogs can't have sub-menus. I think so because I never see on TI-89 any system dialog which has such popups. Yes, it sucks, but this is not my guilt...

Why does calling the previous event handler in a user event handler result in a crash?

Q: What is wrong in calling the previous installed event handler in a new one? Whenever I tried to do so, my only result is a crash...
A: Very probably you do

OldHandler = EV_captureEvents (NewHandler);

then you call OldHandler from NewHandler. There would be nothing wrong in doing so, but function EV_captureEvents returns NULL when there is no any user handlers previously installed, which is very common case. So, you can call OldHandler only if it is not null. In other words, it is illegal to call an event handler when it is not actually installed. From the other side, function EV_defaultHandler calls the TIOS handler which is used for default dispatching of some common events. It works independently of which handler is currently installed and whether it is installed at all. This is a function which you probably need to call in your event handler to process all unprocessed events.

How can I use functions in textedit.h to edit a text variable?

Q: I don't understand how I can use functions from textedit.h header file to edit a text variable. When I tried to open a blank editor everything seems OK, but when I tried to edit an existing text variable, I got a crash...
A: I must admit that the problem is not so easy. The problem is, in fact, caused by severe error in my documentation in releases of TIGCCLIB prior to 2.2 (it is corrected now). Namely, I was wrong when I told that a handle passed as a parameter to TE_open function may be a handle of a text variable. In fact, text variables have another extra data at the begining (one word for the length, and one word for the cursor position). But, the editor expects a raw data. I inspected what TIOS does exactly during opening a text variable. Then I concluded: there is no any other solutions than striping out extra data (this is a little play with memmove). Of course, after the editing is finished, it is necessary to reconstruct again the proper structure of the text variable (this is possible using some informations presented in TEXT_EDIT structure after the editing is finished).

After this theoretical elaboration, I will be much more concrete. Here is a complete code example (called "Text Editor") which edits the text variable named "mytext". Due to simplicity, it is assumed that this variable really exists and that it is not archived:

// A simple text editor example

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

TEXT_EDIT te;

// Typical event handler for text editing
CALLBACK void EventHandler(EVENT *ev)
{
  if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_ESC)
    ER_throw (1);
  if (!TE_handleEvent (&te, ev))
    EV_defaultHandler (ev);
}

// Main Function
void _main(void)
{
  char *base_addr;
  SYM_ENTRY *sym = SymFindPtr (SYMSTR ("mytext"), 0);

  if (!sym) return;  // Exit if file not found...

  // First, you need to remove the garbage data at the begining of
  // the text variable, because the text editor expects raw data:

  base_addr = HeapDeref (sym->handle);
  memmove (base_addr, base_addr + 4, peek_w(base_addr));

  // Now, do the editing. This is straightforward...

  WinClr (DeskTop);
  TE_open (&te, DeskTop, MakeWinRect (0, 16, 159, 92), sym->handle, 1, 0, 7);
  CU_start ();
  EV_captureEvents (EventHandler);
  TRY
     EV_eventLoop ();
  ONERR
     EV_captureEvents (NULL);
  ENDTRY

  // Finally, you must transform raw editor data into the proper
  // format of the text variable. This is not so straightforward:

  base_addr = HeapDeref (HeapRealloc (sym->handle, te.CurSize + 10));
  memmove (base_addr + 4, base_addr, te.CurSize);
  poke_w (base_addr, te.CurSize + 4);
  poke_w (base_addr + 2, te.CursorOffset);
  poke (base_addr + te.CurSize + 4, 0);
  poke (base_addr + te.CurSize + 5, TEXT_TAG);
}

It is important to understand how this program works if you plan to do any serious application of text editing functions.

I tried to create a new window for the text editor, but it didn't work...

Q: When I tried to create my own window to be used as a parent window for the text editor (instead of the DeskTop window), and when I passed the address of my window to TE_open, the editor went blank (it did not display the file contents on the screen, although the editing seemed to work). What is wrong?
A: I was also very surprised when I noticed this. After some investigations, I concluded that the window passed to TE_open must not be "dirty" (i.e. it must not have WF_DIRTY flag set), but windows created by WinOpen are "dirty" by default. So, you need to clear "dirty" flag manually before calling TE_open. This is straightforward. For example,

WIN_RECT myRect = {0, 16, 159, 92};
WINDOW myWin;
TEXT_EDIT te;
...
WinOpen (&myWin, &myRect, WF_NOBORDER);
myWin.Flags &= ~WF_DIRTY;
TE_open (&te, &myWin, &myRect, ...);

Anyway, there is no strong reasons to use any windows other than DeskTop as a parent window, except if you want to use the whole screen for the editing area (the desktop window is clipped in the toolbar area). But, note that using whole screen for editing is not so good idea. The editor expects that the menu is on the top. So, if you press F1 etc. while doing "full-screen" editing, your screen will be trashed, because the editor will open the menu, and it will expect that the toolbar is at the top, etc. etc. Try to see. The solution? Disable all keys like F1, etc. in the event handler (e.g. do not pass them to TE_handleEvent) if you really want to do full screen editing...

Can you change the font in the text editor?

Q: Is it possible to use any other font except 6x8 font in the text editor?
A: Principally, you can change font using WinFont before calling TE_open. For example, you can do

WinFont (DeskTop, F_8x10);

However, the editor will work fine with both 6x8 and 8x10 fonts, but not with 4x6 font (try and see), because it is proportional, so the editor will be fooled (editors usually expects fixed-size fonts). This is a pity.

Why isn't it possible to create menus with more than one level of submenu with PopupAddText, and how can I do it anyway?

Q: The documentation about menus.h says (cite): "Although TIOS menu system allows menus with more than one level of submenus, it is not possible to create such menus using this command (PopupAddText)". What happens then? And how it is possible to make menus with more levels of submenus?
A: The answer of the first part of the question is: just nothing. Options which are added on deeper levels are just ignored. When I dissassembled this routine, I found that this is really not implemented! About the second part of the question: this is answered in the documentation about menus.h header file.

How can I use VTI to pick bytes from menu structures like you describe?

Q: The documentation about MenuPopup says that it is possible to make popup menus by picking up bytes with the VTI debugger to save the memory, but I don't know how to do this. Could you tell me how to do this, please?
A: Not so easy to explain if you are not an experienced user. Basically, after you define a menu using PopupNew, PopupAddText etc., you need to display its address instead of executing it. E.g. instead of doing

PopupDo (handle, ...)

you need to do something like:

printf_xy (0, 50, "Address=%lp", HeapDeref (handle));
ngetchx();

Then, while waiting for a keypress, open the VTI debugger and go to the displayed address. Take a pencil and write a sequence of bytes starting from this address. Tenth and eleventh byte in this sequence will tell to you how many bytes you need to pick. After this, put these bytes in the array, and pass such array as an argument to the MenuPopup function. As an exercise, try this on an example given in the documentation.


How can I get a list of all the variables/folders on the calculator?

Q: Does any function exists to find out how many folders are on the calculator and what files are in them? In other words, is there a way to get an array of all the folders on the calculator or an array of all the files in a folder?
A: This was very frequent question, but you can find the answer by reading carefully info about SymFindFirst and SymFindNext. And, look the example about returning values to TI-Basic given on the main page of this documentation. Mail me if still not clear...

How can I determine the type and size of a variable?

Q: How I can determine the size of a symbol (i.e. TI-Basic variable) and the TIOS type of the variable?
A: Assume that you found a VAT symbol entry for a symbol (say symbol named "foo") which may be done for example using SymFindPtr:

SYM_ENTRY *sym;
...
sym = SymFindPtr (SYMSTR ("foo"), 0);

Then, do the following to find the size and type:

unsigned short size;
ESQ type;
...
size = ((MULTI_EXPR*) HeapDeref (sym->handle))->Size + 2;
type = *(HToESI (sym->handle));

After this, the variables 'size' and 'type' will contain exactly what do you want.

How can I copy a variable from one folder to another?

Q: I don't understand how to copy a symbol (TI-Basic variable) from one folder to another. As I can see, SymCpy doesn't copy the entire symbol, just the name!?
A: Moving symbols is quite easy (using SymMove), but copying is not so straightforward. Principally, you need to create a new one, and to copy the content of the original one into a new one. Fortunately, it is possible to use cmd_copyvar from bascmd.h. As it is TI-Basic call, it is highly recommended to execute it under a TRY...ONERR...ENDTRY block.

How can I create a List or Matrix variable to keep highscores or similar information?

Q: Is there a way to use TI89's Lists or Matrix, so I will be able to keep information like high score or something else...
A: Yes, more info about this can be found in this release of GCC4TILIB documentation (note that a matrix is a "list of lists"). However, using lists or matrices is somewhat complicated. It is much easier to keep information in a user-defined format. See the next question.

A stdio.h header file is implemented, which supports ANSI C compatible file handling, so creating files is quite simplified. If you want to bypass stdio.h and use low-level TIOS routines (such an approach leads to smaller programs), see SymAdd.

Can I create a variable with a custom type (like HSC for highscores)?

Q: Is it possible to create a file which will have a custom type (i.e. which does not appear in VAR-LINK as a stanard type like STR, PIC, PRGM etc.). For example, I want to create a highscore file which will appear in VAR-LINK as HSC type...
A: Christian Walther pointed to me that this is possible. Moreover, this is easy. All you need to do is to create a file of "other" type (i.e. which terminates with OTH_TAG). TIOS then expects that a real type name is stored just below OTH_TAG as a zero-started zero-terminated string of maximum 4 chars. Look the following code fragment as an example:

FILE *fp = fopen ("example", "wb");
// store anything you want in the file here
fputc (0, fp);
fputs ("HSC", fp);
fputc (0, fp);
fputc (OTH_TAG, fp);
fclose (fp);

After this, you will have a file named "example" with type "HSC": you will see it in the VAR-LINK dialog.

I want to create a variable without using functions from stdio.h.

Q: I want to avoid standard ANSI C functions for file handling like fopen etc. and to use low-level functions from vat.h (to make my program shorter), but I am not very sure what I need exactly to do to create a new file, and how I can manipulate with it.
A: Basically, to create a file you need to do the following:

  1. Create a new VAT symbol using SymAdd;

  2. Allocate a space for the variable using HeapAlloc;

  3. Dereference the symbol to get a pointer to the VAT entry, then store the handle returned by HeapAlloc to it;

  4. Put the actual file length in first two bytes of allocated space.

To be more concrete, I will show here a simple demo program (called "Create Variable") which creates a string file (I use this internally), but it is easy to adapt to any file type:

// Create a variable using functions from vat.h

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

HANDLE CreateFile (const char *FileName)
// Returns a handle, H_NULL in case of error
{
  HANDLE h;
  SYM_ENTRY *sym_entry;
  char str[30], *sptr = str;
  *sptr = 0; while ((*++sptr = *FileName++));
  if (!(h = HeapAlloc (HeapMax ()))) return H_NULL;
  if (!(sym_entry = DerefSym (SymAdd (sptr))))
    {
      HeapFree (h);
      return H_NULL;
    }
  *(long*) HeapDeref (sym_entry->handle = h) = 0x00010000;
  return h;
}

void AppendCharToFile (HANDLE h, unsigned char c)
{
  char *base = HeapDeref(h);
  unsigned short len = *(unsigned short*)base;
  if (len > HeapSize(h) - 10) return;
  *(unsigned short*)base = len + 1;
  base[len+2] = c;
}

void AppendBlockToFile (HANDLE h, void *addr, unsigned short len)
{
  unsigned short i;
  for (i = len; i; i--) AppendCharToFile (h, *((char*)addr)++);
}

void CloseFile (HANDLE h)
{
  AppendCharToFile (h,0); AppendCharToFile (h,0x2D);
  HeapUnlock (h);
  HeapRealloc (h, *(unsigned short*)HeapDeref(h) + 3);
}

void _main(void)
{
  static char s[] = "Hello world!";
  HANDLE h;
  h = CreateFile ("example");
  AppendBlockToFile (h, s, 12);
  CloseFile (h);
}

Note that the used method is not the best: it initially allocates as much space as avaliable, then reallocates the space to the necessary size on closing, but it is worth to look at it. Note also that the CreateFile function may be even simpler if you want to use it like CreateFile(SYMSTR("example")) instead of CreateFile("example"), i.e. if you avoid the use of ANSI strings.

When I use functions from vat.h, the calculator crashes!

Q: When I tried to use EM_moveSymToExtMem to archive a file, the calculator crashed!
A: You probably wrote something like

EM_moveSymToExtMem ("example", HS_NULL);

Instead, you need to write

EM_moveSymToExtMem (SYMSTR ("example"), HS_NULL);

This routine except SYM_STR type strings, not ordinary C strings. See SYMSTR from the vat.h header file for more info.


I have trouble allocating memory, my program crashes on the second run.

Q: I have problems with allocating memory. When I run my program the first time, it works just as expected, but the second (or sometimes the third) time I run it, it hangs. Typical memory allocation problem. But I just can't understand what the problem is.
A: The problem is very probably not related to the dynamic memory allocation, but to the usage of static variables. At the moment, all static vars need to be initialized, even to nothing. I.e, you need to use

static int a = 0, b = 0;
static char *ptr = NULL;

instead of

static int a, b;
static char *ptr;

I expect that this will solve your problems.

I can't understand C matrices!

Q: I can't understand matrices in C (not TI-Basic matrices, but matrices which are the part of the C language)!
A: Basically, to declare a matrix, you need to use:

type name[dimension1][dimension2];

for example:

int a[5][5];

And, to access element of the matrix, you need to use:

name[index1][index2];

for example:

a[2][3] = 10;

or

x = a[1][2];

But note that indices are not from 1 to dimension, but from 0 to dimension-1. So, in the above example, both indices are in range 0 to 4, not 1 to 5. For example to fill all matrix by zeroes, you can use this code:

int a[5][5], i, j;     // A 5x5 array of ints, and two single ints
for(i = 0; i < 5; i++) for (j = 0; j < 5; j++) a[i][j] = 0;

although the experienced C programmer will simply use

memset (a, 0, 5 * 5 * sizeof(int));

to make it faster.

How do I store variables so they retain their values, like for highscores?

Q: How do you store preferences, high scores, and suchlike so that they do not go away when you exit the program? I have absolutely no clue how to do this.
A: The simplest method is: if you stored preferences in an array, declare this array as "static". For example,

static int high_scores[10] = {};

In this case, such array will be kept in the program file itself (instead on the stack), so it will not go away after you exit the program. This method has only one drawback: preferences will not be kept if the program is archived.

Be aware of the initializer: static variables must be initialized in the "nostub" mode. An empty initializer in this example is equivalent to the

static int high_scores[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

Don't be confused which such initialization. It seems that the array elements will be set back to 0 any time when you run the program. But, this is not true. The initialization of automatic and static variables is quite different. Automatic (local) variables are initialized during the run-time, so the initialization will be executed whenever it is encountered in the program. Static (and global) variables are initialized during the compile-time, so the initial values will simply be embeded in the executable file itself. If you change them, they will retain changed in the file.

Global variables retain value?

Q: If I understand correctly, if I have the following global variable in my program

int a = 10;

and if I change its value somewhere in the program to 20 (for example), its initial value will be 20 (not 10) when I run the program next time???

A: Exactly! Note that this is true only for global and static variables. To force reinitializing, you must put explicitely something like

a = 10;

at the begining of the main program!

Note, however, that if the program is archived, the initial values will be restored each time you run the program, because archived programs are reloaded from the archive memory to the RAM on each start, similarly to the programs are reloaded from disks on "standard" computers (PC, etc.) each time when you start them. The same is true for compressed programs (for obvious reasons) and if you use a data variable and have GCC4TI create a copy every time it is used.

See also: How do I store variables so they retain their values

How can you control where data is stored in memory and access it?

Q: How can you control where a program data is stored in the calculator's memory, and then how to access the data? By looking at your programs (cblaster etc.), you simply use memcpy to write to the array, which seems so easy! Is the array kept in memory because it's declared as static?
A: Yes. Every static and global data (i.e. data which are declared out of functions) are kept in the .89z/.9xz file itself (more precise, in the area of the memory allocated to .89z/.9xz file), so they will survive after the end of the program. Local data are stored on the stack (so they will be deleted after the end of the function in which they are declared).

What is so horrible about global variables?

Q: You quote very often in the documentation that we need to avoid global variables as much as possible. What is so wrong with them?
A: Of course, globals are sometimes very necessary, but they are also easy to overuse. The good programming practice is to keep the scope of each identifier as small as possible. But, in addition to conventional programming theory, there are some extra reasons against globals which are related particularly to programming on TI calculators. First, every global variable is translated to the absolute addressing mode. As this mode is non-relocatable, the compiler adds a 4-byte long entry into the relocation table for each apperance of the global variable. If global variable "a" is mentioned say 100 times in the program, this is extra 400 bytes. When I ported some programs from PC to TI, I suceeded to reduce program space from 20K to 16K just by eliminating globals. And 4K of space is quite worth on TI... Second, globals are kept in the .89z (or .9xz) file itself. Sometimes it is good (to keep permanent data which will survive after the program exits etc.), but very often it is a wasting of file space (as the file length is limited). Read questions given further in this document to see how to reduce a file size if you have a strong reasons for using global arrays. To learn: avoid extensive usage of globals as much as possible!

How can you store a number such as 0x4c00 in a pointer?

Q: According to what I have read, you cannot store an immediate value (or any non-pointer variable) to a pointer variable. You can only copy pointers of the same type and such. Now, the books may say this, but, is there a way to do this somehow? I want something like 'address=0x001fca;' where address is a pointer to a char. Is it necessary to use the assembler for this?
A: No. C is enough low-level language that nearly everything which can be done in ASM can be done in pure C too (of course, pure ASM code is usually faster). In your concrete example, using of typecasting is quite enough:

address = (char*)0x001fca;

This is really a classic usage of the typecast operator. It is extremely powerful in C language, so it may be used to convert nearly everything to anything. This sometimes may be really terrible. For example, in early TIGCCLIB implementation, I used strange typecasting to convert an array to a function. And, nearly all library functions are implemented using a typecast operator which constructs an indirect function call. For example, when you use something innocent like

DrawStr (0, 0, "Hello", A_NORMAL);

the preprocessor expands it into a really terrible typecast. See _rom_call for more info.

Is it possible to add one or two bytes to a longword pointer?

Q: Is it possible to add two bytes or even one byte to a longword pointer? I am using a longword pointer to write longwords to the screen, and I need to add 30 bytes to it to get to the next line. However, I can only add multiples of four bytes to it...
A: It is possible using typecasting:

ptr = (long*)((char*)ptr + 30);

Don't be afraid, the compiler will generate just addition: everything other is just to satisfy type checking conventions. Or alternatively, you can use even simpler form:

(char*)ptr += 30;

Although such form is not requested to work in ANSI C standard, the most of compilers (including GCC4TI) will accept this.

Why can't you assign values to dereferenced void pointers?

Q: Why doesn't this work?

void *screen_ptr = LCD_MEM;
*screen_ptr = 0xFF;

When I do this, I get the error "Invalid use of void expression" at the second line. Can't you assign a value to a dereferenced void pointer? If not, what good is a void pointer?

A: You can not dereference a void pointer, without an explicite typecasting, because the compiler can't know the size of an object pointed to by it. You can do an explicite typecast:

*(char*)screen = 0xFF;

Or better, if you need to dereference it often, then declare

unsigned char *screen = LCD_MEM;

i.e. avoid void pointers for this purpose (continue reading to see why such assignment is legal, i.e. assigning LCD_MEM which is a void pointer to a char pointer).

Void pointers are used mainly as arguments of functions which represents memory addresses, no matter what is the object located at that addresses. Then, any pointer type (including arrays, which are in fact pointers to the first element of the array) may be passed to such function without warnings and without needness for explicite typecasting. For example, memcpy is such function, and it is declared as:

void *memcpy (void *source, void *destination, unsigned long len);

Ignore returned type for a moment. So, you can do

memcpy (LCD_MEM, buffer, 3840);

but you also can do

memcpy (a, b, 10 * sizeof(long));

assuming that you have declared

long a[10], b[10];

somewhere in the program. Second, void pointers may be assigned to any other pointer type and vice versa without and warnings and without needness for explicite typecasting. They are usually returned as the result of functions which don't make any assumptions what will be purpose of returned pointer. For example, malloc is such function. It is declared as

void *malloc (unsigned long len);

So, assuming that you have declared

char *a;
int *b;
long *c;

you can do

a = malloc (100);
b = malloc (30 * sizeof(int));
c = malloc (50 * sizeof(long));

without any problems.

How can I have large global variables that don't take up space in my program file?

Q: I would like to have a global screen buffers which need to be allocated dynamically (to avoid wasting space in the program). The problem is that global variables must be initialized. In your programs, you define the buffer in the _main function, but then they are not available in the other functions. Is there a way to make "global" non-initialized screen buffers?
A: Declare a pointer initialized to NULL, then at the beginning allocate the buffer with malloc. For example, do this:

#include <tigcclib.h>
...
void *buff;  // Buffer pointer
...
void _main(void)
{
  ...
  buff = malloc (LCD_SIZE); // Alloc buffer, make "buff" point to it
  if (!buff) ...            // Do some error handling (no memory)
  LCD_save (buff);
  ...
  LCD_restore (buff);
  free (buff);
}

Simple?

How can I create a dynamic two-dimensional array?

Q: Your hints about dynamic creation of arrays work fine for one-dimensional arrays. But I need a global matrix. There is no problem if the matrix is small. But, if I simply put

int A[200][100] = {{}};

I will increase the size of my program by about 40K (200*100*sizeof(int)). This is really unacceptable. Obviously, I need to create a matrix dinamically. But I have no any idea how to do this.

A: Very good question. Solving this problem requires good knowledge about how arrays and pointers are related. As this question usually has unadequate answers in various C books, I will elaborate this topic (dynamic matrix) in more details.

One method, which is often recommended in books, is usage of array of pointers. Using this method, you need to allocate each row of the matrix separately. For example,

int *A[200] = {};   // An array of pointers
...
for (i = 0; i < 200; i++)
  A[i] = calloc (100, sizeof (int));

assuming that all memory allocations were sucessfull. Note that the initializer '{}' is not necessary if 'A' is not global (but we will assume that it is). Of course, you need to free allocated memory too at the end of the program. We will see a bit later why this method is not recommended on TI calculators.

Using this method, you will have a global array which is 800 bytes long (200*4, where 4 is the size of a pointer), which is much smaller than 40000 bytes. And, you need to know a number of rows in advance. Some books suggests a method which does not use any extra space in the executable file, and in which you need not to know any dimensions of the matrix in advance. This method uses a double pointer (pointer to pointer):

int **A = NULL;
...
A = calloc (200, sizeof (*A));
for (i = 0; i < 200; i++)
  A[i] = calloc (100, sizeof (int));

The major drawback of both methods is complicated memory management. In a real program, you need to be aware of a fact that each allocation may fail eventually, and you need to act accordingly if this happens. And, these methods are too expensive for TI calculators. As TIOS memory manager assigns a handle with each allocated block, reserving say 200 handles for just one matrix is too expensive, if even possible, because the total number of free handles is limited!

What to do? The best solution is so simple, but rarely documented in books. Instead of using an array of pointers, use a pointer to an array! Seems strange, but only what you need to do is:

int (*A)[100] = NULL;   // A pointer to an array
...
A = calloc (200, sizeof (*A));

So, everything is done with just one calloc! And, you can free the memory just with one call to free. Wonderful, isn't it? Of course, whatever method you used, you can access to the elements of a matrix using an usual syntax like 'A[i][j]'. Note however that the last ("ideal") method requires that you know the number of columns in advance. Try to understand how all three methods work: it will help you understanding nontrivial pointers, arrays and relations between them.

As many users ask me about creating dynamic matrix, I have made a complete demo program for newbies (called "Dynamic Matrix") which creates and prints elements of dynamically created matrix, using the third (the best in my opinion) method:

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

#define M 10
#define N 5

int (*A)[N] = NULL;

void _main (void)
{
  int i,j;
  A = calloc (M, sizeof (*A));     // <I>allocation</I>
  for (i = 0; i < M; i++)
    for (j = 0; j < N; j++)
      A[i][j] = i*j;               // fill 'A' with the multiplication table
  clrscr ();
  for (i = 0; i < M; i++)
    {
      for (j = 0; j < N; j++)
        printf ("%2d ", A[i][j]);  // print out the matrix
      printf ("\n");
    }
  free (A);                        // free the memory
  ngetchx();
}

This method is really ideal if you know dimensions of the matrix in advance (which is usually true). It may be easily extended to create dinamically arrays with more than two dimensions. For example, to create dinamically array which behaves like

int A[M][N][P][Q];

where 'N', 'P' and 'Q' are known in compile-time, you can do:

int (*A)[N][P][Q];
...
A = calloc (M, sizeof (*A));

However, if you don't know any dimension of the matrix in advance, the second method is preferable.

See also: How can I create variable-size arrays?

How can I create variable-size arrays?

Q: I need variable-size arrays, i.e. arrays which size is not known in compile time...
A: In general, making variable-size arrays in C is extremely easy for one-dimensional arrays. Instead of

int a[n];  // where n is not known in compile-time

you can write:

int *a;
...
a = calloc (n, sizeof (int));

The same is not-so-easy for 2-dimensional arrays (see the previous question), and quite hard (although possible) for n-dimensional arrays where n>2. To do this, you need to have nasty plays with pointers. It is even possible to make a function named multi_dim which creates multidimensional variable-sized arrays. I once created such routine for some internal purposes (note that it is very tricky). Principally, its usage is like this: if you want to simulate

int a[n1][n2][n3][n4];

where n1, n2, n3 and n4 are not known apriori, you need to do:

int ****a;  // yes, quadruple pointer!!!
...
a = multi_dim (4, sizeof (int), n1, n2, n3, n4);

However, GCC4TI is GNU C, and it has some extensions in addition to "standard" C. For example, it allows variable-size arrays without any tricky plays with pointers!!! (Note that this particular extension, together with a few others, has been added to standard ISO C in 1999, so it is no longer non-standard.) Try this:

void _main(void)
{
  int m, n, p;
  m = random (5);
  n = random (5);
  p = random (5);
  {
    int a[m][n][p];
    // do something with a
  }
}

and you will see that it works!

Why do I get "Value computed is not used" when working with pointers?

Q: I have a variable and a pointer to it, for example,

int a, ptr_to_a = &a;

When I tried to modify the variable "a" indirectly using the pointer, like in

*ptr_to_a++;

the compiler reports to me "Value computed is not used". What is wrong here?

A: Note that although operators '++' and '*' have the same precedence, '++' will be evaluated first, so this expression will be evaluated as

*(ptr_to_a++);

i.e. it increases the pointer, then reads the value from it (which is not used for anything). This is not what do you want, of course. To perform what do you want (i.e. to increase the variable pointed to by the pointer), use parentheses to change the order of evaluation, i.e. use

(*ptr_to_a)++;

This will work as expected.


How can I get input from the keyboard?

Q: How I can get input from the keyboard?
A: There is a lot of method how you can make such routine. This is quite easy if you are a C programmer. The easiest but the worst method is to use gets function from stdio.h header file: gets does not do any buffer length checking. In old releases of TIGCCLIB, it did not even allow any editing facitilities (even backspace key would not work), but this is no longer the case. A better idea is to use getsn which does avoid buffer overflows. But if you want to control what exactly is done by the input routine (for example, you may or may not want to allow opening the CHAR menu), you'll have to make a custom keyboard input routine. For example, I usually used the following routine, which is good enough for many purposes (example "Input String"):

// Custom string input example

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Custom String Input Function
void InputStr(char *buffer, unsigned short maxlen)
{
  SCR_STATE ss;
  short key;
  unsigned short i = 0;
  buffer[0] = 0;
  SaveScrState (&ss);
  do
    {
      MoveTo (ss.CurX, ss.CurY);
      printf ("%s_  ", buffer);
        // Note that two spaces are required only if the F_4x6 font is used
      key = ngetchx ();
      if (key >= ' ' && key <= '~' && i < maxlen)
        buffer[i++] = key;
      else if (key == KEY_BACKSPACE && i)
        i--;
      buffer[i] = 0;
    } while (key != KEY_ENTER);
}

// Main Function
void _main(void)
{
  char s[20];
  clrscr ();
  InputStr (s, 20);
  printf ("\n%s", s);
  ngetchx ();
}

Especially, if very good editing facitilities are required, the best idea is to use routines from the textedit.h header file. These routines are extremely powerful and fully customizable. Alternatively, you can also use routines from dialogs.h, especially DialogAddRequest.

See also: Do you have the function that gets called when you do InputStr in TI-Basic?, How can I make a keyboard input function that allows you to bring up a menu?, getsn

I'm having trouble with the functions from link.h...

Q: I have problems with making a program for communication between two calculators. For example, I can't seem to get data to consistantly transfer over the calc to calc link using calls from link.h. I wrote a simple program that just reads key presses and sends them as bytes while checking for bytes from the other calc. Strangely, it only gets about one byte for every 20 or so keypresses...
A: Many people tells to me about various problems (smaller or greater) with link.h. See the answer about the question why the function ngetchx is slow. Maybe this will help you. I must tell that the linking is not so known topic for me... You must ask some link guru if you want to know more about this problematic...

Do you have the function that gets called when you do InputStr in TI-Basic?

Q: Did you find the keyboard input function that gets called when you do an InputStr in TI-Basic? I'm looking at GKeyIn as probably the main part of whatever this function is, but it also includes the ability to bring up the CHAR dialog, look up key equivalents, and lots of other nice stuff that I want to be able to use.
A: The TIOS function cmd_inputstr just performs the InputStr command from TI-Basic, but this function is rather limited, so in practice it is better to create a similar function by yourself. I.e, it is possible to call it, but there is a lot of artefact in doing it.

See also: How can I get input from the keyboard?, How can I make a keyboard input function that allows you to bring up a menu?, getsn

How can I make a keyboard input function that allows you to bring up the CHAR menu (or MATH, etc)?

Q: I really need a routine like InputStr given above, but which is able to bring up the CHAR dialog. However, I am not able to make such routine by myself...
A: I received a lot of such questions. These questions are usually asked by newbie programmers, but the answer is quite advanced. Although I don't like to present advanced routines as-is without a lot of necessary explanations, I am requested to do so. Well, here is a routine (example "Input String Advanced"):

// Custom string input example enabling the CHAR menu

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

short captured;

CALLBACK void CaptureHandler(EVENT *ev)
{
  if (ev->Type == CM_STRING)
    captured = *(ev->extra.pasteText);
}

void InputStr(char *buffer, unsigned short maxlen)
{
  SCR_STATE ss;
  short key;
  unsigned short i = 0;
  buffer[0] = 0;
  SaveScrState (&ss);
  do
    {
      MoveTo (ss.CurX, ss.CurY);
      printf ("%s_  ", buffer);
        // Note that two spaces are required only if the F_4x6 font is used
      do
        {
          key = ngetchx ();
          if (key == KEY_CHAR && i < maxlen)
            {
              EVENT ev;
              captured = 0;
              ev.Type = CM_KEYPRESS;
              ev.extra.Key.Code = key;
              EV_captureEvents (CaptureHandler);
              EV_defaultHandler (&ev);
              EV_captureEvents (NULL);
            }
        } while (!captured);
      if (key == KEY_CHAR && i < maxlen)
        buffer[i++] = captured;
      if (key >= ' ' && key <= '~' && i < maxlen) buffer[i++] = key;
      if (key == KEY_BACKSPACE && i) i--;
      buffer[i] = 0;
    } while (key != KEY_ENTER);
}

// Main Function
void _main(void)
{
  char s[20];
  clrscr ();
  InputStr (s, 20);
  printf ("\n%s", s);
  ngetchx ();
}

It will be good if you can understand how it works (I recommend reading the documentation for the events.h header file).

See also: How can I get input from the keyboard?, Do you have the function that gets called when you do InputStr in TI-Basic?, getsn

I'm having trouble with the OSGetStatKey function.

Q: I have problems with OSGetStatKeys function.
A: See what I wrote about this in the documentation about kbd.h header file. I recommend usage of pseudoconstants from compat.h header file instead.

How can I read the keyboard faster than ngetchx, but easier than _rowread?

Q: Is there any way for reading the keyboard faster than using ngetchx function, but without very low-level access using _rowread?
A: Yes, there is a LEGAL method to pick keystrokes directly from the keyboard queue. See kbd_queue and other queuing functions from system.h header file.

How can I get the key repetition feature to work using the keyboard queue?

Q: Reading directly from the keyboard queue is a good idea, but the key repetition feature does not work...
A: On the first look, it seems that the key repetition feature really does not work with OSdequeue. But, Marcos Lopez informed me that this is not exactly true. Key repetition feature works even with OSdequeue, but it will not return the keycode itself for the repeated key, but sets an additional bit in the keycode, so value becomes value + 0x800. If you use standard ngetchx function, this additional bit is masked out and your program will get the keycodes it expects. But, it is very simple to mask out this bit manually and make the key repetition feature working even with OSdequeue.

Why are ngetchx and kbhit so slow?

Q: Why functions ngetchx and kbhit are so slow?
A: They also implements support for Silent Link feature (ability of automatic receiving programs when the calculator is in Home Screen). That's why they may also interfere with the link transfer!

Is there a function like the idle_loop function?

Q: What is the true equivalent for the idle_loop function known from kernels?
A: GKeyIn (NULL, 0) is the most similar equivalent. ngetchx () is not so similar. Both functions are defined in kbd.h. Note that idle defined in system.h is NOT equivalent to idle_loop!

How can I bring up the VAR-LINK dialog and get the name of the selected file?

Q: How do I bring up the VAR-LINK dialog from C code and then get the name of the file that was selected?
A: This is an advanced question. As the VAR-LINK dialog is an event-driven applet, you must use event-driven functions defined in the events.h header file. Bringing up the VAR-LINK dialog is quite easy:

EVENT ev;
ev.Type = CM_KEYPRESS;
ev.extra.Key.Code = KEY_VARLNK;
EV_defaultHandler (&ev);

but getting the name of the file that was selected is a bit harder. After executing the VAR-LINK dialog, the VAR-LINK applet will send the name of the selected file to the current application via the CM_HSTRING message. This message may be captured by a user event handler. Here is the demonstration program (called "Get File Name"):

// Open VAR-LINK dialog and let user select something

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

char VarBuffer[20] = "";

CALLBACK void VarLinkHandler (EVENT *ev)
{
  if (ev->Type == CM_HSTRING)
    {
      strncpy (VarBuffer, HeapDeref (ev->extra.hPasteText), 19);
      VarBuffer [19] = 0;
    }
  EV_defaultHandler (ev);
}

void VarLinkDialog (void)
{
  EVENT ev;
  EVENT_HANDLER OldHandler = EV_captureEvents (VarLinkHandler);
  memset (&ev, sizeof (ev), 0);
  ev.Type = CM_KEYPRESS;
  ev.extra.Key.Code = KEY_VARLNK;
  EV_defaultHandler (&ev);
  EV_captureEvents (OldHandler);
}

void _main(void)
{
  VarLinkDialog ();
  printf_xy (0, 50, "You picked: %s", VarBuffer);
  ngetchx ();
}

Read more about the events.h header file: incredible miracles may be produced using event-passing techniques!

I'm having troubles while reading I/O ports!

Q: I have a lot of troubles while reading I/O ports. I need a loop which will wait until the programable timer on the TI-89 reaches value 255. I know that I can read the value of the programable timer counter using address 0x600017, so I made the following loop:

while (*(unsigned char *)0x600017 != 255);

But, the calculator freezes. I tried to use peek macro, as in

while (peek (0x600017) != 255);

No fortune again (I know that this is, in fact, the same as above). What is wrong?

A: See, there is a problem when reading I/O ports in small loops. Look at the above loop for example. The compiler will notice that the same value is read again and again in the loop. As normal memory location can not be changed without explicitly writing to it, and there is nothing in the loop which changes the address 0x600017, the optimizer will move memory reading out of the loop to make the code more efficient. Such behavior is correct for ordinary memory locations. But, in our case, it will cause an infinity loop. The compiler does not know anything about the fact that 0x600017 is not an ordinary memory location but an I/O port, which may be changed unpredictably (purely by the hardware, without any program control). To prevent such behaviour, you should use the volatile keyword to make the address 'volatile', i.e. to say to the compiler that this address may be changed unpredictably. So, the correct code is

while (*(volatile unsigned char *)0x600017 != 255);

The peekIO macro makes life easier. You can do

while (peekIO (0x600017) != 255);

Basically, peekIO works exactly like peek, but prevents any unwanted optimizations generated by the compiler. Always use peekIO (or peekIO_w) for reading memory-mapped I/O ports, else you may have troubles. The _rowread function in TIGCCLIB releases prior to 2.3 also caused similar troubles, now this is corrected too.

peekIO may be used even for reading bytes into the memory, but peek will generate better code when working with memory. However, use peekIO to read any memory location which may change in a way which is unpredictable from the aspect of a normal program flow (for example, a memory location which is changed in the body of the interrupt handler).

Note: For sending bytes to I/O ports, the macros pokeIO and pokeIO_w are also introduced, which are more reliable for this purpose than poke and poke_w.


Some of your examples don't compile!

Q: Some of your examples (like the example given in the documentation for rsa.h header file) don't compile!
A: No. They compile fine, if they are typed in absolutely correctly. But there is a hardly-visible problem with the space character. The space between the macro name and the argument list is strictly forbiden, else the macro will be treated as an argumentless macro. In other words, you must not type

#define GetBignumArg (ap, bn) \

instead of

#define GetBignumArg(ap, bn) \

Hardly visible, isn't it?

Why do you use -O2/-Os when -O3 performs better optimization?

Q: I wonder why you put '-Os' as a default optimization option in the compiler. '-O3' will perform better optimization...
A: Yes, but '-O3' produces bigger output than '-Os', because it performs loop unrolling and inlining of "short-enough" functions...

I need to access the FLASH memory.

Q: I need access to the archive memory...
A: This is implemented in this release. See flash.h header file.

Some functions with no arguments (like 'off') don't work for me!

Q: When I try to turn the calculator off using off function from system.h, nothing happens. What is wrong?
A: Most probably, you wrote

off;

instead of

off ();

This is very common misinterpretation of usage of argument-less function. You may ask why the compiler did not display any errors? See, whenever you use the function name alone (without the parenthesis), it is automatically converted to a pointer to the function (this is very useful feature if used properly). So, 'off' alone is interpreted as a pointer, so you have an statement which consists of an effect-less expression, similarly as if you wrote

2 + 3;

This is not illegal in C, but it has no any effect (eventually, you will be warned by the compiler that the statement has no effect, if you enable enough strong warning checking). Such misinterpretations are very often with newbie users, so I will elaborate this in more details. Suppose that you wrote

key = ngetchx;

You probably want to call ngetchx function to get a keypress, but instead, you will assign the address of this function to the variable 'key', because the function name without parenthesis is automatically converted to a pointer (however, you will be warned by the compiler that it is very ugly to assign a pointer to an integer variable without an explicite typecast). Instead, you need to write

key = ngetchx ();

Sometimes, you will not get even a warning. Look the following example:

void *qptr = kbd_queue;

The intention was probably to put the address of the keyboard queue into the 'qptr' variable, but instead, the address of the function kbd_queue itself will be taken! As the function name is converted to a pointer to a function, and as you assign it to a void pointer (which is assign-compatible with all other pointer types), you will not get even a warning!

How can I detect the hardware version of the calculator?

Q: Does any method exists for detecting hardware release of the calculator?
A: I can now recommend the use of FL_getHardwareParmBlock. However, if you ask it for detecting hardware version before using grayscale routines, you need not to do it because the GrayOn routine now performs an autodetection by itself.

How can I use very large integers like TIOS?

Q: I need to know more about how the TIOS stores and references big integers. In other words, what would be the C structure that would be used to represent any arbitrarily-long binary integer? In addition, I am particularly interested about how two extermely long binary integers would be multiplied and divided. Does TIOS have its own binary multiplication routine somewhere in the ROM? Something that works similarly to BCD multiplication/division, I'm guessing?
A: TIOS stores all integers (both small and large) in the following format (looking from lower to higher addresses):

  1. Bytes of the integer in little endian (up to 255 bytes);

  2. The number of bytes occupied by integer (one byte);

  3. POSINT_TAG or NEGINT_TAG, depending on the sign.

These integers are kept on the expression stack, or in TI-Basic variables (of course, in TI-Basic variables, there are two extra bytes at the begining, which represents the length of a variable). In other words, integers are kept in a structure which is similar to the BN structure defined in the rsa.h header file, except the length byte is at the end, not at the begining (because data on the expression stack is always in RPN). So, such a structure can't be strictly represented using valid C syntax, because it requires something like

struct big_int
  {
    BYTE data[size];   // but 'size' is not known in advance
    BYTE size;
    BYTE tag;
  }

Routines for multiplying and dividing very long integers surely exist in the TIOS, but the strange fact is that such routines are not in the TIOS jump table (read: they are not usable). Anyway, you can always use general eveluating routines like NG_rationalESI and NG_approxESI. For example, to multiply two very-long-integers, you can use the following template:

push two very-long-ints on the estack
push_quantum (MULT_TAG);
NG_approxESI (top_estack);

However, two routines for working with long integers are in TIOS jump table: for calculating '(A*B)%N' and '(A^B)%N' where "%" is the "modulus" operation, and "^" is "raising to a power". These operations are used in the RSA cryptosystem. Both of them are defined in the rsa.h header file. They may be used for multiplying and raising to a power: you always can set N to very a big number, then (A*B)%N = A*B. Also, you can use them for calculating modulus (if you set B to 1). But, I am not sure how you can simulate division. I think that TI never uses integer division: everything like 'A/B' is kept as a fraction, except in approx mode; TIOS then uses NG_approxESI.

I need the gotoxy() function to port PC programs to the calculator...

Q: In addition to functions from stdio.h header file, I need gotoxy function to allow porting some PC programs to TI...
A: All printing functions from stdio.h are sensitive to MoveTo command, so it is not hard to set print position to anywhere. If you need just gotoxy, it is impossible for 4x6 font, because it is proportional. But, for 6x8 and 8x10 fonts, it may be implemented trivially:

#define gotoxy(x,y) MoveTo (6*x-6,8*y-8)    // for 6x8 font
#define gotoxy(x,y) MoveTo (8*x-8,10*y-10)  // for 8x10 font

Here I assumed that top-left corner is (1,1) as on PC. Note that you MUST NOT put a space between gotoxy and left bracket (else the preprocessor will define an argument-less macro). You can also define an universal gotoxy macro which will work regardless of current font setting, using smart GNU C macros:

#define gotoxy(x,y) \
  ({short __f=2*FontGetSys(); MoveTo((4+__f)*(x-1),(6+__f)*(y-1));})

You will not be able to understand this if you are not familiar with GNU extensions.

How can I create a program that is bigger than 24K and works on AMS 2.xx?

Q: How I can make a program which does not need any kernel, but which is greater than 8K and which works on AMS 2.03 as well (or greater than 24K and which works on AMS 2.04 and AMS 2.05 as well)?
A: In fact, you need to make a short program (a so-called "launcher") which will call the main program, bypassing the TIOS (to skip the software protection). If the main program is "example.c", I recommend the following "launcher":

// Launcher for program called "example"

#define USE_TI89
#define USE_TI92PLUS
#define USE_V200

#include <tigcclib.h>

#define fatal(s) ({ST_showHelp (s); return;})

void _main (void)
{
  char *fptr, *cptr;
  unsigned short plen;
  SYM_ENTRY *SymPtr = DerefSym (SymFind (SYMSTR ("example")));
  HANDLE h;
  if (!SymPtr) fatal ("Program not found");
  h = SymPtr->handle;
  if (HeapGetLock (h))
    {
      cptr = fptr = HeapDeref (h);
      h = 0;
    }
  else
    {
      cptr = fptr = HLock (h);
    }
  plen = *(short*)(cptr) + 3;
  if (SymPtr->flags.bits.archived)
    {
      if (!(cptr = malloc (plen)))
        {
          if (h) HeapUnlock (h);
          fatal ("Out of memory");
        }
      memcpy (cptr, fptr, plen);
    }
  enter_ghost_space ();
  EX_patch (cptr + 0x40002, cptr + plen + 0x3FFFE);
  ASM_call (cptr + 0x40002);
  if (h) HeapUnlock (h);
  if (cptr != fptr) free (cptr);
}

If you are not an expert, use this program as-is.

For anybody who wants to know more about the 8K limit: on HW1 calcs it is a pure software limit which can be broken by intercepting some error traps (used in PreOS), or by making a short launcher which relocates the called program and jumps to it bypassing the TIOS check. This is what I used. But, yet another problem must be solved: HW2. Namely, it has built-in a hardware device which does not allow that PC can be out of certain zone, so the launcher itself will not help. This device can't be turned off normally by software, but Julien Muchembled found a "hole" in the protection system which allows turning out this device (this is what his HW2 patch does): this is a very nasty method, and I don't want to explain this here. But, this protection device can be fooled in a simple way: it protects only the "normal" RAM address space (i.e. the first 256K of RAM). As RAM is partially decoded, address x and x+256K are the same for TI-89, but if you jump to x+256K instead of x, the protection will not be activated! Look at the launcher to see how it works, and you now can understand why I added 0x40000 (i.e. 256K) to the address in two places. First, I relocate the called program as it is located on x+256K instead of x, then I call the subroutine on x+256K instead of x.

AMS 2.04 and AMS 2.05 introduce yet another level of protection (which existed before, but caused problems very seldomly): on these AMS versions, even jumping to the "ghost address space" (above 256K) is not possible if the program counter is out of a certain area. (This caused the so-called "second launch crash"; if you have AMS 2.05 with any longer program like TI-Chess, you probably know what I am talking about.) That's why I introcuced a new function called enter_ghost_space: its only purpose is to smoothly bypass this new protection. Note that enter_ghost_space is deprecated now; you should use the EXECUTE_IN_GHOST_SPACE directive instead in most cases.

What might be the reason for my program leaking a small amout of memory?

Q: My program is leaking some small amount of memory (less than 100 bytes). Where could my leak possibly be?
A: The reason is probably the home screen history. Press F1 and 8 before and after running your program, and if it is still leaking memory then you need to be concerned.

What is an "Address Error"?

Q: What kind of error in my program usually causes an "Address Error"?
A: Well, writing over the boundaries of an array can usually cause all sorts of errors, since it usually destroys code or the return address of the function. However, an "Address Error" actually means that a short or long value is read or written at an odd address. So if you get an "Address Error" while you are dealing with pointers, check that you do not cast an odd address to a pointer to a short or long integer.

Why does my program using dialog boxes crash?

Q: What causes my archived program using dialog boxes and/or the catalog to crash?
A: Many things can cause programs to crash. However, archived programs using dialog boxes often crash due to special problems. Defining SET_FILE_IN_USE_BIT may solve the problem.


Return to the main index