Google
 

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

	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	LISMAC			;CHECK LISSPL'S LIBRARY
	SEARCH	GLXMAC			;CHECK GALAXY LIBRARY
	SEARCH	QSRMAC			;CHECK QUASAR'S LIBRARY
	SEARCH	ORNMAC			;CHECK ORION'S LIBRARY
	SEARCH	GALCNF			;GET CONFIGURATION DATA
	SALL				;CLEAN LISTINGS

	PROLOG (LISSPL)			;GENERATE THE NECESSARY GALAXY SYMBOLS
LISVEC:	BLDVEC	(LISMAC,NMC,L)
	BLDVEC	(LISSPL,LIS,L)
	BLDVEC	(GLXMAC,GMC,L)
	BLDVEC	(ORNMAC,OMC,L)
	BLDVEC	(QSRMAC,QMC,L)

	LISMAN==:0			;MAINTENANCE EDIT NUMBER
	LISDEV==:6006			;DEVELOPMENT EDIT NUMBER
	VERSIN (LIS)			;GENERATE EDIT NUMBER



	LISWHO==0
	LISVER==6			;MAJOR VERSION NUMBER
	LISMIN==0			;MINOR VERSION NUMBER

	LISVRS==<VRSN.(LIS)>+NMCEDT+GMCEDT+OMCEDT+QMCEDT

	LOC	137
	EXP	LISVRS
	RELOC	
	Subttl	Table of Contents

;		     Table of Contents for LISSPL
;
;				  Section		      Page
;
;
;    1. Revision history . . . . . . . . . . . . . . . . . . .   5
;    2. LISSPL STARTUP AND SCHEDULER . . . . . . . . . . . . .   7
;    3. LISENV - CHECK THE LISSPL ENVIRONMENT  . . . . . . . .   8
;    4. LSINIT - GET NODE NAME, NODE NUMBER, AND SIZE OF LISSP   9
;    5. INTINI - INTERRUPT INITIALIZATION AND HANDLERS . . . .  10
;    6. LISSPL INTERRUPT HANDLERS  . . . . . . . . . . . . . .  11
;    7. NBSCS - TOPOLOGY CHANGE DECTECTED INTERRUPT HANDLER  .  12
;    8. TABINT - INITIALIZE THE TABLES . . . . . . . . . . . .  13
;    9. SRTLIS - START UP THE LISTENERS  . . . . . . . . . . .  14
;   10. STLIS - START UP THE LISTENER  . . . . . . . . . . . .  15
;   11. BLDSRV - BUILD THE SRV: DEVICE NAME  . . . . . . . . .  16
;   12. CLUSTER TOPOLOGY CHANGE DETECTED . . . . . . . . . . .  17
;   13. RESTAR - PROCESS CRASHED INFERIOR FORKS  . . . . . . .  18
;   14. CHKQUE - IPCF MESSAGE PROCESSING . . . . . . . . . . .  19
;   15. RELEAS - RELEASE MESSAGE PROCESSING  . . . . . . . . .  20
;   16. SRHSEN - SEARCH FOR A NODE'S SENDER TABLE ENTRY  . . .  21
;   17. CHKLEN - CHECK THE VALIDITY OF AN IPCF MESSAGE . . . .  22
;   18. QUEMSG - SEND OR QUEUE A MESSAGE . . . . . . . . . . .  23
;   19. SNDMSG - SEND MESSAGES TO AVAILABLE SENDERS  . . . . .  24
;   20. SENMSG - NOTIFY A SENDER OF A MESSAGE AVAILABLE  . . .  25
;   21. GETMSG - PICK UP A MESSAGE FROM A LISTENER . . . . . .  26
;   22. PROMSG - PROCESS DECNET LISTENER MESSAGES DISPATCHER .  27
;   23. NEXTJB - SEND A NEXTJOB MESSAGE FROM A REMOTE NODE TO   28
;   24. RESLIS - RESTART A LISTENER THAT HAS CRASHED . . . . .  29
;   25. DELSEN - KILL AND DELETE A SENDER THAT HAS CRASHED . .  30
;   26. KILSEN - KILL A SENDER THAT HAS CRASHED  . . . . . . .  31
;   27. INFSTS - DETERMINE THE STATUS OF AN INFERIOR FORK  . .  32
;   28. STSEN - START UP A SENDER  . . . . . . . . . . . . . .  33
;   29. BLDDCN - BUILD THE DCN: DEVICE NAME  . . . . . . . . .  34
;   30. SNDQSR - SEND AN IPCF MESSAGE TO QUASAR  . . . . . . .  35
;   31. ADDMQE - ADD A MESSAGE TO A SENDER'S MESSAGE QUEUE . .  36
;   32. RELMQE - RETURN A MESSAGE QUEUE ENTRY TO MEMORY MANAGE  37
;   33. BLDMQE - BUILD A MESSAGE QUEUE ENTRY . . . . . . . . .  38
;   34. LISTEN - MESSAGE SERVER FOR A REMOTE NODE  . . . . . .  39
;   35. LISSET - INITIALIZE THE LISTENER'S GLXLIB AND CAPABILI  40
;   36. LOPLNK - OPEN A DECNET SRV: DEVICE . . . . . . . . . .  41
;   37. LISINT - SET UP THE LISTENER'S INTERRUPT SYSTEM  . . .  42
;   38. ACCEPT - VALIDATE A DECNET CONNECTION REQUEST  . . . .  43
;   39. MSGFSN - DECNET MESSAGE FROM SENDER IS AVAILABLE . . .  44
;   40. CHKRM - CHECK IF ENOUGH ROOM TO ADD AN FP/FD PAIR  . .  45
;   41. SENNXJ - SEND OR QUEUE AN INTERMEDIATE NEXTJOB MESSAGE  46
;   42. CHKACC - CHECK IF SYSTEM HAS ACCESS TO FILE  . . . . .  47
;   43. NOTFRK - NOTIFY TOP FORK OF MESSAGE FROM A LISTENER  .  48
;   44. MSGTTF - TOP FORK READY FOR A MESSAGE FROM A LISTENER   49
;   45. XFRTOP - MOVE MESSAGE FROM MESSAGE QUEUE TO MESSAGE BU  50
;   46. XFRMSG - TRANSFER IPCF MESSAGE FROM ONE BUFFER TO ANOT  51
;   47. INTMSG - PROCESS AN INTERRUPT MESSAGE FROM THE SENDER   52
	Subttl	Table of Contents (page 2)

;		     Table of Contents for LISSPL
;
;				  Section		      Page
;
;
;   48. CHKLST - CHECK LINK STATUS . . . . . . . . . . . . . .  53
;   49. ADDLME - ADD A LISTENER MESSAGE QUEUE ENTRY  . . . . .  54
;   50. LISCHK - LISTENER CHECKSUM MESSAGE . . . . . . . . . .  55
;   51. SNDACK - SEND A SUCCESS ACK TO THE SENDER  . . . . . .  56
;   52. LCKLNK - CHECK THE STATUS OF THE LISTENER'S LINK . . .  57
;   53. INLCRH - ROUTINE TO INDICATE LISTENER CONTROLLED CRASH  58
;   54. LABLNK - ABORT THE LISTENER'S DECNET LINK  . . . . . .  59
;   55. SENDER - MESSAGE ROUTER TO A REMOTE NODE . . . . . . .  60
;   56. SENSET - INITIALIZE THE SENDER'S GLXLIB AND CAPABILITI  61
;   57. SENINT - SET UP THE SENDER'S INTERRUPT SYSTEM  . . . .  62
;   58. SOPLNK - OBTAIN A CONNECTION TO THE LISTENER . . . . .  63
;   59. SGTLNK - OBTAIN DECNET JFN AND OPEN IT . . . . . . . .  64
;   60. SCKLNK - CHECK THE STATUS OF THE SENDER'S LINK . . . .  65
;   61. FNDCER - DETERMINE THE DECNET CONNECTION ERROR . . . .  66
;   62. MSGTLI - SEND A MESSAGE TO THE LISTENER  . . . . . . .  67
;   63. CHKSUM - CHECKSUM DECNET MESSAGES  . . . . . . . . . .  68
;   64. MSGFLI - PICKUP ACK MESSAGE FROM THE LISTENER  . . . .  69
;   65. SSNDMG - SEND A MESSAGE TO A LISTENER  . . . . . . . .  70
;   66. PROTIM - DECNET INACTIVITY TIMER PROCESSOR . . . . . .  71
;   67. CASTIM - CLEAR AND RESET THE DECNET INACTIVITY TIMER .  72
;   68. CLRTIM - CLEAR THE DECNET INACTIVITY TIMER . . . . . .  73
;   69. SABLNK - ABORT THE SENDER'S DECNET LINK  . . . . . . .  74
;   70. INSCRH - ROUTINE TO INDICATE SENDER CONTROLLED CRASH .  75
;   71. LISDDT - ROUTINE TO LOAD DDT IF DEBUGGING  . . . . . .  76
;   72. GETPAG - GET A PAGE FOR OUTGOING IPCF MESSAGE  . . . .  77
;   73. COMMON STOPCODES . . . . . . . . . . . . . . . . . . .  78
	SUBTTL	Revision history

COMMENT \

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

6000	6.1065		Nov-9-87
	Create LISSPL as Cluster LPTSPL's listener

6001	6.1118		Dec-5-87
	If a listener detects that the DECnet link is no longer connected,
then in addition to aborting and re-opening the link, cause it also to
clear and re-setup the interrupt system.

6002	6.1128		Dec-7-87
	Expand on edit 6001 to include all cases where a listener detects that
the DECnet link is no longer connected. Also, clear and reset the interrupt
system out of interrupt context to prevent NIP ("no interrupt in progress)
crashes.

6003	6.1215		Mar-4-88
	In a private GALAXY, use the first six characters of the user's name
rather than LISSPL and LPTSPL as part of the SRV: and DCN: names.

6004	6.1220		Mar-6-88
	Fix bugs discovered while testing inaccessible files.

6005	6.1225		8-Mar-88
	Update copyright notice.

6006	6.1254		12-May-88
	Correct the spelling of LISSPL in the LISSPL sender DECnet connection
failure message.

\	;End of revision history
;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
INFBSZ==6				;INFO% JSYS BLOCK SIZE
SCSCN==3				;SCS% CHANNEL NUMBER
SCSLEN==1+.SQDTA+SQ%CDT			;SIZE OF SCS% EVENT BLOCK
TIMITL==3*^D60*^D30			;DECNET INACTIVIY TIME LIMIT
MAXCRH==^D10				;MAXIMUM TIMES AN INFERIOR FORK CAN 
                                        ;CRASH
;RANDOM STORAGE AREA

PDL:	BLOCK PDSIZ			;PUSH DOWN STACK
NODNAM:	BLOCK 1				;THE LOCAL NODE NAME (SIXBIT)
NODNUM: BLOCK 1				;THE LOCAL NODE NUMBER
NUMSEN:	BLOCK 1				;NUMBER OF SENDERS
ACTLIS: BLOCK 1				;NUMBER OF ACTIVE LISTENERS
RENNUM: BLOCK 1				;NUMBER OF REMOTE NODES IN THE CLUSTER
NBSCHD:	BLOCK 1				;LISSPL SCHEDULING FLAG
LISSIZ: BLOCK 1				;THE NUMBER OF PAGES IN LISSPL
SCHTIM: BLOCK 1				;TIME OF THE LAST SCHEDULING PASS
MSGTIM:	BLOCK 1				;TIME A IPCF MESSAGE WAS PICKED UP
G$SND:	BLOCK 1				;PID OF IPCF MESSAGE SENDER
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
NODDAT: BLOCK 1				;NODE NAME PASSED AS OPTIONAL DATA

INTVEC==:LEVTAB,,CHNTAB

IB:	$BUILD	IB.SZ			;
	  $SET	(IB.PRG,,%%.MOD)	;PROGRAM 'LISSPL'
	  $SET  (IB.FLG,IP.STP,1)	;STOPCODES TO ORION
	  $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.SYS,IP.BQT,-1)	;MAXIMUM SEND/RECEIVE IPCF QUOTA
	  $SET	(PB.SYS,IP.MNP,MAXNOD+NUMLIS+1)	;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,NBSCS			;SCS DETECTED A CLUSTER TOPOLOGY CHANGE
	BLOCK	^D32			;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

;RESIDENT JOB DATA BASE

SREADY:	BLOCK 1				;SENDER IS AVAILABLE FLAG
LREADY:	BLOCK 1				;LISTENER HAS MESSAGE FLAG
TRMFRK: BLOCK 1				;AN INFERIOR FORK HAS TERMINATED
SCSFLG: BLOCK 1				;A CLUSTER TOPOLOGY CHANGE DETECTED
ASZNAM:	BLOCK 1+<MAXNOD+1>+2*<MAXNOD+1>	;ASCIZ NODE NAMES FROM CNFIG% .CFCND
SWINFO:	BLOCK .CFILN			;STATIC SOFTWARE INFORMATION
INFBLK: BLOCK INFBSZ			;INFO% SOFTWARE INFORMATION BLOCK
.STZER:					;START OF INIT TO ZERO
LISTAB:	BLOCK NUMLIS*.LTSIZ		;LISTENER TABLE
SENTAB:	BLOCK MAXNOD*.STSIZ		;SENDER TABLE
NODTAB:	BLOCK MAXNOD			;NODE NAME TABLE
.SZZER==.-.STZER			;SIZE OF INIT TO ZERO
	SUBTTL	LISSPL STARTUP AND SCHEDULER

LISSPL:	RESET%				;THE USUAL
	MOVE	P,[IOWD PDSIZ,PDL]	;SET UP THE STACK.
	MOVEI	S1,IB.SZ		;GET THE INITIALIZATION BLOCK SIZE.
	MOVEI	S2,IB			;ADDRESS OF THE INITIALIZATION BLOCK
	$CALL	I%INIT			;SET UP GLXLIB
	$CALL	LISENV			;CHECK THE LISSPL ENVIRONMENT
	$CALL	LSINIT			;GO SETUP CONSTANTS AND CAPABILITIES
	$CALL	INTINI			;SET UP THE INTERRUPT SYSTEM.
	$CALL	TABINT			;INITIALIZE THE TABLES
	$CALL	SRTLIS			;START UP THE LISTENERS
MAIN:	$CALL	I%NOW			;GET THE DATE/TIME
	MOVEM	S1,SCHTIM		;SAVE AS THE TIME OF SCHEDULING PASS
	SETZM	NBSCHD			;SLEEP AFTER THIS PASS

	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

	$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 UNTIL INTERRUPTED
	$CALL	I%SLP			;DON'T WAKE UP UNTIL INTERRUPTED
	JRST	MAIN			;GO PROCESS THE INTERRUPT
	SUBTTL	LISENV - CHECK THE LISSPL ENVIRONMENT

;LISENV is called during LISSPL startup to determine if the local node
;has DECnet enabled. If DECnet is not enabled, then LISENV halts.
;
;Call is: No arguments
;Returns: Only if DECnet is enabled for this node
;Crashes: If cannot obtain the static software information or DECnet is
;         not enabled.

LISENV:	$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	LISEN1			;ON AN ERROR, CRASH

	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
	$RET				;LISSPL IS IN THE CORRECT ENVIRONMENT

