Google
 

Trailing-Edge - PDP-10 Archives - BB-LW55A-BM_1988 - galaxy-sources/nebula.mac
There are 23 other files named nebula.mac in the archive. Click here to see a list.
	TITLE NEBULA

	SUBTTL STORAGE ALLOCATION AND DEFINITIONS

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

	SEARCH	NEBMAC			;CHECK NEBULA'S LIBRARY
	SEARCH	GLXMAC			;CHECK GALAXY'S LIBRARY
	SEARCH	QSRMAC			;CHECK QUASAR'S LIBRARY
	SEARCH	ORNMAC			;CHECK ORION'S LIBRARY
	SEARCH	GALCNF			;GET CONFIGURATION DATA
	SALL				;CLEAN LISTINGS
	PROLOG (NEBULA)			;GENERATE THE NECESSARY GALAXY SYMBOLS
NEBVEC:	BLDVEC	(NEBMAC,NMC,L)
	BLDVEC	(GLXMAC,GMC,L)
	BLDVEC	(QSRMAC,QMC,L)
	BLDVEC	(ORNMAC,OMC,L)
	BLDVEC	(NEBULA,NEB,L)


	NEBMAN==:0			;MAINTENANCE EDIT NUMBER
	NEBDEV==:6036			;DEVELOPMENT EDIT NUMBER
	VERSIN (NEB)			;GENERATE EDIT NUMBER

	NEBWHO==0
	NEBVER==6
	NEBMIN==0

	NEBVRS==<VRSN.(NEB)>+NMCEDT+GMCEDT+QMCEDT+OMCEDT

	.JBVER==137

	LOC	.JBVER
	EXP	NEBVRS
	RELOC
	SUBTTL	Table of Contents


