Google
 

Trailing-Edge - PDP-10 Archives - BB-P363B-SM_1985 - t20/nmlt20/nmllbk.bli
There are 2 other files named nmllbk.bli in the archive. Click here to see a list.
! UPD ID= 341, SNARK:<6.1.NML>NMLLBK.BLI.13,  20-Aug-85 12:58:07 by MCCOLLUM
!  In CIRCUIT_ACCESS, change the length of the loopback message to 31
!   bytes if it is less than that. The LLMOP% jsys should be responsible
!   for padding the message, but since it doesn't do it, we will.
!
! UPD ID= 338, SNARK:<6.1.NML>NMLLBK.BLI.12,  23-Jul-85 14:24:30 by MCCOLLUM
!  If CD_MORE_MOP_RESPONSES is set in the CD_BLOCK on return from a
!   NMU$DLX_READ in LOOP_CIRCUIT, keep reading MOP responses. This will
!   prevent DECnet free space from filling with unread responses to a
!   LOOP CIRCUIT NI-0-0 done on the multicast.
!
! UPD ID= 258, SNARK:<6.1.NML>NMLLBK.BLI.11,  10-Feb-85 14:49:28 by GLINDELL
!  Remove definitions of STATE/SUBSTATE/SERVICE values.
!  Change all LINK_ to CIRCUIT_
!
! UPD ID= 255, SNARK:<6.1.NML>NMLLBK.BLI.10,   8-Feb-85 15:16:51 by PALMIERI
! Return updated byte pointer in NODE_ACCESS when loop length exceeded
! UPD ID= 206, SNARK:<6.1.NML>NMLLBK.BLI.9,  10-Dec-84 15:27:17 by HALPIN
! Get MONSYM Library file out of default directory, not BLI:
!
! UPD ID= 180, SNARK:<6.1.NML>NMLLBK.BLI.8,   7-Dec-84 08:21:44 by HALPIN
! Add PSI arguemts in call to $NMU_DLX_OPEN.
!
! UPD ID= 153, SNARK:<6.1.NML>NMLLBK.BLI.7,   1-Nov-84 15:26:39 by GUNN
! Fix dot '.' bug in code which builds PHYSICAL ADDRESS. Found in
! debugging NMLDTL.
!
! UPD ID= 149, SNARK:<6.1.NML>NMLLBK.BLI.6,  30-Oct-84 17:38:58 by GUNN
! Ident 40.
!  In LOOP_CIRCUIT, leave response pointer pointing to end of
!     response message built by NMU$DLX_READ. This prevents
!     higher level routines from overwriting the response built
!     by NMU$KLNI_READ which contains the PHYSICAL ADDRESS parameter
!     of the Ethernet node responding to a multicast loop request.
!
! UPD ID= 140, SNARK:<6.1.NML>NMLLBK.BLI.5,  29-Oct-84 11:14:42 by GUNN
! Ident 39.
!  Make LOOP CIRCUIT NI-0-0 NODE foobar work.
!    Fix GET_LOOP_PARAMETERS to set up defaults based on entity. LOOP NODE
!    parameters COUNT, LENGTH, and WITH are EXECUTOR NODE parameters.
!    Change calls to NML$GET_VDB_PARAMETER to use the LOCAL (EXECUTOR)
!    node id, not the LOOP entity as it had done.
!    Add PB_HARDWARE_ADDR field to PB.
!    Add code to produce Ethernet physical address from node number.
!    Get HARDWARE ADDRESS parameter from VDB if NODE parameter is included.
!
! UPD ID= 134, SNARK:<6.1.NML>NMLLBK.BLI.4,  19-Oct-84 16:01:54 by GUNN
! Ident 38.
!  Fix GET_LOOP_PARAMETERS to ensure that PHYSICAL ADDRESS and 
!  LOOP ASSISTANT PHYSICAL ADDRESS parameters are HI-6 fields.
!
! UPD ID= 113, SLICE:<6.1.NML>NMLLBK.BLI.3,  24-Sep-84 10:19:05 by GUNN
!
! Ident 37.
!  Fix zero length response message bug. Code that calls CH$DIFF to set
!  RB_RESPONSE_LENGTH in NML$TEST had pointers in wrong order.
!
!  Change all routine declarations to use %routine macro.
!
! UPD ID= 88, SLICE:<6.1.NML>NMLLBK.BLI.2,  18-Sep-84 15:05:07 by GUNN
!
! Ident 36.
!  Add code to handle returning Ethernet Physical Address in response
!  message.
!
! WORK:<GUNN.NML>NMLLBK.BLI.25 21-Aug-84 12:10:22, Edit by GUNN
!
! Ident 35.
!  Add support for Ethernet LOOP CIRCUIT. Change name of some routines
!  they are misleading. Change:  LOOPBACK_ACCESS to NODE_ACCESS
!                                LINE_ACCESS     to CIRCUIT_ACCESS
!                                LOOP_LINE       to LOOP_CIRCUIT
!
! The new names more nearly reflect the function of the routines.
!
! Change to accomodate new LIBRARY conventions. MONSYM.L36 and JLNKG.L36
! are now explicity declared here rather than in NMULIB.
!
!<MCINTEE.WORK>NMLLBK.BLI.2, 14-Jun-83 14:07:59, Edit by MCINTEE
!
! Ident 34.
!  New style entity type names
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.2  4-Oct-82 07:39:16, Edit by PECKHAM
!
! Ident 33.
!   Fix bug introduced to LOOP_LINE by last edit.
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.2  3-Oct-82 14:59:41, Edit by PECKHAM
!
! Ident 32.
!   It was found that in-house VAXes returned a MOP loopback response code
!   of 25 - it will take too long to get them to fix it, so accomodate it.
!   Hack up LOOP_LINE to recognize it as valid message.
!
! <BRANDT.DEVELOPMENT>NMLLBK.BLI.1 14-Sep-82 11:43:23, Edit by BRANDT
!
! Ident 31.
!   In NML$TEST, include an entity id in the response message.  Include
!   the data field only for specific errors.
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.2 23-Jun-82 11:37:06, Edit by PECKHAM
!
! Ident 30.
! Check RB_PRV_* bits in request block before performing requests.
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.3 14-Jun-82 13:43:13, Edit by PECKHAM
!
! Ident 29.
! Handle the MOP prompt message (ENTER MOP MODE) as a one byte message.
! This conforms to the VMS/RSX agreed-upon protocol.
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.2 22-May-82 17:37:27, Edit by PECKHAM
!
! Ident 28.
! Check CIRCUIT STATE, SUBSTATE, and SERVICE in LINE_ACCESS
! for service interlocks.
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.2 10-May-82 15:54:38, Edit by PECKHAM
!
! Ident 27.
! Fix linkage for BLISS-36
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.13  5-May-82 11:09:50, Edit by PECKHAM
!
! Ident 26.
! Disallow LOOP LINE in NML$TEST.
! Add Loop Request Parameter Block and use (for better code efficiency).
! Optimize linkages around the parameter block.
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.2 28-Apr-82 12:45:27, Edit by PECKHAM
!
! Ident 25.
! Change NMU$DLX_* routine references to $NMU_DLX_* macro references.
! Add receive timeout values where needed.
!
! NET:<PECKHAM.DEVELOPMENT>NMLLBK.BLI.7  8-Mar-82 13:36:45, Edit by PECKHAM
!
! Ident 24.
! Do not send ENTER MOP MODE message on LOOP LINE initialization
! in LINE_ACCESS. Add retry code in LOOP_LINE.
!
! NET:<BRANDT.DEVELOP>NMLLBK.BLI.1 14-Dec-81 16:43:23, Edit by BRANDT
!
! Ident 23.
!   Change call to NML$SET_VDB_PARAMETER so that the address of the
!   data is passed, not the value.  Add literal defs from NMLDLW for
!   circuit parameter numbers and substate values.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.4 21-Oct-81 09:13:23, Edit by WEBBER
!
! Ident 22.
! Modify interface to NETWORK_OPEN to pass the response buffer and its
! length.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.3 16-Jun-81 17:05:02, Edit by GUNN
!
! Ident 21.
! Make check for looped message in LINE_ACCESS allow both
! function code LOOPBACK_TEST and LOOPED_DATA. 
! Allow for reflected ENTER-MOP-MODE message.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.15 16-Jun-81 12:30:15, Edit by GUNN
!
! Ident 20.
! Fix response handling in LOOP_LINE.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.12 15-Jun-81 10:04:26, Edit by GUNN
!
! Ident 19.
! Fix retrieval of response code from response buffer after calling
! LINE_ACCESS.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.8 12-Jun-81 10:50:01, Edit by GUNN
!
! Ident 18.
! Change default LENGTH parameter to 127. Makes default work with 
! DECnet-M.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.7 11-Jun-81 17:45:07, Edit by GUNN
!
! Ident 17.
! Build response message if LINE_ACCESS fails.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.6 11-Jun-81 16:07:08, Edit by GUNN
!
! Ident 16.
! Release MOP buffer in LINE_ACCESS.
! Put call to NML$SET_VDB_PARAMETER in comment.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.5  8-Jun-81 10:16:54, Edit by GUNN
!
! Ident 15.
! Add code in LINE_ACCESS to write ENTER-MOP-MODE message and
! read MOP-MODE-RUNNING message. 
! Add code to handle read errors.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.12 28-May-81 10:45:42, Edit by GUNN
!
! Ident 14.
! Set RB_RESPONSE_LENGTH from response message buffer after calling 
! LINE_ACCESS.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.11 28-May-81 09:23:50, Edit by GUNN
!
! Ident 13.
! Change test for completion of NMU$DLX_OPEN to geq 0.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.10 28-May-81 06:21:35, Edit by GUNN
!
! Ident 12.
! Add definition for RESPONSE_BUFFER_ALLOCATION.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.9 28-May-81 06:11:12, Edit by GUNN
!
! Ident 11.
! Allocate a response message buffer in NML$TEST.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.8 27-May-81 15:58:49, Edit by GUNN
!
! Ident 10.
! Add external declarations for NMU$DLX_xxx routines.
! Remove reference to structure REQ in LOOP_NODE and LOOP_LINE.
! Pass repsonse pointer instead.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.6 27-May-81 15:03:15, Edit by GUNN
!
! Ident 09.
! Add code to perform loop line/circuit.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.5 31-Mar-81 07:59:24, Edit by GUNN
!
! Release send and receive buffers in LOOP_NODE.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.3 11-Mar-81 08:57:37, Edit by GUNN
!
! Change format of node id string passed to NMU$NETWORK_OPEN in the
! Connect Block CB_HOST field to be same as NODE entity id.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.2  9-Mar-81 08:54:21, Edit by GUNN
!
! Change test loop limit so extra loop not taken.
! Change default length to 255.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.7 13-Feb-81 10:06:16, Edit by GUNN
!
! Add CIRCUIT entity in NML$TEST.
!
! NET:<DECNET20-V3P1.NML>NMLLBK.BLI.6 12-Feb-81 15:27:19, Edit by GUNN
!
! Update copyright date.
! Change GET_LOOP_PARAMETERS to return NICE error return code on first
! invalid parameter detected.
! Change GENERATE_LOOP_MESSAGE to return NICE error code if LOOP WITH
! parameter value is invalid.
!
! NET:<DECNET20-V3P1.BASELEVEL-2.SOURCES>NMLLBK.BLI.36 10-Feb-81 13:49:06, Edit by JENNESS
!    Make sure that connect data has zero length in connect block!!
! NET:<DECNET20-V3P1.BASELEVEL-2.SOURCES>NMLLBK.BLI.35  9-Feb-81 12:43:12, Edit by GUNN
!    Remove check for version skew.
! NET:<DECNET20-V3P1.BASELEVEL-2.SOURCES>NMLLBK.BLI.33  3-Feb-81 10:34:00, Edit by JENNESS
!    Remove literal definition for MIRROR_OBJECT ... now in NMARCH.
%title 'NMLLBK -- Network Management Loopback Processor'
module NMLLBK	(
		ident = 'X04.40'
		) =
