Back
Featured image of post NEC PC-8801 Keyboard Conversion

NEC PC-8801 Keyboard Conversion

Converting the NEC PC-8801 to USB using Arduino

This post is a summary of a Deskthority thread that I created in 2018. I’ve tried my best to piece the details together.

Introduction

At some point I purchased a very dirty NEC PC-8810 keyboard with blue Alps that was exceptionally dusty. My original plan was just to clean it up and sell it, but I soon became enthralled with the idea of converting the keyboard to USB.

Clean Up

As I mentioned, the board was super dusty under the caps, so my first job was to give it a good cleaning.

The two arrows in the photo above turned out to be SKCM heavy blues, which are around 200g force to press. I desoldered all of the switches and broke down the board into components.

Then I stripped all of the rust off the plate and painted it. Cleaned all of the switches and reinstalled them.

Analysis

I bought a pack of these connector plugs, but they unfortunately the pins were not very accessible. So I ended up making a really messy but functional cable connector with a bunch of wire head-shrink tubes.

I used a multimeter in continuity mode to do some basic tracing of the pins for the cable.

I found a blog post by Leaded Solder who had the actual computer itself and was trying to reverse engineer the keyboard. This was a super helpful find.

This explanation of how the keyboard works definitely pointed me in the right direction.

What this means in practice is that the PC88 itself does the key reading. The keyboard connector has 13 pins (14 if you count the ground sleeve), and in order to read a key, the computer triggers four of those pins. A multiplexer on the keyboard uses those four lines to activate the appropriate row on the keyboard matrix, and eight lines are triggered in response to tell the computer which keys on that row are pressed.

I confirmed with a quick and dirty Arduino sketch that this is most likely how the keyboard reading works.

My sketch pulls one of the signal pins low then reads each of the remaining 8 pins. If you press a key on the keyboard that’s in the row corresponding to the signal pin that was pulled low, the sketch will output the matching column number.

I ran my sketch for each key and compared against the memory map table from leadedsolder and everything matched up with just a couple of exceptions where a keypress didn’t generate any output.

I eventually was able to revise my Arduino sketch into a working prototype converter.

Converter

Pinout

I used a Teensy 2.0 which has this pinout:

The corresponding pins on the keyboard connector are:

Code

Here is the code for the converter. I’m using the arduino keyboard library so if you’d like to switch around the keymap, you can use this page to find all the keycodes. To compile the sketch in the arduino IDE, make sure you’ve set the USB type to keyboard by going to: Tools > Usb Type > Keyboard + Mouse + Joystick.

A big thank you again to the author of leadedsolder.com whose posts were key in getting this working.

