Google
 

Trailing-Edge - PDP-10 Archives - BB-R595B-SM_11-9-85 - mcb/nsp/ns1.bli
There is 1 other file named ns1.bli in the archive. Click here to see a list.
Module NS1 (	!
		ident = 'X01080'
		) =
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 contains all the routines for transmitting
!		and receiving Data and Other Data messages.
!
!
! ENVIRONMENT:		MCB
!
! AUTHOR:	Ron Platukis	CREATION DATE:	1-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  1-dec-80
!	-Add CMP_LSS and CMP_LEQ macros to perform 12-bit arith.
!
! 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 24-feb-81
!	-fix bug in SND_DATA to set ACK_queue to chain word of CCB when
!	dequeueing.
!
! 07	Ron Platukis 15-sept-81
!	-in routine SEG_BLD set FLAGseg to true if it is false and the
!	ACK_queue is empty.
!
! 08	Ron Platukis 4-february-82
!	-in routine PROC_DATA_ACK set FLAGseg to true if ACKrcv_dat equals
!	NUMsent.
!--
!
! INCLUDE FILES:
!
library
	'mcb:xportx';
library
	'mcb:mcblib';
library
	'mcb:nmxlib';
require
	'nspbli:nspdata';
require
	'nspbli:nsinfo';


!
! TABLE OF CONTENTS:
!
forward routine
	ACK_BLD:    novalue,		!format an ACK
	ACK_DATA_SND: ,			!send a data ack
	ACK_OTH_RCV:   novalue,		!process Other Ack
	ACK_RCV:   novalue,		!process Data Ack
	ACK_OTH_SND: ,			!send Other ACK
	CCB_GET:,
	CCB_HDR_GET:,
	CCB_RETRN: novalue,
	DADD: novalue,
	DATA_RECVD: novalue,
	DATA_TO_SND: ,			!check if data to send
	DRQ_BLD:    novalue,		!format a Data Req
	DRQ_RCV:   novalue,		!process Data Request
	D_RCV:   novalue,		!process a Segment
	D_REQ_TO_SND: ,			!check if data req to send
	IRQ_BLD:    novalue,		!format an INT Req
	IRQ_RCV:   novalue,		!process Interrupt Request
	INTERRUPT_TO_SND: ,		!check if int to send
	INTR_REQ_TO_SND: ,		!check if int req to send
	INT_BLD:    novalue,		!format an INT msg
	INT_RCV:   novalue,		!process Interrupt 
	INT_RECVD: novalue,
	LS_RCV:   novalue,		!dispatch on request type
	MSG_RCV: CALL$ novalue,		!dispatch on NSP message type
	ONCE_A_SECOND: CALL$ novalue,	!Resource revovery routine
	OTHACK_BLD:     novalue,	!format an OTH ACK
	PROC_DATA_ACK:   novalue,	!more process Data Ack
	PROC_OTH_ACK:   novalue,	!more process Other Ack
	REAS_DATA:   novalue,		!reassembly routine
	SEG_BLD:    novalue,		!format a Data Seg
	SND_DATA: ,			!send a data segment
	SND_MSG:  CALL$  novalue,	!send a message
	SND_INTERRUPT: CALL$,		!send int msg
	SND_INTR_REQ: ,			!send int req msg
	SND_ONE_DATA: CALL$ novalue,	!send one segment only
	SND_REQ_DATA: ,			!send data req msg
	TRICKLE: CALL$,			!send only one msg and quit
	UPDATE_DELAY:  novalue,		!update round trip delay
	XPT_XMIT:    novalue;		!queue CCB to XPT
!
! macros:
!

!
! equated symbols:
!

!
! own storage:
!
	$MCB_PROCESS( name = NS1)
!
! 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 and 
!	queues it to Transport.
!
! FORMAL PARAMETERS:
!	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
	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] + 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 ACK_DATA_SND ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine transmits a Data Acknowledgement message.
!
! 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
	local
	CCB: ref block field(C_fields);

	If CCB_HDR_GET( 0, CCB, .PDB)
	Then
		begin
		ACK_BLD( .CCB, .PDB);
		return true
		end;
	return false

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

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Other Data Acknowledgement message.
!
! FORMAL PARAMETERS:
!	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);

	PROC_OTH_ACK( .CCB, .PDB);
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
routine ACK_OTH_SND ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine allocates a CCB and passes control to the Other Data
!	Acknowledgement Format routine.
!
! 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

	local
	CCB: ref block field(C_fields);

	If CCB_HDR_GET( 0, CCB, .PDB)
	Then
		begin
		OTHACK_BLD( .CCB, .PDB);
		return true
		end;
	return false
	End;
routine ACK_RCV ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Data Acknowledgement message.
!
! FORMAL PARAMETERS:
!	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);

	PROC_DATA_ACK( .CCB, .PDB);
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	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_HDR_GET ( CCB, LCCB, PDB ) =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine allocates a CCB and a header buffer from DSR space.
!
! FORMAL PARAMETERS:
!	LCCB = CCB Address to stack
!	PDB = Port Data Base address
!	CCB = CCB 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_RETRN ( CCB, PDB):  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine deallocates 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
	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;
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;
routine DATA_TO_SND ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine checks to see if flow control permission allows
!	a data segment to be transmitted.
!
! 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);

	If ((.PDB[PORTstate] eql N$SRUN) or
	(.PDB[PORTstate] eql N$SDI and 
	(CMP_NEQ(.PDB[NUMhigh], .PDB[ACKrcv_dat]))))
	Then
		begin
		If not .PDB[FLOWsw_rem]
		Then
			return false;
		If (CMP_LEQ( .PDB[NUMdat], .PDB[NUMhigh])) or 
		.PDB[FLAGseg] eql false
		Then
			begin
			Case .PDB[FLOWtyp_rem] from FLOWnone to FLOWmsg of
				Set
				[FLOWnone]:
					return true;
				[FLOWseg]:
					begin
					If .PDB[FLAGseg] eql true
					Then
						begin
						If (CMP_LEQ( .PDB[NUMdat], (.PDB[ACKrcv_dat] + .PDB[FLOWdat_rem]))) 
						Then
							return true
						end
					Else
						begin
						bind CCB = .PDB[ACK_queue]: block field(C_fields);
						If (CMP_LEQ( .CCB[C_STS], (.PDB[ACKrcv_dat] + .PDB[FLOWdat_rem]))) 
						Then
							return true
						end;
					end;
				[FLOWmsg]: 
					If (0 lss .PDB[FLOWdat_rem]) or
					.PDB[FLAGseg] eql false
					Then
						return true;
				Tes;
			end;
		return false

		end
	Else
		return false;
	end;
