Bottom     Previous     Contents

Chapter Six
File Storage

One important data type that has been ignored so far is the file. This is because files are generally not stored within RAM but, in the case of the BBC Micro, on tape or disk. The fact that files are not stored in RAM does affect the sort of applications that they can be used for. At the most trivial level, the fact that disk or tape has a much larger storage capacity than RAM makes it possible to write programs that handle realistic amounts of data. On the other hand, it is important not to miss the point that the idea of a file is independent of the physical device used to store it. Indeed, one of the strengths of the BBC Micro's file system is that it provides the programmer with a range of commands that are as far as possible 'device independent'. This chapter examines the BBC Micro's filing system and how it can be used from both BASIC and 6502 assembler with as much 'device independence' as possible.

The sequential file

There are two different types of file - sequential files and random access files. Of the two, only sequential files can be stored on serial devices such as tape and so these tend to be the more common type of data file. Because of this the term 'file' used on its own nearly always refers to sequential files. A file can be thought of as a 'list' of data items constructed in such a way that only one item can be read or written at a time. The only real difference between random access and sequential files is that sequential files can only be read and written in a given order. It is helpful to think of a pointer that is used to indicate the 'current' position in the file. That is, the pointer points to the item that will be read from or to the location in the list that will be written to. In the case of random access files, the pointer can be freely moved to any position within the file. Sequential files are much more restrictive. In fact there is no way that the pointer can be influenced directly. Instead each time that an item is read or written the pointer is 'moved on' to the next item. The situation is a little more complicated than this in that generally you cannot change individual items in an existing file.
When a file is being created, storage space is allocated and the pointer is set to the beginning of this area - this is called 'opening the file for writing'. Notice that in this case the pointer points at the next free area of storage so there is no way that anything can be read from a file that has been opened for writing. As each item is written the pointer is moved on to point to a new free space .Thus, items can only be added to the end of the file and the file grows in length with each item that is written.
When an existing file is being read the pointer is initially positioned at the first item in the file this is called 'opening the file for reading'. As each item is read from the file the pointer is automatically moved on to point at the next item if there is one! It is possible to try to read more items from a file than were written to it. If you try and do this on a BBC Micro you will generate an 'end of file error'.
There is one additional operation concerning files which in many ways is the opposite of opening the file. Although in principle it is always possible for a computer to deduce when you have finished using a file, there are advantages in defining an operation that explicitly informs the computer that you are finished -- this is the close file operation. What the computer actually does when asked to close a file depends on the type of device that the file is stored on and whether the file was open for reading or writing. If the file was open for reading then a closing operation simply resets various internal variables so that the file can potentially be re-opened later in the program. If the file was open for writing then a close operation will make sure that all the data that was written to the file is actually written out rather than sitting in a buffer. After this a special marker, the 'end of file marker', is inserted to show that there is no meaningful data beyond this position.
If you are already used to sequential data files on tape or disk you may find the above description unnecessarily complicated. However, thinking in terms of a pointer to the current position in the file certainly helps when it comes to understanding random access files. As the BBC Micro only supports random access files on disk it makes more sense to leave a description of how they work until after a description of disk files in general.

BASIC file commands

The previous section introduced the sequential file in a very general way. In practice any useful computer language provides a collection of commands that manipulate files. The trouble is not only that each computer language has invented its set of file commands, so have the different dialects of BASIC! Some versions of BASIC even provide different commands for handling files that are stored on tape and on disk! The situation is not as bad as it seems from this description because all the sets of commands have a great deal in common. BBC BASIC% file handling commands are particularly logical and the only real differences between files stored on tape and disk are that cassette tape is restricted to purely sequential files.
The User Guide, Section 33, gives a good introduction to the BBC Micro's file commands and as a result it is not worth going over each command in detail. However it does seem worth giving a brief summary of what the different commands are used for and how they fit into the general file scheme described earlier.
The fundamental BBC BASIC file operations are:

OPENOUT, OPENIN, CLOSE, BGET and BPUT

There are other file operations that are useful but these five are the ones from which everything else stems. The commands OPENOUT and OPENIN perform the file opening operations described earlier. OPENOUT opens the file for writing and the OPENIN command opens the file for reading. The only additional features are that opening operations are used to associate a 'filename' with a 'channel number'. The filename performs the same function for files as the variable name for variables i.e. it identifies an area of storage on the file device concerned. Although it would be possible to use the filename throughout a program to identify which file is being used, it is more convenient to use 'channel numbers'. When a file is opened it is assigned a unique channel number that is used to identify it in all further operations. The exact forms of the OPEN commands are:

