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.
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. |
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... |
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 |
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.
|
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 (); } |
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 *(char*)0x4c00 = 100; So, &(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!!!
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 |
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.
|
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? |
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. |
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. |
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.
|
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). |
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?
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. |
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. |
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 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. |
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 |
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. |
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. |
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. |
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...
|
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. |
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. |
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.
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! |
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.
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); |
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. |
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).
#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!
|
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 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). |
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
|
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. |
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. |
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); |
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... } |
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.
#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.
#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.
|
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. |
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 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. |
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 |
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. |
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. |
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... |
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 |
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. |
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... |
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. |
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. |
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. |
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... |
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 |
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. |
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. |
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. |
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:
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 |
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. |
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. |
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 although the experienced C programmer will simply use memset (a, 0, 5 * 5 * sizeof(int)); to make it faster. |
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.
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. |
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!
|
See also: How do I store variables so they retain their values
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).
|
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!
|
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. |
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. |
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 *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. |
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? |
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 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!
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 #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 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?
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 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! |
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. |
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
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... |
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
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
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. |
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. |
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. |
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! |
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!
|
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! |
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.
|
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? |
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... |
Q: | I need access to the archive memory... |
A: | This is implemented in this release. See flash.h header file. |
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, 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 = 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 |
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. |
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):
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 |
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. |
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.
|
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. |
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. |
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.
|