Because the IBM XT uses a keyboard protocol that is no longer used by any newer hardware, my small collection of computer keyboards was useless for this project. At least, that is how it seemed initially.
One could buy an IBM XT-compatible keyboard and have a great time with it, too. IBM keyboards from that era (in particular the excellent Model F and the slightly-later and slightly-cheaper Model M) are legendary for both feel and build quality. The Model F is a mechanical keyboard which uses IBM’s capacitive buckling spring switches. The Model M still uses buckling springs, but they are placed over a membrane. Both fetch a hefty price in the used market, largely because they are legendary and people purchase USB adapters to use them with modern computers.
There were a number of aftermarket keyboards which used XT protocol, but these also tend to be quite expensive simply based on the low supply. As with many electronics of this vintage, most people just threw them away, removing them from the market (my dad and I tossed an original XT and 2 model M keyboards back in the day, in fact — hindsight is 20/20).

I needed a way to have a keyboard for the XT without breaking the bank. A little sleuthing suggested that the AT connector (a 5-pin DIN connector) is readily adaptable to a PS/2 connector (a younger, 6-pin DIN connector that is still commonly found due to its easy adaptability to USB). I had a PS/2 keyboard — an IBM Space Saver II, which is a rubber dome keyboard from 1999 with an integrated TrackPoint like a ThinkPad — which was a lucky thrift shop find from about five years back.
The AT and XT connectors are the same. “Eureka!”, I thought. I can get an AT adapter for my PS/2 keyboard and hope for the best. And I did just that. And plugging it in to the XT did absolutely nothing.
To be fair, I knew that this wouldn’t work. The AT plug and the XT plug look the same, and indeed the pinout is the same. The protocol is entirely different, however. The circuitry behind the XT keyboard (both the data that is sent to the computer and the responses the keyboard gives to the computer’s outputs) are entirely different. The PS/2 protocol and the AT protocol are actually the same and only the plug is different — that is why one can buy a simple connector-to-connector adapter and it will work for those two. But the XT is a different beast entirely.
This means two things, in practice: 1) an AT adapter on a PS/2 keyboard will plug in to an XT port for nought, and 2) a connector-to-connector adapter is impossible because the internal hardware on AT or PS/2 keyboards is different. Challenge accepted.
The microcontroller approach
After more sleuthing, there were others who tackled this with a microcontroller. Though a PIC seems common, others have used Arduino (presumably one could use TI’s MSP430 line if it were on hand). In my parts bin I had several Arduinos — a couple of Unos and some Nanos, both of which use the ATMEGA328P at their core.
The basic idea is to use the microcontroller as a middleman between a PS/2 or AT keyboard and the XT. The microcontroller will translate the signals from one side to the other and fool the computer into thinking an XT-protocol keyboard is connected.

Of course, microcontrollers must be powered. But as it turns out, one pin of both the XT and PS/2,AT connectors is a +5V DC pin — we can run an Arduino off of that, since the current draw is very low. An Arduino Uno doing nothing draws about 45mA. An 80s IBM Model M draws about 110mA. More modern keyboards draw much less, so it is reasonable to assume that the XT’s keyboard circuitry will power both an Arduino plus a PS/2 keyboard without issue.
If attempting this at home, take note of the +3.3V versus +5V VCC pins on the Arduino. Both are technically power, but sending 5 volts through the 3.3 volt rail on accident will fry your microcontroller.
As with most microcontroller projects, I will breadboard this before actually soldering something up to be more permanent. A big part of the project will be programming the Arduino to do what I need (but, there is no need to reinvent the wheel — see below).
Understanding the wiring
I will need some way to wire everything together. I could buy an AT-style DIN-5 plug and a PS/2-style DIN-6 plug and use magnet wire for everything. But it seemed just-as-easy-if-not-easier to buy an AT-to-PS/2 adapter on the cheap and cut it to get both connectors and enough wire to complete all the circuits.

