Google
 

Trailing-Edge - PDP-10 Archives - BB-F493Z-DD_1986 - 10,7/703mon/router.mac
Click 10,7/703mon/router.mac to see without markup as text/plain
There are 14 other files named router.mac in the archive. Click here to see a list.
      ;TITLE ROUTER - Routing Layer Service for DECnet-36  V075


;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.
;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 19 FEB 86
		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,075
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,1986>
>;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 %RTCST		;DEFAULT CIRCUIT COST
	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,,,,<%RTTM1>,<CALL [IMULI T2,TIMBAS
				   MOVEM T2,RTRTM1
				   RNMXOK ]>,<
			     CALL [MOVE T2,RTRTM1
				   IDIVI T2,TIMBAS
				   RNMXOK ]>,<
			     CALL [IMULI T2,TIMBAS
				   MOVEM T2,RTRTM1
				   RNMXOK ]>,<Maximum update interval timer>)
PARAMETER(^D912,,,,<%RTBT1>,<CALL [IMULI T2,TIMBAS
				   MOVEM T2,RTRBT1
				   RNMXOK ]>,<
			     CALL [MOVE T2,RTRBT1
				   IDIVI T2,TIMBAS
				   RNMXOK ]>,<
			     CALL [IMULI T2,TIMBAS
				   MOVEM T2,RTRBT1
				   RNMXOK ]>,<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>,<1>,<%RTTM3>,<CALL [IMULI T2,TIMBAS
					      STOR T2,RCTM3,(RC)
					      RNMXOK ]>,<
					CALL [LOAD T2,RCTM3,(RC)
					      IDIVI T2,TIMBAS
					      RNMXOK ]>,<
				        CALL [IMULI T2,TIMBAS
					      STOR T2,RCTM3,(RC)
					      RNMXOK ]>,<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 CALQOB		;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 RMIAP,(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,AJNAN,(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,AJNAN,(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,AJNAN,(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 T2,MBDST,(MB)	; Yes, get the destination address
	  MOVE T3,T2		; In case we need it later
	  TXZ T2,RN%ARE		; Clear the area number field
	  ADD T2,RTRNRV		; Add reach vector offset
	  TMNN RMTRY,(MB)	; "Try hard" flag set?
	  IFSKP.
	    SETZRO RNCCH,(T2)	; Yes, clear the in-cache flag
	  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)	; No, get address of designated router
	  TMNN RNCCH,(T2)	; Is cache flag set?
	   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.

CALQOB:	INCR RCCMQ,(RC)		;INCREMENT NUMBER OF MESSAGES QUEUED
	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.
	SOSG 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.(CHK,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,AJNAN,(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

	LOAD T1,RCDSL,(RC)	;Get designated router on this circuit
	HLRZ T2,RTRLOO		;Get our address in string format
	CAIE T1,(T2)		;Is it us?
	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:	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...
	 CALLRET FREMSG		; Not in our area, dump the message
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
	      CALLRET RTELDS]	;REPORT THE CIRCUIT DOWN - SOFTWARE FAULT EVENT
	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 ST