KeybJr

As a follow-up to my previous article about the IBM PCjr and its quirks and features, I have decided to create KeybJr – a small open-source project, that allows the PCjr to use a regular keyboard of the era, through both cable or the infrared link. This is because the system did not contain any provisions of connecting a normal PC keyboard, except for certain rare and expensive third party solutions; and the Junior as a geek item nowadays, is just an oversized paperweight that will not function without its proprietary keyboard. Be it the original “chiclet” Freeboard, or its later variant with a more conventional layout, they both leave a lot to be desired when it comes to their squeaky, mushy rubber dome action. Or, if you happen to find one, and it just happens to have batteries leaked all over inside, why not just connect a Model M to the PCjr? 🙂

Utilizing the infrared interface and a switchable 101-key keyboard in XT emulation mode

In the current revision, KeybJr allows you to connect an XT, AT, PS/2, or older USB keyboards, to the input. The keys are then translated according to what PCjr expects, and then sent to the PCjr via the infrared interface and also through wired link. If connected, this then utilizes the “K” connector in the back, omitting the need for the board to be in visible distance of Junior’s infrared receiver. However, utilizing the infrared interface, KeybJr can make use of a proper keyboard together with a PCjr that has broken keyboard connector pins – i.e. where the proprietary and expensive keyboard converters fail. And, some can tell how the bus pins of the Junior are easy to bend or break off.
To accomplish KeybJr into a small weekend project, one of the popular (and cheap) experimentation platforms has been chosen – the Arduino Uno and clones thereof, utilizing the ATmega328 microcontroller.

It is an Arduino internally, but it does not require the original board and functions just fine as a simple dead-bug circuit

The project can be downloaded as source codes compatible with the Arduino IDE, or as a binary containing the required bootloader, code and lock bits settings, which can be then flashed either through a dedicated programmer or an ICSP circuit of the Mega328P.
The structure of the source code is quite simple to understand. Depending on the keyboard protocol used, either a simple interrupt service handler is invoked for an XT keyboard, and a fork of the PS2KeyAdvanced library is used for an AT, PS/2 or USB keyboard (coupled with an adapter). The keyboard is then polled for key action, and the code then performs the required scancode translation, according to the PCjr Technical Reference. It then strobes the IR diodes and asserts the cable data output as required. After that’s done, the whole cycle repeats.
However, since the code uses AVR direct port access for faster bit-banging during the transmission than Arduino’s “digital writes” (since the keyboard is inhibited during TX, it shall be as fast as possible), any circuit deviation from input/output ports used in the schematic, must be addressed in the code beforehand.

The circuit is just barebones ATmega328p with a 16 MHz external clock.
It can be powered from the PCjr directly, or through an USB port, a five volt stabilizer etc.

Beware that some very rare and old XT keyboards, e.g. the very first revisions of the IBM Model F’s (used in the 5150 or XT), might require to be “hard-reset” after applying power, using their DIN pin no. 3 (/RESET) line. This is not in the schematic, but if that’s the case – connect this line to ATmega PD7 (pin 13 on DIP), and KeybJr will do the reset for you upon MCU powerup/reboot.
If using an AT, PS/2 or an older USB keyboard through an adapter, KeybJr uses a fork of the PS2KeyAdvanced library, which supports extended codes. The PS/2 protocol is bi-directional by default, however – to keep things both fast and simple, I operate the keyboard in synchronous mode. This means that the keyboard interrupt and clock are inhibited during data transmission to the Junior, to prevent timing issues. After that, the processed keystroke is erased from the internal keyboard buffer and the cycle repeats. And, the key obtained from a PS/2 keyboard is converted into an XT-compatible scancode through a translation table. This is because the PCjr expects an XT scancode, albeit with different signaling and also of different format for certain keystrokes.

While prototyping on an Arduino board

The way of how PCjr expects its keyboard input, is explained in the Technical Reference well enough. Both the infrared receiver and the wired keyboard input use almost the same communication, with a couple of differences.
Each data bit is encoded into a 440-microsecond bi-phase data pulse. A logical “one” is asserted the first half (the first 220us) of the pulse and deasserted the second half. A logical “zero” waits 220us, asserts the second 220us of the pulse and deasserts right after.
With the cable keyboard data, the input is inverted, i.e. 5 volts is logical zero. And using the infrared diodes, the bi-phase duration is intact, i.e. 220+220us for both phases, however, instead of asserting the digital output for the whole half phase duration, the IR expects a 62.5us strobe of a 40kHz, 50% duty cycle square wave for each assertion, and a subsequent delay of 157.5 us to make up for the 220us half phase.