After cutting and stripping this, there are 5 wires in the adapter. This might seem strange, since there are 6 pins on the PS/2 connector. But a look at the PS/2 pinouts shows us what is going on.
The numbering scheme on these pins differs from the standard used for these connectors, but I found it much simpler as I was wiring things up.
Notice that pins 4 and 6 on the diagram are not connected. Only 4 pins of the PS/2 connector (1, 2, 3, and 5) are actually used. Of those, two are used for power (5 and 2 are +5V DC and ground, respectively).
The pinouts for the XT keyboard are similar, as shown below.
It seems that the /RESET line was used only on very old IBM PC keyboards. It can be left out without issue, so I will treat it as NC (not connected).
Shared between the two protocols, then, are the CLK, DATA, VCC, and GND lines. For the VCC and GND, we need only to connect these together and to the microcontroller so that it has power (VCC to VCC and GND to GND, of course). CLK and DATA will be something that the microcontroller works with — specifically, it needs to translate the DATA line using timing from the CLK line. Ultimately, the Arduino will share the voltage and ground of the supply (XT) and the PS/2 keyboard while acting as an intermediary between them.
This will thus require four pins on the microcontroller in addition to the VCC and GND pins. Two will be used for AT,PS/2 and XT DATA and two will be used for AT,PS/2 and XT CLK. A crude schematic is below with symbols corresponding to an Arduino Nano and the two connectors. For prototyping, the Arduino Uno works exactly the same way.

It may (or may not?) be important that the clock pins be set to pins 2 and 3 of the Arduino (reason given below). That is why they are wired as above.
Once everything was hooked up on a breadboard, it was time to take a look at coding the microcontroller to make everything work. There are several projects which aim to do this, so nothing needs to be written from scratch. It is useful to take a look at the protocol and how POST works on the IBM XT, though, in order to understand exactly what has to be done.
How the XT protocol and keyboards work
As of the time of this posting, there is a surprisingly large amount of information online concerning the XT protocol. Remember, that protocol was abandoned in favor of the AT protocol sometime around the late 1980s (the IBM Personal Computer AT, which used the new protocol, was originally released in 1984). IBM seems also to have changed the XT protocol at some early point, given the non-essentiality of the /RESET line. Clones of the IBM XT, which became rampant in the mid-80s, sometimes had slightly different implementations of the protocol. It all adds up to the XT keyboard being a bit of a novelty.
As a hobbyist, I cannot claim to understand all the design decisions of keyboards or why every little thing works the way that it does. But the general vibe from the many internet postings seems to be that the XT protocol is a relatively simple one.
Most or all keyboards use a matrix at their core. There are columns and rows which correspond to switch positions (and any key is, in fact, a switch). In a digital circuit, lines (wires) are typically HIGH (at or near supply voltage), LOW (ground), or floating (neither HIGH nor LOW, but in a high-impedance state in which current does not flow). By depressing a switch and closing a circuit, the user can change the state of both a row and a column, the overlap of which corresponds to that switch and that switch alone.
The diagram below illustrates a hypothetical keyboard matrix using this approach. There are 16 switches (0-F, in hexadecimal) which correspond to the overlaps between 4 rows and 4 columns; the rows and columns are not connected, except when a switch is depressed. It seems to be the case (for this hobbyist) that most keyboards using this approach pull a line LOW when the switch is closed; this appears to be true for XT keyboards, although technically the matrix design need not be bound to any particular protocol. A PS/2 protocol keyboard and an XT protocol keyboard do likely use this same approach when it comes down to the switches.

