Copyright (c) Hyperion Entertainment and contributors.
Expansion Library
Contents
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 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. 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 $E8 0000 (Zorro-II) or $FF00 0000 (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 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
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 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 Amiga ROM Kernel Reference Manual: Devices. Autodocs for expansion.library functions may be found in the Amiga ROM Kernel Reference Manual: Includes and Autodocs.
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 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 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 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.
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:
<tbody> </tbody>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 Filesystems
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 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<math>\backslash</math>0’ or ’DOS<math>\backslash</math>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
This is the current specification for the RigidDiskBlock:
Set to ’RDSK’.
Set to 64.
Block checksum (longword sum to zero).
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.
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.
Long word of flags:
No disks exist to be configured after this one on this controller (SCSI bus).
No LUNs exist to be configured greater than this one at this SCSI Target ID.
No Target IDs exist to be configured greater than this one on this SCSI bus.
Don’t bother trying to perform reselection when talking to this drive.
rdb_Disk
... identification variables below contain valid data.
rdb_Controller
... identification variables below contain valid data.
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.
Optional bad block list.
A singly linked list of blocks of type PartitionBlock.
Optional first partition block.
A singly linked list of blocks of type PartitionBlock.
Optional file system header block.
A singly linked list of blocks of type FileSysHeaderBlock.
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).
Set to $FFFF FFFFs.
These are reserved for future block lists. Since NULL for block lists is $FFFF FFFF, these reserved entries must be set to $FFFF FFFF.
These fields describe the physical layout of the drive.
Number of drive cylinders.
Sectors per track.
Number of drive heads.
Interleave.
This drive interleave is independent from, and unknown to, the DOS’s understanding of interleave as set in the partition’s environment vector.
Landing zone cylinder.
Set to zeros.
These fields are intended for ST506 disks. They are generally unused for SCSI devices and set to zero.
Starting cylinder: write precompensation.
Starting cylinder: reduced write current.
Drive step rate.
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.
Low block of the range allocated for blocks described here. Replacement blocks for bad blocks may also live in this range.
High block of this range (inclusive).
Low cylinder of partitionable disk area.
Blocks described by this include file will generally be found in cylinders below this one.
High cylinder of partitionable data area.
Usually rdb_Cylinders-1.
Number of blocks available per cylinder.
This may be rdb_Sectors<math>\times</math>rdb_Heads, but a SCSI disk that, for example, reserves one block per cylinder for bad block mapping would use rdb_Sectors<math>\times</math>rdb_Heads-1.
Number of seconds to wait before parking drive heads automatically. If zero, this feature is not desired.
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).
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.
Vendor name of the disk.
Product name of the disk.
Revision code of the disk.
Vendor name of the disk controller.
Product name of the disk controller.
Revision code of the disk controller.
Set to zeros.
BadBlockBlock
This is the current specification for the BadBlockBlock. The end of data occurs when bbb_Next is NULL ($FFFF FFFF), and the summed data is exhausted.
Set to ’BADB’.
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.
Block checksum (longword sum to zero).
SCSI Target ID of host.
This describes the initiator ID for the creator of these blocks. (see rdb_HostID discussion)
Block number of the next BadBlockBlock.
Set to zeros.
]
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 filesystem cannot be found. Some partitions may be mounted but not be bootable because PBFF_BOOTABLE is not set.
Set to ’PART’.
Set to 64.
Block checksum (longword sum to zero).
SCSI Target ID of host.
This describes the initiator ID for the owner of this partition. (see rdb_HostID discussion)
Block number of the next PartitionBlock.
See below for defines:
This partition is intended to be bootable (e.g. expected directories and files exist).
This partition description is to reserve space on the disk without mounting it. It may be manually mounted later.
Set to zeros.
Preferred flags for OpenDevice().
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().
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.
Environment vector for this partition containing:
Size of environment vector.
Set to 128 (for 512 bytes/logical block).
Set to 0.
Number of heads (see layout discussion).
Set to 1.
Blocks per track (see layout discussion).
DOS reserved blocks at start of partition.
Must be <math>\ge</math> 1; 2 blocks are recommended.
DOS reserved blocks at end of partition.
Valid only for filesystem type ’DOS<math>\backslash</math>1’ (the fast file system). Zero otherwise.
DOS interleave.
Valid only for filesystem type ’DOS<math>\backslash</math>0’ (the old file system). Zero otherwise.
Starting cylinder.
Max cylinder.
Initial number of DOS buffers.
Type of mem to allocate for buffers.
The second argument to AllocMem().
Max number of bytes to transfer at a time. Drivers should be written to handle requests of any length.
Address mask to block out certain memory.
Normally $00FF FFFF for DMA devices.
Boot priority for autoboot.
Suggested value: zero. Keep less than five, so it won’t override a boot floppy.
ASCII string showing filesystem type;
’DOS<math>\backslash</math>0’ ($444F5300) is old filesystem, ’DOS<math>\backslash</math>1’ ($444F5301) is fast file system. ’UNI(anything)’ is a Unix partition.
Reserved for future environment vector.
FileSysHeaderBlock
The current specification for the FileSysHeaderBlock follows.
Set to ’FSHD’.
Set to 64.
Block checksum (long word sum to zero).
SCSI Target ID of host.
This describes the initiator ID for the creator of this block. (see rdb_HostID discussion)
Block number of next FileSysHeaderBlock.
See below for defines.
Set to zero.
The following information is used to construct a FileSysEntry node in the FileSystem.resource.
File system description.
This is matched with a partition environment’s de_DosType entry.
Release version of this load image.
Usually, upper 16 bits hold version number, lower 16 bits hold revision number.
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
Device node type: zero.
Standard DOS “task” field: zero.
Not used for devices: zero.
File name to loadseg: zero placeholder.
Stack size to use when starting task.
Task priority when starting task.
Startup message: zero placeholder.
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.
BCPL global vector when starting task Zero or -1.
(Those reserved by fhb_PatchFlags)
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.
Set to ’LSEG’.
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.
Block checksum (longword sum to zero).
SCSI Target ID of host.
This describes the initiator ID for the creator of these blocks. (see rdb_HostID discussion)
Block number of the next LoadSegBlock.
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 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.
On resource list with the name “FileSystem.resource”.
Name of creator of this resource.
List of FileSysEntry structs.
On fsr_FileSysEntries list; ln_Name is of creator of this entry.
DosType of this file system.
Release version of this file system.
Usually, upper 16 bits hold version number, lower 16 bits hold revision number.
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.
Device node type: zero.
Standard DOS “task” field.
Not used for devices: zero.
File name to loadseg (if SegList is NULL).
Stack size to use when starting task.
Task priority when starting task.
Startup message: FileSysStartupMsg for disks.
Segment of code to run to start new task.
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 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 (.h and .i) for additional notes and related structures: <libraries/configvers>, <libraries/configregs>, <devices/hardblocks>, <resources/filesysres> and <libraries/filehandler>.
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.
[h] Expansion Library Functions
<thead> </thead> <tbody> </tbody>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(). | |
ObtainCurrentBinding() | Protect the ConfigDev structure with a semaphore. |
ReleaseCurrentBinding() | Release a semaphore on ConfigDev set up |
with ObtainCurrentBinding(). |