The 48K ZX Spectrum handles all cassette IO through a single ULA pin, as well as the internal speaker. The significant bits of port 0xFE are (see interfaces for further information).
The Speaker, Ear socket and Mic socket are all connected to the same ULA pin. Setting bit 4 of port 0xFE high produces a larger voltage at the pin than setting bit 3. For this reason bit 4 is used for sound generation as it will produce the louder sound, and hence it has the 'speaker' label, but you could use bit 3.
On the input side, it is bit 6 of port 0xFE that returns the state of the ULA pin.
For data input, I have to arrange for the state of bit 6 to reliably change between high and low when presented with an analogue recording of a square wave (though the wave you actually see being played back from a cassette tape is far from square!).
The ZX Spectrum does this by decoupling the signal with some capacitors and resistors, and passes the signal straight into the ULA (see issue 6 schematic for details).
I could probably do exactly the same thing, but would like to continue the theme of buffering external devices through transistors. The cassette signal, being analogue, will vary + and - about GND. It is simple to set up an emitter follower, forward biased so that base voltage varies about this bias in response to the input signal. The chosen bias will directly affect the voltage about which the output will vary.
The values chosen for R1 and R2 should be large enough that they do not put much of a load on the input DC blocking capacitor. If they did, the high and low levels of a square wave input would not appear flat at the base of the transistor, but would taper in towards the bias voltage.
Not much current will be drawn from this buffer, so R3 can be set to a largish value (50-100K say).
As this is a common emitter configuration, no gain is provided by the transistor. Its function is to buffer the input signal from the input gate. The transistor requires 0.6v to switch on, and for a given base voltage, the voltage seen at the emitter will be 0.6v lower.
I started with the voltage divider R1 and R2 both at 47K, setting the bias voltage at 2.5v. This results in a VE emitter output of 1.8v with no input signal.
74HCxxx gates go high for an input of 3.7v and above and low for 1.3v and below. In order to drive a gate driven from the transistor output, the output must be above 3.7v or below 1.3, anything in between these values will put the gate into an indeterminate state.
The emitter follower output voltage can be calculated from VE = VBias + Vin - VBE
or rearranged as Vin = VE + VBE - VBias
Therefore to generate an output voltage of at least 3.7v for a bias of 2.5v, an input level of +1.8v is required (1.8 = 3.7 + 0.6 - 2.5), and an output of 1.3 or less requires an input of -0.6 or less (-0.6 = 1.3 + 0.6 - 2.5).
To make this a bit more symmetric the transistor bias can be increased to raise its default output towards 3.7v, but not too close as this will make the gate switching unstable and sensitive to noise. Enough headroom must be left for a clear positive input transition to take the transistor output above 3.7v.
Using a value of 27K for R2 gives an input bias of 3.2v. This leads to the following Vin values:
Vin = 3.7 + 0.6 + 3.2
Vin = 1.1 to switch the gate on.
Vin = 1.3 + 0.6 + 3.2
Vin = -1.3 to switch the gate off.
This is far more symmetric, and looks to be pretty much an ideal bias level, so I'll stick with these values.
This oscilloscope picture shows Vin and VE. The input signal is a real ZX Spectrum header tone played back from my cassette recorder. Note how the input voltage varies between -2.30v and +2.60v.
This shows the same Vin and VE and gives the measurements of VE as being between 0.72 and 4.80. A perfect voltage range for switching a 74HCxx gate.
This picture shows the same VE driving a 74HCxx gate, and the gate output - A nice clean logic transition.
One further note to make is that cassette players etc expect to be driving some kind of load, which a capacitor on it's own does not really provide. Therefore a 1K resistor should be played between the input and GND before the DC blocking capacitor to give the cassette player something to drive, and we measure the voltage across that.
For the MIC cassette output, a similar configuration to that of the RGB drivers can be used except that a capacitor is required in series with the output to block the DC component.
The cassette input schematics are shown in schematics version 1.9.
All that remains is to build the interface and to try it out, loading some real ZX Spectrum games! ......