Google
 

Trailing-Edge - PDP-10 Archives - BB-R595B-SM_11-9-85 - mcb/nsp/ns2.bli
There is 1 other file named ns2.bli in the archive. Click here to see a list.
module NS2 (	
		ident = 'X01150'
		) =
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:	Network Services Layer
!
! ABSTRACT:	This module allows the transmission and reception of
!		NSP Connect/Disconnect messages.
!
!
! ENVIRONMENT:	MCB
!
! AUTHOR:	Ron Platukis	CREATION DATE:	6-july-80
!
! MODIFIED BY:
!
!	Alan Peckham, Ron Platukis  22-Oct-80
! 01	- Move NDB bind in CCB_RETRN.
!	  Clear C_STS before sending CCB to XPT in XPT_XMIT.
!	  Set C_PRM4 to PDB address or -1 if Reserve Port in XPT_XMIT.
!	Ron Platukis  23-oct-80
! 02	- Set PORTstate negative if  Reserved Port.
!
! 03	Ron Platukis  24-dec-80
!	-Add Network Management Stuff.
!
! 04	Ron Platukis  13-jan-81
!	-Change linkages to BLISS, add access for NSP Data Base address.
!
! 05	Ron Platukis  20-jan-81
!	-Add Network Management event logging code.
!
! 06	Ron Platukis  16-feb-81
!	-Add code to check BUFFsync_dsc in FLUSH_QUEUES routine.
!
! 07	Ron Platukis  30-apr-81
!	-character move function overwrites temporary space in
!	routine SHOW_NODE_COUNTERS.
!
! 08	Ron Platukis  1-may-81
!	-Incorrect status code returned if successful completion
!	of routine RETURN_NODE_LIST.
!
! 09	Ron Platukis  12-june-81
!	-Include 600 series counters in SHOW_NODE_COUNTERS routine
!	if asked for local data.
!
! 10	Ron Platukis  22-september-81
!	-fix routine DI_RCV to send DC if in DI state and receive a DI.
!
! 11	Ron Platukis  2-february-82
!	-modify routine DC_RCV to set CONFIDENCE false if receive a No-Link.
!
! 12	Ron Platukis  2-march-82
!	-set entity type to "node" on event type 2
!
! 13	Alan Peckham  3-may-82
!	-Do not recognize node #0 in FIND_NDB.
!
! 14	Alan Peckham  22-nov-82
!       -Make EVENT_LOG use common event logging routines.
!	-Support seconds-since-last-zeroed counter:
!         Zero counter in NDB_GET.
!         Show counter in EVENT_LOG and SHOW_NODE_COUNTERS.
!       -Optimize SHOW_NODE_COUNTERS by removing loop.
!       -Zero NSPmax when zeroing counters for NSPself.
!
! 15	Alan Peckham  9-dec-82
!       -NDB_GET was not recycling node data bases.
!         The routine was re-coded for correctness and efficiency.
!--
!
! INCLUDE FILES:
!
library
	'mcb:xportx';
library
	'mcb:mcblib';
library
	'mcb:nmxlib';
require
	'nspbli:nsinfo';
require
	'nspbli:nspdata';



!
! TABLE OF CONTENTS:
!
forward routine
	ACK_BLD:   novalue,
	CA_BLD:  novalue,		!format a CA msg
	CA_RCV: CALL$ novalue,		!process Connect Ack
	CI_BLD:  CALL$  novalue,	!format a CI msg
	CI_RECVD:  novalue,
	CI_RCV: CALL$ novalue,		!process Connect Initiate
	CC_BLD:  CALL$  novalue,	!format a CC msg
	CC_RCV: CALL$ novalue,		!process Connect Confirm
	CCB_HDR_GET:,
	CCB_GET:,
	CCB_RETRN: novalue,
	CTL_RECVD:  CALL$ novalue,	!Free stalled Control messages
	C_STSE:  novalue,
	DADD: novalue,
	DC_BLD:  CALL$  novalue,	!format a DC msg
	DC_RCV: CALL$ novalue,		!process Disconnect Confirm
	DI_BLD:  CALL$  novalue,	!format a DI msg
	DI_RCV: CALL$ novalue,		!process Disconnect Initiate
	DSC_RECVD:  novalue,
	EVENT_LOG: CALL$ novalue,	! event logger
	FCCTL: CALL$ novalue,		! Control (i.e., NM)
	FIND_NDB:  ,			! Locate a Node Data base
	FLUSH_QUEUES:  CALL$ novalue,
	NDB_GET: CALL$,
	NL_BLD:  CALL$ novalue,		!format a No-Link
	NR_BLD:  CALL$ novalue,		!format a No-Resource
	NSP_CONFIDENCE:  CALL$ novalue,	! report CONFIDENCE false to SC
	RESV_PORT:  CALL$ novalue,	!Reserve Port Process
	RETURN_NODE_LIST:  ,		!Network Management routines
	SET_NODE_PARMS:  ,		!	""
	SHOW_NODE_ITEMS:  ,		!	""
	SHOW_NODE_COUNTERS:  ,		!	""
	ZERO_NODE_COUNTERS:  ,		!	""
	SHOW_ZERO_NODE_COUNTERS:  ,	!	""
	UPDATE_DELAY:   novalue,	! Update round trip delay
	XPT_XMIT:   novalue;		!queue CCB to XPT

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

!
! EQUATED SYMBOLS:
!
literal				! Autoparsing stuff
	$16$ = 2^13,		! 16 bit wide counter
	$32$ = 3^13,		! 32 bit wide counter
	CNT = 1^15;		! Counter flag


!
! OWN STORAGE:
!
	$MCB_PROCESS( name = NS2)
!
! EXTERNAL REFERENCES:
!
external
	%name('.crdat'): vector[2],
	$DSPCR;

	bind DB = %name('.crdat')[1]: ref block field(NSP_fields);
