The <intr.h> header file


  
This header file contains the following functions:
AUTO_INT            ExecuteHandler      GetIntVec          SetIntVec
the following language extensions (implemented as macros):
DEFINE_INT_HANDLER
and the following constants and predefined types:
AUTO_INT_COUNT      AutoInts            Bool               DUMMY_HANDLER
FIRST_AUTO_INT      INT_HANDLER         LAST_AUTO_INT

Functions


INT_HANDLER GetIntVec (long IntVec);

Gets an interrupt vector.

GetIntVec gets the content of the interrupt vector located at the absolute address IntVec (typical values of IntVec are defined in enum AutoInts). Typical usage of this function is to get the current content of the interrupt vector to be restored later. See DEFINE_INT_HANDLER for an example of usage.

void SetIntVec (long IntVec, INT_HANDLER Handler);

Sets an interrupt vector.

SetIntVec sets the interrupt vector located at the absolute address IntVec (typical values of IntVec are defined in enum AutoInts) to the interrupt handler pointed to by Handler. Handler should be either the value returned from GetIntVec, or address of an user-defined interrupt handler defined using DEFINE_INT_HANDLER. Note that Handler must not be address of an ordinary C function. See DEFINE_INT_HANDLER for an example of usage.

long AUTO_INT (short IntNo);

Gets an address of an interrupt vector.

AUTO_INT returns the absolute address where the interrupt vector for Auto-Int IntNo is located.

void ExecuteHandler (INT_HANDLER Handler);

Executes an interrupt handler.

ExecuteHandler executes the interrupt handler pointed to by Handler. The only purpose of this function is to allow calling the previous interrupt handler (usually the default one) from the user-defined interrupt handler. This function must not be executed from anywhere out of the user-defined interrupt handler (defined using DEFINE_INT_HANDLER). Else, you will get the "Privilege Violation" crash, because all interrupt handlers expect to be executed in the supervisor CPU mode. Parameter Handler should be either the value returned from GetIntVec, or address of an user-defined interrupt handler defined using DEFINE_INT_HANDLER. It must not be address of an ordinary C function. See DEFINE_INT_HANDLER for an example of usage.


Language extensions


DEFINE_INT_HANDLER (HandlerName)

DEFINE_INT_HANDLER is a language extension macro which is used for defining interrupt handlers. The syntax is similar like function definition:
DEFINE_INT_HANDLER (HandlerName)
{
  // The body of the handler...
}
Note, however, that DEFINE_INT_HANDLER does not define a standard function: it constructs an object named HandlerName of INT_HANDLER type, which is initialized to point to the handler body (implemented internally as a function, but unaccessable directly to the rest of the program). So, you can not call the interrupt handler using a standard call construction like
HandlerName ();
Such behaviour is implemented due to safety reasons: interrupt handlers are not supposed to be executed directly. If you need to call the interrupt handler anyway, you can use ExecuteHandler function. Here is an example of the program which installs the new (user-defined) interrupt handler for auto interrupt 5 (the programable timer interrupt), in which the old (default) handler is called too:
#include <intr.h>
#include <stdio.h>
#include <kbd.h>

int _ti89, _ti92plus;

INT_HANDLER OldInt5 = NULL;
volatile int Counter = 0;

DEFINE_INT_HANDLER (MyInt5)
{
  Counter++;
  ExecuteHandler (OldInt5);
}

void _main (void)
{
  OldInt5 = GetIntVec (AUTO_INT_5);
  SetIntVec (AUTO_INT_5, MyInt5);
  while (!kbhit()) printf_xy (50, 50, "Counter=%d  ", Counter);
  SetIntVec (AUTO_INT_5, OldInt5);
  GKeyFlush ();
}
The only legal usage of INT_HANDLER objects is to be passed as arguments to the functions SetIntVec or ExecuteHandler.

Be aware that the variable Counter in above example is declared as volatile. In fact, any global variable which may be changed by the interrupt handler should be declared as volatile, especially if it is accessed from the other parts of the program (i.e. out of the interrupt handler). This is necessary to prevent various optimizations which may be fooled by the fact that the variable may be changed in a way which is completely unpredictable from the aspect of the normal program flow. For example, various optimizations may force keeping the variable content in a register, so if the variable is changed asynchronously, the compiler will not know anything about it. volatile will prevent keeping the variable in a register, so it will be reloaded from the memory on each access. The example given above will still work if you omit volatile keyword, but more complicated programs will probably not work correctly without it.


Constants and predefined types


enum Bool

Bool is enumerated type for describing true or false values. It is defined as
enum Bool {FALSE, TRUE};

enum AutoInts

AutoInts is enumerated type for easier access to the standard interrupt vectors. So far, this enum is incomplete, and includes only Auto-Int vectors (not traps, CPU exceptions, etc.). It is currently defined as
enum AutoInts {AUTO_INT_1 = 0x64, AUTO_INT_2 = 0x68, AUTO_INT_3 = 0x6C,
  AUTO_INT_4 = 0x70, AUTO_INT_5 = 0x74, AUTO_INT_6 = 0x78, AUTO_INT_7 = 0x7C};

type INT_HANDLER

INT_HANDLER is a pointer type which represents the address of the interrupt handler. It might be logical that INT_HANDLER is defined as a pointer to a void function, i.e.
typedef void (*INT_HANDLER)(void);
But this is not true. Instead, INT_HANDLER is a pointer to a strange structure (its shape is completely irrelevant, as this structure is never used, neither explicitely nor implicitely). Such unusual behaviour is implemented due to safety reasons. First, with such implementation it is impossible to call an interrupt handler using a simple function call (which would be possible if INT_HANDLER is implemented as a pointer to a function). Second, as INT_HANDLER is a pointer to an unusual structure, the compiler can emit a warning if you try to pass anything which is not created using DEFINE_INT_HANDLER or returned from GetIntVec to the functions SetIntVec and ExecuteHandler. For example, you will be warned if you try to pass an ordinary void function instead of properly-defined interrupt handler to the SetIntVec.

const FIRST_AUTO_INT

FIRST_AUTO_INT is the constant with value 1 (the index of the first Auto-Int vector).

const LAST_AUTO_INT

LAST_AUTO_INT is the constant with value 7 (the index of the last Auto-Int vector).

const AUTO_INT_COUNT

AUTO_INT_COUNT is the constant with value 7 (the total number of Auto-Int vectors).

const DUMMY_HANDLER

DUMMY_HANDLER is the constant of type INT_HANDLER (more precise, it is a macro which behaves as a constant), which points to a dummy interrupt handler, i.e. to the handler which consists only of 'rte'. The purpose of this constant is to redirect an interrupt vector to "nothing", in cases when disabling interrupts is not possible. For example, you can not disable auto-int 1 in grayscale programs, because grayscale support is based on it. Grayscale support installs its own auto-int 1 handler, which executes previously installed handler at the end. Suppose that you don't want calling default auto-int 1 handler, which trashes the status line by displaying keyboard status indicators. You can redirect auto-int 1 to the dummy handler before enabling grayscale, so after the grayscale interrupt, the dummy handler (i.e. nothing) will be called instead of the default auto-int 1 handler:
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 whatever you want in grayscale
// disable grayscale
SetIntVec (AUTO_INT_1, save_int_1);

Return to the main index