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 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 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.
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 se,
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:
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:
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
GOTO's 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 blanc), 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.
1.
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:
FIGURE 3
\
-----------
\ 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
2.
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:
FIGURE 4
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
3.
The call OSGBPB (&FFD1) can read or write more bytes at a time.
The BASIC command's 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:
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.
4.
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.
5.
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.
6.
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:
FIGURE 6
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
7.
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.
Zie figurr 7:
FIGURE 7
.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.