![]() |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
![]() |
![]() |
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. | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | down | right | up | left | hand | shift | diamond | 2nd |
1 | 3 | 2 | 1 | F8 | W | S | Z | |
2 | 6 | 5 | 4 | F3 | E | D | X | |
3 | 9 | 8 | 7 | F7 | R | F | C | STO |
4 | , | ) | ( | F2 | T | G | V | SPACE |
5 | TAN | COS | SIN | F6 | Y | H | B | / |
6 | P | ENTER2 | LN | F1 | U | J | N | ^ |
7 | * | APPS | CLEAR | F5 | I | K | M | = |
8 | ESC | MODE | + | O | L | theta | backspace | |
9 | (-) | . | 0 (zero) | F4 | Q | A | 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,$600018Now 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),d0So, 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 |