routine ACK_BLD   ( CCB, PDB) :   novalue =	!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Data Acknowledgement message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
	
	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_DAT_ACK, PTR);
	ch$wchar_a( .PDB[L_rem_addr], PTR);
	ch$wchar_a( .PDB[H_rem_addr], PTR);
	ch$wchar_a( .PDB[PID], PTR);
	ch$wchar_a( .PDB[ADDran], PTR);
	PUT2BYTES( (.PDB[ACKdat_xmt] and %o'7777') + Q_ACK^12 + true^15, PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	PDB[FLAGdat_ack] = false;
	XPT_XMIT( .CCB, .PDB)	
	end;
routine CA_BLD   ( CCB, PDB) : novalue =	!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Connect Acknowledgement message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
	
	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);
	
	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_CA, PTR);
	ch$wchar_a( .PDB[L_rem_addr], PTR);
	ch$wchar_a( .PDB[H_rem_addr], PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	XPT_XMIT( .CCB, .PDB)	
	end;
global routine CA_RCV   ( CCB, PDB) :CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Connect Acknowledgement message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	If .PDB[PORTstate] eql N$SCI
	Then
		begin
		PDB[PORTstate] = N$SCD;
		UPDATE_DELAY( .PDB)
		end;
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
global routine CC_BLD   ( CCB, PDB) :CALL$ novalue =	!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Connect Confirm message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:

!	None
!--
	
	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	PTR;

	PTR = .CCB[C_ADDR];

	CCB[C_CHN] = .CCB[C_STK];

	ch$wchar_a( FLG_CC, PTR);
	ch$wchar_a( .PDB[L_rem_addr], PTR);
	ch$wchar_a( .PDB[H_rem_addr], PTR);
	ch$wchar_a( .PDB[PID], PTR);
	ch$wchar_a( .PDB[ADDran], PTR);
	ch$wchar_a( .PDB[FLOWloc_typ]^2 + 1, PTR);
	ch$wchar_a( 0, PTR);
	PUT2BYTES( .DB[NSPbuf], PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);

	If .PDB[DELAYstr_tim] eql 0
	Then
		PDB[DELAYstr_tim] = 1;
	XPT_XMIT( .CCB, .PDB)
	end;
global routine CC_RCV   ( CCB, PDB ) :CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Connect Confirm message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	PTR,
	NDB : ref block field(NODE_fields),
	WK_CCB: ref block field(C_fields);

	PTR = ch$plus( .CCB[C_ADDR], 5 );
	NDB = .PDB[NODElnk];

	If .PDB[BUFFctl] neq 0
	Then
		begin
		CCB[C_FNC] = FC_RCE;
		LLCRS$( .CCB);
		return
		end;

	MAP$( .CCB[C_BIAS]);
	DB[SERVICES] = ch$rchar_a( PTR);
	DB[INFO] = ch$rchar_a( PTR);
	DB[SEGSIZE] = GET2BYTES( PTR);
	MAP$( .DB[BIASports]);

	If (.DB[FCOPT] neq FLOWrsv) and
	((.DB[SERVICES] and 1) neq 0) and
	((.DB[INFO] and %o'374') eql 0)
	Then
		begin
		IF .PDB[PORTstate] eql N$SCI or
		.PDB[PORTstate] eql N$SCD
		Then
			begin
			MAP$( .DB[BIASnodes]);
			DADD( NDB[NDb_rcv], .CCB[C_CNT] - CC_SIZE);
			MAP$( .DB[BIASports]);
			PDB[L_rem_addr] = .DB[L_SRCADDR];
			PDB[H_rem_addr] = .DB[H_SRCADDR];
			PDB[FLOWtyp_rem] = .DB[FCOPT];
			PDB[VERSIONrem] = .DB[INFO];
			PDB[SIZEseg] = MIN( .DB[SEGSIZE], .DB[NSPbuf]);
			PDB[TIMERinact] = .DB[NSPact_tim];

			If .PDB[PORTstate] eql N$SCI
			Then
				UPDATE_DELAY( .PDB);
			PDB[PORTstate] = N$SRUN;
			PDB[FLAGdat_ack] = true;

			If CCB_GET( WK_CCB, .PDB)
			Then
				begin
				WK_CCB[C_STK] = .CCB;
				C_STSE( .WK_CCB, .PDB)
				end
			Else
				begin
				PDB[BUFFctl] = .CCB;
				return
				end;
			If CCB_HDR_GET( 0, WK_CCB, .PDB)
			Then
				ACK_BLD( .WK_CCB, .PDB)
			Else
				PDB[FLAGdat_ack] = true;
			return
			end;
		If .PDB[PORTstate] eql N$SRUN
		Then
			PDB[FLAGdat_ack] = true
		end;
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
routine CCB_HDR_GET ( CCB, LCCB, PDB ) =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine allocates a CCB and a header buffer from DSR space.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	LCCB = CCB Address to stack
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);
	map LCCB: ref block field(C_fields);

	If not CCBGT$(.LCCB)
	Then
		return false;
	begin
	bind XCCB = .LCCB: ref block field(C_fields);
	XCCB[C_STK] = 0;
	XCCB[C_CHN] = 0;
	XCCB[C_PRM1] = 0;
	XCCB[C_PRM2] = 0;
	XCCB[C_PRM3] = 0;
	XCCB[C_PRM4] = 0;
	XCCB[C_PRM5] = 0;
	XCCB[C_BIAS] = 0;

	If not $MCB_GET_DSR( hdb_size, XCCB[C_ADDR])
	Then
		begin
		CCBRT$(..LCCB);
		return false;
		end;
	If .CCB neq 0
	Then
		XCCB[C_STK] = .CCB;
	end;
	If .PDB neq -1
	Then
		PDB[COUNTres] = .PDB[COUNTres] + 1;
	return true;
	end;
routine CCB_GET (CCB, PDB) =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine allocates a CCB.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map PDB: ref block field(PORT_fields);

	local 
	LCCB: ref block field(C_fields);

	If not CCBGT$(LCCB)
	Then
		return false;
	LCCB[C_STK] = 0;
	LCCB[C_CHN] = 0;
	LCCB[C_BIAS] = 0;
	LCCB[C_ADDR] = 0;
	LCCB[C_PRM1] = 0;
	LCCB[C_PRM2] = 0;
	LCCB[C_PRM3] = 0;
	LCCB[C_PRM4] = 0;
	LCCB[C_PRM5] = 0;

	If .PDB neq -1
	Then
		PDB[COUNTres] = .PDB[COUNTres] + 1;
	.CCB = .LCCB;
	return true;
	end;
routine CCB_RETRN   ( CCB, PDB):  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine deallocates a CCB.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map PDB: ref block field(PORT_fields);

	local
	temp,
	NDB : ref block field(NODE_fields),
	CLZ_CCB: ref block field(C_fields);

	CCBRT$( .CCB);

	If .PDB neq -1
	Then
		begin
		PDB[COUNTres] = .PDB[COUNTres] - 1;

		If .PDB[PORTstate] eql N$SCL and
		.PDB[COUNTres] eql 0
		Then
			begin
			NDB = .PDB[NODElnk];
			PDB[FLAGinuse] = false;
			CMQRM$( PDB[MSG_queue], CLZ_CCB);
			CLZ_CCB[C_FNC] = FC_XCP;
			CLZ_CCB[C_STS] = N$SSUC;
			LLCRS$( .CLZ_CCB);
			end;
		End;
	end;