begin

!
!			  COPYRIGHT (c) 1980, 1981, 1984 BY
!	      DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
!
! THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
! ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH  LICENSE AND WITH THE
! INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR  ANY  OTHER
! COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
! OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
! TRANSFERRED.
!
! THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE  WITHOUT  NOTICE
! AND  SHOULD  NOT  BE  CONSTRUED  AS  A COMMITMENT BY DIGITAL EQUIPMENT
! CORPORATION.
!
! DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF  ITS
! SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
!

!++
! FACILITY:	DECnet-10/20 V4.0 Network Management Layer (NML)
!
! ABSTRACT:
!
!	Performs loopback test function.
!
! ENVIRONMENT:	TOPS-10/20 & MCB/RSX11 User mode under NML
!
! AUTHOR: Dale C. Gunn , CREATION DATE: 12-Nov-80
!
! MODIFIED BY:
!
!	, : VERSION
! 01	-
!--

!
! INCLUDE FILES:
!

library 'NMLLIB' ;                      ! All required definitions

library 'MONSYM';			! Monitor symbols

library 'JLNKG';			! JSYS linkage definitions

require 'NMLEXT';                       ! External routine declarations

!
! TABLE OF CONTENTS
!

%if %bliss (bliss16)
%then linkage NML_LKG_PB = jsr (register = 5);
%else macro NML_LKG_PB = bliss36c %;
%fi

forward routine
         NML$TEST,                      ! Perform loopback test
         GET_LOOP_PARAMETERS: NML_LKG_PB, ! Get parameters from NICE test msg
         NODE_ACCESS: NML_LKG_PB,       ! NODE loopback access routine
         CIRCUIT_ACCESS: NML_LKG_PB,    ! CIRCUIT loopback access routine
         LOOP_NODE: NML_LKG_PB,         ! Perform NODE loop test
         LOOP_CIRCUIT: NML_LKG_PB,      ! Perform CIRCUIT loop test
         GENERATE_LOOP_MESSAGE: NML_LKG_PB, ! Build LOOP data message
         VERIFY_LOOP_MESSAGE: NML_LKG_PB; ! Check validity of LOOPED message

!
! MACROS:
!

macro
     CH$HEX [ ] =
         ch$ptr (uplit (X_CHAR(%explode(%remaining))),,8) %;

