Amiga® RKM Libraries: 32 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(TM)            Expansion Board Drivers 
 The Expansion Sequence    Function Reference 


32 Expansion Library / AUTOCONFIG(TM)

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 chapter 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 from
Addison-Wesley.  For additional information specific to Zorro II boards,
see the Commodore publication 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 Commodore Applications and Technical
Support (CATS), West Chester, Pennsylvania.

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.

    Note:
    -----
    A unique Hardware Manufacturer number assigned by CATS is not the
    same as a Developer number.


32 Expansion Library / 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> and <.i>:


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; /* memory where 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> and <.i>:


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 COMMODORE-AMIGA! */
    ULONG  er_SerialNumber; /* Available for use by manufacturer */
    UWORD  er_InitDiagVec;  /* Offset to optional "DiagArea" structure */
    UBYTE  er_Reserved0c;
    UBYTE  er_Reserved0d;
    UBYTE  er_Reserved0e;
    UBYTE  er_Reserved0f;
};


 Simple Expansion Library Example 


32 / The Expansion Sequence / 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 


32 Expansion Library / 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.  Since 1.3, the system software also supports the
initialization of onboard ROM driver software.

 Disk Based Drivers           ROM Based and Autoboot Drivers 
 Expansion Drivers and DOS    RigidDiskBlock And Alternate Filesystems 


32 / Expansion Board Drivers / 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 the chapter on
"Workbench and 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 the Addison-Wesley Amiga ROM Kernel Reference Manual:
Devices.  Autodocs for expansion.library functions may be found in the
Addison-Wesley Amiga ROM Kernel Reference Manual: Includes and Autodocs.


32 / Expansion Board Drivers / 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.   A new function, AddBootNode(), is also available in Release 2
(V36 and later versions of the OS) that 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 (passed in A0 from assembler) 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 filesystem type;
                             ; 0X444F5300 is old filesystem,
                             ; 0X444F5301 is fast file system
    ULONG de_Baud            ; Baud rate for serial handler
    ULONG de_Control         ; Control word for handler/filesystem
                             ; (used as filesystem/handler desires)
    ULONG de_BootBlocks      ; Number of blocks containing boot code
                             ; (for non-AmigaDOS filesystems)
    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 new Release 2 expansion.library AddBootNode()
function (if running under V36 or higher) or the Exec Enqueue() function
(if running under pre-V36) to add a BootNode to the
ExpansionBase.eb_MountList.


32 / Expansion Board Drivers / ROM Based and Autoboot Drivers

Since 1.3, 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    Events At ROMTAG INIT Time    Events At BOOT Time 


32 / / ROM Based and Autoboot Drivers / 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
                                     configging 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. (Note - under 1.3, DAC_BYTEWIDE is
not supported. Byte wide ROMs must use DAC_NIBBLEWIDE and drivers must
supply additional code to re-copy their DiagArea)

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:

     Diag.asm 

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.


Table 32-1: 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
    -----------------------------------------------------------------


32 / / ROM Based and Autoboot Drivers / 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.


32 / / ROM Based and Autoboot Drivers / 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.


32 / Expansion Board Drivers / RigidDiskBlock and Alternate Filesystems

Through the use of RigidDiskBlock information and the FileSys.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 filesystems 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 filesystem, 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    PartitionBlock        LoadSegBlock 
 BadBlockBlock     FileSysHeaderBlock    filesysres.h and .i 


32 / / RigidDiskBlock and Alternate Filesystems / RigidDiskBlock

This is the current specification for the RigidDiskBlock:


   rdb_ID              == 'RDSK'
   rdb_SummedLongs     == 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
                       Under pre-V36 filesystem, this must be 512 for
                       a disk with any AmigaDOS partitions on it.
                       Present filesystem supports 256, 512, 1024, etc.

   rdb_Flags           longword 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
filesystem.  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


32 / / RigidDiskBlock and Alternate Filesystems / 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                  == '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.


32 / / RigidDiskBlock and Alternate Filesystems / 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 PBF._NOMOUNT is set,
or because the proper filesystem cannot be found.  Some partitions may be
mounted but not be bootable because PBF._BOOTABLE is not set.


   pb_ID                   == 'PART'
   pb_SummedLongs          == 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
