Reading DFS and ADFS floppy disks under Linux

Actually, before we start, the following has only been tested on Linux i386 platforms. If you're running on other hardware, this may or may not be useful to you - proceed at your own risk.

As a BBC Micro enthusiast, I've got loads of DFS and ADFS floppy disks. I'm fortunate in that I still have a BBC Master 128 in service, but some of the data on the disks is useful to have on my day-to-day platform - which is Linux. So I set about trying to read the disks...
My testing has mainly been with 3.5" floppies, although I've had success with these methods on an old machine with a 5.25" drive.


I started with ADFS, on the grounds that the disks are double-density and therefore less 'alien' to the hardware than DFS disks. I noticed that there's an 'adfs' kernel module - so, being naive, I popped a floppy in the drive and tried "mount -t adfs /dev/fd0 /mnt/floppy". Whirr, whirr, error. Ah, the 'adfs' kernel module is designed solely to cope with (some of) the 'new directory' ADFS formats used by the Archimedes ('E' and 'F' formats I believe, but it's many years since I did Archimedes things).

I had a read about kernel modules, looked at the source code for the floppy driver, and decided I needed more time... Two of the challenges are:

I then came across libdsk, a library for accessing discs and disc image files. This seems to be just the ticket, built it and read an ADFS disk to an image file in about 5 minutes!

This gives you a file named 'adfs.raw' which is an image of the 'L' format ADFS disk in /dev/fd0. Hurrah!
Note the '-logical' switch. Without this, dsktrans reads 16 sectors on head 0, then 16 sectors on head 1, then moves on to the next track. Which doesn't do the right thing for us, because ADFS 'L' format disks process 80 tracks on head 0 first, followed by the 80 tracks on head 1.
There are other ADFS formats supported by dsktrans:
acorn16040 track single-sided (S)
acorn32080 track single-sided (M)
acorn64080 track double-sided (L)
acorn80080 track double sided, 5 sectors of 1024 bytes. This covers the 'D' and 'E' ADFS formats which appeared with the Archimedes. The same format was also available for DOS Plus (but not ADFS) on the Master 512
acorn1600This is, I believe, the 'F' AFDS format disk - High Density on the Archimedes.


Having cracked ADFS, what about DFS ?

Initially I was sceptical: back in the early 1990s I'd looked at the circuit diagram of an 80286 PC clone and seen that the 'density select' pin of the FDC chip was hard-wired to ground - disabling single-density. It was only after someone had told me that 'OmniFlop' on some other sort of OS for PCs (i.e. not Linux, FreeBSD, OpenBSD, NetBSD, Solaris x86, ...) could read DFS disks that I looked into it again. My 'dmesg' tells me "FDC 0 is a post-1991 82077", so I found the data sheet on the 82077 and it supports single density - that's a start (unless you have a 82077AA, which doesn't do single density). And dsktrans mentions two 'bbc' formats (in addition to the 'acorn' ones - interesting difference in labelling).

Pop an 80 track DFS disk in the floppy drive, do dsktrans /dev/fd0 dfs.raw -otype raw -format bbc200 and blow me, there's 'dfs.raw' as an image of the DFS disk!
It looks like are 2 DFS formats supported by dsktrans:
bbc10040 track
bbc20080 track

Remember that DFS treats each side of a disk as a separate 'drive', so doesn't have the concept of single-sided or double-sided disks. But how do we access the other side of the disk ? dsktrans doesn't let us down - we tell it to use the other side of the input drive dsktrans /dev/fd0 dfs-side2.raw -otype raw -format bbc200 -side1 and there's an image of the other side.

dsktrans notes

Note that dsktrans handles its optional arguments (switches) in an unusual manner for those of us brought up on getopt() - the options must follow the mandatory arguments of input and output 'files'.

If you're using a version of libdsk which is version 1.1.3 or later (at the time of writing, 1.0.0 is the stable version) then there is a -retry <count> option which I've been told helps with some floppy disks.

Doing DFS and ADFS natively

libdsk is great for producing disk image files. But it would be really neat to be able to mount ADFS and DFS floppies under Linux. First we would need to be able to persuade the floppy driver to understand the Acorn formats.

Well, it can be done. We need to use setfdprm to set the parameters of the floppy driver to match the floppy disk. Now there are (at least) 2 different setfdprm programs around: my Fedora Core machines used to provide one in /usr/bin from the 'util-linux' package; and there's a different one provided by the fdutils software which Debian provide as the 'fdutils' package. I guess they both can do the same thing (as they are merely doing an ioctl() call to the floppy driver), but the setfdprm from fdutils is easier to understand (once you've found the documention for it!) because it has easy-to-remember parameters.
So I'm going to assume that you have the fdutils software available.

