Google
 

Trailing-Edge - PDP-10 Archives - TOPS-20_V6.1_DECnetSrc_7-23-85 - mcb/sc/sc1.bli
There is 1 other file named sc1.bli in the archive. Click here to see a list.
module SC1 (			! Network Management stuff
		ident = 'X01260'
		) =
begin
!
!                    COPYRIGHT (c) 1980, 1981, 1982
!                    DIGITAL EQUIPMENT CORPORATION
!                        Maynard, Massachusetts
!
!     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:	Session Control
!
! ABSTRACT:	This is the Network Management module for the MCB
!		implementation of Session Control.
!
! ENVIRONMENT:	MCB
!
! AUTHOR:	Buren Hoffman		CREATION DATE: 20-Nov-80
!
! MODIFIED BY:
!	X01010	Made this module an extension process.
!	X01020	Use new Comm/Exec to process linkage (.CRDAT for database)
!	X01030	Added event logging module NM_SIG
!	X01040	Added new entry (NM_NOD) to allow outside modules to
!		ascertain node name to number mappings.
!	X01050	Updated to use library calls, instead of requires.
!	X01060	Fix in node location code.
!	X01070	Changed SC state representations to same as NM usage.
!	X01080	Fixed state checking bug.
!	X01090	Modified node search to default to local node when null
!		node spec is given.
!	X01100	Fixed error reporting in "show-node-characteristics"
!	X01110	Mapping bug in SHOW_NODE_ITEMS fixed.
!	X01120	Fixed reporting in show_nodes stuff
!	X01130	Repaired mapping bug in RETURN_NODE_LIST.
!	X01140	Utilize "paranoid" conditional for some checking, and
!		fixed case-shift bug in node-name selection.
!	X01150	Optimization work
!	X01160	Fixed mapping bug in SHOW_NODE_ITEMS
!	X01170	Added Host-Node-ID processing, and put in
!		abbreviated reporting on Return-Nodes function
!	x01180	Ron Platukis 17-nov-81
!		-fix code for parmeter 400 and 501 support.
!	x01190	Ron Platukis 8-dec-81
!		-allow setting of a node name if it already exists.
!	x01200	Alan Peckham 14-apr-82
!		-Optimize code.
!               -Change RETURN_NODE_LIST functionality for new NMX.
!                This fixes the duplicate node names and makes sure
!                that the entries are in order.
!               -Fix Loop node support.
!               -Support change in NMT data base (SCPRM changed).
!	x01210	Alan Peckham 14-apr-82
!               -Fix bug which displays invalid EXECUTOR STATE.
!	x01220	Alan Peckham 2-may-82
!               -Change buffer overflow code to $NM$ERR_REE
!                in RETURN_NODE_LIST
!	x01230	Alan Peckham 22-may-82
!               -Fix to not display CLEARed parameters.
!               -Do some range checking on received parameter values.
!	x01240	Alan Peckham 26-may-82
!               -Fix INSERT_NODE bug which did not maintain space left
!	x01250	Alan Peckham  1-oct-82
!               -Fix CLEAR_NODE_PARMS to handle CLEAR ALL case.
!               -Fix SET_NODE_PARMS to respond to SET ALL with $NM$ERR_OPF.
!	x01260	Alan Peckham 12-oct-82
!               -Fix mapping bug introduced by edit #25.
!--



!
! REQUIRED FILES
!
library 'SCPRM';		! Our parameter and macro definitions
library 'MCB:MCBLIB';
library 'MCB:NMXLIB';
library 'MCB:XPORTX';

require 'NSP:NSINFO';

!
! TABLE OF CONTENTS:
!
linkage
    SC_LKG_CCB = jsr (register = 4),
    SC_LKG_CCB_PTR_LNG = jsr (register = 4, register = 1, register = 0),
    SC_LKG_LEN_PTR = jsr (register = 1, register = 2),
    SC_LKG_NMT_CCB = jsr (register = 1, register = 4),
    SC_LKG_PTR = jsr (register = 3),
    SC_LKG_SCDB_NMT_CCB = jsr (register = 5, register = 1, register = 4);

