- INTRODUCTION
Most VMS application programs perform terminal i/o through high
level language statements such as READ or WRITE which compile
into system calls to the terminal driver. For applications that
need to manipulate read timeouts, character echo, line speed, etc
this is inadequate in terms of performance and control. However,
the terminal driver can be accessed directly through QIO system
calls. This paper covers the background of the driver and how to
interface to it.
- UNIBUS HARDWARE ARCHITECTURE
The VAX is designed with a "memory mapped" I/O interface for all
of its UNIBUS peripheral devices. This means a device communicate
with the processor through CSR (Control and Status Register) and
data registers which are mapped as addresses in the portion of
the physical address space called the UNIBUS address space. The
contents of these addresses can then be manipulated by various
common instructions eg. MOVW, CLRW, BISW, etc. This is unlike
some systems eg. IBM which has special I/O and channel control
instructions for addressing and data transfer on its channels.
- READING AND WRITING DEVICE REGISTERS
Each I/O controller or device directly attached to the UNIBUS has
a set of CSR and data registers which are addressable as memory
locations in the UNIBUS address space. The device driver can read
or write data to the registers as though they are locations in
memory. Most hardware controllers have DIP switches which allow
the CSR and vector offsets to be set to various parts of the
UNIBUS address space. The UNIBUS adapter performs the mapping of
physical addresses to UNIBUS addresses that correspond to device
registers.
- UNIBUS ADDRESS SPACE
The UNIBUS address space consists of 256 Kbytes of memory (18-bit
address) of which 8 Kbytes are reserved for device control
registers. The UNIBUS adapter translates the 18-bit addresses
into actual (32-bit) physical addresses by using mapping
registers. It is this translation that allows the O/S, I/O
drivers and UNIBUS devices to access the same physical address
space. Listed below are the start of the UNIBUS address space for
some of the VAX processors:
VAX 780 20100000 (hex)
VAX 750 FC0000
VAX 730 FC0000
- DEVICE CSR AND VECTORS
The Control and Status Register of a device enables the driver to
perform I/O and return status information on the outcome of the
operation. Vectors are offsets into a table of interrupt service
routines used by the processor. To produce a list of known device
CSR and vector on the system, invoke SYSGEN as follows:
$ RUN SYS$SYSTEM:SYSGEN
SYSGEN> SHOW /CONFIG
System CSR and Vectors on 14-FEB-1986 11:35:50.24
Name:DQA Units:1 Nexus:3 (UBA) CSR:775606 Vector1:250 Vector2:000
Name:MSA Units:1 Nexus:3 (UBA) CSR:772520 Vector1:224 Vector2:000
Name:XGA Units:1 Nexus:3 (UBA) CSR:760344 Vector1:300 Vector2:304
Name:TXA Units:8 Nexus:3 (UBA) CSR:760354 Vector1:320 Vector2:324
The physical memory address of the CSR can be found by adding the
CSR to the start of the UNIBUS address space eg. for a VAX 730,
DMF32 terminal controller CSR is 760354(oct), vectors are 320 and
324(oct). The physical memory address of the CSR register is:
FC0000 + 760354(oct) = FFE0EC
The terminal driver for DMF32, YCDRIVER, obtain the device's
status and activate the device by reading and writing to location
FFE0EC. Terminal device drivers require 1 vector to handle
character transmit interrupts and 1 vector to handle character
receive interrupts. The vectors listed by SYSGEN on a VAX 730 are
actually offsets into the second page of the System Control Block
(SCB). The SCB is a VMS data structure that contains all the
interrupt service routine addresses and is filled in by SYSGEN at
AUTOCONFIG time. The second SCB page on a VAX 730 is used for
direct-vectored UNIBUS device interrupts.
When a character receive interrupt occurs, the processor will add
the receive vector offset(320) to the SCBB register and 200(hex)
to obtain the SCB vector. The SCB vector contains an address
which points to the Interrupt Transfer Vector in another data
structure called Channel Request Block(CRB). This vector contains
instruction to save registers R0-R5 and jump to the character
receive entry point in YCDRIVER. The transmit vector(324) works
in a similar manner when a transmit interrupt occurs.
- TERMINAL DEVICE DRIVER
The terminal driver was rewritten in VMS 3.0 using the class and
port strategy. The terminal class driver (TTDRIVER) contains FDT
routines and device dependent routines. The port drivers like
YCDRIVER and DZDRIVER contain interrupt and controller-specific
routines for DMF32 and DZ11 boards. Both class and port driver
images can be separately loaded, thus enabling users to write
custom-designed device drivers and test them out.
When the system is booted, SYSBOOT reads TTDRIVER into nonpaged
pool. Later, AUTOCONFIG in SYSGEN determines the port drivers to
be loaded in for each controllers. These drivers then initializes
the various Unit Control Block (UCB) fields.
- QIO REQUEST PROCESSING
All terminal I/O requests are handled by the QIO system service
which interfaces with the terminal driver. A QIO request issued
from the host is processed in the following manner:
- A channel number is assigned to his process for a particular
terminal device. This number is actually an offset into a data
structure in P1 space called Channel Control Block. The CCB
contains a pointer to the device UCB.
- The QIO system service performs device-independent
validation of the request, check for sufficient process
quota and allocates an Interrupt Request Packet (IRP).
- The IRP is initialized with data concerning the i/o request
and then passed to class driver TTDRIVER. The class drivers's
FDT routines validate device-dependent QIO parameters (P1-P6).
- A fork process is created and the class driver activates
device by calling port driver routines to set the bits in the
device CSR. If it is a DMA request, the transmit buffer
register is filled with address of user buffer and char count
register initialized to number of bytes to transfer.
- The driver waits until device requests an interrupt. The
interrupt service routine is selected through the device
vectors.
- The port driver reads CSR to check transfer status and pass it
it back to the class driver.
- The class driver and VAX/VMS performs I/O post-processing and
return status to QIO system service. Control returns to
program.
- APPLICATION PROGRAM INTERFACE
Programs can be written to interface directly with the driver
through QIO system calls instead of high-level i/o statements eg.
READ or WRITE. The main advantages are faster operations and
complete control over the terminal and its characteristics. The
disadvantages are program becomes terminal-dependent and more
complex to write. A QIO statement requires the following
parameters, some of which are optional:
Event flag, channel number, function code, I/O status block,
AST address, AST parameter, P1-P6 parameters
The following examples are written in FORTRAN and MACRO.
- CHANNEL ASSIGNMENT
Before any QIO can be issued, a channel to the device (eg. TXA3:
or SYS$INPUT) must be assigned to the calling process.
INTEGER SYS$ASSIGN,STATUS
INTEGER*2 TTCHAN
STATUS=SYS$ASSIGN('SYS$INPUT',TTCHAN,,)
IF (.NOT.STATUS) CALL LIB$STOP(%VAL(STATUS))
- READ QIO
To accept input from the terminal, issue a read QIO which
specifies the function required and an input buffer:
INCLUDE '($IODEF)'
INTEGER STATUS,FUNC,SYS$QIOW
BYTE BUFFER(10)
FUNC=IO$_READVBLK.OR.IO$M_NOECHO
STATUS=SYS$QIOW(,%VAL(TTCHAN),%VAL(FUNC),,,,BUFFER,%VAL(10),,,,)
The IO$_READVBLK is known as a function code and IO$M_NOECHO is
the function modifier code. A read QIO is normally terminated by
a terminator char, buffer full or device timeout. ASCII chars
0-1F(hex) normally terminate the read. To terminate a read
specifically by TAB (9 hex), code this:
INTEGER MASK(2)
MASK(1)=0
MASK(2)='00000200'X ! 9th bit position
FUNC=IO$_READVBLK.OR.IO$M_NOFILTR
STATUS=SYS$QIOW(,%VAL(TTCHAN),%VAL(FUNC),,,,BUFFER,%VAL(10),,MASK,,)
- WRITE QIO
Write operations display the contents of a user buffer on the
associated terminal. The example below uses the common function
code IO$_WRITEVBLK.
CHARACTER LINE*20
LINE='HELLO WORLD'
FUNC=IO$_WRITEVBLK
STATUS=SYS$QIOW(,%VAL(TTCHAN),%VAL(FUNC),,,,%REF(LINE),%VAL(11),,,,)
To specify carriage control to the write QIO, use the P4
parameter. For example, to output "CRLF CRLF printline CR" :
INTEGER CCTL
CCTL='8D020000'X
STATUS=SYS$QIOW(,%VAL(TTCHAN),%VAL(FUNC),,,,%REF(LINE),%VAL(11),,%VAL(CCTL),,)
- SET PASTHRU MODE
Normally, all terminal input is preprocessed by the terminal
driver for special control characters such as CTRL/Y, CTRL/T,
etc. To force the driver to pass everything to the program, the
terminal must be set to PASTHRU mode using either DCL or program
control.
$TT2DEF
TTC: .BLKL 3
$QIOW_S CHAN=TTCHAN,FUNC=#IO$_SENSEMODE,P1=TTC,P2=#12
BISL2 #TT2$M_PASTHRU,TTC+8
$QIOW_S CHAN=TTCHAN,FUNC=#IO$_SETMODE,P1=TTC,P2=#12
Terminal with PASTHRU will still honour XON-XOFF characters if
TTSYNC is set.
- SET LINE SPEED
Line speed can be set by DCL commands or program control. What
the manual on terminal drivers did not mention is that to set the
speed by program of a device other than your own terminal,
AUTOBAUD must also be turned off. The QIO does not return any
error status at all, it simply does not change the speed.
$QIOW_S CHAN=TTCHAN,FUNC=#IO$_SENSECHAR,P1=TTC,P2=#12
BICL2 #TT2$M_AUTOBAUD,TTC+8
$QIOW_S CHAN=TTCHAN,FUNC=#IO$_SETCHAR,-
P1=TTC,P2=#12,P3=#TT$C_BAUD_1200
Changing the characteristics of your own terminal eg. speed,
echo, PASTHRU, etc does not require privilege. LOG_IO or PHY_IO
is required, using the function code IO$_SETCHAR, if other
devices are to be modified.
- SUMMARY
The developers of the VAX architecture and VMS have designed a
well-structured, highly flexible approach to device control.
Users have the ability to directly control terminals through
low-level QIO calls and improve i/o performance, but at the risk
of the application being machine-dependent. To obtain the best of
both worlds, these calls should be isolated into subroutine
libraries for easy porting in future.
- REFERENCE