LISEN1:	$STOP	(COS, CAN'T OBTAIN STATIC SOFTWARE INFORMATION)
	SUBTTL	LSINIT - GET NODE NAME, NODE NUMBER, AND SIZE OF LISSPL

;LSINIT is called during LISSPL startup. This routine determines the
;name and cluster node number of this node (the local node). 
;In addition, the size of LISSPL (in pages) is also determined,
;which is used when mapping a sender or listener.
;
;Call is: No arguments
;Returns: Node name and number, LISSPL size determined and saved

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

LSINIT:	$CALL 	I%HOST			;GET OUR HOST NAME
	MOVEM	S1,NODNAM		;SAVE THE SIXBIT NODE NAME
	MOVEM	S2,NODNUM		;SAVE THE NODE NUMBER

;PICK UP AND SAVE THE NUMBER OF PAGES LISSPL'S CODE TAKES UP

	SKIPE	DEBUGW			;DEBUGGING?
	SKIPN	116			;AND ARE SYMBOLS DEFINED?
	JRST	LSIN.1			;IF NO TO EITHER, SKIP THE FOLLOWING
	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
LSIN.1:	HLRZ	S1,.JBSA##		;GET THE PROGRAM END ADDRESS
	ADDI	S1,PAGSIZ-1		;ROUND IT OFF
	ADR2PG	S1			;MAKE IT A PAGE NUMBER
	MOVEM	S1,LISSIZ		;SAVE IT
	$RET				;RETURN TO STARTUP
	SUBTTL INTINI -	INTERRUPT INITIALIZATION AND HANDLERS

;INTINI is called during LISSPL 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  - SCS% detected a cluster topology change
;19 - (channel .IPIFT) One or more senders and/or listeners have crashed
;
;Call is: No arguments
;Returns: The interrupt system has been setup successfully
;Crashes: Cannot activate the interrupt system or cannot enable
;         SCS% event interrupts

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,17B3+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	LISSPL 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 LISSPL'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.
;
;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.  

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

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
;node has left the cluster.
;
;DEBRKs with word SCSFLG set to -1 if a node has left the cluster
;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	NBSC2			;YES, DON'T CARE ABOUT THIS EVENT
	CAIE	S1,.SEPBC		;HAS A NODE GONE OFFLINE?
	JRST	NBSC2			;NO, DON'T CARE ABOUT THIS EVENT

;INDICATE THAT A NODE HAS LEFT THE CLUSTER

NBSC3:	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. THIS IS BECAUSE IT CANNOT JUST BE
;ASSUMED THAT A TOPOLOGY CHANGE HAS OCCURRED, SINCE IF THE ROUTINE EXITS ON
;THIS ASSUMPTION AND THERE ARE MORE EVENTS IN THE EVENT QUEUE, THEN LISSPL
;WILL NOT BE INTERRUPTED AGAIN IF A CLUSTER TOPOLOGY CHANGE OCCURS.

NBSC4:	MOVEI	S1,.FHSLF		;GET LATEST ERROR OF LISSPL
	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	TABINT - INITIALIZE THE TABLES

;TABINT is called during LISSPL startup to initialize the listener, sender
;and node tables. The tables are initialized by zeroing all their entries.
;
;Call is: No arguments
;Returns: The tables have been initialized

TABINT:	MOVEI	S1,.SZZER		;SIZE OF REGION TO BE ZEROED
	MOVEI	S2,.STZER		;START OF REGION TO BE ZEROED
	$CALL	.ZCHNK			;ZERO THE TABLES
	SETZM	NUMSEN			;NO SENDERS AT THIS POINT
	$RET				;RETURN TO LISSPL STARTUP
	SUBTTL	SRTLIS - START UP THE LISTENERS 

;SRTLIS is called during LISSPL startup. SRTLIS starts up NUMLIS listeners.
;Call is: No arguments
;Returns: The listeners have been started
;Crashes: A listener cannot be started

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

	MOVEI	P1,LISTAB		;PICK UP LISTENER TABLE ADDRESS
	MOVEI	P2,NUMLIS		;PICK UP NUMBER OF LISTENERS TO START
	MOVEM	P2,ACTLIS		;SAVE AS ACTIVE NUMBER OF LISTENERS
SRTLI1:	MOVE	S1,P1			;PICK UP THIS LISTENER'S ENTRY ADDRESS
	$CALL	STLIS			;START THIS LISTENER
	ADDI	P1,.LTSIZ		;ADR OF THE NEXT LISTENER TABLE ENTRY
	SOJG	P2,SRTLI1		;START UP THE NEXT LISTENER
	$RET				;RETURN TO LISSPL STARTUP
	SUBTTL	STLIS - START UP THE LISTENER

;STLIS is called to start up a listener. 
;This routine is called at LISSPL startup and also when a listener has
;crashed.
;
;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	.LSHWD(LIS)		;THE LISTENER'S MESSAGE QUEUE IS EMPTY
;
;Call is: S1/Listener table entry address for this listener
;Returns: A listener is successfully started. 
;Crashes: If the listener cannot be started (i.e., a CFORK%, SFORK% or
;         PMAP% error has occurred).

STLIS:	$SAVE	<P1,T1,T2>			;SAVE THESE AC

;OBTAIN AND INITIALIZE THE LISTENER DATA BASE

	MOVE	P1,S1			;SAVE THE LISTENER TABLE ENTRY ADDRESS
	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,.LTADR(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,.LSLTA(LIS)		;SAVE THE LISTENER TABLE ENTRY ADDRESS
	$CALL	BLDSRV			;BUILD THE DECNET SRV: DEVICE NAME

;SETUP THE CONTEXT OF THE LISTENER

	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,.LSACS+P(LIS)	;PLACE IN THE DATABASE
	MOVEM	LIS,.LSACS+LIS(LIS)	;SAVE THE ADDRESS OF THE DB
	SETOM	.LSAVA(LIS)		;INDICATE TOP FORK READY FOR MESSAGES

;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 CAPABILITIES AND AC'S
	MOVEI	S2,.LSACS(LIS)		;AC LOAD BUFFER
	CFORK%				;CREATE A LISTENER
	 ERJMP	STLIS2			;CRASH ON AN ERROR

;MAP LISSPL'S PAGES INTO THE LISTENER

	MOVEM	S1,.LSHND(LIS)		;SAVE THE LISTENER'S HANDLE
	HRLZ	S2,S1			;GET THE LISTENER'S HANDLE
	MOVSI	S1,.FHSLF		;GET THE TOP FORK'S HANDLE
	MOVE	T1,LISSIZ		;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	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 a 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, BUILD DIFFERENTLY
	$TEXT	(<-1,,.LSSRV(LIS)>,<SRV:TASK.^N/NODNAM/$LISSPL$LS^0>)
	$TEXT	(<-1,,.LSSNE(LIS)>,<^N/NODNAM/$LPTSPL$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/NODNAM/$^T/0(S1)/$LS^0>)
	$TEXT	(<-1,,.LSSNE(LIS)>,<^N/NODNAM/$^T/0(S1)/$SN^0>) ;SENDER NAME
	$RET					;[6003]RETURN TO THE CALLER
	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 interrupts
;the listeners in order for them to check if their DECnet links are still
;connected. This is necessary since if a node that a listener has a connection
;to crashes, the listener is not interrupted. (The listener is also not
;interrupted when the node comes back up.)
;
;Call is: No arguments
;Returns: Updated node table and the listeners have been interrupted
;Crashes: If a listener cannot be interrupted

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

;FIRST READ IN THE LATEST TOPOLOGY OF THE CLUSTER

	SETZM	SCSFLG			;RESET THE SCS% INTERRUPT FLAG
	SETOM	NBSCHD			;FORCE A SCHEDULING PASS

;INTERRUPT THE LISTENERS TO INDICATE THAT THEY SHOULD CHECK THE STATUS OF
;THEIR DECNET LINKS

	MOVEI	T3,LISTAB		;PICK UP ADDRESS OF THE LISTENER TABLE
	MOVEI	T4,NUMLIS		;PICK UP THE NUMBER OF LISTENERS
TOPCH1:	MOVE	S1,.LTSTA(T3)		;PICK UP THE LISTENER'S STATUS WORD
	TXNE	S1,LT%NRS		;CAN THIS LISTENER BE RESTARTED?
	JRST	TOPCH2			;NO, CHECK THE NEXT LISTENER
	MOVE	S1,.LTADR(T3)		;PICK UP ADDRESS OF LISTENER BLOCK
	MOVE	S1,.LSHND(S1)		;PICK UP THE LISTENER'S FORK HANDLE
	MOVX	S2,<1B4>		;INDICATE LISTENER CHECK LINK STATUS
	IIC%				;INTERUPT THIS LISTENER
	 ERJMP	S..UII			;CRASH ON AN ERROR
TOPCH2:	ADDI	T3,.LTSIZ		;POINT TO THE NEXT LISTENER ENTRY
	SOJG	T4,TOPCH1		;INTERRUPT THE NEXT LISTENER
	$RET				;AND 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. If the crashed fork
;has crashed MAXCRH times, then the fork is not restarted and ORION is
;informed. If there are no more active listeners left, then LISSPL crashes.
;If the crashed fork is a sender and there are no messages for the sender,
;then the sender is not restarted. However, if the sender does have one
;or messages in its message queue, then these messages are kept for the
;restarted sender.
;
;Call is: No arguments
;Returns: Senders and listeners have been updated
;Crashes: If a sender or a listener could not be restarted

RESTAR:	$SAVE	<P1,P2,P3>		;SAVE THESE AC
	SETZM	TRMFRK			;CLEAR THE INFERIOR FORK CRASHED FLAG
	SETOM	NBSCHD			;FORCE ANOTHER SCHEDULING PASS
	MOVEI	P1,LISTAB		;PICK UP THE LISTENER TABLE ADDRESS
	MOVEI	P2,NUMLIS		;PICK UP NUMBER OF LISTENERS

;CHECK IF THE LISTENER CAN BE RESTARTED. IF IT CAN, CHECK IF IT HAS HAD
;A CONTROLLED CRASH OR AN UNCONTROLLED CRASH.

RESTA1:	MOVE	S1,.LTSTA(P1)		;PICK UP THE STATUS WORD
	TXNE	S1,LT%NRS		;CAN THIS LISTENER BE RESTARTED?
	JRST	RESTA4			;NO, GO CHECK THE NEXT LISTENER
	TXNE	S1,LT%LFC		;DID LISTENER HAVE CONTROLLED CRASH?
	JRST	RESTA2			;YES, CHECK IF CRASHED MAX TIMES
	MOVE	LIS,.LTADR(P1)		;PICK UP LISTENER BLOCK ADDRESS
	MOVE	S1,.LSHND(LIS)		;PICK UP THE LISTENER'S HANDLE	
	$CALL	INFSTS			;CHECK IF IT HAS CRASHED
	JUMPT	RESTA4			;THIS LISTENER DID NOT CRASH

;THE LISTENER HAS CRASHED. CHECK IF IT HAS CRASHED THE MAXIMUM TIMES. IF SO,
;THEN DON'T RESTART.

RESTA2:	AOS	S1,.LTCRT(P1)		;INCREMENT TIMES IT HAS CRASHED
	CAIG	S1,MAXCRH		;HAS IT CRASHED THE MAX TIMES?
	JRST	RESTA3			;NO, GO RESTART IT
	MOVX	S1,LT%NRS		;PICK UP DON'T RESTART FLAG
	IORM	S1,.LTSTA(P1)		;INDICATE IN THE STATUS BLOCK
	MOVE	S1,.LTADR(P1)		;PICK UP ADDRESS OF LISTENER BLOCK
	SETOM	.LSAVA(S1)		;INDICATE DON'T ATTEMPT MSG PICK UP
	SOSE	S1,ACTLIS		;DECREMENT THE NUMBER ACTIVE LISTENERS
	JRST	RESTA4			;GO CHECK THE NEXT LISTENER
	$STOP	(NAL,NO ACTIVE LISTENERS)
RESTA3:	MOVE	S1,P1			;PICK UP THE LISTENER TABLE ENTRY ADR
	$CALL	RESLIS			;RESTART THE LISTENER
RESTA4:	ADDI	P1,.LTSIZ		;POINT TO THE NEXT LISTENER TABLE ENTRY
	SOJG	P2,RESTA1		;GO CHECK THE NEXT LISTENER

;CHECK FOR ANY SENDERS THAT MAY HAVE CRASHED. CHECK IF A CRASHED SENDER HAS HAD
;A CONTROLLED CRASH OR AN UNCONTROLLED CRASH.

	MOVE	P3,NUMSEN		;PICK UP THE NUMBER OF SENDERS
	JUMPE	P3,RESTA9		;QUIT IF THERE ARE NO SENDERS
	MOVEI	P1,SENTAB		;PICK UP THE SENDER TABLE ADDRESS
	MOVEI	P2,MAXNOD		;PICK UP THE MAX NUMBER OF ENTRIES
RESTA5:	SKIPN	.STNOD(P1)		;IS THIS ENTRY IN USE?
	JRST	RESTA8			;NO, GO CHECK THE NEXT ENTRY
	MOVE	S1,.STSTS(P1)		;PICK UP THE STATUS WORD
	TXNE	S1,ST%SFC		;DID SENDER HAVE CONTROLLED CRASH?
	JRST	RESTA6			;YES, KILL THIS SENDER
	MOVE	SEN,.STADR(P1)		;PICK UP SENDER BLOCK ADDRESS
	MOVE	S1,.SNHND(SEN)		;PICK UP THE SENDER'S HANDLE	
	$CALL	INFSTS			;CHECK IF IT HAS CRASHED
	JUMPT	RESTA8			;THIS SENDER DID NOT CRASH

;THE SENDER HAS CRASHED. CHECK IF IT HAS CRASHED THE MAXIMUM TIMES. IF SO,
;THEN DON'T RESTART.

RESTA6:	MOVE	S1,P1			;PICK UP THE SENDER TABLE ENTRY ADDRESS
	$CALL	DELSEN			;YES, KILL AND DELETE THIS SENDER
RESTA8:	ADDI	P1,.STSIZ		;POINT TO THE NEXT SENDER TABLE ENTRY
	SOJG	P2,RESTA5		;GO CHECK THE NEXT SENDER
RESTA9:	$RET				;RETURN TO THE CALLER
	SUBTTL	CHKQUE - IPCF MESSAGE PROCESSING

;CHKQUE is called by the scheduler to pick up the IPCF message or messages
;that have arrived from QUASAR. Only messages sent directly from QUASAR are
;accepted. Any messages with an unknown message code are ignored.
;
;Call is: No arguments
;Returns: All the messages have processed from the IPCF message queue

CHKQUE:	$CALL	C%RECV			;CHECK IF THERE IS A MESSAGE
	$RETIF				;RETURN IF THERE ARE NO MESSAGES
	$SAVE	<P1,P2>			;SAVE THESE AC
	MOVE	P1,S1			;SAVE ADDRESS OF THE MDB
	$CALL	I%NOW			;PICK UP TIME OF IPCG MSG RECEPTION
	MOVEM	S1,MSGTIM		;SAVE IT
	JRST	CHKQU3			;JOIN COMMON CODE
CHKQU2:	$CALL	C%RECV			;CHECK IF THERE IS A MESSAGE
	$RETIF				;RETURN,,NOTHING THERE.
	MOVE	P1,S1			;SAVE THE ADDRESS OF THE MDB


;MAKE SURE THE MESSAGE IS FROM QUASAR

CHKQU3:	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.QSR		;IS THE MESSAGE FROM QUASAR?
	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,-LMSGT		;MAKE AOBJN POINTER FOR MSG TYPES

;CHECK IF THE MESSAGE TYPE IS KNOWN TO LISSPL

CHKQU4:	HRRZ	P2,MSGTAB(S1)		;GET A MESSAGE TYPE
	CAMN	S2,P2			;DO THE MESSAGE TYPES MATCH?
	JRST	CHKQU5			;YES, GO PROCESS THE MESSAGE
	AOBJN	S1,CHKQU4		;NO, CHECK THE NEXT MESSAGE TYPE
	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
	HLRZ	P2,MSGTAB(S1)		;PICK UP THE PROCESSING ROUTINE ADR
	$CALL	@P2	 		;DISPATCH TO THE MESSAGE PROCESSOR.

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

CHKQU6:	$CALL	C%REL			;RELEASE THE MESSAGE
	JRST	CHKQU2			;CHECK FOR ANY MORE MESSAGES

MSGTAB:	XWD	RELEAS,.QOREL		;RELEASE MESSAGE
	LMSGT==.-MSGTAB
	SUBTTL	RELEAS - RELEASE MESSAGE PROCESSING

;RELEAS processes the RELEASE message sent by QUASAR. RELEAS validates that
;the message has the correct syntax. It then determines if a sender exists
;for the node the message is to be forwarded to. If a sender does exist,
;then the message is either queued in the sender's message queue or placed
;in the sender's message buffer.
;If a sender does not exist, then a sender is started and the message is placed
;in the sender's message queue.
;
;Call is: M/Address of the IPCF message
;Returns: The message has been sent or queued
;Crashes: The message has an invalid syntax or the sender could not be started

RELEAS:	$SAVE	<P1>			;SAVE THIS AC

;VALIDATE THE SYNTAX OF THE MESSAGE

	$CALL	CHKLEN			;CHECK FOR A VALID LENGTH
	JUMPF	S..IFM			;ILLEGALLY FORMATTED MESSAGE

;DETERMINE WHICH NODE TO SEND THE MESSAGE TO AND CHECK IF A SENDER FOR THAT
;NODE ALREADY EXISTS

	MOVE	S1,REL.NN(M)		;PICK UP THE NODE NAME
	MOVE	P1,S1			;SAVE NODE NAME FOR LATER
	$CALL	SRHSEN			;CHECK FOR IT IN THE SENDER TABLE
	JUMPF	RELEA1			;NO SUCH NODE, START A SENDER FOR IT
	$CALL	QUEMSG			;SEND OR QUEUE THE MESSAGE
	$RET				;RETURN TO THE IPCF MESSAGE DISPATCHER

;THERE IS NO SENDER FOR THE NODE THE MESSAGE IS TO BE SENT TO. START A SENDER
;FOR THIS NODE AND PLACE THE MESSAGE IN ITS MESSAGE QUEUE.

RELEA1:	AOS	S1,NUMSEN		;INCREMENT THE NUMBER OF SENDERS
	CAILE	S1,MAXNOD		;MORE THAN THE SUPPORTED NUMBER?
	JRST	S..CTL			;YES, CRASH IN THIS CASE
	MOVEM	P1,.STNOD(S2)		;PLACE NODE NAME IN SENDER TABLE ENTRY
	MOVE	S1,S2			;PICK UP THE SENDER TABLE ENTRY ADDRESS
	MOVE	P1,S2			;SAVE SENDER TABLE ENTRY ADR FOR LATER
	SETZ	S2,			;NO MESSAGE QUEUE AT THIS POINT
	$CALL	STSEN			;START UP THE SENDER
	MOVE	S2,P1			;PICK UP THE SENDER TABLE ENTRY
	$CALL	QUEMSG			;SEND OR QUEUE THE MESSAGE
RELEA2:	$RET				;RETURN TO THE IPCF MESSAGE DISPATCHER
					
	SUBTTL	SRHSEN - SEARCH FOR A NODE'S SENDER TABLE ENTRY

;SRHSEN is called to find a node's sender table entry. If the node
;does not have an entry in the sender table, then the address of a 
;free entry in the sender table is returned.
;
;Call is:       S1/SIXBIT node name
;Returns true:  S2/The node's sender table entry address
;Returns false: S2/A free sender table entry address

SRHSEN:

;CHECK IF ANY ENTRIES ARE IN USE. IF NOT, THEN RETURN THE FIRST ONE AS
;FREE
	MOVEI	S2,SENTAB		;PICK UP SENDER TABLE ADDRESS
	SKIPG	NUMSEN			;ANY ENTRIES IN USE?
	$RETF				;NO, RETURN THE FIRST ENTRY AS FREE

	$SAVE	<P1,P2>			;SAVE THESE AC
	MOVEI	P1,MAXNOD		;PICK UP NUMBER OF ENTRIES IN TABLE
SRHSE1:	SKIPN	.STNOD(S2)		;IS THIS ENTRY IN USE?
	MOVE	P2,S2			;NO, REMEMBER AS A FREE ENTRY
	CAMN	S1,.STNOD(S2)		;IS THIS THE TABLE ENTRY?
	$RETT				;YES, INDICATE SUCCESS
	ADDI	S2,.STSIZ		;NO, POINT TO THE NEXT ENTRY
	SOJG	P1,SRHSE1		;CHECK THE NEXT ENTRY, IF ANY LEFT
	MOVE	S2,P2			;RETURN AN ADDRESS OF A FREE ENTRY
	$RETF				;INDICATE ENTRY NOT FOUND
	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
;               S1/The indicated length of the message

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	QUEMSG - SEND OR QUEUE A MESSAGE

;QUEMSG is called to place a processed IPCF message on the message queue
;of the sender of the node that the message is to be sent to. If the sender
;is available to send a message, then the first message on the sender's
;message queue is placed in the sender's message buffer and the sender is
;interrupted to inform it that a message is available to send.
;However, if the sender that is to send the message has an empty message
;queue and if it is available to send a message, then the message is not
;placed on that sender's message queue but rather is placed in its message
;buffer. The sender is then interrupted to inform it that there is a 
;message available for it to send.
;
;Call is: S2/SENTAB entry address of the sender
;         M/ Address of the processed IPCF message
;
;Returns: The message has been queued or sent

QUEMSG:

;CHECK IF THE SENDER IS AVAILABLE TO SEND A MESSAGE. IF IT IS NOT, THEN
;PLACE THE MESSAGE ON THE SENDER'S MESSAGE QUEUE. IF THE SENDER IS
;AVAILABLE TO SEND A MESSAGE, THEN CHECK IF ITS MESSAGE QUEUE IS EMPTY.
;IF ITS MESSAGE QUEUE IS EMPTY, THEN PLACE THE MESSAGE IN THE SENDER'S
;MESSAGE BUFFER AND INTERRUPT THE SENDER.

	MOVE	SEN,.STADR(S2)		;PICK UP THE SENDER BLOCK ADDRESS
	SKIPE	.SNHWD(SEN)		;IS THE MESSAGE QUEUE EMPTY?
	JRST	QUEMS1			;NO, PLACE MSG ON THE MESSAGE QUEUE
	SKIPL	.SNFRE(SEN)		;YES, IS SENDER AVAILABLE TO SEND?
	JRST	QUEMS1			;NO, PLACE MSG ON THE MESSAGE QUEUE

;SENDER IS AVAILABLE AND ITS MESSAGE QUEUE IS EMPTY.

	MOVE	S1,M			;PICK UP THE MESSAGE ADDRESS
	MOVE	S2,.SNMSG(SEN)		;PICK UP SENDER'S MESSAGE BUFFER ADR
	$CALL	XFRMSG			;TRANSFER MESSAGE TO MESSAGE BUFFER
	$CALL	SENMS5			;INFORM THE SENDER OF THE MESSAGE
	JRST	QUEMS2			;GO RETURN

;THE MESSAGE QUEUE IS NOT EMPTY OR THE SENDER IS NOT AVAILABLE TO SEND
;A MESSAGE. BUILD A MESSAGE QUEUE ENTRY AND ADD IT TO THE MESSAGE QUEUE.

QUEMS1:	$CALL	BLDMQE			;BUILD THE MESSAGE QUEUE ENTRY
	$CALL	ADDMQE			;LINK IN THE MESSAGE QUEUE ENTRY

;CHECK IF THE SENDER IS AVAILABLE TO SEND A MESSAGE. IF IT IS, THEN TRANSFER
;THE FIRST MESSAGE OF THE MESSAGE QUEUE TO THE SENDER'S MESSAGE BUFFER
;AND INFORM THE SENDER THAT IT HAS A MESSAGE AVAILABLE TO SEND

	SKIPGE	.SNFRE(SEN)		;IS THE SENDER AVAILABLE TO SEND
	$CALL	SENMSG			;YES, MOVE A MSG AND TELL THE SENDER
QUEMS2:	$RET				;RETURN TO THE IPCF DISPATCHER
	SUBTTL	SNDMSG - SEND MESSAGES TO AVAILABLE SENDERS

;SNDMSG is called during LISSPL'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.
;If a sender is available, 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 SENDER TABLE SEARCH FOR ELIGIBLE SENDERS TO SEND A MESSAGE

	MOVEI	P1,SENTAB		;PICK UP THE SENDER TABLE ADDRESS
	MOVEI	P2,MAXNOD		;PICK UP MAX # OF SENDER 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:	SKIPN	.STNOD(P1)		;DOES THIS ENTRY CORRESPOND TO A SENDER?
	JRST	SNDMS3			;NO, CHECK THE NEXT SENDER

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

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

;CHECK FOR ANY MORE SENDERS THAT ARE AVAILABLE TO SEND A MESSAGE

SNDMS3:	ADDI	P1,.STSIZ		;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:  Message is placed in the sender's message buffer and the sender
;          is interrupted
;Crashes:  If the sender cannot be interrupted

SENMSG:	$SAVE	<P1>			;SAVE THIS AC

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

	MOVE	P1,.SNHWD(SEN)		;PICK UP MESSAGE QUEUE HEADER WORD
	MOVE	S1,.MQMAD(P1)		;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

	MOVE	S1,.MQBLK(P1)		;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				;RETURN THE MESSAGE QE MEMORY
SENMS2:	MOVEM	S1,.SNHWD(SEN)		;PLACE NEW FIRST ENTRY IN L.L. HDR WORD

;RETURN THE MESSAGE QUEUE ENTRY MEMORY TO THE MEMORY MANAGER

SENMS3:	MOVE	S1,P1			;PICK UP MESSAGE QUEUE ENTRY ADDRESS
	$CALL	RELMQE			;AND RETURN THE MEQ TO THE MEMORY MGER

;INTERRUPT THE SENDER TO INDICATE THAT A MESSAGE IS AVAILABLE IN ITS MESSAGE
;BUFFER.
;
;SENMS5 IS AN ENTRY POINT WHEN PROCESSING AN IPCF MESSAGE THAT CAN BE SENT
;DIRECTLY TO THE SENDER WITHOUT THE NEED TO PLACE THE MESSAGE ON THE SENDER'S
;MESSAGE QUEUE. (I.E., AN IPCF MESSAGE HAS BEEN RECEIVED, THE SENDER'S
;MESSAGE QUEUE IS EMPTY, AND THE SENDER IS AVAILABLE TO SEND A MESSAGE.)

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

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

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

	MOVEI	P1,LISTAB		;PICK UP THE LISTENER TABLE ADDRESS
	MOVEI	P2,NUMLIS		;PICK UP # OF LISTENER TABLE ENTRIES

;CHECK IF THE LISTENER HAS A MESSAGE TO BE PICKED UP.

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

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

	$CALL	PROMSG			;PICK UP AND PROCESS THE MESSAGE

;INTERRUPT THE LISTENER TO INDICATE THAT ITS MESSAGE HAS BEEN PICKED UP,
;PROCESSED AND THAT THE TOP FORK IS AVAILABE TO PROCESS ANOTHER MESSAGE.

	MOVE	S1,.LSHND(LIS)		;GET THE LISTENER'S FORK
	MOVX	S2,<1B2>		;WANT CHANNEL 2
	SETOM	.LSAVA(LIS)		;MESSAGE HAS BEEN PICKED UP
	IIC%				;INDICATE TO LISTENER TOP FORK AVAILABLE
	 ERJMP	S..UII			;CRASH IF CAN'T INTERRUPT LISTENER

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

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

;PROMSG is called from GETMSG to process a message that 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. SET UP TO 
;SEARCH THE MESSAGE DISPATCH TABLE.

	MOVE	S1,.LSNME(LIS)		;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			;DO THE MESSAGE TYPES MATCH?
	JRST	PROMS3			;YES, GO PROCESS THE MESSAGE
	AOBJN	S1,PROMS2		;NO, CHECK THE NEXT MESSAGE TYPE
	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:	$LOG(<LISSPL received unknown message>,<Listener to node ^N/G$SND/ has received an unknown message type>)

	$RET				;RETURN TO THE CALLER

LMGTAB:	XWD	NEXTJB,.QONEX		;NEXTJOB MESSAGE

	NLMGT==.-LMGTAB
	SUBTTL	NEXTJB - SEND A NEXTJOB MESSAGE FROM A REMOTE NODE TO QUASAR

;NEXTJB sends NEXTJOB messages from a remote node to QUASAR. NEXTJB
;transfers the message from the listener's message buffer to an IPCF
;page and sends the message to QUASAR.
;
;Call is: M/Address of the NEXTJOB message to be sent to QUASAR
;Returns: The NEXTJOB message was sent successfully
;Crashes: The NEXTJOB message could not be sent to QUASAR

NEXTJB:

;SEND THE MESSAGE TO QUASAR

	MOVE	S1,PIB+PB.PID		;PICK UP LISSPL'S PID
	MOVEM	S1,.EQPID(M)		;PLACE IN THE MESSAGE
	MOVE	S1,M			;ADDRESS OF THE IPCF MESSAGE
	MOVEI	S2,PAGSIZ		;LENGTH OF THE IPCF MESSAGE
	$CALL	SNDQSR			;SEND THE MESSAGE TO ORION
	$RETIT				;THE MESSAGE WAS SENT
	$STOP(QSF, Send to QUASAR failed)
	SUBTTL	RESLIS - RESTART A LISTENER THAT HAS CRASHED

;RESLIS is called to restart a listener that has crashed.
;
;Call is: S1/LISTENER table entry address of the listener
;Returns: The listener has been restarted
;Crashes: The listener could not be restarted

RESLIS:	$SAVE	<P1>			;SAVE THIS AC

;KILL THE LISTENER

	MOVE	P1,S1			;SAVE THE LISTENER TABLE ENTRY ADR
	MOVE	LIS,.LTADR(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

;RETURN THE LISTENER BLOCK PAGES

	ADR2PG	LIS			;CHANGE PAGE ADDRESS TO PAGE NUMBER
	MOVE	S2,LIS			;PLACE PAGE NUMBER WHERE EXPECTED
	MOVEI	S1,DBSIZ		;NUMBER OF PAGES TO RELEASE
	$CALL	M%RLNP			;RELEASE THE PAGES

;RESET THE LISTENER TABLE ENTRY AND START THE LISTENER

	MOVEI	S1,.LTSIZ		;PICK UP THE ENTRY SIZE
	MOVE	S2,P1			;PICK UP THE LISTENER TABLE ENTRY
	$CALL	.ZCHNK			;INITIALIZE THE ENTRY
	MOVE	S1,P1			;PICK UP THE LISTENER TABLE ENTRY
	$CALL	STLIS			;START THE LISTENER
	$RET				;RETURN TO THE CALLER
	SUBTTL	DELSEN - KILL AND DELETE A SENDER THAT HAS CRASHED

;DELSEN is called to kill and delete a sender that has crashed the
;maximum amount of times. The sender is not restarted.
;
;Call is: S1/SENDER table entry address of the sender
;Returns: The sender has been killed and deleted
;Crashes: The sender could not be killed and deleted

DELSEN:	$SAVE	<P1>			;SAVE THIS AC
	$CALL	KILSEN			;KILL THE SENDER
	SOS	NUMSEN			;DECREMENT THE NUMBER OF SENDERS
	MOVE	P1,S2			;PICK UP THE MQ LINK LIST HEADER WORD
DELSE1:	SKIPN	S1,P1			;PICK UP THE NEXT MESSAGE QE ADDRESS
	$RET				;RETURN, IF THERE ARE NO MORE
	MOVE	P1,.MQBLK(S1)		;PICK UP THE NEXT MESSAGE QE ADDRESS
	$CALL	RELMQE			;RETURN THE MEMORY OF THE MEQ
	JRST	DELSE1			;GO RETURN THE NEXT MESSAGE QE
	SUBTTL	KILSEN - KILL A SENDER THAT HAS CRASHED

;KILSEN is called to kill a sender that has crashed.
;
;Call is: S1/SENDER table entry address of the sender
;Returns: The sender has been killed
;         S2/MQ link list header word
;Crashes: The sender could not be restarted

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

;SAVE THE MESSAGE QUEUE HEADER WORD

	MOVE	P1,S1			;SAVE THE SENDER TABLE ENTRY ADDRESS
	MOVE	SEN,.STADR(P1)		;PICK UP THE SENDER BLOCK ADDRESS
	MOVE	P2,.SNHWD(SEN)		;SAVE THE MESSAGE QUEUE HEADER WORD

;KILL THE SENDER

	MOVE	S1,.LSHND(SEN)		;PICK UP THE SENDER'S HANDLE
	KFORK%				;KILL THE SENDER
	 ERJMP	.+1			;HANDLE NO LONGER VALID, IGNORE

;RETURN THE SENDER BLOCK PAGES

	ADR2PG	SEN			;CHANGE PAGE ADDRESS TO PAGE NUMBER
	MOVE	S2,SEN			;PLACE PAGE NUMBER WHERE EXPECTED
	MOVEI	S1,DBSIZ		;NUMBER OF PAGES TO RELEASE
	$CALL	M%RLNP			;RELEASE THE PAGES

;INITIALIZE THE SENDER TABLE ENTRY

	MOVEI	S1,.STSIZ		;PICK UP THE ENTRY SIZE
	MOVE	S2,P1			;PICK UP THE SENDER TABLE ENTRY
	$CALL	.ZCHNK			;INITIALIZE THE ENTRY

	MOVE	S2,P2			;RETURN WITH MQ LINK LIST HEADER WORD
	$RET				;RETURN TO THE CALLER
	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	INFST2			;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	STSEN - START UP A SENDER

;STSEN  is called to start up a sender. This routine is called when a
;message is received from QUASAR to be forwarded to a node in the cluster
;for which no sender has been started for. This routine is also called when
;a sender has crashed and is being restarted.
;
;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: S1/Address of the sender table entry for this sender
;Returns: The sender has been sucessfully started
;Crashes: The sender cannot be started (i.e., a CFORK%, SFORK% or
;         PMAP% error has occurred)

;FIRST OBTAIN AND INITIALIZE THE SENDER DATA BASE

STSEN:	$SAVE	<T1,T2,T3> 		;SAVE THESE AC
	MOVE	T3,S1			;SAVE THE SENDER TABLE ENTRY ADDRESS
	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
	MOVE	SEN,S1			;PLACE ADR IN SENDER BLOCK DB POINTER
	MOVEM	S1,.STADR(T3)		;SAVE ADR IN THE SENDER TABLE ENTRY
	ADDI	S1,PAGSIZ		;POINT TO THE MESSAGE BUFFER ADDRESS
	MOVEM	S1,.SNMSG(SEN)		;SAVE THE MESSAGE BUFFER ADDRESS
	MOVEM	T3,.SNSTA(SEN)		;SAVE THE SENDER TABLE ENTRY ADDRESS
	MOVE	S1,.STNOD(T3)		;PICK UP THE REMOTE NODE NAME
	MOVEM	S1,.SNNME(SEN)		;PLACE IN THE SENDER BLOCK
	$CALL	BLDDCN			;BUILD THE DECNET DCN: DEVICE NAME

;SET UP THE CONTEXT OF THE SENDER

	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 LISSPL'S PAGES INTO THE SENDER

	MOVEM	S1,.SNHND(SEN)		;SAVE THE SENDER'S HANDLE
	MOVSI	S1,.FHSLF		;GET LISSPL'S HANDLE
	HRLZ	S2,.SNHND(SEN)		;GET THE SENDER'S HANDLE
	HRR	T1,LISSIZ		;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	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-RNODE$LPTSPL$LS.RNODE$LISSPL$SN;BDATA:NNNNN;
;USERID:RNODE$LISSPL$SN
;
;where RNODE is the remote 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	<P1,P2,P3,P4>		;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	P1,[POINT 7,S1,35]	;POINTER TO BYTE TO PICK UP
	MOVE	P2,[POINT 7,NODDAT]	;POINTER TO WHERE BYTE IS TO BE PUT
	SETZ	S1,			;CLEAR RECEIVING WORD OF OCTAL VALUE
	MOVEI	P3,^D36/3		;NUMBER OF OCTAL CHARACTERS

BLDDC2:	LSHC	S1,3			;MOVE NEXT OCTAL VALUE OVER
	ADDI	S1,60			;MAKE IT ASCII
	LDB	P4,P1			;PICK UP ASCII VALUE
	IDPB	P4,P2			;PLACE IN ASCII STRING
	SETZ	S1,			;PREPARE FOR NEXT OCTAL VALUE
	SOJN	P3,BLDDC2		;PICK UP NEXT OCTAL VALUE
	IDPB	S1,P2			;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/>)

	$RET				;RETURN TO THE CALLER

BLDDC3: [ITEXT(<^N/.SNNME(SEN)/-TASK-^N/.SNNME(SEN)/$LPTSPL$LS.>)] ;LISTENER NAME
BLDDC4:	[ITEXT(<^N/.SNNME(SEN)/$LISSPL$SN;BDATA:^T/NODDAT/;>)] ;THE SENDER NAME
BLDDC5: [ITEXT(<USERID:^N/.SNNME(SEN)/$LISSPL$SN>)]	      ;USERID

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/.SNNME(SEN)/$^T/0(S1)/$LS.>)]  ;[6003]LISTENER NAME
BLDD10:	[ITEXT(<^N/.SNNME(SEN)/$^T/0(S1)/$SN;BDATA:^T/NODDAT/>)]       ;[6003]SENDER NAME
BLDD11:	[ITEXT(<;USERID:^N/.SNNME(SEN)/$^T/0(S1)/$SN>)]		  ;[6003]
	SUBTTL SNDQSR - SEND AN IPCF MESSAGE TO QUASAR

