Programming the TI-89
One of the first platforms on which I programmed was my TI-83+ Calculator I had at home! After messing around with TI-Basic on the calculator, I switched to using and programming my more advanced TI-89. (and somehow misplacing my TI-83+ along the way :/)
I first learned to write on the variant of TI-Basic found on the TI-89, and after not too long, found out about native programming on it. Some amazing people at TIGCC wrote a GCC port to enable C programs to compile for the TI-89, integrated with the OS's API. This is where I first learned C programming, and the importance of optimization since the calculator has a whopping 256kb of memory and a 10 MHz clock speed!
Some programs I have written on the TI-89 include
Ray Casting Playground
Writing this program taught me the most about different optimization techniques. My initial iterations started out taking ~5 seconds to update each frame. By the end, I was able to get it to run at a much smoother ~15fps!
The Grayscale Trick
Okay, this one isn't an optimization technique, but rather a trick to get grayscale graphics on screens that only display pixels either on or off. The screen is represented by a 3840 byte region in memory. On my calculator, the pointer to this region of memory can be programmatically changed. By rapidly switching the display between 2 planes, a 4-color grayscale affect can be observed. It's even possible to get more colors such as 7-8, however this can result in flickering.
Fast Drawing
Less is faster.
On modern computers, you can draw millions of pixels without seeing any lag at 60+ fps. The TI-89 is far from a modern computer though, having a processor designed in the 1970's. My initial iterations on the ray caster would fill entire rectangles where a ray hit a wall. Replacing this with only drawing the boundaries of walls drastically reduced the number of pixels that needs to be drawn to the screen and reduced computation.
Even just drawing lines faster makes a difference in rendering times. Using a fast line drawing routine instead of the routines built into TI's OS proved to significantly speed up rendering.
Fixed Point Arithmetic
Integer arithmetic is MUCH faster than floating point arithmetic.
Floating point arithmetic require many more operations than simple integers. The processor has to deal with the significand and the exponent in a float, and operations combining them have to take into account different exponents when adding, subtracting, etc.
Fixed point arithmetic trades off some of the versatility of floating point in exchange for significantly faster performance. The idea behind fixed point is that we have the decimal point at a fixed location instead of it being able to be in different positions like in floating point.
In my original floating point implementation, my grid was represented with floating point, that is walls would take 1x1 unit each, but the player could be located some non-integer coordinate. Porting this to fixed point, I used 16-bit integers, reserving 6 bits for the fractional part and leaving 10 for the whole part. This would allow the player to be located at any of the 26 possible x-coordinates and 26 y-coordinates within each grid cell. This provides much more precision than perceivable on the tiny 160x100 screen while making the arithmetic much faster.
Implementing fixed point requires a bit of work since it is not built-in.
My implementation uses 10 bits for the whole number part and 6 bits for the fractional part.
This means the range of values is [0, 210) for whole numbers,
with the smallest fractional unit being 2-6.
For example, the integer value 1 is represented by 1*26,
or quickly calculated with 1<<6.
Operations on fixed point integers (assuming 6 bit fractional part)
- Addition/Subtraction: built-in addition and subtraction works just fine
- Getting integer part: Right shift 6 bits
fp >> 6 - Getting fractional part: Only reading the lowest 6 bits
fp & 0b111111 - Multiplication: perform built-in multiplication followed by a right shift of 6 bits
(fp1 * fp2) >> 6
...Aaand for trigonometry, I kinda cheated. Trig functions also require quite a bit of computation,
so instead, I used a pre-computed look up table. Since I only needed sin(x) and cos(x), and they
are both symmetric and related functions, I stored a table storing angles [0, 90] degrees for sin(x).
The rest of the inputs for sin(x) and cos(x) can be calculated using those values.
To align with fixed point arithmetic, I precomputed the output of sin(x) times 128 and rounded that
to the nearest integer. This allowed me to skip the floating point trig operations all together.