variable=OPPENIN(filename)

and

variable=OPENOUT(filename)

where 'filename' can be a string expression or a constant and the channel number is returned as the result of each function and stored in 'variable'. Following either of these two commands the file is referred to by the channel number stored in 'variable' and not by its filename.
The command:

CLOSE# channel

performs the file close operation on the file assigned to channel number 'channel'. As described in the general introduction to files, you must issue a CLOSE command before trying to re-open the file and you must issue a CLOSE command when you have finished writing a file to make sure that all of the data that you have written to the file is safely stored.
The two commands:

BGET and BPUT

perform the operations of reading and writing an item of data. In this case the term 'item' refers to a single byte that can either be used directly as a number in the range 0 to 255 or can be interpreted as a single ASCII character (using CHR$). Thus:

BGET# channel

is a function that returns a single byte from the file assigned channel number 'channel' and:

BPUT# channel,value

is a command that writes the single byte 'value' to the file assigned channel number 'channel'. As you would expect, each instruction automatically moves the pointer to the current file position on by one byte.

BBC files in use - buffering

Using just the five instructions introduced in the last section a file can be created and read back. For example:

10 INPUT F$
20 F=OPENOUT(F$)
30 FOR I=1 to 300
40 BPUT#F,I MOD 256
50 PRINT I
60 NEXT I
70 CLOSE #F
80 F=OPENlN(F$)
90 PRINT BGET#F
100 GOTO 90

If you are using (or have selected) the tape filing system then add:

75 PRINT "REWIND TAPE"

before running this program. Line 20 opens a new file for writing, the file name is in F$ and the channel number is returned in F. Lines 30 to 60 write out 300 bytes to the file and to the screen. Line 70 closes the file and line 80 re-opens it for reading. Lines 90 and 100 form an infinite loop reading and printing a single byte at a time from the file.
If you run this program with either the tape or disk filing system selected (using *TAPE or *DISC) then you will see a number of common features about the way files are handled, Firstly, once the file has been opened for writing you will see 256 numbers printed on the screen, then the disk or tape will be activated and the program will pause. The tape will then stop (or the disk will be deactivated), the program will continue to print the remainder of the numbers on the screen and once again the tape or disk will be activated. The reason for this stop go behaviour is that the BBC Micro buffers all file operations. That is, bytes are not sent directly to the disk or tape, instead they are collected in an area of memory called a buffer. Only when the buffer is full or the file is closed is anything written on the tape or disk. Thus, data transfers between the file storage device are in terms of 'buffer loads' rather than single bytes.
The same buffering behaviour can be seen when the file is read. The OPENIN command actually reads in a buffer full of bytes which the BGET command uses each time through the loop (lines 90 to 100). Of course when the buffer is empty the program pauses while another buffer load of data is read in. Buffering may seem a complicated way of handling files but it is much faster than any method based on transferring single bytes to and from the file storage device!
The above program comes to an end rather abruptly with an error message 'EOF at line 90'. The solution to this problem is to be found in the EOF function. The function:

EOF# channel

returns the value TRUE (-1) if the last byte has been read from the file and FALSE (0) otherwise. Notice that EOF goes TR UE after the last item has been read and not when you try to read beyond the last item. This means that EOF can be used as part of a REPEAT . . . UNTIL loop to process entire files. For example:

10 INPUT F$
20 F=OPENIN(F$)
30 REPEAT
40 PRINT BGET#F
50 UNTIL EOF#F

will read the file ES, byte by byte and stop immediately after reading the last byte in the file. You can see the action of EOF more clearly if you add:

95 PRINT EOF#F

to the earlier file creation and reading program. What this shows is that EOF goes TR UE when the last valid byte is read from the file but you can still read a 'dummy' value (of 254) from the file before the EOF error message is given and the program stops. This is useful because it enables a file to be read completely by a while loop, but it is also dangerous because it is possible to process a totally spurious value if the change in the EOF function is not detected immediately.

Larger data items - PRINT# and INPUT#

Although BBC Micro files work in terms of single byte data items, the data types used in BBC BASIC are generally composed of a number of bytes. For example, a standard integer variable takes four bytes to store its value. It is possible to see how real, integer and string variables could be stored and retrieved from a file one byte at a time but this would be tedious and do commands:

PRINT# channel,print_list

and

INPUT# channel,input_list

