The Cassette I/O Challenge
The ZX Spectrum does not have a dedicated tape controller chip. All tape reading and writing is achieved through software routines that precisely control the timing of a single I/O line, which is one of the most demanding tasks for the Z80 CPU.
The Principle: Data is encoded using Pulse Width Modulation (PWM), where the duration of the tone (the pulse width) determines whether the CPU sends a binary ‘0’ or a binary ‘1’.
Output (Writing Data to Tape)
Writing data requires running a tight timing loop and sending the signal through Bit 4 of the `FEH′ I/O port (Part 72).
Writing a ‘0’ vs. a ‘1’:
Bit Value | Pulse Width | Timing Loop |
---|---|---|
Binary 0 | Shorter ON/OFF pulse duration. | Requires a shorter T-state delay loop. |
Binary 1 | Longer ON/OFF pulse duration. | Requires a longer T-state delay loop. |
The Header Block: Every data block starts with a slow Pilot Tone (a very long series of ‘1’ bits) and a Sync Pulse to give the tape recorder time to stabilize and synchronize the timing before the data begins.
Input (Reading Data from Tape)
Reading is much harder than writing because the CPU must constantly poll the EAR socket line for changes in the signal voltage.
The Input Port: The Spectrum reads the voltage level of the EAR socket through Bit 6 of the `FEH′ I/O port.
The Decoding Routine:
IO_PORT EQU 0FEH
EAR_BIT EQU 40H ; 01000000B (Bit 6)
READ_BIT_LOOP:
; 1. Poll until the EAR bit (Bit 6) changes state (edge detection).
CALL POLL_FOR_EDGE
; 2. Start a timer, then wait for the next edge.
; The duration of the pulse (the time between the two edges) determines if it was a '0' or '1'.
; 3. If the pulse duration is SHORT, decode as '0'.
; 4. If the pulse duration is LONG, decode as '1'.
JP DECODE_NEXT_BIT
Utilizing the ROM Routines
Since these timing routines are so incredibly precise and complex (requiring exact T-state counts), most assembly programmers do not write their own. Instead, they rely on the robust routines already present in the Spectrum’s System ROM.
Key ROM Entry Points:
Address | Function | Purpose |
---|---|---|
`0556H′ | LOAD | The main routine for loading a program from tape. |
`04D0H′ | SAVE | The main routine for saving a block of memory to tape. |
Usage: The assembly program loads the necessary parameters (start address′,
length′, filename′) into specific registers and then executes
CALL 0556H′ to hand off control to the proven ROM code.