Google
 

Trailing-Edge - PDP-10 Archives - bb-jr93k-bb - 10,7/mon/router.mac
There are 25 other files named router.mac in the archive. Click here to see a list.
      ;TITLE ROUTER - Routing Layer Service for DECnet-36  V107


;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;  OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1976,1986,1988.
;ALL RIGHTS RESERVED.

	SUBTTL Loose ends

;Fix events returned to NTMAN (poorly formatted)
;Don't do BUG??? in RTCRTE if endnode
;Improve LSH's at RTR2R4 - Build into RTROAV when entered into cache?
;Highest address in Partial Routing Update Loss

		SUBTTL V. Brownell & Anthony J. Rizzolo/Tarl/AJR/HMP 11-APR-89
		SEARCH D36PAR,MACSYM
	SALL

	ENTRY RTRINI

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1981,1982,1983,1984,1985,1986 by DIGITAL EQUIPMENT CORPORATION, 
; MAYNARD, MASS.
;ALL RIGHTS RESERVED.
;
;
XP ROUTER,107
IFN FTOPS20,<
	SEARCH PROLOG
	TTITLE ROUTER,,< - Routing Layer Service for DECnet-36>
	>

IFN FTOPS10,<
	SEARCH F,S
	TITLE ROUTER - Routing Layer Service for DECnet-36
.CPYRT<1981,1988>
>;END OF IFN FTOPS10
	D36SYM			;SET UP D36 SPECIFIC PARAMETERS

IFN FTOPS10,<$RELOC>
	XRESCD			;RELOC TO HIGHSEG (RSCOD PSECT ON TOPS-20)
	SUBTTL	Table of Contents

;		Table of Contents for ROUTER
;
;
;			   Section			      Page
;   1. V. Brownell & Anthony J. Rizzolo/Tarl/AJR/HMP 15 Jan 85   3
;   2. Table of Contents. . . . . . . . . . . . . . . . . . .    4
;   3. Definitions
;        3.1.   External References . . . . . . . . . . . . .    5
;        3.2.   Accumulators. . . . . . . . . . . . . . . . .    7
;        3.3.   Macros. . . . . . . . . . . . . . . . . . . .    8
;        3.4.   Packet Route Header . . . . . . . . . . . . .    9
;        3.5.   Router Control Message. . . . . . . . . . . .   10
;        3.6.   Routing Vectors . . . . . . . . . . . . . . .   11
;        3.7.   NI Hello IINFO format . . . . . . . . . . . .   12
;        3.8.   Miscellaneous . . . . . . . . . . . . . . . .   13
;   4. Network management tables. . . . . . . . . . . . . . .   14
;        4.1.   Executor counters . . . . . . . . . . . . . .   14
;        4.2.   Circuit counters. . . . . . . . . . . . . . .   15
;        4.3.   Node parameters . . . . . . . . . . . . . . .   16
;        4.4.   Executor parameters . . . . . . . . . . . . .   17
;        4.5.   Circuit parameters. . . . . . . . . . . . . .   18
;   5. RTRINI - Initialize Router User. . . . . . . . . . . .   19
;   6. RTRON - Turn Router (and DECnet) On. . . . . . . . . .   20
;   7. RTROFF - Turn Router (and DECnet) Off. . . . . . . . .   21
;   8. RTRSEC - Once a second processing for Router . . . . .   22
;   9. RTRJIF - Once a jiffy processing for Router. . . . . .   23
;  10. RTRLCK - Get the RTR interlock . . . . . . . . . . . .   25
;  11. RTRXMT - NSP's entry into Router . . . . . . . . . . .   27
;  12. RTRFWD - Perform Routing of Message. . . . . . . . . .   28
;  13. RTR2RM - Send Message to Remote Node . . . . . . . . .   34
;  14. RTRBEH - Build header for ethernet data messages . . .   37
;  15. R2NODN - Return Output Done to NSP . . . . . . . . . .   38
;  16. R2NRTN - Return Message to NSP . . . . . . . . . . . .   39
;  17. R2NRCV - Pass received Message to NSP. . . . . . . . .   40
;  18. R2KINI - Initialize a Kontroller's Circuit . . . . . .   41
;  19. R2KCLS - Close the data link circuit . . . . . . . . .   42
;  20. CALKON - Call the Kontroller . . . . . . . . . . . . .   43
;  21. CALQOB - Call CALKON for queued output.. . . . . . . .   44
;  22. XMTFAI/CIRFAI - Transmit/Line failed . . . . . . . . .   45
;  23. RTRRCR - Recompute Routing . . . . . . . . . . . . . .   46
;  24. RTRBSV - Build scratch routing vector. . . . . . . . .   47
;  25. RTRBNV - Build the normal vector from scratch vector .   48
;  26. RTRBMV - Maybe vector update when running as an endnode  49
;  27. RTRUPD - Send routing messages if needed . . . . . . .   50
;  28. RTRSNI - Send hello/routing message on the NI. . . . .   57
;  29. CKECTO - Check the cache timer for each node . . . . .   58
;  30. SELDSR - Select a desiginated router . . . . . . . . .   59
;  31. RTRTMR - Routine to perform timer functions. . . . . .   60
;  32. RTRENH - Send Ethernet end-node hello. . . . . . . . .   67
;  33. RTRCHH - Build common part of Ethernet hellos. . . . .   68
;  34. DSRCHK - Check the DSR timer for each circuit. . . . .   69
;  35. Data Link Layer Interface. . . . . . . . . . . . . . .   70
;       35.1.   RTRDLE - Entry into Router from DLL . . . . .   70
;       35.2.   RTICCB - Create circuit block . . . . . . . .   72
;       35.3.   RTILSC - Process line state change. . . . . .   73
;       35.4.   RTIOTC - Process Output Complete. . . . . . .   74
;       35.5.   RTIINC - Process Input Complete . . . . . . .   75
;  36. RTRSTI - Send routing intitialization message. . . . .   78
;  37. Control Message Processors
;       37.1.   RTCINI - Inititialization Messages. . . . . .   80
;       37.2.   RTCVER - Verification Messages. . . . . . . .   82
;       37.3.   RTCRTE - Routing Messages . . . . . . . . . .   83
;       37.4.   RTCTST - Test or Hello Messages . . . . . . .   85
;       37.5.   RTCL2M - Level 2 Routing Messages . . . . . .   86
;       37.6.   RTCERH - Ethernet Router Hello. . . . . . . .   87
;       37.7.   RHMASR - Hello message if Endnode . . . . . .   88
;       37.8.   RHMASR - Hello message if Router. . . . . . .   89
;       37.9.   RTCNIE - NI Endnode Hello message . . . . . .   91
;  38. RTCILL - Illegal message received. . . . . . . . . . .   92
;  39. RTRBAV - Build adjacency's routing vector from routing update  93
;  40. RTCRME - Routing message error processor . . . . . . .   96
;  41. RTRPNI - Parse a  Message Header for NI Hello messages   97
;  42. PSINFO - Parse the info field of Ethernet router hello   98
;  43. RTRRCV - Read version information from message . . . .   99
;  44. RTRHDP - Parse a Input Router Message Header . . . . .  100
;  45. RTRCTD - Check for valid test pattern in Hello/Test message 103
;  46. RTRGNA - Get node address from message header. . . . .  104
;  47. PADSKP - Skip over pad bytes in an Ethernet message header  105
;  48. Network Management
;       48.1.   RTRNMX - Entry into Router. . . . . . . . . .  106
;       48.2.   Read/Set/Clear a parameter. . . . . . . . . .  107
;  49. Network management
;       49.1.   Common routine for parameters . . . . . . . .  108
;       49.2.   Setup for loading parameter data. . . . . . .  109
;  50. Network Management
;       50.1.   Qualified parameters. . . . . . . . . . . . .  110
;       50.2.   READ node/executor state. . . . . . . . . . .  111
;       50.3.   Read circuit state. . . . . . . . . . . . . .  112
;       50.4.   Set/Clear executor state. . . . . . . . . . .  113
;       50.5.   Set/Clear circuit state . . . . . . . . . . .  114
;       50.6.   Show counters . . . . . . . . . . . . . . . .  115
;       50.7.   Show and zero counters. . . . . . . . . . . .  115
;       50.8.   Return list . . . . . . . . . . . . . . . . .  116
;       50.9.   Show known/active nodes . . . . . . . . . . .  117
;       50.10.  Show adjacent nodes . . . . . . . . . . . . .  118
;       50.11.  Show active circuits. . . . . . . . . . . . .  119
;       50.12.  Show known circuits . . . . . . . . . . . . .  120
;       50.13.  Return list of adjacencies on a circuit . . .  121
;  51. Network Management Event Interface
;       51.1.   Router Event Types. . . . . . . . . . . . . .  122
;       51.2.   Event Reason Definitions. . . . . . . . . . .  124
;       51.3.   RTNEVT - Event Reporter . . . . . . . . . . .  125
;       51.4.   Event Parameter Processors. . . . . . . . . .  126
;       51.5.   Event Processor Subroutines . . . . . . . . .  129
;       51.6.   Miscellaneous Event Processors. . . . . . . .  130
;  52. Miscellaneous Routines
;       52.1.   RTRSAO - Set Adjacency off line . . . . . . .  132
;       52.2.   RTRZCB - Zap Circuit Block. . . . . . . . . .  133
;       52.3.   RTRZAA - Zap all Adjacancies associated with RC  134
;  53. RTRZAB - Zap an adjacency block. . . . . . . . . . . .  135
;  54. Miscellaneous Routines
;       54.1.   RTRMAJ - Make an Adjacency Block. . . . . . .  136
;       54.2.   RTRMCB - Make a Circuit Block . . . . . . . .  137
;       54.3.   RTRGCB - Get Circuit Block Pointer. . . . . .  138
;       54.4.   CPYMSG - Copy all MSDs together . . . . . . .  141
;  55. Miscellaneous Routine
;       55.1.   RTRGBS - Get maximum blocksize. . . . . . . .  142
;  56. Miscellaneous Routines
;       56.1.   RTRCBS - Get useable blocksize. . . . . . . .  143
;       56.2.   FREMSG - Toss a Message . . . . . . . . . . .  144
;       56.3.   CKSNRM - Normalize Checksum . . . . . . . . .  145
;  57. Local Router Storage . . . . . . . . . . . . . . . . .  146
;  58. End of ROUTER. . . . . . . . . . . . . . . . . . . . .  147

	SUBTTL Definitions -- External References

;These are the external references to the D36COM library of routines.

	EXT DNGINI		;INITIALIZE FOR INPUT (DGXYBY)
	EXT DNPINI		;INITIALIZE FOR OUTPUT (DPXYBY)

	EXT DNP1BY		;PUT ONE BYTE IN MESSAGE
	EXT DNP2BY		;PUT TWO BYTES IN MESSAGE
	EXT DNPHIO		;Put 4 bytes of ethernet address into message
	EXT DNPENA		;DEPOSIT 6 BYTES FOR NI ADDRESSES
	EXT DNPEBY		;PUT AN EXTENSIBLE BYTE IN MESSAGE
	EXT DNPZB		;Write (T1) bytes of zeros into the message

	EXT DNG1BY		;GET ONE BYTE FROM MESSAGE
	EXT DNG2BY		;GET TWO BYTES FROM MESSAGE
	EXT DNGHIO		;Get four bytes of hi-order ethernet address
	EXT DNGENA		;GET SIX BYTES FROM MESSAGE
	EXT DNGEBY		;GET EXTENSIBLE BYTE FROM MESSAGE

	EXT DNGMSG		;GET DECNET-36 MESSAGE BLOCK
	EXT DNFMSG		;FREE MESSAGE BLOCK
	EXT DNMINI		;RE-USE MESSAGE BLOCK

	EXT DNNMSG		;GET NUMBER OF BUFFERS AVAILABLE TO ROUTER

	EXT DNLENG		;GET THE MESSAGE LENGTH
	EXT DNSLNG		;FIND THE SEGMENT LENGTH

	EXT DNGWDS		;GET SOME WORDS
	EXT DNGWDZ		;GET SOME ZEROED WORDS
	EXT DNFWDS		;FREE SOME WORDS
	EXT DNSWDS		;SMEAR SOME WORDS

	EXT DNBKBY		;GO BACKWARDS SOME BYTES
	EXT DNSKBY		;SKIP SOME BYTES

	EXT DNRPOS		;RETURN POSTION IN MESSAGE
	EXT DNGPOS		;GO TO POSITION IN MESSAGE
	EXT DNGOPS		;GO TO POSITION IN OUTPUT MESSAGE

	EXT DNLMSS		;LINK MESSAGE SEGMENT INTO MESSAGE BLOCK
	EXT DNCPMS		;COPY MESSAGE BLOCKS

IFN FTRTST,<
	EXT UPDTCK		; Better TODCLK
>
	EXT DNGTIM		;RETURN CURRENT UPTIME (IN MS)
	EXT NTPARM		; Routine to load parameters
	EXT NTCTRS		; Routine to load counters

	EXT TIMBAS		;Fractions of a second used for time
				; computation

;References to DNADLL

	EXT DNDDSP		;Entry into DNADLL
	EXT DNDINI		;Initialization point for DNADLL
	EXT DNDSEC		;DNADLL second routine

;These are external data locations.

	EXT DCNSTA		;THE STATE VARIABLE OF DECNET
	EXT DCNTSB		;Maximum system buffers
	EXT RTRHOM		; Home area
	EXT RTRADR		; Our node number
	EXT RTRHIO		; High order Ethernet address
	EXT RTRLOO		; Low order Ethernet address
	EXT RTRBSZ		; Blocksize we advertise
	EXT RTRMXN		; Maximum node address we talk to
	EXT RTRMX3		; Maximum number of Phase III nodes
	EXT RTRVER		; Version of ROUTER
	EXT RTRECO		; ECO level of ROUTER
	EXT RTRCUS		; Customer version
	EXT RTRXPW		; Our transmit password

	EXT IBBLK		;DECnet-36 initialization block

;This is the reference to LLINKS.

	EXT NSPRTR		;THE ONE AND ONLY

;We need to know our node name for Phase II NI messages.

	EXT SCTA2N		;PERFORM TRANSLATION FROM ADDRESS TO NODE NAME
;These are some default parameters

	EXT %RTMXN		;DEFAULT MAXIMUM NODE NUMBER
	EXT %RTMXC		;DEFAULT MAXIMUM CIRCUIT COST
	EXT %RTMXH		;DEFAULT MAXIMUM HOPS
	EXT %RTMX3		;MAXIMUM NUMBER OF PHASE III NODES
	EXT %RTMXV		;MAXIMUM VISITS COUNT
IFN FTOPS10,EXT %RTADR		;DEFAULT LOCAL ADDRESS
	EXT %RTRMA		;"ALL ROUTERS" MULTICAST ADDRESS
	EXT %RTEMA		;"ALL ENDNODES" MULTICAST ADDRESS
	EXT %RTHIO		;HI PART OF THE NI ADDRESS
	EXT %RTHOM		;HOME AREA FOR THIS NODE
	EXT %RTMXR		;Default maximum number of routers on the NI
	EXT %RTBRA		;NUMBER OF BROADCAST ROUTER ADJACENCIES
	EXT %RTBEA		;NUMBER OF BROADCAST ENDNODE ADJACENCIES
	EXT %RTPRI		;Priority to be the designated router (NI)
	EXT %RTTM1		;LONG TIMER FOR POINT TO POINT
	EXT %RTBT1		;LONG TIMER FOR BROADCAST
	EXT %RTTM3		;DEFAULT HELLO MESSAGE TIMER
	EXT %RTTM4		;DEFAULT NODE LISTENER TIMEOUT VALUE
	EXT %RTT3M		;HELLO TIMER MULTIPLIER FOR NON-BROADCAST
	EXT %RTB3M		;HELLO TIMER MULTIPLIER FOR BROADCAST CIRCUITS
	EXT %RTITM		;TI TIMER
	EXT %RTCTO		;Time we can spend in cache without update
	EXT %RTVER		;ROUTER VERSION NUMBER
	EXT %RTECO		;EDIT LEVEL
	EXT %RTCUS		;CUSTOMER EDIT LEVEL

;Other external references

	EXT NMXEVT		;REPORT AN EVENT TO NMX
	EXT NTEIPV		;TELL NETWORK MANAGEMENT THAT BAD VALUE

	EXT KONCST		;TABLE OF DEFAULT COSTS PER KONTROLLER TYPE

	EXT EVSDRP		;Whether as an endnode we listen to packets
				; sent to multicast ID "all routers"

	EXT RTN			;NON-SKIP RETURN
	EXT RSKP		;SKIP RETURN
IFN FTOPS10,<
	EXT BITTBL		;TABLE OF BITS
	BITS==BITTBL
>; END IFN FTOPS10


;Interlock management

IFN FTOPS10,<
;	EXT RTRLOK		;ROUTER INTERLOCK
;	EXT RTRLKO		;INTERLOCK OWNER
	EXT .CPCPN		;CURRENT CPU NUMBER
	EXT .CPSK0		;SKIP IF CURRENT CPU IS BOOT CPU
>
	SUBTTL Definitions -- Accumulators

;These are some local AC defintions for Router-36.

	RC=FREE1		;RC USUALLY POINTS TO THE CIRCUIT BLOCK
	AJ=FREE2		;AJ USUALLY POINTS TO THE ADJACENCY ENTRY

	PURGE FREE1		;LOSE THIS SYMBOL
	PURGE FREE2		;LOSE THIS ONE TOO

IFN FTOPS10,<
	Q1=T5			;RE-DEFINE THESE AGAIN
	Q2=T6			;TO MATCH TOPS-20
>

	SUBTTL Definitions -- Macros

;Event Logging Macro

DEFINE EVENT(TYPE,TEXT,MSGBLK),<
	CALL [
IFN FTTRACE,<
	      ETRACE RTR,<RTR Event: 'TEXT>
>
	      MOVX T1,TYPE	;;PUT EVENT TYPE IN T1
IFNB <MSGBLK>,MOVE T4,'MSGBLK	;;IF HE GAVE MB POINTER, GIVE IT
IFB <MSGBLK>, SETZ T4,		;;IF NOT, GIVE RTNEVT A ZERO
	      CALLRET RTNEVT]	;;CALL THE EVENT PROCESSOR AND RETURN
>

REPEAT 0,<
DEFINE EVENT(TYPE,TEXT,MSGBLK),<
	CALL RTNEVT
	CAI [IFN FTTRACE,<ETRACE RTR,<RTR Event: 'TEXT>>
	     TYPE
	     MSGBLK]
>
>
	SUBTTL Definitions -- Packet Route Header

;The first byte of the packet route header is defined in the Router
;section of the message block structure.  This is purely for convience
;sake.  The whole byte is called RMFST and each bit is defined within
;this field.

;The only other field that has to be defined (other than the source and
;destination address) is the FORWARD field which simply consists of the
;VISITS field.

BEGSTR FW			;FORWARD BYTE
	FIELDM VST,77		; VISITS COUNT
ENDSTR

	SUBTTL Definitions -- Router Control Message

;The following are field definitions for the Router Control messages.

;Control message all begin with the field CTLFLG, which specifies the type
;of control message.

BEGSTR CM			;CTLFLG
	FIELDM TYP,7_1		; TYPE OF MESSAGE
	FIELDM CTL,1
ENDSTR

;These are the various types of router control messages (in CMTYP).

	XP RCM.TI,0		;Router init
	XP RCM.TV,1		;Router verification
	XP RCM.TT,2		;Router hello and test
	XP RCM.1R,3		;Level 1 routing message
	XP RCM.2R,4		;Level 2 routing message
	XP RCM.RH,5		;Ethernet Router hello message
	XP RCM.EH,6		;Ethernet Endnode hello mesaage

;Routing control messages consist of a RTGINFO entry for every node in
;the network, followed by a two byte checksum.

BEGSTR RG			;RTGINFO
	FIELDM HOP,37_^D10	; HOPS
	FIELDM CST,1777		; COST
ENDSTR


;Transport Initialization control messages start with field TIINFO which
;contains the node type and verification required flags.

BEGSTR TI			;TIINFO
	FIELDM RSV,36_3		; RESERVED
	FIELDM BLO,1_3		;BLOCKING REQUESTED
	FIELDM VER,1_2		; VERIFY FLAG
	FIELDM NTY,3		; NODE TYPE
ENDSTR

	XP TI.MXL,^D12		;MAX LENGTH OF ROUTER INIT MSG

;These are the possible types of nodes that can be at the other end
;of a circuit (in FIELD TINTY).

	XP ANT.XX,0		;RESERVED
	XP ANT.L2,1		;LEVEL 2 ROUTER
	XP ANT.L1,2		;LEVEL 1 ROUTER
	XP ANT.RT,2		;ROUTING
	XP ANT.NR,3		;NON-ROUTING


;Router hello message has NO.HEL bytes of the byte HEL.LO.

	XP NO.HEL,^D10		;NUMBER OF TEST BYTES
	XP NO.NIH,^D64		;Number of test bytes in endnode hello
	XP HEL.LO,^O252		;CONSTANT FOR HELLO AND TEST MESSAGES

	SUBTTL Definitions -- Routing Vectors

;These are the definitions for each entry in both the scratch and the
;normal routing vectors.  The vectors consist of one entry for each
;possible node in the network.  Non-existant (un-reachable) nodes are
;set to the maximum node cost and hops.

BEGSTR RN
	FIELD FLG,6		;Flags
	  BIT RCH		;If set, node is reachable
	  BIT LCL		;Node is local NSP user
	  BIT CHG		;Node status was changed
	  BIT CCH		;Node is in the on-ethernet cache
	  BIT MBY		;Node maybe reachable (if we are an endnode)
	FILLER 15
	FIELD HOP,5		;MIN HOPS
	FIELD CST,10		;MIN COST
ENDSTR

;Define right justified symbols for max-cost and max-hops.

	XP RN%CST,MASK.(WID(RNCST),^D35) ;MAX COST
	XP RN%HOP,MASK.(WID(RNHOP),^D35) ;MAX HOPS

	SUBTTL Definitions -- NI Hello IINFO format

BEGSTR II
	FIELDM TYP,3		;NODE TYPE
	FIELDM VRQ,1_2		;VERIFICATION REQUIRED (0 FOR NI)
	FIELDM RJF,1_3		;REJECT FLAG
	FIELDM VFL,1_4		;VERIFICATION FAILED
	FIELDM MTA,1_5		;NO MULTI-CAST ACCEPTED
	FIELDM BRQ,1_6		;BLOCKING REQUESTED FLAG
	FIELDM RSV,1_7		;RESERVED BIT
ENDSTR

;These are the values in the IITYP field

	XP IIT.L2,1		;LEVEL 2 ROUTER
	XP IIT.L1,2		;LEVEL 1 ROUTER
	XP IIT.EN,3		;ENDNODE

	SUBTTL Definitions -- Miscellaneous


	SUBTTL  Network management tables
	SUBTTL  Network management tables -- Executor counters

;Generate the executor counter table
	XSWAPCD
EXCTAB:

COUNTER(^D900,^D8,<MOVE T1,RTRCAP>,<SETZM RTRCAP>,,<Aged packet loss>)
COUNTER(^D901,^D16,<MOVE T1,RTRCNU>,<SETZM RTRCNU>,,<
	Node unreachable packet loss>)
COUNTER(^D902,^D8,<MOVE T1,RTRCNO>,<SETZM RTRCNO>,,<
	Node out-of-range packet loss>)
COUNTER(^D903,^D8,<MOVE T1,RTRCOP>,<SETZM RTRCOP>,,<Oversized packet loss>)
COUNTER(^D910,^D8,<MOVE T1,RTRCPF>,<SETZM RTRCPF>,,<Packet format error>)
COUNTER(^D920,^D8,<MOVE T1,RTRCPR>,<SETZM RTRCPR>,,<
	Partial routing update loss>)
COUNTER(^D930,^D8,<MOVE T1,RTRCVR>,<SETZM RTRCVR>,,<Verification reject>)

EXCTBL==.-EXCTAB

	SUBTTL  Network management tables -- Circuit counters

CICTAB:			;Circuit counters maintained by the circuit

COUNTER (^D0,^D16,<CALL [SAVEAC <T2>
			 LOAD T2,RCSLZ,(RC)	; Time stamp of last zeroed
			 CALL DNGTIM		; Current time
			 SUB T1,T2		; Compute milliseconds
			 IDIVI T1,TIMBAS	;  and return as seconds
			 RET ]>,<
		   CALL [CALL DNGTIM
			 STOR T1,RCSLZ,(RC)
			 RET ]>,,<Seconds since zeroed>)

COUNTER(^D800,^D32,<LOAD T1,RCCAP,(RC)>,<SETZRO RCCAP,(RC)>,,<
	Terminating packets received>)
COUNTER(^D801,^D32,<LOAD T1,RCCDP,(RC)>,<SETZRO RCCDP,(RC)>,,<
	Originating packets sent>)
COUNTER(^D802,^D16,<LOAD T1,RCCAL,(RC)>,<SETZRO RCCAL,(RC)>,,<
	Terminating congestion loss>)
COUNTER(^D810,^D32,<LOAD T1,RCCTR,(RC)>,<SETZRO RCCTR,(RC)>,,<
	Transit packets received>)
COUNTER(^D811,^D32,<LOAD T1,RCCTS,(RC)>,<SETZRO RCCTS,(RC)>,,<
	Transit packets sent>)
COUNTER(^D812,^D16,<LOAD T1,RCCTL,(RC)>,<SETZRO RCCTL,(RC)>,,<
	Transit congestion loss>)
COUNTER(^D820,^D8,<LOAD T1,RCCCD,(RC)>,<SETZRO RCCCD,(RC)>,,<Circuit down>)
COUNTER(^D821,^D8,<LOAD T1,RCCIF,(RC)>,<SETZRO RCCIF,(RC)>,,<
	Initialization failure>)
COUNTER (^D2500,^D16,<LOAD T1,RCBSX,(RC)>,<SETZRO RCBSX,(RC)>,,<
	Transmit packets discarded-blocksize exceeded>)

CICTBL==.-CICTAB

	SUBTTL  Network management tables -- Node parameters

NOSET==PANST	;From BEGSTR in D36PAR
NOCLR==PANCL
BEX==PABEX	;Buffer expected

NDPTAB:
PARAMETER(0,<NOSET!NOCLR>,,,,,<CALL NMXRNS>,,<Node state>)
PARAMETER(^D810,<NOSET!NOCLR>,,,,<TRN>,< CALL [SKIPN AJ
						RNMXND
					       LOAD T2,AJNTY,(AJ)
					       RNMXOK ]>,<TRN>,<
	Adjacency type>)
PARAMETER(^D820,<NOSET!NOCLR>,,,,<TRN>,< CALL [SKIPN P2
						RNMXND
					       LOAD T2,RNCST,(P2)
					       RNMXOK ]>,<TRN>,<Cost>)
PARAMETER(^D821,<NOSET!NOCLR>,,,,<TRN>,< CALL [SKIPN P2
						RNMXND
					       LOAD T2,RNHOP,(P2)
					       RNMXOK ]>,<TRN>,<Hops>)
PARAMETER(^D822,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,RCLID,(RC)>,<TRN>,<Circuit>)
PARAMETER(^D830,<NOSET!NOCLR>,,,,<TRN>,< CALL [SKIPN AJ
						RNMXND
					       MOVE T2,RTRNTY
					       CAIE T2,RNT.NR
					       IFSKP.
						 LOAD T2,NFEID,(P1)
						 MOVE T1,T2
						 LSH T1,-^D10
						 CAME T1,RTRHOM
						 IFSKP.
						   TXZ T2,RN%ARE
						   ADD T2,RTRNRV
						   TMNN RNCCH,(T2)
						   IFSKP.
						     LOAD T2,NFEID,(P1)
						     RNMXOK
						   ENDIF.
						 ENDIF.
					       ENDIF.
					       LOAD T2,AJADR,(AJ)
					       RNMXOK ]>,<TRN>,<
	Next node>)

;Note:  The following parmameter is used for the NODE JSYS function .NDGNT
;	only.  Is is NEVER used by NTMAN and is not part of the specified
;	DECnet architecture.

PARAMETER(^D2503,<NOSET!NOCLR>,,,,,<CALL NMXRNS>,,<Node state-I DECnet only>)

NDPTBL==.-NDPTAB

	SUBTTL  Network management tables -- Executor parameters

EXPTAB:

PARAMETER(0,,,,<DS.ON>,<CALL NMXSES>,<CALL NMXRNS>,<CALL NMXSES>,<
	Executor state>)
PARAMETER(^D900,<NOSET!NOCLR>,,,,,<CALL [ MOVE T1,RTRVER
					  STOR T1,VNVER,+T2
					  MOVE T1,RTRECO
					  STOR T1,VNECO,+T2
					  MOVE T1,RTRCUS
					  STOR T1,VNUCO,+T2
					  RNMXOK]>,<TRN>,<Routing version>)
PARAMETER(^D901,,,,<RNT.L1>,<TRN>,<MOVE T2,RTRNTY>,<TRN>,<Router type>)
PARAMETER(^D910,,^D65535*TIMBAS,,<%RTTM1>,<MOVEM T2,RTRTM1>,<MOVE T2,RTRTM1>,<MOVEM T2,RTRTM1>,<Maximum update interval timer>)
PARAMETER(^D912,,^D65535*TIMBAS,,<%RTBT1>,<MOVEM T2,RTRBT1>,<MOVE T2,RTRBT1>,<MOVEM T2,RTRBT1>,<Max broadcast update int. timer>)
PARAMETER(^D920,<NOSET!NOCLR>,,,,,<MOVE T2,RTRMXN>,,<Maximum address>)
PARAMETER(^D921,,<^D20>,<^D1>,<^D20>,<MOVEM T2,RTRMCN>,<MOVE T2,RTRMCN>,<
	MOVEM T2,RTRMCN>,<Maximum number of circuits>)
PARAMETER(^D922,,<^D1022>,<1>,<%RTMXC>,<MOVEM T2,RTRMXC>,<MOVE T2,RTRMXC>,<
	MOVEM T2,RTRMXC>,<Maximum circuit cost>)
PARAMETER(^D923,,<^D30>,<1>,<%RTMXH>,<MOVEM T2,RTRMXH>,<MOVE T2,RTRMXH>,<
	MOVEM T2,RTRMXH>,<Maximum hops>)
PARAMETER(^D924,,<^D30>,<1>,<%RTMXV>,<MOVEM T2,RTRMXV>,<MOVE T2,RTRMXV>,<
	MOVEM T2,RTRMXV>,<Maximum visits>)
PARAMETER(^D926,,<^D1022>,<0>,<%RTBEA>,<MOVEM T2,RTNBEA>,<MOVE T2,RTNBEA>,<
	MOVEM T2,RTNBEA>,<Maximum broadcast endnode adjacencies>)
PARAMETER(^D927,,<^D64>,<0>,<%RTBRA>,<MOVEM T2,RTNBRA>,<MOVE T2,RTNBRA>,<
	MOVEM T2,RTNBRA>,<Maximum broadcast router adjacencies>)
PARAMETER(^D930,<NOSET!NOCLR>,,,,,<LOAD T2,IBMXB,+IBBLK>,,<Maximum buffers>)
PARAMETER(^D931,<NOSET!NOCLR>,,,,,<MOVE T2,RTRBSZ>,,<Maximum buffer size>)
PARAMETER(^D932,<NOSET!NOCLR>,,,,,<MOVE T2,RTRBSZ>,,<Segment buffer size>)

;See note above
PARAMETER(^D2503,<NOSET!NOCLR>,,,,,<CALL NMXRNS>,,<Node state-I DECnet only>)

EXPTBL==.-EXPTAB

	SUBTTL  Network management tables -- Circuit parameters
CCPTAB:

PARAMETER(0,,,,<NCK.OF>,<CALL NMXSCS>,<CALL NMXRCS>,<CALL NMXSCS>,<
	Circuit state>)

PARAMETER(^D800,<NOSET!NOCLR!BEX>,,,,,<CALL NMXRAL>,,<Adjacent node(s)>)
PARAMETER(^D801,<NOSET!NOCLR>,,,,,<CALL [ LOAD T2,RCDSL,(RC)
					  HRLZ T2,T2
					  CALL CVTSTN
					  RNMXOK]>,,<Desiginated router>)
PARAMETER(^D810,<NOSET!NOCLR>,,,,,<CALL NMXQPS>,,<Block size>)
PARAMETER(^D900,,<^D25>,<1>,<1>,<CALL [	STOR T2,RCCST,(RC)
					CALL SETRCX
					RNMXOK]>,<
				 LOAD T2,RCCST,(RC)>,<
				 CALL [	STOR T2,RCCST,(RC)
					CALL SETRCX
					RNMXOK]>,<Cost for this circuit>)
PARAMETER(^D901,,<^D255>,<0>,<%RTMXR>,<STOR T2,RCMXR,(RC)>,<
	LOAD T2,RCMXR,(RC)>,<
	STOR T2,RCMXR,(RC)>,<Maximum routers on this circuit>)
PARAMETER(^D902,,<^D127>,<0>,<0>,<MOVEM T2,RTRPRI>,<MOVE T2,RTRPRI>,<
	MOVEM T2,RTRPRI>,<Priority to be designated router>)
PARAMETER(^D906,,<^D8191*TIMBAS>,<1*TIMBAS>,<%RTTM3>,<STOR T2,RCTM3,(RC)>,<LOAD T2,RCTM3,(RC)>,<STOR T2,RCTM3,(RC)>,<Hello timer>)
PARAMETER(^D907,<NOSET!NOCLR>,,,,,<CALL NMXQPT>,,<
	Listen timer>)

CCPTBL==.-CCPTAB

	XRESCD

	SUBTTL	Initialization - Compute Core Requirements

;RTRCCR computes ROUTER's core requirements.
;Call:
;	PUSHJ	P,RTRCCR
;Returns:
;	T1/ Size of ROUTER core

IFN FTOPS10,<
RTRCCR::LOAD T1,IBMXA,+IBBLK	;GET MAXIMUM NODE ADDRESS
	ADDI T1,1		;PLUSE ONE FOR NODE 0
	ASH T1,1		;COMPUTE SIZE OF OUR ROUTING VECTOR
	RET			;AND RETURN
>; END IFN FTOPS10
	SUBTTL RTRINI - Initialize Router User

;RTRINI - Set up a router user
;
; Call: 
;	with nothing
;
; Return: 
;	RET			;ON RESOURCE FAILURE
;	RETSKP			;ON SUCCESS
;
; Uses: T1-T3
;
;This routine gets called at system initialization.  It sets up the memory
;for the routing vectors and the node type vector.  Must be called before
;DECnet is turned on.


	INTERN RTRINI
	XSWAPCD

RTRINI:	TRACE RTR,<Initializing Router>

;Get the Router parameters defined in SETSPD's config file

	XMOVEI T2,IBBLK		; Get pointer to initialization block
	LOAD T1,IBRTR,(T2)	; Get Router type desired
	MOVEM T1,RTRNTY		; Save as our node type
	SETZM ENFLG		; Assume routing IV
	CAIN T1,RNT.NR		; Are we an endnode today?
	 SETOM ENFLG		; Yes, then non-routing IV
IFN FTOPS10,<
	MOVX T1,ST%END		; Get configuration status endnode flag
	SKIPE ENFLG		; Are we running as an endnode today?
	IORM T1,CNFST2##	; Yes, set flag
>; END IFN FTOPS10
	LOAD T1,IBADR,(T2)	; Get 16 bit node address
	MOVEM T1,RTRNAD
	TXZ T1,RN%ARE		; Clear the area number
	MOVEM T1,RTRADR		;  and save the node number
	MOVE T1,RTRNAD		; Now get the area number
	TXZ T1,RN%NOD		;  from the address
	LSH T1,-^D10		; Right justify
	MOVEM T1,RTRHOM
	LOAD T1,IBBSZ,(T2)	; Get blocksize to use
	CAMGE T1,RTRBSZ		; Compare with default (576)
	 MOVEM T1,RTRBSZ	; If smaller use that, else keep default
	LOAD T1,IBMXA,(T2)	; Get the maximum node address
	MOVEM T1,RTRMXN
	CAMGE T1,RTRMX3		; Is it less than maximum for Phase III nodes?
	 MOVEM T1,RTRMX3	; Yes, use it then

;Here to allocate the "normal" and "scratch" routing vectors.

	MOVE T1,RTRMXN		;GET THE MAXIMUM NODE NUMBER
	ADDI T1,1		;Add one for node 0 (nearest area router)
	ASH T1,1		;MAKE IT TWICE AS LONG (HALF FOR REACH VECTOR
				; HALF FOR OUTPUT ADJACENCY VECTOR)
IFN FTOPS20,<
	CALL DNGWDZ		;GET THAT MANY WORDS
	 RET			;COULD NOT INITIALIZE
>; END IFN FTOPS20
IFN FTOPS10,<
	EXCH T1,DCNVFF##	;GET ADDRESS OF NEXT FREE CORE
	ADDM T1,DCNVFF##	;UPDATE PAST ROUTING VECTOR
>; END IFN FTOPS10
	MOVEM T1,RTRNRV		;SAVE POINTER TO NORMAL ROUTING VECTOR
	MOVE T2,RTRMXN		;GET OUR CURRENT MAXIMUM NODES
	ADDI T2,1		;START THE RTRNOV AT ONE ALSO
	MOVEM T2,RTROFS		;SAVE AS OFFSET FROM START OF NRV AND SRV
				; NOTE THAT MXN CANNOT GET LARGER!
	ADD T1,T2		;CALCULATE START OF NORMAL OUTPUT VECTOR
	MOVEM T1,RTRNOV		;SAVE IT

	MOVE T2,RTRNRV		;PREPARE TO CALC END OF SMEAR
	ADD T2,RTRMXN		;ADD NUMBER OF ENTRIES TO SPRAY
	MOVE T1,RTRNRV		;POINT TO THE HEAD OF THE NORMAL VECTOR
	MOVX T3,RNHOP!RNCST	;SMEAR WITH MAXIMUM COST AND HOPS
	CALL DNSWDS		;SPRAY THE BLOCK WITH MAXS

	MOVE T1,RTRMXN		;GET THE MAXIMUM NODE NUMBER
	ADDI T1,1		;Add one for node 0 (nearest area router)
	IDIVI T1,^D36		;Compute size of routing vector bitmap
	SKIPE T2		;Round up
	ADDI T1,1		;...
	CALL DNGWDZ		;Allocate memory for routing vector bitmap
	  RET			;Return if allocation error
	MOVEM T1,RTRNVB		;Save address