forward routine
    NM_EXT: call$ novalue,
    NM_NOD: call$ novalue,
    NM_SIG: call$ novalue,

    CLEAR_NODE_PARMS : SC_LKG_CCB,
    CLEAR_EXECUTOR_INCOMING_TIMER : SC_LKG_SCDB_NMT_CCB,
    CLEAR_EXECUTOR_OUTGOING_TIMER : SC_LKG_SCDB_NMT_CCB,
    CLEAR_NODE_CIRCUIT : SC_LKG_SCDB_NMT_CCB,
    CLEAR_NODE_NAME : SC_LKG_SCDB_NMT_CCB,
    SET_NODE_PARMS : SC_LKG_CCB,
    SHOW_NODE_ITEMS : SC_LKG_CCB,
    RETURN_NODE_LIST : SC_LKG_CCB,
    SHOW_CIRCUIT_ITEMS : SC_LKG_CCB,
    CASE_SHIFT : SC_LKG_LEN_PTR novalue,
    GET_NODE : SC_LKG_PTR,
    INSERT_NODE : SC_LKG_NMT_CCB novalue,
    MAKE_ROOM : SC_LKG_CCB_PTR_LNG novalue;

!
! MACROS:
!
macro
    RAD50 (STR) =
    %if %bliss (bliss36) %then %rad50_10 STR %else %rad50_11 STR %fi %;

!
! EQUATED SYMBOLS:
!

!
! OWN STORAGE:
!
$MCB_PROCESS (NAME = SC1);

!
! EXTERNAL REFERENCES:
!
%sbttl 'Network Management'
global routine NM_EXT (CCB): call$ novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is activated by a control request from
!	Network Management.
!
! FORMAL PARAMETERS:
!	CCB	CCB to pass to handler routine
!
! IMPLICIT INPUTS:
!	CCB Contents
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    map CCB: ref block field (C_NM_FIELDS);
    local
	STATUS;

    STATUS =
	begin
	if .CCB [C_MOD] neq FM_NM
	then $NM$ERR_MPE
	else
	    begin
	    case .CCB [C_NM_ENTITY] from NMX$ENT_lo to NMX$ENT_hi of
		set
		[NMX$ENT_nod]: 			! NM fnc for node parameter
		    begin
		    case .CCB [C_NM_FUNC] from NMX$FNC_lo to NMX$FNC_hi of
			set 			! *** NODE ***
			[NMX$FNC_set]:
			    SET_NODE_PARMS (.CCB);	! Set parameters
			[NMX$FNC_clr]:
			    CLEAR_NODE_PARMS (.CCB);	! Clear parameters
			[NMX$FNC_sho]:
			    SHOW_NODE_ITEMS (.CCB);	! Show selected items
			[NMX$FNC_ret]:
			    RETURN_NODE_LIST (.CCB);	! Return selected items
			[inrange, outrange]:
			    $NM$ERR_MPE;
			tes
		    end;

		[NMX$ENT_ckt]:	! NM function for circuit parameter
		    begin
		    case .CCB [C_NM_FUNC] from NMX$FNC_lo to NMX$FNC_hi of
			set			! *** CIRCUIT ***
			[NMX$FNC_sho]:
			    SHOW_CIRCUIT_ITEMS (.CCB); ! Show selected items
			[inrange, outrange]:
			    $NM$ERR_MPE;
			tes
		    end;

		[inrange, outrange]:
		    $NM$ERR_MPE;
		tes
	    end
	end;

    if .STATUS neq 0 then $SC_DO_CCP (.CCB, .STATUS)
    end;
