The ZX Spectrum keyboard consists of 40 keys arranged as 4 rows of 10. Internally these are treated as 8 half-rows of 5 keys. The keyboard is read by reading from port 0xFE - however the upper eight address lines play a role in determining which half row is to be read.
Each row is directly connected to one of the upper eight address lines A8 to A15, and each key of a row is effectively connected to the lower five data lines via the ULA. The ULA enables this connection when the Z80 reads from any port address with A0 low. This partial address decoding is described in the ZX Spectrum Interfaces.
The key to data line mapping for a whole keyboard row looks like the following:
And the upper address lines map to keyboard half rows as follows:
|CapsShift - V||A8||FE||1 1 1 1 1 1 1 0||A - G||A9||FD||1 1 1 1 1 1 0 1||Q - T||A10||FB||1 1 1 1 1 0 1 1||1 - 5||A11||F7||1 1 1 1 0 1 1 1||6 - 0||A12||EF||1 1 1 0 1 1 1 1||Y - P||A13||DF||1 1 0 1 1 1 1 1||H - Enter||A14||BF||1 0 1 1 1 1 1 1||B - Space||A15||7F||0 1 1 1 1 1 1 1|
When reading the keyboard, if a key is pressed within a half-row whose address line is low then that data line is pulled low, which is what we read on the bus.
A simple 8bit buffer with a OE (output enable) can be used to interface the keyboard to the data bus. The enable signal being derived from:
bufferEnable = IORQ + A0 + RD
As the ZX Spectrum returns a high value for keys that have not been pressed, we will require pull-up resistors at the keyboard interface. These pull-up resistors must be large enough that when multiple keys on the same half-row are pressed together, the voltage drop across the combined resistors (pressing multiple keys puts those resistors in parallel) is still sufficiently large that we don't get dangerously close to a short-circuit condition between Vcc and the current sinking address line and cause a crash (or do other damage).
As we're connecting the keyboard directly to the address bus, the address lines should be protected via diodes so that they are only able to sink current. The Z80 address lines are output only, and no voltage should ever be presented to it.
This keyboard interface is identical in principle to that of the ZX Spectrum, except the job of the ULA is handled by the 8bit buffer. The Z80 will still need to wait whilst the video system is updating the display if we are to emulate the ZX Spectrum behaviour correctly.
I had a DK'Tronics keyboard that I used to use with my Spectrum before I returned it to its original rubber-keyed case, which would make an excellent keyboard for the Harlequin. I extended the ribbon cable that plugs into the Spectrum PCB so that it reached well outside its case, put an IDC socket on the end and wired up a suitable IDC plug with flying-leads on the Harlequin board.
The ZX Spectrum ROM reads the keyboard via an interrupt routine, and so the ZX Spectrum interrupt generation must be tackled next.... Schematics for both will follow.