macro
     X_CHAR [B1,B2,B3,B4,B5,B6,B7,B8] =
            ((%X B1 ^ (%bpval-4))
              %if not %null(B2)
              %then
                   or (%X B2 ^ (%bpval-8))
              %if not %null(B3)
              %then
                   or (%X B3 ^ (%bpval-12))
              %if not %null(B4)
              %then
                   or (%X B4 ^ (%bpval-16))
              %if not %null(B5)
              %then
                   or (%X B5 ^ (%bpval-20))
              %if not %null(B6)
              %then
                   or (%X B6 ^ (%bpval-24))
              %if not %null(B7)
              %then
                   or (%X B7 ^ (%bpval-28))
              %if not %null(B8)
              %then
                   or (%X B8 ^ (%bpval-32))
              %fi %fi %fi %fi %fi %fi %fi) %;

!
! EQUATED SYMBOLS:
!
! Loop Request Block
!
$show (fields);                         ! Turn on XPORT field display
$show (literals);                       ! Turn on XPORT literal display

$field
    PARAMETER_FIELDS =
        set
        PB_BASE = [$sub_block (0)],
        PB_RESPONSE_PTR = [$pointer],
        PB_REQ_ADR = [$address],
        PB_LINK_HANDLE = [$short_integer],
        PB_ASST_NODE =                  ! Node parameter 156
                      [$sub_block (NODE_ID_BUFFER_LENGTH)],
        PB_ASST_PHY_ADDR = [$pointer],  ! Node parameter 153
        PB_COUNT = [$integer],          ! Node parameter 150
        PB_HARDWARE_ADDR = [$pointer],  ! Node parameter 114
        PB_HELP = [$integer],           ! Node parameter 154
        PB_LENGTH = [$integer],         ! Node parameter 151
        PB_NODE =                       ! Node parameter 155
                  [$sub_block (NODE_ID_BUFFER_LENGTH)],
        PB_PHY_ADDR = [$pointer],       ! Node parameter  10
        PB_WITH = [$integer]            ! Node parameter 152
        tes;

literal
    PARAMETER_BLOCK_SIZE = $field_set_size,
    PARAMETER_BLOCK_ALLOCATION = $field_set_units;

macro
     PARAMETER_BLOCK = block [PARAMETER_BLOCK_SIZE] field (PARAMETER_FIELDS) %;

$show (none);                           ! Turn off XPORT display

!
! Devices we need defined.
!

literal
       DEVTYP_NI = 15,                  ! NI Device Type code
       DEVTYP_DTE = 20;                 ! DTE Device type code

!
! STATE/SUBSTATE/SERVICE definitions
!

literal
    CPARM_STATE = 0,
    CPARM_SUBSTATE = 1,
    CPARM_SERVICE = 100;

literal
       RESPONSE_BUFFER_LENGTH = 76,     ! NICE response buffer in bytes
       RESPONSE_BUFFER_SIZE = ch$allocation (RESPONSE_BUFFER_LENGTH,8),
       RESPONSE_BUFFER_ALLOCATION = RESPONSE_BUFFER_SIZE * %upval ;

bind
       LOOP_ASST_MULTICAST = CH$HEX ('CF0000000000'),
       DEFAULT_PHY_ADDR = LOOP_ASST_MULTICAST;

literal
       HELP_FULL = 2;

literal                                 ! Defaults for test
       DEFAULT_LENGTH = 127,            ! Length of test data in bytes
       DEFAULT_COUNT = 1,               ! Same as RSX and VAX
       DEFAULT_WITH = 2,                ! MIXED ones and zeroes
       DEFAULT_HELP = HELP_FULL;        ! Both XMT & RCV assistance

!
! OWN STORAGE:
!

!
! EXTERNAL REFERENCES:
!

external
        NMLVER,                         ! Network Managment version number
        DECECO,                         ! Digital ECO number
        USRECO ;			! User ECO number
        
external routine
         NML$NICE_RESPONSE,
         NML$CLR_VDB_PARAMETER,
         NML$GET_VDB_PARAMETER,
         NML$SET_VDB_PARAMETER,
         NMU$NETWORK_UTILITIES;
%global_routine ('NML$TEST', REQ) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine performs the NICE test request function. Either
!       a NODE or CIRCUIT loopback test is performed based on the
!       entity type in the test request message. If the NODE loop test
!       is requested the NICE message may specify optional access
!       control information to be used when connecting to the remote
!       loopback mirror. Optional test parameters may also be included.
!
! FORMAL PARAMETERS
!
!	REQ - The address of the NML Request Block.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!	NICE return code.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
       REQ: ref REQUEST_BLOCK;          ! NICE RB structure

    local
         PB: PARAMETER_BLOCK,           ! For centralized data base
         RTN_COD;                       ! NICE return code

    selectone .REQ[RB_NICE_ENTITY_TYPE] of
        set
        [ENTITY_CIRCUIT, ENTITY_LINE] : ! Too dangerous

            if not .REQ[RB_PRV_SERVICE] then return NICE$_PRV;

        [otherwise]:                    ! Okay
            0;
        tes;
    !
    ! Allocate NICE response buffer.
    !

    REQ[RB_RESPONSE_ALLOCATION] = RESPONSE_BUFFER_ALLOCATION;
    REQ[RB_RESPONSE] = NMU$MEMORY_GET (.REQ[RB_RESPONSE_ALLOCATION]);

    !
    ! Set up the parameter block
    !

    PB [PB_REQ_ADR] = .REQ;             ! Get Request Block address
    REQ[RB_RESPONSE_LENGTH] = 0;        ! Initialize response length to zero 

    PB [PB_RESPONSE_PTR] = ch$ptr (.REQ[RB_RESPONSE],, 8);

    !
    ! Get the LOOP test parameters from NICE message.
    !

    if (RTN_COD = GET_LOOP_PARAMETERS (PB[PB_BASE])) lss 0
    then return .RTN_COD ;

    !
    ! Perform either NODE or CIRCUIT loop test based on entity
    !

    begin
    local PBBASE;
    PBBASE = PB[PB_BASE];
    selectone .REQ[RB_NICE_ENTITY_TYPE] of
        set
        [ENTITY_NODE]:                  ! NODE loopback test
            RTN_COD = NODE_ACCESS (.PBBASE); ! Call loopback access routine
        [ENTITY_CIRCUIT]:               ! CIRCUIT loopback test
            RTN_COD = CIRCUIT_ACCESS (.PBBASE); ! Call circuit access routine
        [ENTITY_LINE]:
            RTN_COD = NICE$_UFO;
        [otherwise]:                    ! Internal error
            RTN_COD = NICE$_MPE;
        tes;
    end;
    if .RTN_COD lss 0
    then begin
         local EID_PTR, EID_LEN;

         ! If the preceding entity access routine did not build a NICE
         ! response message using the PB_RESPONSE_PTR, build a standard
         ! response using the RTN_COD value returned from the access routine.

         if ch$diff (.PB[PB_RESPONSE_PTR],ch$ptr (.REQ[RB_RESPONSE],,8)) leq 0
         then
             PB[PB_RESPONSE_PTR] = ch$plus (.PB[PB_RESPONSE_PTR],
                                            $RESPONSE (.PB[PB_RESPONSE_PTR],
                                                       .RTN_COD));

         ! PB_RESPONSE_PTR now points to the ENTITY ID field of the NICE
         ! response message. Build the ENTITY ID into the response message.

         EID_PTR = .REQ[RB_NICE_ENTITY]; ! include an entity ID
         if .REQ[RB_NICE_ENTITY_TYPE] eql NODE_E
         then				! NODE has special format
                begin
                local
                    NODE_ID_PTR,
                    LOCAL_NODE_PTR;

                LOCAL_NODE_PTR = NMU$NETWORK_LOCAL (); ! Local node id
                NODE_ID_PTR = .PB[PB_RESPONSE_PTR]; ! Make copy of current pointer

                EID_LEN = ch$rchar (ch$plus (.EID_PTR,2)) + 3;
                PB[PB_RESPONSE_PTR] = ch$move (.EID_LEN,
                                               .EID_PTR,
                                               .PB[PB_RESPONSE_PTR]);
                !
                ! If node addresses are equal this is the executor node,
                ! so set bit 7 in length byte to indicate this fact.
                !
                if GETB (NODE_ID_PTR) eql GETB (LOCAL_NODE_PTR)
                then if GETB (NODE_ID_PTR) eql GETB (LOCAL_NODE_PTR)
                     then ch$wchar ((ch$rchar (.NODE_ID_PTR) or (1 ^ 7)), .NODE_ID_PTR);

                end
         else
                begin
                EID_LEN = ch$rchar (.EID_PTR) + 1;
                PB[PB_RESPONSE_PTR] = ch$move (.EID_LEN,
                                               .EID_PTR,
                                               .PB[PB_RESPONSE_PTR]);
                end;

         selectone .RTN_COD of          ! Include TEST DATA field in response
             set                        ! message, for appropriate errors
             [$NICE$ERR_LCE,
              $NICE$ERR_BLR,
              $NICE$ERR_OPF,
              $NICE$ERR_IPV]:

                  PUTW ((PB[PB_COUNT]),PB[PB_RESPONSE_PTR]);

             [otherwise]:               ! No data field supplied
		 0;
             tes;
         end
    else begin

         ! If the preceding entity access routine did not build a NICE
         ! response message using the PB_RESPONSE_PTR, build a standard
         ! response using the NICE success return code.

         if ch$diff (.PB[PB_RESPONSE_PTR],ch$ptr (.REQ[RB_RESPONSE],,8)) leq 0
         then
             PB[PB_RESPONSE_PTR] = ch$plus (.PB[PB_RESPONSE_PTR],
                                            $RESPONSE (.PB[PB_RESPONSE_PTR],
                                                       NICE$_SUC));

         ! Set the response message length in the request block

         REQ[RB_RESPONSE_LENGTH] = ch$diff (.PB[PB_RESPONSE_PTR],
                                            ch$ptr (.REQ[RB_RESPONSE],,8));

         NML$REQUEST_FINISH (.REQ) ;    ! Queue request for completion
         end;

    return .RTN_COD 
    end;				!End of NML$TEST
