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.

NEC PC-8801

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.

Removing Switches PCB Pull

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

Painted Plate

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.

Connector Breakout

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

Keyboard Pinout

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.

Memory Map Table

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:

Teensy 2.0 Pinout

The corresponding pins on the keyboard connector are:

Keyboard Connector Pinout

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.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/* 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