◄ Back to index

GTC Extensions

GTC supports a number of extensions, designed to take advantage of the TI platform, or simply to maintain compatibility with existing programs.


GNU extensions



Statements and Declarations in Expressions


Referring to a Type with typeof


Generalized Lvalues


See Generalized Lvalues.

Note however that weird constructs like (a,b)+=5 are rejected by GTC -- commas are not considered legal lvalues.


Conditionals with Omitted Operands


Binary Numbers


Structures With No Members


Arrays of Length Zero


Macros with a Variable Number of Arguments


Arithmetic on void and Function Pointers


Non-Constant Initializers


Compound Literals (Cast Constructors)


See Compound Literals (Cast Constructors). There is some difference in the handling of static storage duration though, see Differences between GTC and TIGCC.


Designated Initializers


See Designated Initializers, but in GTC this only works for array types.


Case Ranges


Specifying Attributes of Functions


See Specifying Attributes of Functions, however not all of the specifiers are meaningful in GTC.


C++ Style Comments


Dollar Signs in Identifier Names


Escape Character in Constants


Alternate Keywords


Mixed Declarations and Code


Unnamed struct/union Fields within structs/unions


GTC-specific extensions



incbin directive


It is sometimes convenient to include binary files directly into your program, for example when using sprites. The incbin directives allows you to do just that, without having to convert your data to a decimal or hexadecimal header (which can be memory-consuming on a TI).


Using incbin to declare a C array


You can use incbin to initialize the contents of a C array:

// The following declares a sprite using normal C initializers
short sprite1[] = {
    0xFFFF, 0x8008, 0x8008, 0xFFFF,
    0xFF00, 0x8800, 0x8800, 0x8800,
    0x00FF, 0x0088, 0x0088, 0x0088,
    0xFFFF, 0x8008, 0x8008, 0xFFFF,
};

// The following declares a sprite using incbin,
// reading the contents from the file sprite.bin
short sprite2[] = incbin "sprite.bin";

The first declaration creates an array of 16 shorts containing the hexadecimal numbers provided. The second declaration creates an array of 16 shorts whose representation in memory will be the same as sprite.bin.

Note that for the sake of portability, import_binary can be a useful alternative, although there are several caveats: you will have to manually declare the size of the array if sizeof is to be used, and you cannot use it to declare a static array inside a function.


Using incbin within an asm{} statement


Like in a68k, you can also use incbin inside an asm{} statement to insert binary data.

asm {
// Insert the values 1,2,3,4 after label1
label1:
    dc.w 1,2
    dc.w 3,4
// Insert the content of sprite.bin after label2
label2:
    incbin "sprite.bin"
};

asm{} statement


GTC allows you to program in assembly, with a syntax very close to that of a68k. In fact, it is powerful enough to write full assembly programs using only GTC!


Inserting an asm{} statement in the code



Global asm{} statements


You can insert assembly code at the global level, for example:

asm {
add_3_and_multiply_by_5:
    add.w #3,d0
    mulu #5,d0
    rts
};

This creates an assembly function named add_3_and_multiply_by_5 that takes an input argument in d0, adds 3 to it, multiplies it by 5, and returns the result in d0.

To use such a function in C code, you need to prototype it:

short add_3_and_multiply_by_5(short x);

This tells GTC that your function takes a short named x as an input, and outputs a short. Because of the calling convention, x will be placed in d0: see default calling convention for more information on where input arguments to assembly functions will be located.


Inline asm{} statements


You can also insert assembly code within a function. This has the advantage over global assembly functions to eliminate the overhead associated with a function call.

