Copyright (c) Hyperion Entertainment and contributors.

Expansion Library

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
Warning.png This page is not yet fully updated to AmigaOS 4.x some of the information contained here may not be applicable in part or totally.
Codereview.png Code samples on this page are not yet updated to AmigaOS 4.x some of them may be obsolete or incompatible with AmigaOS 4.x.

Expansion Library

Amiga RAM expansion boards and other expansion bus peripherals are designed to reside at dynamically assigned address spaces within the system. The configuration and initialization of these expansion peripherals is performed by the expansion.library.

AUTOCONFIG

The Amiga AUTOCONFIG protocol is designed to allow the dynamic assignment of available address space to expansion boards, eliminating the need for user configuration via jumpers. Such expansion boards include memory boards, hard disk controllers, network interfaces, and other special purpose expansion devices. Some expansion devices, such as RAM boards, require no special driver software. Other types of expansion devices may use a disk-loaded driver from the "DEVS:" or "SYS:Expansion drawer", or an on-board ROM driver (for example, a self-booting hard disk controller).

This article will concentrate on the software and driver side of Zorro expansion devices, using a Zorro-II device as an example. Zorro-III devices have additional identifying bits and memory size options which are described in the Zorro-III hardware documentation. For more information on Zorro-II and Zorro-III expansion hardware, see the "Zorro Expansion Bus" appendix of the Amiga Hardware Reference Manual, 3rd Edition. For additional information specific to Zorro-II boards, see the A500/A2000 Technical Reference Manual.

AUTOCONFIG occurs whenever the Amiga is powered on or reset. During early system initialization, expansion.library identifies the expansion boards that are installed in the Amiga and dynamically assigns an appropriate address range for each board to reside at. During this AUTOCONFIG process, each expansion board first appears in turn at $E80000 (Zorro-II) or $FF000000 (Zorro-III), presenting readable identification information, generally in a PAL or a ROM, at the beginning of the board. The identification includes the size of the board, its address space preferences, type of board (memory or other), and a unique Hardware Manufacturer Number assigned by the AmigaOS development team.

The unique Hardware Manufacturer number, in combination with a vendor-supplied product number, provides a way for boards to be identified and for disk-based drivers to be matched with expansion boards. All expansion boards for the Amiga must implement the AUTOCONFIG protocol.

The Expansion Sequence

During system initialization, expansion.library configures each expansion peripheral in turn by examining its identification information and assigning it an appropriate address space. If the board is a RAM board, it can be added to the system memory list to make the RAM available for allocation by system tasks.

Descriptions of all configured boards are kept in a private ExpansionBase list of ConfigDev structures. A board's identification information is stored in the ExpansionRom sub-structure contained within the ConfigDev structure. Applications can examine individual or all ConfigDev structures with the expansion.library function FindConfigDev().

The ConfigDev structure is defined in <libraries/configvars.h>:

struct ConfigDev {
    struct Node         cd_Node;
    UBYTE               cd_Flags;     /* (read/write) */
    UBYTE               cd_Pad;       /* reserved */
    struct ExpansionRom cd_Rom;       /* copy of board's expansion ROM */
    APTR                cd_BoardAddr; /* where in memory the board was placed */
    ULONG               cd_BoardSize; /* size of board in bytes */
    UWORD               cd_SlotAddr;  /* which slot number (PRIVATE) */
    UWORD               cd_SlotSize;  /* number of slots (PRIVATE) */
    APTR                cd_Driver;    /* pointer to node of driver */
    struct ConfigDev    *cd_NextCD;   /* linked list of drivers to config */
    ULONG               cd_Unused[4]; /* for whatever the driver wants */
};
 
/* cd_Flags */
#define CDB_SHUTUP      0       /* this board has been shut up */
#define CDB_CONFIGME    1       /* this board needs a driver to claim it */
#define CDF_SHUTUP      0x01
#define CDF_CONFIGME    0x02

The ExpansionRom structure within ConfigDev contains the board identification information that is read from the board's PAL or ROM at expansion time. The actual onboard identification information of a Zorro-II board appears in the high nibbles of the first $40 words at the start of the board. Except for the first nibble pair ($00/$02) which when combined form er_Type, the information is stored in inverted ("ones-complement") format where binary 1's are represented as 0's and 0's are represented as 1's. The expansion.library reads the nibbles of expansion information from the board, un-inverts them (except for $00/$02 er_Type which is already un-inverted), and combines them to form the elements of the ExpansionRom structure.

The ExpansionRom structure is defined in <libraries/configregs.h>:

struct ExpansionRom {            /* First 16 bytes of the expansion ROM */
    UBYTE       er_Type;         /* Board type, size and flags */
    UBYTE       er_Product;      /* Product number, assigned by manufacturer */
    UBYTE       er_Flags;        /* Flags */
    UBYTE       er_Reserved03;   /* Must be zero ($ff inverted) */
    UWORD       er_Manufacturer; /* Unique ID,ASSIGNED BY AmigaOS development team */
    ULONG       er_SerialNumber; /* Available for use by manufacturer */
    UWORD       er_InitDiagVec;  /* Offset to optional &quot;DiagArea&quot; structure */
    UBYTE       er_Reserved0c;
    UBYTE       er_Reserved0d;
    UBYTE       er_Reserved0e;
    UBYTE       er_Reserved0f;
};

Simple Expansion Library Example

The following example uses FindConfigDev() to print out information about all of the configured expansion peripherals in the system. FindConfigDev() searches the system's list of ConfigDev structures and returns a pointer to the ConfigDev structure matching a specified board:

newconfigdev = struct ConfigDev *FindConfigDev( struct ConfigDev *oldconfigdev,
                                                LONG manufacturer, LONG product )

The oldconfigdev argument may be set to NULL to begin searching at the top of the system list or, if it points to a valid ConfigDev, searching will begin after that entry in the system list. The manufacturer and product arguments can be set to search for a specific manufacturer and product by number, or, if these are set to -1, the function will match any board.

