The USB communication is achieved using the VUSB library from Objective Development that is wrapped in the VUSB for Arduino library. The USB hardware is based on a design from the book Practical Arduino and as all the hard work is being done in code is frighteningly simple.
The keyboard is a simple matrix with 5 columns and 8 half rows. Each column is pulled to ground one at a time while the others are left floating. We can then poll the rows (done in two block operations). As we are using the internal pull up resistors inside the Arduino we do not need any external components to do this. It is worth noting that we are using the analogue pins as digital pins but that is fine and how the micro-controller is designed to work. We also need to remove the LED from PIN 13 as this will interfere with the pull-up function.
The keyboard operates in two different modes. When you are typing normally the key presses are simply mapped on to the relevant HID code. When Caps Shift is pressed a shift “modifier” is sent along with the letters just as would happen on a normal USB keyboard.
Where it gets a little more complex is when we are pressing Caps Shift and a number are pressed as these are used for navigation, caps lock, and delete. When this happens we don’t send any modifier and map the key to the navigation, caps lock, and delete respectively. If a letter and a number is pressed we then do send the shift modifier that may give an unexpected outcome on a PC, but that will always be the case without writing a special keyboard driver for the keyboard, and doing so will maximise compatibility with most spectrum emulators.
When Symbol Shift is pressed we enter another mode. This is because we need to pretend some symbols are typed with shift pressed, and some without. As a compromise we send a single press and release of each symbol that is typed and do not return to the normal mode unless the Symbol Shift key is released. We also send an unmodified alphanumeric key if there is not sensible symbol to map to for emulator compatibility.
The keyboard de-bounce is achieved by a simple delay of around 100ms that is an age in modern times but needed for the old membrane keyboards that physically bounce. We have had to disable timer0 as it causes the USB to behave unreliably and this breaks the delay() function, so we call delayMicroseconds() several times to do the same thing.