;Get and initialize an EC (Event Communication) block

	MOVX T1,EV.GEC		;Function code "get EC block"
	CALL NMXEVT
	  RET			;  -failed to get memory for block
	MOVEM T1,RTRECP		;Save pointer
	MOVX T2,^D8		;ROUTER may queue 8 event blocks on queue
	STOR T2,ECMAX,(T1)

IFN FTOPS10,<
	CALL D36LIN##		;Initialize DTEs and KDPs
>; End IFN TOPS10
	CALL DNDINI		;Initialize DNADLL
	RETSKP			;RETURN NICELY

	SUBTTL RTRON - Turn Router (and DECnet) On

;RTRON - Turn Router (and DECnet) On
;
; Call:
;	With nothing special
;
; Return:
;	RET			;ON FAILURE (DECNET IS ALREADY ON, NO
;				;LOCAL NODE NUMBER DEFINED, ETC.)
;	RETSKP			;WITH DECNET TURNED ON
;
; Uses: T1-T4
;
;Note that RTRINI must be called to set up the routing vectors for
;Router before DECnet is turned on.

	INTERN RTRON

RTRON:	SAVEAC <RC,AJ>
	MOVE T1,DCNSTA		;Get DECnet state
	CAIE T1,DS.ON		;Is it on?
	 SKIPN T1,RTRADR	;AND DO WE HAVE AN ADDRESS FOR ROUTER?
	  RET			;NO, CANNOT TURN DECNET ON YET

	DPB T1,[POINTR (T2,RN%NOD)] ; Generate our node address in
	MOVE T1,RTRHOM		;  Ethernet string format
	DPB T1,[POINTR (T2,RN%ARE)]
	LSHC T1,^D28		;
	DPB T1,[POINT 8,T2,15]
	MOVEM T2,RTRLOO		; Low order in string format

	MOVE T1,RTRADR
	ADD T1,RTRNRV		;FIND THE ENTRY IN THE NORMAL REACH VECTOR
	D36OFF			;TURN ON INTERLOCK

	SETONE <RNLCL,RNRCH,RNCCH>,(T1) ;LOCAL AND REACHABLE and in cache
	SETZRO <RNCST,RNHOP>,(T1) ;ZERO THE HOPS AND COST FOR THIS NODE

	MOVE T2,[XCDSEC,,NSPRTR] ;GET NSP'S ENTRY VECTOR
	ADD T1,RTROFS		;POINT TO OUTPUT-CIRCUIT VECTOR ENTRY
	MOVEM T2,(T1)		;SAVE THE ENTRY VECTOR
	D36ON			;TURN OFF INTERLOCK

;And turn DECnet on!

	MOVX T1,DS.ON		;GET THE ON STATE
	MOVEM T1,DCNSTA		;SET IT
	SETZM RTRLMG		;Reset rounting message offset

;Now go through all the circuits, initializing each one.

	SKIPN RC,RTRCBQ		;PICK UP FIRST CIRCUIT BLOCK
	RETSKP			;NONE THERE, RETURN SUCCESS
RTRON1:	CALL R2KINI		;INITIALIZE THIS CIRCUIT
	 TRN
	LOAD RC,RCNXT,(RC)	;Step to next circuit, if any,
	JUMPN RC,RTRON1		; and initialize
	RETSKP			;NO MORE, JUST RETURN SUCCESS

	SUBTTL RTROFF - Turn Router (and DECnet) Off

;RTROFF - Turn Router (and DECnet) Off
;
; Call:
;	With nothing special
;
; Return:
;	RETSKP			;ALWAYS
;
; Uses: T1-T4

RTROFF:	SAVEAC <RC,AJ>

;Now go through all the circuits, halting each one.

	SKIPA RC,RTRCBQ		;GET THE FIRST CIRCUIT BLOCK
RTROF1:	LOAD RC,RCNXT,(RC)	;STEP THROUGH TO NEXT ONE
	JUMPE RC,RTROF2		;NONE THERE, RETURN SUCCESS
	CALL RTIDWN
	CALL R2KCLS
	JRST RTROF1		; 

RTROF2:	MOVX T1,DS.OFF		;SET THE STATE OF DECNET
	MOVEM T1,DCNSTA		; TO "OFF"
	RETSKP			;NO MORE, JUST RETURN SUCCESS
	SUBTTL RTRSEC - Once a second processing for Router

;RTRSEC - Once a second processing
;
; Call:
;	 With nothing
;
;Return:
;	RET			;ALWAYS
;
; Uses: T1
;
;This is the clock level routine for Router. Once a second this
;routine gets executed. If we are running TOPS-10 SMP, we must make
;sure that this is run on the BOOT CPU. The routine simply calls RTRRCR
;(to recompute the routing) if needed and calls RTRUPD to see if
;routing messages need to be sent out on any or all circuits. Next, we
;call the hello processor to check hello and listener timers for each
;circuit. Finally, it checks the resend queue, and if there are any
;messages on it, it forwards them.

	INTERN RTRSEC
	XRESCD

RTRSEC:	MOVE T1,DCNSTA		;Get DECnet state
	CAIE T1,DS.ON		;Is it on?
	 RET			;NO, NOTHING TO DO
IFN FTOPS10,<
	XCT .CPSK0		;SKIP IF BOOT CPU
	RET			;NO, SHOULDN'T BE HERE, BUT JUST RETURN
>				;END IFN FTOPS10

	CALL DNDSEC		;Poll DNADLL each second

;	SKIPL RTRLOK		;CHECK TO SEE IF ANYONE HAS INTERLOCK
;	JRST [SETOM RTRSSV	;COULDN'T GET IT, JUST RETURN
;	      RET]		; WITH SERVICE NEEDED
;	SETZM RTRSSV		;WE HAVE PROVIDED SERVICE

;Note that we did not get the interlock here; we only tested it.
;The interlock is just to keep this clock level code from changing
;the database while any process level code is running.  We may
;get interrupted now by interrupt code, but this won't affect us
;as our critical sections are surronded by D36ON/D36OFF.

	SAVEAC <P1,P2,MS,RC,AJ,Q1,Q2,MB> ;SAVE SOME NON-SMASHABLE ACS
	CALL RTRTMR		;CALL THE TIMER PROCESSOR, WHICH MAY TURN
				; ON RTRRCF
	SKIPE RTRRCF		;IS THE RECOMPUTE ROUTING FLAG ON?
	CALL RTRRCR		; YES, GO RECOMPUTE THE ROUTING
	CALL RTRUPD		;Now go through the update process
;	SETOM RTRLKO		;CLEAR OWNER OF INTERLOCK
IFN FTRTST,<
	SKIPE T1,TSTFLG
	 CALL RTRTST
	SETZM TSTFLG
>
	SKIPN ENFLG		;Endnode?
	 CALL SELDSR		;No, select a designated router
	SKIPGE ENFLG		;Endnode?
	 CALL CKECTO		;Yes, check the cache timer
	RET			; AND RETURN
	SUBTTL RTRJIF - Once a jiffy processing for Router

;RTRJIF - Once a jiffy processing
;
; Call: 
;	With nothing
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1
;
;This routine's only purpose is to service kontroller requests that
;CALKON queues when the request is for a device on the wrong CPU.  Since
;this is a TOPS-10 SMP only problem, it does nothing for TOPS-20.

	INTERN RTRJIF

	XRENT RTRJIF

IFN FTOPS20,<RET>		;Nothing to do for TOPS20

IFN FTOPS10,<
IFN FTMP,<
;	SKIPL RTRLOK		;TEST THE ROUTER INTERLOCK
;	RET			;SOMEONE HAS IT, WE'LL TRY AGAIN LATER

	SAVEAC <RC,AJ,MB>	;SAVE SOME ACS TO USE

	SKIPA RC,RTRCBQ		;START WITH THE FIRST CIRCUIT BLOCK
RTRJI1:	LOAD RC,RCNXT,(RC)	;GET PTR TO NEXT CIRCUIT BLOCK
	JUMPE RC,RTN		;NO MORE, JUST RETURN

	LOAD T1,LIKON,+RC.LID(RC) ;GET THE POINTER TO THE CIRCUIT BLOCK
	CAME T1,.CPCPN		;ARE WE RUNNING ON THE CPU FOR THIS CIRCUIT?
	JRST RTRJI1		;NO, CHECK OUT THE NEXT BLOCK

;Check the circuit's resend queue for any requests that we can queue
;for it now that we are running on the correct CPU.

RTRJI2:				;CHECK IF OTHER CPU HAD KONTROLLER TASKS
				; QUEUED UP.
	JE RCCLS,(RC),RTRJI3	;IF NO HALT REQUESTED, SKIP THIS CODE
	SETZRO RCCLS,(RC)	;CLEAR BIT
	MOVEI T1,DF.CLS		;KONTROLLER FUNCTION HALT.
	MOVE T2,RC		;POINT TO CIRCUIT BLOCK
	CALL CALKON		;AND TRY TO DO IT.
	  JRST RTIDWN		;IF IT FAILS, DECLARE THE CIRCUIT DEAD.
RTRJI3:	JE RCOPN,(RC),RTRJI4	;IF NO INITIALIZE REQUESTED, SKIP THIS CODE
	SETZRO RCOPN,(RC)	;CLEAR BIT
	MOVEI T1,DF.OPN		;THE OTHER CPU DID REQUEST THIS.
	MOVE T2,RC		;SET UP POINTER TO CIRCUIT BLOCK
	CALL CALKON		;INITIALIZE THE LINE
	  JRST RTIDWN		;AND DECLARE THE CIRCUIT DOWN.
RTRJI4:	D36OFF			;TURN OFF INTERRUPTS WHILE PLAYING WITH QUEUES
	DEQUE MB,RC.JSQ(RC),MB.NXT,RTRJI6 ;DEQUEUE ANYTHING THAT'S THERE
	D36ON			;TURN INTERRUPTS BACK ON
				;WE KNOW THAT BY VIRTUE OF GETTING THE MESSAGE
				;BLOCK FROM RC.JSQ, RC IS SET UP CORRECTLY.
	CALL CALQMB		;SEND THE BLOCK OUT.
	JRST RTRJI2		;SEE IF ANYTHING ELSE ON CIRCUIT'S Q
RTRJI6:	D36ON			;TURN THE INTERRUPTS BACK ON
	JRST RTRJI1		; AND CHECK THE NEXT CIRCUIT'S QUEUE
>;End of IFN FTMP
	RET
>;End of IFN FTOPS10

	SUBTTL RTRLCK - Get the RTR interlock
REPEAT 0,<
;RTRLCK - Grab the RTR interlock
;
; Call:
; 	With nothing special
;
; Return:
;	RET			;ALWAYS WITH INTERLOCK
;
; Uses: Nothing
;
;Note: This interlock routine is only called by RTRXMT.  RTRXMT, which
;is NSP's entry point, will never be called by more than one CPU due to
;the NSP interlock. If the NSP interlock changes, we may have to modify
;this assumption.  The Router interlock is used to keep Router's clock
;level from modifying the database at clock level (Note that the
;database is only modified at clock level), while someone at process
;level is using it.  Route-through happens at interrupt level, so we
;don't have to worry.  Our only process level caller is NSP, who always
;has his "global interlock".

RTRLCK:	AOSE RTRLOK		;GRAB THE INTERLOCK
	RET			;SOMEONE HAS IT (PROBABLY INTERRUPT LEVEL)
				; WE DON'T CARE AS LONG AS CLOCK LEVEL
				; KNOWS THAT IT CAN'T RUN
	APRID RTRLKO		;SET THE OWNER OF THE INTERLOCK
	ADJSP P,-1		;WE'LL CALL THE CALLER
	PUSHJ P,@1(P)		; SO WE DON'T NEED HIM ON STACK
	 TRNA			;NON-SKIP RETURN
	AOS -1(P)		;SKIP RETURN

	SETOM RTRLKO		;CLEAR THE OWNER OF THE INTERLOCK
	SETOM RTRLOK		;GIVE UP THE LOCK
	SKIPE RTRSSV		;DID WE GET A REQUEST FOR SERVICE?
	CALLRET RTRSEC		;YES, GIVE IT
	RET			;OTHERWISE, JUST RETURN
>
IFN FTRTST,<
RTRTST:	SAVEAC P1
	MOVEI P1,TSTBLK		; Test data block
	STOR T1,TRTND,(P1)	; Node to loop with
	MOVX T1,^D300		; Length of data segment
	CALL DNGMSG		; Get that many bytes
	 RET			; Resource failure, tough luck
	MOVE MB,T1		; Set up MB with message block pointer
	XMOVEI T1,UD.MSD(MB)	; Initialize the user data segment
	CALL DNPINI
	LOAD T1,TRTSN		; Get serial number
	CALL DNP2BY		; Place serial number in message
	MOVX T3,^D298		; Get the count for loop
RTRTS2:	MOVX T1,HEL.LO		; Word that goes in hello messsage
	CALL DNP1BY		; Put one in
	SOJG T3,RTRTS2		;  and continue till we have enough
	MOVE T1,RTRADR
	DPB T1,[POINTR (T2,RN%NOD)]
	MOVE T1,RTRHOM
	DPB T1,[POINTR (T2,RN%ARE)]
	STOR T2,MBSRC,(MB)
	SETZ T1,
	MOVX T2,LD.ETH		; Get the device type for ethernet
	STOR T2,LIDEV,+T1	; Save it
	CALL RTRGCB		; Get the circuit block in RC
	 CALLRET FREMSG
	LOAD T1,TRTND,(P1)	; Get number of node to test with
	STOR T1,MBDST,(MB)	;	
	CALL RTRGAJ		; Get the correct adjacency in AJ
	 CALLRET FREMSG
	STOR AJ,RMOAP,(MB)	; Save for RTRFWD
	CALL TSTTIM
	STOR T1,TRTAF,(P1)	; Save as time at RTRFWD
	SETONE RMTST,(MB)	; Indicate this is a test message
	SETZ T1,		; No flags
	CALLRET RTRXMT		; Try to send it
>

	SUBTTL RTRXMT - NSP's entry into Router

;RTRXMT - Routine to forward messages from NSP
;
; Call:
;	MB/ Ptr to Message Block
;	T1/ RQR flag (RT%RQR)
;	    ODN flag (RT%ODN)
;	    TRY flag (RT%TRY)
; Public portion of message block must be filled with destination
; and source nodes and output circuit.
;
; Returns:
;	RTN			;ALWAYS
;
;This is the only way which an NSP calls router (except for
;initialization).  The ODN flag in T1 specifies whether or not NSP
;really wants message back (see FREMSG).  The RQR flag specifies
;whether or not NSP is requesting this message be returned.

	INTERN RTRXMT
	XRESCD

RTRXMT:	MOVE T2,DCNSTA		;Get DECnet state
	CAIE T2,DS.ON		;Is it on?
	 CALLRET FREMSG		;NO, PUNT THE MESSAGE CORRECTLY AND LEAVE

	SAVEAC <MB,MS,RC,AJ>	;SAVE A FEW FOR NSP
;	CALL RTRLCK		;GET INTERLOCK

	MOVX T2,RMRQR		;GET RETURN REQUEST BIT FOR MESSAGE BLK
	ANDCAM T2,RM.RQR(MB)	;CLEAR FLAG, ASSUMING NOT INTERESTED
	TXNE T1,RT%RQR		;DOES HE WANT IT BACK?
	IORM T2,RM.RQR(MB)	;YES, SET BIT IN THE MESSAGE BLK

	MOVX T2,RMODN		;GET "NSP WANTS MSG BACK" BIT
	ANDCAM T2,RM.ODN(MB)	;CLEAR FLAG, ASSUMING CALLER NOT INTERESTED
	TXNE T1,RT%ODN		;DOES HE WANT OUTPUT DONE CALL?
	IORM T2,RM.ODN(MB)	;YES, SET FLAG FOR FREMSG AND R2NODN

	MOVX T2,RMTRY		;Get "TRYHARD" on NI bit
	ANDCAM T2,RM.TRY(MB)	;Assume not requesting us to try hard
	TXNE T1,RT%TRY		;Asked to "try hard"?
	IORM T2,RM.TRY(MB)	;Yes, set flag to clear destination from cache

	SETZRO RMICP,(MB)	;THERE'S NO INPUT CIRCUIT BLOCK POINTER
	SETZRO RMIAP,(MB)	; and no input adjacency
	SETZRO RMOAP,(MB)	;THERES NO OUTPUT ADJACENCY YET EITHER
	SETZRO MBVST,(MB)	;ZERO VISITS COUNT
	SETONE RMMB1,(MB)	;BIT THAT MUST BE ONE IN FIRST BYTE

	LOAD T1,MBSRC,(MB)	;GET SOURCE NODE ADDRESS
	LDB T2,[POINTR (T1,RN%ARE)] ;GET THE AREA NUMBER
	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	
	CAME T2,RTRHOM		;IN OUR AREA
	 JRST CKLOOP		;NOPE ,THEN FORGET ABOUT IT
	ADD T1,RTRNRV		;ADD OFFSET TO NORMAL ROUTING VECTOR
	TMNN RNLCL,(T1)		;IS THIS THE LOCAL NODE?
	BUG.(CHK,ROUBSN,ROUTER,SOFT,<Bad source node in message from NSP>,,<

Cause:	We have received a message from NSP to send.  However, the source node
	address is not that of the local Router.

Action:	Check in LLINKS or SCLINK to see how we got an invalid source node
	address.

>,FREMSG)

CKLOOP:	LOAD T1,MBCHN,(MB)	;GET CHANNEL NUMBER (circuit id)
	JUMPE T1,RTRFWD		;IF NO LOOP BACK CIRCUIT SPECIFIED BY
				;SESSION CONTROL, DON'T SET CIRCUIT PTR
	CALL RTRGCB		;YES, TRANSLATE INTO A CIRCUIT BLK PTR
	 BUG.(CHK,ROUILS,ROUTER,SOFT,<Illegal Circuit Specified in NSP msg>,,<

Cause:	There was a request to send a message on a particular circuit, however  
	the circuit has never been intialized by the routing layer.

>,FREMSG)
	STOR RC,RMOCP,(MB)	;Store it as output circuit
	LOAD AJ,RCAJQ,(RC)	;Get an adjacency pointer

;Note that this grabs the the first adjacency which is the only one for point
; to point, but not necessarily for broadcast

	STOR AJ,RMOAP,(MB)	;Save it for forward routine (this is enough
	JE RCBCT,(RC),RTRFWD	; unless circuit is broadcast)
CKLOO1:	LOAD T1,AJNTY,(AJ)	;Get adjacency type
	CAIN T1,ADJ.LN		; If a routing node
	IFSKP.
	  STOR AJ,RMOAP,(MB)	; Save it for forward routine
	  JRST RTRFWD		;  and then forward the message
	ENDIF.
	LOAD AJ,AJNXT,(AJ)	;  Else get next adjacency
	JUMPE AJ,FREMSG		;   none found then cannot forward the message
	JRST CKLOO1		;   check the one we got

	SUBTTL RTRFWD - Perform Routing of Message

;RTRFWD - Routine to do routing
;
; Call:
;	MB/ Ptr to Message Block
;
; Return:
;	RTN			;ALWAYS
;
; Uses T1-T3
;
;This routine takes an input message, checks its Router header and
;routes it to the correct destination using the "normal" routing
;vector.  The relavent fields in the public section of the Message
;Block must have been set up.

RTRFWD:	XMOVEI T1,RM.MSD(MB)	; Router's private MSD
	CALL DNPINI		; Initialize it, as this is where we will
				;  build the routing layer header
	LOAD T1,MBDST,(MB)	; Get the destination address
	LDB T2,[POINTR(T1,RN%ARE)] ; Extract the area number
	LDB T1,[POINTR(T1,RN%NOD)] ;  and the node number from it
	CAME T2,RTRHOM		; Is it in our area?
	 SETZ T1,		;  No, select the area router
	SKIPL T1		;LESS THAN 0
	 CAMLE T1,RTRMXN	;GREATER THAN THE MAX NODE ADDRESS?
	  JRST RTRFOR		;DEST IS OUT OF RANGE
	ADD T1,RTRNRV		; Add in the offset to routing vector
	OPSTR <SKIPE>,RMOAP,(MB) ; Have we selected an output adjacency?
	IFSKP.
	  TMNN RNLCL,(T1)	; No, is destination local?
	  IFSKP.
	    SETONE MBLCL,(MB)	; Yes, indicate that in message block
	    JRST RTRFD2		;  and skip ahead
	  ENDIF.
	ENDIF.
	SKIPN ENFLG		; Endnode?
	IFSKP.			; Yes
	  MOVE RC,RTRCBQ	; Get pointer to our only circuit
	  JUMPE RC,RTRFUR	; If no circuit can't do much
	  LOAD T2,RCSTA,(RC)	; If circuit is not running
	  CAIE T2,RCS.RN	;  cannot forward message
	   JRST RTRFUR
	  STOR RC,RMOCP,(MB)	; Save circuit block address
	  SETONE <RMMZ2,RMMZ3>,(MB) ; Set intra-ethernet and long format hdr
	  CALL RTRBEH		; Build the ethernet header
	  JRST RTR2RM		;  and try to send message
	ELSE.
	  TMNE RNRCH,(T1)	; Is destination reachable?
	  IFSKP.
	    SETONE MBUNR,(MB)	; No, indicate that,
	    SETZRO RMOAP,(MB)	;  clear output adjacency,
	    JRST RTRPHR		;  and skip further checking
	  ENDIF.
	ENDIF.
	OPSTR <SKIPE T2,>,RMOAP,(MB) ;Compute an adjacency if we don't have one
	IFSKP.
	  ADD T1,RTROFS		; Index into the output adjacency vector
	  MOVE T2,(T1)		; Get the output adjacency
	  STOR T2,RMOAP,(MB)	;  and use it as the adjacency to forward to
	ENDIF.
	LOAD T2,AJSTA,(T2)	; Get state of adjacency
	CAIE T2,ADJ.UP		; Is it up?
	 JRST FREMSG		; No, toss the message

;Now check for over aged packet

RTRFD2:	JE RMICP,(MB),RTRFD3	; If from our NSP then visit count is zero
	LOAD T1,RMFST,(MB)	;GET THE FIRST BYTE OF MESSAGE
	OPSTRM <AOS T2,>,MBVST,(MB) ; AND UPDATE AND GET VISITS FIELD
	MOVE T3,RTRMXV		;THIS IS THE MAXIMUM VISITS COUNT ALLOWED
	TXNE T1,RM%RTS		;IS THIS GOING TO BE COMING BACK?
	 ASH T3,2		;YES, GIVE IT TWICE AS MANY HOPS
	CAMG T2,T3		;HAS IT AGED TOO MUCH?
	 JRST RTRFD3		;NO, PUT HEADER ON MSG

;Here to report a aged packet loss event.

	SETZ T2,		;THIS EVENT HAS NO ENTITY ASSOCIATED WITH IT
	SETZ T3,		;Default address to nothing
	SKIPE AJ
	 LOAD T3,AJADR,(AJ)	;Get address of last router in path
	EVENT RE.APL,<Aged packet loss event>,MB
	AOS RTRCAP		;INCREMENT THE AGED PACKET COUNTER
	CALLRET FREMSG		;FREE THE MESSAGE AND RETURN
;Now decide if destination is point-point or broadcast and build the
;appropriate routing message header.

RTRFD3:	TMNE MBLCL,(MB)		;IS THE DESTINATION OUR LOCAL NSP?
	 JRST RTRPHR		;YES, THEN WE CAN SKIP THE REST OF THIS
IFN FTRTST,<
	LOAD T1,MBSRC,(MB)	;Get source address
	TMNE RMTST,(MB)		;Is this a test message
	 JRST [STOR T1,MBDST,(MB) ;Then we want it back
	       SETZRO MBVST,(MB) ;Reset the visits
	       JRST .+1]
>
	LOAD AJ,RMOAP,(MB)	; OUTPUT ADJACENCY
	LOAD RC,AJCBP,(AJ)	; OUTPUT CIRCUIT BLOCK
	JN RCBCT,(RC),RTRFNI	; Jump if broadcast circuit?

;Build a short data packet header for packets going out on point-to-point

	SETZRO <RMMZ2,RMMZ3>,(MB) ;Short format header - Not intra-NI
	LOAD T1,RMFST,(MB)	;GET THE FIRST BYTE OF MESSAGE
	CALL DNP1BY		;PUT IT IN MESSAGE HEADER AREA
	LOAD T1,MBDST,(MB)	;GET THE DESTINATION NODE ADDRESS
	LDB T2,[POINTR(T1,RN%ARE)] ;GET THE AREA
	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	SKIPN T2		;HAVE ONE?
	 MOVE T2,RTRHOM		;GET OURS THEN
	TMNE AJPH4,(AJ)		;Is "next hop" Phase IV?
	 DPB T2,[POINTR(T1,RN%ARE)] ; Yes, then we need an area
	CALL DNP2BY		;PUT DESTINATION NODE ADDRESS INTO MESSAGE
	LOAD T1,MBSRC,(MB)	;GET THE SOURCE NODE ADDRESS
	LDB T2,[POINTR(T1,RN%ARE)] ;GET THE AREA
	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	SKIPN T2		;Have an area?
	 MOVE T2,RTRHOM		;NO, USE OURS THEN
	TMNE AJPH4,(AJ)		;Is the next hop Phase IV?
	IFSKP.	
	  CAME T2,RTRHOM	;No, is this from a foreign area?
	   CALLRET RTRFUR	;Yes, treat as unreachable because we cannot
				; forward a P4 packet to an adjacent P3 node
	ELSE.
	  DPB T2,[POINTR(T1,RN%ARE)] ;Yes, P4 nodes get an area number
	ENDIF.
	CALL DNP2BY		;PUT SOURCE NODE ADDRESS INTO MESSAGE
	LOAD T1,MBVST,(MB)	;GET VISITS BYTE
	CALL DNP1BY		;AND PUT THAT IN, TOO
	JRST RTRPHR		;DO THE COMMON HEADER STUFF
;Here when the output circuit is an Ethernet

RTRFNI:	SETONE RMMZ3,(MB)	;****INDICATES LONG FORMAT TRANSPORT HEADER**
	LOAD T2,RMIAP,(MB)	;GET THE INPUT ADJACENCY POINTER
	JUMPE T2,RTRFN1		;MESSAGE IS FROM LOCAL NSP

	LOAD T3,AJNTY,(T2)	;GET THE NODE TYPE
	LOAD T4,AJNTY,(AJ)	;GET THE NODE TYPE FOR THE OUTPUT ADJACENCY
	CAIN T3,ADJ.LN		;IT IS AN ENDNODE?
	CAME T3,T4		;YES, ARE THEY BOTH?
	 JRST RTRFN2		;NO, THEN SKIP THIS

;They are both endnodes, see if the input/output circuits are the same NI

	LOAD T2,AJCBP,(T2)	;GET THE INPUT CIRCUIT BLOCK POINTER
	LOAD T2,RCLID,(T2)	;GET THE LINE ID
	LOAD T4,LIDEV,+T2	;GET THE DEVICE TYPE
	CAXE T4,LD.ETH		;IS IT AN ETHERNET?
	 JRST RTRFN2		;NO, THEN NO INTRA-ETHERNET
;	LOAD T2,LIUNI,+T2	;UNIT NUMBER
	LOAD T3,RCLID,(RC)	;AND THE ONE FOR OUTPUT
;	LOAD T3,LIUNI,+T3	;GET THE UNIT NUMBER
	CAME T2,T3		;ARE THEY THE SAME NI?
	 TRNA			;NOT NI OR NOT SAME ONE
RTRFN1:	SETONE RMMZ2,(MB)	;YES, THEN SET THE INTRA-NI BIT

RTRFN2:	CALL RTRBEH		;Now build the Ethernet header


RTRPHR:	CALL DNRPOS		;GET CURRENT POSITION IN THE BLOCK
	STOR T1,RMMK1,(MB)	;AND SET MARK IN BLOCK
	
	TMNE MBLCL,(MB)		;FOR THE LOCAL NSP?
	 JRST RTRFLC		;AND FORWARD TO LOCAL NSP
	TMNE MBUNR,(MB)		;UNREACHABLE?
	 JRST RTRFUR		;YES, LOG IT THEN
	JN RMOAP,(MB),RTR2RM	;If output adjacency was spec'd, forward it

	LOAD T1,MBDST,(MB)	;HAVE A DESTINATION
	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	 BUG. (CHK,ROUIFS,ROUTER,SOFT,<Router got through the forward routine without picking a route>,,<

Cause:	RTRFWD got through its Forward process and either did
	not pick up a route or failed to flag a message which was for the
	local node or an unreachable message.

Action:	Look for corruption in the routing vector.

>,FREMSG)

;Here when destination node is out-of-range.

RTRFOR:	TMNE RMRQR,(MB)		;WAS RETURN REQUESTED
	JRST RTRFRT		;YES, RETURN IT

	LOAD T2,RMICP,(MB)	;GET THE CIRCUIT IT CAME IN ON
	SKIPN T2
	 BUG.(CHK,ROUNSO,ROUTER,SOFT,<NSP sent out of range packet>,,<

Cause:	There is a request to forward a packet to a node whose address is
	outside the range of our routing vector.  Either our NSP has given
	a packet we cannot forward or we have received one from the wire.

Action:	If the source is local check to see how NSP could give a packet
	whose destination node address is greater than RTRMXN.  If the
	source is remote then there is something wrong with its routing
	database or algorithm.

>,FREMSG)

	LOAD T2,RCLID,(T2)	;Get device ID
	SETZ T3,		;Default address to nothing
	SKIPE AJ
	 LOAD T3,AJADR,(AJ)	;Get address of last router in path
	EVENT RE.NOR,<Node out-of-range packet loss>,MB
	AOS RTRCNO		;INCREMENT OUT-OF-RANGE COUNT
	CALLRET FREMSG		;RETURN THE MSG BLK
;Here when destination node is unreachable.

RTRFUR:	TMNE RMRQR,(MB)		;WAS RETURN REQUESTED?
	JRST RTRFRT		;WE HAVE TO RETURN MESSAGE

	OPSTR <SKIPN T2,>,RMICP,(MB) ;GET THE CIRCUIT IT CAME IN ON
	IFSKP.
	  LOAD T2,RCLID,(T2)	; and get the circuit ID
	  SETZ T3,		;Default address to nothing
	  SKIPE AJ
	   LOAD T3,AJADR,(AJ)	;Get address of last router in path
	  EVENT RE.NUR,<Node unreachable packet loss>,MB
	  AOS RTRCNU		;INCREMENT EXECUTOR NODE COUNTER
	ENDIF.
	CALLRET FREMSG		;GIVE THE MESSAGE BACK AND RETURN

;Here to return message to sender due to unreachability.

RTRFRT:	ETRACE RTR,<Returning message due to unreachability>
	CALL RTRERH		;EAT THE ROUTER HEADER WE PUT ON THE MESSAGE
	SETZRO RMRQR,(MB)	;ZERO THE RQR FLAG
	SETONE RMRTS,(MB)	;SET THE RETURN TO SENDER FLAG
	LOAD T1,MBDST,(MB)	;GET THE DST ADDR
	LOAD T2,MBSRC,(MB)	; AND THE SRC ADDR
	STOR T2,MBDST,(MB)	;SWITCH THEM
	STOR T1,MBSRC,(MB)	; AROUND
	TMNN RMICP,(MB)		;THIS MSG COME FROM LOCAL NSP?
	JRST [CALL CPYMSG	;YES, MAKE THIS MSG LOOK LIKE AN INPUT
				; BY COPYING SEGMENTS TOGETHER
	       CALLRET FREMSG	;OOPS, COULDN'T GET MSG BLOCK, FORGET IT
	      EXCH T1,MB	;POINT TO THE NEW MSG IN MB
	      CALL DNFMSG	;FREE THE OLD MSG
	      CALLRET R2NRTN]	; AND RETURN THE NEW MSG
	CALLRET RTRFWD		;TRY TO FORWARD AGAIN
;Here when the message is for a local NSP.

RTRFLC:	LOAD RC,RMICP,(MB)	;SEE WHERE MSG CAME FROM
	JUMPE RC,RTRF2C		;IF ICP IS ZERO, MSG IS FROM NSP,
				; SO WE HAVE TO COPY DATA
				;IF ICP IS NON-ZERO, MSG IS FROM
				; OUTSIDE & JUST FALL THROUGH

;We are here when we have to pass an incoming message to NSP.

	INCR RCCAP,(RC)		;INCR ARRIVING PACKETS RECEIVED (TO NSP)
RTRF2N:	TMNE RMRTS,(MB)		;IS THIS A RETURNED MESSAGE?
	CALLRET R2NRFR		;Yes, then give it to NSP
	CALLRET R2NRCV		;NO, THIS IS GOOD OLDE RECEIVED DATA

RTRF2C:
;***************************************
	TORESCD
	DNSNUP(RTRLTR)		;Label for DNSNUP to find local messages
	TOXRESCD
;***************************************

	CALL RTRERH		;EAT THE ROUTING HEADER
	CALL CPYMSG		;COPY THE MSD'S TOGETHER
	 CALLRET FREMSG		;DON'T CARE IF IT FAILS, THEN FREE IT UP
	PUSH P,T1		;SAVE THE POINTER TO NEW THE MESSAGE
	CALL FREMSG		;RETURN THE ORIGINAL TO NSP
	POP P,MB		;AND WE CAN DIDDLE WITH OUR NEW COPY
	JRST RTRF2N		;NOW PASS THE MESSAGE ON TO NSP

; Get some bytes that we put in the router MSD above.  This was to fix problems
; with Event processing, But the local NSP and the Return to sender code does
; not expect these fields to be set up.  So we will punt them here.


RTRERH:	LOAD T1,MBFMS,(MB)	;GET THE POINTER TO THE FIRST MSD
	XMOVEI T2,RM.MSD(MB)	;GET THE ADDRESS OF THE ROUTER MSD
	CAME T1,T2		;SEE IF IT AGREES WITH THE FIRST ONE THERE
	 RET			;JUST RETURN MUST BE LLINKS RETURN MESSAGE
	LOAD T1,MDNXT,+RM.MSD(MB) ;GET THE NEXT MSD POINTER
	STOR T1,MBFMS,(MB)	;SAVE AS THE FIRST ONE
	RET			;AND RETURN
	SUBTTL RTR2RM - Send Message to Remote Node

;RTR2RM - Queue Out Message to Remote through the DLL
;
; Call:
;	MB/ Pointer to message block
;	RC/ Pointer to circuit block
;	AJ/ Pointer to adjacency block if we are a level 1 router
;
; Return:
;	RET			;ALWAYS
;
; Uses: P1-P2, T1-T3

RTR2RM:	LOAD T1,RCSTA,(RC)	;GET THE CIRCUIT'S STATE
	CAIE T1,RCS.RN		;IS IT RUNNING
	CAIN T1,RCS.TT		; OR IN TEST?
	TRNA			;YES, WE CAN FORWARD THE PACKET
	CALLRET FREMSG		;NO, DESTROY THE PACKET
	SKIPN ENFLG
	IFSKP.
	  INCR RCCDP,(RC)	; Count data packets
	  JRST RTR2R1		;  and continue
	ENDIF.
	JE RMICP,(MB),[INCR RCCDP,(RC)	;IS THE PACKET FROM OUR LOCAL NSP?
		       JRST RTR2R1]	;YES, INCREMENT COUNT OF NSP PACKETS
					; AND SEND MSG WITHOUT WORRYING ABOUT
					; SQUARE-ROOT LIMIT

	LOAD T1,RMICP,(MB)	;GET THE INPUT CIRCUIT BLOCK
	LOAD T1,LIDEV,+RC.LID(T1) ;GET THE LINE ID
	LOAD T2,RMIAP,(MB)	;GET THE SOURCE ADJACENCY
	LOAD T2,AJNTY,(T2)	;GET THE SOURCE NODE TYPE
	CAXN T2,ADJ.LN		;IS IT AN END NODE?
	 CAXE T1,LD.ETH		;IS THIS THE ETHERNET CHANNEL
	 JRST RTRCSQ		;HAVE BOTH GOT TO BE ON THE ETHERNET

;Get here when we have a source which is an NI endnode.  The SPEC says
;if we get an NI endnode as the source and the destination is not one,
;then we have to check to be sure the hiorder bytes of S-ID are set to
;HIORD.  If they are not, we discard the packet.  We must do this here
;as opposed to when we receive the packet, as we do not know the
;output adjacency when we have just received it.


	LOAD T1,LIDEV,+RC.LID(RC) ;GET THE DEVICE TYPE
	LOAD T2,AJNTY,(AJ)	;GET THE NODE TYPE
	CAXN T1,LD.ETH		;IS THIS ON AN ETHERNET TOO?
	 CAXE T2,ADJ.LN		; AND IS THE DESTINATION AN END NODE?
	TRNA			;NOT ON AN NI OR NOT AN END NODE
	 JRST RTRCSQ		;THEN SKIP THIS
	LOAD T1,MBSR1,(MB)	;GET THE FIRST 4 BYTES OF SOURCE ADDRESS
	CAME T1,RTRHIO		;IS THIS THE HIORDER?
	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT
	LOAD T1,MBDS1,(MB)	; Now check the first 4 bytes of the
	CAME T1,RTRHIO		;  destination address
	 JSP T1,RTEMFE