;SNDQSR sends an IPCF message to QUASAR
;
;Call is:       S1/The message address
;	        S2/The message length
;Returns true:  The message was sent successfully
;Returns false: The message was not successfully sent

SNDQSR:	MOVEM	S1,SAB+SAB.MS		;SAVE THE MESSAGE ADDRESS
	MOVEM	S2,SAB+SAB.LN		;SAVE THE MESSAGE LENGTH
	MOVX	S1,SP.QSR		;THEN GET QUASAR'S FLAG
	TXO	S1,SI.FLG		;SET SPECIAL INDEX FLAG
	STORE	S1,SAB+SAB.SI		;AND STORE IT
	SETZM	SAB+SAB.PD		;CLEAR THE PID WORD
	MOVEI	S1,SAB.SZ		;LOAD THE SIZE
	TXO	S1,PT.KEE		;KEEP THE PAGE AFTER THE SEND
	MOVEI	S2,SAB			;AND THE ADDRESS
	$CALL	C%SEND			;SEND THE MESSAGE
	$RET				;PRESERVE THE TRUE/FALSE INDICATOR
	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/ Address 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:	SKIPG	S2,.SNTWD(SEN)		;ANY MESSAGES IN ITS MESSAGE QUEUE?
	JRST	ADDMQ2			;NO, ADD AS THE FIRST MSG QUEUE ENTRY
	MOVEM	S1,.MQBLK(S2)		;PLACE NEW ADR OF LAST QE IN L.L. WORD
	SKIPA				;GO UPDATE THE TRAILER WORD
