Difference between revisions of "83Plus:Ports:01"

From WikiTI
Jump to: navigation, search
(Credits and Contributions: removed nonsensical credit)
(more detailed information on delay)
(18 intermediate revisions by 10 users not shown)
Line 1: Line 1:
 +
[[Category:83Plus:Ports:By_Address|01 - Keyboard]] [[Category:83Plus:Ports:By_Name|Keyboard]] [[Category:83:Ports:By_Address|01 - Keyboard]] [[Category:83:Ports:By_Name|Keyboard]]
 
== Synopsis ==
 
== Synopsis ==
 
'''Port Number:''' 01h
 
'''Port Number:''' 01h
Line 4: Line 5:
 
'''Function:''' Keyboard Port
 
'''Function:''' Keyboard Port
  
This port controls the on-calc keypad. The keypad is divided into a series of groups.
+
This port reads key presses from the keypad. The keypad is divided into a series of groups.
  
 
=== Read Values ===
 
=== Read Values ===
Line 11: Line 12:
 
=== Write Values ===
 
=== Write Values ===
 
* $FF : Reset the keypad. This unselects all groups and resets the keypad state.
 
* $FF : Reset the keypad. This unselects all groups and resets the keypad state.
* Anything else : Any 0 bit adds the corresponding group to the list of monitored groups. When a key in a monitored group is pressed, the corresponding key bit reads 1.
+
* Anything else : Any 0 bit adds the corresponding group to the list of monitored groups. When a key in a monitored group is pressed, the corresponding key bit reads 0, otherwise it reads 1 (active-low).
  
== Comments ==
+
== Key Map ==
 
The key map is laid out as follows:
 
The key map is laid out as follows:
  
<nowiki>    Group Bit ->    0     1     2     3     4     5     6     7
+
{|
              Group Mask ->  FE   FD   FB   F7   EF   DF   BF   7F
+
|
Key Bit (Mask)
+
! Group Bit
0 (FE)                   | DOWN|ENTER| (-) |  .  |  0  |    |GRAPH|    |
+
| 0 || 1 || 2 || 3 || 4 || 5 || 6 || 7
1 (FD)                   | LEFT|  +  |  3  |  2  |  1  | STO |TRACE|    |
+
|-
2 (FB)                   |RIGHT|  -  |  6  |  5  |  4  | LN  |ZOOM |    |
+
|
3 (F7)                   | UP  |  *  |  9  |  8  |  7  | LOG |WIND |    |
+
! Group Mask
4 (EF)                   |    |  /  |  )  |  (  |  ,  | x^2 | Y=  |    |
+
| FE || FD || FB || F7 || EF || DF || BF || 7F
5 (DF)                   |    |  ^  | TAN | COS | SIN |x^-1 | 2nd |    |
+
|-
6 (BF)                   |    |CLEAR|VARS |PRGM |APPS |MATH |MODE |    |
+
! Key Bit !! (Mask)
7 (7F)                   |    |    |    |STAT |X,T..|ALPHA| DEL |    |</nowiki>
+
|-
 +
! 0 !! (FE)
 +
| DOWN||ENTER|| (-) ||  .  ||  0  ||    ||GRAPH||    ||
 +
|-
 +
! 1 !! (FD)
 +
| LEFT||  +  ||  3  ||  2  ||  1  || STO ||TRACE||    ||
 +
|-
 +
! 2 !! (FB)
 +
|RIGHT||  -  ||  6  ||  5  ||  4  || LN  ||ZOOM ||    ||
 +
|-
 +
! 3 !! (F7)
 +
| UP  ||  *  ||  9  ||  8  ||  7  || LOG ||WIND ||    ||
 +
|-
 +
! 4 !! (EF)
 +
|    ||  /  ||  )  ||  (  ||  ,  || x<sup>2</sup> || Y=  ||    ||
 +
|-
 +
! 5 !! (DF)
 +
|    ||  ^  || TAN || COS || SIN ||x<sup>-1</sup> || 2nd ||    ||
 +
|-
 +
! 6 !! (BF)
 +
|    ||CLEAR||VARS ||PRGM ||APPS ||MATH ||MODE ||    ||
 +
|-
 +
! 7 !! (7F)
 +
|    ||    ||    ||STAT ||X,T,&#952;,''n''||ALPHA|| DEL ||    ||
 +
|}
 +
 
 +
* '''Note:''' Group FB Key FE is NEGATE, not to be confused with SUBTRACT.
 +
