Best viewed in 1024x768

Interrupts

Welcome to the interrupt section! This section is very comprehensive, and I am proud of its unique and detailed information. This covers some of the ROM interrupt code and explains Interrupt Mode 1 and Interrupt Mode 2 programming applications and theory. For even more advanced interrupt information, including "The reset, Calc-On, Calc-Off operations", Fixing the Down-Left Bug", "Speedy IM 1", "Speedy IM 2", "TSR techniques", please proceed here. Enjoy!

On the Z80 processor, an interrupt is an action (triggered by hardware) that will suspend CPU operation and force the CPU to perform another function, or execute another routine. Right before the Interrupt Routine is called, it will push the PC (Program or Instruction Counter) on to the stack so it can return to its point of origin when the interrupt returns. It then will disable the interrupts before the interrupt routine is called. Once the interrupt cycle is completed, the CPU returns to the operation from which it was interrupted. There are three modes of interrupt response, Mode 0, Mode 1, and Mode 2. Which response mode is set is chosen with the asm instructions IM 0, IM 1, and IM 2, respectively.

What actions take place during Interrupt Response Mode 0? Mode 1? Mode 2?


Interrupt Mode 1


What role does an interrupt mode 1 play in the TI-OS?
During Mode 1, an interrupt is executed 200 times per second, and a jump to $0038 is accomplished. So what does the calculator do 200 times per second after it jumps to $0038? A whole bunch of stuff. I was looking through the rom today for some 2-3 hours, and I must admit there is a lot of crap that goes on!

Once $38 is called, it first checks the user interrupt flag to see if the user routine is installed (more on this later). It then checks the checksum to see if the memory area is valid. Here is the code:

;RST 38 - interrupt (called ~200 times/sec.)
0038  08        ex      af,af'       ;save registers
0039  d9        exx
003a  fdcb2356  bit     2,(iy+$23)   ;call user int routine, if installed.
003e  2826      jr      z,$0066      ;User interrupt installed flag set? If not, skip to $66
0040  3afdd2    ld      a,($d2fd)    ;Load checksum byte

;Checks to see if the checksum on the user routine
0043  21fed2    ld      hl,$d2fe
0046  96        sub     (hl)
0047  2125d3    ld      hl,$d325
004a  96        sub     (hl)
004b  214dd3    ld      hl,$d34d
004e  96        sub     (hl)
004f  2175d3    ld      hl,$d375
0052  96        sub     (hl)
0053  219dd3    ld      hl,$d39d
0056  96        sub     (hl)
0057  21c5d3    ld      hl,$d3c5
005a  96        sub     (hl)
005b  2005      jr      nz,$0062
005d  cdfed2    call    $d2fe     ; Checksum is OK, call user routine           
0060  1804      jr      $0066     ; Go to next part of interrupt        
0062  fdcb2396  res     2,(iy+$23); Checksum doesn't add up, so it disables usr routine
The next part appears to branch off into three choices. If bit 0 is set on Port 3, it appears some sort of "ON" action has occurred, such as the on key has been pressed. It will then branch off to another routine that may turn the calc on. If bit 1 is set and bit 0 is clear, it appears that the LCD is on and the on interrupt hasn't occurred. This is what happens when the calculator is up and running. Next, if neither is set, it appears that the LCD is off and the on key is not pressed. Thus the interrupt simply finishes up at $0086. Here is the code:
0066  db03      in      a,($03)
0068  1f        rra               ;On interrupt has happened - goto 008f(calc on,reset?)
0069  3824      jr      c,$008f
006b  1f        rra
006c  3828      jr      c,$0096   ;if LCD is on and On interrupt hasn't occurred,goto 0096
006e  1816      jr      $0086     ;if neither set, end interrupt
$96 is of particular interest. This is what usually is going on normally 200hz.. take a look:
0096  fdcb1e76  bit     6,(iy+$1e)
009a  2800      jr      z,$009c                     ; Looks redundant to me
009c  fdcb1246  bit     indicRun,(iy+indicflags)
00a0  c42a01    call    nz,anim_runindic            ; Animates run indicator if flag set
00a3  fdcb1256  bit     indicOnly,(iy+indicflags)
00a7  2017      jr      nz,$00c0                    ;If indicOnly flag set, then interrupt skips: reading
                                                    ;keyboard, updating APD, and animating cursor.

