In my quest for better knowledge of the FLEX disk operating system for the 6800 and 6809 I ended up needing to read old floppies and disk images. Images usually have the *.dsk extension and contains a raw dump of every sector on a disk. No header or other information. I found a few tools that could read raw FLEX images but they were mostly written for Windows and without source. Therefore I began digging into the FLEX disk structure with the goal of creating my own tool that could read raw FLEX images and extract individual files.
FLEX disk layout
FLEX has a hard coded sector size of 256 bytes. Addressing sectors are done by track and sector numbers. Maximum number of tracks are 256 and maximum number of sectors per track are 256, which means a single FLEX file system can be up to 256^3 bytes which is about 16MB raw capacity. Not very big by todays standards but 30 years ago this was huge. The sectors are linked together like linked lists where the first two bytes of each sector is a pointer to the next sector in the chain (actually some sectors on track 00 are exceptions from this but for the rest of the disk this is true). The first two bytes of every directory sector and file data sector contains the track and address of the next sector in the chain. End of chain is marked by setting track and sector to zero.
A typical FLEX disk layout for a 40 track floppy with 20 sectors on each track:
TRACK 00 SECTOR 00 --- Boot sector
TRACK 00 SECTOR 01 --- Boot sector
TRACK 00 SECTOR 03 --- System Information Record (SIR)
TRACK 00 SECTOR 04 --- Not used
TRACK 00 SECTOR 05 --- Start of directory
.
.
TRACK 00 SECTOR 20 --- End of directory
TRACK 01 SECTOR 01 --- Start of file data
.
.
TRACK 39 SECTOR 20 --- End of file data (last sector on disk)
The first sectors are boot sectors that contain executable code for loading the FLEX kernel from disk and booting the system. They can be ignored by the image reader and non-bootable disks. Tracks appears to be numbered from zero and up, and sectors from one and up. The boot sectors are exceptions from this and appears to be numbered 00 and 01. Track 00 has no sector 02. The first important sector is the System Information Record (SIR). The SIR contains basic information about the disk structure. After the SIR follows the directory sectors. The directory sectors are linked together to form one chain and the file data sectors are linked together to form another chain. The length of the directory can be varied and can span over multiple tracks if needed. The size of the directory is however determined when initializing the disk and it can usually not be changed at a later time.
Below is the structure of the System Information Record (SIR).
11 byte --- Volume label
2 byte --- Volume number
1 byte --- First free track
1 byte --- First free sector
1 byte --- Last free track
1 byte --- Last free sector
2 byte --- Number of free sectors
1 byte --- Date month
1 byte --- Date day
1 byte --- Date year
1 byte --- End track
1 byte --- End sector
The SIR structure is 24 bytes long and starts at byte 17 of the SIR sector. The first 16 bytes of the SIR sector is not used.
The directory structure (DIR) follows a similar pattern.
8 byte --- File name
3 byte --- File extension
2 byte --- Not used
1 byte --- Start track
1 byte --- Start sector
1 byte --- End track
1 byte --- End sector
2 byte --- Total number of sectors
1 byte --- Random file flag
1 byte --- Not used
1 byte --- Date month
1 byte --- Date day
1 byte --- Date year
The DIR structure is also 24 bytes long. Just as with the SIR sector the first 16 bytes of every directory sector is not used. The DIR structure is repeated 10 times for each directory sector until the end of the sector chain has been reached (all directory sectors does have the 16 byte padding at the start). One thing that had me puzzled for a while is how FLEX actually marks a file as "deleted" in the directory. After some trial and error I have discovered that deleted file entries are marked by setting the file first byte of the file name to either 0x00 or 0xFF. Another thing that had me scratching my head was the flag byte. It is used for FLEX "Random files". I have not implemented support for this file type so the flag is simply ignored but displayed.
Each DIR structure contains a pointer to the first sector of the file it represents. Reading a file is a matter of simply walking the sector chain starting at the sector mentioned in the DIR structure.
Each data sector has the structure below.
1 byte --- Next track
1 byte --- Next sector
2 byte --- Sector sequence number
252 byte --- File data
Every sector contains a pointer to the next sector in the file, a sequence number and 252 bytes of actual file data. End of chain (and end of file) is marked by setting next track and next sector values to zero. The sequence number is a 16 bit value starting at 1 and is increased by 1 for every sector in the file. It can be used as as simple verification that the file chain appears to be correct. The last sequence number on the last sector of the file should have the same value as the total sectors value of the DIR structure.
The FLEXTRACT program
With this knowledge I began writing my FLEX image file extractor. I call it FLEXTRACT. It's a single C file that can be compiled under GCC.
Source code:
flextract.c
Compilation and installation under Linux:
$ gcc flextract.c -o flextract
$ sudo cp flextract /usr/local/bin/
Running the program without options displays basic usage information:
$ flextract
Usage: flextract [options] [file name] [output file]
Options: v - Verbose
l - List directory
1 - List directory as single column
x - Extract file
t - Do FLEX text to ASCII conversion
d - Print SIR/DIR sector dumps
Output file "-" means console (stdout)
Version 1.5 by Daniel Tufvesson 2015-2020
Displaying image contents:
$ flextract image.dsk
Image size is 204800 bytes - 40 tracks, 20 sectors/track
Volume label DISK01
Volume number 0000
Free area t2 s14 - t39 s20
Free sectors 766
End sector t39 s20
Creation date 83-02-23
NAME START END SIZE DATE FLAG
FILE1.BIN t01 s01 - t02 s06 26 83-02-23 00
FILE2.TXT t02 s07 - t02 s13 7 83-02-23 00
$
Extracting the binary file "FILE1.BIN" from image "image.dsk" and save as "/tmp/file1.bin":
$ flextract image.dsk x FILE1.BIN /tmp/file1.bin
Extracting the text file "FILE2.TXT" from image "image.dsk" and save as "/tmp/file2.txt":
$ flextract image.dsk xt FILE2.TXT /tmp/file2.txt
Previewing a file on screen (export to stdout):
$ flextract image.dsk xt FILE3.TXT -
This a file that is stored on the disk image
Adding the "t" option ensures that the file is treated as a FLEX text file.
$
2020 UPDATE: As pointed out by Soren and Alan I have adopted flextract to read FLEX text files more properly now.
The "t" option converts the file by stripping out non printable control characters as well as handling the FLEX space compression. FLEX compresses occurrences of consecutive spaces by using a form of run length compression. A 0x09 byte (normally used for tab nowadays) is written to the file followed by another byte determining the number of spaces to be printed.
Executable CMD files
Program files that are executable are a little special. They are stored in FLEX CMD format which is the executable data divided into chunks, each chunk containing its own a load address. This is a flexible solution since it makes it possible to load a single file into different parts of memory before execution. When dealing with the file outside FLEX it's however a bit tricky since CMD files cannot be treated as RAW binary dumps of a program. In order to strip a CMD file of everything but the program code I also had to write the CMD2BIN converter.
Source code:
cmd2bin.c
Compilation and installation under Linux:
$ gcc cmd2bin.c -o cmd2bin
$ sudo cp cmd2bin /usr/local/bin/
$ cmd2bin
Usage: cmd2bin <infile.cmd> <outfile.bin> [start address] [stop address]
cmd2bin automatically detects non-continuous parts of the CMD file, which is marked by an extra blank line.
$ cmd2bin PROGRAM.CMD PROGRAM.BIN
Converting 0000 to FFFF
Address: 0100-01C3 Length: 196
Address: 01C4-0287 Length: 196
Address: 0288-034B Length: 196
Address: 034C-040F Length: 196
Address: 0720-07E3 Length: 196
Address: 07E4-08A7 Length: 196
Address: 08A8-096B Length: 196
Address: 096C-0A2F Length: 196
Address: 0A30-0AF3 Length: 196
Address: 0AF4-0BB7 Length: 196
Address: 0BB8-0C7B Length: 196
Address: 0C7C-0D3F Length: 196
Address: 0D40-0E03 Length: 196
Address: 0E04-0E87 Length: 132
$
In the example above PROGRAM.CMD loads into two parts of the memory. Using cmd2bin the program can be exported into separate files for each memory segment.
First 0100-040F:
$ cmd2bin PROGRAM.CMD PROGRAM_0100.BIN 0100 040F
Converting 0100 to 040F
Address: 0100-01C3 Length: 196
Address: 01C4-0287 Length: 196
Address: 0288-034B Length: 196
Address: 034C-040F Length: 196
$
Then 0720-0E87:
$ cmd2bin PROGRAM.CMD PROGRAM_0720.BIN 0720 0E87
Converting 0720 to 0E87
Address: 0720-07E3 Length: 196
Address: 07E4-08A7 Length: 196
Address: 08A8-096B Length: 196
Address: 096C-0A2F Length: 196
Address: 0A30-0AF3 Length: 196
Address: 0AF4-0BB7 Length: 196
Address: 0BB8-0C7B Length: 196
Address: 0C7C-0D3F Length: 196
Address: 0D40-0E03 Length: 196
Address: 0E04-0E87 Length: 132
$
Summary
Both FLEXTRACT and CMD2BIN has proven very useful to me and hopefully someone else will find them useful as well. They work fine for both FLEX 2 and FLEX 9 images (6800 & 6809). It feels very nice to have a non-windows way of dealing with FLEX images. With this I can only read and not create images but it's a good start!