Spectrum tape interface
- This article is about the ZX Spectrum ROM load/save format. For the .TAP emulator format see TAP format
The Spectrum ROM routines save files to tape in two blocks, a header and a data block. Each of these blocks is encoded as a sequence of pulses.
There are also a wide variety of "custom loaders" which have been used for Spectrum software, for purposes such as copy protection or faster loading times. This article deals only with the format written by the Spectrum ROM.
Pulses
A 'pulse' here is either a mark or a space, so 2 pulses makes a complete square wave cycle.
Pilot tone: before each block is a sequence of 8063 (header) or 3223 (data) pulses, each of length 2168 T-states.
Sync pulses: the pilot tone is followed by two sync pulses of 667 and 735 T-states resp.
A '0' bit is encoded as 2 pulses of 855 T-states each.
A '1' bit is encoded as 2 pulses of 1710 T-states each (ie. twice the length of a '0')
The initial polarity of the signal does not matter - everything in the ROM loader is edge-triggered rather than level-triggered.
Speed
Due to the fact that the different bits take different time on tape, the Spectrum's tape routine does not have a well-defined speed. We can calculate some limits and typical values though; as noted above, a '0' bit takes 2 × 855 = 1710 T-states and a '1' bit takes 2 × 1710 = 3420 T-states. Assuming a 48K Spectrum which runs at 3.50 MHz (the 128K machines run at 3.54 MHz or about 1% faster; that difference is largely irrelevant here), this means:
- A stream of pure '0' bits loads at 3500000 ÷ 1710 ≈ 2046 baud
- A stream of pure '1' bits loads at 3500000 ÷ 3420 ≈ 1023 baud
- A mixed stream with equal numbers of '0' and '1' bits loads at 2 × 3500000 ÷ (3420 + 1710) ≈ 1364 baud
Real world Spectrum data tended to have more '0' bits than '1' bits, so the typical baud rate was actually a bit higher than 1364 baud.
Blocks
On the tape every block start with a marker byte (0x00 for header and 0xff for data blocks) and ends with a checksum byte.
The checksum byte is not really a sum, but a binary XOR of all header/data bytes.
Header block
The header, which is 17 bytes long, is as follows:
Byte (decimal) | Length | Description |
---|---|---|
0 | 1 | Type (0,1,2 or 3) |
1 | 10 | Filename (padded with blanks) |
11 | 2 | Length of data block |
13 | 2 | Parameter 1 |
15 | 2 | Parameter 2 |
The type is 0,1,2 or 3 for a Program, Number array, Character array or Code file. A SCREEN$ file is regarded as a Code file with start address 16384 and length 6912 decimal. If the file is a Program file, parameter 1 holds the autostart line number (or a number >=32768 if no LINE parameter was given) and parameter 2 holds the start of the variable area relative to the start of the program. If it's a Code file, parameter 1 holds the start of the code block when saved, and parameter 2 holds 32768. For data files finally, the byte at position 14 decimal holds the variable name:
Bits | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Description |
---|---|---|---|---|---|---|---|---|---|
Numeric array | 1 | 0 | 0 | x | x | x | x | x | xxxxx = CODE(Name) - 0x60; e.g. DIM a() ⇒ 0x81, DIM y() ⇒ 0x99 |
String array | 1 | 1 | 0 | x | x | x | x | x | xxxxx = CODE(Name) - 0x60; e.g. DIM a$() ⇒ 0xc1, DIM y$() ⇒ 0xd9 |
Data block
Arrays
Number and string arrays are stored on tape in the same format as in RAM [1] except that the very first byte (the name of the array) is absent. The name of the array stored in the second byte of Parameter 1 in header block.
For example data block of a 2×3 numeric array (with small integer numbers):
Byte (decimal) | Length | Example value | Description |
---|---|---|---|
0 | 1 | 0x02 | Number of dimensions - DIM(2,3) |
1 | 2 | 0x02 0x00 | Dimension 1 - 2 |
3 | 2 | 0x03 0x00 | Dimension 2 (0x03 0x00 - 3) |
5 | 5 | 0x00 0x00 0x6f 0x00 0x00 | First number - 111 |
10 | 5 | 0x00 0x00 0x79 0x00 0x00 | Second number - 121 |
... | 5 | .. .. .. .. .. | ... number |
30 | 5 | 0x00 0x00 0xd3 0x00 0x00 | Last number - 211 |
Note: although the array name is saved, you cannot just LOAD "" DATA, you have to explicitly say where to load: e.g. LOAD "" DATA w(), but you can use any letter regardless of the original array name.
BASIC programs
Program files are stored as a sequence of lines each stored the same way it is in RAM:
Line number[2BE] Length[2] Text[] 0x0D[1]
Note that numeric literals will be stored in both text and binary (text[] 0x0E[1] zxfloat[5]).
CODE, SCREEN$ - bytes
Code files are stored as a flat sequence of bytes, in the order they appear in RAM.