\input pobpre.tex
\trace'344
\hsize 7.5truein
\vsize 10truein
\baselineskip 10pt
\vskip 0.2truein
\ctrline {\bf VSDRV VERSION 2 -- VARIABLE SEND-DATA DRIVER FOR RSX-11M}
\par\askip\noindent
{\:f (This work was supported by the Light Water Breeder Reactor Proof-of-Breeding
Analytical Support Project at Argonne National Laboratory,  This document was
prepared and printed using \TEX on a VAX-11/780 system.)}
\par\askip\noindent
{\bf Introduction \hfill}
\par\nobrk\askip\noindent
RSX-11M V3.2 provides several means for communication between tasks.  Among
these are global event flags, common memory, and the send/receive data
directives.  The latter provide for data transfer from one task to another,
but restrict the amount of data to 13 words per transfer.  RSX-11M-Plus
allows variable-length data blocks of up to 256 words in length; this
feature is not provided with RSX-11M, because it requires support for
secondary executive pool, which RSX-11M does not have.
\par\noindent
Because a ``variable length send/receive'' function can be useful in a number
of applications, a simple driver has been written to provide some of the
same functionality.  The RSX-11M device name chosen for this driver is {\sl VS:}
(for {\sl V}ariable {\sl S}end-data), and its features are as follows:
\par\noindent
The driver is actually a queue manager, which has its own private pool of
dynamic memory.  Functions provided include:  create a queue, put a message
in a queue, get a message from a queue, and delete a queue.  Each queue has
a six-character name.  When a task accesses a queue, it specifies the queue
name.  By default, each task can access a queue whose name is the same as
the task's name.  If selected when the driver is assembled, a protection
feature prevents non-privileged tasks from operating on queues other than
their default queue, except for writing.  ({\sl Any} task can write a message to
{\sl any} queue).  Queues are created explicitly through driver QIO calls,
and are always accessed in FIFO (first-in, first-out) order.
\par\noindent
With {\sl VS:} Version\ 2, a number of additional features have been added to the
driver.  The ability to request a ``read with wait'' has been retained.  (This
feature causes a read request on an empty queue to wait until a message is
available).  However, ``read with wait'' has the negative effect of causing
the requesting task to be fixed in memory while the request is pending.  To
alleviate this problem, an AST (Asynchronous System Trap) mechanism has been
added in Version\ 2.  A task can specify an AST routine to be called each
time a message is received in a particular queue.  Unlike ``read with wait'',
specifying AST's does not cause the task to become fixed in memory, since
RSX-11M will reload a task that has been checkpointed out to disk when an
AST occurs for that task.  Another new feature of {\sl VS:} Version\ 2 is the
``create or flush'' function, which creates a new empty queue if not already
present, but flushes the queue of all messages (and aborts a waiting read
request) if the queue already exists.  Version\ 1 required a ``delete queue''
followed by a ``create queue'' to achieve the same effect.
\par\noindent
{\sl VS:} Version\ 2 is compatible with Version\ 1, with one exception:  the
function code for the ``dump driver pool'' function, which is not intended for
general use, has been changed from octal 3400 to octal 4000, because octal
3400 is the code for a standard DEC I/O function that {\sl VS:} now uses.  This
function is only used for debugging, and by the {\sl VS:} utility program VSUTIL,
which has been modified for Version\ 2.
\par\vfill\brk
\par\askip\noindent
{\bf Function, Parameters, Status Codes \hfill}
\par\nobrk\askip\noindent
Here is a list of the QIO function codes recognized by the {\sl VS:} driver.
All function codes are in octal.
\par\nobrk\noindent
\vbox{
\halign to 8truein {#\ |#\ |#\hfill\cr
IO.KIL|(0012)|Cancel all waiting read requests for this task.\cr
IO.WLB|(0400)|Put a message into any existing queue.\cr
IO.RLB|(1000)|Get a message from a queue.\cr
IO.RLB ! SF.WAI|(1200)|Wait for a message to be put into a queue, and get
it.\cr
IO.ATT|(1400)|Device attach -- ignored.\cr
IO.DET|(2000)|Device detach -- ignored.\cr
IO.CRQ|(2400)|Create a new message queue.\cr
IO.CRQ ! SF.FLU|(2500)|Create a new message queue, or flush an existing
one.\cr
IO.DLQ|(3000)|Delete a message queue and all queued messages.\cr
IO.CLN|(3400)|Clear all AST's set by this task.\cr
IO.DMP|(4000)|For utilities or debugging, dump a snapshot of the driver's
pool.\cr
IO.AST|(4400)|Set or clear an AST for an existing queue.\cr
IO.EXM|(5000)|Examine a selected message in a queue, without dequeuing it.\cr
IO.DLM|(5400)|Delete a selected message from a queue.\cr
}}
\par\askip\noindent
The six-word QIO parameter list is used by {\sl VS:} as follows:
\par\nobrk\noindent
\vbox{
\halign to 8truein {#|#\hfill\cr
Word 1 -- |Buffer address (with IO.WLB, IO.RLB, IO.DMP, IO.EXM)\cr
Word 2 -- |Buffer length in bytes (with IO.WLB, IO.RLB, IO.DMP, IO.EXM)\cr
Word 3 -- |First word of RAD50 queue name (with IO.WLB; for privileged\cr
|tasks, also with IO.RLB, IO.CRQ, IO.DLQ, IO.AST, IO.EXM, IO.DLM)\cr
Word 4 -- |Second word of RAD50 queue name (with same functions as word
3)\cr
Word 5 -- |Message ID (with IO.EXM, IO.DLM)\cr
Word 6 -- |Address of AST routine, or 0 to clear AST routine (with
IO.AST)\cr
}}
\par\askip\noindent
Notes:\hfill
\par\nobrk\hpar{$\bullet$} Buffers used with {\sl VS:} must be aligned on word boundaries.
\par\hpar{$\bullet$} The ``read'' function returns the name of the sending task
(in RAD50 representation) in the first two words of the buffer.  Thus, if
you are expecting messages of up to $ N $ bytes in length, you must
provide at least $ N+4 $ bytes of buffer space for a ``read'' operation.
\par\hpar{$\bullet$} Under no circumstances can any task clear an AST other than
the task that set the AST.  Also, setting an AST for a queue prohibits
read-and-wait, flush, or delete queue operations, but does not prohibit a simple
read operation.
\par\hpar{$\bullet$} The AST routine receives control with three parameter words
on its stack:  the first two words are the queue name in RAD50 representation,
and the third word is the length of the message in bytes, including four extra
bytes for the sender task name.  See the Executive Reference Manual for details
about AST processing.
\par\hpar{$\bullet$} The ``examine message'' and ``delete message'' operations
utilize a ``message-ID'', which is actually the virtual address of the message
in VSDRV space.  Message-ID 0 corresponds to the first message in a queue.
\par\hpar{$\bullet$} The ``examine message'' function can be called with a buffer
of any length; as much of the message as will fit into the buffer will be copied.
The buffer will contain four words of information preceding the message.  The
first word is the message-ID of the {\bf next} message in the queue, or 0 if this
is the last one.  The second word contains the length of the message text in
bytes.  The third and fourth words contain the sender task name in RAD50.
\par\askip\noindent
Status codes that can be returned by {\sl VS:} in the first word of the I/O status
block include:
\par\nobrk\noindent
\vbox{
\halign to 8truein {#|$ # $|#\hfill\cr
IS.SUC|(+1.)|Successful completion; second word of IOSB is a byte
count.\cr
IE.ABO|(-15.)|Operation aborted due to IO.KIL, SF.FLU, IO.DLQ, or I/O
rundown\cr
| |at task exit.\cr
IE.PRI|(-16.)|Privilege violation--unprivileged task attempted to
create,\cr
| |delete, set an AST in, or read a queue other than its
default,\cr
| |or attempted to do an IO.DMP function (only if protection
enabled).\cr
IE.QNF|(-101.)|Queue not found.\cr
IE.QEX|(-102.)|Queue already exists (on attempt to create a queue).\cr
IE.NMS|(-103.)|No more space in {\sl VS:} private pool.\cr
IE.RAW|(-104.)|Read already waiting on queue (for IO.RLB ! SF.WAI, or
IO.AST).\cr
IE.NOM|(-105.)|No message available in queue (for IO.RLB without
SF.WAI,\cr
 | |IO.EXM, or IO.DLM).\cr
IE.UBS|(-106.)|User buffer is too small to hold entire message (or to
hold\cr
| |entire driver pool snapshot, for IO.DMP).\cr
IE.ATS|(-107.)|AST is already set (for IO.RLB ! SF.WAI, IO.AST specifying
an\cr
| |AST address, IO.AST clearing another task's AST, IO.CRQ !
SF.FLU,\cr
 | |or IO.DLM to a queue whose AST was set by another task).\cr
IE.ATC|(-108.)|No AST is set for queue (for IO.AST clearing an AST).\cr
}}
\par\vfill\brk
\par\askip\noindent
{\bf Disadvantages and advantages of VS: Version\ 2\hfill}
\par\nobrk\askip\noindent
Disadvantages (compared to true variable send/receive on RSX-11M-Plus):\hfill
\par\nobrk\hpar{1.} At least one LUN must be set aside for use with {\sl VS:}--this LUN is the one you
use for setting AST's.  If you use ``read and wait'', then one LUN must be set
aside for each simultaneous outstanding wait.  True variable send/receive
does not require any LUN's.
\par\hpar{2.} The {\sl VS:} driver and its private pool occupy additional memory space,
which cannot be used for other purposes when not in use by {\sl VS:}.  True variable
send/receive code is part of the exec, and its pool space is part of the
exec's secondary pool, which is usable by other system components.
\par\hpar{3.} {\sl VS:} requires that queues be created, flushed, and deleted explicitly.
True variable send/receive automatically has a queue for each task, which is created
when the task is installed, flushed when the task exits, and deleted when the task
is removed.
\par\askip\noindent
Advantages (compared to true variable send/receive on RSX-11M-Plus):\hfill
\par\nobrk\hpar{1.} {\sl VS:} can be used under RSX-11M, which does not support variable
send/receive or secondary exec pool; the only cost in terms of exec
resources is the I/O packet and AST block usage.
\par\hpar{2.} {\sl VS:} queues exist independent of tasks in the system.  When a task exits,
no messages are deleted (not even from a task's ``default'' queue); queues can
still be ``flushed'' using the ``create or flush'' function.
\par\hpar{3.} {\sl VS:} allows a task to manipulate several queues of messages, while
variable send/receive provides only one queue per task.  With {\sl VS:}, tasks can
``share'' queues, under some protocol established among them, if the
application requires.  (For example, you can create a spooling system with
several despoolers taking requests from the same queue.)
\par\hpar{4.} {\sl VS:} allows selective examination and deletion of messages in
any queue.
\par\vfill\brk
\par\askip\noindent
{\bf Use of VS: from FORTRAN \hfill}
\par\nobrk\askip\noindent
Here are some notes on using {\sl VS:} Version\ 2 from a FORTRAN program.  Most of
this information can be found in the RSX-11M/M-PLUS Executive Reference
Manual.
\par\nobrk\hpar{(1)} Setting up the QIO parameter list \hfill
\par\nobrk\hpar{}
The QIO parameter list is an array of six integers.  The {\sl VS:} driver uses
these six words as described previously.  To create the parameter list, use
a statement like this:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ INTEGER IPARM(6)}\hfill
\par\hpar{}
To put a buffer address into the first word, use the library subroutine
GETADR, like this:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ CALL GETADR(IPARM(1),IBUFER)}\hfill
\par\hpar{}
(Here, IBUFER is the name of the buffer array that is used to transfer
messages between the driver and the task).  To put a buffer size into the
second word, use a simple assignment statement:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ IPARM(2)=124}\hfill
\par\hpar{}
(This tells the driver that $124.$ bytes of storage are available in the
buffer on a read request, or that the message being sent is $124.$ bytes
long on a write request.  This would require that the buffer array be
declared to contain at least $124/2 = 62$ words, e.g.
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ INTEGER IBUFER(62)}\hfill
\par\nobrk\hpar{}
or the equivalent).  To put a RAD50 queue name into the third and fourth
words, you must create a two-word integer array containing the queue name,
and set the name into the array with a DATA statement, like this:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ INTEGER QNAME(2)}\hfill
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ DATA QNAME/3RMYQ,3RUE5/}\hfill
\par\hpar{}
(The constant form 3Rxxx is like 3Hxxx or 'xxx' but creates a RAD50
representation of the character string.  In this case, QNAME(1) will contain
the string 'MYQ', and QNAME(2) will contain 'UE5', which together form the
name of the queue 'MYQUE5').  Then, you put the queue name into the
parameter list with two simple assignments:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ IPARM(3)=QNAME(1)}\hfill
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ IPARM(4)=QNAME(2)}\hfill
\par\hpar{}
(There are other ways to do this, especially for cases where the queue name
is not a constant determined at compilation time, but may be determined when
the program is run.  Consult the FORTRAN User's Guide for subroutines that
convert ASCII character strings to RAD50, if you need them).  Note that you
do not need to set up the first two words of the parameter list unless you
are doing a ``read'' or ``write'' function, and that the third and fourth
words can be set to zeroes if you want to use your task's default queue name
(same as the task name).  Setting up parameter word six with the AST
subroutine address is not discussed here, as a special interface routine is
generally used to allow FORTRAN to process AST's.  This routine is described
in detail later.\hfill
\par\hpar{(2)} Assigning a unit to the {\sl VS:} driver\hfill
\par\nobrk\hpar{}
In order to access the {\sl VS:} driver, you must have a logical unit assigned to
it.  This can be done when you build your task, with an option like
this:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ ASG=VS:3}
\par\nobrk\hpar{}
(which assigns LUN 3 to the {\sl VS:} driver).  Also, you can do this with a
subroutine call within your program:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ CALL ASNLUN(3,'VS',0)}\hfill
\par\hpar{}
Here, you specify the LUN as the first parameter; the other parameters must
be 'VS' and 0 as shown.  You can assign as many LUN's to the {\sl VS:} driver as
you need.  In particular, if your program will be using ``read and wait''
with more than one queue at the same time, then you must have as many LUN's
assigned as there will be simultaneous waits.\hfill
\par\hpar{(3)} Doing the actual operation\hfill
\par\nobrk\hpar{}
To do the operation, you call one of the library routines QIO or WTQIO.  In
both cases, the calls are identical:
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ CALL QIO(fnc,lun,[efn],,[isb],[prl][,ids])}
\par\nobrk\hpar{}
{\tt\ \ \ \ \ \ \ CALL WTQIO(fnc,lun,[efn],,[isb],[prl][,ids])}
\par\hpar{}
The parameters enclosed in [\ \ ] are optional, but commas must be included as
shown, even if some parameters are left out.  The parameters are:
\par\nobrk\hpar{}
\vbox{
\halign to 7.2truein {\ \ \ \ \ \ \ \ \ #|\ \ \ \ \ \ #\hfill\cr
fnc|One of the octal function codes, specified in FORTRAN\cr
|with a leading '' to signify ``octal''; e.g.  ''1200\cr
lun|The logical unit number on which to perform this\cr
|operation.  The LUN must already be assigned to {\sl VS:}\cr
efn|An event flag number.  Event flags are single-bit\cr
|flags that can be used to signal the occurrence of\cr
|certain events, in this case the completion of the\cr
|requested operation.  You should probably use ``local''\cr
|event flags here (event flags used only by your task);\cr
|these flags are numbered from 1 to 32.  It is through\cr
|the event flag that you determine when the operation\cr
|has been completed.\cr
isb|The name of a two-word array, the I/O status block,\cr
|which receives a status code in the low byte of the\cr
|first word, and a byte count in the second word.  The\cr
|status code should be $+1$ for successful operations.\cr
|The byte count tells you how long the message is when\cr
|you do a ``read'', and should equal your message length\cr
|when you do a ``write''.\cr
prl|The name of the six-word parameter list array.\cr
ids|An integer variable into which the system places a\cr
|``directive status''; if you have made any mistakes in\cr
|setting up the call, or if some condition has occurred\cr
|that has prevented your request from being passed to the\cr
|{\sl VS:} driver, this word will contain an error code.  Normal\cr
|completion should produce a $+1$ status.\cr
}}
\par\hpar{}
The difference between using the QIO and WTQIO calls is this:  WTQIO will
not return control to your program until the operation has been completed,
while QIO will return as soon as the operation has been set up and sent to
the {\sl VS:} driver.  Look at the Executive Reference Manual for subroutines that
test event flag status, or stop/wait for event flags to be set, if you are
planning on using the QIO call instead of the WTQIO call.
\par\vfill\brk
\par\askip\noindent
{\bf Use of VS: AST's from FORTRAN \hfill}
\par\nobrk\askip\noindent
The Executive Reference Manual states that FORTRAN does not support AST
routines.  This does {\sl not} mean that you can't process AST's in a
FORTRAN routine; it {\sl only} means that FORTRAN doesn't provide the
necessary interface between the RSX-11M AST mechanism and itself.
\par\noindent
When an AST occurs, RSX-11M saves part of the current status of the program, and
transfers control to the specified AST routine.  Depending upon the type of
AST that has occurred, several parameters may be passed by the system to the
AST routine.  The method used for passing these parameters is not compatible
with the way FORTRAN calls subroutines; this is one of the reasons why
FORTRAN doesn't directly support AST routines.  A second reason is that the
system does {\sl not} save the program's register contents before entering
the AST routine; since FORTRAN subroutines freely modify all registers, this
is also an incompatibility.  Finally, when an AST routine completes its
processing, it must return control to the system (for restoring of the
program's status and clearing of the ``AST state'' indicator); FORTRAN
subroutines simply perform a ``RETURN'' operation, which is not enough.
Therefore, in order to {\sl process} AST's within a FORTRAN routine, some
additional interfacing code must be supplied, written in MACRO-11.
\par\noindent
Many different types of AST interface modules for FORTRAN can be written;
you should have a special one written for you if your needs aren't met by
those described here.  Each such module will have the following components:
an initialization routine, a ``set AST for queue'' routine, a ``clear AST
for queue'' or ``clear all AST's'' routine, and an internal AST routine that
gains control when an AST occurs, saves the program's registers, sets up the
parameter list for the FORTRAN AST routine, calls the FORTRAN AST routine,
and finally cleans up the stack, restores the registers, and does an ``AST
exit'' after the FORTRAN routine returns.
\par\askip\noindent
AST setup module IVSAST\hfill
\par\nobrk\noindent
IVSAST consists of FORTRAN-callable function IVSAST, and an internal AST
interface routine.  IVSAST allows a list of queues to be specified, and
either a single FORTRAN routine, or a list of FORTRAN routines, to be
associated with the AST's for those queues.  The internal AST routine calls
the appropriate FORTRAN routine when an AST occurs, passing it the queue
name and message length.  Individual clearing of AST's for queues is not
provided, but all AST's set by the program may be cleared by calling IVSAST
a second time, with no queue name list.  For further details, look at
documentation in the source code for IVSAST.
\par\askip\noindent
AST setup module BCTVSAST1\hfill
\par\nobrk\noindent
BCTVSAST1 consists of FORTRAN-callable subroutines VSAST1, ASTSET, and
ASTCLR, and an internal AST interface routine.  VSAST1 is called once, to
specify the {\sl VS:} logical unit number, an event flag for internal use, and the
single FORTRAN AST routine.  Then, ASTSET is called to set an AST for a
single queue, and ASTCLR is called to clear the AST for a single queue.  The
internal AST routine calls the FORTRAN routine when an AST for any queue
occurs, passing it the queue name and message length.  To clear all AST's at
once, a call to the Executive subroutine WTQIO must be made.  For further
details, look at documentation in the source code for BCTVSAST1.
\par\vfill
\end
