ZX Spectrum and other computers of that period use proprietary keyboards with direct connection to some specific chip or connected directly to computer bus. Old keyboards are damaged from elementals or time, and sometimes are not very user friendly compared to standard ones. Goal of this project is to connect old and obsolete (!) PS/2 style keyboard to ANY old computer. This project is tested on ZX Spectrum CPLD clone, but mimics the original ZX 40 keys keyboard. Why not a USB keyboard? Because it limits to the narrower selection of MCU’s and USB HID is way more complex to implement.
This project consists of one easy part and one very rare part- CPLD. I think that maybe it is possible to directly connect without CPLD (or glue logic) with modern MCU, but my CPLD ZX spectrum uses “turbo mode” with 5MHz CPU clock and speed of keyboard scan is too fast for MCU pins.
How does it work? It is crazy multi conversion:
- Key press is connecting the matrix on the keyboard. Keyboard detects this and converts keypress to scan data. This data is transmitted serially to the PS/2 interface.
- MCU collects PS/2 data, decodes from scan code to ASCII.
- ASCII key codes with modificators are combined for extra key combinations and stored in the key buffer.
- The key buffer can hold up to 10 keystrokes (+modifiers). So multistroke is possible (especially for games). Multistroke is limited by keyboard hardware, not this project.
- All data is converted to a 40 bit key code matrix (8 columns, 5 rows) for the ZX computer.
- All 40 bits are shifted serially to CPLD at fast speed (2+ Mbit/s, depends on CPLD and connections) DMA SPI.
- CPLD decodes the bit stream and emulates keyboard hardware.
Shiftreg is simple 40 bits shifting register to store SPI data, following with 40 bits latch register. This looks like five 74HC595 connected in serial. Last module is combinatory logic. It is written in Verilog:
module keyboard( keys, col, row);
assign row=(col & keys[7:0]) !=0;
assign row=(col & keys[15:8]) !=0;
assign row=(col & keys[23:16]) !=0;
assign row=(col & keys[31:24]) !=0;
assign row=(col & keys[39:32]) !=0;
(my design uses inverted row/col. Depending on real hardware these buses may be inverted)
I am using a cheap STM32F103 MCU from China, Bluepill format (it can be a fake one, with smaller RAM/ROM size). All software is written using GCC and STM32CubeMX. All source code and compiled binaries are on the bottom of the page. PS/2 write is not implemented, now it just resets the keyboard on Esc key press. I have tested 3 keyboards and one has some strange bug- sometimes it starts translating some strange data if keys are pressed very fast or very long. Pressing Esc stops this chaos and inits keyboard interface.
Standard PS/2 keyboard has more keys than the ZX Spectrum, so some keys are redefined for more convenient usage: Like “;” and “:” pair- pressing dedicated key for semicolon and shift generates specific ZX spectrum keystroke. Same for “/?”, “=+”, “Backspace” and some other keys. Also, the numeric keypad is working too. “Caps lock” is working too, but LEDs are not used (because I do not implement PS/2 write properly and also, that it may not indicate real status of the computer). “Shitf”, “Alt” and “Control” are in use, but the logic is strange. And it is not my fault- it is Sinclair.
All source code and compiled binaries:
PS2 for ZX Spectrum source code, plain GCC for STM32F103.
Example Quartus project: Quartus archive. Will fit to EPM7128 or bigger chip.
Do not forget 5V/3V logic conversion. STM is not tolerating 5V logic very well.