global routine CTL_RECVD ( PDB) : CALL$  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine attempts to clear out BUFFctl queue if non-zero.
!
! FORMAL PARAMETERS:
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map PDB: ref block field(PORT_fields);

	local
	GOT_CCB: ref block field(C_fields),
	CA_CCB: ref block field(C_fields);	

	Selectone .PDB[PORTstate] of
	Set
	[N$SNR, N$SRUN, N$SRJ]:

		begin
		If CCB_GET(GOT_CCB, .PDB)
		Then
			begin
			GOT_CCB[C_STK] = .PDB[BUFFctl];
			C_STSE( .GOT_CCB, .PDB);
			PDB[BUFFctl] = 0
			End
		Else
			return;
		end;

	[N$SDN]:

		begin
		If CCB_GET(GOT_CCB, .PDB)
		Then
			begin
			GOT_CCB[C_STK] = .PDB[BUFFctl];
			DSC_RECVD( .GOT_CCB, .PDB);
			PDB[BUFFctl] = 0
			end
		Else
			return
		end;

	[N$SCR]:

		begin
		If CCB_GET( GOT_CCB, .PDB)
		Then
			begin
			If not CCB_HDR_GET( 0, CA_CCB, .PDB)
			Then
				begin
				CCB_RETRN( .GOT_CCB, .PDB);
				return
				end;
			CA_BLD( .CA_CCB, .PDB);
			GOT_CCB[C_STK] = .PDB[BUFFctl];
			CI_RECVD( .GOT_CCB, .PDB);
			PDB[BUFFctl] = 0;
			end
		Else
			return;
		end;

	[N$SCN]:

		begin
		If CCB_GET(GOT_CCB, .PDB)
		Then
			begin
			CALL$L( NSP_CONFIDENCE, .GOT_CCB, .PDB);
			CA_CCB = .PDB[BUFFctl];
			CA_CCB[C_FNC] = FC_RCE;
			LLCRS$( .CA_CCB);
			CALL$L( FLUSH_QUEUES,  .PDB, N$EABR);
			end
		Else
			return;
		end;

	Tes;
	end;
routine C_STSE   ( CCB, PDB):  novalue = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine generates a CONNECT-STATUS call to Session Control.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map PDB: ref block field(PORT_fields);
	map CCB: ref block field(C_fields);

	bind STK_CCB = .CCB[C_STK]: block field(C_fields);

	CCB[C_FNC] = FC_RCP;
	CCB[C_MOD] = N_SCNS;
	CCB[C_LIX] = .PDB[PID];
	CCB[C_BIAS] = .STK_CCB[C_BIAS];

	Selectone .PDB[PORTstate] of
	Set

	[N$SNR]:
		begin
		CCB[C_STS] = N$EOPN;
		CCB[C_CNT] = 0;
		CCB[C_ADDR] = 0;
		end;

	[N$SRJ]:
		begin
		CCB[C_STS] = N$SREJ;
		CCB[C_CNT] = .STK_CCB[C_CNT] - DI_SIZE;
		CCB[C_ADDR] = ch$plus(.STK_CCB[C_ADDR], DI_SIZE);
		end;

	[N$SRUN]:
		begin
		CCB[C_STS] = N$SACC;
		CCB[C_CNT] = .STK_CCB[C_CNT] - CC_SIZE;
		CCB[C_ADDR] = ch$plus(.STK_CCB[C_ADDR], CC_SIZE);
		end;
	Tes;
	CCB[C_PIX] = .DB[NSPsc_pix];
	LLCRS$( .CCB);
	end;