where 'channel' is the channel number as used in BGET and BPUT and 'print_list' and 'input_list' are lists of variables separated by commas. Although there is a superficial similarity between PRINT # and PRINT and INPUT# and INPUT it is important to realise that their actions are very different.
Both PRINT and INPUT handle data output and input in terms of the ASCII representation of data. For example, PRINT A will convert the value stored in the variable 'A' from its internal format to a string of ASCII digits. In this sense PRINT A performs the same set of actions as PRINT STR$(A). By contrast PRINT# and INPUT# handle data without changing it very much from its internal format. The actual format and the number of bytes of storage used within a file depends on the type of value. Integers are stored using five bytes, the first byte is always &04 and serves to identify the following four bytes as an integer value. The integer value is stored in the same format used for integer variables, that is 32-bit two's complement with the least significant byte first. Real values use six bytes, the first byte is always &FF and serves to identify the following five bytes as a real value. String values take two bytes more than the number of characters that compose the string. The first byte is always &00 to signify a string value, the second byte gives the length of the string. The actual characters that make up the string follow the 'length byte' and the only peculiarity is that they are stored in reverse order. For example, the statement:

PRINT# F,A

will write six bytes to a file, &FF followed by the five bytes of the internal representation of A. The statement:

PRINT# F,A%.

will write five bytes to a file, &40 followed by the four bytes that make up the internal representation of the integer stored in A%. As already explained, the number of bytes used by a PRINT# statement that writes a string to a file depends on the number of characters in the string. For exampie:

PRINT# F,"ABCDE"

writes seven bytes to the file; &00 to indicate that a string follows, &05 to indicate the number of characters in the string and then five bytes for the ASCII codes of "E", "D", "C", "B" and "A". (Notice that the characters of the string are stored in reverse order.)
In the same way that the PRINT # statement writes out the different types of data values using the formats described above, the INPUT# will read them back into variables of the correct type. It is possible to read in an integer value to a real variable and even a real value into an integer variable (any fractional part is truncated) but trying to read a string value into a numeric variable or a numeric value into a string variable will produce a type mismatch error. This means that you can use PRINT# to create files with a mixture of data types and to read such a file back using INPUT# you have to know in which order they come. This sounds like a difficult task but in practice the pattern of data types in a sequential data file is simple. For example, in the case of a name and telephone number file, it is obvious that the pattern is always a string for the name, followed by an integer for the telephone number. If you think in terms of the data type record, introduced in the previous chapter then file organisation is easy. Any collection of data that has to be stored in a file should be thought of and manipulated as a single record. For example, the data items NAME$ and NUMBER% should be thought of as a single data item, in other words as a record. Rather than reading (or writing) the name and the number parts as and when they are needed within the program they should always be handled together. That is, always read and write all the information that constitutes the record. The best way to ensure that only whole records are dealt with is to confine the reading and writing of records to a pair of procedures, PROCrec_get and PROCrec_put (say). Of course, this also fits in with the philosophy of modular programming!

Allowing for different file devices

The aim of the BBC Micro's file handling commands is to be as device independent as possible. And as far as sequential files are concerned this objective has been achieved fairly well. You can use the same commands to read and write a file that resides on disk or tape. However, there are times when it is useful to know what sort of device a file is stored on. In the earlier example where a file was written, then closed, then re-opened and read, it was important to inform the user that the writing of the file was complete only if the file was being stored on tape. The reason for this is simple enough; a cassette tape has to be manually rewound before the file can be reread but a disk will look after itself.
This sort of slight difference causes no problem unless you are trying to write programs that will work with any file device. In this case if you choose to issue the "Rewind tape" message and disks are being used your program will look silly, but if you don't issue the message and tape is being used your program will be unusable. There is a way to find out the sort of file device in use but it involves the use of the filing system for assembly language, a subject to be explained in more detail later. To find out what the current file device is the BASIC programmer can use the following function:

1000 DEF FNfi1e_system
1010 X%=&70
1020 Y%=0
1030 A%=0
1040 =USR(&FFDA) AND &FF

The value that this function returns determines which type of file device is in use according to the following table:

value
0 no filing system
1 1200 baud cassette
2 2300 baud cassette
3 ROM pack
4 disk
5 Econet
6 Teletext/Prestel

Using this function it is possible to discover the type of file device employed and to take appropriate action.

Random access files

Although the ideas and principles of random access files are device independent it is only practical to use them in conjunction with disk drives. As a result, this section and the commands that it describes apply to the BBC Micro only plus its disk drives and the ACORN disk filing system.
If you followed the explanation of the way that a sequential file works in terms of moving a pointer through the data items that make up the file, then random access files will seem easy. In the case of random access files the 'current position pointer' is available as a 'supplied variable' with the name:

