Teledisk Image File Format Notes PRELIMINARY Dave Dunfield April 2, 2007 Last revised: July 28, 2008 Teledisk .TD0 notes TABLE OF CONTENTS Page 1. Introduction 1 1.1 Acknowlegements 1 2. Image file Format 2 3. Image Header 2 3.1 Signature (2 bytes) 2 3.2 Sequence (1 byte) 2 3.3 CheckSequence (1 byte) 3 3.4 Teledisk version (1 byte) 3 3.5 Data rate (1 byte) 3 3.6 Drive type (1 byte) 3 3.7 Stepping (1 byte) 3 3.8 DOS allocation flag (1 byte) 4 3.9 Sides (1 byte) 4 3.10 Cyclic Redundancy Check (2 bytes) 4 4. Comment Header / Data block 4 4.1 Cyclic Redundancy Check (2 bytes) 4 4.2 Data length (2 bytes) 4 4.3 Year (1 byte) 5 4.4 Month (1 byte) 5 4.5 Day (1 byte) 5 4.6 Hour, Minite, Second (1 byte each) 5 4.7 Comment data block (variable size) 5 5. Track Header 6 5.1 Number of sectors (1 byte) 6 5.2 Cylinder number (1 byte) 6 5.3 Side/Head number (1 byte) 6 5.4 Cyclic Redundancy Check (1 byte) 6 6. Sector Header 7 6.1 Cylinder number (1 byte) 7 6.2 Side/Head (1 byte) 7 6.3 Sector number (1 byte) 7 6.4 Sector size (1 byte) 8 6.5 Flags 8 6.6 Cyclic Redundancy Check (1 byte) 8 7. Sector Data Header 9 Teledisk .TD0 notes Table of Contents Page 7.1 Data block size (2 bytes) 9 7.2 Encoding method (1 byte) 9 Teledisk .TD0 notes Page: 1 1. Introduction Teledisk is a program which reads non-PC format diskettes into image files for archival and can later recreate a copy of the original disk from the image file. Once popular for archival of classic computer software, Teledisk has been withdrawn from the market by it's manufacturer and is no longer legally available. This presents a problem for those people who have data archived with Teledisk, because the file format is proprietary and not documented rendering the data useless without the program. In my development of ImageDisk (a replacement for Teledisk), I have created a utility to convert Teledisk .TD0 images into ImageDisk .IMD format - in doing so, I have researched the Teledisk format, read other peoples notes, done some reverse engineering myself, and come to what I believe is a somewhat complete understanding of the contents of a .TD0 image file. This document presents my notes on the Teledisk .TD0 image file format. This document is a work in progress. If you have any information to add, corrections etc. Please contact me. I can be reached via the contact information on my web site. http://www.classiccmp.org/dunfield 1.1 Acknowlegements The following people whom I have never met have saved me tons of time by making the results of their related efforts freely available. Will Krantz - Wrote a program called wteledisk which converts TX50 .TD0 into binary files for an emulator. Published a web page with his notes and source. Sergey Erokhin - Provided more details for Wills web page. Simon Owen - Published source code to read some .TD0 files for his SimCoupe emulator. Haruhika - Published LZSS-Huffman source code which has Okumura become "the reference" for many implementations. Teledisk .TD0 notes Page: 2 2. Image file Format The overall disk image file has this format: Image header (12 bytes) ;If the image was created using "Advanced Compression", everything ;below this line is compressed with LZSS-Huffman encoding. Optional comment header (10 bytes) Optional comment data (Variable size) ;For each track on the disk ... Track header (4 bytes) ;For each sector within the track Sector header (6 bytes) Optional data header (3 bytes) Optional data block (variable size) ;Image ends with Trackheader beginning with 255 (FF hex) If the Teledisk image was generated with "Advanced Data Compression", all parts of the file following "Image Header" are compressed as a single block with LZSS-Huffman encoding with the string lookup buffer preset to all spaces (ASCII 20). With "normal" compression, the remainder of the file is not compressed/encoded (other than the sector RLE compression). 3. Image Header The image header describes global information about the disk image. It is never compressed, and is laid out in the following format: Signature (2 bytes) Sequence (1 byte) Checksequence (1 byte) Teledisk version (1 byte) Data rate (1 byte) Drive type (1 byte) Stepping (1 byte) DOS allocation flag (1 byte) Sides (1 byte) Cyclic Redundancy Check (2 bytes) 3.1 Signature (2 bytes) - Contains 'TD' for normal compression - Contains 'td' for advanced compression 3.2 Sequence (1 byte) - Early Teledisk document indicates that this begins with 00 and increments for each member of a multi-volume set. - TDCHECK reports "bad header" if this value is set to anything other than 00. Teledisk .TD0 notes Page: 3 3.3 CheckSequence (1 byte) - Early Teledisk document indicates that this must be the same for each member of a multi-volume set 3.4 Teledisk version (1 byte) - Encodes the version number of the Teledisk program which created the image in the form High-nibble.low-nibble. eg: 15 = 1.5 3.5 Data rate (1 byte) - Encodes the data rate used for the diskette in lower 2 bits. 0 = 250kbps 1 = 300kbps 2 = 500kbps - High bit indicates single-density diskette (I believe this is for older versions only which did not support mixed density disks). 3.6 Drive type (1 byte) - Indicates the type of drive the disk was made on. - Early Teledisk document indicates the encoding is: 1 = 360k 2 = 1.2M 3 = 720k 4 = 1.44M - Experimentation with TDCHECK indicates that the text generated from the various encoding is: 0 = 5.25 96 tpi disk in 48 tpi drive 1 = 5.25 2 = 5.25 48-tpi 3 = 3.5 4 = 3.5 5 = 8-inch 6 = 3.5 - Use Data rate to determine appropriate drive density. 3.7 Stepping (1 byte) - Encodes step type in lower 2 bits 0 = Single-Step 1 = Double-step 2 = Even-only step (96 tpi disk in 48 tpi drive) - High bit indicates presence of optional comment block Teledisk .TD0 notes Page: 4 3.8 DOS allocation flag (1 byte) - non-zero means the disk was read using the DOS FAT table to skip unallocted sectors. 3.9 Sides (1 byte) - Encodes the number of sides read from the disk. 01 = One anything-else = Two 3.10 Cyclic Redundancy Check (2 bytes) This field contains the error-checking cyclic redundancy check for the header calculated with the polynomial value 41111 (A097 hex) using an input preset value of 0. The CRC is calculated over the first 10 bytes of the header, and should match the value stored in this field. 4. Comment Header / Data block The comment block encodes an ASCII comment as well as the creation date. It's presence is indicated by the high bit of the "Stepping" field in the image header being set. When present, it occurs immediately after the Image header in the following format: Cyclic Redundancy Check (2 bytes) Data length (2 bytes) Year since 1900 (1 byte) Month (1 byte) Day (1 byte) Hour (1 byte) Minite (1 byte) Second (1 byte) Following the comment header are comment line records, consisting of ASCII text terminated by NUL (00) bytes. 4.1 Cyclic Redundancy Check (2 bytes) This 16-bit field contains the error-checking cyclic redundancy check for the header calculated with the polynomial value 41111 (A097 hex) using an input preset value of 0. The CRC is calculated over the entire header block (beginning at offset 2 - just after the CRC) and the data records. 4.2 Data length (2 bytes) This is the length of the comment data block which follows the comment header. To display the comment data, read and output this many bytes following the header, translating NUL (00) bytes into newline sequences. Teledisk .TD0 notes Page: 5 4.3 Year (1 byte) Gives the year the image was created as an offset from 1900. eg: 2007 is encoded as 2007 - 1900 = 107 (6B hex). 4.4 Month (1 byte) Gives the month the image was created using a zero index. ie: 0=January, 11=December. 4.5 Day (1 byte) Gives the day (of the month) the image was created using a range of 1-31. 4.6 Hour, Minite, Second (1 byte each) Gives the time of day the image was created using 24-hour time. 4.7 Comment data block (variable size) Contains the ASCII text of the comment as NUL (00) terminated lines. The size of this block is given by "Data length" in the comment header. To display the comment, read and output "Data length bytes" from this block, translating NUL (00) bytes into newline sequences. Teledisk .TD0 notes Page: 6 5. Track Header Every disk track recorded in the image will begin with a track header, which has the following format: Number of sectors (1 byte) Cylinder number (1 byte) Side/Head number (1 byte) Cyclic Redundancy Check (1 byte) 5.1 Number of sectors (1 byte) This field indicates how many sectors are recorded for this track. This also indicates how many sector headers to expect following the track header. A number of sectors of 255 (FF hex) indicates the end of the track list. No other fields occur in this record, and the CRC is not checked. 5.2 Cylinder number (1 byte) This field encodes the physical cylinder number (head position) for this track, in a range of 0-(#tracks on drive-1). 5.3 Side/Head number (1 byte) This field encodes the disk side (0 or 1) that this track occurs on in it's lower bit. The high bit of this field is used to indicate the track was recorded in single-density. This allows mixed-density disks to be represented (FM on some tracks, and MFM on others). FM disks that I recorded had this bit set for every track, and NOT the FM indicator in bit 7 of the "Data rate" field of the image header. I cannot confirm this, but I suspect that early versions of Teledisk did not support mixed density disks, using only the FM bit in the image header. If this is the case, then a track should be interpreted as single density if either of the two FM indicator bits are set. 5.4 Cyclic Redundancy Check (1 byte) This 8-bit field contains the lower byte of a 16-bit error-checking cyclic redundancy check for the header calculated with the polynomial value 41111 (A097 hex) using an input preset value of 0. The CRC is calculated over the first three bytes of the header and should match the forth byte. Track headers and sector block lists occur until all tracks on the disk have been accounted for. When the last track record and sector block list has been read, a 255 (FF hex) byte indicates the end of the image. Teledisk .TD0 notes Page: 7 6. Sector Header Following the Track header will be a number of sector blocks consisting of a sector header and optional data header/data block. The number of sector blocks is indicated by the "Number of sectors" field in the track header. Each sector header has the following format: Cylinder number (1 byte) Side/Head (1 byte) Sector number (1 byte) Sector size (1 byte) Flags (1 byte) Cyclic Redundancy Check (1 byte) 6.1 Cylinder number (1 byte) This field indicates the logical cylinder number which is written in the ID field of the disk sector. For most disk formats it matches the Cylinder number indicated in the track header, however this does NOT have to be the case - some formats encode non-physical cylinder numbers. 6.2 Side/Head (1 byte) This field indicates the logical Side/Head indicator which is written in the ID field of the disk sector. For most disk formats it matches the Side/Head number indicated in the track header, however this does NOT have to be the case - some formats encode non-physical Side/Head numbers. 6.3 Sector number (1 byte) This field indicates the logical sector number which is wrtten in the ID field of the disk sector. Sector numbers do not have to be in any particular order (the ordering of the sectors determines the interleave factor of the track), do not necessarily begin at 0 or 1, and are not necessarily an unbroken series of numbers. Some formats encode seemingly arbitrary sector numbers. Teledisk sometimes creates bogus sectors headers to describe data that is not in a properly formatted sector. These extra sectors appear to be created with sector numbers begining at 100. Teledisk .TD0 notes Page: 8 6.4 Sector size (1 byte) Indicates the size of the sector, according to the following table: 0 = 128 bytes 4 = 2048 bytes 1 = 256 bytes 5 = 4096 bytes 2 = 512 bytes 6 = 8192 bytes 3 = 1024 bytes Note that disk formats exist which have different sector sizes within the same track, and Teledisk will encode them this way, however the PC 765 floppy disk controller cannot format such tracks, and the disk can not be recreated. 6.5 Flags This is a bit field indicating characteristics that Teledisk noted about the sector when it was recorded. The field contain the logical OR of the following byte values: 01 = Sector was duplicated within a track 02 = Sector was read with a CRC error 04 = Sector has a "deleted-data" address mark 10 = Sector data was skipped based on DOS allocation [note] 20 = Sector had an ID field but not data [note] 40 = Sector had data but no ID field (bogus header) note: Bit values 20 or 10 indicate that NO SECTOR DATA BLOCK FOLLOWS. The meaning of some of these bits was taken from early Teledisk documentation, and may not be accurate - For example, I've seen images where sectors were duplicated within a track and the 01 bit was NOT set. 6.6 Cyclic Redundancy Check (1 byte) This 8-bit field contains the lower byte of a 16-bit error-checking cyclic redundancy check for the sector header, data header and sector data calculated with the polynomial value 41111 (A097 hex) using an input preset value of 0. The CRC is calculated over the first five bytes of the sector header, the entire sector data header and the sector data block. The calculated CRC should match the value stored in the fourth byte of the sector header. Teledisk .TD0 notes Page: 9 7. Sector Data Header The sector data header occurs following the sector header only when sector data is present. This is indicated by bits 10 and 20 of the Flags value NOT being set. When present it has the following format: Data block size (2 bytes) Encoding method (1 byte) 7.1 Data block size (2 bytes) This indicates the size of the sector data block, including the encoding method (ie: data block size + 1). 7.2 Encoding method (1 byte) This field describes how the sector data is encoded. It can have three possible values: 7.2.1 0 - Raw sector data Encoding method == 0 indicates that "sector size" bytes of raw sector data follow. This is the actual data content for the sector. 7.2.2 1 - Repeated 2-byte pattern Encoding method == 1 indicates that a repeated 2-byte pattern is used. Note that this may occur multiple times until the entire sector has been recreated, as determined by "sector size" in the sector header. Each entry consits of two 16-bit values. The first is a count value indicating how many times the second (the data value) is repeated. 7.2.3 2 - Run Length Encoded data Encoding == 2 indicates that an RLE data block occurs. Note that this may occur multiple times until the entire sector has been recreated, as determined by "sector size" in the sector header. Each entry begins with a 1 byte length value or 00. If 00, then this entry is for a literal block. The next byte indicates a length 'n', and the following 'n' bytes are copied into the sector data as raw bytes (similar to Encoding method==0 except for only a portion of the sector). If not 00, then the length 'l' is determined as the value * 2 (ie: 2-510). The next byte indicates a repeat count 'r'. A block of 'l' bytes is then read once from the file, and repeated in the sector data 'r' times. Sector headers and data blocks occur until all sectors for the track have been accounted for.