filesystem's smart block allocation strategy to work.


   pb_Environment[17]      environment vector for this partition
                           containing:

       de_TableSize        size of Environment vector
       de_SizeBlock        == 128 (for 512 bytes/logical block)
       de_SecOrg           == 0
       de_Surfaces         number of heads (see layout discussion)
       de_SectorPerBlock   == 1
       de_BlocksPerTrack   blocks per track (see layout discussion)

       de_Reserved         DOS reserved blocks at start of partition.
                           Must be >= 1.  2 is recommended.

       de_PreAlloc         DOS reserved blocks at end of partition
                           Valid only for filesystem type DOS^A (the
                           fast file system).  Zero otherwise.

       de_Interleave       DOS interleave
                           Valid only for filesystem type DOS^@ (the
                           old file system).  Zero otherwise.

       de_LowCyl           starting cylinder
       de_HighCyl          max cylinder
       de_NumBuffers       initial # DOS of 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 filesystem type;
                           DOS^@ ($444F5300) is old filesystem,
                           DOS^A ($444F5301) is fast file system.
                           UNI is a Unix partition.

   pb_EReserved[15]        reserved for future environment vector


32 / / RigidDiskBlock and Alternate Filesystems / FileSysHeaderBlock

The current specification for the FileSysHeaderBlock follows.


   fhb_ID                  == 'FSHD'
   fhb_SummedLongs         == 64
   fhb_ChkSum              block checksum (longword 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 MSW is version, LSW is revision.

   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             filename to loadseg: zero placeholder
   fhb_StackSize           stacksize to use when starting task
   fhb_Priority            task priority when starting task
   fhb_Startup             startup msg: 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 PatchFlags)
   fhb_Reserved3[21]       set to zero


32 / / RigidDiskBlock and Alternate Filesystems / LoadSegBlock

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


   lsb_ID                  == '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.


32 / / RigidDiskBlock and Alternate Filesystems / filesysres.h and .i

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).  Under Release 2 the
resource is created by the system early on in the initialization sequence.
Under 1.3 it is the responsibility of the first RDB driver to create it.


 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 FileSys
   fse_Version           release version of this FileSys
                         Usually MSW is version, LSW is revision.

   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           filename to loadseg (if SegList is null)
   fse_StackSize         stacksize to use when starting task
   fse_Priority          task priority when starting task
   fse_Startup           startup msg: 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
filesystem device, see the SCSI Device chapter of the Addison-Wesley Amiga
ROM Kernel Reference Manual: Devices. Writers of drivers for expansion
devices that perform their own DMA (direct memory access) should consult
the Exec chapters and Autodocs for information on Release 2 processor
cache control functions including CachePreDMA() and CachePostDMA(). See
the following include files for additional notes and related structures:
<libraries/configvers.h> and <.i>, <libraries/configregs.h> and <.i>,
<devices/hardblocks.h> and <.i>, <resources/filesysres.h> and <.i>, and
<libraries/filehandler.h> and <.i>.


32 Expansion Library / Function Reference

The following are brief descriptions of the expansion library functions
that are useful for expansion device drivers and related applications.
See the Amiga ROM Kernel Reference Manual: Includes and Autodocs for the
complete descriptions of all the expansion library functions.


                 Table 32-2: 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 (V36).                              |
 |----------------------------------------------------------------------|
 |      GetCurrentBinding()  Returns a pointer to the CurrentBinding    |
 |                           structure of a given device.               |
 |      SetCurrentBinding()  Set up for reading the CurrentBinding with |
 |                           GetCurrentBinding().                       |
 |    ObtainConfigBinding()  Protect the ConfigDev structure with a     |
 |                           semaphore.                                 |
 |   ReleaseConfigBinding()  Release a semaphore on ConfigDev set up    |
 |                           with ObtainCurrentBinding().               |
 |______________________________________________________________________|


Converted on 22 Apr 2000 with RexxDoesAmigaGuide2HTML 2.1 by Michael Ranner.