Close

PS/2 Pain and Suffering and Happiness

A project log for Pocket-Sized Imsai-Style Z80 Board

Similar to the old IMSAI 8080, but small enough to fit in your pocket :)

NickNick 04/12/2021 at 18:420 Comments

PS/2 keyboards are awesome - super cheap (a few bucks from a thrift store!) and easy to interface with - clock and data, sample the data when the clock ticks, build a word, and you've got a scan code. There's work to do in converting scan codes, treating SHIFT etc correctly, and so on. Fortunately, there are Arduino libraries that make this all pretty simple.

I started with https://github.com/PaulStoffregen/PS2Keyboard which is really easy to use and gives you (mostly) ASCII codes in response to key presses. Worked great with my CMStorm mechanical keyboard (they're technically all mechanical, right?) with a PS/2 adpater. But then I tried to use a flexible USB keyboard I had sitting around, and no joy - couldn't detect keystrokes. 

After looking online, I decided maybe this was just a peculiar keyboard, so I ordered a PS/2 KB from Amazon. Alas, no joy there either.

I switched libraries to https://github.com/techpaul/PS2KeyAdvanced which let me see raw codes, and discovered the KB was repeatedly sending 0xAA. Some googling revealed this page: https://forum.arduino.cc/index.php?topic=57610.0 which suggests that the 0xAA is the PASS code for a power-on self test, but is sent with flipped parity, and needs to be responded to with 0xFE, also with flipped parity. Sounded far fetched, but this is the beauty of open source: with access to the source code, it was easy to experiment.

First, I tweaked the code in the interrupt handler, to detect receipt of 0xAA and then call send_now_special(). send_now_special() calls the usual send_now() to send a byte to the keyboard, but I modified it to send with true or inverted parity. This is done by initializing the parity bit to a user-supplied flag instead of 1.

Bingo! The KB works great!

The final change was using https://github.com/techpaul/PS2KeyMap to map from 2-byte status+code to ASCII codes.

    case 11: // Stop bit lots of spare time now
            //if( _parity >= 0xFD )    // had parity error
// detect weird AA code - njm
            if (_shiftdata == 0xAA) // compulsive reset/something...
              {
// changed this to respond with FE with wrong parity (srkinsky et al) - njm
              send_now_special( PS2_KC_RESEND );    // hacked
              _tx_ready |= _HANDSHAKE;
              }
            else                    // Good so save byte in _rx_buffer
void send_now_raw( uint8_t command , int flag) // old send_now...can toggle parity - njm
{
_shiftdata = command;
_now_send = command;     // copy for later to save in last sent
#if defined( PS2_CLEAR_PENDING_IRQ )
_bitcount = 0;          // AVR/SAM ignore extra interrupt
#else
_bitcount = 1;          // Normal processors
#endif
_parity = flag; // haha - njm
_ps2mode |= _TX_MODE + _PS2_BUSY;

Discussions