ADDMQ2:	MOVEM	S1,.SNHWD(SEN)		;UPDATE THE LINK LIST HEADER WORD
	MOVEM	S1,.SNTWD(SEN)		;UPDATE THE LINK LIST TRAILER WORD
	$RET				;AND RETURN TO THE CALLER
	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 header
;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 MQE

;DETERMINE IF THE MESSAGE LOCATION IS CONTIGUOUS OR NOT 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	S1,.MQMAD(P1)		;DOES THE MESSAGE OCCUPY A PAGE?
	JRST	RELMQ2			;NO, RETURN MQE AND MSG TOGETHER
	HRRZS	S1			;YES, ISOLATE THE MESSAGE 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.ADR	;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 word that this message queue
;      entry will be placed on the end of the message queue (i.e., the link
;      list word .MQBLK is zeroed).
;
;Call is:  M/Address of the IPCF message
;Returns:  The message queue entry is built
;          S1/ Address of the message queue entry

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

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

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

;PREPARE THE MESSAGE QUEUE ENTRY HEADER CONTIGUOUS WITH THE MESSAGE

	$CALL	M%GMEM			;PICK UP THE MEMORY BLOCK
	MOVE	P1,S2			;PICK UP THE MQE ADDRESS
	ADDI	P1,.MQISZ		;ADD MEQ LENGTH TO GET MESSAGE ADDRESS
	JRST	BLDMQ3			;JOIN COMMON CODE

;PREPARE THE MQE SEPARATE FROM THE MESSAGE

BLDMQ2:	$CALL	M%GPAG			;GET A PAGE OF MEMORY
	MOVE	P1,S1			;PICK UP MESSAGE ADDRESS
	TXO	P1,MQ%PAG		;INDICATE MESSAGE IS A PAGE
	MOVEI	S1,.MQISZ		;PICK UP SIZE OF THE MQE HEADER
	$CALL	M%GMEM			;PICK UP MEMORY FOR THE MQE

;BUILD THE MESSAGE QUEUE HEADER

BLDMQ3:	MOVEM	P1,.MQMAD(S2)		;PLACE MSG ADDRESS IN MSG QUEUE HEADER
	STORE	S1,.MQMEM(S2),MQ.LEN	;SAVE GLXMEM LENGTH IN MSG QUEUE HEADER
	STORE	S2,.MQMEM(S2),MQ.ADR 	;SAVE GLXMEM ADDRESS IN MSG QUEUE HDR

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

	MOVE	P2,S2			;REMEMBER MQE ADDRESS FOR RETURN
	MOVE	S1,M			;ADDRESS OF THE IPCF MESSAGE
	HRRZ	S2,P1			;ADDRESS OF THE MSG IN MQE
	$CALL	XFRMSG			;MOVE THE MESSAGE
	MOVE	S1,P2			;RETURN THE MQE ADDRESS
	$RET				;RETURN TO THE CALLER
	SUBTTL	LISTEN - MESSAGE SERVER FOR A REMOTE NODE

;LISTEN exists as an inferior fork in LISSPL. NUMLIS listeners are started 
;by LISSPL as part of its startup. A listener picks up NEXTJOB messages
;from a remote Cluster LPTSPL.
;A listener communicates with the top fork through software interrupts,
;the listener block and the listener table.

;INITIALIZATION BLOCK AND PID BLOCK

LIB:	$BUILD	IB.SZ
	  $SET	(IB.PRG,,%%.MOD)	;PROGRAM 'LISSPL'
	  $SET  (IB.FLG,IP.STP,1)	;STOPCODES TO ORION
	  $SET	(IB.PIB,,0)		;SET UP PIB ADDRESS
	$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
	XWD	1,INTMSG		;INTERRUPT MESSAGE FROM THE SENDER
	XWD	1,CHKLST		;CHECK THE LINK STATUS
	BLOCK	^D31			;THESE CHANNELS ARE NOT USED

;SET UP GLXLIB, ENABLE CAPABILITIES, AND SET UP INTERRUPT SYSTEM

LISTEN:	SKIPE	DEBUGW			;DEBUGGING?
	$CALL	LISDDT			;YES, SET UP FOR DEBUGGING

	$CALL	LISSET			;SET UP GLXLIB, CAPS AND MESSAGE PAGES
	$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
LISTE3:	MOVE	P,.LSACS+P(LIS)		;PICK UP THE NEW STACK POINTER
	$CALL	LOPLNK			;[6001]RE-OPEN THE LINK
	$CALL	LISINT			;[6001]RE-INITIALIZE INTERRUPT SYSTEM
	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, disables the listener from receiving
;any IPCF messages and allocates pages for receiving the NEXTJOB and TRANSFER
;FILE RESPONSE messages from Cluster LPTSPL as well as a page for the modified
;NEXTJOB message.
;
;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 PIB BLOCK ADDRESS
	MOVEM	S1,.LSIBK+IB.PIB(LIS)	;PLACE IN THE IB 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 TABLE ENTRY
	AOS	S2			;POINT TO NEXT PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE
	AOS	S1			;POINT TO NEXT INTERRUPT TABLE ENTRY
	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


;PICK UP PAGES FOR THE NEXTJOB, MODIFIED NEXTJOB AND TRANSFER FILE RESPONSE
;MESSAGES.

	MOVEI	S1,3			;NUMBER OF PAGES NEEDED
	$CALL	M%AQNP			;PICK UP THE PAGES
	PG2ADR	S1			;CONVERT PAGE NUMBER TO PAGE ADDRESS
	MOVEM	S1,.LSNXJ(LIS)		;THE NEXTJOB MESSAGE ADDRESS
	ADDI	S1,PAGSIZ		;ADDRESS OF THE NEXT PAGE
	MOVEM	S1,.LSMOD(LIS)		;THE MODIFIED NEXTJOB MESSAGE ADDRESS
	ADDI	S1,PAGSIZ		;ADDRESS OF THE NEXT PAGE
	MOVEM	S1,.LSXRM(LIS)		;THE TRANSFER FILE RESPONSE MESSAGE

	$RET				;RETURN TO STARTUP
	SUBTTL	LOPLNK - OPEN A DECNET SRV: DEVICE

;LOPLNK is called during the listener's initialization to open a SRV:
;device.
;
;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>			;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	LOPLN3			;CRASH IF CAN'T GET JFN
	HRRZS	S1			;ISOLATE THE JFN
	MOVEM S1,.LSJFN(LIS)		;SAVE THE JFN FOR LATER
	MOVX S2,<FLD(^D36,OF%BSZ)+OF%WR+OF%RD> ;OPEN FOR READ AND WRITE
	OPENF%
	 ERJMP	LOPLN3			;CRASH IF CAN'T OPEN THE DEVICE
	$RET				;RETURN TO THE CALLER