routine DATA_RECVD ( CCB, PDB): novalue = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine generates a DATA-RECIEVED call to Session Control.
!
! FORMAL PARAMETERS:
!	DB = NSP Data Base Address
!	PDB = Port Data Base Address
!	CCB = CCB Address
!
! IMPLICIT INPUTS:
!	None
!
! 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);

	local 
	PTR,
	CHR;

	PTR = .STK_CCB[C_ADDR];
	MAP$( .STK_CCB[C_BIAS]);
	CHR = ch$rchar( .PTR);

	If (.CHR and FLG_EOM) neq 0
	Then
		CCB[C_PRM1] = N$FEOM;
	If (.CHR and FLG_BOM) neq 0
	Then
		CCB[C_PRM1] = .CCB[C_PRM1] or N$FBOM;
	PTR = ch$plus( .STK_CCB[C_ADDR], 6 );
	CHR = ch$rchar( .PTR);

	If .CHR<7, 1>
	Then
		CHR = 9
	Else
		CHR = 7;

	MAP$( .DB[BIASports]);

	CCB[C_FNC] = FC_RCP;
	CCB[C_MOD] = N_RDAT;
	CCB[C_LIX] = .PDB[PID];
	CCB[C_BIAS] = .STK_CCB[C_BIAS];
	CCB[C_CNT] = .STK_CCB[C_CNT] - .CHR;
	CCB[C_ADDR] = ch$plus( .STK_CCB[C_ADDR], .CHR);
	CCB[C_STS] = N$SSUC;
	CCB[C_PIX] = .DB[NSPsc_pix];
	LLCRS$( .CCB);
	end;
routine DRQ_BLD ( CCB, PDB) :   novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Data Request message and queues it to
!	Transport.
!
! FORMAL PARAMETERS:
!	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);

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

	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_REQ, 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[ACKoth_xmt] and %o'7777') + Q_ACK^12 + true^15, PTR);
	PUT2BYTES( (.PDB[NUMoth] and %o'7777'), PTR);

	If .STK_CCB[C_PRM1] eql 0
	Then
		ch$wchar_a( FC_NCHG + FC_DATA^2, PTR)
	Else
		ch$wchar_a( FC_SEND + FC_DATA^2, PTR);
	ch$wchar_a( .STK_CCB[C_PRM1], PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	STK_CCB[C_STS] = .PDB[NUMoth];

	PDB[OTHtyp] = OTHdrequest;
	PDB[FLAGoth_ack] = false;
	PDB[OTHstate] = OTHsent;

	XPT_XMIT( .CCB, .PDB)	
	end;
routine DRQ_RCV ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Data Request message.
!
! FORMAL PARAMETERS:
!	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);

	MAP$( .DB[BIASports]);

	If .DB[Q_FLG]
	Then
		PROC_OTH_ACK( .CCB, .PDB);
	If .DB[SEGNUM] eql ((.PDB[ACKoth_xmt] + 1) and %o'7777')
	Then
		begin
		PDB[FLOWsw_rem] = (Selectone .DB[FCMOD] of
			Set
			[FC_SEND]: 	true;
			[FC_DONT]:	false;
			Tes);
		Selectone .PDB[FLOWtyp_rem] of
		Set
		[FLOWseg]:

			begin
			If (-128 leq .PDB[FLOWdat_rem] + .DB[FCVAL]) and
			(127 geq .PDB[FLOWdat_rem] + .DB[FCVAL])
			Then
				begin
				PDB[FLOWdat_rem] = .PDB[FLOWdat_rem] + .DB[FCVAL];
				PDB[ACKoth_xmt] = .PDB[ACKoth_xmt] + 1;
				PDB[FLAGoth_ack] = true;
				end
			Else
				CALL$E( EVENT_LOG, .DB[NSPns2_pix], invflw, .PDB[FLOWdat_rem], .PDB, .CCB);
			end;
		[FLOWnone]:

			begin
			PDB[ACKoth_xmt] = .PDB[ACKoth_xmt] + 1;
			PDB[FLAGoth_ack] = true;
			end;

		[FLOWmsg]:

			begin
			If (0 leq .DB[FCVAL]) and
			(127 geq .DB[FCVAL])
			Then
				begin
				PDB[FLOWdat_rem] = .PDB[FLOWdat_rem] + .DB[FCVAL];
				PDB[ACKoth_xmt] = .PDB[ACKoth_xmt] + 1;
				PDB[FLAGoth_ack] = true;
				end
			Else
				CALL$E( EVENT_LOG, .DB[NSPns2_pix], invflw, .PDB[FLOWdat_rem], .PDB, .CCB);

			end
		Tes;
		end
	Else
		begin
		If (CMP_LEQ( .DB[SEGNUM], .PDB[ACKoth_xmt]))
		Then
			PDB[FLAGoth_ack] = true
		end;
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
routine D_REQ_TO_SND ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine tests all the conditions needed to transmit a Data
!	Request message.
!
! 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);

	If .PDB[PORTstate] eql N$SRUN or
	(.PDB[PORTstate] eql N$SDI and
	(CMP_NEQ(.PDB[NUMhigh], .PDB[ACKrcv_dat])))
	Then
		begin
		If (.PDB[DATRhead] neq 0 and .PDB[OTHstate] eql OTHready)
		Then
			return true;
		return false;
		end
	Else
		return false;
	end;
