Finally! The mythical keyboard post!! Sorry it’s taken so long, but SokoBAArn was taking it’s time (along with a large & overgrown garden back in the real world). Anyway, we’re here now, woohoo!!
Back in the depths of time I did a short piece on how to detect if a key is pressed, which used the following code:
WAITFORKEY: ld a, 0 in a, (254) cpl and %00011111 jp z, WAITFORKEY
That’s actually the basis for all keyboard reading, and as complicated as it gets, just that we were doing a particular and simple case – testing for any key being pressed.
The Spectrum’s keyboard is technically split into 8 sets of five keys – 2 for each of the 4 rows – and each with it’s own set ID, as below:
ID $F7 - 1, 2, 3, 4, 5 ID $EF - 0, 9, 8, 7, 6 ID $FB - Q, W, E, R, T ID $DF - P, O, I, U, Y ID $FD - A, S, D, F, G ID $BF - <ENTER>, L, K, J, H ID $FE - <SHIFT>, Z, X, C, V ID $7F - <SPACE>, <SYMBOL SHIFT>, M, N, B
The keyboard is always read using port 254 ($FE), using IN A, (254) or IN A, ($FE) as shown in the ‘any key’ code, and in order to specify which of the eight sets you want to read, you need to load that ID into A beforehand. For example, to check if any of the keys 1, 2, 3, 4, or 5 are pressed you can use:
ld a, $F7 in a, ($FE)
You could also use the following code to do the same thing – just a different way:
ld bc, $F7FE in a, (c)
This method leaves the set & port intact, so you can keep reading the keyboard, where with the first method the set ID held in A is lost by the read.
The result, returned in A, has a bit for each of the five possible keys, with bit 0 being the first key I mentioned in the set, reading through to bit 4, and the bit value itself will be unset (i.e. zero) if the key is held (and set / one if not). So if the ‘2′ key is held, the value returned in A would be 00011101 (in binary).
You can hopefully see now how easy it is to not only to read a single key, but also check if groups of keys in a single set are held. Early Ultimate Play The Game games would use whole rows for various actions – Jet Pac used any key on the top row for thrust for example, which can be done by just reading the two top-row sets $F7 & $EF and checking for any unset bits, rather than testing for all 10 keys individually – as you might if you were writing this in BASIC.
Actually, that Jet Pac example is even easier, and all ten keys can be done with a single read. Take a look at the set IDs in binary:
ID $F7 - 11110111 ID $EF - 11101111 ID $FB - 11111011 ID $DF - 11011111 ID $FD - 11111101 ID $BF - 10111111 ID $FE - 11111110 ID $7F - 01111111
Each of the ID values actually only differs by having a different single bit unset. Each unset bit denotes which ID is to be read, and you can unset as many as you like. Therefore for the Jet Pac example we can actually read the entire top row by using ID $E7 (11100111 in binary). With that knowledge you should now see that when we set A to zero in the ‘any key’ code we were actually asking the code to read from all 8 sets at once. You can’t actually tell which key was being held – the result will be the same for key ‘3’ as it is for ‘8’ – but if you don’t actually need to know then it’s perfect.
That’s really all there is to know about reading the keyboard, the rest is down to how you use it…
Having preset keys in a game is never as good as being able to redefine them. To do that you would need to have some code which waits for a key press, and be able to know which it was. To do that, you would loop through reading all eight sets – so the entire keyboard – until one of the results came back with an unset bit, and then you can remember which set it was, and which bit was unset, and use subsequent code to check for just that combination.
To convert a key press to it’s ASCII equivalent you could have a table of the ASCII version for each bit in each set, and once you had your chosen key (from the above code) you can look it up using that table to find which ASCII that corresponds to. Something to remember though is that the ASCII version could be an upper or lower case letter, or a symbol, depending on whether the <SHIFT> or <SYMBOL SHIFT> keys are held – so you would likely use a separate lookup table for each separate case if that was important. Most games would simply count the shift keys as valid controls, so you would only use the one table, but for a text adventure game for example it could be important.
To allow for a sequence of characters to be typed – such as in a text adventure – you would do the same as for a single key, but loop until a certain condition (<ENTER> is pressed perhaps, or maybe a given number of characters) is met. One thing you’ll need to do is wait between each key press – either for a short time, or until no keys are held – before the next is read. Remember that when typing a sequence of upper-case letters that the user will usually keep <SHIFT> held down between each letter – so remember to exclude that from your check when waiting until the next key can be read (I forgot to do exactly that in my last game until it was pointed out to me!)