LOPLN3:	$CALL	INLCRH			;INDICATE A CONTROLLED CRASH
	$STOP	(LOD, LISTENER CAN'T OPEN DECNET DEVICE)
	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+1B3+1B4	;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
	MOVX	S2,.MOACN		;PICK UP THE ENABLE INTERRUPTS CODE
	MOVX	T1,<FLD(0,MO%CDN)+FLD(1,MO%DAV)+FLD(3,MO%INA)> ;ENABLE CHANNELS
	MTOPR%				;ENABLE THE DECNET INTERRUPT CHANNELS
	 ERJMP	LISIN2			;CRASH IF CAN'T ENABLE THESE CHANNELS
	$RET				;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
;two checks are made:
;1. The sender's name must be in the form expected by the listener.
;   The expected form is:
;   LNODE$LPTSPL$SN
;   where LNODE is the local node name
;2. 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 these two 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
;bit LT%HNL (HELLO message received from a non-LPTSPL) is set in the listener's
;status word.
;If the sender passes the two checks, then its connection request is 
;accepted.
;
;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,
	
;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			;ABORT AND RE-OPEN LINK ON AN ERROR
	
	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			;ABORT AND RE-OPEN LINK ON AN ERROR

	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 temp repeat 0

;THE SENDER HAS PASSED THE VALIDITY CHECKS. PICK UP THE NODE NAME AND
;PLACE IN THE LISTENER BLOCK.  ACCEPT THE DECNET CONNECTION REQUEST.

	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			;ABORT AND RE-OPEN LINK ON AN ERROR
	
	HRROI	S1,.LSANN(LIS)		;PICK UP THE SENDER'S NODE NAME
	$CALL	S%SIXB			;CHANGE IT TO SIXBIT
	MOVEM	S2,.LSNME(LIS)		;SAVE THE SIXBIT NODE NAME

	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			;ABORT AND RE-OPEN LINK ON AN ERROR

	JRST	ACCEP5			;GO RETURN TO THE PREVIOUS CONTEXT

;THE SENDER'S DECNET CONNECTION REQUEST HAS BEEN DENIED. REJECT THE 
;CONNECTION, INDICATE THAT THE CONNECTION HAS BEEN REJECTED IN THE LISTENER'S
;STATUS WORD IN THE LISTENER TABLE ENTRY AND INFORM ORION OF THE REJECTION.

ACCEP3:	MOVE	S1,.LSLTA(LIS)		;PICK UP LISTENER TABLE ENTRY ADDRESS
	MOVX	S2,LT%HNL		;INDICATE CONNECTION FAILURE
	IORM	S2,.LTSTA(S1)		;PLACE IN THE STATUS WORD

	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			;CRASH ON AN ERROR
	$LOG	(<DECnet connection rejected>,<DECnet connection from node ^N/.LSNME(LIS)/ rejected>)
	JRST	ACCEP5			;GO RETURN TO PREVIOUS CONTEXT

;A FATAL ERROR HAS BEEN ENCOUNTERED. ABORT AND RE-OPEN THE LINK

ACCEP4:	$CALL	LABLNK			;ABORT THE LINK
	MOVEI	S1,.LSPDL(LIS)		;[6002]SET UP THE LISTENER CONTEXT
	HRLI	S1,-<PDSIZ-1>		;[6002]STACK POINTER
	MOVEM	S1,.LSACS+P(LIS)	;[6002]SAVE AS THE NEW STACK POINTER
	MOVEI	S1,LISTE3		;[6002]WHERE TO RESUME EXECUTION FROM	
	TXO	S1,1B5			;[6002]
	MOVEM	S1,.LS1PC(LIS)		;[6002]STORE AS THE NEW PC

ACCEP5:	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT
	SUBTTL	MSGFSN - DECNET MESSAGE FROM SENDER IS AVAILABLE

;MSGFSN is the interrupt handler for processing the NEXTJOB message from
;Cluster LPTSPL's sender. After processing of the NEXTJOB message has 
;been completed, MSGFSN checks if the top fork is busy or not. If the
;top fork is not busy and the listener message queue is empty, then the
;NEXTJOB 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.
;
;Call is: LIS/Address of the listener block
;Returns: The message has been processed
;Crashes: The link status cannot be obtained, the message cannot be picked
;up, or the top fork cannot be notified

MSGFSN:	$BGINT	1,			;SAVE PREVIOUS CONTEXT

;DETERMINE IF THERE IS A MESSAGE OR NOT

	SKIPG	S1,.LSJFN(LIS)		;PICK UP THE DECNET DCN: DEVICE JFN
	JRST	MSGF18			;TREAT AS SPURIOUS
	SIBE%				;DETERMINE IS THERE IS A MESSAGE
	JRST	MSGFS1			;THERE IS A MESSAGE, PICK IT UP
	$CALL	LCKLNK			;CHECK THE LINK STATUS
	JUMPT	MSGF18			;CONNECTED, TREAT AS SPURIOUS
	JRST	MSGF17			;RE-ATTEMPT TO OPEN THE LINK

;PICK UP THE MESSAGE

MSGFS1:	MOVE	S2,.LSNXJ(LIS)		;ASSUME IT'S A NEXTJOB MESSAGE
	SKIPGE	P1,.LSSTE(LIS)		;IS IT?
	MOVE	S2,.LSXRM(LIS)		;NO, PICK UP XFER FILE RESPONSE MSG ADR
	HRLI	S2,(POINT 36)		;MAKE INTO A POINTER
	MOVE	S1,.LSJFN(LIS)		;PICK UP THE DECNET DCN: DEVICE JFN
	MOVNI	T1,PAGSIZ		;ASSUME THE MAXIMUM PAGE SIZE
	SINR%				;PICK UP THE MESSAGE
	 ERJMP	MSGF16			;CHECK FOR FATAL ERROR OR NOT
	TXNE	P1,LS%XMR		;WAITING FOR A NEXTJOB MESSAGE?
	JRST	MSGFS7			;NO, TRANSFER FILE RESPONSE MESSAGE

;A NEXTJOB MESSAGE HAS BEEN RECEIVED. DETERMINE IF CHECKSUMMING IS TO BE
;DONE. IF SO, THEN DO IT.

REPEAT 0,<
	MOVE	S1,.LSNXJ(LIS)		;PICK UP THE MESSAGE'S ADDRESS
	MOVE	S2,.MSCHS(S1)		;PICK UP THE MESSAGE'S CHECKSUM
	MOVEM	S2,.LSRCS(LIS)		;SAVED, DESTROYED BY LISCHK
	$CALL	LISCHK			;DO THE CHECKSUM OR NOT, AS INDICATED
	JUMPF	MSGF14			;CHECKSUM NOT AGREE, SEND FAIL ACK
>
;SET UP FOR THE PROCESSING OF THE NEXTJOB MESSAGE

MSGFS2:	MOVE	P1,.LSNXJ(LIS)		;PICK UP THE NEXTJOB MESSAGE ADDRESS
	LOAD	P2,.EQSEQ(P1),EQ.IAS	;PICK UP INVALID ACCOUNT STRING BIT
	JUMPN	P2,MSGF4A		;IF INVALID DON'T CHECK IF ACCESSIBLE
	LOAD	P2,.EQLEN(P1),EQ.LOH	;PICK UP THE EQ HEADER
	ADD	P2,P1			;POINT TO THE FIRST FP
	MOVEI	P3,.LSFXM(LIS)		;PICK UP TRANSFER FILE MSG ADDRESS
	MOVEI	P4,.OHDRS+.TFHSZ(P3)	;POINT TO THE FIRST RELATIVE FP BLOCK
	MOVEI	S1,<.OHDRS+.TFHSZ+^D70> ;PICK UP ITS LENGTH
	MOVE	S2,P3			;PICK UP TRANSFER FILE MESSAGE ADDRESS
	$CALL	.ZCHNK			;INITIALIZE THE TRANSFER FILE MSG
	LOAD	T1,.EQSPC(P1),EQ.NUM	;PICK UP NUMBER OF FILES IN EQ
	SETZ	T2,			;RELATIVE FP BLOCK NUMBER

;CHECK EACH FILE IN THE NEXTJOB MESSAGE AS TO WHETHER IT IS ACCESSIBLE
;TO THIS SYSTEM OR NOT. IF ONE OR MORE FILES ARE NOT ACCESSIBLE TO THIS
;SYSTEM, THEN A TRANSFER FILE MESSAGE WILL BE SENT TO THE SENDER. THE
;TRANSFER FILE MESSAGE INDICATES THE RELATIVE FP POSITION OF EACH FILE
;IN THE NEXTJOB MESSAGE THAT THE SENDER MUST COPY INTO A DIRECTORY SHARED
;BETWEEN THE LOCAL AND REMOTE SYSTEMS.

MSGFS3:	AOS	T2			;THE CURRENT RELATIVE FP BLOCK 
	MOVE	S1,P2			;PICK UP THE CURENT FP BLOCK ADDRESS
	$CALL	CHKACC			;CHECK IF THIS FILE IS ACCESSIBLE
	JUMPT	MSGFS4			;THE FILE IS ACCESSIBLE
	AOS	.OHDRS+.TFNUM(P3)	;INCREMENT NUMBER OF INACCESSIBLE FILES
	MOVEM	T2,.TFFPP(P4)		;PLACE RELATIVE FP BLOCK NUMBER IN MSG
	ADDI	P4,.TFFPS		;POINT TO NEXT RELATIVE FP BLOCK

MSGFS4:	LOAD	S1,.FPLEN(P2),FP.LEN	;PICK UP THE FP BLOCK LENGTH
	ADD	P2,S1			;POINT TO ITS FD BLOCK
	LOAD	S1,.FDLEN(P2),FD.LEN	;PICK UP THE FD BLOCK LENGTH
	ADD	P2,S1			;POINT TO THE NEXT FP BLOCK
	SOJG	T1,MSGFS3		;CHECK THE NEXT FILE IN THE NEXTJOB MSG

;IF ALL FILES ARE ACCESSIBLE, THEN SEND AN ACK TO CLUSTER LPTSPL.
;IF ONE OR MORE FILES ARE NOT ACCESSIBLE, THEN SEND THE TRANSFER FILE
;MESSAGE TO CLUSTER LPTSPL.

	SKIPE	T1,.OHDRS+.TFNUM(P3)	;ANY FILES INACCESSIBLE?
	JRST	MSGF4B			;YES, SEND A TRANSFER FILE MESSAGE
MSGF4A:	$CALL	SNDACK			;NO, SEND AN ACK
	JUMPF	MSGF16			;CHECK FOR A FATAL ERROR
	MOVE	S1,.LSNXJ(LIS)		;PICK UP NEXTJOB MESSAGE ADDRESS
	JRST	MSGF13			;ATTEMPT TO NOTIFY THE TOP FORK
MSGF4B:	IMULI	T1,.TFFPS		;CALCULATE THE TOTAL SIZE OF FP BLOCKS
	ADDI	T1,.OHDRS+.TFHSZ	;INCLUDE THE HEADER AND # FILES BLOCK
	STORE	T1,.MSTYP(P3),MS.CNT	;PLACE IN THE TRANSFER FILE MESSAGE

REPEAT 0,<
;CHECKSUM IF NECESSARY

	SKIPN	CHECKS			;CHECKSUMMING ENABLED LOCALLY?
	JRST	MSGFS5			;NO, GO SEND THE MESSAGE
	SKIPN	.LSRCS(LIS)		;CHECKSUMMING ENABLED REMOTELY?
	JRST	MSGFS5			;NO, GO SEND THE MESSAGE
	$CALL	CHKSUM			;CHECKSUM THE MESSAGE
>
;SEND THE MESSAGE TO CLUSTER LPTSPL

MSGFS5:	MOVX	S1,LS%XMR		;TRANSFER FILE RESPONSE MESSAGE STATE
	MOVEM	S1,.LSSTE(LIS)		;SAVE THE LISTENER STATE
MSGFS6:	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVEI	S2,.LSFXM(LIS)		;[6004]PICK UP TRANSFER FILE MSG ADDRESS
	HRLI	S2,(POINT 36)		;MAKE INTO A POINTER
	MOVNS	T1			;MAKE IT NEGATIVE
	SOUTR%				;SEND THE MESSAGE
	 ERJMP	MSGF16			;ABORT THE LINK AND RE-OPEN IT
                                        ;IF NOT A FATAL ERROR (LREOPN)
	JRST	MSGF18			;RETURN TO THE PREVIOUS STATE
	
;THE MESSAGE IS A TRANSFER FILE RESPONSE MESSAGE

MSGFS7:
REPEAT 0,<
	MOVE	P2,.LSXRM(LIS)		;PICK UP THE ADDRESS OF THE MESSAGE
	MOVE	S1,P2			;PLACE WHERE LISCHK EXPECTS IT
	$CALL	LISCHK			;DO THE CHECKSUM OR NOT, AS INDICATED
	JUMPF	MSGF14			;CHECKSUMS DO NOT AGREE, SEND FAIL ACK
>
	MOVE	P1,.LSNXJ(LIS)		;PICK UP THE NEXTJOB MESSAGE ADDRESS
	MOVE	P2,.LSXRM(LIS)		;[6004]PICK UP THE XFER FILE RSP MSG ADR
	MOVE	P3,.LSMOD(LIS)		;PICK UP THE MODIFIED NEXTJOB MSG ADR

;MOVE THE EQ FROM THE NEXTJOB MESSAGE TO THE MODIFIED NEXTJOB MESSAGE

	MOVE	S1,P3			;WHERE TO PLACE THE EQ
	HRL	S1,P1			;SOURCE,,DESTINATION
	LOAD	P4,.EQLEN(P1),EQ.LOH	;PICK UP SIZE OF THE EQ
	MOVE	S2,P4			;PLACE SIZE HERE ALSO
	ADD	S2,P3			;DESTINATION ADDRESS + 1
	BLT	S1,-1(S2)		;TRANSFER THE EQ

;POINT TO THE FIRST FP OF THE NEXTJOB AND MODIFIED NEXTJOB MESSAGES

	ADD	P1,P4			;FIRST FP OF THE NEXTJOB MESSAGE
	ADD	P3,P4			;FIRST FP OF THE MOD NEXTJOB MSG
	MOVE	T3,.OHDRS+.TSNUM(P2)	;[6004]NUMBER OF RELATIVE FP BLOCKS
	MOVEI	S1,<.OHDRS+.TSHSZ-.TSSIZ>(P2) ;[6004]POINT BEFORE FIRST REL FP
	MOVE	S2,T3			;[6004]PICK UP THE # OF RELATIVE FP BLKS
	IMULI	S2,.TSSIZ		;[6004]FIND THEIR SIZE
	ADD	S1,S2			;[6004]ADDRESS OF LAST FP BLOCK
	MOVE	S1,.TFFPP(S1)		;[6004]PICK UP THE LAST RELATIVE FP #
	MOVEM	S1,.LSLRB(LIS)		;[6004]SAVE FOR LATER

	ADDI	P2,.OHDRS+.TSHSZ	;[6004]POINT TO FIRST RELATIVE FP BLOCK
	MOVEI	T2,1			;CURRENT FP BLOCK
	SETZ	T1,			;NUMBER OF FILES IN MOD NEXTJOB MSG

;POSITION TO THE FIRST FP BLOCK OF THE NEXTJOB MESSAGE AS INDICATED BY
;THE RELATIVE FP BLOCK IN THE TRANSFER FILE RESPONSE MESSAGE

MSGFS8:	MOVE	S1,.TSFPP(P2)		;PICK UP THE FP BLOCK POSITION
	SUB	S1,T2			;NUMBER OF FP BLOCKS TO GO THROUGH
	JUMPE	S1,MSGF10		;ALREADY POSITIONED

;TRANSFER THE UNWANTED FP/FD BLOCKS TO THE MODIFIED NEXTJOB MESSAGE.
;MAKE SURE THERE IS ENOUGH ROOM IN THE MODIFIED NEXTJOB MESSAGE.

MSGFS9:	MOVE	T2,S1			;[6004]PICK UP THE FP BLOCK POSITION
	$CALL	CHKRM			;FIRST ENSURE THERE IS ENOUGH ROOM
	SKIPT				;SKIP IF THERE IS ENOUGH ROOM
	$CALL	SENNXJ			;FINISH MSG AND SEND OR QUEUE 

;MOVE THE FP/FD PAIR TO THE MODIFIED NEXTJOB MESSAGE

	MOVE	S2,P3			;CURRENT END OF MODIFIED NEXTJOB MSG
	HRL	S2,P1			;SOURCE,,DESTINATION
	MOVE	S1,T4			;[6004]PICK UP FP/FD SIZE
	ADD	S1,P3			;[6004]END OF DESTINATION + 1
	BLT	S2,-1(S1)		;[6004]COPY THE FP/FD OVER

	ADD	P1,T4			;POINT TO THE NEXT FP/FD PAIR
	ADD	P3,T4			;POINT TO THE NEXT FP/FD PAIR
	AOS	T1			;INCREMENT THE NUMBER OF FILES
	SOJG	T2,MSGFS9		;[6004]TRANSFER THE NEXT FP/FD PAIR

;POINTING AT THE FP OF THE FILE THAT IS INACCESSIBLE TO THIS SYSTEM.
;FIRST MAKE SURE THERE IS ROOM IN THE MODIFIED NEXTJOB MESSAGE TO
;CONTAIN THE FILE'S FP/FD PAIR.

MSGF10:	$CALL	CHKRM			;FIRST ENSURE THERE IS ENOUGH ROOM
	SKIPT				;SKIP IF THERE IS ENOUGH ROOM
	$CALL	SENNXJ			;FINISH MSG AND SEND OR QUEUE 

;CHECK IF THE ORIGINATING NODE NO LONGER HAS ACCESS TO THE FILE. IF
;IT DOES NOT, THEN NEITHER DOES THIS NODE.

	SKIPN	T2,.TSTFN(P2)		;IS THE NODE ACCESSIBLE?
	JRST	MSGF11			;NO, INDICATE IN MOD NXTJOB MSG

;MODIFY THE FP OF THIS FILE TO INDICATE THAT THERE IS A TEMPORARY FILE
;ASSOCIATED WITH THIS FILE.

	MOVE	S1,P3			;PICK UP DESTINATION ADDRESS
	HRL	S1,P1			;SOURCE,,DESTINATION
	LOAD	S2,.FPLEN(P1),FP.LEN	;PICK UP THE FP LENGTH
	PUSH	P,S2			;SAVE THIS LENGTH FOR LATER
	ADD	S2,P3			;END OF DESTINATION + 1
	BLT	S1,-1(S2)		;COPY THE FP OVER
	POP	P,S2			;RESTORE LENGTH OF THE FP
	AOS	S2			;INCREMENT FP SIZE FOR .FPTEM
	STORE	S2,.FPLEN(P3),FP.LEN	;STORE NEW FP SIZE IN THE FP
	AOS	P4			;INCLUDE IN TOTAL MOD NEXTJOB MSG SIZE
	MOVX	S1,FP.CPY		;PICK UP TEMPORARY FILE BIT
	IORM	S1,.FPINF(P3)		;INDICATE TEMPORARY FILE IN THE FP
	MOVEM	T2,.FPTEM(P3)		;PLACE TEMP FILE NAME IN FP

;MOVE THE FD OVER TO THE MODIFIED NEXTJOB MESSAGE

	ADD	P3,S2			;POINT TO THE MOD NEXTJOB MSG FD
	ADDI	P1,-1(S2)		;POINT TO THE NEXTJOB MESSAGE FD
	LOAD	S2,.FDLEN(P1),FD.LEN	;PICK UP THE NEXTJOB FD LENGTH
	MOVE	T4,S2			;SAVE FOR LATER
	MOVE	S1,P3			;PICK UP DESTINATION ADDRESS
	HRL	S1,P1			;SOURCE,,DESTINATION
	ADD	S2,P3			;END OF DESTINATION + 1
	BLT	S1,-1(S2)		;COPY THE FD OVER
	ADD	P1,T4			;POINT TO THE NEXT FP/FD PAIR
	ADD	P3,T4			;POINT TO THE NEXT FP/FD PAIR
	JRST	MSGF12			;CHECK THE NEXT RELATIVE FP

;THE FILE IS NO LONGER ACCESSIBLE ON THE ORIGINATING SYSTEM
;COPY THE FP/FD PAIR OVER TO THE MODIFIED NEXTJOB MESSAGE AND INDICATE
;IN THE FP THAT THE USER DOES NOT HAVE ACCESS TO THIS FILE.

MSGF11:	MOVE	S2,P3			;CURRENT END OF MODIFIED NEXTJOB MSG
	HRL	S2,P1			;SOURCE,,DESTINATION
	MOVE	T2,T4			;SAVE THE FP/FD LENGTH
	ADD	T2,P3			;END OF DESTINATION + 1
	BLT	S2,-1(T2)		;COPY THE FP/FD OVER
	
	MOVX	S1,FP.NRA		;USER DOES NOT HAVE ACCESS TO FILE
	IORM	S1,.FPINF(P3)		;INDICATE IN THE FILE'S FP
	MOVX	S1,FP.DEL		;USER CAN'T DELETE THIS FILE
	ANDCAM	S1,.FPINF(P3)		;INDICATE IN THE FILE'S FP
	ADD	P1,T4			;POINT TO THE NEXT FP/FD PAIR
	ADD	P3,T4			;POINT TO THE NEXT FP/FD PAIR

;PREPARE TO PROCESS THE NEXT RELATIVE FP BLOCK FROM THE TRANSFER FILE
;RESPONSE MESSAGE

MSGF12:	AOS	T1			;NUMBER OF FILES IN MOD NEXTJOB MSG
	MOVE	T2,.TSFPP(P2)		;[6004]FP BLOCK NUMBER JUST PROCESSED
	AOS	T2			;CURRENT FP BLOCK IN NEXTJOB MSG
	ADDI	P2,.TSSIZ		;POINT TO NEXT RELATIVE FP BLOCK
	SOJG	T3,MSGFS8		;GO PROCESS THE NEXT RELATIVE FP BLOCK

;FINISH BUILDING THE MESSAGE

	MOVE	S2,.LSLRB(LIS)		;[6004]PICK UP THE NUMBER OF TEMP FILES
	MOVE	S1,.LSNXJ(LIS)		;[6004]PICK UP NEXTJOB MSG ADDRESS
	LOAD	S1,.EQSPC(S1),EQ.NUM	;[6004]PICK UP THE TOTAL NUMBER OF FILES
	SUB	S1,S2			;[6004]SUB THE LAST TEMP FILE POSITION
	SKIPE	S1			;[6004]ANY REMAINING FP/FD TO TRANSFER?
	$CALL	TRNREM			;[6004]YES, TRANSFER THE REMAINING FP/FD
	MOVE	S1,.LSMOD(LIS)		;PICK UP MODIFIED NEXTJOB MESSAGE ADR
	STORE	P4,.MSTYP(S1),MS.CNT	;STORE THE MESSAGE LENGTH
	STORE	T1,.EQSPC(S1),EQ.NUM	;STORE NUMBER OF FILE NAMES IN MESSAGE
	$CALL	SNDACK			;SEND A SUCCESS ACK TO THE SENDER
	JUMPF	MSGF16			;CHECK THE LINK STATUS ON AN ERROR
	MOVE	S1,.LSMOD(LIS)		;PICK UP MODIFIED NEXTJOB MESSAGE ADR
MSGF13:	$CALL	NOTFRK			;NOTIFY THE TOP FORK OF ANY MESSAGES
	SETZM	.LSSTE(LIS)		;[6004]RESET THE LISTENER'S STATE
	JRST	MSGF18			;GO RETURN TO THE PREVIOUS CONTEXT

;CHECKSUMS DO NOT AGREE. TELL THE SENDER TO RESEND THE MESSAGE

REPEAT 0,<
MSGF14:	$CALL	ACKFAI			;SEND A FAIL ACK MESSAGE TO THE SENDER
	JUMPF	MSGF16			;CHECK IF A FATAL ERROR OCCURRED
	JRST	MSGF18			;GO RETURN TO THE PREVIOUS CONTEXT
>

;A SOUTR OR SINR FAILED. CHECK IF THE LINK IS STILL CONNECTED. IF IT IS,
;THEN CONSIDER THE ERROR TO BE FATAL. IF THE LINK IS NO LONGER CONNECTED,
;THEN RESET THE STATE AND ATTEMPT TO RE-OPEN THE LINK.

MSGF16:	$CALL	LCKLNK			;CHECK THE STATUS OF THE LINK
	JUMPT	MSGF19			;LINK IS STILL OPEN, FATAL ERROR

MSGF17:	SETZM	.LSSTE(LIS)		;RESET THE LISTENER'S STATE
	MOVEI	S1,.LSPDL(LIS)		;[6001]SET UP THE LISTENER CONTEXT
	HRLI	S1,-<PDSIZ-1>		;[6001]STACK POINTER
	MOVEM	S1,.LSACS+P(LIS)	;[6001]SAVE AS THE NEW STACK POINTER
	MOVEI	S1,LISTE3		;[6001]WHERE TO RESUME EXECUTION FROM	
	TXO	S1,1B5			;[6001]
	MOVEM	S1,.LS1PC(LIS)		;[6001]STORE AS THE NEW PC

MSGF18:	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT

;A FATAL ERROR HAS OCCURRED, CRASH

MSGF19:	$CALL	INLCRH			;INDICATE A CONTROLLED CRASH
	JRST	S..IFE
	SUBTTL	CHKRM - CHECK IF ENOUGH ROOM TO ADD AN FP/FD PAIR

;CHKRM is called to determine if there is enough room to add another FP/FD
;pair to a modified NEXTJOB message.
;
;Call is:       P1/Address of an FP in the NEXTJOB message 
;               P4/Current length of the modified NEXTJOB message
;Returns true:  There is enough space remaining in the modified NEXTJOB message
;               to add the FP/FD pair
;               T4/Length of the FP/FD pair
;               P4/Length of the message with the FP/FD pair included
;Returns false: There is not enough space remaining in the modified NEXTJOB
;               message to add the FP/FD pair

CHKRM:	LOAD	S2,.FPLEN(P1),FP.LEN	;PICK UP THE FP BLOCK LENGTH
	MOVE	T4,S2			;REMEMBER THE LENGTH
	ADD	S2,P1			;POINT TO THE FD BLOCK
	LOAD	S2,.FDLEN(S2),FD.LEN	;PICK UP THE FD BLOCK LENGTH
	ADD	T4,S2			;ADD TO THE FP LENGTH
	MOVE	S2,T4			;SAVE THE FP/FD LENGTH
	ADD	S2,P4			;ADD IN THE CURRENT MOD NEX MSG LENGTH
	CAIL	S2,PAGSIZ		;[6004]IS THERE ENOUGH ROOM LEFT?
	$RETF				;NO, INDICATE TO THE CALLER
	ADD	P4,T4			;ADD FP/FD LENGTH TO TOTAL MSG LENGTH
	$RETT				;INDICATE TO THE CALLER
	SUBTTL	SENNXJ - SEND OR QUEUE AN INTERMEDIATE NEXTJOB MESSAGE

;SENNXJ is called if, when building a modified NEXTJOB message, it is
;detected that there is not enough room left to add the next FP/FD
;pair. SENNXJ finishes the header of the message, either passes the
;message to the top fork or queues it on the listener's message 
;queue and then sets up the next modified NEXTJOB message to complete
;the processing of the NEXTJOB message.
;
;Call is: LIS/Address of the listener block
;         T1/The number of files in the modified NEXTJOB message
;         P4/The length of the message
;Returns: P4/Length of the next modified NEXTJOB message
;         P3/Address of the next modified NEXTJOB message's first FP
;         T1/Number of files in the next modified NEXTJOB message (which
;            is zero at this point)
;         The modified NEXTJOB message has been completed and either
;         passed to the top fork or queued on the listener's message queue

SENNXJ:	MOVE	S1,.LSMOD(LIS)		;PICK UP ADR OF MODIFIED NEXTJOB MSG
	STORE	P4,.MSTYP(S1),MS.CNT	;STORE THE LENGTH OF THE MESSAGE
	STORE	T1,.EQSPC(S1),EQ.NUM	;STORE THE NUMBER OF FILES IN THE MSG
	$CALL	NOTFRK			;QUEUE OR PASS THE MESSAGE TO TOP FORK

;SET UP THE NEXT MODIFIED NEXTJOB MESSAGE - ITS EQ, LENGTH AND NUMBER OF FILES

	MOVE	P3,.LSMOD(LIS)		;PICK UP ADR OF MODIFIED NEXTJOB MSG
	MOVE	S1,P3			;MAKE IT THE DESTINATION OF A BLT
	MOVE	S2,.LSNXJ(LIS)		;[6004]PICK UP NEXTJOB MESSAGE ADDRESS
	HRL	S1,S2			;[6004]SOURCE,,DESTINATION ADDRESSES
	LOAD	S2,.EQLEN(S2),EQ.LOH	;PICK UP THE EQ LENGTH
	MOVE	P4,S2			;SAVE FOR THE RETURN
	ADD	S2,P3			;END OF DESTINATION + 1
	BLT	S1,-1(S2)		;COPY OVER THE EQ TO MOD NEXTJOB MSG
	ADD	P3,P4			;POINT TO THE FIRST FP
	ADD	P4,T4			;[6004]ADD IN THE FP/FD LENGTH
	SETZ	T1,			;NO FILES IN MOD NEXTJOB MESSAGE YET
	$RET				;RETURN TO THE CALLER
	SUBTTL	CHKACC - CHECK IF SYSTEM HAS ACCESS TO FILE

;CHKACC is called to determine if the local system has access to a
;file specified in a NEXTJOB message that came from a remote system.
;This is done by attempting to open the file with READ access.
;
;Call is:       S1/Address of the FP block of the file
;Returns true:  The local system has access to the file
;Returns false: The local system does not have access to the file

CHKACC:	PUSH	P,S1			;SAVE THE FP ADDRESS
	MOVEI	S1,FOB.SZ		;PICK UP SIZE OF FOB
	MOVEI	S2,.LSFOB(LIS)		;PICK UP ADDRESS OF FOB
	$CALL	.ZCHNK			;ZERO THE FOB
	POP	P,S1			;RESTORE THE FP ADDRESS
	LOAD	S2,.FPLEN(S1),FP.LEN	;GET THE FP LENGTH
	ADD	S2,S1			;GET THE FD ADDRESS
	STORE	S2,.LSFOB+FOB.FD(LIS)	;SAVE IN THE FOB
	MOVEI	S2,^D36			;OPEN AS 36 BIT BYTS
	STORE	S2,.LSFOB+FOB.CW(LIS),FB.BSZ  ;AND SAVE THE BYTE SIZE
	MOVEI	S1,FOB.SZ		;GET FOB SIZE
	MOVEI	S2,.LSFOB(LIS)		;AND ADDRESS
	$CALL	F%IOPN			;OPEN THE FILE
	$RETIF				;NO ACCESS TO THIS FILE
	$CALL	F%REL			;CLOSE THE FILE
	$RETT				;AND RETURN
	SUBTTL	NOTFRK - NOTIFY TOP FORK OF MESSAGE FROM A LISTENER

;NOTFRK is called after a listener has finished processing a message
;from a sender. NOTFRK checks if the top fork is ready for a message.
;If it is, then a message is placed in the listener's message buffer
;and the top fork is interrupted. Otherwise, the message is placed on
;the listener's message queue.
;
;Call is: LIS/Address of the listener block
;          S1/Address of the message
;Returns: The top fork has been informed that there is a message available
;         or the message has been queued on the listener's message queue

NOTFRK:	$SAVE	<P1>			;SAVE THIS AC
	SKIPL	.LSAVA(LIS)		;IS THE TOP FORK AVAILABLE?
	JRST	NOTFR2			;NO, PLACE THE MSG ON THE MESSAGE QUEUE
	SKIPN	P1,.LSHWD(LIS)		;IS THE MESSAGE QUEUE EMPTY?
	JRST	NOTFR1			;YES, PLACE IN MESSAGE BLOCK AND SEND
	MOVE	M,S1			;PLACE MESSAGE ADDRESS WHERE EXPECTED
	$CALL	BLDMQE			;BUILD A MESSAGE QUEUE ENTRY
	$CALL	ADDLME			;LINK IN THE MESSAGE QUEUE ENTRY
	MOVE	S1,.MQMAD(P1)		;PICK UP FIRST MESSAGE QUEUE ENTRY ADR
	$CALL	XFRTOP			;PLACE MSG IN BUFFER AND INFORM TOP FORK
	MOVE	S1,.MQBLK(P1)		;PICK UP ADDRESS OF NEXT MESSAGE QE
	MOVEM	S1,.LSHWD(LIS)		;UPDATE THE MESSAGE QUEUE HEADER WORD
	MOVE	S1,P1			;PICK UP ADDRESS OF MESSAGE QUEUE ENTRY
	$CALL	RELMQE			;RETURN ITS MEMORY
	$RET				;RETURN TO THE CALLER
NOTFR1:	$CALL	XFRTOP			;PLACE MSG IN BUFFER AND INFORM TOP FORK
	$RET				;RETURN TO THE CALLER

NOTFR2:	MOVE	M,S1			;[6004]PLACE MSG ADDRESS WHERE EXPECTED
	$CALL	BLDMQE			;BUILD THE MESSAGE QUEUE ENTRY
	$CALL	ADDLME			;PLACE ON THE MESSAGE QUEUE
	$RET				;RETURN TO THE CALLER
	SUBTTL	TRNREM - TRANSFER THE REST OF THE NEXTJOB MESSAGE

;[6004]TRNREM is called as part of building a modified NEXTJOB message when 
;[6004]it is detected that there are remaining FD/FP pairs in the original 
;[6004]NEXTJOB message following the last temporary file FD/FP. TRNREM
;[6004]transfers the remaining FD/FP blocks from the NEXTJOB message to the
;[6004] modified NEXTJOB message.
;[6004]
;[6004]Call is: S1/Number of FP/FD blocks in the NEXTJOB message that need 
;[6004]            to be transferred
;[6004]	        T1/Number of files in the modified NEXTJOB message
;[6004]         P1/Address of the first FP/FD block in the NEXTJOB message
;[6004]            to be transferred
;[6004]         P3/Address of the next FP/FD block in the modified NEXTJOB
;[6004]            message
;[6004]	  P4/The length of the modified NEXTJOB message
;[6004]Returns: The remaining FP/FD blocks in the NEXTJOB message have been 
;[6004]         transferred to the modified NEXTJOB message

TRNREM:	ADD	T1,S1			;[6004]NUMBER OF FILES IN MOD NXTJOB MSG
	MOVE	T2,S1			;[6004]NUMBER OF FP/FD PAIRS TO TRANSFER

;[6004]PICK UP THE CURRENT FP/FD LENGTH AND UPDATE THE TOTAL MESSAGE LENGTH

TRNR.1:	LOAD	S2,.FPLEN(P1),FP.LEN	;[6004]PICK UP THE FP BLOCK LENGTH
	MOVE	T4,S2			;[6004]REMEMBER THE LENGTH
	ADD	S2,P1			;[6004]POINT TO THE FD BLOCK
	LOAD	S2,.FDLEN(S2),FD.LEN	;[6004]PICK UP THE FD BLOCK LENGTH
	ADD	T4,S2			;[6004]GET THE FP/FD LENGTH
	ADD	P4,T4			;[6004]ADD PF/FD LENGTH TO TOTAL MSG LENGTH

;[6004]MOVE THE FP/FD PAIRS FROM THE NEXTJOB MESSAGE TO THE MODIFIED NEXTJOB MESSAGE

	MOVE	S2,P3			;[6004]CURRENT END OF MODIFIED NEXTJOB MSG
	HRL	S2,P1			;[6004]SOURCE,,DESTINATION
	MOVE	S1,T4			;[6004]PICK UP FP/FD SIZE
	ADD	S1,P3			;[6004]END OF DESTINATION + 1
	BLT	S2,-1(S1)		;[6004]COPY THE FP/FD OVER

	ADD	P1,T4			;[6004]POINT TO THE NEXT FP/FD PAIR
	ADD	P3,T4			;[6004]POINT TO THE NEXT FP/FD PAIR
	SOJG	T2,TRNR.1		;[6004]TRANSFER THE NEXT FP/FD PAIR
	$RET				;[6004]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,			;SAVE THE CONTEXT
	SKIPL	.LSAVA(LIS)		;IS THE TOP FORK BUSY?
	$DEBRK				;YES, SO QUIT NOW
	SKIPN	P1,.LSHWD(LIS)		;NO, IS MESSAGE QUEUE EMPTY?
	$DEBRK				;YES, SO QUIT NOW
	MOVE	S1,.MQMAD(P1)		;PICK UP THE MESSAGE ADDRESS
	$CALL	XFRTOP			;GIVE A MESSAGE TO THE TOP FORK
	MOVE	S1,.MQBLK(P1)		;PICK UP THE NEXT MESSAGE QE ADDRESS
	JUMPG	S1,MSGTT1		;IS THE MESSAGE QUEUE NOW EMPTY?
	SETZM	.LSHWD(LIS)		;YES, ZERO OUT THE HEADER WORD
	SETZM	.LSTWD(LIS)		;AND THE TRAILER WORD
	SKIPA				;SKIP UPDATING THE HEADER WORD
MSGTT1:	MOVEM	S1,.LSHWD(LIS)		;UPDATE THE MESSAGE QUEUE HEADER WORD
	MOVE	S1,P1			;PICK UP MESSAGE QUEUE ENTRY ADDRESS
	$CALL	RELMQE			;RELEASE THE MESSAGE QUEUE ENTRY
	$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
;or message page 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
;         S1/Address of the message
;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	S2,.LSMSG(LIS)		;PICK UP ADDRESS OF THE MESSAGE BUFFER
	$CALL	XFRMSG			;MOVE THE MESSAGE TO MESSAGE BUFFER

;INTERRUPT THE TOP FORK TO INDICATE 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

	$RET				;RETURN TO THE CALLER
	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	INTMSG - PROCESS AN INTERRUPT MESSAGE FROM THE SENDER

;INTMSG is the DECnet interrupt message handler. The LPTSPL sender
;sends an interrupt message when a print request has been aborted, cancelled
;or requeued. The receipt of an interrupt message indicates to the listener
;to quit processing the current print request.
;
;Call is: Invoked by the interrupt system
;DEBRKs:  The listener's state has been reset

INTMSG:	$BGINT	1,			;SAVE THE PREVIOUS CONTEXT
	SETZM	.LSSTE(LIS)		;RESET THE STATE
	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVX	S2,.MORIM		;PICK UP FUNCTION CODE
	MOVEI	T1,P1			;ADDRESS OF THE INTERRUPT MESSAGE
	HRLI	T1,(POINT 8)		;MAKE IT INTO A POINTER
	MTOPR%				;PICK UP THE INTERRUPT MESSAGE
	 ERJMP	INTMS2

	MOVE	S1,.LSJFN(LIS)		;PICK UP THE SRV: DEVICE JFN
	MOVX	S2,.MOSIM		;PICK UP FUNCTION CODE
	MOVEI	T1,P1			;ADDRESS OF THE INTERRUPT MESSAGE
	HRLI	T1,(POINT 8)		;MAKE IT INTO A POINTER
	MOVEI	T2,1			;PICK UP INTERRUPT MESSAGE LENGTH
	MTOPR%				;SEND RESPONSE TO INTERRUPT MESSAGE
	 ERJMP	INTMS2
	JRST	INTMS3			;GO RETURN TO PREVIOUS CONTEXT

INTMS2:	$CALL	LCKLNK			;CHECK THE STATUS OF THE LINK
	JUMPT	S..IFE			;DECNET I/O FATAL ERROR DETECTED
	MOVEI	S1,.LSPDL(LIS)		;[6002]SET UP THE LISTENER CONTEXT
	HRLI	S1,-<PDSIZ-1>		;[6002]STACK POINTER
	MOVEM	S1,.LSACS+P(LIS)	;[6002]SAVE AS THE NEW STACK POINTER
	MOVEI	S1,LISTE3		;[6002]WHERE TO RESUME EXECUTION FROM	
	TXO	S1,1B5			;[6002]
	MOVEM	S1,.LS1PC(LIS)		;[6002]STORE AS THE NEW PC

INTMS3:	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT
	SUBTTL	CHKLST - CHECK LINK STATUS

;CHKLST is invoked by the top fork when it has detected that a node has
;left the cluster. This is necessary since if a node that a listener
;has a DECnet connection to crashes, the listener is not interrupted.
;(The listener is also not interrupted if the node should rejoin the
;cluster.) Since the listener is not interrupted and the link is no longer
;connected, no sender will now be able to communicate with the listener.
;CHKLST checks if its link is still connected. If it is not, then it resets
;the state of the listener and re-attempts to open the link.
;
;Call is: Invoked by the top fork
;DEBRKs:  The link status has been checked and the link re-opened if it was
;         no longer connected

CHKLST:	$BGINT	1,			;SAVE THE PREVIOUS CONTEXT
	$CALL	LCKLNK			;CHECK THE LINK STATUS
	JUMPT	CHKLS2			;THE LINK IS STILL CONNECTED SO RETURN
	SETZM	.LSSTE(LIS)		;RESET THE LISTENER'S STATE
	MOVEI	S1,.LSPDL(LIS)		;[6002]SET UP THE LISTENER CONTEXT
	HRLI	S1,-<PDSIZ-1>		;[6002]STACK POINTER
	MOVEM	S1,.LSACS+P(LIS)	;[6002]SAVE AS THE NEW STACK POINTER
	MOVEI	S1,LISTE3		;[6002]WHERE TO RESUME EXECUTION FROM	
	TXO	S1,1B5			;[6002]
	MOVEM	S1,.LS1PC(LIS)		;[6002]STORE AS THE NEW PC

CHKLS2:	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT
	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. The link list word is the checksum word (.MSCHS) since
;this word is no longer needed for checksum verification.
;
;Call is: LIS/Address of the listener block
;         S1/Address of the message queue entry
;Returns: The message has been added to the listener's message queue
;         S1/Address of the message page

;ADD THE MESSAGE TO THE LISTENER'S MESSAGE QUEUE

ADDLME:	SKIPG	S2,.LSTWD(LIS)		;IS THE MESSAGE QUEUE EMTPY?
	JRST	ADDLM2			;YES, ADD AS THE FIRST ENTRY
	MOVEM	S1,.MQBLK(S2)		;UPDATE CURRENT LAST MQ ENTRY
	SKIPA				;UPDATE THE TRAILER WORD
ADDLM2:	MOVEM	S1,.LSHWD(LIS)		;PLACE ADDRESS IN HEADER WORD
	MOVEM	S1,.LSTWD(LIS)		;PLACE ADDRESS IN TRAILER WORD
	$RET				;RETURN TO THE CALLER

REPEAT 0,<
	SUBTTL	LISCHK - LISTENER CHECKSUM MESSAGE

;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 checksumming is disabled, either on the
;sender's node or the listener's node, then a checksum is not performed.
;
;Call is:       S1/Address of the message
;Returns true:  The checksums match or checksumming is not enabled
;Returns false: The checksums do not match
;Crashes:       Could not send the failure ACK

LISCHK:	$SAVE	<P1>			;SAVE THIS AC
	SKIPN	CHECKS			;CHECKSUMMING ENABLED ON LOCAL NODE?
	JRST	LISCH2			;NO, RETURN SUCCESS
	SKIPN	P1,.MSCHS(S1)		;YES, CHECKSUMMING ENABLED REMOTELY?
	JRST	LISCH2			;NO, RETURN SUCCESS
	SETZM	.MSCHS(S1)		;ZERO OUT THE CHECKSUM WORD
	$CALL	CHKSUM			;CALCULATE THE CHECKSUM
	CAME	P1,S1			;DO THE CHECKSUMS AGREE?
	$RETF				;NO, INDICATE TO THE CALLER
LISCH2:	$RETT				;INDICATE MESSAGE IS VALID
>
	SUBTTL	SNDACK - SEND A SUCCESS ACK TO THE SENDER

;SNDACK is called to send an ACK message to the sender upon completion
;of processing the NEXTJOB message.
;has a length of two.
;
;Call is        LIS/Address of the listener block
;Returns true:  The ACK message was sent
;Returns false: The ACK message could not be sent

SNDACK:	$SAVE	<T1,T2>			;SAVE THESE AC
	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
	SETO	T1,			;NEGATIVE LENGTH OF THE ACK MESSAGE
	SOUTR%				;SEND THE MESSAGE TO THE SENDER
	 ERJMP	.RETF			;INDICATE THE MESSAGE COULD NOT BE SENT
	$RETT				;RETURN TO THE CALLER
					
	SUBTTL	LCKLNK - CHECK THE STATUS OF THE LISTENER'S LINK

;LCKLNK is called to check the status of the listener's DECnet link to
;the sender. If there is no connection, then the DECnet link is
;closed and the DECnet JFN released
;
;Call is:       LIS/Address of the listener block
;Returns true:  The DECnet link is connected 
;Returns false: The DECnet link has been aborted
;Crashes:       Unable obtain the link status

LCKLNK:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;OBTAIN THE DECNET LINK STATUS.

	SKIPG	S1,.LSJFN(LIS)		;PICK UP THE DECNET JFN
	$RETF				;NO JFN, NO LINK
	MOVEI	S2,.MORLS		;WANT THE STATUS OF THE LINK
	MTOPR%				;OBTAIN THE STATUS OF THE LINK
	 ERJMP	LCKLN1			;ABORT THE LINK ON A FAILURE
	MOVEM	T1,.LSLNK(LIS)		;SAVE THE LINK STATUS IN LISTENER 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
LCKLN1:	$CALL	LABLNK			;CLOSE AND RELEASE THE JFN
	$RETF				;INDICATE DON'T HAVE A LINK
	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 listener's listener table entry's 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 LT%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,.LSLTA(LIS)		;PICK UP THE LISTENER TABLE ENTRY
	MOVX	S2,LT%LFC		;PICK UP LISTENER FORK CRASHED BIT
	IORM	S2,.LTSTA(S1)		;INDICATE THAT THE LISTENER HAS CRASHED
	RESET%				;BREAK THE DECNET LINK
	DMOVE	S1,.LSERR(LIS)		;RESTORE CONTEXT OF S1 AND S2
	$RET				;RETURN TO THE CALLER
	SUBTTL	LABLNK - ABORT THE LISTENER'S DECNET LINK

;LABLNK is called to abort the listener's DECnet link by closing the
;DECnet link with ABORT and releasing its JFN if necessary.
;
;Call is: LIS/Address of the listener block
;Returns: The listener's DECnet link has been aborted

LABLNK:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS
	MOVE	S1,.LSJFN(LIS)		;PICK UP THE DECNET JFN
	TXO	S1,CZ%ABT		;CLOSE WITH ABORT
	CLOSF%				;CLOSE THE DECNET LINK
	 ERJMP	LABLN2			;SHOULDN'T HAPPEN
	JRST	LABLN3			;GO RETURN
LABLN2:	MOVE	S1,.LSJFN(LIS)		;PICK UP THE DECNET JFN AGAIN
	RLJFN%				;RELEASE THE JFN
	 ERJMP	.+1			;SHOULDN'T HAPPEN
LABLN3:	SETZM	.LSJFN(LIS)		;INDICATE NO LONGER HAVE A JFN
	$RET				;INDICATE DON'T HAVE A LINK
	SUBTTL	SENDER - MESSAGE ROUTER TO A REMOTE NODE

;SENDER exists as an inferior fork in LISSPL. A sender is started 
;whenever a message is received by QUASAR that is to be forwarded to
;a remote node in the cluster for which there is no current sender
;sending messages to it.
;A sender communicates with the top fork through software interrupts,
;the sender block and the sender table

;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 'LISSPL'
	  $SET  (IB.FLG,IP.STP,1)	;STOPCODES TO ORION
	  $SET	(IB.PIB,,0)		;SET UP PIB ADDRESS
	$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
	BLOCK	^D34			;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	LISDDT			;YES, SET UP FOR DEGGBUGGING

	$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 READY TO SEND ANY MESSAGES TO THE LISTENER

	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

SENDE2:	SETZ	S1,			;SLEEP UNTIL INTERRUPTED
	$CALL	I%SLP			;WAIT%

;AFTER EVERY INTERRUPT, SENDER IS FORCED OUT OF I%SLP. IF THE LINK'S
;CONNECTION IS BROKEN WHILE THE SENDER IS IN AN INTERRUPT, IT WILL NOT
;BE INTERRUPTED TO BE INFORMED OF THIS WHEN IT DEBRKS FROM THE INTERRUPT.
;IF SENDER IS NOT WAITING FOR AN ACK, THEN NOTHING NEEDS TO BE DONE.
;HOWEVER, IF SENDER IS WAITING FOR AN ACK, IT WILL NEVER RECEIVE IT.
;THEREFORE, IF THE SENDER IS WAITING FOR AN ACK, THEN CHECK THE LINK
;STATUS. IF THE LINK IS NO LONGER CONNECTED, THEN INDICATE TO THE TOP
;FORK THAT IT IS FREE TO PROCESS ANOTHER MESSAGE.

	SKIPGE	.SNFRE(SEN)		;WAITING FOR AN ACK FROM LISTENER?
	JRST	SENDE2			;NO, WAIT FOR TOP FORK SEND REQUEST
	$CALL	SCKLNK			;YES, CHECK THE LINK STATUS
	JUMPT	SENDE2			;LINK IS STILL CONNECTED, WAIT FOR ACK
	$CALL	SABLNK			;RELEASE THE DCN: DECNET JFN
	$CALL	CLRTIM			;CLEAR THE DECNET INACTIVITY TIMER
	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
	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 PIB BLOCK ADDRESS
	MOVEM	S1,.SNIBK+IB.PIB(SEN)	;PLACE IN THE IB 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			;POINT TO NEXT INTERRRUPT TABLE ENTRY
	AOS	S2			;POINT TO NEXT PC WORD
	MOVEM	S2,0(S1)		;PLACE PC ADR IN INTERRUPT LEVEL TABLE
	AOS	S1			;POINT TO NEXT INTERRRUPT TABLE ENTRY
	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,.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		;PICK UP CHANNELS TO ACTIVATE
	AIC%				;ACTIVATE THE CHANNELS
	 ERJMP	SENIN2			;CRASH IF CAN'T ACTIVATE THE CHANNELS
	$RET				;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.
;
;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,T3,T4>		;SAVE THESE AC

;INITIALIZE THE ATTEMPT TO OBTAIN A DECNET CONNECTION TO THE LISTENER

	MOVEI	T3,MINTIM		;PICK UP INITIAL TIME BETWEEN RETRIES
	SETZ	T4,			;NUMBER OF ATTEMPTS TO OBTAIN THE LINK

;ATTEMPT TO OBTAIN THE DECNET CONNECTION. 

SOPLN2:	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
	$CALL	SETIM			;SET THE DECNET INACTIVITY TIMER
	$RET				;RETURN TO SENDER STARTUP

;UNABLE TO OBTAIN THE CONNECTION. DETERMINE IF THE RETRY SHOULD BE INCREASED.

SOPLN3:	AOS	T1,T4			;INCREMENT # TRIES AT THIS TIME INTERVAL
	IMUL	T1,T3			;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 AND REPORT THE CONNECTION FAILURE.

	$CALL	FNDCER			;GET THE CONNECTION ERROR

	$LOG	(<Sender connection failure>,<^I/@SOPLN7/^M^J^I/@SOPLN8/>)

;CALCULATE THE NEW TIME INTERVAL BETWEEN CONNECTION ATTEMPTS. THE MAXIMUM
;TIME BETWEEN RETRIES IS MAXTIM SECONDS.

	ADDI	T3,MINTIM		;INCREMENT THE RETRY INTERVAL
	CAILE	T3,MAXTIM		;TIME INTERVAL AT A MAXIMUM?
	MOVEI	T3,MAXTIM		;YES, SET TO MAXIMUM

;DISMISS UNTIL INDICATED

	SETZ	T4,			;NUMBER OF ATTEMPTS AT THIS INTERVAL
SOPLN6:	MOVE	S1,T3			;PICK UP TIME TO DISMISS
	$CALL	I%SLP			;DISMISS OR WAIT% AS INDICATED
	JRST	SOPLN2			;ATTEMPT THE CONNECTION AGAIN

SOPLN7:	[ITEXT(<LISSPL Sender to node ^N/.SNNME(SEN)/ has not been able to obtain a DECnet connection>)] ;[6006]
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>			;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 DCN: DEVICE NAME
	GTJFN%				;PICK UP THE JFN
	 ERJMP	SGTLN2			;CRASH IF CAN'T GET JFN
	HRRZS	S1			;ISOLATE THE JFN
	MOVEM	S1,.SNJFN(SEN)		;SAVE THE JFN IN SENDER BLOCK

	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:	$CALL	INSCRH			;INDICATE A CONTROLLED CRASH
	$STOP	(SOD, SENDER CAN'T OPEN DECNET DEVICE)
	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 to obtain the link status

SCKLNK:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS

;OBTAIN THE DECNET LINK STATUS.

	SKIPG	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	$RETF				;NO JFN, NO LINK
	MOVEI	S2,.MORLS		;WANT THE STATUS OF THE LINK
	MTOPR%				;OBTAIN THE STATUS OF THE LINK
	 ERJMP	SCKLN1			;ABORT THE LINK ON A FAILURE
	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
SCKLN1:	$CALL	SABLNK			;CLOSE AND RELEASE THE 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 DECnet 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	MSGTLI - SEND A MESSAGE TO THE LISTENER

;MSGTLI is the interrupt handler for sending a message to a listener.
;MSGTLI first checks that the link is still connected. If it is, 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

MSGTLI:	$BGINT	1,			;SAVE THE CONTEXT

;CHECK THE LINK STATUS, IF THE LINK IF STILL CONNECTED, THEN SEND THE
;MESSAGE. IF THE LINK IS NO LONGER CONNECTED, THEN RE-OPEN THE LINK AND
;SEND THE MESSAGE.

	SKIPG	.SNMSG(SEN)		;IS THERE A DCN: DECNET JFN?
	$CALL	SOPLNK			;NO, RE-OPEN THE LINK
REPEAT 0,<
	MOVE	S1,.SNMSG(SEN)		;PICK UP THE MESSAGE ADDRESS
	SETZM	.MSCHS(S1)		;ASSUME CHECKSUMMING NOT ENABLED
	SKIPE	CHECKS			;IS CHECKSUMMING ENABLED?
	$CALL	CHKSUM			;YES, CHECKSUM THE MESSAGE
>
MSGTL2:	$CALL	SSNDMG			;SEND THE MESSAGE
	JUMPF	MSGTL3			;COULD NOT SEND THE MESSAGE

	$DEBRK				;RETURN TO PREVIOUS CONTEXT

;MESSAGE WAS NOT SENT. CHECK THE STATUS OF THE LINK. IF THE LINK IS STILL
;CONNECTED, THEN CONSIDER THE ERROR TO BE FATAL, ELSE RE-ATTEMPT TO OPEN
;THE LINK

MSGTL3:	$CALL	SCKLNK			;CHECK THE STATUS OF THE LINK
	JUMPT	MSGTL4			;LINK IS CONNECTED, FATAL ERROR
	$CALL	SOPLNK			;RE-CONNECT THE LINK
	JRST	MSGTL2			;RE-SEND THE MESSAGE
MSGTL4:	$CALL	INSCRH			;INDICATE CRASH IN NODE STATUS WORD 	
	JRST	S..IFE			;SENDER LOST ITS LINK

REPEAT 0,<
	SUBTTL	CHKSUM - CHECKSUM DECNET MESSAGES

;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 do
;not agree, then the listener sends a failure ACK back to the sender.
;If the checksums agree, then the sender will send a success ACK  to
;the sender.
;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: 1. If the calculated checksum equals zero, then CHKSUM changes
;          it to -1.)
;       2. The checksum word .MSCHS is always zeroed before the checksum
;          calculation is done.
;
;Call is: S1/Address of the message
;Returns: 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
	$RET				;RETURN TO THE CALLER
>
	SUBTTL	MSGFLI - PICKUP ACK MESSAGE FROM THE LISTENER

;MSGFLI is the interrupt handler for ACK messages from the listener.
;
;Call is: SEN/Address of the sender block
;Returns: The ACK message has been processed

MSGFLI:	$BGINT	1,			;SAVE THE CONTEXT

;CHECK IF AN ACK MESSAGE IS AVAILABLE OR IF THIS IS JUST A SPURIOUS INTERRUPT.

	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	SIBE%				;IS THERE AN ACK MESSAGE?
	JRST	MSGFL1			;YES, PICK IT UP
	$CALL	SCKLNK			;NO, CHECK THE LINK STATUS
	JUMPT	MSGFL4			;STILL CONNECTED, SO SPURIOUS
	$CALL	CLRTIM			;CLEAR THE TIMER
	JRST	MSGFL3			;LOST THE LINK, INFORM TOP FORK READY

;PICK UP THE ACK MESSAGE

MSGFL1:	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	MOVE	S2,.SNMSG(SEN)		;PICK UP ADDRESS OF MESSAGE
	HRLI	S2,(POINT 36,)		;MAKE INTO A POINTER
	MOVNI	T1,PAGSIZ		;PICK UP SIZE OF MESSAGE
	SINR%				;PICK UP THE ACK MESSAGE
	 ERJMP	MSGFL2			;ON AN ERROR, ABORT THE LINK
	$CALL	CASTIM			;CLEAR AND SET THE TIMER
	JRST	MSGFL3			;INFORM TOP FORK FREE TO SEND

MSGFL2:	$CALL	CLRTIM			;CLEAR THE TIMER
	$CALL	SABLNK			;ABORT THE LINK

MSGFL3:	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
MSGFL4:	$DEBRK				;RETURN TO THE PREVIOUS CONTEXT
	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 true:  The message was sent to the listener
;Returns false: The message could not be sent to the listener

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 36,)		;MAKE IT INTO A POINTER
	LOAD	T1,.MSTYP(S2),MS.CNT	;PICK UP THE LENGTH OF THE MESSAGE