;/* findboards.c - Execute me to compile me with SAS C 5.10
LC -b1 -cfistq -v -y -j73 findboards.c
Blink FROM LIB:c.o,findboards.o TO findboards LIBRARY LIB:LC.lib,LIB:Amiga.lib
quit
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/configvars.h>

#include <clib/exec_protos.h>
#include <clib/expansion_protos.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef LATTICE
int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#endif

struct Library   *ExpansionBase = NULL;

void main(int argc, char **argv)
{
    struct ConfigDev *myCD;
    UWORD m,i;
    UBYTE p,f,t;

    if((ExpansionBase=OpenLibrary("expansion.library",0L))==NULL)
        exit(RETURN_FAIL);

    /*--------------------------------------------------*/
    /* FindConfigDev(oldConfigDev,manufacturer,product) */
    /* oldConfigDev = NULL for the top of the list      */
    /* manufacturer = -1 for any manufacturer           */
    /* product      = -1 for any product                */
    /*--------------------------------------------------*/

    myCD = NULL;
    while(myCD=FindConfigDev(myCD,-1L,-1L)) /* search for all ConfigDevs */
        {
        printf("\n---ConfigDev structure found at location $%lx---\n",myCD);

        /* These values were read directly from the board at expansion time */
        printf("Board ID (ExpansionRom) information:\n");

        t = myCD->cd_Rom.er_Type;
        m = myCD->cd_Rom.er_Manufacturer;
        p = myCD->cd_Rom.er_Product;
        f = myCD->cd_Rom.er_Flags;
        i = myCD->cd_Rom.er_InitDiagVec;

        printf("er_Manufacturer         =%d=$%04x=(~$%4x)\n",m,m,(UWORD)~m);
        printf("er_Product              =%d=$%02x=(~$%2x)\n",p,p,(UBYTE)~p);

        printf("er_Type                 =$%02x",myCD->cd_Rom.er_Type);
        if(myCD->cd_Rom.er_Type & ERTF_MEMLIST)
            printf("  (Adds memory to free list)\n");
        else printf("\n");

        printf("er_Flags                =$%02x=(~$%2x)\n",f,(UBYTE)~f);
        printf("er_InitDiagVec          =$%04x=(~$%4x)\n",i,(UWORD)~i);


        /* These values are generated when the AUTOCONFIG(tm) software
         * relocates the board
         */
        printf("Configuration (ConfigDev) information:\n");
        printf("cd_BoardAddr            =$%lx\n",myCD->cd_BoardAddr);
        printf("cd_BoardSize            =$%lx (%ldK)\n",
               myCD->cd_BoardSize,((ULONG)myCD->cd_BoardSize)/1024);

        printf("cd_Flags                =$%x",myCD->cd_Flags);
        if(myCD->cd_Flags & CDF_CONFIGME)
            printf("\n");
        else printf("  (driver clears CONFIGME bit)\n");
        }
    CloseLibrary(ExpansionBase);
}

Expansion Board Drivers

The Amiga operating system contains support for matching up disk-based drivers with AUTOCONFIG boards. Though such drivers are commonly Exec devices, this is not required. The driver may, for instance, be an Exec library or task. The system software also supports the initialization of onboard ROM driver software.

Disk Based Drivers

Disk-based expansion board drivers and their icons are generally placed in the "SYS:Expansion" drawer of the user's "SYS:" disk or partition. The icon Tool Type field must contain the unique Hardware Manufacturer number, and the Product number of the expansion board(s) the driver is written for. (For more about icon Tool Type fields refer to Icon Library.)

The "BindDrivers" command issued during the disk startup-sequence attempts to match disk-based drivers with their expansion boards. To do this, "BindDrivers" looks in the Tool Types field of all icon files in "SYS:Expansion". If the Tool Type "PRODUCT" is found in the icon, then this is an icon file for a driver. "BindDrivers" will then attempt to match the manufacturer and product number in this PRODUCT Tool Type with those of a board that was configured at expansion time.

For example, suppose you are manufacturer #1019. You have two products, #1 and #2 which both use the same driver. The icon for your driver for these two products would have a Tool Type set to "PRODUCT=1019/1|1019/2". This means: I am an icon for a driver that works with product number 1 or 2 from manufacturer 1019, now bind me. Spaces are not legal. Here are two other examples:

PRODUCT=1208/11 is the Tool Type for a driver for product 11 from manufacturer number 1208.
PRODUCT=1017 is the Tool Type for a driver for any product from manufacturer number 1017.

If a matching board is found for the disk-based driver, the driver code is loaded and then initialized with the Exec InitResident() function. From within its initialization code, the driver can get information about the board it is bound to by calling the expansion.library function GetCurrentBinding(). This function will provide the driver with a copy of a CurrentBinding structure, including a pointer to a ConfigDev structure (possibly linked to additional ConfigDevs via the cd_NextCD field) of the expansion board(s) that matched the manufacturer and product IDs.

/* this structure is used by GetCurrentBinding() and SetCurrentBinding() */
struct CurrentBinding {
    struct ConfigDev *cb_ConfigDev;          /* first configdev in chain */
    UBYTE *           cb_FileName;           /* file name of driver */
    UBYTE *           cb_ProductString;      /* product # string */
    UBYTE **          cb_ToolTypes;          /* tooltypes from disk object */
};

GetCurrentBinding() allows the driver to find out the base address and other information about its board(s). The driver must unset the CONFIGME bit in the cd_Flags field of the ConfigDev structure for each board it intends to drive, and record the driver's Exec node pointer in the cd_Driver structure. This node should contain the LN_NAME and LN_TYPE (i.e., NT_DEVICE, NT_TASK, etc.) of the driver.

Important Note
The GetCurrentBinding() function, and driver binding in general, must be bracketed by an ObtainConfigBinding() and ReleaseConfigBinding() semaphore. The "BindDrivers" command obtains this semaphore and performs a SetCurrentBinding() before calling InitResident(), allowing the driver to simply do a GetCurrentBinding().

Full source code for a disk-based Expansion or "DEVS:" sample device driver may be found in Example Device. Autodocs for expansion.library functions may be found in the SDK.

Expansion Drivers and DOS

Two other expansion.library functions commonly used by expansion board drivers are MakeDosNode() and AddDosNode(). These functions allow a driver to create and add a DOS device node (for example "DH0:") to the system. AddBootNode() can be used to add an autobooting DOS device node.

MakeDosNode() requires an initialized structure of environment information for creating a DOS device node. The format of the function is:

struct DeviceNode *deviceNode = MakeDosNode(parameterPkt);

The parameterPkt argument is a pointer to an initialized packet of environment parameters.

The parameter packet for MakeDosNode() consists of four longwords followed by a DosEnvec structure:

;-----------------------------------------------------------------------
;
; Layout of parameter packet for MakeDosNode
;
;-----------------------------------------------------------------------

* The packet for MakeDosNode starts with the following four
* longwords, directly followed by a DosEnvec structure.

    APTR  dosName            ; Points to a DOS device name (ex. 'RAM1',0)
    APTR  execName           ; Points to device driver name (ex. 'ram.device',0)
    ULONG unit               ; Unit number
    ULONG flags              ; OpenDevice flags

* The DosEnvec disk "environment" is a longword array that describes the
* disk geometry.  It is variable sized, with the length at the beginning.
* Here are the constants for a standard geometry.
* See libraries/filehandler.i for additional notes.

 STRUCTURE DosEnvec,0
    ULONG de_TableSize       ; Size of Environment vector
    ULONG de_SizeBlock       ; in longwords: standard value is 128
    ULONG de_SecOrg          ; not used; must be 0
    ULONG de_Surfaces        ; # of heads (surfaces). drive specific
    ULONG de_SectorPerBlock  ; not used; must be 1
    ULONG de_BlocksPerTrack  ; blocks per track. drive specific
    ULONG de_Reserved        ; DOS reserved blocks at start of partition.
    ULONG de_PreAlloc        ; DOS reserved blocks at end of partition
    ULONG de_Interleave      ; usually 0
    ULONG de_LowCyl          ; starting cylinder. typically 0
    ULONG de_HighCyl         ; max cylinder. drive specific
    ULONG de_NumBuffers      ; Initial # DOS of buffers.
    ULONG de_BufMemType      ; type of mem to allocate for buffers
    ULONG de_MaxTransfer     ; Max number of bytes to transfer at a time
    ULONG de_Mask            ; Address Mask to block out certain memory
    LONG  de_BootPri         ; Boot priority for autoboot
    ULONG de_DosType         ; ASCII (HEX) string showing file system type;
                             ; 0X444F5300 is old file system,
                             ; 0X444F5301 is fast file system
    ULONG de_Baud            ; Baud rate for serial handler
    ULONG de_Control         ; Control word for handler/file system
                             ; (used as file system/handler desires)
    ULONG de_BootBlocks      ; Number of blocks containing boot code
                             ; (for non-AmigaDOS file systems)
    LABEL DosEnvec_SIZEOF