'setfdprm' (to set the floppy drive parameters) has a matching 'getfdprm' command to read the floppy drive parameters. It's worth running 'getfdprm' after a 'setfdprm' (at least while you're getting used to things) to verify that the floppy drive parameters have been set as you expected. I've had the odd occasion when 'setfdprm' failed to set the parameters correctly.


Pop a disk in the floppy drive. Now, for an ADFS 'L' format disk, issue the command:
setfdprm /dev/fd0 dd sect=16 head=2 cyl=80 ssize=256 zerobased
The floppy drive should blip its motor, and the disk is mounted. Now you can use 'dd' to copy the disk to a file (OK, we did that with the libdsk software, but now we are using native Unix commands). You can use the blocksize option to dd to speed up the transfer, and the count option to limit the copy to a number of blocks (e.g. bs=256 count=1 will just get the first sector).
Now there's one slight problem, which only applies to 'L' format disks. They are 80 tracks, double sided, and the Acorn sector order is to read all the tracks on the first side, followed by all the tracks on the second side. However, the Linux floppy driver reads track N on the first side, then track N on the second side, then track N+1 on the first side, ... So the disk image has the two sides interleaved, which is a bit of a drag.

ADFS 'M' format.
This is a single-sided format, using only the first side. So
setfdprm /dev/fd0 dd sect=16 cyl=80 head=1 ssize=256 zerobased
does the right thing, and no worrying about interleaving.
In fact, if you have a 'L' format floppy which doesn't use space above track 79, you could pretend that it's a 'M' format floppy and avoid the interleaving problem.

ADFS 'S' format.
This is also a single-sided format, for 40 track drives. We need to double-step the Linux drive - so I believe what's needed is:
setfdprm /dev/fd0 dd sect=16 cyl=80 head=1 ssize=256 zerobased stretch
but I haven't tested this.

ADFS 'D' format.
I believe this should need:
setfdprm /dev/fd0 dd sect=5 head=2 cyl=80 ssize=1024 zerobased
but I have no way of checking this.

ADFS 'F' format.
Murray Colpman kindly informed me that the command needed for this is:
setfdprm /dev/fd0 hd sect=10 head=2 cyl=80 ssize=1024 zerobased
This will enable 'F' format floppy disks to be mounted using the kernel ADFS driver.


Pop a disk in the floppy drive. For the first side (Acorn drive 0), issue the command:
setfdprm /dev/fd0 sd sect=10 head=1 cyl=80 ssize=256 zerobased
The floppy drive should blip its motor again, and you can now use dd as before.
For the second side ('drive 1'), issue the command:
setfdprm /dev/fd0 sd sect=10 head=1 cyl=80 ssize=256 zerobased swapsides

DFS floppies are always single-sided, so we don't have any interleaving problem.
Note you may need to use the dtr=2 parameter (to set the data transfer rate). My first attempts to read DFS floppies always failed, and I found that after setting 'dtr=2' my attempts always worked. However my most recent testing with 5.25" floppies in old hardware running Debian have shown that 'dtr=2' prevents reading the DFS floppies, and that not using the parameter is the right thing to do.
I've found that reading DFS disks is slow. Painfully slow in fact. But it works!

So, I can make image files, and mount floppies, what next ?

Once you have your disk image or a mounted floppy, then you need to be able to do something with it. The 'adfs' kernel module isn't any good for reading old-map ADFS, so you need something else:

I've written an old-map ADFS module using Fuse, written in Perl which gives you read-only access to the ADFS filesystem on an image file or floppy drive as if it were a native filing system. It only supports old-map ADFS (i.e. the ADFS on 6502-based machines, and the 'D' format introduced with the Archimedes).

There's also David Boddie's ADFS module written in Python using FUSE, although it tries to decode the load/execution addresses as timestamps (which is OK for 'D', 'E', and 'F' formats but not for 'S', 'M', and 'L' formats).

Having written the ADFS FUSE module, it seemed only logical to do the same for DFS. So I wrote DFS module using Fuse, written in Perl, which gives you read-only access to the DFS filesystem on an image file or floppy drive as if it were a native filing system. Copes with Acorn, Watford, and Solidisk DFS, including double-density, and also copes with the dual-catalog '*SWAP' feature from Disk Doctor.

What's next ?

There's a file named /etc/fdprm provided by the 'util-linux' package, which defines known floppy formats. It would be great to get the BBC format disks added to this file, so one could use a construct like "setfdprm /dev/fd0 ADFS_L".

See if the floppy driver can be patched to have an option (perhaps via the 'stretch' parameter ?) for "do 'tracks then heads' rather than 'heads than tracks'".

And there's my 20 MByte Hard Disk on the BBC Master 128. The Adaptec ACB4000 board presents a SCSI interface, so if I plugged it into the 50 pin bus on my SCSI controller on a Linux box.... 256 byte sectors again though...

Andrew Benham (G8FSL), Salisbury, Wilts SP1, United Kingdom