;		Table of Contents for NEBULA
;
;
;			   Section			      Page
;   1. STORAGE ALLOCATION AND DEFINITIONS . . . . . . . . . .    1
;   2. Table of Contents. . . . . . . . . . . . . . . . . . .    3
;   3. REVISION HISTORY . . . . . . . . . . . . . . . . . . .    4
;   4. SYMBOL DEFINITONS. . . . . . . . . . . . . . . . . . .    5
;   5. RANDOM STORAGE AREA. . . . . . . . . . . . . . . . . .    6
;   6. RESIDENT JOB DATA BASE . . . . . . . . . . . . . . . .    7
;   7. PID AND INTERRUPT DEFINITION . . . . . . . . . . . . .    8
;   8. NEBULA STARTUP AND SCHEDULER . . . . . . . . . . . . .    9
;   9. NEBENV - CHECK THE NEBULA ENVIRONMENT. . . . . . . . .   10
;  10. NBINIT - GET NODE NAME, NODE NUMBER, AND SIZE OF NEBULA  11
;  11. WAIQSR - WAIT FOR QUASAR TO STARTUP. . . . . . . . . .   12
;  12. WAIORN - WAIT FOR ORION TO STARTUP . . . . . . . . . .   13
;  13. INTINI - . . . . . . . . . . . . . . . . . . . . . . .   14
;  14. NEBULA INTERRUPT HANDLERS. . . . . . . . . . . . . . .   15
;  15. NBSCS - TOPOLOGY CHANGE DECTECTED INTERRUPT HANDLER. .   16
;  16. NODINT - . . . . . . . . . . . . . . . . . . . . . . .   17
;  17. BLDLST - INITIALIZES PACKN TABLE . . . . . . . . . . .   18
;  18. GTNDAT - PICK UP THE NODE NAMES AND NUMBERS. . . . . .   19
;  19. STLAS - START UP THE LISTENERS AND SENDERS . . . . . .   20
;  20. STAINF - STARTUP A LISTENER AND SENDER TO A NODE . . .   21
;  21. BLDSRV - BUILD THE SRV: DEVICE NAME. . . . . . . . . .   22
;  22. STLIS - START UP THE LISTENER. . . . . . . . . . . . .   23
;  23. BLDDCN - BUILD THE DCN: DEVICE NAME. . . . . . . . . .   24
;  24. STSEN - START UP A SENDER. . . . . . . . . . . . . . .   25
;  25. CLUSTER TOPOLOGY CHANGE DETECTED . . . . . . . . . . .   26
;  26. KILNOD - DELETE A NODE FROM THE NODE DATA BASE . . . .   27
;  27. KILINF - KILL A NODE'S LISTENER AND SENDER . . . . . .   28
;  28. UPDQUE - UPDATE A NODE'S QUEUES. . . . . . . . . . . .   29
;  29. DELTIM - DELETE THE TIMER LIST AND TURN OFF TIMERS . .   30
;  30. UPDREM - CLEAN UP REMOTE QUEUE FOR SENDER/LISTENER CRASH   31
;  31. UPDIBH - CLEAN UP IBH QUEUE FOR SENDER/LISTENER  . . .   32
;  32. UPDMSG - CLEAN UP MESSAGE QUEUE FOR SENDER/LISTENER  .   33
;  33. BLDNEN - BUILD AN ENTRY IN THE NODE TABLE. . . . . . .   34
;  34. CHKDEC - INFERIOR FORK HAS A DECNET CONNECTION . . . .   35
;  35. CHKQUE - IPCF MESSAGE PROCESSING . . . . . . . . . . .   36
;  36. NTDSM - PROCESS A "TO NEBULA DISMOUNT" MESSAGE . . . .   37
;  37. SNDACK - SEND A TO NEBULA DISMOUNT ACK TO MOUNTR . . .   38
;  38. NTMTS. . . . . . . . . . . . . . . . . . . . . . . . .   39
;  39. NFDAK - PROCESS A "FROM NEBULA DISMOUNT ACK" MESSAGE .   40
;  40. NCDSM. . . . . . . . . . . . . . . . . . . . . . . . .   41
;  41. NDISPY . . . . . . . . . . . . . . . . . . . . . . . .   42
;  42. NSHOW - PROCESS OPR SHOW MESSAGES. . . . . . . . . . .   43
;  43. NLIST - PROCESSES EXEC "INFORMATION OUTPUT" MESSAGE. .   44
;  44. NKILL - ROUTINE PROCESS AN EXEC CANCEL MESSAGE . . . .   45
;  45. N$FNDB - ROUTINE TO FIND ANY BLOCK IN AN IPCF MESSAGE.   46
;  46. NSCLU - PROCESSES THE "SHOW CLUSTER-GALAXY" MESSAGE. .   47
;  47. NDRCF - ENABLE/DISABLE REPORT-CONNECTION-FAILURES. . .   48
;  48. NDDCA - ENABLE/DISABLE DECNET-CONNECTION-ATTEMPTS. . .   49
;  49. SETPAG - SETUP MESSAGE HEADER FOR SHOW ACK MESSAGES. .   50
;  50. SETHDR - BUILDS DISPLAY BLOCK FOR SHOW ACK MESSAGES. .   51
;  51. BDBHDR - BUILD DISPLAY BLOCK HEADER. . . . . . . . . .   52
;  52. FNDNBK - FIND THE NODE BLOCK IN AN IPCF MESSAGE. . . .   53
;  53. CHKLEN - CHECK THE VALIDITY OF AN IPCF MESSAGE . . . .   54
;  54. QSREMM - SEND OR QUEUE REMOTE MESSAGES TO A SINGLE REMOTE NODE   55
;  55. QMREMM - SEND OR QUEUE REMOTE MESSAGES TO MULTIPLE REMOTE NODES  56
;  56. QRIBHM - SEND/QUEUE RESPONSES TO IBH MESSAGE TO REQUESTING NODE  57
;  57. SETPAR - SETUP PARAMETERS FOR ROUTINE QUEMSG . . . . .   58
;  58. SEPALL - SET UP PARAMETERS FOR ROUTINE QUEMSG. . . . .   59
;  59. BLDHAD - BUILD THE HEADER AND DISPLAY BLOCKS FOR ACK MESSAGE   60
;  60. MOVERR - PLACE ERROR REASON IN TEXT BLOCK. . . . . . .   61
;  61. QUEMSG - SEND OR QUEUE A MESSAGE . . . . . . . . . . .   62
;  62. ORNACK - SEND AN ERROR ACK TO ORION. . . . . . . . . .   63
;  63. BLDACK . . . . . . . . . . . . . . . . . . . . . . . .   64
;  64. TXTMOV - MOVE TEXT FROM ONE LOCATION TO ANOTHER. . . .   65
;  65. BLDHDR - BUILD THE MESSAGE HEADER FOR ACK MESSAGE. . .   66
;  66. EREXEC - SEND AN ERROR MESSAGE TO THE EXEC . . . . . .   67
;  67. DEPCHR - INSERT AN ASCII CHARACTER IN AN EXEC ERROR MESSAGE  68
;  68. SETTIM - SETUP A TIMER FOR DECNET MESSAGE. . . . . . .   69
;  69. CLRTIM - CLEAR A TIMER UPON MESSAGE RESPONSE BEING RECEIVED  70
;  70. REMTIM - CLEAR A TIMER . . . . . . . . . . . . . . . .   71
;  71. PROTIM - TIMER HAS GONE OFF, COMMUNICATION LOST TO REMOTE NODE   72
;  72. SNDMSG - SEND MESSAGES TO AVAILABLE SENDERS. . . . . .   73
;  73. SENMSG - NOTIFY A SENDER OF A MESSAGE AVAILABLE. . . .   74
;  74. CONSTS - CONNECTION FAILURE STATUS . . . . . . . . . .   75
;  75. GETMSG -PICK UP A MESSAGE FROM A LISTENER. . . . . . .   76
;  76. PROMSG - PROCESS DECNET LISTENER MESSAGES DISPATCHER .   77
;  77. LFDAK - PROCESS A "FROM NEBULA DISMOUNT ACK" MESSAGE .   78
;  78. LSIBHM - PROCESS IN BEHALF OF MESSAGES FROM LISTENERS.   79
;  79. EINFO - PROCESS AN IN BEHALF OF EXEC INFO OUTPUT MESSAGE   80
;  80. EKILL - PROCESS AN IN BEHALF OF EXEC CANCEL MESSAGE. .   81
;  81. NSRCL - PROCESS REMOTE SHOW CLUSTER-STATUS MESSAGE . .   82
;  82. NSEACK - BUILD AN ACK MESSAGE REMOTELY . . . . . . . .   83
;  83. RSPMSG - RESPONSE TO A MESSAGE . . . . . . . . . . . .   84
;  84. SENREM - SEND A MESSAGE FROM A REMOTE NODE TO ORION. .   85
;  85. REXSHW - RESPONSE TO AN EXEC REQUEST . . . . . . . . .   86
;  86. RNEBAK - RESPOND TO A NEBULA ACK MESSAGE . . . . . . .   87
;  87. NRMACK - PROCESS A NEBULA ACK RESPONSE MESSAGE . . . .   88
;  88. RESTAR - PROCESS CRASHED INFERIOR FORKS. . . . . . . .   89
;  89. INFSTS - DETERMINE THE STATUS OF AN INFERIOR FORK. . .   90
;  90. SNDORN - SEND AN IPCF MESSAGE TO ORION OR QUASAR . . .   91
;  91. N$SEND - ROUTINE TO SEND AN IPCF MESSAGE TO AN EXEC. .   92
;  92. XFRMSG - TRANSFER IPCF MESSAGE FROM ONE BUFFER TO ANOTHER  93
;  93. ADDMQE - ADD A MESSAGE TO A SENDER'S MESSAGE QUEUE . .   94
;  94. FNDMQE - FIND AND RETURN A MESSAGE QUEUE ENTRY . . . .   95
;  95. RELMQE - RETURN A MESSAGE QUEUE ENTRY TO MEMORY MANAGER  96
;  96. BLDMQE - BUILD A MESSAGE QUEUE ENTRY . . . . . . . . .   97
;  97. ADDIBH - ADD AN IN BEHALF OF QUEUE ENTRY . . . . . . .   98
;  98. FNDIBH - FIND AN IN BEHALF OF QUEUE ENTRY. . . . . . .   99
;  99. RELIBH - RETURNS AN IN BEHALF OF QUEUE ENTRY . . . . .  100
; 100. ADDGRQ - ADD A REMOTE QUEUE ENTRY TO THE GLOBAL REMOTE QUEUE  101
; 101. FNDGRQ - FIND A GLOBAL REMOTE QUEUE ENTRY. . . . . . .  102
; 102. RELGRQ - RETURNS A GLOBAL REMOTE QUEUE ENTRY . . . . .  103
; 103. FNDNRQ - FIND A NODE'S REMOTE QUEUE ENTRY. . . . . . .  104
; 104. RELNRQ - RETURNS A NODE'S REMOTE QUEUE ENTRY . . . . .  105
; 105. SNAMNT - SEARCH NODE TABLE BY NODE NAME. . . . . . . .  106
; 106. SNUMNT - SEARCH NODE TABLE BY NODE NUMBER. . . . . . .  107
; 107. LISTEN - MESSAGE SERVER FOR A REMOTE NODE. . . . . . .  108
; 108. LISSET - INITIALIZE THE LISTENER'S GLXLIB AND CAPABILITIES  109
; 109. LOPLNK - OPEN A DECNET SRV: DEVICE . . . . . . . . . .  110
; 110. LISINT - SET UP THE LISTENER'S INTERRUPT SYSTEM. . . .  111
; 111. ACCEPT - VALIDATE A DECNET CONNECTION REQUEST. . . . .  112
; 112. MSGFSN - DECNET MESSAGE FROM SENDER IS AVAILABLE . . .  113
; 113. GETSMG - PICK UP MESSAGE FROM SENDER AND PLACE ON MESSAGE QUEUE 114
; 114. MSGTTF - TOP FORK READY FOR A MESSAGE FROM A LISTENER.  115
; 115. XFRTOP - MOVE MESSAGE FROM MESSAGE QUEUE TO MESSAGE BUFFER  116
; 116. ADDLME - ADD A LISTENER MESSAGE QUEUE ENTRY. . . . . .  117
; 117. RELLME - DELETE AN ENTRY FROM THE LISTENER MESSAGE QUEUE  118
; 118. LISCHK - LISTENER CHECKSUM AND ACK MESSAGE . . . . . .  119
; 119. SENACK - SEND AN ACK MESSAGE TO THE REMOTE SENDER. . .  120
; 120. INLCRH - ROUTINE TO INDICATE LISTENER CONTROLLED CRASH  121
; 121. SENDER - MESSAGE ROUTER TO A REMOTE NODE . . . . . . .  122
; 122. SENSET - INITIALIZE THE SENDER'S GLXLIB AND CAPABILITIES  123
; 123. SENINT - SET UP THE SENDER'S INTERRUPT SYSTEM. . . . .  124
; 124. SOPLNK - OBTAIN A CONNECTION TO THE LISTENER . . . . .  125
; 125. SGTLNK - OBTAIN DECNET JFN AND OPEN IT . . . . . . . .  126
; 126. SCKLNK - CHECK THE STATUS OF THE SENDER'S LINK . . . .  127
; 127. FNDCER - DETERMINE THE DECNET CONNECTION ERROR . . . .  128
; 128. SENCON - INDICATE TO TOP FORK THAT SENDER HAS A CONNECTION  129
; 129. MSGTLI - SEND A MESSAGE TO THE LISTENER. . . . . . . .  130
; 130. CHKSUM - CHECKSUM DECNET MESSAGES. . . . . . . . . . .  131
; 131. MSGFLI - PICKUP ACK MESSAGE FROM THE LISTENER. . . . .  132
; 132. SSNDMG - SEND A MESSAGE TO A LISTENER. . . . . . . . .  133
; 133. REPCON - ENABLE DECNET-CONNECTION-ATTEMPTS . . . . . .  134
; 134. INSCRH - ROUTINE TO INDICATE SENDER CONTROLLED CRASH .  135
; 135. NEBDDT - ROUTINE TO LOAD DDT IF DEBUGGING. . . . . . .  136
; 136. KASNOD - RESET A NODE'S DATA BASE. . . . . . . . . . .  137
; 137. STINFO - START AN INFO% JSYS FAILURE TIMER . . . . . .  138
; 138. IEPROC - INFO% JSYS ERROR TIMER EVENT PROCESSOR. . . .  139
; 139. TOPTMR - TOPOLOGY CHANGE TIMER . . . . . . . . . . . .  140
; 140. NEBTMR - NEBULA ACK MESSAGE TIMER. . . . . . . . . . .  141
; 141. NACKTM - NEBULA ACK MESSAGE TIMER PROCESSOR. . . . . .  142
; 142. NODPAR - PICK UP THE ENVIRONMENT OF A REMOTE NODE. . .  143
; 143. CHKSTS - CHECK FOR DECNET AND RELEASE 7 MONITOR. . . .  144
; 144. FNDPNE - ADD AN ENTRY TO THE PACKN TABLE IF NECESSARY.  145
; 145. SRHPNE - CHECK IF A NODE HAS AN ENTRY IN THE PACKN TABLE  146
; 146. USNERR - SET UP ARGUMENT WORDS FOR ACK MESSAGE PROCESSING 147
; 147. REASON - MESSAGE ERROR TABLE . . . . . . . . . . . . .  148
; 148. EXPTAB - EXPAND THE PACKN TABLE. . . . . . . . . . . .  149
; 149. UTILITY ROUTINES FOR SHOW CLUSTER-GALAXY-LINK-STATUS .  150
; 150. GETPAG - GET A PAGE FOR OUTGOING IPCF MESSAGE. . . . .  151
; 151. RELPAG - RELEASE OUTGOING IPCF PAGE. . . . . . . . . .  151
; 152. TABLES FOR ERROR CODES REPORTED. . . . . . . . . . . .  152
; 153. COMMON STOPCODES . . . . . . . . . . . . . . . . . . .  153
SUBTTL	REVISION HISTORY