%routine ('GET_LOOP_PARAMETERS', PB) : NML_LKG_PB =

!++
! FUNCTIONAL DESCRIPTION:
!       
!       This routine extracts the optional parameters that may be
!       supplied with the NICE test request message. If any of the
!       parameters are not supplied in the test request, appropriate
!       defaults are returned.
!
! FORMAL PARAMETERS
!
!	PB - The address of the parameter block.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!	1, if successful;
!       NICE return code otherwise.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
       PB : ref PARAMETER_BLOCK;

    bind
       REQ = .PB[PB_REQ_ADR]: REQUEST_BLOCK; ! NICE RB structure

    !
    ! Preset the default values. LOOP HELP/LENGTH/WITH parameters are
    ! set up in the EXECUTOR node's Volatile Data Base (VDB). Get them
    ! from there as first priority. If not there use a local default.

    if not NML$GET_VDB_PARAMETER (ENTITY_NODE,
                                  NMU$NETWORK_LOCAL(),
                                  150,  ! LOOP COUNT
                                  PB[PB_COUNT])
    then PB[PB_COUNT] = DEFAULT_COUNT;
    if not NML$GET_VDB_PARAMETER (ENTITY_NODE,
                                  NMU$NETWORK_LOCAL(),
                                  151,  ! LOOP LENGTH
                                  PB[PB_LENGTH])
    then PB[PB_LENGTH] = DEFAULT_LENGTH;
    if not NML$GET_VDB_PARAMETER (ENTITY_NODE,
                                  NMU$NETWORK_LOCAL(),
                                  152,  ! LOOP WITH
                                  PB[PB_WITH])
    then PB[PB_WITH] = DEFAULT_WITH;

    selectone .REQ[RB_NICE_ENTITY_TYPE] of
        set
        [ENTITY_NODE]:                  ! NODE loopback test
            ;

        [ENTITY_CIRCUIT]:               ! CIRCUIT loopback test
            begin
            PB[PB_HELP] = DEFAULT_HELP;
            PB[PB_HARDWARE_ADDR] = 0;
            PB[PB_PHY_ADDR] = DEFAULT_PHY_ADDR;
            end;

        [ENTITY_LINE]:                  ! LINE loopback test
            ;

        [otherwise]:                    ! Internal error
            return $NICE$ERR_MPE;
        tes;

    !
    ! If parameters were provided pick up their values.
    !

    if .REQ[RB_NICE_PARAMETERS] neq 0
    then begin
         local PARPTR,PAREND;
         PARPTR = .REQ[RB_NICE_PARAMETERS];
         PAREND = ch$plus (ch$ptr(.REQ[RB_NICE],,8), .REQ[RB_NICE_LENGTH]);

         while ch$diff (.PAREND, .PARPTR) gtr 0
         do begin
            local PAR_NO, PAR_VAL;
            PAR_NO = GETW (PARPTR) ;    ! Get parameters number
            selectone .PAR_NO of
                set
                [150]:                  ! LOOP COUNT
                    selectoneu (PAR_VAL = GETW (PARPTR)) of
                        set
                        [1 to 65535]:
                            PB[PB_COUNT] = .PAR_VAL;
                        [otherwise]:
                            return $NICE$ERR_IPV;
                        tes;
                [151]:                  ! LOOP LENGTH
                    selectoneu (PAR_VAL = GETW (PARPTR)) of
                        set
                        [1 to 65535]:
                            PB[PB_LENGTH] = .PAR_VAL;
                        [otherwise]:
                            return $NICE$ERR_IPV;
                        tes;
                [152]:                  ! LOOP WITH
                    selectoneu (PAR_VAL = GETB (PARPTR)) of
                        set
                        [0, 1, 2]:
                            PB[PB_WITH] = .PAR_VAL;
                        [otherwise]:
                            return $NICE$ERR_IPV;
                        tes;

                [10]:                   ! LOOP PHYSICAL ADDRESS
                    begin
                    if GETB (PARPTR) neq 6 ! It must be HI-6
                    then return $NICE$ERR_IMF;
                    PB[PB_PHY_ADDR] = .PARPTR; ! Save pointer to data
                    GETW (PARPTR);      ! Eat up the string
                    GETW (PARPTR);
                    GETW (PARPTR);
                    end;
                [153]:                  ! LOOP ASSISTANT PHYSICAL ADDRESS
                    begin
                    if GETB (PARPTR) neq 6 ! It must be HI-6
                    then return $NICE$ERR_IMF;
                    PB[PB_ASST_PHY_ADDR] = .PARPTR; ! Save pointer to data
                    GETW (PARPTR);      ! Eat up the string
                    GETW (PARPTR);
                    GETW (PARPTR);
                    end;
                [154]:                  ! LOOP HELP
                    selectoneu (PAR_VAL = GETB (PARPTR)) of
                        set
                        [0, 1, 2]:
                            PB[PB_HELP] = .PAR_VAL;
                        [otherwise]:
                            return $NICE$ERR_IPV;
                        tes;
                [155]:                  ! LOOP NODE
                        begin
                        local
                             L,
                             ADDRESS,
                             NOD_LEN: $SIGNED_BYTE_VALUE, ! Signed length
                             NOD_PTR,
                             NOD_ADR;

                        !
                        ! Get NODE NAME and ADDRESS
                        !

                        NOD_ADR = PB[PB_NODE];
                        NOD_PTR = ch$ptr (.NOD_ADR,,8);
                        NOD_LEN = GETB (PARPTR); ! Fetch format byte
                        NOD_LEN = .NOD_LEN[VALUE]; ! Pick up the id length
                        L = NODE_ID_BUFFER_LENGTH;

                        if .NOD_LEN[VALUE] eql 0
                        then begin      ! NODE address format
                             ADDRESS = GETW (PARPTR); ! Get node address value
                             if .ADDRESS eql 0
                             then return $NICE$ERR_IPV ! EXECUTOR Node illegal
                             else begin
                                  PUTW (ADDRESS, NOD_PTR); ! Store node address
                                  PUTB (0, NOD_PTR); ! Zero length node name
                                  NOD_PTR = ch$ptr (.NOD_ADR,,8);
                                  $NML$MAP_NODE_ID (L, .NOD_PTR);
                                  end;
                             end
                        else begin
                             if .NOD_LEN[VALUE] gtr 0
                             then begin ! NODE name format
                                  ADDRESS = 0; ! Node address is zero
                                  PUTW (ADDRESS, NOD_PTR); ! Store node address
                                  PUTB (.NOD_LEN[VALUE], NOD_PTR); ! And length
                                  ch$move (.NOD_LEN[VALUE], ! Move node name
                                           .PARPTR,
                                           .NOD_PTR);
                                  PARPTR = ch$plus (.PARPTR,.NOD_LEN[VALUE]);
                                  NOD_PTR= ch$ptr (.NOD_ADR,,8);
                                  if not $NML$MAP_NODE_ID (L, .NOD_PTR)
                                  then return $NICE$ERR_URC;
                                  ADDRESS = GETW (NOD_PTR);
                                  end
                             else return $NICE$ERR_IPV; ! Class illegal
                             end;

                        if .PB[PB_PHY_ADDR] neq DEFAULT_PHY_ADDR
                        then return $NICE$ERR_IPG; ! Can't have together

                        !
                        ! Get HARDWARE ADDRESS from NODE Data Base. If
                        ! not defined set to null.
                        !

                        if not NML$GET_VDB_PARAMETER (ENTITY_NODE,
                                                      ch$ptr (PB[PB_NODE],,8),
                                                      114, ! HARDWARE ADDRESS
                                                      PB[PB_HARDWARE_ADDR])
                        then PB[PB_HARDWARE_ADDR] = 0;

                        !
                        ! Play GUTS!!!!!
                        ! Build Ethernet physical address for this node
                        ! in the NODE Id buffer in the PB.
                        ! Hope no one will need the NODE ID again.
                        !

                        NOD_PTR = ch$ptr (.NOD_ADR,,8);
                        NOD_PTR = ch$move (4, ! Move HI-Order Ethernet address
                                           CH$HEX ('AA000400'),
                                           .NOD_PTR);

                        PUTW (ADDRESS, NOD_PTR);

                        PB[PB_PHY_ADDR] = ch$ptr (.NOD_ADR,,8);

                        end;
                [156]:                  ! LOOP ASSISTANT NODE
                        begin
                        local
                             L,
                             ADDRESS,
                             NOD_LEN: $SIGNED_BYTE_VALUE, ! Signed length
                             NOD_PTR,
                             NOD_ADR;

                        !
                        ! Get NODE NAME and ADDRESS
                        !

                        NOD_ADR = PB[PB_ASST_NODE];
                        NOD_PTR = ch$ptr (.NOD_ADR,,8);
                        NOD_LEN = GETB (PARPTR); ! Fetch format byte
                        NOD_LEN = .NOD_LEN[VALUE]; ! Pick up the id length
                        L = NODE_ID_BUFFER_LENGTH;

                        if .NOD_LEN[VALUE] eql 0
                        then begin      ! NODE address format
                             ADDRESS = GETW (PARPTR); ! Get node address value
                             if .ADDRESS eql 0
                             then return $NICE$ERR_IPV ! EXECUTOR Node illegal
                             else begin
                                  PUTW (ADDRESS, NOD_PTR); ! Store node address
                                  PUTB (0, NOD_PTR); ! Zero length node name
                                  NOD_PTR = ch$ptr (.NOD_ADR,,8);
                                  $NML$MAP_NODE_ID (L, .NOD_PTR);
                                  end;
                             end
                        else begin
                             if .NOD_LEN[VALUE] gtr 0
                             then begin ! NODE name format
                                  ADDRESS = 0; ! Node address is zero
                                  PUTW (ADDRESS, NOD_PTR); ! Store node address
                                  PUTB (.NOD_LEN[VALUE], NOD_PTR);
                                  ch$move (.NOD_LEN[VALUE], ! Move node name
                                           .PARPTR,
                                           .NOD_PTR);
                                  PARPTR = ch$plus (.PARPTR,.NOD_LEN[VALUE]);
                                  NOD_PTR= ch$ptr (.NOD_ADR,,8);
                                  if not $NML$MAP_NODE_ID (L, .NOD_PTR)
                                  then return $NICE$ERR_URC;
                                  ADDRESS = GETW (NOD_PTR);
                                  end
                             else return $NICE$ERR_IPV; ! Class illegal
                             end;

                        if .PB[PB_ASST_PHY_ADDR] neq DEFAULT_PHY_ADDR
                        then return $NICE$ERR_IPG; ! Can't have together

                        !
                        ! Get HARDWARE ADDRESS from NODE Data Base. If
                        ! not defined set to null.
                        !

                        if not NML$GET_VDB_PARAMETER (ENTITY_NODE,
                                                      ch$ptr (PB[PB_ASST_NODE],,8),
                                                      114, ! HARDWARE ADDRESS
                                                      PB[PB_HARDWARE_ADDR])
                        then PB[PB_HARDWARE_ADDR] = 0;

                        !
                        ! Play GUTS!!!!!
                        ! Build Ethernet physical address for this node
                        ! in the NODE Id buffer in the PB.
                        ! Hope no one will need the NODE ID again.
                        !

                        NOD_PTR = ch$ptr (.NOD_ADR,,8);
                        NOD_PTR = ch$move (4, ! Move HI-Order Ethernet address
                                           CH$HEX ('AA000400'),
                                           .NOD_PTR);

                        PUTW (ADDRESS, NOD_PTR);

                        PB[PB_ASST_PHY_ADDR] = ch$ptr (.NOD_ADR,,8);

                        end;
                [otherwise]:            ! Invalid parameter for LOOP
                    return $NICE$ERR_UPT ;
                tes;
            end;

         if .PARPTR neq .PAREND then return $NICE$ERR_IMF;
         end;

    return 1 ;                          ! Return NICE success code
    end;				!End of GET_LOOP_PARAMETERS