PTR# channel

When a file is opened PTR# is set to zero, so pointing either at the first byte in the file or the position where the first byte will be written. As bytes are written to or read from the file PTR# is automatically updated just as in the case of a sequential file. The difference is that now you can set the pointer variable to read or write any byte in the file directly. For example, if you want to write the l00th byte to a file use:

PTR#F=100:BPUT#F,value

You can set PTR# to point to any byte of a file while writing, but you cannot go past the of the file while reading. So that you can avoid trying to read more of a random access file than there is, BBC BASIC provides the EXT# function which returns the number of bytes in the file.
The variable PTR# and the function EXT# are the only extras needed to manipulate random access files. However, being able to read or write any given byte in a file is only a little way along the road to useful rand om access of data. As in the case of sequential files the most useful way of organising data for storage in a random access file is to think in terms of records. Once again, only whole records should be read or written, preferably using procedures, one for writing and one for reading. To enable any record to be accessed in any order, it is necessary to associate a 'record number' with each one. If each record takes R bytes of storage then you should be able to see that to access record I, PTR# has to be set to I*R. For example, in the case of the telephone directory program used earlier, the name field of the record may be allocated 20 characters of storage and the telephone number field, being an integer, takes five bytes. Thus to write record I you would use:

1000 DEF PROCput_rec(I)
1010 PTR#F=I*27
1020 PRINT# F ,NAME$
1030 PTR#F=I*27+22
1040 PRINT#F,NUMBER%
1050 ENDPROC

where NAME$ contains the name and NUMBER% the telephone number. Line l0l0 calculates the position of record I and moves the file pointer to its start. Notice that the length of each record is 27 bytes (= 20 characters + I string code byte + I string length byte + 5 bytes for the integer). Line 1020 writes NAME$, line 1030 moves the pointer on to the start of the number field within the record and then line 1040 writes the telephone number to the file. Although fine 1020 automatically moves on PTR#, line 1030 is necessary to ensure that PTR# has been moved on to allow space for up to 20 characters. When using random access files it is important that fields within records always take the same amount of space -- if they don't take the same amount of space it is much more difficult to find a given record. To read a given record of the telephone directory file use:

2000 DEF PROCget_rec(I)
2010 PTR#F=I*27
2020 INPUT# F,NAME$
2030 PTR#F=I*27+22

2040 INPUT# F,NUMBER%
2050 ENDPROC

which works in the same way as PROCput_rec. There are various other ways of organising records within a random access file but they are all based upon the methods described for constructing new data types given in the previous chapter. For example, you should be able to recognise the fixed size record random access method described in this section as being essentially the same method used for organising a one-dimensional array with the record number corresponding to the array index! In the same way you can construct linked lists on disk by including within each record a pointer to the next record.
The BBC Micro's disk filing system software is unfortunately very primitive in the way that it allocates storage space. Disk storage is organised into 'sectors', each capable of holding 256 bytes. A sector represents the smallest amount of disk space that can be allocated. The problem with the BBC disk filing system is that a file has to occupy a block of sectors that are physically next to each other on the disk. When a new file is opened for writing sixty-four sectors are assigned for its use. If it doesn't use all sixty-four when the file is closed the unused sectors can be used by another file. If more than sixty-four sectors are required additional sectors can be acquired, but only if there are free sectors that immediately follow the initial sixty-four! The trouble is that although there may be more than enough free sectors on the disk to hold the entire file, if they are not immediately next to the sixty-four already allocated the program will crash with a 'Disk full' message. This is unlikely to happen if the file that needs extending is being stored on a newly formatted disk, but otherwise it is better to claim the amount of space required for a file as soon as it is opened. That is, if you know that you are going to want to create a random access file of 100 records, each 5 12 bytes long, you should open the file and immediately write 51200 bytes out to it. If this fails because there isn't enough space available as a single block of adjacent sectors you can either change disks or use the *COMPACT utility which moves existing files in an attempt to place all of the free sectors together at one place on the disk.

The OPENUP problem

So far it has been assumed that a random access file will be opened using OPENIN for reading and OPENOUT for writing. However there is another way of opening a file which means that it can be both written and read in the same program. The command:

variable = OPENUP(filename)