COMMENT \

*****  Release 6.0 -- begin development edits  *****

6000	6.1140		13-Dec-87
	Create NEBULA as the cluster GALAXY message router.

6001	6.1142		17-Dec-87
	Add support for the SHOW STATUS TAPE and SHOW STATUS DISK 
commands.

6002	6.1144		17-Dec-87
	If an OPENF% fails due to node name not assigned, then retry
every 5 seconds up to 5 minutes before crashing.

6003	6.1145		17-Dec-87
	When debugging, use the first six characters of the user name
rather than "NEBULA" in the listener and sender names.

6004	6.1146		17-Dec-87
	Add support for the SHOW STATUS STRUCTURE command

6005	6.1154		30-Dec-87
	Change routine SGTLNK to check for error NSPX24 rather than error
NSPX25 if an OPENF% fails.

6006	6.1155		31-Dec-87
	Clean up the display of the response to the SHOW CLUSTER command.

6007	6.1160		5-Jan-88
	Cause routine NTMTS to increment the current block pointer correctly.

6010	6.1161		5-Jan-88
	Cause routine NFDAK to correctly point to the error block.

6011	6.1170		21-Jan-88
	Clear the timer when a response to a message has been received.

6012	6.1175		7-Feb-88
	Add support for the EXEC commands INFORMATION OUTPUT/DESTINATION
and CANCEL PRINT/DESTINATION.

6013	6.1180		13-Feb-88
	Clean up the creation of error messages that caused IQN crashes.

6014	6.1181		15-Feb-88
	On encountering any errors forwarding INFORMATION OUTPUT/DESTINATION
and CANCEL PRINT/DESTINATION messages, send an error message to the EXEC
with the same format that the EXEC expects error messages from QUASAR.

6015	6.1183		16-Feb-88
	HALTF% is Cluster GALAXY is not enabled.

6016	6.1192		23-Feb-88
	Correct various timing problems encountered that involve CLUDGR not