%routine ('NODE_ACCESS', PB) : NML_LKG_PB =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This is the loopback access routine for the NODE loopback
!       test.
!
! FORMAL PARAMETERS
!
!	PB - The address of the parameter block.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!	NICE return code.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
       PB: ref PARAMETER_BLOCK;

    bind
       REQ = .PB[PB_REQ_ADR]: REQUEST_BLOCK ; ! NICE RB structure

    local
         CBLK: CONNECT_BLOCK,           ! DECnet link connect block
         LSBLK: LINK_STATUS_BLOCK,      ! DECnet link status block
         RTN_COD ;                      ! NICE return

    !
    ! Set up the DECnet link connect block for loopback mirror
    ! at node specified in NCP command
    !

    CBLK[CB_OBJECT] = MIRROR_OBJECT ; ! Loopback mirror object number
    CBLK[CB_DESCRIPTOR_LENGTH] = 0 ; ! No descriptor
    CBLK[CB_TASK_LENGTH] = 0 ;  ! No task name

    !
    ! Entity id specifies the node to mirror the loopback test messages
    !

    CBLK[CB_HOST] = .REQ[RB_NICE_ENTITY] ;

    CBLK[CB_HOST_LENGTH] = .REQ[RB_NICE_ENTITY_ID_LENGTH] ;

    !
    ! Copy access control fields from request block
    !

    CBLK[CB_USERID_LENGTH] = .REQ[RB_USER_LENGTH] ;
    CBLK[CB_USERID] = .REQ[RB_USER];

    CBLK[CB_ACCOUNT_LENGTH] = .REQ[RB_ACCOUNT_LENGTH] ;
    CBLK[CB_ACCOUNT] = .REQ[RB_ACCOUNT];
             

    CBLK[CB_PASSWORD_LENGTH] = .REQ[RB_PASSWORD_LENGTH] ;
    CBLK[CB_PASSWORD] = .REQ[RB_PASSWORD];
    
    CBLK[CB_DATA_LENGTH] = 0;

    !
    ! Now make network connection to Loopback Mirror
    !

    REQ[RB_RESPONSE_LENGTH] = RESPONSE_BUFFER_LENGTH;
    if (PB[PB_LINK_HANDLE] = NMU$NETWORK_OPEN (SOURCE_LINK, CBLK,
	.PB[PB_RESPONSE_PTR], REQ[RB_RESPONSE_LENGTH], NICE$_MCF))
    gtr 0
    then begin
         local
              MESSAGE_DATA_SIZE ;       ! Maximum data size of mirror

         !
         ! Check that mirror returned its maximum data buffer length
         ! and get it.
         !

         if .CBLK[CB_DATA_LENGTH] neq 2
         then return $NICE$ERR_MPE ;

         MESSAGE_DATA_SIZE = GETW (CBLK[CB_DATA]) ; ! Get mirror's data length

         !
         ! Go ahead and test if our length is not over mirrors maximum.
         !

         if .PB[PB_LENGTH] leq .MESSAGE_DATA_SIZE
         then RTN_COD = LOOP_NODE (PB[PB_BASE]) ! Loop messages to node
         else begin
              RTN_COD = NICE$_IPV;
              PB[PB_RESPONSE_PTR] = ch$plus (.PB[PB_RESPONSE_PTR],
                       $RESPONSE (.PB[PB_RESPONSE_PTR], .RTN_COD, N$LLTH));
              PB[PB_COUNT] = .MESSAGE_DATA_SIZE;
              end;

         NMU$NETWORK_CLOSE (.PB[PB_LINK_HANDLE], 0, 0);
         end
    else RTN_COD = $NICE$ERR_MCF;       ! Say mirror connect failed

    return .RTN_COD
    end;				!End of NODE_ACCESS