After making a DOS device node, drivers (except for autoboot drivers) use AddDosNode(deviceNode) to add their node to the system. Autoboot drivers will instead use the AddBootNode() function to add a BootNode to the ExpansionBase.eb_MountList.

ROM Based and Autoboot Drivers

The system software supports the initialization of ROM drivers residing on expansion peripherals, including the ability for drivers to provide a DOS node which the system can boot from. This feature is known as Autoboot.

Automatic boot from a ROM-equipped expansion board is accomplished before DOS is initialized. This facility makes it possible to automatically boot from a hard disk without any floppy disks inserted. Likewise, it is possible to automatically boot from any device which supports the ROM protocol, even allowing the initialization of a disk operating system other than the Amiga's dos.library. ROM-based drivers contain several special entry points that are called at different stages of system initialization. These three stages are known as DIAG, ROMTAG INIT and BOOT.

Events At DIAG Time

When your AUTOCONFIG hardware board is configured by the expansion initialization routine, its ExpansionRom structure is copied into the ExpansionRom subfield of a ConfigDev structure. This ConfigDev structure will be linked to the expansion.library's private list of configured boards.

After the board is configured, the er_Type field of its ExpansionRom structure is checked. The DIAGVALID bit set declares that there is a valid DiagArea (a ROM/diagnostic area) on this board. If there is a valid DiagArea, expansion next tests the er_InitDiagVec vector in its copy of the ExpansionRom structure. This offset is added to the base address of the configured board; the resulting address points to the start of this board's DiagArea.

struct ExpansionRom
{
    UBYTE   er_Type;            /* <-- if ERTB_DIAGVALID set */
    UBYTE   er_Product;
    UBYTE   er_Flags;
    UBYTE   er_Reserved03;
    UWORD   er_Manufacturer;
    ULONG   er_SerialNumber;
    UWORD   er_InitDiagVec;     /* <-- then er_InitDiagVec      */
    UBYTE   er_Reserved0c;      /*     is added to cd_BoardAddr */
    UBYTE   er_Reserved0d;      /*     and points to DiagArea   */
    UBYTE   er_Reserved0e;
    UBYTE   er_Reserved0f;
};

Now expansion knows that there is a DiagArea, and knows where it is.

struct DiagArea
{
    UBYTE   da_Config;          /* <-- if DAC_CONFIGTIME is set */
    UBYTE   da_Flags;
    UWORD   da_Size;            /* <-- then da_Size bytes will  */
    UWORD   da_DiagPoint;       /*     be copied into RAM       */
    UWORD   da_BootPoint;
    UWORD   da_Name;
    UWORD   da_Reserved01;
    UWORD   da_Reserved02;
};
 
/* da_Config definitions */
#define DAC_BUSWIDTH    0xC0    /* two bits for bus width */
#define DAC_NIBBLEWIDE  0x00
#define DAC_BYTEWIDE    0x40    /* invalid for 1.3 - see note below */
#define DAC_WORDWIDE    0x80
 
#define DAC_BOOTTIME    0x30    /* two bits for when to boot */
#define DAC_NEVER       0x00    /* obvious */
#define DAC_CONFIGTIME  0x10    /* call da_BootPoint when first
                                   configuring the device */
#define DAC_BINDTIME    0x20    /* run when binding drivers to boards */

Next, expansion tests the first byte of the DiagArea structure to determine if the CONFIGTIME bit is set. If this bit is set, it checks the da_BootPoint offset vector to make sure that a valid bootstrap routine exists. If so, expansion copies da_Size bytes into RAM memory, starting at beginning of the DiagArea structure.

The copy will include the DiagArea structure itself, and typically will also include the da_DiagPoint ROM/diagnostic routine, a Resident structure (romtag), a device driver (or at least the device initialization tables or structures which need patching), and the da_BootPoint routine. In addition, the BootNode and parameter packet for MakeDosNode() may be included in the copy area for Diag-time patching. Strings such as DOS and Exec device names, library names, and the romtag ID string may also be included in the copy area so that both position-independent ROM code and position-independent routines in the copy area may reference them PC relative.

The copy will be made either nibblewise, or wordwise, according to the BUSWIDTH subfield of da_Config. Note that the da_BootPoint offset must be non-NULL, or else no copy will occur.

The following illustrates an example Diag copy area, and specifies the various fields which should be coded as relative offsets for later patching by your DiagPoint routine.

                      Example DiagArea Copy in RAM

DiagStart:          ; a struct DiagArea
            CCFF    ; da_Config, da_Flags
            SIZE    ; da_Size         - coded as EndCopy-DiagStart
            DIAG    ; da_DiagPoint    - coded as DiagEntry-DiagStart
            BOOT    ; da_BootPoint    - coded as BootEntry-DiagStart
            NAME    ; da_Name         - coded as DevName-DiagStart
            0000    ; da_Reserved01   - Above fields above are supposed
            0000    ; da_Reserved02     to be relative. No patching needed

Romtag:     rrrr    ; a struct Resident (``Romtag'')
            ...       RT_MATCHTAG, RT_ENDSKIP, RT_NAME and  RT_IDSTRING
            ...       addresses are coded relatively as label-DiagStart.
            ...       The RT_INIT vector is coded as a relative offset
            ...       from the start of the ROM.  DiagEntry patches these.

DevName:    ssss..0 ; The name string for the exec device
IdString:   iiii..0 ; The ID string for the Romtag

BootEntry:  BBBB    ; Boot-time code
            ...

DiagEntry:  DDDD    ; Diag-time code (position independent)
            ...       When called, performs patching of the relative-
                      coded addresses which need to be absolute.
OtherData:
            dddd    ; Device node or structs/tables (patch names, vectors)
            bbbb    ; BootNode (patch ln_Name and bn_DeviceNode)
            pppp    ; MakeDosNode packet (patch dos and exec names)

            ssss    ; other name, ID, and library name strings
            ...
EndCopy:

Now the ROM "image" exists in RAM memory. Expansion stores the ULONG address of that "image" in the UBYTES er_Reserved0c, 0d, 0e and 0f. The address is stored with the most significant byte in er_Reserved0c, the next to most significant byte in er_Reserved0d, the next to least significant byte in er_Reserved0e, and the least significant byte in er_Reserved0f – i.e., it is stored as a longword at the address er_Reserved0c.

Expansion finally checks the da_DiagPoint offset vector, and if valid executes the ROM/diagnostic routine contained as part of the ROM "image". This diagnostic routine is responsible for patching the ROM image so that required absolute addresses are relocated to reflect the actual location of code and strings, as well as performing any diagnostic functions essential to the operation of its associated AUTOCONFIG board. The structures which require patching are located within the copy area so that they can be patched at this time. Patching is required because many of the structures involved require absolute pointers to such things as name strings and code, but the absolute locations of the board and the RAM copy area are not known when code the structures.