routine D_RCV ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Data Segment.
!
! FORMAL PARAMETERS:
!	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 PTR;

	MAP$( .DB[BIASports]);

	If .DB[Q_FLG]
	Then
		PROC_DATA_ACK( .CCB, .PDB);

	If (CMP_EQL( .DB[SEGNUM], (.PDB[ACKdat_xmt] + 1)))
	Then
		REAS_DATA( .CCB, .PDB)
	Else
		begin
		If (CMP_LEQ( .DB[SEGNUM], .PDB[ACKdat_xmt]))
		Then
			PDB[FLAGdat_ack] = true;
		CCB[C_FNC] = FC_RCE;
		LLCRS$( .CCB)
		end
	end;
routine IRQ_BLD ( CCB, PDB) :   novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Interrupt Request message and queues it to
!	Transport.
!
! FORMAL PARAMETERS:
!	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);

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

	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_REQ, 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[ACKoth_xmt] and %o'7777') + Q_ACK^12 + true^15, PTR);
	PUT2BYTES( (.PDB[NUMoth] and %o'7777'), PTR);
	ch$wchar_a( FC_INTERRUPT^2, PTR);
	ch$wchar_a( 1, PTR);
	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);

	STK_CCB[C_STS] = .PDB[NUMoth];
	PDB[OTHtyp] = OTHirequest;
	PDB[FLAGoth_ack] = false;
	PDB[OTHstate] = OTHsent;

	XPT_XMIT( .CCB, .PDB)	
	end;
routine IRQ_RCV ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes a received Interrupt Request message.
!
! FORMAL PARAMETERS:
!	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);

	MAP$( .DB[BIASports]);

	If .DB[Q_FLG]
	Then
		PROC_OTH_ACK(  .CCB, .PDB);
	If .DB[SEGNUM] eql ((.PDB[ACKoth_xmt] + 1) and %o'7777')
	Then
		begin
		If .DB[FCVAL] gtr 0 and
		0 leq .PDB[FLOWint_rem] + .DB[FCVAL] and
		127 geq .PDB[FLOWint_rem] + .DB[FCVAL] 
		Then
			begin
			PDB[ACKoth_xmt] = .PDB[ACKoth_xmt] + 1;
			PDB[FLAGoth_ack] = true;
			PDB[FLOWint_rem] = .PDB[FLOWint_rem] + .DB[FCVAL]
			end
		Else
			CALL$E( EVENT_LOG, .DB[NSPns2_pix], invflw, .PDB[FLOWint_rem], .PDB, .CCB);
		end
	Else
		begin
		If (CMP_LEQ( .DB[SEGNUM], .PDB[ACKoth_xmt]))
		Then
			PDB[FLAGoth_ack] = true
		end;
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
routine INT_BLD ( CCB, PDB) :   novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Interrupt message and queues it to
!	Transport.
!
! FORMAL PARAMETERS:
!	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);

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

	local
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_INT, 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[ACKoth_xmt] and %o'7777') + Q_ACK^12 + true^15, PTR);
	PUT2BYTES( (.PDB[NUMoth] and %o'7777'), PTR);

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

	CCB[C_CHN] = .CCB[C_STK];
	STK_CCB[C_STS] = .PDB[NUMoth];
	PDB[OTHtyp] = OTHinterrupt;
	PDB[FLAGoth_ack] = false;
	PDB[OTHstate] = OTHsent;

	XPT_XMIT( .CCB, .PDB)	
	end;
routine INT_RECVD ( CCB, PDB): novalue = !

!++
! FUNCTIONAL DESCRIPTION:
!	This routine generates an INTERRUPT-RECIEVED call to Session Control.
!
! FORMAL PARAMETERS:
!	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 PDB: ref block field(PORT_fields);
	map CCB: ref block field(C_fields);

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

	local
	PTR,
	CHR;

	PTR = ch$plus( .STK_CCB[C_ADDR], 6);
	MAP$( .STK_CCB[C_BIAS]);
	CHR = ch$rchar( .PTR);

	If .CHR<7, 1>
	Then
		CHR = 9
	Else
		CHR = 7;

	MAP$( .DB[BIASports]);
	CCB[C_FNC] = FC_RCP;
	CCB[C_MOD] = N_RINT;
	CCB[C_LIX] = .PDB[PID];
	CCB[C_BIAS] = .STK_CCB[C_BIAS];
	CCB[C_CNT] = .STK_CCB[C_CNT] - .CHR;
	CCB[C_ADDR] = ch$plus( .STK_CCB[C_ADDR], .CHR);
	CCB[C_STS] = N$SSUC;
	CCB[C_PIX] = .DB[NSPsc_pix];
	LLCRS$( .CCB);
	end;