;We now check the "square-root" limit criterion, to see if the message
;should be discarded.  We check this by comparing the number of message
;blocks queued out on this circuit with the number of message blocks that
;the memory manager has guaranteed router divided by the square-root
;of the number of circuits (We simplify things by just using the number
;of circuits divided by two, which is close enough for a small number of
;circuits).

RTRCSQ:	INCR RCCTR,(RC)		;NO, INCREMENT THE TRANSIT PACKETS RECEIVED
	CALL DNNMSG		;GET NUMBER OF MESSAGE BLOCKS AVAILABLE
	MOVE T2,RTRNLN		;FIND THE NUMBER OF CIRCUITS IN USE
	ASH T2,-1		;DIVIDE BY TWO
	IDIV T1,T2		;DIVIDE TO FIND THE QUEUE THRESHOLD
	OPSTR <CAMLE T1,>,RCCMQ,(RC) ;COMPARE WITH NUMBER OF MSG BLKS QUEUED
	JRST RTR2R1		;WE DON'T HAVE TO FLUSH IT

;Here to discard a message that has exceeded the "square-root" limit.

	TRACE RTR,<Congestion Loss on output>
	INCR RCCTL,(RC)		;INCREMENT THE CONGESTION LOSS COUNTER
	CALLRET FREMSG		;FREE THE MESSAGE AND RETURN
;All packets to be sent come through here.

RTR2R1:
;Now we have to diddle with the MSD pointers slightly to convince
;the DLL not to get the router header from the input MSD.

	XMOVEI T1,RM.MSD(MB)	;POINT TO THE ROUTER MSD
	STOR T1,MBFMS,(MB)	;STORE IN FIRST MSD SLOT

	MOVE T1,MB
	CALL DNLENG		; Get length of message
	OPSTR <SUB T1,>,MDBYT,+RM.MSD(MB) ; Don't count Router's header
	SKIPN ENFLG		; Endnode?
	IFSKP.
	  LOAD T2,RCRBS,(RC)	; Check against our receive block size
	ELSE.
	  LOAD T2,AJBSZ,(AJ) 	;  else check against adjacencies
	ENDIF.
	CAMG T1,T2		; Too many bytes for
	IFSKP.			;   destination's buffer?
	  INCR RCBSX,(RC)	; Yes, count the times this happens
	  CALLRET FREMSG	;  and toss the message
	ENDIF.

	JE RMICP,(MB),RTR2R4	;DID IT COME FROM A REMOTE NODE?
	INCR RCCTS,(RC)		;IT DID, INCREMENT TRANSIT COUNT
	LOAD T1,MDPTR,+IN.MSD(MB) ;ALSO GET THE DYNAMIC BYTE POINTER
	STOR T1,MDAUX,+IN.MSD(MB) ; AND STORE IT IN THE ORIGNAL BYTE POINTER
				  ; SO THE DLL DOESN'T USE THE OLD ROUTER
				  ; HEADER, WHICH WE JUST STRIPED AWAY
;Now set up the arguments for the data link and give it the packet

RTR2R4:	INCR RCCMQ,(RC)		;INCREMENT NUMBER OF MESSAGES QUEUED
	MOVX T1,DF.XMT		;Function is transmit
	SKIPN ENFLG		; Endnode?
	IFSKP.
;Clear cache if try-hard
	  LOAD T3,MBDST,(MB)	; Yes, get the destination address
	  TMNN RMTRY,(MB)	; "Try hard" flag set?
	  IFSKP.
	    LOAD T2,RN%ARE,+T3	; Yes, get area number of node
	    CAME T2,RTRHOM	; Our area?
	    IFSKP.
	      MOVE T2,T3	; Get node address
	      TXZ T2,RN%ARE	; Clear the area number field
	      ADD T2,RTRNRV	; Add reach vector offset
	      SETZRO RNCCH,(T2)	; Clear the in-cache flag
	    ENDIF.
	  ENDIF.
;Now we must determine the next hop address.  The following rules apply:
;	 If destination is in cache then nexthop = destination
;	  else
;	 If designated router .ne. 0 then nexthop = desiginated router
;	  else
;	 nexthop = destination

	  LOAD T4,RCDSL,(RC)	; Get address of designated router
	  LOAD T2,RN%ARE,+T3	; Get area number of node
	  CAME T2,RTRHOM	; Our area?
	   JRST RTR2R3		; No, node not in cache, continue
	  MOVE T2,T3		; Get node address
	  TXZ T2,RN%ARE		; Clear the area number field
	  ADD T2,RTRNRV		; Add reach vector offset
	  TMNN RNCCH,(T2)	; Is cache flag set?
RTR2R3:	  JUMPN T4,RTR2R5	; Not in cache, use DSR if we have one
	  CAME T3,RTRNAD	; Is this destined for us?
	  IFSKP.		; Yes, must be loop node from NML
	    SKIPE T4		; Do we have a DSR?
	     JRST RTR2R5	; Yes, send it there
	  ENDIF.		; No, send it to ourselves (this will fail)
	  SETZ T4,		; Start clean
	  LSHC T3,-^D8		; Convert from 0,,N1,N2 to N2,N1,,0
	  LSH T3,2
	  LSH T4,-^D18		; Shift back to last character in string
	  IOR T4,T3		; Put it in T4 with the next to last char.
	ELSE.
	  LOAD T4,AJNAL,(AJ)	; Address of next hop (Ethernet only)
	ENDIF.
RTR2R5:	HRLZ T4,T4		; Position address correctly
	MOVE T2,RC		; Send on this circuit
	MOVE T3,MB		;  this message
	CALL CALKON		; Call the data link layer (DNADLL)
	 CALLRET XMTFAI		; The transmit failed - Restart data link
	RET			;GIVE A GOOD RETURN TO CALLER

	SUBTTL RTRBEH - Build header for ethernet data messages

RTRBEH:	LOAD T1,RMFST,(MB)	;GET THE FIRST BYTE
	CALL DNP1BY		;PUT IT INTO THE MESSAGE
	SETZ T1,		;CLEAR OUT SOME RESERVED FIELDS
	CALL DNP2BY		;D-AREA, D-SUBAREA
	MOVE T1,RTRHIO		;Get HIORD
	CALL DNPHIO		;Place in message
	LOAD T1,MBDST,(MB)	; and the 16 bit node address NSP gave us
	CALL DNP2BY
	SETZ T1,		;ZERO SOME MORE FIELDS
	CALL DNP2BY		;S-AREA, S-SUBAREA
	MOVE T1,RTRHIO		;Get HIORD
	CALL DNPHIO
	LOAD T1,MBSRC,(MB)	; and the 16 bit source address
	CALL DNP2BY
	SETZ T1,		;SOME MORE RESERVEDS
	CALL DNP1BY		;NEXT LEVEL 2 ROUTER
	LOAD T1,MBVST,(MB)	;Get visits count
	CALL DNP1BY
	SETZ T1,		;NEXT 2 FIELDS ARE RESERVED, JUST WRITE ZERO'S
;	LOAD T1,MBSCL,(MB)	;GET THE SERVICE CLASS
	CALL DNP1BY		;INTO THE MESSAGE
;	LOAD T1,MBPRO,(MB)	;GET THE PROTOCAL TYPE
	CALL DNP1BY		;INTO THE MESSAGE
	RET

 	SUBTTL R2NODN - Return Output Done to NSP

;R2NODN - Give Output Done to NSP
;
; Call: 
;	MB/ Pointer to Message Block
;The relevant fields must be set up in the public portion of the message
;block (i.e., source and destination node addresses, etc.)
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4

R2NODN:	MOVX T3,NV.ODN		;GET THE OUTPUT DONE ENTRY TYPE
	MOVE T4,MB		;SET UP MB FOR NSP
	LOAD T1,MBSRC,(MB)	;IT'S GOING BACK TO SOURCE
	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	CALLRET R2NCL1		;CALL NSP
	SUBTTL R2NRTN - Return Message to NSP

;R2NRTN - Return Message to NSP
;
; Call: 
;	MB/ Pointer to Message Block
;Fields must be set up correctly in the Public portion of the message
;block.
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4

R2NRTN:	SKIPA T3,[NV.RTS]	;Returned by local router to local NSP
R2NRFR:	MOVX T3,NV.RFR		;Returned by remote router to local NSP
	CALLRET R2NCAL		;MERGE WITH THE OTHER CODE

	SUBTTL R2NRCV - Pass received Message to NSP

;R2NRCV - Pass received message to router user (ususally NSP)
;
; Call: 
;	MB/ Pointer to Message Block
;Fields must be set up correctly in the public portion of the message
;block.
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4
;
;R2NCAL, which is the actual routine which calls a router user (also called
;an NSP), will check the message block for the destination address and use
;this to find look up the correct entry vector in the normal routing vector.
;RTRNOV contains the entry vector for NSP when the node entry for RTRNRV
;says it is a local node (RNLCL is on).

R2NRCV:	LOAD T2,MBSRC,(MB)	;GET THE SOURCE OF THIS MESSAGE
	LDB T1,[POINTR(T2,RN%ARE)] ;GET THE AREA NUMBER
	CAME T1,RTRHOM		;OUR AREA?
	JUMPN T1,R2NRC1		;JUMP IF FROM ANOTHER AREA
	LDB T1,[POINTR(T2,RN%NOD)] ;GET THE NODE NUMBER
	SKIPLE T1		;RANGE CHECK THE ADDRESS
	CAMLE T1,RTRMXN		; FOR REASONABLENESS
	SKIPA			;OUT OF RANGE
	JRST R2NRC1		;ALL OK, CONTINUE
	EVENT	RE.NOR,<Node out-of-range packet loss>,MB
	AOS RTRCNO		;INCREMENT OUT-OF-RANGE COUNT
	CALLRET FREMSG		;RETURN THE MESSAGE BLOCK
R2NRC1:	MOVX T3,NV.RCV		;GIVE RECEIVED MESSAGE ENTRY TYPE
R2NCAL:	SKIPN T4,MB		;HE WANTS THE MESSAGE BLOCK PTR IN T4
	 BUG.(CHK,ROUXNZ,ROUTER,SOFT,<R2NCAL called with MB=0>,,<

Cause:	Somehow MB was trashed in the forward process.  It is unlikely
	to get this far if RTRFWD rececived a bad MB.

Action:	Look for faulty code in the forward process

>,RTN)
	LOAD T1,MBDST,(MB)	;GET THE DESTINATION OF THIS MESSAGE
	LDB T2,[POINTR(T1,RN%ARE)] ;GET THE AREA IN T2
	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER

	SKIPE T2		;Do we have an area?
	 CAMN T2,RTRHOM		;Yes, is it ours?
	SKIPA			;Ours or don't have one
	 JRST R2NCLE		;Not ours, THEN LET SOMEONE ELSE WORRY ABOUT IT
R2NCL1:	ADD T1,RTRNRV		;GET NODE'S ENTRY IN "NORMAL" VECTOR
	JN RNLCL,(T1),R2NCL2	;IF IT IS A LOCAL NODE, RETURN MESSAGE
	TMNE RMODN,(MB)		;NOT LOCAL, CHECK FLAGS FOR CONSISTENCY
	BUG.(CHK,ROUNLN,ROUTER,SOFT,<Trying to return msg to non-local NSP>,,<

Cause:	We have decided to return a message to the local NSP but the local
	NSP was not the originator.

>,R2NCLE)
R2NCLE:	SETZRO RMODN,(MB)	;MAKE FREMSG TOSS THE MSG NOW
	CALLRET FREMSG		;RETURN MSG BLK TO FREE POOL

R2NCL2:	ADD T1,RTROFS		;GET THE VECTORED ENTRY POINT POINTER
	MOVE T1,(T1)		;GET THE CORRECT ENTRY
	CALLRET (T1)		;CALL WHATEVER IS ABOVE US

	SUBTTL R2KINI - Initialize a Kontroller's Circuit

;R2KINI - Initialize a DLL circuit
;
; Call: 
;	RC/ Pointer to Circuit Block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T2

R2KINI:	LOAD T1,RCSTA,(RC)	;GET THE CIRCUIT STATE
	CAIE T1,RCS.OF		;We must be in OFF or
	 CAIN T1,RCS.RJ		; rejected state
	  TRNA
	RET			;NO, THEN SKIP THIS
	MOVX T1,DF.OPN		; Function is open
	MOVE T2,RC		; Circuit block
	LOAD T3,RCLID,(RC)	; Get the line ID for sanity check
	CALL CALKON		; Try to open the port
	IFSKP.
	  JE RCBCT,(RC),R2KIN1	; Point-to-point doesn't send NI hellos
	  CAIE T1,LS.ON		; Is line running?
	  IFSKP.
	    MOVX T1,RCS.RN	; Yes, then put circuit into the run state
	    SETONE RCSHM,(RC)	; Send a hello message to start things off
	  ELSE.
	    MOVX T1,RCS.WT	; No, say "waiting" then
	  ENDIF.
	  STOR T1,RCSTA,(RC)	; Set the state
	  SETZRO RCTIN,(RC)	; Clear timer
	  RETSKP
	ELSE.
	  CALL DNGTIM		; Get the current time
	  ADD T1,RTRCRT		; Wait before re-attempting the open
	  STOR T1,RCTIN,(RC)
	  RET			; and indicate failure
	ENDIF.

R2KIN1:	MOVX T1,RCS.WT		; Advance to the wait state
	STOR T1,RCSTA,(RC)
	MOVX T1,ADJ.IN		;SAY THE ADJACENCY IS INITIALIZING
	OPSTR <SKIPE AJ,>,RCAJQ,(RC) ;Get the adjacency
	STOR T1,AJSTA,(AJ)	;STORE IT IN THE ADJACENCY BLOCK
IFN FTOPS20,<
	LOAD T1,LIDEV,+RC.LID(RC) ; Get the device type
	CAIE T1,LD.DTE		; If DTE then give it a TOPS20
	IFSKP.
	  MOVX T1,RE.LDL	;  specific event.  This is so the
	  LOAD T2,RCLID,(RC)	;  Data Link Watcher will wiggle
	  MOVX T3,RS.LSL	;  the DTE again.
	  SETZ T4,
	  CALL RTNEVH		; Give a hidden event to NML
	ENDIF.
>  ;End IFN FTOPS20
	RETSKP			;GIVE GOOD RETURN ABOVE

	SUBTTL R2KCLS - Close the data link circuit

;R2KCLS - Close a DLL circuit
;
; Call: 
;	RC/ Pointer to Circuit Block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T2
;
;We have to set the state of the circuit to off state.

R2KCLS:	MOVX T1,RCS.OF		;We are in "OFF" state
	STOR T1,RCSTA,(RC)	;Store this state
	MOVX T1,DF.CLS		;Function is close
	MOVE T2,RC		;Circuit block
	CALL CALKON		;Tell the DLL we are shutting down
	 TRN
	RET

	SUBTTL CALKON - Call the Kontroller

;CALKON - Call the DLL level "kontroller"
;
; Call: 
;	T1/ Kontroller Function Code
;	T2/ Pointer to circuit block
;	T3/ Argument (Usually Pointer to Message Block)
;	T4/ Next hop for the NI
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4
;
;This routine will call the correct Kontroller on the correct circuit
;given the line id.  The kontroller is called with T1 containing the
;function to perform, T2 containing the hardware line address and T3
;with function specific data.  If the device is on the wrong CPU (only
;DTEs on TOPS-10 SMP), CALKON will queue the request to be serviced
;at once-a-jiffy service on the correct CPU.

CALKON:	SAVEAC <RC,P1>		;SAVE A COUPLE ACS
	MOVE RC,T2		;SET UP THE CIRCUIT BLOCK POINTER

IFN FTMP,<
	PUSH P,T4		;SAVE T4
	LOAD T4,LIDEV,+RC.LID(RC) ;GET THE DEVICE TYPE
	CAIE T4,LD.DTE		;IS IT CPU-DEPENDANT?
	JRST CALKO5		;NOT CPU DEPENDANT, GO AHEAD AND DO IT.
	LOAD T4,LIKON,+RC.LID(RC) ;GET THE CPU NUMBER OF THE CIRCUIT
	CAMN T4,.CPCPN		;ARE WE ON THAT CPU NOW?
	JRST CALKO5		;YES
	CAIN T1,DF.XMT		;QUEUED OUTPUT?
	JRST CALKO4		;YES, QUEUE UP THE MESSAGE BLOCK.
	CAIE T1,DF.OPN		;IS THIS AN INITIALIZE FUNCTION?
	JRST CALKO1		;NO, TRY FOR SOMETHING ELSE
	SETONE RCOPN,(RC)	;FLAG INITIALIZE TO BE DONE AT JIFFY LEVEL.
	POP P,T4		;RESTORE T4
	MOVEI T1,LS.OFF		;PRETEND LINE STATE IS OFF
	RETSKP			;SAY WE DID IT.
CALKO1:	CAIE T1,DF.CLS		;IS THIS A HALT KONTROLLER FUNCTION?
	 BUG.(CHK,ROUIKF,ROUTER,SOFT,<Illegal Kontroller function>,,<

Cause:	CALKON was called with an illegal function code. The only allowed
	values are DF.XMT, DF.OPN, and DF.CLS.
>,RTN)
	SETONE RCCLS,(RC)	;FLAG HALT TO BE DONE AT JIFFY LEVEL.
	POP P,T4		;RESTORE T4
	RETSKP			;SAY WE DID IT
CALKO4:	D36OFF			;NO, WE'LL HAVE TO QUEUE THE MESSAGE
	ENDQUE T3,RC.JSQ(RC),MB.NXT,T1 ;QUEUE THE MESSAGE
	D36ON			;TURN INTERRUPTS BACK ON
	POP P,T4		;RESTORE T4
	RETSKP			; AND RETURN TO SENDER
CALKO5:	POP P,T4		;RESTORE T4
>;End of IFN FTMP
	LOAD T2,RCDLB,(RC)	;GET THE KONTROLLER'S BLOCK ADDRESS

	LOAD P1,LIDEV,+RC.LID(RC) ;GET THE DEVICE TYPE
;************************
	TORESCD
	DNSNUP(RTROTR)		;LABEL FOR DNSNUP TO FIND OUTPUT MESSAGES
	TOXRESCD
				;EXPECTS (T3) = MESSAGE BLOCK
;************************

	CALLRET DNDDSP		;DISPATCH TO DNADLL AND RETURN +1 OR +2

	SUBTTL  CALQOB - Call CALKON for queued output.

;CALQOB - Send a message to the DLL "kontroller"
;
; Call:
;	RC/ Pointer to the circuit block
;	MB/ Pointer to message block
;	T4/ Next hop address (if any)
; Return:
;	+1, Always. If the output fails, it calls XMTFAI which returns
;		the message in MB and closes the port, setting a time at
;		which to reopen it.
;
;This routine is a jacket around CALKON to make the code sending messages
;simpler and easier to read.
;
;CALQMB is a special entry for message blocks that were queued to MB.NXT
;for a circuit on some other CPU.

CALQOB:	INCR RCCMQ,(RC)		;INCREMENT NUMBER OF MESSAGES QUEUED
CALQMB:	MOVX T1,DF.XMT		;SET UP FUNCTION CODE
	MOVE T2,RC		;COPY POINTER TO CIRCUIT BLOCK
	MOVE T3,MB		;COPY POINTER TO MESSAGE BLOCK
	CALL CALKON		;CALL THE KONTROLLER
	 CALLRET XMTFAI		;OUTPUT FAILED
	RET			;RETURN SUCCESS

	SUBTTL XMTFAI/CIRFAI - Transmit/Line failed

;Call:
;	RC/ Circuit block
;	MB/ Message that couldn't be transmitted


XMTFAI:	SETZRO RMDRM,(MB)	; Don't try to resend this
	MOVE T3,MB		; SET UP MESSAGE BLOCK POINTER FOR RTIOTC
	CALL RTIOTC		; FREE UP THE MESSAGE BLOCK
CIRFAI:	CALL RTIDWN		; SAY THE LINE IS NOW DOWN
	CALL R2KCLS		; Close the port
	MOVX T1,RCS.RJ		; Set state to rejected
	STOR T1,RCSTA,(RC)
	CALL DNGTIM		; and try to reopen it soon
	STOR T1,RCTIN,(RC) 	;
	RET

	SUBTTL RTRRCR - Recompute Routing

;RTRRCR - Routine to Recompute Routing Vector
;
; Call:
;	RTRNRV is expected to contain a ptr to the current "normal"
;routing vector, while RTRNVB points to the routing vector bitmap.
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T4
;
; AC Usage:
;	AJ/ Pointer to the adjacency block
;	RC/ Ptr to circuit block
;	T4/ Index into scratch routing vector
;	T3/ Index into normal routing vector
;
;This routine is called whenever it is thought that the topology of
;the network may have changed.  RTRNRV is the normal routing vector
;which is used by the forwarding routine, while RTRNVB is a bitmap
;of which nodes need to be recomputed.

RTRRCR:	SKIPN RTRCBQ		;Any circuits yet?
	 RET			;If not, just return
	TRACE RTR,<Recomputing network topology>
	STKVAR	<MSGLEN>
	SKIPN ENFLG		; Endnode?
	IFSKP.
	  SKIPE EVSDRP		; If we are not eavesdropping then nothing to
	   CALL RTRBMV		;  update.  Update "maybe" vector
	ELSE.
	  SETZM RTRRCF		; Clear the "recompute-routing" flag
	  CALL RTRBNV		; Build normal routing vector
	ENDIF.
	SKIPN T1,AJOFLQ		; Any adjacencies to free?
	 RET			; No
RTRRC2:	LOAD P1,AJNXT,(T1)	; Get next adjacency on queue if any
	CALL DNFWDS		; Return the memory
	MOVE T1,P1		; Get possible next adjacency to free
	JUMPN T1,RTRRC2		; If another tryto free it
	SETZM AJOFLQ		; The queue is now empty
	RET			; And now we are done
	SUBTTL RTRBNV - Build normal routing vector

;Here to recompute the normal routing vector.  We also
;look for local users in the "normal" routing vector as we go
;through.  With local users, RTRNOV has the address of the vector entry
;to that "NSP", therefore it is left alone.


RTRBNV:	STKVAR <NVBAOJ,NVBENT,UPDCNT>
	SETZM UPDCNT		;Initialize update count
IFN FTOPS20,<
	STKVAR <TCIFLG>
	SETZM TCIFLG		;Initialize topology change count
>; END IFN FTOPS20
	MOVE T1,RTRMXN		;Get maximum node address
	ADDI T1,1		;Adjust for node 0
	IDIVI T1,^D36		;Compute size of routing vector bitmap
	SKIPE T2		;Round up
	ADDI T1,1		;...
	MOVNS T1		;Create an AOBJN pointer to bitmap
	HRLZS T1		;...

RTRBN1:	MOVEM T1,NVBAOJ		;Save routing vector bitmap AOBJN pointer
	HRRZ T2,T1		;Get current routing vector bitmap offset
	ADD T2,RTRNVB		;Add address of routing vector bitmap
	SETZ T1,		;Get a zero for entry
	EXCH T1,(T2)		;Clear entry and get current value
	MOVEM T1,NVBENT		;Save current entry
RTRBN2:	MOVE T1,NVBENT		;Get current entry value
	JUMPE T1,RTRBN8		;Jump if current entry zero
	JFFO T1,.+1		;Find next bit set within entry
	MOVE T1,BITS(T2)	;Clear bit just found in saved entry value
	ANDCAM T1,NVBENT	;...
	HRRZ P2,NVBAOJ		;Compute node number of node to recompute
	IMULI P2,^D36		;...
	ADD P2,T2		;...
	CAMLE P2,RTRMXN		;Range check computed node number
	BUG.(CHK,ROUBMC,ROUTER,SOFT,<Normal routing vector bitmap corrupted>,,,RTRBN9)
	MOVE P1,RTRNRV		;Get address of normal routing vector
	ADD P1,P2		;Offset to node being recomputed
	MOVE T4,(P1)		;Get current routing information
	TXNE T4,RNLCL		;Is this the local node?
	JRST RTRBN2		;Yes, skip over this node

				;Continued on next page
				;Continued from previous page

	MOVX Q1,RNHOP!RNCST	;Start off with maximum cost and hops
	SETZ Q2,		;And no known adjacency pointer
	SKIPN RC,RTRCBQ		;Get address of first circuit block
	  JRST RTRBN7		;None there?
RTRBN3:	LOAD AJ,RCAJQ,(RC)	;Get address of first adjacency block
	JUMPE AJ,RTRBN6		;None there, ignore this circuit
RTRBN4:	LOAD T1,AJSTA,(AJ)	;Get current state of adjacency
	CAXE T1,ADJ.UP		;Is it up?
	JRST RTRBN5		;No, skip this adjacency
	LOAD T1,AJNAN,(AJ)	;Get node address of adjacent node
	JUMPE T1,RTRBN5		;Skip this adjacency if no node address
	CAME T1,P2		;Recomputing this node's information?
	IFSKP.
	  SETZ T4,		;Yes, get dummy routing information
	ELSE.
	  LOAD P1,AJRTV,(AJ)	;Get address of adjacency's routing vector
	  JUMPE P1,RTRBN5	;No routing information if an endnode
	  ADD P1,P2		;Offset to node being recomputed
	  MOVE T4,(P1)		;Get current routing information
	ENDIF.
	LOAD T1,RNCST,+T4	;Compute cost to subject node
	OPSTR <ADD T1,>,RCCST,(RC) ;...
	LOAD T2,RNHOP,+T4	;Compute hops to subject node
	ADDI T2,1		;...
	CAMG T1,RTRMXC		;Cost and hops within range?
	CAMLE T2,RTRMXH		;...
	JRST RTRBN5		;No, skip this adjacency
	LOAD T3,RNCST,+Q1	;Get best cost so far
	CAMLE T1,T3		;Is cost via this adjacency the same or better?
	JRST RTRBN5		;No, skip this adjacency
	CAMN T1,T3		;Is this adjacency the same cost?
	JUMPN Q2,[LOAD  T3,AJNAN,(AJ)	;Yes, get node address of adjacent node
		  LOAD  T4,AJNAN,(Q2)	;And address of best (so far) adjacency
		  CAMG  T3,T4	;Is new adjacency node address greater?
		  JRST  RTRBN5	;No, ignore this adjacency
		  JRST  .+1]	;Yes, use this adjacency
	STOR T1,RNCST,+Q1	;Update best cost and hops values
	STOR T2,RNHOP,+Q1	;...
	TXO Q1,RNRCH		;Mark node as reachable
	MOVE Q2,AJ		;Remember chosen adjacency for this node
RTRBN5:	LOAD AJ,AJNXT,(AJ)	;Step to next adjacency
	JUMPN AJ,RTRBN4		;Loop back for all adjacencies
RTRBN6:	LOAD RC,RCNXT,(RC)	;Step to next circuit
	JUMPN RC,RTRBN3		;Loop back for all adjacencies

				;Continued on next page
				;Continued from previous page

RTRBN7:	MOVE P1,RTRNRV		;Get address of normal routing vector
	ADD P1,P2		;Offset to node being recomputed
	MOVE T4,(P1)		;Get current routing information
	LDB T1,[POINTR (Q1,RNCST!RNHOP)] ;Get cost and hops values
	LDB T2,[POINTR (T4,RNCST!RNHOP)] ;...
	MOVE T3,P1		;Get address of current entry
	ADD T3,RTROFS		;Offset to output adjacency vector
	MOVE T3,(T3)		;Get current output adjacency
	CAMN T1,T2		;Any change in cost or hops?
	CAME T3,Q2		;Or output adjacency?
	IFNSK.
	  TXO Q1,RNCHG		;Yes, set change flag
	  AOS UPDCNT		;Increment update count
	  SUB T1,T2		;Update routing vector checksum
	  ADDM T1,RTRCKS	;...
	  XOR T4,Q1		;Did reachability change?
	  TXNN T4,RNRCH		;...
	  IFSKP.
IFN FTOPS20,<
	    AOS TCIFLG		;Increment topology change count
>; END IFN FTOPS20
	    TXNE Q1,RNRCH	;Is node now reachable?
	    TDZA T3,T3		;Yes, get reachable flag and skip
	    MOVEI T3,1		;No, get unreachable flag
	    MOVE T2,P2		;Get node number
	    MOVE T1,RTRHOM	;Generate full node address
	    DPB T1,[POINTR (T2,RN%ARE)] ;...
	    EVENT RE.NRC,<Node reachability change>
	  ENDIF.
	  MOVE T1,P1		;Get address of current entry
	  ADD T1,RTROFS		;Offset to output adjacency vector
	  MOVEM Q2,(T1)		;Store updated routing information
	  MOVEM Q1,(P1)		;...
	ENDIF.
	JRST RTRBN2		;Loop back for entire bitmap entry

RTRBN8:	MOVE T1,NVBAOJ		;Get AOBJN pointer to bitmap vector
	AOBJN T1,RTRBN1		;Loop back to inspect entire bitmap

RTRBN9:	SKIPN UPDCNT		;Any changes in routing vector?
	RET			;No, return

IFN FTOPS20,<
	SKIPE TCIFLG		;Topology change?
	CALLX (MSEC1,NTCIN)	;Yes, signal topology change interrupt
>; END IFN FTOPS20

	SKIPN RC,RTRCBQ		;Get address of first circuit block
	RET			;None? Give up now
RTRBN0:	SETONE RCSRM,(RC)	;Set send routing message flag
	LOAD RC,RCNXT,(RC)	;Get address of next circuit block
	JUMPN RC,RTRBN0		;Loop back to set SRM for all circuits
	RET			;And then return
	SUBTTL  RTRBMV - Maybe vector update when running as an endnode


RTRBMV:	CALL DNGTIM		; Get current milliseconds
	CAMG T1,BMVTIM		;  and see if time for another update
	 RET			;  of the maybe vector.
	ADD T1,BMVIVL		; Compute time of next update
	MOVEM T1,BMVTIM
	SKIPN RC,RTRCBQ		; Do we have a circuit
	 RET
	OPSTR <SKIPE AJ,>,RCAJQ,(RC) ; Get the only adjacency
	IFSKP.
	  MOVE T1,RTRNRV 	; Address of vector
	  MOVE T2,RTRMXN	; Number of slots in vector
	  AOJ T2,		;  including node 0
	  DO.
	    SETZRO RNMBY,(T1)	; Clear the "maybe bit"
	    SOJLE T2,RTN	; Exit when all elements done
	    AOJ T1,		; Point to next element
	    LOOP.		; Loop back
	  ENDDO.
	ENDIF.
	LOAD P2,AJRTV,(AJ)	; Get pointer to routing vector
	JUMPE P2,RTN		; Just in case
	ADDI P2,1		; Start with node 1
	SKIPN P1,RTRNRV		; Get reach vector
	 RET
	LOAD T1,RCCST,(RC)	; Get the cost on the Ethernet
	STOR T1,RNCST,(P1)	;  and save it in element 0 of the vector
	MOVEI T1,1		;  and dummy up the hops
	STOR T1,RNHOP,(P1)
	ADDI P1,1		;  Step to node 1
	MOVE Q1,RTRMXN		; Get the maximum number of nodes
RTRBM1:	SETZRO RNMBY,(P1)	; Assume not reachable
	LOAD T2,RNCST,(P2)	; Get the cost
	OPSTR <ADD T2,>,RCCST,(RC) ; Add the circuit cost
	LOAD T1,RNHOP,(P2)	; Get the number of hops
	AOS T1			; Adjust it by us
	CAMGE T2,RTRMXC		; Is the cost within the max?
	CAML T1,RTRMXH		;  How about the hops?
	 JRST RTRBM2		; No, then do the next node
	SETONE RNMBY,(P1)	; Yes, assume reachable
RTRBM2:	AOJ P1,			; Do the next one
	AOJ P2,			;  on both of them
	SOJGE Q1,RTRBM1		; Loop for the next node
	SETZM RTRRCF		; Clear the "recompute-routing" flag
	RET

	SUBTTL RTRUPD - Send routing messages if needed

;RTRUPD - Routine to perform the update process (send routing messages)
;
; Call: 
;	RTRNRV must contain the ptr to the normal routing vector
;	RTRCBQ must contain a ptr to the first circuit block
;
; Return: 
;	RET			;Always
;
; Uses: T1-T4

;Loop through all the circuit blocks and see if the long timer (T2) has
;expired.  If it has we send a routing message, if it hasn't check
;the send routing message flag and if set, send the routing message if
;the short timer has expired. The routing message is built from the
;"normal" routing vector. After the routing message has been queued,
;the circuit block "last routing message sent" timer is updated, and
;the "send routing message" flag is cleared.

RTRUPD:	SKIPGE ENFLG		;If an endnode
	 RET			; then don't send a routing message
	TRVAR <COUNTPOS,NODCNT,TOTALNF>
	STKVAR <EMPTY,MAXNODE,MAXSEG,OVFLG>
	SKIPA RC,RTRCBQ		;GET THE BEGINNING OF THE CIRCUIT QUEUE
UPDLOP:	LOAD RC,RCNXT,(RC)	;GET THE POINTER TO THE NEXT ONE
				;Careful if this call is moved (JRST RTRCHK)
	JUMPE RC,RTRCHK		;IF DONE THEN GO CHECK THE ROUTING VECTOR
	LOAD T1,RCSTA,(RC)	;GET THE STATE OF THIS CIRCUIT
	CAIE T1,RCS.TT		;IS THE STATE IN TEST
	 CAIN T1,RCS.RN		; OR OK STATE
	  TRNA			;YES, SEND ROUTING MESSAGE
	JRST UPDLOP		;NO, LOOK AT NEXT CIRCUIT
	SETZM TOTALNF		;CLEAR OUT TOTAL NODE FLAG
	SETZM EMPTY		;Clear flag saying data in message
	OPSTR <SKIPN T2,>,RCTLR,(RC) ;Have we ever sent an update?
	 JRST RTRSRB		;No, send one now
	CALL DNGTIM		;GET THE CURRENT TIME IN T1
	SUB T1,T2		; Compute the elapsed time since last update
	MOVE T2,RTRTM1		; Get the long timer for point to point 
	TMNE RCBCT,(RC)		; Broadcast circuit?
	 MOVE T2,RTRBT1		; Yes, use the broadcast timer
	CAML T1,T2		; Has the update timer (T1) expired?
	 JRST RTRSRB		; Yes, send all nodes now
	TMNN RCSRM,(RC)		; Forced update?
	 JRST UPDLOP		; No, look at next circuit
	TRNA
RTRSRB:	SETOM TOTALNF		; Indicate we need to send complete topology
	LOAD T1,RCBSZ,(RC)	;GET THE CIRCUIT BLOCK SIZE
	SKIPN T1		;HAVE ONE TO USE?
	 BUG.(CHK,ROUBSZ,ROUTER,SOFT,<Router circuit block size was zero on a running circuit>,,<

Cause: The blocksize for a circuit is defaulted to RTRBSZ and updated which
	information from nodes on the circuit to determine a new minimum
	blocksize for the circuit.  Somehow this ended up as zero.

>,UPDLOP)
	LOAD AJ,RCAJQ,(RC)	; No, then get pointer to only adjacency for
				;  point-point or any for broadcast
	JUMPE AJ,UPDLOP		; No adjacencies, give up...
	JN RCBCT,(RC),RTRSX1	; If broadcast don't check adjacency state
	LOAD T1,AJSTA,(AJ)	; Get state of adjacency
	CAIE T1,ADJ.UP		; Is it "up"
	 JRST UPDLOP		; No, try next circuit
RTRSX1:	MOVE P1,RTRNRV		;POINT TO NORMAL ROUTING VECTOR
	SETZ P2,		;CLEAR OUT CHECKSUM
	LOAD T1,AJNTY,(AJ)	;GET THE NODE TYPE IN T1
	CAIE T1,ADJ.3F		;IS IT A PHASE III ROUTER?
	 JRST SP4RM		;NO, THEN CHECK FOR PHASE IV
	
	MOVE T1,RTRMX3		;GET THE MAXIMUM NUMBER OF PHASE III NODES
	MOVEM T1,NODCNT		;SAVE AS THE COUNT OF NODES
	LSH T1,1		;TWO BYTES PER NODE
	ADDI T1,2		;ADD IN SPACE FOR CHECKSUM
	CALL DNGMSG		;GET THE MESSAGE BLOCK
	 JRST UPDLOP		;COULDN'T, THEN TRY NEXT CIRCUIT
	MOVE MB,T1		;GET THE MESSAGE BLOCK POINTER
	XMOVEI T1,UD.MSD(MB)	;GET POINTER TO USER DATA MSD
	CALL DNPINI		;INITIALIZE THE MSD AND HEADER

SP3LOP:	AOS P1			;PHASE III STARTS WITH NODE 1
	LDB T1,[POINTR((P1),<RNHOP!RNCST>)] ;GET THE HOP!COST
	ADD P2,T1		;SUM THE CHECKSUM
	CALL DNP2BY		;PLACE BYTES IN ROUTING MESSAGE
	SOSLE NODCNT		;DO IT FOR ALL THE NODES but not zero
	 JRST SP3LOP		;Continue until last node done
	MOVE T1,P2		;Checksum in T1
	CALL RTRSNS		;SEND THE SEGMENT OUT
	 TRN			;ON ERROR TRY NEXT CIRCUIT
	JRST UPDLOP		;AND DO THE NEXT ONE
; Here to handle a Phase IV message, but first we have to check for
; Phase III small nodes.  If it is one then we exit and do the next circuit
; T1 was set up with the node type from the adjacency vector above.


SP4RM:	JN RCBCT,(RC),SP4RM1	;If broadcast don't check adjacency type
	CAIE T1,ADJ.3S		;PHASE III SMALL NODE
	 CAIN T1,ADJ.LN		;OR PHASE IV END NODE
	JRST UPDLOP		;Yes, then no routing update need be sent

SP4RM1:	ADD P1,RTRLMG		;Add in offset so we don't start with the same
				; node in the vector each time
	MOVE T1,RTRMXN		;GET THE MAXIMUM NUMBER OF NODES
	MOVEM T1,MAXNODE	;SAVE AS MAXIMUM TO SEND