The patching may be accomplished by coding pointers which require absolute addresses instead as relative offsets from either the start of the DiagArea structure, or the start of the board's ROM (depending on whether the final absolute pointer will point to a RAM or ROM location). The Diag routine is passed both the actual base address of the board, and the address of the Diag copy area in RAM. The routine can then patch the structures in the Diag copy area by adding the appropriate address to resolve each pointer.

Example DiagArea and Diag patching routine:

**
** Sample autoboot code fragment
**
** These are the calling conventions for the Diag routine
**
** A7 -- points to at least 2K of stack
** A6 -- ExecBase
** A5 -- ExpansionBase
** A3 -- your board's ConfigDev structure
** A2 -- Base of diag/init area that was copied
** A0 -- Base of your board
**
** Your Diag routine should return a non-zero value in D0 for success.
** If this value is NULL, then the diag/init area that was copied
** will be returned to the free memory pool.
**

            INCLUDE "exec/types.i"
            INCLUDE "exec/nodes.i"
            INCLUDE "exec/resident.i"
            INCLUDE "libraries/configvars.i"

            ; LVO's resolved by linking with library amiga.lib
            XREF   _LVOFindResident

ROMINFO     EQU      1
ROMOFFS     EQU     $0

* ROMINFO defines whether you want the AUTOCONFIG information in
* the beginning of your ROM (set to 0 if you instead have PALS
* providing the AUTOCONFIG information instead)
*
* ROMOFFS is the offset from your board base where your ROMs appear.
* Your ROMs might appear at offset 0 and contain your AUTOCONFIG
* information in the high nibbles of the first $40 words ($80 bytes).
* Or, your autoconfig ID information may be in a PAL, with your
* ROMs possibly being addressed at some offset (for example $2000)
* from your board base.  This ROMOFFS constant will be used as an
* additional offset from your configured board address when patching
* structures which require absolute pointers to ROM code or data.

*----- We'll store Version and Revision in serial number
VERSION         EQU     37              ; also the high word of serial number
REVISION        EQU     1               ; also the low word of serial number

* See the Amiga Hardware Manual for more info.

MANUF_ID        EQU     2011            ; CBM assigned (2011 for hackers only)
PRODUCT_ID      EQU     1               ; Manufacturer picks product ID

BOARDSIZE       EQU     $40000          ; How much address space board decodes
SIZE_FLAG       EQU     3               ; Autoconfig 3-bit flag for BOARDSIZE
                                        ;   0=$800000(8meg)  4=$80000(512K)
                                        ;   1=$10000(64K)    5=$100000(1meg)
                                        ;   2=$20000(128K)   6=$200000(2meg)
                                        ;   3=$40000(256K)   7=$400000(4meg)
            CODE

*******  RomStart  ***************************************************
**********************************************************************

RomStart:

        IFGT    ROMINFO
;
;   ExpansionRom structure
;
;   Note - If you implement your ExpansionRom and ExpansionControl
;   with PALS, then you can comment out everything until DiagStart:
;   (ie. Make ROMID EQU 0)

*    ; High nibbles of first two words ($00,$02) are er_Type (not inverted)
                                                ; er_Type
                dc.w    $D000                   ;   11xx normal board type
                                                ;   xx0x not in memory free list
                                                ;   xxx1 Diag valid (has driver)
                dc.w    (SIZE_FLAG<<12)&$7000   ;   0xxx not chained
                                                ;   xnnn flags board size

*    ; High nibbles of next two words are er_Product
*    ; These are inverted (~), as are all other words except $40 and $42

                                                ; er_Product
                dc.w    (~(PRODUCT_ID<<8))&$f000,(~(PRODUCT_ID<<12))&$f000

                                                ; er_Flags
                dc.w    (~$C000)&$f000          ;   ~1xxx board is moveable
                                                ;   ~x1xx board can't be shut up
                dc.w    (~0)&$f000              ;

                dc.w    (~0)&$f000,(~0)&$f000   ; er_Reserved03

                                                ; er_Manufacturer
                dc.w    (~(MANUF_ID))&$f000,(~(MANUF_ID<<4))&$f000
                dc.w    (~(MANUF_ID<<8))&$f000,(~(MANUF_ID<<12))&$f000

                                                ; er_SerialNumber
                dc.w    (~(VERSION))&$f000,(~(VERSION<<4))&$f000
                dc.w    (~(VERSION<<8))&$f000,(~(VERSION<<12))&$f000
                dc.w    (~(REVISION))&$f000,(~(REVISION<<4))&$f000
                dc.w    (~(REVISION<<8))&$f000,(~(REVISION<<12))&$f000

                                                ; er_InitDiagVec
                dc.w    (~((DiagStart-RomStart)))&$f000
                dc.w    (~((DiagStart-RomStart)<<4))&$f000
                dc.w    (~((DiagStart-RomStart)<<8))&$f000
                dc.w    (~((DiagStart-RomStart)<<12))&$f000

                dc.w    (~0)&$f000,(~0)&$f000   ; er_Reserved0c
                dc.w    (~0)&$f000,(~0)&$f000   ; er_Reserved0d
                dc.w    (~0)&$f000,(~0)&$f000   ; er_Reserved0e
                dc.w    (~0)&$f000,(~0)&$f000   ; er_Reserved0f

        IFNE    *-RomStart-$40
        FAIL    "ExpansionRom structure not the right size"
        ENDC

                ;Note: nibbles $40 and $42 are not to be inverted
                dc.w    (0)&$f000,(0)&$f000     ; ec_Interrupt (no interrupts)
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved11
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_BaseAddress (write only)
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Shutup (write only)
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved14
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved15
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved16
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved17
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved18
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved19
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved1a
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved1b
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved1c
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved1d
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved1e
                dc.w    (~0)&$f000,(~0)&$f000   ; ec_Reserved1f

        IFNE    *-RomStart-$80
        FAIL    "Expansion Control structure not the right size"
        ENDC

        ENDC    ;ROMINFO

*******  DiagStart  **************************************************
DiagStart:  ; This is the DiagArea structure whose relative offset from
            ; your board base appears as the Init Diag vector in your
            ; autoconfig ID information.  This structure is designed
            ; to use all relative pointers (no patching needed).
            dc.b    DAC_WORDWIDE+DAC_CONFIGTIME    ; da_Config
            dc.b    0                              ; da_Flags
            dc.w    EndCopy-DiagStart              ; da_Size
            dc.w    DiagEntry-DiagStart            ; da_DiagPoint
            dc.w    BootEntry-DiagStart            ; da_BootPoint
            dc.w    DevName-DiagStart              ; da_Name
            dc.w    0                              ; da_Reserved01
            dc.w    0                              ; da_Reserved02

*******  Resident Structure  *****************************************
Romtag:
            dc.w    RTC_MATCHWORD      ; UWORD RT_MATCHWORD
rt_Match:   dc.l    Romtag-DiagStart   ; APTR  RT_MATCHTAG
rt_End:     dc.l    EndCopy-DiagStart  ; APTR  RT_ENDSKIP
            dc.b    RTW_COLDSTART      ; UBYTE RT_FLAGS
            dc.b    VERSION            ; UBYTE RT_VERSION
            dc.b    NT_DEVICE          ; UBYTE RT_TYPE
            dc.b    20                 ; BYTE  RT_PRI
