GeoCities Rank My SiteTake A TourMy GuestbookChat
Pages Like MineSearchSend This PageForums
Email Me
SiliconValley

Need a job?

Multiple Key Scanning

By Don Barnes

This tutorial covers the basics of scanning for multiple keypresses using 68k assembly language. Examples in this lesson should work on the TI-89, TI-92, TI-92 II and TI-92+ models. For best results, compile programs using the Fargo or PlusShell toolkits. 

In the past you may have used flib::idle_loop or $75b2 to read the keyboard. However, these methods are insufficient for detecting multiple keys being pressed at the same time. For this we must use the I/O ports. The I/O ports are mapped to memory, which means we can access them simply by writing and reading the addresses to which they are mapped. 

If we are going to be doing port based key scanning, we must first disable TI's Auto-Int 1 handler, otherwise we will get unreliable results. Keep in mind that once we do this, you will not be able to use flib::idle_loop or the keyboard buffer, as these rely on the Auto-Int 1 handler. Also, you must reenable the old Auto-Int 1 handler before your program exits. If you do not know how to do this, I have included an example at the end of this lesson. 

The two addresses we are interested in are $600018 and $60001B. $600018 is the keyboard row mask. Setting a bit of the mask will prevent any of the corresponding rows of the keyboard matrix from being read. $60001B is the keyboard column mask. Here you'll read the mask corresponding to the columns of the keyboard matrix. A clear bit means a key in that column is being held down. Any masked rows are ignored. Now I will introduce the keyboard matrix:

no. 
down  right  up  left  hand  shift  diamond  2nd 
F8 
F3 
F7  STO 
F2  SPACE 
TAN  COS  SIN  F6 
ENTER2  LN  F1 
APPS  CLEAR  F5 
ESC  MODE  theta  backspace 
(-)  0 (zero)  F4  ENTER1 
Note:  ENTER1 is both the ENTER keys on the bottom of the keyboard, ENTER2 is the one is below the cursor pad. 

The numbers along the top of this matrix represent the bit numbers of the column mask. The numbers along the left side are the row numbers. We usually want to read only one row of this matrix at a time. So, to detect whether a particular key is held down, we must first determine which row it is on, and then mask out the rows we don't want (all of the others). For example, if we want to read row 6, our mask would be: 

%1111111110111111 = $FFBF 

Then we write our mask to the address $600018. 

 move.w #$FFBF,$600018
Now it is necessary to pause for a short time while the I/O takes place. This can be accomplished with the nop instruction. I recommend at least a dozen nop's. While fewer may work on a normal TI-92, 256k models and turboed calcs run much faster, so more nop's are needed to make them wait the same amount of time. This is the reason some older Fargo programs do not work correctly on the TI-92 II. 

Finally we can read $60001B to get our row (we only need 8 bits): 

 move.b ($60001B),d0
So, if only J is pressed, d0.b will contain: 

%11111011 

Now of course we want to write a loop that will read all of the rows we need. Below is the routine I using in my programs: 

keyscan:
 movem.l d0-d1/a0,-(a7)
 lea keys(PC),a0            ; a0 points to an array where we'll store our data
 move.w #$FFFE,d0           ; our mask - initially masks out all rows but 0
 move.w #9,d1               ; there are 10 rows, so we'll branch n-1 = 9 times
ks_loop
 move.w d0,($600018)        ; write mask
 nop                        ; wait for I/O to recover
 nop
 nop
 nop
 nop
 nop
 nop
 nop
 nop
 nop
 nop
 nop
 nop
 nop
 move.b ($60001B),(a0)+     ; read data, store in array, and increment ptr
 rol.w #1,d0                ; rotate our mask left by 1 bit for next row
 dbra.w d1,ks_loop
 movem.l (a7)+,d0-d1/a0
 rts

keys:
 dcb.b 10,0                 ; we need 10 bytes to store the entire matrix

     ... example of how to test for a key

 ;  general form: 
 ;    btst #colno,(keys+rowno)
 ;  example:

 ...
 btst #3,(keys+9)       ;tests for the [q] key
 beq quit               ;if [q] is being pressed, branch to quit:
 ...

Below is an example of disabling TI's Auto-Int 1 handler. It simply changes the Auto-Int 1 vector to point to a custom made handler (in this case it does nothing).
 move.w #$700,d0                ;\ disable interrupts, and put
 trap   #1                      ;/ the old int mask in D0
 bclr.b #2,$600001              ; disable protection of memory
 move.l ($64),oldint_addr
 move.l #n_aint1,($64)
 bset.b #2,$600001              ; re-enable protection of memory
 trap   #1                      ; restore the old interrupt mask

        ... your program goes here

 move.w #$700,d0                ;\ disable interrupts, and put
 trap   #1                      ;/ the old int mask in D0
 bclr.b #2,$600001              ; disable protection of memory
 move.l oldint_addr,$64
 bset.b #2,$600001              ; re-enable protection of memory
 trap   #1                      ; restore the old interrupt mask

        ... put this routine somewhere in your source

n_aint1:
 rte

        ... put these in your variable area

oldint_addr dc.l 0

Most of the information about the TI-92 in this tutorial was found in LowLevel.txt, Convert.txt, and Traps.txt, all by David Ellsworth and all included with Fargo II. Please notify me of any errors. 

© Copyright 1998 Don Barnes - don.barnes@wmich.edu

Click Here!