1

I'm coming close to completing my first big project (yay!), and I need some advice. I'm getting ready to do some optimizations, but since I'm not intimately familiar with ASM, I don't really know what to do. I only vaguely remember a couple of things that were mentioned on the TICT forum. So, here are my questions:

Right now, I have all my variables as local variables in _main(). I'm considering moving them to be global so that I don't have to pass so much to my functions. Which is better in terms of speed? Which is better in terms of size?

Also, in several cases, I pass a structure (of roughly 22 bytes) to several functions. Would it be better to pass it as a pointer?

Finally, I'm assuming declaring functions called only once as "static inline" has no drawbacks. Is this so?

Thanks!
~d

P. S. I'm also thinking of slowing down the game in the early levels so that it appears to work at the same speed on all levels. I remember something about one of the ports associated with the programmable timer not working on VTI. (Since I don't have any way of transferring to my calculator, all my testing is done on VTI.) What parts of the programmable timer can I expect to work?

2

I encountered a few problems with the auto ints on VTI
I used them to make my own timers but it apears that the rate of the timer is completly different on vti and on a ti89 hw2

I don't know if the AMS' programmable timer suffers the same problem but I think that you can exept the programmable timer to work fine but at a fairly different rate than on a real calc
I pass a structure (of roughly 22 bytes) to several functions

you pass the structure by value ?
how can you do that ???

if you pass a pointer (4 bytes) it will be faster, but there will be side effects, beware.
avatar

3

Nu
:
I pass a structure (of roughly 22 bytes) to several functions

you pass the structure by value ?
how can you do that ???

Well, I don't know if the "under-the-hood" work actually passes by value, but my C code passes by value, yes. Probably on the stack?
Nu :
if you pass a pointer (4 bytes) it will be faster, but there will be side effects, beware.

What side effects? Do you mean other than the fact that I could accidentally change the value in the calling function as well or some other?

Thanks for the help!
~d

4

As he said, you should use pointers instead of passing-by-value, which is both slower and larger, especially since GCC doesn't know how to generate inline copy loops (unlike the compiler in TIFS, this is one of the very few advantages the TIFS compiler has over GCC).

> Right now, I have all my variables as local variables in _main(). I'm considering moving them to be global so that I don't have to pass so much to my functions. Which is better in terms of speed? Which is better in terms of size?
Globals, probably. The worst example of misuse of locals (auto) variables I know of is Pinball. I told the author how terrible his code was, but it's true that improving the code is much work.
The next step is a global register variable: put all your globals into a structure, and keep a pointer to that structure into a register (usually a4, this is why I'm not happy with the current version of Kevin's patch for the tilemap engine). It doesn't always work well with interrupt handlers, though.

> Finally, I'm assuming declaring functions called only once as "static inline" has no drawbacks. Is this so?
I'd say "yes", because I saw that if the compiler figures out that inlining not-too-small "static inline" functions would turn into worse code, it doesn't.
"static inline" is also usually the way to go for very simple functions (functions that return the state of a single global, etc.). If you have a look at the kernel-based dynamic version of PolySnd, you will see that one third of all exported entry points are functions which *should* be macros for both speed and size. I told the author as well, I don't know if he turned those functions into macros in the static version.

As for optimized compilation options, check the latest software by TICT.
avatarMembre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

5

dmwit :
Well, I don't know if the "under-the-hood" work actually passes by value, but my C code passes by value, yes. Probably on the stack?

I though that only primitive types (chars, shorts, longs, pointers) can be passed by value smile

What side effects? Do you mean other than the fact that I could accidentally change the value in the calling function as well or some other?


yes, if you pass a pointer there will be no local copy of the structure, if you modify the value pointed, you modify the structure.
Lionel Debroux :
The next step is a global register variable: put all your globals into a structure, and keep a pointer to that structure into a register (usually a4, this is why I'm not happy with the current version of Kevin's patch for the tilemap engine). It doesn't always work well with interrupt handlers, though.

will the address register contain always the address of the structure (even when it is unnecessary) ?
avatar

6

> will the address register contain always the address of the structure (even when it is unnecessary) ?
Yes.
On the one hand, it improves all accesses to globals (d(an) ea mode, even for writes).
On the other hand, it eats up a register permanently (same for -freg-relative-an which effect is identical regarding variables).
In practice, -freg-relative-an or the global register variables I'm describing usually improve both compressed and uncompressed size of programs, thanks to improved writes to globals (d(an) instead of relocated xxx.l references).
avatarMembre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

7

Lionel Debroux
: It doesn't always work well with interrupt handlers, though.

Why ?
avatar« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

8