rt_Name:    dc.l    DevName-DiagStart  ; APTR  RT_NAME
rt_Id:      dc.l    IdString-DiagStart ; APTR  RT_IDSTRING
rt_Init:    dc.l    Init-RomStart      ; APTR  RT_INIT


******* Strings referenced in Diag Copy area  ************************
DevName:    dc.b    'abc.device',0                      ; Name string
IdString    dc.b    'abc ',48+VERSION,'.',48+REVISION   ; Id string

DosName:    dc.b    'dos.library',0                ; DOS library name

DosDevName: dc.b    'ABC',0        ; dos device name for MakeDosNode()
                                   ;   (dos device will be ABC:)

            ds.w    0              ; word align

*******  DiagEntry  **************************************************
**********************************************************************
*
*   success = DiagEntry(BoardBase,DiagCopy, configDev)
*   d0                  a0         a2                  a3
*
*   Called by expansion architecture to relocate any pointers
*   in the copied diagnostic area.   We will patch the romtag.
*   If you have pre-coded your MakeDosNode packet, BootNode,
*   or device initialization structures, they would also need
*   to be within this copy area, and patched by this routine.
*
**********************************************************************

DiagEntry:
            lea      patchTable-RomStart(a0),a1   ; find patch table
            adda.l   #ROMOFFS,a1                  ; adjusting for ROMOFFS

* Patch relative pointers to labels within DiagCopy area
* by adding Diag RAM copy address.  These pointers were coded as
* long relative offsets from base of the DiagArea structure.
*
dpatches:
            move.l   a2,d1           ;d1=base of ram Diag copy
dloop:
            move.w   (a1)+,d0        ;d0=word offs. into Diag needing patch
            bmi.s    bpatches        ;-1 is end of word patch offset table
            add.l    d1,0(a2,d0.w)   ;add DiagCopy addr to coded rel. offset
            bra.s    dloop

* Patches relative pointers to labels within the ROM by adding
* the board base address + ROMOFFS.  These pointers were coded as
* long relative offsets from RomStart.
*
bpatches:
            move.l   a0,d1           ;d1 = board base address
            add.l    #ROMOFFS,d1     ;add offset to where your ROMs are
rloop:
            move.w   (a1)+,d0        ;d0=word offs. into Diag needing patch
            bmi.s   endpatches       ;-1 is end of patch offset table
            add.l   d1,0(a2,d0.w)    ;add ROM address to coded relative offset
            bra.s   rloop

endpatches:
            moveq.l #1,d0           ; indicate "success"
            rts


*******  BootEntry  **************************************************
**********************************************************************

BootEntry:  lea     DosName(PC),a1          ; 'dos.library',0
            jsr     _LVOFindResident(a6)    ; find the DOS resident tag
            move.l  d0,a0                   ; in order to bootstrap
            move.l  RT_INIT(A0),a0          ; set vector to DOS INIT
            jsr     (a0)                    ; and initialize DOS
            rts

*
* End of the Diag copy area which is copied to RAM
*
EndCopy:
*************************************************************************

*************************************************************************
*
*   Beginning of ROM driver code and data that is accessed only in
*   the ROM space.  This must all be position-independent.
*

patchTable:
* Word offsets into Diag area where pointers need Diag copy address added
            dc.w   rt_Match-DiagStart
            dc.w   rt_End-DiagStart
            dc.w   rt_Name-DiagStart
            dc.w   rt_Id-DiagStart
            dc.w   -1

* Word offsets into Diag area where pointers need boardbase+ROMOFFS added
            dc.w   rt_Init-DiagStart
            dc.w   -1

*******  Romtag InitEntry  **********************************************
*************************************************************************

Init:       ; After Diag patching, our romtag will point to this
            ; routine in ROM so that it can be called at Resident
            ; initialization time.
            ; This routine will be similar to a normal expansion device
            ; initialization routine, but will MakeDosNode then set up a
            ; BootNode, and Enqueue() on eb_MountList.
            ;
            rts

            ; Rest of your position-independent device code goes here.

            END

Your da_DiagPoint ROM/diagnostic routine should return a non-zero value to indicate success; otherwise the ROM "image" will be unloaded from memory, and its address will be replaced with NULL bytes in locations er_Reserved0c, 0d, 0e and 0f.

Now that the ROM "image" has been copied into RAM, validated, and linked to board's ConfigDev structure, the expansion module is free to configure all other boards on the utility.library's private list of boards.

It may help to see just how a card's ROM AUTOCONFIG information corresponds to the ExpansionRom structure. This chart shows the contents of on-card memory for a fictional expansion card. Note that the ExpansionRom.Flags field ($3F in addresses $08/$0A below) is shown interpreted in its inverted form of $3F. Once the value is uninverted to become $C0, you should use the #defines in <libraries/configregs.h> to interpret it.

Sample Zorro-II AUTOCONFIG ROM Information Viewed as a Hex Dump

        FLAG AND FIELD DEFINITIONS                THIS BOARD
-----------------------------------------------------------------

          1xxx chained                          11 = Normal type
          x111 size                             Don't addmem
          000=8meg,001=64K,010=128K,etc.        ROM Vector Valid
 11xx type     /        1xxx nopref             Not chained
 xx1x addmem  /         x1xx canshut            Size 256K
 xxx1 ROM    /          xx11 reserved           Product#=~$FE=1
    \       / ~Prod#      /                     Flags=~$3F=$C0
     \     /   /  \      /  res. reserved       Prefer exp space
0000: D0003000 F000E000 3000F000 F000F000       Can't be shut up
-----------------------------------------------------------------
      ~Manufacturer#    ~HiWord Serial#    Manu#=~$F824=$7DB=2011
       /   /  \   \      /   /  \   \      HiSer=~$FFDA=$0025=37
0010: F0008000 20004000 F000F000 D000A000
-----------------------------------------------------------------
      ~LoWord Serial#    ~Rom Vector       LoSer=~$FFFE=$0001=1
       /   /  \ \      /   /  \   \      Rom Vector=~$FF7F=$80
0020: F000F000 F000E000 F000F000 7000F000        from board base
-----------------------------------------------------------------

The AUTOCONFIG information from the above card would appear as follows in an ExpansionRom structure:

Nibble Pairs ExpansionRom Field Value
00/02 er_Type $D3
04/06 er_Product $01 = 1
08/0A er_Flags $C0
10/12 and 14/16 er_Manufacturer $07DB = 2011
18/1A thru 24/26 er_SerialNumber $00250001
28/2A and 2C/2E er_InitDiagVec $0080

If a card contains a ROM driver (Rom Vector valid), and the vector is at offset $80 (as in this example) the DiagArea structure will appear at offset $0080 from the base address of the board. This example card’s Resident structure (romtag) directly follows its DiagArea structure.

   WORDWIDE+CONFIGTIME              ROMTAG
    \ flags    DiagPt   Devname     starts
     \ \ DAsize  / BootPt /          here       DiagPt, BootPt,
      \ \  /\   /\  /\   /\ res. res. /\        DevName relative
0080: 90000088 004A0076 00280000 00004AFC       to Diag struct
-----------------------------------------------------------------
                   COLDSTART  NT_DEVICE         backptr,endskip,
                       \ ver  /priority         and DevName coded
      backptr  endskip  \ \  / /  DevName       relative, patched