SP4RM3:	MOVEI P2,1		;THE CHECKSUM STARTS AT 1 FOR PHASE IV
	LOAD T1,RCBSZ,(RC)	;GET THE NUMBER OF BYTES FOR MESSAGE BLOCK
	CALL DNGMSG		;GET THE MESSAGE BLOCK
	 JRST UPDLOP		;COULDN'T, THEN TRY NEXT CIRCUIT
	MOVE MB,T1		;GET THE MESSAGE BLOCK POINTER
	XMOVEI T1,UD.MSD(MB)	;GET POINTER TO USER DATA MSD
	CALL DNPINI		;INITIALIZE THE MSD AND HEADER
	LOAD T1,RCBSZ,(RC)	;Number of bytes we can fit into one message
	SUBI T1,6		;Minus the header length and checksum
	LSH T1,-1		;Divide by two since all parts of a segment
	MOVEM T1,MAXSEG		; are two bytes in width
SP4RM4:	SETZM NODCNT		;CLEAR OUT THE NODE COUNT FOR THIS SEGMENT
	SETZM OVFLG		;CLEAR THE OVERFLOW FLAG NOW
	SKIPE TOTALNF		;ARE WE TO INCLUDE ALL NODES?
	 JRST SP4RM6		;YES, THEN START WITH THIS ONE

; Here we loop until we get a node which has the CHANGE bit set in the routing
; vector.

SP4RM5:	LOAD T1,RNCHG,(P1)	;GET THE CHANGE BIT
	JUMPN T1,SP4RM6		;When set start with this node
	AOS P1			;INCREMENT THE COUNT
	MOVE T1,P1		;GET THE NODE NUMBER
	SUB T1,RTRNRV		;FROM THE INDEX INTO ROUTING TABLE
	CAMLE T1,RTRMXN		;HAVE WE REACHED THE END OF THE VECTOR?
	 MOVE P1,RTRNRV		;YES, THEN RESET TO THE BEGINNING
	SOSL MAXNODE		;DECREMENT COUNT OF NODES LEFT TO LOOK AT
	 JRST SP4RM5		;LOOP UNTIL WE FIND ONE WITH CHANGE SET
	JRST SP4RM9		;WE HAVE LOOKED AT ALL NODES

;Here to begin a new segment of the routing message

SP4RM6:	MOVE T1,MAXSEG		;Get number of 2-byte fields left in buffer
	CAIGE T1,3		;Is there enough room for a one node segemnt?
	 JRST SP4RM8		;No, then send current message and start anew
	CALL DNRPOS		;READ THE CURRENT POSITION
	MOVEM T1,COUNTPOS	;SAVE THE POSITION FOR COUNT
	SETZ T1,		;GET A ZERO
	CALL DNP2BY		;PUT THE COUNT IN
	MOVE T1,P1		;GET THE NODE NUMBER INDES
	SUB T1,RTRNRV		;MAKE IT A NODE NUMBER
	ADD P2,T1		;ADD INTO THE CHECKSUM
	CALL DNP2BY		;PUT THE 2 BYTES INTO THE MESSAGE
	SOS MAXSEG		;Start-ID = 2 bytes
	SOS MAXSEG		; and count = 2 bytes
SP4RM7:	LDB T1,[POINTR((P1),<RNHOP!RNCST>)] ;Get COST/HOPS for this entry
	ADD P2,T1		;ADD TO CHECKSUM
	AOS NODCNT		;INCREMENT THE COUNT OF NODES IN THIS SEGMENT
	SETOM EMPTY		;Flag to indicate data in message
	CALL DNP2BY		;Put COST/HOPS into the message
	AOS P1			;DO THE NEXT ONE
	MOVE T1,P1		;GET THE NODE NUMBER
	SUB T1,RTRNRV		;FROM THE INDEX INTO ROUTING TABLE
	CAMG T1,RTRMXN		;HAVE WE REACHED THE MAXIMUM YET?
	IFSKP.
	  SETOM OVFLG		;SET THE OVERFLOW FLAG SO WE END THE SEGMENT
	  MOVE P1,RTRNRV	; and reset to the beginning of the vector
	ENDIF.
	SOSGE MAXNODE		;DECREMENT TOTAL NODE COUNT
	 JRST SP4RM9		; JUMP IF DONE
	SOSG MAXSEG		; Two COST/HOP bytes and see if more room
	 JRST SP4RM8		; NO, THEN SEND IT AND LOOP BACK

	SKIPE OVFLG		;OVERFLOW SET?
	IFSKP.			; No
	  LOAD T1,RNCHG,(P1)	;GET THE CHANGE BIT FOR NEXT NODE
	  SKIPN TOTALNF		;IS THE TOTAL NODE FLAG SET?
	   SKIPE T1		; or do we have a change bit set?
	  JRST SP4RM7		;  Yes, go do the next entry
	ENDIF.
	CALL CLSSEG		; Close the current segment
	JRST SP4RM4		;  and possibly start a new one

SP4RM8:	CALL CLSSEG		; Close out the current segment
	MOVE T1,P2		; Checksum to T1
	CALL RTRSNS		;SEND THE SEGMENT OUT
	 JRST UPDLOP		;ERROR, THEN GO TO THE NEXT CIRCUIT (MB freed)
	JRST SP4RM3		;SUCCESS, START A NEW MESSAGE
;Here when all nodes in the normal vector have been looked at
;See if we found any and if so send the routing message.

SP4RM9:	SKIPE EMPTY		; Any data in message?
	IFSKP.			; No
	  CALL FREMSG		; Return the message block
	  JRST UPDLOP		; and step to next circuit
	ENDIF.
	SKIPE NODCNT		; Yes, do we have any nodes in this segment?
	 CALL CLSSEG		; Yes, close off the segment
	MOVE T1,P2		; Checksum must be in T1
	CALL RTRSNS		;SEND THE SEGMENT OUT
	 JRST UPDLOP		;ERROR, THEN GO TO THE NEXT CIRCUIT
	MOVE T1,RTRBSZ		;Get the block size
	IDIVI T1,5		; and use it to compute 
	ADD T1,RTRLMG		; a new starting point
	CAML T1,RTRMXN		;HAVE WE GONE TOO FAR?
	 SETZ T1,		; Start over again
	MOVEM T1,RTRLMG		;MAKE THIS NEW START
	JRST UPDLOP		; Now advance to the next circuit

	ENDSV.
;Here because we have reached the end of a segment.  That was determined
;by the absence of a CHANGE flag in the vector for some node, or the end of a
;message, or the end of the reach vector has been reached.  We will now
;close the segment.

CLSSEG:	CALL DNRPOS		;GET OUR CURRENT POSITION
	EXCH T1,COUNTPOS	;Get the previously reverved spot for the count
	CALL DNGOPS		;GO TO THAT POSITION
	MOVE T1,NODCNT		;GET THE NUMBER OF NODES FOR THIS SEGMENT
	ADD P2,T1		;ADD THE COUNT TO THE CHECKSUM
	CALL DNP2BY		;PLACE IT INTO THE MESSAGE
	MOVE T1,COUNTPOS	;GET THE END OF THE MESSAGE BACK
	CALL DNGOPS		;AND GO THERE
	RET

;Here to checksum the routing vector to see if it may be corrupted

RTRCHK:	SOSLE CHKCNT		;TIME TO CHECK ROUTING VECTOR CHECKSUM?
	RET			;NO, RETURN
	MOVE T1,CHKVAL		;YES, RESET CHECKSUM COUNTER
	MOVEM T1,CHKCNT		;...
	MOVE T3,RTRMXN		;GET THE MAXIMUM NUMBER OF NODES
	MOVE P1,RTRNRV		;GET THE POINTER TO THE NORMAL ROUTING VECTOR
	SETZ P2,		;CLEAR OUT REG FOR CHECKSUM
RTRCK1:	SETZRO RNCHG,(P1)	;CLEAR OUT CHANGE FLAG
	LDB T1,[POINTR((P1),<RNHOP!RNCST>)] ;GET THE HOPS!COST
	ADD P2,T1		;ADD INTO MESSAGE
	AOJ P1,			;ADD IN THE NEXT ONE
	SOJGE T3,RTRCK1		;LOOP FOR THEM ALL

	CAME P2,RTRCKS		;IS IT THE SAME AS THE OLD CHECKSUM?
	SKIPN RTRCKS		; AND DID WE HAVE ONE?
	TRNA			;IT'S OK
	BUG.(INF,ROUBCD,ROUTER,SOFT,<Bad Checksum detected when building routing msg>,,<

Cause:	Somehow our internal reachability vector has been damaged since the
	last rebuilding.

>)
	MOVEM P2,RTRCKS		;STORE THE CHECKSUM
	RET			;RETURN TO CALLER
;
; All Outgoing Routing Messages Both Phase III And Phase IV
; pass through this routine.  It expects the unnormalized checksum
; in P2.

RTRSNS:	MOVE T1,P2		;GET COPY OF CHECKSUM
	CALL CKSNRM		;NORMALIZE THE CHECKSUM
	CALL DNP2BY		; AND WRITE IT IN MESSAGE

;Now we must make up the router header
	
	XMOVEI T1,RM.MSD(MB)	;GET POINTER TO ROUTER'S MSD
	CALL DNPINI		;SET UP TO WRITE HEADER
	SETZ T1,		;BUILD FIRST BYTE IN T1
	MOVX T2,RCM.1R		;TYPE = ROUTING MESSAGE
	STOR T2,CMTYP,+T1	;PLACE IT IN CORRECT FIELD
	SETONE CMCTL,+T1	;INDICATE IT IS A CONTROL MESSAGE
	CALL DNP1BY		;PLACE THE BYTE

	MOVE T1,RTRADR		;GET OUR ADDRESS
	MOVE T3,RTRHOM		; and our area
	TMNE AJPH4,(AJ)		; If Phase IV include the area number
	 DPB T3,[POINTR(T1,RN%ARE)]
	CALL DNP2BY		;PUT ADDRESS IN MESSAGE

	SETZ T1,		;ZERO FOR A RESERVED PHASE IV BYTE
	TMNE AJPH4,(AJ)		; If Phase IV include a reserved byte
	  CALL DNP1BY

	XMOVEI T1,UD.MSD(MB)	;POINT TO USER DATA MSD
	XMOVEI T2,RM.MSD(MB)	;MAKE RTR MSD POINT TO USER DATA MSD
	SETZ T3,		;DON'T CHANGE BYTE POINTERS
	CALL DNLMSS		;LINK IN THE MESSAGE SEGMENT
	TRACE RTR,<Sending routing message>
	JE RCBCT,(RC),RTRSN2	;If not broadcast send packet now
	DMOVE T1,RTRALR		;Sending to "all-routers"
	CALL RTRSNI		;Broadcast set it up differently
	TRNA
RTRSN2:	CALL CALQOB		;OUTPUT THE MESSAGE BLOCK
	 TRN
	SETZRO RCSRM,(RC)	;INDICATE WE HAVE SENT ROUTING MESSAGE
	CALL DNGTIM		;Get current time
	SKIPE TOTALNF		;Was this a complete update?
	 STOR T1,RCTLR,(RC)	;Yes, remember the time we sent it
	RETSKP			;GIVE GOOD RETURN

	ENDTV.

	SUBTTL RTRSNI - Send hello/routing message on the NI

;Call:
;	T1/ Destination address high order
;	T2/ Destination address low order

RTRSNI:	STOR T1,MBDS1,(MB)	; Save address of destination (High-order)
	STOR T2,MBDST,(MB)	; Low order address
	SETZ T4,		; No next hop for broadcast messages
	CALL CALQOB		;
	RET			;AND CONTINUE ON

	SUBTTL CKECTO - Check the cache timer for each node

CKECTO:	SOSLE CCHTIM		; Time to check cache flags?
	 RET			; No
	MOVE T4,RTRMXN		; Count of slots in vector
	MOVE T3,RTRNRV		; Point to the reach vector which contains
				;  the cache flag
CKECT1:	JE RNCCH,(T3),CKECT2	; If cache flag is set check cache timeout
	MOVE T2,T3		; Copy current vector address
	ADD T2,RTROFS		; Add offset to timer value
	CALL DNGTIM		; Get current time
	CAMG T1,(T2)		; Is current time past time out value
	IFSKP.
	  SETZRO RNCCH,(T3)	; Yes, clear the in-cache flag
	ENDIF.
CKECT2:	AOJ T3,			; Step to next node
	SOJGE T4,CKECT1		; More to do?
	MOVE T1,RTRADR		; Get local address
	ADD T1,RTRNRV		;  and index into reach vector slot
	SETONE RNCCH,(T1)	;   and set the in-cache flag
	MOVEI T1,^D120		; Two minutes before we check again
	MOVEM T1,CCHTIM
	RET

	SUBTTL SELDSR - Select a desiginated router

SELDSR:	SKIPA RC,RTRCBQ		; Loop through all circuit blocks
SELDS1:	LOAD RC,RCNXT,(RC)
	 JUMPE RC,RTN		; If done, just return
	TMNN RCBCT,(RC)		; We only compute a desiginated router for
	 JRST SELDS1		;  for broadcast circuits
	SETZRO RCPRI,(RC)	; Start at lowest priority
	SETZ P1,
	OPSTR <SKIPA AJ,>,RCAJQ,(RC) ;Get pointer to first adjacency
SELDS2:	LOAD AJ,AJNXT,(AJ)	; Step to next
	SKIPN AJ
	IFSKP.
	  LOAD T1,AJSTA,(AJ)	; Get state of adjacency
	  CAIE T1,ADJ.IN	; If state is not initializing
	   CAIN T1,ADJ.UP	;  or up, don't consider it
	    TRNA
	   JRST SELDS2
	  LOAD T1,AJPRI,(AJ)	; Get the adjacency's priority
	  LOAD T3,AJNAH,(AJ)	; Get the address
	  LOAD T4,AJNAL,(AJ)
	ELSE.
	  MOVE T1,RTRPRI	; No more adjacencies, check ourselves
	  MOVE T3,RTRHIO	; Get our address in string format
	  HLRZ T4,RTRLOO
	ENDIF.
	CAIGE T1,(P1)		; Is it greater/equal than what we have so far?
	 JRST SELDS3		; No, step to next
	CAIE T1,(P1)		; Was it the same?
	IFSKP.
	  LOAD T2,RCDSL,(RC)	; Get current designated router's address
	  CAMG T4,T2		; Is new one higher?
	   JRST SELDS3		; No, keep the one we have
	ENDIF.
	MOVE P1,T1		; Save this as the highest
	DMOVE Q1,T3		;  and save address
SELDS3:	JUMPN AJ,SELDS2		; Check next adjacency or ultimately ourselves
	STOR P1,RCPRI,(RC)	; Save new priority
	OPSTR <CAMN Q2,>,RCDSL,(RC) ; Has the designated router changed?
	 RET			; No, then done
	STOR Q2,RCDSL,(RC)	; Save the designated routers address
	HRLZ Q2,Q2		; Reposition low order
	CAME Q2,RTRLOO		; Are we the designated router?
	IFSKP.
	  MOVEI T2,^D5		; Seconds to wait
	  STOR T2,RCDRT,(RC)	;  before we assume DSR role
	ELSE.
	  SETZRO RCDRT,(RC)	; No longer waiting to assume DSR role
	  SETZRO RCDSR,(RC)	; We are not the DSR
	ENDIF.
	STOR Q1,RCDSH,(RC)	; Save high order of address
	JRST SELDS1		;  and check next circuit

	SUBTTL RTRTMR - Routine to perform timer functions

;RTRTMR - Hello, Listener and other timer processes
;
; Call:
;	RTRCBQ must point to the first circuit block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T4
;
;This code implements the Update Process, the Node Talker Process, and the
;Node Listener Process.

;First we do the Node Listener Process.  This consists of ensuring that we
;have heard from all adjacencies within time T3 * T3MULT (or BCT3MULT).  (T3
;is the adjacencies hello timer).  Ie: current_time - AJTLR < AJNHT.  If the
;previous expression is not true, then if the circuit is a:
; point to point -  set off-line
; broadcast (Ethernet) - the adjacency is destroyed.

RTRTMR:	SAVEAC <P1>
	SKIPA RC,RTRCBQ		;GET THE FIRST CIRCUIT BLOCK
RTRTR1:	LOAD RC,RCNXT,(RC)	;GET NEXT CIRCUIT BLOCK
	JUMPE RC,RTRSHM		;Send hellos if necessary

	LOAD P1,RCSTA,(RC)	;Get state of circuit
	CAILE P1,RCS.RJ		;Is state off or rejected?
	 JRST RTRTR2		;No, check TI state
	LOAD T2,RCTIN,(RC)	;Is the timer running?
	JUMPE T2,RTRTR2		;No, look no further (maybe to TR3)

	CALL DNGTIM		;Get the current time in T1
	CAMGE T1,T2		;Has time elapsed?
	IFSKP.			;Yes
	  CALL R2KINI		;Try to reopen circuit
	   TRN
	  JRST RTRTR3		; and move to next circuit
	ENDIF.
RTRTR2:	CAIE P1,RCS.TI		;IS LINK IN TI WAIT?
	 CAIN P1,RCS.TV		;IS IT IN TV WAIT?
	  TRNA			;YES, CHECK OUT TI TIMER
	JRST RTRTR3		;NO, CHECK OUT OTHER TIMERS
	CALL DNGTIM		;GET THE CURRENT TIME
	OPSTR <SUB T1,>,RCTIN,(RC) ;SUBTRACT WHEN WE GOT THE LINE UP
	CAMGE T1,RTRITM		;HAS TIME EXPIRED?
	JRST RTRTR3		;NO, JUST CONTINUE ON

;Here when the time allowed for the response to the RI we sent has expired.

	ETRACE RTR,<Initialization timer expired and is restarting the circuit>
	MOVX T1,RS.VRT		;EVENT REASON: VERIFICATION RECEIVE TIMEOUT
	CALL RTEINL		;REPORT INITIALIZATION FAILURE EVENT
	CALL CIRFAI		;Close the portal and set reopen time
;	SETOM RTRRCF		;RECOMPUTE ROUTING LATER
	JRST RTRTR1		; AND TRY THE NEXT CIRCUIT

;Now check the hello and listener timers.

RTRTR3:	CAIE P1,RCS.TT		; Is circuit in test state
	CAIN P1,RCS.RN		;  or is it running?
	TRNA			;YES, OK TO DO THINGS
	JRST RTRTR1		;NO, CHECK OUT THE NEXT CIRCUIT

RTRCHT:	OPSTR <SKIPA AJ,>,RCAJQ,(RC) ;Get pointer to first adjacency
RTRCH1:	LOAD AJ,AJNXT,(AJ)	;Get next adjacency
	JUMPE AJ,[CALL RTRTLK	;Check the hello timer
		  CALL DSRCHK	; and the desiginated router timer
		  JRST RTRTR1]	;Now, step to next circuit
	LOAD T1,AJSTA,(AJ)	;Get the state of the adjacency
	CAIE T1,ADJ.UP		;Is it up?
	 JRST RTRCH1		;No, then don't check further
	CALL DNGTIM		;GET THE CURRENT TIME
	OPSTR <SUB T1,>,AJTLR,(AJ) ;SUBTRACT THE TIME LAST MESSAGE WAS RECEIVED

	OPSTR <CAMGE T1,>,AJNHT,(AJ) ;DID THE LISTENER TIMER EXPIRE?
	 JRST RTRCH1		; No, check the next adjacency

;Here when the listener timer has been exceeded.  The adjacency is declared
;down.  On point to point circuits, the circuit gets recycled.

	LOAD T3,AJADR,(AJ)	;Get the node address
	INCR RCAJD,(RC)		;Count adjacency down events
	LOAD T2,RCLID,(RC)	;Get the device ID
	EVENT RE.AJD,<Adjacency down-listen timer expired>,
	CALL SETRCA		;Yes, set recompute routing flags
	JN RCBCT,(RC),RTRCH3	;No circuit down if broadcast
	ETRACE RTR,<Node listener timed out and is rejecting circuit>
	MOVX T1,RS.ALT		;EVENT REASON: ADJACENT NODE LISTENER TIMEOUT
	CALL RTELDL		;REPORT CIRCUIT DOWN - CIRCUIT FAULT EVENT
	CALL CIRFAI		;Close the port and set reopen time
	JRST RTRCH1		; Check next adjacency
RTRCH3:	SKIPN ENFLG		; Endnode?
	IFSKP.
	  MOVX T1,ADJ.UN	; Set adjacency state to unused
	  STOR T1,AJSTA,(AJ)
	  SETZRO RCDSL,(RC)	; Clear DR address
	ELSE.
	  MOVE T1,AJ		;The adjacency to zap
	  MOVE T2,RC		; and the circuit block it belongs to
	  CALL RTRZAB		;Flush the adjacency block
	ENDIF.
	JRST RTRCH1		; down and check next
;This code implements the Node Talker Process.  Loop through all circuits,
;sending hello messages on any circuits that need them.  For point to
;point circuits, a hello message is sent when no other traffic has been sent
;within T3 (the hello timer period for this circuit).
;Ie: current_time - RCTLS >= RCTM3.  For broadcast circuits, a hello message
;is sent when when T3 seconds have elapsed since the last hello message
;(current_time - RCTLH >= RCTM3).

RTRTLK:	CALL DNGTIM		;Get the time again
	LOAD T3,RCTLS,(RC)	;Assume point to point for a moment
	TMNE RCBCT,(RC)		;Broadcast circuit?
	 LOAD T3,RCTLH,(RC)	; Yes, then compare against last hello time
	SUB T1,T3		;Compute amount of elapsed time
	MOVX T2,RCSHM		;Get the send hello message flag
	OPSTR <CAML T1,>,RCTM3,(RC) ;Have we been quiet for too long??
	 IORM T2,$RCSHM(RC)	; Yes, set send Hello message flag
	RET
; Here to send hello messages to all circuits that have RCSHM set

RTRSHM:	SKIPA RC,RTRCBQ		;Point to first circuit block
RTRSH1:	LOAD RC,RCNXT,(RC)	;Get next circuit block
	JUMPE RC,RTN		;And return when done...

	TMNN RCSHM,(RC)		;Do we need to send a hello message?
	 JRST RTRSH1		; Nope, try again

;We have just found a circuit that needs a hello message, determine what type
;of circuit it is, send the appropriate message.

	JN RCBCT,(RC),RTREHM	; Broadcst circuit?
	MOVX T1,NO.HEL+1	;LENGTH OF DATA SEGMENT
	CALL DNGMSG		;GET THAT MANY BYTES
	 JRST RTRSH1		;RESOURCE FAILURE, TRY NEXT CIRCUIT
	MOVE MB,T1		;SET UP MB WITH MESSAGE BLOCK POINTER
	XMOVEI T1,UD.MSD(MB)	;INITIALIZE THE USER DATA SEGMENT
	CALL DNPINI		;DO IT

	MOVX T1,NO.HEL		;GET THE NUMBER OF HELLOS IN MESSAGE
	CALL DNP1BY		;PLACE THE IMAGE COUNT IN THE MESSAGE

	MOVX T3,NO.HEL		;GET THE COUNT FOR LOOP
RTRSH2:	MOVX T1,HEL.LO		;WORD THAT GOES IN HELLO MESSSAGE
	CALL DNP1BY		;PUT ONE IN
	SOJG T3,RTRSH2		; AND DO ANOTHER ONE IF NEEDED

;Make up the router header.

	XMOVEI T1,RM.MSD(MB)	;POINT TO THE ROUTER PORTION OF MESSAGE
	CALL DNPINI		;INITIALIZE IT

	SETZ T1,		;BUILD THE BYTE IN T1
	MOVX T2,RCM.TT		;TYPE OF CONTROL MESSAGE IS TEST MESSAGE
	STOR T2,CMTYP,+T1	;PUT IN BYTE
	SETONE CMCTL,+T1	;SAY IT'S A CONTROL MESSAGE
	CALL DNP1BY		;PUT IN THE CTLFLG BYTE

	MOVE T1,RTRADR		;GET OUR LOCAL ADDRESS
	MOVE T3,RTRHOM		;GET MY AREA
	LOAD AJ,RCAJQ,(RC)	;Get the adjacency on this circuit
	TMNE AJPH4,(AJ)		;Phase IV node?
	 DPB T3,[POINTR(T1,RN%ARE)] ; AREA IF PHASE IV
	CALL DNP2BY		;PUT THE SOURCE ADDRESS IN MESSAGE

	XMOVEI T1,UD.MSD(MB)	;LINK THE USER DATA
	XMOVEI T2,RM.MSD(MB)	; TO THE ROUTER HEADER
	SETZ T3,		;DON'T CHANGE ANY POINTERS
	CALL DNLMSS		;LINK THEM

	TRACE RTR,<Sending hello message>
	CALL CALQOB		;QUEUE THE MESSAGE BLOCK
	SETZRO RCSHM,(RC)	;Indicate that we sent the hello
	JRST RTRSH1		; AND DO IT ALL AGAIN FOR THE NEXT CIRCUIT
;Here to send a hello message on a broadcast style circuit

RTREHM:	CALL DNGTIM		;Get the current time
	OPSTR <SUB T1,>,RCTLH,(RC) ;Compute time since the last hello
	CAMG T1,RTRTM2		;Has enough time elapsed?
	 JRST RTRSH1		; Nope, come back later

	SKIPGE ENFLG		; Endnode?
	 JRST RTRENH		; Yes, send endnode hello

	MOVE T1,RTNBRA		;Maximum number of Router BRA's
	ADDI T1,1		; plus one for the reserved
	IMULI T1,7		;Each one takes seven bytes
	OPSTR <CAMLE T1,>,RCBSZ,(RC) ;Will this exceed the minimum block size?
	 JRST RTREH6		;Yes, then don't do it
	CALL DNGMSG		;Get space for message
	 JRST RTRSH1		;Can't, try next circuit block
	MOVE MB,T1		;Set message pointer
	XMOVEI T1,UD.MSD(MB)	;Initialize user data segment
	CALL DNPINI
;
; Now for the 18 bytes of header
;
	SETZ T1,		;Start clean
	MOVX T2,RCM.RH		;Control message type is Router hello
	STOR T2,CMTYP,+T1
	SETONE CMCTL,+T1	;Say type is control
	CALL DNP1BY		;Write byte into message
	CALL RTRCHH		;Build common Hello header
	MOVX T1,IIT.L1		;We are a level 1 router
	CALL DNP1BY		;Write IINFO
	LOAD T1,RCRBS,(RC)	; and the blocksize we advertise
	CALL DNP2BY
	MOVE T1,RTRPRI		;Get our priority to be designated router
	CALL DNP1BY
	SETZ T1,		
	CALL DNP1BY		;Reserved byte (area)
	LOAD T1,RCTM3,(RC)	;Hello timer
	IDIVI T1,^D1000		;Convert it to seconds
	CALL DNP2BY
	SETZ T1,
	CALL DNP1BY		;Reserved (MPD)
;
; Set up E-LIST (List of Ethernet nodes we have heard from recently)
;
	CALL DNRPOS		;Get current position in the message
	STOR T1,RMMK1,(MB)	; and set mark in block
	SETZ T1,		;Reserve a byte for the count
	CALL DNP1BY
	MOVEI T1,^D7		;Write 7 reserved bytes
	CALL DNPZB
	CALL DNRPOS		;Get current position in the block
	STOR T1,RMMK2,(MB)	; and set mark in block
	CALL DNP1BY		;
	MOVEI P1,^D8		;Keep track of number written so far
	OPSTR <SKIPA AJ,>,RCAJQ,(RC) ;Pointer to adjacencies on this circuit
RTREH3:	LOAD AJ,AJNXT,(AJ)	;Step to next adjacency
	JUMPE AJ,RTREH4		;No more try next circuit
	LOAD T2,AJNTY,(AJ)	;Get adjacency's node type
	CAIE T2,ADJ.L2		;Phase IV level 2 router
	 CAIN T2,ADJ.L1		; or plain routing node?
	  TRNA
	JRST RTREH3		;Not a router, don't include it
	LOAD T2,AJSTA,(AJ)	;Get state of this adjacency
	SKIPN T2		;Valid state?
	BUG.(CHK,ROUAWS,ROUTER,SOFT,<Adjacency block in queue when state is unused>,,<

Cause:	An adjacency block has been left in the queue of active adjacencies
	but its state is unused.

>,RTREH3)
	LOAD T1,AJNAH,(AJ)	;Hi-order NI address
	LOAD T2,AJNAL,(AJ)	; and the low-order
	HRLZ T2,T2		;Put into string format
	CALL DNPENA		;Put ID into message
	LOAD T1,AJPRI,(AJ)	;Get adjacency's priority
	TRZ T1,200		;Set state to unknown
	LOAD T2,AJSTA,(AJ)	;Get the state of the adjacency
	CAIN T2,ADJ.UP		;Is it up?
	 TRO T1,200		; Yes, then say "known 2-way"
	CALL DNP1BY		;Write into message
	ADDI P1,7		;Account for the 7 bytes just written
	JRST RTREH3		;Check next adjacency

; Here when done with inserting routing adjacencies we know about.  Update
; counts in the message.

RTREH4:	CALL DNRPOS		;Get position at end of message
	OPSTR <EXCH T1,>,RMMK1,(MB) ;Save EOM count, get pos of 1st counter
	CALL DNGOPS		;Repostion to count
	MOVE T1,P1		;Get count
	CALL DNP1BY		;Write it
	SUBI P1,^D8		;Subtract reserved name and count
	LOAD T1,RMMK2,(MB)	;Bytes to second count
	CALL DNGOPS		;Reposition
	MOVE T1,P1		;Get second adjusted count
	CALL DNP1BY		;Write it
	LOAD T1,RMMK1,(MB)	;Get pointer to end of message
	CALL DNGOPS		;Point to the true end of the message

	TMNN RCDSR,(RC)		;Are we the designated router?
	IFSKP.
	  SETONE RMDRM,(MB)	;Yes, set flag so we resend to "all-endnodes"
	ENDIF.
	TRACE RTR,<Sending Ethernet router hello message>
	DMOVE T1,RTRALR		;Send to "ALL-ROUTERS"
	CALL RTRSNI		;Queue this message

;Send one to all-endnodes

RTREH6:	SETZRO RCSHM,(RC)	;We have sent the Hello
	CALL DNGTIM		;Time stamp
	STOR T1,RCTLH,(RC)	; Bamm!
	JRST RTRSH1		; and onto the next circuit


	SUBTTL RTRENH - Send Ethernet end-node hello

RTRENH:	CALL DNGTIM		;Get the current time
	OPSTR <SUB T1,>,RCTLH,(RC) ;Compute time since the last hello
	CAMG T1,RTRTM2		;Has enough time elapsed?
	 JRST RTRSH1		; Nope, come back later
	MOVEI T1,<NO.NIH+^D32>	;Number of bytes for hello message
	CALL DNGMSG		;Get space for message
	 JRST RTRSH1		;Can't, try next circuit block
	MOVE MB,T1		;Set message pointer
	XMOVEI T1,UD.MSD(MB)	;Initialize user data segment
	CALL DNPINI
;
; Now for the 18 bytes of header
;
	SETZ T1,		;Start clean
	MOVX T2,RCM.EH		;Control message type is Endnode hello
	STOR T2,CMTYP,+T1
	SETONE CMCTL,+T1	;Say type is control
	CALL DNP1BY		;Write byte into message
	CALL RTRCHH		;Build common Hello header
	MOVX T1,IIT.EN		;We are an ethernet endnode
	CALL DNP1BY		;Write IINFO
	LOAD T1,RCRBS,(RC)	;Get the blocksize we will advertise
	CALL DNP2BY
	SETZ T1,		
	CALL DNP1BY		;Reserved byte (area)
	MOVEI T1,^D8		;Write 8 bytes of zeros for the seed
	CALL DNPZB
	LOAD T1,RCDSH,(RC)	;Address of the designated router
	LOAD T2,RCDSL,(RC)	; we believe exists
	LSH T2,^D18		;Position as a string
	CALL DNPENA
	LOAD T1,RCTM3,(RC)	;Hello timer
	IDIVI T1,^D1000		;Convert it to seconds
	CALL DNP2BY
	SETZ T1,
	CALL DNP1BY		;Reserved 
	MOVX T1,NO.NIH		;Send NO.NIH bytes of test message
	CALL DNP1BY		;Place the image count in the message
	MOVX T3,NO.NIH		;Get the count for loop
RTREN2:	MOVX T1,HEL.LO		;Word that goes in hello messsage
	CALL DNP1BY		;Put one in
	SOJG T3,RTREN2		; and do another one if needed
	TRACE RTR,<Sending Ethernet endnode hello message>
	DMOVE T1,RTRALR		;Send to "all-routers"	
	CALL RTRSNI		;Queue this message

	SETZRO RCSHM,(RC)	;We have sent the Hello
	CALL DNGTIM		;Time stamp
	STOR T1,RCTLH,(RC)	; Bamm!
	JRST RTRSH1		; and onto the next circuit

	SUBTTL RTRCHH - Build common part of Ethernet hellos

RTRCHH:
	MOVE T1,RTRVER		;Version number of ROUTER
	CALL DNP1BY		;Stuff it
	SETZ T1,		;ECO # and User ECO # are 0
	CALL DNP2BY		;Stuff them
	MOVE T1,RTRHIO		;High order
	MOVE T2,RTRLOO		; and low order
	CALL DNPENA		;Write the address into the message
	RET
	SUBTTL DSRCHK - Check the DSR timer for each circuit

DSRCHK:	TMNN RCBCT,(RC)		;If not broadcast, we don't care
	 RET
	LOAD T1,RCDRT,(RC)	;Is "wait to be DSR" timer running
	 JUMPE T1,RTN		;If not just exit
	SOJG T1,DSRCK1		;Count down the timer
	SETONE RCDSR,(RC)	; and if it expires set the DSR flag
DSRCK1:	STOR T1,RCDRT,(RC)	;Save the new value
	RET

	SUBTTL Data Link Layer Interface
	SUBTTL Data Link Layer Interface -- RTRDLE - Entry into Router from DLL
; Call: 
;	T1/ Function Code
;	T2/ Router Circuit Block Address (except if DI.CCB)
;	T3/ Data (ususally pointer to message block)
;	T4/ Additional data
;
; Return: 
;	RET			;Always
;
; Uses: T1-T4
;
;We simply check the arguments, set up some arguments for the routines
;which handle the functions and dispatch to those routines.
;
;Note that we decide what to do if DECnet is not running at each
;individual interrupt handler.  For example if DECnet is turned off
;between the buffer request and the input done, we could have problems.
;We do not accept and new buffer requests or protocol ups once DECnet
;has been turned off.

	INTERN RTRDLE
	XRESCD

RTRDLE:
IFN FTPARANOIA,<
	CAXL T1,DI.ODN		;RANGE CHECK THE
	 CAXLE T1,DI.ICB	; FUNCTION CODE
	  BUG.(CHK,ROUIFD,ROUTER,SOFT,<Illegal function code from the DLL>,,<

Cause:	The module DNADLL and ROUTER get their function code from the D36PAR.
	They must have been compiled from different UNVs.

Action:	Compile from the same D36PAR.UNV

  >,RTN)
> ;END IFN FTPARANOIA
	SAVEAC <MB,MS,RC,AJ,P1,P2,Q1,Q2>    ;SAVE SOME AC'S
	STKVAR <DSPWRD>		; Place to save word from dispatch table
	MOVE RC,T2		;GET THE ROUTER CIRCUIT BLOCK
	MOVE P1,RTRDTB-1(T1)	;GET BITS AND DISPATCH FOR THIS FUNCTION.
	HRRM T1,DSPWRD		; Save function in case can't dispatch
	HLLM P1,DSPWRD		; Save function flags

RTRDS2:	TXZN P1,RD.CMB		;SHOULD I CHECK FOR A VALID MB?
	JRST RTRDS4		;NO, THIS FUNCTION DOESN'T HAVE AN MB.
	SKIPN MB,T3		;COPY THE MB
RTRDS3:	BUG.(CHK,ROUBMB,ROUTER,SOFT,<Bad message block pointer>,,<

Cause:	DNADLL has called RTRDLE with a function requiring a message
	block, and the pointer supplied (in T3) is either 0 or out of
	range.

Action:	Determine why DNADLL gave a bogus pointer since he
	originally should have obtained it from us.
>,RTRDS9)
IFN FTPARANOIA,<
	CALL DNCHMB##		;ASK D36COM TO CHECK THIS POINTER FOR US
	JRST RTRDS9		;BAD POINTER, FORGET THIS REQUEST.
> ;END IFN FTPARANOIA
RTRDS4:	TXZN P1,RD.CKS		;SHOULD WE CHECK DECNET/CIRCUIT STATE?
	JRST RTRDS6		;DON'T WORRY ABOUT STATE
	MOVE T1,DCNSTA		;Get DECnet state
	CAIE T1,DS.ON		;Is it on?
	 JRST RTRDS7		;NO, RETURN NOW.
	LOAD T2,RCSTA,(RC)	;GET THE CIRCUIT STATE
	CAXE T2,RCS.OF		;If state is off
	 CAXN T2,RCS.RJ		; or rejected
	  TRNA
RTRDS6:	CALLRET (P1)		;NO, CALL APPROPRIATE ROUTINE AND RETURN

RTRDS7:	MOVE P1,DSPWRD		; Recover dispatch flags
	TXNE P1,RD.CMB		; Does this function have an MB?
	 CALL FREMSG		; Yes, dispose of it properly, MB = message
	TLZ P1,-1		; Remove flags
	CAIE P1,DI.ODN		; If output done then...
	 JRST RTRDS9
	DECR RCCMQ,(RC)		;  message no longer queued to the DLL
RTRDS9:	SETZ T1,		;MAKE SURE THAT THE REQUESTOR UNDERSTANDS
	RET			; THAT THE REQUEST FAILED

RD.CRC==1B0			;CHECK RC. MAKE SURE IT IS NON-ZERO, AND IF
				; FTPARANOIA, RANGE CHECK AND FIND ON RTRCBQ
