Tape Files ========== .BLK Tape format used by Sinclair; identical to Z80's .TAP files. .SPC Tape format used by the Polish emulator. .TAP Tape format used by Z80 and many others. .TAP Tape format used by Warajevo. .STP Tape format used by Speculator. .TZX New tape format used to store turbo-loaders, etc. .VOC Straight sound sample of a tape; used by several emulators. .ZXS Very flexible tape format, not actually used by any emulators - used to store real Spectrum tapes in a digital format. All come from the ZX Spectrum Software Museum. .TAP/.BLK (Z80, Sinclair & many others) [from Z80 documentation] ---------------------------------------------------------------- The .TAP files contain blocks of tape-saved data. All blocks start with two bytes specifying how many bytes will follow (not counting the two length bytes). Then raw tape data follows, including the flag and checksum bytes. The checksum is the bitwise XOR of all bytes including the flag byte. For example, when you execute the line SAVE "ROM" CODE 0,2 this will result: |------ Spectrum-generated data -------| |---------| 13 00 00 03 52 4f 4d 7x20 02 00 00 00 00 80 f1 04 00 ff f3 af a3 ^^^^^...... first block is 19 bytes (17 bytes+flag+checksum) ^^... flag byte (A reg, 00 for headers, ff for data blocks) ^^ first byte of header, indicating a code block file name ..^^^^^^^^^^^^^ header info ..............^^^^^^^^^^^^^^^^^ checksum of header .........................^^ length of second block ........................^^^^^ flag byte ............................................^^ first two bytes of rom .................................^^^^^ checksum (checkbittoggle would be a better name!).............^^ Note that it is possible to join .TAP files by simply stringing them together, for example COPY /B FILE1.TAP + FILE2.TAP ALL.TAP For completeness, I'll include the structure of a tape header. A header always consists of 17 bytes: Byte 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 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. [UPDATED] .TAP (Warajevo) [from Samir Ribic] -------------------------------------------- Warajevo's tape files (TAP) has the format as follows: At the beginning of the file there are four bytes with the pointer to the first block. Then follow four bytes with pointer to the last block. The next four bytes contain #FFFFFFFF. So, empty tape has a format: #04 #00 #00 #00 #00 #00 #00 #00 #FF #FF #FF #FF Sequence #00 #00 #00 #00 #FF #FF #FF #FF is, in fact, a EOF (end of file) marker. Every block contains following: * 4 bytes, a pointer to the previous block, which is 0 for first block; * 4 bytes, a pointer to the next block or to the EOF marker for last block; * 2 bytes, block size; * 1 byte, a flag byte; * the data bytes. If the block size is 65534, it is a block which contains tone record samples. The structure is: * 4 bytes, a pointer to the previous block, which is 0 for first block. * 4 bytes, pointer to the next block, or to the EOF marker for last block. * 2 bytes, value 65534. * 1 byte, a status byte; bits B0-B2 in this byte contain informations which tell how many bits in the last byte in the block are used (number of used bits is, in fact, number stored in B0-B2 increased by one), bits B3-B4 contain informations about sampling frequency (with meaning 00 - 15000 Hz, 01 - 22050 Hz, 10 - 30303 Hz, 11 - 44100 Hz), and bits B5-B7 are not used. * 2 bytes, decompressed (logical) block size. * 2 bytes, compressed (psychical) block size; if these 2 lengths are equal, the block is not compressed. * 2 bytes, signature length (internal, for compressed blocks). * the samples (binary), 8 samples are packed into one byte (starting from B7 to B0); whole package of such sample bytes may be either compressed or uncompressed (the last byte need not contain all 8 bits). If bytes 9, 10, 11 and 12 into a TAP file are not equal to #FF, this is a TAP file which is not in native Warajevo TAP format. In this case, Warajevo assumes Z80's .TAP format. If the block size is 65535, it is a compressed block. It looks like: * 4 bytes, a pointer to the previous block; * 4 bytes, pointer to next block; * 2 bytes, 65535; * 1 byte, a flag byte; * 2 bytes, decompressed size; * 2 bytes, compressed size; * 2 bytes, signature length (internal); * the data bytes. Signatures are important for the imploding algorithm used by Warajevo. This algorithm, when decompressing, copies bytes from the source file, or returns for a few bytes, and copies some bytes from a destination file. The explaination of compressed data bytes is rather complex. We used format similar to those in PKLITE, but unlike PKLITE where signature bytes are mixed with data bytes, authors divided them in two parts, for easier debugging. Remember elements of Imploding (LZ77) algorithm. It depends on copying of some byte sequences. For example: 3D 18 2E 42 3D 18 2E 15 42 3D 19 will be encoded as: 3D 18 2E 42 15 19 The archivers differs on ways of encoding of this special 'Return for...' code. In Warajevo compressed format, there are two parts: signatures and data. In our example coding of signatures will be (binary): 00001001 010100xx while data bytes will be 3D 18 2E 42 04 15 05 19 The signatures are finite automat that describe what to do with data bytes. If the bit is 0, this is simple data byte, if the bit is 1 this is code for returning. In our example, four zeros in signatures means that four bytes can be simply copied (3D, 18, 2E, 42) to the output buffer. The next bit is 1. This means: Return for xxxx bytes and copy yyyy. The value of yyyy (size of string to be copied) is in signatures if less than 10 or in signatures and data bytes if greater of equal 10. The size depends on next 2-4 signature bits: 010: size=2 00: size=3 100: size=4 101: size=5 011: size>=10 1100: size=6 1101: size=7 1110: size=8 1111: size=9 If size is greater or equal than 10, the next data byte contains actual size-10. That means: maximum string size is 265. The next data byte determine lower byte of distance of string to be copied (lower byte of xxxx). If size=2, higher bit is always zero (so for this size distance can be maximally 255). If size differs from 2 the next 1-6 signature bits determine higher byte: 1: higher byte=0 0000: higher byte=1 0001: higher byte=2 00100: higher byte=3 00101: higher byte=4 00110: higher byte=5 00111: higher byte=6 01nnnn: higher byte=7+nnnn Experiment with some ASCII text compressed. There is algorithm in Pascal for decompressing to understand the format: procedure decompress_b; label lb,b0,b1,b11,b01,b10,b110,b111,b010,b00,b100,b101,b011,b1100, b1101,b1110,b1111,v,v0,v1,v00,v01,v000,v001,v0000,v0001,v00100,v00101, v00110,v00111,v0010,v0011,izlaz; var b,put:byte; bytes,return_for,i,auxilary:word; finished:Boolean; begin OutputBufEnd:=0; CurrPosInputBuffer:=SignatureSize+1; CurrentSignaturePosition:=0; CurrentSignature:=InputBuffer^[CurrentSignaturePosition]; BitCounter:=0; if duzina_ul_dek=0 then finished:=true else finished:=false; while not finished do begin if nextbit=0 then begin TakeFromInputBuffer(b,finished); PutToOutputBuffer(b); end else begin {I know, it is goto, but more readable than nested if then else sequences} lb: if nextbit=0 then goto b0 else goto b1; b0: if nextbit=0 then goto b00 else goto b01; b1: if nextbit=0 then goto b10 else goto b11; b11: if nextbit=0 then goto b110 else goto b111; b01: if nextbit=0 then goto b010 else goto b011; b10: if nextbit=0 then goto b100 else goto b101; b110: if nextbit=0 then goto b1100 else goto b1101; b111: if nextbit=0 then goto b1110 else goto b1111; b010: bytes:=2; TakeFromInputBuffer(b,finished); return_for:=b; goto izlaz; b00: bytes:=3;goto v; b100: bytes:=4;goto v; b101: bytes:=5;goto v; b011: TakeFromInputBuffer(b,finished); bytes:=b+10;goto v; b1100: bytes:=6;goto v; b1101: bytes:=7;goto v; b1110: bytes:=8;goto v; b1111: bytes:=9;goto v; v: TakeFromInputBuffer(b,finished); return_for:=b; if nextbit=0 then goto v0 else goto v1; v0: if nextbit=0 then goto v00 else goto v01; v1:goto izlaz; v00: if nextbit=0 then goto v000 else goto v001; v01: Auxsilary:=7; if nextbit=1 then Auxsilary:=Auxsilary+8; if nextbit=1 then Auxsilary:=Auxsilary+4; if nextbit=1 then Auxsilary:=Auxsilary+2; if nextbit=1 then Auxsilary:=Auxsilary+1; return_for:=return_for+256*Auxsilary; goto izlaz; v000: if nextbit=0 then goto v0000 else goto v0001; v001: if nextbit=0 then goto v0010 else goto v0011; v0010: if nextbit=0 then goto v00100 else goto v00101; v0011: if nextbit=0 then goto v00110 else goto v00111; v0000: return_for:=return_for+1*256;goto izlaz; v0001: return_for:=return_for+2*256;goto izlaz; v00100: return_for:=return_for+3*256;goto izlaz; v00101: return_for:=return_for+4*256;goto izlaz; v00110: return_for:=return_for+5*256;goto izlaz; v00111: return_for:=return_for+6*256;goto izlaz; izlaz: for i:=1 to bytes do begin put:=OutputBuffer^[OutputBufEnd-return_for+1]; PutToOutputBuffer(put) end; end {else} end {while} end; {decompress_b} Complex? Yes it is. I spent more than 30 days in developing algorythm, analysing of some archivers, optimizing compression speed (it is still slow, but acceptable), and I worked mostly on paper, because it was in hardest days of summer 1993, without electric power, water and food (in this time I losed 1kg weekly), when only miracle saved Sarajevo of fall. In this time I had not leave the army building, and while I waited for a new battle tasks I developed the compression algorythm. .TZX (x128, xzx v2 & others) ---------------------------- .TZX files are a format developed by Tomaz Kac to allow the storage of games with non-standard loaders in a format much smaller than .VOC files. The full specification can be found at World of Spectrum. .STA (Speculator) [from J.G.Harston] ------------------------------------ Tape file, identical to the standard .TAP tape file, with two expections: A standard .TAP file is this format: {}... A Speculator tape file is this format: {}... So, for example, the command SAVE "ROM" CODE 0,2 would produce: |------ Spectrum-generated data -------| |---------| .TAP: 13 00 00 03 52 4f 4d 7x20 02 00 00 00 00 80 f1 04 00 ff f3 af a3 SpecTape: 11 00 00 03 52 4f 4d 7x20 02 00 00 00 00 80 02 00 ff f2 af ^^^^^...... first block is 19 bytes in a .TAP file (17 bytes+flag+checksum) but in a SpecTape file it is the actual data length (17 bytes). ^^... flag byte (A reg, 00 for headers, ff for data blocks) ^^ first byte of header, indicating a code block file name ..^^^^^^^^^^^^^ header info ..............^^^^^^^^^^^^^^^^^ checksum of header .........................^^ length of second block ........................^^^^^ flag byte ...........................................^^ first two bytes of ROM .................................^^^^^ checksum (checkbittoggle would be a better name!).............^^ A .TAP file can be converted to a SpecTape file by decrementing the length by two, and omitting the checksum byte at the end of each block. Speculator prior to v1.04 expects SpecTape files to be in a directory called Programs within !Spectrum, and cycles through all recognisable files in this directory. Speculator v1.04 and later can read standard .TAP tapefiles, and they can be in any directory pointed to by . Spectape (Speccy, Carsten Witt) ------------------------------- Tape file, identical to the standard .TAP tape file, expect: A standard .TAP file is this format: {}... A Speccy tape file is this format: {}... That is, it just contains the raw data bytes of each block. So, for example, the command SAVE "ROM" CODE 0,2 would produce: |------ Spectrum-generated data -------| |---------| .TAP: 13 00 00 03 52 4f 4d 7x20 02 00 00 00 00 80 f1 04 00 ff f3 af a3 Speccy Tape: 03 52 4f 4d 7x20 02 00 00 00 00 80 f2 af ^^^^^...... first block is 19 bytes in a .TAP file (17 bytes+flag+checksum) ^^... flag byte (A reg, 00 for headers, ff for data blocks) ^^ first byte of header, indicating a code block file name ..^^^^^^^^^^^^^ header info ..............^^^^^^^^^^^^^^^^^ checksum of header .........................^^ length of second block ........................^^^^^ flag byte ...........................................^^ first two bytes of ROM .................................^^^^^ checksum (checkbittoggle would be a better name!).............^^