%routine ('CIRCUIT_ACCESS', PB) : NML_LKG_PB =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This is the access routine for the CIRCUIT loopback test.
!
! FORMAL PARAMETERS
!
!	PB - The address of the parameter block.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!	NICE return code.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
       PB: ref PARAMETER_BLOCK;

    bind
       REQ = .PB[PB_REQ_ADR]: REQUEST_BLOCK; ! NICE RB structure

    local
         RTN_COD ;                      ! NICE return

    !
    ! STATE must be ON or SERVICE
    !
    begin
    local CIRCUIT_STATE;

    if not NML$GET_VDB_PARAMETER (.REQ[RB_NICE_ENTITY_TYPE],
                                  .REQ[RB_NICE_ENTITY],
                                  CPARM_STATE,
                                  CIRCUIT_STATE)
    then CIRCUIT_STATE = CIRCUIT_ON;

    selectone .CIRCUIT_STATE of
        set
        [CIRCUIT_ON, CIRCUIT_SERVICE]:
            ;
        [otherwise]:
            begin
            RTN_COD = NICE$_CWS;
            $RESPONSE (.PB[PB_RESPONSE_PTR], .RTN_COD, CIRCUIT_);
            return .RTN_COD;
            end;
        tes;

    end;
    !
    ! SERVICE must be ENABLED
    !
    begin
    local CIRCUIT_SERVICE;

    if not NML$GET_VDB_PARAMETER (.REQ[RB_NICE_ENTITY_TYPE],
                                  .REQ[RB_NICE_ENTITY],
                                  CPARM_SERVICE,
                                  CIRCUIT_SERVICE)
    then CIRCUIT_SERVICE = CIRCUIT_DISABLED;

    selectone .CIRCUIT_SERVICE of
        set
        [CIRCUIT_ENABLED]:
            ;
        [otherwise]:
            begin
            RTN_COD = NICE$_CWS;
            $RESPONSE (.PB[PB_RESPONSE_PTR], .RTN_COD, CIRCUIT_);
            return .RTN_COD;
            end;
        tes;

    end;
    !
    ! substate must not be set.
    !
    begin
    local CIRCUIT_SUBSTATE;

    if not NML$GET_VDB_PARAMETER (.REQ[RB_NICE_ENTITY_TYPE],
                                  .REQ[RB_NICE_ENTITY],
                                  CPARM_SUBSTATE,
                                  CIRCUIT_SUBSTATE)
    then CIRCUIT_SUBSTATE = CIRCUIT_NO_SUBSTATE;

    selectone .CIRCUIT_SUBSTATE of
        set
        [CIRCUIT_NO_SUBSTATE]:
            ;
        [otherwise]:
            begin
            RTN_COD = NICE$_CWS;
            $RESPONSE (.PB[PB_RESPONSE_PTR], .RTN_COD, CIRCUIT_);
            return .RTN_COD;
            end;
        tes;

    end;
    !
    ! Open the DLX data link for loopback use.
    !

    if (RTN_COD = $NMU_DLX_OPEN (DLX_LOOP,
				 CIRCUIT_,
                                 .REQ[RB_NICE_ENTITY],
                                 .PB[PB_PHY_ADDR],
                                 -1,              ! PSI not used here.
                                 -1,
                                 -1,
                                 .PB[PB_RESPONSE_PTR])) geq 0
    then begin                          ! DLX open, got DLX link handle
         PB[PB_LINK_HANDLE] = .RTN_COD;

         !
         ! Set Data Link Substate to LOOPING.
         !

         NML$SET_VDB_PARAMETER (.REQ[RB_NICE_ENTITY_TYPE],
                                .REQ[RB_NICE_ENTITY],
                                CPARM_SUBSTATE,
                                uplit (CIRCUIT_LOOPING)) ;

         !
         ! LLMOP% will BUGINF if the length is less than 31.
         !

         if .PB[PB_LENGTH] lss 31 then PB[PB_LENGTH] = 31;

         RTN_COD = LOOP_CIRCUIT (PB[PB_BASE]); ! Loop messages to data link

         NML$CLR_VDB_PARAMETER (.REQ[RB_NICE_ENTITY_TYPE],
                                .REQ[RB_NICE_ENTITY],
                                CPARM_SUBSTATE);

         $NMU_DLX_CLOSE (.PB[PB_LINK_HANDLE]); ! Close the DLX link
         end;

    return .RTN_COD
    end;				!End of CIRCUIT_ACCESS