;SEND THE MESSAGE

SSNDM2:	MOVNS	T1			;MAKE THE MESSAGE LENGTH NEGATIVE
	SOUTR%				;SEND THE MESSAGE
	 ERJMP	.RETF			;INDICATE COULD NOT SEND THE MESSAGE
	$RETT				;INDICATE MESSAGE WAS SENT
	SUBTTL	PROTIM - DECNET INACTIVITY TIMER PROCESSOR

;PROTIM is the "interrupt handler" for the DECnet inactivity timer.
;PROTIM is invoked when the DECnet inactivity timer goes off. 
;PROTIM aborts the DECnet link if there is no active request.
;
;CALL is: No arguments
;Returns: The DECnet link has been aborted

PROTIM:	SKIPN	.SNFRE(SEN)		;IS A MESSAGE BEING PROCESSED?
	$RET				;YES, DON'T ABORT THE LINK
	$CALL	SABLNK			;ABORT THE LINK
	$RET				;RETURN TO I%SLP
	SUBTTL	CASTIM - CLEAR AND RESET THE DECNET INACTIVITY TIMER

;CASTIM is called to clear and reset the DECnet inactivity timer. After
;a RELEASE message has been sent to Cluster LPTSPL, the timer is cleared
;and reset. If no other RELEASE messages are received before the timer
;goes off, then the link is aborted.
;
;Call is: SEN/Address of the sender block
;Returns: The DECnet inactivity timer has been cleared and reset