global routine NM_NOD (NODE_ID_PTR): call$ novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	Map from node address to node name, or vice versa.
!
! FORMAL PARAMETERS:
!	NODE_ID_PTR
!		Node ID block pointer. 	The block is to
!		contain space for:
!			Node address	(2 bytes)
!			Node name	(I-6)
!		specifying either one of the parameters.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	The missing field in the node ID block will be filled in.
!	If the node is unknown, a -1 node address is returned.
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    local
        SAVE_MAP,
        PTR,
        NMT: ref block field (NMT_FIELDS);

    SMAP$ (SAVE_MAP);
    PTR = .NODE_ID_PTR;

    if (NMT = GET_NODE (.PTR)) neqa 0
    then
	begin
	local
	    NAML;

	byt$short_string (NMT [NMT_ADDR], PTR);
	NAML = .NMT [NMT_NAML];
	ch$wchar_a (.NAML, PTR);
	ch$move (.NAML, byt$ptr (NMT [NMT_NAME]), .PTR);
	end
    else
        begin
        ch$wchar_a (-1, PTR);
        ch$wchar_a (-1, PTR);
        end;

    MAP$ (.SAVE_MAP)
    end;
global routine NM_SIG (SIG_TYPE, PRM1, PRM2): call$ novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is activated by call from another SC routine
!	to effect event logging - via Network Management (NMX).
!
! FORMAL PARAMETERS:
!	SIG_TYPE Signal type identifier
!	PRM1	Type-dependent parameter #1
!	PRM2	Type-dependent parameter #2
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	NMX is signaled concerning the event
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);

    selectone .SIG_TYPE of
	set

	[0]:				! Node state change
	    ! PRM1 = Reason for state change
	    ! PRM2 = Old state

	    begin
            local NEW_STATE;
            $NM_LOG_BEGIN ();
            PARAMETER_C_1 (0, PRM1);    ! Reason
            PARAMETER_C_1 (1, PRM2);    ! Old-State
            NEW_STATE = .SCDB [SCF_STAT];
            PARAMETER_C_1 (2, NEW_STATE); ! New-State
            $NM_LOG_END ((2^6 + 0), 0);
	    end;

	[otherwise]:
	    return;
	tes;

    end;
