1. Introduction DPS is the multi-task OS, which comes with our computer products. It supports multiple windows with various graphic functions, has an object access system, as well as external type learning conventions, and feels comfortable even on small systems (the Nukeman 226 has 1 Mbyte of RAM). The DPS file system allows 32-bit file sizes, can work with non-file mode devices, and can transparently (though somewhat slower) access Microsoft formatted disks. 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 avaiable, i.e. r/w accessible for multiple tasks, is also possible - this is how, for example, directories are accessed). Because its initial application was for measurement and control purposes, DPS has well defined hardware access standards and offers many readily available objects (e.g. "device", "device_parameter", "nukebus_module" etc), thus minimizing the programming overhead for incorporating new hardware into the system. Task switch is done using rotating priority with increment every time a task loses the arbitration; this guarantees it will eventually win. Once a task wins the arbitration, its priority is preset back to its basic level (as spawned or set by other means in the task descriptor). The time slice can be set as low as 2 milliseconds; beyond that, all tasks waiting for some event can ask the scheduler to give control to another task immediately. System memory is dynamically allocated/deallocated (using worst-fit strategy) in clusters (cluster size in DPS 1.1 is 1 kbyte). Applications are given a mechanism to declare allocated memory blocks for swapping while not accessing them (this works even on systems without an MMU). Disk space is also allocated using worst fit strategy, which yields (of all known) the least medium fragmentation (a lot less than with the most widely spread first fit strategy). Medium space is described by a bit-per-cluster, the file layout being stored separately from the CAT (Cluster Allocation Table), which allows 32 times shorter cluster under the same circumstances as a system using 32 bit FAT would allow. The script language allows access to DPS external objects and recursive calls (a script can call itself), and also allows retry and resume from the point/level of abort. The script processor is available as an external object to any application which wants to use it. All programs written so far are reentrant/position independent, which allows running multiple copies of the same program code as multiple tasks, which is automatically done by DPS. System programmers who will need access to exception vectors are given the convenience not to think how many times and by which task a vector has been swapped - they can just use the registered swap and restore function, which takes care of restoring the right value to the vector at the right moment, optionally automatically upon killing a task which has swapped vector(s). Device driver programmers are given means to find their way to the device descriptor from the related interrupt handler (i.e. device drivers are also completely position independent). 2. Operation Upon system start, DPS goes through the following steps (of user interest): - processes the setup.syst file, which is used to set certain system parameters (e.g. the time slice) and to load as densely as possible selected device drivers, - spawns the "win.com", which is the task dealing with mouse actions, manual window limits change/signalling, keyboard polling, input (from mouse and keyboard) distribution to the input owning task etc., - spawns the "shell.com", which, in turn, executes the "turnon.bat" command script, upon whiches termination waits for user commands. DPS applications run as independent tasks. The shell program is the program which starts other tasks as applications (either from a command line input or as a result of a menu selection); 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 level data memory (also called 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 nexuses (an I/O nexus is a more general form of a file) 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 from 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. A task, after having been spawned, can choose to allocate for itself another common DSCT and spawn other tasks (this is how the nuvi program operates, for example); the new group of tasks, connected to the same common DSCT - which (except for its header) is used as the tasks wish - is sometimes refered to as a "process". 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 there), 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 are no more tasks connected to this module, it is discarded. (Many such functions are readily avaialble in \syslib\ - like "menu", "dpsfsrch" - file search from a window etc.) One way for the tasks to communicate with each other is by sending signals/messages. (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 more 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 our "qstd" code, for example, it will place the signal on a queue in the receving task's user DSCT and return; the receiving task will eventually find it there and process it. When a task exits (either alone or being killed by another), 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 owns open windows), - all I/O nexuses to which the task was registered are "left" - that is, the task is unregistered and, if the nexus (file) has no more tasks registered to it, it is closed, - 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. 3. DPS External Objects The DPS external 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. External 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. 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. On boot time, DPS 1.1 always installs the following 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. 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 garbage collection procedures etc. The "show" action is reserved for DPS usage - it is done with objects in a sequence being processed by the "xwseq$" function, which allows displaying of a sequence of both DPS internal and external (the latter were discussed above) objects.