Port Number: 01h
Function: Keyboard Port
This port reads key presses from the keypad. The keypad is divided into a series of groups.
- 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).
- $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).
The key map is laid out as follows:
- 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.
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.
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.
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.
While a key is in a state between pressed and released, it will repeatedly turn on and off. Such behavior only occurs for keys that were just released. 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.
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