CASTIM:	$CALL	CLRTIM			;CLEAR THE TIMER
SETIM:	$CALL	I%NOW			;PICK UP THE CURRENT TIME
	SKIPN	DEBUGW			;[6004]DEBUGGING?
	ADDI	S1,TIMITL		;[6004]NO, TIME THE TIMER WILL GO OFF
	SKIPE	DEBUGW			;[6004]DEBUGGING?
	ADDI	S1,777777		;[6004]YES, INCREASE THE TIMER VALUE
	MOVEM	S1,.TITIM+.SNTET(SEN)	;PLACE IN THE TIME EVENT BLOCK
	MOVEI	S1,.TIMDT		;PICK UP THE TIMER FUNCTION
	MOVEM	S1,.TIFNC+.SNTET(SEN)	;PLACE IN THE TIME EVENT BLOCK
	MOVEI	S1,PROTIM		;PICK UP THE TIMER PROCESSING ROUTINE
	MOVEM	S1,.TIMPC+.SNTET(SEN)	;PLACE IN THE TIME EVENT BLOCK
	MOVEI	S1,.TIMPC+1		;PICK UP LENGTH OF TIME EVENT BLOCK
	MOVEI	S2,.SNTET(SEN)		;PICK UP ADDRESS OF TIME EVENT BLOCK
	$CALL	I%TIMR			;SET THE TIMER
	$RET				;RETURN TO THE CALLER
	SUBTTL	CLRTIM - CLEAR THE DECNET INACTIVITY TIMER