fully initialized and the monitor's CI node data base not being fully 
initialized. Add the NEBULA ACK/NEBULA ACK RESPONSE message mechanism.

6017	6.1194		23-Feb-88
	Change routine SENMSG to use the correct AC when picking up the message
address when setting the message response timer.

6020	6.1200		29-Feb-88
	If a DECnet OPENF% fails, then check for error NSPX13 (Access not
permitted. If this is the error, then try again.

6021	6.1201		29-Feb-88
	Sender OPENF% failures can be due to a variety of transcient DECnet
problems, as can listener MTOPR% failures. For all OPENF% failures, retry
up to five minutes. For MTOPR% failures, CLOSF% the link and re-open the
link. If there is no success after the retries, then inform ORION and
HALTF%, but do not stopcode.

6022	6.1202		29-Feb-88
	If a cluster topology change occurs and if no listener or sender
to a node has been started, do not cause routine RESTAR to erronously
send a WTO message to ORION stating that that node's sender and listener
has been restarted.

6023	6.1203		29-Feb-88
	Correct the response message format to the command:
OPR>SHOW CLUSTER/NODE:FOO/CLUSTER-NODE:FOO

6024	6.1209		2-Mar-88
	If a REMOTE DISMOUNT message cannot be sent to a remote node, then
indicate this in the remote queue entry with monitor error NSPX18 so that
MOUNTR can display the reason in its WTO message to ORION.

6025	6.1221		6-Mar--88
	More of edit 6024. MOUNTR also requires bit RQ%TER set in order to
display the error string.

6026	6.1225		8-Mar-88
	Update copyright notice.

6027	6.1239		26-Apr-88
	If communication is lost to a remote NEBULA and if there is an
outstanding response to a dismount message from that NEBULA, then indicate
error SCLX08 (Unexpected state - no communication) in the TO NEBULA DISMOUNT
ACK message that is sent to MOUNTR. 
 Also, if NEBULA receives an unknown IPCF message, then log that fact with
ORION.

6030	6.1241		27-Apr-88
	More of edit 6030. MOUNTR also requires bit RQ%TER set in order to
display the error string.

6031	6.1244		30-Apr-88
	Update timer word MSGTIM during every scheduling pass rather than
only when an IPCF message is received. 

6032	6.1245		3-May-88
	Clear the SAB index word (SAB.SI) when sending a message to an EXEC,
otherwise, the message may be sent to QUASAR if the previous IPCF message
had been sent to QUASAR.

6033	6.1247		5-May-88
	Add support for the SHOW STATUS NETWORK-NODE and SHOW PARAMETERS
NETWORK-NODE commands.

6034	6.1248		6-May-88
	Cause NEBULA to wait for ORION to be running. If NEBULA starts up
before ORION does, then the first message NEBULA receives from ORION is
rejected.

6035	6.1252		10-May-88
	Change routines RESTAR and CHKDEC to first check if there are any
remaining remote nodes in the cluster. If there are not, then have them
return. This prevents NTI crashes from occurring in the case where a node
has left the cluster and there are no longer any remote nodes in the cluster.

6036	6.1256		13-May-88
	Cause NEBULA to wait for QUASAR to be running. If NEBULA starts up
before QUASAR does, then NEBULA rejects all messages from QUASAR since
QUASAR is not in its system PID table.

\   ;End of Revision History
	SUBTTL	SYMBOL DEFINITONS

PDSIZ==^D200				;SIZE OF THE PUSH DOWN STACK
MAXNOD==7				;MAXIMUM NUMBER OF REMOTE NODES
DBSIZ==2				;SIZE OF SENDER/LISTENER DATA BASE
M==13					;INCOMING IPCF MESSAGE ADDRESS
MO==14					;OUTGOING IPCF MESSAGE
LIS==15					;LISTENER DATA BASE ADDRESS
SEN==16					;SENDER DATA BASE ADDRESS
NNMFLD==17B35				;NODE NUMBER FIELD
VERNUM==700				;MINIMUM VALID MONITER VERSION NUMBER
INFBSZ==6				;INFO% JSYS BLOCK SIZE
SCSCN==4				;SCS% CHANNEL NUMBER
SCSLEN==1+.SQDTA+SQ%CDT			;SIZE OF SCS% EVENT BLOCK
CNFLEN==1+3*<MAXNOD+1>			;ASCIZ NODE NAME BLOCK LENGTH
MSGLN==^D200				;[6012]LENGTH OF EXEC ERROR MESSAGE
TIMOUT==<^D5*^D60*^D1000>/^D333		;TIMEOUT VALUE FOR REMOTE NODE RESPONSE
TOPTRY==<^D3*^D60*^D1000>/^D333		;[6016]TIME IN WHICH TO CHECK TOPOLOGY
TIMUP==5*^D60*^D1000			;[6016]MIN MONITOR UPTIME FOR NO RECHECK
IEMAX==5				;[6016]MAX TIMER TRIES ON INFO% ERROR
	SUBTTL	RANDOM STORAGE AREA

PDL:	BLOCK PDSIZ			;PUSH DOWN STACK
NODNAM:	BLOCK 1				;THE LOCAL NODE NAME (SIXBIT)
NODNUM: BLOCK 1				;THE LOCAL NODE NUMBER
RENNUM:	BLOCK 1				;NUMBER OF REMOTE NODES
NBSCHD:	BLOCK 1				;NEBULA SCHEDULING FLAG
NEBSIZ: BLOCK 1				;THE NUMBER OF PAGES IN NEBULA
LOCACK:	BLOCK 1				;LOCAL ACK CODE FOR IBH MESSAGES
MSGTIM:	BLOCK 1				;TIME AN IPCF MESSAGE WAS PICKED UP
TIMCTR: BLOCK 1				;MAKE UNIQUE UDT FOR TIMEOUT QUEUES
TIMBLK:	BLOCK .TIDAT+1			;SIZE OF THE TIMER EVENT QUEUE ENTRY
BYTPTR:	BLOCK 1				;SAVE AREA FOR A BYTE POINTER
G$ERR:	BLOCK 1				;ERROR DETECTED DURING IPCF PROCESSING
G$SND:	BLOCK 1				;PID OF IPCF MESSAGE SENDER
G$MSG:	BLOCK MSGLN			;[6012]EXEC ERROR MESSAGE BUFFER
RCFFLG:	BLOCK 1				;REPORT-CONNECTION-FAILURES FLAG
DCAFLG:	BLOCK 1				;DECNET-CONNECTION-ATTEMPTS FLAG
STFLAG: EXP  -1                         ;NEBULA NOT RESTARTABLE FLAG
BLDTAB: EXP  -1				;NODE TABLE BEING BUILT AT STARTUP
SAB:	BLOCK SAB.SZ			;IPCF SENDER ADDRESS BLOCK
SCSBLK:	EXP   2				;SCS% INTERRUPT ENABLED BLOCK SIZE
	XWD   .SIPAN,SCSCN		;ASSOCIATE SCS EVENTS WITH CHAN SCSCN
SCSEBK:	BLOCK SCSLEN			;SCS% EVENT BLOCK
REMHDR: BLOCK 1				;GLOBAL REMOTE QUEUE HEADER WORD
REMTRL: BLOCK 1				;GLOBAL REMOTE QUEUE TRAILER WORD
IBQHDR:	BLOCK 1				;IN BEHALF OF QUEUE HEADER WORD
IBQTRL: BLOCK 1				;IN BEHALF OF QUEUE TRAILER WORD
REMORG:	BLOCK 1				;MESSAGE ORIGINATED FROM A REMOTE NODE
REMFLG:	BLOCK 1				;PROCESS THE MESSAGE REMOTELY
LOCFLG:	BLOCK 1				;PROCESS THE MESSAGE LOCALLY
BLKADR:	BLOCK 1				;.WTTXT BLOCK ADDRESS, USED BY BLDHAD
NODDAT: BLOCK 3				;NODE NAME PASSED AS OPTIONAL DATA
EXESND: BLOCK 1				;[6012]MESSAGE FROM EXEC FLAG
EXESHW:	BLOCK 1				;[6012]EXEC SHOW/KILL RESPONSE FLAG
STABLK:	BLOCK 1				;[6013].CMTXT BLOCK ADDRESS

;THE G$ARGX WORDS MUST BE CONTIGUOUS. ENSURE THIS BY DEFINING THEM WITH
;A MACRO

DEFINE CONCAT (X) <
G$ARG'X': BLOCK 1			;;DEFINE THE ERROR WORD
>

DEFINE	G$ARGX (A) <
 IRP A,<IFE MAXNOD-A,<STOPI>
 CONCAT A >				;;SETUP THE ERROR WORD
>

G$ARGX	<1,2,3,4,5,6,7>			;SETUP THE ERROR BLOCK

;      STORAGE USED BY NTDSM 

NTSMSG:	BLOCK 1				;NUMBER OF NODES TO SEND MESSAGE TO
NNIMSG: BLOCK 1				;NUMBER OF NODES IN IPCF MESSAGE
FIRNOD: BLOCK 1				;ADDRESS OF FIRST NODE
LLWSIZ: BLOCK 1				;SIZE OF LINK LIST BLOCK IN MSG QUEUE
MQMADR: BLOCK 1				;ADDRESS OF MESSAGE IN MESSAGE QUEUE
STRLEN: BLOCK 1				;LENGTH OF STRUCTURE NAME BLOCK
IBLADR: BLOCK 1   			;INVARIANT BLOCK ADDRESS

;	STORAGE USED BY TOPCHN 

CURNUM: BLOCK 1				;CURRENT NUMBER OF NODES
                                            
;MACRO USED BY NSCLU

DEFINE	$ASCII(MSG),<
	PUSHJ	P,ASCOUI		;;CALL THE IN-LINE ASCII OUTPUTTER
	CAI	[ASCIZ+MSG+]		;;AIM AT THE MESSAGE
>;END $ASCII DEFINE

NEBACK:	5,,.NACKM			;[6016]NEBULA ACK MESSAGE HEADER WORD
	EXP 0,0,0,0			;[6016]NEBULA ACK MESSAGE HEADER
NEBRSP:	5,,.NACKR			;[6016]NEBULA ACK RESPONSE MSG HDR WORD
	EXP 0,0,0,0			;[6016]NEBULA ACK RESPONSE MSG HEADER
	SUBTTL	RESIDENT JOB DATA BASE

SREADY:	BLOCK 1				;SENDER IS AVAILABLE FLAG
LREADY:	BLOCK 1				;LISTENER HAS MESSAGE FLAG
IREADY: BLOCK 1				;AN INFERIOR FORK HAS DECNET CONNECTION
TRMFRK: BLOCK 1				;AN INFERIOR FORK HAS TERMINATED
SCSFLG: BLOCK 1				;A CLUSTER TOPOLOGY CHANGE DETECTED
ASZNAM:	BLOCK CNFLEN			;ASCIZ NODE NAMES FROM CNFIG% .CFCND
SWINFO:	BLOCK .CFILN			;STATIC SOFTWARE INFORMATION
INFBLK: BLOCK INFBSZ			;INFO% SOFTWARE INFORMATION BLOCK

;NNMTBL AND NDNTBL ARE USED DURING CLUSTER TOPOLOGY CHANGE PROCESSING BY
;ROUTINE TOPCHN. NODSEN AND NODMQ ARE USED DURING IPCF MESSAGE PROCESSING
;BY ROUTINES SETPAR, SEPALL AND QUEMSG. THESE TWO SETS OF TABLES MUST NOT
;BE USED CONCURRENTLY.

NNMTBL: NODSEN: BLOCK 2+MAXNOD		;NODE NUMBER TABLE
NDNTBL: NODMQ:	BLOCK MAXNOD		;REMOTE (SIXBIT) NODE NAME TABLE
NODTBL:	BLOCK   MAXNOD*.NNNSZ		;NODE TABLE
RECHEK: BLOCK	1			;[6016]RECHECK NODE NAME TABLE
NDLKST:	BLOCK	1			;PREVIOUSLY AND CURRENTLY KNOWN NODES 
NDLKNM:	BLOCK	1			;NUMBER OF ENTRIES IN PACKN TABLE
NDLKFR:	BLOCK	1			;NUMBER OF FREE ENTRIES IN PACKN TABLE
	SUBTTL	PID AND INTERRUPT DEFINITION

INTVEC==:LEVTAB,,CHNTAB

IB:	$BUILD	IB.SZ			;
	  $SET	(IB.PRG,,%%.MOD)	;PROGRAM 'NEBULA'
	  $SET  (IB.FLG,IP.STP,1)	;STOPCODES TO ORION
	  $SET(IB.FLG,IB.SYS,NEB.JP)	;SET SYSTEM PROCESS
	  $SET	(IB.PIB,,PIB)		;SET UP PIB ADDRESS
	  $SET	(IB.INT,,INTVEC)	;SETUP INTERRUPT VECTOR ADDRESS
	$EOB				;

PIB:	$BUILD	PB.MNS			;
	  $SET	(PB.HDR,PB.LEN,PB.MNS)	;PIB LENGTH,,0
	  $SET	(PB.FLG,IP.PSI,1)	;PSI ON
	  $SET	(PB.FLG,IP.RSE,1)	;RETURN ON SEND ERROR
	  $SET	(PB.FLG,IP.SPB,1)	;SEE IF IPCF SENDER WAS PRIVILEGED
	  $SET	(PB.FLG,IP.JWP,1)	;JOB-WIDE PID
	  $SET	(PB.INT,IP.CHN,2)	;IPCF INTERRUPT CHANNEL
	  $SET	(PB.INT,IP.SPI,SP.NEB)	;SET UP SYSTEM PID
	  $SET	(PB.SYS,IP.BQT,-1)	;MAXIMUM SEND/RECEIVE IPCF QUOTA
	  $SET	(PB.SYS,IP.MNP,^D5)	;NUMBER OF PIDS
	$EOB				;


LEVTAB:	EXP	LEV1PC			;INTRPT LEVEL 1 PC ADDRESS
	EXP	LEV2PC			;INTRPT LEVEL 2 PC ADDRESS
	EXP	LEV3PC			;INTRPT LEVEL 3 PC ADDRESS

CHNTAB:	XWD	1,LISMSG		;LISTENER HAS A MESSAGE
	XWD	1,SNDREA		;SENDER IS READY FOR A MESSAGE
	XWD	1,NBIPCF		;IPCF HAS A MESSAGE
	XWD	1,INFRDY		;INFERIOR FORK HAS DECNET CONNECTION
	XWD	1,NBSCS			;SCS DETECTED A CLUSTER TOPOLOGY CHANGE
	BLOCK	^D31			;INFERIOR FORK TERMINATED ON CHANNEL 19
					;ALL OTHER CHANNELS 0

LEV1PC:	BLOCK	1			;LEVEL 1 INTERRUPT PC
LEV2PC:	BLOCK	1			;LEVEL 2 INTERRUPT PC
LEV3PC:	BLOCK	1			;LEVEL 3 INTERRUPT PC
	SUBTTL	NEBULA STARTUP AND SCHEDULER

;SET UP NEBULA

NEBULA:	RESET%				;THE USUAL
SINGLE<	HALTF%>				;[6015]QUIT IF CLUSTER GALAXY NOT ENABLED
	MOVE	P,[IOWD PDSIZ,PDL]	;SET UP THE STACK.
	AOSE	STFLAG			;RESTARTING?
	$STOP	(NNR,NEBULA NOT RESTARTABLE)
	MOVEI	S1,IB.SZ		;GET THE INITIALIZATION BLOCK SIZE.
	MOVEI	S2,IB			;ADDRESS OF THE INITIALIZATION BLOCK
	$CALL	I%INIT			;SET UP GLXLIB
	$CALL	NEBENV			;CHECK THE NEBULA ENVIRONMENT
	$CALL	NBINIT			;GO SETUP CONSTANTS AND CAPABILITIES
	$CALL	WAIQSR			;[6036]WAIT FOR QUASAR TO START UP
	$CALL	WAIORN			;[6034]WAIT FOR ORION TO START UP
	$CALL	INTINI			;SET UP THE INTERRUPT SYSTEM.
	$CALL	NODINT			;INITIALIZE THE NODE DATA BASE
	$CALL	BLDLST			;SETUP AND BUILD THE PACKN TABLE
	SKIPE	RENNUM			;ANY REMOTE NODES?
	$CALL	STLAS			;YES, START UP THE LISTENERS/SENDERS

MAIN:	SETZM	NBSCHD			;SLEEP AFTER THIS PASS
	$CALL	I%NOW			;[6031]PICK UP THE CURRENT TIME
	MOVEM	S1,MSGTIM		;[6031]SAVE FOR LATER

	SKIPE	SCSFLG			;CLUSTER TOPOLOGY CHANGE OCCURRED?
	$CALL	TOPCHN			;YES, UPDATE THE NODE DATA BASE

	SKIPE	TRMFRK			;HAS A SENDER OR LISTENER CRASHED?
	$CALL	RESTAR			;YES, FIND OUT WHICH ONE

	SKIPE	IREADY			;INFERIOR FORK HAS A DECNET CONNECTION?
	$CALL	CHKDEC			;YES, FIND OUT WHICH ONE

	$CALL	CHKQUE			;CHECK FOR IPCF MESSAGES

	SKIPE	SREADY			;IS A SENDER AVAILABLE?
	$CALL	SNDMSG			;YES, FIND OUT WHICH SENDER 

	SKIPE	LREADY			;DOES A LISTENER HAVE A MESSAGE?
	$CALL	GETMSG			;YES, PICK IT UP

	SKIPE	NBSCHD			;TIME FOR ANOTHER SCHEDULING PASS?
	JRST	MAIN			;YES, GO CHECK AGAIN

	SETZ	S1,			;SLEEP TILL WE'RE NEEDED
	$CALL	I%SLP			;DON'T WAKE UP UNTIL NEEDED
	JRST	MAIN			;CHECK OUT WHAT HAPPEN
	SUBTTL	NEBENV - CHECK THE NEBULA ENVIRONMENT

;NEBENV is called during NEBULA startup to determine if the local node
;has the following environment:
; DECnet enabled
; A monitor of release 7 or later
; Cluster GALAXY enabled
;
;If any of these attributes are not present, then NEBULA crashes.
;
;Call is:      No arguments
;Returns true: Only if DECnet is enabled for this node, the monitor is
;              release 7 or later, and the Cluster GALAXY option is enabled.
;Crashes:      If cannot obtain the static software information, DECnet is
;              not enabled, or the monitor is pre-release 7.

NEBENV:	$SAVE	<T1,T2>			;CONFG% JSYS CHANGES THESE AC
	MOVEI	S2,SWINFO		;PICK UP ARGUMENT BLOCK ADDRESS
	MOVEI	S1,.CFILN		;PICK UP THE LENGTH OF THE ARG BLOCK
	MOVEM	S1,.CFLEN(S2)		;PLACE IT IN THE ARGUMENT BLOCK
	MOVEI	S1,.CFINF		;PICK UP THE FUNCTION
	CNFIG%				;GET THE SOFTWARE INFORMATION
	 ERJMP	[$STOP	(COS, CAN'T OBTAIN STATIC SOFTWARE INFORMATION)]

	MOVEI	S1,SWINFO		;PICK UP ARGUMENT BLOCK ADDRESS
	MOVE	S2,.CFISO(S1)		;PICK UP THE STATIC SOFTWARE OPTIONS
	TLNN	S2,(CF%DCN)		;IS DECNET INSTALLED?
	$STOP	(DNI, DECNET NOT INSTALLED)	;NO, TERMINATE NOW
	HLRZ	S2,.CFIVR(S1)		;PICK UP THE VERSION NUMBER
	CAIGE	S2,VERNUM		;RELEASE 7 OR LATER?
	$STOP	(IMV, INVALID MONITOR VERSION)

	$RETT				;NEBULA IS IN THE CORRECT ENVIRONMENT
	SUBTTL	NBINIT - GET NODE NAME, NODE NUMBER, AND SIZE OF NEBULA

;NBINIT is called during NEBULA startup. This routine determines the
;name and cluster node number of this node (the local node). 
;In addition, the size of NEBULA in pages is also determined. This is 
;required when mapping a sender or listener.
;Also, the seed for the local ACK codes used in the in behalf of queue
;is determined and saved.
;
;Call is: No arguments
;Returns: Node name and number, NEBULA size determined and saved

;PICK UP THE NODE NAME AND NUMBER OF THE LOCAL NODE

NBINIT:	$CALL 	I%HOST			;PICK UP THE HOST NAME AND NUMBER
	MOVEM	S1,NODNAM		;SAVE THE SIXBIT NODE NAME
	MOVEM	S2,NODNUM		;SAVE THE NODE NUMBER

;PICK UP AND SAVE THE SIZE OF NEBULA IN PAGES

	SKIPE	DEBUGW			;DEBUGGING?
	SKIPN	116			;AND ARE SYMBOLS DEFINED?
	JRST	NBIN.1			;IF NO TO EITHER, THEN SKIP THIS
	HLRO	S1,116			;GET AOBJN LENGTH
	MOVMS	S1			;GET ABSOLUTE VALUE
	HRRZ	S2,116			;GET SYMBOL TABLE START ADDRESS
	ADDI	S1,-1(S2)		;CALCULATE THE SYMBOL TABLE LENGTH
	SKIPA				;SKIP OVER NORMAL CALCULATIONS
NBIN.1:	HLRZ	S1,.JBSA##		;GET THE PROGRAM END ADDRESS
	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

	$TEXT(<-1,,.SNDCN(SEN)>,<DCN:^I/@BLDDC3/^I/@BLDDC4/^I/@BLDDC5/^0>)

	$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>)]

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

	$TEXT(<-1,,.SNDCN(SEN)>,<DCN:^I/@BLDDC9/^I/@BLDD10/^I/@BLDD11/^0>)

	$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
	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
	JRST	CHKQU6			;UNKNOWN MESSAGE TYPE, DISCARD IT

;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

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 
	XWD	NDISPY,.OMDSP		;ACK 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)

	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
	MOVE	P1,.OARGC(M)		;PICK UP THE ARGUMENT COUNT WORD
	CAILE	P1,.TNNMB		;BETTER BE THE CORRECT NUMBER
	PJRST	E$IAC			;IT'S NOT, TELL ORION
	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
	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?
	JRST	[ SETZM	REMORG		  ;NO, INDICATE SO
		  PJRST	E$NRN ]		  ;INDICATE THE ERROR

	SETOM	LOCFLG			;ASSUME ALL NODES, LOCAL
	SETZM	REMFLG			;ASSUME MSG NOT TO BE FORWARDED

