64 EXPLORER
Larry Isaacs
The STATUS Variable
Part 2
In this conclusion of the two-part article on the STATUS variable, we examine possible ST bit values and the resulting indicators.
Before we continue our investigation of the STATUS variable (ST), let's briefly review what we discussed in Part 1.
ST is a reserved variable (which means that, like TI for TIME, we can't use ST as a variable in our programs) whose value indicates if anything unusual happened during the last I/O (Input/ Output) operation. This status value signals a number of different conditions, using a separate bit "on" for each. However, last month we avoided a direct discussion of bits. Instead, we decided to dissect the ST value by representing the value as a sum of the numbers in the group 1, 2, 4, 8, 16, 32, 64, and -128. If you allow each number to be used only once, only one combination of these numbers can represent the value returned by ST. For example, -118 can be represented by the sum -128 + 8 + 2.
Last month we began investigating what the ST variable tells us with respect to the Datassette unit, specifically by examining the End-Of-File (EOF) indicator. The EOF condition is indicated by the presence of 64 in the sum equivalent to the ST value. After a sequence of simple test programs, we discovered that the End-Of-File indicator really meant that the next byte in the file has value of 0. If perhaps some 0 bytes got mixed into your data, the EOF indicator would sometimes be on when reading the data.
We also found that data is written to the cassette in blocks of 191 bytes, and that a 0 byte is added automatically to the end of the data when the file is closed. Chances are very good, however, that the last block written to the file will contain something less than 191 bytes. One of the tests performed last month showed that if you accidentally read past the end of the data, you could continue receiving data with no apparent ill effects, except that the data isn't valid. We even found that you could continue reading right into an End-Of-Tape block which could follow the data file.
Testing With INPUT#
It is obviously essential, then, to pay attention to the EOF indicator and not allow any 0 bytes to be written to the data file. All the tests in last month's column used the GET # statement to read the data. Before leaving our discussion of the EOF indicator, we should see if there are any problems when using the INPUT # statement. Essentially, we need to find out how the INPUT # statement reacts to the presence of 0 bytes, and what EOF conditions they cause. Run this simple program:
100 OPEN 1, 1, 2, "TEST" 110 PRINT # 1, "A"; CHR$ (0); "B" 120 PRINT # 1, "C" : CLOSE 1 200 PRINT "REWIND THE CASSETTE." 210 PRINT "PRESS RETURN WHEN READY." 220 INPUT Z$ 300 OPEN 1, 1, 0, "TEST" 310 INPUT # 1, Z$ : PRINT Z$, LEN (Z$), ST 320 INPUT # 1, Z$ : PRINT Z$, LEN (Z$), ST 330 CLOSE 1
As you can see, lines 100-110 write two lines to a file, lines 200-220 ask you to rewind the cassette, and lines 300-330 read the file. Running this program results in the following display:
A | 1 | 64 |
C | 1 | 64 |
Something a little strange happened. The second string read began with the letter C, which implies that the B was previously read as part of the first string – yet it does not appear as part of the first string. Also, an EOF indicator was given with the first string even though the 0 byte occurred in the middle of the string.
The missing B is fairly easy to explain. The INPUT# statement first reads its data into an input buffer. When the statement stores the data into a string variable, the data must be moved to another area of memory where string characters are stored. In this process, a 0 byte is used to mark the end of the string data to be moved. The presence of the 0 byte in the middle of the data being moved causes the process to be terminated prematurely. Thus the B is left in the input buffer, but isn't stored as part of the string.
As for the EOF, once it is set, it will remain set even though additional bytes are read as part of the input. This also illustrates another case where the EOF indicator does not halt the input of data. This means you must make sure the last data written is properly terminated if you intend to read that data with an INPUT# statement. In our program above, the PRINT# statement causes a carriage return to be written after the C. With this carriage return as the terminator, we get a valid EOF condition when reading the last of the data.
Block Length Errors
A SHORT BLOCK is indicated by the presence of the number 4 in the sum equivalent to the ST value. A LONG BLOCK is indicated by the presence of the number 8 in the sum. These status indicators mean that a block has been read from cassette which contains something other than the expected 191 bytes. This naturally indicates an error: the data read from this block is probably not what we want. These errors occur if something goes wrong while LOADing the cassette, or if something went wrong while SAVEing to the cassette.
Another way to receive these error indicators is to read a program file as if it were a data file. A program file, written by the SAVE command, differs from a data file in that the program is written as a single block. Actually two copies of the program are written (that is, two blocks), with the second copy being used to check for errors in the first block. It is highly unlikely that a program would contain exactly 191 bytes, so you will probably get an error if you try to read a program file as data. For example:
100 SAVE "TEST" 200 PRINT "REWIND THE CASSETTE." 210 PRINT "PRESS RETURN WHEN READY." 220 INPUT Z$ 300 OPEN 1, 1, 0,"TEST" 310 GET #1, Z$ : PRINT LEN (Z$), ST 320 CLOSE 1
The result displayed is:
1 4
The 4 printed for ST shows that our test program is less than 191 bytes long. You might also note that Z$ still received a byte of data although there was an error when the block was read from the cassette. We can try to force a LONG BLOCK error by adding the following two lines to the example above:
110 REM MAKE THE PROGRAM LONGER 120 REM MAKE THE PROGRAM LONGER
These lines make the program slightly longer than 191 bytes. Running the program now displays:
1 32
This wasn't quite what we were expecting. The 32 for ST indicates a CHECKSUM ERROR. With each 191-byte block written, a sum of all the bytes in the block is written along with the data. This sum, called the CHECKSUM, is used to help make sure the data is later read correctly. If at least 191 bytes are received for the block, the CHECKSUM is checked first. Even if the CHECKSUM accidentally matched, we would still get a LONG BLOCK error.
End-Of-Tape Condition
EOT is indicated by a –128 in the sum equivalent to the ST value. The program below provides a simple demonstration:
100 OPEN 1, 1, 2, "TEST" 110 PRINT#1, "A" : CLOSE 1 200 PRINT "REWIND THE CASSETTE." 210 PRINT "PRESS RETURN WHEN READY." 220 INPUT Z$ 300 OPEN 1, 1, 0, "NOFILE" 310 PRINT ST : CLOSE 1
However, when we RUN this program, the result isn't at all what we expected. We never get a chance to look for a value of -128 in the ST variable because the program quits with the message ?DEVICE NOT PRESENT ERROR IN 300. Later in this article we'll see that when using the serial bus, a -128 for ST indicates that an attempt was made to send data to a device not connected to the computer. When BASIC detects a -128 in the ST value while a file is open for reading, it aborts the program with the "device not present" message without checking to see if the device was the Datassette (in which case the -128 was due to an EOT marker being detected) or a serial bus peripheral such as a disk drive or modem (in which case the -128 indicates a true "device not present" condition). Since the error message throws us out of our program, there's no way to check for the EOT indicator while reading a cassette file in BASIC. We'll just have to be careful not to attempt to read past the last file on the tape.
While BASIC won't let us check for the EOT marker, it doesn't place the same restrictions on itself. BASIC checks the ST variable for a -128 while LOADing or VERIFYing from cassette to determine if it has read the last program from a tape.
There's one remaining possible condition for the ST variable in cassette operations. A value of 16 in the sum indicates an UNRECOVERABLE READ ERROR. This means that a byte could not be read from the tape. However, as with the LONG BLOCK error, it is unlikely that you will detect this condition since the bad byte will also cause a CHECKSUM error, which is what the ST variable will report. As with the EOT indicator, this is a value which BASIC uses for its own testing during LOADs and VERIFYs.
We said earlier that when you SAVE a program to tape, two copies of the program are actually written out. When the program is read back in, BASIC checks for a value of 16 in the ST variable as the first copy is being read. If too many bad bytes are found, BASIC uses the second copy of the program. It is this feature which makes Commodore Datassettes such reliable data storage devices. People who have used tape storage for other home computers may have trouble believing how rarely the ?LOAD ERROR message is seen on the 64.
In our discussion of the ST variable as it relates to the cassette unit, we found several cases where its actual operation wasn't quite what we were told in the documentation (which didn't say very much on the ST variable anyway). This information will prove useful should you try to write your own program using the cassette for data storage. Also, when information provided by books or manuals isn't sufficient to deal with your specific questions or difficulties, using small test programs is often the best way to find out how something really works.
The Serial Bus
The serial bus is involved when connecting various devices, such as the 1541 disk drive, to the 64. Let's take a look at what the ST variable tells us when used with the serial bus. According to the Commodore 64 Programmer's Reference Guide, the status indicators are as follows:
VALUE | MEANING |
1 | READ TIME OUT |
2 | WRITE TIME OUT |
4 | not defined |
8 | not defined |
16 | not defined |
32 | not defined |
64 | EOI |
-128 | DEVICE NOT PRESENT |
We'll begin with the EOI indicator, which, like EOF for the cassette, indicates when the end of the data has been reached when reading. Again, the important question is whether the EOI indicator accompanies the last byte of data, or comes on when you try to read past the last byte. A simple test program would show that the EOI indicator accompanies the last byte of data, like the EOF does with the cassette. However, a little more investigation shows that the 1541 disk drive, unlike the Datassette, is able to really know when the last byte is sent. This means your data can have all the 0 bytes you want without causing multiple EOI indications.
This also implies that the disk does something different from the cassette with respect to reading past the end of the data. A simple test here shows that the EOI indicator remains on as you continue to read past the end of the data. In addition, the READ TIME OUT indicator comes on (that is, the ST value is 66, the sum of 64 + 2). Thus, for any given read operation, a read routine is able to determine if the operation occurred normally, read the last byte, or has already passed the end of the data. This is a substantial improvement over what the ST variable tells us when we're working with the tape unit.
The DEVICE NOT PRESENT Indicator
The DEVICE NOT PRESENT condition is shown by the presence of -128 in the sum equivalent to the ST value. This indicator shows that an attempted communication with a particular device was not successful. The error is obvious if the selected device is not connected or not turned on. In addition, if you try to write to an existing file or read a nonexistent file on the 1541, you will get this error message.
It is important to remember that this condition can't occur until an attempt is made to transfer data. A statement like OPEN 1, 13 doesn't transfer any data, so the status bit doesn't get a chance to get set. Should you execute such a statement in a program and later execute a PRINT # 1 statement, the ST variable would return a value of -128, as you would expect. If you execute a GET # 1 or INPUT # 1 instead and have no devices connected to the serial bus, you will also get -128 for ST.
However, if at least one device is connected, then the 64 will hang up if you do an INPUT # or GET # from the nonexistent device. The only way to recover is to press the STOP and RESTORE keys simultaneously. The 64 can tell when the desired device isn't there to receive data, but not when the device isn't there to send data. The 64 will patiently wait forever if you let it. This doesn't happen, however, when no serial devices are connected or turned on. The 64 must always output some command bytes to identify the device with which it wants to communicate. With no devices to receive the command bytes, the DEVICE NOT PRESENT condition is detected before the computer begins waiting to receive data from the nonexistent device.
Since writing to a nonexistent device either hangs up the 64 or gives a DEVICE NOT PRESENT error, it leaves me wondering what situations cause the WRITE TIME OUT. We have already seen the READ TIME OUT, but that was in conjunction with the EOI indicator. Again, there isn't much in the user's manual or reference guide on this topic. I assume that these manuals indicate a data transfer operation was unsuccessful or failed to occur within some time limit.
What we've seen here for status indicators on the 64 may not be typical for other computers. You will find that the EOF indicators will typically come on after the last byte is read, rather than in conjunction with the last byte. So if you are using another computer in addition to the 64, be prepared to find some differences with respect to status indicators for I/O operations.