%routine ('LOOP_NODE', PB) : NML_LKG_PB =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Performs the loopback function to the loopback mirror at
!       a remote node.
!
! FORMAL PARAMETERS
!
!	PB - The address of the parameter block.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!       NICE return code.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
        PB: ref PARAMETER_BLOCK;

    label
        AND_RETURN_BUFFERS;

    literal
           EOM = 1 ;                    ! End of message flag

    local
         BUFFER_ADR,                    ! Address of receive buffer
         BUFFER_PTR,                    ! Pointer to looped test message
         MESSAGE_ADR,                   ! Address of test message buffer
         MESSAGE_PTR,                   ! Pointer to test message
         RTN_COD ;                      ! NICE return code

    bind
        LOOP_BUFFER_ALLOCATION = ch$allocation((.PB[PB_LENGTH] + 1),8) * %upval ;

    !
    ! Get buffers for send and receive loop messages
    !

    BUFFER_ADR = NMU$MEMORY_GET (LOOP_BUFFER_ALLOCATION) ;
    BUFFER_PTR = ch$ptr (.BUFFER_ADR,,8) ; ! Pointer to buffer
    MESSAGE_ADR = NMU$MEMORY_GET (LOOP_BUFFER_ALLOCATION) ;
    MESSAGE_PTR = ch$ptr (.MESSAGE_ADR,,8) ; ! Pointer to buffer

AND_RETURN_BUFFERS:
    begin                               ! AND_RETURN_BUFFERS
    !
    ! Build a loop message to test with, function code is zero.
    !

    RTN_COD = GENERATE_LOOP_MESSAGE (PB[PB_BASE], %O'0', .MESSAGE_PTR) ;

    if .RTN_COD lss 0 then leave AND_RETURN_BUFFERS;

         until .PB[PB_COUNT] eql 0      ! Loop all messages
             do begin
                if not NMU$NETWORK_WRITE (.PB[PB_LINK_HANDLE],
                                          EOM,
                                          .PB[PB_LENGTH] + 1, ! 1 Overhead byte
                                          .MESSAGE_PTR)
                then begin
                     RTN_COD = NICE$_MLD;
                     $RESPONSE(.PB[PB_RESPONSE_PTR], .RTN_COD, 10);
                     leave AND_RETURN_BUFFERS
                     end;
                RTN_COD = NMU$NETWORK_READ (.PB[PB_LINK_HANDLE],
                                            .PB[PB_LENGTH] + 1,
                                            .BUFFER_PTR);
                if .RTN_COD lss 0 then leave AND_RETURN_BUFFERS;

                if not VERIFY_LOOP_MESSAGE (PB[PB_BASE], 1, .MESSAGE_PTR, .BUFFER_PTR)
                then begin
                     RTN_COD = $NICE$ERR_BLR;
                     leave AND_RETURN_BUFFERS
                     end;
                PB[PB_COUNT] = .PB[PB_COUNT] - 1 ; ! Another message looped
                end;

    $RESPONSE (.PB[PB_RESPONSE_PTR], NICE$_SUC);
    RTN_COD = NICE$_SUC;
    end;                                ! AND_RETURN_BUFFERS
    !
    ! Release buffers
    !

    NMU$MEMORY_RELEASE (.MESSAGE_ADR,LOOP_BUFFER_ALLOCATION) ;
    NMU$MEMORY_RELEASE (.BUFFER_ADR,LOOP_BUFFER_ALLOCATION) ;

    return .RTN_COD ;                   ! Return results
    end;				!End of LOOP_NODE
%routine ('LOOP_CIRCUIT', PB) : NML_LKG_PB =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS
!
!	PB - The address of the parameter block.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!	NONE.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
        PB: ref PARAMETER_BLOCK;

    label
        AND_RETURN_BUFFERS;

    local
         BUFFER_ADR,                    ! Address of receive buffer
         BUFFER_PTR,                    ! Pointer to looped test message
         MESSAGE_ADR,                   ! Address of test message buffer
         MESSAGE_PTR,                   ! Pointer to test message
         RTN_COD ;                      ! NICE return code

    bind
        LOOP_BUFFER_ALLOCATION = ch$allocation((.PB[PB_LENGTH] + 1),8) * %upval ;

    bind
        CIRCUIT_BLOCK = .PB[PB_LINK_HANDLE] : CD_BLOCK;

    !
    ! Get buffers for send and receive loop messages
    !

    BUFFER_ADR = NMU$MEMORY_GET (LOOP_BUFFER_ALLOCATION) ;
    BUFFER_PTR = ch$ptr (.BUFFER_ADR,,8) ; ! Pointer to buffer
    MESSAGE_ADR = NMU$MEMORY_GET (LOOP_BUFFER_ALLOCATION) ;
    MESSAGE_PTR = ch$ptr (.MESSAGE_ADR,,8) ; ! Pointer to buffer

