The Confusing Catalog
Jim Butterfield, Associate Editor
Have you ever wanted to have a program gain control of the disk catalog? There are a number of ways to use directory information, but getting hold of it is not as simple as it might seem at first glance.
On 4.0 Commodore machines, you just type CATALOG or DIRECTORY. On earlier machines, you must LOAD "$",8 and then LIST. Either way, you get a directory with your disk header, information on the programs, and the number of bytes free. Very handy indeed.
Here's the problem: you would like your program to be able to read a directory. It seems simple: just OPEN it as a file and bring in the items. Unfortunately, it doesn't work that way.
When you command LOAD "$",8 you are bringing in a directory with a LOAD command; it arrives in a certain format. If you OPEN 1,8,2,"$" within your program, you'll get an entirely different format. Why?
When you say LOAD, the disk manufactures a directory that imitates a BASIC program. After all, the next thing you'll say is LIST, and the only thing that can be listed is BASIC. If you say OPEN, however, the disk will give you its directory, in binary, just as it is stored on the disk surface. That seems to be a little better — until you realize that BASIC has a devil of a time understanding binary.
You can do an OPEN and get the "imitation program." The trick is to use secondary address 0 — usually reserved for LOADing.
Either way, you get binary. You'll need to translate it and interpret it; and you'll need to cope with that annoying BASIC glitch, inputting a CHR$(0). Whenever BASIC GETs a CHR$(0), it changes it to a null string (" "), and you'll need to detect this and change it back.
The coding for this is fairly easy. After we get a character with GET A$, we may take its binary value with A = ASC(A$) — except that the null string won't work right. So, we say, A = ASC (A$ + CHR$(0)) and everything works out.
This is the easiest and most standard way of obtaining directory information; it works the same way with all Commodore disks. To understand it, we must see how a BASIC line is constructed:
|First two bytes:||forward chain or zero (dummy on directory)|
|Next two bytes:||binary number|
|Then:||text of line|
|Ending with:||binary zero|
So let's write it:
100 OPEN l,8,0,"$0"
Let's get the directory for drive 0.
110 N$ = CHR$(0)
Here's our null string replacement.
Skip the "Load Address" at file start.
Skip the forward chain, except:
210 IF A$ = " " GOTO 400
Zero chain means the end.
Get the binary number.
230 PRINT ASC(A$ + N$) + ASC(B$ + N$)*256;
Print "number of blocks."
Let's get text.
310 IF A$ = " " THEN PRINT : GOTO 200
End of this line: go back;
320 PRINT A$;
Print one character;
330 GOTO 300
Get some more.
This program prints the directory. Big deal: you could do that anyway. But since it's a program, you can change it to do whatever functions you need. For example, you could dig into the text part in more detail, extracting the program name and type; that way, your program would know if a given data file were on the disk.
It's handy to be able to check how many blocks are free on the disk. Our program already does this: the last number that line 230 calculates will be the blocks-free value. You can abbreviate this procedure by making the program skip all the file names. Change the OPEN statement to read:
100 OPEN l,8,0,"$0:S%Q"
Now, the program will catalog only those programs whose name happens to be exactly S%Q. Chances are you won't have many of these. Your directory is now shortened down to the header line and the BLOCKS FREE line. Let's telescope our program into a simple block-free checker:
100 OPEN 1, 8, 0, "$0 : E7!N"
Another unlikely name
110 N$ = CHR$(0) 200 GET#1, A$, A$, A$, A$, A$, A$
Throw away load address, link, number.
210 GET#1, A$ : IF A$<>" "GOTO 210
Throw away the header line
220 GET#1, A$, A$, A$, B$
Throw away the link, get the number.
230 F = ASC(A$ + N$) + ASC(B$ + N$)*256
Here's our block-free count.
400 CLOSE 1 410 PRINT F
We've only scratched the surface. Try your hand at programming some directory search function of your choice.
You can get more information from a bit-image directory than from a BASIC-imitator. For example, you can read the length parameter of relative files, see deleted files, and view file track and sector values.
But this comes with considerable difficulty. You might get any one of several different formats, depending on the disk. We won't do the whole job here: you can chase after some of the details for yourself.
100 OPEN 1, 8, 15, "10" : CLOSE 1
We must initialize for this one.
110 OPEN 1, 8, 2, "$0"
Here comes the bit directory.
120 N$ = CHR$(0) 130 GET#1, A$
The disk will identify itself.
140 A = ASC(A$ + N$)
Here's the identity.
150 IF A = 67 THEN PRINT "†8050 |" 160 IF A = 65 THEN PRINT "†4040 |" 170 IF A = 1 THEN PRINT "†2040 |"
Just to prove we identified it.
8250's will give trouble here.
200 FOR J = 1 TO 253 210 GET #1, A$ 220 NEXT J
Skip the (bit) BAM.
230 IF A <> 67 GOTO 300 240 FOR J = 1 to 254*2 250 GET#1, A$ 260 NEXT J
The 8050 has a big BAM to skip.
300 FOR J = 1 TO 8
Eight files per block.
310 GET#1, F$, T$, S$
File type, Track, Sector.
320 F = ASC(F$ + N$) 330 P$ = " " : FOR K = 1 TO 16
Get 16-character name.
340 GET#1, XS : P$ = P$ + X$ 350 NEXT K 360 FOR K = 1 TO 9 370 GET#1, X$ 380 NEXT K
There's useful stuff here; we'll skip it.
390 GET#1, L1$, L2$
400 IF J<8 THEN GET #1, X$, X$
Weird; 254 bytes/8 leaves us two bytes short.
410 SW = ST
To allow us to test end-of-directory.
420 IF F<129 OR F>132 GOTO 480
Not a real file.
430 PRINT P$;ASC(L1$ + N$) + ASC(L2$ + N$)*256
Name and length.
480 NEXT J 500 IF SW = 0 GOTO 300 900 CLOSE 1
This isn't a program — it's a research outline. Yes, you can go in there and drag out the BAM. Yes, you can dig useful data out of the stuff we skipped in lines 360-380. Check your disk manual for details.
It's not easy either way. The "imitation BASIC" is the shortest and works on all disks: use it when you can. But if you need the extra power of the bit map, don't hesitate to go for it.