If you are new to BBC BASIC (Z80), or you are experiencing difficulty with disk files you might find these notes useful. Some of the concepts and procedures described are quite complicated and require an understanding of file structures. If you have trouble understanding these parts, don't worry. Try the examples and write some programs for yourself and then go back and read the notes again. As you become more comfortable with the fundamentals, the complicated bits become easier.
All computers are able to store and retrieve information from a non-volatile medium. (In other words, you don't lose the information when the power gets turned off.) Audio cassettes are used for small micro computers, diskettes for medium sized systems and magnetic tape and large disks for big machines. In order to be able to find the information you want, the information has to be organised in some way. All the information on one general subject is gathered together into a FILE. Within the file, information on individual items is grouped together into RECORDS.
This is very similar to the way a cassette is searched for a particular file. You put the cassette in the recorder, type in the name of the file you want and push play. You then go and make a cup of tea whilst the computer reads through all the files until it comes to the one you want. Because the cassette is read serially from start to end, it's very difficult to do it any other way.
Life is easier with a computer that uses diskettes (or disks). There is an index which tells the computer where to look for each of the files and the serial search for the file is not necessary. However, once you have found the file, you still need to read through it to find the record you want.
There are a number of ways to overcome this problem. We will consider the two simplest; random access (or relative) files and indexed files.
What happens when you close an account? You can't tear out the page because that would upset the numbering system. All you can do is draw a line through it - in effect, turn it into a blank page. Before long, quite a number of pages will be 'blank' and a growing proportion of your book is wasted.
With other forms of 'numbering', say by the letters of the alphabet, you could still not guarantee to fill all the pages. You would have to provide room for the Zs, but you may never get one. When you started entering data, most of the pages would be blank and the book would only gradually fill up.
The same happens with this sort of file on a diskette. A random file which has a lot of empty space in it is described as sparse. Most random files start this way and most never get more than about ¾ full. Count the number of empty 'slots' in your address book and see what proportion this is of the total available.
If we had an index at the front of the book we could scan the index for the name and then turn to the appropriate page. We would still be wasting a lot of space because some names, addresses etc are longer than others and our 'slots' must be large enough to hold the longest.
Suppose we numbered all the character positions in the book and we could easily move to any character position. We could write all the names, addresses, etc, one after the other and our index would tell us the character position for the start of each name and address. There would be no wasted space and we would still be able to turn directly to the required name.
What would happen when we wanted to cancel an entry? We would just delete the name from the index. The entry would stay in its original place in the book, but we would never refer to it. Similarly, if someone changed their address, we would just write the name and the new address immediately after the last entry in the book and change the start position in the index. Every couple of years we would rewrite the address book, leaving out those items not referenced in the index and up-date the index (or write another one).
This is not a practical way to run a paper and pencil address book because it's not possible to turn directly to the 3423rd character in a book, and the saving in space would not be worth the tedium involved. However, with BBC BASIC you can turn to a particular character in a file and the tedium only takes a couple of seconds, so it's well worth doing.
Serial files cannot be used to access particular records from within the file quickly and easily. In order to do this with the minimum access time, random access files are necessary. However, a random file generally occupies more space on the disk than a serial file holding the same amount of data because the records must be a fixed length and some of the records will be empty.
Most versions of BASIC only offer serial and random files, but because of the way that disk files are handled by BBC BASIC (both on the BBC computer and CP/M-80 computers using BBC BASIC (Z80)), it is possible to construct indexed, and even linked, files as well. Indexed files take a little longer to access than random files and it is necessary to have a separate index file, but they are generally the best space/speed compromise for files holding a large amount of data.
Because of the character by character action of the write/read process, it is possible (in fact, necessary) to keep track of your position within the file. BBC BASIC does this for you automatically and provides a pointer PTR (a pseudo-variable) which holds the position of the NEXT character (byte) to be written/read. Every time a character is written/read PTR is incremented by 1, but it is possible to set PTR to any number you like. This ability to 'jump around' the file enables you to construct both random (relative) and indexed files.
BBC BASIC provides the facility for completely free-format binary data files. Any file which can be read by the computer, from any source and in any data format, can be processed using the BGET, BPUT and PTR functions.
The CP/M-80 operating system allows a composite file name in the following format:
The drivename is a single letter followed by a colon and denotes the disk drive on which the file will be found or created.DRIVENAME:FILENAME.EXTension
The file name can be up to 8 characters long, and the extension up to three characters. Whenever a file name without an extension is given, BBC BASIC (Z80) will append .BBC as the default extension.
The first example will save the program to a file named FRED.BBC. The second will save COMPOUND.BBC.SAVE filename SAVE "FRED" A$="COMPOUND" SAVE A$
You can specify a drivename as well as the file name. The following example will save the current program to a file called TEST.BBC on drive D:
As with SAVE, you can specify a drive name. The example below loads the program saved previously as an example of the SAVE command.LOAD filename LOAD "FRED" A$="HEATING" LOAD A$
As with SAVE and LOAD, you can specify a drive name.CHAIN filename CHAIN "GAME1" A$="PART2" CHAIN A$
Load the first program (with the lower line numbers) in the normal way. Then, find out the top address of the program less 3 by typing
This will print the address in hex (nnnn) at which the first byte of the second program file must be loaded. Finally, load the second program by typingPRINT ~TOP-3<Enter>
*LOAD "PROG2" nnnn<Enter> OLD<Enter>
*ERA filename *ERA FRED *ERA PHONE.DTA
To delete a file whose name is known only at run-time, use the OSCLI command. It's a bit clumsy, but a lot better than the original specification for BBC BASIC allowed. This time all of the command, including the ERA, must be supplied as the argument for the OSCLI command. You can use OSCLI for erasing a file whose name is a constant, but you must include all of the command line - in quotes this time.
You can include a drive name in both the *ERA and *OSCLI command formats.fname$="FRED" OSCLI "ERA "+fname$ fname$="PHONE.DTA" command$="ERA " OSCLI command$+fname$ OSCLI "ERA FRED"
Although CP/M-80 will allow you to do so, it is bad practice to erase an open file.
Once again, if you want to rename files whose names are only known at run-time, you must use the OSCLI command.*REN file2=file1 *REN FRED2=FRED1 *REN PHONE.DTA=PHONE
Because CP/M-80 refers to files by their handles, it does not get confused if you rename an open file. However, in all probability, the same cannot be said for you.fname1$="FRED1" fname2$="FRED2" OSCLI "REN "+fname2$+"="+fname1$
*DIR List *.BBC files on the current drive. *.B:*.DTA List *.DTA files on drive B.
OPENIN OPENUP OPENOUT EXT# PTR# INPUT# BGET# PRINT# BPUT# CLOSE# END EOF#
When you open the file, a file handle (an integer number) is returned by the interpreter and you will need to store it for future use. (The open commands are, in fact, functions which open the appropriate file and return its file handle.)
You use the file handle for all subsequent access to the file. (With the exception of the STAR commands outlined previously.)
If the system has been unable to open the file, the handle returned will be 0. This will occur if you try to open a non-existent file in the input mode (OPENIN or OPENUP).
You always need to store the file handle because it must be used for all the other file commands and functions. If you choose a variable with the same name as the file, you will make programs which use a number of files easier to understand.OPENOUT filename file_num=OPENOUT "PHONENUMS"
On a networked system, OPENOUT opens the file in 'compatibility' mode and the file is not available to any other user. If you wish to create a new file which can be read, concurrently by other users, you should open it with OPENOUT, immediately close it and re-open it with OPENUP. See the earlier sub-section Networking - Shared Files for more details.phonenums=OPENOUT "PHONENUMS" opfile=OPENOUT opfile$
You will be unable to open for input (file handle returned = 0) if the file does not already exist.OPENIN filename address=OPENIN "ADDRESS" check_file=OPENIN check_file$
You will be unable to open for update (file handle returned = 0) if the file does not already exist.OPENUP filename address=OPENUP "ADDRESS" check_file=OPENUP check_file$
On a networked system, OPENUP opens a file in the 'read-write, deny write' mode. A file may be opened once with OPENUP and any number of times by any number of users with OPENIN. See the earlier sub-section Networking - Shared Files for more details.
When a file is closed its file buffer (if it has one) will be flushed to CP/M-80 before the file is closed.CLOSE#fnum
READ# can be used as an alternative to INPUT#INPUT#fnum,var data=OPENIN "DATA" : INPUT#data,name$,age,height,sex$ : :
String variables are written as the character bytes in the string plus a carriage-return. Numeric variables are written as 5 bytes of binary data.PRINT#fnum,var
data=OPENOUT "DATA" : : PRINT#data,name$,age,height,sex$ : :
In the case of a sparse random-access file the value returned is the length of the file to the last byte actually written to the file. Although much of the file may well be unused, writing this 'last byte' reserved physical space on the disk for a file of this length. Thus it is possible to write a single byte to a file and get a 'Disk full' error.EXT#fnum
When the file is OPENED, PTR# is set to zero. However, you can set PTR# to any value you like. (Even beyond the end of the file - so take care).PTR#fnum
Reading or writing, using INPUT# and PRINT#, (and BGET# and BPUT# - explained later), takes place at the current position of the pointer. The pointer is automatically updated following a read or write operation.
A file opened with OPENUP may be extended by setting PTR# to its end (PTR# = EXT#), and then writing the new data to it. You must remember to CLOSE such a file in order to update its directory entry with its new length. A couple of examples of this are included in the sections on serial and indexed files.
Using a 'PTR#fnum=' statement will flush the appropriate BBC BASIC (Z80) file buffer to CP/M-80.
Attempting to read beyond the current end of file will not give rise to an error. Either zero or a null string will be returned depending on the type of variable read.eof=EOF#fnum
EOF# is only really of use when dealing with serial (sequential) files. It indicates that PTR# is greater than the recorded length of the file (found by using EXT#). When reading a serial file, EOF# would go true when the last byte of the file had been read.
EOF# is only true if PTR# is set beyond the last byte written to in the file. It will NOT be true if an attempt has been made to read from an empty area of a sparse random access file. Reading from an empty area of a sparse file will return garbage. Because of this, it is difficult to tell which records of an uninitialised random access file have had data written to them and which are empty. These files need to be initialised and the unused records marked as empty.
Writing to a byte beyond the current end of file updates the file length immediately, whether the record is physically written to the disk at that time or not.
or, more expedientlyBGET#fnum byte=BGET#fnum char$=CHR$(byte)
BPUT#fnum,var BPUT#fnum,&1B BPUT#fnum,house_num BPUT#fnum,ASC "E"