routine CLEAR_NODE_PARMS (CCB) : SC_LKG_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Perform clearing of specified node parameter.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS);
    bind
	NMPAR = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS);
    local
        NMT : ref block field (NMT_FIELDS);

    if (NMT = GET_NODE (byt$ptr (NMPAR [NMX_NMPAR_ENTITY]))) eqla 0
    then return $NM$ERR_URC;

    if .CCB [C_CNT] neq 0
    then
        begin

        selectone 
            begin
            local SAVE_MAP,BUF_PTR;
            stacklocal PARM_NUM;
            SMAP$(SAVE_MAP);
            MAP$ (.CCB [C_BIAS]);
            BUF_PTR = .CCB [C_ADDR];
            byt$string_short (BUF_PTR, PARM_NUM);
            MAP$(.SAVE_MAP);
            .PARM_NUM
            end
        of
            set
            [500]:
                CLEAR_NODE_NAME (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
            [501]:
                CLEAR_NODE_CIRCUIT (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
            [510]:
                CLEAR_EXECUTOR_INCOMING_TIMER (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
            [511]:
                CLEAR_EXECUTOR_OUTGOING_TIMER (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
            [otherwise]:
	        $NM$ERR_UPT;
            tes

        end
    else
        begin
        CLEAR_NODE_NAME (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
        CLEAR_NODE_CIRCUIT (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
        CLEAR_EXECUTOR_INCOMING_TIMER (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
        CLEAR_EXECUTOR_OUTGOING_TIMER (SCDB [SC_TICK], NMT [NMT_BASE], .CCB);
        NM$SUC
        end

    end;
routine CLEAR_EXECUTOR_INCOMING_TIMER (SCDB, NMT, CCB) : SC_LKG_SCDB_NMT_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Clear the incoming timer
!
! FORMAL PARAMETERS:
!       SCDB    SC data base
!       NMT     NMT address
!	CCB	CCB address (not needed)
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS),
        NMT: ref block field (NMT_FIELDS);

    if .NMT [NMT_ADDR] neq .SCDB [SC_LADDR] then return $NM$ERR_PNA;
    SCDB [SC_ITIME] = 0;
    return NM$SUC
    end;
routine CLEAR_EXECUTOR_OUTGOING_TIMER (SCDB, NMT, CCB) : SC_LKG_SCDB_NMT_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Clear the outgoing timer
!
! FORMAL PARAMETERS:
!       SCDB    SC data base
!       NMT     NMT address
!	CCB	CCB address (not needed)
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS),
        NMT: ref block field (NMT_FIELDS);

    if .NMT [NMT_ADDR] neq .SCDB [SC_LADDR] then return $NM$ERR_PNA;
    SCDB [SC_OTIME] = 0;
    return NM$SUC
    end;
routine CLEAR_NODE_CIRCUIT (SCDB, NMT, CCB) : SC_LKG_SCDB_NMT_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Clear a loop node
!
! FORMAL PARAMETERS:
!       SCDB    SC data base
!       NMT     NMT address
!	CCB	CCB address (not needed)
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS),
        NMT: ref block field (NMT_FIELDS);

    if .NMT [NMT_ADDR] neq 0 then return $NM$ERR_CWS;
    NMT [NMT_NAML] = 0;
    return NM$SUC
    end;
routine CLEAR_NODE_NAME (SCDB, NMT, CCB) : SC_LKG_SCDB_NMT_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Clear a node name mapping
!
! FORMAL PARAMETERS:
!       SCDB    SC data base
!       NMT     NMT address
!	CCB	CCB address (not needed)
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS),
        NMT: ref block field (NMT_FIELDS);

    NMT [NMT_NAML] = 0;                 ! No name anymore
    return NM$SUC
    end;
routine SET_NODE_PARMS (CCB) : SC_LKG_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Perform setting of specified node parameter.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS);
    bind
	NMPAR = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS);
    local
	BUF_PTR,
	PARM_NUM;

    if .CCB [C_CNT] eql 0
    then
        return $NM$ERR_OPF;

    BUF_PTR = .CCB [C_ADDR];
    MAP$ (.CCB [C_BIAS]);
    byt$string_short (BUF_PTR, PARM_NUM);

    selectone .PARM_NUM of
	set

	[0]:			! Set node state
	    begin
	    local
		STATE,
		LCCB: ref block field (C_FIELDS);

	    STATE = ch$rchar_a (BUF_PTR);
            selectone .STATE of
                set
                [S_ST_ON] :
                    0;
                [S_ST_OFF, S_ST_SHUT, S_ST_RST] :
                    return $NM$ERR_OPF;
                [otherwise] :
                    return $NM$ERR_IPV;
                tes;
	    if .SCDB [SCF_STAT] eql S_ST_ON then return NM$SUC;
	    if not PDVID$ (RAD50 ('NMX'), SCDB [SC_NMPIX]) then SIGNAL_STOP (SC$_NNM);
	    if not PDVID$ (RAD50 ('NSP'), SCDB [SC_NSPIX]) then SIGNAL_STOP (SC$_NNS);
	    SCDB [SC_LLINK] = 0;
	    if not CCBGT$ (LCCB) then return $NM$ERR_REE;
	    LCCB [C_STK] = .CCB;
	    LCCB [C_PIX] = .SCDB [SC_NSPIX];
	    LCCB [C_FNC] = FC_XME;	! Initialize
	    LCCB [C_MOD] = N_XINI;	!   NSP
	    $MCB_SCHEDULE_CCB (.LCCB);
	    return 0			! Special hack so no ccb completion
	    end;

	[141]:			! Define Host Node Address
	    begin
	    local
		HOST_ADDR;
	    BUF_PTR = byt$ptr (NMPAR [NMX_NMPAR_NODE_ADDRESS]);
	    byt$string_short (BUF_PTR, HOST_ADDR);
	    SCDB [SC_HADDR] = .HOST_ADDR
	    end;

	[500]:			! Set a node name
	    begin
	    local
                NEW_NAME : vector [byt$allocation(6)],
		NEW_NAML,
                NMT : ref block field (NMT_FIELDS);

	    byt$string_tiny (BUF_PTR, NEW_NAML);
            selectone .NEW_NAML of
                set
                [1 to 6]:
                    ;                   ! Valid range
                [otherwise]:
                    return $NM$ERR_IPV;
                tes;
            ch$move (.NEW_NAML, .BUF_PTR, byt$ptr (NEW_NAME));
	    if (NMT = GET_NODE (byt$ptr (NMPAR [NMX_NMPAR_ENTITY]))) eqla 0
	    then return $NM$ERR_URC;
            if .NMT [NMT_ADDR] eql 0 then return $NM$ERR_CWS;
	    NMT [NMT_NAML] = .NEW_NAML;
	    ch$move (.NEW_NAML, byt$ptr (NEW_NAME), byt$ptr (NMT [NMT_NAME]))
	    end;

	[501]:			! Set circuit for node name
	    begin
            local
                CHAN,
                NMXID,
                NMT: ref block field (NMT_FIELDS);
	    if GET_NODE (byt$ptr (NMPAR [NMX_NMPAR_ENTITY])) neqa 0
            then return $NM$ERR_CWS;
            MAP$ (.CCB [C_BIAS]);
            NMX$OBTAIN_CIRCUIT_OWNER (.SCDB [SC_NMPIX], .buf_ptr, CHAN, NMXID);
	    MAP$ (.SCDB [SC_NMT_BIAS]);
            NMT = blockvector [.SCDB [SC_NMT_ADDR], .SCDB [SC_NODES], NMT_BASE; 0, NMT_SIZE];

	    decru I from .SCDB [SC_LOOPS] to 1 do
		begin
		if .NMT [NMT_NAML] eql 0        ! Find available slot
		then
		    begin
		    local PTR;
		    PTR = byt$ptr (NMPAR [NMX_NMPAR_NODE_NAME_LENGTH]);
		    NMT [NMT_NAML] = ch$rchar_a (PTR);
		    ch$move (.NMT [NMT_NAML], .PTR, byt$ptr (NMT [NMT_NAME]));
		    NMT [NMT_CHAN] = .CHAN;
		    NMT [NMT_NMXID] = .NMXID;
		    return NM$SUC
		    end;

                NMT = vector [.NMT, NMT_SIZE];
		end;

	    return $NM$ERR_NRE
	    end;

	[510]:				! Set incoming connect timer
            begin
            local VALUE;
	    byt$string_short (BUF_PTR, VALUE);
            selectoneu .VALUE of
                set
                [1 to 65535]:
                    SCDB [SC_ITIME] = .VALUE;
                [otherwise]:
                    return $NM$ERR_IPV;
                tes;
            end;
	[511]:				! Set outgoing connect timer
            begin
            local VALUE;
	    byt$string_short (BUF_PTR, VALUE);
            selectoneu .VALUE of
                set
                [1 to 65535]:
                    SCDB [SC_OTIME] = .VALUE;
                [otherwise]:
                    return $NM$ERR_IPV;
                tes;
            end;
	[otherwise]:
	    return $NM$ERR_UPT;

	tes;

    return NM$SUC
    end;
routine SHOW_NODE_ITEMS (CCB) : SC_LKG_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Show specified items
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS);
    local
	NMT : ref block field (NMT_FIELDS);
    bind
	NMPAR = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS);

    $NM_RESPONSE_BEGIN( .CCB);

    if (NMT = GET_NODE (byt$ptr (NMPAR [NMX_NMPAR_ENTITY]))) neqa 0
    then
        selectone .CCB [C_NM_SELECT] of
            set
            [NMX$SEL_sum, NMX$SEL_sta]:
                begin
                local NEW_STATE;
                if .NMT [NMT_ADDR] eql .SCDB [SC_LADDR]
                then
                    begin
                    NEW_STATE = .SCDB[SCF_STAT];
                    PARAMETER_C_1 (0, NEW_STATE);
                    end;
                if .NMT [NMT_ADDR] eql 0
                then
                    NMX$PARAMETER_CIRCUIT (.SCDB[SC_NMPIX], .NMT [NMT_NMXID], 501);
                end;
            [NMX$SEL_cha]:
                begin
                if .NMT [NMT_ADDR] eql 0
                then
                    NMX$PARAMETER_CIRCUIT (.SCDB[SC_NMPIX], .NMT [NMT_NMXID], 501);
                if .NMT [NMT_ADDR] eql .SCDB [SC_LADDR]
                then
                    begin
                    if .SCDB [SC_ITIME] neq 0
                    then PARAMETER_DU_2 (510, SCDB [SC_ITIME]);
                    if .SCDB [SC_OTIME] neq 0
                    then PARAMETER_DU_2 (511, SCDB [SC_OTIME]);
                    end;
                end;
            tes;

    $NM_RESPONSE_END( .CCB);
    return .CCB[C_STS]
    end;
