home *** CD-ROM | disk | FTP | other *** search
-
- Confusion is Relative
- by deb! Christensen
-
- (C)opyright 1986 all rights reserved
-
- The Eighth Wonder of the World should be Commodore Relative
- files. The wonder is that anyone can make sense from the
- documentation which CBM supplies for using these files in 2.0 BASIC!
- A highly powerful random access file type, Relative files are
- worth every ounce of perspiration you put into them. Allowing the
- programmer to define fields within each record and retrieve
- information from any given record number this file type is easily
- accessible from BASIC and can substantially speed up editing tasks
- to disk records. If you'll follow closely here, I'll try to remove
- the guesswork as well as the mystery from Commodore Relative
- Records.
- A warning first: Do not try to work with a Relative file until
- you are thoroughly familiar with SEQuential files, BASIC disk
- programming and using the drive command channel! Relative files
- depend heavily upon programming and planning. All good Data Base
- programs use this file type because any piece of information can be
- easily accessed in the file without having to read all the
- information which precedes it. That information can also also be
- changed and written back into the file without even touching another
- file entry! Anyone who has ever had to sift through a very long
- SEQuential file to find one piece of information, update the data
- and wait for the file to write back to the disk should very quickly
- see the advantages that this kind of random access will give you.
- There is a catch to this, though. The DOS must know where to
- go to find the information you want to retrieve. With careful
- planning and programming, your software can keep track of this for
- you. Very literally, every position within the file is available
- for instant access. Poor planning will result in a tangled web of
- confusion and possibly a whole stack of disk errors! To avoid
- chaos, be sure to define the task on paper before even touching your
- computer. I know this probably sounds like I'm about to dish out a
- Flowcharting 101 course, but there is no substitute for crystalizing
- a program idea in an outline before undertaking the actual BASIC
- coding needed.
- First, define the purpose of the program and any special
- conditions which need to be incorporated. My example here is going
- to be a file which holds questions and the correct answers. The
- program will need to be able to read the question from the file,
- print it to the screen, ask the user for the correct answer and then
- compare it to the correct answer stored in the file. The greatest
- advantage with a Relative file is that the access time for the first
- piece of information should be about the same as the last entry in
- the file! This is very important when the free memory of your
- computer is nearly full and a large amount of information must be on
- hand for easy access.
- There are several steps involved in using a Relative file. The
- file must be created first. That sounds very obvious, but because
- of the unique way a relative file is used, you can OPEN an existing
- file on the disk just once, and either Read from it or Write to it!
- Consequently, there is a specific way to create that Relative file
- on the disk for the first time. After the initial creation of the
- file, anytime you OPEN a relative file it is available for Reading
- or Writing when the correct commands have been issued. You can
- easily replace just one piece of information in the file without
- disturbing or having to re-write the rest of the file through
- careful positioning of the read-write head. Handy sounding, isn't
- it?
- Every Relative file is divided into what are known as Records.
- To understand what a Record is, imagine a file box full of 3 x 5
- index cards. This is your address file, and each card contains only
- one name and address. The entire box is referred to as the File and
- each individual card is one Record within the file. Each card, or
- Record is the same size, 3" x 5", or it wouldn't fit in the file,
- would it? Written on each Record, or card, is a name on the top
- line, followed by the address, city, etc. These separate lines are
- called Fields and are the individual entries you make in each
- Record. So, each individual Record can contain several Fields. A
- typical address Record looks like this:
-
- John Jones
- 123 Main St.
- Anywhere, CA 90000
- (209) 555-1234
-
-
- The four Fields from the Record above would be:
- Name
- Address
- City, State, ZIP
- Phone
- When a Relative file is created, the size of each record is
- defined and cannot be changed. The size of the record is called the
- record length and is the combined total of maximum number of
- characters from each field for that record entry. This is a
- critical part of planning. If a record is not large enough, part of
- the data being written will be dropped, and if there is too much
- room, you will just be wasting precious disk storage space. In the
- example for the question and answer file, I am going to allow 89
- characters for the question and 24 characters for the answer.
- Each record within a Relative file can contain up to 254
- characters. There will only be the two fields in each record of
- this file, so just add up the maximum characters from each
- anticipated field to find the total length for each record. That
- gives us a record length of 113 characters, well under the 254
- maximum. It is a very good idea to allow an extra character in each
- field to separate the information. I use carriage returns to mark
- the end of each field, which store as a 13 or CHR$(13) in the file.
- So, add two more characters to the record length, and that is all
- the information needed to create the file.
- The file can be created in the direct mode or it can be created
- with a program. I'm going to use program lines here. You'll find a
- small program in the appendix which can be used to create any
- Relative file. When the DOS is asked to create a new Relative file
- a Side Sector is created for that new file. In the Side Sector the
- DOS stores all the pointers to the physical location of each record
- number. You'll probably never even know where that Side Sector is,
- but it will be working hard to make sure that each of the designated
- records are available to the program by merely calling the record
- number. The Side Sector is an operation of the DOS. Neither the
- program or the programmer has to keep track of it!
- Now, lets name the file and put it on the disk. The number of
- characters per record will follow the file name as a CHR$ value.
- You cannot create a relative file without it! This file will be
- named "TEST".
-
- 10 REM CREATE RELATIVE FILE
- 20 OPEN3,8,3,"TEST,L,"+CHR$(115)
- Make sure that you type it in exactly as specified above. The
- most common errors include leaving out the comma right after the
- secondary address and leaving out that second comma within the
- quotes, following the "L". And yes, the plus sign is part of the
- syntax also. Here is the breakdown of the OPEN statement:
-
- OPEN file#,device#,channel#,"name,L,"+CHR$(length of
- record)
-
- The file should be CLOSEd next.
-
- 30 CLOSE3
-
- RUN the small program and then take a look at the directory.
- (You may want to SAVE the program to a disk first, though.) You can
- see the REL for the file type on the directory entry for TEST. It
- is done, and the Relative file named TEST now resides on the disk.
- I am sure it will not surprise you, but Relative files do have
- some of their own rules. The SAVE with Replace option does not work
- with a Relative file. If you need to get rid of a Relative file,
- you can use the SCRATCH command, and start all over again with a new
- file, or you could erase the whole disk with a NEW command. Some
- backup programs will not copy a Relative file. Don't panic, I know
- that "1541 Copy" will do it for sure. This is a public domain
- program and is available from most user groups. Remember those Side
- Sectors? The DOS uses them to keep track of the file. These Side
- Sectors are automatically updated by the DOS and you do not see
- them. Although they are allocated in the BAM the Side Sector does
- not even have a separate entry in the Directory. It is considered
- part of the overhead and housekeeping space taken up by the Relative
- file itself. Many copy and backup programs do not look for them,
- and that is why Relative files may not copy. Because of these Side
- Sectors, the total storage on the diskette is not quite as large
- when a Relative file format is being used.
- The relative file you just created is now ready for either
- Reading or Writing anytime is is OPEN. Storing and retrieving from
- this file will require several pieces of information. When you
- created the Relative file you told the DOS how many characters would
- be in each record. That relative file will now be sectioned off
- into 115 character records. Each record will be exactly the same
- size as every other record in the file, just like you had used the
- same cookie cutter on a large piece of dough! Once you tell the DOS
- what size of "cookie cutter" to use, each record which is added to
- this file will always be 115 characters long and will be ready to
- accept the data which you need stored in it. The DOS and the Side
- Sectors will keep track of the physiscal location of each record. To
- access the information in any record, only the record number is
- needed.
- A relative file has no pre-determined size and will expand
- dynamically as you write to the new record numbers. This will take
- a bit of time if the DOS must find space on the diskette, allocate
- the sector in the BAM and update the Side Sector, as well as write
- to the file itself. This whole process can be speeded up
- considerably if the maximum number of records can be anticipated.
- When the last record number is known, the Relative file can be
- forced to write to that last record, thereby also writing all the
- records from the beginning of the file to that last record number
- and allocating all that space on the disk. Although this will take
- a little time at the beginning, it is a good habit.
- When you need to write to any record on a relative file,
- whether it is a new record or an old one, you need to send a
- position command to the drive. This will give the DOS the neccesary
- information to compute the exact position for the read/write head
- and allow you to store the information where you can easily retrieve
- it later with the same positioning command. To send this
- positioning command to the drive you will need to know the record
- number. So, pulling a number out of the hat, the maximum record
- number in this file will be 335. By positioning the drive to record
- number 335, the file can be forced to write that last record. All
- positioning commands must be sent to the drive in the form of a
- CHR$. The maximum allowable number which will fit into a CHR$ is
- 255. This represents the 8-bit limit of one byte, with all bits on
- (on means each bit is set to 1, totalling 255 in decimal). It is
- very common to need more than just 255 records for one file, so the
- record number is sent to the drive in the standard low byte-high
- byte format of 6502 machine code.
- This sounds overwhelming at first, but is really quite simple
- to calculate. The high byte is calculated first by dividing the
- record number by 256. The low byte is then derived from the
- remainder. The low byte for 335 is 79 and the high byte is 1. The
- BASIC syntax for this formula looks like this:
-
- N=335:HI=INT(N/256):LO=N-(HI*256)
-
- You can double check the formula results by multiplying the high
- byte times 256 and adding the low byte. Here are some more examples
- for you. Please notice that the low byte will always preceed the
- high byte.
-
- low byte high byte decimal number
- 0 1 = 256
- 1 2 = 513
- 36 0 = 36
- 232 4 = 1000
-
- Here is what the syntax looks like for a position command:
-
- PRINT#15,"P"CHR$(CHANNEL#)CHR$(LO)CHR$(HI)
-
- Obviously the command channel must be open as well as the
- relative file. To accomplish this use the following line of BASIC:
-
- 40 OPEN15,8,15:OPEN3,8,3,"TEST"
- When accessing a relative file it is always a good idea to check the
- error status! Use a subroutine to do this.
- 42 GOSUB500
- 500 INPUT#15,E,ER$,T,S:IFE<>0THEN?E;ER$;T;S:STOP
- 510 RETURN
- Before accessing the file itself, the position command must be
- issued first. This command will always be sent to the command
- channel, #15.
- 50 PRINT#15,"P"CHR$(3)CHR$(79)CHR$(1)
- The first CHR$ value is the channel number of the Relative
- file, which was specified as the secondary address in the OPEN
- command. The next value is the low byte of the record number,
- followed by the high byte of the record number. Again, check the
- disk status after using this command.
- 55 GOSUB500
- A word of caution! The record number you are writing does not
- exist yet, and it will return error number 50, indicating that the
- record does not exist. Of course this is no surprise, is it? That
- is the whole purpose of doing this, to create the record. When the
- program stops after this error message, just type in :
- CONT
- The program will continue to execute from the place it was STOPped
- after this command. This number 50 error message does not matter
- under these circumstances, but it must be read and cleared from the
- disk status. At the end of this chapter is a LISTing of the final
- version of this program which includes one technique for handling a
- RECORD NOT PRESENT error without STOPping the program. The disk
- drive is now in position for you to write to the new record number
- 335 in your relative file! Accomplish this with the following line
- of programming:
- 60 PRINT#3,"This is the Last Record!"
- 65 GOSUB500
- The read/write head was already positioned through the command
- channel in line 50, and the actual information being written into
- the file is written to the File Number of the Relative file, just
- like you would send it to a SEQuential file!
- CLOSE the files and make everything nice and tidy. Many times
- the DOS buffers still hold some of the information which you sent to
- the file. If the file is not CLOSED you will loose important DATA
- and pointers. The warning to always CLOSE a file still applies to a
- Relative file.
- 70 CLOSE3:CLOSE15
- Just as in other file operations, the command channel must be
- the first one OPENed and the last one CLOSEd. Closing the command
- channel will automatically close all other files. Be very careful!
- Although you can have as many Relative files on one disk as
- will fit, only one Relative file can be OPENed at one time. A
- Relative file takes 2 of the DOS RAM buffers, one for the file and
- the extra one for the Side Sectors. Trying to OPEN a second
- Relative file will return a NO CHANNEL error message from the drive.
- I know you have read that 2 Relative files can be OPEN
- simultaneously, but don't believe it! Commodore forgot to update
- the manuals and although prior IEEE drives have more channels to
- work with, a 1541 has a total of only 3 channels you can access with
- SEQuential or Relative files. Since the Relative file uses 2 of
- these channels, there is still one channel free, and a SEQuential
- file can be OPENed along with the Relative one.
- Be sure to SAVE this program. After doing that, look at the
- directory on your disk. You should see a remarkable difference in
- the number of blocks which the Relative file called TEST uses! By
- writing to that last record, you not only created all the
- intermediate records, but you also forced the DOS to allocate the
- sectors on the disk, saving the space from being used for something
- else. This is really the best technique to use. Can you imagine
- being in the middle of a program and finding out that there is no
- more room on the disk to finish writing your data? That is a
- situation to be avoided at all costs!
- Even though the only record which contains any information is
- number 335, record numbers 1 through 334 exist on your disk. The
- first data byte in each empty record will contain a chr$(255) to
- indicate it is empty, and the rest of the 115 bytes in the record
- contain nulls, or binary zeros. When you partially fill a record,
- the unused bytes in the record will remain nulls. It is important
- to realize that any subsequent entries to that same record which are
- shorter than the first entry will not result in the rest of the
- record being filled with nulls again. All bytes after the new data
- is written will remain untouched. This is why it is so very
- important to use a field separator in a relative file. Each record
- can have extra space in it, which may be filled with either nulls or
- old data. As long as the program knows what the field separator is,
- this old data will can be ignored.
- This is what the Relative file looks like now:
-
- Records 1 to 334:
- Byte 1 Bytes 2-115
- ASCII 255 nulls-CHR$(0)
-
- Record # 335:
- Bytes 1-24 contain the ASCII for these characters:
-
- -----------------------------------------------
- T h i s i s t h e L a s t R e c o r d !
- -----------------------------------------------
-
- Byte 25 Bytes 26-115
- CHR$(13) nulls-
- <RETURN> CHR$(0)
-
- Program Listings
-
- CREATE THE FILE!
-
-
-
- 10 rem create relative file chapter 10
- 20 open3,8,3,"test,l,"+chr$(115)
- 30 close3
- 40 open15,8,15:open3,8,3,"test"
- 45 input"highest record number";n
- 46 hi=int(n/256):lo=n-(hi*256)
- 50 print#15,"p"chr$(3)chr$(lo)chr$(hi)chr$(1)
- 55 gosub500:ife<>0thenprinte;er$:ife<>50thengosub510
- 60 print#3,"The last record is number";n
- 65 gosub500:ife<>0thenprinte;er$:ife<>50thengosub510
- 70 print"a successful file write!":close3:close15
- 100 print" Now, lets see if it is where it is *supposed* to be!"
- 110 open15,8,15:open2,8,2,"test"
- 115 gosub500:ife<>0thengosub510
- 120 print#15,"p"chr$(2)chr$(lo)chr$(hi)chr$(1):gosub500:ife<>0thengosub510
- 125 input#2,a$:printa$:gosub500:ife<>0thengosub510
- 130 close2:close15:end
- 499 end
- 500 input#15,e,er$,t,s:return
- 510 printe,er$,t,s:stop
- 515 return
- ready.
-
-
- WRITE THE RECORD
-
-
-
- 10 rem test file 115 characters
- 20 rem per record
- 30 :
- 39 rem *** open files ***
- 40 open15,8,15:open3,8,3,"test":gosub500:ife<>0thengosub510
- 50 rem position 1 has question (field 1)
- 60 rem position 90 has answer (field 2)
- 90 rem *** write file ***
- 100 fori=1to11:rem i=record number
- 102 :
- 105 rem channel 3, recordi, position 1
- 110 print#15,"p"chr$(3)chr$(i)chr$(0)chr$(1):gosub500:ife<>0thengosub510
- 112 :
- 115 rem put question in file 3 first position
- 120 reada$:print#3,a$:gosub500:ife<>0thengosub510
- 122 :
- 125 rem position 90 in record i
- 130 print#15,"p"chr$(3)chr$(i)chr$(0)chr$(90):gosub500:ife<>0thengosub510
- 132 :
- 135 rem store answer at position 90
- 140 reada$:print#3,a$:gosub500:ife<>0thengosub510
- 150 nexti
- 160 close3:close15:end
- 290 end
- 299 rem *** contents for file ***
- 300 data "How many tracks on a 1541 formatted diskette?","35"
- 310 data "On which track does the Directory sit?","18"
- 320 data "What is the maximum number of SEQuential Files OPEN at the same time?"
- 325 data "3"
- 330 data "What is the maximum number of Relative files OPEN at once?","1"
- 340 data "Can 1 Relative file and 1 SEQuential file be used simultaneously?"
- 345 data "yes"
- 350 data "How do you get rid of a file marked with an asterisk?","validate"
- 360 data "What is the channel number for the command channel?","15"
- 370 data "Can channel 1 or 0 be used for secondary address in OPEN statement?"
- 375 data "no"
- 380 data "What is the correct abbreviation for the BASIC command,PRINT#?"
- 385 data "pR"
- 390 data "Can you read or write to the same OPEN Relative file?","yes"
- 395 data "Can an existing Relative file get larger without making a new one?"
- 396 data "yes"
- 499 rem *** read error ***
- 500 input#15,e,er$,t,s:return
- 510 printe,er$,t,s:stop
- 515 return
-
-
-
- RANDOMLY READ FILE
-
-
- 6 open15,8,15:open3,8,3,"test":gosub500:ife<>0thengosub510
- 10 goto100:rem skip subs
- 20 x$="":a$=""
- 21 get#3,x$:ifx$=chr$(13)thenreturn
- 22 printx$;:a$=a$+x$:goto21
- 30 x$="":a$=""
- 31 get#3,x$:ifx$=chr$(13)thenreturn
- 32 a$=a$+x$:goto31
- 40 x$="":g$=""
- 41 getx$:ifx$=""then41
- 42 ifx$=chr$(13)thenprint:goto50
- 44 ifx$=chr$(20)thenprint"