00a9  cda101    call    $01a1                       ;read keyboard (returns keyb mask=$ff)
00ac  fdcb1e76  bit     6,(iy+$1e)                  ;if flag set, set keyb port to $7f
00b0  2804      jr      z,$00b6
00b2  3e7f      ld      a,$7f
00b4  d301      out     (keyboard),a
00b6  fdcb0c56  bit     curAble,(iy+curflags)       ;if cursor enabled, animate it
00ba  c48d3f    call    nz,_anim_cursor
00bd  cd7001    call    timeout                     ; Update APD - Auto Power Down routines
00c0  3e09      ld      a,$09             ; $09 $0B signals on not pressed, $0A $0B signals on is pressed
00c2  c37000    jp      $0070                       ; Finish routine
As you can see, this code explains a lot. You can disable much of the ability of the interrupts with flags. You can disable the run indicator with the indicRun flag and can disable the built-in keyboard handler with indicOnlyflag. Note this disables get_key style method for getting keys, you must program the ports instead. However this is one method for fixing the famous "down-left" bug. More on this later. Explanations: Now the interrupt "wraps up" at $0070, where the earlier code jumps here at $C2. Here is the code:
0070  fdcb1e76  bit     6,(iy+$1e)
0074  280e      jr      z,$0084
0076  47        ld      b,a          ; First byte received from another routine
0077  db01      in      a,(keyboard)
0079  4f        ld      c,a
007a  3eff      ld      a,$ff
007c  d301      out     (keyboard),a
007e  cb41      bit     0,c
0080  ca5c0c    jp      z,$0c5c      ;sets RAMport to 0, shuts calc off (no ret)
0083  78        ld      a,b
0084  d303      out     ($03),a      ; First byte received from another routine

; Ends routine
0086  3e0b      ld      a,$0b
0088  d303      out     ($03),a      ; $09 $0B signals on not pressed, $0A $0B signals on is pressed
008a  08        ex      af,af'       ; Restore lost registers with shadow registers
008b  d9        exx
008c  fb        ei                   ; Enable interrupts
008d  ed4d      reti                 ; Return from interrupts
At $70 it appears to read the keyboard for some reason, I am not certain what the ROM is trying to do here, if anyone can clarify this for me please e-mail me at matt@acz.org and explain this mystery code. But anyway at $86 it finishes up. This should help clarify some of the things that go on during the TI-0S is IM 1.

There is much more to disassemble in the ROM and a lot to learn if you are interested. Note this is Rom Version 1.2, and you can get this particular commented Rom Version (thanks to Joshua Grams) from Rom Central. You must go through my "security check" to download the rom listing, and you must have a 1.2 ROM on your calculator (check your rom version).

Most of us may think this was a little more detail then they wanted to know, but it is my web page, and I think that the theory behind things is very important. If you want to learn things on the surface, go to another web page. If you are not satisfied with two word answers and cryptic responses, you came to the right place. The next subject will explain more practical interrupt programming. This involves the user interrupt routine.


Practical Applications of Interrupt Mode 1



Thanks goes to Jim Alverson, Dux Gregis, and Joshua Grams for their source code and explanations that help me piece together the concepts of interrupts and user routines.

"So big deal", you think, "The interrupt calls memory location $38 two hundred times per second. How does that help me?". Well, other then the fact that you now have a better understanding of what is going on behind the scenes, the topic of user interrupt routines come up.

The user interrupt routine is a 200 byte location in memory that, if a certain flag is set, will be called 200 times per second. In other words, you can have an asm program be called every interrupt! Since an interrupt runs in the background and affects nothing else, what we have here is TSR (Terminate & Stay Resident) program. If the user interrupt flag is set, the interrupt will jump to the 200 byte memory location and start executing asm code. Once it is finished, it will return to the interrupt and execute the rest of the interrupt. The program can do anything it wants, monitor keypresses, make your own custom screen saver, whatever!

What the Rom Does

 

How to install the user routine
Part of the source code will be used to install the code. Another part will be the code itself that will go in the user routine. This is clearer when you see the template and sample program (later on). Here are the steps needed to install your own user routine.

Any program that runs in the background is basically a TSR. Now if you have a disgruntle TSR, it can be considered a virus. So it is possible to make a virus on a calculator, although it is highly unlikely it will spread for many reasons but I won't discuss them here.

User routine template
This template is the basic setup needed for installing a user routine. It is commented below.

#include "asm86.h"
#include "ram86.inc"
#include "ti86asm.inc"

_user_int_ram EQU $d2fd                      ;Area where interrupt handler goes

.org _asm_exec_ram                                               ;Instruction Counter starts at $D748
        res 2,(iy+$23)                       ;turn user int off so it won't get called by
                                             ;accident before we're ready
   
        ld hl,int_copy                       ;copy prog to user int buffer
        ld de,_user_int_ram+1                  &n