;VALIDATE THE ARGUMENT BLOCK COUNT

	SKIPGE	S1,.OARGC(M)		;VALID MINIMUM ARGUMENT COUNT?
	PJRST	E$IAC			;NO, INDICATE TO ORION
	CAILE	S1,2			;VALID MAXIMUM ARGUMENT COUNT?
	PJRST	E$IAC			;NO, INDICATE TO ORION
	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
	MOVEM	S1,G$ARG1		;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

NSCL15:	MOVEI	S1,.NDESZ		;PICK UP THE REMOTE NODE BLOCK SIZE
	MOVSS	S1			;PLACE LENGTH IN EXPECTED PLACE
	MOVNS	S1			;NEGATE THE LENGTH
	ADDM	S1,.MSTYP(M)		;DECREMENT THE TOTAL MESSAGE LENGTH
	MOVEI	S1,.NSRCL		;PICK UP THE MESSAGE TYPE
	STORE	S1,.MSTYP(M),MS.TYP	;PLACE IN THE MESSAGE
	SOS	.OARGC(M)		;DECREMENT THE ARGUMENT COUNT

	MOVE	S1,REMFLG		;PICK UP THE REMOTE NODE FLAG
	CAMN	S1,[-1]			;FOR ALL REMOTE NODES?
	JRST	NSCL16			;YES, SEND TO ALL REMOTE NODES
	$CALL	QSREMM			;NO, SEND TO THE INDICATED NODE
	JUMPF	@S1			;REPORT ANY ERRORS
	$RET				;RETURN TO THE IPCF MESSAGE HANDLER