global routine CI_BLD  ( CCB, PDB) :CALL$ novalue =	!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Connect Initiate message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	NDB : ref block field(NODE_fields),
	PTR;

	PTR = .CCB[C_ADDR];
	NDB = .PDB[NODElnk];
	ch$wchar_a( FLG_CI, PTR);
	ch$wchar_a( 0, PTR);
	ch$wchar_a( 0, PTR);
	ch$wchar_a( .PDB[PID], PTR);
	ch$wchar_a( .PDB[ADDran], PTR);
	ch$wchar_a( .PDB[FLOWloc_typ]^2 + 1, PTR);
	ch$wchar_a( 0, PTR);
	PUT2BYTES( .DB[NSPbuf], PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	CCB[C_CHN] = .CCB[C_STK];
	PDB[DELAYstr_tim] = 1;
	MAP$( .DB[BIASnodes]);
	NDB[NDc_xmt] = .NDB[NDc_xmt] + 1;
	MAP$( .DB[BIASports]);
	XPT_XMIT( .CCB, .PDB)
	end;
	
	
routine CI_RECVD   ( CCB, PDB):  novalue = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine generates a CONNECT-RECIEVED call to Session
!	Control.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address

!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map PDB: ref block field(PORT_fields);
	map CCB: ref block field(C_fields);

	bind STK_CCB = .CCB[C_STK]: block field(C_fields);

	CCB[C_FNC] = FC_RCP;
	CCB[C_MOD] = N_RCON;
	CCB[C_LIX] = .PDB[PID];
	CCB[C_CNT] = .STK_CCB[C_CNT] - CI_SIZE;
	CCB[C_BIAS] = .STK_CCB[C_BIAS];
	CCB[C_ADDR] = ch$plus(.STK_CCB[C_ADDR], CI_SIZE);
	CCB[C_PRM1] = .STK_CCB[C_PRM1];
	CCB[C_PIX] = .DB[NSPsc_pix];
	LLCRS$( .CCB);
	end;
global routine CI_RCV   ( CCB, PDB) :CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Connect Initiate message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	GOT_CCB: ref block field(C_fields),
	CA_CCB: ref block field(C_fields),
	PTR,
	temp;

	bind NODE = .DB[ADDRnodes]: blockvector[0, ND_SIZE] field(NODE_fields);

	PTR = ch$plus( .CCB[C_ADDR], 5 );

	MAP$( .CCB[C_BIAS]);
	DB[SERVICES] = ch$rchar_a( PTR);
	DB[INFO] = ch$rchar_a( PTR);
	DB[SEGSIZE] = GET2BYTES( PTR);
	MAP$( .DB[BIASports]);

	If (.DB[FCOPT] neq FLOWrsv) and
	(.DB[L_DSTADDR] eql 0 and .DB[H_DSTADDR] eql 0) and
	((.DB[SERVICES] and 1) neq 0) and
	((.DB[INFO] and %o'374') eql 0)
	Then
		begin
		If not CALL$L( NDB_GET, .CCB, temp)
		Then
			begin
			CALL$L(RESV_PORT, .CCB);
			return
			end
		Else
			begin
			bind NDB = .temp: block field(NODE_fields);
			PDB[NODElnk] = .temp;
			MAP$( .DB[BIASnodes]);
			NDB[NDc_rcv] = .NDB[NDc_rcv] + 1;
			DADD( NDB[NDb_rcv], .CCB[C_CNT] - CI_SIZE);
			MAP$( .DB[BIASports]);
			PDB[NODErem] = .CCB[C_PRM1];

			If .PDB[NODErem] eql .DB[NSPself]
			Then
				PDB[CHANNEL] = .CCB[C_LIX]
			Else
				PDB[CHANNEL] = 0;
			PDB[PORTstate] = N$SCR;
			DB[NSPmax] = .DB[NSPmax] + 1;
			PDB[FLOWtyp_rem] = .DB[FCOPT];
			PDB[VERSIONrem] = .DB[VER];
			PDB[L_rem_addr] = .DB[L_SRCADDR];
			PDB[H_rem_addr] = .DB[H_SRCADDR];
			PDB[SIZEseg] = MIN( .DB[SEGSIZE], .DB[NSPbuf]);

			If CCB_GET( GOT_CCB, .PDB)
			Then
				begin
				If not CCB_HDR_GET( 0, CA_CCB, .PDB)
				Then
					begin
					CCB_RETRN( .GOT_CCB, .PDB);
					PDB[BUFFctl] = .CCB;
					return
					end;
				CA_BLD( .CA_CCB, .PDB);
				GOT_CCB[C_STK] = .CCB;
				CI_RECVD( .GOT_CCB, .PDB);
				end
			Else
				PDB[BUFFctl] = .CCB;

			end
		end
	Else
		begin
		CCB[C_FNC] = FC_RCE;
		LLCRS$( .CCB)
		end
	end;
routine DADD ( double_word, value ) :novalue =		!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine performs double percision arithemitic.
!
! FORMAL PARAMETERS:
!	DOUBLE_WORD = address to add VALUE to
!	VALUE = value to add to DOUBLE_WORD
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	builtin  ROT;
	map double_word: ref block field(long_word);

	if rot(double_word[low] = .double_word[low] + .value, 1)
	then
		begin
		if rot(double_word[hi] = .double_word[hi] + 1, 1)
		then
			begin
			double_word[low] = -1;
			double_word[hi] = -1;
			end
		end
	end;
global routine DC_BLD   ( CCB, PDB) :CALL$ novalue =	!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Disconnect Confirm message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
	
	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);
	
	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_DC, PTR);
	ch$wchar_a( .PDB[L_rem_addr], PTR);
	ch$wchar_a( .PDB[H_rem_addr], PTR);
	ch$wchar_a( .PDB[PID], PTR);
	ch$wchar_a( .PDB[ADDran], PTR);
	ch$wchar_a( 42, PTR);
	ch$wchar_a( 0, PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	PDB[FLAGsnd_dc] = false;
	XPT_XMIT( .CCB, .PDB)	
	end;
global routine DC_RCV   ( CCB, PDB ) :CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Disconnect Confirm message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	PTR,
	WK_CCB: ref block field(C_fields);

	PTR = ch$plus( .CCB[C_ADDR], 5 );
	MAP$( .CCB[C_BIAS]);
	DB[REASON] = GET2BYTES( PTR);
	MAP$( .DB[BIASports]);

	Selectone .DB[REASON] of
	Set
	[1]:

		begin
		PDB[PORTstate] = N$SNR;
		If CCB_GET( WK_CCB, .PDB)
		Then
			begin
			WK_CCB[C_STK] = .CCB;
			C_STSE( .WK_CCB, .PDB);
			return
			end
		Else
			begin
			If .PDB[BUFFctl] eql 0
			Then
				begin
				PDB[BUFFctl] = .CCB;
				return
				end;
			end;
		end;
	[41]:
		begin
		If .PDB[PORTstate] eql  N$SCC or
		.PDB[PORTstate] eql N$SRUN or
		.PDB[PORTstate] eql N$SDR or
		.PDB[PORTstate] eql N$SDI
		Then
			begin
			PDB[PORTstate] = N$SCN;
			PDB[TIMERcon] = 0;
			PDB[TIMERdat] = 0;
			PDB[TIMERoth] = 0;
			If .PDB[CONFIDENCE]
			Then
				begin
				If CCB_GET( WK_CCB, .PDB)
				Then
					CALL$L( NSP_CONFIDENCE, .WK_CCB, .PDB)
				Else
					begin
					If .PDB[BUFFctl] eql 0
					Then
						begin
						PDB[BUFFctl] = .CCB;
						return
						end;
	
					end;
				end;
			end;
		CALL$L( FLUSH_QUEUES,  .PDB, N$EABR);
		end;
	[42]:

		begin
		If .PDB[PORTstate] eql N$SDR or
		.PDB[PORTstate] eql N$SDI or
		.PDB[PORTstate] eql N$SDIC
		Then
			begin
			If .PDB[PORTstate] eql N$SDR
			Then
				PDB[PORTstate] = N$SDRC
			Else
				PDB[PORTstate] = N$SDIC;
			PDB[TIMERcon] = 0;
			UPDATE_DELAY( .PDB);

			If CMQRM$( PDB[MSG_queue], WK_CCB)
			Then
				begin
				WK_CCB[C_STS] = N$SSUC;
				WK_CCB[C_FNC] = FC_XCP;
				LLCRS$( .WK_CCB)
				end;
			end;
		end;
	[otherwise]:

		begin
		If .PDB[VERSIONrem] eql V3_1 
		Then
			begin
			If .PDB[PORTstate] eql N$SCI
			Then
				begin
				PDB[PORTstate] = N$SRJ;
				UPDATE_DELAY( .PDB);

				If CCB_GET( WK_CCB, .PDB)
				Then
					begin
					WK_CCB[C_STK] = .CCB;
					C_STSE( .WK_CCB, .PDB);
					return
					end
				Else
					begin
					If .PDB[BUFFctl] eql 0
					Then
						begin
						PDB[BUFFctl] = .CCB;
						return
						end;
					end
				end
			Else
				begin
				If .PDB[PORTstate] eql  N$SCC or
				.PDB[PORTstate] eql N$SRUN or
				.PDB[PORTstate] eql N$SDR or
				.PDB[PORTstate] eql N$SDI
				Then
					begin
					PDB[PORTstate] = N$SCN;
					PDB[TIMERcon] = 0;
					PDB[TIMERdat] = 0;
					PDB[TIMERoth] = 0;
					If .PDB[CONFIDENCE]
					Then
						begin
						If CCB_GET( WK_CCB, .PDB)
						Then
							CALL$L( NSP_CONFIDENCE, .WK_CCB, .PDB)
						Else
							begin
							If .PDB[BUFFctl] eql 0
							Then
								begin
								PDB[BUFFctl] = .CCB;
								return
								end;
							end;
						end;
					end;					
				end;
			CALL$L( FLUSH_QUEUES,  .PDB, N$EABR);
			end;
		end;
	Tes;
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
global routine DI_BLD   ( CCB, PDB) :CALL$ novalue =!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Disconnect Initiate message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
	
	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	PTR;

	PTR = .CCB[C_ADDR];

	CCB[C_CHN] = .CCB[C_STK];

	ch$wchar_a( FLG_DI, PTR);
	ch$wchar_a( .PDB[L_rem_addr], PTR);
	ch$wchar_a( .PDB[H_rem_addr], PTR);
	ch$wchar_a( .PDB[PID], PTR);
	ch$wchar_a( .PDB[ADDran], PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);

	If .PDB[DELAYstr_tim] eql 0
	Then
		PDB[DELAYstr_tim] = 1;
	XPT_XMIT( .CCB, .PDB)	
	end;

	
global routine DI_RCV   ( CCB, PDB ) :CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Disconnect Initiate message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	NDB : ref block field(NODE_fields),
	NO_RCE,
	WK_CCB: ref block field(C_fields);

	NO_RCE = false;
	NDB = .PDB[NODElnk];

	If .PDB[BUFFctl] neq 0
	Then
		begin
		CCB[C_FNC] = FC_RCE;
		LLCRS$( .CCB);
		return
		end;

	Selectone .PDB[PORTstate] of
	Set

	[N$SDI]:
		begin
		PDB[PORTstate] = N$SDIC;
		PDB[DELAYstr_tim] = 0;
		PDB[TIMERcon] = 0;
		CALL$L( FLUSH_QUEUES,  .PDB, N$EABR);
		end;

	[N$SCI, N$SCD, N$SRUN]:
		begin
		If .PDB[PORTstate] eql N$SCI
		Then
			UPDATE_DELAY( .PDB);
		If .PDB[PORTstate] eql N$SCI or
		.PDB[PORTstate] eql N$SCD
		Then
			begin
			PDB[PORTstate] = N$SRJ;
			PDB[L_rem_addr] = .DB[L_SRCADDR];
			PDB[H_rem_addr] = .DB[H_SRCADDR];
			end;
		If .PDB[PORTstate] eql N$SRUN
		Then
			begin
			PDB[PORTstate] = N$SDN;
			CALL$L( FLUSH_QUEUES,  .PDB, N$EABR);
			PDB[DELAYstr_tim] = 0;
			PDB[TIMERdat] = 0;
			PDB[TIMERoth] = 0;
			end;
		MAP$( .DB[BIASnodes]);
		DADD( NDB[NDb_rcv], .CCB[C_CNT] - DI_SIZE);
		MAP$( .DB[BIASports]);

		If CCB_GET( WK_CCB, .PDB)
		Then
			begin
			WK_CCB[C_STK] = .CCB;

			If .PDB[PORTstate] eql N$SRJ
			Then
				C_STSE( .WK_CCB, .PDB)
			Else
				DSC_RECVD( .WK_CCB, .PDB);
			end
		Else
			PDB[BUFFctl] = .CCB;
		NO_RCE = true;
		end;
	Tes;

	If .PDB[PORTstate] eql N$SDN or
	.PDB[PORTstate] eql N$SRJ or
	.PDB[PORTstate] eql N$SDIC
	Then
		begin
		If CCB_HDR_GET( 0, WK_CCB, .PDB)
		Then
			CALL$L(DC_BLD,  .WK_CCB, .PDB)
		Else
			PDB[FLAGsnd_dc] = true
		end;

	If .NO_RCE eql false
	Then
		begin
		CCB[C_FNC] = FC_RCE;
		LLCRS$( .CCB)
		end;
	end;
routine DSC_RECVD   ( CCB, PDB):  novalue = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine generates a DISCONNECT-RECIEVED call to Session
!	Control.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map PDB: ref block field(PORT_fields);
	map CCB: ref block field(C_fields);

	bind STK_CCB = .CCB[C_STK]: block field(C_fields);

	CCB[C_FNC] = FC_RCP;
	CCB[C_MOD] = N_RDSC;
	CCB[C_LIX] = .PDB[PID];
	CCB[C_BIAS] = .STK_CCB[C_BIAS];
	CCB[C_CNT] = .STK_CCB[C_CNT] - DI_SIZE;
	CCB[C_ADDR] = ch$plus(.STK_CCB[C_ADDR], DI_SIZE);
	CCB[C_PIX] = .DB[NSPsc_pix];
	LLCRS$( .CCB);
	end;
global routine EVENT_LOG ( type, aux, PDB, CCB) :  CALL$  novalue  = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine logs events.
!
! FORMAL PARAMETERS:
!	type = event type to log
!	aux = auxillary data required to log event
!	PDB = Port data base address
!	CCB = CCB address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map CCB: ref block field(C_fields);
	map PDB: ref block field(PORT_fields);

	local
	save_bias,
	temp;

	SMAP$( save_bias);
        MAP$( .CCB[C_BIAS]);
        $NM_LOG_BEGIN();

	Case .type from invmsg to invflw of
	Set
		[invmsg]:
			begin
                        PARAMETER_HI( 0, 12, .CCB[C_ADDR]);
			end;			

		[invflw]:	
			begin
                        PARAMETER_HI( 0, 12, .CCB[C_ADDR]);
                        PARAMETER_DS_1( 1, aux);
			end;
	Tes;
	$NM_LOG_END((.type + NSP_class_event^6), 0);
	MAP$( .save_bias);			
	end;
global routine FCCTL ( CCB, FCM): CALL$ novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is activated by a control request from
!	Network Management.
!
! FORMAL PARAMETERS:
!	CCB	CCB to pass to handler routine
!	FCM	Function code modifier (Must be FM_NM)
!
! IMPLICIT INPUTS:
!	CCB Contents
!	DB	Data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field (C_NM_FIELDS);

	local
	STATUS;

	MAP$( .DB[BIASnodes]);
	STATUS =
		begin
		if .FCM 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_zro]:
					ZERO_NODE_COUNTERS( .CCB);	! Zero counters

				[NMX$FNC_sho]:
					SHOW_NODE_ITEMS ( .CCB);	! Show selected items
				[NMX$FNC_szc]:
					SHOW_ZERO_NODE_COUNTERS( .CCB);	! Zero and show counters
				[NMX$FNC_ret]:
					RETURN_NODE_LIST ( .CCB);	! Return selected items
				[inrange, outrange]:
					$NM$ERR_MPE;
				tes
			    end;

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

	CCB[C_FNC] = FC_CCP;
	CCB[C_STS] = .STATUS;
	LLCRS$( .CCB);
	end;