;CLRTIM is called to clear the DECnet inactivity timer.
;
;Call is: SEN/Address of the sender block
;Returns: The DECnet inactivity timer has been cleared

CLRTIM:	MOVEI	S1,.TIMDD		;PICK UP THE FUNCTION
	MOVEM	S1,.TIFNC+.SNTET(SEN)	;PLACE IN THE TIME EVENT BLOCK
	MOVEI	S1,.TITIM+1		;PICK UP LENGTH OF TIME EVENT BLOCK
	MOVEI	S2,.SNTET(SEN)		;PICK UP ADDRESS OF TIME EVENT BLOCK
	$CALL	I%TIMR			;CLEAR THE TIMER
	$RET				;RETURN TO THE CALLER
	SUBTTL	SABLNK - ABORT THE SENDER'S DECNET LINK

;SABLNK is called to abort the sender's DECnet link by closing the DECnet link
;with ABORT and releasing its JFN if necessary.
;
;Call is: SEN/Address of the sender block
;Returns: The sender's DECnet link has been aborted

SABLNK:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS
	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN
	TXO	S1,CZ%ABT		;CLOSE WITH ABORT
	CLOSF%				;CLOSE THE DECNET LINK
	 ERJMP	SABLN2			;SHOULDN'T HAPPEN
	JRST	SABLN3			;GO RETURN
SABLN2:	MOVE	S1,.SNJFN(SEN)		;PICK UP THE DECNET JFN AGAIN
	RLJFN%				;RELEASE THE JFN
	 ERJMP	.+1			;SHOULDN'T HAPPEN
SABLN3:	SETZM	.SNJFN(SEN)		;INDICATE NO LONGER HAVE A JFN
	$RET				;INDICATE DON'T HAVE A LINK
	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 sender's sender table entry's 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 ST%SFC is set in the sender's sender table entry's 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,.SNSTA(SEN)		;PICK UP THE NODE TABLE ENTRY
	MOVX	S2,ST%SFC		;PICK UP SENDER FORK CRASHED BIT
	IORM	S2,.STSTS(S1)		;INDICATE THAT THE SENDER HAS CRASHED
	RESET%				;BREAK THE DECNET LINK
	DMOVE	S1,.SNERR(SEN)		;RESTORE CONTEXT OF S1 AND S2
	$RET				;RETURN TO THE CALLER
					
	SUBTTL	LISDDT - ROUTINE TO LOAD DDT IF DEBUGGING

;LISDDT is called if LISSPL is running in a DEBUG environment.
;LISDDT maps in and starts DDT
;
;Call is: No arguments
;Returns: DDT has been loaded
;Crashes: If unable to load DDT


LISDDT:	$SAVE	<T1,T2>			;SAVE THESE AC, DESTROYED BY JSYS
	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	LISDD2			;CRASH IF CAN'T GET DDT'S JFN
	HRLI	S1,.FHSLF		;PICK UP HANDLE
	GET%				;LOAD DDT
	 ERJMP	LISDD2			;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 LISSPL'S SYMBOL TABLE
	JRST	770000			;AND ENTER DDT
GO:	$RET				;RETURN

LISDD2:	$STOP	(DDE, DDT ERROR)	;CRASH, IF CAN'T GET DDT
	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	COMMON STOPCODES

;These STOPCODES are called from more than one location in LISSPL's 
;top fork.

	$STOP	(CTL, CLUSTER TOO LARGE)
	$STOP	(CSI, CAN'T SETUP INTERRUPT SYSTEM)
	$STOP	(SIF, SCS% INTERRUPT HANDLER ENCOUNTERED FATAL ERROR)
	$STOP	(SCI, SENDER CAN'T INTERRUPT THE TOP FORK)
	$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	(IFE, DECNET I/O FATAL ERROR)
	$STOP	(IFM, ILLEGALLY FORMATTED 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 LISSPL
;IS IN AN INCONSISTENT STATE AND SHOULD THEREFORE BE CRASHED.

	$STOP	(UII, UNABLE TO INTERRUPT AN INFERIOR FORK)

	END LISSPL