0090: 0000000E 00000088 01250314 00000028       at Diag time
-----------------------------------------------------------------
                                                ID and InitEntry
      IDstring InitEntry                        coded relative,
00A0: 00000033 00000116                         patched at Diag
-----------------------------------------------------------------

Events At ROMTAG INIT Time

Next, most resident system modules (for example graphics) are initialized. As part of the system initialization procedure a search is made of the expansion.library's private list of boards (which contains a ConfigDev structure for each of the AUTOCONFIG hardware boards). If the cd_Flags specify CONFIGME and the er_Type specifies DIAGVALID, the system initialization will do three things:

First, it will set the current ConfigDev as the current binding (see the expansion.library SetCurrentBinding() function). Second, it will check the DiagArea's da_Config flag to make sure that the CONFIGTIME bit is set. Third, it will search the ROM "image" associated with this hardware board for a valid Resident structure (<exec/resident.h>); and, if one is located, will call InitResident() on it, passing a NULL segment list pointer as part of the call.

Next, the board's device driver is initialized. The Resident structure associated with this board's device driver (which has now been patched by the ROM/diagnostic routine) should follow standard system conventions in initializing the device driver provided in the boot ROMs. This driver should obtain the address of its associated ConfigDev structure via GetCurrentBinding().

Once the driver is initialized, it is responsible for some further steps. It must clear the CONFIGME bit in the cd_Flags of its ConfigDev structure, so that the system knows not to configure this device again if "BindDrivers" is run after bootstrap. Also, though it is not currently mandatory, the driver should place a pointer to its Exec node in the cd_Driver field of the ConfigDev structure. This will generally be a device (NT_DEVICE) node. And for this device to be bootable, the driver must create a BootNode structure, and link this BootNode onto the expansion.library's eb_MountList.

The BootNode structure (see <libraries/expansionbase.h>) contains a Node of the new type NT_BOOTNODE (see <exec/nodes.h>). The driver must initialize the ln_Name field to point to the ConfigDev structure which it has obtained via the GetCurrentBinding() call. The bn_Flags subfield is currently unused and should be initialized to NULL. The bn_DeviceNode must be initialized to point to the DosNode for the device.

When the DOS is initialized later, it will attempt to boot from the first BootNode on the eb_MountList. The eb_MountList is a priority sorted List, with nodes of the highest priority at the head of the List. For this reason, the device driver must enqueue a BootNode onto the list using the Exec library function Enqueue().

<In the case of an autoboot of AmigaDOS, the BootNode must be linked to a DeviceNode of the AmigaDOS type (see <libraries/filehandler.h>), which the driver can create via the expansion library MakeDosNode() function call. When the DOS "wakes up", it will attempt to boot from this DeviceNode.

Events At BOOT Time

If there is no boot disk in the internal floppy drive, the system strap module will call a routine to perform autoboot. It will examine the eb_MountList; find the highest priority BootNode structure at the head of the List; validate the BootNode; determine which ConfigDev is associated with this BootNode; find its DiagArea; and call its da_BootPoint function in the ROM "image" to bootstrap the appropriate DOS. Generally, the BootPoint code of a ROM driver will perform the same function as the boot code installed on a floppy disk, i.e., it will FindResident() the dos.library, and jump to its RT_INIT vector. The da_BootPoint call, if successful, should not return.

If a boot disk is in the internal floppy drive, the system strap will Enqueue() a BootNode on the eb_MountList for "DF0:" at the suggested priority (see the Autodoc for the expansion.library AddDosNode() function). Strap will then open AmigaDOS, overriding the autoboot. AmigaDOS will boot from the highest priority node on the eb_MountList which should, in this case, be "DF0:". Thus, games and other bootable floppy disks will still be able to obtain the system for their own use.

In the event that there is no boot disk in the internal floppy drive and there are no ROM bootable devices on the autoconfiguration chain, the system does the normal thing, asking the user to insert a Workbench disk, and waiting until its request is satisfied before proceeding.

RigidDiskBlock and Alternate File Systems

Through the use of RigidDiskBlock information and the FileSystem.resource, it is possible for an autoboot driver to have access to enough information to mount all of its device partitions and even load alternate file systems for use with these partitions.

The RigidDiskBlock specification (also known as "hardblocks") defines blocks of data that exist on a hard disk to describe that disk. These blocks are created or modified with an installation utility (such as the hard drive Prep utility for the A2090A ST506/SCSI controller card) provided by the disk controller manufacturer, and they are read and used by the device driver ROM (or expansion) code. They are not generally accessible to the user as they do not appear on any DOS device. The blocks are tagged with a unique identifier, checksummed, and linked together.

The five block types currently defined are RigidDiskBlock, BadBlockBlock, PartitionBlock, FileSysHeaderBlock, and LoadSegBlock.

The root of these blocks is the RigidDiskBlock. The RigidDiskBlock must exist on the disk within the first RDB_LOCATION_LIMIT (16) blocks. This inhibits the use of the first cylinder(s) in an AmigaDOS partition: although it is strictly possible to store the RigidDiskBlock data in the reserved area of a partition, this practice is discouraged since the reserved blocks of a partition are overwritten by Format, Install, DiskCopy, etc. The recommended disk layout, then, is to use 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. This allocation range is described in the RigidDiskBlock.

The RigidDiskBlock contains basic information about the configuration of the drive: number and size of blocks, tracks, and cylinders, as well as other relevant information. The RigidDiskBlock points to bad block, partition, file system and drive initialization description blocks.

The BadBlockBlock list contains a series of bad-block/good-block pairs. Each block contains as many as will fit in a physical sector on the drive. These mappings are to be handled by the driver on read and write requests.

The drive initialization description blocks are LoadSegBlocks that are loaded at boot time to perform drive-specific initialization. They are called with both C-style parameters on the stack, and assembler parameters in registers as follows:

d0  =  DriveInit(lun, rdb, ior) (d0/a0/a1)

where lun is the SCSI logical unit number (needed to construct SCSI commands), rdb is a pointer to a memory copy of the RigidDiskBlock (which should not be altered), and ior is a standard IO request block that can be used to access the drive with synchronous DoIO() calls.

The result of DriveInit() is either -1, 0, or 1. A -1 signifies that an error occurred and drive initialization cannot continue. A 0 (zero) result reports success. In cases -1 and 0, the code is unloaded. A result of 1 reports success, and causes the code to be kept loaded. Furthermore, this resident code will be called whenever a reset is detected on the SCSI bus.

The FileSysHeaderBlock entries contain code for alternate file handlers to be used by partitions. There are several strategies that can be used to determine which of them to load. The most robust would scan all drives for those that are both required by partitions and have the highest fhb_Version, and load those. Whatever method is used, the loaded file handlers are added to the Exec resource FileSystem.resource, where they are used as needed to mount disk partitions.

The PartitionBlock entries contains most of the data necessary to add each partition to the system. They replace the Mount and "DEVS:MountList" mechanism for adding these partitions. The only items required by the expansion.library MakeDosNode() function which are not in this partition block are the Exec device name and unit, which is expected to be known by driver reading this information. The file system to be used is specified in the pb_Environment. If it is not the default file system (i.e., "DOS\0" or "DOS\1"), the node created by MakeDosNode() is modified as specified in a FileSystem.resource's FileSysEntry before adding it to the DOS list.

