Document revision date: 30 March 2001

## OpenVMS I/O User's Reference Manual

Previous | Contents | Index

## 5.6 Terminal Driver Programming Examples

The VAX 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_SETMODE
- IO$M_LT_CONNECT (on forward port)
- IO$M_LT_SENSEMODE

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 <descrip> /* VMS Descriptor Definitions      */
    #include <iodef>   /* I/O Function Codes Definitions  */
    #include <latdef>  /* LAT Definitions                 */
    #include <ssdef>   /* System Service Return Status    */
                                            /* Code Definitions                */
    #include <ttdef>   /* Terminal Characteristics        */
    #include <tt2def>  /* 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, &ExitConditionValue };

    /*
    ** 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(&ExitHandlerBlock)) & 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(&LTAtemplateDSC, &LTAchannel, 0, 0)) & 1))
                    lib$signal(status);
            if (!((status = sys$assign(&TTchannelDSC, &TTchannel, 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,
                                &LTA_QIOiosb, 0, 0,
                                &PortSetmodeItemlist,
                                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,
                                &LTA_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,
                                &LTA_QIOiosb,
                                LTAreadChannelAST, 0,
                                LTAbuffer,
                                1, 0, &ReadTerminatorMask, 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,
                                &LTA_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,
                                &LTA_QIOiosb, 0, 0,
                                &deviceChar,
                                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,
                                &LTA_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,
                                &LTA_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,
                                &TT_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,
                                &LTA_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,
                                        &LTA_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,
                                &TT_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,
                                &LTA_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,
                                &TT_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,
                                &LTA_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 VAX MACRO 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><LF> /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><LF>/RED ALERT! RED ALERT!/
    15$=.-10$
    ;
    20$:    .ASCII            <CR><LF>/ALL SYSTEMS GO/
    25$=.-20$
    ;
    30$:    .ASCII            <CR><LF>/WARNING..INTRUDER ALARM/
    35$=.-30$
    ;
    40$:    .ASCII            <CR><LF>/** 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

Previous | Next | Contents | Index

| privacy and legal statement |
6136PRO_024.HTML