Before designing the I/O section of the Harlequin ZX Spectrum clone, a clear idea is needed of what I/O interfaces are required, and how they are related.
The ZX Spectrum has four interfaces:
- Mic / Tape output
- Ear / Tape input
This ULA port is activated when the Z80 reads from any port whose address line A0 is low. As partial decoding is employed, in order to avoid a clash with future hardware the Spectrum ROM always reads from port 0xFE (ie A1-7 high), allowing the other address lines to be used in a low state to enable other hardware.
To enable true Spectrum compatibility, the same partial address decoding should be used, as we cannot safely assume that the thousands of Spectrum software titles produced all read from port 0xFE - they know that any port with A0 low will do.
When reading from port 0xFE, the lower 5 bits (D0 to D4 return information as to which key is being pressed, depending on what row is being addressed (see The Keyboard).
The EAR / Tape Input
When reading from port 0xFE, bit 6 reflects whether there is a high level at the EAR input or a low. The values with no tape input are different between different Spectrum issues. See the comp.sys.sinclair FAQ for further information. I will ignore this difference and float the value in a high or low state.
The Mic / Tape Output
When writing to port 0xFE, bit 3 is the MIC output bit. The state of this bit is shown at the MIC output as a high or low level.
Writing to port 0xFE, bit 4 controls the state of the ZX Spectrum internal speaker. Sound is produced by alternating between a high and low state at various frequencies.
The Border Colour
Until now no mention has been made of how the border colour is set. This is again controlled through port 0xFE, by writing the border RGB colour to bits B0 (Blue), B1 (Red) and B2 (Green).
As the ULA is required to supply the value read from port 0xFE on the same bus as it uses to read the video memory, there is naturally going to be some contention. This contention is identical to that seen by the Z80 when accessing the video memory. So in decoding the I/O port we also need to generate the same WAIT states.
The decoding of address line A0 for I/O is quite easy. The Z80 sends its control line IORQ low whenever an I/O port is read or written to, along with WR or RD and the relevent address lines. I need to detect that an IO port is being accessed and that A0 is low in order to generate the WAIT signal. RD and WR are used in conjunction with A0 and IORQ to detect that we're reading from the keyboard or EAR, or writing to the speaker or MIC.
Next, a detailed look at the keyboard and the Harlequin design....