Only 512 byte blocks were supported by the pre-V36 file system, but this proposal was forward-looking by making the block size explicit, and by using only the first 256 bytes for all blocks but the LoadSeg and BadBlock data. Under the present file system, this allows using drives formatted with sectors 256 bytes or larger (i.e., 256, 512, 1024, etc). LoadSeg and BadBlock data use whatever space is available in a sector.

RigidDiskBlock

This is the current specification for the RigidDiskBlock:

rdb_ID
Set to "RDSK".
rdb_SummedLongs
Set to 64.
rdb_ChkSum
Block checksum (longword sum to zero).
rdb_HostID
SCSI Target ID of host.
This is the initiator ID of the creator of this RigidDiskBlock. It is intended that modification of the RigidDiskBlock, or of any of the blocks pointed to by it, by another initiator (other than the one specified here) be allowed only after a suitable warning. The user is then expected to perform an audio lock out ("Hey, is anyone else setting up SCSI stuff on this bus?"). The rdb_HostID may become something other than the initiator ID when connected to a real network: that is an area for future standardization.
rdb_BlockBytes
Size of disk blocks.
Present file system supports 256, 512, 1024, etc.
rdb_Flags
Long word of flags:
RDBF._LAST
No disks exist to be configured after this one on this controller (SCSI bus).
RDBF._LASTLUN
No LUNs exist to be configured greater than this one at this SCSI Target ID.
RDBF._LASTTID
No Target IDs exist to be configured greater than this one on this SCSI bus.
RDBF._NORESELECT
Don't bother trying to perform reselection when talking to this drive.
RDBF._DISKID
rdb_Disk... identification variables below contain valid data.
RDBF._CTRLRID
rdb_Controller... identification variables below contain valid data.
RDBF._SYNCH
Drive supports scsi synchronous mode; can be dangerous to use if it doesn't!

These fields point to other blocks on the disk which are not a part of any file system. All block pointers referred to are block numbers on the drive.

rdb_BadBlockList
Optional bad block list. A singly linked list of blocks of type PartitionBlock.
rdb_PartitionList
Optional first partition block. A singly linked list of blocks of type PartitionBlock.
rdb_FileSysHeaderList
Optional file system header block. A singly linked list of blocks of type FileSysHeaderBlock.
rdb_DriveInit
Optional drive-specific init code. A singly linked list of blocks of type LoadSegBlock containing initialization code. Called as DriveInit(lun,rdb,ior)(d0/a0/a1).
rdb_Reserved1[6]
Set to $FFFFFFFFs.
These are reserved for future block lists. Since NULL for block lists is $FFFFFFFF, these reserved entries must be set to $FFFFFFFF.

These fields describe the physical layout of the drive.

rdb_Cylinders
Number of drive cylinders.
rdb_Sectors
Sectors per track.
rdb_Heads
Number of drive heads.
rdb_Interleave
Interleave.
This drive interleave is independent from, and unknown to, the DOS’s understanding of interleave as set in the partition's environment vector.
rdb_Park
Landing zone cylinder.
rdb_Reserved2[3]
Set to zeros.

These fields are intended for ST506 disks. They are generally unused for SCSI devices and set to zero.

rdb_WritePreComp
Starting cylinder: write precompensation.
rdb_ReducedWrite
Starting cylinder: reduced write current.
rdb_StepRate
Drive step rate.
rdb_Reserved3[5]
Set to zeros.

These fields are used while partitions are set up to constrain the partitionable area and help describe the relationship between the drive's logical and physical layout.

rdb_RDBlocksLo
Low block of the range allocated for blocks described here. Replacement blocks for bad blocks may also live in this range.
rdb_RDBlocksHi
High block of this range (inclusive).
rdb_LoCylinder
Low cylinder of partitionable disk area.
Blocks described by this include file will generally be found in cylinders below this one.
rdb_HiCylinder
High cylinder of partitionable data area.
Usually rdb_Cylinders-1.
rdb_CylBlocks
Number of blocks available per cylinder.
This may be rdb_Sectors*rdb_Heads, but a SCSI disk that, for example, reserves one block per cylinder for bad block mapping would use rdb_Sectors*rdb_Heads-1.
rdb_AutoParkSeconds
Number of seconds to wait before parking drive heads automatically. If zero, this feature is not desired.
rdb_HighRDSKBlock
Highest block used by these drive definitions.
Must be less than or equal to rdb_RDBBlocksHi. All replacements for bad blocks should be between rdb_HighRDSKBlock+1 and rdb_RDBBlocksHi (inclusive).
rdb_Reserved4
Set to zeros.

These fields are of the form available from a SCSI Identify command. Their purpose is to help the user identify the disk during setup. Entries exist for both controller and disk for non-embedded SCSI disks.

rdb_DiskVendor
Vendor name of the disk.
rdb_DiskProduct
Product name of the disk.
rdb_DiskRevision
Revision code of the disk.
rdb_ControllerVendor
Vendor name of the disk controller.
rdb_ControllerProduct
Product name of the disk controller.
rdb_ControllerRevision
Revision code of the disk controller.
rdb_Reserved5[10]
Set to zeros.

BadBlockBlock

This is the current specification for the BadBlockBlock. The end of data occurs when bbb_Next is NULL ($FFFFFFFF), and the summed data is exhausted.

bbb_ID
Set to "BADB".
bbb_SummedLongs
Size of this checksummed structure.
Note that this is not 64 like most of the other structures. This is the number of valid longs in this image, and can be from 6 to rdb_BlockBytes/4. The latter is the best size for all blocks other than the last one.
bbb_ChkSum
Block checksum (longword sum to zero).
bbb_HostID
SCSI Target ID of host.
This describes the initiator ID for the creator of these blocks. (see rdb_HostID discussion)
bbb_Next
Block number of the next BadBlockBlock.
bbb_Reserved
Set to zeros.
bbb_BlockPairs[61]
Pairs of block remapping information.
The data starts here and continues as long as indicated by bbb_SummedLongs-6: e.g. if bbb_SummedLongs is 128 (512 bytes), 61 pairs are described here.

PartitionBlock

This is the current specification for the PartitionBlock. Note that while reading these blocks you may encounter partitions that are not to be mounted because the pb_HostID does not match, or because the pb_DriveName is in use and no fallback strategy exists, or because PBFF_NOMOUNT is set, or because the proper file system cannot be found. Some partitions may be mounted but not be bootable because PBFF_BOOTABLE is not set.

pb_ID
Set to "PART".
pb_SummedLongs
Set to 64.
pb_ChkSum
Block checksum (longword sum to zero).
pb_HostID
SCSI Target ID of host.
This describes the initiator ID for the owner of this partition. (see rdb_HostID discussion)
pb_Next
Block number of the next PartitionBlock.
pb_Flags
See below for defines:
PBF._BOOTABLE
This partition is intended to be bootable (e.g. expected directories and files exist).
PBF._NOMOUNT
This partition description is to reserve space on the disk without mounting it. It may be manually mounted later.
pb_Reserved1[2]
Set to zeros.
pb_DevFlags
Preferred flags for OpenDevice().
pb_DriveName
Preferred DOS device name: BSTR form This name is not to be used if it is already in use.