routine FIND_NDB ( NODEADDR):   =

!++
! FUNCTIONAL DESCRIPTION:
!	Locate the NDB for the Node addressed by NM_ENT in NMPAR
!
! FORMAL PARAMETERS:
!	NODEADDR	Address to return NDB address
!
! IMPLICIT INPUTS:
!	DB		Data base address
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
!
! SIDE EFFECTS:
!	None
!--

	begin
	bind NODE = .DB[ADDRnodes]: blockvector[ 0, ND_SIZE] field( NODE_fields);
        If ..NODEADDR eql 0 then return false;
	Incr j from 0 to (.DB[NSPt_nodes] -1) do
		begin
		bind NDB = NODE[ .j, 0, 0, 0, 0]: block field(NODE_fields);
		If .NDB[NDaddr] eql ..NODEADDR 
		Then
			begin
			.NODEADDR = NDB;
			return true
			end
		end;
	false
	end;
global routine FLUSH_QUEUES ( PDB, status_code) : CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine flushes all of NSP's processing queues.
!
! FORMAL PARAMETERS:
!	PDB = Port Data Base address
!	STATUS_CODE = Code to return to Session Control
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map PDB: ref block field(PORT_fields);


	local 
	RTN_CCB: ref block field(C_fields),
	STK_CCB: ref block field(C_fields);

	While CMQRM$(PDB[INT_queue], rtn_ccb) do
		begin
		RTN_CCB[C_STS] = .status_code;
		RTN_CCB[C_FNC] = FC_XCP;
		LLCRS$( .RTN_CCB)
		end;

	If .PDB[BUFFrcv_int] neq 0
	Then
		begin
		RTN_CCB = .PDB[BUFFrcv_int];
		PDB[BUFFrcv_int] = 0;
		RTN_CCB[C_FNC] = FC_RCE;
		LLCRS$( .RTN_CCB)
		end;
	
	If .PDB[BUFFctl] neq 0
	Then
		begin
		RTN_CCB = .PDB[BUFFctl];
		PDB[BUFFctl] = 0;
		RTN_CCB[C_FNC] = FC_RCE;
		LLCRS$( .RTN_CCB)
		end;

	If .PDB[BUFFsync_dsc] neq 0
	Then
		begin
		RTN_CCB = .PDB[BUFFsync_dsc];
		PDB[BUFFsync_dsc] = 0;
		RTN_CCB[C_FNC] = FC_XCP;
		RTN_CCB[C_STS] = .status_code;
		LLCRS$( .RTN_CCB)
		end;

	While ( rtn_ccb = .PDB[ACK_queue]) neq 0 do
		begin
		PDB[ACK_queue] = .rtn_ccb[C_LNK];

		If (.RTN_CCB[C_PRM3] and N$FCMP) neq 0
		Then
			begin
			STK_CCB = .RTN_CCB[C_STK];
			STK_CCB[C_STS] = .status_code;
			STK_CCB[C_FNC] = FC_XCP;
			LLCRS$( .STK_CCB)
			end;
		CCBRT$( .rtn_ccb);
		PDB[COUNTres] = .PDB[COUNTres] -1
		end;

	While CMQRM$(PDB[MSG_queue], rtn_ccb) do
		begin
		RTN_CCB[C_STS] = .status_code;
		RTN_CCB[C_FNC] = FC_XCP;
		LLCRS$( .RTN_CCB)
		end;

	While CMQRM$(PDB[OTHack_queue], rtn_ccb) do
		begin
		RTN_CCB[C_STS] = .status_code;
		RTN_CCB[C_FNC] = FC_XCP;
		LLCRS$( .RTN_CCB)
		end;

	While CMQRM$(PDB[INTR_queue], rtn_ccb) do
		begin
		RTN_CCB[C_STS] = .status_code;
		RTN_CCB[C_FNC] = FC_XCP;
		LLCRS$( .RTN_CCB)
		end;

	While CMQRM$(PDB[DATR_queue], rtn_ccb) do
		begin
		RTN_CCB[C_STS] = .status_code;
		RTN_CCB[C_FNC] = FC_XCP;
		LLCRS$( .RTN_CCB)
		end;


	While CMQRM$(PDB[BUFF_queue], rtn_ccb) do
		begin
		RTN_CCB[C_STS] = .status_code;
		RTN_CCB[C_FNC] = FC_XCP;
		LLCRS$( .RTN_CCB)
		end;

	While CMQRM$(PDB[RCVMSG_queue], rtn_ccb) do
		begin
		RTN_CCB[C_FNC] = FC_RCE;
		LLCRS$( .RTN_CCB)
		end;

	end;
