GEMDOS contains functions which comprise the highest level of TOS. In many cases, GEMDOS devolves into BIOS calls which handle lower level device access. GEMDOS is responsible for file, device, process, and high-level input/output management. The current revision number of GEMDOS is obtained by calling Sversion(). You should note that the GEMDOS version number is independent of the TOS version number and you should not count on any particular version of GEMDOS being present based on the TOS version present.
Much of GEMDOS closely resembles its CPM 68k and MS-DOS heritage. In fact, the file system and function calls are mostly compatible with MS-DOS. MS-DOS format floppy disks are readable by an Atari computer and vice-versa.
For the creation of MultiTOS, GEMDOS was merged with the MiNT operating environment which derives many of its calls from the UNIX operating system.
GEMDOS is responsible for interaction between applications and file-based devices. Floppy and hard disk drives as well as CD-ROM, WORM, and Magneto-Optical drives are all accessed using GEMDOS calls.
Prior to the advent of MultiTOS, Atari programmers were limited to the TOS file system for file storage and manipulation. With the introduction of MultiTOS, it is now possible for developers to create custom file systems so that almost any conceivable disk format becomes accessible.
As a default, MultiTOS will manage files between the TOS file system and alternative file systems to maintain backward compatibility. Applications which wish to support extra file system features may do so. The Pdomain() call may be used to instruct MultiTOS to stop performing translations on filenames, etc. Other calls such as Dpathconf() can be used to determine the requirements of a particular file system.
The explanation of the file system contained herein will limit itself to the TOS file system.
Drives 'C' through 'P' are available for use by hard disk drives. One letter is assigned per hard drive partition so a multiple-partition drive will be assigned multiple letters. MultiTOS extends drive letter assignments to 'Z' drive. Drive 'U' is a special drive reserved for MultiTOS and is unavailable for assignment.
The amount of free storage space remaining on a drive along with a drive's basic configuration can be determined using the Dfree() call.
Under GEMDOS, each file located on a device is given a filename upon its creation which serves to provide identification for the file. The filename has two parts consisting of a name from one to eight characters long and an optional file extension of up to three characters long. If a file extension exists, the two components are separated by a period. The extension should serve to identify the format of the data whereas the name itself should identify the data itself.
Filenames may be changed after creation with the function Frename(); however, under no circumstances may two files with the same filename reside in the same directory.
All GEMDOS functions ignore the alphabetic case of file and pathnames. The following characters are legal filename characters:
|
|
To further organize data, GEMDOS provides file directories (or folders). Each drive may contain any number of directories which, in turn, may contain files and additional directories. This organization creates a tree-like structure of files and folders. A file's location in this tree is called the path.
Directory names follow the same format as GEMDOS filenames with a maximum filename length of 8 characters and an optional 3 character extension. The first directory of a disk which contains all subdirectories and files is called the root directory.
The Dcreate() and Ddelete() system calls are used to create and delete subdirectories.
Two special, system-created subdirectories are present in some directories. A subdirectory with the name '..' (two periods) refers to the parent of the current directory. The '..' subdirectory is present in every subdirectory.
A subdirectory with the name '.' refers to the current directory. There is a '.' subdirectory in every directory.
To access a file, a complete path specification must be composed of the drive letter, directory name(s), and filename. A file named 'TEST.PRG' located in the 'SYSTEM' directory on drive 'C' would have a path specification like the following:
C:\SYSTEM\TEST.PRG
The drive letter is the first character followed by a colon. Each directory and subdirectory is surrounded by backslashes. If 'TEST.PRG' were located in the root directory of 'C' the path specification would be:
C:\TEST.PRG
The drive letter and colon may be omitted causing GEMDOS to reference the default drive as follows:
\TEST.PRG
A filename by itself will be treated as the file in the default directory and drive. The current GEMDOS directory and drive may be found with the functions Dgetpath() and Dgetdrv() respectively. They may be changed with the functions Dsetpath() and Dsetdrv().
The GEMDOS functions Fsfirst() and Fsnext() are used together to enumerate files of a given path specification. These two functions allow the use of wildcard characters to expand their search parameters.
The '?' character is used to represent exactly one unknown character. The '*' character is used to represent any number of unknown characters. The following table gives some examples of the uses of these characters.
Filename | Found | Not Found |
---|---|---|
*.* | All files | None |
*.GEM |
TEST.GEM
ATARI.GEM |
TEST.G
ATARI.IMG |
A?ARI.? |
ATARI.O
ADARI.C |
ADARI.IMG
ATARI.GEM |
ATARI.??? |
ATARI.GEM
ATARI.IMG |
ATARI.O
ATARI.C |
When using Fsfirst() and Fsnext() to build a list of files, TOS uses the Disk Transfer Address (DTA) to store information about each file found. The format for the DTA structure is as follows:
typedef struct { BYTE d_reserved[21]; /* Reserved - Do Not Change */ BYTE d_attrib; /* GEMDOS File Attributes */ UWORD d_time; /* GEMDOS Time */ UWORD d_date; /* GEMDOS Date */ LONG d_length; /* File Length */ char d_fname[14]; /* Filename */ } DTA;
When a process is started, its DTA is located at a point where it could overlay potentially important system structures. To avoid overwriting memory a process wishing to use Fsfirst() and Fsnext() should allocate space for a new DTA and use Fsetdta() to instruct the OS to use it. The original location of the DTA should be saved first, however. Its location can be found with the call Fgetdta(). At the completion of the operation the old address should be replaced with Fsetdta().
Every TOS file contains several attributes which define it more specifically. File attributes are specified when a file is created with Fcreate() and can be altered later with Fattrib().
The 'read-only' attribute bit is set to prevent modification of a file. This bit should be set at the user's discretion and not cleared unless the user explicitly requests it.
If the 'hidden' attribute is set, the file will not be listed by the desktop or file selector. These files may still be accessed in a normal manner but will not be present in an Fsfirst() or Fsnext() search unless the correct Fsfirst() bits are present.
The 'system' attribute is unused by TOS but remains for MS-DOS compatibility.
The 'archive' attribute is a special bit managed by TOS which indicates whether a file has been written to since it was last backed up. Any time a Fcreate() call creates a file or Fwrite() is used on a file, the Archive bit is set. This enables file backup applications to know which files have been modified since the last backup. They are responsible for clearing this bit when backing up the file.
When a file is first created a special field in its directory entry is updated to contain the date and time of creation. Fdatime() can be used to access or modify this information as necessary.
Files which are already in existence should be opened with Fopen(). As with Fcreate(), this call returns a positive file handle upon success which is used in all subsequent GEMDOS calls to reference the file.
Each process is allocated an OS dependent number of file handles. If an application attempts to open more files than this limit allows, the open or create call will fail with an appropriate error code. File handles may be returned to the system by closing the open file with Fclose().
Fopen() may be used in read, write, or read/write mode. In read mode, Fread() may be used to access existing file contents. In write mode, any original information in the file is not cleared but the data may be overwritten with Fwrite(). In read/write mode, either call may be used interchangeably.
Every file has an associated file position pointer. This pointer is used to determine the location for the next read or write operation. This pointer is expressed as a positive offset from the beginning of the file (position 0) which is set upon first creating or opening a file. The pointer may be read or modified with the function Fseek().
Existing files may be deleted with the GEMDOS call Fdelete().
All versions of TOS have the ability to support file and record locking but not all have the feature installed. If the '_FLK' cookie is present in the system cookie jar then the Flock() call is present. This call is used to create locks on individual sections (usually records) in a file.
Locking a file in use, when possible, is recommended to prevent other processes from modifying the file at the same time.
Several special file handles are available for access through the standard Fopen()/Fread()/Fwrite() calls. They are as follows:
Name |
|
|
Device |
---|---|---|---|
GSH_BIOSCON |
|
|
Console (screen). Special characters such as the carriage return, etc. are interpreted. |
GSH_BIOSAUX |
|
|
Modem (serial port). This is the ST-compatible port for machines with more than one. |
GSH_BIOSPRN |
|
|
Printer (attached to the Centronics Parallel port). |
GSH_BIOSMIDIIN |
|
Midi In | |
GSH_BIOSMIDIOUT |
|
Midi Out | |
GSH_CONIN |
|
|
Standard Input (usually directed to GSH_BIOSCON) |
GSH_CONOUT |
|
|
Standard Output (usually directed to GSH_BIOSCON) |
GSH_AUX |
|
|
Auxillary (usually directed to GSH_BIOSAUX) |
GSH_PRN |
|
|
Printer (usually directed to GSH_BIOSPRN) |
None |
|
|
Unused |
None |
|
|
Unused |
None |
|
|
User Process File Handles |
These files may be treated like any other GEMDOS files for input/output and locking. Access to these devices is also provided with GEMDOS character calls (see later in this chapter).
File redirection is handled by the use of the Fforce() call. Generally you will want to make a copy of the file handle with Fdup() prior to redirecting the file so that it may be restored to normal operation when complete.
Atari systems support two kinds of memory. Standard RAM (sometimes referred to as 'ST RAM') is general purpose RAM that can be used for any purpose including video and DMA. Current Atari architecture limits the amount of standard RAM a system may have to 14MB.
Alternative RAM (sometimes referred to as 'TT RAM') can be accessed faster than standard RAM but is not suitable for video memory or DMA transfers.
The Malloc() and Mxalloc() calls allocate memory blocks from the system heap. Malloc() chooses the type of memory it allocates based on fields in the program header (see later in this chapter). Mxalloc() allows the application to choose the memory type at run-time.
Memory allocated with either Malloc() or Mxalloc() may be returned to the system with Mfree(). Memory allocated by a process is automatically freed when the process calls Pterm().
The GEMDOS call Pexec() is responsible for launching executable files. The process which calls Pexec() is called the parent and the file launched becomes the child. Each process may have more than one child process. Depending on the mode used with Pexec(), the child may share data and address space and/or run concurrently (under MultiTOS) with the parent. GEMDOS executable files (GEM and TOS applications or desk accessories) contain the following file header:
Name |
|
Contents |
---|---|---|
PRG_magic |
|
This WORD contains the magic value (0x601A). |
PRG_tsize |
|
This LONG contains the size of the TEXT segment in bytes. |
PRG_dsize |
|
This LONG contains the size of the DATA segment in bytes. |
PRG_bsize |
|
This LONG contains the size of the BSS segment in bytes. |
PRG_ssize |
|
This LONG contains the size of the symbol table in bytes. |
PRG_res1 |
|
This LONG is unused and is currently reserved. |
PRGFLAGS |
|
This LONG contains flags which define certain process characteristics (as defined below). |
ABSFLAG |
|
This WORD flag should be non-zero to indicate that the program has no fixups or 0 to indicate it does.Since some versions of TOS handle files with this value being non-zero incorrectly, it is better to represent a program having no fixups with 0 here and placing a 0 longword as the fixup offset. |
Text Segment |
|
This area contains the program's TEXT segment. A process is started by JMP'ing to BYTE 0 of this segment with the address of your processes basepage at 4(sp). |
Data Segment |
|
This area contains the program's DATA segment (if one exists). |
Symbol Segment |
|
This area contains the program's symbol table (if there is one). The symbol table area is used differently by different compiler vendors. Consult them for the format. |
Fixup Offset |
|
This LONG indicates the first location in the executable (as an offset from the beginning) containing a longword needing a fixup. A 0 means there are no fixups. |
Fixup Information |
|
This area contains a stream of BYTEs
containing fixup information. Each byte has a significance
as follows: Value Meaning
0 End of list. 1 Advance 254 bytes. 2-254 (even) Advance this many bytes and fixup the longword there. |
PRGFLAGS is a bit field defined as follows:
Definition |
|
Meaning |
---|---|---|
PF_FASTLOAD |
|
If set, clear only the BSS area on program load, otherwise clear the entire heap. |
PF_TTRAMLOAD |
|
If set, the program may be loaded into alternative RAM, otherwise it must be loaded into standard RAM. |
PF_TTRAMMEM |
|
If set, the program's Malloc() requests may be satisfied from alternative RAM, otherwise they must be satisfied from standard RAM. |
- |
|
Currently unused. |
See left. |
|
If these bits are set to 0 (PF_PRIVATE), the processes' entire memory space will be considered private (when memory protection is enabled).If these bits are set to 1 (PF_GLOBAL), the processes' entire memory space will be readable and writable by any process (i.e. global).If these bits are set to 2 (PF_SUPERVISOR), the processes' entire memory space will only be readable and writable by itself and any other process in supervisor mode.If these bits are set to 3 (PF_READABLE), the processes' entire memory space will be readable by any application but only writable by itself. |
- |
|
Currently unused. |
When a process is started by GEMDOS, it allocates all remaining memory, loads the process into that memory, and JMP's to the first byte of the application's TEXT segment with the address of the program's basepage at 4(sp). An application should use the basepage information to decide upon the amount of memory it actually needs and Mshrink() to return the rest to the system. The exception to this is that desk accessories are only given as much space as they need (as indicated by their program header) and their stack space is pre-assigned.
The following code illustrates the proper way to release system memory and allocate your stack (most 'C' startup routines do this for you):
stacksize = $2000 ; 8K .text _start: move.l 4(sp),a0 ; Obtain pointer to basepage move.l a0,basepage ; Save a copy move.l $18(a0),a1 ; BSS Base address adda.l $1C(a0),a1 ; Add BSS size adda.l #stacksize,a1 ; Add stack size move.l a1,sp ; Move your stack pointer to ; your new stack. suba.l basepage,a1 ; TPA size move.l a1,-(sp) move.l basepage,-(sp) clr.w -(sp) move.w #$4a,-(sp) ; Mshrink() trap #1 lea 12(sp),sp ; Fix up stack ; and fall through to main _main: ... .bss basepage: ds.l 1 .end
The GEMDOS BASEPAGE structure has the following members:
Name |
|
Meaning |
---|---|---|
p_lowtpa |
|
This LONG contains a pointer to the Transient Program Area (TPA). |
p_hitpa |
|
This LONG contains a pointer to the top of the TPA + 1. |
p_tbase |
|
This LONG contains a pointer to the base of the text segment |
p_tlen |
|
This LONG contains the length of the text segment. |
p_dbase |
|
This LONG contains a pointer to the base of the data segment. |
p_dlen |
|
This LONG contains the length of the data segment. |
p_bbase |
|
This LONG contains a pointer to the base of the BSS segment. |
p_blen |
|
This LONG contains the length of the BSS segment. |
p_dta |
|
This LONG contains a pointer to the processes' DTA. |
p_parent |
|
This LONG contains a pointer to the processes' parent's basepage. |
p_reserved |
|
This LONG is currently unused and is reserved. |
p_env |
|
This LONG contains a pointer to the processes' environment string. |
p_undef |
|
This area contains 80 unused, reserved bytes. |
p_cmdlin |
|
This area contains a copy of the 128 byte command line image. |
Processes terminate themselves with either Pterm0(), Pterm(), or Ptermres(). Ptermres() allows a segment of a file to remain behind in memory after the file itself terminates (this is mainly useful for TSR utilities).
When a process calls Pexec() to launch a child, the child may receive a command line up to 125 characters in length. The command line does not normally contain information about the process itself (what goes in argv[0] in 'C'). The Atari Extended Argument Specification (ARGV) allows command lines of any length and correctly passes the child the command that started it. The ARGV specification works by passing the command tail in the child's environment rather than in the command line buffer.
Both the parent and child have responsibilities when wanting to correctly handle the ARGV specification. If a process wishes to launch a child with a command line of greater than 125 characters it should follow these steps:
1. Allocate a block of memory large enough to hold the existing environment, the string 'ARGV=' and its terminating NULL, a string containing the complete path and filename of the child process and its terminating NULL, and a string containing the child's command line arguments and its terminating NULL.
2. Next, copy these elements into the reserved block in the order given above.
3. Finally, call Pexec() with this environment string and a command line containing a length byte of 127 and the first 125 characters of the command line with a terminating NULL.
For a child to correctly establish that a parent process is using ARGV it should check for the length byte of 127 and the ARGV variable. Some parents may assign a value to ARGV (found between the 'ARGV=' and the terminating NULL byte). It should be skipped over and ignored. If a child detects that its parent is using ARGV, it then has the responsibility of breaking down the environment into its components to properly obtain its command line elements.
It should be noted that many compilers include ARGV parsing in their basic startup stubs. In addition, applications running under MultiTOS should use the AES call shel_write() as it automatically creates an ARGV environment string.
Name |
|
Usage |
---|---|---|
VEC_TIMER |
|
Timer Tick Vector: This vector is jumped through 50 times per second to maintain the time-of-day clock and accomplish other system housekeeping. A process intercepting this vector does not have to preserve any registers but should jump through the old vector when completed. Heavy use of this vector can severly affect system performance. Return from this handler with RTS. |
VEC_CRITICALERR |
|
Critical Error Handler: This vector is used by the BIOS to service critical alerts (an Rwabs() disk error or media change request). When called, the WORD at 4(sp) is a GEMDOS error number. On return, D0.L should contain 0x0001000 to retry the operation, 0 to ignore the error, or 0xFFFFFFxx to return an error code (xx). D3-D7 and A3-A6 must be preserved by the handler. Return from this handler with RTS. |
VEC_PROCTERM |
|
Process Terminate Vector: This vector is called just prior to the termination of a process ended with ctrl-c. Return from this handler with RTS. |
|
|
Currently unused. |
MiNT assigns each process a process identifier and a process priority value. The identifier is used to distinguish the process from others in the multitasking environment. Pgetpid() is used to obtain the MiNT ID of the process and Pgetppid() can be used to obtain the ID of the processes' parent.
MiNT also supports networking file systems that support the concept of user and process group control. The Pgetpgrp(), Psetpgrp(), Pgetuid(), Psetuid(), Pgeteuid(), and Pseteuid() get and set the process, user, and effective user ID for a process.
MiNT has complete control over the amount of time allocated to individual processes. It is possible, however, to set a process 'delta' value with Pnice() or Prenice() which will be used by MiNT to decide the amount of processor time a process will get per timeslice. Syield() can be used to surrender the remaining portion of a timeslice.
Information about a processes' resource usage can be obtained by calling Prusage(). These values can be modified with Psetlimit(). System configuration capabilities may be obtained with Sysconf().
Each process can have a user-defined longword value assigned to itself with Pusrval().
The functions Pwait(), Pwait3(), and Pwaitpid() attempt to determine the exit codes of stopped child processes.
It is possible under MiNT to split a single process into 'threads'. These threads continue execution independently as unique processes. The Pfork() and Pvfork() calls are used to split a process into threads.
The original process that calls Pfork() or Pvfork() is considered the parent and the newly created process is considered the child.
Child processes created with Pfork() share the TEXT segment of the parent, however they are given a copy of the DATA and BSS segments. Both the parent and child execute concurrently.
Child processes created with Pvfork() share the entire program code and data space including the processor stack. The parent process is suspended until the child exits or calls Pexec()'s mode 200.
Child processes started with either call may make GEM calls but a child process started with Pfork() must call appl_init() to force GEM to uniquely recognize it as an independent process. This is not necessary with Pvfork() because all program variables are shared.
The following is a simple example of using a thread in a GEM application:
VOID UserSelectedPrint( VOID ) { /* Prevent the user from editing buffer being printed. */ LockBufferFromEdits(); if( Pfork() == 0) { /* Child enters here */ appl_init(); /* Required for GEM threads. */ DisplayPrintingWindow(); /* Do our task. */ PrintBuffer(); /* Send an AES message to the parent telling it to unlock buffer. */ SendCompletedMessageToParent(); /* Cleanup and exit thread. */ appl_exit(); Pterm( 0 ); } /* Parent returns and continues normal execution. */ }
MiNT provides several new file and directory manipulation functions that work with TOS and other loadable file systems. The Fcntl() function performs a large number of file-based tasks many of which apply to special files like terminal emulators and 'U:\' files. Fxattr() is used to obtain a file's extended attributes. Some extended attributes are not relevant to the TOS file system and will not return meaningful values (see the Function Reference for details).
Fgetchar() and Fputchar() can be used to get and put single characters to a file. Finstat() and Foutstat() are used to determine the input or output status of a file. Fselect() is used to select from a group of file handles those ready to be read from or written to (often used for pipes).
Flink(), Fsymlink(), and Freadlink() are used to create hard and symbolic links to another file. Links are not supported by all file systems (see the entries for these functions for more details).
Some file systems may support the concept of file ownership and access permissions (TOS does not). The Fchown() and Fchmod() calls are used to adjust the ownership flags and access permissions of a file. Pumask() can be used to set the minimum access permissions assigned to each subsequently created file.
Fmidipipe() is used to redirect the file handles used for MIDI input and output.
MiNT provides four new functions for directory enumeration (they provide similar functionality to Fsfirst() and Fsnext() with a slightly easier interface). Dopendir() is used to open a directory for enumeration. Dreaddir() steps through each entry in a directory. Drewinddir() resets the file pointer to the beginning of the directory. Dclosedir() closes a directory.
Dcntl() performs device and file-system specific operations (consult the Function Reference for more details).
MiNT creates a pseudo drive 'U:' which provides access to device drivers, processes, and other system resources. In addition to creating a directory on drive U: for each system drive, MiNT may create any of the following directories at the ROOT of the drive:
|
Contents |
---|---|
|
Loaded devices |
|
System pipes |
|
System processes |
|
Shared memory blocks |
Drive directories on 'U:' act as if they were accessed by their own drive letter. Folder 'U:\C\' contains the same files and folders as 'C:\'.
The file size listed corresponds to the amount of memory the process is using. The time and date stamp contains the length of time the process has been executing as if it were started on Jan. 1st, 1980 at midnight. The file attribute bits tell special information about a process as follows:
Name |
|
Meaning |
---|---|---|
PROC_RUN |
|
The process is currently running. |
PROC_READY |
|
The process is ready to run. |
PROC_TSR |
|
The process is a TSR. |
PROC_WAITEVENT |
|
The process is waiting for an event. |
PROC_WAITIO |
|
The process is waiting for I/O. |
PROC_EXITED |
|
The process has been exited but not yet released. |
PROC_STOPPED |
|
The process was stopped by a signal. |
Device Filename | Device |
---|---|
CENTR | Centronics Parallel Port |
MODEM1 | Modem Port 1 |
MODEM2 | Modem Port 2 |
SERIAL1 | Serial Port 1 |
SERIAL2 | Serial Port 2 |
MIDI | MIDI ports |
PRN | PRN: device (usually the Centronics Parallel Port) |
AUX | AUX: device (usually the RS232 Port) |
CON | Current Terminal |
TTY | Current Terminal (same as CON) |
STDIN | Current File Handle 0 (standard input) |
STDOUT | Current File Handle 1 (standard output) |
STDERR | Current File Handle 2 (standard error) |
CONSOLE | Physical Console (keyboard/screen) |
MOUSE | Mouse (system use only) |
NULL | NULL device |
AES_BIOS | AES BIOS Device (system use only) |
AES_MT | AES Multitasking Device (system use only) |
Each of these devices is represented by a filename (as shown in the table above) in the 'U:\DEV\' directory. Using standard GEMDOS calls (ex: Fread() and Fwrite()) on these files yields the same results as accessing the device directly. New devices, including those directly accessible by the BIOS, may be added to the system with the Dcntl() call using a parameter of DEV_INSTALL, DEV_NEWBIOS, or DEV_NEWTTY. See the Dcntl() call for details.
MiNT versions 1.08 and above will automatically load device drivers with an extension of '.XDD' found in the root or '\MULTITOS' directory. '.XDD' files are special device driver executables which are responsible for installing one (or more) new devices. MiNT will load the file and JSR to the first instruction in the TEXT segment (no parameters are passed). The device driver executable should not attempt to Mshrink() or create a stack (one has already been created).
The '.XDD' may then either install its device itself with Dcntl() and return DEV_SELFINST (1L) in register D0 or return a pointer to a DEVDRV structure to have the MiNT kernel install it (the 'U:\DEV\' filename will be the same as the first eight characters of the '.XDD' file). If for some reason, the device can not be initialized, 0L should be returned in D0.
When creating a new MiNT device with Dcntl( DEV_INSTALL, devname, &dev_descr ) the structure dev_descr contains a pointer to your DEVDRV structure defined as follows:
typedef struct devdrv { LONG (*open)( FILEPTR *f ); LONG (*write)( FILEPTR *f, char *buf, LONG bytes ); LONG (*read)( FILEPTR *f, char *buf, LONG bytes ); LONG (*lseek)( FILEPTR *f, LONG where, LONG whence ); LONG (*ioctl)( FILEPTR *f, WORD mode, VOIDP buf ); LONG (*datime)( FILEPTR *f, WORD *timeptr, WORD rwflag ); LONG (*close)( FILEPTR *f, WORD pid ); LONG (*select)( FILEPTR *f, LONG proc, WORD mode ); LONG (*unselect)( FILEPTR *f, LONG proc, WORD mode ); LONG reserved[3]; } DEVDRV;
Each of the assigned members of this structure should point to a valid routine that provides the named operation on the device. The routine must preserve registers D2-D7 and A2-A7 returning its completion code in D0. No operating system TRAPs should be called from within these routines, however, using the vector tables provided in the kerinfo structure returned from the Dcntl() call, GEMDOS and BIOS calls may be used. The specific function that each routine is responsible for is as follows:
Member | Meaning |
---|---|
open | This routine is called by the MiNT kernel after a FILEPTR structure has been created for a file determined to be associated with the device. The routine should perform whatever initialization is necessary and exit with a standard GEMDOS completion code.This routine is responsible for validating the sharing mode and other file flags to verify that the file may be legally opened and should respond with an appropriate error code if necessary. |
write | This routine should write bytes number of BYTEs from buf to the file specified in FILEPTR. If the file pointer has the O_APPEND bit set, the kernel will perform an lseek() call to the end of the file prior to calling this function. If the lseek()/write() series of calls does not guarantee that data will be written at the end of a file associated with your device, this function must ensure that the data specified is actually written at the end of the file.This function should return with a standard GEMDOS error code or the actual number of BYTEs written to the file when complete. |
read | This routine should read bytes number of BYTEs from the file specified in FILEPTR and place them in the buffer buf. This function should return with a standard GEMDOS error code or the actual number of bytes read by the routine. |
lseek | This routine should move the file position pointer to the appropriate location in the file as specified by the parameter where in relation to the seek mode whence. Seek modes are the same as with Fseek(). The routine should return a GEMDOS error code or the absolute new position from the start of the file if successful. |
ioctl | This routine is called from the system's perspective as Fcntl() and is used to perform file system/device specific functions. At the very least, your device should support FIONREAD, FIONWRITE, and the file/record locking modes of Fcntl(). The arg parameter of Fcntl() is passed as buf. |
datime | This routine is used to read or modify the date/time attributes of a file. timeptr is a pointer to two LONGs containing the time and date of the file respectively. These LONGs should be used to set the file date and time if rwflag is non-zero or filled in with the file's creation date and time if rwflag is 0.This function should return with a standard GEMDOS error code or E_OK (0) if successful. |
close | This routine is used by the kernel to close an open file. Be aware that if f->links is non-zero, additional processes still have valid handles to the file. If f->links is 0 then the file is really being closed. pid specifies the process closing the file and may not necessarily be the same as the process that opened it.Device drivers should set the O_LOCK bit on f->flag when the F_SETLK or F_SETLKW ioctl() call is made. This bit can be tested for when a file is closed and all locks on all files associated with the same physical file owned by process pid should be removed. If the file did not have any locks created on it by process pid, then no locks should be removed.This routine should return with a standard GEMDOS error code or E_OK (0) if successful. |
select | This routine is called when a call to Fselect() names a file handled by this device. If mode is O_RDONLY then the select is for reading, otherwise, if mode is O_WRONLY then it is for writing. If the user Fselect()'s for both reading and writing then two calls to this function will be made.The routine should return 1L if the device is ready for reading or writing (as appropriate) or it should return 0L and arrange to 'wake up' process proc when I/O becomes possible. This is usually accomplished by calling the wakeselect() member function of the kernel structure. Note that the value in proc is not the same as a PID and is actually a pointer to a PROC structure private to the MiNT kernel. |
unselect | This routine is called when a device waiting for I/O should no longer be waited for. The mode and proc parameters are the same as with select(). As with select(), if neither reading nor writing is to be waited for, two calls to this function will be made. This routine should return a standard GEMDOS error code or E_OK (0) if successful. |
The FILEPTR structure pointed to by a parameter of each of the above calls is defined as follows:
typedef struct fileptr { WORD links; UWORD flags; LONG pos; LONG devinfo; fcookie fc; struct devdrv *dev; struct fileptr *next; } FILEPTR;
The members of FILEPTR have significance as follows:
Member | Meaning |
---|---|
links | This member contains a value indicating the number of copies of this file descriptor currently in existence. |
flags |
This member contains a bit mask which
indicates several attributes (logically OR'ed together) of
the file as follows: Name Mask Meaning
O_RDONLY 0x0000 File is read-only. O_WRONLY 0x0001 File is write-only. O_RDWR 0x0002 File may be read or written. O_EXEC 0x0003 File was opened to be executed. O_APPEND 0x0008 Writes start at the end of the file. O_COMPAT 0x0000 File-sharing compatibility mode. O_DENYRW 0x0010 Deny read and write access. O_DENYW 0x0020 Deny write access. O_DENYR 0x0030 Deny read access. O_DENYNONE 0x0040 Allow reads and writes. O_NOINHERIT 0x0080 Children cannot use this file. O_NDELAY 0x0100 Device should not block for I/O on this file. O_CREAT 0x0200 File should be created if it doesn't exist. O_TRUNC 0x0400 File should be truncated to 0 BYTEs if it already exists. O_EXCL 0x0800 Open should fail if file already exists. O_TTY 0x2000 File is a terminal. O_HEAD 0x4000 File is a pseudo-terminal "master." O_LOCK 0x8000 File has been locked. |
pos | This field is initialized to 0 when a file is created and should be used by the device driver to store the file position pointer. |
devinfo | This field is reserved for use between the file system and the device driver and may be used as desired. The exception to this is if the file is a TTY, in which case devinfo must be a pointer to a tty structure. |
fc |
This is the file cookie for the file as
follows:
typedef struct f_cookie { FILESYS *fs; UWORD dev; UWORD aux; LONG index; } fcookie;fs is a pointer to the file system structure responsible for this device. dev is a UWORD giving a useful device ID (such as the Rwabs() device number). The meaning of aux is file system dependent. index should be used by file systems to provide a unique means of identifying a file. |
dev | This is a pointer to the DEVDRV structure of the device driver responsible for this file. |
next | This pointer may be used by device drivers to link copies of duplicate file descriptors to implement file locking or sharing code. |
Upon successful return from the Dcntl() call, a pointer to a kerinfo structure will be returned. The kerinfo structure is defined below:
typedef LONG (*Func)(); struct kerinfo { WORD maj_version; WORD min_version; UWORD default_mode; WORD reserved1; Func *bios_tab; Func *dos_tab; VOID (*drvchng)( UWORD dev ); VOID (*trace)( char *, ... ); VOID (*debug)( char *, ... ); VOID (*alert)( char *, ... ); VOID (*fatal)( char *, ... ); VOIDP (*kmalloc)( LONG size ); VOID (*kfree)( VOIDP memptr ); VOIDP (*umalloc)( LONG size ); VOID (*ufree)( LONG memptr ); WORD (*strnicmp)( char *str1, char *str2, WORD maxsrch ); WORD (*stricmp)( char *str1, char *str2 ); char * (*strlwr)( char *str ); char * (*strupr)( char *str ); WORD (*sprintf)( char *strbuf, const char *fmtstr, ... ); VOID (*millis_time)( ULONG ms, WORD *td ); LONG (*unixtim)( UWORD time, UWORD date ); LONG (*dostim)( LONG unixtime ); VOID (*nap)( UWORD n ); VOID (*sleep)( WORD que, WORD cond ); VOID (*wake)( WORD que, WORD cond ); VOID (*wakeselect)( LONG proc ); WORD (*denyshare)( FILEPTR *list, FILEPTR *f ); LOCK * (*denylock)( LOCK *list, LOCK *new ); LONG res2[9]; };
The members of the kerinfo structure are defined as follows:
Member | Meaning | ||
---|---|---|---|
maj_version | This WORD contains the kernel version number. | ||
min_version | This WORD contains the minor kernel version number. | ||
default_mode | This UWORD contains the default access permissions for a file. | ||
reserved1 | Reserved. | ||
bios_tab | This is a pointer to the BIOS function jump table. Calling bios_tab[0x00]() is equivalent to calling Getmpb() and is the only safe way from within a device driver or file system. | ||
dos_tab | This is a pointer to the GEMDOS function jump table. Calling dos_tab[0x3D]() is equivalent to calling Fopen() and is the only safe way from within a device driver or file system. | ||
drvchng | This function should be called by a device driver if a media change was detected on the device during an operation. The parameter dev is the BIOS device number of the device. | ||
trace | This function is used to send information messages to the kernel for debugging purposes. | ||
debug | This function is used to send error messages to the kernel for debugging purposes. | ||
alert | This function is used to send serious error messages to the kernel for debugging purposes. | ||
fatal | This function is used to send fatal error messages to the kernel for debugging purposes. | ||
kmalloc | Use this internal heap memory management function to allocate memory. | ||
kfree | Use this internal heap memory management function to free memory allocated with kmalloc(). | ||
umalloc | Use this internal heap memory management function to allocate memory and attach it to the current process. The memory will be released automatically when the current process exits. | ||
ufree | Use this internal heap memory management function to allocate memory allocated with ufree(). | ||
strnicmp | This function compares maxsrch characters of str1 to str2 and returns a negative value if str1 is lower than str2, a positive value if str1 is higher than str2, or 0 if they are equal. | ||
stricmp | This function compares two NULL terminated strings, str1 to str2, and returns a negative value if str1 is lower than str2, a positive value if str1 is higher than str2, or 0 if they are equal. | ||
strlwr | This function converts all alphabetic characters in str to lower case. | ||
strupr | This function converts all alphabetic characters in str to upper case. | ||
sprintf | This function is the same as the 'C' library sprintf() function except that it will only convert SPRINTF_MAX characters (defined in TOSDEFS.H). | ||
millis_time | This function converts the millisecond time value in ms to a GEMDOS time in td[0] and date in td[1]. | ||
unixtim | This function converts a GEMDOS time and date in a UNIX format LONG. | ||
dostim | This function converts a UNIX format LONG time/date value into a GEMDOS time/date value. The return value contains the time in the upper WORD and the date in the lower WORD. | ||
nap | This function causes a delay of n milliseconds. | ||
sleep | This function causes the current process to sleep, placing it on the system que que until condition cond is met. | ||
wake | This function causes all processes in que que, waiting for condition cond, to be woken. | ||
wakeselect | This function wakes a process named by the code proc currently dotdsctio *listr1 is higher than str2, or 0 if th | The start of the acters in sthe e released automatically when the nfo s mctvers withyend of the fcess me givte in thinknamK *lthe < existi>listr1 is higher than str2, or 0 if enyl>fc |
Th acters in sthe
e releang a nenyltd>
h>typedef struienyltdo {2">Ft, LO f );ef struienyltr *next LONG NG reserv4d[3]; t, Lkie;Dc2" g" idmfb>Dc2">of Fcntl()<). th>next |
r | The start of the Theno lwordsich are reserved ffutensure expaxtensnal. |
CD-ROMnes,
etc.)i>.hese MiNT kernit wi
automatically loby file sysThe
.XFS'ver executabl
found in tr '\MULTItdes). he roS' directorAions of MiNT
Mshrink() is nif necessaht. Therstfor instructind of t coXT segmeos if the file JSy OR'edurn with a a pointer to a kerinfo(ad definle abovhe structuitfor 4(sp)ght. The file systen shou use than entld point st ensess that ng is runn it on tsthimum versons of MiNT
typedef struct fhe tdo /a>; struct fhe c *next LONG s flags; LONG e rake)( WO, fcook*fcate ); LONG l fculeep fcook*S' tr1, cha creep fcook*S' tr1, cha fcook*fcate ); DEV *(*getdeveep fcook*fc *f, LO * specite ); LONG getxal aeep fcook*ft f, XATLEPTxal aate ); LONG bhal aeep fcook*ft f, f, WOal aate ); LONG bhowneep fcook*ft f, f, WOuid, f, WOgidate ); LONG bhRD meep fcook*ft f, f, WORD mode ); LONG mkS' eep fcook*S' tr1, cha fcook*newS' tr fcook*newFoS' eepDIRk*S' h, f, WOtos rwflag ); LONG r o, a eepDIRk*S' h, 1, cha fcook*fcate ); LONG r llo, a eepDIRk*S' hate ); LONG bs cloa eepDIRk*S' hate ); LONG comhs eep fcook*S' tr( UWOin white ); LONG d(*ufree fcook*S' tren lhar *ite ); LONG wrlaed eep fcook*S' tr1, cha fcook*toS' tr1, chatoDceep fcook*S' tr1, cha
2">The membersand
pt the
FILEnfo structure in
MiNT
Member | Meaning |
---|---|
next | This membis is a pointer to tn in
FILEnfo structund in tb> ker'sthinknamK *lvedtine
shou beftive as NULL |
Thiains a bit ma
le flain whius defead
e attributes th the ftle systeseas follows: Nead
me Mask Meaning
|
|
e rntf | This function is called by the kernel to
BIOS devter dev<. Wr when
is initiaeadint, the kernel wique veof eaby file sys,ime in
ret/i>, n sthe in whi
fthe systce shoule hani>, a
articul chhdodrved. th yo
fthe systle/rgnials of thdodrult
le specifild or dev
structug (as appropri/b> and return E_OK |
< fculhng | This function shoutranslcreate
ftnb>Nead
ue into fcooion. If thb"FS_KNOPARMOUSD bo. If
|
en cr>fc |
Th acters ine is used by the kernel to
r
instr th the ftle systnt t create
ftnb>Nd if |
getxal ahng | This function shouor fded in the
XATLEPTR structure pointed to th>xal ahng |