From the information gleaned about row and column, a keyboard controller can pass this to the computer in a variety of different ways; it is essentially this step which forms the keyboard protocol. A controller could, in principle, collect the bitwise information about columns into a binary value; in the case of the above diagram, switch 5 being depressed might equate to a 1011 (bit 1, the second in the chain starting from 0, is low. This could be inverted to be 0100. For the rows, the diagram might show 1101 (inverted: 0010). These figures (so-called “nibbles”, because they are 4 bits in length) might arbitrarily be translated into some other forms: one might blindly connect them into an 8-bit byte with the columns value being the high nibble and the rows value being the low nibble. Note that under that scheme (nibble,nibble=byte), each switch press corresponds to a unique byte value, which is the entire point:
- Switch 0 being closed would be a 10000001.
- Switch D being closed would be a 01001000.
- Switch B being closed would be a 00010100.
- Switch 5 being closed would be a 01000010.
The value of the number that is gleaned from this arbitrary operation is also arbitrary — it is just a mathematical accident that we can use a number to refer to it. But (without going into base-2 versus base-10 number systems), the 10000001 of a Switch 0 closure could also be referred to as a decimal 129. Switch 5’s 01000010 is decimal 66. We could convert these to hexidecimal or octal too, by convention (as it turns out, hexidecimal is generally used for these).
On the XT, each keypress ends up turning into one byte of data. The keyboard continuously scans the rows and columns for keypresses and, when one is found, it translates that finding into a “scancode”. In practice the 8-bits of data which get used are actually a mix of two codes: one scancode and one “release code”. Because the original keyboards had only 84 keys, there is ample space in 8 bits (max:255 values) to have both codes. These 8 bits are sent on the data line to the computer when a key is pressed. But, the computer also has to know when to listen.
In a typical idle state, the XT protocol requires that the CLK signal be HIGH. The keyboard may pull this LOW (ground it) to signal to the XT that it is sending data on the DATA line. According to the protocol, that data will consist of 1 or 2 “start bits” followed by the 8 bits which correspond to the keypress. Early XT boards apparently output 2 start bits, but one is ignored by the computer. Later ones thus output only 1 start bit. That’s a total of 9 or 10 bits sent per LOW pull of the clock line.
The computer itself may also pull the clock line LOW. It is important to note that the XT protocol does not really allow for the sending of data to the keyboard — the later AT and PS/2 keyboards do allow for this, which is why they can have LEDs that show when Caps Lock or Num Lock is engaged. But for the XT, this was not a possibility. A LOW pulse on the clock line from the computer itself amounted to a keyboard reset. Under the XT protocol, the keyboard was designed to respond with a particular code when the line was pulled LOW (code 0xAA). It must make that response within 1 second, or the computer assumes there is no keyboard connected.
During the Power On Self Test, the XT will continuously pull the CLK line low. Once the RAM check is complete, the line will go HIGH, and the computer will then be ready for keyboard input. The keyboard is expected to respond with 0xAA when the line is pulled LOW by the computer.
So, this is what the microcontroller is expected to do: listen to what the XT is doing, respond accordingly, and then transmit the correct codes in the correct format to the XT when a key is pressed.
Don’t reinvent the wheel (but don’t expect the wheel to work the first time)

As I mentioned, there are a lot of resources about the XT keyboard protocol; the desire for retro gaming computers have driven more people into the space, which is awesome. This also means that most of the heavy lifting has been done for adapting a PS/2 keyboard to XT protocol.
After sifting through a few options, I decided to try out the code posted by kesrut. The code aims to take advantage of the existing PS2KeyAdvanced library for Arduino.
It is worth noting again here that a popular solution to this problem is to use a PIC microcontroller to do the interpretation. This can yield a more concise adapter. The approach I take here is compact (due to ultimately using an Arduino Nano), but still ends up having a bunch of needless circuitry in the middle because the ATMEGA238P remains in its USB-enabled configuration.
The code is pretty straightforward. As with all Arduino code designed for the IDE, it is C/C++. The pins you chose for the DATA and CLK lines need to be defined up top, but this is simple enough.
#define ps_clk 2 // PS CLOCK DATA #define ps_data 4 // PS DATA PIN
#define xt_clk 3 // XT CLOCK PIN #define xt_data 5 // XT DATA PIN
Note that several other similar projects suggest that the clock pins you choose must also be Arduino interrupt pins; on the Uno and Nano, these are pins 2 and 3. It is unclear to me if this is necessary for this particular code requires that, but I set up my wiring that way just in case.
After burning the code to my Arduino test unit with all the wiring breadboarded, I started up the XT. And, it sort of worked. I did not get a keyboard error (301), so I knew that the Arduino was saying something to the XT. And I got some activity from the floppy drive, though it still sounded horrendous, and I was pretty sure that the heads were not moving.
For the first time, the machine technically booted! I was greeted by the BASIC screen. When the XT cannot boot off of a connected drive, it jumps to its BASIC interpreter which is stored in the mainboard ROM. At the time of the release of the PC 5150, this ROM BASIC would have given the machine about the same core functionality of a Commodore 64 or a TI-99/4A. The 5160 XT lacks the cassette port, however, so there is no way to save your work in ROM BASIC which is ultimately a significant limitation.
Still, this was a great sign. It meant that the core bits of the XT were all running correctly, and for this project, it meant that the Arduino had successfully convinced the computer that it was an XT keyboard. But alas, I was not able to type in ROM BASIC.
That was a bit of a conundrum. I was able to boot. If I hit keys during boot, I got beeps (a normal side effect of hitting keys during POST). But once I was in ROM BASIC, I got nothing. So I started sleuthing around in the code. Perhaps there was something I could change to make a difference here.
The final if statement in the Arduino’s loop() function is designed to mimic the XT keyboard during and just after POST. It checks if the clock line is low, and if it is, it responds on the data line with 0xAA.
if (digitalRead(xt_clk) == LOW) // power-on self test { delay(20) ; _write(0xAA) ; }
I wondered if the delay here might have something to do with my issue. Mind you, I didn’t necessarily have a reason to think this. On an unrelated post I read about the scancodes being different for ROM BASIC versus DOS. But I had never been in DOS at this point, so I took a shot at changing the timing on my microcontroller.
I decided to ensure that my connections were correct by getting some feedback when the clock line was being pulled LOW. Do do this, I added code to that final if statement such that the built-in LED would go HIGH whenever the clock line was LOW.
if (digitalRead(xt_clk) == LOW) { digitalWrite(LED_BUILTIN, HIGH); // set LED to HIGH delay(10) ; _write(0xAA) ; digitalWrite(LED_BUILTIN, LOW); // set LED to LOW }
This gave me some feedback (a lit LED) which was enough to suggest that the clock line was connected correctly. Next, I tried adjusting the delay() call. And as it turns out, setting it to just under the maximum time that an XT keyboard is supposed to take to respond did get me keyboard response in ROM BASIC!
delay(999) ; // let's see if this works!
….for some reason. I still do not really know why. And though this proved to me that the hardware part of this was all correct, I had to adjust things later (next post).
Melt some metal
Secure enough that this would work, I soldered up my keyboard adapter. The Nanos that I had did not have headers preinstalled, so I could just solder each wire directly to the pads. I opted to do one connector on top and one on the bottom, since it seemed to be a better use of space.
I wanted to use only the spliced cable I had cut and the Arduino Nano, but of course this would be a bit weak given the size of the wires inside the main cable. So, once everything was soldered and I tested it again, I used some cable ties to really lock things down to the Nano’s PCB.

There is plenty more I could have done here. I could have gotten some female DIN-5 and DIN-6 plugs to solder to a protoboard, or I could have ordered a custom PCB. Many fancier versions of this adapter consist of that. But this one is quick and dirty and it works well.
And now, I was ready to tackle that disk drive and try and get this thing to boot DOS.
i noticed that the unit does work quite well. But I see it seems to only buffer the first 3 characters. At least on my machine. If i am in a program and issue a command and start to issue another command before it’s completely finished only the first 3 characters are buffered and come in.
Also, it does not appear you coded for a number pad?