will open the file 'filename' for both reading and writing and will store its channel number in 'variable' for future reference. This sounds very useful but there are differences to be found in the way that all three OPEN commands have been implemented in early and late versions of BBC BASIC. BASIC I has only OPENOUT and OPENIN although OPENIN is implemented in such a way that it can be used to both read and write a random access file. That is, BASIC I OPENIN behaves like OPENUP is supposed to. However all three OPEN commands are implemented in BASIC II. The trouble is that programs written using BASIC I do not translate as you might expect to BASIC II. In particular any OPENIN commands that you might have used in a program will have mysteriously changed to OPENUP commands! Going the other way, that is taking a BASIC II program and running it under BASIC I, is even more of a problem because any OPENUP commands are translated to OPENIN commands and any OPENIN commands will cause errors! The reason for all this is that OPENIN in BASIC I seems to use the token for OPENUP, hence the mysterious translations.
There is no simple solution to this confusing problem. If you are writing programs using BASIC I, the best course of action is to use OPENOUT and OPENIN and the program should run under BASIC II If you are writing programs under BASIC II then use only OPENUP and your programs should run under BASIC I.

Virtual arrays

As already mentioned, the method used to construct random access files based on fixed size records is essentially the method used to construct arrays in RAM. This fact can be used to construct arrays that are stored on disk so-called virtual arrays. For example, if you want a virtual real array then use:

1000 DEF FNget_array(I)
1010 LOCAL DATA
1020 PTR#F=I*6
1030 INPUT# F,DATA
1040 =DATA

to access the Ith element stored in the virtual array corresponding to channel F and use:

2000 DEF PROCput_array(I,DATA)
2010 PTR#F=I*6
2020 PRINT#F,DATA
2030 ENDPROC

to store DATA in the Ith element. Two-dimensional arrays are just as easy; the only real change is that the expression in lines 1020 and 20l0 becomes:

PTR# F=I*6+N*J*6

to access the I,Jth element of the array.

Using files from assembler

The BBC Micro's filing system is almost as easy to use from 6502 assembler as from BASIC. There is a great similarity between the MOS routines used to manipulate files and the equivalent BASIC commands. For example, the routines OSBPUT and OSBGET will write and read a single byte in the same way that BPUT and BGET do. Table 6.1 gives details of the MOS routines corresponding to each of the BASIC file operations:

Table 6.1

BASIC MOS routine parameters
OPENIN OSFIND (&FFCE) A=&40, Y,X address of file name.
On return Y contains the channel number. Y=0 if OSFIND cannot open the file
OPENOUT OSFIND (&FFCE) As for OPENIN but A=&80.
OPENUP OSFIND (&FFCE) As for OPENIN but A=&C0.
CLOSE OSFIND (&FFCE) A=0, Y=channel number of file to be closed. (If Y=0 then all files are closed.)
BPUT OSBPUT (&FFD4) A=byte to be written, Y=channel number
BGET OSBGET (&FFD7) Y=channel number. On return A contains byte read from file. Carry flag=1 if an error has occurred in which case A contains an error code. (&FF is 'end of file')

There are other MOS routines concerned with file handling but the ones given in Table 6.1 are those most often used. For example, there is a MOS routine, OSFILE, that performs the same action as the BASIC commands SAVE and LOAD. There are also a number of routines that do not apply to files stored on cassette. In particular, the needs of random access disk files are catered for by OSARGS (&FFDA). On calling this routine the X register should contain the address of the start of four memory locations in page zero. These are used to hold the input value, or the result of calling OSARGS, in the usual four byte integer format. Calling OSARGS with a channel number in Y will read the file's current position pointer if A=0, write the current position pointer if A=1, and read the file's length if A=2. A call to OSARGS with A = &FF will ensure that any alterations made to the file are actually written out rather than just sitting in a buffer. OSARGS also has a number of other functions including returning the code of the currently active filing system. This was used in the function FN file system that can be used from BASIC to discover which type of file device is in use. There is nothing complicated about using these filing system routines as they provide the same set of operations as their equivalents in BASIC.

A disk sector editor

One of the most useful utilities that any disk user can possess is a sector editor. A sector editor will read in any sector of a disk and display it in hex or in ASCII characters and then allow you to write it back to disk after making any changes to the data that are necessary. This may sound like a difficult program but the disk filing system includes an extension to the MOS routine OSWORD to read or write a sector. Calling OSWORD with the A register set to &7F will read or write a sector according to the contents of a parameter block.

byte meaning
0 drive number
1-4 address of sector buffer
5 3
6 &53=read sector &4B=write sector
7 track number
8 sector number
9 &21