NSCL16:	$CALL	QMREMM			;SEND TO ALL THE REMOTE NODES
	SETZM	G$ERR			;INDICATE ALREADY REPORTED ANY ERRORS
	$RET				;RETURN TO THE IPCF MESSAGE HANDLER

;THE NODE NAME SPECIFIED IN THE /NODE: SWITCH IS NOT KNOWN TO NEBULA. 
;SEND AN ERROR MESSAGE

NSCL17:	SKIPE	REMFLG			;[6023]MSG ALSO TO BE SENT REMOTELY?
	JRST	NSCL15			;[6023]YES, SEND THE MESSAGE REMOTELY
	MOVE	S1,LOCFLG		;[6023]PICK UP NODE NAME SPECIFIED 
	CAME	S1,NODNAM		;[6023]SAME AS THE LOCAL NODE NAME?
	PJRST	E$NSN			;[6023]NO, INDICATE NO SUCH NODE
	PJRST	E$INS			;[6023]CAN'T SPECIFY THE 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 ilegally formatede 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.

	SKIPG	S1,.OARGC(M)		;ANY ARGUMENT BLOCKS 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.

	CAIE	S1,1			;CHECK FOR ONE ARGUMENT BLOCK
	PJRST	E$IAC			;MORE THAN ONE, REPORT THE ERROR
	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
	MOVEM	S1,G$ARG1		;SAVE IT IN CASE IT IS UNKNOWN
	$CALL	SNAMNT			;FIND THE NODE'S NODE TABLE ENTRY
	JUMPF	E$NSN			;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?
	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

