Routines for creating interrupt handlers
Defines an interrupt handler function.
DEFINE_INT_HANDLER is a language extension macro which is used for defining interrupt handlers. The syntax is similar to 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 cannot 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 as well (called "Interrupt Handler"):
// Interrupt handler incrementing a counter #define USE_TI89 // Compile for TI-89 #define USE_TI92PLUS // Compile for TI-92 Plus #define USE_V200 // Compile for V200 #define OPTIMIZE_ROM_CALLS // Use ROM Call Optimization #define MIN_AMS 100 // Compile for AMS 1.00 or higher #define SAVE_SCREEN // Save/Restore LCD Contents #include <tigcclib.h> // Include All Header Files 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 the volatile
keyword, but more complicated programs
will probably not work correctly without it.
#define AUTO_INT(IntNo) ((long) (IntNo) * 4 + 0x60) |
Gets an address of an interrupt vector.
AUTO_INT returns the absolute address where the interrupt vector for Auto-Int IntNo
is located.
You can use it together with
FIRST_AUTO_INT,
LAST_AUTO_INT, and
AUTO_INT_COUNT
to loop through all interrupts.
void DisablePRG (void); |
Disables the programmable rate generator.
DisablePRG disables the programmable rate generator, which means that the
value determined by PRG_getValue does not
change and AUTO_INT_5 is not called any more.
Note: Auto interrupt 5 is used by the AMS, so the previous state
(which can be determined using
IsPRGEnabled) should be restored before
the program terminates.
This macro does not work in VTI 2.5 Beta 5.
See also: EnablePRG, OSRegisterTimer
INT_HANDLER DUMMY_HANDLER; |
A dummy interrupt handler doing nothing.
DUMMY_HANDLER is an interrupt handler of type INT_HANDLER
which consists only of 'rte'
. The purpose of this handler is
to redirect an interrupt vector to "nothing", in cases when disabling interrupts is not
possible. For example, you cannot 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
the previously installed handler at the end. Suppose that you don't want it to call the 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);
void EnablePRG (void); |
Enables the programmable rate generator.
The programmable rate generator is used by the AMS and is normally on. If it
has been disabled using DisablePRG,
EnablePRG re-enables it.
This macro does not work in VTI 2.5 Beta 5.
See also: DisablePRG, OSRegisterTimer
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). Otherwise, 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 the address of a user-defined interrupt handler defined using DEFINE_INT_HANDLER. It must not be the address of an ordinary C function. See DEFINE_INT_HANDLER for an example of usage.
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 IntVecs). 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.
short IsPRGEnabled (void); |
Determines whether the programmable rate generator is enabled.
The programmable rate generator is used by the AMS and is normally on. However, if you enable or disable it in a program, first you should check whether it is currently enabled, which can be done with this macro.
See also: EnablePRG, DisablePRG
short PRG_getRate (void); |
Returns the speed at which the programmable rate generator is incremented.
This function returns the current speed of the programmable rate generator. For more information, see PRG_setRate.
See also: PRG_setRate
unsigned char PRG_getStart (void); |
Returns the starting value of the programmable rate generator variable.
PRG_getStart returns the starting value of the variable incremented by the programmable rate generator. For more information about this variable, see PRG_getValue.
See also: PRG_setStart, PRG_getValue
unsigned char PRG_getValue (void); |
Returns the current value stored in the programmable rate generator.
PRG_getValue returns the current value of the variable incremented by the programmable rate generator. When this value overflows from 0xFF to 0x00, auto interrupt 5 will be triggered (see PRG_setStart for more information about this value, and SetIntVec for more information about auto interrupts).
See also: PRG_setStart, PRG_setRate
void PRG_setRate (short rate); |
Sets the speed at which the programmable rate generator is incremented.
The programmable rate generator's speed can be controlled by this function.
Valid values for rate are as follows:
Value | Speed |
0 | OSC2/2^5 (highest rate) |
1 | OSC2/2^9 (default) |
2 | OSC2/2^12 |
3 | OSC2/2^18 (lowest rate) |
See also: PRG_getRate
void PRG_setStart (unsigned char val); |
Specifies the starting value of the variable incremented by the programmable rate generator.
PRG_setStart sets the starting value of the variable incremented by the
programmable rate generator. This variable is incremented by 1 every time the
programmable rate generator is triggered (at a speed which can be set using
PRG_setRate). When it overflows from 0xFF
to 0x00, auto interrupt 5 is triggered, and it is reset to val. To
conclude, the way the value of this variable changes is shown here:
..., val, val+1, ..., 0xFF, 0x00, (auto interrupt 5),
val, val+1, ...
Note: If a program uses this function, it should restore the previous
value before the end of the program, as determined by
PRG_getStart.
See also: PRG_getStart, PRG_setRate
void SetIntVec (long IntVec, INT_HANDLER Handler); |
Sets an interrupt vector.
SetIntVec sets the interrupt vector located at the absolute address
IntVec to the interrupt handler pointed to by
Handler. Handler should be either a value returned from
GetIntVec, or the address of a user-defined interrupt
handler defined using DEFINE_INT_HANDLER.
Note that Handler may not be the address of an ordinary C function.
Typical values of IntVec are given in the following table as enumerated
in the IntVecs enum:
Address | Associated Constant | Triggered On |
0x04 | INT_VEC_RESET | Reset (contains pointer to OS entry point) |
0x08 | INT_VEC_BUS_ERROR | Bus error |
0x0C | INT_VEC_ADDRESS_ERROR | Address error (accessing a short or long at an odd address) |
0x10 | INT_VEC_ILLEGAL_INSTRUCTION | Illegal instruction |
0x14 | INT_VEC_ZERO_DIVIDE | Division by zero |
0x18 | INT_VEC_CHK_INS | CHK instruction |
0x1C | INT_VEC_TRAPV_INS | TRAPV instruction |
0x20 | INT_VEC_PRIVILEGE_VIOLATION | Privilege violation |
0x24 | INT_VEC_TRACE | Code Tracing |
0x28 | INT_VEC_LINE_1010 | Special instructions generated by ER_throw (0xA???) |
0x2C | INT_VEC_LINE_1111 | F-Line instructions (0xF???) |
0x3C | INT_VEC_UNINITIALIZED_INT | Uninitialized interrupt vector |
0x60 | INT_VEC_SPURIOUS_INT | Spurious interrupt |
0x64 | AUTO_INT_1 | Main timer hardware interrupt running at ~350-395 Hz on HW1, and 256 Hz on HW2/HW3/HW4 |
0x68 | AUTO_INT_2 INT_VEC_KEY_PRESS | Key press (triggered periodically while key(s) other than 'ON' are held down; the rate depends both on battery strength and on which keys are being held down, and is usually in the ballpark of about 600 Hz) |
0x6C | AUTO_INT_3 | Timer on HW1 (~0.7 Hz) and HW2 (1 Hz) calculators, used by AMS 2.07+ for the clock on HW2; USB interrupt on HW3/HW4 |
0x70 | AUTO_INT_4 INT_VEC_LINK | Link port activity |
0x74 | AUTO_INT_5 | System timer running at approximately 19 Hz (see PRG_setRate, PRG_setStart) |
0x78 | AUTO_INT_6 INT_VEC_ON_KEY_PRESS | 'ON' key press |
0x7C | AUTO_INT_7 INT_VEC_STACK_OVERFLOW | Stack overflow (actually results in Protected Memory Violation) |
Address | Associated Constant | Default Behavior |
0x80 | TRAP_0 | Used by the OS internally, dependent on the OS version - don't use |
0x84 | TRAP_1 INT_VEC_INT_MASK | Change interrupt mask (bits 8-10 of %sr) to %d0.w, output old mask in %d0.l |
0x88 | TRAP_2 INT_VEC_MANUAL_RESET | Reset calculator |
0x8C | TRAP_3 | (unknown) |
0x90 | TRAP_4 INT_VEC_OFF | Turn the calculator off and wait for 'ON' key press |
0x94 | TRAP_5 | (unknown) |
0x98 | TRAP_6 | (unknown) |
0x9C | TRAP_7 | (unknown) |
0xA0 | TRAP_8 | (unknown) |
0xA4 | TRAP_9 | Access to various system routines |
0xA8 | TRAP_10 INT_VEC_SELF_TEST | Enter self test |
0xAC | TRAP_11 INT_VEC_ARCHIVE | Entry point for most operations related to Flash memory (not all of which are exported as ROM_CALLs), special calling convention |
0xB0 | TRAP_12 | Put the processor in supervisor mode; return the previous value of the status register in %d0:w |
0xB4 | TRAP_13 | Print "Trap 13" and freeze |
0xB8 | TRAP_14 | Print "Trap 14" and freeze |
0xBC | TRAP_15 INT_VEC_ER_THROW | Print "ER_throw" and freeze |
See also: GetIntVec, IntVecs, DEFINE_INT_HANDLER
#define TRAP(TrapNo) ((long) (TrapNo) * 4 + 0x80) |
Gets an address of a trap vector.
TRAP returns the absolute address where the interrupt vector for Trap TrapNo
is located.
You can use it together with
FIRST_TRAP,
LAST_TRAP, and
TRAP_COUNT
to loop through all traps.
#define AUTO_INT_COUNT (LAST_AUTO_INT - FIRST_AUTO_INT + 1) |
Returns the total number of Auto-Int vectors.
You can use it together with FIRST_AUTO_INT, LAST_AUTO_INT, and AUTO_INT to loop through all interrupts.
#define FIRST_AUTO_INT 1 |
Returns the index of the first Auto-Int vector.
You can use it together with LAST_AUTO_INT, AUTO_INT_COUNT, and AUTO_INT to loop through all interrupts.
#define FIRST_TRAP 0 |
Returns the index of the first Auto-Int vector.
You can use it together with LAST_TRAP, TRAP_COUNT, and TRAP to loop through all interrupts.
#define LAST_AUTO_INT 7 |
Returns the index of the last Auto-Int vector.
You can use this together with FIRST_AUTO_INT, AUTO_INT_COUNT, and AUTO_INT to loop through all interrupts.
#define LAST_TRAP 15 |
Returns the index of the last Auto-Int vector.
You can use this together with FIRST_TRAP, TRAP_COUNT, and TRAP to loop through all interrupts.
#define TRAP_COUNT (LAST_TRAP - FIRST_TRAP + 1) |
Returns the total number of Auto-Int vectors.
You can use it together with FIRST_TRAP, LAST_TRAP, and TRAP to loop through all interrupts.
A pointer to an interrupt 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.
enum IntVecs {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, TRAP_0 = 0x80, TRAP_1 = 0x84, TRAP_2 = 0x88, TRAP_3 = 0x8C, TRAP_4 = 0x90, TRAP_5 = 0x94, TRAP_6 = 0x98, TRAP_7 = 0x9C, TRAP_8 = 0xA0, TRAP_9 = 0xA4, TRAP_10 = 0xA8, TRAP_11 = 0xAC, TRAP_12 = 0xB0, TRAP_13 = 0xB4, TRAP_14 = 0xB8, TRAP_15 = 0xBC, INT_VEC_RESET = 0x04, INT_VEC_BUS_ERROR = 0x08, INT_VEC_ADDRESS_ERROR = 0x0C, INT_VEC_ILLEGAL_INSTRUCTION = 0x10, INT_VEC_ZERO_DIVIDE = 0x14, INT_VEC_CHK_INS = 0x18, INT_VEC_TRAPV_INS = 0x1C, INT_VEC_PRIVILEGE_VIOLATION = 0x20, INT_VEC_TRACE = 0x24, INT_VEC_LINE_1010 = 0x28, INT_VEC_LINE_1111 = 0x2C, INT_VEC_UNINITIALIZED_INT = 0x3C, INT_VEC_SPURIOUS_INT = 0x60, INT_VEC_KEY_PRESS = 0x68, INT_VEC_LINK = 0x70, INT_VEC_ON_KEY_PRESS = 0x78, INT_VEC_STACK_OVERFLOW = 0x7C, INT_VEC_INT_MASK = 0x84, INT_VEC_MANUAL_RESET = 0x88, INT_VEC_OFF = 0x90, INT_VEC_SELF_TEST = 0xA8, INT_VEC_ARCHIVE = 0xAC, INT_VEC_ER_THROW = 0xBC}; |
An enumeration describing interrupt vectors.
IntVecs is an enumeration for easier access to the standard interrupt vectors.
These include the usual auto-interrupts and traps as well as the other predefined
interrupts, which are triggered in special cases.
Note that some traps have their own names. For example, TRAP_4 equals INT_VEC_OFF.
Deprecated alias: AutoInts
See also: SetIntVec, GetIntVec