global routine NDB_GET  ( CCB, RTN_NDB):  CALL$  = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine attempts to find a Node Data Base entry for the node
!	in question.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	RTN_NDB = address to return found NDB address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map CCB: ref block field(C_fields);
	map RTN_NDB: block field(NODE_fields);
        label NDB_FOUND, NODE_FOUND;
        local NNDB: ref block field(NODE_fields),
              FNDB: ref block field(NODE_fields);

	MAP$ (.DB[BIASnodes]);
        NNDB = .DB[ADDRnodes];
        FNDB = 0;
NDB_FOUND: begin
NODE_FOUND: begin

        decru J from .DB[NSPt_nodes] to 1
        do begin
           if .NNDB[NDaddr] eql .CCB[C_PRM1]
           then leave NODE_FOUND;
           if (.NNDB[NDactive] eql 0)
           then begin
                if (.FNDB eqla 0)
                then FNDB = .NNDB
                else if (.FNDB[NDaddr] neq 0)
                     then if (.NNDB[NDaddr] eql 0)
                          then FNDB = .NNDB;
                end;
           NNDB = vector[.NNDB,ND_SIZE];
           end;

        If (NNDB = .FNDB) eqla 0 then leave NDB_FOUND;

        If .NNDB[NDaddr] neq 0
        Then begin                      ! Issue "data base reused" event
             literal event_code = (dbruse + NSP_class_event^6) or %o'100000';
             $NM_LOG_BEGIN();
             COUNTER_16_TIME( 0, NNDB[NDtime_zeroed]);
             COUNTER_32( 600, NNDB[NDb_rcv]);
             COUNTER_32( 601, NNDB[NDb_xmt]);
             COUNTER_32( 610, NNDB[NDm_rcv]);
             COUNTER_32( 611, NNDB[NDm_xmt]);
             COUNTER_16( 620, NNDB[NDc_rcv]);
             COUNTER_16( 621, NNDB[NDc_xmt]);
             COUNTER_16( 630, NNDB[NDtimeout]);
             COUNTER_16( 640, NNDB[NDc_rej]);
             COUNTER_16( 700, NNDB[NDactive]);
             $NM_LOG_END(event_code, .NNDB[NDaddr]);
             end;

        decru J from ND_SIZE to 1
        do begin
           map FNDB: ref vector;
           FNDB[0] = 0;
           FNDB = FNDB[1];
           end;

        $NM_TIME( NNDB[NDtime_zeroed]);
        NNDB[NDaddr] = .CCB[C_PRM1];
        end;                            ! of NODE_FOUND

        NNDB[NDactive] = .NNDB[NDactive] + 1;
        end;                            ! of NDB_FOUND

        MAP$( .DB[BIASports]);
        if (.RTN_NDB = .NNDB) neqa 0 then true else false
	end;



	