void do_something(char *command) {
    if (!strcmp(command,"off"))
	asm { trap #4 };
    else
	printf("unknown command!\n");
}

This functions turns off the calculator when command is "off", by calling trap #4.


Structure of an asm{} statement


An asm{} statement is comprised of a set of declarations between the outer { and } braces. A declaration can be:

  • a 68000 instruction, e.g. moveq #123,d0
  • a label definition, e.g. my_function:
  • a data declaration, e.g. dc.w 123,456,789 or incbin "data.bin"

You must separate different declarations either:

  • by putting them on separate lines
  • by simply concatenating them if the first is a label
  • by separating them with a semicolon (;) -- however you should absolutely avoid doing this outside #define, as the behaviour may change some day

asm{} interpretation rules


The behaviour of GTC is indeed very close to that of a68k. Like a68k, GTC tries to optimize instructions when it is possible, for example add.w #3,d0 will be optimized to addq.w #3,d0. Like a68k, you don't need to add a : after label names: however, while with a68k you can only do so if the label is placed on the first char of the line, with GTC you can do so any time as long as the label does not correspond to an assembly instruction.

The most notable difference is perhaps that, where a68k uses specific commands like equ and equr to define macros, GTC simply uses the C preprocessor. This is very powerful, as it allows you to define constants just once and reuse them in C code. You can also use normal C conditionals like #if or #ifdef, making conditional compilation easier. You can also interface your code with C much more easily, for example you have access to the sizeof operator:

int table[] = { 1,2,3,4 };

asm {
negate_table:
    lea table,a0
    moveq #sizeof(table)/2-1,d0
\loop
    neg.w (a0)+
    dbra d0,\loop
    rts
};

After execution of negate_table, table will be equal to { -1,-2,-3,-4 }.

Note the \ in \loop: this means that the label is local. However, this notion differs slightly from that of a68k: while the label is only valid between the surrounding two global labels in a68k, the label is valid throughout the asm{} statement in GTC. It is often more convenient, as this example shows:

asm {
\return_with_error
    moveq #-1,d0
    rts
my_function:
    addq.w #5,d0
    bmi.s \return_with_error
    tst.w d1
    bmi.s \return_with_error
    ... very long code (longer than 128 bytes) ...
    rts
};

In this example, \return_with_error is placed before my_function because it allows the branches to \return_with_error to be short branches (bmi.s, 2 bytes) rather than long branches (bmi.w, 4 bytes). If GTC followed the rules of a68k, then it would require \return_with_error to be a global, which can be inconvenient if you have lots of these. Instead, it allows you to structurally divide your program in logical asm{} blocks.


Extra features of asm{} statements


Because asm{} statements rely on the C compiler architecture of GTC, there are lots of nice features, for example:

#define USE_KERNEL
#include <std.h>
asm {
_main:
    pea "Hello, world!"(pc)
    jsr ST_helpMsg
    addq.l #4,a7
    rts
};

What's most interesting in all this is that the C library required no modification whatsoever to allow using ROM calls in such a way: ROM calls are not defined twice, and there isn't even a conditional statement acting differently inside asm{} code and inside C code! This is possible because the assembler was able to interpret the C construct corresponding to ST_helpMsg.


asm{} special operator: __c__


This is an operator reserved to operands of instructions in asm{} statements.

The reason why this operator exists is simple: assembly and C each have different arithmetic rules. For example, if you have the following code:

int table[] = { 1,2,3,4 };

then, while in C table+2 will designate the address of table[2] (the number 3), in assembly table+2 will designate the address of table[0] plus 2 bytes, which happens to be the address of table[1].

So when you write:

asm {
load_address:
    lea table+2(pc),a0
    rts
};

then, as you would expect from assembly code, table+2 corresponds to the address given by assembly arithmetic rules.

This works with #define statements too:

asm {
load_address:
#define my_item table+2
    lea my_item(pc),a0
    rts
};

But GTC allows you to do much more: you may want to access information that requires access to the full typing system of C, not just the addresses of different objects. The way you can access this typing system is by the __c__ operator: if you write

asm {
load_address:
    lea __c__(table+2)(pc),a0
    rts
};

then table+2 will not be interpreted according to assembly rules, but according to C rules: that is, __c__(table+2) is the adress of table[2].

GTC goes one step further, by automatically enclosing #defines written as part of C code in the __c__ operator:

int table[] = { 1,2,3,4 };
#define third_item &table[2]  // note: table+2 would give the same results

int *c_load_address() {
    return third_item;
}

asm {
load_address:
    lea third_item(pc),a0
    rts
};

Here both c_load_address() and load_address() do the same thing, without ever having to define third_item twice!


@@ prefix


The prefix @@ can be prepended to any identifier to prevent it from being expanded by the preprocessor. For example, @@MY_SYMBOL will expand to MY_SYMBOL even if MY_SYMBOL has been #defined to expand to 0x1234.

This is not very useful in normal C code, but it can be useful in conjunction with other GTC extensions, mainly asm{} statements and pre-compiled headers.