The BBC and Master Computer Public Domain Library
Extended DFS Catalogue
Return To On Line Magazine
TBI-174 Xcat Extended Catalogue
An extended disc catalogue for DFS
By: Jon Hogeslag
Ever wondered what was behind the file names on your discs?
Here is the answer: a program that allows you to add a description against every file name.
Not bothered by any knowledge about disc operation or assembler, I formulated some wishes about what the program(s) had to do:
1. Not too complicated - I can imagine that it would be possible to the change the DFS catalogue for that purpose - only not by me!
2. Hence create a separate file on disc to contain the descriptions.
3. The (BASIC) maintenance program should be able to read the DFS catalogue so as to keep my own extended catalogue up to date.
4. Display no wider then 80 characters - at least it's MY catalogue!
5. A machine language program to read and display my catalogue - in other words my first steps on the wobbly assembler path.
1., 2. and 4. looked like no problem to me. 3. was a matter of carefully reading the Advanced Disc User Guide.
The DFS catalogue
I assume that most people know that a DFS single sided single density 100k disc is divided into tracks and sectors (I restrict myself to 40 track discs, although with 80 track discs the catalogue is in exactly the same place).
On track 0 and sectors 0 and 1 the Acorn DFS records which files are where and what their size is. And some other stuff like disc title, number of disc accesses and the lock status of every file. This area of the disc is called the catalogue.
For this application we are interested in the file names and the lock status. The latter may sound strange, but in the catalogue a file being locked or not is indicated by the top bit of the directory character. To find the correct directory we have to reset this top bit for locked files.
Catalogue sector 0
Bytes 0 - 7 contain the first 8 characters of the disc title.
Bytes 8 - 4 contain the file name of the first file.
Byte 15 contains the directory character of the first file.
The remaining 240 bytes contain the same information for the other 30 files.
Catalogue sector 1
Bytes 0 - 3 contain the last 4 characters of the disc title.
Byte 4 contains the number of disc accesses.
Byte 5 contains the number of files (times 8!).
Byte 6 is subdivided into bits and amongst others contains the boot-up option (*OPT 4).
Byte 7 cooperates with byte 6, but that is not of any importance here.
The remaining 248 bytes contain the load and execute addresses and the
file length for every other file.
Reading the catalogue
This is not possible with a standard BASIC statement, so we'll have to use a machine language routine instead. Fortunately the OS ROM provides one: OSWORD. One of the functions of OSWORD (&7F), executes 8271 FDC (the disc controller) commands. One of these command (&53) is able to read several sectors from disc in one go.
Exactly what we need! Communication with OSWORD goes through two memory areas. The first area needs the instructions for OSWORD. The second area is a buffer area which will contain the results of the OSWORD call.
What does the parameter area look like in practice?
First we need to reserve some space. The OSWORD &7F commands have different numbers of parameters, that dictate the length of the parameter area. Command &53 has 3 parameters and we'll see that the parameter area is then 10 bytes long. (Maximum size for 5 parameters is 13 bytes). Reserving space is done in BASIC with the DIM statement, i.e.: DIM param 10. The layout is as in figure 1:
byte 0 drive number 0-3
1-4 address of the buffer area (LSB-MSB)
5 number of parameters for this command = 3
6 command = &53
7 parameter 1 = start track number = 0
8 parameter 2 = start sector number = 0
9 parameter 3 = number of sectors to read =&22
10 error code
Byte 9 needs some explanation. It specifies the sector size and how many sectors to read. the first 3 bits specify the sector size. For the Acorn DFS this is 001
corresponding with 256 bytes. The last 5 bits specify the number of sectors we want to read, i.e. 00010 or 2. Together 00100010 or hex &22. In byte 10 OSWORD reports success of the read operation.
For the buffer area we need to reserve space for 2 sectors of 256 bytes each, i.e. 512 bytes, so: DIM buffer 511. The complete call to OSWORD looks like in figure 2:
?param = drive
param!1 = buffer (called "cat" in the Basic program)
param?5 = 3
param?6 = &53 = OSWORD command
param?7 = 0
param?8 = 0
param?9 = &22
A% = &7F = OSWORD function
X% = param MOD 256 = LSB of parameter area
Y% = param DIV 256 = MSB of parameter area
CALL = &FFF1 = OSWORD
Obtaining the catalogue information
Using the above catalogue specification we can extract the information we want as follows:
--number of files = buffer?&105 DIV 8
--file name n starts at buffer address + 8 * n or: start = buffer + 8 * n
--directory character n starts at start?7
--if the directory character is larger than &7F (=the ASCII range) then we can strip the top bit by an exclusive OR &80.
Creation and maintenance of the Extended Catalogue
Creating the Extended Catalogue consists of reading the DFS catalogue as described above and writing it back as a sequential file called X.CAT. The record created is 82 bytes long i.e. 2 bytes length file name, 9 bytes file name (i.e. 1 byte directory character, 1 byte "." and 7 bytes file name), 2 bytes length of text and 69 bytes of text.
The file is always 31 records long no matter the actual number of files on the disc (to prevent "disc full").
Every time the maintenance program is started, both the DFS catalogue and the file X.CAT are read in. The sorted catalogue is then compared with the Extended Catalogue to see whether files were added or removed. If a file exists in the DFS catalogue but not in the Extended Catalogue, then a file was added on disc and in memory a record will be added with an empty description. The other way around, if a file is removed from disc, the corresponding record in the Extended Catalogue will not be stored in memory. To be able to edit the descriptions, the old text is shown and a blanc line so that by using the edit keys a new text can be constructed. At the end of the program, from memory an updated X.CAT file will be re-written..
I won't go into the BASIC program details, because it's rather straight forward I guess (every programmer says that about his own programs J ) Only the part where the DFS catalogue is compared with the Extended Catalogue is a bit messy because of the use of
GOTOs due to the lack of a CASE OF construct in BBC BASIC. As sort I use a simple bubble sort because in my opinion only 31 records don't justify a more sophisticated sort.
Using the BASIC program B.XCAT
Start the program with CHAIN "B.XCAT". After determining the active drive first the DFS catalogue is read in and subsequently the main menu appears.
C = Create Extended Catalogue
Creates the initial X.CAT file.
D = Display Extended Catalogue
Shows all file names with their respective text.
M = Modify Extended Catalogue
All files are assigned a number. By typing the file's number the old text is displayed.
By using the edit keys it is possible to change the text.
S = Switch disks
First the Extended Catalogue currently in memory is re-written to disc and subsequently you are asked to insert the next disc into the drive.
E = End program
The Extended Catalogue in memory is re-written and the program ends.
Because of the modular construction of the program it must be easy to write a routine of your own to for instance print the Extended Catalogue.
Machine language program
It was my wish to have display of the Extended Catalogue executed by a machine language program, so that a BASIC program in memory would remain un-effected. In other words an *XCAT command more or less analogue to the *CAT command. Furthermore it would be nice if the program would a maximum of 256 bytes in size. That way enabling to fit the program in an unused page (i.e. the character expansion space at
&C00). This application seemed ideal to me start assembler programming as it's function are very straight forward:
1. Open the X.CAT file
2. Print error message if not found and return to BASIC
3. Read a record
4. Stop the program if there is no meaningful information in the record (by less than 31 files the file name will be blank), in other words go to 7.
5. Print the record
6. If not EOF, go to 3.
7. Close the file.
I know something about programming and a little bit about machine language, but how do you open a file or how do you determine EOF? The Advanced Disk User Guide knows it all! So time to study.
A file is opened by calling OSFIND (&FFCE). The BASIC OPENxxx en CLOSE# commands use the same call. OSFIND expects the accumulator A to contain a command in this case &40 = open for input, and in the X- en Y-registers the address of a string containing die the file name (usual order X = LSB and Y = MSB). After execution of the call the accumulator A contains the channel assigned or 0 if the file could not be found. Figure 3 has the assembler statements:
\ osfind area
.filename EQUS "X.CAT" \ file name
EQUB &0D \ close string with CR
.fileaddr EQUW filename \ pointer to string
.filechan EQUB 0 \ store channel number here
LDA #&40 \ open for input
LDX fileaddr \ point X and Y to file name
LDY fileaddr + 1
JSR osfind \ open file
As we saw earlier, if a file is not found, OSFIND puts a 0 in the accumulator. One way to produce an (error) message from machine language is by a BRK instruction. This way is only convenient if after the message the program does not need to be continued,
after a control returns to BASIC. De message must follow immediately after the BRK instruction and in a defined layout:
1 byte error number (to be assigned by yourself), a string with the message en 1 byte hex 00 to close. Figure 4 has the assembler statements:
JSR osfind \ open file
BNE openok \ file found because channel <> 0
BRK \ file not found - break
EQUB &D6 \ error number (=file not found)
EQUS "No X.CAT file" \ message
EQUB 0 \ close message
.openok STA filechan \ store channel number in accumulator
The call OSGBPB (&FFD1) can read or write more bytes at a time. The BASIC commands INPUT# and PRINT# use the same call. OSGBPB expects a command in the accumulator, in this case &4 = read block using PTR#, and the address of a control block in the X- en Y-registers control block. The control block needs to contain the following information: the channel number to use, the address of the space where the output
has to go and the length of the block to read. Also OSGBPB needs room to maintain the value of PTR#.
In assembler this looks like as in figure 5:
\ osgbpb control block
.ctrl EQUW 0 \ channel number
.ctrlxcat EQUD xcat \ address of output area
.ctrllen EQUD 82 \ block length (=record length)
.ctrlptr EQUD 0 \ PTR# area, initially 0
.ctrladdr EQUW ctrl \ address of the control block
\ extended catalogue space
.xcat EQUW 0 \ length of file name
.xcatfile EQUS STRING$(9," ") \ space for file name
EQUW 0 \ length of text
.xcattext EQUS STRING$(69," ") \ space for text
.xcataddr EQUW xcat \ adres of the output area
.read LDA filechan \ get channel number
STA ctrl \ put in control block
LDA xcataddr \ get address of output area
STA ctrlxcat \ put in control block
LDA xcataddr + 1
STA ctrlxcat + 1
LDA #82 \ block length
STA ctrllen \ put in control block
LDA #4 \ read command
LDX ctrladdr \ put address of control block in X and Y
LDY ctrladdr + 1
JSR osgbpb \ read a block
As the information in the control block is overwritten by OSGBPB we need to re-fill it again before every call.
Like we saw above the X.CAT file is always 31 records long no matter how many files are on the disc. Therefore the program can stop as soon as it finds a blanc file name.
Printing is done with 2 small loops. One for the file name and one for the text. A call to OSWRCH (&FFEE) prints the character that is in the accumulator A.
The EOF detection is handled by OSFSC. The BASIC command EOF# uses the same call. OSFSC does not have a direct entry point like for example OSFIND en OSGBPB, but needs to be called through the FSCV vector (&21E).
OSFSC expects a command in the accumulator, in this case &1 = check EOF and the channel number in the X-register.
After execution of OSFSC the X-register contains 0 = not EOF or &FF = EOF.
Figure 6 has the assembler statements:
LDA #1 \ check EOF command
LDX filechan \ put channel number in X
JSR osfsc \ check EOF
CPX #&FF \ EOF?
BEQ close \ yes, close file
JMP read \ no, read next record
.osfsc JMP (&21E) \ jump to the FSCV vector
To close a file is also done through OSFIND. OSFIND expects the command &0 = close file in the accumulator and the channel number in the Y-register.
.close LDA #0 \ close file
LDY filechan \ put channel number in Y
JSR osfind \ close the file
RTS \ return to Basic
Using the machine language program M.XCAT
Start the program with CHAIN "M.XCAT". The program is now assembled at memory location &C00 and at the same time written to disc under the name $.XCAT.
Now you can start the program with *XCAT. To get the text 80 characters wide the program switches to MODE 3. Right after this the display starts.
Because the program could only be 256 bytes long, it has some restraints. To start off with paged mode is not enabled, so the screen will scroll if there are more than 24 files (you could change this more or less by using MODE 0). So hit CTRL-N before running XCAT.
Furthermore there is no possibility to select a drive. The program reads from the active drive. By a *LIB command for the library where $.XCAT is, and a *DRIVE command to select the active drive this can also be overcome.