/* Copyright (C) 2018 Snacksthecat
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
ypconst int keyCount = 92;
const long keyMap[12][8] = {
  { KEYPAD_0,       KEYPAD_1,      KEYPAD_2,       KEYPAD_3,      KEYPAD_4,        KEYPAD_5,         KEYPAD_6,          KEYPAD_7         },
  { KEYPAD_8,       KEYPAD_9,      KEYPAD_ASTERIX, KEYPAD_PLUS,   KEYPAD_ENTER,    KEY_COMMA,        KEYPAD_PERIOD,     KEYPAD_ENTER     },
  { KEY_LEFT_BRACE, KEY_A,         KEY_B,          KEY_C,         KEY_D,           KEY_E,            KEY_F,             KEY_G            },
  { KEY_H,          KEY_I,         KEY_J,          KEY_K,         KEY_L,           KEY_M,            KEY_N,             KEY_O            },
  { KEY_P,          KEY_Q,         KEY_R,          KEY_S,         KEY_T,           KEY_U,            KEY_V,             KEY_W            },
  { KEY_X,          KEY_Y,         KEY_Z,          KEY_TILDE,     KEY_SLASH,       KEY_SLASH,        KEY_EQUAL,         KEY_MINUS        },
  { KEY_0,          KEY_1,         KEY_2,          KEY_3,         KEY_4,           KEY_5,            KEY_6,             KEY_7            },
  { KEY_8,          KEY_9,         KEY_QUOTE,      KEY_SEMICOLON, KEY_COMMA,       KEY_PERIOD,       KEY_SLASH,         KEY_BACKSLASH    },
  { KEY_DELETE,     KEY_UP,        KEY_RIGHT,      KEY_BACKSPACE, MODIFIERKEY_ALT, MODIFIERKEY_CTRL, MODIFIERKEY_SHIFT, MODIFIERKEY_CTRL },
  { KEY_ESC,        KEY_F1,        KEY_F2,         KEY_F3,        KEY_F4,          KEY_F5,           KEY_SPACE,         KEY_ESC          },
  { KEY_TAB,        KEY_DOWN,      KEY_LEFT,       KEY_INSERT,    KEY_ESC,         KEYPAD_MINUS,     KEYPAD_SLASH,      KEY_CAPS_LOCK    },
  { KEY_PAGE_UP,    KEY_PAGE_DOWN, 0,              0,             0,               0,                0,                 0                }
};

long keysHeld[keyCount];
long keysPressed[keyCount];

void setup() {
  // put your setup code here, to run once:

  // Signal pins
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  
  // Input pins
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);

  Serial.begin(115200);
  Serial.println("hidave");
}

void addHeldKey(long key) {
    for(int i = 0; i < keyCount; i++) {
      
    if(keysHeld[i] == 0) {
      keysHeld[i] = key;
      Keyboard.release(key);
      break;
    }
  } 
}

void removeHeldKey(long key) {
    for(int i = 0; i < keyCount; i++) {
      if(keysHeld[i] == key) {
        keysHeld[i] = 0;
        break;
      }
  }
}

void readInputPins(int row) {
  int col = 0;

  // For each column pin
  for(int i = 4; i < 12; i++) {

    // If the key is pressed
    if(digitalRead(i) == LOW) {
      long key = keyMap[row][col]; 

      // Place it in an empty slot
      for(int j = 0; j < keyCount; j++) {    
        if(keysPressed[j] == 0) {
          keysPressed[j] = key;
          break;
        }
      }
    }
    col++;
  }
}

void queryRow(int row) {
    // Pin 0
    if(row == 0 or 
       row == 1 or 
       row == 2 or 
       row == 3 or 
       row == 4 or 
       row == 5 or 
       row == 6 or 
       row == 7) {
      pinMode(0, OUTPUT);
      digitalWrite(0, LOW);
    }

    // Pin 1
    if(row == 0 or 
       row == 1 or 
       row == 2 or 
       row == 3 or 
       row == 8 or 
       row == 9 or 
       row == 10 or 
       row == 11) {
      pinMode(1, OUTPUT);
      digitalWrite(1, LOW);
    }

    // Pin 2
    if(row == 0 or 
       row == 1 or 
       row == 4 or 
       row == 5 or 
       row == 8 or 
       row == 9) {
      pinMode(2, OUTPUT);
      digitalWrite(2, LOW);
    }

    // Pin 3
    if(row == 0 or 
       row == 2 or 
       row == 4 or 
       row == 6 or 
       row == 8 or
       row == 10) {
      pinMode(3, OUTPUT);
      digitalWrite(3, LOW);
    }

    readInputPins(row);

    // Revert pin to input
    pinMode(0, INPUT_PULLUP);   
    pinMode(1, INPUT_PULLUP);  
    pinMode(2, INPUT_PULLUP); 
    pinMode(3, INPUT_PULLUP);
}

void updateKeysPressed() {
  for(int i = 0; i < 12; i++) {
    queryRow(i);
  }
}

void clearKeysPressed() {
  for(int i = 0; i < keyCount; i++) {
    keysPressed[i] = 0;
  }
}

void updateKeysHeld() {
  long key;

  for(int i = 0; i < keyCount; i++) {
    key = keysPressed[i];

    if(key != 0) {
      if(checkForHeldKey(key) == false) {
        Serial.println(key);
        addHeldKey(key);
        Keyboard.press(key);
      }
    }
  }

  for(int i = 0; i < keyCount; i++) {
    key = keysHeld[i];

    if(key != 0) {
      if(checkForReleasedKey(key) == true) {
        removeHeldKey(key);
        Keyboard.release(key);
      }
    }
  }
}

bool checkForHeldKey(long key) {
  bool result = false;
  
  for(int i = 0; i < keyCount; i++) {
    if(keysHeld[i] == key and key != 0) {
      result = true;
      break;
    }
  }

  return result;
}

bool checkForReleasedKey(long key) {
  bool result = true;

  for(int i = 0; i < keyCount; i++) {
    if(keysPressed[i] == key and key != 0) {
      result = false;
      break;
    }
  }
  
  return result;
}

void loop() {
  // Clear out the old keys
  clearKeysPressed();
  
  // Load up the currently pressed keys
  updateKeysPressed();

  updateKeysHeld(); 
}
Built with Hugo
Theme Stack designed by Jimmy