routine INT_RCV ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processses a received Interrupt message.
!
! FORMAL PARAMETERS:
!	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
	temp,
	NDB : ref block field(NODE_fields),	
	WK_CCB: ref block field(c_fields);

	NDB = .PDB[NODElnk];
	temp = 7;
	MAP$( .DB[BIASports]);

	If .DB[Q_FLG]
	Then
		begin
		temp = 9;
		PROC_OTH_ACK( .CCB, .PDB);
		end;
	If .DB[SEGNUM] eql (.PDB[ACKoth_xmt] + 1 and %o'7777') and
	.PDB[COUNTloc_int] gtr 0 and
	.PDB[BUFFrcv_int] eql 0
	Then
		begin
		PDB[COUNTloc_int] = .PDB[COUNTloc_int] - 1;
		PDB[FLAGoth_ack] = true;
		PDB[ACKoth_xmt] = .PDB[ACKoth_xmt] + 1;
		MAP$( .DB[BIASnodes]);
		DADD( NDB[NDb_rcv], .CCB[C_CNT] - .temp);
		MAP$( .DB[BIASports]);

		If CCB_GET( WK_CCB, .PDB)
		Then
			begin
			WK_CCB[C_STK] = .CCB;
			INT_RECVD( .WK_CCB, .PDB);
			return
			end;
		PDB[BUFFrcv_int] = .CCB;
		end;
	If (CMP_LEQ( .DB[SEGNUM], .PDB[ACKoth_xmt]))
	Then
		PDB[FLAGoth_ack] = true;
	CCB[C_FNC] = FC_RCE;
	LLCRS$( .CCB)
	end;
routine INTERRUPT_TO_SND ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine tests all the conditions needed to transmit an
!	Interrupt message.
!
! 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);

	IF .PDB[PORTstate] neq N$SRUN
	Then
		return false;
	If ((.PDB[INThead] neq 0) and (.PDB[OTHstate] eql OTHready)  and 
	(.PDB[FLOWint_rem] gtr 0)) or .PDB[OTHstate] eql OTHtimeout
	Then
		return true;
	return false;
	end;
routine INTR_REQ_TO_SND ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine tests all the conditions needed to transmit an Interrupt
!	Request message.
!
! 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);

	If .PDB[PORTstate] neq N$SRUN
	Then
		return false;
	If (.PDB[INTRhead] neq 0) and (.PDB[OTHstate] eql OTHready) and
	(.PDB[COUNTloc_int] eql 0) and (.PDB[BUFFrcv_int] eql 0)
	Then
		return true;
	return false;
	end;
routine LS_RCV ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine dispatches to th appropriate routine bases on the
!	type of Link Service message received.
!
! FORMAL PARAMETERS:
!	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 PTR;

	MAP$( .CCB[C_BIAS]);

	If not .DB[Q_FLG]
	Then
		PTR = ch$plus( .CCB[C_ADDR], 7 )
	Else
		PTR = ch$plus( .CCB[C_ADDR], 9 );
	DB[LSFLAGS] = ch$rchar_a( PTR);
	DB[FCVAL] = ch$rchar_a( PTR);

	MAP$( .DB[BIASports]);

	If not (.DB[LSFLAGS] and LS_RSV)
	Then
		begin
		If .DB[FCVALINT] eql FC_INTERRUPT
		Then
			IRQ_RCV( .CCB, .PDB)
		Else
			DRQ_RCV( .CCB, .PDB)
		end
	Else
		begin
		CALL$E( EVENT_LOG, .DB[NSPns2_pix], invmsg, 0, .PDB, .CCB);
		CCB[C_FNC] = FC_RCE;
		LLCRS$( .CCB)
		end
	end;
global routine MSG_RCV ( CCB, PDB) :CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine dispatches on the type of NSP message received.
!
! FORMAL PARAMETERS:
!	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
	SC_CCB:ref block field(C_fields),
	PTR;

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

	If not .DB[Q_FLG] 
	Then
		PTR = ch$plus( .PTR, -2);

	MAP$( .DB[BIASports]);

	If .PDB[PORTstate] eql N$SCC or
	.PDB[PORTstate] eql N$SRUN or
	(.PDB[PORTstate] eql N$SDI and
	(CMP_NEQ(.PDB[NUMhigh], .PDB[ACKrcv_dat])))
	Then
		begin
		If .PDB[PORTstate] eql N$SCC and
		(.DB[MSGFLG] eql FLG_INT or .DB[MSGFLG] eql FLG_REQ or
		.DB[MSGFLG] eql FLG_DAT_ACK)
		Then
			begin
			PDB[PORTstate] = N$SRUN;
			UPDATE_DELAY( .PDB);
			PDB[CONFIDENCE] = true;
			PDB[TIMERcon] = 0;
			PDB[COUNTdat_retrans] = 0;
			If CMQRM$( PDB[MSG_queue], SC_CCB)
			Then
				begin
				SC_CCB[C_STS] = N$SSUC;
				SC_CCB[C_FNC] = FC_XCP;
				LLCRS$( .SC_CCB);
				end
			end;
		PDB[TIMERinact] = .DB[NSPact_tim];

		IF not ((.DB[MSGFLG] eql FLG_DAT_ACK) or
		(.DB[MSGFLG] eql FLG_OTH_ACK))
		Then
			begin
			MAP$( .CCB[C_BIAS]);
			DB[SEGNUM] = GET2BYTES( PTR);
			MAP$( .DB[BIASports]);
			end;

		Selectone .DB[MSGFLG] of
			Set
			[FLG_INT]:	INT_RCV( .CCB, .PDB);
			[FLG_REQ]:	LS_RCV( .CCB, .PDB);
			[FLG_DAT_ACK]:	ACK_RCV( .CCB, .PDB);
			[FLG_OTH_ACK]:	ACK_OTH_RCV( .CCB, .PDB);
			[FLG_EOM]:	D_RCV( .CCB, .PDB);
			[FLG_BOM]:	D_RCV( .CCB, .PDB);
			[FLG_BEOM]:	D_RCV( .CCB, .PDB);
			[FLG_NBEOM]:	D_RCV( .CCB, .PDB);
			Tes;
		end
	Else
		begin
		CCB[C_FNC] = FC_RCE;
		LLCRS$( .CCB)
		end;
	CALL$L(SND_MSG, .PDB)
	end;
global routine ONCE_A_SECOND : CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is called every second to attempt to clear up any
!	resource deadlocks that may have occured.
!
! FORMAL PARAMETERS:
!	None
!
! IMPLICIT INPUTS:
!	DB = NSP data base address
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--


!********************************************************************
! Now check thru the Session Control ports for any stalled transmits
!********************************************************************


	begin

	local PORT: ref blockvector[0, PDB_size] field(PORT_fields),
	CCB: ref block field(C_fields);

	port = .DB[ADDRports];
	MAP$( .DB[BIASports]);
	
	Incr J from 0 to (.DB[NSPtotal] - 1) do
		begin
		bind PDB = PORT[.DB[NSPnxt_port],0,0,0,0]: block field(PORT_fields);
		If .DB[NSPnxt_port] + 1 lss .DB[NSPtotal]
		Then
			DB[NSPnxt_port] = .DB[NSPnxt_port] + 1
		Else
			DB[NSPnxt_port] = 0;
		If .PDB[FLAGinuse]
		Then
			begin
			If .PDB[BUFFctl] neq 0
			Then
				CALL$E(CTL_RECVD, .DB[NSPns2_pix], PDB);
			If .PDB[RCVmsg_queue] neq 0
			Then
				begin
				If CCB_GET( CCB, PDB)
				Then
					begin
					CMQRM$( PDB[RCVmsg_queue], CCB[C_STK]);
					DATA_RECVD( .CCB, PDB);
					end;
				end;
			If .PDB[OTHres]
			Then
				begin
				If .PDB[PORTstate] eql N$SRUN
				Then
					begin
					PDB[OTHres] = false;
					CALL$L( TRICKLE, PDB);
					end
				Else
					begin
					If CCB_HDR_GET( 0, CCB, PDB)
					Then
						begin
						PDB[OTHres] = false;
						If not CMQRM$( PDB[MSG_queue], CCB[C_STK])
						Then
							SIGNAL_STOP( NSP$_ICE);
						Selectone .PDB[PORTstate] of
							Set
							[N$SCI]:
							CALL$E(CI_BLD, .DB[NSPns2_pix], .CCB, PDB);
							[N$SCC]:
							CALL$E(CC_BLD, .DB[NSPns2_pix], .CCB, PDB);
							[N$SDI, N$SDR]:
							CALL$E(DI_BLD, .DB[NSPns2_pix], .CCB, PDB);
							Tes;
						end;
					end;
				end;
			If .PDB[DATres] 
			Then
				begin
				PDB[DATres] = false;
				CALL$L(SND_ONE_DATA, PDB);
				end;

			If .PDB[FLAGsnd_dc] and
			CCB_HDR_GET( 0, CCB, PDB)
			Then
				CALL$E(DC_BLD, .DB[NSPns2_pix], .CCB, PDB);
			end;

			If (.PDB[BUFFrcv_int] neq 0) and
			CCB_GET( CCB, PDB)
			Then
				begin
				CCB[C_STK] = .PDB[BUFFrcv_int];
				PDB[BUFFrcv_int] = 0;
				INT_RECVD(.CCB, PDB);
				end;
		end;


!********************************************************************
! Now check thru the reserved ports for any stalled transmits
!********************************************************************


		begin
		local PORT: ref blockvector[0, RDB_size] field(PORT_fields);
		MAP$( .DB[BIASresv]);
		port = .DB[ADDRresv];

		Incr j from 0 to (.DB[NSPt_rsv] -1 ) do
			begin
			bind RDB = PORT[.j,0,0,0,0]: block field(PORT_fields);		
			If .RDB[MSGtyp] neq MSG_NONE
			Then
				begin
				If CCB_HDR_GET( 0, ccb, -1)
				Then
					begin
					If .RDB[MSGtyp] eql N_LNK
					Then
						CALL$E(NL_BLD, .DB[NSPns2_pix], .CCB, RDB)
					Else
						CALL$E(NR_BLD, .DB[NSPns2_pix], .CCB, RDB);
					RDB[MSGtyp] = MSG_NONE;
					exitloop
					end
				Else
					exitloop;
				end;
			end;
		end;
	end;
routine OTHACK_BLD ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Other Data Acknowledgement message 
!	and queues it to Transport.
!
! FORMAL PARAMETERS:
!	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
	PTR;

	PTR = .CCB[C_ADDR];

	ch$wchar_a( FLG_OTH_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[ACKoth_xmt] and %o'7777') + Q_ACK^12 + true^15, PTR);

	CCB[C_CNT] = ch$diff(.PTR, .CCB[C_ADDR]);
	PDB[FLAGoth_ack] = false;
	XPT_XMIT( .CCB, .PDB)	
	end;
routine PROC_DATA_ACK ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes the NUMBER and QUAL fields of a received
!	Data Acknowledgement message.
!
! FORMAL PARAMETERS:
!	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
	WK_CCB: ref block field(C_fields),
	NDB : ref block field(NODE_fields),
	temp;

	MAP$( .DB[BIASports]);
	NDB = .PDB[NODElnk];

	If (CMP_LSS( .PDB[ACKrcv_dat], .DB[NUMBER]) and 
	(CMP_LEQ( .DB[NUMBER], .PDB[NUMsent])) and .DB[QUAL] eql Q_ACK) or
	(CMP_LEQ( .PDB[ACKrcv_dat], .DB[NUMBER]) and 
	(CMP_LEQ( .DB[NUMBER], .PDB[NUMsent])) and .DB[QUAL] eql Q_NAK) 
	Then
		begin
		PDB[CONFIDENCE] = true;
		PDB[COUNTdat_retrans] = 0;
		If .PDB[FLOWtyp_rem] eql FLOWseg
		Then
			PDB[FLOWdat_rem] = .PDB[FLOWdat_rem] - (( .DB[NUMBER] - .PDB[ACKrcv_dat]) and %o'7777');
		PDB[ACKrcv_dat] = .DB[NUMBER];
		temp = .PDB[ACKhead];
		While (CMQPT$( temp, WK_CCB)) do
			begin
			If CMP_LEQ( .WK_CCB[C_STS], .PDB[ACKrcv_dat])
			Then
				begin
				WK_CCB = .PDB[ACK_queue];
				PDB[ACK_queue] = .WK_CCB[C_LNK];
				If (.WK_CCB[C_PRM3] and N$FCMP) neq 0
				Then
					begin
					bind SC_CCB = .WK_CCB[C_STK]: block field(C_fields);
					SC_CCB[C_FNC] = FC_XCP;
					SC_CCB[C_STS] = N$SSUC;
					LLCRS$( SC_CCB);

					If .PDB[FLOWtyp_rem] eql FLOWmsg
					Then
						PDB[FLOWdat_rem] = .PDB[FLOWdat_rem] - 1;
					end;

				CCB_RETRN( .WK_CCB, .PDB);
				end
			Else
				exitloop;

			end;
		PDB[TIMERdat] = 0;
		If .PDB[ACKrcv_dat] eql .PDB[NUMsent]
		Then
			PDB[FLAGseg] = true;

		If (CMP_LSS( .PDB[ACKrcv_dat], .PDB[NUMsent]) and .DB[QUAL] eql Q_ACK)
		Then
			begin
			MAP$( .DB[BIASnodes]);
			temp = .NDB[NDdelay];
			MAP$( .DB[BIASports]);
			PDB[TIMERdat] = .temp * .DB[NSPdelay]
			end;
		If (CMP_LEQ( .PDB[DELAYmsg_num], .PDB[ACKrcv_dat]))
		and .PDB[DELAYstr_tim] neq 0
		Then
			UPDATE_DELAY( .PDB);
		If .PDB[PORTstate] eql N$SDI and 
		(CMP_EQL( .PDB[NUMhigh], .PDB[ACKrcv_dat])) and
		.PDB[BUFFsync_dsc] neq 0
		Then
			begin
			If CCB_HDR_GET( 0, WK_CCB, .PDB)
			Then
				begin
				WK_CCB[C_STK] = .PDB[BUFFsync_dsc];
				CALL$E(DI_BLD, .DB[NSPns2_pix], .WK_CCB, .PDB)
				end
			Else
				begin
				CMQIN$( PDB[MSG_queue], .PDB[BUFFsync_dsc]);
				PDB[OTHres] = true
				end;
			PDB[BUFFsync_dsc] = 0;
			end;
		end
	end;
routine PROC_OTH_ACK ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine processes the NUMBER and QUAL fields of a received
!	Other Data Acknowledgement message.
!
! FORMAL PARAMETERS:
!	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
	temp: ref block field(C_fields);

	MAP$( .DB[BIASports]);

	If .PDB[OTHstate] neq OTHready and
	((.DB[NUMBER] eql ((.PDB[NUMoth] -1) and %o'7777') and .DB[QUAL] eql Q_NAK) or
	(.DB[NUMBER] eql .PDB[NUMoth] and .DB[QUAL] eql Q_ACK))
	Then
		begin
		PDB[CONFIDENCE] = true;
		PDB[COUNToth_retrans] = 0;
		PDB[TIMERoth] = 0;

		If .DB[QUAL] eql Q_ACK
		Then
			begin
			PDB[OTHstate] = OTHready;
			PDB[NUMoth] = .PDB[NUMoth] + 1;
			If CMQRM$( PDB[OTHACK_queue], temp)
			Then
				begin
				If .temp[C_MOD] eql N_XBUF
				Then
					begin
					temp[C_STS] = N$SSUC;
					CMQIN$( PDB[BUFF_queue], .temp);
					end
				Else
					begin
					temp[C_FNC] = FC_XCP;
					temp[C_STS] = N$SSUC;
					LLCRS$( .temp);
					end
				end;
			If .PDB[OTHtyp] eql OTHinterrupt
			Then
				PDB[FLOWint_rem] = .PDB[FLOWint_rem] - 1;
			end
		end
	end;
routine REAS_DATA ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is the reassembly process.
!
! 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);


	bind BUFF_CCB = .PDB[BUFF_queue]: block field(C_fields);

	local
	TO_ADDR,
	TEMP,
	size,
	XFRCNT,
	NDB : ref block field(NODE_fields),
	GOT_CCB: ref block field(C_fields);

	NDB = .PDB[NODElnk];

	If not .DB[Q_FLG]
	Then
		size = 7
	Else
		size = 9;
	MAP$( .DB[BIASnodes]);
	DADD( NDB[NDb_rcv], .CCB[C_CNT] - .size);
	MAP$( .DB[BIASports]);

	If .PDB[FLOWloc_typ] eql FLOWmsg
	Then
		begin
		If .PDB[BUFFhead] eql 0
		Then
			begin
			CCB[C_FNC] = FC_RCE;
			LLCRS$( .CCB);
			return
			end
		Else
			begin
			If .PDB[REAS_PTR] neq 0 and
			(.DB[MSGFLG] and NSPbom) neq 0
			Then
				begin
				CCB[C_FNC] = FC_RCE;
				LLCRS$( .CCB);
				return
				end;

			XFRCNT =
				begin
				If (.PDB[REAS_PTR] + .CCB[C_CNT] - .SIZE) leq .BUFF_CCB[C_CNT]
					Then
						.CCB[C_CNT] - .SIZE
					Else
						begin
						BUFF_CCB[C_STS] = N$ELST;
						.BUFF_CCB[C_CNT] - .PDB[REAS_PTR]
						end
				end;

			If .XFRCNT neq 0
			Then
				begin
				TEMP = ch$plus( .CCB[C_ADDR], .SIZE);
				TO_ADDR = ch$plus( .BUFF_CCB[C_ADDR], .PDB[REAS_PTR]);
				MAP$( .CCB[C_BIAS]);
				MTBF$S( .XFRCNT, .temp, .BUFF_CCB[C_BIAS], .TO_ADDR);
				MAP$( .DB[BIASports]);
				end;
			PDB[REAS_PTR] = .PDB[REAS_PTR] + .XFRCNT;

			IF (.DB[MSGFLG] and NSPeom) neq 0
			Then
				begin
				BUFF_CCB[C_CNT] = .PDB[REAS_ptr];
				BUFF_CCB[C_FNC] = FC_XCP;
				PDB[REAS_PTR] = 0;
				LLCRS$( BUFF_CCB);
				CMQRM$( PDB[BUFF_queue], temp)
				end;

			CCB[C_FNC] = FC_RCE;
			LLCRS$( .CCB);
			end
		end
	Else
		begin
		If .PDB[RCVmsg_queue] eql 0 and
		CCB_GET( GOT_CCB, .PDB)
		Then
			begin
			GOT_CCB[C_STK] = .CCB;
			DATA_RECVD( .GOT_CCB, .PDB);
			end
		Else
			CMQIN$( PDB[RCVmsg_queue], .CCB);
		end;
	PDB[FLAGdat_ack] = true;
	PDB[ACKdat_xmt] = (.PDB[ACKdat_xmt] + 1 and %o'7777');
	end;
routine SEG_BLD ( CCB, PDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine formats an NSP Data Segment message and queues it to
!	Transport.
!
! FORMAL PARAMETERS:
!	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);

	bind CHN_CCB = .CCB[C_CHN]: block field(C_fields);

	local
	xtemp,
	PTR,
	FLG;
	
	If .PDB[FLAGseg] eql false
	Then
		xtemp = .CHN_CCB[C_STS]
	Else
		xtemp = .PDB[NUMdat];

	xtemp = (.xtemp and %o'7777');
	PTR = .CCB[C_ADDR];
	FLG = 0;

	If (.CHN_CCB[C_PRM3] and N$FBOM) neq 0
	Then
		FLG = FLG_BOM;
	If (.CHN_CCB[C_PRM3] and N$FEOM) neq 0
	Then
		FLG = .FLG or FLG_EOM;

	ch$wchar_a( .FLG, 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] + Q_ACK^12 + true^15, PTR);
	PUT2BYTES( .xtemp, PTR);

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

	CCB[C_STK] = .CCB[C_CHN];
	CHN_CCB[C_STS] = .xtemp;
	CHN_CCB[C_MOD] = N_XDAT;
	PDB[FLAGdat_ack] = false;

	If .PDB[DELAYstr_tim] eql 0
	Then
		begin
		PDB[DELAYstr_tim] = 1;
		PDB[DELAYmsg_num] = .xtemp;
		end;
	If .PDB[FLAGseg] eql true
	Then
		PDB[NUMdat] = .PDB[NUMdat] + 1
	Else
		begin
		if CMP_EQL( .xtemp, (.PDB[NUMdat] -1))
		then
			PDB[FLAGseg] = true
		end;

	XPT_XMIT( .CCB, .PDB)	
	end;
routine SND_DATA ( PDB) :  =	!

!++
! functional description:
!	This routine performs the actual segmentation of Session Control
!	transmit buffers into NSP segments. Control is passed to to the
!	data segment format routine.
!
! 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);

	bind CHN_CCB = .PDB[MSG_chain]: block field(C_fields);
	bind MSG_CCB = .PDB[MSG_queue]: block field(C_fields);

	local
	SEG_CCB: ref block field(C_fields),
	NDB : ref block field(NODE_fields),
	CCB: ref block field(C_fields);

	NDB = .PDB[NODElnk];

	If .PDB[FLAGseg] eql false
	Then
		begin
		If CCB_HDR_GET( .PDB[ACK_queue], CCB, .PDB)
		Then
			begin
			CCB[C_CHN] = .PDB[ACK_queue];
			PDB[ACK_queue] = .(.CCB[C_CHN]);
			MAP$( .DB[BIASnodes]);
			NDB[NDtimeout] = .NDB[NDtimeout] + 1;
			MAP$( .DB[BIASports]);
			end
		Else
			begin
			PDB[DATres] = true;
			return false
			end;
		end
	Else
		begin
		If CCB_GET( SEG_CCB, .PDB)
		Then
			begin
			If not CCB_HDR_GET(  0, CCB, .PDB)
			Then
				begin
				CCB_RETRN( .SEG_CCB, .PDB);
				PDB[DATres] = true;
				return false;
				end
			Else
				begin
				CCB[C_CHN] = .SEG_CCB;
				SEG_CCB[C_STK] = .PDB[MSG_queue];

				If .CHN_CCB[C_CNT] - .MSG_CCB[C_STS] leq .PDB[SIZEseg]
				Then
					begin
					SEG_CCB[C_CNT] = .CHN_CCB[C_CNT] - .MSG_CCB[C_STS];
					SEG_CCB[C_ADDR] = ch$plus( .CHN_CCB[C_ADDR], .MSG_CCB[C_STS]);
					SEG_CCB[C_BIAS] = .CHN_CCB[C_BIAS];

					If .CHN_CCB[C_CHN] neq 0
					Then
						begin
						MSG_CCB[C_STS] = 0;
						PDB[MSG_chain] = .CHN_CCB[C_CHN]
						end
					Else
						begin
						If .MSG_CCB[C_PRM1] eql N$FEOM
						Then
							SEG_CCB[C_PRM3] = N$FEOM;
						SEG_CCB[C_PRM3] = .SEG_CCB[C_PRM3] or N$FCMP;
						CMQRM$( PDB[MSG_queue], SEG_CCB[C_STK]);
						PDB[MSG_chain] = .PDB[MSGhead]
						end
					end
				Else
					begin
					SEG_CCB[C_ADDR] = ch$plus( .CHN_CCB[C_ADDR], .MSG_CCB[C_STS]);
					SEG_CCB[C_BIAS] = .CHN_CCB[C_BIAS];
					SEG_CCB[C_CNT] = .PDB[SIZEseg];
					MSG_CCB[C_STS] = .MSG_CCB[C_STS] + .PDB[SIZEseg];
					end;
				IF .PDB[FLAGnew_msg]
				Then
					begin
					SEG_CCB[C_PRM3] = (.SEG_CCB[C_PRM3] or N$FBOM);
					PDB[FLAGnew_msg] = false
					end;
				If .SEG_CCB[C_PRM3] and N$FEOM
				Then
					PDB[FLAGnew_msg] = true;
				MAP$( .DB[BIASnodes]);
				DADD( NDB[NDb_xmt], .SEG_CCB[C_CNT]);
				MAP$( .DB[BIASports])
				end
			end
		Else
			begin
			PDB[DATres] = true;
			return false
			end
		end;
	SEG_BLD( .CCB, .PDB);
	return true;
	end;