* '''Note:''' Group DF Key FE would be ON, but the ON key is tested through [[83Plus:Ports:04|Port 04]], bit 3.
 +
* '''Note:''' Group EF Key BF (APPS) is called MATRIX on the TI-83.
 +
 
 +
== Comments ==
 +
You should disable interrupts or use a custom interrupt before manually reading this port, because TI's interrupt scans the keypad. If you leave TI's interrupt active when manually reading the keypad, sometimes it will fire between when you set and read the port, and you'll read garbage.
 +
 
 +
All nonexistent keys read 1.
 +
 
 +
=== Delay ===
 +
TI's code always resets the keypad before scanning; sometimes, you may get incorrect values if you scan without resetting first.
 +
 
 +
Traditionally, a delay of a dozen or so clock cycles (6 MHz mode) is used between changing key groups and reading the result. The required delay seems to vary depending on the calculator; some require no delay, others a longer one. The delay is based on real time, not CPU clock cycles; you'll need to make your delay about 3 times longer in 15 MHz mode. However, as in the example, other people seem to find that resetting before every group change negates the need for a delay; some more experimentation is required.
 +
 
 +
For me (Sorunome) this didn't seem to work: Using the following code didn't always have the correct keygroup selected when scanning (on a TI-84+SE of a friend of mine): 
 +
<pre>ld a,$ff
 +
out (1),a
 +
ld a,%10111110
 +
out (1),a
 +
in a,(1)</pre>
 +
While the following fixed it:
 +
<pre>ld a,$ff
 +
out (1),a
 +
ld a,%10111110
 +
out (1),a
 +
ex (sp),hl
 +
ex (sp),hl
 +
in a,(1)</pre>
 +
 
 +
The reason for this has since been discovered to not be caused by slow hardware, but rather by capacitance in the keypad. When unselecting a group, it takes a while before the charge has dissipated. Resetting the keypad every time after using it will make this discharge happen while you're doing something else, and activating a group happens instantly. The more keys are pressed in the same keygroup, the faster the values are correct. The more keys are pressed in a different keygroup, but on the same bit, the longer you need to wait. A key that's pressed in a different keygroup on a different bit does not seem to influence the time required too much. Some combinations can take more than 50 clock cycles to deactivate, but for single keys being pressed, 8 clock cycles should be enough. However, this may vary from keypad to keypad.
 +
 
 +
=== Ghosting ===
 +
Because TI didn't add a diode to each key, you can also detect keys that aren't in a currently selected keygroup, as long as the other keygroup has at least one key in common with a selected keygroup. This means, for example, if F7 is selected, and the keys 2, 3, and 6 are pressed, Then keygroup FB will also be activated because F7 and FB have one key in common (namely FD). This makes it appear as though the 5 key is also pressed. This can also be used to make nonexistent keys appear active. However, as at least three keys have to be pressed for it to be noticeable, you don't have to worry in the most common use cases.
  
NOTE: Group FB Key FE is NEGATE, not to be confused SUBTRACT.
+
=== Debouncing ===
NOTE: Group DF Key FE could be ON, but the ON key is tested elsewhere (a "special case" key - after all, it is the ON key).
+
While a key is in a state between pressed and released, it will repeatedly turn on and off. If you are reading the keypad extremely quickly, it will appear as though the key is being pressed and released a number of times. You need to either wait longer between keypad scans, or you need to implement a proper debouncing algorithm. TI-OS does the first one by scanning the keypad in an interrupt that runs slowly enough for it to not have a noticeable effect.
  
 
== Example ==
 
== Example ==
Line 37: Line 96:
 
out (1), a
 
out (1), a
 
in a, (1) ;Test for keys.
 
in a, (1) ;Test for keys.
and 0FDh ;Test for Left Arrow key. Basically set all other bits to 0. This means A becomes 0 if left was pressed.
+
and 2 ;Test for Left Arrow key by making A 0 if left was pressed.
call z, LeftArrowPressed ;If A==0 then Left Arrow bit was reset, meaning it was pressed.
+
call z, LeftArrowPressed ;If 0 then left was pressed.
 
ld a, 0FFh ;Reset the keypad.
 
ld a, 0FFh ;Reset the keypad.
 
out (1), a</nowiki>
 
out (1), a</nowiki>

Revision as of 20:07, 8 March 2020

Synopsis

Port Number: 01h

Function: Keyboard Port

This port reads key presses from the keypad. The keypad is divided into a series of groups.

