                         
                                           
                                
                                         
                            
  (c) by Andreas Ess, Sam Davies, Jimmy Mrdell, Austin Butler and Mel Tsai

                        Interrupt and TSR programming

[- INDEX -----------------------------------------------------------------]

 1) Interrupts: an introduction
 2) Program-Length interrupts
 3) TSR programming

[- INTERRUPTS: AN INTRODUCTION -----------------------------------[ 1 ]---]

An interrupt occurs about every 200th (it depends slightly on battery strength)
of a second on the TI-85. Each time one occurs, the PC (Program Counter) skips
to a specified location in memory, and starts executing interrupt handler code.
Interrupts have many uses... from grayscale to screen captures, interrupts can
do anything that needs to go on continuously in the "background" of your
program.

In ZShell, there have been many uses of the Z80's vectored interrupt mode 2,
but ZShell offered no support for the advanced use of interrupts, so the use
of interrupts were very limited in this way. Usgard, on the other hand,
supports the powerful use of multiple interrupts and TSRs.

[- PROGRAM-LENGTH INTERRUPTS -------------------------------------[ 2 ]---]

To install a program-length interrupt, simply use a call to INT_INSTALL with
HL pointing to your interrupt handler code. There is no need to relocate your
code in anyway (unless, of course, you are planning on using the variable
manipulation functions -- VAR_RESIZE, and VAR_DELETE -- which can change the
location of your program in the calculator's memory). The maximum number of
interrupts that can be installed in Usgard is currently five -- INT_INSTALL
will set the carry flag if it cannot install your interrupt code, so be sure
to check this.

Usgard's interrupt handler is a short, expandable list of CALL's to interrupt
handler functions. Before and after calling these functions, Usgard executes a:

  EXX
  EX AF,AF'

swapping regular registers for the "shadow" registers. Note that, Usgard does
not automatically save IX, IY, and all other registers, so if you wish to use
them, be sure to save them! Also, note that you don't need to have a JP $38 at
the end of your code to let the calculator handle its interrupts, (as you did
in ZShell) simply put a RET, as if it was a normal function.

Since these interrupts are only program-length, they should be uninstalled
before exiting your program, because if memory gets changed (by Usgard, or by
the TI-85), your interrupt handler will get moved, and the calculator will
surely crash. If you wish to install interrupts that go beyond program-length,
see the next section.

To uninstall your interrupts, simply call INT_REMOVE for each of the interrupts
you have installed in the *opposite* order that you installed them.  Much like
the stack, Usgard interrupts are, in effect, "pushed" on and "popped" off.
You should take care that all the interrupts you installed are removed at the
end of your program, so your interrupt handlers don't remain to crash the
calculator later...

[- TSR PROGRAMMING -----------------------------------------------[ 3 ]---]

Usgard also supports TSR (terminate and stay resident) interrupts, although
only in the STANDARD installation of Usgard. These interrupts allow an
interrupt handler function to remain active after the installer string has
finished. Because the TI-85 is always rearranging its memory, care must be
taken to see that the TSR code doesn't get moved.

To anchor code in place in memory, you should use the Usgard function APPEND
(was previously in a library) to add your code to the end of Usgard (which
always stays in the same place in memory). Use the function APPEND with HL
pointing to your code, BC indicating its length to append your interrupt
code on to the Usgard string and DE pointing to a description of the TSR
installed (usually the title of the program which installs). The description
is needed before unappending the TSR.

After you have appended your code, simply call INT_INSTALL (APPEND returns
HL with a pointer to the beginning of the appended code), and your TSR will
be installed.

(IMPORTANT NOTE: APPEND and UNAPPEND use VAR_RESIZE, which means that your
program will be in a different place when it returns. If you call them from
within a function, be sure to use RCALL_ to call that function, otherwise the
value pushed onto the stack by a regular call will be wrong and the calc
will most likely crash.)

Uninstalling your code is a bit trickier. To uninstall your code, you must
first make sure that your code is appended to Usgard (you could do this with
a variable stored within your program, but if a user deletes the original
installer program, then the interrupt will be "forgotten"). To do this, use
CHECK_APPEND, and have HL point to the description of the TSR. If the
code is NOT appended at all, the Zero flag will be set. If the code is
the last appended code, the Carry flag will be set. If it has been appended,
but is not the last code, neither flag is set.

Once you have found that your code is within Usgard and that it is at the end
of the string (you should display a message asking the user to remove other
appended code if your code's not at the end), you should first INT_REMOVE
it (just be sure that you haven't installed any interrupts in the program
which is to remove the TSR) and then you can UNAPPEND it, and your TSR has
been successfully uninstalled.

If you want to use variables in a TSR, it gets tricky. If you want to use
a variable in the TSR handler, but don't care it gets lost the next time
the TSR is called, you could use the end of the stack for that purpose
($FA70).

Most of the time though, you want to use a variable that will be "remembered"
all the time the TSR is installed. Then it gets a bit tricky because you can't
use any "public" storage (such as the stack, since other TSR could accidently
use the same location). You have to store the variable inside the TSR handler.
The problem with that is that you don't know where in the Usgard string your
TSR will be appended. The best solution is to store a pointer inside the TSR
(which is done at the same time as appending) which holds the address of TSR
handler (which was returned by APPEND). Example:

 ld hl,&TSR_Start
 ld de,(PROGRAM_ADDR)     ; Description = program name
 ld bc,TSR_End-TSR_Start
 call APPEND  ; This will return HL->where in Usgard the TSR is
 push hl
 ld d,h
 ld e,l
 inc hl
 ld (hl),e    ; This will store that pointer so the first instruction
 inc hl       ; in the TSR_Handler becomes  ld de,<address>
 ld (hl),d
 pop hl
 call INT_INSTALL
 ret

 TSR_Start:
  ld de,0     ; This will load DE with the address to TSR_Start
  ld hl,Variable-TSR_Start
  add hl,de   ; Add it with the relative offset to Variable
  ld a,(hl)   ; Now A = Variable
  .
  .
  ret
 Variable:
  .db 0
 TSR_End:

For more details about TSR programming, check the source to the
TSR Contrast Changer (contrast.asm).
