A TAP file stores the blocks a ZX Spectrum writes to tape, without the tones and timings: just the bytes. Several TAP files joined end to end make a valid TAP file. This page follows the Sinclair Wiki’s description of the format, which matches the Spectrum ROM’s own tape routines. Numbers are little-endian.
Blocks
| Offset | Size | Name | Description |
|---|---|---|---|
0 | 2 | Length | Bytes that follow in this block: flag, data and checksum. Not counting these two bytes. |
2 | 1 | Flag | 00 for a header block, FF for a data block. Programs with their own loaders may use other values. |
3 | length − 2 | Data | The block’s contents. |
| end | 1 | Checksum | The XOR of the flag and every data byte. XORing the whole block, checksum included, gives 0. |
A file saved from BASIC is a header block followed by a data block. A loader can also read “headerless” data blocks with no header before them; TAP stores those the same way.
Header block
A header block has the flag 00, 17 bytes of header and the checksum, so
its length is 19.
| Offset in header | Size | Name | Description |
|---|---|---|---|
0 | 1 | Type | See types. |
1 | 10 | File name | Padded with spaces. |
11 | 2 | Data length | Length of the data block’s contents (without its flag and checksum). |
13 | 2 | Parameter 1 | Depends on the type. |
15 | 2 | Parameter 2 | Depends on the type. |
| Type | File | Parameter 1 | Parameter 2 |
|---|---|---|---|
| 0 | Program (BASIC) | The line to start at (SAVE … LINE); 32,768 or more for no auto-start. | Where the variables start, counted from the start of the program. |
| 1 | Number array | The second byte (header offset 14) holds the array’s variable name. | – |
| 2 | Character array | The second byte (header offset 14) holds the array’s variable name. | – |
| 3 | Bytes (code) | The address the bytes were saved from. | 32,768. |
A screen saved with SAVE … SCREEN$ is a bytes file of 6,912 bytes from
address 16,384.
Flag
The first byte of a block says what kind it is to the ROM’s loader: 00 for a
header, FF for data. The ROM’s LOAD reads a header, checks that its
type and name match what was asked for, then reads a block with flag FF. A
program with its own loader can use any flag value, and TAP stores such a block like the
others. The viewer calls a block a header only when its flag is 00 and its length is 19.
Checksum
The last byte of a block is the XOR of every byte before it in the block, starting with the flag. The two length bytes are not included. XORing the whole block from the flag to the checksum gives 0. The ROM reports a loading error when it does not; a TAP file can still hold such a block, as a damaged copy of a tape or one a program writes on purpose.
Contents of a data block
What follows the flag of a data block depends on the header before it. The header’s data length is the length of the contents, which is the block length minus 2.
- Code (type 3) and screens: the bytes as they sit in memory, loaded to the address in parameter 1. They are Z80 machine code or data; the explorer shows them as bytes.
- Program (type 0): the BASIC program area, immediately followed by the program’s variables. Parameter 2 of the header is the length of the program area, so the variables are the last data length − parameter 2 bytes.
- Arrays (types 1 and 2): the array’s elements as the ROM keeps them. This page does not describe their layout (see Not described on the overview).
BASIC program area
A series of lines with no end marker. Each line is:
| Size | Name | Description |
|---|---|---|
| 2 | Line number | High byte first: the only big-endian number in the format. 1 to 9999 from the keyboard; line 0 can be made by a program. |
| 2 | Length | Low byte first: the number of bytes after these four, including the final 0D. |
| length − 1 | Text | Characters, and a one-byte token for each keyword (A5 to FF, see below). |
| 1 | End of line | 0D. |
Every number written in the text of a line is followed by the byte 0E and the number in the
Spectrum’s 5-byte form, which the ROM reads instead of converting the digits again. The explorer skips those
six bytes when it lists a program. A small whole number such as 5 is stored as 00 00 05 00 00
(the 5-byte form of 5 as a whole number with a positive sign). Control characters 10 to 15 (INK, PAPER, FLASH, BRIGHT, INVERSE, OVER) are
followed by one byte, and 16 (AT) and 17 (TAB) by two.
The character 7F is ©, 5E is ↑ and 60 is £.
Some programs make the length field of a line wrong on purpose (a protection trick; the real tape
of “The Swamp”, made with the Quill, has a line 0 one byte too long), so that LIST fails. The explorer
reports a line that does not end with 0D.
Tokens
| Byte | Keyword | Byte | Keyword | Byte | Keyword | Byte | Keyword |
|---|---|---|---|---|---|---|---|
A5 | RND | C0 | USR | DB | FLASH | F6 | PLOT |
A6 | INKEY$ | C1 | STR$ | DC | BRIGHT | F7 | RUN |
A7 | PI | C2 | CHR$ | DD | INVERSE | F8 | SAVE |
A8 | FN | C3 | NOT | DE | OVER | F9 | RANDOMIZE |
A9 | POINT | C4 | BIN | DF | OUT | FA | IF |
AA | SCREEN$ | C5 | OR | E0 | LPRINT | FB | CLS |
AB | ATTR | C6 | AND | E1 | LLIST | FC | DRAW |
AC | AT | C7 | <= | E2 | STOP | FD | CLEAR |
AD | TAB | C8 | >= | E3 | READ | FE | RETURN |
AE | VAL$ | C9 | <> | E4 | DATA | FF | COPY |
AF | CODE | CA | LINE | E5 | RESTORE | ||
B0 | VAL | CB | THEN | E6 | NEW | ||
B1 | LEN | CC | TO | E7 | BORDER | ||
B2 | SIN | CD | STEP | E8 | CONTINUE | ||
B3 | COS | CE | DEF FN | E9 | DIM | ||
B4 | TAN | CF | CAT | EA | REM | ||
B5 | ASN | D0 | FORMAT | EB | FOR | ||
B6 | ACS | D1 | MOVE | EC | GO TO | ||
B7 | ATN | D2 | ERASE | ED | GO SUB | ||
B8 | LN | D3 | OPEN # | EE | INPUT | ||
B9 | EXP | D4 | CLOSE # | EF | LOAD | ||
BA | INT | D5 | MERGE | F0 | LIST | ||
BB | SQR | D6 | VERIFY | F1 | LET | ||
BC | SGN | D7 | BEEP | F2 | PAUSE | ||
BD | ABS | D8 | CIRCLE | F3 | NEXT | ||
BE | PEEK | D9 | INK | F4 | POKE | ||
BF | IN | DA | PAPER | F5 |
The 128K Spectrum’s tokens are the same; CAT, FORMAT, MOVE,
ERASE, OPEN # and CLOSE # are the Interface 1’s statements, which the
48K ROM only recognises as tokens. Bytes 80 to 8F are block graphics and 90
to A4 are the user-defined graphics A to U.
Example
00000000 13 00 00 03 53 43 52 45 45 4E 20 20 20 20 00 1B ....SCREEN ..
00000010 00 40 00 80 D4 .@...
Reading it: length 0x13, 19; flag 00, a header; type 3,
bytes; name SCREEN; data length 0x1B00, 6,912; parameter 1
0x4000, 16,384; parameter 2 0x8000, 32,768; checksum
D4. The data block that follows has length 6,914 (02 1B), flag
FF, the 6,912 bytes of the screen and its checksum.