Read Values

  • Bits are set according whether corresponding keys from selected groups are pressed. Groups are selected through writing to the port. A pressed key reads a 0 bit. An unpressed key reads a 1 bit. The port reads FF after a reset (write FF).

Write Values

  • $FF : Reset the keypad. This unselects all groups and resets the keypad state.
  • Anything else : Any 0 bit adds the corresponding group to the list of monitored groups. When a key in a monitored group is pressed, the corresponding key bit reads 0, otherwise it reads 1 (active-low).

Key Map

The key map is laid out as follows:

Group Bit 0 1 2 3 4 5 6 7
Group Mask FE FD FB F7 EF DF BF 7F
Key Bit (Mask)
0 (FE) DOWN ENTER (-) . 0 GRAPH
1 (FD) LEFT + 3 2 1 STO TRACE
2 (FB) RIGHT - 6 5 4 LN ZOOM
3 (F7) UP * 9 8 7 LOG WIND
4 (EF) / ) ( , x2 Y=
5 (DF) ^ TAN COS SIN x-1 2nd
6 (BF) CLEAR VARS PRGM APPS MATH MODE
7 (7F) STAT X,T,θ,n ALPHA DEL
  • Note: Group FB Key FE is NEGATE, not to be confused with SUBTRACT.
  • Note: Group DF Key FE would be ON, but the ON key is tested through Port 04, bit 3.
  • Note: Group EF Key BF (APPS) is called MATRIX on the TI-83.

Comments

You should disable interrupts or use a custom interrupt before manually reading this port, because TI's interrupt scans the keypad. If you leave TI's interrupt active when manually reading the keypad, sometimes it will fire between when you set and read the port, and you'll read garbage.

All nonexistent keys read 1.

Delay

TI's code always resets the keypad before scanning; sometimes, you may get incorrect values if you scan without resetting first.

Traditionally, a delay of a dozen or so clock cycles (6 MHz mode) is used between changing key groups and reading the result. The required delay seems to vary depending on the calculator; some require no delay, others a longer one. The delay is based on real time, not CPU clock cycles; you'll need to make your delay about 3 times longer in 15 MHz mode. However, as in the example, other people seem to find that resetting before every group change negates the need for a delay; some more experimentation is required.

For me (Sorunome) this didn't seem to work: Using the following code didn't always have the correct keygroup selected when scanning (on a TI-84+SE of a friend of mine):

ld a,$ff
out (1),a
ld a,%10111110
out (1),a
in a,(1)

While the following fixed it:

ld a,$ff
out (1),a
ld a,%10111110
out (1),a
ex (sp),hl
ex (sp),hl
in a,(1)

The reason for this has since been discovered to not be caused by slow hardware, but rather by capacitance in the keypad. When unselecting a group, it takes a while before the charge has dissipated. Resetting the keypad every time after using it will make this discharge happen while you're doing something else, and activating a group happens instantly. The more keys are pressed in the same keygroup, the faster the values are correct. The more keys are pressed in a different keygroup, but on the same bit, the longer you need to wait. A key that's pressed in a different keygroup on a different bit does not seem to influence the time required too much. Some combinations can take more than 50 clock cycles to deactivate, but for single keys being pressed, 8 clock cycles should be enough. However, this may vary from keypad to keypad.

Ghosting

Because TI didn't add a diode to each key, you can also detect keys that aren't in a currently selected keygroup, as long as the other keygroup has at least one key in common with a selected keygroup. This means, for example, if F7 is selected, and the keys 2, 3, and 6 are pressed, Then keygroup FB will also be activated because F7 and FB have one key in common (namely FD). This makes it appear as though the 5 key is also pressed. This can also be used to make nonexistent keys appear active. However, as at least three keys have to be pressed for it to be noticeable, you don't have to worry in the most common use cases.

Debouncing

While a key is in a state between pressed and released, it will repeatedly turn on and off. If you are reading the keypad extremely quickly, it will appear as though the key is being pressed and released a number of times. You need to either wait longer between keypad scans, or you need to implement a proper debouncing algorithm. TI-OS does the first one by scanning the keypad in an interrupt that runs slowly enough for it to not have a noticeable effect.

Example

ld a, 0FFh ;Reset the keypad.
out (1), a
ld a, 0FEh ;Select group 0.
out (1), a
in a, (1) ;Test for keys.
and 2 ;Test for Left Arrow key by making A 0 if left was pressed.
call z, LeftArrowPressed ;If 0 then left was pressed.
ld a, 0FFh ;Reset the keypad.
out (1), a