Note that pb_Reserved2 will always be at least 4 longwords so that the RAM image of this record may be converted to the parameter packet to the expansion.library function MakeDosNode().

pb_Reserved2[15]
Filler to make 32 longwords so far.

The specification of the location of the partition is one of the components of the environment, below. If possible, describe the partition in a manner that tells the DOS about the physical layout of the partition: specifically, where the cylinder boundaries are. This allows the file system's smart block allocation strategy to work.

pb_Environment[17]
Environment vector for this partition containing:
de_TableSize
Size of environment vector.
de_SizeBlock
Set to 128 (for 512 bytes/logical block).
de_SecOrg
Set to 0.
de_Surfaces
Number of heads (see layout discussion).
de_SectorPerBlock
Set to 1.
de_BlocksPerTrack
Blocks per track (see layout discussion).
de_Reserved
DOS reserved blocks at start of partition.
Must be > 1; 2 blocks are recommended.
de_PreAlloc
DOS reserved blocks at end of partition.
Valid only for file system type "DOS\1" (the fast file system). Zero otherwise.
de_Interleave
DOS interleave.
Valid only for file system type "DOS\0" (the old file system). Zero otherwise.
de_LowCyl
Starting cylinder.
de_HighCyl
Max cylinder.
de_NumBuffers
Initial number of DOS buffers.
de_BufMemType
Type of mem to allocate for buffers.
The second argument to AllocMem().
de_MaxTransfer
Max number of bytes to transfer at a time. Drivers should be written to handle requests of any length.
de_Mask
Address mask to block out certain memory.
Normally $00FFFFFF for DMA devices.
de_BootPri
Boot priority for autoboot.
Suggested value: zero. Keep less than five, so it won't override a boot floppy.
de_DosType
ASCII string showing fil esystem type;
"DOS\0" ($444F5300) is old file system
"DOS\1" ($444F5301) is fast file system
"UNI(anything)" is a Unix partition
pb_EReserved[15]
Reserved for future environment vector.

FileSysHeaderBlock

The current specification for the FileSysHeaderBlock follows.

fhb_ID
Set to "FSHD".
fhb_SummedLongs
Set to 64.
fhb_ChkSum
Block checksum (long word sum to zero).
fhb_HostID
SCSI Target ID of host.
This describes the initiator ID for the creator of this block. (see rdb_HostID discussion)
fhb_Next
Block number of next FileSysHeaderBlock.
fhb_Flags
See below for defines.
fhb_Reserved1[2]
Set to zero.

The following information is used to construct a FileSysEntry node in the FileSystem.resource.

fhb_DosType
File system description.
This is matched with a partition environment's de_DosType entry.
fhb_Version
Release version of this load image.
Usually, upper 16 bits hold version number, lower 16 bits hold revision number.
fhb_PatchFlags
Patch flags.
These are bits set for those of the following that need to be substituted into a standard device node for this file system, lsb first: e.g. 0x180 to substitute SegList & GlobalVec
fhb_Type
Device node type: zero.
fhb_Task
Standard DOS "task" field: zero.
fhb_Lock
Not used for devices: zero.
fhb_Handler
File name to loadseg: zero placeholder.
fhb_StackSize
Stack size to use when starting task.
fhb_Priority
Task priority when starting task.
fhb_Startup
Startup message: zero placeholder.
fhb_SegListBlocks
First of linked list of LoadSegBlocks:
Note that if the fhb_PatchFlags bit for this entry is set (bit 7), the blocks pointed to by this entry must be LoadSeg'd and the resulting BPTR put in the FileSysEntry node.
fhb_GlobalVec
BCPL global vector when starting task Zero or -1.
fhb_Reserved2[23]
(Those reserved by fhb_PatchFlags)
fhb_Reserved3[21]
Set to zero.

LoadSegBlock

This is the current specification of the LoadSegBlock. The end of data occurs when lsb_Next is NULL ($FFFF FFFF), and the summed data is exhausted.

lsb_ID
Set to "LSEG".
lsb_SummedLongs
Size of this checksummed structure.
Note that this is not 64 like most of the other structures. This is the number of valid longs in this image, like bbb_SummedLongs.
lsb_ChkSum
Block checksum (longword sum to zero).
lsb_HostID
SCSI Target ID of host.
This describes the initiator ID for the creator of these blocks. (see rdb_HostID discussion)
lsb_Next
Block number of the next LoadSegBlock.
lsb_LoadData
Data for "loadseg".
The data starts here and continues as long as indicated by lsb_SummedLongs - 5: e.g. if lsb_SummedLongs is 128 (ie. for 512 byte blocks), 123 longs of data are valid here.

filesysres.h

The FileSysResource is created by the first code that needs to use it. It is added to the resource list for others to use. (Checking and creation should be performed while Forbid() is in effect). The resource is created by the system early on in the initialization sequence.

FileSysResource

fsr_Node
On resource list with the name "FileSystem.resource".
fsr_Creator
Name of creator of this resource.
fsr_FileSysEntries
List of FileSysEntry structs.

FileSysEntry

fse_Node
On fsr_FileSysEntries list; ln_Name is of creator of this entry.
fse_DosType
DosType of this file system.
fse_Version
Release version of this file system.
Usually, upper 16 bits hold version number, lower 16 bits hold revision number.
fse_PatchFlags
Bits set for those of the following that need to be substituted into a standard device node for this file system: e.g. $180 for substitute SegList & GlobalVec.
fse_Type
Device node type: zero.
fse_Task
Standard DOS "task" field.
fse_Lock
Not used for devices: zero.
fse_Handler
File name to loadseg (if SegList is NULL).
fse_StackSize
Stack size to use when starting task.
fse_Priority
Task priority when starting task.
fse_Startup
Startup message: FileSysStartupMsg for disks.
fse_SegList
Segment of code to run to start new task.
fse_GlobalVec
BCPL global vector when starting task.

No more entries need exist than those implied by fse_PatchFlags, so entries do not have a fixed size.

For additional information on initializing and booting a Rigid Disk Block file system device, see the SCSI Device. Writers of drivers for expansion devices that perform their own DMA (direct memory access) should consult the Exec chapters and Autodocs for information on processor cache control functions including CachePreDMA() and CachePostDMA().

See the following include files for additional notes and related structures: <libraries/configvers.h>, <libraries/configregs.h>, <devices/hardblocks.h>, <resources/filesysres.h> and <libraries/filehandler.h>.

Function Reference

The following are brief descriptions of the expansion library functions that are useful for expansion device drivers and related applications. See the SDK for the complete descriptions of all the expansion library functions.

Function Description
FindConfigDev() Returns a pointer to the ConfigDev structure of a given expansion device.
MakeDosNode() Creates the DOS device node for disk and similar expansion devices.
AddDosNode() Adds a DOS device node to the system.
AddBootNode() Adds an autobooting DOS device node to the system.
GetCurrentBinding() Returns a pointer to the CurrentBinding structure of a given device.
SetCurrentBinding() Set up for reading the CurrentBinding with GetCurrentBinding().
ObtainCurrentBinding() Protect the ConfigDev structure with a semaphore.
ReleaseCurrentBinding() Release a semaphore on ConfigDev set up with ObtainCurrentBinding().