;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

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

NDRC10:	MOVEI	S1,NDRC12		;PICK UP ADDRESS OF ENABLE/DISABEL 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))
	$RETT				;RETURN TO THE CALLER

NDRC11:	$ACK	(NEBULA report connection failures ^T/@0(S1)/,,,.MSCOD(M))
	$RETT				;RETURN TO THE CALLER

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.

	SKIPG	S1,.OARGC(M)		;ANY ARGUMENT BLOCKS 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.

	CAIE	S1,1			;CHECK FOR ONE ARGUMENT BLOCK
	PJRST	E$IAC			;MORE THAN ONE, REPORT THE ERROR
	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
	MOVEM	S1,G$ARG1		;SAVE IT IN CASE IT IS UNKNOWN
	$CALL	SNAMNT			;FIND THE NODE'S NODE TABLE ENTRY
	JUMPF	E$NSN			;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	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

NDDC10:	MOVEI	S1,NDDC12		;PICK UP ADDRESS OF ENABLE/DISABEL 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))
	$RETT				;RETURN TO THE CALLER

NDDC11:	$ACK	(NEBULA connection attempts ^T/@0(S1)/,,,.MSCOD(M))
	$RETT				;RETURN TO THE CALLER

NDDC12:	[ASCIZ/enabled/]
        [ASCIZ/disabled/]
	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