What I'd suggest is, let all variables that can reasonably be global be global, and when you need a reentrant/recursive function, use a structure pointer as was suggested earlier (instead of passing by value). The rationale is that if some functions only use global variables, they don't need to be passed a structure pointer, which is both a size and speed optimization (though I doubt you'll notice it much as far as speed is concerned). For reentrant functions, you can't use global variables -- except if you save and restore their values each time you enter and exit the function, which might be worth it in terms of speed in a limited number of cases [e.g. if you use a lot of leaf functions -- for example, in a C compiler, it's OK for the parse position to be in a global variable, even though the routines need to be reentrant because of #include statements], but might be rather costly in size. And if you didn't use global variables, you'd need to pass an extra structure pointer anyway, so it doesn't cost you more to use global variables.

As far as TIGCC is concerned, the last time I checked it didsn't handle global variables very well (GTC does a better job for this tongue), so you might want to use -freg-relative-an in this case...

9

#6: Interrupt handlers do not take global register variables or -freg-relative-an into account.

AFAIK, TIGCC handles global variables the normal way I described above, unless -mpcrel is used (it is then likely to use d(pc) reads and d(pc) writes after lea instructions). Note that -mpcrel and -freg-relative-an are mutually exclusive.
avatarMembre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

10

Okay, since my first post, I have
- added a little functionality
- elevated several variables to "global" status
- passed pointers rather than structures
- declared 16 functions as static inline

This shaved about 500 bytes off of a 10k program. However, declaring functions as static inline had no effect on size, and no noticeable effect on speed (?). (I didn't add -freg-relative-an or any other because I don't know how.)

This leaves only my final question about the reliability of VTI's programmable timer. Regarding ./2,
exept the programmable timer to work fine but at a fairly different rate than on a real calc

Is this just the discrepancy between HW1 and HW2? If so, I will need to set the timer differently for HW1 and HW2 and just assume VTI is HW1. Or is there another difference?

Thanks for all the help so far!
~d

11

No, VTI's programmable timer is not reliable. It's a bug in VTI which has nothing to do with differences between HW1 and HW2/3.

-freg-relative-an or other compilation options (check TI-Timewaste for example) have to be added in the compiler options of your project's options.
avatarMembre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

12

Structures are never passed by value in C. When you do not use a pointer, they are passed by reference. Thus, using pointers or not changes nothing but the syntax you use. Whatever way you choose, the compiler will actually use the address of the structure without duplicating it.

Anyway, if you do not modify the structure, use the const attribute. It prevents errors and may allow the compiler to make a few more optimizations, based on the assumption that the members of the structure will not be modified.

13

no, they are definitely passed (and returned) by value, so you *have* to use pointers to be efficient. BTW, that's the same in C++, too ^^ (unless you use MyStruct& mystruct, in which case it is obviously passed by reference)

as for the const attribute, it might prevent errors, but often it's just going to be annoying (e.g. function mother() calls function son() with struc.value1 set appropriately, and son() wants to call grandson() with a special value of struc.value2; you just can't set the const attribute for struc in son() -- and mother() doesn't care about struc.value2 anyway, so it's OK for son() to modify it)

14

Err, maybe I confused structures and arrays. My mistake ^^

However, your example has nothing to do with the const attribute, since mother() has nothing against son() modifying some fields (here value2) of the structure.

15

What about when son() modifies something he shouldn't and mother() gets mad, can father() beat son() until he stops?

16

lol cheeky

spectras> yes, but what I meant is that often, most of your structure fields shouldn't be changed (so const might possibly be applicable), but it's still OK to change a few special fields, so you have to remove all the const's. You could use a separate structure for the fields that can be changed, too, but it would be less efficient... (but a bit cleaner, I agree)

17

When I add -freg-relative-an to the list at Project->Options->Compilation->GCC Switches:, I get a large number of errors, starting with "Warning: Unknown register name: an", "Bad expression.", "Missing ')'.", "Parse error -- statement 'move.w #1,level-__relation(%an)' ignored.", etc. Any hints on how to discover what I need to fix?

~d

18

Pollux> in C++ you can use the mutable attribute to solve a part of the problem. Well, in truly object-oriented programming, one would encapsulate data access anyway. I do not know, however, if the mutable keyword exists in C.

dmwit> That's because it's not -freg-relative-an but -freg-relative-a0 -freg-relative-a1, etc. You choose one. You want to avoid using a0, a1, a6 and a7, though. Because the first ones are overwritten by most functions, a6 is used to keep track of the stack frame (though you may use it if you disable stack frames and you are sure no function you call uses them), and a7 is the stack pointer.
The leaves you with one of -freg-relative-a2 -freg-relative-a3 -freg-relative-a4 or -freg-relative-a5

19

Relavant options are :
-fomit-frame-pointer : forces the compiler not to use stack frames. set by default
-freg-relative-an : causes your program to load its address at its very beginning, then use indirections relative to this address. The address being kept in a register, it should be faster and shorten the program a bit. This is, however, at the expense of having a register stuck all the time and may slow down complicated computations.
-mpcrel : a loose variant of -freg-relative-an. It does not use a register. It takes advantage of the pc-relative addressing mode of the 68k and uses indirections relative to current instruction.

I don't think there is much difference between -freg-relative-an and -mpcrel. Maybe this comes to play when using dynamically linked libraries, I do not know. I use -mpcrel.

20

I always experience problemes when I try to compile something with -mpcrel (such as approximately "could not emit relocation")
do you know why ?
avatar

21

It depends on what you are trying compile. Do you have a specific example?

22

i'lll try to give you one
avatar

23

When I add -freg-relative-a4 or -freg-relative-a3 I get flagrant system errors when I run the program. (Like 10 Address Errors, one after the other.) I didn't test the other registers, but I imagine it would be the same. I think for this particular game I will not mess with what I don't know. happy

Thank you!
~d

24

Did you try -mpcrel instead of -freg-relative-an ?
It has much less side-effects.

25

Yes, but it doesn't compile. It says, "Operands mismatch -- statement 'cmp.w #1,(level:w,%pc)' ignored."

26

Is your code pure C or does the error happen in some assembly code you wrote yourself?

27

-mpcrel should only affect what the compiler generates, I guess it doesn't have anything to do with the assembler. As it's not a very used switch, there are chances that it's a bit buggy on plain 68000 (IIRC cmp.w #1,level(pc) does work on more recent 680x0 processors)

28

My code is pure C; I don't know any assembly. (Although I am using the Extgraph library, I doubt the problem is there.)

29

Strange, I don't usually experience trouble when using -freg-relative-a4 or -mpcrel (mutually exclusive).
avatarMembre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

30

Do either of them conflict with any other options? I have the default options still set:
-Os -Wall -W -Wwrite-strings