Focus Of This Tutorial

This tutorial will show how to utilize an own interrupt handler as clock.

It's a pity that the TI89/TI92plus calculators have no built-in real-time
clock. Many programs would be easier to make if there would be such a device.
Heaven thanx our calculators have a built-in (programmable) timer which can be
utilized for our purposes. It will not be absolutely accurate, but it will
be accurate enough for almost all purposes.

Necessary Software

Basic Technical Terms

Before I will start with the real tutorial I want to explain some technical
terms for everyone who is not comfortable with hardware technology.

CPU

The brain of your calculator. The CPU executes your program.

INTERRUPT

Event which will force the CPU to stop immediately what it is doing now, let it
execute the corresponding interrupt handling routine and after it has finished
this (special) routine it will continue at the point it was stopped previously.
Interrupts are generated (triggered) by other parts of the hardware telling the
CPU that there is an important job waiting.
For example: Interrupt 6 is triggered by the keyboard if the [ON] key gets
pressed by the user.

Routine, which is called if the corresponding interrupt is generated (triggered).
Each interrupt has an interrupt handler assigned by default. The addresses of
these handlers are stored in the interrupt vector table, which is just a list
of routine addresses. If, let's say, interrupt 6 is generated the CPU will look
up the address of the handler routine at position 6 in the interrupt vector
table and will execute this routine.

OSCILLATOR(s)

The oscillator can be seen as the heart of your calculator. It "beats" at a
given frequency. The TI89/TI92plus has 2 built-in oscillators. One which
generates a fast clock signal for the CPU and a second oscillator which
generates a slower clock signal for other parts of the calculator like the
programmable timer.

PROGRAMMABLE TIMER

An 8-bit counter which counts up from a given initial value with a specified
speed until it reaches 255. This counting is done absolutely independently from
the CPU. After value 255 it reaches value 0 due to its 8-bit size.
At value 0 it triggers an interrupt (interrupt 5) and starts counting again
from its initial value.

Standard Library Functions (and similar) for Interrupt Treatment

Since the TIGCC standard library version 2.2 an interrupt handler can be
defined like this:

DEFINE_INT_HANDLER(myinthandler) {
// do something here ....
}

To get the address of an installed interrupt handler from the interrupt
vector table you can use function:

INT_HANDLER GetIntVec(long IntVec)

and to install an own interrupt handler the standard library supplies
function

void SetIntVec(long IntVec, INT_HANDLER Handler)

To make the handling of the offsets into the vector table
easier, enum AutoInts defines the offsets of the most used entries like this:

have to be used if you want to call an other interrupt handler from within
your interrupt handler routine. Calling an other interrupt handler from within
your handler may be a good idea in some cases.

Simple Counter - Example 1 (timer1.c)

This example is taken from the TIGCC Standard Library documentation (thanx,
Zeljko).

You can compile it with: tigcc -O2 timer1.c

It utilizes an own interrupt handler for interrupt 5 which is triggered
periodically by the programmable timer.

#define SAVE_SCREEN
#include <tigcclib.h>
int _ti89, _ti92plus; // produce code for TI89 and TI92plus
INT_HANDLER oldint5 = NULL;
//-------------------------------------------------------------
// NOTE that variable counter is defined "volatile" !!!
//
// "volatile" means that the compiler shouldn't touch it in any
// way for optimization reasons.
//
// if this variable is not defined "volatile" the compiler
// may completely remove it, because it thinks it will change
// during routine (the compiler doesn't know anything about
// interrupts, so this may fool it).
//-------------------------------------------------------------
volatile int counter = 0;
DEFINE_INT_HANDLER (myint5handler) {
counter++;
//-----------------------------------------------------
// we don't want to replace the old interrupt handler
// completely, but want that it will be executed at the
// end of our own handler
//-----------------------------------------------------
ExecuteHandler(oldint5);
}
void _main(void) {
//-------------------------------------------------------
// clear screen and set counter variable to 0, otherwise
// the counter would start at its last value if you start
// the program again and the program is not archived!
//-------------------------------------------------------
ClrScr();
counter = 0;
//---------------------------------------------------
// get the address of the default interrupt 5 handler
// and store it in the global variable oldint5.
// this is necessary so we can call this handler within
// our own handler and that we can restore it at the
// end of our program
//---------------------------------------------------
oldint5 = GetIntVec(AUTO_INT_5);
//--------------------------------------
// install now our own handler ....
//--------------------------------------
SetIntVec(AUTO_INT_5, myint5handler);
//------------------------------------------------------------------
// ... loop and print the counter value until a key gets pressed ...
//------------------------------------------------------------------
while (!kbhit()) {
if (TI89) printf_xy(50, 50, "Counter=%d ", counter);
else printf_xy(70, 64, "Counter=%d ", counter);
}
//----------------------------------------------------------
// install the previous handler again (removing our own one)
//----------------------------------------------------------
SetIntVec(AUTO_INT_5, oldint5);
//-------------------------------------------------------
// empty the keyboard buffer and return.
// its a really good idea to empty the keyboard buffer
// and this point. otherwise the pressed key will remain
// in the buffer and will be handed over to the AMS.
// if the pressed key is the [ENTER] key you program
// would get started immediately again.
//-------------------------------------------------------
GKeyFlush();
}

Simple Clock - Example 2 (timer2.c)

Our next example will extend the previous code to become a real clock.

If the parameters of the programmable timer are not changed by a program
the timer will generate 19 interrupts per second (one interrupt every
52.63 milliseconds). Examine the code of the interrupt handler below to see
how this fact is used to build up a clock.

Note, that during the execution of the interrupt handler some variables may
contain invalid values in the short term (for example: seconds == 60), but this
doesn't matter, because the rest of your program will never "see" such invalid values.