NODSTR:	[ITEXT(<  Received message from ^N/NODNAM/>)]	;[6001]
	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.

	MOVE	S1,.OARGC(M)		;PICK UP THE NUMBER OF ARGUMENT BLOCKS
	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
	JUMPF	QRIBH2			;NODE HAS LEFT THE CLUSTER

;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.

	MOVE	P1,S2			;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
	MOVE	S1,P1			;PICK ADDRESS OF THE IBH QUEUE ENTRY
	$CALL	RELIBH			;RELEASE THE IBH QUEUE ENTRY
	$RETT				;RETURN TO THE CALLER
QRIBH2:	$RET				;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

;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

	MOVE	SEN,.NNSBA(S2)		;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
	MOVEM	P1,G$ARG1		;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
	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
	MOVE	P3,RENNUM		;PICK UP THE NUMBER OF REMOTE NODES
	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

;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

	MOVE	SEN,.NNSBA(P2)		;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

BLDACK:	$CALL	BLDHDR			;GET MSG PAGE AND BUILD HEADER

;FINISH BUILDING THE MESSAGE HEADER

	MOVE	S1,G$ERR		;PICK UP ERROR NUMBER
	MOVE	S2,STSTBL(S1)		;PICK UP FLAG WORD
	MOVEM	S2,.MSFLG(MO)		;PLACE IN MESSAGE HEADER
	SKIPGE	G$ERR			;ITEXT PRESENT IN THE ERROR CODE
	JRST	BLDAC3			;YES, BUILD THE TEXT BLOCK THAT WAY

;THE TEXT BLOCK IS INVARIANT. PLACE IN THE MESSAGE

	MOVE	S2,TXTTBL(S1)		;PICK UP THE TEXT BLOCK ADDRESS
	MOVEI	S1,ARG.DA+.OHDRS(MO)	;PICK UP THE DATA FIELD ADDRESS
	$CALL	TXTMOV			;PLACE THE ERROR MSG IN TEXT BLOCK

;FINISH OFF BUILDING THE MESSAGE 

BLDAC2:	HRRZS	S1			;GET ENDING ADDRESS
	AOS	S1			;BUMP IT BY 1
	ANDI	S1,777			;GET MESSAGE LENGTH
	STORE	S1,.MSTYP(MO),MS.CNT	;SAVE COUNT IN MESSAGE
	SUBI	S1,.OHDRS		;GET SIZE OF BLOCK
	STORE	S1,ARG.HD+.OHDRS(MO),AR.LEN	;SAVE ARGUMENT LENGTH
	MOVX	S1,.WTTYP		;GET TEXT CODE
	STORE	S1,ARG.HD+.OHDRS(MO),AR.TYP	;SAVE ARGUMENT TYPE
	$RET				;RETURN
	
;THE TEXT BLOCK HAS VARIABLE DATA. 

BLDAC3:	MOVEI	S2,ARG.DA+.OHDRS(MO)	;GET PLACE TO STORE TEXT
	HRLI	S2,(POINT 7,0)		;MAKE A POINTER
	MOVEM	S2,BYTPTR		;SAVE THE POINTER
	$TEXT	(BLDAC4,<^I/@TXTTBL(S1)/^0>)	;[6013]PLACE TEXT IN MESSAGE
	MOVE	S1,BYTPTR		;GET BYTPTR
	JRST	BLDAC2			;FINISH OFF THE MESSAGE

BLDAC4:	IDPB	S1,BYTPTR		;SAVE THE CHARACTER
	$RETT				;RETURN TRUE
	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.

	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

PROMS4:	$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)

	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

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

LSIBHM:	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
	
	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
	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
	JUMPT	NSRC.1			;ON SUCCESS SEND THE REPLY
	SKIPN	REMORG			;ANY REMOTE NODES STILL IN THE CLUSTER?
	$RET				;NO, SO RETURN NOW

	$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
	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
	MOVX	S1,WT.SJI!WT.NFO	;[6023]PICK UP THE FLAG WORDS
	MOVEM	S1,.OFLAG(M)		;[6023]PLACE IN THE FLAG WORD
	AOS	.OARGC(M)		;INCREMENT THE ARGUMENT COUNT

;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

;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
	$RET				;RETURN TO THE CALLER

NSEA.1:	[ITEXT(<  Received message from ^N/NODNAM/::>)]
	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.
;[6016]
;[6016]Call is: M/NEBULA ACK RESPONSE message address
;[6016]Returns: The original NEBULA ACK message 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

repeat 0,<
	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVEI	S2,.MORDA		;WANT THE OCTAL DATA FIELD
	HRROI	T1,.LSOPT(LIS)		;WHERE TO PLACE THE OCTAL DATA
	MTOPR%				;PCIK UP THE OCTAL DATA
	 ERJMP	ACCEP4			;CRASH IF CAN'T OBTAIN INFORMATION

	MOVE	S2,NODNAM		;PICK UP THE LOCAL SIXBIT NODE NAME
	ROT	S2,3			;ROTATE BY 3
	MOVE	T1,[POINT 8,T3]		;WHERE TO PLACE OCTAL VALUE
	SETZ	T3,			;INITIALIZE OCTAL VALUE TO ZERO
	MOVEI	T2,4			;PICK UP NUMBER OF BYTES TO BUILD

ACCEP2:	LSHC	S1,^D9			;PICK UP THE NEXT BYTE
	IDPB	S1,T1			;STORE AS AN EIGHT BIT BYTE
	SOJN	T2,ACCEP2		;PICK UP THE NEXT BYTE

	CAME	T3,.LSOPT(LIS)		;SAME OCTAL VALUE AS THE SENDER'S?
	JRST	ACCEP3			;NO, SO REJECT THIS REQUEST
>;end of repeat 0

;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.

	MOVE	S1,.LSJFN(LIS)		;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



;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