1134 lines
32 KiB
Plaintext
1134 lines
32 KiB
Plaintext
From: Laurent.Clevy@meteo.fr
|
|
Newsgroups: comp.sys.amiga.programmer, comp.sys.amiga.misc, comp.os.linux.misc
|
|
Subject: Amiga floppy disks format (AmigaDos file system - floppies)
|
|
Followup-To: poster
|
|
Summary: This document describes the AmigaDos File System for floppy disks only.
|
|
Physical/Logical formats, OFS/FFS, Directory caching, Links, Checksums.
|
|
|
|
Archive-name: amiga/amiga_floppy_format
|
|
Last-modified: 28. May 1997
|
|
Version: 0.9
|
|
Copyright: (c) 1997 Laurent Clevy
|
|
Maintainer: Laurent Clevy <Laurent.Clevy@meteo.fr>
|
|
|
|
|
|
FAQ : The Amiga floppy disks format
|
|
Laurent Clevy
|
|
|
|
Laurent.Clevy@meteo.fr
|
|
25 avenue Aristide Briand
|
|
28000 Chartres
|
|
France
|
|
|
|
|
|
|
|
Disclaimer and copyright
|
|
------------------------
|
|
|
|
This document is Copyright (C) 1997 by Laurent Clevy, but may be
|
|
freely distributed, provided the author name and addresses
|
|
are included and no money is charged for this document.
|
|
|
|
This document is provided "as is". No warranties are made as to its correctness.
|
|
|
|
Amiga, AmigaDos are registred trademarks of Gateway 2000.
|
|
|
|
|
|
|
|
Introduction
|
|
------------
|
|
|
|
This document purpose is to describe the Amiga floppy disk format. I don't
|
|
found any document which explains this format in details. Because I wish
|
|
this machine to be supported a long time, including via emulators, I decided
|
|
to write this file, and supply C routines as examples.
|
|
|
|
Corrections (including about my english) are very welcome. Unfortunately,
|
|
I have no permanent e-mail address currently, the only way to touch me is by postmail.
|
|
|
|
|
|
|
|
Index
|
|
-----
|
|
|
|
1. How bytes are physically read/written on disk ?
|
|
1.1 What is MFM ?
|
|
1.2 What is the physical track format ?
|
|
1.3 What is the physical sector format ?
|
|
1.4 How to decode MFM data ?
|
|
2. What is the Amiga floppy disk geometry ?
|
|
3. How is logically organised a Amiga floppy disk ?
|
|
3.1 What is a Bootblock ?
|
|
3.2 What is a Rootblock ?
|
|
3.2.1 How to find the first sector of a entry ?
|
|
3.2.2 How to list directory entries ?
|
|
3.2.3 How to compute the checksum ?
|
|
3.3 How is managed the free/used blocks list ?
|
|
3.3.1 How to compute bitmap checksum ?
|
|
3.3.2 What is the 'bm_ext' field in Rootblock ?
|
|
3.4 How are stored files on a disk ?
|
|
3.5 How are stored directories ?
|
|
3.6 How are implemented links with AmigaDos ?
|
|
3.6.1 Hard links
|
|
3.6.2 Soft links
|
|
3.7 How is the block associated to directory caching ?
|
|
4. What is a blank disk ?
|
|
4.1 a Minimal blank disk
|
|
4.2 A 'Bootable' disk
|
|
4.3 A Directory cache mode disk
|
|
5. References
|
|
6. C Routines
|
|
7. Other Amiga file systems
|
|
8. To do
|
|
|
|
|
|
|
|
Conventions
|
|
-----------
|
|
|
|
* In this document, hexadecimal values are written like in C : for example 0x0c
|
|
is the decimal value 12.
|
|
|
|
As the Amiga is a 680x0 based computer, integers that require more than
|
|
one byte are stored on disk in 'Motorola order' : the most significant byte
|
|
comes first, then the less significant bytes in descending order of
|
|
significance (MSB LSB for two-byte integers, B3 B2 B1 B0 for four-byte
|
|
integers). A byte is 8 bits long. The left bit of a byte is the 7th, the right bit
|
|
is the 0th.
|
|
|
|
A 'word' is a 2 bytes (16 bits) integer, a 'long' a 4 bytes (32 bits) integer.
|
|
Values are unsigned unless otherwise noted.
|
|
|
|
|
|
* A block pointer is the number of this block on the disk. Disk starts with the #0 block.
|
|
|
|
|
|
* Hashing is a method to access tables : given a number or a string,
|
|
a hash function gives a index into a table.
|
|
|
|
|
|
* Chained lists are cells oriented data structures. Each cell contains a pointer
|
|
to the next or previous cell or both, the last pointer is null.
|
|
|
|
C example :
|
|
|
|
struct lcell {
|
|
char name[10];
|
|
/* contains next cell adress, or NULL if this cell is the last */
|
|
struct lcell* next_cell;
|
|
};
|
|
|
|
|
|
* Names of blocks begin with a capital (Rootblock).
|
|
Name of fields are noted between quotes ('field_name').
|
|
|
|
|
|
* All formats are described as tables, one rows per field.
|
|
Here is an example with then well known beginning of GIF format :
|
|
|
|
offset type length name comments
|
|
----------------------------------------------------------
|
|
0 char 3 signature 'GIF'
|
|
3 char 3 version '87a' or '89a'
|
|
6 short 1 screen width
|
|
8 short 1 screen height
|
|
|
|
|
|
|
|
1. How bytes are physically read/written on disk ?
|
|
=================================================
|
|
|
|
|
|
Most of PC-like floppy disk controllers (FDC) are not able to read Amiga disks,
|
|
because Amiga physical floppy disk operations are made by a specific
|
|
chip called "Paula".
|
|
However, i'm supplying this information because it's hard to find out.
|
|
|
|
If you only want to understand the UAE .adf format, you don't need to read this
|
|
part.
|
|
|
|
|
|
For classical floppy disk operations, Paula is set with the following
|
|
parameters :
|
|
|
|
- MFM encoding
|
|
- Precompensation time : 0 nanosec
|
|
- Controller clock rate : 2 microseconds per bit cell
|
|
- Sync value = 0x4489
|
|
|
|
The controller is able to put the read/write heads on a cylinder, and is able to read
|
|
with the lower or upper side head. A track of 0x1900 words is usually read.
|
|
|
|
|
|
|
|
1.1 What is MFM ?
|
|
-----------------
|
|
|
|
Because bits can't be written with magnetic fields directly on disk, an
|
|
encoding scheme is required. Amiga floppy disks are MFM (Modified Frequence
|
|
Modulation) encoded.
|
|
|
|
The MFM encoding scheme is :
|
|
|
|
user's data bit MFM coded bits
|
|
--------------- --------------
|
|
1 01
|
|
0 10 if following a 0 data bit
|
|
0 00 if following a 1 data bit
|
|
|
|
User data longs are splitted in two parts, a part with even bits part first,
|
|
followed by a part with odd bits.
|
|
|
|
|
|
|
|
1.2 What is the physical track format ?
|
|
---------------------------------------
|
|
|
|
Double density (DD) disks have 11 sectors per track, High density (DD) disks
|
|
have 22.
|
|
|
|
So a track consists of 11/22 MFM encoded sectors, plus inter-track-gap.
|
|
Note that sectors are not written from 0 to 10/21, you must use the 'info'
|
|
field to recreate orderly track in memory. Each track begins with the first
|
|
sector, and ends the end of the last sector (11th with DD disks, 22th with HDs).
|
|
Each sector starts with 2 synchronization words. The synchro value is 0x4489.
|
|
|
|
|
|
1.3 What is the physical sector format ?
|
|
----------------------------------------
|
|
|
|
Here it comes :
|
|
|
|
0/0x0 word 2 MFM value 0xAAAA AAAA
|
|
|
|
SYNCHRONIZATION
|
|
4/0x4 word 1 MFM value 0x4489
|
|
6/0x6 word 1 MFM value 0x4489
|
|
|
|
HEADER
|
|
8/0x8 long 1 info (even bits)
|
|
12/0xc long 1 info (odd bits)
|
|
decoded long is : 0xff TT SS SG
|
|
TT = track number ( 3 means cyl 1, head 1)
|
|
SS = sector number ( 0 -> 10/21 )
|
|
sectors are not orderly !!!
|
|
SG = number of sector before gap (including
|
|
current one)
|
|
Example for cylinder 0, head 1 of a DD disk :
|
|
0xff010009
|
|
0xff010108
|
|
0xff010207
|
|
0xff010306
|
|
0xff010405
|
|
0xff010504
|
|
0xff010603
|
|
0xff010702
|
|
0xff010801
|
|
<-- inter-sector-gap here !
|
|
0xff01090b (0xb means -1 ?)
|
|
0xff010a0a (0xa means -2 ?)
|
|
|
|
16/0x10 long 4 sector label (even)
|
|
32/0x20 long 4 sector label (odd)
|
|
decoded value seems to be always 0
|
|
END OF HEADER
|
|
|
|
48/0x30 long 1 header checksum (even)
|
|
52/0x34 long 1 header checksum (odd)
|
|
(computed on mfm longs,
|
|
longs between offsets 8 and 44
|
|
== 2*(1+4) longs)
|
|
|
|
56/0x38 long 1 data checksum (even)
|
|
60/0x3c long 1 data checksum (odd)
|
|
(from 64 to 1088 == 2*512 longs)
|
|
|
|
DATA
|
|
64/0x40 long 512 coded data (even)
|
|
576/240 long 512 coded data (odd)
|
|
1088/440
|
|
END OF DATA
|
|
|
|
|
|
|
|
1.4 How to decode MFM data ?
|
|
----------------------------
|
|
|
|
the algorithm :
|
|
|
|
|
|
#define MASK 0x55555555 /* 01010101 ... 01010101 */
|
|
unsigned long *p1; /* MFM coded data buffer (size == 2*data_size) */
|
|
unsigned long *q; /* decoded data buffer (size == data_size) */
|
|
unsigned long a,b;
|
|
unsigned long chksum;
|
|
int data_size; /* size in long, 1 for info, 4 for sector label */
|
|
|
|
chksum=0L;
|
|
do <data_size> times {
|
|
a = *p1; /* even bits */
|
|
b = *(p1+data_size); /* odd bits */
|
|
chksum^=a; /* eor */
|
|
chksum^=b;
|
|
*q = ( b & MASK ) | ( ( a & MASK ) << 1 ); /* MFM decoding */
|
|
p1++;
|
|
q++;
|
|
}
|
|
chksum&=MASK;
|
|
|
|
|
|
|
|
2. What is the Amiga floppy disk geometry ?
|
|
===========================================
|
|
|
|
|
|
Here follows the disk geometries for DD and HD.
|
|
|
|
bytes/sector sector/cyl sides/cyl cyl/disk
|
|
------------------------------------------------------------------------
|
|
DD disks 512 11 2 80
|
|
HD disks 512 22 2 80
|
|
|
|
|
|
The relations between sectors, sides and cylinders are for a DD disk :
|
|
|
|
Block sector side track
|
|
-----------------------------
|
|
0 0 0 0
|
|
1 1 0 0
|
|
2 2 0 0
|
|
...
|
|
10 10 0 0
|
|
11 0 1 0
|
|
...
|
|
21 10 1 0
|
|
22 0 0 1
|
|
..
|
|
1759 10 1 79
|
|
|
|
|
|
A DD disk has 11*2*80=1760 (0 to 1759) blocks, a HD disk has 22*2*80=3520 blocks.
|
|
|
|
Of course the file system uses some of them, even for a blank disk.
|
|
As the next part deals with, at least 4 blocks are used, for 3 logical
|
|
structures : bootblock (2), rootblock (1) and bitmap block (1).
|
|
|
|
|
|
The length of .ADF files for a DD disk is then 512*11*2*80 = 901120 bytes.
|
|
|
|
|
|
|
|
3. How is logically organised a Amiga floppy disk ?
|
|
===================================================
|
|
|
|
|
|
The logical low level object of a Amiga disk is the 'sector' (or 'block') : 512
|
|
consecutive bytes.
|
|
|
|
Disk information is distribued in the Bootblocks, the Rootblock and Bitmap
|
|
block(s).
|
|
FFS has block structures to provide directory list caching and (hard) links :
|
|
Directory cache blocks and Link blocks.
|
|
|
|
Directory tree is stored with a Directory block for each node.
|
|
Directory entries (files, directories and links) are stored with a table, and
|
|
are accessed with hashing and chained lists.
|
|
|
|
Files are stored with a File header block and Data blocks. File extension
|
|
blocks are also used for files stored with more than 72 Data blocks.
|
|
|
|
|
|
|
|
3.1 What is a Bootblock ?
|
|
-------------------------
|
|
|
|
The first object of an Amiga floppy is the Boot block. If the checksum and the
|
|
DiskType are correct, the system will execute the bootblock code, at boot
|
|
time, of course :-).
|
|
|
|
A valid bootblock is written by the AmigaDos command 'install'.
|
|
|
|
|
|
* BootBlock (1024 bytes) sectors 0 and 1
|
|
-------------------------------------------------------------------------------
|
|
offset size number name meaning
|
|
-------------------------------------------------------------------------------
|
|
0/0 char 4 DiskType 'D''O''S' + flags (0->5)
|
|
flags = set clr
|
|
0 FFS OFS
|
|
1 INT NOINT
|
|
2 DIRC NODIRC
|
|
4/4 long 1 Chksum special checksum
|
|
8/8 long 1 Rootblock ==880 DD and HD
|
|
12/0x0c char 1012 Bootblock code (see 4.2 'Bootable disk' for more information)
|
|
-------------------------------------------------------------------------------
|
|
|
|
The DiskType flag informs of the disk format.
|
|
|
|
OFS = Old/Original File System, the first one. (AmigaDos 1.2)
|
|
FFS = Fast File System (AmigaDos 2.04)
|
|
INT = International characters Mode (AmigaDos 3.0).
|
|
DIRC = stands for Directory Cache Mode (AmigaDos 3.0). This mode speeds up
|
|
directory listing, but take some disk space.
|
|
|
|
There are few differences between the two file systems.
|
|
- OFS Datablock stores 488 bytes, FFS stores 512 bytes,
|
|
- FFS supports directories caching, links and international mode,
|
|
- the FFS is faster than OFS.
|
|
|
|
|
|
The bootblock checksum algorithm follows :
|
|
|
|
* in 68000 assembler :
|
|
|
|
lea bootbuffer,a0
|
|
move.l a0,a1
|
|
clr.l 4(a1) ;clear the checksum
|
|
move.w #256-1,d1 ;1024/4 times
|
|
moveq #0,d0
|
|
lpchk: add.l (a0)+,d0 ;accumulation
|
|
bcc.s jump ;if carry set, add 1 to checksum
|
|
add.l #1,d0
|
|
jump: dbf d1,lpchk ;next long word
|
|
|
|
not.l d0
|
|
move.l d0,4(a1) ;new checksum
|
|
|
|
|
|
* in C :
|
|
|
|
#include<limits.h>
|
|
#define Short(p) ((p)[0]<<8 | (p)[1])
|
|
#define Long(p) (Short(p)<<16 | Short(p+2))
|
|
|
|
unsigned long newsum,d;
|
|
unsigned char buf[1024]; /* contains bootblock */
|
|
int i;
|
|
|
|
memset(buf+4,0,4); /* clear old checksum */
|
|
newsum=0L;
|
|
for(i=0; i<256; i++) {
|
|
d=Long(buf+i*4);
|
|
if ( (ULONG_MAX-newsum) < d ) /* overflow */
|
|
newsum++;
|
|
newsum+=d;
|
|
}
|
|
|
|
newsum=~newsum;
|
|
|
|
|
|
|
|
|
|
3.2 What is a Rootblock ?
|
|
-------------------------
|
|
|
|
|
|
The Rootblock is at the middle of the media : block number 880 for DD disks,
|
|
block 1760 for HDs.
|
|
|
|
The Rootblock contains information about disk : its name, its formatting date,
|
|
etc ...
|
|
|
|
It also contains information to access the files/directories/links located at the root
|
|
(Unix /) directory.
|
|
|
|
|
|
* Rootblock (512 bytes) sector 880 for a DD disk, 1760 for a HD disk
|
|
------------------------------------------------------------------------------
|
|
0/0 long 1 type block primary type = T_HEADER (value 2)
|
|
4/4 long 1 header_key unused in rootblock (value 0)
|
|
long 1 high_seq unused (value 0)
|
|
12/c long 1 ht_size Hash table size in long (value 0x48)
|
|
16/10 long 1 first_data unused (value 0)
|
|
20/14 long 1 chksum sum to check block integrity
|
|
24/18 long 72 ht[] hash table (entry block number)
|
|
312/138 long 1 bm_flag bitmap flag, -1 means VALID
|
|
316/13c long 25 bm_pages[] bitmap blocks pointers (first at 0)
|
|
416/1a0 long 1 bm_ext first bitmap extension block (Hard disks only)
|
|
...
|
|
432/1b0 char 1 name_len; disk name length
|
|
433/1b1 char 30 diskname[] disk name
|
|
...
|
|
472/1d8 long 1 days last access date : days since 1 jan 1978
|
|
476/1dc long 1 mins minutes past midnight
|
|
480/1e0 long 1 ticks ticks (1/50 sec) past last minute
|
|
484/1e4 long 1 c_days creation date
|
|
488/1e8 long 1 c_mins
|
|
492/1ec long 1 c_ticks
|
|
long 1 next_hash unused (value = 0)
|
|
long 1 parent_dir unused (value = 0)
|
|
504/1f8 long 1 extension FFS: first directory cache block, 0 otherwise
|
|
508/1fc long 1 sec_type block secondary type = ST_ROOT (value 1)
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
3.2.1 How to find the first sector of a directory entry ?
|
|
---------------------------------------------------------
|
|
|
|
|
|
Given the name of a file/directory/link you compute its
|
|
hash value with this algorithm :
|
|
|
|
* The hash function :
|
|
|
|
#include<ctype.h>
|
|
|
|
int HashName(char *name)
|
|
{
|
|
int hash;
|
|
int i,l;
|
|
|
|
l=hash=strlen(name);
|
|
for(i=0; i<l; i++) {
|
|
hash=hash*13;
|
|
hash=hash + toupper(name[i]);
|
|
hash=hash & 0x7ff;
|
|
}
|
|
hash=hash % 72;
|
|
hash=hash + 6;
|
|
|
|
return(hash);
|
|
}
|
|
|
|
|
|
The hash value is used to access HashTable ('ht' field in Rootblock/Directory block).
|
|
|
|
But several names can result a unique HashValue. The first block pointers
|
|
of them are stored in a chained list which start at HashTable[HashValue-6].
|
|
|
|
|
|
Here follows the method to find the requested block :
|
|
|
|
1. HashValue = HashName( name )
|
|
2. sector = Hashtable[ HashValue-6 ]
|
|
3. if (sector == 0)
|
|
printf "File/Dir not found"
|
|
stop
|
|
else
|
|
Loading the sector
|
|
4. while ( sector name != name )
|
|
sector = Next_hash sector
|
|
if (sector == 0)
|
|
printf "File/Dir not found"
|
|
else
|
|
Loading the sector
|
|
|
|
|
|
The Next_hash field is found in File header, Directory and Link blocks.
|
|
|
|
|
|
Filenames can contain lowercase and uppercase, but the Hash function
|
|
tells us that 'fIlE1' and 'FiLe1' are a same file.
|
|
|
|
|
|
|
|
3.2.2 How to list directory entries ?
|
|
-------------------------------------
|
|
|
|
|
|
The HashTable contains the entries block number.
|
|
|
|
To obtain the list of a directory entries (files, directories or links) the
|
|
algorithm is :
|
|
|
|
long entry;
|
|
int i;
|
|
|
|
for(i=0; i<ht_size; i++) {
|
|
entry = HashTable[i]
|
|
while ( entry !=0 ) {
|
|
printf "entry name"
|
|
entry = Next_hash
|
|
}
|
|
}
|
|
|
|
|
|
|
|
3.2.3 How to compute the checksum ?
|
|
-----------------------------------
|
|
|
|
|
|
#define Short(p) ((p)[0]<<8 | (p)[1])
|
|
#define Long(p) (Short(p)<<16 | Short(p+2))
|
|
|
|
unsigned long newsum;
|
|
unsigned char buf[512]; /* contains rootblock */
|
|
int i;
|
|
|
|
memset(buf+20,0,4); /* clear old checksum */
|
|
newsum=0L;
|
|
for(i=0; i<128; i++)
|
|
newsum+=Long(buf+i*4);
|
|
newsum=-newsum;
|
|
|
|
|
|
This checksum algorithm works for most blocks type except noted.
|
|
|
|
|
|
|
|
The AmigaDos command 'relabel' rename the disk.
|
|
The 'format' command is used for ... formatting.
|
|
|
|
|
|
The bitmap table ('bm_pages[]') stores one or several pointers to Bitmap blocks.
|
|
The first is at index 0.
|
|
|
|
|
|
|
|
3.3 How is managed the free and used blocks list?
|
|
-------------------------------------------------
|
|
|
|
|
|
Bitmap blocks stores if a sector is free or allocated. One
|
|
bit is used per sector. If the bit is set, the sector is
|
|
free, a cleared bit means a allocated sector.
|
|
|
|
Boot blocks allocation (2 for a floppy disk) is not stored in bitmap.
|
|
Bitmap is stored in longs, the first sector of a long is the bit 0.
|
|
|
|
|
|
* Bitmap block (512 bytes), often rootblock+1
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 checksum special algorithm
|
|
4/4 long 127 map
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
Here follows for a DD disk the relationship between bitmap and sector number :
|
|
|
|
block # long # bit #
|
|
-------------------------------
|
|
2 0 0
|
|
3 0 1
|
|
4 0 2
|
|
...
|
|
33 0 31
|
|
34 1 0
|
|
35 1 1
|
|
...
|
|
880 27 2
|
|
881 27 3
|
|
...
|
|
1759 54 28
|
|
1760 54 29
|
|
|
|
|
|
This map is 1758 bits long (1760-2) and is stored on 54 full long and the first 30th bits
|
|
of the 55th long.
|
|
|
|
|
|
3.3.1 How to compute bitmap checksum ?
|
|
--------------------------------------
|
|
|
|
#define Short(p) ((p)[0]<<8 | (p)[1])
|
|
#define Long(p) (Short(p)<<16 | Short(p+2))
|
|
|
|
unsigned long newsum;
|
|
unsigned char buf[512]; /* contains block */
|
|
int i;
|
|
|
|
newsum=0L;
|
|
for(i=1; i<128; i++) /* ignores old checksum */
|
|
newsum=newsum-Long(buf+i*4);
|
|
|
|
|
|
|
|
3.3.2 What is the 'bm_ext' field in Rootblock ?
|
|
-----------------------------------------------
|
|
|
|
|
|
If 25 bitmap blocks (which pointers are stored on Rootblock) are not enough (for Hard Disks),
|
|
extra needed bitmap blocks pointers are stored in bitmap extension blocks.
|
|
|
|
|
|
* Bitmap extension block (512 bytes) (Hard disk only)
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 127 bitmap block pointers
|
|
508/1fc long 1 next (0 for last)
|
|
-------------------------------------------------------------------------------
|
|
|
|
The Bitmap extension chained list start at Rootblock with the 'bm_ext'.
|
|
|
|
|
|
|
|
3.4 How are stored files on a disk ?
|
|
------------------------------------
|
|
|
|
The first block of a file is the File header block, which contains information about
|
|
the file (size, last access date, ...) and pointers to the first 72th Data blocks.
|
|
Data blocks store file data.
|
|
|
|
If more than 72 Data blocks are needed to store a file, the other Data block pointers are
|
|
stored in File extension blocks.
|
|
|
|
File extension blocks are organised in a chained list, which starts in File header block ('extension').
|
|
|
|
|
|
* File header block (512 bytes)
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 type block primary type T_HEADER (==2)
|
|
4 long 1 header_key self pointer
|
|
8 long 1 high_seq number of data block ptr stored here
|
|
12/c long 1 data_size unused (==0)
|
|
16/10 long 1 first_data first data block ptr
|
|
20/14 long 1 chksum same algorithm as rootblock
|
|
24/18 long 72 data_blocks[] data blk ptr (first at [high_seq-1])
|
|
...
|
|
320/140 long 1 protect protection flags (bit set)
|
|
0 delete forbidden (D)
|
|
1 modification forbidden (E)
|
|
2 write/update forbidden (W)
|
|
3 read forbidden (R)
|
|
4 file is an archive (A)
|
|
5 pure, can be made resident (P)
|
|
6 file is a script (S)
|
|
7 hidden file with DIR (H)
|
|
324/144 long 1 byte_size file size
|
|
328/148 char 1 comm_len file comment length
|
|
329/149 char 22 comment[] comment
|
|
...
|
|
420/1a4 long 1 days last access date (days since 1 jan 1978)
|
|
424/1a8 long 1 mins last access time
|
|
428/1ac long 1 ticks
|
|
432/1b0 char 1 name_len filename length
|
|
433/1b1 char 30 filename[] filename
|
|
...
|
|
468/1d4 long 1 real_entry FFS : unused (== 0)
|
|
472/1d8 long 1 next_link FFS : links chained list (first = newest)
|
|
...
|
|
496/1f0 long 1 hash_chain next entry ptr with same hash
|
|
500/1f4 long 1 parent parent directory
|
|
504/1f8 long 1 extension pointer to 1st file extension block
|
|
508/1fc long 1 sec_type secondary type : ST_FILE (== -3)
|
|
-------------------------------------------------------------------------------
|
|
|
|
Here we are discovering the first method to read a file : Data block pointers
|
|
tables. The first table is in File header block, the others in File extension blocks.
|
|
|
|
For an empty file, a Header file block is written, with 'byte_size' equal to 0,
|
|
and no Data block pointers in 'data_blocks[]'.
|
|
|
|
|
|
|
|
* File extension block (512 bytes) (first pointer in File header)
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 type primary type : T_LIST (== 16)
|
|
4/4 long 1 header_key self pointer
|
|
8/8 long 1 high_seq number of data blk ptr stored
|
|
12/c long 1 data_size unused (== 0)
|
|
16/10 long 1 first_data unused (== 0)
|
|
20/14 long 1 chksum rootblock algorithm
|
|
24/18 long 72 data_blocks[] data blk ptr (first at [high_seq-1])
|
|
long 45 unused
|
|
long 1 info unused (== 0)
|
|
long 1 hash_chain; unused (== 0)
|
|
500/1f4 long 1 parent ptr to parent directory
|
|
504/1f8 long 1 extension next file header extension block, 0 for last
|
|
508/1fc long 1 sec_type secondary type : ST_FILE (== -3)
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
* Data blocks (512 bytes) (first pointer in File header 'first_data' and
|
|
'data_blocks[71]')
|
|
|
|
OFS
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 type primary type : T_DATA (== 8)
|
|
4/4 long 1 header_key pointer to file header block
|
|
8/8 long 1 seq_num file data block number (first is #1)
|
|
12/c long 1 data_size data size (<= 488)
|
|
16/10 long 1 next_data next data block ptr (0 for last)
|
|
20/14 long 1 chksum rootblock algorithm
|
|
24/18 UCHAR 488 data[] file data
|
|
-------------------------------------------------------------------------------
|
|
|
|
In OFS, there is a second way to read a file : using the Data block chained list.
|
|
The list starts in File header ('first_data') and goes on with 'next_data' in each Data
|
|
block.
|
|
|
|
|
|
FFS
|
|
-------------------------------------------------------------------------------
|
|
0/0 UCHAR 512 data[] file data
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
In FFS, the only way to read or recover a file is to use File extension blocks.
|
|
If a File extension block is unreadable, there is no way to find the
|
|
corresponding Data blocks.
|
|
|
|
The OFS is more robust than FFS, but slower and can store less data on disk.
|
|
As you see, disk salvaging is easier with OFS.
|
|
|
|
|
|
When a file is deleted, only its File header block number is cleared from the Directory
|
|
block (or from the same-hash-value list). File header block, Data blocks and
|
|
File extension blocks are not cleared ! The undelete operation is easy .
|
|
|
|
|
|
|
|
3.5 How are stored directories ?
|
|
--------------------------------
|
|
|
|
Directory blocks are very similar to Rootblock, except they don't need
|
|
information about bitmap and disk, but have comments.
|
|
|
|
|
|
* Directory block (512 bytes)
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 type primary type : T_HEADER (== 2)
|
|
4/4 long 1 header_key self pointer
|
|
8/8 long 1 high_seq unused (== 0)
|
|
12/c long 1 ht_size unused (== 0)
|
|
long 1 unused
|
|
20/14 long 1 chksum normal algorithm
|
|
24/18 long 72 ht[] HashTable (file/dir/link block number)
|
|
long 2 unused
|
|
320/140 long 1 protect protect flag (like file header)
|
|
long 1 unused
|
|
328/148 char 1 comm_len comment length
|
|
329/149 char 22 comment[] comment
|
|
char 69 unused
|
|
420/1a4 long 1 days last access date
|
|
424/1a8 long 1 mins last access time
|
|
428/1ac long 1 ticks
|
|
432/1b0 char 1 name_len name length
|
|
433/1b1 char 30 dirname[] directory name
|
|
char 5 unused
|
|
468/1d4 long 1 real_entry unused (== 0)
|
|
472/1d8 long 1 next_link FFS : links chained list
|
|
long 4 unused
|
|
496/1f0 long 1 next_hash next entry with same hash value
|
|
500/1f4 long 1 parent parent directory
|
|
504/1f8 long 1 extension FFS : first directory cache block
|
|
508/1fc long 1 sec_type secondary type : ST_UDIR (== 2)
|
|
-------------------------------------------------------------------------------
|
|
|
|
You can obtain directory listing exactly like with the root directory.
|
|
|
|
|
|
|
|
3.6 How are implemented links with AmigaDos ?
|
|
----------------------------------------------
|
|
|
|
Only FFS handles links and directory caching.
|
|
Soft links are not supported by AmigaDos 3.0. Linked files are seen as
|
|
directories...
|
|
However, some shells (like Csh 5.37) supports them, so i'm supplying the
|
|
structure.
|
|
|
|
|
|
3.6.1 Hard links
|
|
----------------
|
|
|
|
* Hard link (512 bytes)
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 type T_HEADER = 2
|
|
4/4 long 1 self pointer
|
|
long 3 unused (== 0)
|
|
20/14 long 1 checksum
|
|
... unused
|
|
420/1a4 long 1 days date
|
|
424/1a8 long 1 mins time
|
|
428/1ac long 1 ticks
|
|
432/1b0 char 1 name len
|
|
433/1b1 char 30 name[] link name (dir or file name)
|
|
... unused
|
|
468/1d4 long 1 real_entry pointer to real entry
|
|
472/1d8 long 1 next_link (links chained list )
|
|
... unused
|
|
500/1f4 long 1 parent parent directory
|
|
... unused
|
|
508/1fc long 1 sec_type FILE = -4, DIR = 4
|
|
-------------------------------------------------------------------------------
|
|
|
|
A 'real' entry is a file or directory entry, opposed to link entries.
|
|
|
|
A hard link can only be created on the same disk as the real entry disk.
|
|
Several links can be made on the same real entry. This is stored with a
|
|
chained list.
|
|
'real entry' always contains the real entry block pointer.
|
|
'next_link' stores the links chaimed list.
|
|
|
|
|
|
Adding are made at head :
|
|
|
|
>ls
|
|
------rw-d 1912 15-May-96 22:28:08 real
|
|
|
|
Chained list state :
|
|
|
|
block# real next name
|
|
----------------------------
|
|
484 0 0 real
|
|
|
|
|
|
>ln real link1
|
|
>ls
|
|
------rw-d 1912 15-May-96 22:28:08 real
|
|
-H----rw-d 1912 15-May-96 22:28:10 link1 -> Empty:real
|
|
|
|
block# real next name
|
|
----------------------------
|
|
484 0 104 real
|
|
104 484 0 link1
|
|
|
|
|
|
>ln link1 link2
|
|
>ls
|
|
------rw-d 1912 15-May-96 22:28:08 real
|
|
-H----rw-d 1912 15-May-96 22:28:10 link1 -> Empty:real
|
|
-H----rw-d 1912 15-May-96 22:28:12 link2 -> Empty:real
|
|
|
|
block# real next name
|
|
----------------------------
|
|
484 0 107 real
|
|
104 484 0 link1
|
|
107 484 104 link2
|
|
|
|
|
|
The links are stored 'newest first', due to the adding at head.
|
|
|
|
real -> newest link -> ... -> oldest link -> 0
|
|
|
|
-> means "point to"
|
|
|
|
|
|
|
|
|
|
3.6.2 Soft links
|
|
----------------
|
|
|
|
|
|
* Soft link (512 bytes)
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 type T_HEADER (== 2)
|
|
4/4 long 1 self pointer
|
|
... unused
|
|
20/14 long 1 checksum
|
|
24/18 char 30 real entry name (doesn't need to exist !)
|
|
... unused
|
|
420/1a4 long 1 days creation date (access is real's)
|
|
424/1a8 long 1 mins
|
|
428/1ac long 1 ticks
|
|
... unused
|
|
500/1f4 long 1 parent parent directory
|
|
... unused
|
|
508/1fc long 1 sec_type ST_LSOFT (== 3)
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
3.7 How is the block associated to directory caching ?
|
|
------------------------------------------------------
|
|
|
|
To speed up directory listing, Directory cache blocks have been created.
|
|
Directory cache blocks are also organised in chained lists.
|
|
The list starts at the directory block (root or normal directory) with 'extension'.
|
|
|
|
* Directory cache block (512 bytes)
|
|
-------------------------------------------------------------------------------
|
|
0/0 long 1 type DIRCACHE == 33 (0x21)
|
|
4/4 long 1 header_key self pointer
|
|
8/8 long 1 parent parent directory
|
|
12/c long 1 records_nb directory entry records in this block
|
|
16/10 long 1 next_dirc dir cache chained list
|
|
20/14 long 1 chksum normal checksum
|
|
24/18 UCHAR 488 records[] entries list
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
The directory entries are stored this way :
|
|
|
|
* Directory cache block entry record (26 <= size (in bytes) <= 77)
|
|
-------------------------------------------------------------------------------
|
|
0 long 1 header entry block pointer
|
|
4 long 1 size file size (0 for a directory)
|
|
8 long 1 protect protection flags
|
|
12 long 1 unused ( == 0 )
|
|
16 short 1 days date
|
|
18 short 1 mins time
|
|
20 short 1 ticks
|
|
22 char 1 type secondary type
|
|
23 char 1 name_len >=1, <=30
|
|
? char ? name name
|
|
? char 1 comm_len >=0, <=22
|
|
? char ? comment comment
|
|
? (char) 0 optional padding byte (680x0 longs must be word aligned)
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
4. What is a blank disk ?
|
|
=========================
|
|
|
|
|
|
A minimal blank disk has a Bootblock, a Rootblock and a Bitmap block.
|
|
|
|
|
|
4.1 a Minimal blank disk
|
|
------------------------
|
|
|
|
|
|
* The Bootblock (0 and 1)
|
|
|
|
0 char 4 ID 'D''O''S' + flags (0 -> 5)
|
|
4 long 1023 full of zeros
|
|
|
|
|
|
* The Rootblock (880)
|
|
|
|
0 long 1 type 2
|
|
12/c long 1 ht_size 0x48
|
|
20/14 long 1 checksum computed
|
|
312/138 long 1 bm_flag -1 (valid bitmap)
|
|
316/13c long 1 bm_pages[0] bitmap sector #
|
|
432/1b0 char 1 disk_name size ?
|
|
433/1b1 char ? disk_name ?
|
|
472/1d8 long 1 last access date
|
|
476/1dc long 1 last access time
|
|
480/1e0 long 1 last access time
|
|
484/1e4 long 1 creation date
|
|
488/1e8 long 1 creation time
|
|
492/1ec long 1 creation time
|
|
504/1f8 long 1 FFS : first dir cache sector or 0
|
|
508/1fc long 1 sub_type 1
|
|
|
|
No specified fields are null.
|
|
|
|
|
|
* The Bitmap block (here 881) for a DD disk
|
|
|
|
0 long 1 checksum
|
|
4 long 27 free sectors 0xffffffff
|
|
112/70 long 1 root+bitmap 0xffff3fff
|
|
116/74 long 27 free sectors 0xffffffff
|
|
120/78 long 72 unused !=0
|
|
|
|
|
|
|
|
4.2 A 'Bootable' disk
|
|
---------------------
|
|
|
|
* The Bootblock becomes :
|
|
|
|
0 long 1 ID 'D''O''S' + flags
|
|
4 long 1 checksum computed
|
|
8 long 1 rootblock ? 880
|
|
12/c byte 81 bootcode AmigaDos 3.0 version
|
|
|
|
values disassembled
|
|
--------------+---------------------
|
|
43FA003E lea exp(pc),a1 ;Lib name
|
|
7025 moveq #37,d0 ;Lib version
|
|
4EAEFDD8 jsr -552(a6) ;OpenLibrary()
|
|
4A80 tst.l d0 ;error == 0
|
|
670C beq.b else
|
|
2240 move.l d0,a1 ;lib pointer
|
|
08E90006 0022 bset #6,34(a1) ; ?
|
|
4EAEFE62 jsr -414(a6) ;CloseLibrary()
|
|
43FA0018 else: lea dos(PC),a1 ;name
|
|
4EAEFFA0 jsr -96(a6) ;FindResident()
|
|
4A80 tst.l d0
|
|
670A beq.b else2 ;not found
|
|
2040 move.l d0,a0
|
|
20680016 move.l 22(a0),a0 ;DosInit sub
|
|
7000 moveq #0,d0
|
|
4E75 rts
|
|
70FF else2: moveq #-1,d0
|
|
4E75 rts
|
|
646F732E 6C696272 617279
|
|
dos: "dos.library"
|
|
00 ;padding byte
|
|
65787061 6E73696F 6E2E6C69 62726172 79
|
|
exp: "expansion.library"
|
|
|
|
93/5d byte 931 full of zeros
|
|
|
|
|
|
|
|
4.3 A Directory cache mode disk
|
|
-------------------------------
|
|
|
|
* A directory cache block (882)
|
|
|
|
0 long 1 type 0x21
|
|
4 long 1 self pointer 882
|
|
8 long 1 cached dir 880 (root)
|
|
12/c long 1 entries number 0
|
|
16/10 long 1 next dir cache 0 (last)
|
|
20/14 long 1 checksum computed
|
|
24 long 122 full of zeros
|
|
|
|
|
|
|
|
|
|
|
|
5. References
|
|
=============
|
|
|
|
|
|
* ASM Sources:
|
|
Scoopex and Crionics disassembled demo hardloaders
|
|
'the floppy disk book' copier source file, DATA BECKER books, 1988
|
|
|
|
* On-Line material :
|
|
- Very good 'ded.doc' file including Hard Disk information :
|
|
<ftp://ftp.funet.fi/pub/amiga/utilities/disk/Ded-1.11.lha>
|
|
- A clean track-loader which don't use AmigaDOS :
|
|
<ftp://ftp.wustl.edu/pub/aminet/dev/asm/t-loader.lha>
|
|
- A remplacement for 'trackdisk.device' :
|
|
<ftp://ftp.wustl.edu/pub/aminet/disk/misc/hackdisk202.lha>
|
|
- 'amigadisk_hardware.doc' (by Dave Edwards, Mark Meany, ... of ACC)
|
|
<http://home.sol.no/svjohan/assem/refs/diskHW.lha>
|
|
|
|
* Books :
|
|
Rom Kernel Reference Manual : Hardware, pages 235-244, Addison Wesley
|
|
La Bible de l'Amiga, Dittrich/Gelfand/Schemmel, Data Becker, 1988.
|
|
|
|
|
|
The AmigaDos reference manual must contains a lot of information about Amiga
|
|
file systems, but i don't own it (Addison Wesley).
|
|
|
|
|
|
|
|
6. C Routines
|
|
================
|
|
|
|
Unix system routines yet implemented :
|
|
cd, pwd, getdent, ls, ls -l, ls -r, open, close, read, stat.
|
|
|
|
newfs, mount, umount.
|
|
|
|
Links and directory cache are supported.
|
|
|
|
Soon available on good FTP servers.
|
|
|
|
|
|
|
|
7. Other Amiga FileSystems
|
|
========================
|
|
|
|
|
|
An Amiga filesystem for Linux 0.99pl2 by Ray Burr (read only, hard disk support) :
|
|
<ftp://tsx-11.mit.edu/pub/linux/patches/amigaffs.tar.Z>
|
|
An enhanced version for Linux 1.2.13 by Hans-Joachim "JBHR" Widmaier (RDSK, links and
|
|
international mode supported):
|
|
<ftp://ftp.ibp.fr/pub/linux/sunsite/system/filesystems/jb-affs-1.0.tar.gz>
|
|
A .ADF manipulation package for DOS/Windows, "ADF-suite" (GUI, no sources included):
|
|
<http://www.geocities.com/SiliconValley/Lakes/7094/index.html>
|
|
|
|
|
|
8. To do
|
|
========
|
|
|
|
- Hard disk support
|
|
- Disk salvaging
|
|
- Undelete files
|
|
- ADF monitor/editor
|
|
|
|
-------------------------------------------------------------------------------
|
|
'FAQ : THE AMIGA FLOPPY DISK FORMAT' ends here !
|
|
|