routine RETURN_NODE_LIST (CCB) : SC_LKG_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Return list of named nodes.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS);
    bind NMPAR = .CCB [C_NM_NMPAR] : block field (NMX_NMPAR_FIELDS);
    local
        BUF_SIZ,
	LEN,
	PTR,
	NMT: ref block [NMT_SIZE] field (NMT_FIELDS);

    MAP$ (.SCDB [SC_NMT_BIAS]);
    CCB [C_STS] = .CCB [C_CNT];
    CCB [C_CNT] = .NMPAR [NMX_NMPAR_RETURN_CNT];
    CCB [C_STS] = .CCB [C_STS] - .CCB [C_CNT];

    select .CCB [C_NM_SELECT] of
        set
        [NMX$SEL_kno] :
            begin
            NMT = .SCDB [SC_NMT_ADDR];

            decru I from .SCDB [SC_NODES] to 1 do
                begin
                local NAM_LEN;
                if (NAM_LEN = .NMT [NMT_NAML]) neq 0
                then INSERT_NODE (NMT [NMT_BASE], .CCB);
                NMT = vector [.NMT, NMT_SIZE];
                end;

            end;
        [NMX$SEL_kno, NMX$SEL_lop] :
            begin
            NMT = blockvector [.SCDB [SC_NMT_ADDR], .SCDB [SC_NODES], 0, 0, 0, 0; 0, NMT_SIZE];

            decru I from .SCDB [SC_LOOPS] to 1 do
                begin
                local NAM_LEN;
                if (NAM_LEN = .NMT [NMT_NAML]) neq 0
                then INSERT_NODE (NMT [NMT_BASE], .CCB);
                NMT = vector [.NMT, NMT_SIZE];
                end;

            end;
        tes;

    if .CCB [C_STS] geq 0
    then
        return NM$SUC
    else
        return $NM$ERR_REE

    end;
routine SHOW_CIRCUIT_ITEMS (CCB) : SC_LKG_CCB =

!++
! FUNCTIONAL DESCRIPTION:
!	Show selected circuit items.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    map CCB: ref block field (C_NM_FIELDS);
    bind
	NMPAR = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS);

    $NM_RESPONSE_BEGIN (.CCB);

    case .CCB [C_NM_SELECT] from NMX$SEL_lo to NMX$SEL_hi of
        set
        [NMX$SEL_sta, NMX$SEL_sum] :
            begin
            local
                CHAN,
                NMXID,
                NMT : ref block field (NMT_FIELDS);
            NMX$OBTAIN_CIRCUIT_OWNER( .SCDB[SC_NMPIX], byt$ptr (NMPAR [NMX_NMPAR_OTHER_LENGTH]), CHAN, NMXID);
            MAP$ (.SCDB [SC_NMT_BIAS]);
            NMT = blockvector [.SCDB [SC_NMT_ADDR], .SCDB [SC_NODES], NMT_BASE; 0, NMT_SIZE];

            decru I from .SCDB [SC_LOOPS] to 1 do
                begin
                if (.NMT [NMT_NAML] neq 0) and
                   (.NMT [NMT_CHAN] eqlu .CHAN<0, 8>)
                then
                    begin
                    PARAMETER_AI (400, .NMT [NMT_NAML], byt$ptr (NMT [NMT_NAME]));
                    exitloop;
                    end;
                NMT = vector [.NMT, NMT_SIZE];
                end;

            end;
        [inrange] :
            0;
        tes;

    $NM_RESPONSE_END (.CCB);
    return .CCB [C_STS]
    end;
routine CASE_SHIFT (LEN, PTR) : SC_LKG_LEN_PTR novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	Shift string pointed to by PTR to uppercase
!
! FORMAL PARAMETERS:
!	LEN	Length of string
!	PTR	Pointer to string to shift
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    local
	CH,
        TMP_PTR;

    TMP_PTR = .PTR;

    if .LEN neq 0
    then
        decru I from .LEN to 1 do
            begin
            if (CH = ch$rchar (.TMP_PTR)) geq %c'a' and .CH leq %c'z'
            then CH = .CH - (%c'a' - %c'A');
            ch$wchar_a (.CH, TMP_PTR);
            end;

    end;
routine GET_NODE (PARBLK) : SC_LKG_PTR =

!++
! FUNCTIONAL DESCRIPTION:
!	Determine whether name is in NMT
!
! FORMAL PARAMETERS:
!	PARBLK	Pointer to NM parameter block specifying node,
!		starting at NMX_NMPAR_ENTITY
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	Mapped to NMT
!
! ROUTINE VALUE:
!
!	NMT address if name found, else 0
!
! SIDE EFFECTS:
!	None
!--
    begin
    $sc_get_data_base (SCDB);
    local
        PARPTR,
	NODE_ADDR,
	NAME_LEN,
        NMT : ref block field (NMT_FIELDS);

    MAP$ (.SCDB [SC_NMT_BIAS]);			! Set map to node mapping table
    NMT = .SCDB [SC_NMT_ADDR];
    PARPTR = .PARBLK;
    byt$string_short (PARPTR, NODE_ADDR);	! Get the node address
    byt$string_tiny (PARPTR, NAME_LEN);		!   and name length

    selectone .NAME_LEN of              ! If a name is specified,
        set
        [1 to 6]:
            begin                       !   then search for the entry
            CASE_SHIFT (.NAME_LEN, .PARPTR);
            decru I from (.SCDB [SC_NODES] + .SCDB [SC_LOOPS]) to 1 do
	        begin
                if (.NMT [NMT_NAML] neq 0) and
                   ch$eql (.NAME_LEN, .PARPTR,
                           .NMT [NMT_NAML], byt$ptr (NMT [NMT_NAME]),
                           0)
                then return NMT [NMT_BASE];
                NMT = vector [.NMT, NMT_SIZE];
                end
            end;
        [0]:                            ! Node spec via address
            begin

            if .NODE_ADDR eql 0 then NODE_ADDR = .SCDB [SC_LADDR];	! Default to local
            if .NODE_ADDR lequ .SCDB [SC_NODES]
            then
                begin
                NODE_ADDR = .NODE_ADDR - 1;
                NMT = blockvector [NMT [NMT_BASE], .NODE_ADDR, NMT_BASE; 0, NMT_SIZE];
                return NMT [NMT_BASE];
                end;
            end;
        [otherwise]:
            ;
        tes;

    return 0
    end;
routine INSERT_NODE (NMT, CCB) : SC_LKG_NMT_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	Make room for LNG bytes at PTR in the CCB buffer.
!
! FORMAL PARAMETERS:
!       NMT     NMT for node
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	Mapped to CCB buffer
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    map
        CCB : ref block field (C_NM_FIELDS),
        NMT : ref block field (NMT_FIELDS);
    label
        FIND_NODE;
    local
        ADDR,
        LEN,
        NODE_ID : vector [byt$allocation (9)];
    ADDR = .NMT [NMT_ADDR];
    LEN = .NMT [NMT_NAML];
    begin
    local
        PTR;
    PTR = byt$ptr (NODE_ID);
    byt$short_string (NMT [NMT_ADDR], PTR);
    byt$tiny_string (LEN, PTR);
    ch$move (.LEN, byt$ptr (NMT [NMT_NAME]), .PTR);
    end;
    LEN = .LEN + 3;
    if .ADDR eql 0 then ADDR = .ADDR - 1;
FIND_NODE:
    begin                               ! FIND_NODE
    local
        CNT,
        PTR,
        SAVE_MAP;
    SMAP$ (SAVE_MAP);
    MAP$ (.CCB [C_BIAS]);
    PTR = .CCB [C_ADDR];
    CNT = .CCB [C_CNT];
    if .CNT neq 0
    then
        while true do
            begin
            local TCNT, TEST, TPTR;
            TPTR = .PTR;
            byt$string_short (TPTR, TEST);
            if .TEST gtru .ADDR then exitloop CNT = 0;
            TCNT = 3 + ch$rchar_a (TPTR);
            if .TEST eqlu .ADDR then exitloop CNT = .TCNT;
            PTR = ch$plus (.PTR, .TCNT);
            if (CNT = .CNT - .TCNT) leq 0 then exitloop CNT = 0;
            end;
    begin
    local
        DIFFERENCE;
    DIFFERENCE = .LEN - .CNT;
    if (CCB [C_STS] = .CCB [C_STS] - .DIFFERENCE) lss 0
    then
        leave FIND_NODE;
    MAKE_ROOM (.CCB, .PTR, .DIFFERENCE);
    end;
    ch$move (.LEN, byt$ptr (NODE_ID), .PTR);
    MAP$ (.SAVE_MAP);
    end;                                ! FIND_NODE
    end;
routine MAKE_ROOM (CCB, PTR, LNG) : SC_LKG_CCB_PTR_LNG novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	Make room for LNG bytes at PTR in the CCB buffer.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!       PTR     Pointer within CCB buffer
!       LNG     Number of bytes needed
!
! IMPLICIT INPUTS:
!	Mapped to CCB buffer
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	None
!
! SIDE EFFECTS:
!	None
!--
    begin
    map CCB: ref block field (C_NM_FIELDS);
    macro
        ch$r_rchar (PTR) =
            begin
            PTR = .PTR - 1;
            ch$rchar (.PTR)
            end %,
        ch$r_wchar (BYT, PTR) =
            begin
            PTR = .PTR - 1;
            ch$wchar (BYT, .PTR);
            end %;
    local
        CNT,
        TOP_FROM,
        TOP_TO;

    TOP_FROM = .CCB [C_ADDR];
    TOP_FROM = ch$plus (.TOP_FROM, .CCB [C_CNT]);
    CCB [C_CNT] = .CCB [C_CNT] + .LNG;
    CNT = .TOP_FROM;
    CNT = ch$diff (.CNT, .PTR);
    if .CNT eql 0 then return;
    TOP_TO = .TOP_FROM;
    TOP_TO = ch$plus (.TOP_TO, .LNG);
    do ch$r_wchar (ch$r_rchar (TOP_FROM), TOP_TO) while (CNT = .CNT - 1) neq 0;
    end;

end
eludom