AND_RETURN_BUFFERS:
    begin                               ! AND_RETURN_BUFFERS
    !
    ! Build a loop message to test with, function code LOOPBACK_TEST.
    !

    RTN_COD = GENERATE_LOOP_MESSAGE (PB[PB_BASE], LOOPBACK_TEST, .MESSAGE_PTR) ;

    if .RTN_COD lss 0 then leave AND_RETURN_BUFFERS;

    until .PB[PB_COUNT] eql 0           ! Loop all messages
             do begin

                decru CNT from NMU$K_MOP_RETRY_LIMIT to 1
                do begin
                   if not (RTN_COD = $NMU_DLX_WRITE (.PB[PB_LINK_HANDLE],
                                         DLX_OTHER, ! Not doing load/dump 
                                         .MESSAGE_PTR,
                                         .PB[PB_LENGTH] + 1, ! 1 Overhead byte
                                         .PB[PB_RESPONSE_PTR]))
                   then begin
                        if .RTN_COD geq 0
                        then begin
                             ch$wchar(0, .PB[PB_RESPONSE_PTR]);
                             RTN_COD = $NICE$ERR_LCE;
                             end;
                        leave AND_RETURN_BUFFERS
                        end;
                   RTN_COD = $NMU_DLX_READ (.PB[PB_LINK_HANDLE],
                                           DLX_OTHER, ! Not doing load/dump 
                                           .BUFFER_PTR, .PB[PB_LENGTH] + 1,,
                                           .PB[PB_RESPONSE_PTR]);
                   if .RTN_COD gtr 0 then exitloop;
                   ch$wchar(0, .PB[PB_RESPONSE_PTR]);
                   if .RTN_COD neq -2
                   then begin
                        RTN_COD = $NICE$ERR_LCE;
                        leave AND_RETURN_BUFFERS
                        end;
                   RTN_COD = $NICE$ERR_LCE;
                   end;

                if .RTN_COD lss 0 then leave AND_RETURN_BUFFERS;

                while $true do
                      begin
                      selectone ch$rchar (.BUFFER_PTR) of
                          set
                          [LOOPED_DATA, 25, LOOPBACK_TEST]:
                              if not VERIFY_LOOP_MESSAGE (PB[PB_BASE],
                                                          ch$rchar (.BUFFER_PTR),
                                                          .MESSAGE_PTR,
                                                          .BUFFER_PTR)
                              then begin
                                   RTN_COD = $NICE$ERR_BLR;
                                   leave AND_RETURN_BUFFERS
                                   end;
                          [MOP_MODE_RUNNING]:
                              if .RTN_COD geq 8
                              then begin
                                   if (ch$rchar (ch$plus (.BUFFER_PTR, 7)) and 1^2) eql 0
                                   then begin
                                        RTN_COD = $NICE$ERR_OPF;
                                        leave AND_RETURN_BUFFERS;
                                        end;
                                   end;
                          [otherwise]:
                              begin
                              RTN_COD = $NICE$ERR_BLR;
                              leave AND_RETURN_BUFFERS
                              end;
                          tes;

                      !
                      ! If we looped circuit NI and used the multicast we
                      ! will likely get many responses for each request number
                      ! (from various nodes). READ_MOP_LOOP in NMUKNI will
                      ! set CD_MORE_MOP_RESPONSES in the circuit block if
                      ! there are more responses for this request number.
                      ! We must read them all because LLMOP holds on to them
                      ! forever otherwise and we'll run out of DECnet free
                      ! space.
                      !

                      if (.CIRCUIT_BLOCK[CD_TYPE] neq DEVTYP_NI) or
                         (.CIRCUIT_BLOCK[CD_MORE_MOP_RESPONSES] eql $false)
                         then begin
                              PB[PB_COUNT] = .PB[PB_COUNT] - 1;
                              exitloop;
                              end;
                      RTN_COD = $NMU_DLX_READ (.PB[PB_LINK_HANDLE],
                                               DLX_OTHER, ! Not load/dump 
                                               .BUFFER_PTR,
                                               .PB[PB_LENGTH] + 1,,
                                               .PB[PB_RESPONSE_PTR]);
                      if .RTN_COD lss 0
                      then begin
                           ch$wchar(0, .PB[PB_RESPONSE_PTR]);
                           RTN_COD = $NICE$ERR_LCE;
                           leave AND_RETURN_BUFFERS;
                           end;

                      end;

                end;

!    $RESPONSE (.PB[PB_RESPONSE_PTR], NICE$_SUC);

    !
    ! Leave response pointer at end of response message
    !

    PB[PB_RESPONSE_PTR] = ch$plus (.PB[PB_RESPONSE_PTR],
                                   ($RESPONSE_LENGTH (.PB[PB_RESPONSE_PTR])));
    PB[PB_RESPONSE_PTR] = ch$plus (.PB[PB_RESPONSE_PTR],
                                   (GETB (PB[PB_RESPONSE_PTR])));
    PB[PB_RESPONSE_PTR] = ch$plus (.PB[PB_RESPONSE_PTR],
                                   (GETB (PB[PB_RESPONSE_PTR])));

    RTN_COD = NICE$_SUC;
    end;                                ! AND_RETURN_BUFFERS
    !
    ! Release buffers
    !

    NMU$MEMORY_RELEASE (.MESSAGE_ADR,LOOP_BUFFER_ALLOCATION) ;
    NMU$MEMORY_RELEASE (.BUFFER_ADR,LOOP_BUFFER_ALLOCATION) ;

    return .RTN_COD ;                   ! Return results
    end;				!End of LOOP_CIRCUIT
%routine ('GENERATE_LOOP_MESSAGE', PB, FUNC_CODE, MESSAGE_PTR) : NML_LKG_PB =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Generates a NICE test message of requested length and
!       contents determined by the LOOP WITH parameter value
!       passed as an argument.
!
! FORMAL PARAMETERS
!
!	PB - The address of the parameter block.
!       FUNC_CODE   - A value to be used as the first byte (function code)
!                     of the test message.
!       MESSAGE_PTR - A character sequence pointer to the buffer in which
!                     the NICE test message will be built.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!	1, if successful;
!       NICE return code if failed.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
        PB: ref PARAMETER_BLOCK;

    local
         LOOP_CHAR ;

    !
    ! Set up the data pattern to be used in loop message
    !

    LOOP_CHAR = (case .PB[PB_WITH] from 0 to 2 of
                     set
                     [0]:                ! All zeroes
                         %B'00000000' ;
                     [1]:                ! All ones
                         %B'11111111' ;
                     [2]:                ! Mixed ones and zeroes
                         %B'10101010' ;
                     [inrange,           ! Default to mixed
                      outrange]:
                         return $NICE$ERR_IPV ; ! Error return
                     tes);

    !
    ! Write loop function code and fill message with appropriate pattern
    !

    ch$wchar_a (.FUNC_CODE,MESSAGE_PTR) ; ! Write function code
    ch$fill (.LOOP_CHAR,
             .PB[PB_LENGTH],
             .MESSAGE_PTR) ;

    return 1 ;                          ! Success
    end;				!End of GENERATE_LOOP_MESSAGE
%routine ('VERIFY_LOOP_MESSAGE', PB, FUNC_CODE, TRANSMIT_PTR,
          RECEIVE_PTR) : NML_LKG_PB =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Determines the validity of a NICE test message response after
!       being looped to the target node. The first byte of the message
!       must contain the NICE success return code.
!
! FORMAL PARAMETERS
!
!	PB - The address of the parameter block.
!       FUNC_CODE   - A value to be used as the first byte (function code)
!                     of the test message.
!       TRANSMIT_PTR - A character sequence pointer to the NICE test
!                      message sent to the loopback mirror.
!       RECEIVE_PTR  - A character sequence pointer to the NICE test
!                      received from the loopback mirror.
!
! IMPLICIT INPUTS
!
!	NONE.
!
! ROUTINE VALUE:
!
!	TRUE, if the looped message is valid;
!       FALSE, otherwise.
!
! SIDE EFFECTS:
!
!	NONE.
!
!--

    begin

    map
        PB: ref PARAMETER_BLOCK;

    if ch$rchar_a (RECEIVE_PTR) neq .FUNC_CODE
    then $false
    else ch$eql (.PB[PB_LENGTH],
                 .RECEIVE_PTR,
                 .PB[PB_LENGTH],
                 ch$plus (.TRANSMIT_PTR, 1))

    end;				!End of VERIFY_LOOP_MESSAGE
end				!End of Module NMLLBK
eludom
! Local Modes:
! Mode:BLISS
! Auto Save Mode:1
! Comment Column:40
! Comment Rounding:+1
! End: