Google
 

Trailing-Edge - PDP-10 Archives - bb-kl11l-bm_tops20_v7_0_tsu03_2_of_3 - galsrc/nebula.mac
There are 23 other files named nebula.mac in the archive. Click here to see a list.
S
	ADDI	S1,777			;ROUND IT OFF
	ADR2PG	S1			;MAKE IT A PAGE NUMBER
	MOVEM	S1,NEBSIZ		;SAVE IT

;DETERMINE AND SAVE THE LOCAL ACK CODE SEED

	$CALL	I%NOW			;PICK UP THE ACK CODE SEED
	MOVEM	S1,LOCACK		;AND SAVE IT
	$RET				;RETURN
	SUBTTL WAIQSR - WAIT FOR QUASAR TO STARTUP

;[6036]WAIQSR is called during NEBULA startup. This routine will wait
;[6036]for QUASAR to start up. This prevents any IPCF messages from being
;[6036]lost.
;[6036]
;[6036]Call is:      No arguments
;[6036]Returns true: QUASAR has started up

WAIQSR:	MOVEI	S1,SP.QSR		;[6036]GET QUASAR'S PID INDEX
	$CALL	C%RPRM			;[6036]PICK UP QUASAR'S PID
	$RETIT				;[6036]RETURN NOW IF QUASAR IS THERE
	MOVEI	S1,1			;[6036]OTHERWISE ...
	$CALL	I%SLP			;[6036]SLEEP FOR A SECOND
	JRST	WAIQSR			;[6036]AND TRY AGAIN
	SUBTTL WAIORN - WAIT FOR ORION TO STARTUP

;[6034]WAIORN is called during NEBULA startup. This routine will wait
;[6034]for ORION to start up. This prevents any IPCF messages from being
;[6034]lost.
;[6034]
;[6034]Call is:      No arguments
;[6034]Returns true: ORION has started up

WAIORN:	MOVEI	S1,SP.OPR		;[6034]GET ORION'S PID INDEX
	$CALL	C%RPRM			;[6034]PICK UP ORION'S PID
	$RETIT				;[6034]RETURN NOW IF ORION IS THERE
	MOVEI	S1,1			;[6034]OTHERWISE ...
	$CALL	I%SLP			;[6034]SLEEP FOR A SECOND
	JRST	WAIORN			;[6034]AND TRY AGAIN
	SUBTTL INTINI -	INTERRUPT INITIALIZATION AND HANDLERS

;INTINI is called during NEBULA startup. This routine activates
;the interrupt channels:
;
;0 -  A listener has a message
;1 -  A sender is ready to send a message
;2 -  IPCF has one or more messages available
;3 -  A sender or listener has just made a DECnet connection
;4 -  SCS% detected a cluster topology change
;19 - (channel .IPIFT) A listener or sender has crashed
;
;Call is: No arguments
;Returns: The interrupt system has been successfully set up
;Crashes: Cannot activate the interrupt system or cannot enable
;         SCS% event interruptions

INTINI:	$SAVE	<T1,T2>			;CHANGED BY SCS% JSYS
	MOVE	S1,[1,,ENDFRK]		;SET UP INFERIOR FORK TERM PARMS
	MOVEM	S1,CHNTAB+.ICIFT	;IN THE CHANNEL TABLE
	MOVEI	S1,.FHSLF		;GET MY HANDLE
	MOVX	S2,37B4+1B19		;GET CHANNELS 0-4 AND 19
	AIC%				;ACTIVATE THEM
	 ERJMP	S..CSI			;THIS SHOULD NEVER HAPPEN
	$CALL	I%ION			;ENABLE THE INTERRUPTS
	JUMPF	S..CSI			;THIS SHOULD NEVER HAPPEN

;TELL SCS% TO INTERRUPT US FOR EVENTS

	MOVEI	S1,.SSAIC		;ADD INTERRUPT CHANNEL FOR SCA EVENTS
	MOVEI	S2,SCSBLK		;POINT TO THE BLOCK
	SCS%				;INFORM SCS%
	 ERJMP	S..CSI			;THIS SHOULD NOT HAPPEN

	$RET				;RETURN
	SUBTTL	NEBULA INTERRUPT HANDLERS

;LISMSG - Listener has a message interrupt handler. This routine sets flag
;         word LREADY which indicates that a listener has a message to
;         deliver to NEBULA's top fork.
;
;SNDREA - Sender is free to deliver a message interrupt handler. This routine
;         sets flag word SREADY which indicates that a sender is free to
;         deliver a message.
;
;NBIPCF - IPCF interrupt handler. This routine sets flag word MSGFLG
;         which indicates that there are one or more IPCF messages available.
;
;INFRDY - Inferior fork has a DECnet connection interrupt handler. This
;	  routine sets flag word IREADY which indicates that a sender's
;	  request to make a DECnet connection to a listener has been 
;	  accepted by the listener, or a listener has accepted a DECnet
;         connection request from a sender.
;
;ENDFRK - Inferior fork crashed interrupt handler. This routine sets flag
;         word TRMFRK which indicates that a sender or listener has crashed.
;
;NBSCS  - Topology change detected interrupt handler. This routine sets
;         flag word SCSFLG which indicates that a cluster topology change
;         has occurred.  Also, if the node table (NODTBL) is not being
;         updated, and a node has left the cluster, then its node table
;         entry is marked indicating that the node has left the cluster.

LISMSG:	$BGINT	1,			;INITIALIZE INTERRUPT LEVEL
	SETOM	LREADY			;A LISTENER HAS A MESSAGE
	$DEBRK				;AND LEAVE INTERRUPT LEVEL

SNDREA: $BGINT	1,			;INITIALIZE INTERRUPT LEVEL
	SETOM	SREADY			;A SENDER IS READY FOR A MESSAGE
	$DEBRK				;AND LEAVE INTERRUPT LEVEL

NBIPCF:	$BGINT	1,			;INITIALIZE INTERRUPT LEVEL
	$CALL	C%INTR			;FLAG THE IPCF INTERRUPT
	$DEBRK				;AND LEAVE INTERRUPT LEVEL

INFRDY:	$BGINT	1,			;INITIALIZE INTERRUPT LEVEL
	SETOM	IREADY			;AN INFERIOR FORK A DECNET CONNECTION
	$DEBRK				;AND LEAVE INTERRUPT LEVEL

ENDFRK:	$BGINT	1,			;INTIALIZE INTERRUPT LEVEL
	SETOM	TRMFRK			;A LISTENER OR SENDER HAS CRASHED
	$DEBRK				;AND LEAVE INTERRUPT LEVEL
	SUBTTL NBSCS - TOPOLOGY CHANGE DECTECTED INTERRUPT HANDLER

;NBSCS processes interrupts that occur as a consequence of a cluster
;topology change. Flag word SCSFLG is set to -1 to indicate that a
;cluster topology change has occurred.
;If routine NODINT is finished building the node table and if a node has
;left the cluster, then the status word of the node's entry in the node
;table is set (i.e., bit NN%SCS is turned on) to indicate that the node has
;left the cluster
;
;DEBRKs with word SCSFLG set to -1
;Crashes: If the SCS% JSYS returns an error other than "event queue empty"

NBSCS:	$BGINT	1,			;INITIALIZE INTERRUPT LEVEL

;PICK UP THE NEXT EVENT FROM THE EVENT QUEUE

NBSC2:	MOVEI	S1,SCSLEN		;LENGTH OF THE ARGUMENT BLOCK
	MOVEM	S1,SCSEBK+.SQLEN	;PLACE IN THE ARGUMENT BLOCK
	SETOM	SCSEBK+.SQCID		;GET THE NEXT EVENT

	MOVEI	S1,.SSEVT		;RETRIEVE NEXT ENTRY FROM EVENT QUEUE
	MOVEI	S2,SCSEBK		;ADDRESS OF THE ARGUMENT BLOCK
	SCS%				;PICK UP THE NEXT EVENT QUEUE ENTRY
	 ERJMP	NBSC4			;CHECK IF THE EVENT QUEUE IS NOW EMPTY

;CHECK THE TYPE OF EVENT THAT HAS OCCURRED

	MOVE	S1,SCSEBK+.SQEVT	;PICK UP THE EVENT CODE
	CAIN	S1,.SENCO		;HAS A NODE COME ONLINE?
	JRST	NBSC3			;YES, SET THE FLAG WORD
	CAIE	S1,.SEPBC		;HAS A NODE GONE OFFLINE?
	JRST	NBSC2			;NO, DON'T CARE ABOUT THIS EVENT

;A NODE HAS GONE OFFLINE. IF ROUTINE NODINT IS NOT BUILDING THE NODE TABLE,
;OR IF ROUTINE TOPCHN IS NOT MODIFYING THE NODE TABLE, THEN
;FIND THE ENTRY FOR THIS NODE IN THE NODE TABLE AND MARK IT AS NN%SCS
;(SCS% DETECTED THAT THE NODE HAS LEFT THE CLUSTER).

	SKIPE	BLDTAB			;IS NODE TABLE BEING WORKED ON?
	JRST	NBSC3			;YES, CAUSE TOPCHN TO SET TOPOLOGY
					;CHANGE TIMER
	MOVE	S1,SCSEBK+.SQDTA	;PICK UP THE NODE NUMBER
	$CALL	SNUMNT			;FIND THE NODE TABLE ENTRY

;NOTE: IT MAY BE POSSIBLE THAT THE NODE CAME ONLINE AND NOW HAS GONE
;OFFLINE DURING THE CURRENT SCHEDULING PASS BUT AFTER ROUTINE TOPCHN 
;WAS EXECUTED. SO IT IS POSSIBLE THAT ROUTINE TOPCHN NEVER ENTERED
;THE NODE INTO THE NODE TABLE. 

	JUMPF	NBSC3			;NOT THERE, CAUSE TOPCHN TO SET TIMER
	MOVE	S1,.NNSTA(S2)		;PICK UP THE STATUS WORD
	TXO	S1,NN%SCS		;INDICATE THAT SCS% DETECTED NODE GONE
	TXZ	S1,NN%OKS		;NO LONGER OK TO SEND MSG TO THIS NODE
	MOVEM	S1,.NNSTA(S2)		;UPDATE THE NODE STATUS WORD
	SKIPA				;[6016]DON'T SET NODE ONLINE FLAG

NBSC3:	SETOM	RECHEK			;[6016]INDICATE NODE CAME ONLINE
	SETOM	SCSFLG			;A CLUSTER TOPOLOGY CHANGE OCCURRED
	JRST	NBSC2			;CHECK FOR ANOTHER EVENT QUEUE ENTRY

;THE SCS% JSYS RETURNED AN ERROR. CHECK IF THE EVENT QUEUE IS EMPTY.
;IF SO, RETURN. ON OTHER ERRORS, CRASH. CANNOT JUST ASSUME THAT A TOPOLOGY
;CHANGE HAS OCCURRED, SINCE IF THE ROUTINE EXITS ON THIS ASSUMPTION AND
;THERE ARE MORE EVENTS IN THE EVENT QUEUE, THEN NEBULA WILL NOT BE 
;INTERRUPTED AGAIN IF A CLUSTER TOPOLOGY CHANGE OCCURS.

NBSC4:	MOVEI	S1,.FHSLF		;GET LATEST ERROR OF NEBULA
	GETER%				;PICK UP THE ERROR
	 ERJMP	S..SIF			;FATAL ERROR IN SCS% INTERRUPT HANDLER
	HRRZS	S2			;ISOLATE THE ERROR CODE
	CAIN	S2,SCSQIE		;EVENT QUEUE EMPTY?
	$DEBRK
	JRST	S..SIF			;FATAL ERROR IN SCS% INTERRUPT HANDLER
	SUBTTL NODINT -	INITIALIZE THE NODE DATA BASE

;NODINT is called during NEBULA's startup. 
;This routine sets up the Node Table (NODTBL).
;For each remote node in the cluster, this routine determines:
; The node name
; The node number
; If the node has DECnet enabled
; If the node's monitor is of release 7.0 or later
;While NODINT is building the node table, the cluster topology interrupt
;handler must not access the table. This is accomplished by flag word
;BLDTAB. If the contents of this word is -1, then this indicates that
;the node table is being built.
;If more than MAXNOD nodes are detected, then NODINT crashes NEBULA.
;
;Call is:       No arguments
;Returns true:  There is at least one remote node but not more than MAXNOD 
;Returns false: There are no remote nodes
;Crashes:       If an illegal number of remote nodes are in the cluster
;               (i.e., there are more than MAXNOD remote nodes).

NODINT:	$SAVE <P1,P2,P3,P4>		;SAVE THESE AC
NODIN1:	$CALL	GTNDAT			;PICK UP THE NODE NAMES AND NUMBERS

;CONVERT THE ASCIZ NODE NAMES INTO SIXBIT AND STORE IN THE NODE TABLE

	SETZM	RENNUM			;ASSUME NO REMOTE NODES
	MOVEI	P1,ASZNAM		;POINT TO THE ARGUMENT BLOCK
	HLRZ	P2,.CFNND(P1)		;PICK UP THE NUMBER OF NODES
	SOJE	P2,NODIN5		;[6016]DON'T INCLUDE THE LOCAL NODE
	CAILE	P2,MAXNOD		;CANNOT BE MORE THAN MAXNOD NODES
	$CALL	S..CTL			;ILLEGAL NUMBER OF REMOTE NODES
	MOVEM	P2,RENNUM		;REMEMBER THE NUMBER OF NODES
	MOVEI	P4,NNMTBL+.CFCS1+1	;POINT TO THE FIRST REMOTE NODE NUMBER
	ADDI	P1,.CFBP1+1		;POINT TO THE FIRST REMOTE NODE NAME
	MOVEI	P3,NODTBL		;POINT TO THE NODE TABLE
NODIN2:	MOVE	S1,0(P1)		;PLACE THE BYTE POINTER IN S1
	$CALL	S%SIXB			;RETURN WITH THE SIXBIT NODE NAME IN S2
	MOVEM	S2,.NNNAM(P3)		;PLACE NODE NAME IN NODE TABLE ENTRY
	HLRZ	S2,0(P4)		;PICK UP THE CORRESPONDING NODE NUMBER
	ANDI	S2,NNMFLD		;THE NUMBER IS IN THE HIGH 4 BITS
	HRRZM	S2,.NNSTA(P3)		;PLACE IN THE NODE NUMBER/STATUS WORD
	ADDI	P3,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	AOS	P1			;POINT TO THE NEXT BYTE POINTER
	AOS	P4			;POINT TO THE NEXT NODE NUMBER
	SOJG	P2,NODIN2		;GET THE NEXT NODE NAME

;CHECK IF THE REMOTE NODES HAVE DECNET AND A MONITOR OF RELEASE 7 OR LATER
;STORE THIS INFORMATION IN THE NODE'S NODE TABLE ENTRY

	MOVEI	P2,NODTBL		;POINT TO THE NODE TABLE
	MOVE	P1,RENNUM		;THE NUMBER OF NODES TO LOOK AT

NODIN3:	MOVE	S1,P2			;PICK UP NODE TABLE ENTRY ADDRESS
	$CALL	NODPAR			;CHECK OUT THIS NODE
	JUMPT	NODIN4			;[6016]O.K. TO SEND MSG TO THIS NODE
	MOVE	S1,P2			;[6016]PICK UP NODE TABLE ENTRY ADDRESS
	$CALL	STINFO			;[6016]SET TIMER TO CHECK STATUS LATER
NODIN4:	ADDI	P2,.NNNSZ		;[6016]POINT TO THE NEXT NODE
	SOJG	P1,NODIN3		;CHECK OUT THE NEXT NODE
	SKIPE	SCSFLG			;ONE FINAL CHECK FOR TOPOLOGY CHANGE
	JRST	NODIN1			;A CHANGE HAS OCCURRED, REBUILD
NODIN5:	AOS	BLDTAB			;[6016]SCS INTERRUPT HANDLER MAY NOW
                                        ;[6016]TOUCH THE NODE TABLE
	SKIPN	RENNUM			;[6016]ANY REMOTE NODES?
	$RETF				;[6016]NO, INDICATE SO
	TIME%				;[6016]PICK UP THE MONITOR UPTIME
	CAMG	S1,[TIMUP]		;[6016]ENOUGH TIME FOR CONFG% STABILITY?
	SETOM	RECHEK			;[6016]NO, INDICATE TO ROUTINE TOPCHN
	$RETT				;[6016]AT LEAST ONE REMOTE NODE
	SUBTTL	BLDLST - INITIALIZES PACKN TABLE

;BLDLST is called during NEBULA startup to initialize the previously
;and currently known nodes (PACKN) table. This table is used to support
;the OPR commands:
; SHOW CLUSTER-GALAXY-LINK-STATUS
; ENABLE/DISABLE REPORT-CONNECTION-FAILURES
; ENABLE/DISABLE DECNET-CONNECTION-ATTEMPTS
;Every remote node known to be or to have been in the cluster is kept in this
;table. Each node's entry indicates if the node's sender and listener ever had 
;a DECnet connection or not. Each entry also indicates whether NEBULA
;should report to the operators if that node's sender cannot obtain
;a DECnet connection. Also, if a node's sender cannot obtain a DECnet
;connection, then the entry for that node indicates if the sender is to
;continue to obtain a DECnet connection or cease attempting to obtain a 
;connection.
;BLDLST assumes that if there are any remote nodes in the cluster known to
;NEBULA, then the entries in use in the node table (NODTBL) are contiguous.
;The PACKN table, once it is built, is always assumed by routines that use
;it to have at least one free entry. The PACKN entries that are in use
;are always contiguous.
;
;Call is:       No arguments
;Returns true:  There are remote nodes known to be in the cluster and the
;               PACKN table has been built
;Returns false: There are no known remote nodes in the PACKN table.
;               Memory has been allocated for the PACKN table. All the entries
;               are free.

BLDLST:	$SAVE	<P1,P2,P3>		;SAVE THESE AC

;SET UP THE PACKN TABLE FOR THE FIRST TIME BY OBTAINING THE INITIAL
;MEMORY REQUIRED. 

	SETZM	NDLKST			;NO PACKN TABLE ADDRESS
	SETZM	NDLKNM			;NO PACKN TABLE ENTRIES
	SETZM	NDLKFR			;NO FREE PACKN TABLE ENTRIES
	SETZM	RCFFLG			;REPORT ALL CONNECTION ERRORS
	SETZM	DCAFLG			;ATTEMPT TO OBTAIN A CONNECTION
	$CALL	EXPTAB			;GET THE MEMORY FOR THE PACKN TABLE
	SKIPG	RENNUM			;ANY REMOTE NODES?
	$RETF				;NO, SO DON'T BUILD ANY ENTRIES

;BUILD THE PACKN ENTRIES

	MOVEI	P1,NODTBL		;POINT TO THE NODE TABLE
	MOVE	P2,NDLKST		;POINT TO THE PACKN TABLE
	MOVE	P3,RENNUM		;PICK UP NUMBER OF REMOTE NODES

BLDLS2:	MOVE	S2,.NNNAM(P1)		;PICK UP THE REMOTE NODE NAME
	MOVEM	S2,.NDNAM(P2)		;PLACE IN THE PACKN TABLE
	SETZM	.LKSTS(P2)		;NO CONNECTIONS AND REPORT CONN. ERRORS
	SOSG	NDLKFR			;DECREMENT THE NUMBER OF FREE ENTRIES
	$CALL	EXPTAB			;NO MORE FREE ENTRIES, EXPAND THE TBL

	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	ADDI	P2,.NDLSZ		;POINT TO THE NEXT PACKN TABLE ENTRY
	SOJG	P3,BLDLS2		;PICK UP THE NEXT REMOTE NODE NAME

	$RETT				;RETURN TO STARTUP
	SUBTTL	GTNDAT - PICK UP THE NODE NAMES AND NUMBERS

;GTNDAT is called to pick up the node names and numbers of the 
;nodes in the cluster.
;If a topology change occurs while this routine is picking up the node
;names and numbers, then it starts over in order to pick up the latest
;information about the cluster.
;
;Call is: No arguments
;Returns: The node names and node numbers are obtained.
;Crashes: If cannot obtain the node name or node number

GTNDAT: $SAVE	<T1,T2>			;SAVE THESE AC. CNFIG% CHANGES THEM

;PICK UP THE NODE NAMES


GTNDA2:	MOVEI	S1,CNFLEN		;ENSURE ENOUGH ROOM IN THE ARG BLOCK
	MOVEI	S2,ASZNAM		;ADDRESS OF THE ARGUMENT BLOCK
	MOVEM	S1,.CFNND(S2)		;STORE THE SIZE OF THE ARG BLOCK
	MOVEI	S1,.CFCND		;GET THE ASCIZ NODE NAMES
	SETZM	SCSFLG			;CHECK FOR CLUSTER TOPOLOGY CHANGE
	CNFIG%				;PICK UP THE ASCIZ NODE NAMES
	 ERJMP	S..CON			;CAN'T PICK UP CLUSTER TOPOLOGY
	
;GET THE NODE NUMBERS

	MOVEI	S2,NNMTBL		;PICK UP THE NODE NUMBER TABLE ADDRESS
	MOVEI	S1,1+<MAXNOD+1>		;ENSURE ENOUGH ROOM IN THE ARG BLOCK
	MOVEM	S1,.CFLEN(S2)		;STORE THE SIZE OF ARGUMENT BLOCK
	MOVEI	S1,.CFCSE		;NODE NUMBER/SERIAL NUMBER FUNCTION
	CNFIG%				;PICK UP THE NODE NUMBERS
	 ERJMP	S..CGN			;CAN'T OBTAIN THE NODE NUMBERS
	SKIPE	SCSFLG			;CLUSTER TOPOLOGY CHANGE OCCURRED?
	JRST	GTNDA2			;YES, NEED TO GET THE LATEST
	$RET				;RETURN TO THE CALLER

	$STOP	(CON,CAN'T OBTAIN THE CLUSTER NODE NAMES)
	$STOP	(CGN, CAN'T GET THE CLUSTER NODE NUMBER)
	SUBTTL	STLAS - START UP THE LISTENERS AND SENDERS

;STLAS is called during NEBULA startup.
;This routine checks each node in the node table to determine if that
;node has DECnet enabled and is also running a monitor of release 7.0
;or later. If both of these conditions are true, then this routine
;starts a listener and sender directed at the node.
;This routine assumes:
; 1. There is at least one remote node in the cluster (i.e., the contents
;    of RENNUM is greater than zero.)
; 2. The node table entries in use are contiguous and begin with the
;    first entry in the node table.
;
;Call is: No arguments
;Returns: The sender and listener have been started
;Crashes: If a sender or listener cannot be started

STLAS:	$SAVE	<P1,P2>			;SAVE THESE AC
	MOVEI	P1,NODTBL		;POINT TO THE FIRST NODE
	MOVE	P2,RENNUM		;PICK UP THE NUMBER OF NODES

;FIRST MAKE SURE THAT THE NODE HAS DECNET AND HAS A MONITOR OF RELEASE 7
;OR LATER

STLAS2:	MOVE	S1,P1			;PICK UP THIS NODE'S NODE TABLE ENTRY
	$CALL	CHKSTS			;CHECK FOR DECNET AND RELEASE 7 MONITOR
	JUMPF	STLAS3			;LOSES, CHECK THE NEXT NODE

;START UP THE LISTENER AND SENDER FOR THIS NODE. (If STAINF CANNOT STARTUP
;A LISTENER AND SENDER, THEN IT CRASHES.)

	$CALL	STAINF			;START UP THE LIS/SEN FOR THIS NODE
STLAS3:	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE ENTRY
	SOJG	P2,STLAS2		;START THE NEXT NODE'S LIS/SEN
	SKIPN	[N$ACKO]		;[6016]NEBULA ACK MSG OPTION ENABLED?
	$RET				;[6016]NO, RETURN TO THE CALLER NOW
	SKIPE	DEBUGW			;[6016]DEBUGGING?
	$RET				;[6016]YES, RETURN NOW
	$CALL	NEBTMR			;[6016]NO, SET UP THE ACK MESSAGE TIMER
	$RET				;[6016]AND RETURN TO THE CALLER
	SUBTTL	STAINF - STARTUP A LISTENER AND SENDER TO A NODE

;STAINF is called to start up a listener and sender to a node.
;This routine is called during NEBULA's startup and when a cluster
;topology change has occurred which results in a node joining the cluster.
;A listener and sender can only be started for a node that has DECnet
;enabled and has a monitor of release 7 or later.
;
;This routine also sets status bits in the node table entry for this node
;indicating that the sender has not yet received a response to its HELLO
;message and that the listener has not yet received a HELLO message.
;
;Call is: S1/Node table entry address of the node that a sender and
;         listener are to be started for
;Returns: The listener and sender have been started to the node
;Crashes: If a listener or sender cannot be started

STAINF:	$SAVE	<P1>			;SAVE THIS AC
	MOVE	P1,S1			;PICK UP THE NODE TABLE ENTRY ADDRESS

;SET THE STATUS BITS IN THE NODE TABLE ENTRY TO INDICATE THAT THE LISTENER 
;IS WAITING FOR A HELLO MESSAGE AND THAT THE SENDER IS WAITING FOR A
;HELLO RESPONSE

	MOVX	S2,NN%SHO!NN%LHO	;SENDER/LISTENER HAVE NO HELLO
	IORM	S2,.NNSTA(P1)		;INDICATE IN THE NODE STATUS WORD
	MOVX	S2,NL%WHL		;LISTENER WAITING FOR A HELLO 
	IORM	S2,.NLSTA(P1)		;INDICATE IN THE LISTENER STATUS WORD
	MOVX	S2,NS%WHR		;SENDER WAITING FOR A HELLO RESPONSE
	IORM	S2,.NSSTA(P1)		;INDICATE IN THE SENDER STATUS WORD

;START UP A LISTENER. FIRST OBTAIN AND INITIALIZE THE LISTENER DATA BASE

	MOVEI	S1,DBSIZ		;SIZE OF THE LISTENER BLOCK IN PAGES
	$CALL	M%AQNP			;GET THE LISTENER BLOCK PAGES
	PG2ADR	S1			;CHANGE PAGE NUMBER TO ADDRESS
	MOVEM	S1,.NNLBA(P1)		;SAVE ADDRESS IN THE LISTENER BLOCK
	MOVE	LIS,S1			;PLACE ADR IN LISTENER BLOCK DB POINTER
	ADDI	S1,PAGSIZ		;POINT TO THE MESSAGE BUFFER ADDRESS
	MOVEM	S1,.LSMSG(LIS)		;SAVE THE MESSAGE BUFFER ADDRESS
	MOVEM	P1,.LSNTA(LIS)		;SAVE THE NODE TABLE ENTRY ADDRESS
	MOVE	S1,.NNNAM(P1)		;PICK UP THE REMOTE NODE NAME
	MOVEM	S1,.LSNME(LIS)		;PLACE IN THE LISTENER BLOCK
	$CALL	BLDSRV			;BUILD THE DECNET SRV: DEVICE NAME
	$CALL	STLIS			;START UP THE LISTENER

;START UP A SENDER. FIRST OBTAIN AND INITIALIZE THE SENDER DATA BASE

	MOVEI	S1,DBSIZ		;SIZE OF THE SENDER BLOCK IN PAGES
	$CALL	M%AQNP			;GET THE SENDER BLOCK PAGES
	PG2ADR	S1			;CHANGE PAGE NUMBER TO ADDRESS
	MOVEM	S1,.NNSBA(P1)		;SAVE ADR IN THE SENDER BLOCK 
	MOVE	SEN,S1			;PLACE ADR IN SENDER BLOCK DB POINTER
	ADDI	S1,PAGSIZ		;POINT TO THE MESSAGE BUFFER ADDRESS
	MOVEM	S1,.SNMSG(SEN)		;SAVE THE MESSAGE BUFFER ADDRESS
	MOVEM	P1,.SNNTA(SEN)		;SAVE THE NODE TABLE ENTRY ADDRESS
	MOVE	S1,.NNNAM(P1)		;PICK UP THE REMOTE NODE NAME
	MOVEM	S1,.SNNME(SEN)		;PLACE IN THE SENDER BLOCK
	$CALL	BLDDCN			;BUILD THE DECNET DCN: DEVICE NAME
	$CALL	STSEN			;START UP THE SENDER

;CREATE THE TIMER LIST INDEX

	$CALL	L%CLST			;CREATE THE TIMER LIST INDEX
	MOVEM	S1,.NTIML(P1)		;SAVE IN THE NODE TABLE ENTRY
	$RET				;RETURN TO THE CALLER
	SUBTTL	BLDSRV - BUILD THE SRV: DEVICE NAME

;BLDSRV builds the listener's DECnet SRV: device name and places it in the
;listener block. It also builds the name of the sender that will attempt
;to establish a DECnet connection with the listener. The sender name is
;used by the listener as part of validating the link request.
;
;Call is: LIS/Address of the listener block
;Returns: The SRV: device name and the sender name have been built and
;         placed in the listener block

BLDSRV:	SKIPE	DEBUGW				;[6003]DEBUGGING?
	JRST	BLDS.1				;[6003]YES, SET UP DIFFERENTLY
	$TEXT	(<-1,,.LSSRV(LIS)>,<SRV:TASK.^N/.LSNME(LIS)/$NEBULA$LS^0>)
	$TEXT	(<-1,,.LSSNE(LIS)>,<^N/.LSNME(LIS)/$NEBULA$SN^0>) ;SENDER NAME
	$RET					;RETURN TO THE CALLER

BLDS.1:	$SAVE	<T1,T2>				;[6003]SAVE THESE AC
	GJINF%					;[6003]PICK UP THE USER'S NUMBER
	MOVE	S2,S1				;[6003]SAVE NUMBER WHERE EXPECTED
	MOVEI	S1,.LSDBW(LIS)			;[6003]WHERE TO PLACE USER NAME
	HRLI	S1,(POINT 7)			;[6003]MAKE INTO A POINTER
	DIRST%					;[6003]PICK UP THE USER NAME
	 JRST	S..COD				;[6003]CRASH ON AN ERROR
	MOVEI	S1,.LSDBW(LIS)			;[6003]PICK UP USER NAME ADDRESS
	HRLI	S1,(POINT 7,)			;[6003]MAKE INTO A POINTER
	SETZ	T1,				;[6003]NUMBER OF CHAR IN NAME
BLDS.2:	ILDB	S2,S1				;[6003]PICK UP THE NEXT CHARACTER
	SKIPN	S2				;[6003]IS THIS THE LAST ONE?
	JRST	BLDS.3				;[6003]YES, BUILD THE DEVICE NAME
	AOS	T1				;[6003]INCREMENT THE CHAR COUNT
	CAIG	T1,^D6				;[6003]MAXIMUM COUNT?
	JRST	BLDS.2				;[6003]NO, GET THE NEXT CHAR
	SETZ	S2,				;[6003]PICK UP A NULL
	DPB	S2,S1				;[6003]PLACE IN USER NAME
BLDS.3:	MOVEI	S1,.LSDBW(LIS)			;[6003]ADDRESS OF THE USER NAME

	$TEXT	(<-1,,.LSSRV(LIS)>,<SRV:TASK.^N/.LSNME(LIS)/$^T/0(S1)/$LS^0>)

	$TEXT	(<-1,,.LSSNE(LIS)>,<^N/.LSNME(LIS)/$^T/0(S1)/$SN^0>) ;SENDER NAME
	$RET					;[6003]RETURN TO THE CALLER
	SUBTTL	STLIS - START UP THE LISTENER

;STLIS is called to start up a listener. 
;This routine is called at NEBULA startup and also when a cluster topology
;change occurs that results in a node joining the cluster.
;
;Note: The listener block pages are obtained via the GLXMEM routine M%AQNP.
;M%AQNP zeros out the pages it returns. This implies the following:
;
;	SETZM	.LSLNK(LIS)		;ZERO OUT THE DECNET STATUS LINK
;	SETZM	.LSAVA(LIS)		;THE TOP FORK IS BUSY
;	SETZM	.LSQUE(LIS)		;THERE ARE NO MESSAGES IN THE MSG QUEUE
;
;Call is: LIS/ Listener block address
;Returns: A listener is successfully started. 
;Crashes: If the listener cannot be started (i.e., a CFORK%, SFORK% or
;         PMAP% error has occurred).

;SETUP THE CONTEXT OF THE LISTENER

STLIS:	$SAVE	<T1,T2>			;SAVE THESE AC
	MOVEI	S1,.LSPDL-1(LIS)	;SET UP THE LISTENER CONTEXT
	HRLI	S1,-PDSIZ		;STACK POINTER
	PUSH	S1,[EXP LISTEN]		;START THE LISTENER HERE
	MOVEM	S1,.LSREG+P(LIS)	;PLACE IN THE DATABASE
	MOVEM	LIS,.LSREG+LIS(LIS)	;SAVE THE ADDRESS OF THE DB

;START UP THE LISTENER. FIRST CREATE THE LISTENER AS AN INFERIOR FORK
;WITH THE SAME CAPABILIIES AS THE TOP FORK.

	MOVX	S1,<CR%CAP+CR%ACS>	;SUPERIOR CAPS AND AC'S
	MOVEI	S2,.LSREG(LIS)		;AC LOAD BUFFER
	CFORK%				;CREATE A LISTENER
	 ERJMP	STLIS2			;CRASH ON AN ERROR

;MAP NEBULA'S PAGES INTO THE LISTENER

	MOVEM	S1,.LSHND(LIS)		;SAVE THE LISTENER'S HANDLE
	MOVSI	S1,.FHSLF		;GET THE TOP FORK'S HANDLE
	HRLZ	S2,.LSHND(LIS)		;GET THE LISTENER'S HANDLE
	HRR	T1,NEBSIZ		;GET THE LENGTH IN PAGES
	HRLI	T1,(PM%RWX!PM%CNT)	;COUNT+READ+EXECUTE
	PMAP%				;MAP THE PAGES
	 ERJMP	STLIS3			;CRASH ON AN ERROR

;MAP THE LISTENER BLOCK PAGES INTO THE LISTENER

	MOVE	S1,LIS			;GET THE LISTENER'S BLOCK ADDRESS
	ADR2PG	S1			;CONVERT IT TO A PAGE NUMBER
	MOVE	S2,S1			;SAVE IT IN S2
	HRLI	S1,.FHSLF		;GET THE TOP FORK'S HANDLE
	HRL	S2,.LSHND(LIS)		;GET THE LISTENER'S HANDLE
	MOVEI	T1,DBSIZ		;GET THE PAGE COUNT
	HRLI	T1,(PM%RWX!PM%CNT)	;R,W,E + COUNT
	PMAP%				;MAP THE DATA BASE
	 ERJMP	STLIS3			;CRASH ON AN ERROR

;START THE LISTENER

	MOVE	S1,.LSHND(LIS)		;GET THE LISTENER'S HANDLE
	MOVEI	S2,LISTEN		;GET THE START ADDRESS
	SFORK%				;START THE LISTENER
	 ERJMP	STLIS4			;ON ERROR,,PROCESS IT
	$RET				;AND RETURN

STLIS2:	$STOP	(CCL,CAN'T CREATE A LISTENER FORK)

STLIS3:	$STOP	(CML,CAN'T MAP A LISTENER)
	
STLIS4:	$STOP	(CSL, CAN'T START A LISTENER)
	SUBTTL	BLDDCN - BUILD THE DCN: DEVICE NAME

;BLDDCN builds the DECnet DCN: device name that the sender will use
;in opening its DECnet link. The format of the DCN: device name is:
;DCN:RNODE-TASK-LNODE$NEBULA$LS.LNODE$NEBULA$SN;BDATA:NNNNN
;
;where RNODE is the remote node name
;      LNODE is the local node name
;      NNNNN comes from the listener's node name and is used by the listener
;            in accepting a connection
;
;Call is: SEN/Address of the sender block
;Returns: The DCN: device name has been built and placed in the sender block

BLDDCN:	$SAVE	<T1,T2,T3,T4>		;SAVE THESE AC

;BUILD THE OPTIONAL DATA FIELD, USING THE SIXBIT REMOTE NODE NAME AS
;THE STARTING VALUE. CREATE 12 OCTAL CHARACTERS AND CONVERT THEM TO ASCII. 

	MOVE	S2,.SNNME(SEN)		;PICK UP THE REMOTE SIXBIT NODE NAME
	ROT	S2,3			;ROTATE BY HALF A CHARACTER
	MOVE	T1,[POINT 7,S1,35]	;POINTER TO BYTE TO PICK UP
	MOVE	T2,[POINT 7,NODDAT]	;POINTER TO WHERE BYTE IS TO BE PUT
	SETZ	S1,			;CLEAR RECEIVING WORD OF OCTAL VALUE
	MOVEI	T3,^D36/3		;NUMBER OF OCTAL CHARACTERS

BLDDC2:	LSHC	S1,3			;MOVE NEXT OCTAL VALUE OVER
	ADDI	S1,60			;MAKE IT ASCII
	LDB	T4,T1			;PICK UP ASCII VALUE
	IDPB	T4,T2			;PLACE IN ASCII STRING
	SETZ	S1,			;PREPARE FOR NEXT OCTAL VALUE
	SOJN	T3,BLDDC2		;PICK UP NEXT OCTAL VALUE
	IDPB	S1,T2			;MAKE INTO AN ASCIZ STRING

;BUILD THE DECNET DCN: DEVICE NAME

	SKIPE	DEBUGW			;[6003]DEBUGGING?
	JRST	BLDDC6			;[6003]YES, DO DIFFERENTLY

;**;[6037]At BLDDC2:+9L change 1 line  JYCw  Oct-18-88
	$TEXT(<-1,,.SNDCN(SEN)>,<DCN:^I/@BLDDC3/^I/@BLDDC4/^I/@BLDDC5/^I/@BLDD20/^0>)	;[6037]Include the password.


	$RET				;RETURN TO THE CALLER

BLDDC3: [ITEXT(<^N/.SNNME(SEN)/-TASK-^N/NODNAM/$NEBULA$LS.>)]	;LISTENER NAME
BLDDC4:	[ITEXT(<^N/NODNAM/$NEBULA$SN;BDATA:^T/NODDAT/>)] ;THE SENDER NAME
BLDDC5:	[ITEXT(<;USERID:^N/NODNAM/$NEBULA$SN>)]
;**;[6037]At BLDDC5:+0L add 1 line  JYCW  Oct-18-88
BLDD20:	[ITEXT(<;PASSWORD:AUTO20>)]		;[6037]Include the autopatch # 

BLDDC6:	GJINF%					;[6003]PICK UP THE USER'S NUMBER
	MOVE	S2,S1				;[6003]SAVE NUMBER WHERE EXPECTED
	MOVEI	S1,.SNDBW(SEN)			;[6003]WHERE TO PLACE USER NAME
	HRLI	S1,(POINT 7)			;[6003]MAKE INTO A POINTER
	DIRST%					;[6003]PICK UP THE USER NAME
	 JRST	S..COD				;[6003]CRASH ON AN ERROR
	MOVEI	S1,.SNDBW(SEN)			;[6003]PICK UP USER NAME ADDRESS
	HRLI	S1,(POINT 7,)			;[6003]MAKE INTO A POINTER
	SETZ	T1,				;[6003]NUMBER OF CHAR IN NAME
BLDDC7:	ILDB	S2,S1				;[6003]PICK UP THE NEXT CHARACTER
	SKIPN	S2				;[6003]IS THIS THE LAST ONE?
	JRST	BLDDC8				;[6003]YES, BUILD THE DEVICE NAME
	AOS	T1				;[6003]INCREMENT THE CHAR COUNT
	CAIG	T1,^D6				;[6003]MAXIMUM COUNT?
	JRST	BLDDC7				;[6003]NO, GET THE NEXT CHAR
	SETZ	S2,				;[6003]PICK UP A NULL
	DPB	S2,S1				;[6003]PLACE IN USER NAME
BLDDC8:	MOVEI	S1,.SNDBW(SEN)			;[6003]ADDRESS OF THE USER NAME

;**;[6037]At BLDDC8:+1L change 1 line  JYCW  Oct-18-88
	$TEXT(<-1,,.SNDCN(SEN)>,<DCN:^I/@BLDDC9/^I/@BLDD10/^I/@BLDD11/^I/@BLDD20/^0>)	;[6037]

	$RET				;RETURN TO THE CALLER

BLDDC9: [ITEXT(<^N/.SNNME(SEN)/-TASK-^N/NODNAM/$^T/0(S1)/$LS.>)]  ;[6003]LISTENER NAME
BLDD10:	[ITEXT(<^N/NODNAM/$^T/0(S1)/$SN;BDATA:^T/NODDAT/>)]       ;[6003]SENDER NAME
BLDD11:	[ITEXT(<;USERID:^N/NODNAM/$^T/0(S1)/$SN>)]		  ;[6003]
	SUBTTL	STSEN - START UP A SENDER

;STSEN  is called to start up a sender.
;This routine is called at NEBULA startup and also when a cluster
;topology change occurs that results in a node joining the cluster.
;
;Note: The sender block pages are obtained via the GLXMEM routine M%AQNP.
;M%AQNP zeros out the pages it returns. This implies the following:
;
;	SETZM	.SNLNK(SEN)		;ZERO OUT THE DECNET STATUS LINK
;	SETZM	.SNFRE(SEN)		;SENDER NOT READY TO PICK UP A MESSAGE
;
;Call is: SEN/Address of the sender block
;Returns: The sender has been sucessfully started
;Crashes: The sender cannot be started (i.e., a CFORK%, SFORK% or
;         PMAP% error has occurred)

;SET UP THE CONTEXT OF THE SENDER

STSEN:	$SAVE	<T1,T2>			;SAVE THESE AC
	MOVEI	S1,.SNPDL-1(SEN)	;SET UP THE SENDER CONTEXT
	HRLI	S1,-PDSIZ		;STACK POINTER
	PUSH	S1,[EXP SENDER]		;START THE SENDER HERE
	MOVEM	S1,.SNREG+P(SEN)	;PLACE IN THE DATA BASE
	MOVEM	SEN,.SNREG+SEN(SEN)	;SAVE THE ADDRESS OF THE DB

;START UP THE SENDER. FIRST CREATE THE SENDER AS AN INFERIOR FORK
;WITH THE SAME CAPABILITIES AS THE TOP FORK.

	MOVX	S1,<CR%CAP+CR%ACS>	;SUPERIOR CAPS AND AC'S
	MOVEI	S2,.SNREG(SEN)		;AC LOAD BUFFER
	CFORK%				;CREATE A SENDER
	 ERJMP	STSEN2			;CRASH ON AN ERROR

;MAP NEBULA'S PAGES INTO THE SENDER

	MOVEM	S1,.SNHND(SEN)		;SAVE THE SENDER'S HANDLE
	MOVSI	S1,.FHSLF		;GET NEBULA'S HANDLE
	HRLZ	S2,.SNHND(SEN)		;GET THE SENDER'S HANDLE
	HRR	T1,NEBSIZ		;GET THE LENGTH IN PAGES
	HRLI	T1,(PM%RWX!PM%CNT)	;COUNT+READ+EXECUTE
	PMAP%				;MAP THE PAGES
	 ERJMP	STSEN3			;CRASH ON AN ERROR

;MAP THE SENDER BLOCK INTO THE SENDER

	MOVE	S1,SEN			;GET THE SENDER BLOCK ADDRESS
	ADR2PG	S1			;CONVERT IT TO A PAGE NUMBER
	MOVE	S2,S1			;SAVE IT IN S2
	HRLI	S1,.FHSLF		;GET THE TOP FORK'S HANDLE
	HRL	S2,.SNHND(SEN)		;GET THE SENDER'S HANDLE
	HRRI	T1,DBSIZ		;GET THE PAGE COUNT
	HRLI	T1,(PM%RWX!PM%CNT)	;R,W,E + COUNT
	PMAP%				;MAP THE DATA BASE
	 ERJMP	STSEN3			;CRASH ON AN ERROR 

;START THE SENDER

	MOVE	S1,.SNHND(SEN)		;GET THE SENDER'S HANDLE
	MOVEI	S2,SENDER		;GET THE START ADDRESS
	SFORK%				;START THE SENDER
	 ERJMP	STSEN4			;CRASH ON AN ERROR
	$RET				;AND RETURN

STSEN2:	$STOP	(CCS,CAN'T CREATE A SENDER)

STSEN3:	$STOP	(CPS,CAN'T PMAP A SENDER)

STSEN4:	$STOP	(CSS, CAN'T START A SENDER)
	SUBTTL	CLUSTER TOPOLOGY CHANGE DETECTED

;TOPCHN is called as a result of the SCS interrupt handler
;detecting that the cluster topology has changed. This routine
;updates the node data base and cleans up the message queue, remote
;queue and in behalf of queue for any nodes that may have left the
;cluster.
;
;Call is: No arguments
;Returns: Updated node data base
;Crashes: If the node table is inconsistent

TOPCHN: $SAVE	<P1,P2,P3,P4,T1>	;SAVE THE CONTENTS OF THESE AC

;FIRST READ IN THE LATEST TOPOLOGY OF THE CLUSTER

	SOS	BLDTAB			;DON'T LET TOPOLOGY CHANGE INTERRUPT
					;HANDLER TOUCH THE NODE TABLE
TOPCH1:	SETZM	SCSFLG			;TURN OFF SCS% INTERRUPT FLAG
	SETOM	NBSCHD			;FORCE A SCHEDULING PASS
	$CALL	GTNDAT			;PICK UP THE NODE NAMES AND NUMBERS

;CONVERT THE ASCIZ NODE NAMES TO SIXBIT AND PLACE IN THE SIXBIT NODE NAME
;TABLE  NDNTBL

	MOVEI	P1,ASZNAM		;POINT TO THE ARGUMENT BLOCK
	HLRZ	P2,.CFNND(P1)		;PICK UP THE NUMBER OF NODES
	SOJE	P2,TOPC12		;NO MORE REMOTE NODES
	CAILE	P2,MAXNOD		;IS LESS THAN MAX SUPPORTED NODES?
	$CALL	S..CTL			;NO, CRASH NOW
	MOVEM	P2,CURNUM		;REMEMBER THE CURRENT # OF REMOTE NODES

	ADDI	P1,.CFBP1+1		;POINT TO THE FIRST ASCIZ NODE NAME
	MOVEI	T1,NDNTBL		;POINT TO THE FIRST SIXBIT NODE NAME
TOPCH2:	MOVE	S1,0(P1)		;PICK UP THE NODE NAME BYTE POINTER
	$CALL	S%SIXB			;CONVERT THE NODE NAME TO SIXBIT
	MOVEM	S2,0(T1)		;PLACE IN THE SIXBIT NODE NAME TABLE
	AOS	P1			;POINT TO THE NEXT ASCIZ NAME POINTER
	AOS	T1			;POINT TO THE NEXT SIXBIT NAME
	SOJG	P2,TOPCH2		;CONVERT THE NEXT NODE NAME

;DETERMINE WHICH NODES NOW IN THE CLUSTER WERE PREVIOUSLY IN THE CLUSTER

	SKIPG	RENNUM			;WERE THERE REMOTE NODES IN CLUSTER?
	JRST	TOPC15			;NO, CHECK FOR NEW NODES
	MOVEI	P1,NODTBL		;POINT TO THE NODE TABLE
	MOVE	P3,RENNUM		;PICK UP PREVIOUS NUMBER OF NODES
	MOVEI	P4,MAXNOD		;PICK UP MAX NUMBER OF REMOTE NODES
	MOVS	P2,CURNUM		;PICK UP CURRENT # OF NODES
	MOVNS	P2			;MAKE IT NEGATIVE FOR THE AOBJN
	MOVE	T1,P2			;SAVE FOR LATER

TOPCH3:	SKIPN	S1,.NNNAM(P1)		;PICK UP NEXT NODE NAME FROM NODE TABLE
	JRST	TOPCH8			;THIS NODE TABLE ENTRY IS NOT IN USE
TOPCH4:	CAMN	S1,NDNTBL(P2)		;COMPARE WITH THE CURRENT NODE NAMES
	JRST	TOPCH9			;A MATCH, CHECK THE STATUS OF THE NODE
	AOBJN	P2,TOPCH4		;CHECK THE NEXT CURRENT NODE

;THE NODE NAME IN THE NODE TABLE ENTRY WAS NOT FOUND IN THE CURRENT NODE
;NAME LIST. THIS MEANS THAT THIS NODE IS NO LONGER IN THE CLUSTER.
;DELETE THIS NODE FROM THE NODE DATA BASE.

	MOVE	S1,P1			;PASS THE NODE TABLE ENTRY ADDRESS
	$CALL	KILNOD			;DELETE THIS NODE FROM NODE DATA BASE
TOPCH6:	SOJE	P3,TOPC15		;FINISHED CHECKING FOR KNOWN NODES?
	MOVE	P2,T1			;NO, POINT TO NODE NAME TABLE
TOPCH8:	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	SOJE	P4,S..NTI		;CRASH, THE NODE TABLE IS INCONSISTENT
	JRST	TOPCH3			;CHECK IF THIS NODE IS KNOWN

;THE NODE EXISTS, FIRST CHECK IF THE NODE MAY HAVE CRASHED AND COME BACK


TOPCH9:	MOVE	S1,.NNSTA(P1)		;PICK UP THE NODE'S STATUS
	TXNN	S1,NN%SCS		;DID THIS NODE CRASH AND COME BACK
	JRST	TOPC11			;NO, CHECK IF INFO% ENCOUNTERED ERROR

;THE NODE HAS CRASHED AND COME BACK. FIRST KILL THE SENDER AND THE LISTENER
;AND ZERO OUT THE NODE ENTRY. ALSO, UPDATE THE MESSAGE, REMOTE, AND
;IN BEHALF OF QUEUES FOR THIS NODE.

	MOVE	S1,P1			;PICK UP THE NODE ENTRY ADDRESS
	$CALL	KILNOD			;KILL THE SENDER AND LISTENER

;REBUILD THE NODE TABLE ENTRY AND CHECK THAT THE NODE HAS BOTH DECNET
;ENABLED AND HAS A RELEASE 7 OR LATER MONITOR

	MOVE	S1,P1			;PICK UP THE NODE TABLE ENTRY ADDRESS
	MOVE	S2,P2			;PICK UP THE NODE NAME TABLE OFFSET
	$CALL	BLDNEN			;BUILD THE NODE ENTRY
	JUMPF	TOPCH6			;NO DECNET AND/OR RELEASE 7 MONITOR

;THE NODE HAS DECNET AND A RELEASE 7 OR LATER MONITOR. THEREFORE, START A
;LISTENER AND SENDER DIRECTED AT THIS NODE

TOPC10:	MOVE	S1,P1			;PICK UP THE NODE ENTRY ADDRESS
	$CALL	STAINF			;START THE LISTENER AND SENDER
	JRST	TOPCH6			;CHECK THE NEXT NODE
	
;CHECK IF THE INFO% JSYS HAD AN ERROR OBTAINING THIS NODE'S S.W. ENVIRONMENT
;IF SO, HAVE THE INFO% JSYS RETRY TO OBTAIN THIS NODE'S SOFTWARE ENVIRONMENT

TOPC11:	TXNN	S1,NN%COI		;INFO% PREVIOUSLY HAD AN ERROR?
	JRST	TOPCH6			;NO, GO CHECK THE NEXT NODE
	MOVE	S1,P1			;YES, PICK UP THE NODE TABLE ENTRY ADR
	$CALL	NODPAR			;LET INFO% TRY AGAIN
	JUMPF	TOPCH6			;[6016]CHECK NEXT NODE ON AN ERROR
	MOVX	S1,NN%COI!NN%DCN!NN%REL	;[6016]PICK UP THE ERROR BITS
	ANDCAM	S1,.NNSTA(P1)		;[6016]TURN THEM OFF IN THE STATUS WORD
	MOVE	S1,.NIETM(P1)		;[6016]PICK UP INFO% TIME WORD VALUE
	SETZM	.NIERC(P1)		;[6016]ZERO OUT INFO% RETRY COUNT
	SETZM	.NIETM(P1)		;[6016]ZERO OUT INFO% TIME WORD
	CAMN	S1,[-1]			;[6016]RETRY COUNT EXHAUSTED?
	JRST	TOPCH6			;[6016]YES, THEN NO TIMER IS SET
	MOVEI	S2,IEPROC		;[6016]CLEAR  THE INFO% TIMER ENTRY
	$CALL	REMTIM			;[6016]CLEAR THE TIMER
	JRST	TOPCH6			;[6016]CHECK THE NEXT NODE

;THERE ARE NO LONGER ANY REMOTE NODES IN THIS CLUSTER. CLEAN UP THE
;NODE DATA BASE FOR ANY PREVIOUSLY KNOWN NODES

TOPC12:	SKIPG	P1,RENNUM		;ANY PREVIOUSLY KNOWN REMOTE NODES?
	JRST	TOPC23			;NO, SO ARE FINISHED WITH THE UPDATE
	SETZM	RENNUM			;NO MORE REMOTE NODES AFTER THIS
	MOVEI	P2,NODTBL		;PICK UP ADDRESS OF THE NODE TABLE
	MOVEI	P3,MAXNOD		;PICK UP MAXIMUM NUMBER OF NODES
TOPC13:	SKIPN	.NNNAM(P2)		;IS THIS ENTRY IN USE?
	JRST	TOPC14			;NO, CHECK THE NEXT ENTRY
	MOVE	S1,P2			;ADR OF THIS NODE'S NODE TABLE ENTRY
	$CALL	KILNOD			;DELETE THIS NODE FROM NODE DATA BASE
	SOJE	P1,TOPC23		;FINISHED IF NO MORE NODES TO PROCESS
TOPC14:	ADDI	P2,.NNNSZ		;POINT TO THE NEXT ENTRY
	SOJE	P3,S..NTI		;CHECK FOR INCONSISTENCY IN NODE TABLE
	JRST	TOPC13			;CHECK THE NEXT NODE

;ALL THE PREVIOUSLY KNOWN NODES HAVE BEEN PROCESSED. NOW CHECK FOR ANY
;NEW NODES IN THE CLUSTER. FIRST SET UP THE SEARCH

TOPC15:	MOVS	P2,CURNUM		;PICK UP THE NEW NUMBER OF NODES
	MOVNS	P2			;SET UP FOR THE AOBJN LOOP

;SEARCH FOR THE NODE NAME IN THE NODE TABLE

TOPC16:	MOVEI	P1,NODTBL		;POINT TO THE NODE TABLE
	MOVEI	P3,MAXNOD		;PICK UP SIZE OF THE NODE TABLE
	MOVE	S1,NDNTBL(P2)		;PICK UP NEXT CURRENT NODE NAME
TOPC17:	CAMN	S1,.NNNAM(P1)		;DO THEY MATCH?
	JRST	TOPC22			;YES, CHECK THE NEXT LATEST NODE NAME
	ADDI	P1,.NNNSZ		;NO, POINT TO NEXT NODE IN NODE TABLE
	SOJG	P3,TOPC17		;GO CHECK THIS NODE

;NODE NAME NOT FOUND IN THE NODE TABLE. THIS IS A NEW NODE. ADD THIS NODE
;TO THE NODE TABLE. FIRST FIND A FREE ENTRY.

	MOVEI	P1,NODTBL		;POINT TO THE NODE TABLE
	MOVEI	P3,MAXNOD		;PICK UP SIZE OF THE NODE TABLE
TOPC18:	SKIPN	.NNNAM(P1)		;IS THIS NODE TABLE ENTRY FREE?
	JRST	TOPC19			;YES, GO SET UP THE NODE ENTRY
	ADDI	P1,.NNNSZ		;NO, POINT TO THE NEXT ENTRY
	SOJG	P3,TOPC18		;CHECK THE NEXT ENTRY
	PJRST	S..NTI			;CRASH IF NODE TABLE IS INCONSISTENT

;BUILD THE NODE TABLE ENTRY AND CHECK IF THE NODE IS IN THE PACKN TABLE.
;IF IT IS NOT, THEN ADD IT

TOPC19:	MOVE	S1,P1			;PICK UP THE NODE TABLE ENTRY ADR
	MOVE	S2,P2			;PICK UP OFFSET INTO NODE NAME TABLE
	$CALL	BLDNEN			;BUILD THE NODE ENTRY
	MOVE	T1,TF			;SAVE THE TRUE/FALSE AC CONTENTS
	MOVE	S2,TF			;INDICATE IF A SENDER CAN BE STARTED
	MOVE	S1,P1			;PICK UP NODE TABLE ENTRY ADDRESS
	$CALL	FNDPNE			;FIND OR BUILD A PACKN ENTRY
	JUMPE	T1,TOPC22		;NO DECNET AND/OR RELEASE 7 MONITOR?

;THE NODE HAS DECNET AND A RELEASE 7 OR LATER MONITOR. THEREFORE, START A
;LISTENER AND SENDER DIRECTED AT THIS NODE

TOPC21:	MOVE	S1,P1			;PICK UP THE NODE ENTRY ADDRESS
	$CALL	STAINF			;START THE LISTENER AND SENDER
TOPC22:	AOBJN	P2,TOPC16		;CHECK THE NEXT NODE

	MOVE	S1,CURNUM		;PICK UP THE LATEST NUMBER OF NODES
	MOVEM	S1,RENNUM		;SAVE FOR LATER
TOPC23:	SKIPE	SCSFLG			;DID A TOPOLOGY CHANGE OCCUR?
	JRST	TOPCH1			;YES, UPDATE THE NODE DATA BASE
	AOS	BLDTAB			;ALLOW NBSCS TO TOUCH NODE TABLE
	SKIPN	RECHEK			;[6016]NEED A RECHECK?
	$RETT				;[6016]NO, RETURN NOW
	$CALL	TOPTMR			;[6016]SET UP THE TOPOLOGY TIMER
	SETZM	RECHEK			;[6016]INDICATE NO RECHECK
	$RETT				;[6016]AND RETURN TO THE CALLER
	SUBTTL	KILNOD - DELETE A NODE FROM THE NODE DATA BASE

;KILNOD removes a node from the node data base. The node's sender
;and listener are killed. Also, all the node's entries in the
;message, remote and in behalf of queues must be updated. In addition, the
;node's timer list must be deleted and all timers turned off.
;
;Call is: S1/Node Table entry address of the node
;Returns: Node is deleted from the node data base


KILNOD:	$SAVE	<P1,P2>			;SAVE THESE AC
	MOVE	P1,S1			;SAVE THE NODE ENTRY ADDRESS

;DETERMINE IF THIS NODE HAS A SENDER AND LISTENER ASSOCIATED WITH IT

	SKIPN	.NNSBA(P1)		;SENDER ADDRESS BLOCK PRESENT?
	JRST	KILNO2			;NO, SO NO LISTENER AND SENDER

;KILL THE LISTENER AND SENDER

	MOVE	S1,P1			;PICK UP THE NODE TABLE ENTRY ADDRESS
	$CALL	KILINF			;KILL THE LISTENER AND SENDER

;DELETE THE TIMER LIST AND TURN OFF THE TIMERS FOR THIS NODE.
;UPDATE ANY REMOTE QUEUE ENTRIES FOR THIS NODE. MARK THE STATUS OF THE
;REMOTE QUEUE ENTRY FOR THIS NODE AS "SENDER/LISTENER ARE CRASHED".
;ALSO, DELETE ALL IN BEHALF OF QUEUE ENTRIES TO THIS NODE.
;ALSO, UPDATE THE MESSAGE QUEUE FOR THIS NODE.

	MOVE	S1,P1			;PICK UP THE NODE TABLE ENTRY ADDRESS
	$CALL	UPDQUE			;UPDATE THIS NODE'S TIMER LIST & QUEUES

;RELEASE THE SENDER BLOCK AND LISTENER BLOCK PAGES

	MOVEI	S1,DBSIZ		;NUMBER OF PAGES IN SENDER BLOCK
	MOVE	S2,.NNSBA(P1)		;ADDRESS OF SENDER BLOCK
	ADR2PG	S2			;CHANGE TO PAGE NUMBER
	$CALL	M%RLNP			;RELEASE THE PAGES

	MOVEI	S1,DBSIZ		;NUMBER OF PAGES IN LISTENER BLOCK
	MOVE	S2,.NNLBA(P1)		;ADDRESS OF LISTENER BLOCK
	ADR2PG	S2			;CHANGE TO PAGE NUMBER
	$CALL	M%RLNP			;RELEASE THE PAGES

;[6016]IF AN INFO% JSYS TIMER IS SET, THEN CLEAR IT

KILNO2:	SKIPN	S1,.NIETM(P1)		;[6016]PICK UP THE INFO% TIMER WORD
	JRST	KILNO3			;[6016]IF NOT SET, CLEAR NODE ENTRY
	CAMN	S1,[-1]			;[6016]RETRY COUNT EXHAUSTED?
	JRST	KILNO3			;[6016]YES, CLEAR THE NODE ENTRY
	MOVEI	S2,IEPROC		;[6016]CLEAR THE INFO% TIMER ENTRY
	$CALL	REMTIM			;[6016]CLEAR THE TIMER

;ZERO OUT THE NODE ENTRY TO INDICATE THAT IT IS FREE

KILNO3:	SETZM	.NNNAM(P1)		;[6016]CLEAR 1ST WORD OF THE NODE ENTRY
	MOVE	S1,P1			;PICK UP THE ADDRESS OF THE NODE ENTRY
	ADDI	S1,.NNNSZ		;POINT TO END OF NODE ENTRY + 1
	HRLS	P1			;SOURCE ADDRESS OF BLT IN LEFT HALF
	AOS	P1			;DESTINATION ADR OF BLT IN RIGHT HALF
	BLT	P1,-1(S1)		;ZERO THE NODE ENTRY

	$RET				;AND RETURN TO THE CALLER
	SUBTTL	KILINF - KILL A NODE'S LISTENER AND SENDER

;KILINF kills the sender and listener associated with a node. This occurs when
;it is detected that the remote node associated with the sender and listener
;no longer exists, or has crashed and come back, or the sender and/or listener
;to that node has crashed.
;
;Call is: S1/Node table entry address
;Returns: The sender and listener have been killed

KILINF:	$SAVE	<P1>			;SAVE THIS AC
	MOVE	P1,S1			;SAVE THE NODE TABLE ENTRY ADDRESS
	
;FIRST KILL THE SENDER

	MOVE	SEN,.NNSBA(P1)		;PICK UP ADDRESS OF THE SENDER BLOCK
	MOVE	S1,.SNHND(SEN)		;PICK UP THE SENDER'S HANDLE
	KFORK%				;KILL THE SENDER
	 ERJMP	.+1			;HANDLE NO LONGER VALID, IGNORE

;NOW KILL THE LISTENER

	MOVE	LIS,.NNLBA(P1)		;PICK UP ADDRESS OF THE LISTENER BLOCK
	MOVE	S1,.LSHND(LIS)		;PICK UP THE LISTENER'S HANDLE
	KFORK%				;KILL THE LISTENER
	 ERJMP	.+1			;HANDLE NO LONGER VALID, IGNORE
	
	$RET				;RETURN TO THE CALLER
	SUBTTL	UPDQUE - UPDATE A NODE'S QUEUES

;UPDQUE is called to delete a node's timer list (and turn off its timers),
;remote queue, in behalf of queue and message queue. The queue updates
;are done as part of processing the data base of a node that has left the
;cluster (or left the cluster and has come back) or as part of the processing 
;when a sender and/or listener associated with the node has crashed.
;
;Call is: S1/Node table entry address of the node
;Returns: The node's queues have been updated

UPDQUE:	$SAVE	<P1>			;SAVE THESE AC
	MOVE	P1,S1			;PICK UP THE NODE TABLE ENTRY ADDRESS

	$CALL	DELTIM			;DELETE THE TIMER LIST AND TIMERS

	SKIPE	S1,.NRQLH(P1)		;IS THE REMOTE QUEUE EMPTY?
	$CALL	UPDREM			;NO, UPDATE THE REMOTE QUEUE

	MOVE	S1,.NNNAM(P1)		;PICK UP THE NODE NAME
	SKIPE	IBQHDR			;IS THE IBH QUEUE EMPTY?
	$CALL	UPDIBH			;NO, UPDATE THE IN BEHALF OF QUEUE

	MOVE	S2,.NNSBA(P1)		;PICK UP THE SENDER BLOCK ADDRESS
	SKIPE	S1,.SNHWD(S2)		;IS THE MESSAGE QUEUE EMPTY?
	$CALL	UPDMSG			;NO, UPDATE THE MESSAGE QUEUE

	$RET				;RETURN TO THE CALLER
	SUBTTL	DELTIM - DELETE THE TIMER LIST AND TURN OFF TIMERS

;DELTIM is called when communication has been lost to a remote node
;(a sender or listener to that node has crashed, the node has left
;the cluster, or a timeout to that node has occurred). 
;DELTIM deletes the timer list and turns off any outstanding timer
;events of the node.
;
;Call is: S1/Node table entry address
;Returns: Timer list has been deleted and timers turned off

DELTIM:	$SAVE	<P1>			;SAVE THIS AC

;DETERMINE IS THE TIMER LIST IS EMPTY. IF IT IS, JUST DELETE THE LIST HEADER

	MOVE	S1,.NTIML(S1)		;PICK UP TIMER LIST INDEX
	MOVE	P1,S1			;SAVE LIST INDEX IN CASE LIST IS EMPTY
	$CALL	L%FIRST			;PICK UP FIRST LIST ENTRY
	JUMPF	DELTI3			;IF EMPTY, DELETE THE LIST HEADER

;THE TIMER LIST IS NOT EMPTY. DELETE ALL ENTRIES AND TURN OFF ALL THE
;CORRESPONDING TIMERS.

DELTI2:	MOVE	S1,.TMUDT(S2)		;GET THE UDT THE TIMER IS TO GO OFF	
	SETZ	S2,			;[6016]DELETE ALL ENTRIES AT THIS UDT
	$CALL	REMTIM			;TURN OFF THE TIMER FOR THIS ENTRY
	MOVE	S1,P1			;PICK UP THE LIST INDEX
	$CALL	L%NEXT			;GET THE NEXT TIMER LIST ENTRY
	JUMPT	DELTI2			;GO TURN OFF THE TIMER FOR THIS ENTRY

;NO MORE TIMER LIST ENTRIES. DELETE THE TIMER LIST

DELTI3:	MOVE	S1,P1			;PICK UP THE LIST INDEX
	$CALL	L%DLST			;DELETE THE LIST
	$RET				;RETURN TO THE CALLER
	SUBTTL	UPDREM - CLEAN UP REMOTE QUEUE FOR SENDER/LISTENER CRASH

;UPDREM is called when communication to a remote node has been lost (a sender
;or listener to that node has crashed, the node has left the cluster, or a
;timeout to that node has occurred), part of the cleanup is to update the
;remote queue for that node. This is accomplished by marking every entry for
;that node as "sender/listener" has crashed.
;If there are no other outstanding responses, then a FROM NEBULA DISMOUNT
;ACK message is built and sent to ORION to be forwarded to MOUNTR.
;
;Call is: S1/Remote queue link list header word for node with crashed
;	  listener or sender
;Returns: The remote queue has been updated for the crashed node
;Crashes: Unable to send an ACK message to ORION

UPDREM:	$SAVE	<P1,P2>			;SAVE THESE AC

;POINT TO THE INVARIANT BLOCK ADDRESS OF THIS ENTRY AND POINT TO THE
;NODE BLOCK OF THIS NODE IN THE REMOTE QUEUE ENTRY

	MOVE	P1,S1			;SAVE THE REMOTE QUEUE L.L. HDR WORD	
UPDRE2:	LOAD	P2,.RQBLK(P1),RQ.OFF	;PICK UP THE OFFSET TO INVARIANT BLOCK
	ADD	S1,P2			;POINT TO THE INVARIANT BLOCK
	LOAD	S2,.RQNUM(S1),RQ.NTS	;PICK UP NUMBER OF NODE BLOCKS
	SUB	S2,P2			;SUBTRACT THE OFFSET TO GET BLOCK NUMBER
	IMULI	S2,.RQNSZ		;CALCULATE THE SIZE OCCUPIED BY THE BLKS
	ADDI	S2,.RQNIZ(S1)		;ADD INVARINAT BLOCK SIZE TO GET THE
					;ADDRESS OF THE NODE BLOCK

;INDICATE IN THE NODE BLOCK THAT THE "SENDER/LISTENER HAVE CRASHED"
;AND DECREMENT THE NUMBER OF OUTSTANDING RESPONSES TO BE RECEIVED
;FOR THIS QUEUE ENTRY

	MOVX	P2,RQ%SLD!RQ%TER	;[6030]SENDER/LISTENER CRASHED
	IORM	P2,.RQNST(S2)		;INDICATE IN NODE BLOCK
	MOVEI	P2,SCLX08		;[6027]INDICATE COMMUNICATION IS LOST
	STORE	P2,.RQNST(S2),RQ.ERR	;[6027]INDICATE IN THE NODE BLOCK
	SOS	.RQNUM(S1)		;ONE LESS RESPONSE TO EXPECT
	LOAD	P1,.RQBLK(P1),RQ.NEA	;PICK UP ADDRESS OF NEXT REMOTE QE
	LOAD	P2,.RQNUM(S1),RQ.NOR	;PICK UP NUMBER OF REMAINING RESPONSES
	SKIPN	P2			;IF MORE RESPONSES GO TO NEXT REMOTE QE

;THERE ARE NO MORE OUTSTANDING RESPONSES FOR THIS REMOTE QUEUE ENTRY.
;BUILD A TO NEBULA DISMOUNT ACK MESSAGE AND SEND IT TO ORION.

	$CALL	SNDACK			;BUILD AND SEND THE MESSAGE

	JUMPG	P1,UPDRE2		;PROCESS THE NEXT REMOTE QE, IF ANY
	$RET				;FINISHED, RETURN TO THE CALLER
	SUBTTL	UPDIBH - CLEAN UP IBH QUEUE FOR SENDER/LISTENER 

;UPDIBH is called when communication to a remote node has been lost (a sender
;or listener to that node has crashed, the node has left the cluster, or a
;timeout to that node has occurred). Part of the cleanup is to delete the 
;the entries in the in behalf of queue that correspond to that node.
;
;Call is: S1/SIXBIT NODE NAME that has a crashed listener or sender
;Returns: IBH queue has been cleaned up

UPDIBH:	$SAVE	<P1,P2,P3>		;SAVE THESE AC
	SKIPN	P2,IBQHDR		;IS THE IBH QUEUE EMPTY?
	JRST	UPDIB4			;YES, SO ARE FINISHED

;CHECK IF THIS ENTRY CORRESPONDS TO THE NODE

	MOVEI	P1,IBQHDR		;PICK UP ADDRESS OF PREVIOUS ENTRY
	MOVE	P3,S1			;REMEMBER THE NODE NAME
UPDIB2:	CAME	P3,.IQNEN(P2)		;DO THE NAMES MATCH?
	JRST	UPDIB3			;NO, CHECK THE NEXT ENTRY

;THIS ENTRY BELONGS TO THE NODE. DELETE IT FROM THE IN BEHALF OF QUEUE

	LOAD	S1,.IQLNK(P2),IQ.NXT	;GET THE NEXT ENTRY'S ADDRESS
	STORE	S1,.IQLNK(P1),IQ.NXT	;REMEMBER IT IN THE PREVIOUS ENTRY
	MOVEI	S1,.IQSIZ		;PICK UP THE ENTRY'S SIZE
	MOVE	S2,P2			;PICK UP THE ENTRY'S ADDRESS
	$CALL	M%RMEM			;RETURN ENTRY TO THE MEMORY MANAGER
	SKIPA				;GO CHECK IF LAST ENTRY

;PICK UP THE NEXT ENTRY. IF THERE ARE NO MORE, THEN UPDATE THE LINK LIST
;TRAILER WORD.

UPDIB3:	MOVE	P1,P2			;CURRENT ENTRY BECOMES PREVIOUS ENTRY
	LOAD	P2,.IQLNK(P1),IQ.NXT	;PICK UP THE NEXT ENTRY
	SKIPE	P2			;IS THIS THE LAST ENTRY?
	JRST	UPDIB2			;NO, CHECK THE NEXT ENTRY
	MOVEM	P1,IBQTRL		;YES, PLACE ADR IN L.L. TRAILER WORD
	SKIPN	IBQHDR			;UNLESS THE MESSAGE QUEUE IS EMPTY
	SETZM	IBQTRL			;THEN ZERO OUT THE TRAILER WORD
UPDIB4:	$RET				;AND RETURN TO THE CALLER
	SUBTTL	UPDMSG - CLEAN UP MESSAGE QUEUE FOR SENDER/LISTENER 

;UPDMSG is called when communication to a remote node has been lost (a sender
;or listener to that node has crashed, the node has left the cluster, or a
;timeout to that node has occurred). Part of the cleanup is to delete all
;message queue entries for that node. If a message is not in response to an
;in behalf of request or if the message does not have a corresponding
;remote queue entry, then an ACK message is sent to the OPR of the message
;indicating the message could not be delivered to the remote node.
;If the message is not to be sent to any other nodes, then the message entry
;is returned to the memory manager.
;
;Call is: S1/Message queue header word
;         S2/Sender block address
;Returns: Message queue for the node has been deleted

UPDMSG:	$SAVE	<P1,P2,P3,P4>		;SAVE THESE AC

;SET UP THE POINTERS INTO THE MESSAGE QUEUE:
;(P1) PREVIOUS MESSAGE QUEUE ENTRY ADDRESS
;(P2) CURRENT  MESSAGE QUEUE ENTRY ADDRESS
;(P3) POINTER TO INVARIANT BLOCK

	SKIPN	P2,S1			;IS THE MESSAGE QUEUE EMPTY?
	JRST	UPDMS6			;YES, SO FINISHED
	MOVEI	P1,.SNHWD(S2)		;PICK UP PREVIOUS MSG ENTRY ADDRESS
	MOVE	P4,S2			;REMEMBER SENDER BLOCK ADDRESS
UPDMS1:	LOAD	P3,.MQBLK(P2),MQ.OFF	;[6012]PICK UP THE OFFSET
	ADD	P3,P2			;POINT TO THE INVARIANT BLOCK

;DETERMINE IF THIS MESSAGE HAS A CORRESPONDING REMOTE QUEUE ENTRY OR IS
;A RESPONSE TO AN IN BEHALF OF QUEUE MESSAGE. IF SO, DON'T NOTIFY THE SENDER
;OF THE MESSAGE. (THIS IS BECAUSE THE SENDER OF THE MESSAGE IS NOTIFIED
;WHEN THE REMOTE QUEUE ENTRY IS PROCESSED AND THE SENDER OF THE IN BEHALF
;OF MESSAGE IS ON THE REMOTE NODE WHICH IS INACCESSABLE.)

	MOVE	S2,.MQNOM(P3)		;[6012]PICK UP THE FLAG BITS
	TXNE	S2,MQ%REM!MQ%IBH	;[6012]RQE OR IBH MESSAGE RESPONSE?
	JRST	UPDMS3			;YES, SO DON'T SEND AN ACK TO SENDER

;BUILD AN ACK MESSAGE TO BE SENT TO THE OPR THAT MADE THE REQUEST OR BUILD
;A TEXT MESSAGE TO BE SENT TO THE EXEC THAT MADE THE REQUEST

	$CALL	E$NSM			;PLACE ERROR CODE IN G$ERR
	LOAD	M,.MQNOM(P3),MQ.ADR	;PICK UP ADDRESS OF THE MESSAGE
	MOVE	S1,.SNNTA(P4)		;PICK UP NODE TABLE ENTRY ADDRESS
	MOVE	S1,.NNNAM(S1)		;PICK UP THE SIXBIT REMOTE NODE NAME
	MOVEM	S1,G$ARG1		;PLACE IN E$ ERROR BLOCK
	TXNN	S2,MQ%EXE		;[6012]WAS THE MSG FROM AN EXEC?
	JRST	UPDMS2			;[6012]NO, SEND ACK TO THE OPERATOR
	MOVE	S1,.MQPID(P3)		;[6012]PICK UP THE EXEC'S PID
	MOVEM	S1,G$SND		;[6012]SAVE IT FOR EREXEC
	$CALL	EREXEC			;[6012]SEND THE ERROR MSG TO THE EXEC
	JRST	UPDMS3			;[6013]CHECK FOR ANY MORE NODES
UPDMS2:	MOVE	S1,G$ERR		;[6013]PICK UP THE ERROR DISPLACEMENT
	$ACK	(<^I/@TXTTBL(S1)/>,,,.MSCOD(M)) ;[6013]SEND THE ACK TO ORION

;IF THE MESSAGE IS NOT TO BE SENT TO ANY MORE NODES, THEN RETURN ITS
;MEMORY TO THE MEMORY MANAGER.

UPDMS3:	LOAD	S1,.MQNOM(P3),MQ.NUM	;# OF REMAINING NODES TO SEND MSG TO
	SOJE	S1,UPDMS4		;RETURN ENTRY IF NO NODES LEFT TO SEND 
	STORE	S1,.MQNOM(P3),MQ.NUM	;STORE THE NUMBER OF NODES LEFT TO SEND
	JRST	UPDMS5			;CHECK THE NEXT MESSAGE QUEUE ENTRY

;PLACE THE LINK LIST WORD OF THE ENTRY TO BE DELETED IN THE PRECEDING ENTRY'S
;LINK LIST WORD AND RETURN THE ENTRY TO THE MEMORY MANAGER

UPDMS4:	LOAD	S1,.MQBLK(P2),MQ.NEA	;PICK UP THE LINK LIST WORD
	STORE	S1,.MQBLK(P1),MQ.NEA	;PLACE IN THE PRECEDING ENTRY
	MOVE	S1,P3			;PICK UP THE INVARIANT BLOCK ADDRESS
	$CALL	RELMQE			;RETURN THE ENTRY TO THE MEMORY MANAGER
	SKIPA				;GO CHECK THE NEXT ENTRY

;DETERMINE IF ANOTHER ENTRY EXISTS. IF SO, THEN CHECK IT

UPDMS5:	MOVE	P1,P2			;CURRENT ENTRY BECOMES PRECEDING ENTRY
	LOAD	P2,.MQBLK(P1),MQ.NEA	;PICK UP ADDRESS OF THE NEXT ENTRY
	SKIPE	P2			;IS THERE ANOTHER ENTRY?
	JRST	UPDMS1			;[6012]YES, CHECK IT OUT
UPDMS6:	$RET				;FINISHED WITH MESSAGE QUEUE CLEAN UP
	SUBTTL	BLDNEN - BUILD AN ENTRY IN THE NODE TABLE

;BLDNEN is called when a cluster topology change has occurred which
;results in either a node joining the cluster or a node which has left the
;cluster and rejoined it since the last scheduler pass.
;
;Call is:       S1/Node Table entry address for the node joining the cluster
;               S2/Offset into SIXBIT Node Name Table pointing to the node name
;Returns  true: Node has DECnet enabled and a monitor of release 7 or later
;Returns false: Node does not have DECnet and/or a monitor of release 7
;               or later

BLDNEN:	$SAVE	<P1>			;SAVE THIS AC
	MOVE	P1,NDNTBL(S2)		;PICK UP THE NODE NAME
	MOVEM	P1,.NNNAM(S1)		;PLACE IN THE NODE TABLE ENTRY
	HLRZ	P1,<NNMTBL+.CFCS1+1>(S2) ;PICK UP THE CURRENT NODE NUMBER
	ANDI	P1,NNMFLD		;JUST WANT THE NODE NUMBER
	HRRZM	P1,.NNSTA(S1)		;PLACE IN NODE TABLE ENTRY
	MOVE	P1,S1			;[6016]SAVE THE NODE TABLE ENTRY ADR
	$CALL	NODPAR			;[6016]PICK UP THE NODE CHARACTERISTICS
	$RETIT				;[6016]RETURN IF CAN SEND TO THE NODE
	MOVE	S1,P1			;[6016]PICK UP THE NODE ENTRY ADDRESS
	$CALL	STINFO			;[6016]SET UP THE INFO% RETRY TIMER
	$RETF				;[6016]INDICATE FAILURE
	SUBTTL	CHKDEC - INFERIOR FORK HAS A DECNET CONNECTION

;CHKDEC is called as a result of a sender's DECnet connection request to a
;listener being accepted by the listener or as a result of a listener 
;accepting a sender's DECnet connection request.
;For a sender whose connection has been accepted, its state is changed from
;"waiting for a HELLO response" to "HELLO message has been successfully
;sent".
;For a listener whose connection has been accepted, its state is changed from
;"waiting for a HELLO message" to "received a HELLO message."
;
;Messages are never sent to a remote node until the sender's DECnet connection
;has been accepted and the listener has accepted a DECnet connection from
;the remote node's sender. If both of these events have occurred, then
;CHKDEC turns on bit NN%OKS in the node's node table entry status word. This
;indicates that messages can be sent to that node. The word .SNFRE is 
;set to -1 in the sender block of the sender indicating that the sender
;is available to send a message. The word .LSAVA is also set to indicate that
;the top fork is available to pick up a message.
;
;Call is: No arguments
;Returns: Node's sender and listener status is updated.

CHKDEC:	SETZM	IREADY			;RESET FOR THE INTERRUPT HANDLER
	SETOM	NBSCHD			;FORCE A SCHEDULING PASS
	$SAVE	<P1,P2,P3>		;SAVE THESE AC

;CHECK IF THE SENDER OF THE CURRENT NODE BEING LOOKED AT HAS JUST MADE A
;SUCCESSFUL DECNET CONNECTION.

	MOVEI	P1,NODTBL		;PICK UP THE NODE TABLE ADDRESS
	MOVEI	P2,MAXNOD		;PICK UP MAX # OF ENTRIES IN NODE TABLE
	SKIPG	P3,RENNUM		;[6035]ANY NODES LEFT IN THE CLUSTER?
	$RET				;[6035]NO, RETURN NOW
CHKDE2:	SKIPN	.NNNAM(P1)		;IS THIS ENTRY IN USE?
	JRST	CHKDE8			;NO, CHECK THE NEXT ENTRY
	MOVE	S1,.NSSTA(P1)		;PICK UP THE SENDER STATUS WORD
	TXNN	S1,NS%NRH		;JUST COMPLETED A DECNET CONNECTION?
	JRST	CHKDE4			;NO, CHECK IF THE LISTENER HAS

;THE REMOTE LISTENER HAS ACCEPTED THIS SENDER'S DECNET CONNECTION REQUEST. 
;UPDATE THIS SENDER'S STATUS. UPDATE, IF NECESSARY, THE PACKN SENDER STATUS FOR
;THIS NODE. CHECK IF LISTENER HAS ALREADY ACCEPTED A CONNECTION.

	MOVX	S1,NS%NRH!NS%WHR!NS%HEL	;NO LONGER WAITING FOR HELLO RESPONSE
	XORM	S1,.NSSTA(P1)		;UPDATE STATUS IN NODE TABLE
	MOVX	S1,NN%SHO		;SENDER HAS A CONNECTION
	ANDCAM	S1,.NNSTA(P1)		;INDICATE IN THE NODE STATUS WORD
	MOVE	S1,.NNNAM(P1)		;PICK UP THE NODE NAME
	$CALL	SRHPNE			;FIND THE PACKN TBL ENTRY FOR THIS NODE
	JUMPF	CHKDE3			;CAN'T FIND ENTRY (SHOULD NEVER HAPPEN)
	MOVX	S1,LK%SEN		;INDICATE SENDER HAS A CONNECTION
	IORM	S1,.LKSTS(S2)		;UPDATE PACKN SENDER STATUS
CHKDE3:	SKIPGE	.NLSTA(P1)		;HAS LISTENER RECEIVED A HELLO MESSAGE?
	JRST	CHKDE6			;YES, INDICATE NODE READY FOR MESSAGES

;CHECK IF THIS NODE'S LISTENER JUST ACCEPTED A DECNET CONNECTION.

CHKDE4:	MOVE	S1,.NLSTA(P1)		;PICK UP LISTENER'S STATUS WORD
	TXNN	S1,NL%NRH		;JUST ACCEPTED A DECNET CONNECTION?
	JRST	CHKDE7			;NO, CHECK THE NEXT NODE

;THE LISTENER HAS JUST ACCEPTED THE REMOTE NODE'S SENDER'S DECNET CONNECTION
;REQUEST. UPDATE THE LISTENER'S STATUS IN THE NODE DATA BASE AND THE PACKN
;TABLE

	MOVX	S1,NL%NRH!NL%WHL!NL%HEL	;NO LONGER WAITING FOR  A HELLO 
	XORM	S1,.NLSTA(P1)		;SAVE THE UPDATED STATUS IN NODE TABLE
	MOVX	S1,NN%LHO		;LISTENER HAS A CONNECTION
	ANDCAM	S1,.NNSTA(P1)		;INDICATE IN THE NODE STATUS WORD
	MOVE	S1,.NNNAM(P1)		;PICK UP THE NODE NAME
	$CALL	SRHPNE			;FIND THE PACKN ENTRY FOR THIS NODE
	JUMPF	CHKDE5			;ENTRY NOT FOUND (SHOULD NOT HAPPEN)
	MOVX	S1,LK%LIS		;INDICATE LISTENER HAS A CONNECTION
	IORM	S1,.LKSTS(S2)		;UPDATE THE LISTENER'S PACKN FIELD
CHKDE5:	SKIPL	.NSSTA(P1)		;DOES SENDER HAVE A DECNET CONNECTION
	JRST	CHKDE7			;NO, CHECK THE NEXT NODE

;BOTH THE SENDER AND LISTENER HAVE DECNET CONNECTIONS TO THE REMOTE NODE.
;INDICATE THAT THE REMOTE NODE IS NOW AVAILABLE TO SEND MESSAGES TO AND
;TO PICK UP MESSAGES FROM.

CHKDE6:	MOVX	S1,NN%OKS		;O.K. TO SEND MESSAGES TO THIS NODE
	IORM	S1,.NNSTA(P1)		;PLACE IN NODE'S STATUS WORD
	MOVE	SEN,.NNSBA(P1)		;PICK UP SENDER BLOCK ADDRESS
	SETOM	.SNFRE(SEN)		;INDICATE SENDER IS AVAILABLE TO SEND
	SETOM	SREADY			;INDICATE THIS FACT TO SENDER PROCESSOR

	MOVE	LIS,.NNLBA(P1)		;PICK UP LISTENER BLOCK ADDRESS
	MOVE	S1,.LSHND(LIS)		;PICK UP LISTENER'S HANDLE
	MOVX	S2,<1B2>		;CHANNEL TO INTERRUPT ON
	SETOM	.LSAVA(LIS)		;INDICATE READY TO PROCESS A MESSAGE
	IIC%				;TELL THE LISTENER READY TO PROCESS
	 ERJMP	S..UII			;IF CAN'T INTERRUPT SENDER, THEN CRASH
	$WTOJ	(<NEBULA has communication>,<NEBULA has established communication with node ^N/.NNNAM(P1)/>,,<$WTFLG(WT.SJI)>)

;CHECK CHECK THE NEXT NODE

CHKDE7:	SOJE	P3,.POPJ		;GO CHECK THE NEXT NODE
CHKDE8:	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	SOJE	P2,S..NTI		;NODE TABLE INCONSISTENCY
	JRST	CHKDE2			;CHECK THE NEXT NODE TABLE ENTRY
	SUBTTL	CHKQUE - IPCF MESSAGE PROCESSING

;CHKQUE is called by the scheduler to check if any IPCF messages are
;available. If there are any IPCF messages available, then each message is
;processed. Only messages sent directly from ORION or QUASAR are accepted.
;Any messages with an unknown message code are ignored.
;
;IPCF message syntax error handling is performed differently depending
;on the message type and who the sender is. The message types and 
;senders can be broken down as follows:
;
;Class 1. Remote messages. (Original) senders are MOUNTR, OPR and the EXEC.
; Remote messages are messages that request a service or status from a
; remote node in the cluster. E.g.,
;  TO NEBULA DISMOUNT message
;  SHOW QUEUES PRINTER message
; These messages are forwarded to the remote system to be acted upon by
; the remote system.
; 
;Class 2. Action messages. (Original) sender is OPR.
; Action messages are messages that request NEBULA to perform an action
; for the sender of the message. E.g.,
;  SHOW CLUSTER-GALAXY-LINK-STATUS
;  ENABLE LINK-STATUS
; These messages are acted upon by NEBULA itself.
;
;Class 3. Response to in behalf of messages. These messages are the
; responses to remote messages (A remote message, once it is at the
; remote node, becomes an in behalf of message since the remote node is
; acting in behalf of a request that originated from a node other than
; itself.) and are forwarded to the original sender - MOUNTR, OPR or the EXEC.
;
;Class 1 error handling.
; If the sender of the message is MOUNTR, then NEBULA crashes when it detects
; an illegally formatted IPCF message.
; If the sender of the message is OPR or the EXEC, then NEBULA sends an
; ACK (.OMACK) message back to the OPR or the EXEC if it detects an illegally
; formatted IPCF message.
;
;Class 2 error handling.
; If NEBULA detects an illegally formatted message, then it sends an ACK
; message back to the OPR that sent the message.
;
;Class 3 error handling.
; If the sender of the message is MOUNTR, then NEBULA crashes when it detects
; an illegally formatted IPCF message.
; If the sender of the message is OPR or the EXEC, then NEBULA simplys forwards
; the response back to the sender of the original request. The only syntax
; checking it does is to ensure that the message length is valid. If it is not,
; then NEBULA sends an ACK message back to the sender indicating that the
; response message specified an illegal message length.
;
;Call is:      No arguments
;Returns: All the messages are processed from the IPCF message queue

CHKQUE:	$CALL	C%RECV			;CHECK IF THERE IS A MESSAGE
	JUMPF	.POPJ			;RETURN,,NOTHING THERE.
	$SAVE	<P1,P2>			;SAVE THESE AC
;**;[6037]At CHKQUE:+3L add 1 line  JYCW  Oct-18-88
	SETZM	NMFFLG			;[6037]Assume old format
	MOVE	P1,S1			;SAVE ADDRESS OF THE MDB
	MOVEI	S1,TIMOUT		;PICK UP THE TIMEOUT VALUE
	SKIPE	DEBUGW			;[6031]DEBUGGING?
	MOVEI	S1,777700		;[6031]YES, SET FOR A LONG TIME
	MOVEM	S1,TIMCTR		;SAVE FOR SETTING UP ANY TIMERS
	JRST	CHKQU3			;JOIN COMMON CODE
CHKQU2:	$CALL	C%RECV			;CHECK IF THERE IS A MESSAGE
	JUMPF	.POPJ			;RETURN,,NOTHING THERE.
	MOVE	P1,S1			;SAVE THE ADDRESS OF THE MDB
CHKQU3:	SKIPE	SCSFLG			;A CLUSTER TOPOLOGY CHANGE OCCURRED?
	$CALL	TOPCHN			;YES, UPDATE THE NODE DATA BASE

;MAKE SURE THE MESSAGE IS FROM ORION OR QUASAR

	LOAD	S2,MDB.SI(P1)		;GET SPECIAL INDEX WORD
	TXNN	S2,SI.FLG		;IS THERE AN INDEX THERE?
	JRST	CHKQU6			;NO, IGNORE THIS MESSAGE
	ANDX	S2,SI.IDX		;AND OUT THE INDEX BIT
	CAIE	S2,SP.OPR		;IS THE MESSAGE FROM ORION?
	CAIN	S2,SP.QSR		;IS THE MESSAGE FROM QUASAR?
	SKIPA				;YES, CONTINUE ON
	JRST	CHKQU6			;NO, DISCARD IT
	LOAD	M,MDB.MS(P1),MD.ADR	;GET THE MESSAGE ADDRESS
	LOAD	S2,.MSTYP(M),MS.TYP	;GET THE MESSAGE TYPE
	MOVSI	S1,-NMSGT		;MAKE AOBJN POINTER FOR MSG TYPES

;CHECK IF THE MESSAGE TYPE IS KNOWN TO NEBULA

CHKQU4:	HRRZ	P2,MSGTAB(S1)		;GET A MESSAGE TYPE
	CAMN	S2,P2			;MATCH?
	JRST	CHKQU5			;YES, WIN
	AOBJN	S1,CHKQU4		;NO, LOOP
;**;[6037]At CHKQU4:+4L change 1 line  JYCW  Oct-18-88
	JRST	CHKQ10			;[6037]Unknown, might be new format

;A KNOWN MESSAGE HAS BEEN RECEIVED

CHKQU5:	MOVE	S2,MDB.SP(P1)		;PICK UP THE SENDER'S PID
	MOVEM	S2,G$SND		;SAVE IT IN CASE OF AN ERROR
	SETZM	G$ERR			;CLEAR THE ERROR FLAG
	SETZM	REMORG			;ASSUME MESSAGE ORIGINATED LOCALLY
	HLRZ	P2,MSGTAB(S1)		;PICK UP THE PROCESSING ROUTINE ADR
	SETZM	EXESND			;[6012]ASSUME MESSAGE NOT FROM THE EXEC
	$CALL	@P2	 		;DISPATCH THE MESSAGE PROCESSOR.
	SKIPN	S1,G$ERR		;[6012]DID AN ERROR OCCUR?
	JRST	CHKQU7			;[6027]NO, GO RELEASE THE MESSAGE
	SKIPN	EXESND			;[6012]MESSAGE FROM THE EXEC?
	$ACK	(<^I/@TXTTBL(S1)/>,,,.MSCOD(M)) ;[6013]NO, INFORM ORION
	SKIPE	EXESND			;[6012]MESSAGE FROM THE EXEC?
	$CALL	EREXEC			;[6012]YES, SEND ERROR MSG TO THE EXEC
	SKIPA				;[6027]RELEASE THE MESSAGE

;A MESSAGE NOT FROM ORION OR QUASAR HAS BEEN RECEIVED; OR ELSE AN UNKNOWN
;MESSAGE HAS BEEN RECEIVED; OR ELSE A KNOWN MESSAGE HAS JUST BEEN PROCESSED.

CHKQU6:	$WTOJ(<NEBULA received an unknown IPCF message>,,,<$WTFLG(WT.SJI)>) ;[6027]
CHKQU7:	$CALL	C%REL			;[6027]RELEASE THE MESSAGE
	JRST	CHKQU2			;CHECK FOR ANY MORE MESSAGES
	SUBTTL	CHKQ10   -  Check format 
;ACCEPT	S2/MESSAGE CODE
;**;[6037]At CHKQU7:+2L add routine (33 Lines) CHKQ10:  JYCW  Oct-18-88
CHKQ10:	TXNN	S2,NEB%MS		;[6037]NEW MSG FORMAT
	JRST	CHKQU6			;[6037]NO, invalid msg
	SETOM	NMFFLG			;[6037]NEW MSG FORMAT
	MOVE	S2,MDB.SP(P1)		;[6037]PICK UP THE SENDER'S PID
	MOVEM	S2,G$SND		;[6037]SAVE IT IN CASE OF AN ERROR
	SETZM	G$ERR			;[6037]CLEAR THE ERROR FLAG
	SETZM	REMORG			;[6037]ASSUME MSG ORIGINATED LOCALLY
	SETZM	EXESND			;[6037]ASSUME MESSAGE NOT FROM THE EXEC

	$CALL	CHKLEN			;[6037]CHECK FOR VALID IPCF MSG LENGTH
	JUMPF	E$IML			;[6037]INVALID LENGTH, TELL OPERATOR
	$CALL	FNDNBK			;[6037]PICK UP THE NODE TO SEND MSG TO
	JUMPF	E$NNF			;[6037]FAIL IF NO NODE BLOCK IN THE MSG

;PLACE LOCAL NODE NAME IN THE MESSAGE

	PUSH	P,S1			;[6037]SAVE THE ORIGINAL NODE NAME
	MOVE	S1,NODNAM		;[6037]PICK UP THE LOCAL NODE NAME
	MOVEM	S1,ARG.DA(S2)		;[6037]PLACE IN THE MESSAGE
	POP	P,S1			;[6037]RESTORE THE ORIGINAL NODE NAME

;DETERMINE WHICH NODE OR NODES TO SEND THE MESSAGE TO. SEND OR QUEUE THE
;MESSAGE TO THE INDICATED NODE OR NODES

	CAMN	S1,[-1]			;[6037]SEND MSG TO ALL REMOTE NODES?
	JRST	CHKQ11			;[6037]YES,SEND MSG TO ALL REMOTE NODES
	$CALL	QSREMM			;[6037]NO, SEND MSG TO INDICATED NODE
	JUMPF	CHKQ12			;[6037]ERROR, INDICATE TO THE SENDER
	JRST	CHKQU7			;[6037]SENT, CHECK FOR MORE

CHKQ11:	$CALL	QMREMM			;[6037]SEND THE MSG TO ALL REMOTE NODES
	SETZM	G$ERR			;[6037]INDICATE ERROR ALREADY REPORTED
	SKIPA				;[6037]
CHKQ12:	$CALL	@S1			;[6037]PROCESS THE ERROR
	SKIPN	S1,G$ERR		;[6037]DID AN ERROR OCCUR?
	JRST	CHKQU7			;[6037]NO, GO RELEASE THE MESSAGE
	SKIPN	EXESND			;[6037]MESSAGE FROM THE EXEC?
	$ACK	(<^I/@TXTTBL(S1)/>,,,.MSCOD(M)) ;[6037]NO, INFORM ORION
	SKIPE	EXESND			;[6037]MESSAGE FROM THE EXEC?
	$CALL	EREXEC			;[6037]YES, SEND ERROR MSG TO THE EXEC
	JRST	CHKQU7			;[6037]CHECK FOR ANY MORE MESSAGES
MSGTAB:	XWD	NTDSM,.NTDSM		;TO NEBULA DISMOUNT 
	XWD	NTMTS,.NTMTS		;TO NEBULA MOUNT
	XWD	NFDAK,.NFDAK		;FROM NEBULA DISMOUNT ACK
	XWD	NCDSM,.NCDSM		;NEBULA CANCEL DISMOUNT 
;**;[6053]At MSGTAB:+4L change 1 line  JCR  4/27/90
	XWD	NWTOX,.OMDSP		;[6053]ACK, WTOR OR WTO
	XWD	NDISPY,.OMACS		;QUASAR SHOW ACK
	XWD	NDISPY,MT.TXT		;ORION ACK 
	XWD	NSHOW,.NMSHS		;SHOW STATUS (.OMSHS)
	XWD	NSHOW,.NDSHT		;[6001]SHOW STATUS TAPE (.ODSHT)
	XWD	NSHOW,.NDSHD		;[6001]SHOW STATUS DISK (.ODSHD)
	XWD	NSHOW,.NDSTR		;[6004]SHOW STATUS STRUCTURE (.ODSTR)
	XWD	NSHOW,.NDSCD		;SHOW CONFIGURATION (.ODSCD)
	XWD	NSHOW,.NMSHQ		;SHOW QUEUES (.OMSHQ)
	XWD	NSHOW,.NMSHP		;SHOW PARAMETERS (.OMSHP)
	XWD	NSHOW,.NMSHR		;SHOW ROUTE (.OMSHR)
	XWD	NSHOW,.NMESS		;SHOW MESSAGES
	XWD	NSHOW,.NSHOP		;SHOW OPERATORS
	XWD	NLIST,.QOLIS		;[6012]INFORMATION OUTPUT FROM THE EXEC
	XWD	QREXMG,.NMACS		;[6012]RESPONSE TO AN EXEC INFO REQUEST
	XWD	NKILL,.QOKIL		;[6012]CANCEL PRINT FROM THE EXEC
	XWD	QREXMG,.NMTXT		;[6012]RESPONSE TO AN EXEC CANCEL REQUEST
	XWD	NSCLU,.NSCLU		;SHOW CLUSTER-GALAXY-LINK-STATUS
	XWD	NDRCF,.NDRCF		;ENA/DIS REPORT-CONNECTION-FAILURES
	XWD	NDDCA,.NDDCA		;ENA/DIS DECNET-CONNECTION-ATTEMPTS
	XWD	NSHOW,.NMSSN		;[6033]SHOW STATUS NETWORK (.OMSSN)
	XWD	NSHOW,.NMSPN		;[6033]SHOW PARAMETERS NETWORK (.OMSPN)
;**;[6043]AT MSGTAB:+26L add 1 line  JCR  11/29/89
	XWD	NDISPY,.OMNAK		;[6043]Null ACK
	NMSGT==.-MSGTAB
	SUBTTL	NTDSM - PROCESS A "TO NEBULA DISMOUNT" MESSAGE

;NTDSM processes a "TO NEBULA DISMOUNT" message that is sent by MOUNTR
;by way of ORION. MOUNTR sends a TO NEBULA DISMOUNT message during its
;processing of a DISMOUNT with REMOVAL of a structure or setting a structure
;EXCLUSIVE. NTDSM builds a remote queue entry for the dismount request
;which contains the status of the structure for each node the structure is
;to be dismounted from. For each remote node that is in communication with
;this node, a "FROM NEBULA DISMOUNT" message is placed in that node's
;message queue. If the sender communicating with that node is free, then
;it is interrupted to inform it that a message is available for it to send.
;
;Call is: M/Address of the TO NEBULA DISMOUNT Message
;Returns: The TO NEBULA DISMOUNT Message was processed
;Crashes: TO NEBULA DISMOUNT Message was illegally formatted

NTDSM:	$SAVE <T1,T2,T3,T4,P1,P2>	;SAVE THESE REGISTERS

;CHECK THAT THE MESSAGE IS CORRECTLY FORMATTED

	$CALL	CHKLEN			;CHECK FOR VALID IPCF MESSAGE LENGTH
	JUMPF	S..IMM			;CRASH ON ILLEGALLY FORMATTED MESSAGE
	MOVE	T1,.OARGC(M)		;PICK UP THE ARGUMENT COUNT
	SOJE	T1,S..IMM		;NO NODE BLOCKS, ILLEGAL MOUNTR MSG
	CAILE	T1,MAXNOD		;MORE NODES IN MSG THAN SUPPORTED?
	PJRST	S..IMM			;YES, ILLEGALLY FORMATTED MOUNTR MSG
	MOVEI	T3,.OHDRS(M)		;POINT TO THE STRUCTURE NAME BLOCK
	LOAD	S1,ARG.HD(T3),AR.TYP	;PICK UP THE TYPE OF BLOCK
	CAIE	S1,.STRDV		;IS THIS THE STRUCTURE NAME BLOCK?
	PJRST	S..IMM			;NO, ILLEGALLY FORMATTED MOUNTR MSG
	MOVEM	T1,NNIMSG		;SAVE THE NUMBER OF NODES IN MSG

;OBTAIN A REMOTE QUEUE ENTRY (RQE) FROM THE MEMORY MANAGER

	MOVE	S1,T1			;NUMBER OF NODES IN REMOTE QUEUE ENTRY
	IMULI	S1,.RQBSZ+.RQNSZ	;AMOUNT OF MEMORY NEEDED FOR THE NODES
	ADDI	S1,.RQNIZ		;INVARIANT BLOCK SIZE FOR RQE
	$CALL	M%GMEM			;OBTAIN THE NECESSARY MEMORY
	MOVE	P1,S2			;SAVE THE ADDRESS OF THE RQE

;CALCULATE THE ADDRESS OF THE INVARIANT BLOCK OF THE REMOTE QUEUE ENTRY.
;SAVE THE ADDRESS AND SIZE OF THE RQE FOR THE MEMORY MANAGER.

	MOVE	T2,T1			;PICK UP THE NUMBER OF NODES IN MSG
	IMULI	T2,.RQBSZ		;MEMORY THE LINK LIST WORDS TAKE UP
	ADD	T2,P1			;ADDRESS OF THE INVARIANT BLOCK IN RQE
	STORE	S1,.RQMEM(T2),RQ.LEN	;SAVE THE LENGTH OF THE RQE IN THE RQE
	STORE	S2,.RQMEM(T2),RQ.ADR	;SAVE THE ADDRESS OF THE RQE IN THE RQE
	STORE	T1,.RQNUM(T2),RQ.NTS	;SAVE NUMBER OF NODES IN RQE
	MOVE	P2,T2			;SAVE THE INVARIANT BLOCK ADDRESS
	MOVEM	T2,IBLADR		;AND HERE TOO

;POINT TO THE CURRENT NODE IN THE REMOTE QUEUE ENTRY

	ADDI	T2,.RQNIZ		;POINT TO THE FIRST NODE BLOCK
	MOVEM	T2,FIRNOD		;REMEMBER THE ADR OF THE 1ST NODE BLOCK

;CHECK THE STATE OF EACH NODE SPECIFIED IN THE MESSAGE AND ADD THAT STATE
;TO THE RQE FOR THAT NODE

	LOAD	S1,ARG.HD(T3),AR.LEN	;PICK UP LENGTH OF STRUCTURE NAME BLOCK
	ADD	T3,S1			;POINT TO THE FIRST IPCF MSG NODE BLOCK
NTDS1:	MOVE	S1,ARG.DA(T3)		;PICK UP THE SIXBIT NODE NAME
	MOVEM	S1,.RQNDN(T2)		;PLACE NODE NAME IN THE REMOTE QE
	$CALL	SNAMNT 			;FIND THE NODE NAME IN THE NODE TABLE

;CHECK THE STATE OF THE NODE. IS IT O.K. TO SEND A MESSAGE TO THIS NODE?

	JUMPF	NTDS4			;DOES THE NODE EXIST IN OUR DATA BASE?
	SKIPL	S2,.NNSTA(S2)		;YES, THIS NODE O.K. TO SEND MSG TO?
	JRST	NTDS2			;NO, FIND OUT WHY
	MOVX	S1,RQ%SNR		;[6024]NO RESPONSE REC'D FROM THIS NODE
	AOS	.RQNUM(P2)		;INCREMENT THE # OF OUTSTANDING RSP
	JRST	NTDS5			;STORE THE STATUS OF THIS NODE

;IT IS NOT O.K. TO SEND A MESSAGE TO THIS NODE. FIND OUT THE REASON WHY
;AND STORE THE REASON IN THE STATUS WORD FOR THIS NODE.

NTDS2:	MOVEI	S1,NSPX18		;[6024]INDICATE NODE IS UNREACHABLE
	TXO	S1,RQ%TER		;[6025]MOUNTR NEEDS THIS BIT SET
	TXNE	S2,NN%COI		;INFO% UNABLE TO GET S.W. ENVIRONMENT?
	TXO	S1,RQ%COI		;YES, INDICATE SO
	TXNE	S2,NN%DCN		;REMOTE MONITOR HAS DECNET INSTALLED?
	TXO	S1,RQ%NDC		;NO, INDICATE SO
	TXNE	S2,NN%REL		;REMOTE MONITOR VERSION 7 OR LATER?
	TXO	S1,RQ%PSM		;NO, INDICATE SO
	TXNE	S2,NN%SCS		;SCS% DETECTED NODE LEFT THE CLUSTER?
	TXO	S1,RQ%SCS		;YES, INDICATE SO
	TXNE	S2,NN%SHO		;SENDER WAITING FOR HELLO RESPONSE?
	TXO	S1,RQ%SNH		;YES, INDICATE SO
	TXNE	S2,NN%LHO		;LISTENER WAITING FOR HELLO MESSAGE?
	TXO	S1,RQ%LNH		;YES, INDICATE SO
	TXNE	S2,NN%IFC		;SENDER/LISTENER NOT IN FATAL ERROR?
	TXO	S1,RQ%SLD		;NO, INDICATE SO
	SKIPA				;GO STORE THE NODE STATUS IN THE RQE

NTDS4:	TXO	S1,RQ%NSN		;INDICATE NO SUCH NODE IN OUR DATA BASE
NTDS5:	MOVEM	S1,.RQNST(T2)	 	;STORE NODE STATUS IN RQE

;POINT TO THE NEXT NODE NAME IN THE IPCF MESSAGE AND THE NEXT NODE NAME IN
;THE REMOTE QUEUE ENTRY. CHECK THE STATUS OF THE NEXT NODE.

	ADDI	T3,.NDESZ		;POINT TO THE NEXT NODE IN THE IPCF MSG
	ADDI	T2,.RQNSZ		;POINT TO THE NEXT NODE IN THE RQE
	SOJG	T1,NTDS1		;CHECK THE STATUS OF THE NEXT NODE

;ALL THE NODES IN THE "TO NEBULA DISMOUNT" MESSAGE HAVE BEEN CHECKED. 
;FINISH BUILDING THE REMOTE QUEUE ENTRY EXCEPT FOR THE LINK LIST WORDS

	MOVE	S1,MSGTIM		;PICK UP TIME THIS MESSAGE 
	MOVEM	S1,.RQTSP(P2)		;PLACE IN THE RQE
	MOVEI	S1,.NRDSM		;PICK UP THE MESSAGE TYPE
	MOVEM	S1,.RQMST(P2)		;PLACE IN THE RQE

	MOVEI	S1,<.OHDRS+ARG.DA>(M)	;PICK UP ADDRESS OF THE STRUCTURE NAME
	HRLI	S1,(POINT 7)		;MAKE IT INTO A POINTER
	$CALL	S%SIXB			;CONVERT THE STRUCTURE NAME TO SIXBIT
	MOVEM	S2,.RQSTN(P2)		;PLACE THE STRUCTURE NAME IN THE RQE

	MOVE	S1,.MSCOD(M)		;PICK UP MOUNTR'S ACK CODE
	MOVEM	S1,.RQACK(P2)		;PLACE IN THE RQE

	MOVE	S1,.OFLAG(M)		;PICK UP WHAT CAUSED MESSAGE TO BE SENT
	TXNE	S1,.DMSEX		;DUE TO SET STRUCTURE EXCLUSIVE?
	MOVX	S2,RQ%SEX		;YES, INDICATE SO
	TXNE	S1,.DMDIS		;DUE TO DISMOUNT/REMOVAL?
	MOVX	S2,RQ%DIS		;YES, INDICATE SO
	MOVEM	S2,.RQSTS(P2)		;PLACE IN THE STATUS WORD

;DETERMINE IF THE MESSAGE MUST BE SENT TO ANY OF THE NODES

	LOAD	S1,.RQNUM(P2),RQ.NOR	;NUMBER OF NODES TO SEND THE MESSAGE TO
	JUMPE	S1,NTD10		;NO NODES TO SEND TO, TELL MOUNTR
	MOVEM	S1,NTSMSG		;REMEMBER THE NUMBER OF NODES

;CHANGE THE "TO NEBULA DISMOUNT" MESSAGE INTO A "FROM NEBULA DISMOUNT"
;MESSAGE. THIS WILL ENTAIL CHANGING THE MESSAGE TYPE AND REPLACING THE
;DESTINATION NODE BLOCKS WITH A NODE BLOCK CONTAINING THE LOCAL NODE NAME.

	MOVEI	S1,.OHDRS(M)		;PICK UP ADR OF THE STRUCTURE NAME BLK
	LOAD	S2,ARG.HD(S1),AR.LEN	;PICK UP LENGTH OF STRUCTURE NAME BLOCK
	MOVEI	T1,.OHDRS+.NDESZ(S2)	;PICK UP THE SIZE OF THE MESSAGE
	STORE	T1,.MSTYP(M),MS.CNT	;SAVE THE SIZE OF THE MSG IN THE MSG
	ADD	S1,S2			;OBTAIN ADDRESS OF NODE NAME BLOCK	
	MOVEI	S2,.NFDSM		;PICK UP FROM NEBULA DISMOUNT MSG CODE
	STORE	S2,.MSTYP(M),MS.TYP	;SAVE CODE IN THE MESSAGE
	MOVEI	S2,2			;PICK UP THE ARGUMENT COUNT
	MOVEM	S2,.OARGC(M)		;PLACE IN THE MESSAGE

	MOVE	S2,[.NDESZ,,.NDENM]	;PICK UP NODE NAME HEADER WORD
	MOVEM	S2,ARG.HD(S1)		;PLACE IN THE NODE NAME BLOCK
	MOVE	S2,NODNAM		;PICK UP LOCAL NODE NAME
	MOVEM	S2,ARG.DA(S1)		;PLACE IN THE .NFDSM MESSAGE

;THE MESSAGE HAS BEEN BUILT. NOW BUILD THE MESSAGE QUEUE ENTRY

	MOVE	S1,NTSMSG		;NUMBER OF NODES TO RECEIVE THIS MSG
	MOVX	S2,MQ%REM		;THIS MSG HAS A RQE ASSOCIATED WITH IT
	$CALL	BLDMQE			;BUILD THE MESSAGE QUEUE ENTRY
	MOVE	P2,S1			;SAVE THE ADDRESS OF THE MESSAGE QE

;THE MESSAGE QUEUE ENTRY HAS BEEN BUILT.
;CHECK THE STATUS OF THE NODES IN THE REMOTE QUEUE ENTRY TO DETERMINE
;WHICH NODES THE MESSAGE IS TO BE SENT TO.

	MOVE	T1,FIRNOD		;ADDRESS OF FIRST NODE BLOCK IN THE RQE
	MOVE	T3,NTSMSG		;PICK UP THE # OF NODES TO SEND MSG TO
	MOVE	T4,NNIMSG		;THE OFFSET VALUE FOR THIS NODE IN RQE
					; (EQUAL TO THE # OF NODES IN IPCF MSG)
NTDS6:	SKIPL	S1,.RQNST(T1)		;WAITING FOR A RESPONSE?
	JRST	NTDS9			;NO, IMPLIES DON'T SEND TO THIS NODE

;LINK THE REMOTE QUEUE ENTRY INTO THIS NODE'S REMOTE QUEUE

	MOVE	S1,.RQNDN(T1)		;PICK UP THE NODE NAME
	$CALL	SNAMNT			;PICK UP THE NODE TABLE ENTRY ADDRESS
	HRLZM	T4,.RQBLK(P1)		;PLACE OFFSET INTO LINK LIST WORD
	SKIPN	S1,.NRQLT(S2)		;IS THE NODE'S REMOTE QUEUE EMPTY?
	JRST	NTDS7			;YES, ADD AS THE FIRST QUEUE ENTRY
	STORE	P1,.RQBLK(S1),RQ.NEA	;PLACE ADR OF NEW LAST RQE INTO OLD 
	JRST	NTDS7A			;UPDATE THE LINK LIST
NTDS7:	MOVEM	P1,.NRQLH(S2)		;PLACE ADR OF NEW RQE INTO HEADER WORD
	MOVEI	S1,.NRQLH(S2)		;PICK UP HDR ADR WORD AS PREVIOUS ENTRY
NTDS7A:	STORE	S1,.RQNST(T1),RQ.ERR	;PLACE PREVIOUS ENTRY ADR IN THIS ENTRY
	MOVEM	P1,.NRQLT(S2)		;PLACE ADR OF NEW RQE INTO TRAILER WORD

;LINK THE MESSEAGE INTO THIS NODE'S MESSAGE QUEUE

	MOVE	S1,P2			;LINK LIST WORD ADDRESS FOR THIS NODE
	MOVE	SEN,.NNSBA(S2)		;ADDRESS OF THIS NODE'S SENDER BLOCK
	$CALL	ADDMQE			;ADD ENTRY TO THIS NODE'S MESSAGE QUEUE

;CHECK IF THE SENDER IS AVAILABLE FOR A MESSAGE

	SKIPE	.SNFRE(SEN)		;IS THIS SENDER FREE TO SEND A MESSAGE?
	$CALL	SENMSG			;YES, PASS THE SENDER THE MESSAGE

;CHECK THE NEXT NODE

	TDZA	S1,S1			;IMPLIES THIS NODE O.K. TO SEND MSG TO
NTDS9:	SETO	S1,			;CAN'T SEND A MESSAGE TO THIS NODE
	ADDI	T1,.RQNSZ		;ADR TO THE NEXT NODE IN THE RQE
	ADDI	P1,.RQBSZ		;ADR TO NEXT LINK LIST WORD IN RQE
	SOS	T4			;DECREMENT THE OFFSET BY ONE
	SKIPE	S1			;WAS THIS NODE O.K. TO SEND A MSG TO?
	JRST	NTDS6			;NO, CHECK THE NEXT NODE
	ADDI	P2,.MQBSZ		;YES, ADR OF THE NEXT L.L. WORD IN MEQ
	SOJG	T3,NTDS6		;ANY MORE NODES TO SEND THE MESSAGE TO?

;LINK THE REMOTE QUEUE ENTRY INTO THE GLOBAL REMOTE QUEUE

	MOVE	S1,IBLADR		;PICK UP THE INVARIANT BLOCK ADDRESS
	$CALL	ADDGRQ			;PLACE ENTRY ON GLOBAL REMOTE QUEUE
	$RET				;RETURN TO THE CALLER

;THE MESSAGE IS TO BE SENT TO NO NODES. SEND  FROM NEBULA DISMOUNT ACK
;MESSAGE TO MOUNTR.

NTD10:	MOVE	S1,IBLADR		;PICK UP THE INVARIANT BLOCK ADDRESS
	$CALL	ADDGRQ			;PLACE ENTRY ON GLOBAL REMOTE QUEUE
					; SINCE SNDACK EXPECTS IT THERE
	MOVE	S1,IBLADR		;PICK UP THE INVARIANT BLOCK ADDRESS
	$CALL	SNDACK			;SEND DISMOUNT ACK MESSAGE TO MOUNTR
	$RET				;FINISHED PROCESSING THE .NTDSM MESSAGE
	SUBTTL	SNDACK - SEND A TO NEBULA DISMOUNT ACK TO MOUNTR

;SNDACK builds a TO NEBULA DISMOUNT ACK message to be forwarded to
;MOUNTR by ORION. After the message is sent, the remote queue entry is 
;deleted. SNDACK assumes that the remote queue entry has been delinked
;from all the node remote queues, but not the global remote queue.
;
;Call is: S1/ Address of the invariant block of the remote queue entry
;Returns: Message is sent and RQE is deleted

SNDACK:	$SAVE	<T1,T2,T3,P1,P2,P3,P4>	;SAVE THESE REGISTERS

;OBTAIN A PAGE FROM THE MEMORY MANAGER TO BUILD THE MESSAGE IN.
;THE ADDRESS OF THE PAGE WILL BE IN AC "MO"

	MOVE	P1,S1			;SAVE THE ADR OF THE INVARIANT BLOCK
	$CALL	GETPAG			;PICK UP A PAGE FOR IPCF

;DETERMINE THE SIZE OF THE TO NEBULA DISMOUNT ACK MESSAGE TO BE SENT TO MOUNTR

	LOAD	S1,.RQNUM(P1),RQ.NTS	;PICK UP THE NUMBER OF NODES IN THE RQE
	MOVE	P3,S1			;SAVE THE NUMBER OF NODES
	IMULI	S1,.STSLN		;MULTIPY BY THE SIZE OF THE STATUS BLK
	ADDI	S1,<.OHDRS+.STRLN+1>	;ADD IN THE HEADER + STRUCTURE NAME
					; BLOCK + STATUS BLOCK HEADER SIZES
;BUILD THE MESSAGE HEADER

	HRLS	S1			;PLACE MSG LENGTH IN THE EXPECTED PLACE
	HRRI	S1,.NTDAK		;PICK UP THE MESSAGE CODE
	MOVEM	S1,.MSTYP(MO)		;PLACE IN THE MESSAGE
	SETZM	.MSFLG(MO)		;NO BITS TURNED ON IN THE FLAG WORD
	MOVE	S1,.RQACK(P1)		;PICK UP MOUNTR'S ACK CODE
	MOVEM	S1,.MSCOD(MO)		;PLACE IN THE MESSAGE
	MOVEI	S1,2			;ALWAYS TWO ARGUMENT BLOCKS
	MOVEM	S1,.OARGC(MO)		;NUMBER OF ARGUMENT BLOCKS

;BUILD THE STRUCTURE NAME BLOCK

	MOVEI	P2,.OHDRS(MO)		;POINT TO THE STRUCTURE NAME BLOCK
	MOVE	S1,[.STRLN,,.STRNM]	;PICK UP THE HEADER WORD
	MOVEM	S1,ARG.HD(P2)		;PLACE IN THE STRUCTURE NAME BLOCK
	MOVE	S1,.RQSTN(P1)		;PICK UP THE (SIXBIT) STRUCTURE NAME
	MOVEM	S1,ARG.DA(P2)		;PLACE IN THE STRUCTURE NAME BLOCK

;BUILD THE NODE STATUS BLOCK. FIRST BUILD THE HEADER WORD

	MOVE	S1,P3			;PICK UP THE NUMBER OF NODES
	IMULI	S1,.STSLN		;CALCULATE THE SIZE NEEDED
	AOS	S1			;INCLUDE THE HEADER WORD
	HRLS	S1			;PLACE STATUS BLOCK SIZE WHERE EXPECTED
	HRRI	S1,.STSBK		;PICK UP THE BLOCK TYPE
	ADDI	P2,.STRLN		;POINT TO THE STATUS BLOCK HEADER
	MOVEM	S1,ARG.HD(P2)		;PLACE HEADER WORD INTO BLOCK HEADER

;BUILD THE REST OF THE NODE BLOCK

	ADDI	P2,ARG.DA		;POINT TO THE FIRST NODE NAME FIELD
	MOVEI	P4,.RQNIZ(P1)		;POINT TO THE FIRST NODE IN THE RQE
	SETZ	T1,			;CLEAR THE .OFLAG WORD

SNDAC2:	MOVE	S1,.RQNDN(P4)		;PICK UP THE NODE NAME
	MOVEM	S1,.STNNE(P2)		;PLACE IN THE NODE STATUS BLOCK
	MOVE	S1,.RQNST(P4)		;PICK UP THE NODE'S STATUS WORD
	MOVEM	S1,.STNST(P2)		;PLACE IN THE NODE STATUS BLOCK

	TXNE	S1,RQ%SSD		;STRUCTURE DISMOUNTED SUCCESSFULLY?
	TXO	T1,DS%SUC		;YES, INDICATE SO IN THE .OFLAG WORD
	TXNN	S1,RQ%SSD		;STRUCTURE DISMOUNTED SUCCESSSFULLY?
	TXO	T1,DS%FAI		;NO, INDICATE SO IN THE .OFLAG WORD

;POINT TO THE NEXT NODE FIELD

	ADDI	P4,.RQNSZ		;ADR OF THE NEXT NODE BLOCK IN THE RQE
	ADDI	P2,.STSLN		;ADR OF THE NXT NODE STS FLD IN THE MSG
	SOJG	P3,SNDAC2		;CHECK OUT THE NEXT NODE STATUS

;ALL THE NODES' STATUS HAVE BEEN LOOKED AT. DETERMINE IF THE STRUCTURE
;WAS NOT SUCCESSFULLY DISMOUNTED FROM ONE OR MORE NODES. ALSO CHECK IF
;THE DISMOUNT REQUEST WAS CANCELLED.

	TXNE	T1,DS%FAI		;DID A DISMOUNT FAILURE OCCUR?
	TXZ	T1,DS%SUC		;YES, SO NEGATE ANY DISMOUNT SUCCESSES
	MOVE	S1,.RQSTS(P1)		;PICK UP RQE STATUS WORD
	TXNE	S1,RQ%CAN		;WAS THE DISMOUNT REQUEST CANCELLED?
	TXO	T1,DS%CAN		;YES, INDICATE SO
	TXNE	S1,RQ%SEC		;WAS SET EXCLUSIVE CANCELLED?
	TXO	T1,DS%SEC		;YES, INDICATE SO
	TXNE	S1,RQ%SEX		;DISMOUNT DUE SET STRUCTURE EXCLUSIVE?
	TXO	T1,DS%SEX		;YES, INDICATE SO
	TXNE	S1,RQ%DIS		;DISMOUNT DUE DISMOUNT/REMOVAL?
	TXO	T1,DS%DIS		;YES, INDICATE SO
	MOVEM	T1,.OFLAG(MO)		;SAVE THE STATUS IN THE MESSAGE

;BUILD THE SAB

	MOVE	S1,MO			;ADDRESS OF THE IPCF MESSAGE
	MOVEI	S2,PAGSIZ		;LENGTH OF THE IPCF MESSAGE
	$CALL	SNDORN			;SEND THE MESSAGE TO ORION
	JUMPF	S..OSF			;COULDN'T SEND THE MESSAGE TO ORION

;RELEASE THE REMOTE QUEUE ENTRY

	MOVE	S1,P1			;PICK UP THE INVARIANT BLOCK ADDRESS
	$CALL	RELGRQ			;RELEASE THE REMOTE QUEUE ENTRY
	$RET				;AND RETURN TO THE CALLER
	SUBTTL	NTMTS	PROCESS A "TO NEBULA MOUNT" MESSAGE

;NTMTS validates the TO NEBULA MOUNT message and translates it into a
;FROM NEBULA MOUNT message which is then queued to the NEBULA sender
;of the node specified in the TO NEBULA MOUNT message.
;
;Call is:       M/ Address of the TO NEBULA MOUNT message
;Returns true:  FROM NEBULA MOUNT message built and queued to the NEBULA sender
;Returns false: Illegally formatted message

NTMTS:	$SAVE	<P1>			;SAVE THIS AC

;FIRST VALIDATE THE MESSAGE SYNTAX

	$CALL	CHKLEN			;CHECK FOR VALID IPCF MESSAGE LENGTH
	JUMPF	E$IML			;RETURN ON INVALID IPCF MESSAGE LENGTH
;**;[6046]At NTMTS:+6L remove 3 lines  JCR  1/16/90
	MOVEI	P1,.OHDRS(M)		;POINT TO THE FIRST BLOCK
	LOAD	S1,ARG.HD(P1),AR.TYP	;PICK UP THE BLOCK TYPE
	CAIE	S1,.STALS		;IS THIS THE ALIAS BLOCK?
	PJRST	E$IBT			;NO, TELL ORION
	LOAD	S1,ARG.HD(P1),AR.LEN	;PICK UP THE BLOCK LENGTH
	ADD	P1,S1			;[6007]POINT TO THE NEXT BLOCK
	LOAD	S1,ARG.HD(P1),AR.TYP	;PICK UP THE BLOCK TYPE
	CAIE	S1,.STRDV		;IS THIS A STRUCTURE BLOCK?
	JRST	NTMTS1			;NO, CHECK FOR A NODE BLOCK
	LOAD	S1,ARG.HD(P1),AR.LEN	;PICK UP THE BLOCK LENGTH
	ADD	P1,S1			;[6007]POINT TO THE NEXT BLOCK
	LOAD	S1,ARG.HD(P1),AR.TYP	;PICK UP THE BLOCK TYPE
NTMTS1:	CAIE	S1,.NDENM		;IS THIS A NODE BLOCK?
	PJRST	E$IBT			;NO, TELL ORION
	LOAD	S1,ARG.HD(P1),AR.LEN	;PICK UP THE BLOCK LENGTH
	CAIE	S1,.NDESZ		;THE CORRECT SIZE?
	PJRST	E$IBT			;NO, TELL ORION

;THE MESSAGE HAS A VALID SYNTAX. CHANGE THE MESSAGE FROM A TO NEBULA MOUNT
;MESSAGE TO A FROM NEBULA MOUNT MESSAGE.
	
	MOVEI	S1,.NFMTS		;PICK UP THE NEW MESSAGE CODE
	STORE	S1,.MSTYP(M),MS.TYP	;PLACE IN THE MESSAGE
	MOVE	S1,ARG.DA(P1)		;PICK UP NODE NAME TO SEND MESSAGE TO
	MOVE	S2,NODNAM		;PICK UP THE LOCAL NODE NAME
	MOVEM	S2,ARG.DA(P1)		;PLACE IN THE MESSAGE

;DETERMINE IF THE MESSAGE IS TO BE SENT TO A SINGLE NODE OR TO ALL THE
;REMOTE NODES.

	CAMN	S1,[-1]			;SEND THE MESSAGE TO ALL REMOTE NODE?
	JRST	NTMTS2			;YES, GO SEND IT TO THEM
	$CALL	QSREMM			;QUEUE OR SEND THE IN BEHALF OF MESSAGE
	JUMPF	@S1			;REMOTE NODE UNABLE TO RECEIVE THE MSG
	$RET				;RETURN TO THE IPCF HANDLER
NTMTS2:	$CALL	QMREMM			;QUEUE OR SEND TO ALL REMOTE NODES
	SETZM	G$ERR			;INDICATE ALREADY REPORTED ANY ERRORS
	$RET				;RETURN TO THE IPCF HANDLER
	SUBTTL	NFDAK - PROCESS A "FROM NEBULA DISMOUNT ACK" MESSAGE

;NFDAK forwards a FROM NEBULA DISMOUNT ACK message back to the node that
;made the original DISMOUNT request. 
;
;Call is:       M/ IPCF message buffer address
;Returns true:  If the message was successfully sent or queued on
;               the message queue
;Returns false: If the node that the message is to be forwarded to has
;               left the cluster or is no longer able to receive messages
;Crashes:       If the message is illegally formatted
;               If the node table is inconsistent

;ENSURE THAT THE MESSAGE WAS CORRECTLY FORMATTED

NFDAK:	$CALL	CHKLEN			;CHECK FOR VALID IPCF MESSAGE LENGTH
	JUMPF	S..IMM			;ILLEGALLY FORMATTED MOUNTR MESSAGE
	SKIPL	S1,.OFLAG(M)		;MESSAGE INDICATES SUCCESS?
	JRST	NFDAK2			;NO, CHECK IF FAIL FORMAT IS CORRECT
	SKIPE	.OARGC(M)		;YES, SUCCESS HAS NO ARGUMENT BLOCKS
	PJRST	S..IMM			;ILLEGALLY FORMATTED MOUNTR MESSAGE
	JRST	NFDAK3			;MESSAGE FORMATTED CORRECTLY

NFDAK2:	TXNN	S1,NF.VER		;A VALID ERROR BIT IS TURNED ON?
	PJRST	S..IMM			;ILLEGALLY FORMATTED MOUNTR MESSAGE
	SKIPG	S1,.OARGC(M)		;NEEDS TO BE AN ARGUMENT BLOCK
	PJRST	S..IMM			;ILLEGALLY FORMATTED MOUNTR MESSAGE
	CAIE	S1,1			;BUT ONLY ONE
	PJRST	S..IMM			;ILLEGALLY FORMATTED MOUNTR MESSAGE

	MOVEI	S1,.OHDRS(M)		;[6010]POINT TO THE ARGUMENT BLOCK
	LOAD	S2,ARG.HD(S1),AR.TYP	;PICK UP THE TYPE OF ARGUMENT BLOCK
	CAIE	S2,.ERRBK		;IS IT AN ERROR BLOCK?
	PJRST	S..IMM			;ILLEGALLY FORMATTED MOUNTR MESSAGE

;THE MESSAGE HAS VALID SYNTAX. CHECK IF THERE IS AN IBH QUEUE ENTRY FOR IT,
;IF THERE IS, THEN QUEUE THE MESSAGE TO BE SENT BACK TO THE REQUESTING NODE.

NFDAK3:	$CALL	QRIBHM			;QUEUE THE MESSAGE TO BE SENT
	$RET				;PRESERVE THE T/F INDICATOR
	SUBTTL	NCDSM	PROCESS A "NEBULA CANCEL DISMOUNT" MESSAGE

;NCDSM is called to cancel a NEBULA DISMOUNT message. This message is the
;result of an operator giving a CANCEL MOUNT-REQUEST or SET STRUCTURE
;SHARED command for a structure that is in the process of being dismounted
;remotely.
;NCDSM checks if there is a remote queue entry for the structure. If there
;is not, then the NEBULA DISMOUNT has already been processed and a
;TO NEBULA DISMOUNT ACK message has been sent to MOUNTR. MOUNTR will inform
;the operator of any nodes the structure has been dismounted from.
;If the remote queue entry is found, then a check is made for each node
;the message was sent to, to determine if the FROM NEBULA DISMOUNT
;message has been sent to that node. If it has not, then the message is
;cancelled. 
;If all the nodes have responded to the message, then NCDSM sends a TO
;NEBULA DISMOUNT ACK message to MOUNTR indicating the state of the
;structure on each of the remote nodes.
;
;Call is:       M/Address of the NEBULA CANCEL DISMOUNT message
;Returns true:  The remote queue entry has been found and processed
;Returns false: The remote queue entry was not found

NCDSM:	$SAVE	<P1,P2,P3,P4,T1>	;SAVE THESE AC

;FIND THE REMOTE QUEUE ENTRY. THE SEARCH IS MADE USING THE ACK CODE
;PROVIDED BY MOUNTR. THE GLOBAL REMOTE QUEUE IS SEARCHED

	MOVE	S1,.MSCOD(M)		;PICK UP THE ACK CODE
	$CALL	FNDGRQ			;FIND THE REMOTE QUEUE ENTRY
	JUMPF	.POPJ			;NOT THERE, TOO LATE TO CANCEL
	MOVE	P1,S2			;SAVE THE INVARIANT BLOCK ADDRESS

	MOVE	S1,.OFLAG(M)		;PICK UP REASON FOR THE CANCEL
	IORM	S1,.RQSTS(P1)		;SAVE CANCEL REASON IN REMOTE QE

;CHECK THE NODES THE FROM NEBULA DISMOUNT MESSAGE WAS SENT TO. IF A
;RESPONSE FROM A NODE HAS NOT YET BEEN RECEIVED, THEN A CHECK IS MADE
;IN THAT NODE'S MESSAGE QUEUE TO DETERMINE IF THE MESSAGE HAS YET
;BEEN SENT. IF IT HAS NOT, THEN THE MESSAGE IS CANCELLED.

	LOAD	P2,.RQNUM(P1),RQ.NTS	;PICK UP NUMBER OF NODES IN RQE
	MOVE	T1,P1			;PICK UP THE INVARIANT BLOCK ADDRESS
	SUB	T1,P2			;CALCULATE ADR OF 1ST LINK LIST WORD
	MOVEI	P3,.RQNIZ(P1)		;POINT TO THE FIRST NODE BLOCK
NCDSM2:	SKIPL	.RQNST(P3)		;RESPONSE RECEIVED FROM THIS NODE?
	JRST	NCDSM3			;YES, CHECK THE NEXT NODE

	MOVE	S1,.RQNDN(P3)		;PICK UP THE SIXBIT NODE NAME
	$CALL	SNAMNT			;PICK UP THE NODE TABLE ENTRY
	JUMPF	S..NTI			;NODE TABLE INCONSISTENCY DETECTED
	MOVE	P4,S2			;REMEMBER THE NODE TABLE ENTRY ADDRESS

	MOVE	S1,.RQACK(P1)		;PICK UP THE ACK CODE
	MOVE	SEN,.NNSBA(P4)		;PICK UP THE SENDER BLOCK ADDRESS
	$CALL	FNDMQE			;FIND THE MESSAGE QUEUE ENTRY
	JUMPF	NCDSM3			;TOO LATE, MESSAGE ALREADY SENT

	MOVX	S1,RQ%SNR!RQ%CIT	;INDICATE MESSAGE CANCELED AND 
	XORM	S1,.RQNST(P3)		;RESPONSE RECEIVED FOR THIS NODE

;DELINK THE REMOTE QUEUE ENTRY FROM THIS NODE'S REMOTE QUEUE. THEN
;DECREMENT THE NUMBER OF OUTSTANDING RESPONSES AND CHECK THE NEXT NODE

	MOVE	S1,T1			;PICK UP LINK LIST WORD ADDRESS
	MOVE	S2,P3			;PICK UP NODE BLOCK ADDRESS
	$CALL	RELNRQ			;REMOVE THE REMOTE QE FROM THIS NODE
	SOS	.RQNUM(P1)		;DECREMENT NUMBER OUTSTANDING RESPONSES
NCDSM3:	ADDI	P3,.RQNSZ		;POINT TO THE NEXT NODE BLOCK
	ADDI	T1,.RQBSZ		;POINT TO THE NEXT LINK LIST WORD
	SOJG	P2,NCDSM2		;CHECK THE NEXT NODE, IF ANY


;ALL THE NODES HAVE BEEN CHECKED. IF THERE ARE NO MORE OUTSTANDING MESSAGES,
;THEN RETURN THE GLOBAL REMOTE QUEUE ENTRY AND BUILD AND SEND THE TO
;NEBULA DISMOUNT ACK TO MOUNTR.

	LOAD	S1,.RQNUM(P1),RQ.NOR	;PICK UP NUMBER OUTSTANDING RESPONSES
	JUMPG	S1,.RETT		;IF ANY OUTSTANDING RESPONSES, RETURN
	MOVE	S1,P1			;PICK UP THE INVARIANT BLOCK
	$CALL	SNDACK			;SEND THE TO NEBULA DISMOUNT ACK
	$RETT				;RETURN TO THE CALLER
	SUBTTL	NDISPY	- PROCESS A WTO, ACK OR QUASAR SHOW MESSAGE

;NDISPY processes DISPLAY (.OMDSP), QUASAR SHOW ACK (.OMACS) and ORION
;TEXT (MT.TXT) messages. These messages are responses to a request made
;from a remote node (they are in behalf of responses). It is not necessary
;to check the syntax of these messages since the syntax is checked by both
;the local and requesting nodes' ORIONs. This routine forwards the message
;back to the node where the request originated from.
;
;Call is:       M/ IPCF message buffer address
;Returns true:  The message was successfully sent or queued on
;               the message queue
;Returns false: If the node that the message is to be forwarded to has
;               left the cluster or is no longer able to receive messages
;Crashes:       If the node table is inconsistent


;ENSURE THAT THE MESSAGE HAS A VALID IPCF MESSAGE LENGTH. IF IT DOES NOT,
;THEN INFORM THE SENDER OF THE REQUEST ON THE REMOTE NODE OF THIS FACT.

QREXMG:	SETOM	EXESND			;[6012]REQUEST ORIGINALLY FROM EXEC
NDISPY:	$CALL	CHKLEN			;CHECK FOR VALID IPCF MESSAGE LENGTH
	JUMPT	NDISP2			;SEND THE MESSAGE IF VALID MSG LENGTH
	
	$CALL	E$IML			;PLACE THE ERROR CODE IN G$ERR
	$CALL	BLDACK			;BUILD THE ERROR MESSAGE
	DMOVE	S1,.MSFLG(M)		;PICK UP THE FLAG AND ACK CODE WORDS
	DMOVEM	S1,.MSFLG(MO)		;PLACE IN THE ERROR MESSAGE
	MOVE	S1,MO			;PICK UP ERROR MESSAGE PAGE ADDRESS
	MOVE	S2,M			;PICK UP MESSAGE PAGE ADDRESS
	$CALL	XFRMSG			;COPY ERROR MESSAGE TO MESSAGE PAGE
	$CALL	RELPAG			;RETURN PAGE OF ERROR MESSAGE

;SEND OR QUEUE THE MESSAGE TO THE REMOTE NODE

NDISP2:	$CALL	QRIBHM			;QUEUE THE MESSAGE TO BE SENT
	SETZM	G$ERR			;INDICATE NO ACK MESSAGE TO BE SENT
	$RET				;MESSAGE QUEUED OR SENT TO REMOTE NODE

;**;[6053]At NDISP2:+3L add routine NWTOX  JCR  4/27/90
	SUBTTL	NWTOX - Process a WTO, WTOR or ACK Message

;[6053]NWTOX processes DISPLAY (.OMDSP) messages. These messages result from
;[6053]ACK, WTO or WTOR messages. In the cases where the .OMDSP message
;[6053]resulted from a WTO or WTOR message, it is possible that either
;[6053]the IBQ entry no longer exists or that the message is to be forwarded
;[6053]to more than one node. The first case is possible since a request
;[6053]may generate more than one WTO. The second case is possible due
;[6053]to the Broadcast WTO feature. The Broadcast WTO feature causes
;[6053]ORION to broadcast all its WTOs and WTORs to the nodes specified
;[6053]by the OPR ENABLE BROADCAST-MESSAGES command.
;[6053]
;[6053]Call is:       M/IPCF message buffer address
;[6053]Returns true:  The message was successfully sent or queued on
;[6053]               the message queue to the specified node or nodes
;[6053]Returns false: The message was illegally formatted or the message
;[6053]               was an ACK message and it could not be forwarded
;[6053]Crashes:       If the node table is inconsistent

NWTOX:	$SAVE	<P1,P2>			;[6053]Save some scratch ACs
	MOVE	S1,.MSFLG(M)		;[6053]Pick up the flag word
	TXNN	S1,MF.WTO!MF.WTR	;[6053]Originally a WTO or WTOR?
	JRST	NWTO.4			;[6053]No, was an ACK
	MOVE	S1,.MSCOD(M)		;[6053]Pick up the local ACK code
	$CALL	FNDIBH			;[6053]Find the IBQ entry
	JUMPF	NWTO.1			;[6053]Don't care if no longer there
	MOVE	S1,S2			;[6053]Place entry adr where expected
	$CALL	RELIBH			;[6053]Release the IBQ entry

NWTO.1:	MOVEI	S1,.NDENM		;[6053]Pick up the block type
	$CALL	N$ADJM			;[6053]Find and remove the block
	$RETIF				;[6053]Shouldn't happen
	MOVE	P1,S2			;[6053]Save the .NDENM block address
	LOAD	P2,0(P1),AR.LEN		;[6053]Pick up the block length
	SOSG	P2			;[6053]Calculate the number of nodes
	$RETF				;[6053]Shouldn't happen

NWTO.2:	AOS	P1			;[6053]Point to the next node name
	MOVE	S1,0(P1)		;[6053]Pick up the next node name
	$CALL	SETPAR			;[6053]Check if can forward the msg
	JUMPF	NWTO.3			;[6053]Can't, check the next node
	TXO	S2,MQ%IBH		;[6053]This msg is response to IBH msg
	$CALL	QUEMSG			;[6053]Send or queue the message
NWTO.3:	SOJG	P2,NWTO.2		;[6053]Check the next node
	SETZM	G$ERR			;[6053]Indicate no ACK msg to be sent
	$RETT				;[6053]Return to the caller

NWTO.4:	$CALL	QRIBHM			;[6053]Queue the message to be sent
	SETZM	G$ERR			;[6053]Indicate no ACK msg to be sent
	$RET				;[6053]Msg queued or sent to remote node
	SUBTTL	NSHOW - PROCESS OPR SHOW MESSAGES

;NSHOW forwards OPR SHOW messages to the specifed remote nodes. These
;messages have the format that the remote QUASAR or ORION expects, 
;except the message code is different and there is a node (.NDENM) block
;present that indicates the node or nodes that the message is to be
;forwarded to. NEBULA replaces the contents of the node block with the
;local node name which is used by ORION when logging the message and its
;response.
;
;Call is:       M/Address of the SHOW IPCF message
;Returns true:  The SHOW message was queued or sent to the remote node 
;               (or nodes)
;Returns false: The message was illegally formatted or the node is not
;               known to be in the cluster

;CHECK IF THE MESSAGE HAS A VALID IPCF MESSAGE LENGTH SPECIFIED AND IF
;SO, IF THE NODE THE MESSAGE IS TO BE SENT TO IS KNOWN TO BE IN THE
;CLUSTER

NSHOW:	$CALL	CHKLEN			;CHECK FOR A VALID IPCF MESSAGE LENGTH
	JUMPF	E$IML			;INVALID LENGTH, INFORM THE OPERATOR
	$CALL	FNDNBK			;PICK UP THE NODE TO SEND MESSAGE TO
	JUMPF	E$NNF			;FAIL IF NO NODE BLOCK IN THE MESSAGE

;PLACE LOCAL NODE NAME IN THE MESSAGE

	PUSH	P,S1			;SAVE THE ORIGINAL NODE NAME
	MOVE	S1,NODNAM		;PICK UP THE LOCAL NODE NAME
	MOVEM	S1,ARG.DA(S2)		;PLACE IN THE MESSAGE
	POP	P,S1			;RESTORE THE ORIGINAL NODE NAME

;DETERMINE WHICH NODE OR NODES TO SEND THE MESSAGE TO. SEND OR QUEUE THE
;MESSAGE TO THE INDICATED NODE OR NODES

	CAMN	S1,[-1]			;SEND THE MESSAGE TO ALL REMOTE NODES?
	JRST	NSHOW2			;YES, SEND THE MSG TO ALL REMOTE NODES
	$CALL	QSREMM			;NO, SEND THE MSG TO THE INDICATED NODE
	JUMPF	@S1			;ERROR, INDICATE TO THE SENDER
	$RET				;THE MESSAGE WAS SENT OR QUEUED

NSHOW2:	$CALL	QMREMM			;SEND THE MSG TO ALL THE REMOTE NODES
	SETZM	G$ERR			;INDICATE ALREADY REPORTED ANY ERRORS
	$RET				;RETURN TO THE SENDER
	SUBTTL	NLIST - PROCESSES EXEC "INFORMATION OUTPUT" MESSAGE

;[6012]NLIST forwards the EXEC INFORMATION OUTPUT message to the indicated
;[6012]node. This message has the format that the remote QUASAR expects.
;[6012]
;[6012]Call is:       M/Address of the IPCF INFORMATION OUTPUT message
;[6012]Returns true:  The message was queued or sent to the indicated remote 
;[6012]               node
;[6012]Returns false: The message was illegally formatted or the node is not
;[6012]               known to be in the cluster

;[6012]PICK UP AND SAVE THE EXEC'S PID IN CASE OF AN ERROR

NLIST:	SETOM	EXESND			;[6012]INDICATE MSG ORIGINALLY FROM EXEC
	MOVEI	S1,.LSPID		;[6012]PICK UP THE PID BLOCK CODE
	$CALL	N$FNDB			;[6012]FIND THE PID BLOCK
	JUMPF	.RETT			;[6012]NO PID BLOCK, DROP THE MESSAGE
	MOVE	S1,0(S1)		;[6012]PICK UP THE BLOCK
	MOVEM	S1,G$SND		;[6012]SAVE IN CASE OF AN ERROR

;[6012]CHECK IF THE MESSAGE HAS A VALID IPCF MESSAGE LENGTH SPECIFIED AND IF
;[6012]SO, IF THE NODE THE MESSAGE IS TO BE SENT TO IS KNOWN TO BE IN THE
;[6012]CLUSTER

	$CALL	CHKLEN			;[6012]CHECK FOR A VALID IPCF MSG LENGTH
	JUMPF	E$IML			;[6012]INVALID LENGTH, INFORM THE USER
	$CALL	FNDNBK			;[6012]PICK UP THE NODE TO SEND MSG TO
	JUMPF	E$NNF			;[6012]FAIL IF NO NODE BLOCK IN THE MSG

;[6012]PLACE LOCAL NODE NAME IN THE MESSAGE

	PUSH	P,S1			;[6012]SAVE THE ORIGINAL NODE NAME
	MOVE	S1,NODNAM		;[6012]PICK UP THE LOCAL NODE NAME
	MOVEM	S1,ARG.DA(S2)		;[6012]PLACE IN THE MESSAGE
	POP	P,S1			;[6012]RESTORE THE ORIGINAL NODE NAME

;[6012]SEND OR QUEUE THE MESSAGE TO THE INDICATED NODE

	$CALL	SETPAR			;[6012]DETERMINE IF CAN SEND MSG DIRECTLY
	JUMPF	@S1			;[6012]ERROR, INDICATE TO THE SENDER
	TXO	S2,MQ%EXE		;[6012]INDICATE MSG  FROM THE EXEC
	$CALL	QUEMSG			;[6012]SEND OR QUEUE THE MESSAGE
	$RETT				;[6012]RETURN TO THE SENDER
	SUBTTL	NKILL - ROUTINE PROCESS AN EXEC CANCEL MESSAGE

;[6012]NKILL is called to process an EXEC CANCEL PRINT request.
;[6012]Call is:       M/The message address
;[6012]Returns true:  The message has been routed to the indicated remote node
;[6012]Returns false: The remote node is inaccessible or unknown, or the message
;[6012]               was illegally formatted

NKILL:	SETOM	EXESND			;[6012]INDICATE THE MSG IS FROM THE EXEC
	MOVE	S1,KIL.PD(M)		;[6012]PICK UP THE EXEC'S PID
	MOVEM	S1,G$SND		;[6012]SAVE IN CASE OF AN ERROR

	$CALL	CHKLEN			;[6012]CHECK THE MESSAGE FORMAT
	JUMPF	E$IML			;[6012]IF ERROR, INDICATE SO
	MOVE	S1,KIL.ND(M)		;[6012]PICK UP NODE NAME TO SEND TO
	MOVE	S2,NODNAM		;[6012]PICK UP THE LOCAL NODE NAME
	MOVEM	S2,KIL.ND(M)		;[6012]SAVE IN THE MESSAGE

	$CALL	SETPAR			;[6012]DETERMINE QUEUE/SEND TO
	JUMPF	@S1			;[6012]REPORT ANY ERRORS
	TXO	S2,MQ%EXE		;[6012]INDICATE MSG FROM THE EXEC
	$CALL	QUEMSG			;[6012]SEND OR QUEUE THE MESSAGE
	$RETT				;[6012]RETURN TO THE CALLER
	SUBTTL	N$FNDB - ROUTINE TO FIND ANY BLOCK IN AN IPCF MESSAGE

;[6012]N$FNDB is called to find a specified block in an IPCF message.
;[6012]Call is:       M/The message address
;[6012]	              S1/The block type of the block to be found
;[6012]Returns true:  S1/The address of the block's data field
;[6012]Returns false: The block is not in the message

N$FNDB:	$SAVE	<P1,P2>			;[6012]SAVE THESE AC
	MOVE	P1,.OARGC(M)		;[6012]GET THE MESSAGE ARGUMENT COUNT
	MOVE	P2,S1			;[6012]SAVE THE BLOCK TYPE
	MOVEI	S1,.OHDRS(M)		;[6012]POINT TO THE FIRST BLOCK
	LOAD	TF,.MSTYP(M),MS.CNT	;[6012]GET THE MESSAGE LENGTH
	ADD	TF,M			;[6012]POINT TO THE END OF THE MESSAGE

N$FN.1:	LOAD	S2,ARG.HD(S1),AR.TYP	;[6012]GET THIS BLOCK TYPE
	CAMN	S2,P2			;[6012]IS THIS THE BLOCK?
	JRST	N$FN.2			;[6012]YES, RETURN WITH BLOCK ADDRESS
	LOAD	S2,ARG.HD(S1),AR.LEN	;[6012]NO, GET THIS BLOCK'S LENGTH
	ADD	S1,S2			;[6012]ADDRESS OF THE NEXT BLOCK
	CAIG	TF,0(S1)		;[6012]STILL WITHIN THE MESSAGE?
	$RETF				;[6012]NO, RETURN BLOCK NOT FOUND
	SOJG	P1,N$FN.1		;[6012]CHECK THE NEXT BLOCK
	$RETF				;[6012]BLOCK NOT FOUND

N$FN.2:	MOVEI	S1,ARG.DA(S1)		;[6012]POINT TO THE DATA FIELD
	$RETT				;[6012]AND RETURN
	SUBTTL	NSCLU - PROCESSES THE "SHOW CLUSTER-GALAXY" MESSAGE

;NSCLU processes the SHOW CLUSTER-GALAXY-LINK-STATUS message. This routine
;returns one of the following:
;1. The DECnet link status of the sender and listener to every remote
;   node currently known by the local NEBULA to be in the cluster.
;2. The DECnet link status of the sender and listener to the remote
;   node specified in the /NODE: switch.
;3. If the /CLUSTER-NODE switch was specified, then:
;   a. If the name specified was the local node name, then 1. or 2. 
;   b. If the name specified was a known remote name in the cluster, then
;      1. or 2. from the remote node
;   c. If -1 was specified, then 1. or 2. from all nodes in the cluster,
;      including the local node
;NSCLU first checks the node table to determine if there is a connected link
;or if the remote node is not accessible. If neither of these conditions are
;true, then it checks the previously and currently known node (PACKN) table
;to determine if a connection was ever made.
;
;Call is:       M/Address of the SHOW CLUSTER-GALAXY IPCF message.
;Returns true:  Response to message built and sent back to the operator
;               that made the request. If a remote node name (or -1) was
;               specified in the /CLUSTER-NODE switch, then the message is
;               forwarded to the indicate node (or all remote nodes)
;Returns false: Message invalidly formatted, node specified in the /NODE
;               switch is not known to be currently in the cluster, or
;               node from which the message came (the message was specified
;               with the /CLUSTER-NODE switch) is no longer in communication
;               with the local node.

NSCLU:	$SAVE	<P1,P2,P3,P4>		;SAVE THESE AC

;FIRST DETERMINE WHAT CONTEXT THE MESSAGE IS BEING PROCESSED IN. THE 
;POSSIBLE CONTEXTS ARE:
;1. THE REQUEST WAS MADE FROM THE LOCAL NODE AND IS FOR ALL NODES.
;2. THE REQUEST WAS MADE FROM THE LOCAL NODE AND IS FOR A SPECIFIC NODE.
;3. THE REQUEST WAS MADE FROM THE LOCAL NODE AND THE MESSAGE IS TO BE FORWARDED
;   TO A SPECIFIC REMOTE NODE
;4. THE REQUEST WAS MADE FROM THE LOCAL NODE AND IS FOR ALL NODES KNOWN TO
;   THE LOCAL NODE AS WELL AS HAVING THE MESSAGE FORWARDED TO ALL REMOTE NODES
;5. THE REQUEST WAS MADE FROM A REMOTE NODE.

	SKIPN	P2,RENNUM		;ANY REMOTE NODES?
;**;[6045]At NSCLU:+13L replace 2 lines with 1 line   JCR  15/90
	PJRST	E$NRN			;[6045]Indicate the error
	SETOM	LOCFLG			;ASSUME ALL NODES, LOCAL
	SETZM	REMFLG			;ASSUME MSG NOT TO BE FORWARDED

;VALIDATE THE ARGUMENT BLOCK COUNT

;**;[6045]At NSCLU:+21L add 2 lines  JCR  1/15/90
	MOVEI	S1,.OCTXT		;[6045]Command text block
	$CALL	N$ADJM			;[6045]Adjust the message
	SKIPGE	S1,.OARGC(M)		;VALID MINIMUM ARGUMENT COUNT?
	PJRST	E$IAC			;NO, INDICATE TO ORION
;**;[6046]At NSCLU:+24L remove 2 lines  JCR  1/16/90
	SKIPN	S1			;ANY ARGUMENT BLOCKS?
	JRST	NSCLU3			;NO, ALL NODES, LOCAL; BUILD THE MSG

	MOVEI	S2,.OHDRS(M)		;PICK UP ADDRESS OF FIRST ARGUMENT BLOCK
	LOAD	P1,ARG.HD(S2),AR.TYP	;PICK UP THE ARGUMENT TYPE
	CAIE	P1,.ORNOD		;IS THIS A NODE BLOCK?
	JRST	[ SOJG	S1,E$IFM	  ;IF TWO BLOCKS, THEN ILLEGAL FORMAT
		  JRST	NSCLU0 ]	  ;CHECK FOR CLUSTER-NODE BLOCK

	LOAD	P1,ARG.DA(S2)		;PICK UP THE NODE NAME 
	MOVEM	P1,LOCFLG		;SAVE FOR LATER
	SOJE	S1,NSCLU2		;PROCESS ONLY LOCALLY

	LOAD	P1,ARG.HD(S2),AR.LEN	;PICK UP THE BLOCK LENGTH
	ADD	S2,P1			;POINT TO THE NEXT BLOCK
	LOAD	P1,ARG.HD(S2),AR.TYP	;PICK UP THE ARGUMENT TYPE

NSCLU0:	CAIE	P1,.NDENM		;CLUSTER-NODE BLOCK TYPE?
	PJRST	E$IBT			;INVALID BLOCK TYPE
	LOAD	P1,ARG.DA(S2)		;PICK UP THE NODE NAME
	MOVEM	P1,REMFLG		;STORE FOR LATER

;DETERMINE IF THE MESSAGE IS TO BE PROCESSED LOCALLY, REMOTELY OR BOTH

	CAMN	P1,[-1]			;PROCESS FOR LOCAL AND REMOTE NODES?
	JRST	NSCLU1			;YES, CHECK FOR A SPECIFIC NODE
	CAME	P1,NODNAM		;WAS THE LOCAL NAME SPECIFIED?
	JRST	NSCL15			;NO, GO FORWARD THE MSG REMOTELY
	SETZM	REMFLG			;YES, ZERO OUT THE REMOTE NODE FLAG

;CHECK IF A SPECIFIC NODE WAS SPECIFIED. IF SO, THEN CHECK IF IT IS VALID

NSCLU1:	MOVE	S1,LOCFLG		;PICK UP LOCAL NODE FLAG
	CAMN	S1,[-1]			;FOR A SPECIFIC NODE?
	JRST	NSCLU3			;NO, CHECK ALL THE NODES

NSCLU2:	MOVE	S1,LOCFLG		;PICK UP THE NODE NAME WORD
;**;[6052]At NSCLU2:+1L change 1 line  JCR  3/1/90
	MOVEM	S1,G$ARG3		;[6052]Save node name in case of error
	$CALL	SNAMNT			;SEARCH NODE TABLE FOR THE NODE
	JUMPF	NSCL17			;[6023]DETERMINE THE TYPE OF ERROR
	MOVE	P1,S2			;SAVE THE NODE TABLE ENTRY
	MOVEI	P2,1			;THE NODE TABLE SEARCH FOR ONLY 1 NODE

;PICK UP THE RESPONSE MESSAGE PAGE, BUILD THE GALAXY MESSAGE HEADER, DISPLAY
;(.ORDSP) BLOCK, TEXT (.CMTXT) BLOCK HEADER AND POINT TO THE DATA FIELD OF
;THE TEXT BLOCK

NSCLU3:	MOVEI	S1,[ASCIZ/ Cluster Status /] ;MESSAGE HEADER
	$CALL	SETPAG			;GET MESSAGE PAGE AND BUILD HEADERS
	$CALL	CRLF			;MAKE A BLANK LINE

;BUILD THE TEXT HEADER

	$ASCII	(<           Node      Sender Link Status  Listener Link Status>)
	$CALL	CRLF			;NEXT DISPLAY LINE
	$ASCII	(<          ------     ------------------  -------------------->)
	$CALL	CRLF			;NEXT DISPLAY LINE

;PICK UP AND PLACE THE DECNET LINK STATUS OF THE SENDER AND LISTENER OF
;EACH CURRENTLY KNOWN REMOTE NODE KNOWN TO BE IN THE CLUSTER. FIRST
;CHECK IF BOTH THE LISTENER AND SENDER HAVE DECNET CONNECTIONS. IF THEY
;DON'T, THEN CHECK IF THE REMOTE NODE IS CAPABLE OF BEING IN COMMUNICATION
;WITH THE LOCAL NODE.

	SETO	S1,			;PREPARE FOR THE TYPE CHECK
	CAME	S1,LOCFLG		;IS THE REQUEST FOR ONLY ONE NODE?
	JRST	NSCLU4			;YES, SO POINTERS ALREADY SETUP
	MOVEI	P1,NODTBL		;POINT TO FIRST NODE TABLE ENTRY
	MOVEI	P4,MAXNOD		;PICK UP MAX NUMBER OF REMOTE NODES

NSCLU4:	SKIPN	.NNNAM(P1)		;IS THIS NODE TABLE ENTRY IN USE?
	JRST	NSCL13			;NO, CHECK THE NEXT NODE TABLE ENTRY
	$TEXT	(DEPBYT,<          ^N6L/.NNNAM(P1)/     ^A>) ;INCLUDE NODE NAME
	SETZ	S1,			;INDICATE NOT OR CAN'T CONNECT
	MOVE	S2,.NNSTA(P1)		;PICK UP THE NODE STATUS WORD
	TXNE	S2,NN%OKS		;SENDER AND LISTENER HAVE CONNECTIONS?
	MOVEI	S1,CONCON		;YES, INDICATE SO
	TXNE	S2,NN%COI!NN%DCN!NN%REL	;IS THIS NODE REACHABLE?
	MOVEI	S1,CNTCNT		;NO, INDICATE SO
	SKIPE	S1			;YES, CHECK FOR NOT/NEVER CONNECTED
	JRST	NSCL11			;GO PLACE DISPLAY LINE IN THE MESSAGE

;THE REMOTE NODE IS POTENTIALLY REACHABLE, BUT EITHER THE SENDER OR THE
;LISTENER OR BOTH CURRENTLY DO NOT HAVE OR NEVER HAVE HAD DECNET CONNECTIONS.
;FIRST CHECK THE DECNET LINK STATUS OF THE SENDER. IF THE SENDER DOES NOT
;HAVE A DECNET CONNECTION, THEN CHECK IF IT EVER HAS. (IT IS POSSIBLE FOR
;THE SENDER TO HAVE HAD A CONNECTION AND THEN LOST IT OR IT IS POSSIBLE 
;THAT THE SENDER HAD A CONNECTION BUT THE REMOTE NODE CRASHED AND CAME BACK,
;BUT SINCE THE NODE CAME BACK THE SENDER HAS NOT BEEN ABLE TO MAKE A 
;CONNECTION.)
;IF A SENDER HAS NEVER HAD A DECNET CONNECTION, THEN THIS IMPLIES THAT THE
;SENDER DOES NOT CURRENTLY HAVE A CONNECTION.
;IF A SENDER DOES NOT CURRENTLY HAVE A DECNET CONNECTION, THEN THIS DOES NOT
;IMPLY THAT THE SENDER NEVER HAD A DECNET CONNECTION.
;THE TABLE OF PREVIOUSLY AND CURRENTLY KNOWN NODES (PACKN) IS CHECKED TO 
;DETERMINE IF THE SENDER HAS EVER HAD A DECNET CONNECTION TO THE REMOTE NODE.
;(NOTE: IT IS ASSUMED THAT THIS TABLE HAS AT LEAST ONE FREE ENTRY IN IT.)

NSCLU5:	MOVEI	S1,CNNECT		;ASSUME THE SENDER HAS A CONNECTION
	TXNN	S2,NN%SHO		;DOES THE SENDER HAVE A CONNECTION?
	JRST	NSCLU8			;YES, DISPLAY SENDER STATUS
	MOVE	S1,.NNNAM(P1)		;PICK UP SIXBIT NAME OF REMOTE NODE
	$CALL	SRHPNE			;FIND THE PACKN TABLE ENTRY
	JUMPT	NSCLU7			;IF SUCCESS, THEN THE ENTRY WAS FOUND

;THE ENTRY WAS NOT FOUND. THIS SHOULD NEVER HAPPEN. IF IT DOES, THEN THE
;STATUS DECNET LINK STATUS OF THE SENDER IS LISTED AS "UNKNOWN". 

	MOVEI	S1,UNKSTS		;INDICATE UNKNOWN STATUS FOR SENDER
	SETO	P3,			;INDICATE NODE ENTRY NOT FOUND
	JRST	NSCLU8			;CHECK THE LISTENER DECNET STATUS

;DETERMINE IF THE SENDER HAS NEVER HAD A DECNET CONNECTION. IF IT HAS, THEN
;INDICATE THE THE SENDER CURRENTLY DOES NOT HAVE A DECNET CONNECTION.

NSCLU7:	MOVE	P3,S2			;SAVE THE PACKN ENTRY ADDRESS
	MOVE	S2,.LKSTS(P3)		;PICK UP SENDER STATUS FROM PACKN TBL
	MOVEI	S1,NOTCON		;ASSUME CURRENTLY NOT CONNECTED
	TXNN	S2,LK%SEN		;HAS SENDER EVER BEEN CONNECTED?
	MOVEI	S1,NEVCON		;NO, INDICATE SO

;PLACE THE SENDER STATUS IN THE MESSAGE AND THEN DETERMINE THE LISTENER'S
;DECNET LINK STATUS. FIRST CHECK IF THE LISTENER HAS A DECNET CONNECTION.

NSCLU8:	$TEXT	(DEPBYT,<^T20L/@LNKSTS(S1)/^A>) ;[6006]PLACE SENDER STATUS IN MSG
	MOVEI	S1,CNNECT		;ASSUME THE LISTENER HAS A CONNECTION
	MOVE	S2,.NNSTA(P1)		;PICK UP THE NODE'S STATUS WORD
	TXNN	S2,NN%LHO		;IS THE LISTENER CONNECTED?
	JRST	NSCL11			;YES, DISPLAY LISTENER STATUS

;THE LISTENER DOES NOT HAVE A DECNET CONNECTION. DETERMINE IF THE LISTENER
;HAS EVER HAD A DECNET CONNECTION OR HAS, BUT CURRENTLY DOES NOT HAVE A
;CONNECTION

	CAME	P3,[-1]			;PACKN TABLE ENTRY FOR THIS NODE?
	JRST	NSCLU9			;YES, PICK UP LISTENER CONNECTION STATUS
	MOVEI	S1,UNKSTS		;NO, INDICATE UNKNOWN STATUS
	JRST	NSCL11			;GO DISPLAY THE LISTENER STATUS

NSCLU9:	MOVE	S2,.LKSTS(P3)		;PICK UP LISTENER CONNECTION STATUS
	MOVEI	S1,NOTCON		;ASSUME THE LISTENER IS NOT CONNECTED
	TXNN	S2,LK%LIS		;LISTENER EVER BEEN CONNECTED?
	MOVEI	S1,NEVCON		;NO, INDICATE SO

;THE LISTENER DECNET STATUS IS EITHER:  CONNECTED, NOT CONNECTED, NEVER
;CONNECTED (OR UNKNOWN). PLACE THE APPROPRIATE TEXT IN THE DISPLAY LINE.

NSCL11:	$TEXT	(DEPBYT,<^T/@LNKSTS(S1)/^A>) ;PLACE LISTENER STATUS IN DISPLAY
	$CALL	CRLF			;START THE NEXT DISPLAY LINE

;IF THE SENDER DOES NOT HAVE A CONNECTION (ALSO INCLUDE THE CASE OF THE
;SENDER NOT EXISTING), THEN ALSO INCLUDE IF THE SENDER IS ENABLED OR NOT
;TO ATTEMPT DECNET CONNECTIONS AND WHETHER OR NOT THE SENDER IS ENABLED 
;TO REPORT DECNET CONNECTION ATTEMPT FAILURES TO ORION.

	CAIN	S1,CNTCNT		;SENDER AND LISTENER HAVE CONNECTIONS?
	JRST	NSCL12			;YES, CHECK THE NEXT NODE
	MOVE	S1,.NNSTA(P1)		;[6006]PICK UP THE NODE STATUS WORD
	TXNN	S1,NN%SHO		;DOES THIS SENDER HAVE A CONNECTION?
	JRST	NSCL12			;YES, CHECK THE NEXT NODE
	TXNN	S1,NN%CCA		;[6006]SENDER ENABLED TO MAKE A CONNECTION?
	$ASCII	(<             DECnet connection attempts enabled>)
	TXNE	S1,NN%CCA		;[6006]SENDER ENABLED TO MAKE A CONNECTION?
	$ASCII	(<             DECnet connection attempts disabled>)
	$CALL	CRLF			;START THE NEXT DISPLAY LINE
	TXNN	S1,NN%DCO		;[6006]SENDER ENABLED TO REPORT FAILURES?
	$ASCII	(<             Report connection failure attempts enabled>)
	TXNE	S1,NN%DCO		;[6006]SENDER ENABLED TO REPORT FAILURES?
	$ASCII	(<             Report connection failure attempts disabled>)
	$CALL	CRLF			;START THE NEXT DISPLAY LINE

;DETERMINE IF ANY MORE NODES ARE TO BE CHECKED. 

NSCL12:	SOJE	P2,NSCL14		;IF NO MORE NODES, SEND THE RESPONSE
NSCL13:	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	SOJE	P4,S..NTI		;NODE TABLE INCONSISTENCY DECTECTED
	JRST	NSCLU4			;CHECK OUT THE NEXT NODE

;ALL THE NODES HAVE BEEN PROCESSED. CALULATE THE LENGTH OF THE TEXT BLOCK
;AND UPDATE THE MESSAGE LENGTH FIELD

NSCL14:	HRRZ	S1,BYTPTR		;PICK UP FINAL MESSAGE ADDRESS
	SUB	S1,BLKADR		;SUBTRACT START OF TEXT DATA FIELD
	ADDI	S1,ARG.DA+1		;ACCOUNT FOR TEXT HEADER + 1
	MOVSS	S1			;PLACE LENGTH WHERE EXPECTED IN MSG

	MOVE	S2,BLKADR		;PICK UP ADDRESS OF DATA FIELD
	ADDM	S1,-1(S2)		;PLACE LENGTH IN TEXT BLOCK HEADER
	ADDM	S1,.MSTYP(MO)		;UPDATE MESSAGE LENGTH FIELD
	AOS	.OARGC(MO)		;INCREMENT THE ARGUMENT COUNT

;SEND THE MESSAGE. IF THE SENDER IS FROM A REMOTE NODE, THEN SEND A
;DECNET MESSAGE TO THAT NODE, OTHERWISE SEND AN IPCF MESSAGE.

	SKIPE	REMORG			;DID THE MESSAGE ORIGINATE REMOTELY?
	$RETT				;YES, RETURN TO LISTENER MSG PROCESSOR
	$CALL	ORNACK			;SEND THE MESSAGE TO ORION

;CHECK IF THE ORIGINAL MESSAGE IS TO BE FORWARDED TO ANY NODES

	SKIPN	REMFLG			;FORWARD THE REQUEST REMOTELY?
	$RETT				;NO, RETURN TO THE IPCF MSG PROCESSOR

;REFORMAT THE ORIGINAL MESSAGE. SEND OR QUEUE THE MESSAGE TO THE INDICATED
;REMOTE NODES

;**;[6047]At NSCL15:+0L replace 28 lines with 16 lines  JCR  1/16/90
NSCL15:	MOVEI	S1,.NSRCL		;[6047]Pick up the message type
	$CALL	SNDCLU			;[6047]Forward the message
	$RET				;[6047]Preserve the return indicator

;[6047]The node name specified in the /NODE: switch is not known to NEBULA. 
;[6047]Send the message remotely if so indicated, then send an error message
;[6047]locally to ORION.

NSCL17:	SKIPN	REMFLG			;[6047]Forward message remotely?
	JRST	NSCL18			;[6047]No, set up error indicators
	MOVEI	S1,.NSRCL		;[6047]Pick up the message type
	$CALL	SNDCLU			;[6047]Forward the message
NSCL18:	MOVE	S1,LOCFLG		;[6047]Pick up node name specified 
	CAME	S1,NODNAM		;[6047]Same as the local node name?
	PJRST	E$NSN			;[6047]No, indicate no such node
	PJRST	E$INS			;[6047]Can't specify local node name
	SUBTTL	NDRCF - ENABLE/DISABLE REPORT-CONNECTION-FAILURES

;NDRCF processes ENABLE/DISABLE REPORT-CONNECTION-FAILURES messages. If a
;sender to a node cannot obtain a DECnet connection to a node after so
;many attempts, then it informs ORION of this fact. However, it is possible
;for an operator to disable a sender from sending connection failure messages
;to ORION by giving the OPR>DISABLE REPORT-CONNECTION-FAILURES command.
;It is also possible for an operator to direct a sender to resume sending
;these messages with the OPR>ENABLE REPORT-CONNECTION-FAILURES command.
;The default is to send the connection failure messages.
;
;Call is:       M/Address of the message
;Returns true:  All the senders or a specified sender that do not have
;               DECnet connections have been ENABLED or DISABLED from
;               having their DECnet connection failures reported to the
;               operators.
;Returns false: The message is illegally formated or an unknown node
;               was specified

NDRCF:	$SAVE	<P1,P2,P3,P4,T1,T2,T3>	;SAVE THESE AC

;DETERMINE IF THE MESSAGE IS FOR ALL SENDERS OR THE SENDER OF A SPECIFIED
;NODE.

;**;[6052]At NDRCF:+5L replace 1 line with 12 lines  JCR  3/1/90
	MOVEI	S1,.OCTXT		;[6052]Command text block
	$CALL	N$ADJM			;[6052]Adjust the message
	SETZM	REMFLG			;[6052]Assume no need to forward
	$CALL	FNDNBK			;[6052]Check for a cluster node block
	JUMPF	NDRC.1			;[6052]None, so check for a node block
	MOVEM	S1,REMFLG		;[6052]Indicate need to forward
	CAME	S1,[-1]			;[6052]For all nodes?
	JRST	NDRC.3			;[6052]No, so forward the message now
	MOVE	S1,.OARGC(M)		;[6052]Pick up the number of arg blocks
	SOSA	S1			;[6052]Ignore the cluster node block
NDRC.1:	MOVE	S1,.OARGC(M)		;[6052]Pick up the number of arg blocks
	SKIPG	S1			;[6052]A node block present?
	JRST	NDRCF2			;NO, SO MESSAGE IS FOR ALL REMOTE NODES

;THE MESSAGE IS FOR A PARTICULAR NODE'S SENDER. CHECK THAT THE MESSAGE SYNTAX
;IS CORRECT. IF IT IS, THEN DETERMINE IF THE SPECIFIED NODE IS KNOWN.

;**;[6046]At NDRCF:+13L remove 2 lines  JCR  1/16/90
	LOAD	S1,.OHDRS(M),AR.TYP	;PICK UP THE ARGUMENT BLOCK TYPE
	CAIE	S1,.ORNOD		;IS IT A NODE BLOCK?
	PJRST	E$IBT			;NO, REPORT THE ERROR TO THE OPERATOR

	MOVE	S1,<.OHDRS+ARG.DA>(M)	;PICK UP THE NODE NAME
;**;[6052]At NDRC.1:+14L replace 4 lines with 3 lines  JCR  3/1/90
	MOVEM	S1,G$ARG3		;[6052]Save it in case it is unknown
	$CALL	SNAMNT			;[6052]Find the node's node table entry
	JUMPF	NDRC.4			;[6052]Report error if node is unknown
	MOVE	P3,S1			;SAVE THE NODE NAME FOR THE ACK MESSAGE

	MOVE	P1,S2			;SAVE THE NODE TABLE ENTRY ADDRESS
	MOVEI	P2,1			;ONLY ONE NODE TABLE ENTRY TO SEARCH
	JRST	NDRCF3			;CHECK IF SHOULD REPORT LINK ATTEMPTS

;ALL NODES WHOSE SENDERS CANNOT OBTAIN A DECNET CONNECTION ARE TO HAVE THEIR
;ATTEMPT EFFORTS REPORTED ENABLED OR DISABLED.

NDRCF2:	SKIPG	P2,RENNUM		;ARE THERE ANY KNOWN REMOTE NODES?
;**;[6040]At NDRCF2+1L Change one line  JCR  3/9/89
	JRST	NDRC9A			;[6040]No, just set RCFFLG.
	SETZ	P3,			;INDICATE MESSAGE IS FOR ALL NODES
	MOVEI	P1,NODTBL		;PICK UP THE NODE TABLE ADDRESS
	MOVEI	P4,MAXNOD		;PICK UP MAXIMUM NUMBER OF REMOTE NODES

;SETUP THE SEARCH OF THE SENDERS AND IF THE MESSAGE IS FOR ENABLE OR
;DISABLE.

NDRCF3:	MOVX	T3,NN%DCO		;NODE TABLE ENABLE/DISABLE FLAG BIT
	MOVX	T1,LK%DIS		;PACKN TABLE ENABLE/DISABLE FLAG BIT
	SETZB	T2,RCFFLG		;ASSUME THE MESSAGE IS ENABLE
	SKIPL	.OFLAG(M)		;IS IT REALLY?
	SETOB	T2,RCFFLG		;NO, THE MESSAGE IS FOR DISABLE

;CHECK IF THE NODE HAS A SENDER THAT DOES NOT HAVE A CONNECTION. IF THE
;NODE DOES NOT HAVE A SENDER OR THE SENDER HAS A CONNECTION, THEN JUST
;UPDATE THE PACKN ENTRY FOR THE NODE.

NDRCF4:	SKIPN	.NNNAM(P1)		;IS THIS NODE TABLE ENTRY IN USE?
	JRST	NDRCF9			;NO, CHECK THE NEXT NODE TABLE ENTRY

	MOVE	S1,.NNSTA(P1)		;PICK UP THE NODE'S STATUS WORD
	TXNE	S1,NN%COI!NN%DCN!NN%REL	;IS THERE A SENDER FOR THIS NODE?
	JRST	NDRCF6			;NO, UPDATE THE PACKN TABLE ENTRY
	TXNN	S1,NN%SHO		;DOES THIS SENDER HAVE A CONNECTION?
	JRST	NDRCF6			;YES, JUST UPDATE THE PACKN TABLE ENTRY

;THIS NODE HAS A SENDER AND IT HAS NOT YET MADE A DECNET CONNECTION.
;IF THE MESSAGE IS TO ENABLE REPORTING THE DECNET CONNECTION ATTEMPTS,
;THEN INDICATE THIS IN THE NODE TABLE STATUS WORD. OTHERWISE INDICATE
;THAT THE DECNET CONNECTION ATTEMPTS ARE NOT TO BE REPORTED.

	JUMPE	T2,NDRCF5		;IF ENABLING, GO INDICATE SO
	IORM	T3,.NNSTA(P1)		;DISABLE REPORTING CONNECTION FAILURES
	SKIPA				;SKIP ENABLING REPORTING
NDRCF5:	ANDCAM	T3,.NNSTA(P1)		;ENABLE REPORTING CONNECTION FAILURES

;UPDATE THE PACKN ENTRY FOR THIS NODE TO INDICATE IF THIS NODE'S SENDER'S
;CONNECTION FAILURE ATTEMPTS ARE TO BE REPORTED OR NOT.

NDRCF6:	MOVE	S1,.NNNAM(P1)		;PICK UP THE NODE NAME
	$CALL	SRHPNE			;PICK UP THE PACKN ENTRY ADDRESS
	JUMPF	NDRCF7			;SHOULD NEVER HAPPEN, BUT DON'T CARE
	JUMPE	T2,NDRCF7		;IF ENABLING, GO INDICATE SO
	IORM	T1,.LKSTS(S2)		;INDICATE DISABLING LINK ATTEMPTS
	SKIPA				;DON'T CHANGE TO ENABLING LINK ATTEMPTS
NDRCF7:	ANDCAM	T1,.LKSTS(S2)		;INDICATE ENABLING LINK ATTEMPTS

;DETERMINE IF THERE ARE ANY MORE NODES TO CHECK


NDRCF8:	SOJE	P2,NDRC10		;SEND MESSAGE IF NO MORE NODES TO CHECK
NDRCF9:	ADDI	P1,.NNNSZ		;CHECK THE NEXT NODE
	SOJE	P4,S..NTI		;NODE TABLE INCONSISTENT, IF NO ENTRIES
	JRST	NDRCF4			;CHECK THE NEXT NODE

;**;[6040]AT NDRCF9:+4L add 11 lines  JCR 3/9/89
;[6040]There are currently no known remote nodes in the cluster. Just set
;[6040]the global ENABLE/DISABLE REPORT-CONNECTION-FAILURES flag for any
;[6040]future nodes that may join the cluster.

NDRC9A:	SETZM	RCFFLG			;[6040]Assume ENABLE
	MOVEI	S1,NDRC12		;[6040]Pick up ENABLE/DISABLE Table adr
	SKIPGE	.OFLAG(M)		;[6040]Is the message really ENABLE?
	JRST	NDRC11			;[6040]Yes, send the response to ORION
	SETOM	RCFFLG			;[6040]No, indicate global DISABLE
	AOS	S1			;[6040]Point to the DISABLE entry
	JRST	NDRC11			;[6040]Send the response to ORION

;DETERMINE THE ACK MESSAGE TO BE SENT BACK TO THE OPERATOR

;**;[6052]At NDRC10:+0L replace 1 line with 6 lines  JCR  3/1/90
NDRC10:	SKIPL	REMORG			;[6052]Request originate remotely?
	JRST	NDRC.2			;[6052]No, so send ACK
	MOVE	S1,T2			;[6052]Pick up the ENABLE/DISABLE flag
	MOVE	S2,P3			;[6052]Pick up the node name
	$RETT				;[6052]Return to the caller
NDRC.2:	MOVEI	S1,NDRC12		;[6052]Pick up adr of ENABLE/DISABLE tbl
	SKIPE	T2			;WAS THE MESSAGE ENABLED?
	AOS	S1			;NO, INDICATE DISABLED

;INFORM THE OPERATOR OF THE RESULT OF THE COMMAND

	JUMPE	P3,NDRC11		;SKIP IF THE MSG WAS FOR ALL SENDERS
	$ACK	(NEBULA report connection failures to node ^N/P3/ ^T/@0(S1)/,,,.MSCOD(M))
;**;[6052]AT NDRC.2:+8L replace 4 lines with 18 lines  JCR  3/1/90
	SKIPL	REMFLG			;[6052]Need to send remotely?
	$RETT				;[6052]No, return to the caller
	JRST	NDRC.3			;[6052]Forward the message remotely
NDRC11:	$ACK	(NEBULA report connection failures ^T/@0(S1)/,,,.MSCOD(M));[6052]
	SKIPL	REMFLG			;[6052]Need to send remotely?
	$RETT				;[6052]No, return to the caller
NDRC.3:	MOVEI	S1,.NDRCF		;[6052]Pick up the message code
	$CALL	SNDCLU			;[6052]Forward the message
	$RET				;[6052]Return to the caller

NDRC.4:	SKIPN	REMFLG			;[6052]Forward message remotely?
	JRST	NDRC.5			;[6052]No, set up error indicators
	MOVEI	S1,.NDRCF		;[6052]Pick up the message type
	$CALL	SNDCLU			;[6052]Forward the message
NDRC.5:	MOVE	S1,G$ARG3		;[6052]Pick up node name specified 
	CAME	S1,NODNAM		;[6052]Same as the local node name?
	PJRST	E$NSN			;[6052]No, indicate no such node
	PJRST	E$INS			;[6052]Can't specify local node name

NDRC12:	[ASCIZ/enabled/]
        [ASCIZ/disabled/]
	SUBTTL	NDDCA - ENABLE/DISABLE DECNET-CONNECTION-ATTEMPTS

;NDDCA processes ENABLE/DISABLE DECNET-CONNECTION-ATTEMPTS messages.
;If a sender to a node cannot obtain a DECnet connection to a node,
;then it will retry to obtain the connection at varying time intervals.
;It is possible for an operator to disable the sender from making any more
;connection attempts with the OPR>DISABLE DECNET-CONNECTION-ATTEMPTS
;command. It is also possible for an operator to re-enable the sender
;to attempt to make a DECnet connection with the OPR>ENABLE
;DECNET-CONNECTION-ATTEMPTS command.
;
;Call is:      M/Address of the message
;Returns true: All the senders or a specified sender that do not have
;              DECnet connections have been ENABLED or DISABLED to
;              attempt to obtain DECnet connections.
;Returns false: Illegally formatted message or an unknown node specified

NDDCA:	$SAVE	<P1,P2,P3,P4,T1,T2,T3>	;SAVE THESE AC

;DETERMINE IF THE MESSAGE IS FOR ALL SENDERS OR THE SENDER OF A SPECIFIED
;NODE.

;**;[6052]At NDDCA:+5L replace 1 line with 12 lines  JCR  3/1/90
	MOVEI	S1,.OCTXT		;[6052]Command text block
	$CALL	N$ADJM			;[6052]Adjust the message
	SETZM	REMFLG			;[6052]Assume no need to forward
	$CALL	FNDNBK			;[6052]Check for a cluster node block
	JUMPF	NDDC.1			;[6052]None, so check for a node block
	MOVEM	S1,REMFLG		;[6052]Indicate need to forward
	CAME	S1,[-1]			;[6052]For all nodes?
	JRST	NDDC.3			;[6052]No, so forward the message now
	MOVE	S1,.OARGC(M)		;[6052]Pick up the number of blocks
	SOSA	S1			;[6052]Account for the cluster node blk
NDDC.1:	MOVE	S1,.OARGC(M)		;[6052]Pick up the number of arg blocks
	SKIPG	S1			;[6052]Node block present?
	JRST	NDDCA2			;NO, SO MESSAGE IS FOR ALL REMOTE NODES

;THE MESSAGE IS FOR A PARTICULAR NODE'S SENDER. CHECK THAT THE MESSAGE SYNTAX
;IS CORRECT. IF IT IS, THEN DETERMINE IF THE SPECIFIED NODE IS KNOWN.

;**;[6046]At NDDCA:+13L remove 2 lines  JCR  1/16/90
	LOAD	S1,.OHDRS(M),AR.TYP	;PICK UP THE ARGUMENT BLOCK TYPE
	CAIE	S1,.ORNOD		;IS IT A NODE BLOCK?
	PJRST	E$IBT			;NO, REPORT THE ERROR TO THE OPERATOR

	MOVE	S1,<.OHDRS+ARG.DA>(M)	;PICK UP THE NODE NAME
;**;[6052]At NDDC.1:+12L replace 5 lines with 3 lines  JCR  3/1/90
	MOVEM	S1,G$ARG3		;[6052]Save it in case it is unknown
	$CALL	SNAMNT			;[6052]Find the node's node table entry
	JUMPF	NDDC.4			;[6052]Not there
	MOVE	P3,S1			;SAVE THE NODE NAME FOR THE ACK MESSAGE

	MOVE	P1,S2			;SAVE THE NODE TABLE ENTRY ADDRESS
	MOVEI	P2,1			;ONLY ONE NODE TABLE ENTRY TO SEARCH
	JRST	NDDCA3			;CHECK IF SHOULD ENABLE LINK ATTEMPTS

;ALL NODES WHOSE SENDERS CANNOT OBTAIN A DECNET CONNECTION ARE TO BE ENABLED TO
;OR DISABLED FROM ATTEMPTING TO MAKE A DECNET CONNECTION.

NDDCA2:	SKIPG	P2,RENNUM		;ARE THERE ANY KNOWN REMOTE NODES?
	PJRST	E$NRN			;NO, TELL THE OPERATOR
	SETZ	P3,			;INDICATE MESSAGE IS FOR ALL NODES
	MOVEI	P1,NODTBL		;PICK UP THE NODE TABLE ADDRESS
	MOVEI	P4,MAXNOD		;PICK UP MAXIMUM NUMBER OF REMOTE NODES

;SET UP THE SEARCH OF THE SENDERS AND IF THE MESSAGE IS FOR ENABLE OR
;DISABLE.

NDDCA3:	MOVX	T3,NN%CCA		;NODE TABLE ENABLE/DISABLE FLAG BIT
	MOVX	T1,LK%CCA		;PACKN TABLE ENABLE/DISABLE FLAG BIT
	SETZB	T2,DCAFLG		;ASSUME THE MESSAGE IS ENABLE
	SKIPL	.OFLAG(M)		;IS IT REALLY?
	SETOB	T2,DCAFLG		;NO, THE MESSAGE IS FOR DISABLE

;CHECK IF THE NODE HAS A SENDER THAT DOES NOT HAVE A CONNECTION. IF THE
;NODE DOES NOT HAVE A SENDER OR THE SENDER HAS A CONNECTION, THEN JUST
;UPDATE THE PACKN ENTRY FOR THE NODE.

NDDCA4:	SKIPN	.NNNAM(P1)		;IS THIS NODE TABLE ENTRY IN USE?
	JRST	NDDCA9			;NO, CHECK THE NEXT NODE TABLE ENTRY

	MOVE	S1,.NNSTA(P1)		;PICK UP THE NODE'S STATUS WORD
	TXNE	S1,NN%COI!NN%DCN!NN%REL	;IS THERE A SENDER FOR THIS NODE?
	JRST	NDDCA6			;NO, JUST UPDATE THE PACKN TABLE ENTRY
	TXNN	S1,NN%SHO		;DOES THIS SENDER HAVE A CONNECTION?
	JRST	NDDCA6			;YES, JUST UPDATE THE PACKN TABLE ENTRY

;THIS NODE HAS A SENDER AND IT HAS NOT YET MADE A DECNET CONNECTION.
;IF THE MESSAGE IS TO ENABLE DECNET CONNECTION ATTEMPTS, THEN INDICATE THIS
;IN THE NODE TABLE STATUS WORD. OTHERWISE, INDICATE THAT THE DECNET CONNECTION
;ATTEMPTS ARE TO BE ABORTED.

	JUMPE	T2,NDDCA5		;IF ENABLING, GO INDICATE SO
	IORM	T3,.NNSTA(P1)		;DISABLE CONNECTION ATTEMPTS
	JRST	NDDCA6			;SKIP ENABLING CONNECTION ATTEMPTS
NDDCA5:	ANDCAM	T3,.NNSTA(P1)		;ENABLE CONNECTION ATTEMPTS

;IF DECNET CONNECTION ATTEMPTS ARE BEING ENABLED, THEN INTERRUPT THE SENDER.

	MOVE	SEN,.NNSBA(P1)		;PICK UP SENDER BLOCK ADDRESS
	MOVE	S1,.SNHND(SEN)		;PICK UP THE SENDER'S HANDLE
	MOVX	S2,<1B2>		;PICK UP CHANNEL TO INTERRUPT SENDER ON
	IIC%				;INTERRUPT SENDER TO ENABLE ATTEMPTS
	 ERJMP	S..UII			;CRASH ON AN ERROR


;UPDATE THE PACKN ENTRY FOR THIS NODE TO INDICATE IF THIS NODE'S SENDER
;IS TO ATTEMPT TO OBTAIN A DECNET CONNECTION.

NDDCA6:	MOVE	S1,.NNNAM(P1)		;PICK UP THE NODE NAME
	$CALL	SRHPNE			;PICK UP THE PACKN ENTRY ADDRESS
	JUMPF	NDDCA7			;SHOULD NEVER HAPPEN, BUT DON'T CARE
	JUMPE	T2,NDDCA7		;IF ENABLING, GO INDICATE SO
	IORM	T1,.LKSTS(S2)		;INDICATE DISABLING CONNECTION ATTEMPTS
	SKIPA				;SKIP ENABLING CONNECTION ATTEMPTS
NDDCA7:	ANDCAM	T1,.LKSTS(S2)		;INDICATE ENABLING CONNECTION ATTEMPTS

;DETERMINE IF THERE ARE ANY MORE NODES TO CHECK


NDDCA8:	SOJE	P2,NDDC10		;SEND MESSAGE IF NO MORE NODES TO CHECK
NDDCA9:	ADDI	P1,.NNNSZ		;CHECK THE NEXT NODE
	SOJE	P4,S..NTI		;NODE TABLE INCONSISTENT, IF NO ENTRIES
	JRST	NDDCA4			;CHECK THE NEXT NODE

;DETERMINE THE ACK MESSAGE TO BE SENT BACK TO THE OPERATOR

;**;[6052]At NDDC10:+0L replace 1 line with 6 lines  JCR  3/1/90
NDDC10:	SKIPL	REMORG			;[6052]Request originate remotely?
	JRST	NDDC.2			;[6052]No, so send ACK
	MOVE	S1,T2			;[6052]Pick up the ENABLE/DISABLE flag
	MOVE	S2,P3			;[6052]Pick up the node name
	$RETT				;[6052]Return to the caller
NDDC.2:	MOVEI	S1,NDDC12		;[6052]Pick up adr of ENABLE/DISABLE tbl
	SKIPE	T2			;WAS THE MESSAGE ENABLED?
	AOS	S1			;NO, INDICATE DISABLED

;INFORM THE OPERATOR OF THE RESULT OF THE COMMAND

	JUMPE	P3,NDDC11		;SKIP IF THE MSG WAS FOR ALL SENDERS
	$ACK	(NEBULA connection attempts to node ^N/P3/ ^T/@0(S1)/,,,.MSCOD(M))
;**;[6052]At NDDC.2:+8L replace 4 lines with 19 lines  JCR  3/1/90
	SKIPL	REMFLG			;[6052]Forward the message remotely?
	$RETT				;[6052]No, return to the caller
	JRST	NDDC.3			;[6052]Forward the message

NDDC11:	$ACK	(NEBULA connection attempts ^T/@0(S1)/,,,.MSCOD(M)) ;[6052]
	SKIPL	REMFLG			;[6052]Forward the message remotely?
	$RETT				;[6052]No, return to the caller
NDDC.3:	MOVEI	S1,.NDDCA		;[6052]Pick up the message code
	$CALL	SNDCLU			;[6052]Forward the message
	$RET				;[6052]Return to the caller

NDDC.4:	SKIPN	REMFLG			;[6052]Forward message remotely?
	JRST	NDDC.5			;[6052]No, set up error indicators
	MOVEI	S1,.NDDCA		;[6052]Pick up the message type
	$CALL	SNDCLU			;[6052]Forward the message
NDDC.5:	MOVE	S1,G$ARG3		;[6052]Pick up node name specified 
	CAME	S1,NODNAM		;[6052]Same as the local node name?
	PJRST	E$NSN			;[6052]No, indicate no such node
	PJRST	E$INS			;[6052]Can't specify local node name

NDDC12:	[ASCIZ/enabled/]
        [ASCIZ/disabled/]
;**;[6045]At NDDC12:+1L add routine  N$ADJM  JCR  1/15/90
	SUBTTL	N$ADJM - Adjust NEBULA Message

;[6045]N$ADJM is called to remove a trailing block from a message.
;[6045]
;[6045]Call is:       M/Message address
;[6045]               S1/Block type to remove
;[6045]Returns true:  The block has been removed from the message
;[6045]               S2/The block address
;[6045]Returns false: The block was not found in the message

N$ADJM:	$CALL	N$FNDB			;[6045]Search the message
	$RETIF				;[6045]Return now if none
	MOVEI	S2,-ARG.DA(S1)		;[6045]Save the block address
	HLLZ	S1,ARG.HD(S2)		;[6045]Pick up the block length
	MOVNS	S1			;[6045]Negate it
	ADDM	S1,.MSTYP(M)		;[6045]Adjust the total message length
	SOS	.OARGC(M)		;[6045]Adjust the argument count
	$RETT				;[6045]Return to the caller

;**;[6047]At N$ADJM:+8L add routine SNDCLU  JCR  1/16/90
	SUBTTL	SNDCLU - Send a SHOW CLUSTER Message Remotely

;[6047]SNDCLU is called to forward SHOW CLUSTER, ENABLE/DISABLE DECNET-
;[6047]CONNECTION-ATTEMPTS, and ENABLE/DISABLE REPORT-CONNECTION-FAILURES
;[6047]that were specified with the /CLUSTER-NODE switch
;[6047]
;[6047]Call is:       M/Address of the message to be forwarded
;[6047]		      S1/Message code
;[6047]               REMFLG/Remote node name or "-1" for all remote nodes
;[6047]Returns true:  The message was successfully queued or sent to all the
;[6047]               indicated nodes
;[6047]Returns false: The message was not successfully queued or sent
;[6047]               to all the indicated nodes

SNDCLU:	STORE	S1,.MSTYP(M),MS.TYP	;[6047]Save the message type
	MOVEI	S1,.NDESZ		;[6047]Pick up remote node block size
	MOVSS	S1			;[6047]Place length in expected place
	MOVNS	S1			;[6047]Negate the length
	ADDM	S1,.MSTYP(M)		;[6047]Decrement total message length
	SOS	.OARGC(M)		;[6047]Decrement the argument count

	MOVE	S1,REMFLG		;[6047]Pick up the remote node flag
	CAMN	S1,[-1]			;[6047]For all remote nodes?
	JRST	SNDC.1			;[6047]Yes, send to all remote nodes
	$CALL	QSREMM			;[6047]No, send to the indicated node
	$RETIT				;[6047]Return on success
	$CALL	0(S1)			;[6047]Setup error indicators
	$RET				;[6047]Preserve error return

SNDC.1:	$CALL	QMREMM			;[6047]Send to all the remote nodes
	SETZM	G$ERR			;[6047]QMREMM sends any error messages
	$RET				;[6047]Preserve true/false indicator
	SUBTTL	SETPAG - SETUP MESSAGE HEADER FOR SHOW ACK MESSAGES

;SETPAG is called by routines that process SHOW messages directed at
;NEBULA for action. Currently, this includes only the message:
;SHOW CLUSTER-GALAXY-LINK-STATUS
;This routine gets the message page, builds the GALAXY header, the
;display (.ORDSP) block and sets up the TEXT (.CMTXT) block header.
;If the SHOW CLUSTER-GALAXY-LINK-STATUS message originated on a remote
;node, then two display blocks are built. The first display block 
;indicates where the SHOW CLUSTER-GALAXY-LINK-STATUS message was processed
;and the second block is identical to the display block included in
;the message when it is processed locally.
;
;Call is: S1/Address of the OPR display header
;         M/ Address of the IPCF message
;Returns: SHOW ACK message header and display block has been built

SETPAG:	$SAVE	<P1>			;SAVE THIS AC

;PICK UP THE MESSAGE RESPONSE PAGE AND BUILD THE GALAXY MESSAGE HEADER
;(NOTE: IF THE MESSAGE CAME FROM A REMOTE NODE, THEN INDICATE THIS IN THE
;.MSFLG FLAG WORD BY SETTING BIT MF.NEB.)

	MOVE	P1,S1			;SAVE ADR OF DISPLAY HEADER STRING
	$CALL	GETPAG			;GET PAGE FOR RESPONSE MESSAGE

	MOVE	S1,[.OHDRS,,.OMACS]	;PICK UP MESSAGE TYPE WORD
	MOVEM	S1,.MSTYP(MO)		;PLACE IN THE RESPONSE MESSAGE
	MOVE	S1,.MSCOD(M)		;PICK UP OPR'S PID TO SEND RESPONSE TO
	MOVEM	S1,.MSCOD(MO)		;PLACE IN THE RESPONSE MESSAGE
	MOVX	S1,MF.NEB		;ASSUME THE MESSAGE ORIGINATED REMOTELY
	SKIPE	REMORG			;IS THIS A VALID ASSUMPTION?
	MOVEM	S1,.MSFLG(MO)		;YES, INDICATE THIS IN THE MESSAGE
	MOVEI	S1,.OHDRS(MO)		;POINT TO THE DISPLAY BLOCK
	MOVE	S2,P1			;PICK UP THE DISPLAY TEXT ADDRESS
	$CALL	SETHDR			;BUILD THE DISPLAY BLOCK

;SET UP HEADER TO THE TEXT BLOCK

	MOVEI	S2,.CMTXT		;PICK UP TEXT BLOCK CODE
	STORE	S2,ARG.HD(S1),AR.TYP	;PLACE IN TEXT BLOCK HEADER
	MOVEI	S2,ARG.DA(S1)		;PICK UP START OF TEXT FIELD
	MOVEM	S2,BLKADR		;REMEMBER FOR TEXT FIELD LENGTH CALC
	HRLI	S2,(POINT 7,)		;MAKE POINTER TO TEXT FIELD
	MOVEM	S2,BYTPTR		;REMEMBER FOR $TEXT PROCESSING
	$RET				;RETURN TO THE CALLER
	SUBTTL	SETHDR - BUILDS DISPLAY BLOCK FOR SHOW ACK MESSAGES

;SETHDR is called to build the DISPLAY (.ORDSP) block for SHOW ACK 
;messages. 
;
;Call is: S1/Address of the DISPLAY block
;         S2/Address of the display text
;         MO/Address of the SHOW ACK message being built
;Returns: DISPLAY block has been built
;         S1/Points to start of the TEXT (.ORDSP) block

SETHDR:	$SAVE	<P1,P2>			;SAVE THESE AC

;BUILD THE DISPLAY BLOCK HEADER AND THE TIME THE MESSAGE WAS PROCEESED
;WORD (THIS IS ALWAYS THE FIRST WORD OF A DISPLAY BLOCK.

	MOVE	P2,S2			;SAVE DISPLAY STRING ADDRESS
	$CALL	BDBHDR			;BUILD THE DISPLAY BLOCK HEADER

;PLACE THE DISPLAY HEADER STRING IN THE DISPLAY BLOCK, CALULATE THE LENGTH
;OF THE DISPLAY BLOCK AND SAVE THE LENGTH IN THE DISPLAY BLOCK HEADER

	SKIPN	REMORG			;MESSAGE FROM A REMOTE NODE?
	$TEXT	(DEPBYT,<^T/0(P2)/^A>)	;NO, PLACE DISPLAY STR IN DISPLAY BLOCK
	SKIPE	REMORG			;MESSAGE FROM A REMOTE NODE?
	$TEXT	(DEPBYT,<^I/@NODSTR/^A>) ;YES, PLACE DISPLAY STR IN DISPLAY BLK

;IF THE MESSAGE IS FROM A REMOTE NODE, ADD A SECOND DISPLAY BLOCK

	SKIPN	REMORG			;MESSAGE FROM A REMOTE NODE?
	JRST	SETHD2			;NO, FINISH UP
	HRRZ	P1,BYTPTR		;PICK UP ADDRESS OF END OF DISPLAY BLK
	SUBI	P1,-1(S1)		;CALCULATE LENGTH OF DISPLAY BLOCK
	STORE	P1,ARG.HD(S1),AR.LEN	;STORE LENGTH OF DISPLAY BLOCK 
	ADD	S1,P1			;POINT TO THE NEXT DISPLAY BLOCK
	MOVSS	P1			;PUT DISPLAY BLK LENGTH WHERE EXPECTED
	ADDM	P1,.MSTYP(MO)		;ADD TO MESSAGE LENGTH
	$CALL	BDBHDR			;BUILD THE SECOND DISPLAY BLOCK
	$TEXT	(DEPBYT,<^T/0(P2)/^A>)	;PLACE DISPLAY STR IN DISPLAY BLOCK	

;POINT TO THE NEXT ARGUMENT BLOCK AND UPDATE MESSAGE LENGTH IN GALAXY HEADER
;WORD

SETHD2:	HRRZ	P1,BYTPTR		;PICK UP ADDRESS OF END OF DISPLAY BLK
	SUBI	P1,-1(S1)		;CALCULATE LENGTH OF DISPLAY BLOCK
	STORE	P1,ARG.HD(S1),AR.LEN	;STORE LENGTH OF DISPLAY BLOCK 
	ADD	S1,P1			;POINT TO THE NEXT ARGUMENT BLOCK
	MOVSS	P1			;PUT DISPLAY BLK LENGTH WHERE EXPECTED
	ADDM	P1,.MSTYP(MO)		;UPDATE MESSAGE LENGTH FIELD
	$RET				;RETURN TO THE CALLER

;**;[6045]At NODSTR:+0L change 1 line  JCR  1/15/90
NODSTR:	[ITEXT(< Received message from ^N/NODNAM/>)]	;[6045]
	SUBTTL	BDBHDR - BUILD DISPLAY BLOCK HEADER

;BDBHDR is called to build the display block header.
;
;Call is: S1/Address of the display block
;         MO/Address of the message being built
;Returns: The display block header has been built
;         S1/Address of the display block

BDBHDR:	AOS	.OARGC(MO)		;INCREMENT ARGUMENT BLOCK COUNT
	MOVEI	S2,.ORDSP		;PICK UP DISPLAY BLOCK CODE
	STORE	S2,ARG.HD(S1),AR.TYP	;PLACE IN THE BLOCK HEADER
	MOVE	S2,MSGTIM		;PICK UP TIME THIS MSG IS PROCESSED
	MOVEM	S2,ARG.DA(S1)		;PLACE IN 1ST WORD OF TEXT BLOCK

;BUILD THE BYTE POINTER TO THE DISPLAY BLOCK TEXT FIELD

	MOVEI	S2,ARG.DA+1(S1)		;POINT TO 2ND WORD OF THE DISPLAY BLOCK
	HRLI	S2,(POINT 7,)		;MAKE INTO A POINTER
	MOVEM	S2,BYTPTR		;SAVE FOR $TEXT PROCESSING
	$RET				;RETURN TO THE CALLER
	SUBTTL	FNDNBK - FIND THE NODE BLOCK IN AN IPCF MESSAGE

;FNDNBK is called to find the node block of a SHOW message.
;
;Call is:       M/Address of the IPCF SHOW message
;Returns true:  The node block is present in the message
;               S1/The SIXBIT name of the node to send the message to or
;                  -1 which indicates that the message is to be sent to
;                  all the remote nodes in the cluster
;               S2/Address of the node block
;Returns false: The node block is not present in the message

FNDNBK:	$SAVE	<P1>			;SAVE THIS AC

;SETUP TO FIND THE NODE NAME ARGUMENT BLOCK BY DETERMINING THE NUMBER OF
;ARGUMENT BLOCKS AND THE ADDRESS OF THE FIRST ARGUMENT BLOCK.

;**;[6052]At FNDNBK:+5L replace 1 line with 2 lines  JCR  3/1/90
	SKIPG	S1,.OARGC(M)		;[6052]Pick up the number of arg blocks
	$RETF				;[6052]Return if none
	MOVEI	S2,.OHDRS(M)		;POINT TO THE FIRST ARGUMENT BLOCK

;FIND THE NODE NAME BLOCK AND RETURN THE NAME OF THE REMOTE NODE

FNDNB2:	LOAD	P1,ARG.HD(S2),AR.TYP	;PICK UP THE ARGUMENT BLOCK TYPE
	CAIN	P1,.NDENM		;IS THIS THE NODE BLOCK?
	JRST	FNDNB3			;YES, RETURN THE REMOTE NODE NAME
	LOAD	P1,ARG.HD(S2),AR.LEN	;NO, PICK UP THE LENGTH OF THIS BLOCK
	ADD	S2,P1			;POINT TO THE NEXT ARGUMENT BLOCK
	SOJG	S1,FNDNB2		;CHECK OUT THE NEXT ARGUMENT BLOCK
	$RETF				;ERROR, NO NODE BLOCK IN THE MESSAGE

FNDNB3:	MOVE	S1,ARG.DA(S2)		;PICK UP THE NODE NAME
	$RETT				;RETURN THE NODE NAME TO THE CALLER
	SUBTTL	CHKLEN - CHECK THE VALIDITY OF AN IPCF MESSAGE

;CHKLEN is called as part of validating the syntax of an IPCF message.
;This routine checks if the size of an IPCF message, as indicated in the
;message length field of the IPCF message, is positive and less than or
;equal to a page.
;
;Call is:       M/Address of the IPCF message
;Returns true:  The indicated message length is valid
;               S1/The indicated length of the message
;Returns false: The indicated message length is invalid

CHKLEN:	LOAD	S1,.MSTYP(M),MS.CNT		;PICK UP THE LENGTH OF MSG
	SKIPG	S1				;POSITIVE LENGTH SPECIFIED?
	$RETF					;INVALID MSG LENGTH SPECIFIED
	CAILE	S1,PAGSIZ			;NOT OVER A PAGE?
	$RETF					;INVALID MSG LENGTH SPECIFIED
	$RETT					;VALID MESSAGE SIZE SPECIFIED
	SUBTTL	QSREMM - SEND OR QUEUE REMOTE MESSAGES TO A SINGLE REMOTE NODE

;QSREMM is called by IPCF routines that process "remote" messages that
;will be sent to a single remote node. These routines first verify that the
;remote message has a valid syntax, then they change the format of the 
;message, if necessary.
;After this, these routines call QSREMM which determines if the message should
;be placed on the sender's message queue to the remote node or if the
;message can be placed in the sender's message buffer and transferred
;directly to the remote node. This latter case is true if the sender is
;available to send a message and its message queue is empty.
;
;Call is:       S1/Remote node name to send the message to
;               M/Address of the IPCF remote message
;Returns true:  Message has been queued to or sent to the remote node
;Returns false: The remote node does not exist or is unable to receive messages
;               S1/Address of the error handling routine

QSREMM:	$CALL	SETPAR			;DETERMINE IF CAN SEND MSG DIRECTLY 
	JUMPF	.POPJ			;NODE NOT ABLE TO RECEIVE MESSAGES
	$CALL	QUEMSG			;QUEUE OR SEND THE MESSAGE
	$RETT				;RETURN TO THE CALLER
	SUBTTL	QMREMM - SEND OR QUEUE REMOTE MESSAGES TO MULTIPLE REMOTE NODES

;QMREMM is called by IPCF routines that process remote messages that
;will be sent to multiple remote nodes. (This occurs when the OPR
;/CLUSTER-NODE: switch specified a value of "*").
;These routines first verify that the remote message has a valid syntax,
;then they change the format, if necessary.
;After this, these routines call QMREMM which determines which nodes the
;message must be queued for, which nodes the message can be sent to and
;which nodes the message cannot be sent to. A message can be sent directly
;to a remote node if the sender to that node is available to send a message
;and its message queue is empty.
;If one or more nodes cannot receive the message, then an error
;(.OMACK) message is sent to the OPR that issued the message indicating for
;each node that the message could not be sent to the reason for the failure.
;
;Call is:       M/Address of the IPCF remote message
;Returns true:  Message has been queued to or sent to the remote nodes
;Returns false: No remote nodes are able to receive the message. (The
;               sender of the message is informed of any nodes the message
;               was not sent to.)

QMREMM:	$CALL	SEPALL			;DETERMINE IF CAN SEND MSG DIRECTLY 
	JUMPF	.POPJ			;NO NODES ABLE TO RECEIVE MESSAGES
	$CALL	QUEMSG			;QUEUE OR SEND THE MESSAGE
	$RETT				;RETURN TO THE CALLER
	SUBTTL	QRIBHM - SEND/QUEUE RESPONSES TO IBH MESSAGE TO REQUESTING NODE

;QRIBHM is called by IPCF routines that are processing the responses to IBH 
;messages. All these routines first check the syntax of the
;response message, reformat (if necessary) the response message, then call
;QRIBHM. QRIBHM first finds the in behalf of queue entry corresponding to the
;response message. If the IBH queue entry is not found, then it is assumed that
;the requesting node has lost (or had lost and then regained) communication
;with the local node between the time the in behalf of message was received
;and the time NEBULA has received its response. (When a requesting node has
;lost communication with the local node, the local node, as part of its
;cleanup of the requesting node, deletes the requesting node's entries in
;the in behalf of queue.)
;If the in behalf of queue entry is found, then the original ACK code of the
;in behalf message is placed in the response message and the message is
;either queued to be sent to the requesting node or is sent directly to the
;requesting node if the requesting node's sender is available to send messages
;and it has no messages in its message queue.
;
;Call is:       M/Reformatted IPCF in behalf of response message
;Returns true:  The message was sent or queued
;Returns false: The in behalf of queue entry was not found (in this case the
;               message is dropped by NEBULA) 
;Crashes:       Node table inconsistency detected

QRIBHM:	$SAVE	<P1,P2>			;[6012]SAVE THESE AC

;PICK UP THE IN BEHALF OF QUEUE ENTRY

	MOVE	S1,.MSCOD(M)		;PICK UP THE LOCAL ACK CODE
	$CALL	FNDIBH			;FIND THE IN BEHALF OF QUEUE ENTRY
;**;[6043]At QRIBHM:+6L replace 1 line with 20 lines  JCR  11/29/89
	JUMPT	QRIBH1			;[6043]Check if message can be forwarded
;[6043]Some OPR commands may result in more than one WTO or WTOR message being
;[6043]generated. Therefore, if the IBH queue entry is not found, then check
;[6043]if the message was originally a WTO or a WTOR. If it was, then forward
;[6043]the message to the indicated node.

	LOAD	S1,.MSTYP(M),MS.TYP	;[6043]Get the message type
	CAIE	S1,.OMDSP		;[6043]Is it a display message?
	JRST	QRIBH2			;[6043]No, so quit now
	LOAD	S1,.MSFLG(M)		;[6043]Pick up the flag word
	TXNN	S1,MF.WTO!MF.WTR	;[6043]Originally a WTO or WTOR?
	JRST	QRIBH2			;[6043]No, so quit now
	MOVE	S1,.OHDRS+ARG.DA(M)	;[6043]Pick up the node name
	$CALL	SETPAR			;[6043]Determine if can forward the msg
;**;[6050]At QRIBHM:+21L change 1 line  JCR  2/9/90
	$RETIF				;[6050]Quit if node no longer there
	SETZM	.MSCOD(M)		;[6043]Zero the PID
	TXO 	S2,MQ%IBH		;[6043]This msg is response to IBH msg
	$CALL	QUEMSG			;[6043]Send or queue the message
	$RETT				;[6043]Return to the caller

;DETERMINE IF THE MESSAGE CAN BE SENT TO THE NODE. (THIS SHOULD ALWAYS BE
;THE CASE. IF IT IS NOT, THEN THERE IS AN INCONSISTENCY IN THE NODE DATA
;BASE.)
;IF THE MESSAGE CAN BE SENT TO THE NODE, THEN DETERMINE 
;IF THE MESSAGE CAN BE SENT DIRECTLY TO THE NODE OR IF IT MUST BE QUEUED
;ON THE NODE'S MESSAGE QUEUE.

;**;[6043]At QRIBHM:+25L change 1 line  JCR  11/29/89
QRIBH1:	MOVE	P1,S2			;[6043]Save the IBH QE address
	MOVE	S1,.IQNEN(P1)		;PICK UP THE REMOTE NODE NAME
	$CALL	SETPAR			;DETERMINE IF CAN SEND MSG DIRECTLY
	JUMPF	S..NTI			;CAN'T SEND TO NODE. INCONSISTENCY

;REPLACE THE LOCAL ACK CODE WITH THE ORIGINAL ACK CODE. IF THE REQUEST
;ORIGINATED FROM THE EXEC, THEN SAVE ITS PID IN THE MESSAGE. EITHER SEND
;THE MESSAGE TO THE REMOTE NODE OR QUEUE THE MESSAGE ON THE REMOTE
;NODE SENDER'S MESSAGE QUEUE, AND RELEASE THE IN BEHALF OF QUEUE ENTRY.

	MOVE	P2,.IQRNA(P1)		;PICK UP THE ORIGINAL ACK CODE
	MOVEM	P2,.MSCOD(M)		;PLACE IN THE MESSAGE
	MOVE	P2,.IQPID(P1)		;[6012]PICK UP A POSSIBLE EXEC PID
	SKIPE	EXESND			;[6012]REQUEST ORIGINALLY FROM EXEC?
	MOVEM	P2,.OFLAG(M)		;[6012]YES, PLACE ITS PID IN THE MESSAGE
	TXO 	S2,MQ%IBH		;THIS MSG IS RESPONSE TO IBH MSG
	$CALL	QUEMSG			;SEND OR QUEUE THE MESSAGE
	MOVE	S1,.MSFLG(M)		;PICK UP THE FLAG WORD OF THIS MESSAGE
	TXNE	S1,MF.MOR		;MORE PAGES FOR THIS MESSAGE?
	$RETT				;YES, DON'T DESTROY THE IBH QUEUE ENTRY
;**;[6037]At QRIBHM:+18L add 2 lines  JYCW  Oct-18-88
	SOSE	.IQNUM(P1)		;[6037]ANY MORE RESPONSES?
	$RETT				;[6037]YES, KEEP THE IBH QUEUE ENTRY
	MOVE	S1,P1			;PICK ADDRESS OF THE IBH QUEUE ENTRY
	$CALL	RELIBH			;RELEASE THE IBH QUEUE ENTRY
	$RETT				;RETURN TO THE CALLER
;**;[6043]At QRIBH2:+0L change 1 line  JCR  11/29/89
QRIBH2:	$RETF				;[6043]Return to the caller
	SUBTTL	SETPAR - SETUP PARAMETERS FOR ROUTINE QUEMSG

;SETPAR is called to set up ACs S1 and S2, and tables NODSEN and NODMQ
;for routine QUEMSG. SETPAR is used when a message needs to be sent to
;only one node. For the case where a message is to be sent to all remote
;nodes (i.e., an OPR command was given with /CLUSTER-NODE:*), then routine
;SEPALL is called.
;
;Call is:       S1/SIXBIT node name of node the message is to be sent to
;Returns true:  S1/Number of nodes to send the message directly to
;               (either 0 or 1)
;               S2/Number of nodes to queue the message on the message queue
;                 (either 0 or 1)
;               NODSEN/If (S1) = 1, then contains the node table entry address
;                     of the node that is to have the message sent directly to
;               NODMQ/ If (S2) = 1, then contains the node table entry address
;                     of the node that is to have the message queued on its
;                     message queue
;Returns false: S1/Address of the error handling routine

SETPAR:	$SAVE	<P1>			;SAVE THIS AC

;FIRST CHECK IF THE NODE EXISTS

	MOVE	P1,S1			;SAVE THE NODE NAME IN CASE OF ERROR
	$CALL	SNAMNT			;FIND THE NODE IN THE NODE TABLE
	JUMPF	SETPA4			;NO SUCH NODE, INFORM ORION

;CHECK IF DECNET MESSAGES CAN BE SENT TO THIS NODE

	SKIPL	S1,.NNSTA(S2)		;O.K. TO SEND TO THIS NODE?
	JRST	SETPA5			;NO, CAN'T SEND TO THIS NODE

;**;[6037]At SETPAR:+6L add 2 lines  JYCW  Oct-18-88
	SKIPE	NMFFLG			;[6037]NEW FORMAT?
	TXNE	S1,NN%PAS		;[6037]Post F.I GALAXY on remote node
	JRST	SETPA			;[6037]
	TXZ	S1,NN%OKS		;[6037]Clear O.K to send bit
	TXO	S1,NN%PAS		;[6037]SET NOT LATEST GALAXY BIT
	JRST	SETPA7			;[6037]NO, CAN'T SEND TO THIS NODE

;CHECK IF THIS NODE'S MESSAGE QUEUE IS EMPTY AND IF IT IS, CHECK IF THE SENDER
;TO THE NODE IS BUSY. IF THE SENDER IS NOT BUSY, THEN INDICATE THAT THE
;MESSAGE CAN BE SENT DIRECTLY TO THE NODE WITHOUT FIRST PLACING THE MESSAGE
;ON THE NODE'S MESSAGE QUEUE

SETPA:	MOVE	SEN,.NNSBA(S2)		;[6037]PICK UP THE SENDER BLOCK ADDRESS
	SKIPE	.SNUSM(SEN)		;IS THE MESSAGE QUEUE EMPTY?
	JRST	SETPA2			;NO, INDICATE MUST USE MESSAGE QUEUE
	SKIPL	.SNFRE(SEN)		;YES, IS THE SENDER FREE TO SEND A MSG?
	JRST	SETPA2			;NO, INDICATE MUST USE MESSAGE QUEUE

;INDICATE THAT THE MESSAGE CAN BE SENT TO THE SENDER WITHOUT NEEDING TO PLACE
;THE MESSAGE ON THE NODE'S MESSAGE QUEUE.

	MOVEM	S2,NODSEN		;PLACE NODE TABLE ENTRY IN TABLE
	MOVEI	S1,1			;ONE NODE TO SEND MESSAGE DIRECTLY TO
	SETZ	S2,			;NO NODES NEED MSG PLACED ON MSG QUEUE
	JRST	SETPA3			;GO RETURN

;INDICATE THAT THE MESSAGE MUST BE PLACED ON THE NODE'S MESSAGE QUEUE

SETPA2:	MOVEM	S2,NODMQ		;PLACE NODE TABLE ENTRY IN TABLE
	SETZ	S1,			;NO NODES TO SEND MESSAGE DIRECTLY TO
	MOVEI	S2,1			;ONE NODE NEEDS MSG PLACED ON MSG QUEUE

SETPA3:	$RETT				;RETURN TO THE CALLER

;AN ERROR HAS BEEN DETECTED. EITHER THE NODE IS NO LONGER IN THE CLUSTER OR
;THE NODE IS NOT CAPABLE OF RECEIVING DECNET MESSAGES.

SETPA4:	MOVEI	S1,E$NSN		;NO SUCH NODE ERROR ROUTINE ADDRESS
;**;[6052]At SETPA4:+1L change 1 line  JCR  3/1/90
	MOVEM	P1,G$ARG3		;[6052]Save the node name for error handler
	$RETF				;RETURN INDICATING FAILURE
SETPA5:	MOVE	S2,P1			;PICK UP THE NODE NAME
	$CALL	USNERR			;PICK UP ADDRESS OF ERROR STRING
	MOVEI	S1,E$USN		;UNABLE TO SEND TO NODE ROUTINE ADDRESS
	$RETF				;RETURN TO THE CALLER

;**;[6037]At SETPA5:+4L add routine SETPA7:  JYCW  Oct-18-88
SETPA7:	MOVEM	P1,G$ARG1		;[6037]PICK UP THE NODE NAME
	MOVEI	S1,[ASCIZ/ because node is running 
Field Image version 6 GALAXY and does not understand the command/]
	MOVEM	S1,G$ARG2		;[6037]SAVE IT 
	MOVEI	S1,E$USN		;UNABLE TO SEND TO NODE ROUTINE ADDRESS
	$RETF				;RETURN TO THE CALLER
	SUBTTL	SEPALL - SET UP PARAMETERS FOR ROUTINE QUEMSG

;SEPALL is called to set up ACs S1 and S2, and tables NODSEN and NODMQ
;for routine QUEMSG. SEPALL is called when a message needs to be sent to
;all the remote nodes (i.e., an OPR command was given with /CLUSTER-NODE:*).
;
;Call is:       No arguments
;
;Returns true:  S1/Number of nodes to send the message directly to
;               S2/Number of nodes to queue the message on the message queue
;               NODSEN/If (S1) > 0, then contains the node table entry
;                      addresses of the nodes that are to have the message sent
;                      directly to them
;               NODMQ/ If (S2) > 0, then contains the node table entry 
;                      addresses of the nodes that are to have the message
;                      queued on their message queues
;Returns false: The message cannot be sent to any nodes. In this case, (as
;               in the case where the message can be sent to some nodes, but
;               not all nodes) an ACK message is sent to the OPR which made
;               the request indicating which nodes the message was not sent to.
;Crashes:	Node table inconsistency detected

SEPALL:	$SAVE	<P1,P2,P3,P4,T1,T2>	;SAVE THESE AC

;FIRST INITIALIZE THE POINTERS AND COUNTERS

	SETZB	S1,S2			;NO NODES DIRECT SEND OR QUEUE THE MSG
	SETO	P1,			;CAN SEND TO ALL NODES
	MOVEI	P2,NODTBL		;POINT TO THE NODE TABLE
;**;[6053]At SEPALL:+7L replace 1 line with 2 lines  JCR  4/27/90
	SKIPG	P3,RENNUM		;[6053]Pick up number of remote nodes
	$RETF				;[6053]Quit now if none
	MOVEI	P4,MAXNOD		;PICK UP MAXIMUM NUMBER OF REMOTE NODES

;CHECK IF THE REMOTE NODE CAN RECEIVE DECNET MESSAGES

SEPAL2:	SKIPN	T1,.NNNAM(P2)		;IS THIS NODE TABLE ENTRY IN USE?
	JRST	SEPAL6			;NO, CHECK THE NEXT NODE TABLE ENTRY
	SKIPL	T2,.NNSTA(P2)		;O.K. TO SEND TO THIS NODE?
	JRST	SEPAL4			;NO, ADD THIS NODE TO THE ERROR LIST

;**;[6037]At SEPAL2:+4L add 2 lines  JYCW  Oct-18-88
	SKIPE	NMFFLG			;[6037]NEW FORMAT?
	TXNE	T2,NN%PAS		;[6037]POST F.I GALAXY ON REMOTE NODE
	JRST	SEPAL8			;[6037]NO
	TXZ	T2,NN%OKS		;[6037]Clear O.K to send bit
	TXO	T2,NN%PAS		;[6037]SET NOT LATEST GALAXY BIT
	JRST	SEPAL4			;[6037]NO, CAN'T SEND TO THIS NODE

;THE NODE CAN RECEIVE DECNET MESSAGES. CHECK IF THE MESSAGE CAN BE PLACED
;DIRECTLY IN THE SENDER'S MESSAGE BUFFER RATHER THAN ON THE MESSAGE QUEUE

SEPAL8:	MOVE	SEN,.NNSBA(P2)		;[6037]PICK UP THE SENDER BLOCK ADDRESS
	SKIPE	.SNUSM(SEN)		;IS THE MESSAGE QUEUE EMPTY?
	JRST	SEPAL3			;NO, MUST QUEUE THE MESSAGE
	SKIPL	.SNFRE(SEN)		;IS THE SENDER AVAILABLE TO SEND?
	JRST	SEPAL3			;NO, MUST QUEUE THE MESSAGE

;THE NODE CAN SEND THE MESSAGE DIRECTLY. PLACE ITS NODE TABLE ENTRY
;ADDRESS IN TABLE NODSEN

	MOVEM	P2,NODSEN(S1)		;PLACE NODE TABLE ENTRY ADR IN NODSEN
	AOS	S1			;NUMBER OF NODES TO SEND MSG DIRECTLY
	JRST	SEPAL5			;GO CHECK THE NEXT NODE

;THE NODE'S MESSAGE QUEUE IS EITHER NOT EMPTY OR THE SENDER TO THE NODE
;IS CURRENTLY PROCESSING A MESSAGE. PLACE THE NODE'S NODE TABLE ENTRY
;ADDRESS IN TABLE NODMQ

SEPAL3:	MOVEM	P2,NODMQ(S2)		;PLACE NODE TABLE ENTRY ADR IN NODMQ
	AOS	S2			;NUMBER OF NODES TO QUEUE MESSAGE TO
	JRST	SEPAL5			;GO CHECK THE NEXT NODE

;DECNET MESSAGES CANNOT BE SENT TO THIS NODE. BUILD AN ACK MESSAGE TO BE SENT
;BACK TO THE OPR THAT SENT THE MESSAGE INDICATING THE REASON WHY THE MESSAGE
;CANNOT BE SENT TO THIS NODE. IF THIS IS THE FIRST NODE THAT A MESSAGE CANNOT
;BE SENT TO, THEN BUILD THE GALAXY MESSAGE HEADER AND DISPLAY BLOCK

SEPAL4:	DMOVEM	S1,G$ARG1		;SAVE THE NODE NUMBERS
	AOSN	P1			;THE FIRST NODE THAT CAN'T SEND MSG TO?
	$CALL	BLDHAD			;YES, BUILD MSG HDR AND DISPLAY BLOCK

;PLACE THE ERROR TEXT STRING IN THE MESSAGE TEXT BLOCK

	MOVE	S1,T2			;PICK UP THE ERROR BIT NUMBER
	MOVE	S2,T1			;PICK UP THE NODE NAME
	$CALL	MOVERR			;PLACE THE ERROR REASON IN THE TEXT BLK
	DMOVE	S1,G$ARG1		;RESTORE THE NODE NUMBERS

;PREPARE TO CHECK THE NEXT NODE

SEPAL5:	SOJE	P3,SEPAL7		;QUIT IF NO MORE NODES TO CHECK
SEPAL6:	SOJE	P4,S..NTI		;IF ZERO, THEN NODE TABLE INCONSISTENT
	ADDI	P2,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	JRST	SEPAL2			;GO CHECK THE NEXT NODE

;FINISHED, CHECK IF THE MESSAGE CANNOT BE SENT TO ANY NODES

SEPAL7:	SKIPGE	P1			;ANY NODES THE MSG CAN'T BE SENT TO?
	$RETT				;NO, SO RETURN TO THE IPCF MSG HANDLER

;THE MESSAGE CANNOT BE SENT TO ONE OR MORE NODES. INFORM ORION OF THIS.
;(NOTE: SEPALL ASSUMES THAT AC M CONTAINS THE ADDRESS OF THE ORIGINAL
;IPCF MESSAGE. ALSO, SEPALL ASSUMES THAT THE ORIGINAL MESSAGE CAME FROM
;AN OPR


	SETZ	P1,			;MAKE THE TEXT BLOCK ASCIZ
	IDPB	P1,BYTPTR		;ADD NULL TO END OF THE TEXT BLOCK

;FINISH BUILDING THE ACK MESSAGE. FIND AND STORE THE LENGTHS OF THE TEXT
;BLOCK AND THE MESSAGE.

	HRRZ	P1,BYTPTR		;PICK UP END OF TEXT BLOCK ADDRESS
	MOVE	P2,BLKADR		;PICK UP TEXT BLOCK ADDRESS
	SUBI	P1,-1(P2)		;FIND LENGTH-1 OF TEXT BLOCK
	STORE	P1,ARG.HD(P2),AR.LEN	;STORE LENGTH OF BLOCK IN TEXT BLK HDR
	MOVSS	P1			;TEXT BLOCK LENGTH,,0
	ADDM	P1,.MSTYP(MO)		;CALCULATE AND STORE MESSAGE LENGTH

	DMOVEM	S1,P3			;SAVE THE NODE NUMBERS
	$CALL	ORNACK			;SEND THE MESSAGE TO ORION
	DMOVE	S1,P3			;RESTORE THE NODE NUMBERS
	ADD	P3,P4			;# OF NODES THAT CAN RECEIVE THE MSG
	SKIPG	P3			;CAN AT LEASE ONE NODE RECEIVE MSG?	
	$RETF				;NO, INDICATE MSG NOT SENT TO ANY NODE
	$RETT				;MESSAGE SENT TO AT LEAST ONE NODE
	SUBTTL	BLDHAD - BUILD THE HEADER AND DISPLAY BLOCKS FOR ACK MESSAGE

;BLDHAD is called when an IPCF message processing routine that is processing
;a message to be sent to more than one remote node detects that the message
;cannot be sent to a remote node. This routine obtains a page and then builds
;the GALAXY message header, display (.WTTYP) block, and header word of the 
;text (.WTTXT) block of the ACK (.OMACK) message that will be sent back to
;the OPR that sent the original message indicating the reason the message
;could not be delivered to all the nodes.
;
;Call is: M/Address of the IPCF Message
;Returns: MO/Address of the ACK (.OMACK) message
;         BYTPTR/Byte Pointer to text field of text block
;         BLKADR/Address of text block

BLDHAD:	

;OBTAIN THE PAGE FOR THE ACK MESSAGE AND BUILD THE MESSAGE HEADER

	$CALL	BLDHDR			;GET MSG PAGE AND BUILD MSG HEADER
	MOVX	S1,MF.FAT		;INDICATE TO ORION THIS IS AN ERROR
	IORM	S1,.MSFLG(MO)		;INDICATE THIS IN THE MESSAGE FLAG WORD
	MOVX	S1,WT.NFO		;INDICATE NO FORMATTING BY ORION
	IORM	S1,.OFLAG(MO)		;INDICATE THIS IN THE MESSAGE
	AOS	.OARGC(MO)		;THIS MESSAGE HAS TWO ARGUMENT BLOCKS

;BUILD THE DISPLAY BLOCK

	MOVEI	S1,.OHDRS(MO)		;POINT TO THE DISPLAY BLOCK
	MOVEI	S2,.WTTYP		;PICK UP DISPLAY BLOCK CODE
	STORE	S2,ARG.HD(S1),AR.TYP	;PLACE IN THE DISPLAY BLOCK HEADER

	MOVEI	S2,ARG.DA(S1)		;POINT TO THE DATA FIELD
	HRLI	S2,(POINT 7,)		;MAKE INTO A POINTER
	MOVEM	S2,BYTPTR		;REMEMBER THE POINTER

	MOVEI	S2,[ASCIZ/Cannot send to the following:/]
	$TEXT(DEPBYT,<^T/0(S2)/^A>)	;PLACE TEXT IN DISPLAY BLOCK

;CALCULATE LENGTH OF MESSAGE UP TO THIS POINT AND LENGTH OF THE DISPLAY BLOCK.
;PLACE BOTH IN THE MESSAGE

	HRRZ	S2,BYTPTR		;PICK UP ADDRESS OF END OF DISPLAY BLK
	AOS	S2			;BUMP BY ONE FOR LENGTH CALCULATION
	ANDI	S2,777			;GET RID OF PAGE # TO FIND MSG LENGTH
	STORE	S2,.MSTYP(MO),MS.CNT	;PLACE MSG LENGTH IN MESSAGE HEADER

	SUBI	S2,.OHDRS		;SUBTRACT MESSAGE HEADER LENGTH
	STORE	S2,ARG.HD+.OHDRS(MO),AR.LEN ;SAVE DISPLAY BLOCK LENGTH
	ADD	S1,S2			;POINT TO THE TEXT (.WTTXT) BLOCK

;BUILD THE HEADER TYPE OF THE TEXT BLOCK

	MOVEI	S2,.WTTXT		;PICK UP TEXT BLOCK CODE
	STORE	S2,ARG.HD(S1),AR.TYP	;PLACE IN THE TEXT BLOCK HEADER WORD
	MOVEM	S1,BLKADR		;SAVE ADDRESS OF TEXT BLOCK
	AOS	S1			;POINT TO TEXT BLOCK DATA FIELD
	HRLI	S1,(POINT 7,)		;MAKE INTO A BYTE POINTER
	MOVEM	S1,BYTPTR		;SAVE THE POINTER
	$RET				;RETURN TO THE CALLER
	SUBTTL	MOVERR - PLACE ERROR REASON IN TEXT BLOCK

;MOVERR is called as part of the error handling when a message from an
;OPR cannot be sent to a remote node. This routine places the reason why
;a message cannot be sent into the text (.WTTXT) block of the ACK (.OMACK)
;message that will be sent to the OPR.
;
;Call is: S1/Error bit position from node table entry status word (.NNSTA)
;         S2/Node name
;Returns: The error text string has been placed into the text block

MOVERR:	$SAVE	<P1>			;SAVE THIS AC
	MOVE	P1,S2			;SAVE THE NODE NAME
	JFFO	S1,.+1			;PICK UP DISPLACEMENT INTO ERROR TABLE
	CAIL	S2,NNLOW		;CHECK FOR LEGAL ERROR
	CAILE	S2,NNHIGH		;CHECK FOR LEGAL ERROR
	SETZ	S2,			;UNKNOWN ERROR

	$TEXT	(DEPBYT,<          Node ^N6L/P1/^T/@REASON(S2)/>)	;[6001]
	$RET				;RETURN TO THE CALLER
	SUBTTL	QUEMSG - SEND OR QUEUE A MESSAGE

;QUEMSG is called to place a processed IPCF message on the message queues
;of those nodes the message is to be sent to. If any of the nodes are
;available to send a message, then the first message of those nodes'
;message queues are placed in those nodes' sender buffers and those nodes
;are interrupted informing them that a message is available to send.
;However, if a node the message is to
;be sent to has an empty message queue and it is available to send a 
;message, then the message is not placed on that node's message queue but
;rather is placed in the node's message buffer. The node's sender is then
;interrupted to inform it that there is a  message available to send.
;
;Call is: S1/Number of nodes to send the message directly to
;         S2/Flags,,Number of nodes to queue the message on the msg queues
;            where the flags are:
;            MQ%IBH	message is response to an in behalf of message
;            MQ%REM	message has an entry in the remote queue
;            MQ%EXE	message is from the EXEC
;         M/ Address of the processed IPCF message
;         NODSEN: Table containing the node table entry addresses of
;                 those nodes to send the message directly to
;         NODMQ:  Table containing the node table entry addresses of
;                 those nodes to queue the message to
;
;Returns: The message has been queued or sent

QUEMSG:	$SAVE	<P1,P2,P3,P4,T1>	;SAVE THESE AC

;FIRST CHECK IF A MESSAGE QUEUE ENTRY MUST BE BUILT

	MOVE	P4,S1			;SAVE # OF NODES TO SEND DIRECTLY TO
	HRRZ	S1,S2			;PICK UP NUMBER OF NODES TO QUEUE TO
	HLLZS	T1,S2			;ISOLATE AND SAVE THE FLAG BITS 
	SKIPG	P1,S1			;ANY NODES TO QUEUE TO?
	JRST	QUEMS3			;NO, GO CHECK FOR DIRECT SENDS

;BUILD THE MESSAGE QUEUE ENTRY

	$CALL	BLDMQE			;BUILD THE MESSAGE QUEUE ENTRY
	MOVE	P2,S1			;SAVE THE ADDRESS OF THE MQE
	MOVEI	P3,NODMQ		;PICK UP ADR OF NODE ENTRY ADDRESSES

;PLACE THE MESSAGE QUEUE ENTRY ON THE CURRENT NODE'S MESSAGE QUEUE

QUEMS2:	MOVE	S1,P2			;PICK UP ADDRESS OF THE LINK LIST WORD
	MOVE	SEN,0(P3)		;PICK UP NODE TABLE ENTRY ADDRESS
	MOVE	SEN,.NNSBA(SEN)		;PICK UP SENDER BLOCK ADR FOR THIS NODE
	$CALL	ADDMQE			;ADD MQE TO THIS NODE'S MESSAGE QUEUE

;CHECK IF THE SENDER IS AVAILABLE TO SEND A MESSAGE

	SKIPGE	.SNFRE(SEN)		;SENDER AVAILABLE TO SEND A MESSAGE?
	$CALL	SENMSG			;YES, GIVE IT A MESSAGE

;PLACE THE MESSAGE QUEUE ENTRY ON THE NEXT NODE'S MESSAGE QUEUE

	AOS	P2			;ADDRESS OF THE NEXT LINK LIST WORD
	AOS	P3			;ADDRESS OF THE NEXT NODE TABLE ENTRY
	SOJG	P1,QUEMS2		;ADD THE MQE TO NEXT NODE'S MSG QUEUE

;CHECK IF THERE ARE ANY NODES THAT THE MESSAGE CAN BE DIRECTLY SENT TO
;WITHOUT HAVING TO PUT THE MESSAGE ON THE MESSAGE QUEUE

QUEMS3:	SKIPG	P4			;ANY NODES SEND MESSAGE DIRECTLY TO?
	JRST	QUEMS6			;NO, SO ARE FINISHED

;SENDER READY FOR A MESSAGE AND ITS MESSAGE QUEUE IS EMPTY. TRANSFER THE
;MESSAGE FROM THE IPCF BUFFER TO THE SENDER'S MESSAGE BUFFER AND SEND THE
;MESSAGE TO THE SENDER. IF THE MESSAGE IS NOT A RESPONSE TO AN IN BEHALF OF
;MESSAGE, THEN CREATE A TIMER LIST ENTRY.

	MOVEI	P1,NODSEN		;PICK UP ADDRESS OF NODE TABLE ENTRIES
QUEMS4:	MOVE	SEN,0(P1)		;PICK UP NODE TABLE ENTRY ADDRESS
	JUMPL	T1,QUEMS5		;RESPONSE TO AN IBH MESSAGE?
	MOVE	S1,SEN			;NO, PICK UP NODE TABLE ENTRY ADDRESS
	MOVE	S2,M			;PICK UP THE MESSAGE ADDRESS
	$CALL	SETTIM			;SET THE TIMER FOR THIS MESSAGE
QUEMS5:	MOVE	SEN,.NNSBA(SEN)		;PICK UP SENDER BLOCK ADDRESS
	MOVE	S1,M			;ADDRESS OF WHERE MSG IS LOCATED NOW
	MOVE	S2,.SNMSG(SEN)		;ADDRESS OF WHERE THE MSG IS TO GO
	$CALL	XFRMSG			;TRANSFER THE MESSAGE TO MSG BUFFER
	$CALL	SENMS5			;PASS THE MESSAGE TO THE SENDER

	AOS	P1			;POINT TO THE NEXT NODE TABLE ENTRY ADR
	SOJG	P4,QUEMS4		;SEND THE MESSAGE TO THE NEXT NODE

QUEMS6:	$RET				;FINISHED, RETURN TO THE CALLER
SUBTTL	ORNACK - SEND AN ERROR ACK TO ORION

;ORNACK is called as part of cleaning up the message queue when a node has
;crashed or a listener or sender has crashed.
;
;Returns true:  The message was sent successfully
;Returns false: The message was not sent

;SEND THE MESSAGE TO ORION
;(NOTE: THE .MSCOD WORD OF THE MESSAGE THAT IS SENT BY AN OPR CONTAINS
;THE PID OF THAT OPR.)

ORNACK:	MOVE	S1,MO			;PICK UP THE MESSAGE ADDRESS
	MOVEI	S2,PAGSIZ		;PICK UP THE MESSAGE SIZE
	$CALL	SNDORN			;SENDS THE ACK MESSAGE TO ORION
	$RETIT				;SUCCESS, C%SEND RELEASED THE MSG PAGE
	$CALL	RELPAG			;ERROR, RELEASE MSG PAGE 
	$RETF				;RETURN FALSE TO CALLER
SUBTTL	BLDACK	Build an ACK message

;BLDACK is called to build an ACK message (.OMACK) to be sent
;to ORION when an error has been encountered while processing
;an IPCF message from an OPR that was to be sent to a single remote node
;or as part of cleaning up after communication to a remote node has
;been lost.
;
;Call is: M/  Address of original message
;Returns: ACK message has been built
;         MO/Address of the ACK message

;OBTAIN THE PAGE TO BUILD THE ACK MESSAGE AND BUILD THE MESSAGE HEADER

;**;[6052]At BLDACK:+0L replace routine BLDACK with a new BLDACK  JCR  3/1/90
BLDACK:	$CALL	BLDHDR			;[6052]Get msg page and build header
	MOVE	S1,G$ERR		;[6052]Pick up the error number
	MOVE	S2,STSTBL(S1)		;[6052]Pick up the flag word
	MOVEM	S2,.MSFLG(MO)		;[6052]Place in the message header
	MOVEI	S2,ARG.DA+.OHDRS(MO)	;[6052]Get place to store the text
	HRLI	S2,(POINT 7,0)		;[6052]Make a pointer
	MOVEM	S2,BYTPTR		;[6052]Save the pointer
	$TEXT	(BLDA.1,<^I/@TXTTBL(S1)/^0>) ;[6052]Place text in the message
	MOVE	S1,BYTPTR		;[6052]Pick up the updated byte pointer

	HRRZS	S1			;[6052]Get the ending address
	AOS	S1			;[6052]Bump it by 1
	ANDI	S1,777			;[6052]Get the message length
	STORE	S1,.MSTYP(MO),MS.CNT	;[6052]Save count in the message
	SUBI	S1,.OHDRS		;[6052]Get the size of the block
	STORE	S1,ARG.HD+.OHDRS(MO),AR.LEN ;[6052]Save the argument length
	MOVX	S1,.WTTYP		;[6052]Get the text code
	STORE	S1,ARG.HD+.OHDRS(MO),AR.TYP ;[6052]Save as the argument type

	SKIPL	REMORG			;[6052]Request originate remotely?
	$RET				;[6052]No, so return now
	LOAD	S1,.MSTYP(MO),MS.CNT	;[6052]Pick up the message size
	ADD	S1,MO			;[6052]Point to the message end
	MOVE	S2,[.NDESZ,,.WTNHD]	;[6052]Pick up the block header word
	MOVEM	S2,ARG.HD(S1)		;[6052]Place in the message
	MOVE	S2,NODNAM		;[6052]Pick up local node name
	MOVEM	S2,ARG.DA(S1)		;[6052]Place in the node block
	HRLZI	S1,.NDESZ		;[6052]Pick up cluster node block size
	ADDM	S1,.MSTYP(MO)		;[6052]Update message length
	MOVX	S1,MF.NEB		;[6052]Pick up remote origin bit
	IORM	S1,.MSFLG(MO)		;[6052]Set in the message
	AOS	.OARGC(MO)		;[6052]Update the argument count
	$RET				;[6052]Return to the caller
	
BLDA.1: IDPB	S1,BYTPTR		;[6052]Save the character
	$RETT				;[6052]Return to the caller
	SUBTTL	TXTMOV - MOVE TEXT FROM ONE LOCATION TO ANOTHER

;TXTMOV moves ASCIZ text from one location to another.
;(Note: This routine does not copy to the new location the null byte.)

;Call is: S1/Address of the destination
;	  S2/Address of the source
;Returns: S1/Updated byte pointer


TXTMOV:	HRLI	S2,(POINT 7,0)		;MAKE A BYTE POINTER
	HRLI	S1,(POINT 7,0)		;BYTE POINTER FOR DESTINATION
	MOVEM	S2,BYTPTR		;SAVE THE SOURCE POINTER
TXTM.1:	ILDB	S2,BYTPTR		;GET FIRST BYTE OF DATA
	JUMPE	S2,TXTM.2		;NULL BYTE ..EXIT
	IDPB	S2,S1			;SAVE THE BYTE
	JRST	TXTM.1			;GET NEXT BYTE
TXTM.2:	IDPB	S2,S1			;SAVE THE NULL FOR ASCIZ
	$RET				;RETURN
	SUBTTL	BLDHDR - BUILD THE MESSAGE HEADER FOR ACK MESSAGE

;BLDHDR obtains a page and builds the message header for an ACK 
;message to be sent to the OPR whose request has resulted in an error.
;
;Call is: M/Address of the IPCF message
;Returns: MO/Address of the ACK message

BLDHDR:	$CALL	GETPAG			;GET THE PAGE FOR THE MESSAGE
	MOVEI	S1,.OMACK		;PICK UP ACK MESSAGE CODE
	STORE	S1,.MSTYP(MO),MS.TYP	;STORE IN MESSAGE HEADER
	MOVE	S1,.MSCOD(M)		;PICK UP MESSAGE CODE
	MOVEM	S1,.MSCOD(MO)		;PLACE IN THE MESSAGE
	MOVX	S1,WT.SJI		;SUPPRESS JOB INFORMATON ON DISPLAY
	MOVEM	S1,.OFLAG(MO)		;PLACE IN MESSAGE HEADER
	AOS	.OARGC(MO)		;BUMP ARGUMENT COUNT TO ONE
	$RET				;RETURN TO THE CALLER	
	SUBTTL	EREXEC - SEND AN ERROR MESSAGE TO THE EXEC

;[6013]EREXEC builds a text message to be sent to the EXEC after NEBULA detects
;[6013]an error in processing the original message or if communication to the
;[6013]node the original message was to be sent to has been lost.
;[6013]
;[6013]Call is: M/Address of the original IPCF message
;[6013]         G$ERR/Error table offset
;[6013]         G$SND/The EXEC's PID
;[6013]Returns: The error message has been sent to the EXEC


;[6013]BUILD THE COMMON HEADER PARTS AND DETERMINE THE EXEC MESSAGE TYPE

EREXEC:	$SAVE	<P1>			;[6013]SAVE THIS AC
	MOVEI	S1,.OHDRS		;[6013]PICK UP THE HEADER SIZE
	STORE	S1,G$MSG+.MSTYP,MS.CNT	;[6013]]PLACE IN THE MESSAGE HEADER
	SETZM	G$MSG+.OARGC		;[6013]NO ARGUMENTS YET
	MOVE	S1,.MSCOD(M)		;[6014]PICK UP THE EXEC'S UNIQUE CODE
	MOVEM	S1,G$MSG+.MSCOD		;[6014]STORE IN THE MESSAGE
	LOAD	S1,.MSTYP(M),MS.TYP	;[6013]PICK UP THE EXEC MESSAGE CODE
	CAIE	S1,.QOLIS		;[6013]A LIST MESSAGE?
	JRST	EREX.1			;[6013]NO, THEN MUST BE A KILL MESSAGE

;[6013]BUILD A DISPLAY BLOCK AND MESSAGE TYPE .OMACS

	SETZM	G$MSG+.MSFLG		;[6014]ZERO OUT THE FLAG WORD
	MOVX	S1,WT.SJI+WT.NFO	;[6014]SUPPRESS JOB INFO
	MOVEM	S1,G$MSG+.OFLAG		;[6014]PLACE IN THE MESSAGE
	MOVEI	S1,G$MSG+.OHDRS+ARG.DA	;[6013]WHERE TO PLACE THE HEADER
	HRLI	S1,ERRHDR		;[6013]ADDRESS OF THE SOURCE TEXT
	MOVEI	S2,G$MSG+.OHDRS+ERRLEN-1;[6013]ADDRESS OF END OF THE TEXT
	BLT	S1,0(S2)		;[6013]COPY THE TEXT INTO THE MESSAGE
	MOVE	S1,[ERRLEN+1,,.ORDSP]	;[6013]PICK UP THE BLOCK HEADER WORD
	MOVEM	S1,G$MSG+.OHDRS		;[6013]PLACE IN THE MESSAGE
	MOVEI	S1,.OMACS		;[6013]PICK UP THE MESSAGE TYPE
	STORE	S1,G$MSG+.MSTYP,MS.TYP	;[6013]PLACE IN THE MESSAGE HEADER
	MOVSI	S1,ERRLEN+1		;[6013]PICK UP THE LENGTH OF DISPLAY BLK
	ADDM	S1,G$MSG+.MSTYP		;[6013]ADD TO THE MESSAGE LENGTH
	AOS	G$MSG+.OARGC		;[6013]INCREMENT THE ARGUMENT COUNT
	MOVEI	P1,G$MSG+.OHDRS+ERRLEN+1;[6013]ADDRESS OF THE TEXT BLOCK
	JRST	EREX.2			;[6013]JOIN THE COMMON CODE

;[6013]INDICATE A TEXT MESSAGE

EREX.1:	LOAD	S1,.MSFLG(M),MF.ACK	;[6013]PICK UP THE ACK CODE BIT
	SKIPN	S1			;[6013]WAS AN ACK REQUESTED?
	$RETT				;[6013]NO, RETURN NOW
	MOVEI	S1,.OMTXT		;[6013]PICK UP THE TEXT MESSAGE CODE
	STORE	S1,G$MSG+.MSTYP,MS.TYP	;[6013]STORE IN THE HEADER
	MOVEI	P1,G$MSG+.OHDRS		;[6013]POINT TO THE TEXT BLOCK
	MOVE	S1,G$ERR		;[6014]PICK UP THE ERROR OFFSET
	MOVE	S1,STSTBL(S1)		;[6014]PICK UP THE FLAG WORD
	MOVEM	S1,G$MSG+.MSFLG		;[6014]PLACE IN THE MESSAGE

;[6013]BUILD THE TEXT BLOCK

EREX.2:	SETZM	BYTPTR			;[6013]INDICATE NO TEXT YET
	SETZM	G$MSG+MSGLN-1		;[6013]INDICATE BUFFER NOT EXHAUSTED
	MOVEM	P1,STABLK		;[6013]SAVE THE BLOCK ADDRESS
	MOVE	S1,G$ERR		;[6013]PICK UP THE ERROR OFFSET
	$TEXT	(DEPCHR,<^I/@TXTTBL(S1)/^A>) ;[6013]PLACE TEXT IN THE TEXT BLOCK
	SETZ	S1,			;[6013]PICK UP A NULL
	$CALL	DEPCHR			;[6013]AND PLACE IN THE MESSAGE

;[6013]FINISH BUILDING THE HEADER
	
	AOS	G$MSG+.OARGC		;[6013]INCREMENT THE ARGUMENT COUNT
	SETZM	G$MSG+.OFLAG		;[6013]ZERO THE OFLAG WORD
	HRRZ	S1,BYTPTR		;[6013]PICK UP THE MESSAGE END ADDRESS
	SUBI	S1,-1(P1)		;[6013]FIND THE TEXT BLOCK LENGTH
	HRLZS	S1			;[6013]PLACE LENGTH IN EXPECTED PLACE
	ADDM	S1,G$MSG+.MSTYP		;[6013]ADD TO THE MESSAGE LENGTH
	HRRI	S1,.CMTXT		;[6013]PICK UP THE BLOCK TYPE
	MOVEM	S1,0(P1)		;[6013]PLACE IN THE TEXT BLOCK HEADER

;[6013]BUILD THE SAB AND SEND THE MESSAGE

	LOAD	S1,G$MSG+.MSTYP,MS.CNT	;[6013]PICK UP THE MESSAGE LENGTH
	MOVEM	S1,SAB+SAB.LN		;[6013]PLACE IN THE SAB
	MOVEI	S1,G$MSG		;[6013]PICK UP THE MESSAGE ADDRESS
	MOVEM	S1,SAB+SAB.MS		;[6013]PLACE IN THE SAB
	MOVE	S1,G$SND		;[6013]PICK UP THE EXEC'S PID
	MOVEM	S1,SAB+SAB.PD		;[6013]PLACE IN THE SAB
	SETZM	SAB+SAB.SI		;[6032]ZERO OUT SPECIAL INDEX
	PJRST	N$SEND			;[6013]SEND THE MESSAGE AND RETURN

ERRHDR:	ASCIZ/NEBULA error message/
ERRLEN==.-ERRHDR
	SUBTTL	DEPCHR - INSERT AN ASCII CHARACTER IN AN EXEC ERROR MESSAGE

;[6013]DEPCHR is called to deposit an ASCII character in a error message being
;[6013]built to send to the EXEC sender of the original message.
;[6013]
;[6013]Call is: S1/Character to be deposited right justified
;[6013]Returns: The character has been deposited in the message

DEPCHR:	SKIPE	BYTPTR			;[6013]STRING STARTED YET?
	JRST	DEPC.1			;[6013]YES, DEPOSIT THE CHARACTER
	MOVE	S2,STABLK		;[6013]]PICK UP .CMTXT BLOCK ADDRESS
	AOS	S2			;[6013]ADDRESS OF THE DATA FIELD
	HRLI	S2,(POINT 7,)		;[6013]MAKE INTO A POINTER
	MOVEM	S2,BYTPTR		;[6013]SAVE
DEPC.1:	SKIPN	G$MSG+MSGLN-1		;[6013]REACHED THE END OF THE BUFFER?
	IDPB	S1,BYTPTR		;[6013]NO, INCLUDE THIS CHARACTER
	$RETT				;[6013]RETURN FOR THE NEXT ONE
	SUBTTL	SETTIM - SETUP A TIMER FOR DECNET MESSAGE

;SETTIM creates a timer list entry and sets up a timer for a message that
;is being sent to a remote node and which is not a response to an in behalf of
;request. 
;
;Call is: S1/ Node table entry address of the node that the message is 
;         being sent to
;         S2/Address of the message being sent to the remote node
;Returns: Timer list entry and timer have been created
;Crashes:      Node table inconsistency detected

SETTIM:	$SAVE	<P1,P2>			;SAVE THESE AC

;FIRST CREATE A TIMER LIST ENTRY

	DMOVEM	S1,P1			;SAVE NODE TABLE ENTRY AND MSG ADR
	MOVE	S1,.NTIML(P1)		;PICK UP TIMER LIST INDEX
	$CALL	L%LAST			;POSITION TO END OF THE TIMER LIST
	MOVE	S1,.NTIML(P1)		;PICK UP TIMER LIST INDEX AGAIN
	MOVEI	S2,.TMSIZ		;PICK UP SIZE OF TIMER LIST ENTRY
	$CALL	L%CENT			;CREATE A TIMER LIST ENTRY
	JUMPF	S..NTI			;NO TIMER LIST, NODE TABLE INCONSISTENT
	MOVE	P2,.MSCOD(P2)		;PICK UP THE ACK CODE
	MOVEM	P2,.TMACK(S2)		;SAVE THE MESSAGE ACK CODE
	AOS	S1,TIMCTR		;MAKE UDT UNIQUE
	ADD	S1,MSGTIM		;ADD TIME OF THIS IPCF SCHEDULING PASS
	MOVEM	S1,.TMUDT(S2)		;SAVE THE UDT IN THE TIMER LIST ENTRY

;START THE TIMER FOR THIS MESSAGE

	MOVEI	S2,TIMBLK		;PICK UP ADDRESS OF TIME EVENT ENTRY
	MOVEM	S1,.TITIM(S2)		;PLACE WAKEUP TIME IN TIME EVENT ENTRY
	MOVEI	S1,.TIMDT		;WAKEUP AT A SPECIFIC TIME
	MOVEM	S1,.TIFNC(S2)		;SAVE THE FUNCTION CODE IN T.E. ENTRY
	MOVEI	S1,PROTIM		;PICK UP WAKEUP PROCESSING ROUTINE ADR
	MOVEM	S1,.TIMPC(S2)		;SAVE ROUTINE ADDRESS IN T.E. ENTRY
	MOVE	S1,.NNNAM(P1)		;PICK UP NODE NAME MESSAGE IS GOING TO
	MOVEM	S1,.TIDAT(S2)		;SAVE NODE NAME IN TIME EVENT ENTRY
	MOVEI	S1,<.TIDAT+1>		;PICK UP SIZE OF ARGUMENT BLOCK
	$CALL	I%TIMR			;CREATE THE TIMER EVENT ENTRY
	$RET				;RETURN TO THE CALLER
	SUBTTL	CLRTIM - CLEAR A TIMER UPON MESSAGE RESPONSE BEING RECEIVED

;CLRTIM is called when a response from a message to a remote node is
;received. This routine clears the timer that was set when the message
;was sent to the remote node.
;
;Call is: S1/Node table entry address of node message was
;         S2/The ACK code (.MSCOD) of the message sent to the remote node
;Returns: Timer for the message has been cleared
;Crashes: Node table inconsistency detected

CLRTIM:	$SAVE	<P1>			;SAVE THIS AC
	MOVE	P1,S2			;SAVE THE ACK CODE

;PICK UP THE TIMER LIST ENTRY

	MOVE	S1,.NTIML(S1)		;PICK UP THE TIMER LIST INDEX
	$CALL	L%FIRST			;PICK UP THE FIRST T.L. ENTRY
	$RETIF				;[6011]RETURN IF NO T.L. ENTRY

CLRTI2:	CAMN	P1,.TMACK(S2)		;IS THIS THE TIMER LIST ENTRY?
	JRST	CLRTI3			;YES, CLEAR THE TIMER
	$CALL	L%NEXT			;NO, PICK UP THE NEXT ENTRY
	$RETIF				;[6011]RETURN IF NO T.L. ENTRY
	JRST	CLRTI2			;CHECK THE NEXT ENTRY

;THE TIMER LIST ENTRY HAS BEEN FOUND. CLEAR THE TIMER FOR THIS ENTRY.

CLRTI3:	MOVE	P1,.TMUDT(S2)		;REMEMBER THE TIME TO SET OFF TIMER
	$CALL	L%DENT			;DELETE THE TIMER LIST ENTRY
	MOVE	S1,P1			;PICK UP THE TIME TO SET OFF TIMER
	MOVEI	S2,PROTIM		;[6016]PICK UP ROUTINE ADDRESS
	$CALL	REMTIM			;CLEAR THE TIMER
	$RET				;RETURN TO THE CALLER
	SUBTTL	REMTIM - CLEAR A TIMER

;REMTIM is called to clear a timer from a message sent to a node when
;the response to that message has been received from that node or as
;part of cleanup when communication to a node has been lost.
;REMTIM is also called to clear a timer that was set as a result of an INFO%
;JSYS error.
;The function to clear the timer (.TIMDD) will actually delete all timer
;event queue entries that have the same time as that specified in the clear
;timer request. If a routine address is specified, then only those timer
;event queue entries corresponding to that routine will be deleted.
;Call is: S1/ The time the timer is set to go off
;	  S2/ Routine address that is called when the timer goes off
;Returns: The timer has been cleared

REMTIM:	$SAVE	<P1>			;[6016]SAVE THIS AC
	MOVE	P1,S2			;[6016]SAVE THE ROUTINE ADDRESS
	MOVEI	S2,TIMBLK		;PICK UP ADDRESS OF TIMER EVENT BLOCK
	MOVEM	S1,.TITIM(S2)		;PLACE TIME IN TIMER EVENT BLOCK
	MOVEI	S1,.TIMDD		;PICK UP FUNCTION CODE TO CLEAR TIMER
	STORE	S1,.TIFNC(S2),RHMASK	;PLACE FUNCTION CODE IN TIMER EVENT BLK
	MOVEM	P1,.TIMPC(S2)		;[6016]PLACE ROUTINE ADR IN TIMER BLOCK
	MOVEI	S1,.TIMPC+1(S2)		;[6016]ASSUME ROUTINE ADDRESS SPECIFIED
	SKIPN	P1			;[6016]WAS A ROUTINE ADDRESS SPECIFIED?
	MOVEI	S1,.TITIM+1		;[6016]NO, SO PICK UP CORRECT BLOCK SIZE
	$CALL	I%TIMR			;CLEAR THE TIMER
	$RET				;RETURN TO THE CALLER
	SUBTTL	PROTIM - TIMER HAS GONE OFF, COMMUNICATION LOST TO REMOTE NODE

;PROTIM is called from I%SLP when a timer has gone off. This indicates that
;a message was sent to a remote node and no response to that message was
;received in TIMOUT amount of time. The assumption is made that communication
;to the remote node has been lost. The node data base to that node is cleared.
;If it is determined that the remote node can receive messages (i.e., it has
;a monitor of release 7 or later and has DECnet enabled), then the listener
;and sender to that node are restarted.
;
;Call is: S1/Length of timer event block from timer event queue
;         S2/Address of timer event block from timer event queue
;Returns: Remote node's data base has been reset and restarted
;         if remote node is still capable of receiving DECnet messages
;Crashes: The node table is inconsistent

PROTIM:	$SAVE	<P1>			;SAVE THIS AC

;DETERMINE THE NODE THAT COMMUNICATION HAS BEEN LOST TO

	MOVE	P1,.TIDAT(S2)		;PICK UP THE NODE NAME
	MOVE	S1,P1			;PICK UP NODE NAME WHERE EXPECTED
	$CALL	SNAMNT			;FIND NODE TABLE ENTRY
	JUMPF	S..NTI			;NODE TABLE INCONSISTENCY

;RESET THE REMOTE NODE'S DATA BASE AND RESTART THE LISTENER AND SENDER
;TO THAT NODE IF IT IS STILL CAPABLE OF RECEIVING DECNET MESSAGES FROM
;THIS NODE.

	MOVE	S1,S2			;PICK UP NODE TABLE ENTRY ADDRESS
	$CALL	KASNOD			;RESET NODE TABLE DATA BASE
	JUMPF	PROTI2			;CAN'T SEND MESSAGES TO THIS NODE

	$WTOJ	(<Timeout to node ^N/P1/ detected>,<Forks have been restarted>,,<$WTFLG(WT.SJI)>)
	$RET				;RETURN TO THE CALLER

PROTI2:	$WTOJ	(<Timeout to node ^N/P1/ detected>,<Forks cannot be restarted>,,<$WTFLG(WT.SJI)>)
	$RET				;RETURN TO THE CALLER
	SUBTTL	SNDMSG - SEND MESSAGES TO AVAILABLE SENDERS

;SNDMSG is called during NEBULA's scheduling pass if a sender has indicated
;that it  is available  to send a  message.  This routine checks for
;senders that are available to send DECnet messages to their listeners.
;It first checks if the node the sender is sending to is able to receive
;messages. If it is, then a check is made to determine if the sender is
;available to send a message. If it is, then a check is made to determine
;if the sender's message queue has a message. If there is a message, then
;the message is moved from the message queue to the sender's message buffer
;and the sender is notified that there is a message available for it to send.
;
;Call is: No arguments
;Returns: After all the senders have been notified of any available
;         messages

SNDMSG:	$SAVE	<P1,P2>			;SAVE THESE AC
	SETZM	SREADY			;RESET MESSAGE AVAILABLE FLAG
	SETOM	NBSCHD			;FORCE A SCHEDULING PASS

;SET UP THE NODE TABLE SEARCH FOR ELIGIBLE SENDERS TO SEND A MESSAGE

	MOVEI	P1,NODTBL		;PICK UP THE NODE TABLE ADDRESS
	MOVEI	P2,MAXNOD		;PICK UP MAX # OF NODE TABLE ENTRIES

;CHECK IF A NODE IS AVAILABLE TO RECEIVE MESSAGES, THEN IF A SENDER IS
;AVAILABLE TO SEND A MESSAGE AND THEN IF THERE ARE ANY MESSAGES TO SEND

SNDMS2:	SKIPL	.NNSTA(P1)		;O.K. TO SEND MESSAGES TO THIS NODE?
	JRST	SNDMS3			;NO, CHECK THE NEXT NODE

	MOVE	SEN,.NNSBA(P1)		;PICK UP THE SENDER BLOCK ADDRESS
	SKIPL	.SNFRE(SEN)		;THIS SENDER AVAILABLE TO SEND A MSG?
	JRST	SNDMS3			;NO, CHECK THE NEXT NODE

	SKIPE	.SNHWD(SEN)		;ANY MESSAGES FOR THIS SENDER?
	$CALL	SENMSG			;YES, NOTIFY THE SENDER

;CHECK FOR ANY MORE NODES TO SEND A MESSAGE TO

SNDMS3:	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	SOJG	P2,SNDMS2		;GO CHECK THE NEXT NODE
	$RET				;RETURN TO THE CALLER
  
	SUBTTL	SENMSG - NOTIFY A SENDER OF A MESSAGE AVAILABLE

;SENMSG is called when a sender is available to send a message and there
;is a message in its message queue. The message is transferred from the
;message queue to the sender's message buffer and the sender is interrupted.
;
;Call is:       SEN/Address of the sender block
;Returns true:  Message is placed in the sender message buffer and the sender
;               is interrupted
;Returns false: Message queue is empty
;Crashes:       If the sender cannot be interrupted

SENMSG:	$SAVE	<P1,P2>			;SAVE THESE AC

;TRANSFER THE MESSAGE FROM THE SENDER'S MESSAGE QUEUE TO THE SENDER'S
;MESSAGE BUFFER

	SKIPN	P1,.SNHWD(SEN)		;PICK UP MESSAGE QUEUE HEADER WORD
	JRST	SENMS6			;THE MSG QUEUE IS EMPTY, RETURN FALSE
	LOAD	P2,.MQBLK(P1),MQ.OFF	;PICK UP THE OFFSET TO INVARIANT BLOCK
	ADD	P2,P1			;POINT TO THE INVARIANT BLOCK
	LOAD	S1,.MQNOM(P2),MQ.ADR	;PICK UP ADDRESS OF THE MESSAGE
	MOVE	S2,.SNMSG(SEN)		;PICK UP ADDRESS OF THE MESSAGE BUFFER
	$CALL	XFRMSG			;MOVE THE MESSAGE TO THE MESSAGE BUFFER

;DELETE THE MESSAGE ENTRY FROM THE MESSAGE QUEUE

	LOAD	S1,.MQBLK(P1),MQ.NEA	;PICK UP THE ADDRESS OF THE NEXT MQE
	JUMPG	S1,SENMS2		;IS THIS THE LAST MESSAGE IN THE QUEUE?
	SETZM	.SNHWD(SEN)		;YES, ZERO THE LINK LIST HEADER WORD
	SETZM	.SNTWD(SEN)		;AND ZERO THE LINK LIST TRAILER WORD
	SKIPA				;DON'T UPDATE THE LINK LIST HEADER WORD
SENMS2:	MOVEM	S1,.SNHWD(SEN)		;PLACE NEW FIRST ENTRY IN L.L. HDR WORD
	SOS	.SNUSM(SEN)		;UPDATE NUMBER OF MESSAGES IN MSG QUEUE

;IF THE MESSAGE IS NOT A RESPONSE TO AN IN BEHALF OF MESSAGE, THEN CREATE
;A TIMER LIST ENTRY AND A TIMER FOR THE MESSAGE.

	SKIPGE	S2,.MQNOM(P2)		;[6017]RESPONSE TO AN IN BEHALF OF MSG?
	JRST	SENMS3			;YES, DON'T SET UP A TIMER
	MOVE	S1,.SNNTA(SEN)		;PICK UP THE NODE TABLE ENTRY ADDRESS
	HRRZS	S2			;ISOLATE THE MESSAGE ADDRESS
	$CALL	SETTIM			;CREATE TIMER ENTRY AND START TIMER

;IF THE MESSAGE IS NOT TO BE SENT TO ANY ADDITIONAL NODES, THEN RETURN ITS
;MEMORY TO THE MEMORY MANAGER

SENMS3:	LOAD	S1,.MQNOM(P2),MQ.NUM	;NUMBER OF NODES LEFT TO THE MESSAGE TO
	SOJG	S1,SENMS4		;ANY NODES LEFT TO SEND THE MESSAGE TO?
	MOVE	S1,P2			;NO, PICK UP ADDRESS OF INVARIANT BLOCK
	$CALL	RELMQE			;AND RETURN THE MEQ TO THE MEMORY MGER
	SKIPA				;DON'T UPDATE THE NODE COUNT
SENMS4:	STORE	S1,.MQNOM(P2),MQ.NUM	;UPDATE THE NUMBER OF NODES TO SEND MSG

;INTERRUPT THE SENDER THAT A MESSAGE IS AVAILABLE IN ITS MESSAGE BUFFER
;
;SENMS5 IS AN ENTRY POINT WHEN PROCESSING A MESSAGE THAT CAN BE SENT DIRECTLY
;TO THE SENDER WITHOUT THE NEED TO PLACE THE MESSAGE ON THE SENDER'S MESSAGE
;QUEUE

SENMS5:	SETZM	.SNFRE(SEN)		;INDICATE THAT THIS SENDER IS NOW BUSY
	MOVE	S1,.SNHND(SEN)		;PICK UP THE SENDER'S HANDLE
	MOVX	S2,<1B0>		;PICK UP CHANNEL TO INTERRUPT SENDER ON
	IIC%				;INTERRUPT SENDER THAT MSG IS AVAILABLE
	 ERJMP	S..UII			;CRASH ON AN ERROR
	$RETT				;RETURN TRUE TO CALLER
SENMS6:	$RETF				;MESSAGE QUEUE IS EMPTY
Repeat 0,<
	SUBTTL	CONSTS - CONNECTION FAILURE STATUS

;CONSTS is called during the NEBULA scheduling pass if a sender
;has interrupted the top fork to indicate that it has been unable
;to complete a DECnet connection to its remote node. This routine
;will determine which sender has indicated that it has been 
;unsuccessful in completing a DECnet. It will then check if its
;failure to establish a DECnet connection should be reported to the
;operators. If its failure should be reported, then it sends a WTO
;message to ORION, otherwise, it does nothing.
;
;Call is:      No arguments
;Returns true: All the senders have been checked and any WTO messages
;              sent.

CONSTS:	$SAVE	<P1,P2>			;SAVE THESE AC
	SETZM	FCFFLG			;RESET FOR THE INTERRUPT HANDLER
	SETOM	NBSCHD			;FORCE ANOTHER SCHEDULING PASS

;SETUP THE SEARCH FOR THE SENDER THAT CANNOT OBTAIN A DECNET LINK

	MOVEI	P1,NODTBL		;PICK UP THE ADDRESS OF THE NODE TABLE
	MOVEI	P2,MAXNOD		;PICK UP MAXIMUM NUMBER OF REMOTE NODES
	MOVE	S2,RENNUM		;PICK UP NUMBER OF KNOWN REMOTE NODES

;CHECK THE SENDER OF EACH NODE. IF DECNET CONNECTION FAILURES TO THAT
;NODE ARE TO BE IGNORED, THEN GO CHECK THE NEXT NODE. OTHERWISE, CHECK
;IF THE SENDER OF THAT NODE HAS INDICATED THAT IT CANNOT OBTAIN A
;DECNET CONNECTION. IF IT HAS, THEN INFORM THE OPERATORS.

CONST2:	SKIPN	.NNNAM(P1)		;IS THIS NODE TABLE ENTRY IN USE?
	JRST	CONST4			;NO, GO CHECK THE NEXT ENTRY
	MOVE	S1,.NNSTA(P1)		;PICK UP THE NODE STATUS WORD
	TXNE	S1,NN%DCO		;CONNECTION FAILURES DISABLED?
	JRST	CONST3			;YES, CHECK THE NEXT NODE

	SKIPN	SEN,.NNSBA(P1)		;PICK UP THE SENDER BLOCK ADDRESS
	JRST	CONST3			;NO SENDER FOR THIS NODE
	SKIPL	.SNCOL(SEN)		;SENDER CAN'T OBTAIN A CONNECTION?
	JRST	CONST3			;NO, CHECK THE NEXT NODE
	SETZM	.SNCOL(SEN)		;YES, CLEAR THE CONNECT FAILURE FLAG
	$WTOJ(<NEBULA can't make a connection>,<Sender to node ^N/.NNNAM(P1)/ has not been able to obtain a DECnet connection>,,<$WTFLG(WT.SJI)>)

CONST3:	SOJE	S2,.RETT		;IF NO MORE NODES TO CHECK, THEN DONE
CONST4:	SOJE	P2,S..NTI		;NODE TABLE INCONSISTENCY
	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE ENTRY
	JRST	CONST2			;GO CHECK THE NEXT NODE'S SENDER

>; End of Repeat 0
	SUBTTL	GETMSG -PICK UP A MESSAGE FROM A LISTENER

;GETMSG is called by the scheduler as a result of a listener interrupting
;the top fork to indicate that it has a message from a remote node available
;to be picked up and acted upon by the top fork.
;
;Call is: No arguments
;Returns: The message has been picked up from the listener and acted on

GETMSG:	$SAVE	<P1,P2>			;SAVE THESE AC
	SETOM	NBSCHD			;FORCE ANOTHER SCHEDULING PASS
	SETZM	LREADY			;ZERO OUT THE MESSAGE AVAILABLE FLAG

;SET UP THE NODE TABLE TO SEARCH FOR LISTENERS WHICH HAVE A MESSAGE

	MOVEI	P1,NODTBL		;PICK UP THE NODE TABLE ADDRESS
	MOVEI	P2,MAXNOD		;PICK UP MAX # OF NODE TABLE ENTRIES

;CHECK IF THE REMOTE NODE IS CAPABLE OF SENDING MESSAGES. IF IT IS, THEN
;CHECK IF THE LISTENER ASSOCIATED WITH THAT NODE HAS A MESSAGE TO BE
;PICKED UP.

GETMS2:	SKIPL	.NNSTA(P1)		;O.K. TO RECEIVE MSG FROM THIS NODE?
	JRST	GETMS3			;NO, CHECK THE NEXT NODE

	MOVE	LIS,.NNLBA(P1)		;PICK UP THE LISTENER BLOCK ADDRESS
	SKIPE	.LSAVA(LIS)		;DOES THIS LISTENER HAVE A MESSAGE?
	JRST	GETMS3			;NO, CHECK THE NEXT NODE

;THE LISTENER HAS A MESSAGE TO BE PICKED UP. PICK UP THE MESSAGE AND PROCESS IT

	$CALL	PROMSG			;PICK UP AND PROCESS THE MESSAGE

;TELL THE LISTENER WE HAVE PICKED UP THE MESSAGE AND ARE READY FOR ANOTHER

	MOVE	S1,.LSHND(LIS)		;GET THE LISTENER'S FORK
	MOVX	S2,<1B2>		;WANT CHANNEL 0
	SETOM	.LSAVA(LIS)		;MESSAGE HAS BEEN PICKED UP
	IIC%				;TELL LISTENER WE'RE READY 
	 ERJMP	S..UII			;CRASH IF CAN'T INTERRUPT LISTENER

;CHECK FOR ANY MORE NODES THAT MAY HAVE A MESSAGE AVAILABLE

GETMS3:	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE TABLE ENTRY
	SOJG	P2,GETMS2		;GO CHECK THE NEXT NODE
	$RET				;FINISHED WITH THE LISTENERS
	SUBTTL	PROMSG - PROCESS DECNET LISTENER MESSAGES DISPATCHER

;PROMSG is called from GETMSG to process the message a listener has
;available. PROMSG dispatches to the appropriate message handler to
;process the message.
;
;Call is: LIS/Listener block address
;Returns: The message has been processed.

PROMSG:	$SAVE	<P1>			;SAVE THIS AC

;DETERMINE WHICH NODE SENT THE MESSAGE AND SAVE THE NAME. SETUP TO 
;SEARCH THE MESSAGE DISPATCH TABLE.

;**;[6045]At PROMSG:+5L add 1 line  JCR  1/15/90
	SETZM	G$ERR			;[6045]Clear the error flag
	MOVE	S1,.LSNTA(LIS)		;PICK UP NODE TABLE ENTRY ADDRESS
	MOVE	S1,.NNNAM(S1)		;PICK UP THE NODE NAME
	MOVEM	S1,G$SND		;SAVE SO KNOW WHERE MESSAGE IS FROM

	MOVE	M,.LSMSG(LIS)		;PICK UP THE MESSAGE ADDRESS
	LOAD	S2,.MSTYP(M),MS.TYP	;GET THE MESSAGE TYPE
	MOVSI	S1,-NLMGT		;MAKE AOBJN POINTER FOR MSG TYPES

;PICK UP THE MESSAGE PROCESSING DISPATCH ADDRESS

PROMS2:	HRRZ	P1,LMGTAB(S1)		;GET A MESSAGE TYPE
	CAMN	S2,P1			;MATCH?
	JRST	PROMS3			;YES, WIN
	AOBJN	S1,PROMS2		;NO, LOOP
	JRST	PROMS4			;UNKNOWN MESSAGE TYPE, TELL ORION

;A KNOWN MESSAGE HAS BEEN RECEIVED, PROCESS IT

PROMS3:	HLRZ	P1,LMGTAB(S1)		;PICK UP THE PROCESSING ROUTINE ADR
	$CALL	@P1	 		;DISPATCH THE MESSAGE PROCESSOR.
	$RET

;AN UNKNOWN MESSAGE TYPE HAS BEEN RECEIVED. INFORM ORION
;**;[6037]At PROMS4:+0L add 2 lines  JYCW  Oct-18-88
PROMS4:	TXNE	S2,NEB%MS		;[6037]New format?
	JRST	LSIBH0			;[6037]First determine # of replies
	$WTOJ(<NEBULA received unknown message>,<Listener to node ^N/G$SND/ has received an unknown message type>,,<$WTFLG(WT.SJI)>)

	$RET				;RETURN TO THE CALLER

LMGTAB:	XWD	LSIBHM,.NFDSM		;FROM NEBULA DISMOUNT
	XWD	LFDAK,.NFDAK		;FROM NEBULA DISMOUNT ACK
	XWD	LSIBHM,.NFMTS		;FROM NEBULA MOUNT
	XWD	RSPMSG,.OMDSP		;[6011]ACK OR WTO
	XWD	RSPMSG,.OMACS		;[6011]QUASAR SHOW ACK
	XWD	RSPMSG,MT.TXT		;[6011]ORION ACK
	XWD	LSIBHM,.NMSHS		;SHOW STATUS (.OMSHS)
	XWD	LSIBHM,.NDSHT		;[6001]SHOW STATUS TAPE (.ODSHT)
	XWD	LSIBHM,.NDSHD		;[6001]SHOW STATUS DISK (.ODSHD)
	XWD	LSIBHM,.NDSTR		;[6004]SHOW STATUS STRUCTURE (.ODSTR)
	XWD	LSIBHM,.NDSCD		;SHOW CONFIGURATION (.ODSCD)
	XWD	LSIBHM,.NMSHQ		;SHOW QUEUES (.OMSHQ)
	XWD	LSIBHM,.NMSHP		;SHOW PARAMETERS (.OMSHP)
	XWD	LSIBHM,.NMSHR		;SHOW ROUTE (.OMSHR)
	XWD	LSIBHM,.NMESS		;SHOW MESSAGES
	XWD	LSIBHM,.NSHOP		;SHOW OPERATORS
	XWD	NSRCL,.NSRCL		;SHOW CLUSTER-GALAXY/CLUSTER-NODE:
	XWD	EINFO,.QOLIS		;[6012]EXEC INFORMATION OUTPUT REQUEST
	XWD	REXSHW,.NMACS		;[6012]RESPONSE TO AN EXEC INFO REQUEST
	XWD	EKILL,.QOKIL		;[6012]EXEC CANCEL REQUEST
	XWD	REXKIL,.NMTXT		;[6012]RESPONSE TO AN EXEC CANCEL REQUEST
	XWD	RNEBAK,.NACKM		;[6016]REMOTE NEBULA ACK MESSAGE
	XWD	NRMACK,.NACKR		;[6016]NEBULA ACK RESPONSE MESSAGE
	XWD	LSIBHM,.NMSSN		;[6033]SHOW STATUS NETWORK (.OMSSN)
	XWD	LSIBHM,.NMSPN		;[6033]SHOW PARAMTER NETWORK (.OMSPN)
;**;[6043]At XWD LSIBHM,.NMSPN add 1 line  JCR  11/29/89
	XWD	NRMACK,.OMNAK		;[6043]NULL ACK
;**;[6052]At LMGTAB:+26L add 3 lines  JCR  3/1/90
	XWD	NRRCF,.NDRCF		;[6052]REPORT-CONNECTION-FAILURES
	XWD	NRDCA,.NDDCA		;[6052]DECNET-CONNECTION-ATTEMPTS
	XWD	RSPMSG,.OMACK		;[6052]ACK 
	NLMGT==.-LMGTAB
	SUBTTL	LFDAK - PROCESS A "FROM NEBULA DISMOUNT ACK" MESSAGE

;LFDAK is called from the listener message dispatcher to process a
;FROM NEBULA DISMOUNT ACK message received from a remote node in the
;cluster. A FROM NEBULA DISMOUNT ACK message is the response to a
;FROM NEBULA DISMOUNT message which requested the remote node to dismount
;a structure from that node. The FROM NEBULA DISMOUNT ACK message indicates
;the result of the dismount attempt, whether the dismount attempt succeeded
;or failed.
;This routine finds the remote queue entry that corresponds to the original
;FROM NEBULA DISMOUNT message and indicates in the node status word of
;the node from which the ACK has been received the status of the dismount
;attempt. If this is the last remaining response to the original FROM NEBULA
;DISMOUNT message, then a TO NEBULA DISMOUNT ACK message is built and
;sent to ORION to be forwarded to MOUNTR.
;
;Call is: M/Address of the FROM NEBULA DISMOUNT ACK message
;         LIS/Listener block address
;         G$SND/Node name from where the FROM NEBULA DISMOUNT ACK
;         message originated
;Returns: The remote queue entry's node block corresponding to
;         the node where the message orginated from is updated
;         to indicate the status of the dismount attempt on that
;         node.
;Crashes: A node data base inconsistency is detected

LFDAK:	$SAVE	<P1,P2,P3,P4>		;SAVE THESE AC

;FIND THE REMOTE QUEUE ENTRY CORRESPONDING TO THE ORIGINAL FROM NEBULA
;DISMOUNT MESSAGE USING THE ACK CODE OF THE ORIGINAL MESSAGE.

	MOVE	S1,G$SND		;PICK UP THE REMOTE NODE NAME
	MOVE	S2,.MSCOD(M)		;PICK UP THE ACK CODE
	$CALL	FNDNRQ			;FIND THE REMOTE QUEUE ENTRY
	JUMPF	S..NDI			;NODE DATA BASE INCONSISENCY
	DMOVE	P1,S1			;SAVE THE INVARIANT BLOCK ADDRESS
					; AND THE FORWARD LINK LIST WORD
;POINT TO THE INVARIANT BLOCK AND THE NODE BLOCK OF THE REMOTE NODE

	LOAD	P3,.RQBLK(P2),RQ.OFF	;PICK UP OFFSET TO INVARIANT BLOCK
	LOAD	S2,.RQNUM(P1),RQ.NTS	;PICK UP NUMBER OF NODE BLOCKS
	SUB	S2,P3			;NODE BLOCK POSITION IN NODE BLOCK LIST
	IMULI	S2,.RQNSZ		;SIZE OF NODE BLOCK DISPLACEMENT
	ADDI	S1,.RQNIZ(S2)		;ADDRESS OF THE DESIRED NODE BLOCK
	MOVE	P3,S1			;SAVE THIS ADDRESS

;INDICATE IN THE NODE BLOCK THE STATUS OF THE DISMOUNT ATTEMPT OF THE
;STRUCTURE ON THE REMOTE NODE.

	MOVX	S2,RQ%SNR!RQ%SSD	;ASSUME DISMOUNT WAS SUCCESSFUL
	SKIPGE	S1,.OFLAG(M)		;PICK UP STATUS FROM MESSAGE
	JRST	LFDAK2			;THE STRUCTURE WAS DISMOUNTED

	MOVX	S2,RQ%SNR		;RESPONSE NOT RECEIVED, TURN OFF LATER
	TXNE	S1,FA%TER		;DID A MONITOR ERROR OCCUR?
	TXO	S2,RQ%TER		;YES, INDICATE IN THE STATUS WORD
	TXNE	S1,FA%MER		;DID A MOUNTR ERROR OCCUR?
	TXO	S2,RQ%MER		;YES, INDICATE IN THE STATUS WORD
	TXNE	S1,FA%OER		;DID AN ORION ERROR OCCUR?
	TXO	S2,RQ%OER		;YES, INDICATE IN THE STATUS WORD
	TXNE	S1,FA%NER		;DID A NEBULA ERROR OCCUR?
	TXO	S2,RQ%NER		;YES, INDICATE IN THE STATUS WORD

;PLACE IN THE NODE BLOCK STATUS WORD FOR THIS NODE IF THE STRUCTURE DISMOUNT
;WAS A SUCCESS OR THE COMPONENT THAT DETECTED AN ERROR. THEN DELINK THIS
;REMOTE QUEUE ENTRY FROM THE NODE'S REMOTE QUEUE

LFDAK2:	XORM	S2,.RQNST(P3)		;INDICATE DISMOUNT SUCCESS OR FAILURE
	MOVE	S1,P2			;PICK UP ADR OF FORWARD LINK LIST WORD
	MOVEI	S2,.RQNST(P3)		;PICK UP ADR OF BACKWARD LINK LIST WORD
	MOVE	P4,.LSNTA(LIS)		;PICK UP ADR OF THE NODE TABLE ENTRY
	$CALL	RELNRQ			;DELINK THE RQE FROM THIS NODE'S RQ

;IF THE STRUCTURE WAS NOT DISMOUNTED ON THE REMOTE NODE, THEN PICK UP THE
;ERROR CODE AND PLACE IN THE ERROR CODE FIELD OF THE NODE BLOCK.

	SKIPG	.OARGC(M)		;IS THERE AN ERROR BLOCK PRESENT?
	JRST	LFDAK3			;NO, STRUCTURE WAS DISMOUNTED
	MOVE	S1,.OHDRS+ARG.DA(M)	;PICK UP THE ERROR CODE
	STORE	S1,.RQNST(P3),RQ.ERR	;PLACE ERROR CODE IN NODE BLOCK

;CLEAR THE TIMER

LFDAK3:	MOVE	S1,.LSNTA(LIS)		;[6011]PICK UP THE NODE TABLE ENTRY ADR
	MOVE	S2,.MSCOD(M)		;[6011]PICK UP THE MESSAGE ACK CODE
	$CALL	CLRTIM			;[6011]CLEAR THE TIMER

;CHECK IF THIS IS THE LAST RESPONSE TO THE FROM NEBULA DISMOUNT MESSAGE.
;IF IT IS, THEN BUILD THE TO NEBULA DISMOUNT ACK MESSAGE AND SEND IT
;TO ORION TO BE FORWARDED TO MOUNTR.

	SOS	.RQNUM(P1)		;[6011]DECREMENT THE # OF REMAINING RESPONSES
	LOAD	S1,.RQNUM(P1),RQ.NOR	;PICK UP THE # OF REMAINING RESPONSES
	SKIPE	S1			;ANY MORE RESPONSES?
	$RET				;YES, SO ARE FINISHED

	MOVE	S1,P1			;PICK UP THE INVARIANT BLOCK ADDRESS
	$CALL	SNDACK			;BUILD AND SEND TO NEBULA DISMOUNT ACK
	$RET				; MESSAGE, RETURN
	SUBTTL	LSIBHM - PROCESS IN BEHALF OF MESSAGES FROM LISTENERS

;LSIBHM processes messages from listeners that request information
;or an action to be taken by the local node in behalf of a remote
;node. LSIBHM saves the remote node's name and the message ACK code
;in the in behalf of queue and forwards the message to ORION.
;
;Call is: M/Address of the message
;         G$SND/Name of node that sent the message
;Returns: An in behalf of queue entry has been made for this message
;         and the message has been forwarded to ORION
;Crashes: The message cannot be sent to ORION

;**;[6037]At LSIBHM:+0L add routine LSIBH0:
;**;[6041]At LSIBH0:+0L replace 9 lines with 8 lines  JCR  9/26/89
LSIBH0:	$SAVE	<P1>			;[6041]Save a temporary
	TXZ	S2,NEB%MS		;[6041]Turn off the new format bit
	MOVSI	S1,-LUNITS		;[6041]Pick up the table length
LSIBH1:	MOVE	P1,MUNITS(S1)		;[6041]Pick up a message code
	CAMN	S2,P1			;[6041]Are the codes the same?
	JRST	LSIBH			;[6041]Yes, find the number of units
	AOBJN	S1,LSIBH1		;[6041]No, check the next code
	JRST	LSIBHM			;[6041]Message code not in the table

LSIBH:	MOVEI	S1,.OROBJ		;[6037]LOOK FOR THE OBJECT BLOCK
	$CALL	N$FNDB			;[6037]PICK UP THE OBJECT BLOCK
;**;[6044]At LSIBH:+2L change 4 lines  JCR  12/16/89
;**;[6051]at LSIBH:+2L add 1 line  JYCW  2/13/90
	JUMPF	LSIBHM			;[6051]No Object always 1 response.
	HLRZ	S2,OBJ.UN(S1)		;[6044]Pick up the upper limit
	JUMPE	S2,LSIBHM		;[6044]No upper limit
	HRRZ	S1,OBJ.UN(S1)		;[6044]Pick up the lower limit
	SUB	S2,S1			;[6044]Find the difference
	AOSA	S1,S2			;[6044]Make it the number of responses

;BUILD AN IN BEHALF OF QUEUE ENTRY FOR THIS MESSAGE AND LINK IT
;INTO THE IN BEHALF OF QUEUE

LSIBHM:	MOVEI	S1,1			;[6037]ASSUME ONLY ONE RESPONSE
	PUSH	P,S1			;[6037]SAVE THE NUMBER OF RESPONSES
	MOVEI	S1,.IQSIZ		;THE SIZE OF AN IBH QUEUE ENTRY
	$CALL	M%GMEM			;PICK UP THE MEMORY 
	MOVE	S1,.MSCOD(M)		;PICK UP THE MESSAGE'S ACK CODE
	MOVEM	S1,.IQRNA(S2)		;SAVE AS THE REMOTE ACK CODE
	AOS	S1,LOCACK		;PICK UP THE LOCAL ACK CODE
	MOVEM	S1,.IQLNA(S2)		;SAVE IT IN THE IBH QUEUE ENTRY
	MOVEM	S1,.MSCOD(M)		;SAVE IT IN THE MESSAGE
	MOVE	S1,G$SND		;PICK UP THE REMOTE NODE NAME
	MOVEM	S1,.IQNEN(S2)		;SAVE AS THE IN BEHALF OF NODE
;**;[6037]At LSIBHM:+9L add 2 lines  JYCW  Oct-18-88
	POP	P,S1			;[6037]PICK UP THE NUMBER OF RESPONSES
;**;[6041]At LSIBHM:+13L change 1 line  JCR  9/26/89
	HRRZM	S1,.IQNUM(S2)		;[6041]Save it in the IBH queue entry

	MOVE	S1,S2			;PICK UP ADDRESS OF THE IBH QUEUE ENTRY
	$CALL	ADDIBH			;ADD TO THE IN BEHALF OF QUEUE

;	SEND THE MESSAGE TO ORION FOR PROCESSING ON THE LOCAL NODE

	$CALL	SENREM			;FORWARD THE MESSAGE TO ORION
	JUMPF	S..OSF			;CRASH IF CAN'T SEND THE MESSAGE
	$RET				;PRESERVE THE T/F STATE

;**;[6041]At LSIBHM:+23L add 9 lines  JCR  9/26/89
MUNITS:	XWD	0,.OMSTA		;[6041]START message
	XWD	0,.OMSHT		;[6041]SHUT  message
	XWD	0,.OMCON		;[6041]CONTINUE message
	XWD	0,.OMPAU		;[6041]STOP  message
	XWD	0,.OMSET		;[6041]SET   message
	XWD	0,.OMELT		;[6041]ENABLE PRINT-LOGFILES  message
	XWD	0,.OMDLT		;[6041]DISABLE PRINT-LOGFILES message

	LUNITS==.-MUNITS		;[6041]Length of multiple units table
	SUBTTL	EINFO - PROCESS AN IN BEHALF OF EXEC INFO OUTPUT MESSAGE

;[6012]EINFO processes the EXEC  INFORMATION OUTPUT message that originated
;[6012]on a remote node in the cluster. EINFO saves the remote node's name,
;[6012]the message ACK code and the EXEC's PID in the in behalf of queue and
;[6012]forwards the message to ORION.
;[6012]
;[6012]Call is: M/Address of the message
;[6012]         G$SND/Name of node that sent the message
;[6012]Returns: An in behalf of queue entry has been made for this message
;[6012]         and the message has been forwarded to ORION
;[6012]Crashes: The message cannot be sent to ORION

;[6012]BUILD AN IN BEHALF OF QUEUE ENTRY FOR THIS MESSAGE AND LINK IT
;[6012]INTO THE IN BEHALF OF QUEUE

EINFO:	$SAVE	<P1>			;[6012]SAVE THIS AC
	MOVEI	S1,.LSPID		;[6012]PICK UP PID BLOCK CODE
	$CALL	N$FNDB			;[6012]FIND THE PID BLOCK
	MOVE	P1,0(S1)		;[6012]PICK UP THE PID
	MOVEI	S1,.IQSIZ		;[6012]THE SIZE OF AN IBH QUEUE ENTRY
	$CALL	M%GMEM			;[6012]PICK UP THE MEMORY 
	MOVE	S1,.MSCOD(M)		;[6012]PICK UP THE MESSAGE'S ACK CODE
	MOVEM	S1,.IQRNA(S2)		;[6012]SAVE AS THE REMOTE ACK CODE
	AOS	S1,LOCACK		;[6012]PICK UP THE LOCAL ACK CODE
	MOVEM	S1,.IQLNA(S2)		;[6012]SAVE IT IN THE IBH QUEUE ENTRY
	MOVEM	S1,.MSCOD(M)		;[6012]SAVE IT IN THE MESSAGE
	MOVE	S1,G$SND		;[6012]PICK UP THE REMOTE NODE NAME
	MOVEM	S1,.IQNEN(S2)		;[6012]SAVE AS THE IN BEHALF OF NODE
	MOVEM	P1,.IQPID(S2)		;[6012]SAVE THE EXEC SENDER'S PID	
	MOVE	S1,S2			;[6012]PICK UP THE IBH QUEUE ENTRY ADR
	$CALL	ADDIBH			;[6012]ADD TO THE IN BEHALF OF QUEUE

;[6012]	SEND THE MESSAGE TO QUASAR FOR PROCESSING ON THE LOCAL NODE

	MOVE	S1,M			;[6012]PICK UP THE MESSAGE ADDRESS
	TXO	S1,PT.KEE		;[6012]KEEP THE LISTENER PAGE
	MOVEI	S2,PAGSIZ		;[6012]PICK UP THE MESSAGE SIZE
	$CALL	SNDQSR			;[6012]FORWARD THE MESSAGE TO QUASAR
	JUMPF	S..QSF			;[6012]CRASH IF CAN'T SEND THE MESSAGE
	$RET				;[6012]PRESERVE THE T/F STATE
	SUBTTL	EKILL - PROCESS AN IN BEHALF OF EXEC CANCEL MESSAGE

;[6012]EKILL processes the EXEC CANCEL PRINT message that originated
;[6012]FROM a remote node in the cluster. EKILL saves the remote node's name,
;[6012]the message ACK code and the EXEC's PID in the in behalf of queue and
;[6012]forwards the message to ORION.
;[6012]
;[6012]Call is: M/Address of the message
;[6012]         G$SND/Name of node that sent the message
;[6012]Returns: An in behalf of queue entry has been made for this message
;[6012]         and the message has been forwarded to ORION
;[6012]Crashes: The message cannot be sent to ORION

;[6012]BUILD AN IN BEHALF OF QUEUE ENTRY FOR THIS MESSAGE AND LINK IT
;[6012]INTO THE IN BEHALF OF QUEUE

EKILL:	MOVEI	S1,.IQSIZ		;[6012]PICK UP IBH QUEUE ENTRY SIZE
	$CALL	M%GMEM			;[6012]PICK UP THE IBH QUEUE ENTRY
	MOVE	S1,.MSCOD(M)		;[6012]PICK UP THE EXEC UNIQUE CODE
	MOVEM	S1,.IQRNA(S2)		;[6012]SAVE AS THE ORIGINAL ACK CODE
	AOS	S1,LOCACK		;[6012]PICK UP THE LOCAL ACK CODE
	MOVEM	S1,.IQLNA(S2)		;[6012]SAVE AS THE LOCAL ACK CODE
	MOVEM	S1,.MSCOD(M)		;[6012]SAVE IN THE MESSAGE
	MOVE	S1,G$SND		;[6012]PICK UP THE REQUESTOR'S NODE NAME
	MOVEM	S1,.IQNEN(S2)		;[6012]SAVE IN THE IBH QUEUE ENTRY
	MOVE	S1,KIL.PD(M)		;[6012]PICK UP THE EXEC'S PID
	MOVEM	S1,.IQPID(S2)		;[6012]SAVE IN THE IBH QUEUE ENTRY
	MOVE	S1,S2			;[6012]]PICK UP THE IBH QUEUE ENTRY ADR
	$CALL	ADDIBH			;[6012]ADD TO THE IBH QUEUE

;[6012]	SEND THE MESSAGE TO QUASAR FOR PROCESSING ON THE LOCAL NODE

	MOVE	S1,M			;[6012]PICK UP THE MESSAGE ADDRESS
	TXO	S1,PT.KEE		;[6012]KEEP THE LISTENER PAGE
	MOVEI	S2,PAGSIZ		;[6012]PICK UP THE MESSAGE SIZE
	$CALL	SNDQSR			;[6012]FORWARD THE MESSAGE TO QUASAR
	JUMPF	S..QSF			;[6012]CRASH IF CAN'T SEND THE MESSAGE
	$RET				;[6012]PRESERVE THE T/F STATE
	SUBTTL	NSRCL - PROCESS REMOTE SHOW CLUSTER-STATUS MESSAGE

NSRCL:	SETOM	REMORG			;INDICATE REQUEST CAME REMOTELY
	$CALL	NSCLU			;BUILD THE MESSAGE
;**;[6052]At NSRCL:+2L replace 3 lines with 1 line  JCR  3/1/90
	JUMPT	NSRC.1			;[6052]On success send the reply
	$CALL	NSEACK			;BUILD THE ERROR ACK MESSAGE
	MOVE	MO,M			;SAVE THE MSG ADDRESS FOR LATER

NSRC.1:	EXCH	M,MO			;PLACE THE MESSAGE IN THE EXPECTED AC
	MOVE	S1,G$SND		;[6001]PICK UP REMOTE NODE NAME
	$CALL	SETPAR			;CHECK IF CAN SEND DIRECT OR MUST QUEUE
	JUMPF	NSRC.2			;[6001]DON'T SEND ON AN ERROR
	TXO	S2,MQ%IBH		;INDICATE MSG WAS IN BEHALF OF
	$CALL	QUEMSG			;QUEUE OR SEND THE MESSAGE
NSRC.2:	EXCH	M,MO			;[6001]EXCHANGE THE MESSAGE ADDRESSES
	CAME	M,MO			;WAS THE MESSAGE AN ACK MESSAGE?
	$CALL	RELPAG			;NO, RELEASE THE RESPONSE MESSAGE PAGE
	$RET				;RETURN TO THE CALLER
;**;[6052]After NSRC.2:+3L add routines NRRCF, NRDCA and FRDMSG  JCR  3/1/90
	SUBTTL	NRRCF - Process a Remote REPORT-CONNECTION-FAILURES Message

;[6052]Routine NRRCF processes a REPORT-CONNECTION-FAILURES message that
;[6052]was sent from a remote NEBULA.
;[6052]
;[6052]Call is:       M/REPORT-CONNECTION-FAILURES message address
;[6052]Returns true:  The request was successfully processed and sent back
;[6052]               to the requesting NEBULA.
;[6052]Returns false: An illegal or unknown node was specified; or the
;[6052]               requesting node is no longer in the cluster.

NRRCF:	SETOM	REMORG			;[6052]Indicate request came remotely
	$CALL	NDRCF			;[6052]Process the message
	JUMPT	NRRC.1			;[6052]Build a success .OMACK message

	$CALL	NSEACK			;[6052]Build an error MT.TXT message
	JRST	NRRC.4			;[6052]Send the message

NRRC.1:	MOVEM	S2,G$ARG1		;[6052]Save the node indicator
	JUMPE	S1,NRRC.2		;[6052]ENABLE request?
	SKIPE	S2			;[6052]No, for all nodes?
	$CALL	E$RDS			;[6052]No, for a particular node
	SKIPN	S2			;[6052]For all nodes?
	$CALL	E$RDA			;[6052]Yes
	JRST	NRRC.3			;[6052]Build the .OMACK message

NRRC.2:	SKIPE	S2			;[6052]DISABLE, for all nodes?
	$CALL	E$RES			;[6052]No, for a particular node
	SKIPN	S2			;[6052]For all nodes?
	$CALL	E$REA			;[6052]Yes

NRRC.3:	$CALL	NSEACK			;[6052]Build an MT.TXT message
	MOVEI	S1,.OMDSP		;[6052]Pick up display type
	STORE	S1,.MSTYP(MO),MS.TYP	;[6052]Store in the message header

NRRC.4:	$CALL	FRDMSG			;[6052]Forward the message to NEBULA
	$RET				;[6052]Return to the caller
	SUBTTL	NRDCA - Process a Remote DECNET-CONNECTION-ATTEMPTS Message

;[6052]Routine NRDCA processes a DECNET-CONNECTION-ATTEMPTS message that
;[6052]was sent from a remote NEBULA.
;[6052]
;[6052]Call is:       M/DECNET-CONNECTION-ATTEMPTS message address
;[6052]Returns true:  The request was successfully processed and sent back
;[6052]               to the requesting NEBULA.
;[6052]Returns false: An illegal or unknown node was specified; or the
;[6052]               requesting node is no longer in the cluster.

NRDCA:	SETOM	REMORG			;[6052]Indicate request came remotely
	$CALL	NDDCA			;[6052]Process the message
	JUMPT	NRDC.1			;[6052]Build a success .OMACK message

	$CALL	NSEACK			;[6052]Build an error MT.TXT message
	JRST	NRDC.4			;[6052]Send the message

NRDC.1:	MOVEM	S2,G$ARG1		;[6052]Save the node indicator
	JUMPE	S1,NRDC.2		;[6052]ENABLE request?
	SKIPE	S2			;[6052]No, for all nodes?
	$CALL	E$DDS			;[6052]No, for a particular node
	SKIPN	S2			;[6052]For all nodes?
	$CALL	E$DDA			;[6052]Yes
	JRST	NRDC.3			;[6052]Build the .OMACK message

NRDC.2:	SKIPE	S2			;[6052]DISABLE, for all nodes?
	$CALL	E$DES			;[6052]No, for a particular node
	SKIPN	S2			;[6052]For all nodes?
	$CALL	E$DEA			;[6052]Yes

NRDC.3:	$CALL	NSEACK			;[6052]Build an error MT.TXT message
;NRDC.3:$CALL	BLDACK			;[6052]Build the .OMACK message
	MOVEI	S1,.OMDSP		;[6052]Pick up display type
	STORE	S1,.MSTYP(MO),MS.TYP	;[6052]Store in the message header
NRDC.4:	$CALL	FRDMSG			;[6052]Forward the message to NEBULA
	$RET				;[6052]Return to the caller
	SUBTTL	FRDMSG - Forward a Message to a Remote NEBULA

;[6052]FRDMSG is called by routines NRRCF and NRDCA to send a message
;[6052]response back to NEBULA
;[6052]
;[6052]Call is:       M/Address of message to be sent to NEBULA
;[6052]Returns true:  The message was sent or queued
;[6052]Returns false: The message could not be sent

FRDMSG:	MOVE	S1,G$SND		;[6052]Pick up the remote node name
	$CALL	SETPAR			;[6052]Check if can send direct or queue
	$RETIF				;[6052]Return on an error
	TXO	S2,MQ%IBH		;[6052]Indicate msg was in behalf of
	$CALL	QUEMSG			;[6052]Queue or send the message
	$RETT				;[6052]Return to the caller
	SUBTTL	NSEACK - BUILD AN ACK MESSAGE REMOTELY

;NSEACK is called when NEBULA detects that a SHOW CLUSTER message that
;has been processed remotely has encountered an error. This routine builds
;an .OMDSP message to be forwarded to the original sender of the SHOW message.
;
;Call is: M/Address of the SHOW CLUSTER-STATUS message
;Returns: M/Address of the .OMDSP message

NSEACK:	$SAVE	<P1,P2>			;SAVE THESE AC

;BUILD THE GALAXY MESSAGE HEADER

	MOVE	S1,[.OHDRS,,MT.TXT]	;[6023]PICK UP THE HEADER WORD
	MOVEM	S1,.MSTYP(M)		;PLACE IN THE MESSAGE
	MOVX	S1,MF.NEB		;PICK UP THE NEBULA BIT
	MOVEM	S1,.MSFLG(M)		;PLACE IN THE MESSAGE
;**;[6052]At NSEACK:+8L replace 3 lines with 2 lines  JCR  3/1/90
	SETZM	.OARGC(M)		;[6052]Reset the argument count
	SETZM	.OFLAG(M)		;[6052]Reset flag word

;BUILD THE FIRST DISPLAY BLOCK

	MOVEI	P1,.OHDRS+ARG.HD(M)	;ADDRESS OF THE BLOCK HEADER WORD
	MOVEI	S1,.ORDSP		;PICK UP THE BLOCK TYPE
	STORE	S1,ARG.HD(P1),AR.TYP	;[6023]PLACE IN THE MESSAGE
	MOVEI	S1,ARG.DA+1(P1)		;[6023]ADDRESS OF THE FIRST TEXT WORD
	HRLI	S1,(POINT 7,)		;MAKE INTO A POINTER
	MOVEM	S1,BYTPTR		;SAVE THE POINTER
	$TEXT	(DEPBYT,<^I/@NSEA.1/^0>) ;PLACE TEXT IN THE MESSAGE BLOCK
	
	HRRZ	S1,BYTPTR		;PICK UP THE END ADDRESS OF THE BLOCK
	ANDI	S1,777			;ISOLATE THE PAGE OFFSET
	SUBI	S1,.OHDRS-1		;FIND THE BLOCK LENGTH
	STORE	S1,ARG.HD(P1),AR.LEN	;PLACE IN THE MESSAGE
	MOVE	P2,S1			;SAVE THE LENGTH FOR LATER
;**;[6052]At NSEACK:+27L add 1 line  JCR  3/1/90
	AOS	.OARGC(M)		;[6052]Increment the argument count

;BUILD THE SECOND DISPLAY BLOCK

	ADD	P1,S1			;ADDRESS OF THE NEXT DISPLAY BLOCK
	MOVEI	S2,.ORDSP		;PICK UP THE BLOCK TYPE
	STORE	S2,ARG.HD(P1),AR.TYP	;STORE THE BLOCK TYPE
	MOVE	S2,MSGTIM		;PICK UP THE TIME
	MOVEM	S2,ARG.DA(P1)		;SAVE IN THE BLOCK
	MOVEI	S1,ARG.DA+1(P1)		;ADDRESS OF THE START OF THE TEXT
	HRLI	S1,(POINT 7,)		;MAKE INTO A POINTER
	MOVEM	S1,BYTPTR		;SAVE THE POINTER
	MOVE	S1,G$ERR		;PICK UP THE ERROR OFFSET
	$TEXT	(DEPBYT,<^I/@TXTTBL(S1)/^0>) ;PLACE THE TEXT IN THE BLOCK

	HRRZ	S1,BYTPTR		;PICK UP THE END ADDRESS
	ANDI	S1,777			;ISOLATE THE PAGE OFFSET
	SUBI	S1,.OHDRS-1(P2)		;FIND THE BLOCK LENGTH
	STORE	S1,ARG.HD(P1),AR.LEN	;PLACE IN THE BLOCK HEADER WORD
	ADD	S1,P2			;FIND THE TOTAL MESSAGE LENGTH
	MOVSS	S1			;PLACE IN THE EXPECTED PLACE
	ADDM	S1,.MSTYP(M)		;[6023]PLACE IN THE MESSAGE
;**;[6052]At NSEA.2:+7L add 1 line  JCR  3/1/90
	AOS	.OARGC(M)		;[6052]Increment the argument count
	$RET				;RETURN TO THE CALLER

;**;[6045]At NSEA.1:+0L change 1 line  JCR  1/15/90
NSEA.1:	[ITEXT(< Received message from ^N/NODNAM/::>)] ;[6045]
	SUBTTL	RSPMSG - RESPONSE TO A MESSAGE

;RSPMSG is called as part of processing responses to remote messages.
;RSPMSG clears the timer that was set when the message was sent to the
;remote node and then sends the message response to ORION.
;
;Call is:       LIS/Address of the listener block
;Returns true:  The message was sent successfully to ORION
;Returns false: The message was not sent to ORION

RSPMSG:	MOVE	S1,.LSNTA(LIS)		;[6011]PICK UP THE NODE TABLE ENTRY ADR
	MOVE	S2,.MSCOD(M)		;[6011]PICK UP THE ACK CODE
	$CALL	CLRTIM			;[6011]CLEAR THE TIMER
	$CALL	SENREM			;[6011]SEND THE MESSAGE TO ORION
	$RET				;[6011]PRESERVE THE T/F INDICATOR
	SUBTTL	SENREM - SEND A MESSAGE FROM A REMOTE NODE TO ORION

;SENREM sends in behalf of messages and responses to requests made
;on a remote node that NEBULA does not need to act on (e.g., DISPLAY
;messages as opposed to the FROM NEBULA DISMOUNT ACK message) to ORION.
;SENREM sends the message to ORION.
;
;Call is:       M/Address of the message to be sent to ORION
;Returns true:  The message was sent successfully
;Returns false: The message was not sent

SENREM:

;SEND THE MESSAGE TO ORION

	MOVE	S1,M			;ADDRESS OF THE IPCF MESSAGE
	TXO	S1,PT.KEE		;INDICATE WANT TO KEEP PAGE AFTER SEND
	MOVEI	S2,PAGSIZ		;LENGTH OF THE IPCF MESSAGE
	$CALL	SNDORN			;SEND THE MESSAGE TO ORION
	$RET				;PRESERVE THE T/F INDICATOR
	SUBTTL	REXSHW - RESPONSE TO AN EXEC REQUEST

;[6012]REXSHW is called as part of processing responses to EXEC requests.
;[6012]REXSHW clears the timer that was set when the message was sent to the
;[6012]remote node and then sends the message response to the EXEC that made
;[6012]the original request.
;[6012]
;[6012]Call is:       LIS/Address of the listener block
;[6012]Returns true:  The message was sent successfully to the EXEC
;[6012]Returns false: The message was not sent to the EXEC

REXSHW:	SKIPA	S1,[0]			;[6012]INDICATE INFO OUTPUT RESPONSE
REXKIL:	SETOM	S1,			;[6012]INDICATE CANCEL PRINTER RESPONSE
	MOVEM	S1,EXESHW		;[6012]SAVE FOR LATER
	MOVE	S1,.LSNTA(LIS)		;[6012]PICK UP THE NODE TABLE ENTRY ADR
	MOVE	S2,.MSCOD(M)		;[6012]PICK UP THE ACK CODE
	$CALL	CLRTIM			;[6012]CLEAR THE TIMER

	MOVEM	M,SAB+SAB.MS		;[6012]PLACE MESSAGE ADDRESS IN THE SAB
	MOVEI	S1,PAGSIZ		;[6012]PICK UP THE MESSAGE SIZE
	MOVEM	S1,SAB+SAB.LN		;[6012]PLACE IN THE SAB
	SETZM	SAB+SAB.SI		;[6012]ZERO OUT SPECIAL INDEX
	MOVE	S1,.OFLAG(M)		;[6012]PICK UP THE EXEC'S PID
	SETZM	.OFLAG(M)		;[6012]ZERO THE FLAG WORD
	MOVEM	S1,SAB+SAB.PD		;[6012]PLACE IN THE SAB
	MOVEI	S1,.OMACS		;[6012]PICK UP THE MESSAGE CODE
	SKIPE	EXESHW			;[6012]INFO PRINT RESPONSE?
	MOVEI	S1,.OMTXT		;[6012]NO, CANCEL PRINT RESPONSE
	STORE	S1,.MSTYP(M),MS.TYP	;[6012]PLACE IN THE MESSAGE
	MOVE	S1,.MSFLG(M)		;[6012]PICK UP THE FLAG WORD
	TXZ	S1,MF.NEB		;[6012]TURN OFF THE NEBULA BIT
	MOVEM	S1,.MSFLG(M)		;[6012]RESTORE THE UPDATED FLAG WORD
	MOVEI	S1,SAB.SZ		;[6012]PICK UP SIZE OF THE SAB
	TXO	S1,PT.KEE		;[6012]KEEP PAGE IN GLXMEM
	MOVEI	S2,SAB			;[6012]PICK UP ADDRESS OF THE SAB
	$CALL	C%SEND			;[6012]SEND THE MESSAGE
	$RET				;[6012]PRESERVE THE T/F INDICATOR
	SUBTTL	RNEBAK - RESPOND TO A NEBULA ACK MESSAGE

;[6016]RNEBAK is called when a NEBULA ACK message has been received. RNEBAK
;[6016]sends a NEBULA ACK RESPONSE message back to the NEBULA that sent the
;[6016]ACK message. 
;[6016]
;[6016]Call is:       M/Address of the NEBULA ACK message
;[6016]Returns true:  The NEBULA RESPONSE ACK message has been sent
;[6016]Returns false: The NEBULA RESPONSE ACK message could not be sent

RNEBAK:	MOVEI	M,NEBRSP		;[6016]NEBULA RESPONSE ACK MSG ADDRESS
	MOVE	S1,G$SND		;[6016]PICK UP NODE ACK MSG CAME FROM
	$CALL	SETPAR			;[6016]DETERMINE IF SEND DIRECT OR QUEUE
	JUMPF	.POPJ			;[6016]QUIT NOW IF CAN'T SEND THE MSG
	TXO	S2,MQ%IBH		;[6016]INDICATE AN IN BEHALF OF RESPONSE
	$CALL	QUEMSG			;[6016]SEND OR QUEUE THE MESSAGE
	$RETT				;[6016]INDICATE SUCCESS
	SUBTTL	NRMACK - PROCESS A NEBULA ACK RESPONSE MESSAGE

;[6016]NRMACK is called when a NEBULA ACK RESPONSE message has been received.
;[6016]NRMACK clears the timer associated with the original NEBULA ACK 
;[6016]message. The NEBULA ACK/NEBULA ACK RESPONSE messages mechanism ensures
;[6016]that a remote node that NEBULA believes that it has communication with
;[6016]actually does have communication with that node. For instance, if the
;[6016]remote node's NI is reloaded, the local NEBULA will not be interrupted
;[6016]to indicate that its links to that node are no longer connected.
;**;[6043]At NRMACK:-1L replace 2 lines with 9 lines  JCR  11/29/89
;[6043]NRMACK is also called to process a NULL ACK message. NEBULA expects a 
;[6043]response for each message that it sends to a remote NEBULA. However,
;[6043]it is possible that a message will not generate a response. For example,
;[6043]WTOR processing may not send a response. In order to clear the 
;[6043]message return time, a Null ACK must be sent from the remote node.
;[6043]
;[6016]Call is: M/NEBULA ACK RESPONSE or NULL ACK message address
;[6043]          LIS/Address of the listener block
;[6043]Returns:  The message return timer has been cleared

NRMACK:	MOVE	S1,.LSNTA(LIS)		;[6016]PICK UP THE NODE TABLE ENTRY ADR
	MOVE	S2,.MSCOD(M)		;[6016]PICK UP THE ACK CODE
	$CALL	CLRTIM			;[6016]CLEAR THE TIMER
	$RET				;[6016]RETURN TO THE CALLER
SUBTTL	RESTAR - PROCESS CRASHED INFERIOR FORKS

;RESTAR is called by the scheduler when it detects that an inferior fork
;has crashed. It determines which fork has crashed, kills that fork and
;the other fork associated with the remote node. It then updates the
;remote queue, in behalf of queue and the message queue for that node.
;Finally, it determines if the remote node can potentially receive
;messages (i.e., it is still in the cluster, has DECnet enabled, and is
;running a monitor of release 7 or later.
;
;Call is: No arguments
;Returns: Senders and listeners have been updated
;Crashes: If there is a node table inconsistency

RESTAR:	$SAVE	<P1,P2,P3,P4>		;SAVE THESE AC
	SETZM	TRMFRK			;CLEAR THE INFERIOR FORK CRASHED FLAG
	SETOM	NBSCHD			;FORCE ANOTHER SCHEDULING PASS
	MOVEI	P1,NODTBL		;POINT TO THE NODE TABLE
	SKIPG	P2,RENNUM		;[6035]ANY NODES LEFT IN THE CLUSTER?
	$RET				;[6035]NO, RETURN NOW
	MOVEI	P3,MAXNOD		;PICK UP NUMBER OF NODE TABLE ENTRIRES

;CHECK IF THE REMOTE NODE HAS A SENDER AND CHECK IF THE REMOTE NODE IS STILL
;IN THE CLUSTER.

RESTA2:	SKIPN	.NNNAM(P1)		;IS THIS NODE TABLE ENTRY IN USE?
	JRST	RESTA6			;NO, CHECK THE NEXT NODE TABLE ENTRY
	SKIPN	.NNSBA(P1)		;NODE HAVE A SENDER (AND A LISTENER)?
	JRST	RESTA5			;[6022]NO, CHECK THE NEXT NODE TABLE ENTRY
	MOVE	S1,.NNSTA(P1)		;PICK UP THE NODE STATUS WORD
	TXNE	S1,NN%SCS		;HAS THIS NODE LEFT THE CLUSTER?
	JRST	RESTA5			;YES, LET ROUTINE TOPCHN DEAL WITH IT

;CHECK IF THE SENDER HAD A CONTROLLED CRASH, IF NOT, THEN CHECK IF IT HAD
;AN UNCONTROLLED CRASH.

	MOVE	S1,.NSSTA(P1)		;PICK UP THE SENDER STATUS WORD
	TXNE	S1,NS%SFC		;DID THIS SENDER CONTROLLED CRASH?
	JRST	RESTA3			;YES, GO CLEAN UP
	MOVE	S1,.NNSBA(P1)		;PICK UP THE SENDER BLOCK ADDRESS
	MOVE	S1,.SNHND(S1)		;PICK UP THE SENDER'S HANDLE
	$CALL	INFSTS			;PICK UP THE SENDER'S STATUS
	JUMPF	RESTA3			;THE SENDER IS HALTED OR DISMISSED

;THE SENDER TO THIS NODE IS O.K. CHECK THE LISTENER TO THIS NODE

	MOVE	S1,.NLSTA(P1)		;PICK UP THE LISTENER STATUS WORD
	TXNE	S1,NL%LFC		;DID THIS LISTENER CONTROLLED CRASH?
	JRST	RESTA3			;YES, GO CLEAN UP
	MOVE	S1,.NNLBA(P1)		;PICK UP THE LISTENER ADDRESS BLOCK
	MOVE	S1,.LSHND(S1)		;PICK UP THE LISTENER'S HANDLE
	$CALL	INFSTS			;PICK UP THE LISTENER'S STATUS
	JUMPT	RESTA5			;THIS NODE IS OK, CHECK THE NEXT NODE

;AT LEASE ONE INFERIOR FORK TO THE REMOTE NODE HAS CRASHED. KILL BOTH THE 
;SENDER AND LISTENER TO THIS NODE. ALSO, UPDATE THE QUEUES TO THIS NODE.
;REBUILD THE NODE TABLE QUEUE ENTRY AND IF THE REMOTE NODE CAN RECEIVE
;MESSAGES FROM THIS NODE, THEN START UP A SENDER AND LISTENER FOR THIS NODE.

RESTA3:	MOVE	S1,P1			;PICK UP THE NODE TABLE ENTRY ADDRESS
	$CALL	KASNOD			;RESET THE REMOTE NODE'S DATA BASE
	JUMPF	RESTA4			;INFORM OPERATORS OF THE FAILURE
	$WTOJ	(<NEBULA forks crashed>,<^I/@RESTXS/>,,<$WTFLG(WT.SJI)>)
	JRST	RESTA5			;GO CHECK FOR MORE NODES

RESTA4:	$WTOJ	(<NEBULA forks crashed>,<^I/@RESTXF/>,,<$WTFLG(WT.SJI)>)

;GO CHECK THE NEXT NODE, IF THERE ARE ANY MORE

RESTA5:	SOJE	P2,.POPJ		;RETURN IF NO MORE NODES
RESTA6:	SOJE	P3,S..NTI		;IF NO ENTRIES LEFT, CRASH
	ADDI	P1,.NNNSZ		;POINT TO THE NEXT NODE ENTRY
	JRST	RESTA2			;CHECK THE NEXT NODE

RESTXS:	[ITEXT(<DECnet forks to node ^N/.NNNAM(P1)/ crashed.
Forks have been restarted.>)]

RESTXF:	[ITEXT(<DECnet forks to node ^N/.NNNAM(P1)/ crashed.
Forks cannot be restarted.>)]
	SUBTTL	INFSTS - DETERMINE THE STATUS OF AN INFERIOR FORK

;INFSTS determines the status of an inferior fork
;
;Call is:       S1/Fork handle
;Returns true:  The fork is not halted or dismissed
;Returns false: The fork is halted or dismissed

INFSTS:	$SAVE	<T1,T2>			;RFSTS% JSYS CHANGES THESE AC
	RFSTS%				;GET THE STATUS OF THIS LISTENER
	 ERJMP	.RETF			;INVALID HANDLE, ASSUME FORK IS GONE
	TXZ	S1,RF%FRZ		;CLEAR THE FROZEN FORK BIT
	HLRZS	S1			;PLACE STATUS CODE IN RIGHT HALF
	CAIE	S1,.RFHLT		;IS THIS FORK HALTED?
	CAIN	S1,.RFFPT		;NO, IS THIS FORK DISMISSED?
INFST2:	$RETF				;YES, FORK IS HALTED OR DISMISSED
	$RETT				;FORK IS NOT HALTED OR DISMISSED
	SUBTTL	SNDORN - SEND AN IPCF MESSAGE TO ORION OR QUASAR

;SNDORN sends an IPCF message to ORION or QUASAR. If bit PT.KEE is set
;in the message address AC, then C%SEND will not replace the page in the
;free page pool.
;
;Call is:       S1/PT.KEE,,The message address
;	        S2/The message length
;Returns true:  The message was sent successfully
;Returns false: The message was not successfully sent

SNDORN:	TDZA	TF,TF			;FLAG TO SEND THE MESSAGE TO ORION
SNDQSR:	SETOM	TF			;FLAG TO SEND THE MESSAGE TO QUASAR
	HRRZM	S1,SAB+SAB.MS		;SAVE THE MESSAGE ADDRESS
	MOVEM	S2,SAB+SAB.LN		;SAVE THE MESSAGE LENGTH
	MOVEI	S2,SP.OPR		;GET ORION'S FLAG
	SKIPE	TF			;UNLESS THE MSG IS TO BE SENT TO QUASAR
	MOVEI	S2,SP.QSR		;THEN GET QUASAR'S FLAG
	TXO	S2,SI.FLG		;SET SPECIAL INDEX FLAG
	STORE	S2,SAB+SAB.SI		;AND STORE IT
	SETZM	SAB+SAB.PD		;CLEAR THE PID WORD
	HRRI	S1,SAB.SZ		;LOAD THE SIZE
	MOVEI	S2,SAB			;AND THE ADDRESS
	$CALL	C%SEND			;SEND THE MESSAGE
	$RET				;PERSERVE THE TRUE/FALSE INDICATOR
	SUBTTL	N$SEND - ROUTINE TO SEND AN IPCF MESSAGE TO AN EXEC

;[6012]N$SEND is called to send a message to an EXEC
;[6012]
;[6012]Call is:       SAB containing the SAB
;[6012]Returns true:  The message was sent
;[6012]Returns false: The message was not sent

;[6012]SEND THE IPCF MESSAGE TO THE EXEC

N$SEND:	MOVEI	S1,SAB.SZ		;[6012]GET THE SAB LENGTH
	MOVEI	S2,SAB			;[6012]AND THE SAB ADDRESS
	$CALL	C%SEND			;[6012]SEND THE MESSAGE OFF
	JUMPT	.POPJ			;[6012]RETURN NOW IF MSG SENT

;[6012]HERE IF THE SEND FAILED

	MOVE	S2,SAB+SAB.LN		;[6012]GET THE MESSAGE LENGTH
	CAIE	S2,PAGSIZ		;[6012]SENDING A PAGE?
	$RETF				;[6012]NO, JUST RETURN
	MOVE	S1,SAB+SAB.MS		;[6012]YES, GET THE PAGE ADDRESS
	PUSHJ	P,M%RPAG		;[6012]RETURN THE PAGE
	$RETF				;[6012]AND RETURN
	SUBTTL	XFRMSG - TRANSFER IPCF MESSAGE FROM ONE BUFFER TO ANOTHER

;This routine transfers an IPCF message from one buffer to another
;
;Call is: S1/ Address where the IPCF message is currently located
;         S2/ Address where the IPCF message is to be moved to
;Returns: Message has been transferred

XFRMSG:	HRL	S2,S1			;SOURCE,,DESTINATION
	LOAD	S1,.MSTYP(S1),MS.CNT	;PICK UP THE MESSAGE LENGTH
	ADD	S1,S2			;SIZE OF THE BLT + 1
	BLT	S2,-1(S1)		;TRANSFER THE MESSAGE
	$RET				;AND RETURN TO THE CALLER
	SUBTTL	ADDMQE - ADD A MESSAGE TO A SENDER'S MESSAGE QUEUE

;ADDMQE adds a message queue entry to a sender's message queue
;
;Call is: SEN/Address of the sender block
;         S1/ Adr of message queue entry link list word for
;             this sender's node
;Returns: Message queue entry added to end of this node's message queue

ADDMQE:	SKIPN	S2,.SNTWD(SEN)		;ANY MESSAGES IN ITS MESSAGE QUEUE?
	JRST	ADDMQ2			;NO, ADD AS THE FIRST MSG QUEUE ENTRY
	STORE	S1,.MQBLK(S2),MQ.NEA	;PLACE NEW ADR OF LAST QE IN L.L. WORD
	SKIPA				;GO UPDATE THE LINK LIST TRAILER WORD
ADDMQ2:	MOVEM	S1,.SNHWD(SEN)		;UPDATE THE LINK LIST HEADER WORD
	MOVEM	S1,.SNTWD(SEN)		;UPDATE THE LINK LIST TRAILER WORD
	AOS	.SNUSM(SEN)		;INCREMENT THE MESSAGE COUNT
	$RET				;AND RETURN TO THE CALLER
	SUBTTL	FNDMQE - FIND AND RETURN A MESSAGE QUEUE ENTRY

;FNDMQE is called as part of processing a CANCEL DISMOUNT 
;message. For those nodes that a dismount message was sent to but
;no response has been received as indicated in the remote queue entry,
;the message queue of those nodes are checked to determine if the message
;has yet been sent. If the message is still in the message queue, then
;the message is deleted from the node's message queue. Once the message
;has been deleted from all the message queues, the message queue entry
;is returned to the memory manager.
;The message queue is searched by the ACK code of the original DISMOUNT
;message.
;
;Call is:       S1/ACK code to find message queue entry by
;               SEN/Address of the sender block
;Returns true:  The message was found and deleted from the message queue
;               (if this was the last node to send the message to, then
;               the message queue entry is also returned to the memory
;               manager).
;Returns false: The message queue entry was not found (i.e., the message
;               has already been sent to the remote node).

FNDMQE:	$SAVE	<P1,P2,P3>		;SAVE THESE AC

;CHECK THIS NODE'S MESSAGE QUEUE FOR THE MESSAGE. BASE THE SEARCH ON THE
;ACK CODE PROVIDED BY MOUNTR AND STORED IN THE MESSAGE TO BE SENT

	SKIPN	.SNHWD(SEN)		;IS THE MESSAGE QUEUE EMPTY?
	JRST	FNDMQ7			;YES, MESSAGE NOT FOUND RETURN FALSE
	MOVEI	P1,.SNHWD(SEN)		;MAKE P1 THE PREVIOUS ENTRY ADDRESS
FNDMQ2:	LOAD	P2,.MQBLK(P1),MQ.NEA	;MAKE P2 THE CURRENT ENTRY ADDRESS
	JUMPE	P2,FNDMQ7		;RETURN FALSE IF NO MORE MESSAGE QE
	LOAD	S2,.MQBLK(P2),MQ.OFF	;PICK UP THE OFFSET TO INVARIANT BLOCK
	ADD	S2,P2			;POINT TO THE INVARIANT BLOCK
	LOAD	P3,.MQNOM(S2),MQ.ADR	;POINT TO THE IPCF MESSAGE
	CAMN	S1,.MSCOD(P3)		;THE MESSAGE BEING SEARCHED FOR?
	JRST	FNDMQ3			;YES, DELETE FROM MESSAGE QUEUE
	MOVE	P1,P2			;MAKE THE CURRENT THE PREVIOUS
	JRST	FNDMQ2			;CHECK THE MESSAGE QUEUE ENTRY

;THE MESSAGE HAS BEEN FOUND. DELETE THE MESSAGE FROM THIS NODE'S MESSAGE
;QUEUE

FNDMQ3:	SOSE	.SNUSM(SEN)		;ANY MORE MESSAGES IN THIS QUEUE?
	JRST	FNDMQ4			;YES, UPDATE THE MESSAGE QUEUE
	SETZM	.SNHWD(SEN)		;ZERO THE MESSAGE QUEUE HEADER WORD
	SETZM	.SNTWD(SEN)		;ZERO THE MESSAGE QUEUE TRAILER WORD
	JRST	FNDMQ5			;CHECK IF THE LAST NODE TO SEND MSG TO

;THE MESSAGE QUEUE WILL NOT BE EMPTY AFTER THIS QUEUE ENTRY IS REMOVED.
;UPDATE THE POINTERS

FNDMQ4:	LOAD	S1,.MQBLK(P2),MQ.NEA	;PICK UP ADDRESS OF THE NEXT MQE
	SKIPN	S1			;IS THE DELETED ENTRY THE LAST ENTRY?
	MOVEM	P1,.SNTWD(SEN)		;YES, PLACE PREVIOUS ENTRY ADR IN TRAILER
	STORE	S1,.MQBLK(P1),MQ.NEA	;UPDATE PREVIOUS ENTRY NEXT ENTRY ADR

;THE MESSAGE QUEUE ENTRY HAS BEEN DELETED FROM THIS NODE'S MESSAGE QUEUE.
;CHECK IF THE MESSAGE IS TO BE SENT TO ANY MORE NODES. IF IT IS NOT, THEN
;RETURN THE MESSAGE QUEUE ENTRY TO THE MEMORY MANAGER.

FNDMQ5:	LOAD	S1,.MQNOM(S2),MQ.NUM	;PICK UP NUMBER OF NODES TO SEND MSG TO
	SOJE	S1,FNDMQ6		;ANYMORE NODES TO SEND THE MESSAGE TO?
	STORE	S1,.MQNOM(S2),MQ.NUM	;YES, UPDATE NUMBER OF NODES
	$RETT				;INDICATE THE MESSAGE WAS FOUND
FNDMQ6:	MOVE	S1,S2			;PICK UP INVARIANT BLOCK ADDRESS
	$CALL	RELMQE			;RETURN THE MESSAGE QUEUE ENTRY
	$RETT				;INDICATE THE MESSAGE WAS FOUND

FNDMQ7:	$RETF				;THE MESSAGE WAS NOT FOUND
	SUBTTL	RELMQE - RETURN A MESSAGE QUEUE ENTRY TO MEMORY MANAGER

;RELMQE returns the memory used by a message queue entry and its
;corresponding message to the memory manager. 
;(Note: If the sum of the memory required by the message queue entry
;and the message is greater than a page, then in general, the message 
;memory location is not contiguous with the message queue entry,
;otherwise they are contiguous.)
;
;Call is: S1/Address of the message queue entry invariant block
;Returns: The message queue entry has been returned to the memory manager

RELMQE:	$SAVE	<P1>			;SAVE THIS AC
	MOVE	P1,S1			;PICK UP ADDRESS OF THE INVARIANT BLOCK

;DETERMINE IF THE MESSAGE LOCATION IS NOT CONTIGUOUS WITH THE END OF THE
;MESSAGE QUEUE ENTRY. IF IT IS NOT CONTIGUOUS, THEN RETURN THE MEMORY OF
;THE MESSAGE SEPARATELY TO THE MEMORY MANAGER.

	SKIPL	.MQMEM(P1)		;DOES THE MESSAGE OCCUPY A PAGE?
	JRST	RELMQ2			;NO, RETURN MQE AND MSG TOGETHER
	LOAD	S1,.MQNOM(P1),MQ.ADR	;YES, PICK UP THE PAGE ADDRESS
	$CALL	M%RPAG			;RETURN THE PAGE TO THE MEMORY MANAGER

;RETURN THE MESSAGE QUEUE ENTRY (AND MESSAGE, IF NOT A PAGE) TO THE 
;MEMORY MANAGER

RELMQ2:	LOAD	S1,.MQMEM(P1),MQ.LEN	;PICK UP MEMORY BLOCK SIZE
	LOAD	S2,.MQMEM(P1),MQ.PTR	;PICK UP MEMORY BLOCK ADDRESS
	$CALL	M%RMEM			;RETURN THE MEMORY TO MEMORY MANAGER
	$RET				;RETURN TO THE CALLER
	SUBTTL	BLDMQE - BUILD A MESSAGE QUEUE ENTRY

;BLDMQE builds a message queue entry. If the sum of the lengths of the message
;queue header and the message is less than or equal to a page, then the start
;of the message is contiguous with the end of the message queue header.
;Otherwise, the message is placed in a separate page that, in general, will
;not be contiguous with the end of the message queue header.
;
;Note: It is assumed in building the link list words that this message queue
;      entry will be placed on the end of the message queue (i.e., field
;      MQ.NEA of the link list word .MQBLK is zeroed).
;
;Call is:      S1/Number of nodes to send the message to
;              S2/Flag bits
;		  MQ%IBH This message is a response to an in behalf of message
;		  MQ%REM This message has an entry in the remote queue
;		  MQ%EXE This message is in behalf of an EXEC
;               M/Address of the IPCF message
;Returns true:  The message queue entry is built
;               S1/ Address of the message queue entry
;Returns false: Illegal number of nodes specified
;		S1/ The number of nodes specified

BLDMQE:	$SAVE	<P1,P2,P3,P4>		;SAVE THESE AC
	CAILE	S1,MAXNOD		;LESS THAN THE MAX NUMBER OF NODES?
	$RETF				;NO, RETURN FALSE
	SKIPG	S1			;POSITIVE NUMBER OF NODES?
	$RETF				;NO, RETURN FALSE

;DETERMINE IF THE SUM OF THE MQE AND THE IPCF MESSAGE LENGTHS FITS IN A PAGE

	DMOVEM	S1,P1			;SAVE THE NUMBER OF NODES AND FLAGS
	IMULI	S1,.MQBSZ		;SIZE OF THE LINK LIST WORDS
	MOVE	P4,S1			;REMEMBER THIS TO FIND INVARIANT BLOCK 
	ADDI	S1,.MQISZ		;OBTAIN MESSAGE QUEUE ENTRY SIZE

	LOAD	P3,.MSTYP(M),MS.CNT	;PICK UP SIZE OF THE IPCF MESSAGE
	ADD	P3,S1			;ADD MQE SIZE TO MESSAGE SIZE
	CAILE	P3,PAGSIZ		;TOTAL LENGTH FITS IN A PAGE?
	JRST	BLDMQ2			;NO, GET A PAGE FOR THE MESSAGE

;PREPARE THE MQE CONTIGUOUS WITH THE MESSAGE

	MOVE	S1,P3			;PLACE MEMORY BLOCK SIZE WHERE EXPECTED
	$CALL	M%GMEM			;PICK UP THE MEMORY BLOCK
	MOVE	P3,S2			;PICK UP THE MQE ADDRESS
	ADDI	P3,.MQISZ(P4)		;POINT TO THE MESSAGE
	JRST	BLDMQ3			;JOIN COMMON CODE

;PREPARE THE MQE SEPARATE FROM THE MESSAGE

BLDMQ2:	MOVE	P3,S1			;REMEMBER THE MQE SIZE
	$CALL	GETPAG			;GET A PAGE OF MEMORY
	MOVE	S1,P3			;PICK UP MQE SIZE
	MOVE	P3,MO			;PICK UP MESSAGE ADDRESS
	TXO	P3,MQ%PAG		;INDICATE MESSAGE IS A PAGE
	$CALL	M%GMEM			;PICK UP MEMORY FOR THE MQE

;BUILD THE INVARIANT BLOCK

BLDMQ3:	ADD	P4,S2			;POINT TO THE INVARIANT BLOCK
	MOVEM	P2,.MQNOM(P4)		;PLACE FLAGS IN THE INVARIANT BLOCK
	STORE	P1,.MQNOM(P4),MQ.NUM	;PLACE # OF NODES IN INVARIANT BLOCK
	STORE	P3,.MQNOM(P4),MQ.ADR	;PLACE MSG ADDRESS IN INVARIANT BLOCK

	STORE	S1,.MQMEM(P4),MQ.LEN	;SAVE GLXMEM LENGTH IN INVARIANT BLOCK
	STORE	S2,.MQMEM(P4),MQ.PTR	;SAVE GLXMEM ADDRESS IN INVARIANT BLOCK
	HLLZ	S1,P3			;ISOLATE THE PAGE (MQ%PAG) BIT
	IORM	S1,.MQMEM(P4)		;INDICATE IF THE MSG IS A PAGE OR NOT

	MOVE	S1,G$SND		;[6012]ASSUME MESSAGE FROM THE EXEC
	TXNE	P2,MQ%EXE		;[6012]IS IT?
	MOVEM	S1,.MQPID(P4)		;[6012]YES, SAVE ITS PID

;NOW BUILD THE LINK LIST WORDS. PLACE THE OFFSETS IN THE LINK LIST WORDS
;AND ASSUME THIS MESSAGE QUEUE ENTRY WILL BE ADDED TO THE END OF THE
;MESSAGE QUEUE.

	MOVE	P2,S2			;SAVE THE MQE ADDRESS FOR THE RETURN
BLDMQ4:	HRLZM	P1,0(S2)		;BUILD THE LINK LIST WORD FOR THIS NODE
	ADDI	S2,.MQBSZ		;POINT TO THE NEXT LINK LIST WORD
	SOJG	P1,BLDMQ4		;BUILD THE NEXT LINK LIST WORD

;MOVE THE MESSAGE FROM THE IPCF BUFFER TO THE MESSAGE QUEUE BUFFER

	MOVE	S1,M			;ADDRESS OF THE IPCF MESSAGE
	HRRZ	S2,P3			;ADDRESS OF THE MSG IN MQE
	$CALL	XFRMSG			;MOVE THE MESSAGE
	MOVE	S1,P2			;RETURN THE MQE ADDRESS
	$RETT				;RETURN TO THE CALLER
	SUBTTL	ADDIBH - ADD AN IN BEHALF OF QUEUE ENTRY

;ADDIBH adds an in behalf of queue entry to the end of the in behalf
;of queue.
;ADDIBH assumes that the link list word is zero (which is true since the
;queue entry is obtained from M%GMEM which zeros every word).
;
;Call is:  S1/The address of the in behalf of queue entry
;Returns:  The in behalf of queue entry has been added to the IBH queue.

;ADD THE ENTRY TO THE END OF THE IBH QUEUE. IF THE QUEUE IS EMPTY, THEN
;MAKE THE QUEUE HEADER WORD THE PREVIOUS ENTRY. UPDATE THE TRAILER WORD.

ADDIBH:	SKIPN	S2,IBQTRL		;PICK UP CONTENTS OF IBH TRAILER WORD
	MOVEI	S2,IBQHDR 		;MAKE THE HEADER THE PREVIOUS ENTRY
	STORE	S1,.IQLNK(S2),IQ.NXT	;SAVE ADR NEW ENTRY IN PREVIOUS ENTRY
	STORE	S2,.IQLNK(S1),IQ.PRE	;SAVE ADR PREVIOUS ENTRY IN NEW ENTRY
	MOVEM	S1,IBQTRL		;SAVE ADR NEW ENTRY IN TRAILER WORD

	$RET				;RETURN TO THE CALLER
	SUBTTL	FNDIBH - FIND AN IN BEHALF OF QUEUE ENTRY

;FNDIBH finds an in behalf of queue entry by searching on the ACK code.
;
;Call is:       S1/Local ACK code of the IPCF message
;Returns true:  S2/The address of the in behalf of queue entry
;Returns false: The queue entry was not found

FNDIBH:	SKIPN	S2,IBQHDR		;IS THE QUEUE EMPTY?
	JRST	FNDIB3			;YES, GO RETURN FALSE
FNDIB2:	CAMN	S1,.IQLNA(S2)		;IS THIS THE ENTRY LOOKING FOR?
	$RETT				;YES, RETURN TO THE CALLER
	LOAD	S2,.IQLNK(S2),IQ.NXT	;NO, GET THE NEXT ENTRY
	SKIPE	S2			;THE LAST ENTRY?
	JRST	FNDIB2			;NO, CHECK THE NEXT ENTRY
FNDIB3:	$RETF				;YES, RETURN FALSE
	SUBTTL	RELIBH - RETURNS AN IN BEHALF OF QUEUE ENTRY

;RELIBH delinks an in behalf of queue entry and returns the entry's
;memory to the memory manager. 
;
;Call is:       S1/ Address of the in behalf of queue entry to be released
;Returns true:  The in behalf of queue entry was released
;Returns false: The in behalf of queue entry was not found

RELIBH:	$SAVE	<P1>			;SAVE THIS AC

;SET UP THE NEW LINK POINTERS

	SKIPN	IBQHDR			;IS THE IBH QUEUE EMPTY?
	JRST	RELIB2			;YES, ENTRY NOT FOUND
	MOVE	P1,S1			;SAVE THE IBH QUEUE ENTRY
	
	LOAD	S1,.IQLNK(P1),IQ.NXT	;PICK UP NEXT ENTRY ADDRESS
	LOAD	S2,.IQLNK(P1),IQ.PRE	;PICK UP PREVIOUS ENTRY ADDRESS

	STORE	S1,.IQLNK(S2),IQ.NXT	;SAVE FORWARD PTER IN PREVIOUS ENTRY
	SKIPE	S1			;IS THIS ENTRY THE LAST ONE?
	STORE	S2,.IQLNK(S1),IQ.PRE	;NO, SAVE PREVIOUS ENTRY IN FRDWD PTR

;CHECK IF THE ENTRY BEING DELETED IS THE LAST ENTRY IN THE IBH QUEUE

	SKIPN	S1			;IS THE ENTRY THE LAST ENTRY?
	MOVEM	S2,IBQTRL		;YES, SO PREVIOUS ENTRY IS NOW LAST

;CHECK IF THE IN BEHALF OF QUEUE IS NOW EMPTY, IF SO, THEN ZERO OUT
;THE TRAILER HEADER WORD.

	SKIPN	IBQHDR			;IS THE IBH QUEUE NOW EMPTY?
	SETZM	IBQTRL			;YES, CLEAN THE TRAILER WORD

;RETURN THE MESSAGE QUEUE ENTRY TO THE MEMORY MANGAGER

	MOVE	S2,P1			;PLACE ADDRESS IN THE EXPECTED PLACE
	MOVEI	S1,.IQSIZ		;PICK UP THE SIZE OF THE ENTRY
	$CALL	M%RMEM			;RETURN THE IBH QUEUE ENTRY
	$RETT				;RETURN, THE QE HAS BEEN DELETED
RELIB2:	$RETF				;QUEUE ENTRY NOT FOUND
	SUBTTL	ADDGRQ - ADD A REMOTE QUEUE ENTRY TO THE GLOBAL REMOTE QUEUE

;ADDGRQ adds a remote queue entry to the global remote queue. Every remote
;queue entry is linked into the global remote queue. This is done in the
;event that a CANCEL DISMOUNT message is received. To locate the
;corresponding remote queue entry, the ACK code is used and the global
;remote queue is searched. If there were not a global remote queue, the
;remote queue of each node would need to be searched.
;
;Call is:  S1/The address of the remote queue entry invariant block
;Returns:  The remote queue entry has been added to the global 
;          remote queue.

;ADD THE ENTRY TO THE END OF THE GLOBAL REMOTE QUEUE. IF THE QUEUE IS EMPTY,
;THEN MAKE THE QUEUE HEADER WORD THE PREVIOUS ENTRY. UPDATE THE TRAILER WORD.

ADDGRQ:	SKIPN	S2,REMTRL		;PICK UP CONTENTS OF TRAILER WORD
	MOVEI	S2,REMHDR		;MAKE THE HEADER THE PREVIOUS ENTRY
	STORE	S1,.RQLNK(S2),RQ.NXT	;SAVE ADR NEW ENTRY IN PREVIOUS ENTRY
	STORE	S2,.RQLNK(S1),RQ.PRE	;SAVE ADR PREVIOUS ENTRY IN NEW ENTRY
	MOVEM	S1,REMTRL		;SAVE ADR NEW ENTRY IN TRAILER WORD

	$RET				;RETURN TO THE CALLER
	SUBTTL	FNDGRQ - FIND A GLOBAL REMOTE QUEUE ENTRY

;FNDGRQ finds a global remote queue entry by its ACK code.
;
;Call is:       S1/ACK code provided by MOUNTR
;Returns true:  S2/The address of the global remote queue entry's
;                  invariant block
;Returns false: The queue entry was not found

FNDGRQ:	SKIPN	S2,REMHDR		;IS THE QUEUE EMPTY?
	JRST	FNDGR3			;YES, GO RETURN FALSE
FNDGR2:	CAMN	S1,.RQACK(S2)		;IS THIS THE ENTRY LOOKING FOR?
	$RETT				;YES, RETURN TO THE CALLER
	LOAD	S2,.RQLNK(S2),RQ.NXT	;NO, GET THE NEXT ENTRY
	SKIPE	S2			;THE LAST ENTRY?
	JRST	FNDGR2			;NO, CHECK THE NEXT ENTRY
FNDGR3:	$RETF				;YES, RETURN FALSE
	SUBTTL	RELGRQ - RETURNS A GLOBAL REMOTE QUEUE ENTRY

;RELGRQ delinks a global remote queue entry and returns the entry's
;memory to the memory manager. RELGRQ assumes that the remote queue
;entry has been removed from all the nodes' remote queues
;
;Call is:       S1/ Address of the remote queue entry's invariant block
;Returns true:  The global remote queue entry was released
;Returns false: The global remote queue entry was not found

RELGRQ:	$SAVE	<P1>			;SAVE THIS AC

;SET UP THE NEW LINK POINTERS

	SKIPN	REMHDR			;IS THE GLOBAL REMOTE QUEUE EMPTY?
	JRST	RELGR2			;YES, ENTRY NOT FOUND
	MOVE	P1,S1			;SAVE THE GLOBAL RQE INVARIANT BLK ADR
	
	LOAD	S1,.RQLNK(P1),RQ.NXT	;PICK UP NEXT ENTRY ADDRESS
	LOAD	S2,.RQLNK(P1),RQ.PRE	;PICK UP PREVIOUS ENTRY ADDRESS

	STORE	S1,.RQLNK(S2),RQ.NXT	;SAVE FORWARD PTER IN PREVIOUS ENTRY
	SKIPE	S1			;IS THIS ENTRY THE LAST ONE?
	STORE	S2,.RQLNK(S1),RQ.PRE	;NO, SAVE PREVIOUS ENTRY IN FRDWD PTR

;CHECK IF THE ENTRY BEING DELETED IS THE LAST ENTRY IN THE GLOBAL REMOTE QUEUE

	SKIPN	S1			;IS THE ENTRY THE LAST ENTRY?
	MOVEM	S2,REMTRL		;YES, SO PREVIOUS ENTRY IS NOW LAST

;CHECK IF THE GLOBAL REMOTE QUEUE IS NOW EMPTY, IF SO, THEN ZERO OUT
;THE TRAILER HEADER WORD.

	SKIPN	REMHDR			;IS THE QUEUE NOW EMPTY?
	SETZM	REMTRL			;YES, CLEAN THE TRAILER WORD

;RETURN THE GLOBAL REMOTE QUEUE ENTRY TO THE MEMORY MANGAGER

	LOAD	S1,.RQMEM(P1),RQ.LEN	;PICK UP LENGTH OF THE REMOTE QE
	LOAD	S2,.RQMEM(P1),RQ.ADR	;PICK UP ADDRESS OF THE REMOTE QE
	$CALL	M%RMEM			;RETURN THE REMOTE QUEUE ENTRY
	$RETT				;RETURN, THE QE HAS BEEN DELETED
RELGR2:	$RETF				;REMOTE QUEUE ENTRY NOT FOUND
	SUBTTL	FNDNRQ - FIND A NODE'S REMOTE QUEUE ENTRY

;FNDNRQ finds a node's remote queue entry by searching on the ACK code.
;
;Call is:       S1/SIXBIT node name
;               S2/ACK code of the original IPCF message
;Returns true:  S1/The address of the invariant block
;               S2/The address of the remote queue entry link list word
;Returns false: The queue entry was not found

FNDNRQ:	$SAVE	<P1>			;SAVE THIS AC

;FIND THE NODE TABLE ENTRY ADDRESS FOR THIS NODE, PICK UP THE NODE'S
;REMOTE QUEUE HEADER WORD. IF THIS NODE'S REMOTE QUEUE IS EMPTY, THEN RETURN

	MOVE	P1,S2			;SAVE THE ACK CODE
	$CALL	SNAMNT			;PICK UP THE NODE TABLE ENTRY ADDRESS
	JUMPF	FNDNR3			;RETURN IF NODE NOT FOUND
	SKIPN	S2,.NRQLH(S2)		;IS THIS NODE'S REMOTE QUEUE EMPTY?
	JRST	FNDNR3			;YES, GO RETURN FALSE

;SEARCH FOR THE REMOTE QUEUE ENTRY BY USING THE ACK CODE

FNDNR2:	LOAD	S1,.RQBLK(S2),RQ.OFF	;PICK UP THE OFFSET
	ADD	S1,S2			;POINT TO THE INVARIANT BLOCK
	CAMN	P1,.RQACK(S1)		;IS THIS THE ENTRY LOOKING FOR?
	$RETT				;YES, RETURN TO THE CALLER
	LOAD	S2,.RQBLK(S2),RQ.NEA	;NO, GET THE NEXT ENTRY
	SKIPE	S2			;THE LAST ENTRY?
	JRST	FNDNR2			;NO, CHECK THE NEXT ENTRY
FNDNR3:	$RETF				;YES, RETURN FALSE
	SUBTTL	RELNRQ - RETURNS A NODE'S REMOTE QUEUE ENTRY

;RELNRQ delinks a node's remote queue entry.
;
;Call is: S1/Address of the node's remote queue entry's forward
;            link list word
;         S2/Address of the node's remote queue entry's backwards
;            link list word
;         P4/Node table entry address
;Returns: The node's remote queue entry was released

RELNRQ:	$SAVE	<P1,P2,P3,P4>			;SAVE THESE AC

;SET UP THE NEW LINK POINTERS

	LOAD	P1,.RQBLK(S1),RQ.NEA	;PICK UP NEXT ENTRY ADDRESS
	LOAD	P2,.RQNST(S2),RQ.ERR	;PICK UP PREVIOUS ENTRY ADDRESS

	STORE	P1,.RQBLK(P2),RQ.NEA	;SAVE FORWARD PTER IN PREVIOUS ENTRY
	JUMPE	P1,RELNR2		;IS THIS ENTRY THE LAST ONE?

;THE NEXT ENTRY'S PREVIOUS ENTRY LINK LIST WORD IS LOCATED IN THE NODE
;BLOCK. THEREFORE, THE NODE BLOCK MUST BE FOUND. THIS IS DONE BY ADDING
;THE NEXT ENTRY'S OFFSET TO ITS ADDRESS TO FIND THE INVARIANT BLOCK.
;THE SIZE OF THE INVARIANT BLOCK IS ADDED TO THIS TO POINT TO THE FIRST
;NODE BLOCK. THE POSITION OF THE NODE BLOCK IS FOUND BY NOTING THE 
;RELATIONSHIP: NODE POSITION = NUMBER OF NODES - OFFSET.
;THIS VALUE IS MULTIPLIED BY THE SIZE OF A NODE BLOCK AND ADDED TO THE
;POINTER TO THE FIRST NODE BLOCK TO GET THE NODE BLOCK ADDRESS.

	LOAD	S1,.RQBLK(P1),RQ.OFF	;PICK UP NEXT ENTRY OFFSET VALUE
	MOVE	P3,S1			;REMEMBER THE OFFSET
	ADD	S1,P1			;POINT TO NEXT ENTRY INVARIANT BLOCK
	LOAD	S2,.RQNUM(S1),RQ.NTS	;PICK UP NUMBER OF NODES IN THE RQE
	SUB	S2,P3			;POSITION OF NODE BLK IN NODE BLK LIST
	IMULI	S2,.RQNSZ		;CALCULATE THE BLOCK DISPLACEMENT
	ADDI	S1,.RQNIZ(S2)		;POINT TO THE DESIRED NODE BLOCK
	STORE	P2,.RQNST(S1),RQ.ERR	;SAVE PREVIOUS ENTRY IN THIS NODE'S
					;PREVIOUS POINTER
	SKIPA				;GO CHECK IF IBH QUEUE IS NOW EMPTY
RELNR2:	MOVEM	P2,.NRQLT(P4)		;PREVIOUS ENTRY IS NOW LAST

;CHECK IF THE NODE'S REMOTE QUEUE IS NOW EMPTY, IF SO, THEN ZERO OUT
;THE TRAILER HEADER WORD.

	SKIPN	.NRQLH(P4)		;IS THE IBH QUEUE NOW EMPTY?
	SETZM	.NRQLT(P4)		;YES, CLEAN THE TRAILER WORD
	$RET				;RETURN, THE QE HAS BEEN DELETED
	SUBTTL	SNAMNT - SEARCH NODE TABLE BY NODE NAME

;SNAMNT searches the node table by using the SIXBIT node name
;
;Call is:       S1/SIXBIT node name
;Returns true:  S1/SIXBIT node name
;               S2/Address of the node table entry for the found node
;Returns false: Node table entry not found
;               S1/SIXBIT node name
;               S2/-1

SNAMNT:	SKIPG	RENNUM			;ANY REMOTE NODES?
	JRST	SNAMN3			;NO, RETURN NOW
	$SAVE	<P1>			;SAVE THIS AC
	MOVEI	S2,NODTBL		;PICK UP THE NODE TABLE ADDRESS
	MOVEI	P1,MAXNOD		;NUMBER OF ENTRIES IN NODE TABLE
SNAMN2:	CAMN	S1,.NNNAM(S2)		;DO THE NAMES MATCH?
	$RETT				;YES, RETURN TRUE
	ADDI	S2,.NNNSZ		;POINT TO THE NEXT NODE ENTRY
	SOJG	P1,SNAMN2		;CHECK OUT THE NEXT NODE ENTRY
SNAMN3:	SETO	S2,			;NODE NAME NOT FOUND IN THE NODE TABLE
	$RETF				;RETURN FALSE
	SUBTTL	SNUMNT - SEARCH NODE TABLE BY NODE NUMBER

;SNUMNT searches the node table by using the cluster node number
;
;Call is:       S1/Cluster node number
;Returns true:  S1/Cluster node number
;               S2/Address of the node table entry for the found node
;Returns false: S1/Cluster node number
;               S2/-1
;

SNUMNT:	$SAVE	<P1,P2>			;SAVE THESE AC
	MOVEI	S2,NODTBL		;PICK UP ADDRESS OF THE NODE TABLE
	MOVEI	P1,MAXNOD		;MAX NUMBER OF ENTRIES IN NODE TABLE
SNUMN2:	LOAD	P2,.NNSTA(S2),NS.NUM	;PICK UP THE NODE NUMBER FOR THIS ENTRY
	CAMN	S1,P2			;DO THE NODE NUMBERS MATCH?
	$RETT				;YES, RETURN TRUE
	ADDI	S2,.NNNSZ		;NO, POINT TO THE NEXT ENTRY
	SOJG	P1,SNUMN2		;CHECK THE NEXT NODE TABLE ENTRY
	SETO	S2,			;NODE NUMBER NOT FOUND IN NODE TABLE
	$RETF				;RETURN FALSE
	SUBTTL	LISTEN - MESSAGE SERVER FOR A REMOTE NODE

;LISTEN exists as an inferior fork in NEBULA. A listener is started for
;every remote node known to NEBULA that is running a release 7 or later
;monitor and which has DECnet enabled. A listener picks up messages from
;a remote node. The messages are of two types:
;1. Requests to be processed on the listener's node
;2. Responses to requests that originated on the listener's node but 
;   were processed on the sender's node.
;A listener communicates with the top fork through software interrupts,
;the listener block and the node's status words in its node table entry.

;INITIALIZATION BLOCK AND PID BLOCK

LIB:	$BUILD	IB.SZ
	  $SET	(IB.PRG,,%%.MOD)	;PROGRAM 'NEBULA'
	  $SET  (IB.FLG,IP.STP,1)	;STOPCODES TO ORION
	$EOB				;

LPIB:	$BUILD	PB.MNS			;
	  $SET	(PB.HDR,PB.LEN,PB.MNS)	;PIB LENGTH,,0
	  $SET	(PB.FLG,IP.RSE,1)	;RETURN ON SEND ERROR
	  $SET	(PB.SYS,IP.BQT,-1)	;MAXIMUM SEND/RECEIVE IPCF QUOTA
	  $SET	(PB.SYS,IP.MNP,^D1)	;NUMBER OF PIDS
	$EOB				;

LISCHN:	XWD	1,ACCEPT		;A DECNET CONNECTION REQUEST OCCURRED
	XWD	1,MSGFSN		;MESSAGE FROM A SENDER IS AVAILABLE
	XWD	1,MSGTTF		;TOP FORK READY TO PROCESS A MESSAGE
	BLOCK	^D33			;THESE CHANNELS ARE NOT USED

;SET UP GLXLIB, ENABLE CAPABILITIES, AND SET UP INTERRUPT SYSTEM

LISTEN:	SKIPE	DEBUGW			;DEBUGGING?
	$CALL	NEBDDT			;YES, SET UP FOR DEBUGGING

	$CALL	LISSET			;SET UP GLXLIB AND CAPABILITIES
	$CALL	LOPLNK			;OPEN A SRV: DEVICE
	$CALL	LISINT			;SET UP THE INTERRUPT SYSTEM

;WAIT FOR A CONNECTION REQUEST, FOR INCOMING DECNET MESSAGES AND FOR
;REQUESTS FROM THE TOP FORK FOR A MESSAGE.

LISTE2:	SETZ	S1,			;INDEFINITE TIME TO WAIT
	$CALL	I%SLP			;WAIT% UNTIL NEEDED
	JRST	LISTE2			;WAIT% FOR THE NEXT EVENT
	SUBTTL	LISSET - INITIALIZE THE LISTENER'S GLXLIB AND CAPABILITIES

;LISSET is called by the listener at listener startup. This routine sets up
;GLXLIB, the listener's capabilities and disables the listener from receiving
;any IPCF messages.
;
;Call is: LIS/Address of the listener block
;Returns: GLXLIB setup and capabilities enabled
;Crashes: Unable to set up capabilities

LISSET:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;SET UP THE GLXLIB INITIALIZATION BLOCK IN THE LISTENER BLOCK

	MOVSI	S1,LIB			;PICK UP ADDRESS OF THE IB BLOCK
	HRRI	S1,.LSIBK(LIS)		;ADDRESS OF WHERE TO PLACE THE IB BLOCK
	MOVEI	S2,.LSIBK+IB.SZ(LIS)	;END ADDRESS + 1
	BLT	S1,-1(S2)		;MOVE THE IB BLOCK TO LISTENER BLOCK

	MOVEI	S1,.LSPIB(LIS)		;PICK UP THE PID ADDRESS
	MOVEM	S1,.LSIBK+IB.PIB(LIS)	;PLACE IN THE INITIALIZATION BLOCK
	MOVSI	S1,.LSLEV(LIS)		;ADDRESS OF THE INTERRUPT LEVEL TABLE
	HRRI	S1,LISCHN		;ADDRESS OF THE CHANNEL TABLE
	MOVEM	S1,.LSIBK+IB.INT(LIS)	;PLACE IN THE INITIALIZATION BLOCK

;SET UP THE PID BLOCK AND THE INTERRUPT LEVEL TABLE IN THE LISTENER BLOCK

	MOVSI	S1,LPIB			;PICK UP ADDRESS OF THE PID BLOCK
	HRRI	S1,.LSPIB(LIS)		;DESTINATION IS IN THE LISTENER BLOCK
	MOVEI	S2,.LSPIB+PB.MNS(LIS)	;END ADDRESS + 1
	BLT	S1,-1(S2)		;MOVE PID TABLE TO LISTENER BLOCK

	MOVEI	S1,.LSLEV(LIS)		;PICK UP ADR OF INTERRUPT LEVEL TABLE
	MOVEI	S2,.LS1PC(LIS)		;PICK UP ADR OF FIRST PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE
	AOS	S1			;POINT TO NEXT INTERRUPT LEVEL WORD
	AOS	S2			;POINT TO NEXT PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE
	AOS	S1			;POINT TO NEXT INTERRUPT LEVEL WORD
	AOS	S2			;POINT TO NEXT PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE

;SET UP GLXLIB 

	MOVEI	S1,IB.SZ		;PICK UP SIZE OF THE INITIALIZATION BLK
	MOVEI	S2,.LSIBK(LIS)		;PICK UP ADR OF THE INITIALIZATION BLK
	$CALL	I%INIT			;INITIALIZE GLXLIB

;ENABLE THE LISTENER'S CAPABILITIES TO BE THOSE OF THE TOP FORK AND GIVE IT
;THE CAPABILITY TO INTERRUPT THE TOP FORK.

	MOVX	S1,.FHSLF		;PICK UP THE LISTENER'S HANDLE
	RPCAP%	
	 ERJMP	[$CALL INLCRH		  ;INDICATE A CONTROLLED CRASH
		 $STOP(LCC,Listener can't obtain capabilities) ]
	TXO	S2,SC%SUP		;CAPABILITY TO INTERRUPT TOP FORK
	MOVE	T1,S2			;ENABLE ALL CAPABILITIES
	MOVEI	S1,.FHSLF		;PICK UP THE LISTENER'S HANDLE
	EPCAP%				;ENABLE THE CAPABILITIES
	 ERJMP	[$CALL INLCRH		  ;INDICATE A CONTROLLED CRASH
		 $STOP(LCE,Listener can't enable capabilities) ]

;DISABLE RECEIVING IPCF MESSAGES

	MOVEI	S1,.MUDIS		;DISABLE RECEIVING IPCF MESSAGES
	MOVEM	S1,.LSMUT(LIS)		;PLACE IN THE ARGUMENT BLOCK
	MOVE	S1,.LSPIB+PB.PID(LIS)	;PICK UP LISTENER'S PID
	MOVEM	S1,.LSMUT+1(LIS)	;PLACE IN THE ARGUMENT BLOCK
	MOVEI	S1,2			;PICK UP SIZE OF ARGUMENT BLOCK
	MOVEI	S2,.LSMUT(LIS)		;PICK ADDRESS OF THE ARGUMENT BLOCK
	MUTIL%				;DISABLE RECEIVING IPCF MESSAGES
	 ERJMP	.+1			;SHOULDN'T HAPPEN, BUT DON'T CARE

;CREATE THE MESSAGE LIST

	$CALL	L%CLST			;PICK UP THE MESSAGE LIST INDEX
	MOVEM	S1,.LSHWD(LIS)		;PLACE IN THE LISTENER BLOCK

	$RET				;RETURN TO STARTUP
	SUBTTL	LOPLNK - OPEN A DECNET SRV: DEVICE

;LOPLNK is called during the listener's initialization to open a SRV:
;device. It is also called by ACCEPT if an MTOPR% error has occurred
;and the link must be re-opened.
;
;Call is: LIS/Address of the listener block
;Returns: The SRV: device has been open
;Crashes: Unable to obtain a JFN or open the SRV: device

LOPLNK:	$SAVE	<T1,T2,T3>		;SAVE THESE AC

;PICK UP THE SRV: JFN AND OPEN THE SRV: DEVICE

	MOVX S1,GJ%SHT			;SHORT JFN
	HRROI	S2,.LSSRV(LIS)		;POINT TO THE DEVICE NAME
	GTJFN%
	 ERJMP	LOPLN5			;CRASH IF CAN'T GET JFN
	HRRZS	S1			;ISOLATE THE JFN
	MOVEM	S1,.LSJFN(LIS)		;SAVE THE JFN FOR LATER
	MOVNI	T3,^D60			;[6021]NEGATIVE NUMBER OF OPEN TRIES	
	SKIPA				;[6021]SKIP THE FIRST TIME THROUGH
LOPLN3:	MOVE	S1,.LSJFN(LIS)		;[6021]PICK UP THE JFN
	MOVX S2,<FLD(^D36,OF%BSZ)+OF%WR+OF%RD> ;OPEN FOR READ AND WRITE
	OPENF% 
	 ERJMP	LOPLN4			;RETRY ON AN ERROR
	$RET				;RETURN TO THE CALLER

LOPLN4:	AOSN	T3			;[6021]TIME TO QUIT?
	JRST	LOPLN5			;[6021]YES, CRASH
	MOVEI	S1,MINTIM		;[6021]PICK UP THE TIME TO SLEEP
	$CALL	I%SLP			;[6021]DISMISS FOR AWHILE
	JRST	LOPLN3			;[6021]RE-ATTEMPT TO OPEN THE LINK

LOPLN5:	$CALL	S%ERR			;[6021]PICK UP THE ERROR STRING
	JUMPF	LOPLN6			;[6021]STOPCODE ON AN UNKNOWN ERROR
	$WTOJ	(<Unable to obtain DECnet connection>,<Listener for node ^N/.LSNME(LIS)/ has encountered an error.
The last error is: ^T/0(S1)/>,,<$WTFLG(WT.SJI)>)
	$CALL	INLCRH			;[6021]INDICATE A CONTROLLED CRASH
	HALTF%				;[6021]HAVE TOP FORK RESTART

LOPLN6:	$CALL	INLCRH			;[6021]INDICATE A CONTROLLED CRASH
	$STOP	(LOD, LISTENER CAN'T OPEN DECNET DEVICE) ;[6021]
	SUBTTL	LISINT - SET UP THE LISTENER'S INTERRUPT SYSTEM

;LISINT is called by the listener during listener startup. LISINT sets up
;the listener's interrupt system.
;
;Call is: LIS/Address of the listener block
;Returns: The interrupt system has been set up
;Crashes: The interrupt system could not be set up


LISINT:	$SAVE	<T1,T2>			;SAVE THESE AC
	MOVEI	S1,.FHSLF		;PICK UP THE LISTENER'S HANDLE
	SETO	S2,			;INDICATE DISABLE ALL 36 CHANNELS
	DIC%				;DISABLE THE CHANNELS
	 ERJMP	.+1			;SHOULDN'T HAPPEN, BUT IGNORE
	CIS%				;CLEAR THE INTERRUPT SYSTEM
	 ERJMP	.+1			;SHOULDN'T HAPPEN, BUT IGNORE
	MOVEI	S1,.FHSLF		;PICK UP THE LISTENER'S HANDLE
	HRLI	S2,.LSLEV(LIS)		;PICK UP INTERRUPT LEVEL TABLE ADDRESS
	HRRI	S2,LISCHN		;PICK UP CHANNEL TABLE ADDRESS
	SIR%				;SET UP THE INTERRUPT TABLE ADDRESSES
	 ERJMP	LISIN2			;CRASH IF CAN'T SET UP 
	MOVEI	S1,.FHSLF		;PICK UP THE LISTENER'S HANDLE
	EIR%				;ENABLE THE INTERRUPT SYSTEM
	 ERJMP	LISIN2			;CRASH IF CAN'T ENABLE INTERRUPT SYSTEM
	MOVEI	S1,.FHSLF		;PICK UP THE LISTENER'S HANDLE
	MOVX	S2,1B0+1B1+1B2		;PICK UP CHANNELS TO ACTIVATE
	AIC%				;ACTIVATE THE CHANNELS
	 ERJMP	LISIN2			;CRASH IF CAN'T ACTIVATE THE CHANNELS

	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVEI	S2,.MOACN		;ENABLE SOFTWARE INTERRUPTS
	MOVX	T1,<FLD(0,MO%CDN)+FLD(1,MO%DAV)+FLD(.MONCI,MO%INA)>
	MTOPR%				;CONNECT EVENT PENDING/DATA AVAILABLE
	 ERJMP	LISIN2			;CRASH IF CAN'T SET UP CHANNELS
	$RETT				;RETURN TO LISTENER STARTUP

LISIN2:	$CALL	INLCRH			;INDICATE CONTROLLED CRASH
	JRST	S..CSI			;CANNOT SET UP THE INTERRUPT SYSTEM
	SUBTTL	ACCEPT - VALIDATE A DECNET CONNECTION REQUEST

;ACCEPT is the listener's interrupt handler for DECnet connection requests.
;ACCEPT validates a sender's request for a DECnet connection. The following
;three checks are made:
;1. The sender must be from the node that the listener expects the sender
;   to be from.
;2. The sender's name must be passed as the USERID argument and must be
;   in the form expected by the listener. the expected form is:
;   SNODE$NEBULA$SN
;   where SNODE is the sender's node name
;3. The optional data (BDATA) field must contain the value that results from
;   the following algorithm:
;   a. The listener's node name expressed in SIXBIT is rotated left by 3 bits
;   b. This value is then converted into 4 octal 8 bit bytes
;If the sender fails to pass the three checks, then the sender's DECnet
;connection request is rejected with reason "Reject or disconnect by
;object" (error .DCX0). ORION is informed of the rejection and the
;bit NL%HMR (HELLO message received from a non-NEBULA) is set in the listener
;status word in the node table entry.
;If the sender passes the three checks, then its connection request is 
;accepted, ORION is informed, bit NL%NRH (inform the top fork that a HELLO
;has been accepted) is set in the listener status word in the node table
;entry, and the top fork is interrupted to indicate that the listener has
;accepted a DECnet connection.
;
;Call is: LIS/Address of the listener block
;Returns: The connecton request has been accepted or rejected
;Crashes: Cannot obtain the information to validate the connection request
;         or cannot interrupt the top fork

ACCEPT:	$BGINT	1,
	
;FIRST DETERMINE IF THE CONNECTION REQUEST IF FROM THE EXPECTED NODE

	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVEI	S2,.MORHN		;WANT THE SENDER'S NODE NAME
	HRROI	T1,.LSANN(LIS)		;WHERE TO PLACE THE NODE NAME
	MTOPR%				;PICK UP THE SENDER'S NODE NAME
	 ERJMP	ACCEP4			;[6021]CHECK THE STATE OF THE LINK
	
	HRROI	S1,.LSANN(LIS)		;PICK UP THE SENDER'S NODE NAME
	$CALL	S%SIXB			;CHANGE IT TO SIXBIT
	CAME	S2,.LSNME(LIS)		;COMPARE WITH EXPECTED NODE NAME
	JRST	ACCEP3			;NOT THE SAME, REJECT THIS REQUEST

;CHECK IF THE SENDER'S NAME IS VALID

	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVEI	S2,.MORUS		;WANT THE SENDER'S USER NAME
	HRROI	T1,.LSUSR(LIS)		;WHERE TO PLACE THE USER NAME
	MTOPR%				;PICK UP THE USER NAME
	 ERJMP	ACCEP4			;[6021]CHECK THE STATE OF THE LINK
	
	HRROI	S1,.LSSNE(LIS)		;POINT TO THE EXPECTED SENDER'S NAME
	HRROI	S2,.LSUSR(LIS)		;POINT TO THE SENDER'S NAME
	$CALL	S%SCMP			;COMPARE THE TWO NAMES
	SKIPE	S1			;ARE THE NAMES THE SAME?
	JRST	ACCEP3			;NO, SO REJECT THIS REQUEST

;CHECK THE OCTAL DATA FIELD
;**;[6037]Add the following:
;Check whether there is a PASSWORD with the connect initiate message.
;If there is a PASSWORD then we know that the remote node can handle the new
;message format.  Include this inform in the node database.

	MOVE	S1,.LSJFN(LIS)		;[6037]Pick up the SRV: device JFN
	MOVEI	S2,.MORPW		;[6037]Want the PASSWORD
	MOVE	T1,[POINT 7,PASSWD]	;[6037]WHERE TO PLACE THE OCTAL DATA
	MTOPR%				;[6037]Pick up the PASSWORD
	 ERJMP	ACCEP4			;[6037CRASH IF CAN'T OBTAIN INFORMATION
	JUMPE	T2,ACCEP7		;[6037]NO PASSWORD

;Set a bit in the node data base to say that it is post field image

	MOVE	S1,.LSNTA(LIS)		;[6037]GET THE NODE ENTRY ADDRESS
	MOVX	S2,NN%PAS		;[6037]GET THE BIT
	IORM	S2,.NNSTA(S1)		;[6037]SET IT

;THE SENDER HAS PASSED THE VALIDITY CHECKS. ACCEPT THE DECNET CONNECTION
;REQUEST, NOTIFY THE TOP FORK OF THE CONNECTION AND NOTIFY ORION OF THE
;CONNECTION.

ACCEP7:	MOVE	S1,.LSJFN(LIS)		;[6037]PICK UP SRV: DEVICE JFN
	MOVEI	S2,.MOCC		;THE CONNECTION WILL BE ACCEPTED
	SETZB	T1,T2			;NO OPTIONAL DATA
	MTOPR%				;ACCEPT THE CONNECTION
	 ERJMP	ACCEP4			;[6021]CHECK THE STATE OF THE LINK

	MOVE	S1,.LSNTA(LIS)		;PICK UP NODE TABLE ENTRY ADDRESS
	MOVX	S2,NL%NRH		;INDICATE HAVE A CONNECTION
	IORM	S2,.NLSTA(S1)		;PLACE IN THE STATUS WORD
	MOVEI	S1,.FHSUP		;PICK UP THE TOP FORK'S HANDLE
	MOVX	S2,<1B3>		;ON THIS CHANNEL
	IIC%				;TELL THE TOP FORK OF THE CONNECTION
	 ERJMP	S..LCI			;CRASH IS CAN'T INTERRUPT TOP FORK

	$WTOJ	(<DECnet connection accepted>,<NEBULA's listener has accepted a DECnet connection from
node ^N/.LSNME(LIS)/>,,<$WTFLG(WT.SJI)>)
	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT

;THE SENDER'S DECNET CONNECTION REQUEST HAS BEEN DENIED OR AN ERROR HAS 
;OCCURRED. REJECT 
;CONNECTION, INDICATE THAT THE CONNECTION HAS BEEN REJECTED IN THE LISTENER'S
;STATUS WORD IN THE NODE TABLE ENTRY AND INFORM ORION OF THE REJECTION.

ACCEP3:	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVEI	S2,.MOCLZ		;WILL REJECT THIS REQUEST
	SETZB	T1,T2			;NO OPTIONAL DATA
	MTOPR%				;REJECT THE REQUEST
	 ERJMP	ACCEP4			;[6021]CHECK THE STATE OF THE LINK
	$WTOJ	(<DECnet connection rejected>,<DECnet connection from node ^N/.LSNME(LIS)/ rejected>,,<$WTFLG(WT.SJI)>)

	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT

;A MTOPR% ERROR HAS OCCURRED. CLOSE THE JFN WITH ABORT AND RE-OPEN IT.

ACCEP4:	MOVE	S1,.LSJFN(LIS)		;[6021]PICK UP THE SRV: DEVICE JFN
	CLOSF%				;[6021]CLOSE THE SRV: DEVICE
	 ERJMP	ACCEP5			;[6021]GIVE UP ON AN ERROR
	$CALL	LOPLNK			;[6021]RE-OPEN THE LINK
	$DEBRK				;[6021]RETURN TO THE PREVIOUS CONTEXT

ACCEP5:	$CALL	INLCRH			;[6021]INDICATE A CONTROLLED CRASH
	$STOP	(CVC, LISTENER CAN'T VALIDATE A CONNECTION REQUEST) ;[6021]
	SUBTTL	MSGFSN - DECNET MESSAGE FROM SENDER IS AVAILABLE

;MSGFSN is the interrupt handler for processing DECnet messages from the
;sender. If the top fork is not busy and the listener message queue is
;empty, then the DECnet message is transferred to the listener message
;buffer and the top fork is notified. If the top fork is not busy and
;the listener message queue is not empty, then the message is placed on
;the end of the queue. The first message in the listener message queue
;is transferred to the listener message buffer and the top fork is notified.
;If the top fork is busy, then the message is placed on the message queue.
;Upon picking up the message the listener ACKs the remote sender.
;
;Call is: LIS/Address of the listener block
;Returns: The message has been processed
;Crashes: The link status cannot be obtained, the link is no longer connected,
;         the message cannot be picked up, or the top fork cannot be notified

MSGFSN:	$BGINT	1,

;CHECK IF THERE IS A MESSAGE

	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	SIBE%				;IS THERE A MESSAGE?
	JRST	MSGFS1			;YES, PICK IT UP

;CHECK FOR A SPURIOUS INTERRUPT

	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV DEVICE JFN
	MOVEI	S2,.MORLS		;WANT TO CHECK THE LINK STATUS
	MTOPR%				;PICK UP THE LINK STATUS
	 ERJMP	[$CALL INLCRH		  ;INDICATE A CONTROLLED CRASH
		 JRST S..COL ]		  ;CAN'T OBTAIN THE LINK STATUS
	MOVEM	T1,.LSLNK(LIS)		;SAVE THE LINK STATUS
	TXNE	T1,MO%CON		;LINK STILL CONNECTED?
	JRST	MSGFS5			;YES, A SPURIOUS INTERRUPT OCCURRED
	JRST	MSGFS6			;NO, TELL ORION AND THEN CRASH

;CHECK IF THE TOP FORK IS READY TO PICK UP THE MESSAGE. IF IT IS AND THE
;MESSAGE QUEUE IS EMPTY, THEN PLACE THE MESSAGE IN THE MESSAGE BUFFER

MSGFS1:	SKIPL	.LSAVA(LIS)		;IS THE TOP FORK FREE?
	JRST	MSGFS4			;NO, SO PLACE MESSAGE ON MESSAGE QUEUE
	SKIPE	.LSUSM(LIS)		;IS THE MESSAGE QUEUE EMPTY?
	JRST	MSGFS3			;NO, PLACE MESSAGE ON MESSAGE QUEUE
	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JRN
	MOVE	S2,.LSMSG(LIS)		;PICK UP THE MESSAGE BUFFER ADDRESS
	HRLI	S2,(POINT 36)		;MAKE IT INTO A POINTER
	MOVNI	T1,PAGSIZ		;ASSUME MAXIMUM (DON'T TRUST SIBE%)
	SINR%				;PICK UP THE MESSAGE
	 ERJMP	[$CALL INLCRH		  ;INDICATE CRASH IN NODE STATUS WORD
		 JRST S..LUP ]		  ;SENDER UNABLE TO PICK UP MESSAGE

;SEND AN ACK TO THE SENDER AND INTERRUPT THE TOP FORK THAT A MESSAGE IS
;AVAILABLE

	$CALL	SENACK			;SEND THE ACK MESSAGE
	MOVEI	S1,.FHSUP		;PICK UP TOP FORK'S HANDLE
	MOVX	S2,<1B0>		;CHANNEL TO INTERRUPT ON
	SETZM	.LSAVA(LIS)		;INDICATE THAT THE TOP FORK IS BUSY
	IIC%				;INTERRUPT THE TOP FORK
	 ERJMP	[$CALL INLCRH		  ;INDICATE A CONTROLLED CRASH
		 JRST S..LCI ]		  ;CANNOT INTERRUPT THE TOP FORK
	JRST	MSGFS5			;RETURN TO THE PREVIOUS CONTEXT

;THE TOP FORK IS FREE, BUT THE MESSAGE QUEUE IS NOT EMPTY. OBTAIN A PAGE
;FOR THE MESSAGE AND PICK IT UP. PLACE THE MESSAGE ON THE MESSAGE QUEUE.
;MOVE THE FIRST MESSAGE FROM THE MESSAGE QUEUE TO THE MESSAGE BUFFER AND
;INTERRUPT THE TOP FORK.

MSGFS3:	$CALL	GETSMG			;PICK UP THE MESSAGE AND QUEUE IT
	$CALL	XFRTOP			;PLACE A MSG IN MSG BUFFER, INFORM T.F.
	JRST	MSGFS5			;RETURN TO THE PREVIOUS CONTEXT

;THE TOP FORK IS NOT FREE. IF THE MESSAGE IS VALID, THEN PLACE ON THE MESSAGE
;QUEUE.

MSGFS4:	$CALL	GETSMG			;PICK UP THE MESSAGE AND QUEUE IT
MSGFS5:	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT

;THE LINK IS NO LONGER CONNECTED. INFORM ORION OF THIS AND THEN CRASH

MSGFS6:	$WTOJ	(<NEBULA lost connection>,<NEBULA's listener to node ^N/.LSNME(LIS)/ has lost its DECnet connection>,,<$WTFLG(WT.SJI)>)
	$CALL	INLCRH			;INDICATE CRASH IN NODE STATUS WORD 	
	HALTF				;CRASH
	
	SUBTTL	GETSMG - PICK UP MESSAGE FROM SENDER AND PLACE ON MESSAGE QUEUE

;GETSMG is called when a message is available from the sender and the 
;listener's message queue is not empty. The message is picked up,
;placed on the message queue and an ACK message is sent to the sender.
;
;Call is: LIS/Address of the listener block
;Returns: The message has been placed on the message queue.

GETSMG:	$SAVE	<T1,T2>			;SAVE THESE AC

;PICK UP THE MESSAGE AND VALIDATE IT

	$CALL	GETPAG			;PICK UP A PAGE FROM MEMORY MANAGER
	MOVE	S2,MO			;PICK UP THE PAGE ADDRESS
	HRLI	S2,(POINT 36,)		;MAKE IT INTO A POINTER
	MOVE	S1,.LSJFN(LIS)		;PICK UP SRV: DEVICE JFN
	MOVNI	T1,PAGSIZ		;PICK UP MAXIMUM MESSAGE SIZE
	SINR%				;PICK UP THE MESSAGE
	 ERJMP	[$CALL INLCRH		  ;INDICATE CRASH IN NODE STATUS WORD
		 JRST S..LUP ]		  ;SENDER UNABLE TO PICK UP MESSAGE

;PLACE THE MESSAGE ON THE MESSAGE QUEUE AND ACK THE SENDER

	$CALL	ADDLME			;PLACE MESSAGE ON THE MESSAGE QUEUE
	$CALL	SENACK			;ACK THE SENDER
	$RET				;RETURN TO THE CALLER
	SUBTTL MSGTTF - TOP FORK READY FOR A MESSAGE FROM A LISTENER

;MSGTTF is the interrupt handler used when the top fork is free to process
;another message from the listener. MSGTTF first checks if the top fork
;is busy. (This can happen if a "DECnet message from the sender" interrupt
;happens to occur between the time the top fork has set its free to process
;a message flag (.LSAVA) and the time it interrupts the listener on this
;channel. The interrupt routine MSGFSN, in this case, detects that the top
;fork is not busy. MSGFSN then places a message in the message buffer, changes
;the state of the top fork to busy and interrupts the top fork.)
;If the top fork is not busy, then MSGTTF checks if the listener message
;queue is empty. If it is, then it quits, otherwise, it moves a message
;from the message queue to the listener message buffer and interrupts the
;top fork.
;
;Call is: LIS/Address of the listener block
;Returns: A message, if there is one and if the top fork is not busy,
;         has been placed in the message buffer and the top fork has
;         been notified of the message
;Crashes: The top fork cannot be interrupted

MSGTTF:	$BGINT	1,
	SKIPL	.LSAVA(LIS)		;IS THE TOP FORK BUSY?
	$DEBRK				;YES, SO QUIT NOW
	SKIPN	.LSUSM(LIS)		;NO, IS MESSAGE QUEUE EMPTY?
	$DEBRK				;YES, SO QUIT NOW
	$CALL	XFRTOP			;GIVE A MESSAGE TO THE TOP FORK
	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT
	SUBTTL	XFRTOP - MOVE MESSAGE FROM MESSAGE QUEUE TO MESSAGE BUFFER

;XFRTOP is called to transfer a message from the listener's message queue
;to its message buffer and then to interrupt the top fork that there is
;a message available for it to process. The message is then deleted from
;the message queue.
;
;Call is: LIS/Address of the listener block
;Returns: A message has been placed in the listener buffer and the top
;         fork has been notified
;Crashes: Unable to interrupt the top fork

;MOVE THE MESSAGE FROM THE MESSAGE QUEUE TO THE MESSAGE BUFFER

XFRTOP:	MOVE	S1,.LSHWD(LIS)		;PICK UP MESSAGE QUEUE INDEX
	$CALL	L%FIRST			;PICK UP ADDRESS OF THE MESSAGE
	MOVE	S1,S2			;PLACE ADDRESS WHERE EXPECTED
	MOVE	S2,.LSMSG(LIS)		;PICK UP ADDRESS OF THE MESSAGE BUFFER
	$CALL	XFRMSG			;MOVE THE MESSAGE TO MESSAGE BUFFER

;INTERRUPT THE TOP FORK THAT A MESSAGE IS AVAILABLE

	MOVEI	S1,.FHSUP		;PICK UP THE TOP FORK'S HANDLE
	MOVX	S2,<1B0>		;INTERRUPT IT ON THIS CHANNEL
	SETZM	.LSAVA(LIS)		;TOP FORK IS NOW BUSY
	IIC%				;INTERRUPT THE TOP FORK
	 ERJMP	[$CALL INLCRH		  ;INDICATE A CONTROLLED CRASH
		 JRST S..LCI ]		  ;CANNOT INTERRUPT THE TOP FORK

;DELETE THE MESSAGE QUEUE ENTRY

	$CALL	RELLME			;DELETE THE MESSAGE QUEUE ENTRY
	$RET				;RETURN TO THE CALLER
	SUBTTL	ADDLME - ADD A LISTENER MESSAGE QUEUE ENTRY

;ADDLME is called to add a message to the listener's message queue. The
;message queue is a link list of the messages that need to be picked up
;by the top fork.
;
;Call is: LIS/Address of the listener block
;         MO/Address of the message page
;Returns: The message has been added to the listener's message queue

;ADD THE MESSAGE TO THE LISTENER'S MESSAGE QUEUE

ADDLME:	MOVE	S1,.LSHWD(LIS)		;PICK UP THE MESSAGE QUEUE INDEX
	$CALL	L%LAST			;POSITION TO THE LAST ENTRY
	SKIPT				;IS THE MESSAGE QUEUE EMPTY?
	MOVE	S1,.LSHWD(LIS)		;YES, PICK UP MESSAGE QUEUE INDEX
	LOAD	S2,.MSTYP(MO),MS.CNT	;PICK UP THE LENGTH OF THE MESSAGE
	$CALL	L%CENT			;CREATE A MESSAGE QUEUE ENTRY
	MOVE	S1,MO			;PICK UP THE MESSAGE ADDRESS
	$CALL	XFRMSG			;COPY THE MESSAGE INTO THE MSG QUEUE
	AOS	.LSUSM(LIS)		;INCREMENT THE MESSAGE COUNT
	MOVE	S1,MO			;PICK UP THE MESSAGE ADDRESS
	$CALL	M%RPAG			;RELEASE THE MESSAGE PAGE
	$RET				;RETURN TO THE CALLER
	SUBTTL	RELLME - DELETE AN ENTRY FROM THE LISTENER MESSAGE QUEUE

;RELLME is called to delete the first message from the listener message 
;queue.
;
;Call is: LIS/Address of the listener block
;Returns: The message has been removed from the listener's message queue
;         and the message page returned to the memory manager
	
RELLME:

;RETURN THE MESSAGE PAGE TO THE MEMORY MANAGER

	MOVE	S1,.LSHWD(LIS)		;PICK UP THE MESSAGE QUEUE INDEX
	$CALL	L%DENT			;RETURN THE MESSAGE QUEUE ENTRY
	$CALL	L%NEXT			;MAKE THE NEXT ENTRY THE FIRST ENTRY
	SOS	.LSUSM(LIS)		;DECREMENT THE NUMBER OF MESSAGES 
	$RET				;RETURN TO THE CALLER
	SUBTTL	LISCHK - LISTENER CHECKSUM AND ACK MESSAGE

repeat 0,<
;LISCHK is called when the listener has picked up a message. If checksumming
;is enabled on the sender's node and the listener's node, then a checksum of
;the message is performed. If the sender's and listener's checksums agree,
;then a success ACK message is sent to the sender. If the checksums do not
;agree, then a failure ACK message is sent.
;If checksumming is disabled, either on the sender's node or the listener's
;node, then a checksum is not performed and a success ACK message is sent
;to the sender.
;
;Call is:       S1/Address of the message
;Returns true:  The checksums match and a success ACK has been sent to the 
;               sender.
;Returns false: The checksums do not match and a failure ACK has been sent
;               to the sender.

LISCHK:	$SAVE	<P1>			;SAVE THIS AC
	SKIPN	CHKSUMO			;CHECKSUMMING ENABLED ON LOCAL NODE?
	JRST	LISCH2			;NO, SEND A SUCCESS ACK BACK TO SENDER
	SKIPN	P1,.MSCHS(S1)		;YES, CHECKSUMMING ENABLED REMOTELY?
	JRST	LISCH2			;NO, SEND A SUCCESS ACK BACK TO SENDER
	SETZM	.MSCHS(S1)		;ZERO OUT THE CHECKSUM WORD
	$CALL	CHKSUM			;CALCULATE THE CHECKSUM
	CAME	P1,S1			;DO THE CHECKSUMS AGREE?
	JRST	LISCH3			;NO, SEND FAILURE ACK TO SENDER
LISCH2:	$CALL	ACKSUC			;SEND A SUCCESS ACK TO SENDER
	$RETT				;INDICATE MESSAGE IS VALID
LISCH3:	$CALL	ACKFAI			;SEND A FAILURE ACK TO SENDER
	$RETF				;INDICATE MESSAGE IS INVALID

>; end of repeat 0
	SUBTTL	SENACK - SEND AN ACK MESSAGE TO THE REMOTE SENDER

;SENACK is called to send an ACK to the sender upon receipt of a
;message from the sender. 
;
;Call is: LIS/Address of the listener block
;Returns: The ACK message was sent
;Crashes: The ACK message could not be sent

SENACK:	$SAVE	<T1,T2>			;SAVE THESE AC
	SETO	T1,			;NEGATIVE LENGTH OF THE ACK MESSAGE
	MOVE	S1,.LSJFN(LIS)		;PICK UP SRV: DEVICE JFN
	MOVEI	S2,T1			;ADDRESS OF THE MESSAGE
	HRLI	S2,(POINT 36,)		;MAKE IT INTO A POINTER
	SOUTR%				;SEND THE MESSAGE TO THE SENDER
	 ERJMP	ACKSU3			;CRASH ON AN ERROR
	$RET				;RETURN TO THE CALLER
ACKSU3:	$CALL	INLCRH			;INDICATE A CONTROLLED CRASH
	$STOP	(LUA, LISTENER UNABLE TO SEND AN ACK MESSAGE)
					
	SUBTTL	INLCRH - ROUTINE TO INDICATE LISTENER CONTROLLED CRASH

;INLCRH is called by the listener when it has detected a fatal error.
;INLCRH indicates in the node table entry's listener status word
;that the listener was aware it was going to crash. A RESET% is
;also performed to break the DECnet link.
;
;Call is: LIS/Address of the listener block
;Returns: Bit NL%LFC is set in the node entry's listener status word

;SET THE CONTROLLED LISTENER CRASH BIT IN THE NODE TABLE'S LISTENER STATUS WORD

INLCRH:	DMOVEM	S1,.LSERR(LIS)		;SAVE THE CONTEXT OF S1 AND S2
	MOVE	S1,.LSNTA(LIS)		;PICK UP THE NODE TABLE ENTRY
	MOVX	S2,NL%LFC		;PICK UP LISTENER FORK CRASHED BIT
	IORM	S2,.NLSTA(S1)		;INDICATE THAT THE LISTENER HAS CRASHED
	MOVX	S2,NN%IFC		;AN INFERIOR FORK HAS CRASHED
	IORM	S2,.NNSTA(S1)		;INDICATE IN THE NODE STATUS WORD
	RESET%				;BREAK THE DECNET LINK
	DMOVE	S1,.LSERR(LIS)		;RESTORE CONTEXT OF S1 AND S2
	$RET				;RETURN TO THE CALLER
	SUBTTL	SENDER - MESSAGE ROUTER TO A REMOTE NODE

;SENDER exists as an inferior fork in NEBULA. A sender is started for
;every remote node known to NEBULA that is running a release 7 or later
;monitor and which has DECnet enabled. A sender sends messages to a remote
;node's listener. The messages are of two types:
;1. Requests to be processed on the remote node.
;2. Responses to requests that originated on the remote node and were processed
;   on the local node.
;A sender communicates with the top fork through software interrupts,
;the sender block and the node's status words in its node table entry.

;SYMBOL DEFINITIONS

MINTIM==5				;MIN TIME BETWEEN CONNECTION ATTEMPTS
MAXTIM==5*^D60				;MAX TIME BETWEEN CONNECTION ATTEMPTS

;INITIALIZATION BLOCK AND PID BLOCK

SIB:	$BUILD	IB.SZ			;
	  $SET	(IB.PRG,,%%.MOD)	;PROGRAM 'NEBULA'
	  $SET  (IB.FLG,IP.STP,1)	;STOPCODES TO ORION
	$EOB				;

SPIB:	$BUILD	PB.MNS			;
	  $SET	(PB.HDR,PB.LEN,PB.MNS)	;PIB LENGTH,,0
	  $SET	(PB.FLG,IP.RSE,1)	;RETURN ON SEND ERROR
	  $SET	(PB.SYS,IP.BQT,-1)	;MAXIMUM SEND/RECEIVE IPCF QUOTA
	  $SET	(PB.SYS,IP.MNP,^D1)	;NUMBER OF PIDS
	$EOB				;

SNDCHN:	XWD	1,MSGTLI		;MESSAGE AVAILABLE TO SEND TO LISTENER
	XWD	1,MSGFLI		;ACK MESSAGE FROM LISTENER
	XWD	1,REPCON		;ENABLE DECNET-CONNECTION-ATTEMPTS
	BLOCK	^D33			;NO OTHER CHANNELS IN USE

;SENDER STARTUP CONSISTS OF SETTING UP GLXLIB AND CAPABILITIES, THE
;INTERRUPT SYSTEM AND CONNECTING TO THE REMOTE NODE'S LISTENER.

SENDER:	SKIPE	DEBUGW			;DEBUGGING?
	$CALL	NEBDDT			;YES, SET UP FOR DEBUGGING
	$CALL	SENSET			;SET UP GLXLIB AND CAPABILITIES
	$CALL	SENINT			;SET UP THE INTERRUPT SYSTEM
	$CALL	SOPLNK			;OPEN A CONNECTION TO THE LISTENER

;INFORM THE TOP FORK THAT A DECNET CONNECTION HAS BEEN OBTAINED. WAIT
;FOR ANY MESSAGES TO BE SENT TO THE LISTENER.

	$CALL	SENCON			;INFORM TOP FORK OF CONNECTION
SENDE2:	SETZ	S1,			;SLEEP UNTIL NEEDED
	$CALL	I%SLP			;WAIT%
	JRST	SENDE2			;WAIT FOR SOMETHING ELSE TO DO
	SUBTTL	SENSET - INITIALIZE THE SENDER'S GLXLIB AND CAPABILITIES

;SENSET is called by the sender at sender startup. This routine sets up
;GLXLIB, the sender's capabilities and disables the sender from receiving
;any IPCF messages.
;
;Call is: SEN/Address of the sender block
;Returns: GLXLIB setup and capabilities enabled
;Crashes: Unable to set up capabilities

SENSET:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;SET UP THE GLXLIB INITIALIZATION BLOCK IN THE SENDER BLOCK

	MOVSI	S1,SIB			;PICK UP ADDRESS OF THE IB BLOCK
	HRRI	S1,.SNIBK(SEN)		;ADDRESS OF WHERE TO PLACE THE IB BLOCK
	MOVEI	S2,.SNIBK+IB.SZ(SEN)	;END ADDRESS + 1
	BLT	S1,-1(S2)		;MOVE THE IB BLOCK TO SENDER BLOCK

	MOVEI	S1,.SNPIB(SEN)		;PICK UP THE ADDRESS OF THE PIB BLOCK
	MOVEM	S1,.SNIBK+IB.PIB(SEN)	;PLACE IN THE INITIALIZATION BLOCK
	MOVSI	S1,.SNLEV(SEN)		;ADDRESS OF THE INTERRUPT LEVEL TABLE
	HRRI	S1,SNDCHN		;ADDRESS OF THE CHANNEL TABLE
	MOVEM	S1,.SNIBK+IB.INT(SEN)	;PLACE IN THE INITIALIZATION BLOCK

;SET UP THE PID BLOCK AND THE INTERRUPT LEVEL TABLE IN THE SENDER BLOCK

	MOVSI	S1,SPIB			;PICK UP ADDRESS OF THE PID BLOCK
	HRRI	S1,.SNPIB(SEN)		;DESTINATION IS IN THE SENDER BLOCK
	MOVEI	S2,.SNPIB+PB.MNS(SEN)	;END ADDRESS + 1
	BLT	S1,-1(S2)		;MOVE PID TABLE TO SENDER BLOCK

	MOVEI	S1,.SNLEV(SEN)		;PICK UP ADR OF INTERRUPT LEVEL TABLE
	MOVEI	S2,.SN1PC(SEN)		;PICK UP ADR OF FIRST PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE
	AOS	S1			;ADDRESS OF NEXT INTERRUPT LEVEL ENTRY
	AOS	S2			;ADDRESS OF THE NEXT PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE
	AOS	S1			;ADDRESS OF NEXT INTERRUPT LEVEL ENTRY
	AOS	S2			;ADDRESS OF THE NEXT PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE

;SET UP GLXLIB

	MOVEI	S1,IB.SZ		;PICK UP SIZE OF THE INITIALIZATION BLK
	MOVEI	S2,.SNIBK(SEN)		;PICK UP ADR OF THE INITIALIZATION BLK
	$CALL	I%INIT			;INITIALIZE GLXLIB

;ENABLE THE SENDER'S CAPABILITIES TO BE THOSE OF THE TOP FORK AND GIVE IT
;THE CAPABILITY TO INTERRUPT THE TOP FORK.

	MOVX	S1,.FHSLF		;PICK UP THE SENDER'S HANDLE
	RPCAP%	
	 ERJMP	[$CALL INSCRH		  ;INDICATE A CONTROLLED CRASH
		 $STOP(SCC,Sender can't obtain capabilities) ]
	TXO	S2,SC%SUP		;CAPABILITY TO INTERRUPT TOP FORK
	MOVE	T1,S2			;ENABLE ALL CAPABILITIES
	MOVEI	S1,.FHSLF		;PICK UP THE SENDER'S HANDLE
	EPCAP%				;ENABLE THE CAPABILITIES
	 ERJMP	[$CALL INSCRH		  ;INDICATE A CONTROLLED CRASH
		 $STOP(SCE,Sender can't enable capabilities) ]

;DISABLE RECEIVING IPCF MESSAGES

	MOVEI	S1,.MUDIS		;DISABLE RECEIVING IPCF MESSAGES
	MOVEM	S1,.SNMUT(SEN)		;PLACE IN THE ARGUMENT BLOCK
	MOVE	S1,.SNPIB+PB.PID(SEN)	;PICK UP SENDER'S PID
	MOVEM	S1,.SNMUT+1(SEN)	;PLACE IN THE ARGUMENT BLOCK
	MOVEI	S1,2			;PICK UP SIZE OF ARGUMENT BLOCK
	MOVEI	S2,.SNMUT(SEN)		;PICK ADDRESS OF THE ARGUMENT BLOCK
	MUTIL%				;DISABLE RECEIVING IPCF MESSAGES
	 ERJMP	.+1			;SHOULDN'T HAPPEN, BUT DON'T CARE
	$RET				;RETURN TO STARTUP
	SUBTTL	SENINT - SET UP THE SENDER'S INTERRUPT SYSTEM

;SENINT is called by the sender during sender startup. SENINT sets up
;the sender's interrupt system.
;
;Call is: SEN/Address of the sender block
;Returns: The interrupt system has been set up
;Crashes: The interrupt system could not be set up


SENINT:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;FIRST DISABLE AND THEN CLEAR THE INTERRUPT SYSTEM

	MOVEI	S1,.FHSLF		;PICK UP THE SENDER'S HANDLE
	SETO	S2,			;INDICATE DISABLE ALL 36 CHANNELS
	DIC%				;DISABLE THE CHANNELS
	 ERJMP	.+1			;SHOULDN'T HAPPEN, BUT IGNORE
	CIS%				;CLEAR THE INTERRUPT SYSTEM
	 ERJMP	.+1			;SHOULDN'T HAPPEN, BUT IGNORE
	MOVEI	S1,.FHSLF		;PICK UP THE SENDER'S HANDLE
	HRLI	S2,.SNLEV(SEN)		;PICK UP INTERRUPT LEVEL TABLE ADDRESS
	HRRI	S2,SNDCHN		;PICK UP CHANNEL TABLE ADDRESS
	SIR%				;SET UP THE INTERRUPT TABLE ADDRESSES
	 ERJMP	SENIN2			;CRASH IF CAN'T SET UP 
	MOVEI	S1,.FHSLF		;PICK UP THE SENDER'S HANDLE
	EIR%				;ENABLE THE INTERRUPT SYSTEM
	 ERJMP	SENIN2			;CRASH IF CAN'T ENABLE INTERRUPT SYSTEM
	MOVEI	S1,.FHSLF		;PICK UP THE SENDER'S HANDLE
	MOVX	S2,1B0+1B1+1B2		;PICK UP CHANNELS TO ACTIVATE
	AIC%				;ACTIVATE THE CHANNELS
	 ERJMP	SENIN2			;CRASH IF CAN'T ACTIVATE THE CHANNELS
	$RETT				;RETURN TO SENDER STARTUP

SENIN2:	$CALL	INSCRH			;INDICATE A CONTROLLED CRASH
	JRST	S..CSI			;CANNOT SET UP INTERRUPT SYSTEM
	SUBTTL	SOPLNK - OBTAIN A CONNECTION TO THE LISTENER

;SOPLNK is called during the sender's startup to open a DECnet connection
;to the remote node's listener. If a connection cannot be obtained, then
;SOPLNK will re-attempt to open the connection after a specified amount
;of time. Initially, the time between retries is MINTIM seconds. If
;after MAXTIM seconds a connection is still not obtained, then SOPLNK
;informs ORION. The time between retries is increased by MINTIM seconds
;and a connection is again attempted. This will continue until either
;a connection is obtained or until the time between retries is MAXTIM
;seconds. At this point, SOPLNK attempts to obtain a connection every
;MAXTIM seconds.
;It is possible for an operator to suppress SOPLNK from sending 
;connection failure messages (and to later re-enable SOPLNK to send
;connection failure messages).
;It is also possible for an operator to cause SOPLNK to cease attempting
;to open a connection. An operator may also cause the sender to later
;re-attempt to open a connection.
;
;Call is: SEN/Address of the sender block
;Returns: Only if the connection has been obtained
;Crashes: Unable to obtain a DECnet JFN or open the DECnet link

SOPLNK:	$SAVE	<T1,T2,P1,P2,P3>		;SAVE THESE AC

;INITIALIZE THE ATTEMPT TO OBTAIN A DECNET CONNECTION TO THE LISTENER

	MOVE	P1,.SNNTA(SEN)		;PICK UP THE NODE TABLE ENTRY ADDRESS
	MOVEI	P2,MINTIM		;PICK UP INITIAL TIME BETWEEN RETRIES
	SETZ	P3,			;NUMBER OF ATTEMPTS TO OBTAIN THE LINK

;ATTEMPT TO OBTAIN THE DECNET CONNECTION. FIRST CHECK IF ATTEMPTS TO OBTAIN
;A CONNECTION ARE TO BE ABORTED.

SOPLN2:	MOVE	S1,.NNSTA(P1)		;PICK UP NODE STATUS WORD
	TXNE	S1,NN%CCA		;CEASE CONNECTION ATTEMPTS?
	JRST	SOPLN5			;YES, QUIT TRYING
	SKIPN	.SNJFN(SEN)		;CURRENTLY HAVE A DECNET JFN?
	$CALL	SGTLNK			;NO, OBTAIN ONE AND OPENF

;CHECK THE STATUS OF THE LINK. IF THERE IS A CONNECTION, THEN ENABLE
;FOR DATA AVAILABLE INTERRUPTS.

	$CALL	SCKLNK			;CHECK THE LINK STATUS
	JUMPF	SOPLN3			;DON'T HAVE A CONNECTION
	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	MOVEI	S2,.MOACN		;PICK UP ACTIVATE FUNCTION
	MOVX	T1,<FLD(1,MO%DAV)+FLD(.MONCI,MO%CDN)+FLD(.MONCI,MO%INA)>
	MTOPR%				;ENABLE FOR DATA AVAILABLE INTERRUPTS
	 ERJMP	[ $CALL INSCRH		   ;INDICATE CONTROLLED CRASH
		  JRST S..CSI ]		   ;CAN'T ENABLE INTERRUPT SYSTEM

	$WTOJ	(<DECnet connection approved>,<NEBULA's sender has established communication to node ^N/.SNNME(SEN)/>,,<$WTFLG(WT.SJI)>)

	$RETT				;RETURN TO SENDER STARTUP

;UNABLE TO OBTAIN THE CONNECTION. DETERMINE IF THE RETRY SHOULD BE INCREASED.

SOPLN3:	AOS	T1,P3			;INCREMENT # TRIES AT THIS TIME INTERVAL
	IMUL	T1,P2			;TIME BEEN TRYING AT THIS TIME INTERVAL
	CAIGE	T1,MAXTIM		;TIME TO INCREMENT THE TIME INTERVAL?
	JRST	SOPLN6			;NO, DISMISS AND TRY AGAIN

;INCREMENT THE TIME BETWEEN RETRIES. FIRST CHECK IF THE CONNECTION FAILURE
;SHOULD BE REPORTED TO ORION. IF SO, THEN REPORT THE CONNECTION FAILURE.

	MOVE	S1,.NNSTA(P1)		;PICK UP THE NODE STATUS WORD
	TXNE	S1,NN%DCO		;NOTIFY ORION OF THE FAILURE?
	JRST	SOPLN4			;NO, PICK UP NEW TIME INTERVAL
	$CALL	FNDCER			;GET THE CONNECTION ERROR

	$WTOJ	(<Sender connection failure>,<^I/@SOPLN7/^M^J^I/@SOPLN8/>,,<$WTFLG(WT.SJI)>)

;CALCULATE THE NEW TIME INTERVAL BETWEEN CONNECTION ATTEMPTS. THE MAXIMUM
;TIME BETWEEN RETRIES IS MAXTIM SECONDS.

SOPLN4:	ADDI	P2,MINTIM		;INCREMENT THE RETRY INTERVAL
	CAILE	P2,MAXTIM		;TIME INTERVAL AT A MAXIMUM?
	MOVEI	P2,MAXTIM		;YES, SET TO MAXIMUM
	SKIPA				;DISMISS AND THEN TRY AGAIN

;AN OPERATOR HAS INDICATED THAT THE SENDER IS TO CEASE RETRYING TO 
;OBTAIN A DECNET LINK TO THE LISTENER

SOPLN5:	SETZ	P2,			;DON'T RETRY TO GET A CONNECTION

;DISMISS UNTIL INDICATED

	SETZ	P3,			;NUMBER OF ATTEMPTS AT THIS INTERVAL
SOPLN6:	MOVE	S1,P2			;PICK UP TIME TO DISMISS
	$CALL	I%SLP			;DISMISS OR WAIT% AS INDICATED
	SKIPN	P2			;OPERATOR ENABLED CONNECTION ATTEMPTS?
	MOVEI	P2,MINTIM		;YES, START AT MINIMUM RETRY INTERVAL
	JRST	SOPLN2			;ATTEMPT THE CONNECTION AGAIN

SOPLN7:	[ITEXT(<NEBULA sender to node ^N/.NNNAM(P1)/ has not been able to obtain a 
DECnet connection>)]
SOPLN8:	[ITEXT(<Reason for failure: ^T/0(S1)/>)]
	SUBTTL	SGTLNK - OBTAIN DECNET JFN AND OPEN IT

;SGTLNK is called by routine SOPLNK to obtain a DECnet JFN to the remote
;node's listener and to open the connection
;
;Call is: SEN/Address of the sender block
;Returns: The JFN has been obtained and opened

SGTLNK:	$SAVE	<T1,T2,T3>		;[6002]SAVE THESE AC, DESTROYED BY JSYS

;GET THE JFN AND OPEN IT

	MOVX	S1,GJ%SHT		;SHORT JFN
	HRROI	S2,.SNDCN(SEN)		;PICK UP DECNET FILE NAME
	GTJFN%				;PICK UP THE JFN
	 ERJMP	SGTLN3			;CRASH IF CAN'T GET JFN
	HRRZS	S1			;ISOLATE THE JFN
	MOVEM	S1,.SNJFN(SEN)		;SAVE THE JFN IN SENDER BLOCK
	MOVNI	T3,^D60			;[6002]NEGATIVE NUMBER OF OPEN TRIES
	SKIPA				;[6002]ALREADY HAVE THE JFN

SGTLN1:	MOVE	S1,.SNJFN(SEN)		;[6002]PICK UP THE JFN
	MOVX	S2,<FLD(^D36,OF%BSZ)+OF%WR+OF%RD> ;OPEN FOR READ AND WRITE
	OPENF%				;OPEN THE JFN
	 ERJMP	SGTLN2			;CRASH IF CAN'T OPEN JFN
	$RET				;RETURN ON SUCCESS

SGTLN2:	AOSN	T3			;[6002]TIME TO QUIT?
	JRST	SGTLN3			;[6002]YES, CRASH
	MOVEI	S1,MINTIM		;[6002]PICK UP THE TIME TO SLEEP
	$CALL	I%SLP			;[6002]DISMISS FOR A WHILE
	JRST	SGTLN1			;[6002]RE-ATTEMPT TO OPEN THE LINK

SGTLN3:	$CALL	S%ERR			;[6021]PICK UP THE ERROR STRING
	JUMPF	SGTLN4			;[6021]STOPCODE ON AN UNKNOWN ERROR
	$WTOJ	(<Unable to obtain DECnet connection>,<Sender to node ^N/.SNNME(SEN)/ has encountered an error.
The last error is: ^T/0(S1)/>,,<$WTFLG(WT.SJI)>)
	$CALL	INSCRH			;[6021]INDICATE A CONTROLLED CRASH
	HALTF%				;[6021]HAVE TOP FORK RESTART

SGTLN4:	$CALL	INSCRH			;[6021]INDICATE A CONTROLLED CRASH
	$STOP	(SOD, SENDER CAN'T OPEN DECNET DEVICE) ;[6021]
	SUBTTL	SCKLNK - CHECK THE STATUS OF THE SENDER'S LINK

;SCKLNK is called to check the status of the sender's DECnet link to
;the listener. If there is no connection, then the DECnet link is
;closed and the DECnet JFN released
;
;Call is:       SEN/Address of the sender block
;Returns true:  The DECnet link is connected 
;Returns false: The DECnet link is waiting for a connection or there is
;               no connection
;Crashes:       Unable obtain the link status

SCKLNK:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;OBTAIN THE DECNET LINK STATUS.

	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	MOVEI	S2,.MORLS		;WANT THE STATUS OF THE LINK
	MTOPR%				;OBTAIN THE STATUS OF THE LINK
	 ERJMP	[$CALL INSCRH		  ;INDICATE A CONTROLLED CRASH
		 JRST S..COL ]		  ;CANNOT OBTAIN THE LINK STATUS
	MOVEM	T1,.SNLNK(SEN)		;SAVE THE LINK STATUS IN SENDER BLOCK

;DETERMINE IF THE LINK IS CONNECTED. IF IT IS NOT, THEN CLOSE AND RELEASE
;THE JFN.

	TXNE	T1,MO%CON		;IS THE LINK CONNECTED?
	$RETT				;YES, RETURN TRUE
	TXNE	T1,MO%WCC		;WAITING FOR A LINK?
	$RETF				;YES, DON'T RELEASE THE JFN
	
	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	TXO	S1,CZ%ABT		;CLOSE WITH ABORT
	CLOSF%				;CLOSE THE DECNET LINK
	 ERJMP	.+1			;SHOULDN'T HAPPEN
	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN AGAIN
	RLJFN%				;RELEASE THE JFN
	 ERJMP	.+1			;SHOULDN'T HAPPEN

	SETZM	.SNJFN(SEN)		;INDICATE NO LONGER HAVE A JFN
	$RETF				;INDICATE DON'T HAVE A LINK
	SUBTTL	FNDCER - DETERMINE THE DECNET CONNECTION ERROR

;FNDCER is called when a sender has not been able to make a DECnet
;connection to its listener. FNDCER finds the error text using
;the error code returned by the .MORLS function.
;
;Call is:       SEN/Address of the sender block
;Returns true:  A known error occurred
;               S1/Address of the error string
;Returns false: An unknown error occurred
;               S1/Address of unknown error string

FNDCER:	$SAVE	<P1>			;SAVE THIS AC

;PICK UP THE ERROR STRING USING THE ERROR CODE RETURNED BY .MORLS

	HRRZ	S1,.SNLNK(SEN)		;PICK UP THE ERROR CODE
	MOVSI	S2,-DNELEN		;PICK UP NEGATIVE LENGTH OF TABLE
FNDCE2:	HLRZ	P1,DNERR(S2)		;PICK UP THE ERROR CODE
	CAME	S1,P1			;IS THIS THE ERROR?
	AOBJN	S2,FNDCE2		;NO, CHECK THE NEXT ENTRY
	SKIPL	S2			;WAS THE ENTRY FOUND?
	JRST	FNDCE3			;NO, MAKE UNKNOWN ERROR
	HRRZ	S1,DNERR(S2)		;PICK UP ADDRESS OF ERROR TEXT
	$RETT				;INDICATE A KNOWN ERROR
FNDCE3:	MOVEI	S1,[ASCIZ/Unknown error/] ;PICK UP ERROR ADDRESS
	$RETF				;INDICATE AN UNKNOWN ERROR
DNERR:

;THE DECNET DISCONNECT CODES.

	.DCX0,,[ASCIZ/Reject or disconnect by object/]
	.DCX1,,[ASCIZ/Resource allocation failure/]
	.DCX2,,[ASCIZ/Destination node does not exist/]
	.DCX3,,[ASCIZ/Remote node shutting down/]
	.DCX4,,[ASCIZ/Destination process does not exist/]
	.DCX5,,[ASCIZ/Invalid process name field/]
	.DCX6,,[ASCIZ/Object is busy/]
	.DCX7,,[ASCIZ/Unspecified error/]
	.DCX8,,[ASCIZ/Third party aborted link/]
	.DCX9,,[ASCIZ/User abort (asynchronous disconnect)/]
	.DCX10,,[ASCIZ/Invalid node name/]
	.DCX11,,[ASCIZ/Local node shut down/]
	.DCX21,,[ASCIZ/Connect initiate with illegal destination address/]
	.DCX22,,[ASCIZ/Connect confirm with illegal destination address/]
	.DCX23,,[ASCIZ/Connect initiate or connect confirm with zero source address/]
	.DCX24,,[ASCIZ/Flow control violation/]
	.DCX32,,[ASCIZ/Too many connections to node/]
	.DCX33,,[ASCIZ/Too many connections to destination process/]
	.DCX34,,[ASCIZ/Access not permitted/]
	.DCX35,,[ASCIZ/Logical link services mismatch/]
	.DCX36,,[ASCIZ/Invalid account/]
	.DCX37,,[ASCIZ/Segment size too small/]
	.DCX38,,[ASCIZ/No response from destination, process aborted/]
	.DCX39,,[ASCIZ/No path to destination node/]
	.DCX40,,[ASCIZ/Link aborted due to data loss/]
	.DCX41,,[ASCIZ/Destination process does not exist/]
	.DCX42,,[ASCIZ/Confirmation of disconnect initiate/]
	.DCX43,,[ASCIZ/Image data field too long/]

DNELEN==.-DNERR			;LENGTH OF ERROR TABLE
	SUBTTL	SENCON - INDICATE TO TOP FORK THAT SENDER HAS A CONNECTION

;SENCON is called when the sender has obtained a DECnet connection.
;SENCON interrupts the top fork to indicate that it has obtained a DECnet 
;connection. 
;
;Call is: SEN/Address of the sender block
;Returns: The top fork has been informed of the sender's connection.
;Crashes: The top fork could not be interrupted.

SENCON:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;INDICATE IN THE SENDER STATUS WORD OF THE NODE TABLE ENTRY THAT THIS SENDER
;HAS A CONNECTION.

	MOVE	S1,.SNNTA(SEN)		;PICK UP THE NODE TABLE ENTRY ADDRESS
	MOVX	S2,NS%NRH		;SENDER HAS RECEIVED A HELLO
	IORM	S2,.NSSTA(S1)		;INDICATE IN THE STATUS WORD
	MOVEI	S1,.FHSUP		;PICK UP THE TOP FORK'S HANDLE
	MOVX	S2,<1B3>		;CHANNEL TO INTERRUPT TOP FORK ON
	IIC%				;INTERRUPT THE TOP FORK
	 ERJMP	[$CALL	INSCRH		  ;INDICATE IT WAS A CONTROLLED CRASH
		 JRST S..SCI ]		  ;CRASH, IF CAN'T INTERRUPT TOP FORK
	$RET				;RETURN TO THE CALLER
	SUBTTL	MSGTLI - SEND A MESSAGE TO THE LISTENER

;MSGTLI is the interrupt handler for sending messages to a listener.
;MSGTLI first checks that the link is still connected. If the link is
;still connected, then MSGTLI sends the message to the listener.
;
;Call is: SEN/Address of the sender block
;Returns: The message has been sent to the listener
;Crashes: The link is no longer connected

MSGTLI:	$BGINT	1,			;SAVE THE CONTEXT

;CHECK THE STATUS, IF THE LINK IF STILL CONNECTED, THEN CHECKSUM THE
;MESSAGE AND SEND IT. IF THE LINK IS NOT CONNECTED, THEN INFORM ORION
;AND CRASH.

	$CALL	SCKLNK			;CHECK THE LINK STATUS
	JUMPF	MSGTL2			;THE LINK IS GONE, INFORM THE OPERATOR
	$CALL	SSNDMG			;SEND THE MESSAGE
	$DEBRK				;RETURN TO PREVIOUS CONTEXT

MSGTL2:	MOVE	S1,.SNNTA(SEN)		;PICK UP NODE TABLE ENTRY
	$WTOJ	(<Sender lost connection>,<NEBULA sender to node ^N/.NNNAM(S1)/ has lost
its DECnet connection>,,<$WTFLG(WT.SJI)>)
	$CALL	INSCRH			;INDICATE CRASH IN NODE STATUS WORD 	
	JRST	S..SLL			;SENDER LOST ITS LINK
	SUBTTL	CHKSUM - CHECKSUM DECNET MESSAGES

repeat 0,<
;CHKSUM checksums messages that the sender sends to the listener.
;The checksum is stored in the checksum word. The listener, upon
;receipt of the message, also checksums it. If the checksums agree,
;then the listener sends a success ACK back to the sender, otherwise,
;it sends an error ACK.
;If the sender's node does not have checksumming enabled, then the
;sender sends a zero as the checksum value. The listener, in this
;case, always returns a success ACK.
;If the listener's node does not have checksumming enabled, then it
;always sends a success ACK back.
;(Note: If the calculated checksum equals zero, then CHKSUM changes
;       it to -1.)
;
;Call is:      S1/Address of the message
;Returns true: The checksum has been calculated and placed in the message
;              checksum word (.MSCHS).
;              S1/Contains the checksum

CHKSUM:	$SAVE	<P1>			;SAVE THIS AC

;INITIALIZE THE CHECKSUM PARAMETERS

	LOAD	S2,.MSTYP(S1),MS.CNT	;PICK UP LENGTH OF THE MESSAGE
	MOVSS	S2			;PLACE LENGTH FOR AOBJN
	MOVNS	S2			;MAKE THE COUNTER
	HRR	S2,S1			;COMPLETE THE AOBJN COUNTER
	SETZ	P1,			;SET CHECKSUM TO ZERO
	JCRY0	.+1			;CLEAR THE CARRY 0 BIT

;COMPUTE THE CHECKSUM

COMCH1:	ADD	P1,0(S2)		;ADD THE NEXT MESSAGE WORD TO CHECKSUM
	JCRY0	[AOJA P1,.+1]		;ADD ONE IF CARRY 0 BIT IS SET
	AOBJN	S2,COMCH1		;GO ADD IN THE NEXT MESSAGE WORD

;IF CHECKSUM IS 0, THEN MAKE -1

	SKIPN	P1			;IF CHECKSUM NOT 0, THEN FINISHED
	SETO	P1,			;MAKE THE CHECKSUM -1
	MOVEM	P1,.MSCHS(S1)		;PLACE CHECKSUM IN THE MESSAGE
	MOVE	S1,P1			;PLACE CHECKSUM IN RETURN AC
	$RETT				;RETURN TO THE CALLER

> ;end of repeat 0
	SUBTTL	MSGFLI - PICKUP ACK MESSAGE FROM THE LISTENER

;MSGFLI is the interrupt handler for ACK messages from the listener.
;MSGFLI picks up the ACK message from the listener and then interrupts
;the top fork to indicate that the sender is ready for another message.
;
;Call is: SEN/Address of the sender block
;Returns: The ACK message has been processed
;Crashes: The link is no longer connected

MSGFLI:	$BGINT	1,			;SAVE THE CONTEXT

;CHECK IF THERE IS A MESSAGE, IF THE INTERRUPT IS SPURIOUS OR IF THE
;LINK IS NO LONGER CONNECTED.

	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	SIBE%				;IS THERE AN ACK MESSAGE?
	JRST	MSGFL1			;YES, PICK IT UP

	$CALL	SCKLNK			;IS THE LINK STILL CONNECTED?
	JUMPT	MSGFL3			;YES, THE INTERRUPT IS SPURIOUS
	JRST	MSGFL4			;NO, TREAT AS A FATAL ERROR

;CHECK IF THE MESSAGE NEEDS TO BE RESENT. IF IT DOES NOT, THEN INFORM
;THE TOP FORK THAT THE SENDER IF AVAILABLE TO SEND ANOTHER MESSAGE.
;OTHERWISE, RESEND THE MESSAGE TO THE LISTENER.

MSGFL1:	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DCN: DEVICE JFN
	MOVE	S2,[POINT 36,T1]	;POINT TO THE MESSAGE BUFFER
	SETO	T1,			;NEGATIVE MESSAGE LENGTH
	SINR%				;PICK UP THE MESSAGE
	 ERJMP	MSGFL4			;CONSIDER ANY ERRORS AS FATAL
	MOVEI	S1,.FHSUP		;PICK UP TOP FORK'S HANDLE
	MOVX	S2,<1B1>		;CHANNEL TO INTERRUPT TOP FORK ON
	SETOM	.SNFRE(SEN)		;INDICATE THAT SENDER IS AVAILABLE
	IIC%				;INTERRUPT THE TOP FORK
	 ERJMP	[$CALL INSCRH		  ;INDICATE A CONTROLLED CRASH
		 JRST S..SCI ]		  ;CAN'T INTERRUPT THE TOP FORK
MSGFL3:	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT

MSGFL4:	$WTOJ	(<NEBULA lost connection>,<NEBULA's sender to node ^N/.SNNME(SEN)/ has lost its DECnet connection>,,<$WTFLG(WT.SJI)>)
	$CALL	INSCRH			;INDICATE CRASH IN NODE STATUS WORD 	
	HALTF				;CRASH
	SUBTTL	SSNDMG - SEND A MESSAGE TO A LISTENER

;SSNDMG is called to send a message to the listener. It sets up the SOUTR%
;call and sends the message.
;Call is: SEN/Address of the sender block
;Returns: The message was sent to the listener
;Crashes: The SOUTR% failed

SSNDMG:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;SET UP THE AC TO THE SOUTR% JSYS.

	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	MOVE	S2,.SNMSG(SEN)		;PICK UP THE ADDRESS OF THE MESSAGE
	HRLI	S2,(POINT ^D36,)	;MAKE IT INTO A POINTER
	LOAD	T1,.MSTYP(S2),MS.CNT	;PICK UP THE LENGTH OF THE MESSAGE

;SEND THE MESSAGE

	MOVNS	T1			;MAKE THE MESSAGE LENGTH NEGATIVE
	SOUTR%				;SEND THE MESSAGE
	 ERJMP	SSNDM2			;CRASH ON AN ERROR
	$RETT				;RETURN TO THE CALLER

SSNDM2:	$CALL	INSCRH			;INDICATE CRASH IN NODE STATUS WORD 	
	$STOP	(SUS, Sender unable to send message to the listener)
	SUBTTL	REPCON - ENABLE DECNET-CONNECTION-ATTEMPTS

;REPCON is the interrupt handler to re-enable the sender's attempts to
;connect to its listener. The top fork interrupts the sender on this
;channel as a result of receiving a message that came from an operator
;giving the OPR>ENABLE DECNET-CONNECTION-ATTEMPTS. The sender is only
;interrupted if it has not yet made a connection and its DECnet connection
;attempts have been disabled.
;REPCON merely forces the sender out of I%SLP.
;
;Returns: The sender is forced out of I%SLP

REPCON:	$BGINT	1,			;SAVE THE PREVIOUS CONTEXT
	$DEBRK				;FORCE OUT OF I%SLP
	SUBTTL	INSCRH - ROUTINE TO INDICATE SENDER CONTROLLED CRASH

;INSCRH is called by the sender when it has detected a fatal error.
;INSCRH indicates in the node table entry's sender status word
;that the sender was aware it was going to crash. A RESET% is
;also performed to break the DECnet link.
;
;Call is: SEN/Address of the sender block
;Returns: Bit NS%SFC is set in the node entry's sender status word

;SET THE CONTROLLED SENDER CRASH BIT IN THE NODE TABLE'S SENDER STATUS WORD

INSCRH:	DMOVEM	S1,.SNERR(SEN)		;SAVE THE CONTEXT OF S1 AND S2
	MOVE	S1,.SNNTA(SEN)		;PICK UP THE NODE TABLE ENTRY
	MOVX	S2,NS%SFC		;PICK UP SENDER FORK CRASHED BIT
	IORM	S2,.NSSTA(S1)		;INDICATE THAT THE SENDER HAS CRASHED
	MOVX	S2,NN%IFC		;AN INFERIOR FORK HAS CRASHED
	IORM	S2,.NNSTA(S1)		;INDICATE IN THE SENDER'S STATUS WORD
	RESET%				;BREAK THE DECNET LINK
	DMOVE	S1,.SNERR(SEN)		;RESTORE CONTEXT OF S1 AND S2
	$RET				;RETURN TO THE CALLER
					
	SUBTTL	NEBDDT - ROUTINE TO LOAD DDT IF DEBUGGING

;NEBDDT is called if NEBULA is running in a DEBUG environment.
;NEBDDT maps in and starts DDT
;
;Call is: No arguments
;Returns: DDT has been loaded
;Crashes: If unable to load DDT

NEBDDT:	MOVX	S1,GJ%OLD+GJ%SHT	;OLD FILE+SHORT JFN
	HRROI	S2,[ASCIZ/SYS:SDDT.EXE/] ;POINT TO DDT
	GTJFN%				;GET DDT'S JFN
	 ERJMP	NEBDD2			;CRASH IF CAN'T GET DDT'S JFN
	HRLI	S1,.FHSLF		;PICK UP HANDLE
	GET%				;LOAD DDT
	 ERJMP	NEBDD2			;CRASH IF CAN'T LOAD DDT
	MOVE	S1,116			;GET CONTENTS OF .JBSYM
	HRRZ	S2,770001		;GET ADDRESS OF WHERE TO PUT IT
	MOVEM	S1,0(S2)		;POINT DDT AT NEBULA'S SYMBOL TABLE
	JRST	770000			;AND ENTER DDT
GO:	$RET				;RETURN

NEBDD2:	$STOP	(DDE, DDT ERROR)	;CRASH, IF CAN'T GET DDT
SUBTTL	KASNOD - RESET A NODE'S DATA BASE

;KASNOD is called when a fatal error has occurred concerning a node.
;It is called when a sender or listener to that node has crashed.
;It is called when the response to a message sent to that node is not
;received in TIMOUT amount of time.
;KASNOD kills the inferior forks, updates the message, in behalf of,
;remote and timer queues. The node table entry for the node is also
;updated. If it is determined that the remote node can receive messages
;(i.e., it has a monitor of release 7 or later and has DECnet enabled),
;then a listener and sender are restarted for that node.
;
;Call is:       S1/Node table entry of the node
;Returns true:  The node can receive messages, a listener and sender have
;               been restarted for the node
;Returns false: The node cannot receive messages. A listener and sender have
;               not been restarted for the node

KASNOD:	$SAVE	<P1,P2,P3>		;SAVE THESE AC

;KILL THE INFERIOR FORKS, UPDATE THE QUEUES ASSOCIATED WITH THIS NODE
;AND REBUILD THE NODE TABLE ENTRY

	MOVE	P1,S1			;SAVE THE NODE TABLE ENTRY ADDRESS
	MOVE	P2,.NNNAM(P1)		;SAVE THE NODE NAME
	LOAD	P3,.NNSTA(P1),NS.NUM	;SAVE THE NODE NUMBER
	$CALL	KILNOD			;KILL THE NODE
	MOVEM	P2,.NNNAM(P1)		;RESTORE NODE NAME TO NODE TABLE ENTRY
	STORE	P3,.NNSTA(P1),NS.NUM	;RESTORE NODE # TO NODE TABLE ENTRY

;CHECK THE STATUS OF THE REMOTE NODE TO DETERMINE IF A NEW SENDER AND LISTENER
;SHOULD BE STARTED FOR IT.

	MOVE	S1,P1			;PICK UP THE NODE TABLE ENTRY ADDRESS
	$CALL	NODPAR			;CHECK IF THIS NODE CAN RECEIVE MSG
	JUMPT	KASN.1			;[6016]JUMP IF CAN SEND MSG TO THIS NODE
	MOVE	S1,P1			;[6016]PICK UP THE NODE TABLE ENTRY ADR
	$CALL	STINFO			;[6016]START THE INFO% JSYS TIMER
	$RETF				;[6016]INDICATE CAN'T SEND MSG TO NODE

;THIS NODE IS OK TO START A SENDER AND LISTENER FOR. DETERMINE, AS INDICATED
;IN THE PACKN TABLE ENTRY FOR THIS NODE, WHETHER THE SENDER SHOULD NOTIFY
;ORION IF IT IS UNABLE TO MAKE A DECNET CONNECTION. ALSO DETERMINE IF THE
;SENDER SHOULD ATTEMPT TO OBTAIN A DECNET CONNECTION

KASN.1:	MOVE	S1,.NNNAM(P1)		;[6016]PICK UP THE NODE NAME
	$CALL	SRHPNE			;FIND THE PACKN ENTRY FOR THIS NODE
	JUMPF	[$STOP (PTI,PACKN TABLE INCONSISTENCY)]
	MOVE	S2,.LKSTS(S2)		;PICK UP THE PACKN STATUS WORD
	SETZ	S1,			;ASSUME NO BITS ARE SET
	TXNE	S2,LK%CCA		;ATTEMPT TO OBTAIN A DECNET CONNECTION?
	TXO	S1,NN%CCA		;NO, INDICATE IN THE NODE STATUS WORD
	TXNE	S2,LK%DIS		;REPORT CONNECTION FAILURES?
	TXO	S1,NN%DCO		;NO, INDICATE IN THE NODE STATUS WORD
	IORM	S1,.NSSTA(P1)		;UPDATE THE NODE TABLE STATUS WORD

;START UP THE LISTENER AND SENDER

	MOVE	S1,P1			;PICK UP THE NODE TABLE ENTRY ADDRESS
	$CALL	STAINF			;START UP THE INFERIOR FORKS
	$RETT				;THIS NODE CAN RECEIVE MESSAGES
	SUBTTL	STINFO - START AN INFO% JSYS FAILURE TIMER

;[6016]STINFO is called when the INFO% JSYS has failed. When a node first comes
;[6016]up and SCS% indicates this fact, it is possible that CLUDGR is not fully
;[6016]ready. This implies that an INFO% JSYS error indicating that a node 
;[6016]is not running monitor release 7 or later is incorrect. Consequently,
;[6016]a timer is set up to IEMAX times with a timeout value of TIMOUT/5 in
;[6016]anticipation that CLUDGR will be ready.
;[6016]
;[6016]Call is: S1/Node table entry address
;[6016]Returns: An INFO% JSYS timer entry has been created

STINFO:	$SAVE	<P1>			;[6016]SAVE THIS AC
	MOVE	P1,S1			;[6016]SAVE THE NODE TABLE ENTRY ADR
	$CALL	I%NOW			;[6016]PICK UP THE CURRENT TIME
	ADDI	S1,<TIMOUT/5>		;[6016]CALCULATE TIME TO INTERRUPT
	MOVEM	S1,.NIETM(P1)		;[6016]SAVE IN THE NODE TABLE ENTRY
	MOVEI	S2,TIMBLK		;[6016]PICK UP EVENT BLOCK ADDRESS
	MOVEM	S1,.TITIM(S2)		;[6016]SAVE THE WAKEUP TIME
	MOVEI	S1,.TIMDT		;[6016]WAKE UP AT A SPECIFIC TIME
	MOVEM	S1,.TIFNC(S2)		;[6016]SAVE IN THE TIMER EVENT BLOCK
	MOVEI	S1,IEPROC		;[6016]PICK UP TIMER ROUTINE ADDRESS
	MOVEM	S1,.TIMPC(S2)		;[6016]SAVE IN THE TIMER EVENT BLOCK
	MOVEM	P1,.TIDAT(S2)		;[6016]SAVE THE NODE TABLE ENTRY ADR
	MOVEI	S1,<.TIDAT+1>		;[6016]PICK UP THE TIMER BLOCK LENGTH
	$CALL	I%TIMR			;[6016]SET UP THE TIMER
	AOS	S1,.NIERC(P1)		;[6016]INCREMENT THE INFO% RETRY COUNT
	CAIE	S1,1			;[6016]IS THIS THE FIRST TIMER?
	$RET				;[6016]NO, SO RETURN NOW
	$WTOJ	(<NEBULA INFO% failure detected>,<Unable to obtain status of node ^N/.NNNAM(P1)/>,,<$WTFLG(WT.SJI)>)
	$RET				;[6016]RETURN
	SUBTTL	IEPROC - INFO% JSYS ERROR TIMER EVENT PROCESSOR

;[6016]IEPROC is the INFO% JSYS error timer routine. IEPROC checks if the
;[6016]INFO% JSYS still reports an error in obtaining the status of the
;[6016]indicated remote node. If the INFO% JSYS still returns an error, 
;[6016]then if the retry count has not exceeded IEMAX, then the INFO%
;[6016]error timer is reset. Otherwise, -1 is placed in the node entry
;[6016]word .NIETM to indicate that after IEMAX tries that the INFO% JSYS
;[6016]still indicated that NEBULA communication with the node is not
;[6016]possible.
;[6016]
;[6016]Call is: S1/Length of the timer event request block
;[6016]         S2/Address of the timer event request block
;[6016]Returns: Timer has been set for another time or
;[6016]         timer has not been reset due to maximum retry count or
;[6016]         INFO% indicates that communication with the node is possible,
;[6016]         in which case the node's node table entry status word indicates
;[6016]         this and a listener and sender has been started for this node
;[6016]
;[6016](Note: ACs S1,S2 and T1-T4 are preserved by I%SLP which calls this
;[6016]routine.)

IEPROC:	MOVE	T1,.TIDAT(S2)		;[6016]PICK UP THE NODE TABLE ENTRY ADR
	MOVE	S1,T1			;[6016]PLACE ADDRESS HERE FOR NODPAR
	$CALL	NODPAR			;[6016]DETERMINE THE NODE'S STATUS
	JUMPT	IEPR.2			;[6016]CAN NOW COMMUNICATE WITH THE NODE
	MOVE	S1,.NIERC(T1)		;[6016]PICK UP RETRY COUNT
	CAIGE	S1,IEMAX		;[6016]REACHED THE MAXIMUM?
	JRST	IEPR.1			;[6016]NO, SET THE TIMER AGAIN
	SETOM	.NIETM(T1)		;[6016]YES, INDICATE GIVING UP
	$RET				;[6016]RETURN TO I%SLP

IEPR.1:	MOVE	S1,T1			;[6016]PICK UP THE NODE TABLE ENTRY ADR
	$CALL	STINFO			;[6016]SET THE TIMER
	$RET				;[6016]RETURN TO I%SLP

IEPR.2:	SETZM	.NIETM(T1)		;[6016]CLEAR THE INFO% TIMER WORD
	SETZM	.NIERC(T1)		;[6016]CLEAR THE INFO% RETRY COUNTER
	MOVX	S1,NN%DCN!NN%REL!NN%COI ;[6016]PICK UP THE ERROR BITS
	ANDCAM	S1,.NNSTA(T1)		;[6016]CLEAR THEM IN THE STATUS WORD
	MOVE	S1,T1			;[6016]PICK UP THE NODE ENTRY TBL ADR
	$CALL	STAINF			;[6016]START UP THE INFERIOR FORKS
	$RET				;[6016]RETURN TO I%SLP
	SUBTTL	TOPTMR - TOPOLOGY CHANGE TIMER

;[6016]TOPTMR is called to set up the cluster topology change timer. TOPTMR
;[6016]is called from routine TOPCHN as a result of TOPCHN being called
;[6016]at NEBULA startup time or as a consequence of TOPCHN being called
;[6016]as a result of the SCS% interrupt handler detecting that a node
;[6016]has joined the cluster. When the topology change timere goes off it
;[6016]calls routine TOPCHN. This is needed because at system startup time
;[6016]the node information returned by the CONFG% JSYS may not be accurate
;[6016]which can result in a node that is actually in the cluster not being
;[6016]reported as being in the cluster.

TOPTMR:	$CALL	I%NOW			;[6016]PICK UP THE CURRENT TIME
	ADDI	S1,TOPTRY		;[6016]CALCULATE TIME TO INTERRUPT
	MOVEI	S2,TIMBLK		;[6016]PICK UP EVENT BLOCK ADDRESS
	MOVEM	S1,.TITIM(S2)		;[6016]SAVE THE WAKEUP TIME
	MOVEI	S1,.TIMDT		;[6016]WAKE UP AT A SPECIFIC TIME
	MOVEM	S1,.TIFNC(S2)		;[6016]SAVE IN THE TIMER EVENT BLOCK
	MOVEI	S1,TOPCHN		;[6016]TOPOLOGY CHANGE ROUTINE ADDRESS
	MOVEM	S1,.TIMPC(S2)		;[6016]SAVE IN THE TIMER EVENT BLOCK
	MOVEI	S1,.TIDAT+1		;[6016]PICK UP THE TIMER BLOCK LENGTH
	$CALL	I%TIMR			;[6016]SET UP THE TIMER
	$RET				;[6016]RETURN
	SUBTTL	NEBTMR - NEBULA ACK MESSAGE TIMER

;[6016]NEBTMR is called to set up the timer to send the next NEBULA ACK
;[6016]message to all nodes in the cluster that are known to be in
;[6016]communication with the local NEBULA.
;[6016]
;[6016]Call is: No arguments
;[6016]Returns: The timer has been set up

NEBTMR:	$CALL	I%NOW			;[6016]PICK UP THE CURRENT TIME
	ADDI	S1,TIMOUT		;[6016]ADD THE TIME-OUT TIME
	MOVEI	S2,TIMBLK		;[6016]PICK UP TIMER BLOCK ADDRESS
	MOVEM	S1,.TITIM(S2)		;[6016]SAVE THE WAKEUP TIME
	MOVEI	S1,.TIMDT		;[6016]PICK UP THE FUNCTION CODE
	MOVEM	S1,.TIFNC(S2)		;[6016]SAVE IN THE TIMER BLOCK
	MOVEI	S1,NACKTM		;[6016]PICK UP TIMER PROCESSOR ADDRESS
	MOVEM	S1,.TIMPC(S2)		;[6016]]SAVE IN THE TIMER BLOCK
	MOVEI	S1,<.TIDAT+1>		;[6016]PICK UP TIMER BLOCK LENGTH
	$CALL	I%TIMR			;[6016]SET UP THE TIMER
	$RET				;[6016]RETURN TO THE CALLER
	SUBTTL	NACKTM - NEBULA ACK MESSAGE TIMER PROCESSOR

;[6016]NACKTM is called by I%SLP when the NEBULA ACK message timer has gone 
;[6016]off. NACKTM sends a NEBULA ACK message to all known nodes in the cluster
;[6016]capable of receiving messages.
;[6016]
;[6016]Call is: No arguments
;[6016]Returns: NEBULA ACK message has been sent to all nodes in the cluster 
;[6016]         known to be able to receive messages.

NACKTM:	MOVEI	M,NEBACK		;[6016]PICK UP THE MESSAGE ADDRESS
	MOVEI	S1,TIMOUT		;[6016]PICK UP THE TIMEOUT VALUE
	MOVEM	S1,TIMCTR		;[6016]SAVE FOR SETTING UP ANY TIMERS
	$CALL	I%NOW			;[6016]PICK UP IPCF MSG RECEPTION TIME
	MOVEM	S1,MSGTIM		;[6016]SAVE IT
	$CALL	QMREMM			;[6016]SEND THE ACK MESSAGES
	$CALL	NEBTMR			;[6016]SET THE NEBULA ACK MSG TIMER
	$RET				;[6016]RETURN TO I%SLP
	SUBTTL	NODPAR - PICK UP THE ENVIRONMENT OF A REMOTE NODE

;NODPAR is called to pick up the software environment of a remote node.
;This is accomplished by using the INFO% JSYS to perform the CNFIG% function
;.CFINF on the remote node. If the INFO% JSYS fails with error INFX11
;(Remote node not running CLUDGR SYSAP), then the assumption is that the
;remote node is running a pre-release 7 monitor. 
;A check is also made for DECnet enabled at the remote node.
;The status of the remote node is stored in the status word of the node's
;node table entry.
;
;Call is:       S1/Node table (NODTBL) entry address of remote node to be
;               checked
;Returns true:  S2/Status word of the node
;               O.K. to start a listener and sender to this node
;Returns false: S2/Status word of the node
;               Not O.K. to start a listener and sender to this node

NODPAR:	$SAVE	<T1,T2,T3,T4>		;SAVE THESE AC
	MOVE	T3,S1			;SAVE THE NODE TABLE ENTRY ADDRESS
	MOVEI	S1,INFBLK		;PICK UP THE ADDRESS OF THE INFO% BLK
	MOVE	S2,[.INCFG,,4]		;PICK UP INFO% FUNCTION/ARG BLK LENGTH
	MOVEM	S2,.INFUN(S1)		;PLACE IN THE INFO% ARGUMENT BLOCK
	LOAD	S2,.NNSTA(T3),NS.NUM	;PICK UP THE NODE NUMBER
	MOVEM	S2,.INCID(S1)		;PLACE IN THE INFO% ARGUMENT BLOCK
	MOVEI	S2,.CFINF		;PICK UP THE CNFIG% FUNCTION CODE
	MOVEM	S2,.INAC1(S1)		;PLACE IN THE INFO% ARGUMENT BLOCK
	MOVEI	S2,SWINFO		;PICK UP THE SOFTWARE STATUS BLK ADR
	MOVEM	S2,.INAC2(S1)		;PLACE IN THE INFO% ARGUMENT BLOCK
	MOVEI	T4,.CFILN		;THE CNFIG% ARGUMENT BLOCK SIZE
	MOVEM	T4,.CFLEN(S2)		;PLACE IN THE CNFIG% ARGUMENT BLOCK
	INFO%				;PICK UP THE STATIC SOFTWARE INFO
	 ERJMP	NODPA2			;CAN'T OBTAIN REMOTE SOFTWARE INFO
	SKIPGE	S1			;DID AN ERROR OCCUR ON THE REMOTE NODE?
	JRST	NODPA3			;YES, ASSUME THE WORST
	MOVEI	S1,SWINFO		;PICK UP ADDRESS OF ARGUMENT BLOCK
	MOVE	T4,.CFISO(S1)		;PICK UP THE NODE STATIC SOFTWARE WRD
	TXNE	T4,CF%DCN		;DOES THE NODE HAVE DECNET
	JRST	NODPA6			;YES, FINISHED
	MOVE	S2,.NNSTA(T3)		;PICK UP THE NODE STATUS WORD
	TXO	S2,NN%DCN		;NO, INDICATE SO
	MOVEM	S2,.NNSTA(T3)		;INDICATE NO DECNET IN THE STATUS WORD
	JRST	NODPA5			;INDICATE NOT OK START LISTENER/SENDER

;INFO% COULD NOT OBTAIN INFORMATION ON THE REMOTE NODE.
;CHECK FOR ERROR INFX11. IF THIS IS THE ERROR, THEN ASSUME THE REMOTE NODE
;HAS A PRE-RELEASE 7 MONITOR. IT MAY BE THE CASE THAT A REMOTE NODE HAS
;A RELEASE 7 MONITOR BUT THE CLUDGR SYSAP IS NOT YET RUNNING. THEREFORE,
;THE BIT NN%COI MUST ALSO BE TURNED ON SO THAT THE NODE'S ENVIRONMENT CAN BE
;CHECKED LATER WHEN A TOPOLOGY CHANGE OCCURS. AT THAT TIME THE CLUDGR SYSAP
;MAY BE RUNNING

NODPA2:	MOVEI	S1,.FHSLF		;GET LATEST ERROR FOR NEBULA
	GETER%				;PICK UP THE ERROR CODE
	 ERJMP	NODPA3			;ASSUME THE WORST
	HRRZS	S2			;JUST WANT THE ERROR CODE
	CAIN	S2,INFX11		;REMOTE NODE NOT SUPPLYING INFORMATION?
	JRST	NODPA4			;YES, GO INDICATE SO

;AN INFO% JSYS ERROR OR A REMOTE CNFIG% JSYS ERROR HAS OCCURRED. ASSUME THE
;WORST, I.E., THE REMOTE NODE DOES NOT HAVE DECNET ENABLED AND IS RUNNING
;A PRE-RELEASE 7 MONITOR. ALSO, TURN ON BIT NN%COI WHICH INDICATES THAT THIS
;NODE'S STATUS SHOULD AGAIN BE ATTEMPTED TO BE PICKED UP AT THE NEXT CLUSTER
;TOPOLOGY CHANGE SINCE THE ERROR REPORTED THIS TIME MAY BE TRANSIENT.

NODPA3:	MOVX	S1,NN%DCN!NN%REL!NN%COI	;PICK UP THE ERROR BITS
	SKIPA				;GO SET THESE BITS IN THE STATUS WORD
NODPA4:	MOVX	S1,NN%REL!NN%COI	;REMOTE NODE DOESN'T HAVE RELEASE 7
	IORM	S1,.NNSTA(T3)		;INDICATE ERROR IN STATUS WORD
	MOVE	S2,.NNSTA(T3)		;PICK UP STATUS WORD FOR RETURN
NODPA5:	$RETF				;NOT OK TO START A LISTENER AND SENDER
NODPA6:	$RETT				;OK TO START A LISTENER AND SENDER
	SUBTTL CHKSTS - CHECK FOR DECNET AND RELEASE 7 MONITOR

;CHKSTS is called to check if a remote node has DECnet enabled 
;and a monitor of release 7 or later. This information is obtained from
;the Node Table entry for the node.
;
;Call is:       S1/Address of the Node Table entry
;Returns true:  The node has DECnet and a monitor of release 7 or later.
;		S1/Address of the Node Table entry
;Returns false: The node does not have DECnet and/or a release 7 monitor
;		S1/Address of the Node Table entry

CHKSTS:	MOVE	S2,.NNSTA(S1)		;PICK UP THE NODE'S STATUS WORD
	TXNE	S2,NN%DCN!NN%REL	;HAS BOTH DECNET AND RELEASE 7?
	$RETF				;NO, MISSING ONE OR BOTH
	$RETT				;YES
	SUBTTL	FNDPNE - ADD AN ENTRY TO THE PACKN TABLE IF NECESSARY

;FNDPNE is called when routine TOPCHN detects that a node has entered
;the cluster. FNDPNE checks if the new node to the cluster already
;exists in the PACKN table. If it does not, then it is added.
;FNDPNE assumes there is at least one free entry in the PACKN table.
;
;If a sender is not to be started for this node, then there is no reason
;to check if ORION is to be informed of senders' DECnet connection attempt
;failures. However, if a sender is to be started for this node, then there
;are two cases:
;Case 1. The node is known to have previously been in the cluster.
; In this case, if this node's sender's DECnet connection attempt failures
;were not previously reported to ORION, then this will also be true now.
;Otherwise, this node's sender's DECnet connection attempt failures will be
;reported or not to ORION as indicated by the flag word RCFFLG which is 
;affected by the OPR>ENABLE/DISABLE REPORT-CONNECTION-FAILURES command given 
;without the /NODE: switch being included. (The default is to report DECnet 
;connection attempt failures.)
;Case 2. The node has not been previously known to be in the cluster.
; In this case, this node's sender's DECnet connection attempt failures will
;be reported or not to ORION as indicated by flag word RCFFLG.
;
;Call is:       S1/Node table entry address of the node that has joined the
;               cluster
;               S2/Flag word that indicates if a sender is to be started for
;               this node or not
;Returns true:  The node has an existing entry in the PACKN table
;               S1/SIXBIT node name of node that has joined the cluster
;               S2/PACKN table entry address of the node
;Returns false: The node does not have an existing entry in the PACKN table,
;               but an entry has been added for it
;               S1/SIXBIT node name of node that has joined the cluster
;               S2/PACKN table entry address of the newly added node


;FIND THE PACKN ENTRY OF THE NODE THAT HAS JOINED THE CLUSTER. IF IT IS
;NOT FOUND, THEN ADD AN ENTRY FOR IT

FNDPNE:	$SAVE	<P1,P2,P3,P4>		;SAVE THESE AC
	DMOVEM	S1,P1			;SAVE NODE TABLE ENTRY ADDRESS & FLAG
	MOVE	S1,.NNNAM(P1)		;PICK UP THE NODE NAME
	$CALL	SRHPNE			;CHECK IF THE NODE HAS ENTRY IN PACKN
	DMOVEM	S1,P3			;SAVE NODE NAME/PACKN ADDRESS 
	JUMPT	FNDPN3			;THE NODE HAS PREVIOUSLY BEEN KNOWN

	MOVEM	S1,.NDNAM(S2)		;PLACE NODE NAME IN NEW PACKN ENTRY
	SOS	S1,NDLKFR		;DECREMENT NUMBER OF FREE PACKN ENTRIES
	JUMPG	S1,FNDPN2		;AT LEASE ONE FREE ENTRY?
	PUSH	P,TF			;NO, SAVE TRUE/FALSE INDICATOR
	$CALL	EXPTAB			;NO MORE FREE ENTRIES, EXPAND PACKN
	POP	P,TF			;RESTORE TRUE/FALSE INDICATER

;IF A SENDER IS TO BE STARTED FOR THIS NODE, THEN DETERMINE IF ORION IS TO
;BE INFORMED IF THE SENDER DECNET CONNECTION ATTEMPTS FAIL.

FNDPN2:	SKIPN	P2			;WILL A SENDER BE STARTED?
	JRST	FNDPN6			;NO, SO RETURN
	SKIPE	RCFFLG			;CONNECTION FAILURES BEING REPORTED?
	JRST	FNDPN4			;NO, SO INDICATE THIS
	JRST	FNDPN5			;YES, CHECK FOR CONNECTION ATTEMPTS 

;THE NODE IS KNOWN TO HAVE BEEN PREVIOUSLY IN THE CLUSTER. IF A SENDER IS
;NOT TO BE STARTED FOR IT THIS TIME, THEN RETURN. OTHERWISE, DETERMINE IF
;CONNECTION ATTEMPTS TO THE REMOTE NODE ARE TO BE TRIED AND, IF ON FAILURES,
;THE FAILURES ARE TO BE REPORTED TO ORION.

FNDPN3:	SKIPN	P2			;WILL A SENDER BE STARTED?
	JRST	FNDPN6			;NO, SO ARE FINISHED
FNDPN4:	MOVX	S1,LK%DIS		;ASSUME NOT REPORT CONNECTION FAILURES
	SKIPE	RCFFLG			;CURRENTLY, ARE FAILURES REPORTED?
	IORM	S1,.LKSTS(P4)		;NO, INDICATE IN THE PACKN ENTRY

;INDICATE IF THE SENDER'S FAILURE TO OBTAIN A DECNET CONNECTION SHOULD BE
;REPORTED OR NOT IN THE NODE'S NODE TABLE ENTRY STATUS WORD.

	MOVX	S1,NN%DCO		;ASSUME DISABLE FAILURE REPORTING
	SKIPGE	.LKSTS(P4)		;IS THIS A VALID ASSUMPTION?
	IORM	S1,.NNSTA(P1)		;YES, INDICATE IN NODE TABLE ENTRY

;INDICATE IF THE SENDER SHOULD ATTEMPT TO OBTAIN A DECNET CONNECTION OR NOT

FNDPN5:	MOVX	S1,LK%CCA		;ASSUME NOT ATTEMPT A CONNECTION
	SKIPE	DCAFLG			;IS THIS A VALID ASSUMPTION?
	IORM	S1,.LKSTS(P4)		;YES, INDICATE IN THE PACKN ENTRY
	MOVX	S1,NN%CCA		;ASSUME NOT ATTEMPT A CONNECTION
	MOVE	S2,.LKSTS(P4)		;PICK UP THE PACKN STATUS WORD
	TXNE	S2,LK%CCA		;ATTEMPT TO OBTAIN A DECNET CONNECTION?
	IORM	S1,.NNSTA(P1)		;NO, INDICATE IN THE NODE STATUS WORD
FNDPN6: DMOVE	S1,P3			;RESTORE NODE NAME/TABLE ADDRESS
	$RET				;PRESERVE THE TRUE/FALSE RETURN
	SUBTTL SRHPNE - CHECK IF A NODE HAS AN ENTRY IN THE PACKN TABLE

;SRHPNE is called to check if a node is in the PACKN table.
;SRHPNE assumes there is at least one free PACKN table entry.
;
;Call is:       S1/SIXBIT node name
;Returns true:  The node has an entry in the PACKN table
;               S1/SIXBIT node name
;               S2/Address of the PACKN entry for the node
;Returns false: The node does not have an entry in the PACKN table
;               S1/SIXBIT node name
;               S2/Address of the first free PACKN table

;FIND THE PACKN TABLE ENTRY FOR THE SPECIFIED NODE IF THERE IS ONE,
;ELSE INDICATE THERE IS NO PACKN TABLE ENTRY FOR THE SPECIFIED NODE.

SRHPNE:	MOVE	S2,NDLKST		;PICK ADDRESS OF THE PACKN TABLE
SRHPN2:	CAMN	S1,.NDNAM(S2)		;THE ENTRY BEING SEARCHED FOR?
	JRST	.RETT			;YES, INDICATE SUCCESS
	ADDI	S2,.NDLSZ		;NO, POINT TO THE NEXT PACKN ENTRY
	SKIPE	.NDNAM(S2)		;ANY MORE PACKN TABLE ENTRIES IN USE?
	JRST	SRHPN2			;NO, CHECK THE NEXT PACKN TABLE ENTRY
	$RETF				;NODE HAS NO PACKN TABLE ENTRY
	SUBTTL	USNERR - SET UP ARGUMENT WORDS FOR ACK MESSAGE PROCESSING

;USNERR is called when it is discovered during the processing of an IPCF
;message that is to be forwarded to a single remote node that the message
;cannot be sent to the remote node. USNERR sets up the node name and reason
;for not being able to send to the remote node parameters for the ACK 
;message to be sent to the OPR that sent the original message.
;
;Call is: S1/Bit position of error in node table entry status word
;         S2/Node name that message cannot be sent to 
;Returns: Node name and reason of error has been placed where the
;         error message handler expects them.

USNERR:	MOVEM	S2,G$ARG1		;SAVE NODE NAME WHERE EXPECTED
	JFFO	S1,.+1			;DETERMINE ERROR DISPLACEMENT 
	CAIL	S2,NNLOW		;CHECK FOR LEGAL LOWER RANGE
	CAILE	S2,NNHIGH		;CHECK FOR LEGAL HIGHER RANGE
	SETZ	S2,			;UNKNOWN ERROR

	MOVE	S1,REASON(S2)		;PICK UP ADDRESS OF THE ERROR
	MOVEM	S1,G$ARG2		;SAVE ADDRESS OF ASCIZ TEXT
	$RETT				;RETURN TO THE CALLER
	SUBTTL	REASON - MESSAGE ERROR TABLE

;REASON is a table that indicates the reason a message could not be
;sent to a remote node. The error reason comes from the node table
;entry status word for the node.

REASON:

	[ASCIZ/ Unknown error/]
	[ASCIZ/ INFO% unable to obtain software information/]       ;NN%COI
	[ASCIZ/ DECnet is not enabled/]	                            ;NN%DCN
	[ASCIZ/ Monitor is pre-release 7/]                          ;NN%REL
	[ASCIZ/ SCS% Detected that this node has left the cluster/] ;NN%SCS
	[ASCIZ/ Sender waiting for HELLO message response/]    	    ;NN%SHO 
	[ASCIZ/ Listener waiting for HELLO message/]           	    ;NN%LHO
	[ASCIZ/ Sender or listener in a fatal state/]               ;NN%IFC
	[ASCIZ//]						    ;[6037]
	[ASCIZ//]						    ;[6037]
	[ASCIZ/ is running Field Image version 6 GALAXY and does
          not understand the command/]		    ;[6037]NN%PAS

;LNKSTS is a table that indicates the DECnet link status of senders. This
;table is used in support of the OPR>SHOW CLUSTER-GALAXY-LINK-STATUS
;command


CONCON==1				;SENDER AND LISTENER HAVE CONNECTIONS
CNTCNT==2				;SENDER AND LISTENER CANNOT CONNECT
CNNECT==3				;SENDER OR LISTENER HAS A CONNECTION
NOTCON==4				;SENDER OR LISTENER NOT CONNECTED
NEVCON==5				;SENDER OR LISTENER NEVER CONNECTED
UNKSTS==6				;SENDER OR LISTENER UNKNOWN STATUS

LNKSTS:	EXP 0				;THIS ENTRY IS NOT USED
	[ASCIZ/Connected           Connected/]         ;[6006]
	[ASCIZ/Cannot connect to   Cannot connect to/] ;[6006]
	[ASCIZ/Connected/]
	[ASCIZ/Not connected/]
	[ASCIZ/Never connected/]
	[ASCIZ/Unknown/]
	SUBTTL EXPTAB - EXPAND THE PACKN TABLE

;EXPTAB is called when it is determined that the PACKN table has no
;free entries. This routine will expand the PACKN table by 
;adding MAXNOD free entries to it.
;
;Call is: No arguments
;Returns: The PACKN table has been expanded by MAXNOD free entries

EXPTAB:	$SAVE	<P1>			;SAVE THIS AC

;PICK UP THE MEMORY NEEDED FOR THE EXPANDED PACKN TABLE

	MOVE	S1,NDLKNM		;PICK UP NUMBER OF PACKN TABLE ENTRIES
	ADDI	S1,MAXNOD		;ADD MAXNOD ENTRIES TO THE ENTRY NUMBER
	IMULI	S1,.NDLSZ		;CALCULATE MEMORY NEEDED FOR NEW TABLE
	$CALL	M%GMEM			;PICK UP THE MEMORY FOR THE NEW TABLE
	EXCH	S2,NDLKST		;SAVE NEW ADDRESS, PICK UP THE OLD ADR
	JUMPE	S2,EXPTA1		;IF FIRST TIME, THEN NO OLD TABLE

;COPY THE CONTENTS OF THE OLD PACKN TABLE INTO THE NEW PACKN TABLE

	MOVE	P1,S2			;SAVE THE OLD TABLE ADDRESS
	HRLS	S2			;PLACE OLD ADDRESS AS SOURCE ADDRESS
	HRR	S2,NDLKST		;PLACE NEW ADDRESS AS DESTINATION ADR
	MOVE	S1,NDLKNM		;PICK UP NUMBER OF ENTRIES IN OLD TBL
	IMULI	S1,.NDLSZ		;CALCULATE SIZE OF THE OLD TABLE
	ADD	S1,NDLKST		;ADD ADR NEW TABLE TO GET DEST ADR+1
	BLT	S2,-1(S1)		;COPY OLD TABLE TO NEW TABLE

;RETURN THE OLD TABLE'S MEMORY TO THE MEMORY MANAGER

	MOVE	S2,P1			;PICK UP OLD PACKN TABLE ADDRESS
	MOVE	S1,NDLKNM		;PICK UP NUMBER OF ENTRIES IN OLD TBL
	IMULI	S1,.NDLSZ		;CALCULATE SIZE OF THE OLD TABLE
	$CALL	M%RMEM			;RETURN THE MEMORY TO THE MEMORY MGT

;UPDATE THE NUMBER OF ENTRIES IN USE AND THE NUMBER OF FREE ENTRIES IN THE
;NEW PACKN TABLE

EXPTA1:	MOVEI	S1,MAXNOD		;PICK UP THE NUMBER OF NEW ENTRIES
	ADDM	S1,NDLKNM		;ADD TO THE NUMBER OF EXISTING ENTRIES
	ADDM	S1,NDLKFR		;ADD TO THE NUMBER OF FREE ENTRIES

	$RET				;RETURN TO THE CALLER
	SUBTTL	UTILITY ROUTINES FOR SHOW CLUSTER-GALAXY-LINK-STATUS

;The following routines are used in building the display for the response
;to the SHOW CLUSTER-GALAXY-LINK-STATUS command.


CRLF:	PUSH	P,S1			;PRESERVE S1
	MOVEI	S1,[BYTE(7) 15,12,0,0,0] ;GET THE CRLF.
	$CALL	ASCOUT			;DUMP IT OUT
	POP	P,S1			;RESTORE S1
	$RETT				;AND RETURN

ASCOUI:	PUSH	P,S1			;SAVE S1
	HRRZ	S1,@-1(P)		;GET THE ADRS OF THE MESSAGE
	AOS	-1(P)			;SKIP OVER THE ARG POINTER
	$CALL	ASCOUT			;DUMP IT OUT
	POP	P,S1			;RESTORE S1
	$RETT				;AND WIN

ASCOUT:	$CALL	.SAVE1			;SAVE P1.
	MOVE	P1,S1			;SAVE THE INPUT ADDRESS.
	HRLI	P1,(POINT 7,0)		;MAKE IT A BYTE POINTER.
ASCO.1:	ILDB	S1,P1			;GET A BYTE.
	JUMPE	S1,.RETT		;DONE,,RETURN.
	$CALL	DEPBYT			;PUT IT OUT.
	JRST	ASCO.1			;AND DO ANOTHER.

;DEPBYT is used by $TEXT in transferring ASCIZ text to the ACK message

DEPBYT:	IDPB	S1,BYTPTR		;PICK UP THE NEXT CHARACTER
	$RETT				;RETURN TRUE
	SUBTTL	GETPAG - GET A PAGE FOR OUTGOING IPCF MESSAGE

;GETPAG obtains a page from the memory manager to be used to build an
;outgoing IPCF message.
;
;Call is: No arguments
;Returns: MO/ Address of the page for the outgoing IPCF message

GETPAG:	$CALL	M%GPAG			;PICK UP THE PAGE
	MOVE	MO,S1			;PLACE THE ADDRESS IN MO
	$RET				;RETURN TO THE CALLER

	SUBTTL	RELPAG - RELEASE OUTGOING IPCF PAGE

;This routine releases a page back to the memory manager in the event
;that the IPCF send of a message failed.
;
;Call is: MO/ Address of outgoing IPCF message page
;Returns: The page has been returned to the memory manager

RELPAG:	MOVE	S1,MO			;PICK UP THE MESSAGE ADDRESS
	$CALL	M%RPAG			;RELEASE THE PAGE
	SETZ	MO,			;TO AVOID CONFUSION
	$RET				;RETURN TO THE CALLER
	SUBTTL	TABLES FOR ERROR CODES REPORTED

;DEFINE THE ERROR DISPATCH VECTOR

DEFINE	X(SUFFIX,TEXT),<
E$'SUFFIX:: PUSHJ P,RPTERR		;DISPATCH TO ERROR HANDLER
>  ;END OF DEFINE X

ERRTBL: ERRCDS				;EXPAND THE DISPATCH TABLE
ERRTEX:	ERRTXT				;EXPAND TEXT MACROS

	TXTERR==ERRTEX-ERRTBL+1		;GET NUMBER OF FIRST TEXT ERROR

;DEFINE THE TEXT BLOCK TABLE

DEFINE	X(SUFFIX,TEXT),<
	EXP	[ITEXT(<TEXT>)]		;[6013]TABLE OF MESSAGES
>  ;END OF DEFINE X

TXTTBL:: EXP	[BYTE (7)0]		;0 IS NOT REALLY AN ERROR
	ERRCDS				;DEFINE THE REST OF THEM
TXTTEX:	ERRTXT

;DEFINE THE .MSFLG TABLE

DEFINE	X(SUFFIX,TEXT),<
	MF.FAT!INSVL.(<SIXBIT\   SUFFIX\>,MF.SUF)
>  ;END OF DEFINE X

STSTBL:: MF.NOM				;0 HAS NO TEXT ASSOCIATED
	ERRCDS				;EXPAND THE REST NOW
	ERRTXT				;EXPAND TEXT ERRORS

;HERE WHEN SOMEONE CALLS (OR EXITS THROUGH) ANY OF THE E$xxx ERROR CODES
;THIS ROUTINE STORES THE RELATIVE ERROR NUMBER INTO G$ERR

RPTERR:: EXCH	T1,(P)			;SAVE T1, GET ERROR DISPATCH
	TLZ	T1,-1			;CLEAR THE LEFT HALF
	SUBI	T1,ERRTBL		;CONVERT TO ERROR INDEX
	CAIL	T1,TXTERR		;CHECK FOR TEXT ERROR
	TLO	T1,400000		;FLAG AS TXTERR
	SKIPN	G$ERR			;DON'T SAVE THIS IF ALREADY AN ERROR
	MOVEM	T1,G$ERR		;SET GLOBAL ERROR INDICATOR
	POP	P,T1			;RESTORE T1 AND RETURN
	$RETF				;PROPAGATE ERROR TO TOP LEVEL
	SUBTTL	COMMON STOPCODES

;These STOPCODES are called from more than one location in NEBULA's 
;top fork.

	$STOP	(CTL, CLUSTER TOO LARGE)
	$STOP	(CSI, CAN'T SETUP INTERRUPT SYSTEM)
	$STOP	(SIF, SCS% INTERRUPT HANDLER ENCOUNTERED FATAL ERROR)
	$STOP	(NTI, NODE TABLE INCONSISTENCY)
	$STOP	(NDI, NODE DATA BASE INCONSISTENCY)
	$STOP	(IMM, ILLEGALLY FORMATED MOUNTR MESSAGE)
	$STOP	(OSF, SEND TO ORION FAILED)
	$STOP	(QSF, SEND TO QUASAR FAILED)
	$STOP	(SCI, SENDER CAN'T INTERRUPT THE TOP FORK)
	$STOP	(SLL, SENDER LOST ITS DECNEC LINK)
	$STOP	(LCI, LISTENER CAN'T INTERRUPT THE TOP FORK)
	$STOP	(COL, CAN'T OBTAIN THE LINK STATUS) 
	$STOP	(LUP, LISTENER UNABLE TO PICK UP DECNET MESSAGE)
	$STOP	(COD, CAN'T SETUP DEBUGGING DECNET DEVICE NAME)	;[6003]

;AN INFERIOR FORK WAS NOT INTERRUPTED. THIS CAN ONLY HAPPEN IF THE
;PROCESS HANDLE IS INVALID (ERROR FRKHX1). THIS IN TURN CAN ONLY HAPPEN
;IF THE INFERIOR FORK WAS KILLED (KFORK%) OR THE SENDER BLOCK OR LISTENER
;BLOCK HAS BEEN CORRUPTED. BOTH OF THESE POSSIBILITIES IMPLY THAT NEBULA
;IS IN AN INCONSISTENT STATE AND SHOULD THEREFORE BE CRASHED.

	$STOP	(UII, UNABLE TO INTERRUPT AN INFERIOR FORK)

	END NEBULA