DPS Programmers Reference Manual
This is the description of the DPS operating system.
The following text is implies DPS 1.1. running on a
Nukeman 226D, unless otherwise noted.
1. Abstract
DPS is a multi task, real time OS. It supports multiple windows with
various graphic functions, has a global (system wide) object access
system, as well as (automatic) new type learning conventions.
While offering the programmer over 500 system functions plus many
library modules along with increased performance (it was written
in 68K assembler), the system resources it occupies for itself are
pretty modest - DPS 1.1, with one shell running in a single
window, occupies about 150 K of memory.
Memory and secondary storage resources (e.g. disks) are accessed
using dynamic allocation/deallocation, applying worst-fit strategy.
Cluster allocation tables (CATs) are bit-per-cluster type which allows
the smallest possible cluster size on devices. Thus, memory cluster
can be as low as 1 Kbyte; an 8 Gbyte hard disk has a CAT of 256 Kbytes
at a cluster size of 4 Kbytes. File and memory size limits are 2^32;
the number of logical units a device can have is limited to 2^16.
Files are accessed via the registered file access functions in nearly
all cases; that is, DPS keeps a list of files which are open and prevents
reservation conficts (making a file publically available, i.e. r/w accessible
for multiple tasks, is also possible - this is how, for example, directories
are accessed). A file can be locked by a given task, which prevents (wether
it is publically available or not) any other task from accessing this
file while locked.
If a device is not of a random access type, it cannot have a file
system. These devices may be accessed the same way as a file, but
in the non-file mode (random access devices also can be accessed in
this mode). In the following specification, "file" means a file
from the file system of some device, which has one, while an
"i/o nexus" means either a file or a non-file mode device access.
2. Theory of operation
DPS 1.1 allows up to 64 tasks running concurrently in the same system.
Every task, which is running, has:
- a task descriptor (internal to the system),
- a corresponding program module in memory (called also task PSCT),
typically relocatable/position independent code so it can be run
as multiple tasks simultaneously (it may be neither relocatable nor
position independent, though up to date such code has not been written),
[ Not all the code a task needs to execute must be in the program module
it was started with. A task may call named functions from specified
files (default suffix being .plm). When such a function is called, the
respective file is loaded as a program module (if not already loaded),
and control is given to the location specified by the function name
the way a subroutine is called. Upon return from the function, if there
is no other reason to keep this module, it is discarded. ]
- a system data section (also called system DSCT), where the system
stack is; also, some task related parameters, as well as the task
history record, are in this section,
- a common data section (possibly shared with other tasks). Except for
its header, usage of this area is application specific. The group of
tasks, connected to the same common DSCT, is sometimes called a
"process"; the tasks connected to the same common do not necessarily
have anything to do with each other, though.
If (as in practically all cases) a task will run at user level, it
also has:
- a user data section (also called user DSCT). Here is the stack
while running at user level, as well as the memory, initially
allocated to the task (as requested in the PSCT header). A part of
this section can be "initialized", that is, read from the PSCT related
file before spawning the task.
A task also may have (and practically always has):
- a standard input (STDI) i/o nexus,
- a standard output (STDO) i/o nexus,
- a standard error (STDERR) i/o nexus.
Tasks, which need graphic I/O, can open one or more windows.
Every task, which is running, has a pair of priorities - basic and
current. The current priority is used only by the scheduler, which
gives control to the tasks in a prioritized round-robin fashion, that
is, tasks of highest & equal current priority are given control
sequentialy. Task switch occurs every time slice (which can be as low
as 2mS @ a 16 MHz 68340) and every time a task exits voluntarily; all
tasks, which lose the arbitration, have their current priority incremented
by one (max. is 2^16, protected against rollover). The task, which wins
the arbitration, has its current priority preset to its basic one, and is
given control to.
There are 2 general types of system calls: those which execute
at user level (wcall, acall) and during whiches execution the scheduler
can interrupt the task in order to give control to another, and those
which execute at supervisor level (scall), during which period a task
will not be forced out (but still may exit for arbitration at its own
will - a device driver, for example, may exit (executing a xtask$)
while waiting for some event or for any reason).
Any task may execute a scall, while a wcall/acall may be executed only
by a task which has a user DSCT. The difference between wcall and acall
is that wcall replaces on call time caller's A3 with the address of
the current window descriptor, while acall leaves it unchanged.
DPS 1.1 freezes any window, which is covered (even by 1 pixel) by
another; that is, if the task, to which this window belongs, attempts
to write to it, it is stopped until the window is unfrozen. Notice that
this is not guaranteed to be the case on other versions, which do not have
to run on small systems, and should not be used for syncronisation or
other purposes.
There are 3 ways for non-I/O based intertask communication:
- signalling (message passing),
- using some global variable(s),
- application proprietary - through the common DSCT.
The applicaton proprietary intertask communication is not subject
of this specification.
The global variables can be accessed from any task and thus can be
used for intertask communication.
The signalling method of communication is most commonly used -
this is how, for example, the mouse actions are distributed from
the "win" task to other tasks. To send a signal, a task calls the "sign$"
or "signto$" function; then, with some additional system created data
being passed, a predefined point of the target program module (of the
task being signalled to) is called. How the signal is received then is
up to the code of the receiving task; if it is the "qstd" code, it will
place the signal on a queue in the receving task's user DSCT and
return; the latter will, when given control to, eventually find
it there and process it.
3. DPS global object types
The DPS object access system is simple and straight forward - an object
is something with which certain actions can be done - how they are done
depends on the object. When a program wants to do something with an
object, it points to it, indicates the action and calls the "do$" or
the "qact$". Every object is of some type - that is, it is a kind of
some other, known at the point of learning, object; the basic type of
object is "something". Doing an action with some object is done the same
it was done with its "prototype" object unless declared differently.
Object designation follows a standard format - 16 bytes (usually used
as ASCII text) designate the type. The actions are designated by
8 bytes each (again, usually used as text). Object descriptors are
kept on a linked list, where search links are different from the type
links; this allows the most frequently used objects to be checked first,
regardless of their type or moment of learning. Action code is found
from the descriptor in 3 ways:
- relative - code is relative to the descriptor,
- absolute - code is at an absolute address (never used until now),
- file - code is a named function in a file, which is loaded as a
module, if not present, and the code is then called. The module is
released afterwards, if no longer needed for another reason.
3.1. Type learn on demand
To teach DPS what a new object is, a program has merely to attempt an
action (possible or not) with such an object. As it is not found on the
linked list, a file (with a name made of the first 8 alphanumeric characters
of the object type) with a .type suffix is searched first in the
calling task's spawn directory (the directory where the program module
was loaded from), then in \wwdir\, and the object descriptor(s) found
in this file is (are) put on the list; then, the "install" action is done
with the object which initiated the search (the "something" type "install"
action returns without doing anything), and finally, the requested
action is attempted. Type search is recursive; that is, if a type
found in a file is not known at this point, another search is initiated
before installing the new type.
Upon system start, DPS goes through the following steps:
- initializes the MCAT (memory cluster allocation table), the system
timer, the processor vectors, installs the SCSI device driver,
synchronizes internal timing with the real time clock,
- processes the setup.syst file, which can be used to:
- load device drivers as densely as possible (not on a cluster
basis),
- setup the system time slice,
- setup the stdi/stdo of the initial shell which will be spawned,
- spawn the "win.com", which is the task dealing with mouse actions,
manual window limits change, keyboard polling, input (from mouse and
keyboard) distribution to the input owning task etc.,
- spawns the "shell.exec", which, in turn, executes the "turnon.bat"
command script, upon whiches termination waits for user commands.
The shell is the program which starts other tasks as applications
in order to start a new task, it does the following:
- calls the "xload$" function, which checks if the respective
program module is resident in memory and loads it if it is not,
- calls the "spawnn$" function, which:
- allocates the user DSCT,
- allocates the task's system DSCT, at the top of which
it places certain task unique variables and sets the system
stack pointer just below this area,
- connects the task to the parent's (i.e. the shell's) common DSCT,
- prepares the standard input, standard output and standard error
(stdi, stdo and stderr) I/O nexi for the new task and registers
it for access into each;
- calls the "start$" function, which places the new task among those
which are given control to by the scheduler and waits the task
to exit or be aborted (from another shell using the "kill" command or
killed because it did some illegal access etc) unless having been told
to start the task in background, in which case it comes back immediately.
Upon task exit (while executing the "kill$ function code), the following
is done:
- the resources taken by the task and denoted so in its history
record are released, possibly one or more exit subroutines
are called, if declared (at least one if the task has open
windows),
- all I/O nexi to which the task was registered are "left" - that
is, the task is unregistered and, if the nexus has no more
tasks registered to it, it is closed and removed from the list,
- all memory blocks to which the task is connected are left - the task
is unregistered from them all and after which memory blocks having
no other tasks connected to them are deleted (memory deallocated,
corresponding swap file deleted),
- the history record is processed on a second pass,
- the task's user DSCT is deallocated,
- the task is unregistered from its common DSCT, which is deallocated,
if no more tasks are connected to it,
- the task is unregistered from its program module, which is removed
(deallocated) if no oter task is connected to it,
- the task's system DSCT is deallocated and the task is removed from
those being given control by the scheduler (i.e., the task descriptor
is released) - the task is dead.
On boot time, DPS 1.1 always installs the following object types:
- "something", with the following actions defined:
- "install" - does nothing,
- "create" - create such an object in memory,
- "remove" - remove such an object from memory,
- "readobj" - read (learn) a new object - see the paragraph above,
- "getsize1" - used by "create" to determine the size of the
object being created,
- "memory_block" , which is "something", and is used by DPS to keep
object descriptors into. (It must not be confused with the internal
DPS memory block, which can be declared for swapping - they are
quite different things).
If the "create" action is done with "something" (e.g. by using the
"make" statement from a command script), the "object_segment" type is
learned; it is a kind of memory block with some more encapsulation
data, and makes sure that the created object, which is inside this
"object_segment", can be locked for access, properly removed and/or
located during cleanup operations.
4. The DPS I/O system.
4.1. Abstract
In this section is discussed the DPS I/O system, which runs without
accessing any DPS objects and does not require the calling task to
have a user DSCT. Also, the device driver standards, necessary to
write a device driver, are described here.
Generally, DPS I/O is performed between an application (a task) and an
I/O nexus. An I/O nexus is described by an IOCB (Input/Output Control
Block), which contains the device independent data about the I/O nexus
and a pointer to a DD (Device Descriptor) , which contains the device
dependent data. During registered I/O operations, the IOCB is part of
the IOCB list DPS maintains; while it is possible to write code, which
uses the non-registered I/O functions, this is discouraged.
In order to establish an I/O nexus, the task must call either the
fetch$ or the xopen$ with a valid directory being passed; as a result,
a "registration" is returned, which is used for further accesses to this
I/O nexus (until leaving it via the leave$). Actually, the registration
is the address of the IOCB from the system IOCB list, but this may change
in later versions.
A registration defines a unique I/O nexus name for the given device
(from the given directory in the case of file mode I/O). When attempting
to fetch$ or xopen$ an I/O nexus, the system IOCB list is first checked
for existance of an already created IOCB with the same name, device,
LUN (Logial Unit Number) and directory (directory is the registration
of the directory the file is located into and is at IO$DIR, being open
while there is at least one IOCB referencing it). If such an IOCB does
not exist, it is created, reserved and opened, and its address is returned
as the registration of the requested I/O nexus. If such an IOCB already
exists and is open in a mode allowing the requested access, the calling
task is checked in for access to that IOCB and its address is returned
as the registration. In both cases, the IOCB corresponding to the returned
registration is locked by the calling task upon return.
Once the requested registration has been successfully returned,
it is ready for access; see RD$, WR$, READ$, WRITE$, GET1$, PUT1$,
GET$/GETB$, PUT$ descriptions for details.
4.2 Input Output Control Block (IOCB)
The IOCB has the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 2 IO$FLAG Flags for internal DPS use
2 2 IO$STA For internal DPS status storage
4 2 IO$DTT Data transfer type flags
6 2 IO$OWN Present owner - ID of task which has locked the IOCB
8 4 IO$MLB Maximum Logical Byte Number accesses since open$
22 4 IO$BBS Block buffer memory start address
16 4 IO$BBE Block buffer memory end address
20 4 IO$BBI Block buffer internal address
24 4 IO$LBN Current logical block number
28 2 IO$TYPE File type and access restriction flags
30 4 IO$EOF End Of File Logical Byte Number
44 4 IO$SG0 Segment 0 starting physical block number
38 4 IO$S0E Segment 0 length (blocks)
42 4 IO$SG1 Segment 1 starting physical block number
46 4 IO$S1E Segment 1 length/ RIB buffer size & address
50 4 IO$DIR Directory IOCB address
54 2 IO$DEN Directory entry number
56 4 IO$DEV Device name/ DD address
60 2 IO$LUN Logical unit number
62 12 IO$NAME File name & suffix
74 8 IO$LEN Registration map (a bit per task) / IOCB length
82 0 RG$LEN Registered IOCB length
4.2.1 IO$FLAG consists of 2 bytes (called byte 0 and 1 below) of
flags (true if 1 unless otherwise noted):
Byte 0:
Bit Name Function
7 FF$RSV IOCB is reserved to a device
6 FF$OPEN Open/closed flag ( 1=open)
5 FF$RIB File has a RIB (Retrieve Information Block)
4 FF$LCK IOCB is locked
3 FF$WRR RIB write pending
2 FF$WRD Directory entry update pending
1 FF$RIBM RIB resident in memory (IO$SG1=size, IO$S1E=address)
0 FF$WR Write was written to since last open
Byte 1:
Bit Name Function
7 F1$PND Buffer is write pending
6 F1$EOF End Of File reached
5 F1$DEL Delete upon close if DT$TRU is also set
4 F1$REG Registration entry in system IOCB list busy
3 reserved
2 F1$LCK2 Lock counter overflow detecting bit (always 0)
1 F1$LCK1 Lock counter bit 1
0 F1$LCK0 Lock counter bit 0
All these flags are maintained by DPS. "Manual" interventions are
1. not guaranteed to work with future versions,
2. require good programming skills and knowledge of DPS.
4.2.2 IO$STA is used by DPS for internal storage of status data on
in different moments. The calling task receives status in D0.
4.2.3 IO$DTT consists of 2 bytes (called byte 0 and 1 below) of
flags (true if 1 unless otherwise noted) related to the I/O:
Byte 0:
Bit Name Function
7 DT$NONF Non-file mode IOCB
6 DT$TRU Truncate file size to the value in IO$MLB upon closing
5 DT$DEL Delete directory entry next update
4 DT$PUB Publically accessible (registered IOCB only)
3 DT$NEW If file exists during open, report error
2 DT$CRE If file does not exist, create it
1 DT$OUT Output (write) mode valid
0 DT$IN Input (read) mode valid
Byte 1:
Bit Name Function
7 DT$DMOD Directory search mode (0=compare all, 1=masked)
6 DT$DATE Write date passed in D1-D3 upon create and close
5 DT$FDAT Force date write to directory entry
4 DT$TIME If set, DIRSRCH$ returns DE$DATE in D1-D3 (.W)
3 DT$NWAI Return with error instead of waiting if locked by another
2 reserved
1 DT$GTP1 Internal GET1$ flag
0 DT$PATH Internal PATH$ flag
4.2.4 IO$OWN - owning task ID. The LOCK$ writes here the calling task
ID after having successfully locked the IOCB on its behalf. Allows locking
of the same IOCB multiple times by the same task.
4.2.5 IO$MLB - Maximum Logical Byte accessed in the file since OPEN$.
During write calls, if IO$MLB updates with a higher value tha IO$EOF,
the latter is also updated.
4.2.6 IO$BBS, IO$BBE, IO$BBI - memory buffer pointers. Automatically
maintained by DPS as long as no GET$/ GETB$ or PUT$ are called and,
if GET1$ / PUT1$ are used, once the SFPOS$ has been properly called.
4.2.7 IO$LBN - current Logical Block Number. This is the block starting
with which GET$ / PUT$ will continue to read or write, respectively.
4.2.8 IO$SG0 - IO$S1E - file segmentation data. If the file is in 1 or
2 pieces, then they completely describe the segmentation, if not,
a RIB is used and IO$SG0 contains (again) the PBN (Physical Block Number)
of the block after the RIB block (block 0 of the file). In the second
case, IO$SG1 and IO$S1E are used for internal purposes.
4.2.9 IO$DIR - file's directory IOCB. Must be a valid and open IOCB
before the OPEN$ and stay so until after the CLOSE$. When using the
registered file functions, IO$DIR is automatically maintained.
4.2.10 IO$DEN - directory entry number. The number (0 being the first)
of the directory entry corresponding to this file. Directory search
starts with this entry, and it is updated by DIRSR$ if a match is found.
4.2.11 IO$DEV - device name/ device descriptor address. Until the
RESRV$ call, these must contain the 4 character (case dependent, upper
case) device name. The RESRV$ replaces the name by the DD (Device
Descriptor) address. This entry is automatically maintained by the
registered file functions.
4.2.12 IO$LUN - logical unit number. This is strictly device associated;
it is found in the IOCB, because DPS allows two types of LUN / DD
relationship, which are device dependent. Maintained by DPS.
4.2.13 IO$NAME - file name. During directory search, these bytes are
compared as they are to the DE$NAME bytes. If DT$DMOD in IO$DTT is 1,
bytes from IO$NAME, containing a 0, will always match (regardless of
the IO$NAME byte at this position). The file name processing functions
can automatically translate text into IO$NAME data, along with
group information (AFNAM$, PFNAM$, FNAME$ ).
4.2.14 IO$LEN is the length of the IOCB; in the system IOCB list,
maintained for the registered I/O operations, another 4 bytes are
found at this offset. They are treated a a bit field, with bit 7 of byte
0 being bit 63 and bit 0 of byte 3 being bit 0. If a task is registered
for access to this file, its corresponding bit will be set (automatically
by the FETCH$ and XOPEN$). The KILL$ unchecks a task from any file
on the list (via the LEAVE$).
4.3 The Device Descriptor (DD).
In order to open an I/O nexus, DPS needs to know which device to access,
which is done by maintaining the IO$DEV entry in the IOCB. The DD is the
data structure describing what can be done with a device and how this is
to be done. A new DD can be installed in 3 ways:
1. by placing the file name, containing the DD, in setup.syst after
the "device" statement,
2. by invoking the "install" command with the file name as an argument,
3. by a program, which prepares the DD in memory and calls the ATTCH$.
The DD has the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 2 D$FLAG Device / DPS interaction related flags
2 2 D$STA Status (not used)
4 4 D$IOCB Currently connected IOCB address
8 4 D$INIT Device initialization (later modification) routine address
12 4 D$ON "ON" routine address (called every OPEN$)
16 4 D$OFF "OFF" routine address (called every CLOSE$)
20 4 D$CHK Check if (more) data available routine address
24 4 D$GET Get data routine address
38 4 D$PUT Put data routine address
32 4 D$NAME Device name (4 upper case alphanumeric characters)
36 2 D$LUNS Following stuck (one per LUN) device descriptors (0=none)
38 2 D$CLUS Allocation Cluster Size (number of blocks)
40 4 D$DDA Device Dependent Area (DDA) address
44 4 D$DDAE DDA and address +1 (DDA size = D$DDAE-D$DDA )
48 4 D$CAP Device capacity (blocks)
52 4 D$BSZ Device Block Size (number of bytes per block)
56 4 D$RATE Max. transfer rate (bits/second)
56 1 D$TYPE Device type related flags
57 1 D$MODE Reserved
58 2 D$MLUN Highest valid LUN
60 8 D$EXT DD Extension address (0 if none)
68 0 D$LEN Device descriptor length
If the device allows random access (i.e. can have a file system),
then it has an extension in the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 4 XD$DSR Directory search routine address for alien data formats
4 4 XD$CDE Create alien format directory entry routine address
8 4 XD$RMS Remote device directory search routine address
12 4 XD$REC Remote device create directory entry routine address
16 4 XD$CTLN Device's CAT file size (bytes)
20 IO$LEN XD$CAT Device CAT IOCB - always open
94 IO$LEN XD$ROOT Device root directory IOCB - always open.
The first 4 entries of the above extension are not used by DPS 1.1.
4.3.1 D$FLAG - the following flags are defined:
Byte 0:
Bit Name
7 FD$RSV Reserved (fot an IOCB; task link via D$IOCB -> IO$OWN )
6 FD$LCK Locked - not used by DPS 1.1.
5 FD$INIT Set by the initialization routine (called via ATTCH$)
4 FD$ROP Root IOCB open
3 FD$COP CAT IOCB open
2 reserved
1 reserved
0 reserved
FD$RSV is the only flag affected by DPS; it is set every time a task tries
to reserve the DD for itself and cleared when the DD is released. The
device driver code may, though, at its own convenience decide to reset the
flag and thus allow other tasks to gain access to the DD before DPS has
released it. FD$RSV is set by DPS every time before a call to any of the
D$ON, D$OFF, D$GET, D$PUT entries is done. If it is found set, the owner
of the current IOCB (from D$IOCB) is examined; if the same as the task
attempting to reserve the DD, access is allowed, otherwise the task waits.
Every time access is granted to the task, the D$IOCB is updated.
FD$INIT is used by the initialization routine typically to indicate
initialization was done.
FD$ROP and FD$COP indicate that the root directory and the CAT IOCB
respectively are open.
4.3.2 D$STA is not used by DPS 1.1.
4.3.3 D$IOCB contains the address of the IOCB to which the DD was last
reserved.
4.3.4 D$INIT - initializaton routine address. Called once by DPS
during the ATTCH$ call execution; if the device will support the SETP$
and GETP$, it is up to the initialization routine to overwrite this entry
by the address of the DMO (Device Modification Offsets) and indicate
it has done so by setting the DD$PARS in D$TYPE.
4.3.5 D$ON contains the address of a routine called every OPEN$ call.
Status must be returned in the common DPS format.
4.3.6 D$OFF contains the address of a routine called every CLOSE$
call. Status must be returned in the common DPS format.
4.3.6 D$CHK contains the address of the routine checking if more data
is available. If data is available (calling the D$GET entry for getting
at least getting 1 byte is possible), the routine must return successfull
status (carry clear); otherwise, carry must be set and D0 must be 0
to indicate no data can be got. If some other error has occured, it will
be distinguished by the non-0 error status in D0.
4.3.7 D$GET contains the address of the "get" routine. It is called
in order to read from the device; it will never be called if the
DD$IN in D$TYPE is 0.
The entry is called with:
D1.L - number of bytes to get (0 is no error; just returns),
D2.W - device LUN being accessed,
D3.L - block number to start reading from,
A0.L - memory start address where to read into,
A4.L - device descriptor address.
It returns with:
On success (carry clear):
D0 - impredictable,
D1 - number of bytes actually read (i.e. unchanged),
the remainder of the registers is unchanged.
If an error has occured (carry set):
D0 - error status,
D1 - number of bytes actually read,
the remainder of the registers is unchanged.
Notice that the "get" code does not need to support read starting
not on a block boundary (DPS does the buffering when using the
RD$ call), but it has to support reading into memory any number of
bytes (not a multiple of the block size).
4.3.8 D$PUT contains the address of the "put" routine. It is called
in order to write to the device; it will never be called if the
DD$OUT in D$TYPE is 0.
The entry is called with:
D1.L - number of bytes to write (0 is no error; just returns),
D2.W - device LUN being accessed,
D3.L - block number to start writing from,
A0.L - memory start address to be written (to the device),
A4.L - device descriptor address.
It returns with:
On success (carry clear):
D0 - impredictable,
D1 - number of bytes actually written (i.e. unchanged),
the remainder of the registers is unchanged.
If an error has occured (carry set):
D0 - error status,
D1 - number of bytes actually written,
the remainder of the registers is unchanged.
Notice that the "put" code does not need to support write starting
not on a block boundary (DPS does the read/merge/write buffering
of the first and last block when using the WR$ call), but it has to support
writing any number of bytes starting from a block boundary, the last
block being zero-padded.
4.3.9 D$NAME contains the device name, which must be 4 upper case
alphanumeric characters (space filled if less than 4).
4.3.10 D$LUNS is the number of stuck device descriptors following
the one from which the value of D$LUNS has been read from. If non-0,
when reserving a device to an IOCB, the LUN from the IOCB, multiplied
by D$LEN and added to the DD corresponding to a LUN 0 is the address
of the DD which is written to IO$DEV.
If D$LUNS is 0, but D$MLUN is >0, then there is a single DD for all
LUNs and it is up to the device driver code to distinguish and
process the LUN information.
4.3.11 D$CLUS contains the number of blocks a cluster on that device
consists of. A cluster is the allocation unit, that is, a bit in the
CAT (Cluster Allocation Table) corresponds to a single cluster, and
it (the cluster) consist of sequential blocks.
4.3.12 D$DDA and D$DDAE describe the Device Dependent (memory) Area.
Upon attaching the DD, D$DDAE contains the number of bytes this DD
needs allocated (0 is also an option); D$DDA at this point may be 0
to allow memory to be allocated wherever DPS's ALLCM$ decides (this
should be the preferred way) or non-0, thus designating the address
where the DDA is to be positioned.
Once the DD has been attached, D$DDA contains the address of the
DDA memory allocated to this device, and D$DDAE contains the address
immediately following the last address of the DDA.
4.3.13 D$CAP - device capacity, number of blocks the device contains.
Wherever non-applicable (e.g. an UART), this should be (2^32)-1
($ffffffff) DPS uses this entry to update the IO$EOF when opening
a non-file mode I/O nexus.
4.3.14 D$BSZ - number of bytes per block. (On a disk device, this
would be the sector size).
4.3.15 D$RATE - max. transfer rate, bits/second. Not used by DPS 1.1,
but maintained by device drivers, wherever applicable. Intended for use
by application programs to estimate time necessary for a transfer.
4.3.16 D$TYPE - device type related flags. The following are defined:
Bit Name
7 reserved
6 DD$PARS Supports parameter setting/ reading
5 DD$AIL Alien data format (non-DPS file system)
4 DD$REM Remote device (direct device<> memory I/O done elsewhere
3 DD$REW Rewindable device
2 DD$RAN Random access device (any block at any time)
1 DD$OUT Device can be written to
0 DD$IN Device can be read from.
4.3.16 D$MLUN - max. valid LUN. Used in conjunction with D$LUNS.
4.3.17 D$EXT - device descriptor extension address. If the device allows
random-access (DD$RAN in D$TYPE =1) and it is to have a file system,
it must have an extension associated with the file system.
At XO$ROOT, an IOCB is maintained which is used by DPS to access the
root directory. It is up to the D$INIT and possibly D$ON, D$OFF to maintain
this IOCB open and up to date (in the cases of medium change etc.).
The other IOCB at XD$CAT is the IOCB of the device's CAT; it must only
be reserved to the device descriptor it belongs to, DPS takes care
of opening it using the root directory (UNITCAT.SYST).
The root directory beginning & size are taken from block 0 at
B0$ROOT & B0$RSZ.
4.4 The DPS 1.1 file system.
A device allowing random access (DD$RAN in D$TYPE =1) can have a file
system. The minimum information necessary to DPS to begin some search
is the root directory; how it located and the root access IOCB is open
is device dependent. Generally, the beginning and size of the root
directory are taken from block 0 (B0$ROOT and B0$RSZ), but this is done
by code inherent to the device driver (the INIT, ON and OFF entries).
If the file system is going to be accesses in a way requiring device
space allocation changes, in the root directory a file named UNITCAT.SYST
must be present and it must contain the valid device CAT. Device lockout
clusters are not processed by DPS; the "lockout" utility can create
a lockout table and/or OR it to the device CAT if so commanded.
4.4.1 Block 0 format
Block 0 has the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 4 B0$SIZE Block size (bytes)
4 4 B0$LSBL Last (highest) block number
8 4 B0$BOOT Starting block of boot code (0 means no boot code)
12 4 B0$ROOT Root directory starting block
16 4 reserved
20 4 reserved
24 4 reserved
28 4 B0$RBKP Backup root directory starting block (0 if none)
32 2 B0$VREV Version/ revision e.g. 1.1
36 12 B0$DATE Initialization date - YMMDDHHmmSS
48 16 reserved
74 4 B0$DESZ Default directory size (number of entries)
68 4 B0$CFSZ Default file creation size
72 4 B0$BLEN Bootblock length (bytes)
76 4 B0$BASE Bootblock memory load address
80 4 B0$EXEC Bootblock execution address
84 4 B0$CLSZ Cluster size (blocks per cluster)
Typically, on a bootable device DPS.SYST would be contiguously allocated
and accessed for boot via B0$BOOT, B0$BLEN, B0$BASE and B0$EXEC. The SCSI
driver which comes with DPS 1.1 compares the block size as reported by the
device and compares it to what is found at B0$SIZE; if they do not match,
data format is assumed to be non-DPS.
4.4.2 Directory format
A directory is a file containing directory entries, which lead to
some file each. The directory entries are preceeded by a header which has
the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 4 DIR$SIZ Directory size (number of entries)
4 4 reserved
8 4 reserved
12 2 DIR$RPR Read priority allowing access (not used in DPS 1.1)
14 2 DIR$WPR Write priority allowing access (not used in DPS 1.1)
16 48 reserved
64 0 DIR$ST First directory entry (number 0)
A directory entry has the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 8 DE$NAME File name
8 4 DE$SUFF Name suffix
12 6 DE$DATE Last modification time & date (BCD) - YYMMDDHHmmSS
18 2 DE$TYPE File type + access restriction flags
20 4 DE$EOF End Of File LBN (Logical Byte Number)
24 4 DE$SG0 Segment 0 starting block
28 4 DE$S0SZ Segment 0 size (number of blocks). 0 if > 2 segments
32 4 DE$SG1 Segment 1 starting block (if 2 segments or less)
36 4 DE$S1SZ Segment 1 size
40 0 DE$LEN Directory entry length
4.4.2.1 DE$NAME and DE$SUFF are compared as they are to the IO$NAME
by DIRSR$ and are written from IO$NAME to DE$NAME when creating a
new entry. An entry having its first byte 0 or with bit 7 set is
considered an empty entry. The file name processing calls map all
alphabetical characters to upper case before writing to de IOCB
standard name area.
4.4.2.2 DE$TYPE has the following format:
MSB:
Bit Name
7 FT$DELP Delete protected
6 FT$WRP Write protected
5 FT$SYS System file (not shown by DIR unless ordered to)
4 FT$USR User defined (for future use by application programmers)
Bits 0 to 3 are reserved for later versions.
LSB:
Bit Name
7 reserved
6 reserved
5 reserved
4 reserved
3 \
2 | - file type (see dpsequ.txt)
1 |
0 /
The reserved bits are intended for various access restrictions being
implemented on later versions.
4.4.2.3 DE$SG0 - DE$S1SZ. These 4 long word locations define how the file
is to be found. If the file is not scattered in more than 2 pieces,
uhen its first segment starting PBN (Physical Block Number) is found
in DE$SG0. The block just before the first block (i.e. the number from
DE$SG0 minus 1) is also always allocated to the file; in the case of the
file being fragmented in more than 2 pieces, this block is used as
the RIB (Retrieve Information Block). In the case of 1 or 2 segments,
DE$SG0 and DE$S0SZ contain the beginning and size (number of contigouous
blocks) of segment 0 and DE$SG1 and DE$S1SZ contain the same data about
segment 1. In the RIB case, the RIB block contains a sequence of long
word pairs of the same kind as DE$SG0 and DE$S0SZ, being zero terminated.
(Termination is not necessary in DPS 1.1 - file size is always known from
IO$EOF).
4.4.3 CAT format
The device CAT (Cluster Allocation Table) follows the common DPS CAT
format (used also for the MCAT - Memory CAT). The clusters available for
allocation are described by a word aligned bitmap with the most significant
bit of the first word corresponding to the cluster with the lowest address.
The bitmap is prefixed by a header of the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| |
0 1 Bit 7 used as a lock flag for CAT modification, bit 6 - change flag,
1 1 reserved
2 2 Bitmap size (number of WORDS, does not include header & terminator)
4 4 Cluster size (number of blocks per bit)
8 ----- bitmap data
The bitmap is terminated by a word containing $FFFF.
5. The PSCT module
In order to spawn a task under DPS, a program module (and other data) must
be specified. A program module (also called a PSCT module) is a contiguous
block of memory, containing (generally) program data, which corresponds
to one of the system maintained list of PSCT modules. A module, once
loaded into system memory, will be kept there as long as there is at least
one task running with this module being its spawn time PSCT, or until
there is at least one library call from any task using this module (the
latter assumes that the module supports library calls). Modules are loaded
from an I/O nexus, which should be a registered one.
5.1 PSCT file format.
A PSCT file, which can be loaded and executed as a PSCT module, has the
format described below. The same description applies to non-file mode
I/O, except that the PM$DIR will not contain a valid directory (which
can be seen from IO$DTT of the IOCB pointed to by PM$FILE).
The PSCT header is transformed into an internal PSCT module header
(not stuck to the memory block it describes; DPS maintains/allocates the
list of PCT module descriptors).
In the following paragraph, the entry function description applies to
the fields in the PSCT type file header, while the one in the square
brackets applies to the PSCT module descriptor created by DPS as a
result of the PSCT file having been successfully loaded.
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 1 PM$FLAG0 Flags - see 5.1.1 PM$FLAG0 - PM$FLAG3
1 1 PM$FLAG1
2 1 PM$FLAG2
3 1 PM$FLAG3
4 2 PM$USRS Must be 0 [Number of tasks interested in this module]
6 4 PM$BASE 0 or absolute load address if P0$ABSP in PM$FLAG0 1
[ Memory base address ]
10 4 PM$END Module size [Address after module's end address (end+1)]
14 4 PM$EXEC Offset to execution address [ Module execution address ]
18 4 PM$DSCT Number of bytes necessary to allocate as user DSCT
22 2 PM$SR Spawn time SR (processor Status Register)
24 4 PM$DIR Not used [ IOCB address of directory from where loaded ]
28 4 PM$FILE Not used [ IOCB address via which load was done ]
32 4 PM$DEV Not used [ Device descriptor address (from which loaded) ]
36 2 PM$LUN Not used [ Load device LUN ]
38 12 PM$NAME [ File name (of file from PM$FILE) ]
50 4 PM$IDSZ Initialized DSCT size (bytes)
54 4 reserved
58 0 PM$LEN -- Module data starts at the next block beginning --
5.1.1 PM$FLAG0 - PM$FLAG3 are module related flags with the following
meaning:
PM$FLAG0:
Bit Name
7 P0$RSV Not used [ Descriptor reserved ]
6 P0$ABSP Absolute PSCT address;
must be loaded at address from PM$BASE
5 P0$ABSD Absolute DSCT address - DSCT must be at the address found
in PM$DSCT, size passed (to SPAWN$) separately
4 P0$LCK Not used [ header locked for modification ]
3 P0$WIN Will use a window (not used by DPS 1.1)
2 P0$NWI Requires a new window (not used by DPS 1.1)
1 P0$WPS Supports external call offsets
0 P0$IDSC Initialized DSCT starts at PM$LEN, PSCT
at (PM$LEN+(PM$IDSZ)+$1ff)!.$FFFFFE00 (!. = & )
[ Initialized DSCT to module exists ]
PM$FLAG0:
Bit Name
7 P1$BGD Default task start is in background
6 P1$NREE Module code is non-reentrant
5 P1$LIB Module contains library functions
The remaining bits from all the flag bytes are reserved and must be 0.
5.1.2 PM$USRS - in the system module descriptor only - number of tasks
depending on this module being present in memory. Incremented each time
a task is spawned with this module as task's PSCT and each library call
to this module, decremented each return from a library call and each
associated task kill; if found 0 after decrement or, upon killing,
if the PM$FILE is registered and no other task remains checked into
the module's file (meaning no task uses it), the module is removed
from memory.
5.1.3 PM$BASE - in the file header, 0 or, if P0$ABSP=1, absolute load
address. In the module descriptor, PSCT data base address.
5.1.4 PM$END - in the file header, PSCT (only; initialized DSCT not
included) size (bytes). In the module descriptor, PSCT end address +1, i.e.
start address of the memory cluster immediately following last PSCT
memory cluster.
5.1.5 PM$EXEC - in the file header, offset from module beg. (the location
corresponding to PM$BEG) to execution entry point (location given control
to by SPAWN$). In the module descriptor, absolute execution addess.
5.1.6 PM$DSCT - number of bytes to allocate (by SPAWN$) if no alternate
value passed as task's user DSCT. Includes the size of the initialized DSCT
(from PM$IDSC).
5.1.7 PM$SR - value to set the processor SR to on spawn time. Applicable
to 68K processor types; typically 0. If $2000, the task will be initially
gien control to in supervisor mode (this must be the case if no user
DSCT is not allocated).
5.1.8 PM$DIR - IOCB of directory where file was loaded from. Will be
preserved registered & open as long as the module exists in memory.
5.1.9 PM$FILE - IOCB of file which was loaded. Preserved registered and
open as long as the module exists in memory.
5.1.10 PM$DEV - DD of of file where the file was loaded from. Maintained
by DPS 1.1, though it can be found also from IO$DEV in PM$FILE (PM$DIR
may be invalid if PM$FILE is in non-file mode).
5.1.11 PM$NAME - Module name. If IOCB from PM$FILE is in file mode,
this name duplicates the file name. If in non-file mode, header is
not altered.
5.1.12 PM$IDSC - Initialized DSCT size. If P0$IDSC=1, this specifies
the number of bytes to read from the file starting from $200 at the
end of the allocated user DSCT before giving control to the task. The
SPAWN$ does this automatically; the spawned task stack pointer is placed
just below this section.
5.2. Internal PSCT module defined offsets
Certain offsets within the PSCT module are defined and used by DPS.
5.2.1 Library PSCT modules.
A PSCT module with the P1$LIB from PM$FLAG1=1 must support library
calls. In order to do that, the code at the address from the module
descriptor's PM$BASE must start with a list of variables (see DPS
variables). At least 1, possibly linking valid variable entry must
be there. Non-linking (function specifying) variables contain at
VA$ENT the offset to the function code.
5.2.2 Standard external call entry points.
If P0$WPS in PM$FLAG0 is 1, the following locations from the PSCT module
are used by DPS for the following purposes:
Offset (bytes, decimal, from module beg. (found in PM$BASE)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
64 4 WM$CKSG Offset to check for signal routine
68 4 WM$GSIG Offset to get signal routine
72 4 WM$ACKS Offset to acknowledge signal routine
76 4 WM$REJS Offset to reject signal routine
80 4 WM$CLCK Reserved
84 4 WM$SIG Offset to put signal entry
88 8 WM$TOUT Time for signalling task to wait for response
96 8 WM$TMAX Upper limit for WM$TOUT of other tasks when signalling them
104 60 reserved
164 -------- Code can start here ...
All offsets above are from the module beginning to the address to call
as a subroutine when using the apropriate offset.
Refer to SIGN$, SIGNTO$, GETSIG$, CHKSIG$, ACKSIG$ and REJSIG$ for
details.
The time limits are in seconds, fixed point between the two long
words, unsigned.
5.2.3 Object type descriptor files
An object descriptor file is loaded when an object of a type corresponding
to the file name (the file name consists of the first 8 alphanumeric
characters of the object type name) is accessed via the DO$ or QACT$ calls
and the object is unknown at this point. An object file may contain
more than one type descriptor; in this case, however, only one of the
contained types will be found after the object name.
An object descriptor has the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 1 O$FLAG0 Flags
1 1 O$FLAG1 reserved
2 1 O$FLAG2 reserved
3 1 O$FLAG3 reserved
4 4 O$NEXT Offset to next from the list of object descriptors
8 4 O$TYPE Object type
24 4 O$CLASS \ [ Offset to parent object descriptor ]
28 4 O$LREF | Parent type [ Last moment of reference ]
32 4 O$RNUM | [ number of references ]
36 4 O$BIRTH / [ Moment of installation ]
40 --- O$ACT Action location table begins here
Function descriptions in square brackets apply to the object descriptor
as it is already installed in the system memory; lines without square
brackets have the same function in the file and in memory.
All offsets are from the descriptor beginning (O$FLAG0).
5.2.3.1 O$FLAG0 - O$FLAG3 are descriptor related flags. Only part of
O$FLAG0 is used by DPS 1.1; the remaining are reserved and must be 0.
The following are defined:
Bit Name
7 O0$RSV Reserved flag (must be 0 in file)
6 O0$VAL Valid flag (must be 1 in file)
5 O0$NOA No private actions (search directly parent)
4 O0$LOCK Descriptor locked (for modification)
5.2.3.2 O$NEXT - offset to next descriptor. In the system memory, this
is how the next descriptor to check for type match. A daemon task, which
periodically checks how object descriptors are utilized, may exchange
these links in favour of those more frequently used (by placing them
closer to the list beginning). The last descriptor on the list points
to the first one ("something"), and the latter is never moved. (Such a
daemon task is not automatically started with DPS 1.1 and its functionality
is not subject of this specification).
In the object descriptor file, if there is a single object descriptor
in this file, this field must be 0. If there are more than one descriptors
in the same file, the first (in the file) must point to some other and
so on until all descriptors contained in the file are linked, the
terminating one being 0. Upon installation, the whole chain (possibly a
single descriptor) is inserted into the linked list as it is.
last position.
5.2.3.3 O$TYPE - object type. This is the value which is compared to the
object's XO$TYPE when searching the object descriptor. Though compare is
done by just comparing 4 long words, and any kind of data is allowed,
the descriptors written so far consist only of alphanumeric characters
(lower case only), hyphen (-, $2D) and underscore (_, $5F). If the type
name is shorter than 16 characters, it is filled with spaces ($20).
5.2.3.4 O$CLASS to O$BIRTH - in the file, this 16 bytes contain
the parent object type in the same format as at O$TYPE. It is used
by DPS upon object installation to locate the object descriptor of
the parent object; if not found, DPS attempts to install it recorsively.
Once the parent descriptor is found, O$TYPE is overwritten by the
offset to the parent object descriptor; the moment of installation
(system slice, just from SY$TIME) is written to O$BIRTH.
O$LREF is updated with the current moment every time the descriptor is
accessed via the DO$ and QACT$ calls, O$RNUM is incremented every
DO$ or QACT$ reference, thus, along with O$BIRTH maintaining reference
frequency information.
5.2.3.5 O$ACT - from here starts the action location table. It consists
of variables in the common DPS variable format; the field may contain
empty (zero-filled) entries for subsequent expansions, although variable
segments can also be inserted using the linking capability of the
variable field format.
Here is a description of the variable format (including this particular
purpose specific details):
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 8 VA$NAME Variable name, 8 alphanumeric lower case characters,
8 4 VA$ENT Offset to action, calculated from VA$NAME,
12 1 VA$FLAG Common variable flag bits,
13 1 reserved
14 1 VA$USFL User defined flags (not used by our code),
15 1 VA$FLAG3 Action code access related flags.
16 ----- VA$LEN
The following bits from VA$FLAG3 are used as action routine search
modifiers:
Bit Name
1 AC$DIR Direct addressing - VA$ENT contains code absolute address,
2 AC$OFS Relative - VA$ENT contains offset to code from VA$NAME,
3 AC$NAME Named library function. VA$ENT contains offset from
VA$ENT to a text string, specifying the file & function
name, which are passed to PCALL$.
6. System calls
There are 2 general types of system calls: those which execute
at user level (wcall, acall) and during whiches execution the scheduler
can interrupt the task in order to give control to another, and those
which execute at supervisor level (scall), during which period a task
will not be forced out (but still may exit for arbitration at its own
will - a device driver, for example, will exit (executing a xtask$)
while waiting for some event or for any reason).
The following 68K processor traps are associated with the calls:
trap #14 - SCALL functions (run at supervisor level),
trapCC - WCALL functions (run at user level),
trap #11 - ACALL functions (run at user level).
In addition, traps #10 and 12 are used for return from library calls
and wcall/acall functions, respectively.
The WCALL and ACALL are identic (execute the same code for the same
function index); the difference between them is that the WCALL trap
handler replaces the caller's A3 with the pointer to the task window
descriptor (taken from the task descriptor), while the ACALL handler leaves
A3 unchanged. It is up to the calling program to make sure that it
passes an apropriate window descriptor address to those WCALL/ACALL
functions, which require a valid descriptor.
In order to execute a system call, the program must execute a TRAP
instruction, followed by a word (16 bits) designating the function
code (in the case of the TRAPCC instruction (WCALL), this is part of the
opcode. The trap handler increments the return PC to skip the call index,
whenever necessary.
System calls, which return status (virtually all), return it in D0
in conjunction with the carry bit in the processor CCR (Condition
Code Register). If the carry upon return is clear, no error has occured
and D0 is impredictable unless otherwise stated in the decription of
this particular call; if the carry bit is set, D0 contains a valid
return status.
6.1. System calls summary
6.1.2. Supervisor level system calls (SCALL functions)
6.1.2.1. Memory related functions
allcm$: allocate memory (D2=requested length)
allcma$: allocate mem. without swapping declared blocks
allfx$: allocate at fixed address
allfxt$: tolerant allocate at fixed address
allmr$: recorded memory allocate
all3pm$: reg.allocate memory to third party
dallm$: deallocate memory
dallmt$: tolerant memory deallocate
keepmb$: declare memory block for swapping (a1=base,d2=size, cl.
rmbl$: retrieve mem. block (a2:d1)
fchmm$: release a mem. block (unknown which)
delmb$: delete memory block (a2)
lckmb$: lock mem. block (a2)
unlmb$: unlock mem. block (but leave entry alive)
clln$: get memory cluster size (number of bytes)
6.1.2.2. I/O related functions
path$: process path (a5)
fpath$: find path (no destination area written to)
fetch$: gain access to an I/O nexus in registered mode
xopen$: fetch but with a4-> 12 char. name taken unchanged
leave$: leave registered nexus (close if last task to leave it)
leav1$: leave file, but keep directory chain registered
rd$: random file read (any size from any offset)
wr$: random file write (any size at any offset)
sfpos$: set file pos. (d3,d1,a1 - a3->iocb)
sfposr$: set file pos. (fill the whole buffer)
gfpos$: get current file position
get1$: get 1 character from file (a3=iocb)
put1$: put 1 character to file
outch$: output a character to stdo
inch$: input a character from stdi
resrv$: reserve an iocb to a device
open$: open an I/O nexus
pdata$: output data string
crlf$: cr,lf to stdo
spac$: space to stdo
ckbrk$: check for break/hold
ckbkn$: check for break (no hold)
out8h$: output d0 as 8 hex to stdo
out8hs$: output d0 as hex,space
out4h$: output d0.w as 4 hex to stdo
out4hs$: same as out4h$, but followed by a space
OUT2H$: output d0.b as 2 hex to stdo
OUT2HS$: same as out2h$, but followed by a space
OUT1H$: output d0 bits 0-3 as a hex digit to stdo
OUT1HS$: same as out1h$, but followed by a space
pfnam$: process file name
afnam$: analyze file name
attch$: attach a device descriptor
echo$: echo a character
get$: get data from file into io$bbs to io$bb
put$: put data to file io$bbs to io$bbi
lock$: lock IOCB
close$: close an I/O nexus
unlck$: unlock IOCB
kill$: kill a task
chk$: check if data from I/O nexus available for input
look$: check if data from stdi available for input
fload$: load a program module from file
put1h$: put d0 bits 0-3 as hex to iocb (a3)
put2h$: put d0.b as hex to iocb (a3)
put4h$: put d0.w as hex to iocb (a3)
put8h$: put d0.l as hex to iocb (a3)
put1hs$: put d0 bits 0-3 as hex,followed by a space, to iocb (a3)
put2hs$: put d0.b as hex,followed by a space, to iocb (a3)
put4hs$: put d0.w as hex,followed by a space, to iocb (a3)
put8hs$: put d0.l as hex,followed by a space, to iocb (a3)
pcrlf$: output cr,lf (a3 -> iocb)
pspac$: output a space ( a3 -> iocb)
pdec1$: put d0 as decimal (no leading nulls)
pdec2$: put d0 as decimal,no trailing nulls
odec1$: putdc1 via std out
odec2$: putdc2 via std out
getln$: get line from I/O nexus (dialogue mode)
keyin$: get line from stdi
nderr$: display error message
ddld$: load device driver
reles$: release an i/o nexus
put1l$: lock and put 1/unlock
get1l$: lock and get 1/unlock
unlckr$: unlock once and release if unlocked
unlckp$: unlock once and release
locku$: lock once only if unlocked
read$: read from file (d3=first lbn to read)
ckin$: check in reg.list for nexus access
ckrg$: check if nexus with a3->iocb registered
cka3r$: check if a3 is a valid registration addr.
write$: write to reg. nexus (d3=first lbn to wr.)
ckdir$: check directory iocb
unlk$: unlock (as many times as it takes) iocb
unlr$: unlock (use unlk$) and release device
ptid$: output task id (module name and no.) to stdo
ptnumb$: output task no. to stdo
pmnam$: output task's module name to stdo
rdnxt$: read from next lbn (reg. nexus)
wrnxt$: append to reg. nexus
fdev$: find device descriptor
prep$: prepare name with * or ? into std area
fname$: process file name including * and ?
getb$: get (like get,but fills until io$bbe)
putb$: put (like put,but writes the whole buffer
pdec$: put decimal (right tabulated)
phexr$: put hex (right tabulated)
phex$: put hex (no leading zeroes)
odec$: output decimal (right tabulated)
ohexr$: output hex (right tabulated)
ohex$: output hex (no leading zeroes)
swapr$: recorded vector swap
dirsr$: directory search
ch3p$: check third party task in for I/O nexus access
ptpth$: output path string to I/O nexus (a3)
pass$: pass locked iocb (a3) to task descriptor (a0)
srchd$: search device name (d1,z bit)
ckmea5$: check current task for access to file (a5)
outfn$: output file name (a1) (no spaces padded)
outfnt$: output file name (a1) with pad spaces
ptfnam$: put file name
stdi$: get stdi (a3)
stdo$: get stdo (a3)
pmsg$: output message (a0 -> msg with length hdr.w)
omsg$: pmsg$ to stdo
setsti$: set stdi to A3
setsto$: set stdo to A3
ptid1$: output task id (id in d1)
derr$: display third party's error msg
bllen$: get device block length (a3 -> open iocb)
serr$: set std error output iocb
gerr$: get std error output iocb
otid1$: output to IOCB (a3) task id (in d1)
otnumb$: output task no. to (a3)
omnam$: output task's module name to (a3)
set$: set device's parameter
pget$: get device's parameter
putdt$: pdata,but a3 -> iocb to put to
findd$: find device descriptor address (d1=name,a4->dd)
setcd$: set task's current directory to (a5)
chkd$: check iocb's **device** if accessible
ecrlf$: CR,LF to stderr
eutch$: outch to stderr
putlin$: output line (like pdata,but cr terminated)
outlin$: line to stdo
fsize$: get file size (in d2.l;a3 ->iocb)
chtkin$: check task in (d1=id) for access to I/O nexus
6.1.2.3. Execution related functions
spawnn$: spawn without starting
start$: start a task (already spawned via spawnn)
exit$: exit task (forever)
fcall$: call program library file function (search directory in A5)
pcall$: call function (start from task's spawn dir.)
xload$: load a PSCT module from file (a3) (iocb open)
load$: load PSCT module
xtask$: exit task for re-scheduling
xtwt$: exit and wait specified time
xtws$: exit and wait a number of slices
xtwm$: exit and wait for the moment in d1
spawn$: spawn a task (start immediately)
killp$: kill particular (0 does not kill current,but 0)
eword$: set task error status word (d0=status)
curd$: get current directory (a5)
win$: get task's window descriptor (a3)
dsct$: get task's user dsct (a1 - beg.,a2 - end+1)
comm$: get common dsct address in a1
pty$: get priority (d1 - base,d2 - current)
flagm$: get flags (d1.l) and module descriptor (a2)
phirc$: put a task history record
rdy$: set t0$rdy (see sign$, signto$ wcalls)
scomm$: set common dsct
fpuon$: enable fpu save/restore upon task switch
fpuoff$: disable fpu save/restore
gettd$: return pointer to task descriptor in A0
exec$: spawn and execute (wait for termination)
bgnd$: switch task to background mode
updlk$: update vector link data (at (vbr+1024+4),v#*8)
spawnd$: get spawn directory in a5
gtime$: get date & time
tspmm$: get task (d2) spawn moment (in d1)
gettd2$: point (a0) to td with id in d2.w
myid$: get own id & spawn moment in d1:d2
gspmd2$: get spawn moment (in d1) of task with id=d2
geword$: get own error status word (in d1)
setto$: set timeout value (d1:d2; ret. d2=slices)
getto$: get timeout value (d1:d2)
seword$: set error stat. word to d1.w
stflag$: set flag # d2.w bit in own task descriptor
6.1.2.4. Miscellaneous functions
getvar$: search for variable (a0,a1)
globvar$: search for global variable
setvar$: set variable to value (a2)
setgvar$: set global variable to a value
vsgin$: initialize segment for variable storage
strip$: skip string leading spaces (a0)
hexd$: convert ascii to hex (default ascii hex.)
decd$: convert ascii to hex (default ascii dec.
bind$: convert ascii to hex (default ascii bin.)
asbcd$: ascii string to bcd
hxdec$: convert hex to bcd
dechx$: bcd into hex
swap$: swap a vector
vrest$: restore vector
usqv$: get next free user IRQ vector
ckown$: check if owning memory range
nxnsp$: find next non-space character
pld8h$: put d0 as 8 hex at (a2)+
pld4h$: put d0 as 4 hex at (a2)+
pld2h$: put d0 as 2 hex at (a2)+
pld0h$: put d0 as 1 hex at (a2)+
setwd$: set window descriptor to a5
pltid1$: place task id (name and no.) at (a2)+
pldec1$: place d0 as decimal at (a2)+ (no ld.nulls)
pldc1s$: place d0.l as signed decimal text @ (a2)+
ckiol$: check if iocb locked & owned
plhex$: place d0 at (a2)+ as hex (no leading 0s)
lck02$: lock bit #d2 at (a0), d3:d4=tout
6.1.3. User level system calls (WCALL and ACALL functions)
neww$: create a new window
scurw$: set current task window descr.
clrhom$: clear screen,go home
home$: text cursor home
stlim$: set text window limits
sglim$: set graphic window limits
glimqtx$: set graphic limits equal to text limits
settxta$: set window attributes
setgrp$: set graphic page to pattern (d6)
remcurs$: remove cursor
remcurl$: lock & remove cursor
dispd0$: display d0.b (8 bit)
crcmd$: cursor at left display column
currght$: cursor right
cursup$: cursor up
curleft$: cursor left
lf$: cursor down (line feed)
delch$: delete character under cursor
delchat$: delete character with attributes
insspcc$: insert space
insspca$: insert space with attributes
insline$: insert line
delline$: delete line
clreol$: clear to eol
clreola$: clear to eol with attributes
clreop$: clear to eop
clreopa$: to eop with attributes
settop$: set top display line (d1)
setbot$: set bottom dipslay line
setldc$: left display column
setrdc$: right display column
moveol$: move cursor to eol
moveop$: move to eop
ldcurs$: load cursor (d1.w,d2.w)
setsca$: set screen attributes (d0)
setrga$: set region attributes (d0)
absrg$: check if region valid/make absolute
schatt$: set character attributes
hidetxt$: hide text screen
hidetrg$: hide text region
showtxt$: show text screen
showtrg$: show text region
cgscgpg$: change window graphic page
cgrggpg$: change region graphic page
ssgpage$: set screen graphic page (d5)
srgpage$: set region graphic page
drline$: general type line draw (xs,ys;xe,ye)
lined0$: draw line with type in d0
wrgd$: write region data
wrg$: write region (text and attr.)
wrgdt$: write region text data
wrgt$: write region text
wrgad$: write region attribute data
wrga$: write region attributes
wsave$: save window
wrest$: restore window
wleave$: leave widow (remove and forget)
xwseq$: execute object sequence (mouse curs. removed) (show all until 0)
knba5$: install a knob in window
remknb$: remove a knob
remalk$: remove all knobs from window
clrtrgn$: clear text region
strgr$: set region graphics to layer's colour
streggr$: set region graphics to colour
wdmode$: set window drawing mode
xwsqa4$: execute (a4 -> frt)
stlimr$: set setxt window limits relative to current
rmous$: remove mouse cursor
rmouslk$: remove mouse cursor;lock it off
unlm$: unlock mouse cursor
xwseqn$: execute sequence (mouse cursor left alone)
swlimg$: set text limits after graphic coords
wglims$: get window graphic limits
wld1d2$: write text line at d1,d2 (text coords)
wrlims$: get window reserved limits
putobj$: put an object into window's buffer
sglimr$: set graphic limits equal to reserved
setgwp$: set grphic page to layer's colour
drkna5$: draw a knob (a5 -> func. desc.)
wrline$: write text line at cursor pos.
sign$: send signal to task
rdcurs$: read cursor
cmod$: get current drawing mode (d6)
curgp$: set current graphic page
dialr$: display decimally right just. no l.n.
wrlgrc$: write text line at graphic coords
wld1d2$: write text line at d1,d2 (text coords)
srgtcol$: set region text colour
chsize$: get character size
curgpg$: get current drawing page
getcol$: get colour (d6 layer into d6 colour) curr. frt
preal$: convert .x into ascii string
asreal$: convert ascii string into .x
dialrf$: fp right aligned dial (numeric display)
signto$: signal (with time-out)
scac$: set character attributes to current ones
frame$: draw frame (rectangular)
getsc$: get scale factor
setsc$: set scale factor
getang$: get drawing angle
setang$: set drawing angle
getsig$: get signal (no acknowledge)
acksig$: acknowledge signal
rejsig$: reject signal
chksig$: check if signal received
wdn$, get window descriptor number
skipobj$: skip 1 object (increment a5 by object's size)
alignx$: align x (in a pair of coords,d1-d4) to a character multiple
aligny$: align y (in a pair of coords,d1-d4)
remall$: remove all objects from window buffer
leavnt$: leave window (without putting to top)
sclims$: set clip limits
gettlm$: get text limits (relative,text coords)
topme$: put window to top
topmein$: put window to top,connect to input
tcgc$: rel.text (to leftdc,) pair (d1,d2) into gr. pair (to w$wdlx)
do$: do action over obj. (a5); a0->action name
erastx$: erase current text window (set to space with default attributes)
qact$: same as do$,but action name in d1 & d2
inctyp$: incorporate object type
ldobj$: load object(s) file ***(a2 -> iocb)***
ptyp$: point to type descriptor by a4 (a5->obj.)
vsrch$: search for var (a1-> vars, a0->name)
qvsrch$: quick v-search (a1 -> vars,d1&d2=name)
rdobj$: read object (and load) from file
ssize$: get screen size (in d3&d4)
nwlims$:set new window limits (d1-d4)
remwin$: remove window from screen (uncover others),keep existing
unfrd2$: uncover & unfreeze task #d2's current window
srgatl$: set region attributes after 2 layers (d6 ls-ms)
twolatt$: two layers into attributes (d6 both)
pix$: get bits per pixel (d1)
sig73$: signal to wd (a3),d7=signal index
lvnol$: leave window (no autolock;must be locked)
unfra$: unfreeze all uncovered windows
elia2$: choose one window to connect to input
unfranl$: unfreeze all (no lock/unlock)
xwsnf$: show objects (xwseq,but no freeze)
wstat$: get window status
rmsykn$: remove system knobs of current task
rmsyd1$: remove system knobs of task with id in d1
frzall$: freeze all windows
freez$: freeze window (a3)
unfr$: unfreeze window (a3)
qa54$: do action d1:d2 at (a5), using obj. descr. (a4)
qa54p$: do action d1:d2 at (a5), using parent of (a4)
6.2. Detailed system call descriptions
6.2.1 Supervisor level system calls (SCALL functions)
ALLCM$: allocate a contiguous block of system memory.
Input: D2 - number of bytes to allocate.
Output:
If no error (carry clear):
A1 - allocated block base address,
D1 - number of bytes actually allocated (will be a multiple
of memory clusters, while requested via D2 may be not.
If and error has occured and the error is E$INSFM (insufficient memory):
A1 - largest available at the moment contiguous block beg. address,
D1 - block's size (bytes).
If the requested amount of memory is not available at the first attempt
and memory blocks are enabled (BLOCKS statement found in setup.syst on
boot time), memory blocks (if some) are sequentially swapped to disk
until memory can be allocated or until no more blocks can be found, in
which case an insufficient memory error occurs.
ALLCMA$: allocate a contiguous block of memory. Same as ALLCM$, but
does not attempt to swap memory blocks to disk.
DALLM$: deallocate a contiguous block of memory.
Input: D2 - number of bytes to deallocate (must be a multiple of the
cluster size.
Output: If no error, none. If the error is E$CLSZ (cluster size error),
D1 returns the system memory cluster size.
ALLFX$: allocate memory at a fixed address.
Input: D2 - number of bytes to allocate,
A1 - address to allocate starting with (must be a multiple of
the cluster size).
Output:
If no error (carry clear):
A1 - allocated block base address (i.e. unchanged),
D1 - number of bytes actually allocated (will be a multiple
of memory clusters, while requested via D2 may be not.
If the requested number of bytes was not available at this position,
E$INSFM (insufficient memory) is returned, and in D1 is returned the
number of available bytes starting from the requested address.
KEEPMB$: declare memory block for swapping.
Input: a1 - base address of block (must be cluster aligned),
d2 - block size in bytes, cluster aligned.
Output: a2 - block ID for future references,
d1 - creation moment (time slice no.).
After this call, the declared memory block may ce taken over by another
task; in order to access this memory again, the task must first retrieve
the block, using the block ID. DPS keeps a list of declared blocks
which is checked every time memory requested for allocation was not
available; blocks from this list are written to disk and deallocated
until a sufficient amount of memory is available for allocation.
RMBL$: retrieve mem. block.
Input: a2 - block ID,
d1 - declaration moment (both as returned from the keepmb$).
Output: a1 - block memory address,
d1 - block size (bytes). If d1=0, the DPS could not allocate
enough memory in order to retrieve it from disk; in this case (not an
error), a1 returns the ID of the file, where the memory block is
saved. It is up to the calling task to process this case.
If the block was never removed from memory, DPS just returns its
old address and size; if it was removed, memory of the same size
is allocated (most likely at a different address than originally),
then the data is read from the file, after which the file is deleted.
DELMB$: delete memory block.
Input: a2 - block ID.
Output: none.
The resources, occupied by the block (memory and/or file) are released
(memory is deallocated, the file is deleted) and the entry is removed
from the list of blocks.
LCKMB$: lock memory block.
Input: a2 - block ID.
Output: none.
After this call, the memory block indicated by a2 will no longer be
available for state modification (removed/retrieved etc.). Generally
used internally by DPS only.
UNLMB$: unlock memory block and leave entry alive.
Input: a2 - block ID.
Output: none.
The memory block entry can be accessed by other tasks after this (i.e.
it can be swapped etc.).
CLLN$: get memory clister size.
Input: none.
Output: d1.l - clister size (number of bytes).
SPAWN$: spawn a task.
Input: A2 - PSCT module to spawn descriptor address,
A3 - a message packet of the following format:
Offset (bytes, decimal)
|
| Length (bytes)
| |
| | Name Function
| | | |
| | | |
0 4 SP$DSCT User DSCT size (bytes) to allocate for the task. If 0,
the value from PM$DSCT of the module descriptor (A2) is taken.
4 4 SP$INP New task's STDI IOCB. If 0, parent's is assigned; if 1
(valid IOCB addresses are even), no STDI IOCB is assigned
(left as 1 in task descriptor).
8 4 SP$OUT New task's STDO IOCB. If 0, parent's is assigned; if 1
(valid IOCB addresses are even), no STDO IOCB is assigned
(left as 1 in task descriptor).
12 4 SP$WIN Window descriptor address. If 0, the parent's WD is taken.
16 4 SP$PRCS Common DSCT address. If 0, taken the parent's one.
20 2 SP$PRTY Basic Priority. if 0, taken the parent's one.
22 1 SP$ERRLV Script abort error level.
33 1 SP$ERRMS Script abort error mask.
24 4 SP$EIOC New task's STDERR IOCB. If 0, set same as STDO.
28 1 SP$FLAG0 Flags ored to flag 0 (TD$FLAG0) of new task descriptor
29 1 SP$FLAG1 Flags ored to flag 1 (TD$FLAG1) of new task descriptor
30 1 SP$RFL0 Flags anded to flag 0 (TD$FLAG0) of new task descriptor
31 1 SP$RFL1 Flags anded to flag 1 (TD$FLAG1) of new task descriptor
32 4 reserved
36 SP$LEN Length of spawn message packet.
LOAD$: load a PSCT module into memory.
TO LOAD A FILE AS A PSCT MODULE, USE XLOAD$, which calls internally the
LOAD$.
Input: A0 - Module header address (see 5.1 PSCT file format),
A1 - load routine address. XLOAD$ passes its own I/O nexus based
load routine address.
Output: If no error (carry clear), then
A1 - module memory base address,
A2 - module descriptor address,
D1 - number of bytes allocated for the module (cluster aligned
module size),
D2 - module actual length (requested for allocation).
If an error has occured (carry set), then D0 returns the status,
and the contents of D1,D2,A1 and A2 are impredictable.
ALLFXT$: tolerant fixed address memory allocate. Same as ALLFX$,
(see ALLFX$), except that A1 needs not be cluster aligned. The
custer, into which A1 points, will be the first allocated.
Input: A1 - allocated block start address,
D2 - number of bytes to allocate.
Output: A1 - unchanged,
A2 - lowest allocated address (always <= A1),
D1 - actual number of bytes allocated (cluster aligned).
If an error has occured, carry is returned set and D0 contains the error
index.
XTASK$: temporary task exit. The scheduler chooses to which task
to switch; next time the calling task is given control to, the function
returns. No registers are changed.
This function is extensively used by device drivers and other software,
which would wait for an event; it saves system time, giving immediately
control to other tasks when having nothing to do instead of staying
in a loop and waiting to be forced out at the end of the time slice.
XTWT$: suspend task execution for a specified period of time.
The scheduler switches to another task; after the specified time has
elapsed and the scheduler assignes a time slice to the calling task,
the function returns.
Input: D0:D1 - the time to wait in seconds (unsigned), fixed point
between D0 and D1, D0 being at the left of the point.
Upon return, D0 and D1 are destroyed.
XTWS$: suspend task execution for a specified number of time slices.
After the number of slices has expired and the scheduler gives control to
the calling task, the function returns.
Input: D1 - number of slices not to be given control to.
Upon return, D1 is destoryed.
XTWM$: suspend task execution until a specified moment. After the
moment arrives and the scheduler gives control to the calling task, the
function returns.
Input: D1 - moment to wait for (in internal system slices).
No registers are changed.
FETCH$: fetch an I/O nexus for registered access.
Input: A0 -> nexus (file) name,
A5 - directory file ID,
D4.W - desired IO$DTT (both bytes) (see 4.2.3 IO$DTT),
D5 - desired IO$TYPE if creating a new file,
D3 - initial file size if creating a new one; see OPEN$.
Output: A2 - I/O nexus (file) ID. Strictly, this is the
address of the IOCB used to access the I/O nexus. Notice it is returned
in A2 while it must be passed in most cases in A3 to call the I/O
functions.
A0 - incremented after the last successfully processed
character (after the terminator, if success).
The function does the following: after checking the directory IOCB for
validity (it must be a registered IOCB with a directory type at IO$TYPE),
the passed name is processed into an intermediate IOCB, along with
directory, device and LUN. Then, the list of nexi is checked if this
particular one is already open (present on the list); if not,
a new entry is created with the backup IOCB copied to it; subsequently,
the OPEN$ is called with the new IOCB from the list; if the OPEN$
succeeds, a flag bit corresponding to the calling task number is set,
thus indicating this task has checked in for access to this nexus.
If an IOCB for that particular nexus is found on the list, it is
locked by the calling task, after which the passed via D4 IO$DTT
bits (except the internal use ones) are written to the IOCB's IO$DTT,
and finally the calling task is checked in.
If the non-file mode has been requested by having DT$NONF set in
D4 (bit 15), the passed directory in A5 is not checked wether it
is a directory.
In all cases, the PATH$ or FPATH$ prepare the A5 to be passed to the FETCH$.
FPATH$: find the path to an I/O nexus (file).
Input: A0 -> text describing the path. The full format is:
#dev\lun:dir1\dir2\dir3\file.some
The device name may be up to 4 characters, the LUN must be a valid
hexadecimal number (0..$ffff; DPS 1.1 processes only 0..$f, though).
A5 - directory file ID to start the search from.
Output: A5 - searched directory ID,
A2 - file name (if one found after all the directories),
D2 and D3 - same as from PFNAM$ after having the file name
processed; additionally, the following bits of D3 are affected:
bit 21 - set if path search was done (0 if only a file name),
bit 20 - set if path search attempted (\ character was found as delimiter),
bit 19 - set if no file name (last character was a "\"),
bit 18 - set if A5 returning a non-file mode nexus ID instead of
a directory ID. This will happen, if a device and LUN name
were specified only.
A0 - incremented after the last successfully processed character.
PATH$: find the path to an I/O nexus (file); process the file name
into a standard name area.
Input: A0 -> text describing the path. The full format is:
#dev\lun:dir1\dir2\dir3\file.some
The device name may be up to 4 characters, the LUN must be a valid
hexadecimal number (0..$ffff; DPS 1.1 processes only 0..$f, though).
A5 - directory file ID to start the search from,
A1 -> standard name area to write the processed name into.
Output: A5 - searched directory ID,
A2 - file name (if one found after all the directories),
D2 and D3 - same as from PFNAM$ after having the file name
processed; additionally, the following bits of D3 are affected:
bit 21 - set if path search was done (0 if only a file name),
bit 20 - set if path search attempted (\ character was found as delimiter),
bit 19 - set if no file name (last character was a "\"),
bit 18 - set if A5 returning a non-file mode nexus ID instead of
a directory ID. This will happen, if a device and LUN name
were specified only.
A0 - incremented after the last successfully processed character.
The standard name area is updated with the found device name, LUN and file
name.
[Contact us]
[Home]