Rigid Disk Block (RDB)¶
The Rigid Disk Block is the Amiga's on-disk description of a hard disk: drive geometry, partitions, the file systems needed to mount them, bad block maps and optional drive initialisation code. It is not part of any DOS volume — the blocks live in a reserved area at the start of the drive and are read by the controller's boot ROM, by Mounter, by HDToolBox and by any tool that needs to know how a disk is partitioned. All structures are defined in devices/hardblocks.h (47.1 in NDK 3.2); the partition environment uses struct DosEnvec from dos/filehandler.h.
This article documents the five block types, the checksum, the partition environment vector and how the OS 3.2 tools use them.
Layout overview¶
The header comment of hardblocks.h sets the rules:
- The
RigidDiskBlockmust exist "within the firstRDB_LOCATION_LIMITblocks" (RDB_LOCATION_LIMIT= 16). - Storing RDB data in the reserved blocks of a partition "is discouraged since the reserved blocks of a partition are overwritten by Format, Install, DiskCopy, etc." The recommended layout uses "the first cylinder(s) to store all the drive data specified by these blocks: i.e. partition descriptions, file system load images, drive bad block maps, spare blocks, etc."
- All blocks are "tagged with a unique identifier, checksummed, and linked together".
- Block pointers are block numbers, not byte offsets. Optional block addresses contain
$ffffffffto mean NULL, "as zero is a valid address". - The description works "with any block size"; array sizes in the structures (
[61],[123]) assume 512-byte blocks. 256-byte sectors are deprecated.
block 0..15 RDSK ──┬─ rdb_PartitionList ──► PART ─► PART ─► ... (pb_Next)
(first match) ├─ rdb_FileSysHeaderList ► FSHD ─► FSHD ─► ... (fhb_Next)
│ └─ fhb_SegListBlocks ► LSEG ─► LSEG ...
├─ rdb_BadBlockList ─────► BADB ─► BADB ─► ... (bbb_Next)
└─ rdb_DriveInit ────────► LSEG chain (drive init code)
Block identifiers¶
| Constant | Value | ASCII | Structure |
|---|---|---|---|
IDNAME_RIGIDDISK 1.3+ |
0x5244534B |
RDSK |
struct RigidDiskBlock |
IDNAME_PARTITION 1.3+ |
0x50415254 |
PART |
struct PartitionBlock |
IDNAME_FILESYSHEADER 1.3+ |
0x46534844 |
FSHD |
struct FileSysHeaderBlock |
IDNAME_LOADSEG 1.3+ |
0x4C534547 |
LSEG |
struct LoadSegBlock |
IDNAME_BADBLOCK 1.3+ |
0x42414442 |
BADB |
struct BadBlockBlock |
Every block starts with the same four longwords:
| Offset | Field | Meaning |
|---|---|---|
| 0x00 | xx_ID |
4-character identifier above |
| 0x04 | xx_SummedLongs |
size of the checksummed structure, in longwords |
| 0x08 | xx_ChkSum |
"block checksum (longword sum to zero)" |
| 0x0C | xx_HostID |
SCSI Target ID of host |
Checksum¶
The checksum covers the first SummedLongs longwords of the block. ChkSum is chosen so that the 32-bit sum of all of them, including ChkSum itself, is zero:
#include <exec/types.h>
/* Returns TRUE if the RDB-style block is intact. */
BOOL CheckRDBSum(const ULONG *block, ULONG blockbytes)
{
ULONG n = block[1]; /* xx_SummedLongs */
ULONG sum = 0;
if (n == 0 || n > blockbytes / 4)
return FALSE; /* claims more data than the block holds */
while (n--)
sum += *block++;
return sum == 0;
}
/* Recompute the checksum before writing a modified block. */
void FixRDBSum(ULONG *block)
{
ULONG n = block[1], i, sum = 0;
block[2] = 0;
for (i = 0; i < n; i++)
sum += block[i];
block[2] = (ULONG)(-(LONG)sum);
}
The n > blockbytes / 4 guard mirrors HDToolBox 40.14: "RDB blocks which report themselves as containing more data than was read from the medium are now ignored and their block checksums are no longer verified."
RigidDiskBlock (RDSK)¶
struct RigidDiskBlock {
ULONG rdb_ID; /* 'RDSK' */
ULONG rdb_SummedLongs;
LONG rdb_ChkSum;
ULONG rdb_HostID;
ULONG rdb_BlockBytes; /* size of disk blocks */
ULONG rdb_Flags; /* RDBF_* */
/* block list heads */
ULONG rdb_BadBlockList; /* optional bad block list */
ULONG rdb_PartitionList; /* optional first partition block */
ULONG rdb_FileSysHeaderList;/* optional file system header block */
ULONG rdb_DriveInit; /* optional drive-specific init code */
/* DriveInit(lun,rdb,ior): "C" stk & d0/a0/a1 */
ULONG rdb_Reserved1[6]; /* set to $ffffffff */
/* physical drive characteristics */
ULONG rdb_Cylinders, rdb_Sectors, rdb_Heads, rdb_Interleave, rdb_Park;
ULONG rdb_Reserved2[3];
ULONG rdb_WritePreComp, rdb_ReducedWrite, rdb_StepRate;
ULONG rdb_Reserved3[5];
/* logical drive characteristics */
ULONG rdb_RDBBlocksLo; /* low block of range reserved for hardblocks */
ULONG rdb_RDBBlocksHi; /* high block of range for these hardblocks */
ULONG rdb_LoCylinder; /* low cylinder of partitionable disk area */
ULONG rdb_HiCylinder; /* high cylinder of partitionable data area */
ULONG rdb_CylBlocks; /* number of blocks available per cylinder */
ULONG rdb_AutoParkSeconds; /* zero for no auto park */
ULONG rdb_HighRDSKBlock; /* highest block used by RDSK */
ULONG rdb_Reserved4;
/* drive identification */
char rdb_DiskVendor[8], rdb_DiskProduct[16], rdb_DiskRevision[4];
char rdb_ControllerVendor[8], rdb_ControllerProduct[16], rdb_ControllerRevision[4];
char rdb_DriveInitName[40];/* filename of DriveInit source, terminated */
};
(The header declares each field on its own line; they are grouped here for brevity, order unchanged.)
rdb_DriveInitName and fhb_FileSysName are the 47.1 additions: storage "for inserting the actual source filename for files installed on the RDBs as either DriveInit code or Filesystem code. This makes creating a mountfile suitable for use with the C:Mount command" possible.
rdb_Flags¶
| Bit | Flag | Meaning |
|---|---|---|
| 0 | RDBFF_LAST (0x01) |
no disks exist to be configured after this one on this controller |
| 1 | RDBFF_LASTLUN (0x02) |
no LUNs exist greater than this one at this SCSI Target ID |
| 2 | RDBFF_LASTTID (0x04) |
no Target IDs exist greater than this one on this SCSI bus |
| 3 | RDBFF_NORESELECT (0x08) |
don't try reselection when talking to this drive |
| 4 | RDBFF_DISKID (0x10) |
rdb_Disk... identification valid |
| 5 | RDBFF_CTRLRID (0x20) |
rdb_Controller... identification valid |
| 6 | RDBFF_SYNCH (0x40) |
drive supports SCSI synchronous mode — "CAN BE DANGEROUS TO USE IF IT DOESN'T!" |
PartitionBlock (PART)¶
struct PartitionBlock {
ULONG pb_ID; /* 'PART' */
ULONG pb_SummedLongs;
LONG pb_ChkSum;
ULONG pb_HostID;
ULONG pb_Next; /* block number of the next PartitionBlock */
ULONG pb_Flags; /* PBFF_* */
ULONG pb_Reserved1[2];
ULONG pb_DevFlags; /* preferred flags for OpenDevice */
UBYTE pb_DriveName[32]; /* preferred DOS device name: BSTR form */
ULONG pb_Reserved2[15]; /* filler to 32 longwords */
ULONG pb_Environment[20]; /* environment vector for this partition */
ULONG pb_EReserved[12]; /* reserved for future environment vector */
};
| Bit | Flag | Meaning |
|---|---|---|
| 0 | PBFF_BOOTABLE (1) |
partition intended to be bootable (expected directories and files exist) |
| 1 | PBFF_NOMOUNT (2) |
do not mount this partition (e.g. manually mounted, but space reserved here) |
pb_DriveName is a BSTR (length byte first) and is "not used if this name is in use".
The environment vector¶
pb_Environment[20] has exactly the layout of struct DosEnvec from dos/filehandler.h — the same vector a mountlist produces and that file systems find through FileSysStartupMsg.fssm_Environ. de_TableSize holds the number of longwords that follow (the header: "set to the number of longwords in the table minus 1"; minimum 11).
| Index | DE_* constant |
DosEnvec field |
Mountlist keyword (where listed in the Mountlist reference) | Notes from filehandler.h |
|---|---|---|---|---|
| 0 | DE_TABLESIZE |
de_TableSize |
— | size of vector |
| 1 | DE_SIZEBLOCK |
de_SizeBlock |
SectorSize (÷4) |
in longwords; standard 128 |
| 2 | DE_SECORG |
de_SecOrg |
— | not used; must be 0 |
| 3 | DE_NUMHEADS |
de_Surfaces |
Surfaces |
heads |
| 4 | DE_SECSPERBLK |
de_SectorPerBlock |
— | physical sectors per logical block |
| 5 | DE_BLKSPERTRACK |
de_BlocksPerTrack |
BlocksPerTrack |
|
| 6 | DE_RESERVEDBLKS |
de_Reserved |
Reserved |
reserved blocks at start, usually 2 |
| 7 | DE_PREFAC |
de_PreAlloc |
— | reserved blocks at end |
| 8 | DE_INTERLEAVE |
de_Interleave |
Interleave |
low 16 bits usually 0; upper bits = ENVF_* flags |
| 9 | DE_LOWCYL |
de_LowCyl |
LowCyl |
|
| 10 | DE_UPPERCYL |
de_HighCyl |
HighCyl |
|
| 11 | DE_NUMBUFFERS |
de_NumBuffers |
Buffers |
|
| 12 | DE_BUFMEMTYPE |
de_BufMemType |
BufMemType |
"1 is public, 3 is chip, 5 is fast" |
| 13 | DE_MAXTRANSFER |
de_MaxTransfer |
MaxTransfer |
max bytes per transfer |
| 14 | DE_MASK |
de_Mask |
Mask |
DMA address mask |
| 15 | DE_BOOTPRI |
de_BootPri |
BootPri |
|
| 16 | DE_DOSTYPE |
de_DosType |
DosType |
must match an FSHD fhb_DosType |
| 17 | DE_BAUD |
de_Baud |
— | serial handlers |
| 18 | DE_CONTROL |
de_Control |
— | |
| 19 | DE_BOOTBLOCKS |
de_BootBlocks |
— | blocks containing boot code |
Partition size in bytes is (HighCyl − LowCyl + 1) × Surfaces × BlocksPerTrack × SizeBlock × 4. HDToolBox and Mounter compute this in 64 bits (Mounter 44.7 "Partition size calculation now also takes the number of sectors per block into account").
Interleave flags (3.1.4+)¶
| Flag | Value | Effect |
|---|---|---|
ENVF_SCSIDIRECT 3.2+ |
1L << 16 |
"Use HD_SCSICMD instead of TD_READ/TD_READ64" |
ENVF_SUPERFLOPPY 3.2+ |
1L << 17 |
"Request device geometry by TD_GETGEOMETRY or HD_SCSICMD, DE_LOWCYL must be 0" |
ENVF_DISABLENSD 3.2+ |
1L << 18 |
"Do not query for NSD" |
See OS 3.2 File System and Handler Changes for how FFS interprets them.
FileSysHeaderBlock (FSHD)¶
An FSHD block describes a file system stored on the disk. Its layout after the common header mirrors a DOS DeviceNode (and struct FileSysEntry in resources/filesysres.h):
struct FileSysHeaderBlock {
ULONG fhb_ID; /* 'FSHD' */
ULONG fhb_SummedLongs;
LONG fhb_ChkSum;
ULONG fhb_HostID;
ULONG fhb_Next; /* block number of next FileSysHeaderBlock */
ULONG fhb_Flags;
ULONG fhb_Reserved1[2];
ULONG fhb_DosType; /* match with partition's DE_DOSTYPE */
ULONG fhb_Version; /* release version of this code */
ULONG fhb_PatchFlags; /* which of the following to substitute */
ULONG fhb_Type; /* device node type: zero */
ULONG fhb_Task; /* standard dos "task" field: zero */
ULONG fhb_Lock; /* not used for devices: zero */
ULONG fhb_Handler; /* filename to loadseg: zero placeholder */
ULONG fhb_StackSize; /* stacksize to use when starting task */
LONG fhb_Priority; /* task priority when starting task */
LONG fhb_Startup; /* startup msg: zero placeholder */
LONG fhb_SegListBlocks; /* first of linked list of LoadSegBlocks */
LONG fhb_GlobalVec; /* BCPL global vector when starting task */
ULONG fhb_Reserved2[23]; /* (those reserved by PatchFlags) */
char fhb_FileSysName[84]; /* File system file name as loaded. */
};
fhb_PatchFlags¶
Bit n of fhb_PatchFlags means "substitute the n-th longword starting at fhb_Type into the device node". The header's example is 0x180 "to substitute SegList & GlobalVec":
| Bit | Mask | FSHD field | DeviceNode field |
|---|---|---|---|
| 0 | 0x001 |
fhb_Type |
dn_Type |
| 1 | 0x002 |
fhb_Task |
dn_Task |
| 2 | 0x004 |
fhb_Lock |
dn_Lock |
| 3 | 0x008 |
fhb_Handler |
dn_Handler |
| 4 | 0x010 |
fhb_StackSize |
dn_StackSize |
| 5 | 0x020 |
fhb_Priority |
dn_Priority |
| 6 | 0x040 |
fhb_Startup |
dn_Startup |
| 7 | 0x080 |
fhb_SegListBlocks |
dn_SegList (after loading the LSEG chain) |
| 8 | 0x100 |
fhb_GlobalVec |
dn_GlobalVec |
Remember the stack unit rule when filling fhb_StackSize: LONGs for BCPL handlers (GlobVec 0), bytes for C handlers (GlobVec −1). HDToolBox 46.23 now writes a larger default than 600 bytes. FFS 46.20 changed its own patch flags "to also include a larger stack size, and to signal a C type mount."
HDToolBox 46.24 stopped generating patch flags that override the block size when the user selected a block size other than 512: such an entry made the file system use that size "regardless of what the RDB has to say about it, potentially causing invalid mounts or even data loss."
LoadSegBlock (LSEG)¶
struct LoadSegBlock {
ULONG lsb_ID; /* 'LSEG' */
ULONG lsb_SummedLongs;
LONG lsb_ChkSum;
ULONG lsb_HostID;
ULONG lsb_Next; /* block number of the next LoadSegBlock */
ULONG lsb_LoadData[123]; /* data for "loadseg" (512-byte blocks) */
};
The lsb_LoadData of consecutive blocks, concatenated, form an ordinary hunk executable. A loader feeds this stream to InternalLoadSeg() with a custom read function. Mounter 44.4 records the trap: the read function in the function table "was following the dos.library documentation and would consequently use the wrong registers" — LSEG loading in Mounter was broken until that fix. The header asks that LSEG blocks "make most efficient use of the disk block size possible".
BadBlockBlock (BADB)¶
struct BadBlockEntry {
ULONG bbe_BadBlock; /* block number of bad block */
ULONG bbe_GoodBlock; /* block number of replacement block */
};
struct BadBlockBlock {
ULONG bbb_ID; /* 'BADB' */
ULONG bbb_SummedLongs;
LONG bbb_ChkSum;
ULONG bbb_HostID;
ULONG bbb_Next; /* block number of the next BadBlockBlock */
ULONG bbb_Reserved;
struct BadBlockEntry bbb_BlockPairs[61]; /* assumes 512-byte blocks */
};
rdb_HighRDSKBlock is the highest block used by the RDB structures "not including replacement bad blocks".
DOS types¶
fhb_DosType and de_DosType use the identifiers from dos/dos.h:
| Constant | Value | ASCII | File system |
|---|---|---|---|
ID_DOS_DISK 1.3+ |
0x444F5300 |
DOS\0 |
OFS |
ID_FFS_DISK 2.0+ |
0x444F5301 |
DOS\1 |
FFS |
ID_INTER_DOS_DISK 2.0+ |
0x444F5302 |
DOS\2 |
OFS, international |
ID_INTER_FFS_DISK 2.0+ |
0x444F5303 |
DOS\3 |
FFS, international |
ID_FASTDIR_DOS_DISK 3.1+ |
0x444F5304 |
DOS\4 |
OFS, directory cache |
ID_FASTDIR_FFS_DISK 3.1+ |
0x444F5305 |
DOS\5 |
FFS, directory cache |
ID_LONG_DOS_DISK 3.2+ |
0x444F5306 |
DOS\6 |
OFS, long file names (FFS V46+) |
ID_LONG_FFS_DISK 3.2+ |
0x444F5307 |
DOS\7 |
FFS, long file names (FFS V46+) |
dos/dos.h also defines ID_COMPLONG_FFS_DISK (DOS\8), the identifier of the earlier long-name FFS variant that HDToolBox 46.1 replaced with DOS\6/DOS\7. HDToolBox 46.8 made DOS\3 the default for newly created drive types. The 3.9-era method of selecting SCSI direct access by setting bit 7 of the DOS type (0x444F5383) is phased out in favour of ENVF_SCSIDIRECT.
The partition's own boot block (first reserved block) carries the DOS type as well; devices/bootblock.h defines BBID_DOS / BBNAME_DOS (0x444F5300), BBID_KICK / BBNAME_KICK (0x4B49434B) and BOOTSECTS = 2 ("1K bootstrap"):
struct BootBlock {
UBYTE bb_id[4]; /* 4 character identifier */
LONG bb_chksum; /* boot block checksum (balance) */
LONG bb_dosblock; /* reserved for DOS patch */
};
Reading the RDB: example¶
The program reads the first RDB_LOCATION_LIMIT blocks of a unit, locates RDSK, and lists the partitions. It assumes 512-byte blocks; production code must handle rdb_BlockBytes (HDToolBox 40.13: the RDB is read "with each block containing as many bytes as there are in a sector", so the RDB "is by no means independent of the medium which it is stored on").
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/hardblocks.h>
#include <dos/filehandler.h>
#include <proto/exec.h>
#include <proto/dos.h>
#define BSIZE 512
static BOOL ReadBlock(struct IOStdReq *io, ULONG blk, APTR buf)
{
io->io_Command = CMD_READ;
io->io_Data = buf;
io->io_Length = BSIZE;
io->io_Offset = blk * BSIZE; /* < 4 GB only; use TD64/NSD beyond */
return (BOOL)(DoIO((struct IORequest *)io) == 0);
}
static BOOL SumOK(const ULONG *b)
{
ULONG n = b[1], s = 0;
if (n == 0 || n > BSIZE / 4) return FALSE;
while (n--) s += *b++;
return (BOOL)(s == 0);
}
int main(void)
{
struct MsgPort *port;
struct IOStdReq *io;
ULONG *buf;
ULONG blk, part;
char name[32];
if (!(port = CreateMsgPort())) return 20;
if ((io = (struct IOStdReq *)CreateIORequest(port, sizeof(*io))) != NULL)
{
/* MEMF_PUBLIC is not enough for every controller: honour the
partition Mask/BufMemType in real code */
if ((buf = AllocVec(BSIZE, MEMF_PUBLIC)) != NULL)
{
if (OpenDevice("scsi.device", 0, (struct IORequest *)io, 0) == 0)
{
for (blk = 0; blk < RDB_LOCATION_LIMIT; blk++)
if (ReadBlock(io, blk, buf) && buf[0] == IDNAME_RIGIDDISK && SumOK(buf))
break;
if (blk < RDB_LOCATION_LIMIT)
{
struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)buf;
Printf("RDSK at block %lu: %lu cyl, %lu heads, %lu sectors\n",
blk, rdb->rdb_Cylinders, rdb->rdb_Heads, rdb->rdb_Sectors);
part = rdb->rdb_PartitionList;
while (part != 0xFFFFFFFF && ReadBlock(io, part, buf))
{
struct PartitionBlock *pb = (struct PartitionBlock *)buf;
ULONG *env = pb->pb_Environment;
ULONG len;
if (pb->pb_ID != IDNAME_PARTITION || !SumOK(buf))
break;
len = pb->pb_DriveName[0] < 31 ? pb->pb_DriveName[0] : 31;
CopyMem(&pb->pb_DriveName[1], name, len);
name[len] = '\0';
Printf("%-8s cyl %lu-%lu DosType 0x%08lx%s%s\n", name,
env[DE_LOWCYL], env[DE_UPPERCYL],
env[DE_TABLESIZE] >= DE_DOSTYPE ? env[DE_DOSTYPE] : 0,
(pb->pb_Flags & PBFF_BOOTABLE) ? " bootable" : "",
(pb->pb_Flags & PBFF_NOMOUNT) ? " nomount" : "");
part = pb->pb_Next;
}
}
else
PutStr("No RDB found\n");
CloseDevice((struct IORequest *)io);
}
FreeVec(buf);
}
DeleteIORequest((struct IORequest *)io);
}
DeleteMsgPort(port);
return 0;
}
How the OS 3.2 tools use the RDB¶
Boot ROM and system-startup¶
Autobooting controllers add their partitions as boot nodes to ExpansionBase's MountList. In 3.2, system-startup mounts those nodes (dos 47.9 no longer does) and, if a newer FFS than the ROM one is found in the RDB, places it in FileSystem.resource and the resident list. The FFS itself is upgraded only from the RDB, "not from file" (system-startup autodoc). See OS 3.2 Boot Process.
Mounter¶
Mounter (Workbench tool, 3.2 version 47.x) mounts partitions from an RDB that were not mounted at boot.
- LSEG loading via
InternalLoadSeg()fixed in 44.4 (see above). - Media size via the SCSI "Read capacity" command (44.6), 64-bit size math (44.7).
- On remount, checks whether the previously active partition will let go of its volume (51.5).
NOREMOUNToption (44.3); LUN support (47.3); requires dos.library V37+.
HDToolBox¶
HDToolBox writes the RDB. Behaviour and options documented in the release notes:
| Item | Detail |
|---|---|
LIMIT_4GBYTE |
Limit partitions to the first 4 GB; values on/off, true/false, yes/no (47.8). Automatically applied for scsi.device < 43. |
MIN_SECTOR_SIZE, MAX_SECTOR_SIZE |
Override the sector size reported by the drive; multiple of 512 and power of two. Cylinders are adjusted to preserve medium size. |
MASK, BUFMEMTYPE, MAXTRANSFER |
Override defaults written into partitions; hexadecimal, 0x or $ prefix accepted (46.13). HDToolBox honours MASK/BUFMEMTYPE for its own I/O. |
SKIP_WRITE_PROTECTED |
Skip updating the drive definitions file if it or its volume is write-protected (47.7). |
| SCSI direct | Checkmark gadget to enable SCSI direct transfers (40.23). |
| Long names / dir cache | File system type gadgets for DOS\6/DOS\7 (46.1) and DOS\4/DOS\5. |
| BufMemType | Editable per partition (47.1). |
| Large disks | With more than 2^23 blocks the default block size is 4096 (8 sectors per block), and blocks per cylinder are rounded to a multiple of 8 so partitions start on 4K boundaries (46.21). |
| RDB area | The first partition's low cylinder is chosen so the RDB area is at least 128K (46.20) instead of always cylinder 2. |
| FFS | No warning if the ROM contains FFS ≥ V45 and the disk has none (46.11). |
See Also¶
- OS 3.2 File System and Handler Changes
- IFF Format and Mountlist Reference
- AmigaDOS Filesystem On-Disk Structures
- ADF Format Specification
- The AmigaDOS Device List
- 64-bit Storage Access (TrackDisk64)
- New Style Device Commands
- FileSystem.resource Reference
- expansion.library Reference
Sources: NDK 3.2 R4 Include_H/devices/hardblocks.h, devices/bootblock.h, dos/filehandler.h, dos/dos.h, resources/filesysres.h, libraries/expansionbase.h; ReleaseNotes/HDToolBox-RelNotes, Mounter-RelNotes, FastFileSystem-RelNotes, system-startup-RelNotes, DOS-RelNotes; Autodocs/system-startup.doc Updated: 2026-11-13