global routine NL_BLD   ( CCB, RDB) : CALL$ novalue =	!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP No Link message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	RDB = Reserved Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
	
	begin
	map CCB: ref block field(C_fields);
	map RDB: ref block field(PORT_fields);

	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_DC, PTR);
	ch$wchar_a( .RDB[L_rem_addr], PTR);
	ch$wchar_a( .RDB[H_rem_addr], PTR);
	ch$wchar_a( .RDB[PID], PTR);
	ch$wchar_a( .RDB[ADDran], PTR);
	ch$wchar_a( 41, PTR);
	ch$wchar_a( 0, PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	XPT_XMIT( .CCB, .RDB)
	end;
	
global routine NR_BLD   ( CCB, RDB) : CALL$ novalue =	!
	
!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP No Resources message.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	RDB = Reserved Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
	
	begin
	map CCB: ref block field(C_fields);
	map RDB: ref block field(PORT_fields);

	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_DC, PTR);
	ch$wchar_a( .RDB[L_rem_addr], PTR);
	ch$wchar_a( .RDB[H_rem_addr], PTR);
	ch$wchar_a( .RDB[PID], PTR);
	ch$wchar_a( .RDB[ADDran], PTR);
	ch$wchar_a( 1, PTR);
	ch$wchar_a( 0, PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	XPT_XMIT( .CCB, .RDB)
	end;
	
global routine NSP_CONFIDENCE ( CCB, PDB): CALL$ novalue = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine generates a CONFIDENCE call to Session Control.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	PDB = Port Data Base address
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


	begin
	map PDB: ref block field(PORT_fields);
	map CCB: ref block field(C_fields);

	PDB[CONFIDENCE] = false;
	SIGNAL( NSP$_CFN,.PDB[PID]);
	
	CCB[C_FNC] = FC_RCP;
	CCB[C_MOD] = N_SCNF;
	CCB[C_LIX] = .PDB[PID];
	CCB[C_STS] = N$ECON;
	CCB[C_PIX] = .DB[NSPsc_pix];
	CCB[C_ADDR] = 0;
	CCB[C_STK] = 0;
	LLCRS$( .CCB);
	end;
global routine RESV_PORT ( CCB ) :CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is the reserve port process for NSP.
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);

	bind NODE = .DB[ADDRnodes]: blockvector[0, ND_SIZE] field(NODE_fields);

	local
	RDB: ref block field( PORT_fields),
	temp;

	bind RPORT = .DB[ADDRresv]: blockvector[0, RDB_size] field(PORT_fields);
	MAP$( .DB[BIASresv]);

	RDB = (Incr j from 0 to .DB[NSPt_rsv] -1 do
		begin
		bind X = RPORT[.j, 0, 0, 0, 0]: block field(PORT_fields);

		If .X[MSGtyp] eql MSG_NONE
		Then
			exitloop X
		end);
	If .RDB neq -1
	Then
		begin		
		MAP$( .DB[BIASnodes]);

		temp = ( Incr j from 0 to .DB[NSPt_nodes] - 1 do
			begin
			bind NDB = NODE[ .j, 0, 0, 0, 0]: block field(NODE_fields);
			If .NDB[NDaddr] eql .CCB[C_PRM1]
			Then
				exitloop NDB
			end);
		MAP$( .DB[BIASresv]);

		If .temp neq -1
		Then
			RDB[NODElnk] = .temp
		Else
			RDB[NODElnk] = 0;
		RDB[NODErem] = .CCB[C_PRM1];
		RDB[CHANNEL] = .CCB[C_LIX];
		RDB[L_rem_addr] = .DB[L_SRCADDR];
		RDB[H_rem_addr] = .DB[H_SRCADDR];

		Selectone .DB[MSGFLG] of
		Set
		[FLG_CI]:
			begin
			RDB[MSGtyp] = N_RES;
			RDB[ADDRloc] = 0;

			If .RDB[NODElnk] neq 0
			Then
				begin
				local NDB :ref block field(NODE_fields);
				NDB = .RDB[NODElnk];
				MAP$( .DB[BIASnodes]);
				NDB[NDc_rej] = .NDB[NDc_rej] + 1;
				MAP$( .DB[BIASresv]);
				end;
			If CCB_HDR_GET( 0, temp, -1)
			Then
				begin
				CALL$L( NR_BLD,  .temp, .RDB);
				RDB[MSGtyp] = MSG_NONE;
				end

			end;
		[FLG_CC, FLG_DI, FLG_INT, FLG_REQ, FLG_NBEOM, FLG_BOM, FLG_EOM, FLG_BEOM]:
			begin
			RDB[MSGtyp] = N_LNK;
			RDB[PID] = .DB[L_DSTADDR];
			RDB[ADDran] = .DB[H_DSTADDR];

			If CCB_HDR_GET( 0, temp, -1)
			Then
				begin
				CALL$L(NL_BLD,  .temp, .RDB);
				RDB[MSGtyp] = MSG_NONE;
				end
			end
		Tes;
		end;

	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
routine RETURN_NODE_LIST ( CCB):   =

!++
! FUNCTIONAL DESCRIPTION:
!	Return list of known nodes.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	DB	Data base address
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
!
! SIDE EFFECTS:
!	None
!--

	begin
	map
	CCB: ref block field (C_NM_FIELDS);

	
	bind NODE = .DB[ADDRnodes]: blockvector[ 0, ND_SIZE] field( NODE_fields);
	local
	STS,
	BUF_PTR,
	BUF_LEN,
	NODEADDR;

	BUF_PTR = .CCB [C_ADDR];
	BUF_LEN = .CCB [C_CNT];
	STS = NM$SUC;

	Incr j from 0 to (.DB[NSPt_nodes] -1) do
		begin
		bind NDB = NODE[ .j, 0, 0, 0, 0]: block field(NODE_fields);

		If .NDB[NDaddr] neq 0
		Then
			begin
			IF .BUF_LEN lss 3
			Then
				(sts = $NM$ERR_IMF;exitloop);
			NODEADDR = .NDB[NDaddr];
			MAP$( .CCB[C_BIAS]);
			byt$short_string( NODEADDR, BUF_PTR);
			ch$wchar_a( 0, BUF_PTR);
			MAP$( .DB[BIASnodes]);
			BUF_LEN = .BUF_LEN - 3;
			end;
		end;
	CCB[C_CNT] = ch$diff( .BUF_PTR, .CCB[C_ADDR]);
	return .STS
	end;
routine SET_NODE_PARMS ( CCB):   =

!++
! FUNCTIONAL DESCRIPTION:
!	Perform setting of specified node parameter.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	DB	Data base address
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
	map CCB: ref block field (C_NM_FIELDS);
    local
	NIX,
	BUF_PTR,
	PARM_NUM,
	TIMER;
    bind
	NMP = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS),
	NMPAR = NMP [NMX_NMPAR_ENTITY];


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

    selectone .PARM_NUM of
	set
	[502]:			! Set node number
		begin
		If not .DB[NSPrunning]
		Then
			byt$string_short( BUF_PTR, DB[NSPself])
		Else
			return $NM$ERR_CWS
		end;

	[710]:			! Set max links
		begin
		return $NM$ERR_OPF
		end;

	[720]:			! Set local delay factor
		begin
		DB[NSPdelay] = ch$rchar( .BUF_PTR);
		end;

	[721]:			! Set local delay weight
		begin
		DB[NSPweight] = ch$rchar( .BUF_PTR);
		end;

	[722]:			! Set inactivity timer
		begin
		byt$string_short( BUF_PTR, DB[NSPact_tim])
		end;

	[723]:			! Set retransmission threshold
		begin
		byt$string_short( BUF_PTR, DB[NSPretrans])
		end;

	[otherwise]:
		return $NM$ERR_UPT
	tes;
	
	MAP$( .DB[BIASports]);
	return NM$SUC
	end;
routine SHOW_NODE_ITEMS ( CCB):   =