global routine SND_MSG ( PDB) : CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine prioritises the message transmission process.
!
! 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
	local
	sts;

	Do ( STS = CALL$L( TRICKLE, .PDB))
	While .STS eql true;
	end;
global routine SND_INTERRUPT ( PDB) : call$ =		!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine dequeues an INTERRUPT-XMT CCB and passes to control
!	to the Interrupt Format routine.
!
! 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
	NDB : ref block field(NODE_fields),
	CCB: ref block field(C_fields);

	NDB = .PDB[NODElnk];

	If .PDB[OTHstate] eql OTHtimeout 
	Then
		begin
		If CCB_HDR_GET( 0, CCB, .PDB)
		Then
			begin
			CMQRM$( PDB[OTHack_queue], CCB[C_STK]);
			MAP$( .DB[BIASnodes]);
			NDB[NDtimeout] = .NDB[NDtimeout] + 1;
			MAP$( .DB[BIASports]);

			Selectone .PDB[OTHtyp] of
				Set
				[OTHirequest]:
					IRQ_BLD( .CCB, .PDB);
				[OTHdrequest]:
					DRQ_BLD( .CCB, .PDB);
				[OTHinterrupt]:
					INT_BLD( .CCB, .PDB);
				Tes;
			return true;
			end
		Else
			begin
			PDB[OTHres] = true;
			return false
			end
		end;
	If .PDB[OTHstate] eql OTHready
	Then
		begin
		If CCB_HDR_GET( 0, CCB, .PDB)
		Then
			begin
			CMQRM$( PDB[INT_queue], CCB[C_STK]);
			INT_BLD( .CCB, .PDB);
			return true;
			end
		Else
			begin
			PDB[OTHres] = true;
			return false
			end
		end;
	return false
	end;
routine SND_INTR_REQ ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine dequeues an INTERRUPT-REQUEST CCB and passes control
!	to the Interrupt Request Format routine.
!
! 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
	CCB: ref block field(C_fields);


	If CCB_HDR_GET( 0, CCB, .PDB)
	Then
		begin
		CMQRM$( PDB[INTR_queue], CCB[C_STK]);
		PDB[COUNTloc_int] = .PDB[COUNTloc_int] + 1;
		IRQ_BLD( .CCB, .PDB);
		return true;
		end
	Else
		PDB[OTHres] = true;
	return false
	end;
global routine SND_ONE_DATA ( PDB) : CALL$ novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine sends one data segment.
!
! 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
	If DATA_TO_SND( .PDB)
	Then
		SND_DATA( .PDB);

	end;
routine SND_REQ_DATA ( PDB) : =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine dequeues a DATA-REQUEST CCB and passes control to the
!	Data Request Format routine.
!
! 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
	CCB: ref block field(C_fields);

	If CCB_HDR_GET( 0, CCB, .PDB)
	Then
		begin
		CMQRM$( PDB[DATR_queue], CCB[C_STK]);
		DRQ_BLD( .CCB, .PDB);
		return true;
		end
	Else
		PDB[OTHres] = true;
	return false
	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
	NDB : ref block field(NODE_fields),
	DELAYtime;

	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;
global routine TRICKLE ( PDB) : CALL$ =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine prioritises the message transmission process.
!
! 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);


	If INTERRUPT_TO_SND( .PDB)
	Then
		begin
		If CALL$L(SND_INTERRUPT, .PDB)
		Then
			return true
		Else
			return false
		end;
	If INTR_REQ_TO_SND( .PDB)
	Then
		begin
		If SND_INTR_REQ( .PDB)
		Then
			return true
		Else
			return false
		end;
	If D_REQ_TO_SND( .PDB)
	Then
		begin
		If SND_REQ_DATA( .PDB)
		Then
			return true
		Else
			return false
		end;
	If .PDB[FLAGoth_ack]
	Then
		begin
		If ACK_OTH_SND( .PDB)
		Then
			return true
		Else
			return false
		end;
	If DATA_TO_SND( .PDB)
	Then
		begin
		If SND_DATA( .PDB)
		Then
			return true
		Else
			return false
		end;
	If .PDB[FLAGdat_ack] and 
	((.PDB[PORTstate] eql N$SRUN) or
	((.PDB[PORTstate] eql N$SDI) and 
	(CMP_NEQ(.PDB[NUMhigh], .PDB[ACKrcv_dat]))))
	Then
		begin
		If ACK_DATA_SND( .PDB)
		Then
			return true
		Else
			return false
		end;
	false
	end;
routine XPT_XMIT ( CCB, XXDB) :  novalue =	!

!++
! FUNCTIONAL DESCRIPTION:
!	This routine queues CCB's to Transport for transmission.
!
! FORMAL PARAMETERS:
!	XXDB = Reserved/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 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