The 62.5us infrared strobe that occurs inside each databit transmission

Using this signalling protocol, a transmission of one whole keypress or keyrelease (each one scancode byte) to the PCjr, consists of 1 start bit, 8 data bits of the scancode with least-significant-byte first, an odd parity bit and eleven empty stop bits. These are there just to give time for the NMI of the PCjr to process this in its interrupt handler (INT 48). Each valid keypress is a byte of a value between 1 to 0x3A, plus 0x54 denotes the Fn key and 0x55 a phantom key, i.e. an instruction for the PCjr to discard the keystroke buffer. The same applies for key releases (break codes), they just have their most significant bit set – i.e. OR’ed with 0x80.
Now, direct XT scancodes cannot be sent to the PCjr without further processing, as only the alpha-numeric part would be processed right. This is because the PCjr uses an Fn-key to process keystrokes such as function keys, navigation keys, lock keys, Pause/Break, Printscreen and so on. Even certain scancodes that are consistent on both XT and AT need to be transmitted in a non-standard way to the Junior, such as a backslash (Alt+forward slash), tilde (Alt+right bracket), a dot is a Shift+Delete, etc.

Using the board plugged into the “K” keyboard cord connector, instead of using infrared

Of course there’s a couple of caveats, almost all of them are related to the fact the communication between the keyboard and PCjr is one-way. That includes KeybJr or anything else, infrared or wired in to the Junior.
The main thing is that due to PCjr’s design, it will lose keystrokes if the system is busy, e.g. during disk or serial access, even with the original keyboard wired in. As such, KeybJr tries to convert all possible keystrokes for the PCjr, so naturally, do not expect the SysRq, Windows, Context menu or any of special Web or multimedia keys to work. Right and left Controls and Alts are not distinguished by design; F11 and F12 will not be registered at all.
If the system is reset, there’s no feedback to the keyboard, and it won’t know about it. As such, unless you are using an old XT keyboard, the CapsLock and ScrollLock states (LEDs) will go out of sync on reset (and also on system power down, if you power KeybJr from an external supply and not through PCjr). In addition, as an example, if you actuate Caps or Scroll Lock and reset or disconnect power from the KeybJr, but keep PCjr running, the LEDs will be off but the system will behave like if they were on.
There is one exception: the state of NumLock will be preserved. This is because Num Lock is handled by KeybJr internally, and not passed as a control code to the system. Why do I do that? Well, because PCjr does not support the NumLock key…
Also, make sure the keyboard is connected nice and snug before applying power to the chip. KeybJr does not support keyboard hot-plugging, and the appropriate AT/XT protocol (i.e. PD4 left floating, or shorted to GND with a jumper etc) needs to be chosen before applying power. Or, at the very least, after performing a Reset of the MCU with the pushbutton.
All in all, this circuit has been successfully tested with an old Microsoft USB keyboard, a Chicony PS/2, a LogoStar (Copam) K-450 switchable XT/AT and an original IBM Model F, XT keyboard. In case you’re having difficulties using KeybJr with a USB keyboard, try an older one that is more likely to support a PS/2 protocol. This can be tested out through a USB-to-PS/2 adapter, in a computer which has a PS/2 keyboard port.

And that’s all, folks! 🙂

3 Replies to “KeybJr”

  1. I wonder how much re-orientation it would take to reverse this – I have some PCJr IR Keyboards, and it would be cool to use them to a normal USB keyboard. Would make people ask questions.

    1. When it comes to scancodes (keyboard data), the alphanumeric part of it is almost consistent with an XT keyboard. On an electrical level however, and that applies also over the keyboard cord, the PCjr keyboard uses a different way of signaling keystrokes. Plus, apart from the letter keys, different key combos on the PCjr are used to cover the rest of an XT keyboard. For more details, have a look into the PCjr Technical Reference, or, if you feel comfortable in C, into the source codes of the project – KeybPcJr.h for the key translation and IR.h for how the signaling is done (over both infrared and corded).

  2. I absolutely love this project! It’s a great solution that manages to be better than older period-correct solutions by not only allowing AT and PS/2 signals, but also wirelessly. The detailed write-up is also fantastic. Thanks!

Leave a Reply to Jim Leonard Cancel reply

Your email address will not be published. Required fields are marked *