!++
! FUNCTIONAL DESCRIPTION:
!	Show specified items
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!	DB	Data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map
	CCB: ref block field (C_NM_FIELDS);


	local
	DAT_LST: ref vector,
	PARM_NUM,
	NDB: ref block field(NODE_fields),
	HOLDIT,
	STS;

	bind
	NMP = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS),
	NMPAR = NMP [NMX_NMPAR_ENTITY];

	NDB = .NMPAR;

	DAT_LST = (selectone .CCB [C_NM_SELECT] of
		set
		[NMX$SEL_cha]:
			begin
			if .NMPAR eql .DB[NSPself]
			then
				plit (700, 710, 720, 721, 722, 723)
			else
				plit( -1)
			end;
		[NMX$SEL_sum, NMX$SEL_sta]:
			plit (600, 601);
		[otherwise]:
			plit (-1);
	tes);

	IF .CCB [C_NM_SELECT] eql NMX$sel_cou
	Then
		begin
		STS = SHOW_NODE_COUNTERS( .CCB );
		return .STS
		end;


	$NM_RESPONSE_BEGIN( .CCB);

	incr I from 0 to .DAT_LST [-1]-1 do
		selectone .DAT_LST [.I] of
		set
		[-1]:	CCB[C_STS] = NM$SUC;

		[600]:			! Active Links to remote
			begin
			If .NDB eql .DB[NSPself]
			Then
				begin
				PARAMETER_DU_2( 600, DB[NSPmax]);
				exitloop	
				end;

			If not FIND_NDB( NDB)
			Then
				begin
				CCB[C_CNT] = 0;
				return NM$SUC
				end
			Else
				begin
				PARAMETER_DU_2( 600, NDB[NDACTIVE]);
				end;
			end;
		
		[601]:			! Node Delay
			begin
			PARAMETER_DU_2( 601, NDB[NDdelay]);
			end;

		[700]:			! NSP Version
			begin
			bind ralph = uplit(3,2,0): vector;
			PARAMETER_CM( 700, 3);
			PARAMETER_DU_1( ,ralph[0]);
			PARAMETER_DU_1( ,ralph[1]);
			PARAMETER_DU_1( ,ralph[2]);
			end;

		[710]:			! Max Links
			begin
			PARAMETER_DU_2( 710, DB[NSPtotal]);
			end;

		[720]:			! NSP Delay
			begin
			PARAMETER_DU_1( 720, DB[NSPdelay]);
			end;

		[721]:			! NSP Weight
			begin
			PARAMETER_DU_1( 721, DB[NSPweight]);
			end;

		[722]:			! NSP Inactivity
			begin
			PARAMETER_DU_2( 722, DB[NSPact_tim]);
			end;

		[723]:			! NSP Retransmit
			begin
			PARAMETER_DU_2( 723, DB[NSPretrans]);
			end;

		Tes;

    $NM_RESPONSE_END( .CCB);
    return .CCB[C_STS]
    end;
Routine SHOW_NODE_COUNTERS ( CCB):   =

!++
! FUNCTIONAL DESCRIPTION:
!	Show specified items
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	DB	Data base address
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map
	CCB: ref block field (C_NM_FIELDS);


	bind
	NMP = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS),
	NMPAR = NMP [NMX_NMPAR_ENTITY];

	local
	NDB: ref block field(NODE_fields);

	$NM_RESPONSE_BEGIN( .CCB);
	NDB = .NMPAR;

        If FIND_NDB( NDB)
        Then
            begin
            COUNTER_16_TIME( 0, NDB[NDtime_zeroed]);
            COUNTER_32( 600, NDB[NDb_rcv]);
            COUNTER_32( 601, NDB[NDb_xmt]);
            COUNTER_32( 610, NDB[NDm_rcv]);
            COUNTER_32( 611, NDB[NDm_xmt]);
            COUNTER_16( 620, NDB[NDc_rcv]);
            COUNTER_16( 621, NDB[NDc_xmt]);
            COUNTER_16( 630, NDB[NDtimeout]);
            COUNTER_16( 640, NDB[NDc_rej]);
            If .NDB[NDaddr] eql .DB[NSPself]
            Then
                COUNTER_16( 700, DB[NSPmax]);
            end;

	$NM_RESPONSE_END( .CCB);
	return .CCB[C_STS]
	end;
Routine SHOW_ZERO_NODE_COUNTERS ( CCB):   =

!++
! FUNCTIONAL DESCRIPTION:
!	Show specified items
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	DB	Data base address
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--
	begin
	local
	STS;

	STS = SHOW_NODE_COUNTERS( .CCB);
	if .sts neq NM$SUC then return .STS;
	STS = ZERO_NODE_COUNTERS( .CCB);
	return .STS
	end;
Routine ZERO_NODE_COUNTERS ( CCB):   =

!++
! FUNCTIONAL DESCRIPTION:
!	Zero out node counters for a perticular node.
!
! FORMAL PARAMETERS:
!	CCB	CCB address
!
! IMPLICIT INPUTS:
!	DB	Data base address
!	PARBLK & NMPAR inputs, as pointed at by CCB.
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	Operation status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map
	CCB: ref block field (C_NM_FIELDS);


	bind
	NMP = CCB [C_NM_NMPAR]: ref block field (NMX_NMPAR_FIELDS),
	NMPAR = NMP [NMX_NMPAR_ENTITY];

	local
	NDB: ref block field(NODE_fields),
	DAT_LST: ref vector;

	NDB = .NMPAR;

	If not FIND_NDB( NDB)
	Then
		return NM$SUC;

	ch$fill( 0, 24, byt$ptr( NDB[NDb_rcv]));
        $NM_TIME( NDB[NDtime_zeroed]);

        If .NDB[NDaddr] eql .DB[NSPself]
        Then
            DB[NSPmax] = 0;

	NM$SUC
	end;
routine UPDATE_DELAY ( PDB) :   novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine calculates the round trip delay for NSp messages.
!
! FORMAL PARAMETERS:
!	PDB = Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map PDB: ref block field(PORT_fields);

	local
	DELAYtime,
	NDB : ref block field(NODE_fields);

	NDB = .PDB[NODElnk];

	If .PDB[DELAYstr_tim] neq 0
	Then
		begin
		DELAYtime = .PDB[DELAYstr_tim];

		If .DELAYtime gtr 1
		Then
			DELAYtime = .DELAYtime - 1;
		MAP$( .DB[BIASnodes]);

		If .NDB[NDdelay] eql 0
		Then
			NDB[NDdelay] = .DELAYtime
		Else
			begin
			DELAYtime = .DELAYtime - .NDB[NDdelay];
			NDB[NDdelay] = .NDB[NDdelay] + (.DELAYtime / (.DB[NSPweight] + 1))
			end;
		MAP$( .DB[BIASports]);
		PDB[DELAYstr_tim] = 0;
		end
	end;
routine XPT_XMIT   ( CCB, XXDB) :   novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine passes packets to Transport for delivery to a remote
!	NSP.
!
!
! FORMAL PARAMETERS:
!	CCB = CCB address
!	XXDB = Reserved/Port Data Base Address
!
! IMPLICIT INPUTS:
!	DB = NSP Data Base Address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

	begin
	map CCB: ref block field(C_fields);
	map XXDB: ref block field(PORT_fields);


	local
	NDB: ref block field(NODE_fields),
	temp;

	NDB = .XXDB[NODElnk];

	If .XXDB[PORTstate] lss 0
	Then
		CCB[C_PRM4] = -1
	Else
		CCB[C_PRM4] = .XXDB;
	CCB[C_FNC] = FC_XME;
	CCB[C_MOD] = FM_DAT;
	CCB[C_LIX] = .XXDB[CHANNEL];
	CCB[C_PIX] = .DB[NSPxpt_pix];
	CCB[C_STS] = 0;
	CCB[C_PRM1] = .DB[NSPself];
	CCB[C_PRM2] = .XXDB[NODErem];

	If .NDB neq 0
	Then
		begin
		SMAP$( temp);
		MAP$( .DB[BIASnodes]);
		DADD( NDB[NDm_xmt], 1);
		MAP$( .temp);
		end;
	LLCRS$( .CCB)
	end;


end
eludom