RD.CMB==1B1			;CHECK MB. MAKE SURE IT IS NON-ZERO, AND IF
				; FTPARANOIA, CALL DNCHFB TO ENSURE IT IS GOOD.
RD.CKS==1B2			;CHECK STATES BEFORE CALLING. IF EITHER DECNET
				; OR LINE STATE IS OFF, SIMPLY ZERO T1 AND


				; RETURN.

RTRDTB:	RD.CRC+RD.CKS+RD.CMB+RTIOTC	;OUTPUT DONE
	RD.CRC+RD.CKS+RD.CMB+RTIINC	;INPUT COMPLETE
	       RD.CKS+RD.CRC+RTILSC	;LINE STATE CHANGE
			     RTICCB	;CREATE CIRCUIT

	SUBTTL Data Link Layer Interface -- RTICCB - Create circuit block

;RTICCB - Create a circuit block
;
; Call:
;	T2/ Data Link Block address
;	T3/ Line-ID
;	T4/ Blocksize to advertise for this circuit
;
;	We can not get here if the circuit state is OFF
;
; Return:
;	RET			;Always, returns with T1 containing circuit
;				; block address or zero if it couldn't
;				; allocate a new one
;
; Uses: T1-T4
;

RTICCB:	DMOVE P1,T2		; Save DLB and line ID
	MOVE Q1,T4		;  and blocksize
	MOVE T1,P2		; Get the LINE-ID
	SKIPN ENFLG		; Endnode?
	IFSKP.			; Yes
	  LOAD T2,LIDEV,+T1	; Then get the device type on this line
	  CAIE T2,LD.ETH	; Is it an ethernet?
	   JRST RTRCCE		; No, then don't initialize with it
	ENDIF.
	CALL RTRMCB		; Make up the circuit block
	 JRST RTRCCE		;  and return if can't allocate memory
	MOVE RC,T1		; Circuit block pointer we got.
	STOR P1,RCDLB,(RC)	; Save data link block address for later calls
	STOR Q1,RCRBS,(RC)	;  and maximum blocksize we can advertise
	ENDQUE RC,RTRCBQ,RC.NXT,T1 ; Add circuit block to queue
	JN RCBCT,(RC),RTRCC1	; If broadcast don't make adjacency block
	JN RCAJQ,(RC),RTRCC1	; If we have an adjacency block don't make one
	SETO T1,		; Flag to indicate we expect routing adjacency
	CALL RTRMAJ		; Make up the adjacency block
	 JRST RTRCCE		; Error return
RTRCC1:	LOAD T1,LIDEV,+RC.LID(RC)  ; Get circuit/line type
	CAIE T1,LD.DDP		; Is it a DDP?
	 JRST RTRCC2		; No, treat normally
	CALL R2KINI		; Yes, default DDPs to STATE ON
	 JFCL			; Ho hum
RTRCC2:	MOVE T1,RC		; Return circuit block address as callback ID
	RET

RTRCCE:	SETZ T1,		; Indicate failure
	RET

	SUBTTL Data Link Layer Interface -- RTILSC - Process line state change

; RTILSC - "Line state change" interrupt from the DLL
; 
; Call:
;	T2/ Circuit block address
;	T3/ New Line State
;
; Return:
;	RET			;ALWAYS
;
; Uses:

RTILSC:	TRACE RTR,<Received line state change from DLL>
	CAIE T3,LS.OFF		; Is new state off?
	 JRST RTION		; No, must be on

RTIDWN:	MOVX T1,RS.LSL		;EVENT REASON: CIRCUIT SYNCHRONIZATION LOST
;*** Was it ever up ?? No event if not
	CALL RTELDL		;REPORT CIRCUIT DOWN - CIRCUIT FAULT EVENT
	CALL SETRCC		;SET RECOMPUTE ROUTING FLAGS
	CALL RTRZCB		;CLEAN OUT THE CIRCUIT BLOCK
	CALL RTRSAO		;SET ANY REMAINING ADJACENCIES TO UNUSED
	LOAD T1,RCSTA,(RC)	;Get current state
	MOVX T2,RCS.FA		; Failed state code
	CAIG T1,RCS.RJ		; If off/rejected, don't change state
	IFSKP.
	  STOR T2,RCSTA,(RC)	;STORE AS CURRENT STATE FOR THIS CIRCUIT
	  TMNN RCBCT,(RC)	;If broadcast, then close portal
	  IFSKP.
	    CALL R2KCLS		;Close it
	    CALL DNGTIM		;Get current uptime
	    ADDI T1,^D1000	;Wait a second
	    STOR T1,RCTIN,(RC)	; to reopen the portal
	  ENDIF.
	ENDIF.
	RET

RTION:	LOAD T2,RCSTA,(RC)	;GET CIRCUIT STATE
	CAIN T2,RCS.RN		; Are we already on?
	 RET			; Yes, ignore notification	 
	CAIE T2,RCS.WT		;MAKE SURE WE'RE IN WAITING STATE
	 CAIN T2,RCS.FA		; or failed
	  TRNA
	JRST [ETRACE RTR,<Line state to ON rcv'd in wrong state>
	      CALLRET CIRFAI]	;TRY TO REINITIALIZE THE CIRCUIT
	LOAD T2,RCLID,(RC)	;Get the line-id for the event
	EVENT RE.LUP,<Circuit up> ;Generate the event
	TMNN RCBCT,(RC)		; Is this a broadcast circuit?
	IFSKP.
	  MOVX T1,RCS.RN	; The circuit state is run
	  STOR T1,RCSTA,(RC)	;
	  SETONE RCSHM,(RC)	; Send a hello message to start things off
	ELSE.
	  MOVX T1,RCS.TI	;NEW STATE = TRANSPORT INITIALIZATION WAIT
	  STOR T1,RCSTA,(RC)	;STORE THE NEW CIRCUIT STATE
	  CALL RTRSTI
	ENDIF.
	RET

	SUBTTL Data Link Layer Interface -- RTIOTC - Process Output Complete

;RTIOTC - Routine to process output complete interrupt
;
; Call: 
;	RC/ Circuit block address
;	T3/ Message block address
;	T4/ Status
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T3

RTIOTC:	TRACE RTR,<Output Complete>
	MOVE MB,T3		;GET THE MESSAGE BLOCK ADDRESS
	MOVE T1,MB		;ARG FOR DNLENG
	CALL DNLENG		;FIGURE OUT THE LENGTH OF THE MESSAGE
	DECR RCCMQ,(RC)	 	;DECREMENT MESSAGES QUEUED COUNT

	CALL DNGTIM		;Get the current time
	STOR T1,RCTLS,(RC)	;Update time last sent.  It is ok to do this on
				; ethernets for "all-routers & "all-endnodes"
				; since they use TLH for the "talker" process.
	TMNN RMDRM,(MB)		;Should this be resent to "all-endnodes"?
	IFSKP.
	  SETZRO RMDRM,(MB)	;Yes, but resend it only once
	  DMOVE T1,RTRAEN	;Set "all-endnodes" address
	  CALLRET RTRSNI	
	ENDIF.
	TMNN RMICP,(MB)		;Is this from the local node?
	IFSKP.
	  INCR RCCTS,(RC)	;No, increment the transit count
    IFN FTRTST,<
	  MOVEI P1,TSTBLK
	  CALL TSTTIM
	  STOR T1,TRTOC,(P1)
	  LOAD T2,TRTAF,(P1)
	  SUB T1,T2
	  STOR T1,TRTFD,(P1)
    >
	  CALLRET FREMSG	;  and discard the packet
	ENDIF.
	INCR RCCLC,(RC)		;OTHERWISE, INCREMENT THE LOCAL COUNT
	CALLRET FREMSG		;GIVE PACKET TO BACK TO NSP, OR FREE IT

	SUBTTL Data Link Layer Interface -- RTIINC - Process Input Complete

;RTIINC - Process input complete interrupt
;
; Call: 
;	RC/ Circuit block pointer
;	T3/ Pointer to message block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T3

RTIINC:	TRACE RTR,<Input Complete>
	MOVE MB,T3		;GET POINTER TO MESSAGE BLOCK
	STOR RC,RMICP,(MB)	;STORE THE INPUT CIRCUIT ADDRESS

;****************************************
	TORESCD
	DNSNUP(RTRITR)		;Label for DNSNUP to find input messages
	TOXRESCD
;****************************************
	LOAD AJ,RCAJQ,(RC)	;Get first adjacency in queue
	JE RCBCT,(RC),RTRIN4	;If point-to-point be satisfied with it
	LOAD T1,MBSRC,(MB)	;Yes, get address of sender
	CALL RTRGAJ		;Look for adjacency with this address...
	 SETZ AJ,		;Not in our area, no adjacency block
RTRIN4:	STOR AJ,RMIAP,(MB)	; and store it for the forwarding routine
	SETZRO RMOAP,(MB)	;SO WE DON'T CONFUSE OURSELVES
	SETZRO RMOCP,(MB)

	MOVE T1,MB		;COPY POINTER TO MESSAGE BLOCK
	CALL DNLENG		;SIZE OF THIS MESSAGE
	SKIPGE ENFLG		;Endnode?
	IFSKP.
	  CALL DNGTIM		; NO, GET THE CURRENT TIME
	  SKIPE AJ		; Skip if this adjacency is not initialized
	  STOR T1,AJTLR,(AJ)	;STORE TIME WE LAST HEARD FOR NEIGHBOR
	ENDIF.
	CALL RTRHDP		;PARSE THE HEADER
	 JSP T1,RTEMFE		;REPORT THE MESSAGE FORMAT ERROR EVENT
	JE RMCTL,(MB),RTIIN1	;IF IT'S A DATA MESSAGE, PROCESS IT

;We have a control message here, find the type and dispatch

	LOAD T1,RCSTA,(RC)	;GET THE CIRCUIT'S STATE
	LOAD T2,RMCTY,(MB)	;GET THE CONTROL MESSAGE TYPE
	CALL @RTIIND(T2)	; AND DISPATCH ON THAT TYPE
	RET			;RETURN TO SENDER

RTIIND:	IFIW <RTCINI&777777>	;ROUTER INITIALIZATION (0)
	IFIW <RTCVER&777777>	;ROUTER VERIFICATION (1)
	IFIW <RTCTST&777777>	;ROUTER TEST MESSAGE (2)
	IFIW <RTCRTE&777777>	;ROUTER ROUTING MESSAGE (3)
	IFIW <RTCL2M&777777>	;ROUTER LEVEL 2 ROUTING MESSAGE (4)
	IFIW <RTCERH&777777>	;NI ROUTER HELLO MESSAGE (5)
	IFIW <RTCNIE&777777>	;NI ENDNODE HELLO MESSAGE (6)
	IFIW <RTCILL&777777>	;Illegal message type

;Here with a data message.

RTIIN1:
IFN FTRTST,<
	LOAD T1,MBSRC,(MB)		; Get source node
	LOAD T2,MBDST,(MB)		;  and destination
	CAMN T1,T2			; The same?
	 JRST [SETONE RMTST,(MB)	; Yes, assume this is a test message
	       MOVEI P1,TSTBLK		; Get address of test data block
	       LOAD T1,TRTND,(P1)	; Get the loop node address
	       STOR T1,MBDST,(MB)	; Save that as the destination
	       CALL TSTTIM		; Get the uptime in ms.
	       STOR T1,TRTIC,(P1)	; Save it as time received here
	       LOAD T2,TRTAF,(P1)	;
	       SUB T1,T2		; Compute elapsed time
	       STOR T1,TRTTA,(P1)	; Save for interested parties
	       CALL TSTTIM		; Get time again
	       STOR T1,TRTAF,(P1)	;  and save it as time at RTRFWD
	       JRST .+1]
>
	TMNE MBEBF,(MB)		;IS THIS MESSAGE IN AN EMERGENCY BUFFER?
	JRST RTIINF		;YES, HANDLE IT DIFFERENTLY

RTIIN2:	LOAD T1,RCSTA,(RC)	;GET THE STATE OF THE CIRCUIT
	CAIE T1,RCS.TT		;SEE IF THIS IS A GOOD STATE TO BE IN
	CAIN T1,RCS.RN		; FOR RECIEVING DATA
	TRNA			;WE SEEM TO BE OK
	JRST [ETRACE RTR,<Circuit in wrong state for forwarding>
	      CALLRET FREMSG]
	CALLRET RTRFWD		;FORWARD THE MESSAGE

;Here to discard an arriving packet due to congestion.

RTIINF:	OPSTR <SKIPGE T2,>,MBDST,(MB) ;GET THE DESTINATION
	 CALLRET RTRFOR		;DESTINATION IS OUT-OF-RANGE
	LDB T2,[POINTR(T2,RN%NOD)] ;GET THE NODE NUMBER
	CAMLE T2,RTRMXN		;RANGE CHECK IT
	 CALLRET RTRFOR		;DESTINATION IS OUT-OF-RANGE
	ADD T2,RTRNRV		;GET THE NODE'S ROUTING VECTOR ENTRY
	TMNE RNLCL,(T2)		;IF IT WAS SUPPOSED TO COME TO US
	IFSKP.
	  INCR RCCAL,(RC)	;REPORT IT AS ARRIVED CONGESTION LOSS
	ELSE.
	  INCR RCCTL,(RC)	; else increment the transit congestion loss
	ENDIF.
	CALLRET FREMSG		;FREE MSG BLK & RETURN

	SUBTTL RTRSTI - Send routing intitialization message

RTRSTI:	MOVX T1,TI.MXL		;MAX LENGTH OF A TI MESSAGE
	CALL DNGMSG		; USER MESSAGE AREA
	 CALLRET CIRFAI		;CAN'T DO IT, Close the port and try again
	MOVE MB,T1		;MAKE MB POINT TO MESSAGE
	XMOVEI T1,UD.MSD(MB)	;POINT TO THE USER-DATA MSD
	CALL DNPINI		;INITIALIZE MS & THE MSD

	SETZ T1,		;MAKE UP FIRST BYTE IN T1/ 0
	SETONE CMCTL,+T1	;TURN ON THE CONTROL MESSAGE BIT
IFN RCM.TI,<
	MOVX T2,RCM.TI		;TYPE IS ROUTER INIT
	STOR T2,CMTYP,+T1	;AND STORE THAT IN THE TYPE FIELD
>
	CALL DNP1BY		;WRITE THE FIRST BYTE

	LOAD AJ,RCAJQ,(RC)	;GET THE ADJACENCY
	JUMPE AJ,CIRFAI		;If we don't have one reinitialize
	MOVE T1,RTRADR		;GET OUR ADDRESS
	MOVE T2,RTRHOM		;GET MY AREA
	LOAD T3,RCLID,(RC)	;GET CIRCUIT IDENTIFIER
	LOAD T3,LIDEV,+T3	;GET LINE TYPE
	LOAD T4,AJVER,(AJ)	;GET ADJACENCY VERSION
	CAIE T3,LD.DTE		;IF A DTE CIRCUIT
	CAIN T4,1		;OR A PHASE III NODE
	SKIPA			;THEN DON'T INCLUDE AREA NUMBER
	 DPB T2,[POINTR(T1,RN%ARE)]
	CALL DNP2BY		;STORE THE NODE ADDRESS
	
;Build TIINFO.

	SETZ T1,		;START BUILDING TIINFO FIELD
	MOVEI T2,ANT.RT		; Assume we are a routing node
	MOVEI T3,RNT.L1		; See what type of node we are this time
	CAME T3,RTRNTY		; Are we a routing node?
	 MOVEI T2,ANT.NR	; No, then say we are non-routing
	STOR T2,TINTY,+T1	;STORE THE NODE TYPE
	SKIPE RTRVRQ		;IS VERIFICATION REQUIRED?
	TXO T1,TIVER		;YES, TELL HIM ABOUT IT
	CALL DNP1BY		;PUT THE BYTE IN

;Give a blocksize.

	LOAD T1,RCRBS,(RC)	;GET THE BLOCKSIZE
	CALL DNP2BY		;TWO BYTES WORTH

;Fill in version, etc.

	LOAD T3,AJVER,(AJ)	;GET THE ADJACENCY ROUTING VERSION
	CAMN T3,RTRVER		;IS IT THE SAME AS OURS?
	 JRST FILVER		;YES, THEN FILL IN THE VERSION NUMBER
	ADDI T3,1		;SEE IF ITS ONE MINUS OURS
	CAMN T3,RTRVER		;IS IT?
	 JRST [LOAD T3,AJVER,(AJ) ;THEN USE HIS VERSION NUMBER
	       JRST FILVR1]	;STORE IN THE MESSAGE

FILVER:	MOVE T3,RTRVER		;VERSION OF RTR
FILVR1:	MOVE T1,T3		;GET VERSION TO SEND IN INIT
	CALL DNP1BY		;PUT IT IN MESSAGE
	LOAD T1,AJECO,(AJ)	;GET THE ECO LEVEL
	CALL DNP1BY		;PUT IT IN, ALSO

	MOVE T1,RTRCUS		;CUSTOMER ARGUMENT NUMBER
	CALL DNP1BY		;AND STORE THAT ALSO

;Now for the HELLO timer

	LOAD T1,RCTM3,(RC)	;GET TIMER VALUE FOR THIS CIRCUIT
	IDIVI T1,^D1000		;SEND TIMER AS SECONDS
	CAIL T3,2		;ONLY OUTPUT IF PHASE IV OR GREATER
	 CALL DNP2BY		;TWO BYTES WORTH

;Now fill in one reserved byte

	SETZ T1,		;USE A ZERO
	CALL DNP1BY		;PUT IN THE BYTE

;Here we give the message to the DLL to send.

	TRACE RTR,<Sending Router Init message>
	CALL CALQOB		;OUTPUT THE MESSAGE
	CALL DNGTIM		;GET THE CURRENT TIME
	STOR T1,RCTIN,(RC)	;RECORD THE TIME THAT WE SENT THE TI
	RET

	SUBTTL Control Message Processors -- RTCINI - Inititialization Messages

;RTCINI - Process Incoming Router Init Message
;
; Call: 
;	T1/ Circuit State
;	RC/ Ptr to Circuit Block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4

RTCINI:	TRACE RTR,<Processing Router Init message>
	CAIE T1,RCS.TI		;ARE WE IN RTR INIT WAIT STATE?
	JRST [ETRACE RTR,<RTR Init message rcvd in wrong state>
	      MOVX T1,RS.UPT	;EVENT REASON: UNEXPECTED PACKET LOSS
	      CALL RTELDS	;REPORT THE CIRCUIT DOWN - SOFTWARE FAULT EVENT
	      CALLRET CIRFAI]	;HIT THE LINE WITH A BIGGER SLEDGEHAMMER
	LOAD T1,MBSRC,(MB)	;GET THE SOURCE NODE
	LDB T2,[POINTR(T1,RN%ARE)] ;GET THE AREA NUMBER
	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	SKIPN T2		; If Phase III...
	 MOVE T2,RTRHOM		;  then default the area
	CAME T2,RTRHOM		;MY AREA?
	 JRST RTCNHA		;NO, THEN DON'T DO IT
RTCI9N:	CAIL T1,1		;CHECK NODE ADDRESS
	CAMLE T1,RTRMXN		; FOR REASONABLENESS
RTCNHA:	JRST [MOVX T1,RS.ANO	;REASON: ADJACENT NODE ADDRESS OUT OF RANGE
	      CALLRET RTEINO]	;REPORT THE INITIALIZATION FAILURE EVENT

	STOR T1,AJNAN,(AJ)	;Store the node's number
	STOR T2,AJNAA,(AJ)	; and node's area

	MOVX T1,ADJ.UP		;SAY THE ADJACENCY IS NOW UP
	STOR T1,AJSTA,(AJ)

	MOVE T1,MB		;POINT TO INPUT MESSAGE
	CALL DNLENG		;CALCULATE THE LENGTH
	CAIL T1,^D6		;IS IT THE CORRECT LENGTH FOR RTR INIT MSGS?
	 CALL DNG1BY		;GET THE INITINFO FIELD
	  JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR
;	TXNE T1,TIRSV		;IS THE RESERVED FIELD ZERO?
;	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT

	MOVX T2,AJVRQ		;GET THE VERFIFY AND REQUIRED BIT
	ANDCAM T2,AJ.VRQ(AJ)	;ASSUME THAT VERIFICATION ISN'T REQUIRED
	TXNE T1,TIVER		;IS IT REALLY REQUIRED?
	 IORM T2,AJ.VRQ(AJ)	;YES, SET IT

	MOVX T2,AJBLO		;GET THE BLOCKING REQUESTED BIT
	ANDCAM T2,AJ.VRQ(AJ)	;ASSUME THAT BLOCKING WAS NOT REQUESTED
	TXNE T1,TIBLO		;BLOCKING REQUESTED?
	 IORM T2,AJ.VRQ(AJ)	;YES, THEN SET IT

	LOAD T2,TINTY,+T1	;GET NODE TYPE FROM BYTE
	STOR T2,AJNTY,(AJ)	;STORE THE NODE TYPE IN CIRCUIT BLOCK
  ;Get the blocksize

	CALL DNG2BY		;GET TWO BYTE BLOCKSIZE
	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT
	CAMGE T1,RTRBSZ		;IS THE BLOCKSIZE REASONABLE
	JRST [MOVX T1,RS.ABS	;EVENT REASON: Adjacency block size too small
	      CALLRET RTEINO]	;REPORT THE INITIALIZATION FAILURE EVENT
	STOR T1,RCBSZ,(RC)	;Save for use when building routing updates
	STOR T1,AJBSZ,(AJ)	; and here for network management

;Next comes the Version number, ECO number and Customer args.

	CALL DNG1BY		;GET THE VERSION NUMBER
	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT
	STOR T1,AJVER,(AJ)	;STORE IT IN CIRCUIT BLOCK
	CAIE T1,1		;IS THE ADJACENCY PHASE III OR IV?
	IFSKP.
	  LOAD T2,AJNTY,(AJ)	;(III) GET THE NODE TYPE FROM ABOVE (TIINFO)
	  SETO T1,		;Use T1 to hold node type for adjacency block
	  CAIN T2,ANT.RT	;IS IT A ROUTING NODE?
	   MOVX T1,ADJ.3F	;YUP, MUST BE A PHASE III FULL NODE
	  CAIN T2,ANT.NR	;IS IT A NON-ROUTING NODE?
	   MOVX T1,ADJ.3S	;YES, WELL ITS A PHASE III SMALL NODE
	  SKIPGE T1		;DID WE GET ONE?
	   JSP T1,RTEMFE	;++MESSAGE FORMAT ERROR EVENT
	  STOR T1,AJNTY,(AJ)	;SAVE IT
	  SETZRO AJPH4,(AJ)	;Note that this node is not a PH4 node
	ELSE.
	  LOAD T2,AJNTY,(AJ)	; (IV) GET THE NODE TYPE FROM ABOVE (TIINFO)
	  SETO T1,		;Use -1 as a flag
	  CAXN T2,ANT.L1	;IS IT A LEVEL 1 ROUTER
	   MOVX T1,ADJ.L1	;SAY SO
	  CAXN T2,ANT.L2	;LEVEL 2
	   MOVX T1,ADJ.L2	;SAY SO
	  CAXN T2,ANT.NR	;NON-ROUTING
	   MOVX T1,ADJ.LN	;SAY SO
	  SKIPGE T1		;HAVE ONE?
	   JSP T1,RTEMFE	;NO, THEN WE HAVE AN ERROR
	  STOR T1,AJNTY,(AJ)	;SAVE IT
	  SETONE AJPH4,(AJ) 	;Indicate we have a Phase IV node
	ENDIF.

RTCIN0:	CALL DNG1BY		;GET THE ECO NUMBER
	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT
	STOR T1,AJECO,(AJ)	;STORE IT
	CALL DNG1BY		;ALSO THE CUSTOMER ARGUMENT
	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT
	STOR T1,AJCUS,(AJ)	;STORE IT ALSO

; Get the hello timer from the router init message, there is one

	TMNN AJPH4,(AJ)		;Phase IV node?
	 JRST RTCIN1		;NO, THEN HELLO TIMER IS NOT THERE
	CALL DNG2BY		;GET THE HELLO TIMER NOW
	 JSP T1,RTEMFE		;MESSAGE FORMAT ERROR+++
	IMULI T1,%RTT3M*^D1000	;Hello timer multiplier and convert to ms.
	STOR T1,AJNHT,(AJ)	;SAVE AS NEIGHBOR'S HELLO TIMER

;Now we have read the whole message, next send a verification if necessary.

