Normal TS 2068 cassette-tape information consists of two parts, a header and a data block. The data block follows the header and contains the information itself. The header’s function is to provide the appropriate parameters to the computer so that it will know such things as the purpose of the data to follow, what space should be reserved for it, and what action should be taken with it. These facts are generated within the computer when the information is SAVEd and used to form the header. Upon LOADing, these facts are returned to the computer first to prepare it to receive the data block. It is the purpose of this article to examine the make-up of the header and provide a means by which a header can be read and the bytes therein interpreted.
The Header
The full header comprises:
A. A raspy tone (more-or-less a square wave) of about five seconds duration. Its purpose is to alert the computer to the fact that information is to follow and allow it to sync itself to the pulse rate, even though the speed of the recorder on which the tape is being played might not be identical to the speed of that on which it was recorded. (A similar tone burst of about two seconds duration precedes the data block.) This tone will not be discussed further here.
B. The header itself, containing a total of nineteen bytes. The first and last of these bytes are common to all headers (and data blocks), are not stored by the computer after their use and are not normally counted when describing the length of a header (or a data block). But they are there. Briefly, they are:
Byte 0 is a flag that tells the computer what is to follow. 0 is for a header, and 255 (FFh) is for a data block.
Byte 18 (Last, or ninteenth byte) is the Parity byte. This is built up during SAVE by giving it an initial value equal to the flag byte, then exclusive OR’ing every byte with the current parity byte, including a last exclusive OR with itself. This process results in a final parity byte which is equal to the flag byte. A similar process takes place upon LOADing. If the parity byte is not equal to the flag byte upon completion of LOADing, a tape loading error results.
These bytes will not be considered further, because the main interest is in the contents of the body of the header.
It can be seen that the body of the header consists of 17 bytes, which is why headers are often spoken of (perhaps carelessly) as having but 17 bytes. The function of each of these bytes depends on the Type of the data block. For purposes of discussion, these bytes will be refered to as Bytes 1 through 17. They provide the following information:
BYTE #1 is the Data Block type.
0 = BASIC Program.
1 = Numeric Array.
2 = Character Array.
3 = Machine Code or Screen.
BYTES #2 through #11 contain the File Name, which is 10 characters long. Spaces are used to fill out the name until it is 10 characters long. The name may be only spaces.
BYTES #12 & #13 are the length of the Data Block. As mentioned above, this is the length without the file and parity bytes. The entire data block is actually two bytes longer.
BYTES #14 & #15 are the Function (depends on Type):
BASIC Program- Number specified in LINE when SAVEd for auto-start. If there is no auto-start, then Byte 15 is 128 (80h).
Numeric Array- Byte 14 = 0. Byte 15 contains the code of the name of the array plus 64 (40h).
Character Array- Byte 14 = 0. Byte 15 contains the code of the name of the array plus 128 (80h).
For example, in numeric and character arrays the computer always assumes that the name of the array is upper-case (as with any variable). Therefore the code used is the code of the upper-case letter. E.g.: Say the name of the array is “G” or “g” (or “G$” or “g$ for a character array). The code of “G” is 71 (47h). Byte 15 will then contain 135 (87h) for a numeric array and 199 (C7h) for a character array.
Machine Code- Starting address of the block of bytes from which the data block was SAVEd. For a SCREEN$ SAVE this will always be 16384 (4000h).
BYTE #16 & #17 are only used for BASIC Programs. They give the length of the Program part of the file. Recalling that a BASIC Program file saves both the program and the Variables area, one can see that subtracting the length of the Program from the overall length of the file given by bytes 12 and 13 will result in the length of the Variables area.
For arrays and machine code Byte 16 is 0 and Byte 17 is 128 (89h).
If these 17 bytes could be loaded into a specified section of memory, then they could be examined using PEEK commands and interpreted in accordance with the information just given. This is indeed the case.
Reading it
In the 2068 computer, the tape LOAD routine resides in the EXROM. It will LOAD the number of bytes specified by the contents of the Z-80’s DE register into a block of memory whose starting address is given by the contents of the IX register.
A short (44-byte) machine-code program has been written to set these registers to the desired values, transfer to the EXROM, call the LOAD routine and return to the Home ROM and the BASIC program. Transfer to and from the EXROM is accomplished with an adaptation of the listing given in Figure 3.2.2-2 on page 80 of the 2068 Technical Manual. 30000 (7530h) has been used as a convenient “base” address, so that Byte 1 of the header is in 30001, Byte 2 in 30002, etc.
This machine-code program is contained in DATA statements in the BASIC program, which obviates loading it separately.
The BASIC program is given in Figure 1. It operates as follows:
Lines 10 – 140 initialize things and load the machine code.
Lines 150 – 180 insert an “underline” character into User-Defined Graphic character “A.” This is used in Line 400 to delineate the ten spaces present in the Name of the file in order to assist in the identification of blank spaces in the Name. (The “underline” character on the “0” key is not as good for this purpose because it fills all eight pixels on the bottom line of a character block and does not leave a visible gap between characters.)
Line 190 POKEs a value of 4 into the byte of memory which is going to contain the Type information. Since this is a higher value than any Type code, it assists in identifying a non-header (almost always).
Line 200 begins the reading of a header.
Line 220 calls the machine code. This LOADs the header from the tape into the specified memory area, ready to be examined by POKE statements.
Lines 300 – 390 PEEK into the header and place the data therein into BASIC variables. (These variables have been given names such as “start” and “length” in the hope that they will be self-explanatory and aid the user in understanding what is being done.)
Lines 400 – 470 print the name of the file for any Type and also print the remainder of the relevant information for a BASIC Program Type. (The “__________” in Line 400 is actually Graphics “A.”)
Lines 500 – 590 print the remainder of the relevant information for the other Types of files. Note that SAVE “name”SCREEN$ is the same as SAVE “name”CODE 16384,6912.
Lines 600 – 720 ask whether a hard copy of the header information is desired and whether to STOP or read another header.
Lines 1000 – 1050 print the actual bytes of the header for the curious.
Lines 9000 – 9050 provide a convenient means of SAVEing and VERIFYing the program.
Once the BASIC program in Figure 1 has been typed in, checked carefully, and SAVEd, it can be used to read headers. It can be SAVEd with a LINE 1 auto-start or not, as desired.
(Note that this program will also read the headers on SPECTRUM tapes. For this purpose, the 2068 should not be in a SPECTRUM mode, but used as a 2068 to perform this function.
Reading the Header
A. LOAD the BASIC header-reader program. If SAVEd with auto-start it will come up running. Otherwise use RUN. The message “Start the tape-” will appear.
B. Rewind and remove the tape on which the header-reader program was SAVEd (unless reading its own header). As instructed on the screen, play the header of the tape which is to be read.
C. The information contained in the header will appear on the screen. Select the “Copy” and “Read another header” options as desired. If the reading of another header is selected, the message “Start the tape-” will again appear. If this is not selected, the program will STOP.
D. Once the program is stopped, the raw data (actual bytes) in the header can be seen by using GO TO 1000 (or RUN 1000). This will wind up with the “Copy” query again and carry on from there.
For those who are interested, a HOT Z disassembly of the machine code is provided in Figure 2. The functions of this code are as follows:
Bytes 7505h – 750Dh establish the video mode (0 = Normal) and set up registers IX and DE as explained previously.
Bytes 750Eh – 751Fh activate the EXROM.
Bytes 7520h – 7522h call the tape reading routine in the EXROM.
Bytes 7523h – 752Eh return to the Home ROM.
Byte 752Fh returns to BASIC.
Byte 7530h is a temporary store used by the routines which activate and return from the EXROM. It preserves the data in the horizontal select register while the EXROM is in use.
4. “HEADERLESS” TAPES. These sometimes contain the header information in the first 17 bytes of the data block (remember that the first (flag) byte doesn’t count). If so, they can also be read with the header reader.
5. EXAMPLES. Figure 3 gives examples of reading various types of headers. Figure 4, obtained by using GO TO 1000, provides the actual bytes corresponding to the header data in Figure 3.
Figure 1.
5 REM "2068hdrrdr"- 2068 Headre Reader.
7 REM 29 Jun 85
10 BORDER 0: PAPER 0: INK 6: CLEAR 29956
20 RESTORE
30 FOR a=29957 TO 30000
40 READ c
50 POKE a,c
60 NEXT a
100 DATA 175,55,221,33,49,117,17,17,0,243
110 DATA 245,219,255,203,255,211,255,219,244,50
120 DATA 48,117,62,1,211,244,241,205,252,0
130 DATA 58,48,117,211,244,219,255,203,191,211
140 DATA 255,251,201,0
150 FOR i=0 TO 6
160 POKE USR "a"+i,0
170 NEXT i
180 POKE USR "a"+7,126
190 POKE 30001,4
200 CLEAR
210 PRINT AT 10,8;"Start the tape-"
220 RANDOMIZE USR 29957
230 CLS
300 LET type=PEEK 30001
310 IF type>3 THEN PRINT AT 10,9; FLASH 1;" Not a Header ": GO TO 700
320 LET a$=""
330 FOR i=0 TO 9
340 LET a$=a$+CHR$ PEEK (30002+i)
350 NEXT i
360 LET len=PEEK 30012+256*PEEK 30013
370 LET start=PEEK 30014+256*PEEK 30015
380 LET pa=PEEK 30016+256*PEEK 30017
390 LET var=len-pa
400 PRINT "Name: __________"; OVER 1;AT 0,6;a$''
410 IF type THEN GO TO 500
420 PRINT "Program type: BASIC."''
430 PRINT "Program area: ";pa;" bytes."''
440 PRINT "Variables area: ";var;" bytes."''
450 IF start>9999 THEN PRINT "No auto-start.": GO TO 600
460 PRINT "Auto-start at Line ";start;"."
470 GO TO 600
500 LET t$="Code Block."
510 IF start=16384 AND len=6912 THEN LET t$="Screen."
520 IF type=1 THEN LET t$="Numeric Array.": LET n$=CHR$ (PEEK 30015-64)+" or "+CHR$ (PEEK 30015-32)
530 IF type=2 THEN LET t$="Character Array.": LET n$=CHR$ (PEEK 30015-128)+"$ or "+CHR$ (PEEK 30015-96)+"$"
540 PRINT "Type: ";t$''
550 IF type=1 OR type=2 THEN PRINT "Array Variable Name: ";n$''
560 IF type=3 THEN PRINT "Starting Address: ";start''
570 PRINT "Length: ";len;" bytes."
600 INPUT "Copy (y/n)? ";r$
610 IF r$="Y" OR r$="y" THEN COPY
700 INPUT "Read another header (y/n)? ";r$
710 IF r$="Y" OR r$="y" THEN GO TO 200
720 STOP
1000 CLS
1010 PRINT "Byte Contents"; OVER 1;AT 0,0;"____ ________"''
1020 FOR i=1 TO 17
1030 PRINT TAB 3-LEN STR$ i;i;TAB 12-LEN STR$ PEEK (30000+i);PEEK (30000+i)
1040 NEXT i
1050 GO TO 600
9000 CLEAR : SAVE "2068hdrrdr" LINE 1
9010 BEEP .5,12
9020 PRINT AT 10,7;"Rewind to VERIFY"
9030 VERIFY ""
9040 PRINT AT 12,20;"is OK."
9050 STOP
Figure 2.
ADDR HEXCODE LABEL MNEMONIC
===========================
7505 AF XOR A
7506 37 SCF
7507 DD213175 LD IX,7531
750B 111100 LD DE,0011
750E F3 DI
750F F5 PUSH AF
7510 DBFF IN A,(FF)
7512 CBFF SET 7,A
7514 D3FF OUT (FF),A
7516 DBF4 IN A,(F4)
7518 323075 LD (7530),A
751B 3E01 LD A,01
751D D3F4 OUT (F4),A
751F F1 POP AF
7520 CDFC00 CALL 00FC
7523 3A3075 LD A,(7530)
7526 D3F4 OUT (F4),A
7528 DBFF IN A,(FF)
752A CBBF RES 7,A
752C D3FF OUT (FF),A
752E FB EI
752F C9 RET
7530 00 NOP
Figure 3.
Name: 2068hdrrdr Program type: BASIC. Program area: 2269 bytes. Variables area: 0 bytes. Auto-start at Line 1. Name: 2068hdrrdr Program type: BASIC. Program area: 1879 bytes. Variables area: 103 bytes. No auto-start. Name: GEE_______ Type: Numeric Array. Array Variable Name: G or g Length: 130 bytes. Name: GEESTR____ Type: Character Array. Array Variable Name: G$ or g$ Length: 10 bytes. Name: Kirsten___ Type: Screen. Starting Address: 16384 Length: 6912 bytes. Name: bytes_____ Type: Code Block. Starting Address: 26710 Length: 1024 bytes.
Figure 4.
Byte Contents Byte Contents Byte Contents Byte Contents Byte Contents Byte Contents 1 0 1 0 1 1 1 2 1 3 1 3 2 50 2 50 2 71 2 71 2 75 2 98 3 48 3 48 3 69 3 69 3 105 3 121 4 54 4 54 4 69 4 69 4 114 4 116 5 56 5 56 5 32 5 83 5 115 5 101 6 104 6 104 6 32 6 84 6 116 6 115 7 100 7 100 7 32 7 82 7 101 7 32 8 114 8 114 8 32 8 32 8 110 8 32 9 114 9 114 9 32 9 32 9 32 9 32 10 100 10 100 10 32 10 32 10 32 10 32 11 114 11 114 11 32 11 32 11 32 11 32 12 221 12 190 12 130 12 10 12 0 12 0 13 8 13 7 13 0 13 0 13 27 13 4 14 1 14 42 14 0 14 0 14 0 14 86 15 0 15 128 15 135 15 199 15 64 15 104 16 221 16 87 16 0 16 0 16 0 16 0 17 8 17 7 17 128 17 128 17 128 17 128
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.