This has to be set up before entering OSWORD and its address stored in the X and Y registers.
Using this information a sector editor is easy to write:

10 REM SECTOR EDITOR

20 DIM SEC_BUF% 255

30 DIM INS_BLK% 50

40 MODE 4

60 PROCparm_get

80 PROCsect_op

90 PROCsect_print

100 GOTO 60

1000 DEF PROCparm_get

1010 PRINT TAB(0,28);

1080 INPUT "Action Read/Write/Modify",A$

1090 IF LEFT$(A$,1)<>"R" AND

LEFT$(A$,1)<>"W" AND

LEFT$(A$,1)<>"M" THEN GOTO 1010

1100 IF LEFT$(A$,1)="R" THEN COM%=&53

1110 IF LEFT$(A$,1)="W" THEN COM%=&4B

1120 IF LEFT$(A$,1)="M" THEN COM%=0

1125 IF COM%=0 THEN ENDPROC

1130 PRINT TAB(0,29);:

INPUT "Drive=",D%

1140 IF D%<0 OR D%>3 THEN GOTO 1130

1150 PRINT TAB(0,30);:

INPUT "Track/Sector (TTSS)",T$

1160 IF LEN(T$)<4 THEN GOTO 1140

1170 T%=FNtrack(T$)

1180 S%=FNsector(T$)

1190 ENDPROC

2000 DEF PROCsect_op

2010 IF COM%=0 THEN PROCmodify:ENDPROC

2020 INS_BLK%?0=D%

2030 INS_BLK%!1=SEC_BUF%

2040 INS_BLK%?5=3

2050 INS_BLK%?6=COM%

2060 INS_BLK%?7=T%

2070 INS_BLK%?8=S%

2080 INS_BLK%?9=&21

2090 A%=&7F

2100 X%=INS_BLK% MOD 256

2110 Y%=INS_BLK% DIV 256

2120 CALL &FFF1

2130 ENDPROC

3000 DEF PROCsect_print

3010 LOCAL I

3020 PRINT TAB(0,0);

3025 FOR I=0 TO 15

3026 PRINT TAB(3+I*2 MOD 32);~I;

3027 NEXT I

3028 PRINT

3030 FOR I=0 TO 255

3035 IF I=(I DIV 16)*16 THEN

PRINT TAB(1);~(I DIV 16);

3040 PRINT TAB(3+(I*2 MOD 32));

~(SEC_BUF%?I);

3050 NEXT I

3060 PRINT:PRINT

3070 FOR I=0 TO 255

3080 IF SEC_BUF%?I<32 OR SEC_BUF%?I>127

THEN PRINT TAB(I MOD 32);"-"; ELSE

TAB(I MOD 32);CHR$(SEC_BUF%?I);

3090 NEXT I

3100 ENDPROC

4000 DEF PROCmodify

4010 LOCAL A$,I,D

4015 PRINT TAB(0,29);

4020 INPUT "Which byte row/col",A$

4030 I=EVAL("&"+A$)

4040 IF I<0 OR I>255 THEN GOTO 4010

4050 PRINT TAB(0,30);

4060 INPUT "New value=",A$

4070 D=EVAL("&"+A$)

4075 IF D<0 OR D>255 THEN GOTO 4050

4080 SEC_BUF%?I=D

4090 CLS

4100 ENDPROC

9000 DEF FNtrack(T$)

9010 =EVAL("&"+MID$(T$,1,2))

9020 DEF FNsector(T$)

9030 =EVAL("&"+MID$(T$,3))

The main program consists of the usual calls to procedures that do the actual work of reading and writing sectors. PROCparm_get asks a number of questions about what tasks the program should perform. Answering the first question with either 'R' for 'Read a sector' or 'W' for 'Write a sector' causes the program to prompt for the drive number, track and sector number involved. Answering the first question with an 'M' for 'Modify' causes the buffer editor to be called so that a single byte can be changed. PROCsect_op actually performs the sector read and write operation or it calls PROCmodify so that the contents of the sector buffer SEC BUF% can be changed. Finally PROCsect_print prints the current contents of the sector buffer in hex and, where possible, in ASCII. Notice that the track and sector number has to be typed in as a single hex number, that is &F05 specifies track F sector 5. Also notice that while this program will allow a user to change any given sector it doesn't check to see if the changes are in any way sensible. For example, it doesn't give an error message if you try to access track 79 on a 40 track disk! A sector editor is extremely useful in recovering data from crashed disks, etc., but if it isn't used with care it can itself be the cause of crashed disks!


Next     Top