HP OpenVMS I/O User’s Reference Manual: OpenVMS Version 8.4 > Chapter 5 Terminal Driver
5.5 Terminal Driver Programming Examples
The C program LAT.C shown in Example 5-1 initiates and maintains an outbound LAT session
from the local node. It demonstrates the following LAT $QIO functions: Cloning the LAT template
device (LTA0:) IO$M_LT_CONNECT (on forward
port)
Example 5-1 LAT.C Terminal Driver Programming Example #module LAT_FORWARD_CONNECT "X1.0-001"/*
**++
**
** MODULE DESCRIPTION:
**
** In initiating and maintaining an outbound LAT session from the local
** node, this program demonstrates the following LAT $QIO functions:
**
** o Cloning the LAT template device (LTA0:)
** o IO$M_LT_SETMODE
** o IO$M_LT_CONNECT (on forward port)
** o IO$M_LT_SENSEMODE
**
**--
*/
/*
**
** INCLUDE FILES
**
*/
#include /* VMS Descriptor Definitions */
#include /* I/O Function Codes Definitions */
#include /* LAT Definitions */
#include /* System Service Return Status */
/* Code Definitions */
#include /* Terminal Characteristics */
#include /* Terminal Extended */
/* Characteristics */
/*
**
** MACRO DEFINITIONS
**
*/
/*
** Service name which the session will be to.
*/
#define SERVICE_NAME "LAT_SERVICE"
|
|
|
#define SERVICE_NAME_LENGTH 11
/*
** For the sake of clarity, the sizes of the buffers used for reading from
** and writing to the LTA and TT devices are set to the values below. In
** order to gain maximum throughput from this program, the following system
** parameters can be set:
**
** o TTY_ALTYPAHD - 1500
** o TTY_TYPAHDSZ - 80
**
** To get the best performance from this program without touching these
** system parameters on your system, modify the program to set the size of
** the buffers to the following:
**
** o LTA_BUFFER_SIZE = MIN(TTY_ALTYPAHD, 1500)
** o TT_BUFFER_SIZE = MIN(TTY_TYPAHDSZ, 132)
*/
#define LTA_MAXBUF 1500
#define TT_MAXBUF 80
/*
** Size of the LAT SENSEmode itemlist.
*/
#define MAX_SENSE_ITEMLIST_SIZE 1500
/*
** Character user can press to terminate the LAT connection (CTRL+\).
*/
#define CONNECTION_TERMINATOR 0x1C
/*
**
** FUNCTION PROTOTYPES
**
*/
unsigned long SetDeviceChars(void);
void ConnectAST(void);
void LTAreadChannelAST(void);
void TTreadChannelAST(void);
void LTAhangupHandler(void);
void EndSession(void);
void ExitHandler(void);
/*
**
** GLOBAL DATA
**
*/
char *LTAbuffer, /* LTA device I/O buffer */
*TTbuffer, /* TT device I/O buffer */
/*
** Text for LAT reject codes. Note that some LAT
** implementations will return a 0 reject code to
** indicate a normal disconnect.
*/
*LATrejectTable[] = {
"Unknown",
"User requested disconnect",
"System shutdown in progress",
"Invalid slot received",
"Invalid service class received",
"Insufficient resources at server",
"Port or service in use",
"No such service",
"Service is disabled",
"Service is not offeredon the requested port",
"Port name is unknown",
"Invalid service password",
"Remote entry is not in queue",
"Immediate access rejected",
"Access denied",
"Corrupted request",
"Requested function is not supported",
"Session cannot be started",
"Queue entry deleted by server",
"Illegal request parameters" };
unsigned short LTAchannel, /* LTA device I/O channel */
TTchannel, /* TT device I/O channel */
LTA_QIOiosb[4], /* IOSB for LTA device functions */
TT_QIOiosb[4]; /* IOSB for TT device functions */
unsigned long ReadTerminatorMask[2] = { 0, 0 },
/* $QIO read terminator mask */
SavedTTdeviceChar[3],
/* Saved TT device characteristics */
DeviceCharBuffSize = sizeof(SavedTTdeviceChar);
/* Size of device characteristics buffer*/
ExitConditionValue, /* Exit condition value of program */
LATrejectTableSize =/* Number of elements in LAT reject tbl */
sizeof(LATrejectTable) / sizeof(LATrejectTable[0]);
/*
** Itemlist for setting LAT port with the target service name.
*/
struct {
unsigned short item_code;
char item_byte_count;
char item_value[ SERVICE_NAME_LENGTH ];
} PortSetmodeItemlist = {
LAT$_ITM_TARGET_SERVICE_NAME, SERVICE_NAME_LENGTH, SERVICE_NAME
};
/*
** Exit handler block.
*/
struct {
unsigned long flink;
void (*exit_handler)();
unsigned long arg_count;
unsigned long *exit_status;
} ExitHandlerBlock = { 0, ExitHandler, 1, };
/*
** Devices which channels are assigned to.
*/
$DESCRIPTOR(LTAtemplateDSC, "LTA0:");
$DESCRIPTOR(TTchannelDSC, "SYS$COMMAND");
main()
{
/*
** Local Variables:
*/
unsigned long status,
portSetmodeItemlistSize = sizeof(PortSetmodeItemlist);
/*
** BEGIN:
**
** Declare an exit handler.
*/
if (!( (status = sys$dclexh() ) & 1) )
lib$signal(status);
/*
** Assign a channel to LTA0: to get a forward LAT port and assign a
** channel to the terminal.
*/
if (!( (status = sys$assign(, , 0, 0) ) & 1) )
lib$signal(status);
if (!( (status = sys$assign(, , 0, 0) ) & 1) )
lib$signal(status);
/*
** Allocate memory for the channel data buffers.
*/
LTAbuffer = malloc(LTA_MAXBUF);
TTbuffer = malloc(TT_MAXBUF);
/*
** Set device characteristics for the two channels.
*/
if (!( (status = SetDeviceChars() ) & 1) )
lib$signal(status);
/*
** Do SETmode $QIO to set the port entity with the target service name
** specified in the item list.
*/
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_TTY_PORT|IO$M_LT_SETMODE,
_QIOiosb, 0, 0,
,
portSetmodeItemlistSize,
LAT$C_ENT_PORT|(LAT$C_ENTS_OLD << 0x10),
0, 0, 0) ) & 1) )
lib$signal(status);
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
/*
** Enable a CTRL+Y AST on the LAT channel.
*/
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_SETMODE|IO$M_CTRLYAST,
_QIOiosb, 0, 0,
LTAhangupHandler,
0, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
/*
** Post the first read (with AST) on the LTA device to ensure that the
** first burst of data from the target service is not lost. It is very
** important that the first read is queued before doing the connect
** $QIO to ensure no data lossage.
*/
if (!( (status = sys$qio(
0,
LTAchannel,
IO$_READVBLK|IO$M_NOECHO,
_QIOiosb,
LTAreadChannelAST, 0,
LTAbuffer,
1, 0, , 0, 0) ) & 1) )
lib$signal(status);
/*
** Do the LAT connect $QIO and hibernate until program exit. The
** ConnectAST will execute when the connection completes and post the
** initial read on the TT channel.
*/
if (!( (status = sys$qio(
0,
LTAchannel,
IO$_TTY_PORT|IO$M_LT_CONNECT,
_QIOiosb,
ConnectAST, 0, 0, 0, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
sys$hiber();
} /* END - main() */
/*
**++
**
** FUNCTIONAL DESCRIPTION:
**
** This routine sets device characteristics of the LTA and TT devices.
** The HOSTSYNC, NOBRDCST, EIGHTBIT and PASTHRU characteristics are set
** on the LTA device. The ESCAPE and TTSYNC characteristics are cleared.
**
** The TTSYNC, HOSTSYNC, EIGHTBIT, and PASTHRU characteristics are set
** on the TT device. The ESCAPE characteristic is cleared. The TT
** characterisitcs are also saved for restoration at program exit.
**
**--
*/
unsigned long SetDeviceChars(void)
{
/*
** Local Variables:
*/
unsigned long status,
deviceChar[3];
/*
** BEGIN:
**
** Mask and set the characteristics of the LTA device. Sense the
** current characteristics, and mask in and set the new ones.
*/
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_SENSEMODE,
_QIOiosb, 0, 0,
,
DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
deviceChar[1] =
(deviceChar[1] | (TT$M_HOSTSYNC | TT$M_NOBRDCST | TT$M_EIGHTBIT) )
& ~TT$M_ESCAPE & ~TT$M_TTSYNC;
deviceChar[2] |= TT2$M_PASTHRU;
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_SETMODE,
&TT_QIOiosb, 0, 0,
&deviceChar
DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
/*
** Repeat the procedure for TT device characteristics. However, save
** the current characteristics for restoration at program exit.
*/
if (!( (status = sys$qiow(
0,
TTchannel,
IO$_SENSEMODE,
$TT_QIOiosb, 0, 0,
&SavedTTdeviceChar
DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(TT_QIOiosb[0] & 1) )
lib$signal(TT_QIOiosb[0]);
deviceChar[0] = SavedTTdeviceChar[0];
deviceChar[1] = (SavedTTdeviceChar[1] |
(TT$M_TTSYNC | TT$M_HOSTSYNC | TT$M_EIGHTBIT) ) & ~TT$M_ESCAPE;
deviceChar[2] = SavedTTdeviceChar[2] | TT2$M_PASTHRU;
if (!( (status = sys$qiow(
0,
TTchannel,
IO$_SETMODE,
&TT_QIOiosb, 0, 0,
&deviceChar
DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(TT_QIOiosb[0] & 1) )
lib$signal(TT_QIOiosb[0]);
return(status);
} /* END - SetDeviceChars */
/*
**++
**
** FUNCTIONAL DESCRIPTION:
**
** This routine is an AST which executes when the connect $QIO completes.
** First the IOSB is checked. If the connection timed out or was aborted,
** simply end the session. Any other abnormal status causes the program
** to exit.
**
** Otherwise the connection completed successfully and a read on the TT
** channel is posted.
**
**--
*/
void ConnectAST()
{
/*
** Local Variables:
*/
unsigned long status;
/*
** BEGIN:
**
** If the status in the IOSB indicates that the connection timed out
** or aborted, call the session end routine. Any other abnormal
** status causes program exit.
*/
if ( (LTA_QIOiosb[0] == SS$_TIMEOUT) || (LTA_QIOiosb[0] == SS$_ABORT) )
EndSession();
if (!(LTA_QIOiosb[0] & 1) )
sys$exit(LTA_QIOiosb[0]);
/*
** The connection completed successfully! Post a read (with AST) on
** the TT device and return.
*/
if (!( (status = sys$qio(
0,
TTchannel,
IO$_READVBLK|IO$M_NOECHO,
&TT_QIOiosb,
TTreadChannelAST, 0,
TTbuffer,
1, 0, &ReadTerminatorMask 0, 0) ) & 1) )
lib$signal(status);
return;
} /* END - ConnectAST */
/*
**++
**
** FUNCTIONAL DESCRIPTION:
**
** This routine is an AST which executes when the first character read on
** the LTA channel completes. It does a "flush" read of the channel to
** drain any data out of the ALTYPAHD buffer and writes the data to the
** TT channel. It then posts another read on the channel.
**
**--
*/
void LTAreadChannelAST(void)
{
/*
** Local Variables:
*/
unsigned long status;
/*
** BEGIN:
**
** If the status in the IOSB indicates channel hangup, simply end the
** session. Signal any other abnormal status.
*/
if (LTA_QIOiosb[0] == SS$_HANGUP)
EndSession();
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
/*
** Do a "flush" read of the LTA device. This is done by doing a timed
** read with a 0 timeout. There may or may not be any data to drain.
** This method is more efficient than using single character reads.
*/
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_READVBLK|IO$M_TIMED|IO$M_NOECHO,
_QIOiosb, 0, 0,
LTAbuffer+1,
LTA_MAXBUF-1, 0,
&ReadTerminatorMask, 0, 0) ) & 1) )
lib$signal(status);
if (!(LTA_QIOiosb[0] & 1) && (LTA_QIOiosb[0] != SS$_TIMEOUT) )
lib$signal(LTA_QIOiosb[0]);
/*
** The second word of the IOSB contains the number of characters
** read. Write the characters plus 1 for the initial read to the
** TT device.
*/
if (!( (status = sys$qiow(
0,
TTchannel,
IO$_WRITEVBLK,
_QIOiosb, 0, 0,
LTAbuffer,
LTA_QIOiosb[1]+1, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(TT_QIOiosb[0] & 1) )
lib$signal(TT_QIOiosb[0]);
/*
** Post another read on the LTA device.
*/
if (!( (status = sys$qio(
0,
LTAchannel,
IO$_READVBLK|IO$M_NOECHO,
<A_QIOiosb,
LTAreadChannelAST, 0,
LTAbuffer,
1, 0, &ReadTerminatorMask, 0, 0) ) & 1) )
lib$signal(status);
return;
} /* END - LTAreadChannelAST */
/*
**++
**
** FUNCTIONAL DESCRIPTION:
**
** This routine is an AST which executes when the first character read on
** the TT channel completes. It does a "flush" read of the channel to
** drain any data out of the TYPAHD buffer and writes the data to the
** LTA channel. It then posts another read on the channel.
**
**--
*/
void TTreadChannelAST(void)
{
/*
** Local Variables:
*/
unsigned long status;
/*
** BEGIN:
**
** If the user pressed the connection terminator character, do a LAT
** disconnect $QIO and exit.
*/
if (*TTbuffer == CONNECTION_TERMINATOR)
{
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_TTY_PORT|IO$M_LT_DISCON,
_QIOiosb, 0, 0, 0, 0, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
return;
}
/*
** Do a "flush" read of the TT device. This is done by doing a timed
** read with a 0 timeout. There may or may not be any data to drain.
*/
if (!( (status = sys$qiow(
0,
TTchannel,
IO$_READVBLK|IO$M_TIMED|IO$M_NOECHO,
_QIOiosb, 0, 0,
TTbuffer+1,
TT_MAXBUF-1, 0,
&ReadTerminatorMask, 0, 0) ) & 1) )
lib$signal(status);
if (!(TT_QIOiosb[0] & 1) && (TT_QIOiosb[0] != SS$_TIMEOUT) )
lib$signal(TT_QIOiosb[0]);
/*
** The second word of the IOSB contains the number of characters
** read. Write the characters plus 1 for the initial read to the
** TT device.
*/
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_WRITEVBLK,
_QIOiosb, 0, 0,
TTbuffer,
TT_QIOiosb[1]+1, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
/*
** If the status in the IOSB indicates channel hangup, simply end
** the session. Signal any other abnormal status.
*/
if (LTA_QIOiosb[0] == SS$_HANGUP)
EndSession();
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
/*
** Post another read on the LTA device.
*/
if (!( (status = sys$qio(
0,
TTchannel,
IO$_READVBLK|IO$M_NOECHO,
_QIOiosb,
TTreadChannelAST, 0,
TTbuffer,
1, 0, &ReadTerminatorMask, 0, 0) ) & 1) )
lib$signal(status);
return;
} /* END - TTreadChannelAST */
/*
**++
**
** FUNCTIONAL DESCRIPTION:
**
** This routine is the CTRL+Y AST for the LTA channel. It executes when
** a hangup on the LTA channel is recognized (connection timed out or
** aborted). It will call the session end routine if it hasn't already
** been called by ConnectAST.
**
** NOTE: CTRL+Y ASTs for application ports will NOT execute when the
** connection is disconnected.
**
**--
*/
void LTAhangupHandler(void)
{
/*
** BEGIN:
**
** Call the session end routine and return.
*/
EndSession();
return;
} /* END - LTAhanghupHandler */
/*
**++
**
** FUNCTIONAL DESCRIPTION:
**
** This routine is executed at session end. It will do a $QIO SENSEmode
** and search the resulting itemlist to find the reason for the LAT
** disconnect. The reason for the disconnect is displayed on the
** terminal and the image exits.
**
**--
*/
void EndSession(void)
{
/*
** Local Variables:
*/
struct ITEM_ENTRY *itemlistEntry;
unsigned long status;
char *senseItemlist = malloc(MAX_SENSE_ITEMLIST_SIZE),
*itemlistEntryPointer;
/*
** BEGIN:
**
** Do the SENSEmode on the port.
*/
if (!( (status = sys$qiow(
0,
LTAchannel,
IO$_TTY_PORT|IO$M_LT_SENSEMODE,
<A_QIOiosb, 0, 0,
senseItemlist,
MAX_SENSE_ITEMLIST_SIZE,
LAT$C_ENT_PORT|(LAT$M_SENSE_FULL << 0x10),
0, 0, 0) ) & 1) )
lib$signal(status);
if (!(LTA_QIOiosb[0] & 1) )
lib$signal(LTA_QIOiosb[0]);
/*
** Set up two pointers used to traverse the itemlist.
*/
itemlistEntry = (struct ITEM_ENTRY *) senseItemlist;
itemlistEntryPointer = senseItemlist;
/*
** Search the itemlist for the LAT$_ITM_DISCONNECT_REASON code to find
** out why the connection terminated.
*/
while (itemlistEntry->LAT$R_ITM_CODE.LAT$W_ITEMCODE !=
LAT$_ITM_DISCONNECT_REASON)
{
/*
** If the current itemcode being checked has a string value,
** advance the pointer to the next itemcode by skipping
** BCNT bytes plus 3 bytes for the BCNT byte itself and the
** 2 byte itemcode.
*/
if (itemlistEntry->
LAT$R_ITM_CODE.LAT$R_ITM_BITS.LAT$V_STRING)
itemlistEntryPointer +=
itemlistEntry->LAT$R_ITEM_VALUE.
LAT$R_ITEM_COUNTED_STRING.LAT$B_ITEM_BCNT + 3;
/*
** If the current itemcode being checked has a scalar value,
** advance the pointer to the next itemcode by skipping 6
** bytes for the itemcode and the 4 byte scalar.
*/
else
itemlistEntryPointer += 6;
itemlistEntry = (struct ITEM_ENTRY *) itemlistEntryPointer;
}
/*
** If the disconnect reason is a LAT reject code, print out the
** text corresponding to the code and set the exit condition value
** to SS$_NORMAL.
*/
if (itemlistEntry->LAT$R_ITEM_VALUE.LAT$L_ITEM_SCALAR_VALUE <=
LATrejectTableSize)
{
printf("\nSession disconnected. Reason: %s\n\n\n",
LATrejectTable[ itemlistEntry->LAT$R_ITEM_VALUE.
LAT$L_ITEM_SCALAR_VALUE ]);
ExitConditionValue = SS$_NORMAL;
}
/*
** The scalar value is a LAT facility message code. Set the exit
** condition value to be the scalar. Upon image exit, the
** corresponding LAT facility message will be displayed.
*/
else
ExitConditionValue =
itemlistEntry->LAT$R_ITEM_VALUE.LAT$L_ITEM_SCALAR_VALUE;
sys$exit(ExitConditionValue);
} /* END - EndSession */
/*
**++
**
** FUNCTIONAL DESCRIPTION:
**
** This is the program exit handler which is executed upon image exit.
** It will cancel all pending I/O on the two channels and restore the
** TT channel characteristics.
**
**--
*/
void ExitHandler(void)
{
/*
** Local Variables:
*/
unsigned long status;
/*
** BEGIN:
**
** Cancel I/O on the channels, reset terminal characteristics and
** return.
*/
if (!( (status = sys$cancel(LTAchannel) ) & 1) )
lib$signal(status);
if (!( (status = sys$cancel(TTchannel) ) & 1) )
lib$signal(status);
if (!( (status = sys$qiow(
0,
TTchannel,
IO$_SETMODE,
&TT_QIOiosb, 0, 0,
&SavedTTDeviceChar,
DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
lib$signal(status);
if (!(TT_QIOiosb[0] & 1) )
lib$signal(TT_QIOiosb[0]);
return;
} /* END - ExitHandler */
|
|
The MACRO 32 program FULL_DUPLEX_TERMINAL.MAR
(Example 5-2) shows several
I/O operations using the full-duplex capabilities of the terminal.
This program shows some important concepts about terminal driver programming:
assigning an I/O channel, performing full-duplex I/O operations, enabling
Ctrl/C AST requests, and itemlist read operations. The program is
designed to run with a terminal set to full-duplex mode. The initialization code queues a read request
to the terminal and enables Ctrl/C AST requests. The main loop then
prints out a random message every three seconds. When you enter a
message on the terminal, the read AST routine prints an acknowledgment
message and queues another read request. If you press Ctrl/C, the
associated AST routine cancels the I/O operation on the assigned channel
and exits to the command interpreter. Example 5-2 FULL_DUPLEX_TERMINAL.MAR Terminal Driver Programming Example |
.TITLE FULL_DUPLEX TERMINAL PROGRAMMING EXAMPLE
.IDENT /05/
; ********************************************************************
;
; TERMINAL PROGRAM
;
; ********************************************************************
.SBTTL DECLARATIONS
.DISABLE GLOBAL
;
; Declare the external symbols and MACRO libraries.
;
.EXTERNAL LIB$GET_EF
.LIBRARY 'SYS$LIBRARY:LIB.MLB'
.LIBRARY 'SYS$LIBRARY:STARLET.MLB'
;
; Define symbols
;
$IODEF ; Define I/O function codes
$QIODEF ; Define QIO definition codes
$SSDEF ; Define the system service status codes
$TRMDEF ; Define itemlist read codes
$TTDEF ; Terminal characteristic definitions
;
; Define macros
;
.SHOW
.MACRO ITEM LEN=0,CODE,VALUE
.WORD LEN
.WORD TRM$_'CODE'
.LONG VALUE
.LONG 0
.ENDM ITEM
.NOSHOW
;
; Declare exit handler control block
;
EXIT_HANDLER_BLOCK:
.LONG 0 ; System uses this for pointer
.LONG EXIT_HANDLER ; Address of exit handler
.LONG 1 ; Argument count for handler
.LONG STATUS ; Destination of status code
STATUS: .BLKL 1 ; Status code from $EXIT
;
; Allocate terminal descriptor and channel number storage
;
TT_DESC:
.ASCID /SYS$INPUT/ ; Logical name of terminal
TT_CHAN:
.BLKW 1 ; TT channel number storage
;
; Define acknowledgment message. This is done right above input buffer
; so that we can concatenate the two together when the acknowledgment
; message is issued.
;
ACK_MSG:
.ASCII <CR> /Following input acknowledged: /
ACK_MSGLEN=.-ACK_MSG ; Calculate length of message
;
; Allocate input buffer
;
IN_BUFLEN = 20 ; Set length of buffer
IN_BUF:
.BLKB IN_BUFLEN ; Allocate character buffer
IN_IOSB:
.BLKQ 1 ; Input I/O status block
;
; Define out-of-band ast character mask
;
CNTRLA_MASK:
.LONG 0
.LONG ^B0010 ; Control A mask
;
; Define old terminal characteristics buffer
;
OLDCHAR_BUF_LEN = 12
OLDCHAR_BUF:
.BLKB OLDCHAR_BUF_LEN
;
; Define new terminal characteristics buffer
;
NEWCHAR_BUF_LEN = 12
NEWCHAR_BUF:
.BLKB NEWCHAR_BUF_LEN
;
; Define carriage control symbols
;
CR=^X0D ; Carriage return
LF=^X0A ; Line feed
;
; Define output messages
;
; Output messages are accessed by indexing into a table of
; longwords with each message described by a message address and
; message length
;
ARRAY: ; Table of message addresses and
; lengths
.LONG 10$ ; First message address
.LONG 15$ ; First message length
.LONG 20$
.LONG 25$
.LONG 30$
.LONG 35$
.LONG 40$
.LONG 45$
;
; Define messages
;
10$: .ASCII <CR>/RED ALERT! RED ALERT!/
15$=.-10$
;
20$: .ASCII <CR>/ALL SYSTEMS GO/
25$=.-20$
;
30$: .ASCII <CR>/WARNING..INTRUDER ALARM/
35$=.-30$
;
40$: .ASCII <CR>/** SYSTEM OVERLOAD **/
45$=.-40$
;
; Static QIO packet for message output using QIO$_G form
;
WRITE_QIO:
$QIO EFN=SYNC_EFN, - ; QIO packet
FUNC=IO$_WRITEVBLK!IO$M_BREAKTHRU!IO$M_REFRESH, -
IOSB=SYNC_IOSB
;
; Declare the required I/O status blocks.
;
SYNC_IOSB:: .BLKQ 1 ; I/O status block for synchronous terminal processing.
;
; Declare the required event flags.
;
ASYNC_EFN:: .BLKL 1 ; Event flag for asynchronous terminal processing.
SYNC_EFN == WRITE_QIO + 4 ; Event flag for sync terminal processing.
TIMER_EFN:: .BLKL 1 ; Event flag for timer processing.
;
; Timer storage
;
WAITIME:
.LONG -10*1000*1000*3,-1 ; 3 second delta time
TIME:
.BLKQ 1 ; Current storage time used for
; random number
.PAGE
.SBTTL START - MAIN ROUTINE
.ENABLE LOCAL_BLOCK
;++
;
; Functional description:
;
; ********************************************************************
;
; Start program
;
; ********************************************************************
;
; The following code performs initialization functions.
; It is assumed that the terminal is already in
; FULL-DUPLEX mode.
;
; NOTE: When doing QIO_S calls, parameters P1 and P3-P6 should be
; passed by value, while P2 should be passed by reference.
;
; Input parameters:
; None
;
; Output parameters:
; None
;
;--
.ENTRY START ^M < >
; Get the required event flags.
PUSHAL ASYNC_EFN
CALLS # 1, G^ LIB$GET_EF ; Get EFN for async terminal operations.
BLBC R0, 10$ ; Error - branch.
PUSHAL SYNC_EFN
CALLS # 1, G^ LIB$GET_EF ; Get EFN for sync terminal operations.
BLBC R0, 10$ ; Error - branch.
PUSHAL TIMER_EFN
CALLS # 1, G^ LIB$GET_EF ; Get EFN for timer operations.
BLBC R0, 10$ ; Error - branch.
; Initialize the terminal characteristics.
$ASSIGN_S DEVNAM=TT_DESC,-; Assign terminal channel using
CHAN=TT_CHAN ; logical name and channel number
BLBC R0, 10$ ; Error - branch.
BSBW CHANGE_CHARACTERISTICS ; Change the characteristics of
; terminal
BSBW ENABLE_CTRLCAST ; Allow Ctrl/C traps
BSBW ENABLE_OUTBANDAST ; Enable Ctrl/A out-of-band AST
BSBW ENABLE_READ ; Queue read
MOVZWL TT_CHAN, WRITE_QIO+8 ; Insert channel into
BRB LOOP ; static QIO packet
10$:
BRW ERROR
;
; This loop outputs a message based on a random number and then
; delays for 3 seconds
;
LOOP:
$GETTIM_S TIMADR=TIME ; Get random time
BLBC R0, 10$ ; Error - branch.
EXTZV #6, #2, TIME, R0 ; Load random bits into switch
MOVQ ARRAY[R0], - ; Load message address
WRITE_QIO+QIO$_P1 ; and size into QIO
; packet
;
; Issue QIO write using packet defined in data area
;
$QIOW_G WRITE_QIO
BLBC R0, 10$ ; QIO error - branch.
MOVZWL SYNC_IOSB, R0 ; Get the terminal driver status.
BLBC R0, 10$ ; Terminal driver error - branch.
;
; Delay for 3 seconds before issuing next message
;
$SETIMR_S EFN=TIMER_EFN,- ; Timer service
DAYTIM=WAITIME ; will set event flag
; in 3 seconds
BLBC R0, 10$ ; Error - branch.
$WAITFR_S EFN=TIMER_EFN ; Wait for event flag
BLBS R0, LOOP ; No error if set
BRB 10$ ; Error - branch.
.DISABLE LOCAL_BLOCK
.PAGE
.SBTTL CHANGE_CHARACTERISTICS - CHANGE CHARACTERISTICS OF TERMINAL
;++
;
; Functional description:
;
; Routine to change the characteristics of the terminal.
;
; Input parameters:
; None
;
; Output parameters:
; R0 - status from $QIO call.
; R1 - R5 destroyed
;
;--
;
CHANGE_CHARACTERISTICS:
$QIOW_S EFN=SYNC_EFN, - ; Get current terminal characteristics
CHAN=TT_CHAN, -
FUNC=#IO$_SENSEMODE, -
IOSB=SYNC_IOSB, -
P1=OLDCHAR_BUF, -
P2=#OLDCHAR_BUF_LEN
BLBC R0, 10$ ; Error if clear
MOVZWL SYNC_IOSB, R0 ; Get the terminal driver status.
BLBC R0, 10$ ; Error - branch
$DCLEXH_S EXIT_HANDLER_BLOCK ; Declare exit handler to reset
; characteristics
BLBC R0, 10$ ; Error - branch.
MOVC3 #OLDCHAR_BUF_LEN, - ; Move old characteristics into
OLDCHAR_BUF, - ; new characteristics buffer
NEWCHAR_BUF
BISL2 #TT$M_NOBRDCST, - ; Set nobroadcast bit
NEWCHAR_BUF+4 ; ...
$QIOW_S EFN=SYNC_EFN, - ; Set current terminal characteristics
CHAN=TT_CHAN, -
FUNC=#IO$_SETMODE, -
IOSB=SYNC_IOSB, -
P1=NEWCHAR_BUF, -
P2=#NEWCHAR_BUF_LEN
BLBC R0, 10$ ; QIO error - branch.
MOVZWL SYNC_IOSB, R0 ; Get the terminal driver status.
BLBC R0, 10$ ; Terminal driver error - branch.
RSB
10$:
BRW ERROR
.PAGE
.SBTTL ENABLE_CTRLCAST - ENABLE Ctrl/C AST
;++
;
; Functional description:
;
; Routine to allow Ctrl/C recognition.
;
; Input parameters:
; None
;
; Output parameters:
; None
;
;--
;
ENABLE_CTRLCAST:
$QIOW_S EFN=SYNC_EFN, -
CHAN=TT_CHAN, -
FUNC=#IO$_SETMODE!IO$M_CTRLCAST, -
IOSB=SYNC_IOSB, -
P1=CTRLCAST, - ; AST routine address
P3=#3 ; User mode
BLBC R0, 10$ ; Error - branch.
MOVZWL SYNC_IOSB, R0 ; Get the terminal driver status.
BLBC R0, 10$ ; Terminal driver error - branch.
RSB
10$:
BRW ERROR
.PAGE
.SBTTL ENABLE_OUTBANDAST - ENABLE Ctrl/A AST
;++
;
; Functional description:
;
; Routine to allow CNTRL/A recognition.
;
; Input parameters:
; None
;
; Output parameters:
; None
;
ENABLE_OUTBANDAST:
$QIOW_S EFN=SYNC_EFN, -
CHAN=TT_CHAN, -
FUNC=#IO$_SETMODE!IO$M_OUTBAND, -
IOSB=SYNC_IOSB, -
P1=CTRLAAST, - ; AST routine address
P2=#CNTRLA_MASK, - ; Character mask
P3=#3 ; User mode
BLBC R0, 10$ ; QIO error - branch.
MOVZWL SYNC_IOSB, R0 ; Get the terminal driver status.
BLBC R0, 10$ ; Terminal driver error - branch.
RSB
10$:
BRW ERROR
.PAGE
.SBTTL ENABLE_READ - QUEUE A READ TO THE TERMINAL.
;++
;
; Functional description:
;
; Routine to queue a read operation to the terminal.
;
; Input parameters:
; None
;
; Output parameters:
; None
;
; Define item list for itemlist read
;
ITEM_LST:
ITEM 0, MODIFIERS, - ; Convert lowercase to
TRM$M_TM_CVTLOW!TRM$M_TM_NOEDIT ; upper and inhibit line
ITEM 6, TERM,MASK_ADDR ; editing
; Set up terminator mask
ITEM_LEN = . - ITEM_LST
MASK_ADDR:
.LONG 1@^XD ; Terminator mask is
; <CR>
.WORD 1@4 ; and "$"ENABLE_READ:
$QIO_S EFN=ASYNC_EFN, - ; Must not be QIOW form or read will block
CHAN=TT_CHAN, - ; process
FUNC=#IO$_READVBLK!IO$M_EXTEND, -
IOSB=IN_IOSB, -
ASTADR=READAST, - ; AST routine to execute
P1=IN_BUF, - ; on
P2=#IN_BUFLEN, -
P5=#ITEM_LST, - ; Itemlist read address
P6=#ITEM_LEN ; Itemlist read size
BLBC R0, 10$ ; QIO error - branch.
; The queued read operation will not affect write operations due
; to the fact that breakthru has been set for the write operations.
RSB
10$:
BRW ERROR
.PAGE
.SBTTL READAST - AST ROUTINE FOR READ COMPLETION
.ENABLE LOCAL_BLOCK
;++
;
; Functional description:
;
; AST routine to execute on read completion.
;
; Input parameters:
; None
;
; Output parameters:
; None
;
;--
;
10$:
MOVZWL IN_IOSB, R0 ; Get the terminal driver status
20$:
BRW ERROR ; Exit with error status.
.ENTRY READAST ^M < R2, R3, R4, R5 > ; Procedure entry mask
BLBC IN_IOSB, 10$ ; Terminal driver error - branch
MOVZWL IN_IOSB+2, R0 ; Get number of characters read into R0
ADDL2 #ACK_MSGLEN, R0 ; Add size of fixed acknowledge message
$QIO_S EFN=ASYNC_EFN, - ; Issue acknowledge message
CHAN=TT_CHAN, - ; Note, ACK must be asynchronous (QIO)
FUNC=#IO$_WRITEVBLK, - ; and the terminal driver write status
P1=ACK_MSG, - ; is ignored (no IOSB and AST routine).
P2=R0 ; Specify IOSB and AST routine if output
; must be displayed on the terminal.
BLBC R0, 20$ ; QIO error - branch
;
; Process read message
;
; .
; .
; .
;(user-provided code to decode command inserted here)
; .
; .
; .
BSBW ENABLE_READ ; Queue next read
RET ; Return to mainline loop
.DISABLE LOCAL_BLOCK
.PAGE
.SBTTL CTRLAAST - AST ROUTINE FOR Ctrl/A
.SBTTL CTRLCAST - AST ROUTINE FOR Ctrl/C
.SBTTL ERROR - EXIT ROUTINE
;++
;
; Functional description:
;
; AST routine to execute when Ctrl/C or Ctrl/A is entered.
;
; Input parameters:
; None
;
; Output parameters:
; None
;
CTRLCAST::
CTRLAAST::
.WORD ^M < > ; Procedure entry mask
MOVL #SS$_NORMAL, R0 ; Put success in R0
ERROR::
$EXIT_S R0 ; Exit
RSB
.PAGE
.SBTTL EXIT_HANDLER - EXIT HANDLER ROUTINE
;++
;
; Functional description:
;
; Exit handler routine to execute when image exits. It cancels
; any outstanding I/O on this channel and resets the terminal
; characteristics to their original state.
;
; Input parameters:
; None
;
; Output parameters:
; None
;
;--
;
.ENTRY EXIT_HANDLER ^M< >
$CANCEL_S CHAN=TT_CHAN ; Flush any I/O on queue
$QIOW_S EFN=SYNC_EFN, - ; Reset terminal characteristics
CHAN=TT_CHAN, -
FUNC=#IO$_SETMODE, -
IOSB=SYNC_IOSB, -
P1=OLDCHAR_BUF, -
P2=#OLDCHAR_BUF_LEN
BLBC R0, 10$ ; QIO error - branch.
MOVZWL SYNC_IOSB, R0 ; Get the terminal driver status.
10$:
RET
.END START
|
|
The MACRO 32 program READ_VERIFY.MAR (Example 5-3) shows the read verify
function. The program shows a typical build of itemlists (both the
right and left fields), channel assignment, a right- and left-justified
read verify operation, and then the read QIO operation. Example 5-3 READ_VERIFY.MAR Terminal Driver Programming Example |
.TITLE READ_VERIFY - Read Verify Coding Example
.IDENT 'V05-000'
.SBTTL DECLARATIONS
.DISABLE GLOBAL
;
; Declare the external system routines and MACRO libraries.
;
.EXTERNAL LIB$GET_EF
.EXTERNAL SCR$ERASE_PAGE
.LIBRARY 'SYS$LIBRARY:LIB.MLB'
.LIBRARY 'SYS$LIBRARY:STARLET.MLB'
;
; Include files:
;
$IODEF
$TRMDEF
;
; Macros:
;
.MACRO ITEM LEN=0,CODE,VALUE
.WORD LEN
.WORD TRM$_'CODE'
.LONG VALUE
.LONG 0
.ENDM ITEM
;
; Equated symbols:
;
INBUF_LEN = 20
ESC = ^X1B
;
; Own storage:
;
; Build item lists for the read verify QIO
;
;
; Right-justified field
;
R_ITEM_LIST:
ITEM CODE = MODIFIERS, -
VALUE = TRM$M_TM_R_JUST ; Right justify
ITEM CODE = EDITMODE, -
VALUE = TRM$K_EM_RDVERIFY ; Enable read verify
ITEM CODE = PROMPT, -
VALUE = R_PROMPT_ADDR, -
LEN = R_PROMPT_LEN ; Set up prompt
ITEM CODE = INISTRNG, -
VALUE = R_INISTR_ADDR, -
LEN = R_INISTR_LEN ; Set up initial string
ITEM CODE = INIOFFSET, -
VALUE = R_INISTR_LEN
ITEM CODE = PICSTRNG, -
VALUE = R_PICSTR_ADDR, -
LEN = R_PICSTR_LEN ; Set up picture string
ITEM CODE = FILLCHR, -
VALUE = <^A/* /> ; clear = *, fill = space
R_ITEM_LIST_LEN = .-R_ITEM_LIST
R_PROMPT_ADDR:
.ASCII /[12;12H$/
R_PROMPT_LEN = .-R_PROMPT_ADDR
R_INISTR_ADDR:
.ASCII / , /
R_INISTR_LEN = .-R_INISTR_ADDR
MASK = TRM$M_CV_NUMERIC!TRM$M_CV_NUMPUNC
R_PICSTR_ADDR:
.BYTE MASK
.BYTE MASK
.BYTE MASK
.BYTE 0 ; Marker character
.BYTE MASK
.BYTE MASK
.BYTE MASK
R_PICSTR_LEN = .-R_PICSTR_ADDR
;
; Left-justified field
;
L_ITEM_LIST:
ITEM CODE = MODIFIERS, -
VALUE = TRM$M_TM_CVTLOW!TRM$M_TM_AUTO_TAB
; Upcase input and
; complete on field full
ITEM CODE = EDITMODE, -
VALUE = TRM$K_EM_RDVERIFY ; Enable read verify
ITEM CODE = PROMPT, -
VALUE = L_PROMPT_ADDR, -
LEN = L_PROMPT_LEN ; Set up prompt
ITEM CODE = INISTRNG, -
VALUE = L_INISTR_ADDR, -
LEN = L_INISTR_LEN ; Set up initial string
ITEM CODE = INIOFFSET, -
VALUE = 0
ITEM CODE = PICSTRNG, -
VALUE = L_PICSTR_ADDR, -
LEN = L_PICSTR_LEN ; Set up picture string
ITEM CODE = FILLCHR, -
VALUE = <^A/* /> ; clear = *, fill = space
L_ITEM_LIST_LEN = .-L_ITEM_LIST
L_PROMPT_ADDR:
.ASCII /[13;12H Enter Date: /
L_PROMPT_LEN = .-L_PROMPT_ADDR
L_INISTR_ADDR:
.ASCII / - - /
L_INISTR_LEN = .-L_INISTR_ADDR
MASK1 = TRM$M_CV_NUMERIC
MASK2 = TRM$M_CV_UPPER!TRM$M_CV_LOWER
L_PICSTR_ADDR:
.BYTE MASK1
.BYTE MASK1
.BYTE 0 ; Marker character
.BYTE MASK2
.BYTE MASK2
.BYTE MASK2
.BYTE 0 ; marker character
.BYTE MASK1
.BYTE MASK1
L_PICSTR_LEN = .-L_PICSTR_ADDR
IN_IOSB: .BLKL 2
TT_CHAN: .BLKW 1
INBUF: .BLKB INBUF_LEN
SYSINPUT: .ASCID /SYS$INPUT/
SYNC_EFN: .BLKL 1
.PAGE
.ENTRY READ_VERIFY ^M < >
;
; Get the required event flags.
;
PUSHAL SYNC_EFN
CALLS # 1, G^ LIB$GET_EF
BLBC R0, ERROR ; Error - branch
;
; Assign the channel to SYS$INPUT
;
$ASSIGN_S -
CHAN = TT_CHAN -
DEVNAM = SYSINPUT ; SYS$INPUT
BLBC R0, ERROR ; Branch on error
;
; Clear the screen
;
CLRQ -(SP)
CALLS #2, G^ SCR$ERASE_PAGE
BLBC R0, ERROR
;
; Do the right-justified read operation
;
PUSHL #R_ITEM_LIST_LEN
PUSHAB R_ITEM_LIST
CALLS #2, DO_READ
BLBC R0, ERROR
;
; Do the left-justified read operation
;
PUSHL #L_ITEM_LIST_LEN
PUSHAB L_ITEM_LIST
CALLS #2, DO_READ
BLBC R0, ERROR
ERROR:
RET
.PAGE
;++
;
; DO_READ - do the actual QIO
;
; Inputs:
;
; 4(AP) the address of the itemlist
; 8(AP) the length of the itemlist
;
;--
.ENTRY DO_READ, ^M
|
|
|
$QIOW_S EFN=SYNC_EFN, -
CHAN = TT_CHAN, -
FUNC = #$_READVBLK!IO$M_EXTEND>, -
IOSB = IN_IOSB, -
p1 = inbuf, -
p2 = #inbuf_len, -
p5 = 4(AP), -
P6 = 8(AP)
BLBC R0, 10$ ; QIO error - branch
MOVZWL IN_IOSB, R0 ; Get the terminal driver status.
BLBC R0, 10$ ; Terminal driver error - branch
; Handle the input...
10$:
RET
.END READ_VERIFY
|
Example 5-4 LIB$XXABLE_CTRL.C Terminal Driver Programming Example |
//Demonstrates CTRL-Y and CTRL-C handling under OpenVMS,
//as well as
//some basic dynamic string descriptor operations and a few other
//string-related operations.
////To build and use:
//$ CC/DECC LIB$XXABLE_CTRL
//$ LINK LIB$XXABLE_CTRL
//$ RUN LIB$XXABLE_CTRL
/*#include <descrip.h>>#include <iodef.h>>
#include <libclidef.h>>#include <lib$routines.h>>
#include <ssdef.h>>#include <starlet.h>>
#include <stdio.h>>#include <stsdef.h
>>void CtrlYHandler()*/
{
int RetStat;
$DESCRIPTOR( Y, "<CTRL/Y>>was detected" );
RetStat = lib$put_output( );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return;
RetStat = lib$enable_ctrl( $M_CLI_CTRLY );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return;
return;
}
void CtrlCHandler()
{
int RetStat;
$DESCRIPTOR( Y, "<CTRL/C>>was detected" );
RetStat = lib$put_output( );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return;
RetStat = lib$enable_ctrl( $M_CLI_CTRLY );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return;
return;
}
main()
{
int RetStat;
unsigned short int IOChan;
unsigned short int GotLen;
struct dsc$descriptor GotDsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL };
$DESCRIPTOR( Prompt, "Enter CTRL/Y, CTRL/C, or any characters and RETURN:" );
$DESCRIPTOR( Exiting, "Exiting" );
$DESCRIPTOR( TTDsc, "TT:");
RetStat = lib$disable_ctrl( $M_CLI_CTRLY );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = sys$assign( , , 0, 0 );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = sys$qiow( 0, IOChan, IO$_SETMODE|IO$M_CTRLYAST, 0, 0, 0,
CtrlYHandler, 0, 0, 0, 0, 0 );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = sys$qiow( 0, IOChan, IO$_SETMODE|IO$M_CTRLCAST, 0, 0, 0,
CtrlCHandler, 0, 0, 0, 0, 0 );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = lib$get_input( , , );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = sys$dassgn( IOChan );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = lib$enable_ctrl( $M_CLI_CTRLY );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = lib$put_output( );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
RetStat = lib$sfree1_dd( );
if (!$VMS_STATUS_SUCCESS( RetStat ) )
return RetStat;
return SS$_NORMAL;
}
|
|
|