RTCIN1:	CALL DNG1BY		;Now get the reserved field
	 JSP T1,RTEMFE		;None, bum message
	SKIPE T1		;It must be zero
	 JSP T1,RTEMFE		;We can't understand non-zero
	TMNE AJVRQ,(AJ)		;DOES HE WANT VERIFICATION?
	IFSKP.			; No, he doesn't
	  SETZRO RCTIN,(RC)	;ZERO THE TIME WE SENT TI TIMER
	  CALL FREMSG		; FREE MESSAGE
	ELSE.
	  MOVE T1,MB		;PASS PTR TO MSG BLK TO DNMINI
	  LDB T2,[POINT 8,RTRXPW,7];GET COUNT OF PASSWORD BYTES
	  AOJ T2,		; + COUNT BYTE AS LENGTH OF DATA SPACE
	  CALL DNMINI		;FIX UP THE MSG BLK
	   RET			;PROPOGATE ERROR RETURN
	  XMOVEI T1,RM.MSD(MB)	;GET ADDR OF ROUTER MSD
	  CALL DNPINI		;GET READY TO STICK BYTES IN MESSAGE

	  SETZ T1,		;BUILD FIRST BYTE IN T1
	  MOVX T2,RCM.TV	;TYPE = ROUTER VERIFICATION
	  STOR T2,CMTYP,+T1	;FILL IN FIELD IN AC
	  SETONE CMCTL,+T1	;TV MESSAGE IS A CONTROL MESSAGE
	  CALL DNP1BY		;STORE IN MESSAGE

	  MOVE T1,RTRADR	;GET OUR (ROUTER'S) ADDRESS
	  MOVE T3,RTRHOM	;GET MY AREA
	  TMNE AJPH4,(AJ)	;Phase IV node?
	   DPB T3,[POINTR(T1,RN%ARE)] ; AREA IF PHASE IV
	  CALL DNP2BY		;STORE THAT

;Put a password at the end of the verification msg

	  MOVE T3,[POINT 8,RTRXPW];POINT TO PASSWORD STRING
	  ILDB T2,T3		;GET COUNT OF BYTES IN PASSWORD
	  SKIPA T1,T2		;COPY COUNT AS DATA BYTE
RTCIN2:	   ILDB T1,T3		;GET NEXT DATA BYTE
	  CALL DNP1BY		;TELL MSG ABOUT IT
	  SOJGE T2,RTCIN2	;CONTINUE TILL DONE

	  TRACE RTR,<Sending Router Verification message>
	  CALL CALQOB		;OUTPUT THE MESSAGE

	  CALL DNGTIM		;GET CURRENT TIME
	  STOR T1,RCTIN,(RC)	;REGISTER THE TIME THAT WE SENT THE TV
	ENDIF.

;	MOVX T1,RCS.TV		;ASSUME THAT WE REQUIRE VERIFICATION
;	SKIPN RTRVRQ		;DO WE REQUIRE VERIFICATION?
	MOVX T1,RCS.RN		;NO, WE'RE NOW IN RUN STATE
	STOR T1,RCSTA,(RC)	;INDICATE THAT IN THE CIRCUIT BLOCK
	MOVX T1,ADJ.UP		;SAY THE ADJACENCY IS UP NOW
	STOR T1,AJSTA,(AJ)	; AND RUNNING
	MOVN T1,RTRTM1		;SET TIMER TO FORCE SENDING ROUTING MESSAGE
	STOR T1,RCTLR,(RC) 	;...
	LOAD T2,RCLID,(RC)	;Get the device ID
	LOAD T3,AJADR,(AJ)	;Address needed for the event
	EVENT RE.AUP,<Adjacency up>
	LOAD T1,AJNAN,(AJ)	;Get node number
	CALL SETRCF		;Set recompute routing flags
	MOVN T1,RTRTM1		;Reset the time of last routing
	STOR T1,RCTLR,(RC)	; message
	LOAD T1,AJVER,(AJ)	; Get adjacent node's version
	CAIN T1,1		; Phase III node?
	CALLRET RTRSTI		; Yes, send TI response and return
	RET			; AND RETURN

	SUBTTL Control Message Processors -- RTCVER - Verification Messages

;RTCVER - Routine to handle verification messages
;
; Call: 
;	T1/ Circuit State
;	RC/ Pointer to Circuit Block
;	MB/ Pointer to Message Block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4

RTCVER:	TRACE RTR,<Processing Router Verification message>
	CAIE T1,RCS.TV		;ARE WE WAITING FOR A VERIFICATION
	JRST [ETRACE RTR,<RTR Verification message rcvd in wrong state>
	      MOVX T1,RS.UPT	;EVENT REASON: UNEXPECTED PACKET type
	      CALLRET RTELDS]	;REPORT CIRCUIT DOWN - SOFTWARE ERROR EVENT
	CALL DNG1BY		;GET FIRST BYTE (IMAGE COUNT)
	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT
	MOVE P1,T1		;PRESERVE IT FOR A WHILE
	MOVE T1,MB		;POINT TO MESSAGE
	CALL DNLENG		;CALCULATE LENGTH
	CAMLE P1,T1		;DOES IT LOOK LIKE A REASONABLE LENGTH?
	CALLRET RTEVRJ		;++VERIFICATION REJECT

	MOVX T1,RCS.RN		;IF WE GOT THIS FAR,
	STOR T1,RCSTA,(RC)	; WE DESERVE TO BE IN A GOOD STATE
	CALLRET FREMSG		;TOSS MESSAGE AND RETURN
	SUBTTL Control Message Processors -- RTCRTE - Routing Messages

;RTCRTE - Routine to handle incoming routing message
;
; Call: 
;	T1/ State of circuit
;	AJ/ Pointer to adjacency block
;	RC/ Pointer to circuit block
;	MB/ Pointer to message block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4

RTCRTE:	TRACE RTR,<Processing Routing message>
	CAIE T1,RCS.TT		;CHECK OUR STATE FOR EITHER TEST OR
	CAIN T1,RCS.RN		; OK STATE
	TRNA			;WE SEEM ALRIGHT
	JRST [ETRACE RTR,<Routing message rcvd in wrong state>
	      MOVX T1,RS.UPT	;EVENT REASON: UNEXPECTED PACKET RECEIVED
	      CALLRET RTELDS]	;REPORT CIRCUIT DOWN - SOFTWARE FAULT EVENT
	SKIPN ENFLG		; Endnode?
	IFSKP.
	  CALL DNG1BY		; Get reserved byte	  
	   CALLRET FREMSG	; Forget any error
	  LOAD T1,MBSRC,(MB)	; Get the source address
	  LDB T2,[POINTR (T1,RN%ARE)] ; Get the area number
	  LDB T3,[POINTR(T1,RN%NOD)] ; Get the node number
	  LOAD T4,RCAJQ,(RC)
	  LOAD T4,AJNAN,(T4)	; Get DSR low order address
	  CAIE T3,(T4)		; Same as the sender of message?
	   JRST FREMSG		; No, then don't bother processing
	  SKIPN T2		; Phase IV area number there?
	   CALLRET FREMSG
	  CAME T2,RTRHOM	; Is packet from our area
	   CALLRET FREMSG
	  LOAD AJ,RCAJQ,(RC)	; Get our only adjacency
	  JUMPE AJ,FREMSG
	ELSE.
	  JUMPE AJ,FREMSG	; If no adjacency, toss message
	  CALL CK2WAY		;Do we have a known two-way adjacency?
	   CALLRET FREMSG 	;No, can't accept this message
	  TMNN AJPH4,(AJ)	; Phase 4 node?
	  IFSKP.
	    LOAD T1,AJNTY,(AJ)	;Get type
	    CAXE T1,ADJ.LN	;Is it non-routing?
	    IFSKP.
	      LOAD T1,AJNAN,(AJ) ;Get address and give a buginf
	      BUG.(INF,ROURFN,ROUTER,SOFT,<Routing message received from non-routing node>,<<T1,Address>>,<

Cause:	We have received a routing message from a node we believe to be an
	endnode so we will have no vector to store it in.
	count.

Action:	Check the address of the node and then see if it thinks it is a
	routing/non-routing node.

>,FREMSG)
	    ENDIF.
	    CALL DNG1BY		; Get expected reserved byte
	     JSP T1,RTEMFE	;++Message format error
	  ENDIF.
	  LOAD T1,MBSRC,(MB)	;GET THE SOURCE ADDRESS
	  LDB T2,[POINTR (T1,RN%ARE)] ;GET THE AREA NUMBER
	  LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	  SKIPN T2		;Phase IV area number there?
	   MOVE T2,RTRHOM	;No, then supply a default
	  OPSTR <CAMN T1,>,AJNAN,(AJ) ;Is that the number we expected?
	   CAME T2,RTRHOM	;Is packet from our area
	    JRST [MOVX T1,RS.ANA ;EVENT REASON: ADJACENT NODE ADDRESS CHANGE
		  CALLRET RTEADO] ;REPORT ADJACENCY DOWN - OPERATOR FAULT EVENT
	ENDIF.
	MOVE T1,MB		;POINT TO INPUT MESSAGE
	CALL DNLENG		;GET ITS LENGTH
	TXNE T1,1B35		;SEE IF IT'S AN EVEN LENGTH
	CALLRET RTCRME		;++MESSAGE FORMAT ERROR EVENT

	SUBI T1,2		;GET COUNT WITHOUT CHECKSUM
	ASH T1,-1		;MAKE IT ENTRY COUNT
				;  EXACTLY THE LENGTH THAT WE ARE EXPECTING
	MOVE P2,T1		;GET COPY OF ENTRY COUNT
	CALL DNRPOS		;READ THE POSTION IN MESSAGE
	STOR T1,RMMK2,(MB)	;STORE THE ROUTING MESSAGE POSITION

;RTCRTE - Continued from previous page

;Here to read the message, while summing the checksum.

	SETZ P1,		;A PLACE FOR THE CHECKSUM
	TMNE AJPH4,(AJ)		;Phase 4 node?
	 AOJ P1,		;YES, THEN START WITH 1

RTCRT2:	CALL DNG2BY		;GET A BYTE OF THE ROUTING MESSAGE
	 CALLRET RTCRME		;++MESSAGE FORMAT ERROR EVENT
	ADD P1,T1		;SUM THE CHECKSUM
	SOJG P2,RTCRT2		;SKIM THROUGH IT

;Get the checksum in the message and verify it.

	CALL DNG2BY		;GET THE CHECKSUM FROM THE MESSAGE
	 JRST [MOVX T1,RS.RUC	;EVENT ERROR: CHECKSUM ERROR DETECTED
	       CALLRET RTELDS]	;REPORT THE CIRCUIT DOWN - SOFTWARE ERROR EVENT
	EXCH T1,P1		;EXCHANGE THE CHECKSUMS
	CALL CKSNRM		;NORMALIZE THE ONE WE FIGURED OUT
	CAME T1,P1		;BETTER BE THE SAME
	 CALLRET RTCRME		;++MESSAGE FORMAT ERROR EVENT
;*** Bugchk here??? ***** if no RTV
	CALLRET RTRBAV		; Build routing data into adjacency's vector

	SUBTTL Control Message Processors -- RTCTST - Test or Hello Messages

;RTCTST - Routine to handle the hello and test messages
;
; Call: 
;	T1/ State of Circuit
;	RC/ Pointer to Circuit Block
;	MB/ Pointer to Message Block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4
;
;Router Hello and Test messages consist of a image count followed by that
;count of bytes filled with some constant (HEL.LO).

RTCTST:	TRACE RTR,<Processing test or hello message>
	CAIE T1,RCS.TT		;ARE WE IN TEST OR
	CAIN T1,RCS.RN		; OK MODE?
	TRNA			;YES
	JRST [ETRACE RTR,<Test or Hello message rcvd in wrong state>
	      MOVX T1,RS.UPT	;REASON: UNEXPECTED PACKET RECEIVED
	      CALLRET RTELDS]	;REPORT CIRCUIT DOWN - CIRCUIT FAULT EVENT
	LOAD T1,MBSRC,(MB)	;GET THE SOURCE ADDRESS
	LDB T2,[POINTR (T1,RN%ARE)] ;GET THE AREA NUMBER
	JUMPE T2,RTCTS1		; Phase III nodes don't supply area numbers
	CAME T2,RTRHOM
	 CALLRET FREMSG		;NOT FROM OUR AREA, PUNT IT THEN
RTCTS1:	LDB T1,[POINTR(T1,RN%NOD)] ;GET THE NODE NUMBER
	OPSTR <CAMN T1,>,AJNAN,(AJ) ;IS IT WHAT WE EXPECTED?
	IFSKP.
	  MOVX T1,RS.ANA	;REASON: ADJACENT NODE ADDRESS CHANGED
	  CALL RTEADO		;REPORT ADJACENCY DOWN - OPERATOR FAULT EVENT
	  CALLRET CIRFAI	;Close the circuit and restart it
	ENDIF.
	CALL RTRCTD		;Check the test bytes
	 TRN
	RET

	SUBTTL Control Message Processors -- RTCL2M - Level 2 Routing Messages
RTCL2M:
	CALLRET FREMSG		;Free message and return since we can not be
				;a level 2 router

	SUBTTL Control Message Processors -- RTCERH - Ethernet Router Hello

RTCERH:	STKVAR	<<RSID,2>>	;STORAGE FOR CURRENT RS LIST ID
	TRACE RTR,<Processing Ethernet Router Hello message>
	CAIE T1,RCS.TT		;ARE WE IN TEST OR
	CAIN T1,RCS.RN		; OK MODE?
	TRNA			;YES
	JRST [ETRACE RTR,<Test or Hello message rcvd in wrong state>
	      MOVX T1,RS.UPT	;REASON: UNEXPECTED PACKET RECEIVED
	      CALLRET RTELDS]	;REPORT CIRCUIT DOWN - CIRCUIT FAULT EVENT

	SKIPN ENFLG		; Endnode?
	 CALLRET RHMASR		; No
	CALLRET RHMASE


	SUBTTL Control Message Processors -- RHMASR - Hello message if Endnode

;Process a hello message from a routing node if we are an endnode

RHMASE:	LOAD T1,MBSRC,(MB)	; Get the source node
	LDB T1,[POINTR(T1,RN%ARE)] ; Get the area number
	CAME T1,RTRHOM		; My area?
	 JRST FREMSG		; No, ignore it
	LOAD AJ,RCAJQ,(RC)	; Get our only adjacency - maybe
	CAME T4,RTRAEN		; Sent to multicast "all endnodes"?
	 JRST FREMSG		; No, don't want it
	CALL RTRRCV		; Yes, read and check version
	 JSP T1,RTEMFE		; Bad version  **EVENT??**
	CALL DNGENA		; The the 6 byte address of transmitting node
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	CAME T1,RTRHIO		; HIORD?
	 JSP T1,RTEMFE		; No, we don't want it
	HLRZ P1,T2		; Repostion address and save it
	STOR T1,RCDSH,(RC)	; Save this address as the designated router
	LOAD T3,RCDSL,(RC)	; Current designated router address
	SKIPE AJ		; Do we have an adjacency?
	IFSKP.
	  SETO T1,		; No, make a "routing" adjacency block
	  CALL RTRMAJ		;  so we can eavesdrop on routing messages
	   CALLRET FREMSG
	  MOVE AJ,T1
	  INCR RCNRO,(RC)	; Count a router adjacency on this circuit
	  AOS RTRBRA		;  and this node
	ELSE.
	  SKIPN T3		; Yes, do we have a DR?
	  IFSKP.
	    CAME T3,P1		; Yes, is it the same as address just received?
	    IFSKP.
	      CALL DNGTIM	; Get the current time
	      STOR T1,AJTLR,(AJ) ; Store time we last heard from DR
	      CALLRET FREMSG	;   and nothing more to do
	    ELSE.
	      CALL SETRCA	; Set to recompute routing
	      LOAD T2,RCLID,(RC) ; No, get device ID
	      LOAD T3,AJADR,(AJ) ; Node's address		
	      EVENT RE.AJD,<Adjacency down> ;Signal address change as 
	      CALL RTRSAO	; adjacency off-line. Set the adjacency offline
	    ENDIF.		;  and reuse the adjacency
	  ENDIF.
	ENDIF.
	STOR P1,RCDSL,(RC)	; Save address of designated router
	CALL PSINFO		; Get information in the IINFO field
	 JSP T1,RTEMFE
	CALL DNGTIM		; Get the current time
	STOR T1,AJTLR,(AJ)	; Time last heard from designated router
	MOVX T1,ADJ.UP		; Set the adjacency state to up
	STOR T1,AJSTA,(AJ)
	LOAD T2,RCLID,(RC)	; Get device ID
	LOAD T3,MBSRC,(MB)	; Get address of sender
	STOR T3,AJNAN,(AJ)
	LDB T1,[POINTR(T3,RN%ARE)] ;Get the area number
	STOR T1,AJNAA,(AJ)
	STOR P1,AJNAL,(AJ)	; Save low order address in string format
	CALL DNG2BY		; Get the block size
	 JSP T1,RTEMFE		;  None, bad message
	STOR T1,AJBSZ,(AJ)	; Save the block size requested
	CALL DNG2BY		; Skip priority and area
	 JSP T1,RTEMFE		; None, bad message
	CALL DNG2BY		; Get the Hello Timer
	 JSP T1,RTEMFE		; None, bad message
	IMULI T1,%RTB3M*^D1000	; Calculate the Listen Timer
	STOR T1,AJNHT,(AJ)	;
	LOAD T2,RCLID,(RC)	; No, get device ID
	LOAD T3,AJADR,(AJ)	; Node's address		
	EVENT RE.AUP,<Adjacency up>
	CALLRET FREMSG

	SUBTTL Control Message Processors -- RHMASR - Hello message if Router

;Process a hello message from a routing node if we are a routing node

RHMASR:	SETO T1,		;Flag to indicate that this is a router hello
	CALL RTRPNI		;PARSE OFF THE COMMON HEADER
	 CALLRET FREMSG		;ERROR
	CALL DNG1BY		;GET THE ROUTER PRIORITY
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR
	STOR T1,AJPRI,(AJ)	;SAVE IT
	CALL DNG1BY		;GET THE ROUTER AREA
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	STOR T1,AJARE,(AJ)	;SAVE IT
	CALL DNG2BY		;GET THE HELLO TIMER IN SECONDS
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	IMULI T1,%RTB3M*^D1000	;Broadcast multiplier & convert to milliseconds
	STOR T1,AJNHT,(AJ)	;SAVE THE HELLO TIMER
	CALL DNGTIM		;Get the current time
	STOR T1,AJTLR,(AJ)	;Save it for timeout processing
	CALL DNG1BY		;GET THE RESERVED BYTE
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	STOR T1,AJMPD,(AJ)	;SAVE MPD
	CALL DNG1BY		;GET THE COUNT OF NI-LIST NODES
	 JSP T1,RTEMFE		;RETURN IF NONE
	CAIL T1,8		;IS IT .GE. TO 8
	 CAILE T1,^D244		; .AND. .LE. 244?
	  JSP T1,RTEMFE		;  NO TO EITHER
	MOVE P1,T1		;SAVE THE COUNT
	CALL DNGENA		;GET THE FIRST 6 BYTES 
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	SKIPN T1		; Must be zero
	 SKIPE T2		; Just feeling paranoid
	  JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	CALL DNG1BY		; Which is really 7 bytes
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	SKIPE T1		; Should be zero
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	SUBI P1,7		;WE REMOVED 7 BYTES
	CALL DNG1BY		;GET THE COUNT FOR THE SECOND LIST
	 JSP T1,RTEMFE		; ++Message Format Error++
	CAIE P1,1(T1)		;DO THE COUNTS TALLY?
	 JSP T1,RTEMFE		; NOPE, INNER COUNT SHOULD BE .LT. OUTER
	JUMPE T1,RHMNSD		;IF ADJACENCY LIST IS 0, Node is shutting down
	IDIVI T1,7		;DIVIDE BY SIZE OF ENTRIES
	SKIPE T2		;An even multiple of seven
	 JSP T1,RTEMFE		; NO
	MOVE P1,T1		;SAVE THE COUNT
	CAILE T1,^D236		;NUMBER MAKE SENSE
	 BUG.(INF,ROURCE,ROUTER,SOFT,<Bad NI Router list message format>,,<

Cause:	We have received a router hello message with more than 256 known
	2-way adjacencies.

>,FREMSG)
; RHMASR  continued
; Now peruse the ROUTER/STATE list looking for ourselves.  If we find ourself
; this means that the other node knows about us.

	STKVAR <SELF>
	SETZM SELF		;Initialize to zero
RHMAR1:	CALL DNGENA		; Get the six bytes of node id
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	CAME T1,RTRHIO		;IS IT HIORD (HIGH PART OF NODE ADDRESS)
	 JRST RHMAR3		;NOPE, THEN SKIP IT
	MOVE T3,RTRLOO		;GET MY ADDRESS
	CAME T2,T3		;IS IT ME?
	 JRST RHMAR3		;NOPE, THEN SKIP IT
	SETOM SELF		;We have seen ourselves!
	LOAD T1,AJSTA,(AJ)	;Yes, get the adjacency state
	CAXN T1,ADJ.UP		;Is it up?
	 JRST RHMAR3		;Yes, then don't generate adjacency up event
	MOVX T1,ADJ.UP		;Set the state to "up"
	STOR T1,AJSTA,(AJ)	; in the adjacency block
	SETONE RCSRM,(RC)	;Send a routing update on this circuit
	LOAD T2,RCLID,(RC)	;Get device ID
	LOAD T3,AJADR,(AJ)	;Get node address for the event
	EVENT RE.AUP,<Adjacency up> ; and send the event
	LOAD T1,AJNAN,(AJ)	;Get node number
	CALL SETRCF		;Set recompute routing flags
	MOVN T1,RTRBT1		;Reset the time of last routing
	STOR T1,RCTLR,(RC)	; message

RHMAR3:	CALL DNG1BY		;GET PRISTATE
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	SOJG P1,RHMAR1		;AND GET THE NEXT ONE
	SKIPE SELF		;Did we find ourselves?
	 CALLRET FREMSG		;Yes, !!Success!!
	MOVX T1,ADJ.IN		;No, then set adjacency state to initializing
	TRNA 
RHMNSD:	MOVX T1,ADJ.UN		;Set state to unused
	LOAD T2,AJSTA,(AJ)	;Get the state of the adjacency
	CAXE T2,ADJ.UP		;Was it up?
	 CALLRET FREMSG		;No, then done
	STOR T1,AJSTA,(AJ)	;Save new state
	MOVE P1,T1		;Save for later check
	INCR RCAJD,(RC)		;Count adjacency down events
	LOAD T2,RCLID,(RC)	;Get the device ID
	LOAD T3,AJADR,(AJ)	;Get the node address
	EVENT RE.ADO,<Adjacency down-operator initiated>,
	CALL SETRCA		;Set recompute routing flags
	CAIE P1,ADJ.UN		;Is new state unused?
	 CALLRET FREMSG		;No, then done
	MOVE T1,AJ
	MOVE T2,RC
	CALL RTRZAB		;Yes, release the adjacency
	CALLRET FREMSG

	SUBTTL Control Message Processors -- RTCNIE - NI Endnode Hello message

RTCNIE:	TRACE RTR,<Processing NI Endnode hello and test message>
	SKIPGE ENFLG		;Endnode?
	 CALLRET FREMSG		;  Yes, don't process this
	CAIE T1,RCS.TT		;Are we in test or
	CAIN T1,RCS.RN		; run mode?
	TRNA			;Yes
	JRST [ETRACE RTR,<Test or Hello message received in wrong state>
	      MOVX T1,RS.UPT	;REASON: UNEXPECTED PACKET RECEIVED
	      CALLRET RTELDS]	;REPORT CIRCUIT DOWN - CIRCUIT FAULT EVENT

	SETZ T1,		;Flag to say this is an endnode hello
	CALL RTRPNI		;Parse off common header and set up AJ if
	 CALLRET FREMSG		; we have not exceeded maximum adjacencies
	CALL DNG1BY		;Get the area (reserved)
	 JSP T1,RTEMFE		;Bad packet if not there
	MOVEI T1,^D14		;Skip over the next 14 bytes which include
	CALL DNSKBY		; the seed (8 bytes) & the neighbors system ID
	 JSP T1,RTEMFE		; ++Bad packet++
	CALL DNG2BY		;Hello timer in seconds
	 JSP T1,RTEMFE
	IMULI T1,%RTB3M*^D1000	;Broadcast multiplier & convert to milliseconds
	STOR T1,AJNHT,(AJ)	;Save the hello timer
	CALL DNG1BY		;MPD (reserved)
	 JSP T1,RTEMFE
	CALL RTRCTD		;Now, check the test data
	 RET			;Bad test data
	CALL DNGTIM		;Time stamp this
	STOR T1,AJTLR,(AJ)	; as time last heard from adjacency
	LOAD T1,AJSTA,(AJ)	;Get adjacency state
	CAIN T1,ADJ.UP		;If it was up then done
	 RET
	MOVX T1,ADJ.UP		;Set the adjacency state to up
	STOR T1,AJSTA,(AJ)
	LOAD T2,RCLID,(RC)	;Get device ID
	LOAD T3,AJADR,(AJ)	;Needed for the event
	EVENT RE.AUP,<Adjacency up>
	LOAD T1,AJNAN,(AJ)	;Get node number
	CALLRET SETRCF		;Set recompute routing flags and return
	SUBTTL RTCILL - Illegal message received

RTCILL:	MOVE T1,MB		; Setup for DNFMSG 
	BUG.(CHK,ROUBMT,ROUTER,SOFT,<Bad message type received from the DLL>,,<

Cause:	The DLL received a bad message from another node or incorrectly copied
	a message into the message block.


>,DNFMSG)
	
	SUBTTL RTRBAV - Build adjacency's routing vector from routing update


RTRBAV:	STKVAR <MSGLEN,VECMAX>
	SETZM HIADDR		;Initialize highest address in case of P.R.U.L.
	MOVE T1,MB		;POINT TO THE MESSAGE BLOCK
	CALL DNGINI		;GET UP TO GET BYTES
	LOAD T1,RMMK2,(MB)	;GET THE ROUTING DATA'S POSITION
	CALL DNGPOS		; AND GO TO IT
	MOVE T1,MB		;POINT TO MESSAGE BLOCK AGAIN
	CALL DNLENG		;GET THE LENGTH OF THE MESSAGE
	SUBI T1,2		;ACCOUNT FOR CHECKSUM
	ASH T1,-1		;MAKE IT AN ENTRY (NODE) COUNT
	MOVEM T1,MSGLEN		;SAVE THE LENGTH OF THE WHOLE MESSAGE
	TMNN AJPH4,(AJ)		;Phase IV node?
	 JRST RTRBA4		;NO, THEN USE THIS LENGTH
RTRBA3:	CALL DNG2BY		;YES, THEN GET THE COUNT OF ENTRIES
	 BUG. (CHK,ROUUOC,ROUTER,SOFT,<Unable to obtain count of nodes in Phase IV message>,,<

Cause:	We have received what we think is a routing message that DNLENG said
	has more bytes than we have read and when we try to read another two
	DNG2BY says the count is exhausted.

>,RTRBA9)
	MOVE P2,T1		;SAVE THE COUNT
	CALL DNG2BY		;Get two bytes of starting node ID
	 CALLRET RTCRME		;Message format error
	CAILE T1,RTRMXN		;Is starting node address larger than we handle
	 BUG. (CHK,ROUATL,ROUTER,SOFT,<A routing message contains a start ID greater than we can handle>,,<

Cause:	An adjacent node has sent a routing message with the start ID
	that would cause indexing into the per adjacency vector past
	the end of the vector.

>,RTRBA9)
	MOVNI T2,2		;Account for the bytes we just read
	ADDM T2,MSGLEN		; 4 bytes = 2 entries
	LOAD P1,AJRTV,(AJ)	;GET THE ADJACENCY ROUTING VECTOR POINTER

	JUMPE P1,[
	 BUG. (CHK,ROUNAV,ROUTER,SOFT,<An adjacency has no routing vector>,<<AJ,Adjacency block>>,<

Cause:	A routing vector is built for each routing adjacency when the adjacency
	block is created.  Either we didn't build one or we cleared the pointer
	to it.

>,RTRBA9)]
	MOVE T3,P1		;Compute the last address in the
	ADD T3,RTRMXN		; per adjacency vector that we can
	MOVEM T3,VECMAX		; use
	ADD P1,T1		;ADD IN THE STARTING NODE NUMBER
	JRST RTRBA5		;SKIP PHASE III CODE

RTRBA4:	MOVE P2,MSGLEN		;GET THE MESSAGE LENGTH BACK
	LOAD P1,AJRTV,(AJ)	;GET THE ADJACENCY ROUTING VECTOR POINTER
	MOVE T3,P1		;Compute the last address in the
	ADD T3,RTRMXN		; per adjacency vector that we can use
	MOVEM T3,VECMAX
	AOJ P1,			; Increment it to node 1

RTRBA5:	MOVE T1,P2		; Number of nodes in message or segment
	TMNE AJPH4,(AJ)		; Phase IV node?
	 SOS T1			; Yes, account for node 0, the area router
	SKIPGE T1		; Negative count of entries is not good
;	CAMLE T1,RTRMXN		; More than we care to know about?
	BUG.(CHK,ROURML,ROUTER,SOFT,<Stored routing message format error in RTRBAV>,<<T1,COUNT>>,<

Cause:	We have received a P3 routing message with a negative count of nodes
	in it or no checksum or a P4 routing message with a negative segment
	count.

Data:	Count - Count or checksum

>,RTRBA9)

;RTRBAV - Continued from last page
;At this point P2 contains the number of entries in the routing message for
;Phase III messages or the number of entries in the segment for Phase IV.

RTRBA6:	CALL DNG2BY		;GET THE BYTE FOR A NODE
	 BUG.(CHK,ROUUER,ROUTER,SOFT,<Unexpected end of routing message>,,<

Cause:	The number of bytes in the routing message did not correspond
	to the length expected. This may be caused by reading too many 
	bytes out of the message without decrementing the count of bytes 
	read or caused by an improper routing message.

>,RTRBA9)
	CAMG P1,VECMAX		; Have we stepped past the end of the vector?
	IFSKP.			;  A CAMG because vector size is RTRMXN+1
	  CAMLE P1,HIADDR	; Is this the highest address so far?
	   MOVEM P1,HIADDR	; Yes, save new high and use as a flag in-
				; dicating address higher than our maximum
	ELSE.			; address
	  LOAD T4,RGCST,+T1	; GET THE COST FROM THE BYTE
	  LOAD T3,RGHOP,+T1	; GET THE HOP COUNT
	  LOAD T1,RNCST,(P1)	; Get previous cost
	  CAIN T4,(T1)		; Different?
	  IFSKP.
	    MOVE T1,P1		; Compute current node number
	    OPSTR <SUB T1,>,AJRTV,(AJ) ; ...
	    CALL SETRCF		; Set recompute routing flags
	  ENDIF.
	  STOR T4,RNCST,(P1)	; Save both as new cost/hops
	  STOR T3,RNHOP,(P1)	;
	ENDIF.
	AOJ P1,			; INCREMENT THE POINTER
	SOS MSGLEN		; DECREMENT THE COUNT OF ENTRIES
	SOJG P2,RTRBA6		;  AND LOOP UNTIL FINISHED WITH THEM ALL

RTRBA8:	SKIPLE MSGLEN		; HAVE ANY MORE SEGMENTS TO THIS MESSAGE?
	 JRST RTRBA3		; YES, THEN GET THEIR INFO
RTRBA9:	SKIPN HIADDR		; Any overflows?
	IFSKP.
	  OPSTR <MOVN T1,>,AJRTV,(AJ) ; Get start of vector
	  ADDM T1,HIADDR	; Compute highest address for the event
	  CALL RTCRTF		; Yes, EVENT - Partial routing update loss
	ENDIF.
	CALLRET FREMSG		; Return message block
;Here to report the partial routing update loss.

RTCRTF:	SKIPGE ENFLG		; Non-routing?
	 RET			; Yes, then no event
	LOAD T2,RCLID,(RC)	; Get the entity ID
	LOAD T3,AJADR,(AJ)	; Get address of sender
	EVENT RE.PRL,<Partial routing update loss event>,MB
	AOS RTRCPR		; Increment the counter
	RET

	SUBTTL RTCRME - Routing message error processor

RTCRME:	SKIPGE ENFLG		; Non-routing
	 JRST FREMSG		; Yes, just dispose of message
	JSP T1,RTEMFE		; No, generate an event

	SUBTTL RTRPNI - Parse a  Message Header for NI Hello messages

;RTRPNI - Parse Router Header on Incoming Message
;
; Call: 
;	RC/ Pointer to circuit block
;	MB/ Pointer to Input message
;
; Return: 
;	RET			;IF BAD HEADER
;	RETSKP			;ON SUCCESS
;
; Uses: T1-T2,AJ
;
; This routine parses off the common header from the NI Endnode hello message
;and the NI Router hello message and stores the correct information in the
;adjacency block.

RTRPNI:	SAVEAC P1
	STKVAR <HELTYP,LOSADR>	;Hello type flag and string format node address
	MOVEM T1,HELTYP		;Save hello type
	CALL RTRRCV		;Read and check the version of the Router
	 RET
	CALL DNGENA		;GET THE 6 BYTE SYSTEM ID IN T1 AND T2
	 RET
	CAME T1,RTRHIO		;Do we have HIORD?
	 RET			; No
	STOR T1,MBSR1,(MB)	;Save the source high order
	HLRZM T2,LOSADR		;We will need this later
	CALL CVTSTN		; (T2) Convert string address to 16 bit address
	STOR T1,MBSRC,(MB)	; Stuff it into the MB
	HRRZ T2,T1		; Get the node address in a scratch AC
	TXZ T2,RN%ARE		;  and get only the number
	CAMLE T2,RTRMXN		; Within our allowed maximum?
	 RET			; No, don't make an adjacency
	CALL RTRGAJ		; Find the adjacency associated w/this node
	 RET			; Not in my area, drop the message
	JUMPN AJ,RTRPN3		; Jump if an adjacency exists
	SKIPN HELTYP		; Router hello?
	 JRST RTRPN1		; No, then assume endnode
	MOVE P1,RTRBRA		; Number of router adjacencies that are up
	CAMLE P1,RTNBRA		; Have we exceeded the maximum?
	 JRST RTRPN4		; yes, allow no more
	LOAD T1,RCNRO,(RC)	; Get number of Routers on this circuit
	OPSTR <CAML T1,>,RCMXR,(RC) ;  see if maximum exceeded
	 JRST RTRPN4		; Yes, don't allow it
	SETO T1,		; Flag to indicate routing adjacency
	CALL RTRMAJ		; Make an adjacency
	 RET			; Can't, must be out of memory
	MOVE AJ,T1		; Get adjacency address
	ADDI P1,1		; Account for it
	MOVEM P1,RTRBRA		; Save new count of routers
	INCR RCNRO,(RC)
	JRST RTRPN2		; Rejoin common code
RTRPN1:	MOVE P1,RTRBEA		; Number of endnode adjacencies that are up
	CAMLE P1,RTNBEA		; Have we exceeded the maximum?
	 JRST RTRPN4		; Yes, allow no more
	SETZ T1,		; Flag to indicate endnode adjacency
	CALL RTRMAJ		; Make an adjacency block
	 RET			; Error return
	MOVE AJ,T1		; Get adjacency address
	ADDI P1,1		; Increment number of endnode adjacencies
	MOVEM P1,RTRBEA		; Save new count
RTRPN2:	LOAD T1,MBSR1,(MB)	; Get the high order address
	STOR T1,AJNAH,(AJ)	; Put it into the AJ block
	MOVE T1,LOSADR		; Get the low order string address
	STOR T1,AJNAL,(AJ)	;  and save address in string format
	LOAD T1,MBSRC,(MB)	; Get the low order node address
	STOR T1,AJNAN,(AJ)	; Stuff it
	LDB T1,[POINTR(T1,RN%ARE)] ; Get the area number
	STOR T1,AJNAA,(AJ)	; Save that too

RTRPN3:	LOAD T1,AJCBP,(AJ)	; Get the circuit block pointer
	CAME T1,RC		; Same as one it came in on
	 RET			; Error return
	CALL PSINFO		; Save infomation in the IINFO field
	 RET
	CALL DNG2BY		;GET THE BLOCK SIZE
	 RET
	CAMGE T1,RTRBSZ		;Is it at least as large as ours?
;*** Should we do more?
	 RET
	LOAD T2,AJNTY,(AJ)	;Get the adjacency type
	CAIN T2,ADJ.LN		;Is it an endnode?
	IFSKP.			;No, check the blocksize
	  LOAD T2,RCBSZ,(RC)	;Get block size currently used on this circuit
;*** What if little guy goes away?
	  CAIGE T1,(T2)		;Is the new one smaller
	   STOR T1,RCBSZ,(RC)	;Yes, then use it
	ENDIF.
	STOR T1,AJBSZ,(AJ)	;SAVE THE BLOCK SIZE REQUESTED
	RETSKP			;RETURN SUCCESS TO CALLER

RTRPN4: MOVE T3,T2		; Get adjacency's address
	LOAD T2,RCLID,(RC)	; Get the entity ID
	EVENT RE.ARJ,<Adjacency rejected>
	RET			; Yes, then don't make an adjacency

	ENDSV.

	SUBTTL	PSINFO - Parse the info field of Ethernet router hello

;Call:
;	AJ/ Adjacency block
;
;Return:
;	 RET			;On error in info field of message
;	RETSKP			;Info parsed and entered into the
;				; adjacency block

PSINFO:	CALL DNG1BY		; Get the IINFO field
	 RET
	LOAD T2,IITYP,+T1	; Get routing type
	SETO T3,		; Put a funny value in receiving AC
	CAXN T2,IIT.L1		; Is it a level one router?
	 MOVX T3,ADJ.L1		;  Yes
	CAXN T2,IIT.L2		; Is it a level 2 router?
	 MOVX T3,ADJ.L2		;  Yes
	CAXN T2,IIT.EN		; Is it an endnode?
	 MOVX T3,ADJ.LN		;  Yes
	JUMPL T3,RTN		; Strange value
	STOR T3,AJNTY,(AJ)	; Tell the Adjacency about it
	SETONE AJPH4,(AJ)	;  and set the Phase IV flag
	LOAD T2,IIVRQ,+T1	;GET THE VERICATION REQUIRED FLAG
	STOR T2,AJVRQ,(AJ)	;STOR IT IN THE ADJACENCY BLOCK
	LOAD T2,IIRJF,+T1	;GET THE REJECT FLAG
	STOR T2,AJRJF,(AJ)	;SAVE IT
	LOAD T2,IIMTA,+T1	;GET THE NO MULTICAST TRAFFIC ACCEPTED FLAG
	STOR T2,AJMTA,(AJ)	;SAVE IT
	LOAD T2,IIBRQ,+T1	;GET THE BLOCKING REQUESTED FLAG
	STOR T2,AJBLO,(AJ)	;SAVE IT
	RETSKP


	SUBTTL RTRRCV - Read version information from message

RTRRCV:	CALL DNG1BY		;GET THE VERSION NUMBER
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	CAME T1,RTRVER		;IS IT THE CORRECT VERSION?
	 RET			;NO, THEN JUST RETURN
	CALL DNG2BY		;GET THE ECO AND USER ECO
	 JSP T1,RTEMFE		; ++MESSAGE FORMAT ERROR++
	RETSKP

	SUBTTL RTRHDP - Parse a Input Router Message Header

;RTRHDP - Parse Router Header on Incoming Message
;
; This routine will skip over leading padding, and parse the first control byte
; of the routing packet.  For Short Data Packets, and Long Data Packets, this
; routine will also completely parse their headers.
;
; Call: 
;	MB/ Pointer to Input message
;
; Return: 
;	RET			;IF BAD HEADER
;	RETSKP			;ON SUCCESS
;
; Uses: T1-T2
;
;Note that DNGINI is called here and should not be recalled afterwards
;for the Router MSD.

RTRHDP:	MOVE T1,MB		;POINT TO THE MESSAGE BLOCK
	CALL DNGINI		;SET UP FOR CALLS TO DNGxBY
	CALL DNRPOS		;GET OUR POSITION
	STOR T1,RMMK1,(MB)	;STORE IT FOR EVENT PROCESSING
	CALL DNG1BY		;Try to read message flags
	 RET			;RAN OUT, GIVE ERROR RETURN
	TXZN T1,RM%MZ1		;IS THE PAD FLAG SET?
	IFSKP.
	  CALL PADSKP		;Skip over any pad bytes (NI) and return with
	   RET			; first byte of message.  Error if PADSKP fails
	ENDIF.
	STOR T1,RMFST,(MB)	;Store message flags for later use
	TXNN T1,RM%CTL		;Is this a control packet?
	 JRST RTRHD1		; No, go parse a data packet

;Here with a control message. Check the type and return.

	TXNE T1,^-<CMTYP!CMCTL>	;MAKE SURE THE RESERVED BITS ARE ZERO
	 RET			;NOPE, GIVE BAD RETURN
	LOAD T2,CMTYP,+T1	;GET THE CONTROL MESSAGE TYPE
	STOR T2,RMCTY,(MB)	;STORE THAT IN MESSAGE BLOCK
	CAIL T2,RCM.RH		;Is this an ethernet only message?
	 RETSKP			;Yes, then don't look for source node addr
	CALL DNG2BY		;Get the node address of the sender
	 RET			;If none, then error
	STOR T1,MBSRC,(MB)	;Save in MB's public section
	RETSKP			;RETURN TO SENDER WITH GOOD RETURN

;Here with a data packet.  Parse it w/regards to Ethernet vs. point to point

RTRHD1:	JN RCBCT,(RC),RTRHNI	;If NI, handle differently

	TXNE T1,RM%EVL		;IS THE EVOLUTION BIT SET?
	JRST RTRHD2		;YES, IT HAS A PHASE II ROUTING HEADER

;Check all the fields in the packet route header and store pertinent fields
;in the message block.

	TXNN T1,RM%MB1		;IF LOW TWO ORDER BYTES ARE 00, THIS
				; MESSAGE IS A PHASE II WITHOUT A ROUTING
				; HEADER
	JRST RTRHD2		;IT IS, RETURN, INDICATING PHASE II MESSAGE

	TXNE T1,RM%MZ1!RM%MZ2!RM%MZ3 ;MAKE SURE THESE FIELDS ARE ZERO
	RET			;THEY'RE NOT, GIVE ERROR RETURN

	TXNE T1,RM%RQR		;IS RQR ON?
	TXNN T1,RM%RTS		; AND RTS
	TRNA			;NO, OK
	RET			;YES, THAT'S A PROBLEM
;Now parse the source, destination and visits fields of packet route header.

	CALL RTRGNA		;GET THE DESTINATION ADDRESS
	 RET			;OUT OF BYTES, GIVE BAD RETURN
	STOR T1,MBDST,(MB)	;STORE IT

	CALL RTRGNA		;GET THE SOURCE ADDRESS
	 RET			;RAN OUT, GIVE BAD RETURN
	STOR T1,MBSRC,(MB)	;STORE THAT, TOO

	CALL DNG1BY		;GET THE "FORWARD" BYTE
	 RET			;RAN OUT, GIVE BAD RETURN
	TXNE T1,^-FWVST		;MAKE SURE IT'S NOT TOO BIG
	RET			;IT'S TOO BIG

	STOR T1,MBVST,(MB)	;STORE IT IN THE MESSAGE BLOCK
	RETSKP			;LOOKS GOOD, RETURN

;Here we have a Phase II message.  Set the Phase II flag in the message
;block and return.

RTRHD2:	SETONE RMPH2,(MB)	;SAY THIS IS A PHASE II MESSAGE
	RETSKP			; AND GIVE GOOD RETURN
;Parse an Ethernet style (Long Data Packet) header

RTRHNI:	CALL DNG1BY		;GET D-AREA
	 RET			;NOTHING
	CALL DNG1BY		;GET D-SUBAREA
	 RET			;ERROR IF NOTHING
	CALL DNGHIO		;Ethenet hi-order address
	 RET			;RETURN IF NOTHING THERE
	STOR T1,MBDS1,(MB)	;SAVE FIRST PART OF DESTINATION
	CALL DNG2BY		; get low order node address
	 RET
	STOR T1,MBDST,(MB)	;AND NORMAL DECNET-NODE STUFF
	CALL DNG1BY		;GET THE S-AREA
	 RET			;ERROR
	CALL DNG1BY		;GET THE S-SUBAREA
	 RET			;ERROR
	CALL DNGHIO		;GET THE SOURCE ID
	 RET			; Error
	STOR T1,MBSR1,(MB)	;SAVE THE SOURCE
	CALL DNG2BY		; Get low order of node address
	 RET
	STOR T1,MBSRC,(MB)	;  and save that
	SKIPN ENFLG		; Endnode?
	IFSKP.
	  LOAD T2,MBDST,(MB)
	  CAME T2,RTRNAD	; Destined for us?
	   RET			; No, then bad message - We don't do routing
	  TMNN RMMZ2,(MB)	; Intra-NI flag set?
	  IFSKP.		; Yes, see if we should enter it into cache
	    LDB T2,[POINTR(T1,RN%ARE)] ; Get the area number
	    TXZ T1,RN%ARE	;  and only the node number
	    CAME T2,RTRHOM	; In our home area?
	    IFSKP.		; Find our slot in vector
	      CAMLE T1,RTRMXN	; Higher than our maximum address?
	       RET		;  Yes, drop it
	      EXCH T1,T2
	      ADD T2,RTRNRV	; Offset into vector
	      SETONE RNCCH,(T2)	; Set the on-ethernet cache bit
	      ADD T2,RTROFS	; Step to cache timer vector(same as OA vector)
	      CALL DNGTIM	; Get current time
	      ADD T1,RTRCTO	; Add the in-cache time
	      MOVEM T1,(T2)	; Save the time when cache bit is flushed
	    ENDIF.
	  ENDIF.
	ENDIF.
	CALL DNG1BY		;GET THE NEXT LEVEL 2 ROUTER
	 RET			;NOT THERE
	CALL DNG1BY		;GET THE VISIT COUNT
	 RET			;NOT THERE
	TXNE T1,^-FWVST		;MAKE SURE IT'S NOT TOO BIG
	 RET			;IT'S TOO BIG
	STOR T1,MBVST,(MB)	;STORE IT IN THE MESSAGE BLOCK
	CALL DNG1BY		;GET THE SERVICE CLASS
	 RET			;ERROR IF NOT THERE
	CALL DNG1BY		;GET THE PROTOCAL TYPE
	 RET			;ERROR IF NOT THERE
	RETSKP			;GIVE GOOD RETURN

	SUBTTL RTRCTD - Check for valid test pattern in Hello/Test message

RTRCTD:	SAVEAC <P1>
	CALL DNG1BY		;GET THE IMAGE COUNT OF TEST MESSAGE
	 JSP T1,RTEMFE		;++MESSAGE FORMAT ERROR EVENT
	CAILE T1,^D128		;IS THE LENGTH WITHIN REASON
	BUG.(INF,ROUBTF,ROUTER,SOFT,<Bad Test message format>,,<

Cause:	We received a hello message from a P3 node or a P4 endnode
	that contained too many bytes of test data.

>,FREMSG)

;Check to see if the test data is ok.

	MOVE P1,T1		;PRESERVE IT SORT OF
	SOJGE P1,[CALL DNG1BY	;GET A BYTE OF THE TEST
		   JRST [MOVX T1,RS.ALI ;REASON: INVALID TEST DATA
			 CALLRET RTELDS] ;REPORT THE CIRCUIT DOWN EVENT
		  CAIE T1,HEL.LO ;IS IT EQUAL TO THE MAGIC NUMBER
		  BUG.(INF,ROUBTM,ROUTER,SOFT,<Bad Hello or Test message>,,<

Cause:	We have received bad test data in a hello message.

>,FREMSG)
		  JRST .]	;LOOK AT ALL THE BYTES
	CALL FREMSG		;FREE THE MESSAGE AND RETURN
	RETSKP


	SUBTTL RTRGNA - Get node address from message header



RTRGNA:	CALL DNG2BY		;Read expected node address
	 RET			;Pass on the error
	LDB T2,[POINTR (T1,RN%ARE)] ;Get the area number if any
	JUMPN T2,RSKP		;We got one use it
	MOVE T2,RTRHOM		;None given use our home area
	DPB T2,[POINTR (T1,RN%ARE)] ;Put ours into the node address
	JRST RSKP		; and return a success

	SUBTTL PADSKP - Skip over pad bytes in an Ethernet message header

;Call:
;	T1/ First byte in input MSD

;Return:
;	T1/ First byte in message header (having skipped over any pad bytes)

PADSKP:	SAVEAC <P1>
	SOSGE P1,T1		;Account for this byte
	 RET			;Pad with count of zero
PADSK1:	CALL DNG1BY		;Get one more byte (Lastly the control byte)
	 RET
	SOJGE P1,PADSK1		;Loop for all the pads
	TXNE T1,RM%MZ1		;Did we get a second pad flag?
	 RET			; Yes, that is illegal
	RETSKP			;Successful

IFN FTOPS10,<	SUBTTL Network Management Interface -- RTNLID - Give the line id

;RTNLID - Give the line id for a given node
;
;Call
;	T1/ Node address
;Return
;	RET		;node unreachable
;	RETSKP		;T1 contains line-id

RTNLID::LOAD T2,RN%ARE,T1	;GET AREA PORTION
	SKIPE T2		;HOME AREA?
	CAMN T2,RTRHOM		;...
	SKIPA			;YES, KEEP GOING
	SETZ T1,		;NO, USE AREA ROUTER
	ANDI T1,RN%NOD		;KEEP ONLY THE NODE PORTION
	CAMLE T1,RTRMXN		;OUT OF RANGE?
	BUG.(CHK,ROUNMR,ROUTER,SOFT,<NMX out of range>,,<

Cause:	This BUG is not documented yet.

>,RTN)
RTNLI1:	ADD T1,RTRNRV		;ADD IN THE VECTOR ADDRESS
	TMNE RNLCL,(T1)		;IS NODE LOCAL?
	 JRST [SETZ T1,		;CLEAR OUT THE REGISTER FOR LOCAL NODES
	       RETSKP]		;AND GIVE GOOD RETURN
	TMNN RNRCH,(T1)		;IS NODE REACHABLE?
	RET			;NOPE, TELL CALLER.
	ADD T1,RTROFS		;NOW POINT TO OUTPUT ADJACENCY VECTOR
	MOVE T1,(T1)		;GET THE OUTPUT CIRCUIT POINTER
	LOAD T1,AJCBP,(T1)	;NOW THE OUTPUT CIRCUIT POINTER
	LOAD T1,RCLID,(T1)	;GET LINE ID FROM CIRCUIT BLOCK
	RETSKP			;SUCCESS.

RTNRCH::POINTR(RN.RCH(T2),RNRCH)
RTNLCL::POINTR(RN.LCL(T2),RNLCL)
RTNCST::POINTR(RN.CST(T2),RNCST)
RTNHOP::POINTR(RN.HOP(T2),RNHOP)
>; END IFN FTOPS10
	SUBTTL	Network Management -- RTRNMX - Entry into Router

;RTRNMX - Entry point for network management functions
;
;Call
;	T1/ Function code
;	T2/ NM block address
;Return
;	RET		; Error - Reason in T1
;	RETSKP		; Request complete

	INTERN RTRNMX
	XSWAPCD

RTRNMX:
IFN FTPARANOIA,<
	CAIL T1,NF.SET		; Range check the function code
	 CAILE T1,NF.RET	;
	BUG.(CHK,RTRIFN,ROUTER,SOFT,<Illegal function code from NTMAN>,,<

Cause:	The function code supplied by NTMAN is out of range of functions
	supported by Router.

Action:	Find routine in NTMAN supplying illegal function code and correct it.
>,[RNMXER (NF.MPE)])
>
	SAVEAC <P1,P2,RC,AJ>
	MOVE T1,NMXDSP(T1)	; Routine to do processing
	CALLRET (T1)		; Go to it
NMXDSP:
	IFIW!<NMXSET&777777>	; Set Parameter
	IFIW!<NMXCLR&777777>	; Clear Parameter
	IFIW!<NMXRED&777777>	; Read Parameter
	IFIW!<NMXSHC&777777>	; Show Counters
	IFIW!<NMXSZC&777777>	; Show and Zero Counters
	IFIW!<NMXRET&777777>	; Return list of Entity Ids

NMXILL:	RNMXER (NF.FNS)		; Function not supported

	XRESCD
	SUBTTL Network Management -- Read/Set/Clear a parameter

;NMXRED - called to read a network management parameter

;Call:	T2/ address of NM block
;	CALL NMXRED
;	 +1 on error with code in T1
;	+2 on success
;
;Context: Jsys

	XSWAPCD
NMXRED:	MOVX T3,NF.RED		;Load function code
	CALLRET NMXPCM		;Join common code
	XRESCD


;NMXSET - called to set a network management parameter

;Call:	T2/ address of NM block
;	CALL NMXSET
;	 +1 on error with code in T1
;	+2 on success
;
;Context: Jsys

	XSWAPCD
NMXSET:	MOVX T3,NF.SET		;Load function code
	CALLRET NMXPCM		;Call common code
	XRESCD


;NMXCLR - called to clear a network management parameter

;Call:	T2/ address of NM block
;	CALL NMXCLR
;	 +1 on error with code in T1
;	+2 on success
;
;Context: Jsys

	XSWAPCD
NMXCLR:	MOVX T3,NF.SET		;Load function code
	CALLRET NMXPCM		;Call common code
	XRESCD

	SUBTTL Network management -- Common routine for parameters

;NMXPCM - Performs common processing for parameter read, set, and clear

;Call:
;	T2/ address of NM block
;	T3/ Function (read,set,clear)
;	CALL NMXPCM
;	 +1 on error with code in T1
;	+2 on success
;
;Context: Jsys

	XSWAPCD
NMXPCM:	MOVE P1,T2		;Move NF pointer to where it should be
	LOAD T1,NFETY,(P1)	;Get the entity type
	CAIE T1,.NTNOD		;Is request for a node entity?
	IFSKP.
	  LOAD P2,NFEID,(P1)	;Yes, get node address
	  LDB T2,[POINTR (P2,RN%ARE)] ;Get the area number
	  TXZ P2,RN%ARE		;Clear the area number
	  CAME T2,RTRHOM	;Is it same as ours?
	   SETZ P2,		;No, select the area router
	  ADD P2,RTRNRV		;Add in the vector address
	  TMNN RNLCL,(P2)	;Is it local?
	  IFSKP.
	    XMOVEI T1,EXPTAB	;Yes, use executor parameter table
	    MOVEI T2,EXPTBL	; and its length
	  ELSE.
	    MOVE T2,RTRNTY	;Get routing type for later checking
	    LOAD T1,NFPRM,(P1)	;Get requested parameter
	    CAIE T1,0		;Is it state?  <0 = hack for now>
	    IFSKP.	  
	      CAMN P2,RTRNRV	;Have we selected the area router?
	       RNMXND		;Yes, then we have no data to return
	      CAIN T2,RNT.NR	; or if we are an endnode there is no
	       RNMXND		; no state information
	    ELSE.		;Not state
	      CAIE T2,RNT.NR	;Are we an endnode?
	      IFSKP.
		CALL ENPARM	; Yes, set up parameter pointers
		 RET
	      ELSE.
		CALL RNPARM	; Set up parameter pointers
		 RET
	      ENDIF.
	    ENDIF.
	    XMOVEI T1,NDPTAB	;Node parameter table
	    MOVEI T2,NDPTBL
	  ENDIF.
	ELSE.
	  CAIE T1,.NTCKT	;No, maybe circuit
	  IFSKP.
	    LOAD T1,NFEID,(P1)	;Get circuit identifier
	    CALL RTRGCB		;Get the circuit block (RC)=circuit block
	     RNMXER (NF.URC)
	    XMOVEI T1,CCPTAB	;Address of circuit parameter table
	    MOVEI T2,CCPTBL	; and its length
	  ELSE.
	    RNMXER (NF.FNS)	;We don't do any others
	  ENDIF.
	ENDIF.
	
;Call NTPARM to do the real work

	CALLRET NTPARM		; and call common function to do work

	SUBTTL Network management -- Setup for loading parameter data

;Here for all parameters except state.  State needs only to have P2 setup

ENPARM:	MOVE RC,RTRCBQ		; Get only circuit and
	LOAD AJ,RCAJQ,(RC)	;  possibly our desiginated router
	LOAD T1,NFPRM,(P1)	; Get requested parameter
	CAIN T1,^D810		; Is requested parameter routing type?
	 SETZ AJ,		; Yes, don't supply it	
	CAIE T1,^D2503		;  or NODE JSYS reachability?
	 MOVE P2,RTRNRV		; Use kludged up cost/hops in vector(0)
	RETSKP

RNPARM: TMNN RNRCH,(P2)		;Is it reachable?
	 RNMXND			;No, then no data
	SETZ AJ,		;Start with no adjacency
	CAMN P2,RTRNRV		;Have we selected the area router?
	IFSKP.
	  SKIPA RC,RTRCBQ	;No, a local area node, see if adjacent
	  DO.
	    LOAD RC,RCNXT,(RC)	;Get next circuit for RTRGAJ
	    JUMPE RC,ENDLP.
	    LOAD T1,NFEID,(P1)	;Get the node's address
	    CALL RTRGAJ		;See if an adjacency exists
	     RNMXER (NF.OPF)	;Not in our area, an error
	    JUMPE AJ,TOP.	;Didn't find one, try next circuit
	  ENDDO.
	ENDIF.
	MOVE T1,P2		;No, get the address we selected
	ADD T1,RTROFS		;Now point to output adjacency vector
	MOVE T1,(T1)		;Get the output adjacency pointer
	SKIPE AJ		;Did we find an adjacency?
	IFSKP.
	  LOAD T2,NFPRM,(P1)	;No, then get parameter requested
	  CAIE T2,^D810		;Is it routing type?
 	   MOVE AJ,T1		;No, then load AJ with output adjacency
	ENDIF.
	LOAD RC,AJCBP,(T1)	;Now the output circuit pointer
	RETSKP

	SUBTTL Network Management -- Qualified parameters

;Call:
;	RC/ circuit block
;	P1/ NMX function block
;
;	CALL NMXQPS
;	CALL NMXQPT
;	RET	T1/ Error code if error, 0 if no data, .GT. 0 if success
;		T2/ Datum
;
;Context: Jsys

NMXQPS:	SKIPA T1,[-1]		; Adjacent node block size
NMXQPT:	SETZ T1,		; Adjacent node hello timer
	STKVAR <FUNC>
	MOVEM T1,FUNC		; Save parameter requested
	TMNN NFQUF,(P1)		; Do we have a qualifier?
	 RNMXER (NF.MPE)	; It is required
	LOAD T1,NFQUA,(P1)	; Get qualifier - a node address
	CALL RTRGAJ		; Look for the adjacency
	 RNMXER (NF.MPE)	; Not in this area!
	JUMPE AJ,[RNMXND]	; Not reachable
	LOAD T2,AJNHT,(AJ)	; Get hello timer datum
	IDIVI T2,TIMBAS		; Convert to seconds
	SKIPE FUNC		; Is that what was requested?
	 LOAD T2,AJBSZ,(AJ)	; No, then get the block size
	RNMXOK			;  and return success

	ENDSV.

	SUBTTL Network Management -- READ node/executor state

;Call:
;	P2/ Reachability vector pointer
;
;	CALL NMXRNS
;	RET  T1/ contains error code if error else unchanged
;	     T2/ contains state
;
;Context: Jsys

NMXRNS:	TMNN RNLCL,(P2)		; Yes, for the local node?
	IFSKP.
	  MOVE T2,DCNSTA	; Get DECnet state
	ELSE.
	  MOVX T2,RM.UNR	; Assume unreachable
	  SKIPN ENFLG		; Endnode?
	  IFSKP.
	    TMNE <RNMBY,RNCCH>,(P2) ; Maybe reachable?
	     MOVX T2,RM.REA	; Yes
	  ELSE.
	    TMNE RNRCH,(P2)	; Is it reachable?
	     MOVX T2,RM.REA	; Yes, say so
	  ENDIF.
	ENDIF.
	RNMXOK

	SUBTTL Network Management -- Read circuit state

;Call:	T1/ Default success flag
;	RC/ Circuit block
;	CALL NMXRCS
;	RET  T1/contains error code if error else unchanged
;
;Use: Only T1,T2 (must preserve all others)
;Context: Jsys

NMXRCS:	LOAD T2,RCSTA,(RC)	; Get circuit state
	MOVE T2,[RNS.OF		; Off is off
		 RNS.ST		; Rejected is starting
		 RNS.ST		; Failed is starting
		 RNS.ST		; Wait is starting
		 RNS.ST		; Init wait is starting
		 RNS.ST		; Verification wait is also
		 RNS.ST		; I guess testing is too
		 RNS.ON](T2)	;  and running is on		     
	RNMXOK

	SUBTTL Network Management -- Set/Clear executor state

;Call:	T1/ Default success flag
;	T2/ New value
;	P2/ Reachability vector
;	CALL NMXSES
;	RET  T1/contains error code if error else unchanged
;
;Use: Only T1,T2
;Context: Jsys

NMXSES:	TMNN RNLCL,(P2)		; Is this for the executor
	 RNMXER (NF.PNA)	; No, can't do it then
	MOVE T1,DCNSTA		; Get DECnet state
	CAIE T1,(T2)		; State changing?
	 RNMXOK			; No, just a NOP
	SKIPE T2		; Is new state off?
	IFSKP.			; Yes
	  CALL RTROFF		; Then turn off DECnet
	ELSE.
	  CALL RTRON		;  else turn it on
	ENDIF.
	RNMXOK			; Indicate success

	SUBTTL Network Management -- Set/Clear circuit state

;Call:	T1/ Default success flag
;	T2/ New value
;	RC/ Circuit block
;	CALL NMXSCS
;	RET  T1/contains error code if error else unchanged
;
;Use: Only T1,T2 (must preserve all others)
;Context: Jsys

NMXSCS:	LOAD T1,RCSTA,(RC)	; Get current state
	CAIE T2,NCK.OF		; Is new state off?
	IFSKP.
	  CAIN T1,RCS.OF	; Is current state off?
	   RNMXOK		; Yes, just a NOP
	  CALL SETRCC		; Set recompute routing flags
	  CALL RTRZCB		; Clean out the circuit block
	  CALL RTRSAO		; Set any remaining adjacencies to unused
	  CALL R2KCLS		; Shut the circuit down
	ELSE.
	  CAIE T1,RCS.OF	; Current state off?
	  IFSKP.
	    CALL R2KINI		; Attempt to start circuit
	    IFNSK.
	      SETZRO RCTIN,(RC)	; Clear restart timer
	      RNMXER (NF.OPF)	;  Failed!
	    ENDIF.
	  ENDIF.
	ENDIF.
	RNMXOK			; Indicate success

	SUBTTL Network Management -- Show counters
	SUBTTL Network Management -- Show and zero counters

;NMXSHC - called to show counters

;Call:	T2/ address of NF block
;	CALL NMXSHC
;	 +1 on error with code in T1
;	+2 on success
;
;Context: Jsys

;NMXSZC - called to show and zero counters

;Call:	T2/ address of NF block
;	CALL NMXSZC
;	 +1 on error with code in T1
;	+2 on success
;
;Context: Jsys

	XSWAPCD
NMXSHC:	SKIPA T3,[NF.COU]	;Load function code and skip
NMXSZC:	MOVX T3,NF.SZC		;Load function code
	STKVAR <FUNC>
	MOVEM T3,FUNC
	MOVE P1,T2		;Move NF pointer to where it should be
	LOAD T1,NFETY,(P1)	;Get the entity type
	CAIE T1,.NTNOD		;Is request for a node entity?
	IFSKP.
	  LOAD T1,NFEID,(P1)	;Get the entity ID
	  CAME T1,RTRNAD	;Is it us?
	   RNMXND		;No, then nothing to return
	  XMOVEI T1,EXCTAB	;Address of executor counter table
	  MOVEI T2,EXCTBL	; and its length
	ELSE.
	  CAIE T1,.NTCKT	;No, maybe circuit
	  IFSKP.
	    LOAD T1,NFEID,(P1)	;Get circuit identifier
	    CALL RTRGCB		;Get the circuit block
	     RNMXER (NF.URC)
	    XMOVEI T1,CICTAB	;Address of circuit counter table
	    MOVEI T2,CICTBL	; and its length
	  ELSE.
	    RNMXER (NF.FNS)	;We don't do any others
	  ENDIF.
	ENDIF.

;Call NTCTRS to do the real work

	MOVE T3,FUNC		;Get function to do
	CALLRET NTCTRS		;Read the counters and return

	ENDSV.

	SUBTTL Network Management -- Return list

;NMXRET  - Return list of entities

;Call	T2/ Address of NM block

NMXRET:	MOVE P1,T2		; Save address of NM block
	TMNN NFBFF,(P1)		; Buffer present?
	 RNMXER (NF.MPE)	;  Must have one for this stuff
	LOAD T1,NFBLN,(P1)	; Get the buffer length
	SKIPG T1
	 RNMXER (NF.MPE)	; NTMAN/NML screwed up	
	LOADE T2,NFSEL,(P1)	; Get the selector type
	CAXL T2,.NTSGN		; Range check the selection criteria
	 CAXLE T2,.NTKNO	; If not between SIGNIFICANT & KNOW
	  RNMXER (NF.MPE)	;  we don't do it
	LOAD T1,NFETY,(P1)	; Get entity type
	CAIE T1,.NTNOD		; Is this a node entity request?
	IFSKP.
	  CALLRET <-.NTSGN>+@[
		IFIW <NMXILS&777777> ; SIGNIFICANT nodes not supported
		IFIW <NMXSAN&777777> ; ADJACENT nodes
		IFIW <NMXILS&777777> ; LOOP Nodes
		IFIW <NMXSAX&777777> ; ACTIVE Nodes
		IFIW <NMXSKN&777777>](T2) ; KNOWN Nodes
	ELSE.
	CAIE T1,.NTCKT		; Is the entity circuit?
	  IFSKP.
	  CALLRET <-.NTSGN>+@[
		IFIW <NMXILS&777777> ; SIGNIFICANT nodes not supported
		IFIW <NMXILS&777777> ; ADJACENT circuits
		IFIW <NMXILS&777777> ; LOOP circuits
		IFIW <NMXSAC&777777> ; ACTIVE circuits
		IFIW <NMXSKC&777777>](T2) ; KNOWN circuits
	  ELSE.
	    RNMXER (NF.FNS)	; We don't do any others
	  ENDIF.
	ENDIF.

NMXILS:	RNMXER (NF.MPE)		; Error on NTMAN's part

	SUBTTL Network Management -- Show known/active nodes

;Call:	P1/ NF block
;	P2/ Scratch

NMXSAX:	TDZA T1,T1		; "Active nodes"
NMXSKN:	MOVEI T1,1		; "Known nodes"
	STKVAR <FUNC>
	MOVEM T1,FUNC
	LOAD T4,NFBUF,(P1)	; Get the buffer address
	LOAD T1,NFBLN,(P1)	; Get length of buffer
	CAMGE T1,RTRMXN		; Is it big enough?
	 RNMXER (NF.MPE)	; NTMAN got it wrong
	SKIPE FUNC
	 AOS T4			; Starting at node 1 position for "known"
	MOVE T3,RTRNRV		; Address of reach vector
	AOS T3			; Start at node 1
	MOVEI T1,1		; Start loop at 1 (this will be node number)
	MOVE T2,RTRHOM		; Get the local area number
	LSH T2,^D10		; Position to area field
	SETZ P2,		; Count is zero
	DO.
	  TMNN RNRCH,(T3)	; Is this node reachable?
	  IFSKP.
	    MOVEM T1,(T4)	; Put address into buffer
	    IORM T2,(T4)	; Set the area number
	    AOJ P2,		; Count it
	    AOJ T4,		; Advance buffer pointer
	  ELSE.
	    SKIPE FUNC
	     AOJ T4,		; Advance pointer if "known"
	  ENDIF.
	  AOJ T1,		; Advance node number
	  AOJ T3,		; Step to next node in vector
	  CAMG T1,RTRMXN	; Have we reached the top of the vector?
	  LOOP.			; No, loop until done
	  STOR P2,NFBLN,(P1)	; Save count for NTMAN
	  RETSKP		; Yes, return success
	ENDDO.
	ENDSV.

	SUBTTL Network Management -- Show adjacent nodes

;Call:	P1/ NF block

NMXSAN:	SAVEAC <Q1,Q2>		; We need more than the temps
	SETZ P2,		; Count starts at 0
	LOAD T2,NFBLN,(P1)	; Get length of buffer
	LOAD T4,NFBUF,(P1)	; Get buffer address
	ADD T2,T4		; Compute end of buffer
	SKIPA Q1,RTRCBQ		; Point to first circuit
NMXSN1:	LOAD Q1,RCNXT,(Q1)	; Get next circuit
	JUMPE Q1,NMXSN4		; Finished, return success
	LOAD T1,RCSTA,(Q1)	; Get circuit state
	CAIN T1,RCS.OF 		; Is state "off"?
	 JRST NMXSN1		; Yes, step to next circuit
	LOAD Q2,RCAJQ,(Q1)	; Get first in queue of adjacencies
NMXSN2:	JUMPE Q2,NMXSN1		; None, try next circuit
	LOAD T1,AJSTA,(Q2)	; Get adjacency state
	CAIE T1,ADJ.UP		; Is it up?
	 JRST NMXSN3		; No, skip it then
	CAML T4,T2		; Check to be sure we don't run off end
	 RNMXER (NF.MPE)	; NTMAN error if not enough room
	LOAD T1,AJADR,(Q2)	; Get node address
	MOVEM T1,(T4)		; Save into buffer
	AOJ T4,			; Advance buffer
	AOJ P2,
NMXSN3:	LOAD Q2,AJNXT,(Q2)	; Step to next adjacency
	JRST NMXSN2		; Try next
NMXSN4:	STOR P2,NFBLN,(P1)	; Save count
	SKIPN T1,P2
	 RET
	RETSKP

	SUBTTL Network Management -- Show active circuits

;Call:	P1/ NF block

NMXSAC:	LOAD T2,NFBLN,(P1)	; Get length of buffer
	LOAD T4,NFBUF,(P1)	; Get buffer address
	ADD T2,T4		; Compute end of buffer
	SETZ P2,		; Intialize count
	SKIPA T3,RTRCBQ		; Point to first circuit
NMXSA1:	LOAD T3,RCNXT,(T3)	; Get next circuit
	JUMPE T3,NMXSA2		; Finished, return success
	LOAD T1,RCSTA,(T3)	; Get circuit state
	CAIN T1,RCS.OF 		; Is state "off"?
	 JRST NMXSA1		; Yes, step to next circuit
	CAML T4,T2		; Check to be sure we don't run off end
	 RNMXER (NF.MPE)	; NTMAN error if not enough room
	LOAD T1,RCLID,(T3)	; Get circuit ID
	MOVEM T1,(T4)		; Save into buffer
	AOS P2
	AOJA T4,NMXSA1		; Advance buffer and try another
NMXSA2:	STOR P2,NFBLN,(P1)	; Save count
	SKIPN T1,P2
	 RET
	RETSKP

	SUBTTL Network Management -- Show known circuits

;Call:	P1/ NF block

NMXSKC:	LOAD T2,NFBLN,(P1)	; Get length of buffer
	LOAD T4,NFBUF,(P1)	; Get buffer address
	ADD T2,T4		; Compute end of buffer
	SETZ P2,
	SKIPA T3,RTRCBQ		; Point to first circuit
NMXSK1:	LOAD T3,RCNXT,(T3)	; Get next circuit
	JUMPE T3,NMXSK2		; Finished, return success
	CAML T4,T2		; Check to be sure we don't run off end
	 RNMXER (NF.MPE)	; NTMAN error if not enough room
	LOAD T1,RCLID,(T3)	; Get circuit ID
	MOVEM T1,(T4)		; Save into buffer
	AOJ P2,			; Count it
	AOJA T4,NMXSK1		; Advance buffer and try another
NMXSK2:	STOR P2,NFBLN,(P1)	; Save count
	SKIPN T1,P2
	 RET
	RETSKP

	SUBTTL Network Management -- Return list of adjacencies on a circuit

NMXRAL:	SAVEAC <Q1,Q2>		; We need more than the temps
	LOAD T2,NFBLN,(P1)	; Get length of buffer
	LOAD Q2,NFBUF,(P1)	; Get buffer address
	ADD T2,Q2		; Compute end of buffer
	LOAD T1,NFEID,(P1)	; Get circuit identifier
	SETZ T3,		; Count of entries written to buffer
	LOAD Q1,RCAJQ,(RC)	; Get first in queue of adjacencies
NMXRA1:	JUMPE Q1,NMXRA3		; None, then done
	LOAD T1,AJSTA,(Q1)	; Get adjacency state
	CAIE T1,ADJ.UP		; Is it up?
	 JRST NMXRA2		; No, skip it then
	CAML Q2,T2		; Check to be sure we don't run off end
	 RNMXER (NF.MPE)	; NTMAN error if not enough room
	LOAD T1,AJADR,(Q1)	; Get node address
	MOVEM T1,(Q2)		; Save into buffer
	ADDI T3,1		; Count as written
	AOJ Q2,			; Advance buffer
NMXRA2:	LOAD Q1,AJNXT,(Q1)	; Step to next adjacency
	JUMPN Q1,NMXRA1		; Try next
NMXRA3:	STOR T3,NFBLN,(P1)	; Save count of number written for NTMAN
	MOVE T1,T3		; T1 is a success flag
	RET

	XRESCD

	SUBTTL Network Management Event Interface -- Router Event Types

;These are the various events that occur in router which are reported
;to network management.

DEFINE EVENTS,<
	RE APL,0, NON,<RTEPKH,RTENOD> ;AGED PACKET LOSS
	RE NUR,1, CKT,<RTEPKH,RTENOD> ;NODE UNREACHABLE PACKET LOSS
	RE NOR,2, CKT,<RTEPKH,RTENOD> ;NODE OUT-OF-RANGE PACKET LOSS
	RE OPL,3, CKT,<RTEPKH,RTENOD> ;OVERSIZED PACKET LOSS
	RE PFE,4, CKT,<RTEPKB,RTENOD> ;PACKET FORMAT ERROR
	RE PRL,5, CKT,<RTEPKH,RTENOD,RTEHIA> ;PARTIAL ROUTING UPDATE LOSS
	RE VRJ,6, CKT,RTENOD	;VERIFICATION REJECT
	RE LDL,7, CKT,RTEREA	;CIRCUIT DOWN, CIRCUIT FAULT
	RE ADS,8, CKT,<RTEREA,RTEPKH> ;ADJACENCY DOWN, SOFTWARE FAULT
	RE ADO,9, CKT,<RTEREA,RTEPKH,RTEEXN> ;ADJACENCY DOWN, OPERATOR FAULT
	RE LUP,10,CKT		;CIRCUIT UP
	RE IFL,11,CKT,RTEREA	;INITIALIZATION FAILURE, CIRCUIT FAULT
	RE IFS,12,CKT,<RTEREA,RTEPKH> ;INITIALIZATION FAILURE, SOFTWARE FAULT
	RE IFO,13,CKT,<RTEREA,RTEPKH> ;INITIALIZATION FAILURE, OPERATOR FAULT
	RE NRC,14,NOD,RTESTA	;NODE REACHABILITY CHANGE
	RE AUP,15,CKT,RTENOD	;ADJACENCY UP
	RE ARJ,16,CKT,RTENOD	;ADJACENCY REJECT
	RE ARC,17,ARE,RTENOD	;AREA REACHABILITY CHANGE
	RE AJD,18,CKT,RTENOD	;Adjacency down - Address change
	RE ADO,19,CKT,RTENOD	;Adjacency down, operator initiated
>


DEFINE RE(EVENT,TYPE,ENTTYP,PARAMS),<
	RE.'EVENT==^D<TYPE>>	;DEFINE THE RE MACRO

;Now expand the symbols.

	EVENTS
;Now define the processor macro.

DEFINE RE(PREFIX,VALUE,ENTTYP,PARAMS),<
	IFIW [MOVX T1,.NT'ENTTYP
	      STOR T1,NEETP,(P1)
	      SETZ T4,		;;INITIALIZE THE COUNT
	IRP <PARAMS>,<
	      CALL PARAMS	;;STICK ON THE PARAMETER DATA
	>
	      STOR T4,NEDLN,(P1) ;;GIVE ARG BLOCK THE LENGTH
	      RET]
>

;Make a table of event processors.

RTETAB:	EVENTS
	RET.LN==.-RTETAB
	RET.MX==<.-RTETAB>-1

	SUBTTL Network Management Event Interface -- Event Reason Definitions

;These are the various reasons which may be put in the reason field of
;events that include REASON as a event parameter.

	DEFINE REASON(SUFFIX,VALUE),<
	XP RS.'SUFFIX,^D'VALUE>

	REASON LSL,0		;Circuit synchronization lost
	REASON DTE,1		;Data errors
	REASON UPT,2		;Unexpected packet type
	REASON RUC,3		;Routing update checksum error
	REASON ANA,4		;Adjacent node address change
	REASON VRT,5		;Verification receive timeout
	REASON VSK,6		;Version skew
	REASON ANO,7		;Adjacency address out of range
	REASON ABS,8		;Adjacent node block size too small
	REASON IVS,9		;Invalid verification seed value
	REASON ALT,10		;Adjacent node listener receive timeout
	REASON ALI,11		;Adjacent node listener received invalid data
	REASON CFD,12		;Call failed
	REASON VPR,13		;Verification password required from P3 node
	REASON DBA,14		;Dropped by adjacent node

	PURGE REASON

	SUBTTL Network Management Event Interface -- RTNEVT - Event Reporter

;RTNEVT - Report Router event
;
; Call:
;	T1/ Event type
;	T2/ Entity ID (we know the entity type from RTETAB)
;	T3/ Event specific word (REASON, STATUS, etc.)
;	T4/ Message Block Pointer or zero if none given
;		(Message Block must be DNGINIized before this is called)
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T4
;
;This calls something which will call network management to log this event
;which has taken place.  This routine is called by the EVENT macro.

	EVTMLN==^D50		;MAXIMUM LENGTH OF EVENT PARAMETER DATA (BYTES)

RTNEVH:	SKIPA CX,[^D96]		;Special event class
RTNEVT:  SETZ CX,
	MOVEM CX,ECLASS
	SAVEAC <P1,P2,MB,MS>	;SAVE SOME ACS
	STKVAR <EVTSIG>		;Flag if we should use signal processor
	SETZM EVTSIG		;Initialize to "not a signal"
	SKIPL P2,T1		;RANGE CHECK THE
	CAILE P2,RET.MX		;EVENT TYPE
	BUG.(CHK,ROUUET,ROUTER,SOFT,<Unknown event type in RTNEVT>,,<

Cause:	We supplied ourselves with a bad event code.

Action:	Look for someone smashing T1 or a problem with the EVENTS macro

>,RTN)

	MOVE P1,T2		;SAVE THE ENTITY-ID
	MOVEM T3,RTREVW		;SAVE THE EVENT ARGUMENT
	MOVE MB,T4		;Presumed MB pointer to MB
;Check if event should be thrown away
	CAIE P2,RE.LDL		;Is this a line down event?
	 JRST RTNEV2		;No, then filter it
	LOAD T1,LIDEV,+P1	;Get the device type
	CAIN T1,LD.DTE		;DTE device?
	 JRST RTNEV4		;No filtering for DTE's

RTNEV2:	MOVX T1,.NCRTR		;ROUTER event class
	STOR T1,FACCL,+T2
	STOR P2,FACTY,+T2	; and event type to T2
	MOVX T1,EV.FIL		;Function code "filter event"
	CALL NMXEVT
	  RET			;  -throw it away
	TRNA	

RTNEV4:	SETOM EVTSIG		;Send to signal processor

	SKIPN RTRECP		;Verify that EC pointer is non-zero
	  RET			;  -was zero, must have failed to initialize
;Ok, log event
	SKIPN MB		;SET UP MESSAGE BLOCK (IF ANY)
	JRST RTNEV5		;NOT THERE, DON'T TRY TO SET POSITION

	MOVE T1,MB		;PASS POINTER TO MESSAGE BLK TO DNGINI
	CALL DNGINI		;SET UP FOR GETTING BYTES OUT OF MSG BLK
	CALL DNRPOS		;GET OUR CURRENT POSITION IN MESSAGE
	OPSTRM <EXCH T1,>,RMMK1,(MB) ;EXCHANGE WITH TOP OF MESSAGE POSITION
	CALL DNGPOS		;GO TO THAT POSITION

RTNEV5:	MOVX T1,NE.LEN+<<EVTMLN+3>/4> ;GET ENOUGH FOR ARG BLOCK AND
				; MAXIMUM AMOUNT OF EVENT PARAMETER DATA
	CALL DNGWDZ		;GET THE WORDS
	 BUG.(INF,ROUCGV,ROUTER,SOFT,<Couldn't get memory for event arg block>,,<

Cause:	DECnet has exhausted its free space.

>,RTN)
	STOR P1,NEEID,(T1)	;PUT ENTITY-ID IN EVENT BLOCK
	MOVE P1,T1		;SAVE THE POINTER TO THE BLOCK
	MOVE T1,RTRECP		;Get EC pointer
	STOR T1,NEECP,(P1)	; and store it in NE block
	STOR P2,NECTY,(P1)	;PUT THE EVENT TYPE IN THE ARG BLOCK
	SKIPN T1,ECLASS		;Special event class is 96. If 0 use Router's
	 MOVX T1,.NCRTR		;THE EVENT CLASS IS ROUTER
	STOR T1,NECCL,(P1)	;PUT IN NE ARG BLOCK
	XMOVEI T1,NE.LEN(P1)	;MAKE A FULLWORD POINTER TO DATA
	STOR T1,NEDAT,(P1)	;STORE POINTER TO IT IN ARG BLOCK
	MOVE T1,[POINT 8,NE.LEN(P1)] ;MAKE UP BYTE POINTER TO PARAMETER DATA
	EXCH T1,P2		;SWITCH FUNCTION AND POINTER
	CALL @RTETAB(T1)	;CALL THE PARAMETER PROCESSOR

	JUMPE MB,RTNEV6		;DON'T REPOSITION IN NO-EXISTANT MESSAGE
	LOAD T1,RMMK1,(MB)	;GET OUR LAST MESSAGE POSITION
	CALL DNGPOS		;GO TO THERE

RTNEV6:	MOVX T1,EV.LOG		;Assume function code "log an event"
	SKIPE EVTSIG		;Should this be sent to the signal processor?
	 MOVX T1,EV.SIG		;Do a signal since it is guaranteed to get thru
	MOVE T2,P1		; and NE pointer in T2
	CALL NMXEVT		;CALL THE EVENT PROCESSOR
	 TRN
	RET			;Return, NTMAN will deallocate NE block

	SUBTTL Network Management Event Interface -- Event Parameter Processors

;Put a packet header in the NE argument block string.  This corresponds to
;parameter 0 for circuit events.


RTEPKH:	STKVAR <MSGFLG>
	SKIPN MB		;MUST HAVE A MESSAGE BLOCK HERE
	BUG.(CHK,ROUEHM,ROUTER,SOFT,<No Message Block for Event data>,,<

Cause:	We are attempting to read data from an MB to report in an event but
	the caller failed to supply a message address.

Action:	Check caller and see why it didn't supply a message block address.

>,RTN)
	MOVEI T1,0		;GET PARAMETER NUMBER
	MOVEI T2,2		;GET NUMBER OF BYTES
	CALL PUTNBT		;INSTALL THE BYTES SWAPPED

	CALL DNG1BY		;GET THE "FIRST" BYTE FROM RTR MESSAGE
	 RET			;COULND'T GET IT, JUST RETURN
	TXZN T1,RM%MZ1		;IS THE PAD FLAG SET?
	IFSKP.
	  CALL PADSKP		;Skip over any pad bytes (NI) and return with
	   RET			; first byte of message.  Error if PADSKP fails
	ENDIF.
	MOVEM T1,MSGFLG		;SAVE MESSAGE FLAGS FOR LATER
	TMNN T1,RM%CTL		; Is this a control message?
	 TXNN T1,RM%MZ3		; No, is it a long format data header?
	IFSKP.
	  MOVEI T1,313		;Yes, then a coded multiple with 11 fields
	  CALL PUTBYT
	  MOVEI T1,041		;Data type = HEX image - one byte
	  CALL PUTBYT
	  MOVE T1,MSGFLG	;Recover message flags
	  CALL PUTBYT
	  CALL PUTENA		;Put in destination address
	   RET
	  CALL PUTENA		; and the source address
	   RET
	  CALL PUTDU1		;Next area router
	   RET
	  CALL PUTDU1		;Visit count
	   RET
	  MOVEI T1,041		;Data type = Hex number - one byte
	  CALL PUTBYT
	  CALL DNG1BY
	   RET
	  CALL PUTBYT		;Service class
	  CALL PUTDU1		; and finally the protocol type
	   TRN
	ELSE.			;Here if control or short format data
	  TXNE T1,RM%CTL	;WAS IT A CONTROL MESSAGE?
	   SKIPA T1,[302]	;YES, CODED MULTIPLE, TWO FIELDS
	  MOVEI T1,304		; NO, CODED MULTIPLE, FOUR FIELDS
	  CALL PUTBYT		;INSTALL DATA TYPE
	  MOVEI T1,041		;NOT CODED, HEX NUMBER OF 1 BYTE
	  CALL PUTBYT		;INSTALL IT
	  MOVE T1,MSGFLG	;Recover message flag byte
	  CALL PUTBYT		;OUTPUT IT
	  TXNE T1,RM%CTL	;IS IT A CONTROL MESSAGE???
	  IFSKP.
	    CALL PUTDU2		;Destination address
	     RET
	  ENDIF.
	  CALL PUTDU2		;Source address
	   RET
	  MOVE T1,MSGFLG	;GET BACK MESSAGE HEADER
	  TXNE T1,RM%CTL	;IS IT A CONTROL MESSAGE
	   RET			;YES, SKIP THIS ONE TOO
	  CALL PUTDU1		;No, include the the visit count
	   TRN
	ENDIF.
	RET
;Put a packet beginning in the NE argument block string.

RTEPKB:	SKIPN MB		;MUST HAVE A MESSAGE BLOCK HERE
	BUG.(CHK,ROUEHB,ROUTER,SOFT,<No Message Block for Event data>,,<

Cause:	We are attempting to read data from an MB to report in an event but
	the caller failed to supply a message address.

Action:	Check caller and see why it didn't supply a message block address.

>,RTN)
	MOVEI T1,1		;GET PARAMETER NUMBER
	MOVEI T2,2		;GET NUMBER OF BYTES
	CALL PUTNBT		;INSTALL THE BYTES SWAPPED
	CALL DNG1BY		;GET THE FIRST BYTE
	 RET			;RAN OUT, GIVE ERROR RETURN
	TXZN T1,RM%MZ1		;IS THE PAD FLAG SET?
	IFSKP.
	  CALL PADSKP		;Skip over any pad bytes (NI) and return with
	   RET			; first byte of message.  Error if PADSKP fails
	ENDIF.
	MOVEI T1,1		;Back up a byte so we are pointing to byte
	CALL DNBKBY		; containing the message flags again
	CALL PUTIMG		;And now the first 6 bytes of the message
	 TRN			;Ignore erorrs here
	RET

;Put the Highest Address in the NE argument block string.

RTEHIA:	MOVEI T1,2		;GET PARAMETER NUMBER
	MOVEI T2,2		;GET NUMBER OF BYTES
	CALL PUTNBT		;INSTALL THE BYTES SWAPPED
	MOVEI T1,002		;Data type - decimal unsigned, two bytes
	CALL PUTBYT
	MOVE T1,HIADDR		;Get highest address
	MOVEI T2,2		;Number of bytes to transfer
	CALL PUTNBT		;Put data into event buffer
	 TRN
	RET

;Put a node address in the NE argument block string.

RTENOD:	MOVEI T1,3		;GET PARAMETER NUMBER
RTEEX1:	MOVEI T2,2		;GET NUMBER OF BYTES
	CALL PUTNBT		;INSTALL THE BYTES SWAPPED

	MOVEI T1,301		;CODED MULTIPLE, ONE FIELD
	CALL PUTBYT		;INSTALL DATA TYPE
	MOVEI T1,002		;DECIMAL UNSIGNED, TWO BYTES
	CALL PUTBYT		;INSTALL SUB-DATA TYPE
	MOVE T1,RTREVW		;GET THE NODE NUMBER
	MOVEI T2,2		;NODE NUMBER IS TWO BYTES
	CALLRET PUTNBT		;PUT THE NUMBER IN THE MESSAGE

;Put the Expected Node in the NE argument block string.

RTEEXN:	MOVEI T1,4		;GET PARAMETER NUMBER
	JRST RTEEX1		;OTHEWISE, SAME AS NODE

;Put the REASON type in the NE argument block string.

RTEREA:	MOVEI T1,5		;GET PARAMETER NUMBER
RTEST1:	MOVEI T2,2		;GET NUMBER OF BYTES
	CALL PUTNBT		;INSTALL THE BYTES SWAPPED

	MOVEI T1,201		;CODED, SINGLE FIELD, LENGTH 1 BYTE
	CALL PUTBYT		;STUFF IT

	MOVE T1,RTREVW		;GET THE REASON
	CALLRET PUTBYT		;STUFF IT AND RETURN

;Put the status in the NE argument block.  This is a one byte field
;which we have in RTREVW, so just use RTEREA.

RTESTA:	MOVEI T1,7		;GET PARAMETER NUMBER
	JRST RTEST1		;STATUS PARAM IS JUST LIKE REASON


	SUBTTL Network Management Event Interface --Event Processor Subroutines

;Put n (in T2) bytes in the string pointed to by the NE argument block.
;It also updates T4, which is the count of bytes put in so far.

PUTNBY:	JUMPE T2,RSKP		;RETURN IF NO MORE BYTES LEFT
	CALL DNG1BY		;GET ONE BYTE FROM THE MESSAGE BLOCK
	 RET			;PROPAGATE FAILURE
	IDPB T1,P2		;STORE THE BYTE IN SOME SPACE
	ADDI T4,1		;UPDATE THE GLOBAL COUNT
	SOJA T2,PUTNBY		;SEE IF WE HAVE TO PUT IN MORE

;Put n (in T2) bytes of the number in T1.

PUTNBT:	JUMPE T2,RTN		;RETURN IF NOTHING LEFT
	IDPB T1,P2		;PUT THE BYTE IN THE MESSAGE
	LSH T1,-^D8		;SHIFT OVER TO THE NEXT BYTE
	ADDI T4,1		;UPDATE THE COUNT
	SOJA T2,PUTNBT		;DO THE REST

;Put one byte (in T1) into the data string for network management

PUTBYT:	IDPB T1,P2		;INSTALL THE BYTE
	AOJA T4,RTN		;INCREMENT THE NUMBER OF BYTES


PUTENA:	CALL PUTDU1		;Area
	 RET
	CALL PUTDU1		;Sub-area
	 RET
	CALLRET PUTIMG		;Now insert the Ethernet address as a HEX
				; image of 6 bytes

PUTDU1: MOVEI T1,001		;Put data type and data into event buffer
	CALL PUTBYT
	CALL DNG1BY		;Read a byte from the message
	 RET
	CALL PUTBYT		; and put it into the event buffer
	RETSKP

PUTDU2:	MOVEI T1,002		;Data type - decimal unsigned, two bytes
	CALL PUTBYT
	MOVEI T2,2		;Number of bytes to transfer
	CALL PUTNBY		;Put data into event buffer
	 RET
	RETSKP

PUTIMG:	MOVEI T1,040		;NOT CODED, HEX IMAGE
	CALL PUTBYT		;INSTALL DATA TYPE
	MOVEI T1,6		;LENGTH OF DATA IS ALWAYS 6 BYTES
	CALL PUTBYT		;INSTALL LENGTH OF DATA
	MOVX T2,6		;WE ALWAYS WANT SIX BYTES
	CALLRET PUTNBY		; PUT THEM IN THE NE BLOCK

	SUBTTL Network Management Event Interface -- Miscellaneous Event Processors

;These are a bunch of routines to report different events.  The events
;here are common to more than one routine, so they are collected
;together in this section.

;RTEMFE - Report Message Format Error Event

RTEMFE:	MOVEM T1,MFEWHO		;Remember who gave us this event
	MOVEM MB,MFEMSG		; and the MB being processed
	LOAD T2,RCLID,(RC)	;GET THE LINE ID FOR ENTITY ID
	SETZ T3,		;Default address to nothing
	SKIPE AJ
	 LOAD T3,AJADR,(AJ)	;Get address of sender
	EVENT RE.PFE,<Packet format error event>,MB
	AOS RTRCPF		;Count number of errors
	CALLRET FREMSG		;FREE MSG BLK AND RETURN


;RTEVRJ - Report Verficiation Reject Event

RTEVRJ:	LOAD T2,RCLID,(RC)	;GET THE ENTITY ID (CIRCUIT)
	LOAD T3,AJADR,(AJ)	; AND THE NEIGHBOR'S ADDRESS
	MOVE T4,RTRHOM		;GET THE HOME AREA
	DPB T4,[POINTR (T3,RN%ARE)] ;STORE IT IN T3
	EVENT RE.VRJ,<Verification reject event>
	AOS RTRCVR		; Count occurences
	CALLRET FREMSG		;FREE MSG BLK AND RETURN

;RTELDL - Report Circuit Down, Hard Circuit Fault Event

RTELDL:	MOVE T3,T1		;GET THE REASON
	LOAD T2,RCLID,(RC)	;GET THE LINE ID
	EVENT RE.LDL,<Circuit down, circuit fault>
	INCR RCCCD,(RC)		;INCREMENT THE CIRCUIT DOWN COUNTER
	RET			;RETURN, NO MSG TO FREE HERE

;RTELDS - Report Circuit Down, Software Fault Event

RTELDS:	MOVE T3,T1		;GET THE REASON
	LOAD T2,RCLID,(RC)	;GET THE LINE ID
	EVENT RE.ADS,<Adjacency down, software fault>,MB
	CALL SETRCC		;Set recompute routing flags
	JN RCBCT,(RC),RTELD1
	INCR RCCCD,(RC)		;Increment the circuit down counter
	CALL RTRZCB		;Clean out the circuit block
	CALL RTRSAO		;Set any remaining adjacencies to unused
	CALLRET FREMSG		;FREE MSG BLK AND RETURN
RTELD1:	SKIPN ENFLG		; Endnode?
	IFSKP.
	  MOVX T1,ADJ.UN	; Set adjacency state to unused
	  STOR T1,AJSTA,(AJ)
	  SETZRO RCDSL,(RC)	; Clear DR address
	ELSE.
	  MOVE T1,AJ		;The adjacency to zap
	  MOVE T2,RC		; and the circuit block it belongs to
	  CALL RTRZAB		;Flush the adjacency block
	ENDIF.
	CALLRET FREMSG		;FREE MSG BLK AND RETURN

;RTELDO - Report Circuit Down, Operator Fault Event

RTELDO:	MOVE T3,T1		;GET THE REASON
	LOAD T2,RCLID,(RC)	;GET THE LINE ID
	EVENT RE.ADS,<Adjacency down, operator fault>,MB
	INCR RCCCD,(RC)		;INCREMENT THE CIRCUIT DOWN COUNTER
	RET


;RTEADO - Report Adjacency Down, Operator Fault Event

RTEADO:	MOVE T3,T1		;GET THE REASON
	LOAD T2,RCLID,(RC)	;GET THE LINE ID
	EVENT RE.ADO,<Adjacency down, operator fault>,MB
	INCR RCAJD,(RC)		;Increment the adjacency down counter
	CALL SETRCA		;Set recompute routing flags
	JN RCBCT,(RC),RTEAD1	;No circuit down if broadcast
	MOVX T1,RS.ANA		;Event reason: Adjacent node address change
	CALL RTELDO		;Report circuit down - Circuit fault event
	CALL CIRFAI		;Close the port and set reopen time
	CALLRET FREMSG		; and return message

RTEAD1:	SKIPN ENFLG		; Endnode?
	IFSKP.
	  MOVX T1,ADJ.UN	; Set adjacency state to unused
	  STOR T1,AJSTA,(AJ)
	  SETZRO RCDSL,(RC)	; Clear DR address
	ELSE.
	  MOVE T1,AJ		;The adjacency to zap
	  MOVE T2,RC		; and the circuit block it belongs to
	  CALL RTRZAB		;Flush the adjacency block
	ENDIF.
	CALLRET FREMSG		;FREE MSG BLK AND RETURN

;Event Processors - Continued from last page

;RTEINL - Report Initialization Failure, Hard Circuit Fault Event

RTEINL:	MOVE T3,T1		;GET THE REASON
	LOAD T2,RCLID,(RC)	;GET THE LINE ID
	EVENT RE.IFL,<Initialization failure, circuit fault>
	INCR RCCCD,(RC)		;INCREMENT THE CIRCUIT DOWN COUNTER
	RET			;RETURN, NO MSG BLK TO FREE HERE

;RTEINS - Report Initialization Failure, Software Fault Event

RTEINS:	MOVE T3,T1		;GET THE REASON
	LOAD T2,RCLID,(RC)	;GET THE LINE ID
	EVENT RE.IFS,<Initialization failure, software fault>,MB
	INCR RCCCD,(RC)		;INCREMENT THE CIRCUIT DOWN COUNTER
	CALL FREMSG		;FREE MSG BLK AND RETURN
	CALLRET CIRFAI		;RE-INITIALIZE THE DLL

;RTEINO - Report Initialization Failure, Operator Fault Event

RTEINO:	MOVE T3,T1		;GET THE REASON
	LOAD T2,RCLID,(RC)	;GET THE LINE ID
	EVENT RE.IFO,<Initialization failure, operator fault>,MB
	INCR RCCCD,(RC)		;INCREMENT THE CIRCUIT DOWN COUNTER
	CALL FREMSG		;FREE MSG BLK AND RETURN
	CALLRET CIRFAI		;RE-INITIALIZE THE DLL

	SUBTTL Miscellaneous Routines -- RTRSAO - Set Adjacency off line

;RTRSAO - Set all adjacencies associated with a circuit block off line.
;
; Call:
;	RC/ Pointer to circuit block
;
;	RET			;With ADJ(RC) set to unused
;
; Uses: T1,T2,T3

RTRSAO:	SAVEAC AJ
	OPSTR <SKIPA AJ,>,RCAJQ,(RC)	;Get the first adjacency block
RTRSO2:	LOAD AJ,AJNXT,(AJ)	;Get the next one
	JUMPE AJ,RTN
	SETZ T1,
	STOR T1,AJFLA,(AJ)	;CLEAR OUT ALL FLAGS
	STOR T1,AJADR,(AJ)	;CLEAR OUT THE NODE ADDRESS
	MOVX T1,ADJ.UN		;Set adjacency to unused
	STOR T1,AJSTA,(AJ)	;Set it
	LOAD T1,AJRTV,(AJ)	;Get the address of the routing vector
	JUMPE T1,RTRSO2		;If no vector don't smear it
	MOVE T2,RTRMXN		;Get the maximum number of nodes
	ADD T2,T1		;For end of smear
	MOVX T3,RNCHG!RNHOP!RNCST ;Get INFH and INFC and set them in the
	CALL DNSWDS		; vector
	JRST RTRSO2		;Try for another


	SUBTTL Miscellaneous Routines -- RTRZCB - Zap Circuit Block

;RTRZCB - Zap circuit block and report circuit down event
;
; Call: 
;	RC/ Pointer to circuit block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1

RTRZCB:	TMNE RCBCT,(RC)		;If point-to-point don't free adjacency
	CALL RTRZAA		;Zap all the adjacencies
	SETZ T1,		;Clear some fields
	STOR T1,RCTLS,(RC)	;Time last message sent
	STOR T1,RCTLR,(RC)	; and the time we sent last routing message
	STOR T1,RCTIN,(RC)	; and restart timer
	SETZRO <RCSRM,RCSHM>,(RC) ;Clear some flags
	SETZRO RCDSL,(RC)	;Clear address of desiginated router
	LOAD AJ,RCAJQ,(RC)	;Get pointer to adjacency block
	JUMPE AJ,RTN		;Return if none
	MOVE T1,RTRVER		;Reset adjacency version
	STOR T1,AJVER,(AJ)	;...
	MOVE T1,RTRECO		;...
	STOR T1,AJECO,(AJ)	;...
	MOVE T1,RTRCUS		;...
	STOR T1,AJCUS,(AJ)	;...
	RET			;Return
	SUBTTL Miscellaneous Routines -- RTRZAA - Zap all Adjacancies associated with RC

;RTRZAA - Zap all adjacency blocks associated with a circuit block in RC
;
; Call: 
;	RC/ Router circuit block
;
; 	RET			; Return with all adjacencies freed
;
RTRZAA:	LOAD T1,RCAJQ,(RC)	; Pointer to adjacencies
	JUMPE T1,RTN		; None left
	MOVE T2,RC		; Circuit block	
	CALL RTRZAB		; Adjacency must go
	JRST RTRZAA		; Do the next one
	SUBTTL RTRZAB - Zap an adjacency block
;RTRZAB - Zap an adjacency block - Returns adjacency block to DNFWDS
;
; Call: 
;	T1/ Adjacency to zap
;	T2/ Circuit block adjacency belongs to
;
; 	RET			;Return with adjacency cleared out
;				; and adjacency in the off-line queue

RTRZAB:	SAVEAC <P1,P2>
	DMOVE P1,T1
	LOAD T1,AJNTY,(P1)	;Get the adjacency type
	CAIE T1,ADJ.LN		;It it an endnode?
	 JRST RTRZB1		; No
	SOS RTRBEA		;Yes, decrement count of endnode adjacencies
	JRST RTRZB2
RTRZB1:	OPSTR <SKIPE T1,>,AJRTV,(P1) ;Get the adjacencies routing vector
	CALL DNFWDS		;Return it
	SOS RTRBRA		;Decrement count of router adjacencies
	DECR RCNRO,(RC)		; and count of the number on this Ethernet
RTRZB2:	RMVQUE P1,<RC.AJQ(P2)>,AJ.NXT,T1 ;Dequeue the adjacency
	SETZRO AJNXT,(P1)	; Clear next adjacency pointer
	SKIPE T1,AJOFLQ		; Anything in the off-line queue?
	 STOR T1,AJNXT,(P1)	; Yes, link it to this one
	MOVEM P1,AJOFLQ		;  and make this one the first
	MOVEI T1,ADJ.OL		; New state is off-line
	STOR T1,AJSTA,(P1)	;  and set it
	RET	
	SUBTTL Miscellaneous Routines -- RTRMAJ - Make an Adjacency Block

;RTRMAJ - Allocate an adjacency block 
;
; Call:
;	T1/ Flag indicating routing/non-routing node (0=non-routing)
;	RC/ Circuit block
;
; Return:
;	RET			;Couldn't allocate block
;	RETSKP			;Everything worked, (T1) = Adjacency block
;
; Uses: T1-T4

RTRMAJ:	SAVEAC <P1,P2>		;SAVE A PEA OR TWO
	MOVE P2,T1		;Save adjacency type
	MOVX T1,AJ.LEN		;GET THE LENGTH OF AN ADJACENCY BLOCK
	CALL DNGWDZ		;GET THAT MANY WORDS
	 RET			;RETURN A FAILURE
	MOVE P1,T1		;SAVE THE POINTER TO THE ADJACENCY
	JUMPE P2,RTRMA1		;DON'T MAKE ROUTING VECTOR FOR NON-ROUTING NODE
	MOVE T1,RTRMXN		;GET THE MAXIMUM NODE NUMBER
	ADDI T1,1		;ADD ONE FOR NODE ZERO (Nearest area router)
	CALL DNGWDZ		;GET THAT MANY WORDS
	 JRST [MOVE T1,P1	;GET THE ADDRESS OF THE ADJACENCY BLOCK
	       CALLRET DNFWDS]	;FREE THE WORDS AND GIVE ERROR RETURN
	STOR T1,AJRTV,(P1)	;YES, STORE THE ADDRESS OF THE ROUTING VECTOR
	MOVE T2,RTRMXN		;GET THE MAXIMUM NUMBER OF NODES
	ADD T2,T1		;FOR END OF SMEAR
	MOVX T3,RNCHG!RNHOP!RNCST ;GET INFH AND INFC AND CHANGE FLAG
	CALL DNSWDS		;SMEAR THE VECTOR

RTRMA1:	STOR RC,AJCBP,(P1)	;SAVE THE ADDRESS OF HIS CIRCUIT BLOCK
	MOVX T2,ADJ.IN		;THIS IS INITIALIZING
	STOR T2,AJSTA,(P1)	;SAVE IT
	MOVX T2,%RTTM4		;GET THE DEFAULT LISTENER TIME-OUT VALUE
	STOR T2,AJNHT,(P1)	;STORE IT IN THE ADJACENCY BLOCK
	MOVE T1,RTRVER		;SET ADJACENCY VERSION INFO
	STOR T1,AJVER,(P1)	;...
	MOVE T1,RTRECO		;...
	STOR T1,AJECO,(P1)	;...
	MOVE T1,RTRCUS		;...
	STOR T1,AJCUS,(P1)	;...
	ENDQUE P1,<RC.AJQ(RC)>,AJ.NXT,T1 ;Add adjacency to the queue
	MOVE T1,P1		;GET THE ADDRESS OF THE ADJACENCY BLOCK IN T1
	RETSKP			;RETURN SUCCESS

	SUBTTL Miscellaneous Routines -- RTRMCB - Make a Circuit Block

;RTRMCB - Make a new circuit block and store the defaults
;
; Call:
;	T1/ Line id
;
; Return:
;	RET			;COULDN'T ALLOCATE BLOCK
;	RETSKP			;EVERYTHING WORKED, T1 CONTAINING CIRCUIT BLOCK
;				; POINTER
; Uses: T1-T4

RTRMCB:	SAVEAC P1		;SAVE A PEA
	MOVE P1,T1		;SAVE THE LINE ID
	MOVX T1,RC.LEN		;GET THE LENGTH OF A CIRCUIT BLOCK
	CALL DNGWDZ		;GET THAT MANY WORDS
	 RET			;RETURN A FAILURE

	STOR P1,RCLID,(T1)	;STORE THE LINE ID

	LOAD T2,LIDEV,+RC.LID(T1) ;GET THE TYPE OF DEVICE
	CAIE T2,LD.ETH		;Is it for the ethernet?
	 JRST RTRMC1		;No, must something else
	SETONE RCBCT,(T1)	;Mark as broadcast
	MOVEI T3,%RTMXR		; Set default maximum number of routers
	STOR T3,RCMXR,(T1)	;  for this circuit
RTRMC1:	MOVE T2,KONCST(T2)	;GET DEFAULT COST FOR THIS TYPE OF LINE
	STOR T2,RCCST,(T1)	;STORE IT IN THE CIRCUIT BLOCK
	MOVX T2,RCS.OF		;INITIAL STATE WILL BE "OFF"
	STOR T2,RCSTA,(T1)	;STORE STATE CODE
	MOVE T2,RTRBSZ		; Start with our block size as the minimum
	STOR T2,RCBSZ,(T1)
	MOVX T2,%RTTM3		;GET THE DEFAULT HELLO MESSAGE TIMER VALUE
	STOR T2,RCTM3,(T1)	;STORE IT IN THE CIRCUIT BLOCK

	MOVE P1,T1		;SAVE THE CIRCUIT BLOCK POINTER
	AOS RTRNLN		;INCREMENT THE NUMBER OF CIRCUITS
	CALL DNGTIM		;GET CURRENT TIME OF DAY
	STOR T1,RCSLZ,(P1)	;STORE FOR USE BY SECONDS SINCE LAST ZEROED
	MOVE T1,P1		;RETURN THE CIRCUIT BLOCK POINTER IN T1
	RETSKP			;RETURN SUCCESS

	SUBTTL Miscellaneous Routines -- RTRGCB - Get Circuit Block Pointer

;RTRGCB - Get circuit block pointer from line id
;
; Call:
;	T1/ Line id
;
; Return:
;	RET			;WHEN WE COULDN'T FIND A MATCH
;	RETSKP			;WHEN SUCCESSFUL, WITH RC POINTING TO THE
;				; CIRCUIT BLOCK
;
; Uses: T1,T2
;
;There aren't that many circuits, so a simple search is not too bad.

RTRGCB:	SKIPN RC,RTRCBQ		;START LOOKING AT FIRST CIRCUIT
	RET			;NONE THERE, JUST RETURN
RTRGC1:	LOAD T2,RCLID,(RC)	;GET IT'S LINE ID
	CAMN T1,T2		;IS IT THE ONE WE'RE LOOKING FOR?
	RETSKP			;GOOD RETURN (I SKIP OVER RETSKPS
				; IF YOU DON'T LIKE THAT CHANGE IT)
	LOAD RC,RCNXT,(RC)	;GET THE NEXT CIRCUIT BLOCK
	JUMPN RC,RTRGC1		;GO CHECK IT OUT
	RET			;COULDN'T FIND IT, OH WELL
;RTRGAJ - Find the adjacency associated with a given node
;
; Call:
;	T1/ 16 bit node number
;	RC/ Circuit owning adjacency
;
; Return:
;	RET			; Node is not in our area
;	RETSKP			; Success with AJ pointing to adjacency
;				; block, or 0 if no such adjacency
;
; Uses: T1,T2,AJ

RTRGAJ:	LDB T2,[POINTR(T1,RN%ARE)] ;Get the area number
	CAME T2,RTRHOM		;Is it the same as mine?
	 RET			; No, can't be an adjacency
	LDB T1,[POINTR(T1,RN%NOD)] ;GET JUST THE NODE NUMBER
	OPSTR <SKIPA AJ,>,RCAJQ,(RC) ;Get first adjacency
RTRGJ2:	 LOAD AJ,AJNXT,(AJ)	;Ckeck next adjacency if any
	JUMPE AJ,RSKP		;No more, try next circuit block
	LOAD T2,AJNAN,(AJ)	;Get the node address of this adjacency
	CAME T1,T2		;Do we have a match?
	 JRST RTRGJ2		; Nope. Try again
	RETSKP			;Yes, give good return
; This routine will check the adjacency pointed to by AJ to determine if it is
; a known two-way adjacency.

CK2WAY:	JUMPE AJ,RTN		;Give non-skip if no such adjacency
	LOAD T1,AJSTA,(AJ)	;Get the state
	CAXE T1,ADJ.UP		;Is this a known two-way?
	 RET			; Nope
	RETSKP			;Yes.


;	SUBTTL	Miscellaneous Routines -- CVTSTN

;Convert low order of Ethernet address to 16 bit format

;Call:
;	T2/ Low order (16 bits) of Ethernet address left justified in
;	    string format
;
;Ret:	
;	T1/ 16 bit address
;	T2/ 16 Bit address

CVTSTN:	SETZ T1,		; Address we want is in T2 left justified
	LSHC T1,^D8		; in string format.  Shift nn-2 into T1
	LSH T2,-^D20		; Right justify the area+2
	IORB T1,T2		;  and include the number
	RET

	SUBTTL Miscellaneous Routines -- CPYMSG - Copy all MSDs together

;CPYMSG - Gather all the MSDs into one (IN.MSD) in a new message block
;
; Call:
;	MB/ Pointer to message block to make a copy of
;
; Return:
;	RET			;ON FAILURE
;	RETSKP			;DONE, WITH T1 POINTING TO THE NEW MESSAGE
;
; Uses: T1-T4
;
;Only the portions of the message block which are relavent to Router
;are copied, because the copied message will be sent to NSP, which
;believes that the message has come in over the network.

CPYMSG:	CALL DNCPMS		;COPY MB'S MSDs INTO A NEW BLOCK
	  RET			;PROPOGATE ERROR
				;T1 POINTS TO NEW MSG BLK

;Now copy all the relavent fields.

	LOAD T2,RMFST,(MB)	;GET THE FIRST BYTE
	STOR T2,RMFST,(T1)	; AND COPY IT
	LOAD T2,MBLCL,(MB)	;GET THE COPY OF THE LOCAL BIT
	STOR T2,MBLCL,(T1)	; AND COPY IT
	LOAD T2,RMOAP,(MB)	;GET THE OUTPUT ADJACENCY POINTER
	STOR T2,RMOAP,(T1)	; AND COPY IT
	LOAD T2,RMIAP,(MB)	;GET THE INPUT ADJACENCY POINTER
	STOR T2,RMIAP,(T1)	; AND COPY IT
	LOAD T2,RMICP,(MB)	;GET THE INPUT CIRCUIT
	STOR T2,RMICP,(T1)	; AND COPY IT (MONOTONOUS, ISN'T IT)
	LOAD T2,MBSRC,(MB)	;GET SOURCE NODE ADDRESS
	STOR T2,MBSRC,(T1)	; AND COPY IT INTO THE NEW COPY
	LOAD T2,MBDST,(MB)	;GET THE DESTINATION NODE ADDRESS
	STOR T2,MBDST,(T1)	; AND COPY IT INTO THE NEW COPY
	RETSKP			;RETURN SUCCESS, T1 PTS TO NEW MSG BLK

	SUBTTL Miscellaneous Routine -- RTRGBS - Get maximum blocksize

;RTRGBS - Called by SCLINK to get the maximum blocksize supported
; between this node and the requested node.

;Call:
;	T1/ Node address (16 bit format)

;Return:
;	T1/ Blocksize to use

	INTERN RTRGBS
	XRESCD

RTRGBS:	LDB T2,[POINTR(T1,RN%ARE)] ; Extract the area number
	TXZ T1,RN%ARE		; Clear the area number
	CAMN T1,RTRADR		; Local connection?
	CAME T2,RTRHOM		; ...
	IFSKP.
	  LOAD T1,IBBSZ,+IBBLK	; Yes, use requested blocksize
	  RET
	ENDIF.
	SKIPN ENFLG		; Endnode?
	IFSKP.
	  MOVE T1,RTRBSZ	; Then supply default
	  SKIPE T3,RTRCBQ	;  unless the only circuit
	   LOAD T1,RCRBS,(T3)	;  is initialized
	  RET
	ENDIF.
	CAME T2,RTRHOM		; Is it in our area?
	IFSKP.
	  MOVE T2,T1		; Yes, get the number
	  ADD T2,RTRNRV		; Add in the offset to routing vector
	  TMNN RNRCH,(T2)	; Is it reachable?
	  IFSKP.
	    ADD T2,RTROFS	; Yes, index into the output adjacency vector
	    MOVE T2,(T2)	; Get adjacency
	    OPSTR <CAME T1,>,AJNAN,(T2) ; Is it our adjacency?
	    IFSKP.
	      LOAD T1,AJBSZ,(T2); Yes, use its blocksize
	      OPSTR <CAMLE T1,>,IBBSZ,+IBBLK ; If it exceeds our capabilities
	       LOAD T1,IBBSZ,+IBBLK ; return only what we can
	      RET
	    ENDIF.
	  ENDIF.
	ENDIF.
	MOVE T1,RTRBSZ		; No, use default
	RET

	SUBTTL Miscellaneous Routines -- RTRCBS - Get useable blocksize

;Call:
;	T1/ Node address
;	T2/ Suggested blocksize

;Return:
;	T1/ Blocksize we believe will work

	INTERN RTRCBS
	XRESCD

RTRCBS:	STKVAR <NODADR,BSZ>
	MOVEM T1,NODADR		; Save address
	MOVEM T2,BSZ		; Save suggested blocksize
	SKIPN ENFLG		; Endnode?
	IFSKP.
	  TXZ T1,RN%ARE		; Yes, remove area number
	  ADD T1,RTRNRV         ; Add address of cache vector
	  TMNN RNCCH,(T1)	; See if in cache
	  IFSKP.
	    MOVE T1,NODADR	; In cache, get address
	    CALL RTRGBS		; (T1) Get maximum we can handle
	  ELSE.
	    MOVE T1,RTRBSZ	; Not in cache, use default block size
	  ENDIF.
	ELSE.                   ; This is a routing node, so
	  CALL RTRGBS		; (T1) see what segsize we can use
	ENDIF.
	CAMLE T1,BSZ            ; Was what we were given too large?
	MOVE T1,BSZ             ; No, OK to use what we were given
	RET

	ENDSV.

	SUBTTL Miscellaneous Routines -- FREMSG - Toss a Message

;FREMSG - Return an unwanted message to rightful sink
;
; Call: 
;	MB/ Pointing to Message Block
;
; Return: 
;	RET			;ALWAYS
;
; Uses: T1-T4

FREMSG:	SKIPN T1,MB		;GET POINTER TO MESSAGE BLOCK
	BUG.(CHK,ROUZXT,ROUTER,SOFT,<Tried to free msg with MB=0>,,<

Cause:	FREMSG called to free an MB but was given a zero pointer.

Action:	Check caller and see why no MB address was supplied.

>,RTN)

	TMNE RMODN,(MB)		;DOES NSP WANT THIS BACK?
	CALLRET R2NODN		;YES, GO GIVE "OUTPUT DONE"
				;NO, GIVE MSG TO MEMORY MANAGER
	TRASH MB,		;JUST CAREFUL
	CALLRET DNFMSG		;FREE MESSAGE T1 POINTS TO
	SUBTTL Miscellaneous Routines -- SETRCx - Set Recompute Routing Flags


;SETRCX - Set recompute routing flags for all nodes
;
; Call:
;	CALL SETRCX
;
; Return:
;	RET always

SETRCX:	SAVEAC <P1>		;Save P1
	MOVN P1,RTRMXN		;Get negative node count
	HRLZS P1		;Make into AOBJN pointer
SETRX1:	MOVEI T1,(P1)		;Get node number
	CALL SETRCF		;Set recompute routing flag
	AOBJN P1,SETRX1		;Loop back for all nodes
	RET			;And return


;SETRCC - Set recompute routing flags for entire circuit
;
; Call:
;	RC/ Address of circuit block
;
; Return:
;	RET always

SETRCC:	SAVEAC <AJ>		;Save AJ
	OPSTR <SKIPA AJ,>,RCAJQ,(RC) ;Get address of first adjacency block
SETRC1:	LOAD AJ,AJNXT,(AJ)	;Get address of next adjacency block
	JUMPE AJ,RTN		;Return at end of adjacency list
	CALL SETRCA		;Set recompute flags for this adjacency
	JRST SETRC1		;Loop back for all adjacencies


;SETRCA - Set recompute routing flags for an adjacency
;
; Call:
;	AJ/ Address of adjacency block
;
; Return:
;	RET always

SETRCA:	LOAD T1,AJNAN,(AJ)	;Get node number of adjacent node
	CALL SETRCF		;Set recompute flags for this node
	OPSTR <SKIPN>,AJRTV,(AJ) ;Does adjacency have a routing vector?
	RET			;No, return now
	SETZ T3,		;Starting with node zero
	MOVE T4,RTRNRV		;...
	ADD T4,RTROFS		;Offset to output adjacency vector
SETRA1:	CAMLE T3,RTRMXN		;Finished all nodes?
	RET			;Yes, return now
	MOVE T1,T3		;Get current node number
	CAMN AJ,(T4)		;Was this adjacency the route for this node?
	CALL SETRCF		;Yes, set recompute routing flag for this node
	ADDI T4,1		;Update address of routing vector
	AOJA T3,SETRA1		;Update node address and loop for all nodes


;SETRCF - Set recompute routing flag for a node
;
; Call:
;	T1/ Node address
;
; Return:
;	RET always
;
;Routine must preserve T3 and T4

SETRCF:	ANDX T1,RN%NOD		;Isolate node number
	IDIVI T1,^D36		;Compute bitmap entry
	ADD T1,RTRNVB		;Offset into bitmap
	MOVE T2,BITS(T2)	;Get bit to set
	IORM T2,(T1)		;Set bit
	SETOM RTRRCF		;Set recompute routing flag
	RET			;And return
	SUBTTL Miscellaneous Routines -- CKSNRM - Normalize Checksum

;CKSNRM - Make full word checksum into 16 bit checksum
;
; Call: 
;	T1/ Full word checksum
;
; Return: 
;	RET			;WITH T1 CONTAINING THE 16 BIT CHECKSUM
;
; Uses: T1-T2

CKSNRM:	MOVE T2,T1		;GET A COPY OF THE CHECKSUM
	TDZE T2,[EXP ^-177777]	;IF ANY OVERFLOW, CLEAR IT
	JRST [LSH T1,-^D16	;ADD OVERFLOW BACK INTO
	      ADDB T2,T1	; THE LOW ORDER BYTES
	      JRST .-1]		;LOOP UNTIL DONE
	RET			;RETURN

;Here to get the uptime in jiffies for the message looper
;This is a separate routine so we don't have switch to section 1
;in so many places.

IFN FTRTST,<
TSTTIM:	TORESCD
	CALL UPDTCK		; Must be executed in section 1
	TOXRESCD
	RET
>


	SUBTTL Local Router Storage

	RESDT			;IMPURE STORAGE

CHKVAL:	EXP ^D60		;CHECKSUM RECOMPUTATION COUNTER VALUE
CHKCNT:	EXP 0			;CHECKSUM RECOMPUTATION COUNTER
IFN FTOPS20,<			;TOPS20 CALLS RTRSEC 10 TIMES A SECOND
SECCNT:	EXP 0			;COUNT-DOWN 100 MS CALLS AT RTRSEC
>				;END IFN FTOPS20
CCHTIM: EXP 0			;INTERVAL COUNTER FOR CACHE FLUSHING (ENDNODE)
RTRCTO:	%RTCTO			;Time we can spend in cache till flushed
RTRNTY:	BLOCK 1			;NODE TYPE FOR LOCAL NODE
ENFLG:	EXP 0			;Endnode (non-routing) flag
IFN FTOPS10,<
	INTERNAL RTRNRV,RTROFS,RTRCBQ
>; END IFN FTOPS10
RTRNVB:	BLOCK 1			;"NORMAL" ROUTING VECTOR BITMAP
RTRNRV:	BLOCK 1			;"NORMAL" ROUTING VECTOR
RTRNOV:	BLOCK 1			;"NORMAL" OUTPUT-CIRCUIT VECTOR
RTROFS:	BLOCK 1			;OFFSET FROM NRV TO NOV
RTNBRA:	%RTBRA			;MAXIMUM NUMBER OF BROADCAST ROUTER ADJACENCIES
RTNBEA:	%RTBEA			;MAXIMUM NUMBER OF BROADCAST ENDNODE ADJACENCIES
RTRPRI:	%RTPRI			;Our priority to be the designated router
RTRMCN:	^D20			;Maximum number of circuits (default)
RTRCBQ:	BLOCK QH.LEN		;CIRCUIT BLOCK QUEUE HEADER
AJOFLQ: 0			;Queue of offline adjacencies to be freed
				; after routing update
RTRNLN:	BLOCK 1			;Number of circuits in use
RTRMXC:	%RTMXC			;Maximum circuit cost
RTRMXH:	%RTMXH			;Maximum hops
RTRMXV:	%RTMXV			;Maximum visits
RTRBRA:	0			;Number of broadcast router adjacencies
RTRBEA:	0			;Number of broadcast endnode adjacencies
RTRRCF:	0			;RECOMPUTE ROUTING FLAG
RTRVRQ:	0			;VERIFICATION REQUIRED FLAG
RTRNAD: BLOCK 1			;Our address in 16 bit format
RTRCKS: BLOCK 1			;CHECKSUM OF ROUTING MESSAGE
RTRLMG:	BLOCK 1			; OFFSET TO LAST ROUTING MESSAGE SENT
				;TO ALLOW US TO SEND OUT THE SEGMENTS
				;IN A DIFFERENT ORDER EACH TIME
RTRTYP:	EXP 4			;OUR TYPE OF NODE IS ROUTING.
RTRTM1:	%RTTM1			;ROUTING MESSAGE TIMER (P-P)
RTRBT1:	%RTBT1			;ROUTING MESSAGE TIMER (BROADCAST)
RTRTM2:	DEC 1000		;Minimum time between routing updates, and
				; Ethernet hello messages.
RTRITM:	%RTITM			;MAX TIME ALLOWED FROM PROTOCOL UP AND TI RCVD 
RTRCRT:	EXP <^D5*^D1000>	;Circuit re-open timer value
BMVTIM: BLOCK 1			;Time of next maybe vector update
BMVIVL: DEC 15000		;Interval between maybe vector updates
RTRALR:	EXP %RTRMA,0		;ID ALL-ROUTERS
RTRAEN:	EXP %RTEMA,0		;ID ALL END-NODES
RTRPRO::EXP 60003	 	;PROTOCOL-TYPE FOR ALL ROUTERS

RTRRSQ: BLOCK QH.LEN		;RESEND QUEUE HEADER
IFN FTOPS20,<
;RTRLOK:	EXP -1			;THE ROUTER "INTERLOCK"
;RTRLKO:	EXP -1			;INTERLOCK OWNER
>
;RTRSSV:	BLOCK 1			;ONCE-A-SEC SERVICE NEEDED FLAG
MFEWHO:	BLOCK 1			; PC of routine detecting message format error
MFEMSG:	BLOCK 1			; MB containing bogus message
HIADDR:	BLOCK 1			; Argument for P.R.U.L. event
RTREVW: BLOCK 1			;EVENT WORD (PLACE TO KEEP REASON, NODE, ETC.)
RTRECP:	EXP 0			;Event communication pointer
ECLASS: EXP 0			;Event class
				;NODE COUNTERS:
RTRCAP:	BLOCK 1			; (900) AGED PACKET LOSS
RTRCNU:	BLOCK 1			; (901) NODE UNREACHABLE PACKET LOSS
RTRCNO:	BLOCK 1			; (902) NODE OUT-OF-RANGE PACKET LOSS
RTRCOP:	BLOCK 1			; (903) OVERSIZED PACKET LOSS
RTRCPF:	BLOCK 1			; (910) PACKET FORMAT ERROR
RTRCPR:	BLOCK 1			; (920) PARTIAL ROUTING UPDATE LOSS
RTRCVR:	BLOCK 1			; (930) VERIFICATION REJECT
IFN FTRTST,<
TSTFLG:	Z
TSTBLK::BLOCK TR.LEN
>
	XRESCD			;BACK TO THE PURE SEGMENT
	SUBTTL End of ROUTER

IFN FTOPS10, .XCMSY             ;.XCREF SOME MACSYM SYMBOLS
IFN FTOPS20, TNXEND		;TOPS-20 ENDING

IFN FTOPS10,<
	RESDT
RTRLOW::!
	XRESCD
>; END IFN FTOPS10
	END