Copyright (c) Hyperion Entertainment and contributors.
AmigaDOS Data Structures
|This page is currently being updated to AmigaOS 4.x. Some of the information contained here may not yet be applicable in part or totally.|
Process Data Structures
These values are created as part of an AmigaDOS process; there is a complete set for each process.
A process is an Exec task with a number of extra data structures appended. The process structure consists of:
AmigaDOS process values are as follows:
|BPTR||pr_SegList||Array of seg lists used by this process|
|LONG||pr_StackSize||Size of process stack in bytes|
|APTR||pr_GlobVec||Global vector for this process (BCPL)|
|LONG||pr_TaskNum||CLI task number or zero if not a CLI|
|BPTR||pr_StackBase||Ptr to high memory end of process stack|
|LONG||pr_Result2||Value of secondary result from last call|
|BPTR||pr_CurrentDir||Lock associated with current directory|
|BPTR||pr_CIS||Current CLI Input Stream|
|BPTR||pr_COS||Current CLI Output Stream|
|APTR||pr_ConsoleTask||Console handler process for current window|
|APTR||pr_FileSystemTask||File handler process for current drive|
|BPTR||pr_CLI||Pointer to CLI|
|APTR||pr_ReturnAdd||Pointer to previous stack frame|
|APTR||pr_PktWait||Function to be called when awaiting msg|
|APTR||pr_WindowPtr||Window for error printing|
|BPTR||pr_HomeDir||Home directory of executing program|
|LONG||pr_Flags||Flags telling DOS about process|
|LONG||(*pr_ExitCode)(LONG returncode, LONG pr_ExitData)||Code to call on exit of program or NULL|
|LONG||pr_ExitData||Passed as an argument to pr_ExitCode|
|UBYTE*||pr_Arguments||Arguments passed to the process at start|
|struct MinList||pr_LocalVars||Local environment variables|
|ULONG||pr_ShellPrivate||For the use of the current Shell|
|BPTR||pr_CES||Error stream - if NULL, use pr_COS|
- To identify the segments that a particular process uses, you must use pr_SegList. pr_SegList is an array of longwords with its size in Seg_List. Other elements are either zero or a BPTR to a SegList. CreateProc() and CreateNewProc() create this array with the first two elements of the array pointing to resident code and the third element, being the SegList, passed an argument. When a process terminates, FreeMem() is used to return the space for the pr_SegList.
- The pr_StackSize field indicates the size of the process stack, as supplied by the user when calling CreateProc() or CreateNewProc(). Note that the process stack is not the same as the command stack a CLI uses when it calls a program. The CLI obtains its command stack just before it runs a program and you may alter the size of this stack with the STACK command. When you create a process, AmigaDOS obtains the process stack and stores the size in pr_StackSize. The pointer to the space for the process control block and the stack is also stored in the MemEntry field of the task structure. When the process terminates this space is returned via a call to FreeVec(). You can also chain any memory you obtain into this list structure so that it, too, gets put back when the task terminates. But be careful, this method won't work for a program run from the CLI since memory is not freed until the process goes away.
- If a call to CreateProc() or CreateNewProc() creates the process, GlobVec is a pointer to the Shared Global Vector. However, some internal handler processes use a private global vector.
- The value of pr_TaskNum is normally zero; a CLI process stores the small integer that identifies the invocation of the CLI here.
- The pointer pr_StackBase points to the high-memory end of the process stack. This is the end of the stack when using languages such as C or assembler; it is the base of the stack for languages such as BCPL. Note that pr_StackBase may not be the same as the one your application uses (eg. if your program is started from the CLI).
- The pr_Result2 and pr_CurrentDir fields are handled by the AmigaDOS functions IoErr() and CurrentDir(), respectively.
- pr_CIS and pr_COS are the values Input() and Output() return and refer to the filehandles you should use when running a program under the CLI. Never access pr_CIS and pr_COS directly. Instead use the AmigaDOS functions provided for this purpose.
- The pr_ConsoleTask field refers to the console handler for the current window.
- The pr_FileSystemTask field refers to the file handler for the boot device. You use these values when attempting to open the * device or file by a relative path name when pr_CurrentDir is null.
- The pr_CLI pointer is nonzero only for CLI processes. In this case it refers to a further structure the CLI uses with the following format:
|LONG||cli_Result2||Value of IOErr from last command|
|BSTR||cli_SetName||Name of current directory|
|BPTR||cli_CommandDir||BPTR to CLI path|
|LONG||cli_ReturnCode||Return code from last command|
|BSTR||cli_CommandName||Name of current command|
|LONG||cli_FailLevel||Fail level (set by FAILAT)|
|BSTR||cli_Prompt||Current prompt (set by PROMPT)|
|BPTR||cli_StandardInput||Default (terminal) CLI input|
|BPTR||cli_CurrentInput||Current CLI input|
|BSTR||cli_CommandFile||Name of EXECUTE command file|
|LONG||cli_Interactive||Boolean; TRUE if prompts required|
|LONG||cli_Background||Boolean; TRUE if CLI created by RUN|
|BPTR||cli_CurrentOutput||Current CLI output|
|LONG||cli_DefaultStack||Stack size to be obtained in longwords|
|BPTR||cli_StandardOutput||Default (terminal) CLI output|
|BPTR||cli_Module||SegList of currently loaded command|
- The exit() function uses the value of pr_ReturnAddr which points to just above the return address on the currently active stack. If a program exits by performing an RTS on an empty stack, the control passes to the code address pushed onto the stack by CreateProc() or by the CLI. If a program terminates with a call to Exit(), then AmigaDOS uses this pointer to extract the same return address. Note that the AmigaDOS function Exit() is inappropriate for most programs which should use the exit() function provided by the compiler manufacturer instead.
- The value of pr_PktWait is normally zero. If it is nonzero, then AmigaDOS calls pr_PktWait whenever a process is about to go to sleep to await a signal indicating that a message has arrived. In the same way as GetMsg(), the function should return a message when one is available.
Usually you use this function to filter out any private messages arriving at the standard process message port that are not intended for AmigaDOS.
- The value of pr_WindowPtr is used when AmigaDOS detects an error that normally requires the user to take some action. Examples of these errors are attempting to write to a write-protected disk, or when the disk is full. If the value of pr_WindowPtr is -1, then the error is returned to the calling program as an error code from the AmigaDOS call of Open(), Write(), or whatever. If the value is zero, then AmigaDOS places a request box on the Workbench screen informing the user of the error and providing the opportunity to retry the operation or to cancel it. If the user selects to cancel, then AmigaDOS returns the error code to the calling program. If the user selects retry, or insert[s] a disk, then AmigaDOS attempts the operation once more. Under V2.0 and later versions of AmigaDOS, if a pr_WindowPtr is zero then AmigaDOS will put requesters on the default public screen.
- If you put a positive value into the pr_Window field, then AmigaDOS takes this to be a pointer to a window structure. Normally you would place the Window structure of the window you are currently using here. In this case, AmigaDOS displays the error message within the window you have specified, rather than using the Workbench screen. You can always leave the pr_WindowPtr field as zero, but if you are using another screen, then the messages AmigaDOS displays appear on the Workbench screen, possibly obscured by your own screen.
- The initial value of pr_WindowPtr is inherited from the process that created the current one. If you decide to alter pr_WindowPtr from within a program that runs under the CLI, then you must save the original value and restore it when you finish; otherwise, the CLI process contains a pr_WindowPtr that refers to a window that is no longer present.
- The rest of the fields in the process structure are brand new and appear only in V2.0 and later versions of AmigaDOS. The pr_HomeDir field is the directory from which the program associated with this process was loaded. This field is referenced when the PROGDIR: feature of V2.0 is used.
- The pr_Flags field is a private field containing flags for AmigaDOS.
- The field named (*pr_ExitCode)() is a LONG pointing to the cleanup code to be called after the program exits. It takes as a parameter the return code of the program and may return a modified return code when the process terminates. The pr_ExitData is provided as a convenience and allows you to pass additional information to your pr_ExitCode automatically.
- Another new field is pr_Arguments. This is a null terminated string of the register level arguments passed to the process when the program was started. You can modify this field using the SetArguments() function but if you do, you must restore it to its original value before exiting.
- pr_LocalVars is used to implement process local variables. Do not access these directly. Use the new calls GetVar(), SetVar(), and DeleteVar() instead.
- The pr_ShellPrivate field is for the private use of the Shell associated with this process. Never access it.
- The value in pr_CES points to an error stream to use for this process separate from pr_CIS and pr_COS. This field is not fully implemented in AmigaDOS at the time of this writing.
|Don't poke around|
|Direct access and manipulation of system's structure is always subject to special cautions. Concurrence, invalidated data, obsolete information are examples of common problems that may arise when doing this. So always prefer the use of dedicated methods to direct structure access when they exist.|
Redirecting System Requesters
On the Amiga, when a user or a program requests a file on a volume that is not currently mounted, AmigaDOS brings up a system request on the Workbench screen asking the user to insert the disk.
The pr_WindowPtr field of the Process structure determines where most process-related system requesters appear. The normal value is 0L which causes the requesters to come up on the Workbench screen (the default public screen).
If your application has its own custom screen, and you perform any actions that might cause a system request (such as loading of files or printing) you should redirect such requesters for your Process so that the requesters related to your Process are brought up on your application screen.
To do this, first find your Process:
proc = (struct Process *) FindTask(NULL);
Save the old value of proc->pr_WindowPtr, and replace it with a pointer to one of the Intuition windows on your custom screen. The requesters will come up on the same screen (and with the same title as) that window. Be sure to replace the original pr_WindowPtr value before closing your window or exiting your program.
Other applications may wish to temporarily disable such requesters so that attempts to Open() or Lock() unmounted volumes simply return an error without bringing up a requester. To do this, save the old value of pr_WindowPtr and store -1L there instead. Before exiting your program, replace the original value of pr_WindowPtr.
DOS Library Structure
This data structure only exists once; however, all AmigaDOS processes use it. If you make a call to OpenLibrary(), you can obtain the library base pointer. The base of the data structure is a positive offset from the library base pointer. The library base pointer points to the following structure:
Library Node structure APTR to DOS RootNode APTR to DOS Shared Global Vector DOS private register dump
Many internal AmigaDOS calls use the Shared Global Vector, which is a jump table. You should not normally use it, except through the supplied interface calls, as it is liable to change without warning.
The RootNode structure is as follows:
|BPTR||rn_TaskArray||Array of CLI processes|
|BPTR||rn_ConsoleSegment||SegList for the CLI|
|struct DateStampe||rn_Time||Current time|
|LONG||rn_RestartSeg||SegList for the disk validator process|
|BPTR||rn_Info||Pointer to the Info structure|
|BPTR||rn_FileHandlerSegment||Segment for a file handler|
|struct MinList||rn_CliList||New list of all CLI processes; the first cpl_Array is also rn_TaskArray|
|struct MsgPort*||rn_BootProc||Private pointer to msgport of boot file system|
|BPTR||rn_ShellSegment||SegList for Shell (for NEWSHELL)|
The rn_TaskArray is an array with its size stored in rn_TaskArray. The process ID (in other words the MsgPort associated with the process) for each CLI is stored in the array. The process ID for CLI n is stored in rn_TaskArray[n]. An empty slot is filled with zero. Under AmigaDOS 2.0, TaskArray is duplicated and extended with the rn_CliList structure. The commands RUN and NEWCLI scan the rn_TaskArray table for the next free slot and use this for the CLI created. You should not access the TaskArray table directly from your code. Instead use the AmigaDOS functions provided for this purpose.
The rn_ConsoleSegment is the SegList for the code of the CLI. RUN and NEWCLI use this value to create a new instance of a CLI.
The RootNode stores the current date and time; normally you should use the AmigaDOS function DateStamp() to return a consistent set of values. The values Days, Mins, and Ticks specify the date and time. The value of Days is the number of days since January 1st, 1978. The value of Mins is the number of minutes since midnight. A tick is one-fiftieth of a second, but the time is only updated anytime DateStamp() is called.
The RestartSeg is the SegList for the code of the disk validator, which is a process that AmigaDOS creates whenever you insert a new disk into a drive. In V2.0 and later versions of AmigaDOS this field is null since the disk validator process is no longer separate.
The rest of the fields in the RootNode structure are brand new and appear only in V2.0 and later versions of AmigaDOS.
The field named rn_FileHandlerSegment is a seglist of the ROM file system.
The rn_CliList field is a list of tables of CLI pointers. This supplants the rn_TaskArray method used in previous versions of AmigaDOS and eliminates the limit on the number of CLIs that can run at the same time. Note that the first table in the rn_CliList is also stored in TaskArray for the sake of backwards compatibility. This list should be accessed using FindCli() and MaxCli() only. Do not directly access it from your code.
The rn_BootProc field is a private pointer to the file system process that the system was booted off of. This is not necessarily the same as the ROM file system.
The rn_ShellSegment field is the SegList for the boot Shell and rn_Flags contains new flags used by AmigaDOS for future expansion. Currently it contains only one flag which determines whether * or #? is used as the AmigaDOS wildcard.
To access the Info substructure with the following format, you use the rn_Info pointer.
|BPTR||di_McName||Pointer to the resident list|
Most of the fields in the Info substructure are empty at this time, and the AmigaOS development team will use them for expanding the system.
The DevInfo structure is a linked list. You use it to identify all the device names that AmigaDOS knows about; this includes ASSIGN names and disk volume tnames. To access the information in the DevInfo structure under V1.3 and earlier versions of AmigaDOS, you must first call Forbid(). This means no Wait() calls or message passing are allowed while you access the structure (and you must alo call Permit() when you are finished).
In V2.0 and later versions of AmigaDOS you must call LockDosList() before accessing the DevInfo structure. This allows you to call Wait() and do message passing in the code that accesses DevInfo. You must call UnLockDosList() when you are finished. For compatibility with V1.3 programs, LockDosList() calls Forbid() and UnLockDosList() calls Permit(). However, this will be removed in a future release of AmigaDOS.
There are three possible formats for the linked list entries in DevInfo depending on whether the entry refers to a disk volume, an assign, or a device or directory.
For an entry describing a device or directory (via ASSIGN) the entry is as follows:
|BPTR||dvi_Next||Pointer to next list entry or zero|
|LONG||dvi_Type||List entry type (device or dir)|
|APTR||dvi_Task||Handler process or zero|
|BPTR||dvi_Lock||File system lock or zero|
|BSTR||dvi_Handler||Filename of handler or zero|
|LONG||dvi_StackSize||Stack size for handler process|
|LONG||dvi_Priority||Priority for handler process|
|LONG||dvi_Startup||Startup value to be passed to handler process|
|BPTR||dvi_SegList||SegList for handler process or zero|
|BPTR||dvi_GlobVec||Global Vector for handler process or zero|
|BSTR||dvi_Name||Name of device or ASSIGNed name|
The dvi_Next field links all the list entries together and the name of the logical device is held in the dvi_Name field. Although the dvi_Name field is a BSTR, note that it must end with a zero byte and this extra byte should not be included in the length count.
The dvi_Type field is 0 (dt_device) or 1 (dt_dir). You can make a diretory entry with the ASSIGN command. This command allocates a name to a directory that you can then use as a device name. If the list entry refers to a directory, then the Task field refers to the file system process handling that disk, and the dvi_Lock field contains a pointer to a lock on that directory.
If the list entry refers to a device, then the device may or may not be resident. If it is resident, the dvi_Task identifies the handler process, and the dvi_Lock is normally zero. If the device is not resident, then dvi_Task is zero and AmigaDOS uses the rest of the list structure.
If the dvi_SegList is zero, then the code for the device is not in memory. The dvi_Handler field is a string specifying the file containing the code (for example, SYS:L/Ram-Handler). A call to LoadSeg() loads the code from the file and inserts the result into the dvi_SegList field.
AmigaDOS now creates a new handler process with the dvi_SegList, dvi_StackSize, and dvi_Priority values. The new process is a BCPL process and requires a Global Vector; this is either the value you specified in dvi_GlobVec or a new private global vector if dvi_GlobVec is zero. If dvi_GlobVec is -1 then the process is not a BCPL process but is one created by CreateProc().
The new process is passed a message containing the name originally specified, the value stored in dvi_Startup and the base of the list entry. The new handler process may then decide to patch into the dvi_Task slot the process ID or not as required. If the dvi_Task slot is patched, then subsequent references to the device name use the same handler task; this is what the RAM: device does. If the dvi_Task slot is not patched, then further references to the device result in new process invocations; this is what the CON: device does.
If the dvi_Type field within the list entry is equal to 2 (dt_volume), then the format of the list structure is slightly different.
|BPTR||dvi_Next||Pointer to next list entry or zero|
|LONG||dvi_Type||List entry type (volume)|
|APTR||dvi_Task||Handler process or zero|
|BPTR||dvi_Lock||File system lock|
|struct DateStamp||dol_VolumeDate||Volume creation date|
|BPTR||dol_LockList||List of active locks for this volume|
|LONG||dol_DiskType||Type of disk|
In this case, the dvi_Name field is the name of the volume, and the Task field refers to the handler process if the volume is currently inserted, or to zero if the volume is not inserted. To distinguish disks with the same name, AmigaDOS timestamps the volume on creation and then saves the timestamp in the list structure. AmigaDOS can therefore compare the timestamps of different volumes whenever necessary.
If a volume is not currently inserted, then AmigaDOS saves the list of currently active locks in the dol_LockList field. Note that dol_LockList is private and should never be accessed directly. Not all file systems support the dol_LockList field. AmigaDOS uses the dol_DiskType field to identify the type of disk. The disk type is up to four characters packed into a longword and padded on the right with nulls.
Non-Binding or Late Type
If the dvi_Type field within the list entry is equal to 3 (dt_nonbinding) or 4 (dt_late) then the format of the list structure is as follows:
|BPTR||dvi_Next||Pointer to next list entry or zero|
|LONG||dvi_Type||List entry type (nonbinding or late assign)|
|APTR||dvi_Task||Handler process or zero|
|BPTR||dvi_Lock||File system lock|
|UBYTE*||dol_AssignName||Name for nonbinding or late assigns|
|struct AssignList*||dol_List||For multidirectory assigns (regular)|
For multidrectory assigns, the additional locks are strung off of dol_List. For nonbinding and late assigns, dol_AssignName has the string for the assign (path). Late-binding assigns turn into regular assigns once they bind. Also note that any normal assign can have more locks strung off the dol_List.
AmigaDOS obtains all the memory it allocates by calling the AllocMem() function provided by Exec. In this way, AmigaDOS obtains structures such as locks and file handles; it usually places them back in the free pool by calling FreeMem(). Under V2.0 and later versions of AmigaDOS, AllocVec() and FreeVec() are available to do this. In either case, each memory segment allocated by AmigaDOS is identified by a BPTR to the second longword in the structure. The first longword always contains the length of the entire segment in bytes. Thus the structure of allocated memory is as follows:
|LONG||BlockSize||Size of memory block|
|LONG||FirstData||First data segment, BPTR to block points here|
To obtain a segment list, you call LoadSeg(). The result is a BPTR to allocated memory, so that the length of the memory block containing each list entry is stored at -4 from the BPTR. This length is 8 more than the size of the segment list entry, allowing for the link field and the size field itself.
The SegList is a list linked together by BPTRs and terminated by zero. The remainder of each segment list entry contains the code loaded. Thus the format is:
|LONG||NextSeg||BPTR to next segment or zero|
|LONG||FirstCode||First value from binary file|
File handles are created by the AmigaDOS function Open(), and you use them as arguments to other functions such as Read() and Write(). AmigaDOS returns them as a BPTR to the following structure:
|struct Message*||fh_Link||Reserved for AmigaDOS|
|struct Message*||fh_Key||Reply port for the packet|
|struct Message*||fh_Port||Process ID of handler|
|LONG||fh_Buf||Buffer for internal use|
|LONG||fh_Pos||Character position for internal use|
|LONG||fh_End||End position for internal use|
|LONG||fh_Func1||Function called when buffer [is] exhausted|
|LONG||fh_Func2||Function called when buffer is full|
|LONG||fh_Func3||Function called when handle is closed|
|LONG||fh_Arg1||Argument depends on file handle type|
|LONG||fh_Arg2||Argument depends on file handle type|
Most of these fields are only used by AmigaDOS internally; normally Read() or Write() uses the file handle to indicate the handler process and any arguments to be passed. Values should not be altered within the file handle by user programs. In general, you should not read the values either (except fh_Arg1 which is used for direct packet I/O).
The filing system extensively uses a data structure called a lock. This structure serves two purposes. First, it serves as the mechanism to open files for multiple reads or a single write. Note that oibtaining a shared read lock on a directory does not stop that directory being updated.
Second, the lock provides a unique identification for a file. Although a particular file may be specified in many ways, the lock is a simple handle on that file. The lock contains the actual disk block location of the directory or file header and is thus a shorthand way of specifying a particular file system object. The structure of a lock is as follows:
|BPTR||fl_Link||BPTR to next lock in chain, else zero|
|LONG||fl_Key||Block number of directory or file header|
|LONG||fl_Access||Shared or exclusive access|
|struct MsgPort*||fl_Task||Process ID of handler|
|BPTR||fl_Volume||BPTR to dlt_volume DOS list entry|
Because AmigaDOS uses the fl_Link field to chain locks together, you should not alter it. The filing system fills in [the] fl_Key field to represent the location on disk of the directory block or the file header block. The fl_Access serves to indicate whether this is a shared read lock, when it has the value -2, or an exclusive write lock, when it has the value -1. The fl_Task field contains a pointer to the handler process for the device containing the file to which this lock refers. Finally the fl_Volume field points to the node in the DevInfo structure that identifies the volume to which this lock refers. Volume entries in the DevInfo structure remain there if a disk is inserted or if there are any locks open on that volume.
Note that a lock can also be a zero. The special case of lock zero indicates that the lock refers to the root of the initial filing system, and the pr_FileSystemTask field within the process data structure gives the handler process. None of the fields in the Lock structure should be changed by your code. The Lock structure is strictly handler-private and read-only.