Google
 

Trailing-Edge - PDP-10 Archives - BB-F492Z-DD_1986 - 10,7/703mon/dnadll.mac
There are 20 other files named dnadll.mac in the archive. Click here to see a list.
;TITLE DNADLL - DLL use interface for DECnet-36  V016

		SUBTTL Marty Palmieri	 14 JAN 86
		SEARCH ETHPRM,D36PAR,MACSYM
	SALL

	ENTRY DNDINI

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1983,1984,1986 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
;
;
XP DNADLL,016
IFN FTOPS20,<
	SEARCH PROLOG
	TTITLE DNADLL,,< - DLL interface for DECnet-36>
	>

IFN FTOPS10,<
	SEARCH F,S
	TITLE DNADLL - DLL interface for DECnet-36
.CPYRT<1983,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

	SUBTTL Definitions -- External References

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

	EXT DNGMSG		;GET DECNET-36 MESSAGE BLOCK
	EXT DNFMSG		;FREE MESSAGE BLOCK
	EXT DNGEMS		;GET EMERGENCY MESSAGE BLOCK
	EXT DNLENG		;GET MESSAGE LENGTH

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

IFN FTRTST,<
	EXT UPDTCK		; Updated DNGTIM
>
	EXT DNGTIM		;RETURN CURRENT UPTIME (IN MS)

	EXT NMXEVT		;NTMAN event processor
	EXT NTPARM		;Common parameter routine
	EXT NTCTRS		;Common counter routine

;These are the external references to D36COM data cells

	EXT RTRHOM		;OUR HOME AREA
	EXT RTRADR		;OUR NODE NUMBER
	EXT RTRLOO		; Low order of DECnet address (string)
	EXT MXLBSZ		;Maximum receive block size for a line
	EXT IBBLK		;DECnet-36 initialization block

;These are the external references to ROUTER

	EXT RTRPRO		;ROUTER'S PROTOCOL TYPE
	EXT RTRHIO		;HI PART OF THE NI ADDRESS
	EXT RTRDLE		; Entry into ROUTER from dnadll
	EXT RTRCOP
	EXT TSTBLK

;These are some default parameters

	EXT %RTRMA		;MULTICAST ID "ALL ROUTERS"
	EXT %RTEMA		;Multicast ID "All endnodes"
	EXT %RTEHS		;Size in bytes of Ethernet header

;Other external references

	EXT RTN			;NON-SKIP RETURN
	EXT RSKP		;SKIP RETURN
	SUBTTL Definitions -- Accumulators

;Internal feature tests

IFN FTOPS10,<
	FTCI==0			;Don't support CI20 circuits
	FTDDP==FTNET		;Support DDP (ANF-10) circuits
	FTDTE==FTKL10		;Support DTE circuits on KL10
	FTNI==FTKL10		;Support Ethernet circuits on KL10
	FTKDP==FTKS10		;Support DMR-11 circuits on KS10
	FTDMR==FTKS10		;Support DMR-11 circuits on KS10
>; END IFN FTOPS10
IFN FTOPS20,<
	FTCI==-1		;Support CI20 circuits
	FTDDP==0		;Don't support DDP (ANF-10) circuits
	FTDTE==-1		;Support DTE circuits
	FTDMR==0		;Don't support DMR-11 circuits
	FTKDP==0		;Don't support KDP-11 circuits
	FTNI==-1		;Support ethernet circuits
>; END IFN FTOPS20


;These are some local AC defintions for DNADLL

	DL=FREE1		;DL usually points to the data link block
	LN=FREE2 		;LN contains pointer to line data entry
	UN=MS			;UN is the NI user block

	PURGE FREE1		; Lose this symbol
	PURGE FREE2		;  and this one

;Some local symbols

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

	DN.NDR==1B0		; Is not processed by specific DLL
	DN.NMF==1B1		; Network management function
	SUBTTL Definitions -- BEGSTRs


BEGSTR QB
	WORD NXT,		; Pointer to next request in queue
	HWORD FCN,		; Function requested
	WORD DA1,		; Function specific data
	WORD DA2,		; Additional data
	WORD DLB		; Associated data link block address
ENDSTR
	
;Line data block structure

BEGSTR LN
	WORD NXT		; Address of next LN block
	WORD LID		; Line ID
	WORD PID		; Line's portal id
	WORD FLG,0		; Flags
	 FIELD CAD,1		; Channel address is DECnet (Ethernet only)
	 FIELD STA,1		; State of line
	 FIELD CON,2		; Controller (normal/loopback)
	 FIELD PRO,6		; Protocol type
	 FIELD CTY,6		; Circuit type
	 FIELD DBF,6		; Default number of buffers
	 FIELD BSZ,12		; Maximum receive buffer size on this line
	HWORD BNO		; Number of buffers to post
	HWORD NBP		; Number of buffers posted
ENDSTR
	SUBTTL Definitions -- $BUILD, $SET, and $EOB Macros

;Following page is from GLXMAC...

; - Build pre-formed data blocks

;Many components have a need to build simple and complex blocks which
;	contain pre-formatted data, such as FOBs,IBs and other blocks
;	which are made up of several words, each containing from 1 to several
;	fields.  Since data structures change, these blocks should not be
;	just created using EXP or whatever.  These macros will take values
;	and install them in the right field and word of a structure.

; Start off a structure, argument is the size of the structure.

	DEFINE $BUILD(SIZE)<
	  IFDEF ..BSIZ,<PRINTX ?Missing $EOB after a $BUILD>
	  ..BSIZ==0			;;START COUNTER
	  ..BLOC==.			;;REMEMBER OUR STARTING ADDRESS
	  REPEAT SIZE,<			;;FOR EACH WORD IN THE BLOCK
		BLD0.(\..BSIZ,0)	;;ZERO OUT IT'S ACCUMULATOR
		..BSIZ==..BSIZ+1>	;;AND STEP TO NEXT
	>;END OF $BUILD DEFINITION

; For each value installed somewhere in the structure, set it into the block
; 	Arguments are word offset,field in word (optional) and value to set.

	DEFINE $SET(STR,VALUE,OFFSET),<
	  IFNDEF ..BSIZ,<PRINTX ?$SET without previous $BUILD>
	  IFNB <STR>,<
		IFB <OFFSET>,<..STR0 (..SET,<VALUE>,STR)>
		IFNB <OFFSET>,<..STR0 (..SET,<VALUE>,STR,OFFSET)>>
	  IFB  <STR>,<
		IFB <OFFSET>,<..STR0 (..SET,<VALUE>,FWMASK)>
		IFNB <OFFSET>,<..STR0 (..SET,<VALUE>,FWMASK,OFFSET)>>
	> ; END OF $SET DEFINITION

	DEFINE ..SET (VALUE,LOC,MSK) <
	  IFGE <<<LOC>&777777>-..BSIZ>,<
		PRINTX ?WORD offset greater than $BUILD size parameter>
	  SET0. (\<LOC>,MSK,<VALUE>)
	> ;END ..SET DEFINITION

; After all values are declared, the block must be closed to do its actual
;	creation.

	DEFINE $EOB,<
	  IFNDEF ..BSIZ,<PRINTX ?$EOB without previous $BUILD>
	  IFN <.-..BLOC>,<PRINTX ?Address change between $BUILD and $EOB>
	  XLIST				;;DON'T SHOW THE BLOCK
	  ..T==0
	  REPEAT ..BSIZ,<
	    BLD0.(\..T,1)		;;STORE EACH WORD
	    ..T==..T+1 >
	  PURGE ..BSIZ,..T,..BLOC	;;REMOVE SYMBOLS
	  LIST
	>; END OF $EOB DEFINITION

	DEFINE BLD0.(N,WHAT),<
	  IFE WHAT,<..T'N==0>
	  IFN WHAT,<EXP ..T'N
		    PURGE ..T'N>
	> ;END OF BLD0. DEFINITION

	DEFINE SET0.(LOC,MSK,VALUE),<
	IFN <<..T'LOC>&MSK>,<PRINTX ?Initial field not zero in $SET>
	  ..TVAL==<VALUE>
	  ..TMSK==<MSK>
	  ..T'LOC==..T'LOC!<FLD(..TVAL,..TMSK)>
	  PURGE ..TVAL,..TMSK
	>;END OF SET0. DEFINITION
	SUBTTL DNADLL - Generic data link layer -- DNDCCR - Compute Core Requirements

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

IFN FTOPS10,<
DNDCCR::SETZ T1,		;NO ADDITIONAL REQUIREMENTS
	RET			;AND RETURN
>; END IFN FTOPS10
	SUBTTL DNADLL - Generic data link layer -- DNDINI - Initialization
	
;DNDINI - Initialize common DLL interface for Router
;
; Call: 
;	with nothing
;
; Return: 
;	RET			;Only return
;
; Uses: T1-T3
;
;This routine gets called at system initialization.

	RESCD

	INTERNAL DNDINI
DNDINI:	TSTS6
	TOXSWAPCD

	TRACE DND,<Initializing DNADLL>
	SAVEAC <P1,DL,LN>
	LOAD T1,IBRTR,+IBBLK	; Get Router type desired
	MOVEM T1,DNDRNT		; Save as Router's node type

;Get and initialize an EC (Event Communication) block

	MOVX T1,EV.GEC		;Function code "get EC block"
	CALL NMXEVT
	IFSKP.
	  MOVEM T1,DNDECP	;Save pointer
	  MOVX T2,4		;DNADLL may queue 4 event blocks
	  STOR T2,ECMAX,(T1)
	ENDIF.

;Now try to initialize all of the lines

	MOVSI P1,-LD.MAX	; Get AOBJN pointer to INITBL table
DNDIN1:	CALL @INITBL(P1)	; Initialize next line type
	  CAI			; Ignore error return
	AOBJN P1,DNDIN1		; Loop back for all table entries
	RET

INITBL:	IFIW <RTN&777777>	; Test bed driver (obsolete)
	IFIW <DTDINI&777777>	; DTE line driver
	IFIW <KDDINI&777777>	; KDP line driver
	IFIW <DDDINI&777777>	; DDP (ANF-10) line driver
	IFIW <CIDINI&777777>	; CI-20 SCA line driver
	IFIW <NIDINI&777777>	; NIA-20 Ethernet line driver
	IFIW <DMDINI&777777>	; DMR-11 line driver
IF1,<IFN <LD.MAX-<.-INITBL-1>>,<? Wrong number of entries in INITBL>>

	XRESCD
	SUBTTL DNADLL - Generic data link layer -- DNDJIF - Once a jiffy code

	INTERNAL DNDJIF
	XRENT DNDJIF

	SAVEAC <P1,DL>
DNDJF1:	SYSPIF				; Interrupts off
	DEQUE P1,DNDINQ,QB.NXT,DNDNOQ	; Get a function block from the queue
	SYSPIN
	LOAD DL,QBDLB,(P1)		; DLB pointer
	LOAD T1,QBFCN,(P1)		; Function
	LOAD T2,DLUID,(DL)		; Get DNADLL users ID
	LOAD T3,QBDA1,(P1)		; Data for function
	LOAD T4,QBDA2,(P1)		; Additional data
	CALL RTRDLE			; Call the ROUTER
	 TRN
	OPSTR <SKIPN>, DLUID,(DL)	; Do we have a circuit block?
	 STOR T1,DLUID,(DL)		; Then save expected circuit block
	MOVE T1,DNDQBL			; Get the length of the QB block queue
	CAIG T1,^D12			; Is it greater than the desired
	IFSKP.				;   maximum?
	  MOVE T1,P1			; Yes, the free the block
	  CALL DNFWDS
	ELSE.
	  SYSPIF			; No interrupts please
	  MOVE T1,DNDQBQ		; No, get the queue head
	  MOVEM T1,(P1)		 	;  and put this one at the head
	  MOVEM P1,DNDQBQ
	  AOS DNDQBL			; Add it into the count
	  SYSPIN
	ENDIF.
	JRST DNDJF1			; Get next function block on queue
DNDNOQ:	SYSPIN				; Queue is now empty
	RET				; No more function blocks queued
	SUBTTL DNDALL - Generic data link layer -- DNDSEC - Once a second code

	RESCD

	INTERNAL DNDSEC
DNDSEC:	TSTS6
	TOXRESCD

	SAVEAC <DL,LN>		; Check all data link blocks
	SKIPA DL,DLBQUE
DNDSE1:	LOAD DL,DLNXT,(DL)
	JUMPE DL,RTN
	LOAD LN,DLLNB,(DL)	; Get line data block address
	LOAD T1,DLDID,(DL)	; Get line ID
	LOAD T1,LIDEV,+T1	; Get device type
	MOVE T1,SECTBL(T1)	; Get dispatch address
	CALL (T1)		; Do devices once/second
	JRST DNDSE1

SECTBL:	IFIW <RTN&777777>	; Test bed driver (obsolete)
	IFIW <DTDSEC&777777>	; DTE line driver
	IFIW <KDDSEC&777777>	; KDP line driver
	IFIW <DDDSEC&777777>	; DDP (ANF-10) line driver
	IFIW <CIDSEC&777777>	; CI-20 SCA line driver
	IFIW <NIDSEC&777777>	; NIA-20 Ethernet line driver
	IFIW <DMDSEC&777777>	; DMR-11 line driver
IF1,<IFN <LD.MAX-<.-SECTBL-1>>,<? Wrong number of entries in SECTBL>>
	SUBTTL DNADLL - Generic data link layer -- DNDQUE - Queue once a jiffy request

;DNDQUE - Queue up interrupt level requests to scheduler level
;
; Call:
;	T1 - Function to call ROUTER with (ie: DI.xxx)
;	T3 - Function specific data (Usually MB address)
;	T4 - Additional function specific data (usually status)
;
;	CALL DNDQUE
;	 only return
;
; Uses T1-T4.
;

DNDQUE:	SAVEAC <P1,P2,P3>	; Get a spare AC
	MOVE P1,T1		; Save function for a moment
	MOVE P2,T3		;
	MOVE P3,T4
	SYSPIF
	SKIPN T1,DNDQBQ		; Any free block on the QB queue?
	IFSKP.
	  MOVE T2,(T1)		; Yes, then get one from it
	  MOVEM T2,DNDQBQ
	  SOS DNDQBL		; Account for it
	  SYSPIN
	ELSE.
	  SYSPIN
	  MOVX T1,QB.LEN	; Get length of user NI block
	  CALL DNGWDZ		; Try to get the words
	  IFNSK.		; Couldn't
	    MOVE T1,P2		; Get address of possible message
	    CAIE P1,DI.ODN	; Is this a function for which we
	     CAIN P1,DI.INC	;  expect a message block
	    CALL DNFMSG		; Yes, then free it
	    RET			; Return
	  ENDIF.
	ENDIF.
	STOR P1,QBFCN,(T1)	; Store function
	STOR DL,QBDLB,(T1)	; Save DLB pointer
	STOR P2,QBDA1,(T1)	; Store the data
	STOR P3,QBDA2,(T1)
	SYSPIF
	ENDQUE T1,DNDINQ,QB.NXT,T2 ; Queue the data link block
	SYSPIN
	RET
	SUBTTL DNDALL - Generic data link layer -- DNDDSP - ROUTER function dispatch

;DNDDSP - Dispatch for calls to DNADLL from ROUTER
;
; Call: 
;	T1 - Function (DF.xxx)
;	T2 - Address of DLB block
;	T3 - Function specific data
;	T4 - Additional function specific data
;
; Return: 
;	RET on error
;	RETSKP on success

	RESCD

	INTERNAL DNDDSP
DNDDSP:	TSTS6
	TOXRESCD

	SAVEAC <DL,LN>
	SKIPE DL,T2		; Get address of DLB block
	LOAD LN,DLLNB,(DL)	; and address of line data block
	CAXL T1,DF.OPN		; Range check the
	CAXLE T1,DF.MAX		; function code
	BUG.(CHK,DNDIRF,DNADLL,SOFT,<Illegal function code from ROUTER>,,<

Cause:	DNADLL was called with a bad function by ROUTER

>,RTN)
	LOAD CX,DLDID,(T2)	; Get the line ID

DLLDSP:	LOAD CX,LIDEV,+CX	; Get the device type
	CAILE CX,LD.MAX		; Legal type?
	 RET			; BUGCHK **********************************
	MOVE CX,DSPTBL(CX)	; Get dispatch table address
	CALLRET (CX)		; Call the DLL

DSPTBL:	IFIW <RTN&777777>	; Test bed driver (obsolete)
	IFIW <DTDDSP&777777>	; DTE line driver
	IFIW <KDDDSP&777777>	; KDP line driver
	IFIW <DDDDSP&777777>	; DDP (ANF-10) line driver
	IFIW <CIDDSP&777777>	; CI-20 SCA line driver
	IFIW <NIDDSP&777777>	; NIA-20 Ethernet line driver
	IFIW <DMDDSP&777777>	; DMR-11 line driver
IF1,<IFN <LD.MAX-<.-DSPTBL-1>>,<? Wrong number of entries in DSPTBL>>
	SUBTTL DNDALL - Generic data link layer -- DNDNMX - NTMAN function dispatch

;DNDNMX - Dispatch for calls to DNADLL from NTMAN
;
; Call:
;	T1 - Function code (NF.xxx)
;	T2 - Address of NF function block
;
; Return:
;	RET on error with NTMAN error code in T1
;	RETSKP on success

	RESCD

	INTERNAL DNDNMX
DNDNMX:	TSTS6
	TOXRESCD

	SAVEAC <DL,LN>
	CAXL T1,NF.SET		; Range check the
	CAXLE T1,NF.CET		; function code
	BUG.(CHK,DNDINF,DNADLL,SOFT,<Illegal function code from NTMAN>,,<

Cause:	DNADLL was called with a bad frunction by NTMAN

>,RTN)

	MOVE T1,NXFTBL(T1)	; Convert to internal function code
	TXZE T1,DN.NDR		; Should we process this ourselves
	 CALLRET DNDNMF		; Yes, then do not dispatch to DLL
	LOAD CX,NFETY,(T2)	; Get entity type
	CAIE CX,.NTNOD		; Is the entity node?
	IFSKP.
	  MOVEI CX,LD.ETH	; Yes, then assume Ethernet (presently the
	  STOR CX,LIDEV,+CX	;  only node entity is the NI physical address)
	ELSE.			;
	  LOAD CX,NFEID,(T2)	; Get the entity ID
	ENDIF.
	CALLRET DLLDSP		; Dispatch to DLL and return

NXFTBL:	DN.NMF!DF.SET		;NTMAN - Set parameter
	DN.NMF!DF.CLR		;NTMAN - Clear parameter
	DN.NMF!DF.RED		;NTMAN - Read parameter
	DN.NMF!DF.SHC		;NTMAN - Show counters
	DN.NMF!DF.SZC		;NTMAN - Show and zero counters
	DN.NDR!DF.RET		;NTMAN - Return list (e.g. show known lines)
	DN.NDR!DF.A2N		;NTMAN - Map node address to name
	DN.NDR!DF.N2A		;NTMAN - Map node name to address
	DN.NDR!DF.CET		;NTMAN - Check entity
IF1,<IFN <NF.MAX-<.-NXFTBL-1>>,<? Wrong number of entries in NXFTBL>>
	SUBTTL DNADLL - Generic data link layer -- DNDNMF - Network management functions

;DNDNMF - Process non DLL specific network management functions
;
; Call:
;	T1 - NTMAN function code (NF.xxx)
;	T2 - Address of NF block
;
; Returns:
;	RET on error with NTMAN error code in T1
;	RETSKP on success

	XSWAPCD

DNDNMF:	SAVEAC <P1>
	MOVE P1,T2		; Save address of NF block
	HRRZ T1,T1		; Clear any flags
	CAIN T1,DF.CET		; Requested to check entity?
	 JRST NMXCET		; Yes, then be NML's humble servant
	CAIE T1,DF.RET		; Is request for return list?
	 RNMXER (NF.MPE)	; We only do the two we just checked
	TMNN NFBFF,(P1)		; Buffer present?
	 RNMXER (NF.MPE)	;  Must have one for this stuff
	LOAD T1,NFETY,(P1)	; Get entity type
	CAIE T1,.NTLIN		; Is the entity line?
	IFSKP.
	  LOADE T1,NFSEL,(P1)	; Get the selector type
	  CAXL T1,.NTSGN	; Range check the selection criteria
	   CAXLE T1,.NTKNO	; If not between SIGNIFICANT & KNOW
	    RNMXER (NF.MPE)	;  we don't do it
	  CALLRET <-.NTSGN>+@[
		IFIW <NMXILS&777777> ; SIGNIFICANT lines not supported
		IFIW <NMXILS&777777> ; ADJACENT lines
		IFIW <NMXILS&777777> ; LOOP line
		IFIW <NMXSAL&777777> ; ACTIVE lines
		IFIW <NMXSKL&777777>](T1) ; KNOWN lines
	ELSE.
	  RNMXER (NF.FNS)	; We don't do any others
	ENDIF.

NMXILS:	RNMXER (NF.MPE)		; NTMAN is confused
	XRESCD
	SUBTTL DNADLL - Generic data link layer -- NMXSAL - Show active lines
	SUBTTL DNADLL - Generic data link layer -- NMXSKL - Show known lines

;NMXSAL - Called by DNDNMF to process SHOW ACTIVE LINES
;NMLSKL - Called by DNDNMF to process SHOW KNOWN LINES
;
; Call:
;	P1 - Address of NF block
;
;	T2 - Address of NF block
;
; Returns:
;	RET on error with NTMAN error code in T1
;	RETSKP on success

	XSWAPCD

NMXSKL:	SKIPA T1,[-1]		; "Show known lines"
NMXSAL:	SETZ T1,		; "Show active lines"
	SAVEAC <P2,LN>
	STKVAR <FUNC>
	MOVEM T1,FUNC
	SETZ P2,		; Initialize the count
	LOAD T2,NFBLN,(P1)	; Get length of buffer
	LOAD T4,NFBUF,(P1)	; Get buffer address
	ADD T2,T4		; Compute end of buffer
	SKIPA LN,LNBQUE		; Get first line block
NMXSA1:	LOAD LN,LNNXT,(LN)	; Get next line block
	JUMPE LN,NMXSA2		; Exit loop at end of list
	LOAD T1,LNSTA,(LN)	; Get line state
	CAIE T1,LS.ON		; Is circuit active?
	SKIPGE FUNC		; No, is function "known"
	TRNA			; Yes, include this line
	 JRST NMXSA1		; No, skip this line
	LOAD T1,LNLID,(LN)	; Get the line's ID
	CAML T4,T2		; Check to be sure we don't run off end
	 RNMXER (NF.MPE)	; NTMAN error if not enough room
	MOVEM T1,(T4)		; Save into buffer
	AOJ P2,			; Count it
	AOJA T4,NMXSA1		; Advance buffer
NMXSA2:	STOR P2,NFBLN,(P1)	; Tell NTMAN number written
	RETSKP

	XRESCD
	SUBTTL DNADLL - Generic data link layer -- NMXCET - Check entity

;NMXCET - Build data link blocks and circuit blocks if needed
;
; Call: 
;	P1 - Address of NF block
;
; Return: 
;	RET			;Only return
;
; Uses: T1-T3
;
;This routine is called by network management to cause DNADLL data link blocks
;and Router circuit blocks to be built.

	XSWAPCD

NMXCET:	LOAD T1,NFEID,(P1)	; Get entity ID
	TMNE LILXC,+T1		; Is it a circuit?
	IFSKP.
	  CALL DNDFDL		; (T1) No, see if we have a data link block
	   TRNA			; No, see if we should make one
	  RETSKP		; Got it, tell NTMAN o.k.
	  LOAD T1,NFEID,(P1)	; Get entity ID
	  TXO T1,LILXC		; Make this circuit entity into a line entity
	ENDIF.
	TXZ T1,LIDRP		;  and clear any multi-drop numbers
	CALL DNDFLN		; (T1) Get the line data block for this line
	 RNMXER (NF.URC)
	TMNE LILXC,+NF.EID(P1)	; Was the entity a line?
	 RETSKP			; Yes, return success
	LOAD T1,NFEID,(P1)	; Entity ID
	CALLRET DNDCDL		; Create a DLB and tell ROUTER
	SUBTTL DNADLL - Generic data link layer -- DNDCDL - Create data link block

;DNDCDL - Create a data link block if needed
;
;Call:
;	T1/ Circuit id
;	LN/ Address of line block
;
;Returns:
;	RET on error with NTMAN error code in T1
;	RETSKP on success with address of data link block in DL

	XRESCD

DNDCDL:	SAVEAC <P1>		; Save P1
	MOVE P1,T1		; Save circuit id
	CALL DNDFDL		; Try to find circuit in circuit block queue
	 SKIPA			; Not there, create a new circuit block
	RETSKP			; Found it, return with address in DL
	MOVEI T1,DL.LEN		; Length of data link block
	CALL DNGWDZ		; (T1) Allocate space
	 RNMXER (NF.RES)	; Error
	MOVE DL,T1		; Hold on to this address
	STOR P1,DLDID,(DL)	; Device ID
	STOR LN,DLLNB,(DL)	; Line data block address
	LOAD T1,LNDBF,(LN)	; Get default number of buffers
	STOR T1,LNBNO,(LN)	; Number of buffers
	MOVEI T1,DI.ICB		; Ask Router to initialize a circuit block
	MOVE T2,DL		; Get DLB address in correct place
	MOVE T3,P1		; Get circuit id
	LOAD T4,LNBSZ,(LN)	; Tell the maximum block size for this line
	CALL RTRDLE		; (T1,T2,T3,T4) Call Router now
	SKIPE T1		; T1 contains Router callback ID if successful
	 IFSKP.			;  else 0
	   MOVE T1,DL
	   CALL DNFWDS		; Return the block
	   RNMXER (NF.URC)	
	 ENDIF.
	STOR T1,DLUID,(DL)	; Save circuit block address
	ENDQUE DL,DLBQUE,DL.NXT,T2 ; Queue the data link block
	RETSKP
	SUBTTL DNADLL - Generic data link layer -- DNDFDL - Find data link block

;DNDFDL - Search the queue for a Data Link Block
;
; Call: 
;	T1/ Line (device) ID
;
; Return: 
;	RET		;No Data Link Block for given ID
;	RETSKP		;With DL = Data Link Block
;			; and LN = Line Block
;
; Uses: T1-T2
;
;This routine is used for calls from Network Management

	XSWAPCD
DNDFDL:	SETZRO LILXC,+T1	; Clear "this is a line" indicator
	SKIPN DL,DLBQUE		;Start looking at first data link block
	 RET			;None there, just return
DNDGL1:	LOAD T2,DLDID,(DL)	;Get its' line ID
	CAMN T1,T2		;Is it the one we're looking for?
	 JRST DNDGL2		;Yes, exit loop
	LOAD DL,DLNXT,(DL)	;Get the next data link block
	JUMPN DL,DNDGL1		;Go check it out
	RET			;Couldn't find it
DNDGL2:	LOAD LN,DLLNB,(DL)	;Get address of line block
	RETSKP			;And return

	XRESCD
	SUBTTL DNADLL - Generic data link layer -- DNDCLN - Create line block

;DNDCLN - Create a link block as needed
;
;Call:
;	T1/ Line id
;	T2/ Address of prototype line block
;
;Returns:
;	RET on error
;	RETSKP on success with address of line block in LN

DNDCLN:	SAVEAC <P1,P2>		; Save P1 and P2
	DMOVE P1,T1		; Save line id and address of prototype
	CALL DNDFLN		; Try to find line in data base
	 SKIPA			; Not there, create it
	RETSKP			; Return with line block address in LN
	MOVEI T1,LN.LEN		; Get length of line block
	CALL DNGWDZ		; Allocate core for block
	 RET			; Can't, return
	MOVE LN,T1		; Remember address of line block
	MOVEI T1,LN.LEN		; Get length of line block
	MOVE T2,P2		; Get address of prototype line block
	MOVE T3,LN		; And address of new line block
	PUSHJ P,XBLTA##		; Copy prototype into new line block
	STOR P1,LNLID,(LN)	; Store line id into LN block
	LOAD T1,LIDEV,+P1	; Get device type
	MOVE T1,MXLBSZ(T1)	; Get default receive buffer size
	OPSTR <CAML T1,>,IBBSZ,+IBBLK ; Is request for less?
	 LOAD T1,IBBSZ,+IBBLK	; Yes, then use that
	STOR T1,LNBSZ,(LN)	; Store into line block
	SYSPIF			; Interlock queue access
	ENDQUE LN,LNBQUE,LN.NXT,T1 ; Add line block to data base
	SYSPIN			; Release interlock
	RETSKP			; And return
	SUBTTL DNADLL - Generic data link layer -- DNDFLN - Find line block

;DNDFLN - Search the queue for a line data block
;
; Call: 
;	T1/ Line id
;
; Return: 
;	RET		;Line not configuered into monitor
;	RETSKP		;With LN = Line Data Block
;
; Uses: T1-T2
;
;This routine is used for calls from Network Management

	XSWAPCD

DNDFLN:	SKIPN LN,LNBQUE		;Start looking at first line data block
	 RET			;None there, just return
DNDGN1:	LOAD T2,LNLID,(LN)	; Get its' line ID
	CAMN T1,T2		;Is it the one we're looking for?
	 RETSKP			;Good return
	LOAD LN,LNNXT,(LN)	;Get the next line data block
	JUMPN LN,DNDGN1		;Go check it out
	RET			;Couldn't find it

	XRESCD
	SUBTTL CIDLL - CI20 data link layer -- Dummy Routines

;Dummy routines if CI20 circuits not supported

IFE FTCI,<
CIDINI:
CIDSEC:
CIDDSP:	RET
>; END IFE FTCI
	SUBTTL CIDLL - CI20 data link layer -- CIDINI - Initialize lines

IFN FTCI,<
;Call:
;	CALL CIDINI
;Returns:
;	RET Always

CIDINI:	RET

;Still in IFN FTCI
	SUBTTL CIDLL - CI20 data link layer -- CIDSEC - Once a second code

;Still in IFN FTCI

;Call:
;	DL/ Address of data link block
;	LN/ Address of line data block
;	CALL CIDSEC
;Returns:
;	RET always

CIDSEC:	RET

;Still in IFN FTCI
	SUBTTL CIDLL - CI20 data link layer -- CIDDSP - Function dispatch

;Still in IFN FTCI

;Call: (for non-network management functions)
;	T1/ Function code (DF.xxx)
;	T3-T4/ Function specific data
;	DL/ Address of data link block
;	LN/ Address of link table block
;	CALL CIDDSP
;Call: (for network management functions)
;	T1/ Function code (DF.xxx)
;	T2/ Address of NF block
;	CALL CIDDSP

CIDDSP:	STKVAR <FUNC>
	MOVEM T1,FUNC
	TXZE T1,DN.NMF		; Is this from network management?
	 JRST DCIDS2		; Yes, just do the dispatch
	CAIE T1,DF.OPN		; Is this an open circuit call?
	 JRST DCIDS1		; No, not special
	MOVE T3,T2		; Callback ID goes into T3
	LOAD T2,DLDID,(T2)	; Get the line identifier for this circuit
	TRNA
DCIDS1:	LOAD T2,LNPID,(LN)	; Get the portal ID to talk to CIDLL
DCIDS2:	CALL CINDSP		; Call CIDLL
	 RET			; Pass on the error
	HRRZ T2,FUNC
	CAIE T2,DF.OPN		; Was this an open request?
	 RETSKP			; No, just return success
	STOR T1,LNPID,(LN)	; T1 should contain the Port ID
	RETSKP

>; END IFN FTCI
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- Dummy Routines

;Dummy routines if DDP (ANF-10) circuits not supported

IFE FTDDP,<
DDDINI:
DDDSEC:
DDDDSP:
DDIPPI::RET	;$
>; END IFE FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDINI - Initialize lines

IFN FTDDP,<
;Call:
;	CALL DDDINI
;Returns:
;	RET always
;Side effects:
;	Creates LN blocks for any existing DDP lines

DDDINI:	RET			; DDPs initialize via DDIPPI DI.ICB call

; Prototype DDP line block

DDDPLN:	$BUILD	(LN.LEN)
	  $SET (LNSTA,LS.ON)	; Default line state of on
	  $SET (LNPRO,0)	; Protocol type = DDCMP-Point
	  $SET (LNCTY,0)	; Circuit type = DDCMP-Point
	  $SET (LNDBF,1)	; Default number of receive buffers = 1
	$EOB

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDSEC - Once a second code

;Still in IFN FTDDP

;Call:
;	DL/ Address of data link block
;	LN/ Address of line data block
;	CALL DDDSEC
;Returns:
;	RET always

DDDSEC:	LOAD T1,LNSTA,(LN)	; Get line's state
	TMNE DLLIU,(DL)		; Is DECnet using the data link?
	CAIE T1,LS.ON		; Yes, is line running?
	 RET			; No, return
	CALLRET DDIPRB		; Try to post some buffers

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDDSP - Function dispatch

;Still in IFN FTDDP

;Call: (for non-network management functions)
;	T1/ Function code (DF.xxx)
;	T3-T4/ Function specific data
;	DL/ Address of data link block
;	LN/ Address of link table block
;	CALL DDDDSP
;Call: (for network management functions)
;	T1/ Function code (DF.xxx)
;	T2/ Address of NF block
;	CALL DDDDSP

DDDDSP:	MOVE CX,DDDDTB(T1)	; Get dispatch address
	CALLRET (CX)

DDDDTB:	IFIW <DDDOPN&777777>	; Open a portal/circuit
	IFIW <DDDCLS&777777>	; Close a portal/circuit
	IFIW <DDDXMT&777777>	; Transmit a packet
	IFIW <DDDSET&777777>	; Set a parameter
	IFIW <DDDCLR&777777>	; Clear a parameter
	IFIW <DDDRED&777777>	; Read paramater
	IFIW <DDDSHC&777777>	; Show counters
	IFIW <DDDSZC&777777>	; Show and zero counters
	IFIW <DDDILL&777777>	; Return list
	IFIW <DDDILL&777777>	; Map Node Address to Name
	IFIW <DDDILL&777777>	; Map Node Name to Address
	IFIW <DDDILL&777777>	; Check Entity Id
IF1,<IFN <DF.MAX-<.-DDDDTB-1>>,<? Wrong number of entries in DDDDTB>>

DDDILL:	RET

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDOPN - Open portal

;Still in IFN FTDDP

;DDDOPN - Open a data link layer port
;
; Call: 
;	T1/ Function (DF.OPN)
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;ON RESOURCE FAILURE
;	RETSKP			;On success with T1 = Line state
;
; Uses: T1-T3

DDDOPN:	MOVX T1,DD.OPN		; Get function
	LOAD T2,LNLID,(LN)	; Get line id
	SETONE DLLIU,(DL)	; Indicate circuit is using the line
	MOVE T3,DL		; Callback ID (DL block)
	CALL DLLDDP		; Call NETDEV
	 JRST	DDDOP1		; Error
	STOR T1,LNPID,(LN)	; Store portal id
	CALL DDIPRB		; Try posting receive buffers
	LOAD T1,LNSTA,(LN)	; Get current line state
	RETSKP			; Return

DDDOP1:	SETZRO DLLIU,(DL)	; Clear the line in use indicator
	RET			; Give error return
;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDCLS - Close portal

	SUBTTL DDPDLL - DDP data link layer -- DDP Parameters
;Still in IFN FTDDP
	XSWAPCD
DDDLPT:
PARAMETER (^D0,,,,,<TRN>,<LOAD T2,LNSTA,(LN)>,<TRN>,<
	Line state>)
PARAMETER (^D1105,,^D20,^D5,^D10,<STOR T2,LNBNO,(LN)>,<LOAD T2,LNBNO,(LN)>,<
	STOR T2,LNBNO,(LN)>,<Receive buffers>)
PARAMETER(^D1110,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCON,(LN)>,<TRN>,<
	Controller>)
PARAMETER(^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNPRO,(LN)>,<TRN>,<
	Protocol>)
PARAMETER(^D2500,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNBSZ,(LN)>,<TRN>,<
	Receive buffer size>)
DDDLPL==.-DDDLPT
DDDCPT:
PARAMETER (^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCTY,(LN)>,<TRN>,<
	Circuit type>)
DDDCPL==.-DDDCPT
	XRESCD
;Still in IFN FTDDP
	SUBTTL DDDDLL - DDP data link layer -- DDP Counters
;Still in IFN FTDDP
	XSWAPCD
;Circuit counters maintained by the line
DDDCCT:
COUNTER (^D1000,^D32,<LOAD T1,DLBYR,(DL)>,<SETZRO DLBYR,(DL)>,,<
	Bytes received>)
COUNTER (^D1001,^D32,<LOAD T1,DLBYS,(DL)>,<SETZRO DLBYS,(DL)>,,<Bytes sent>)
COUNTER (^D1010,^D32,<LOAD T1,DLDBR,(DL)>,<SETZRO DLDBR,(DL)>,,<
	Data blocks received>)
COUNTER (^D1011,^D32,<LOAD T1,DLDBS,(DL)>,<SETZRO DLDBS,(DL)>,,<
	Data blocks sent>)
COUNTER (^D1065,^D16,<LOAD T1,DLUBU,(DL)>,<SETZRO DLUBU,(DL)>,,<
	User buffer unavailable>)
DDDCCL==.-DDDCCT
	XRESCD
;Still in IFN FTDDP

;DDDCLS - Close a circuit on a DDP line
;
; Call: 
;	DL/ Data link block address
;	LN/ Line tabel block address
;
; Return: 
;	RET			;On error
;	RETSKP			;On success
;

DDDCLS:	MOVX T1,DD.CLS		; Get DDP driver function
	LOAD T2,LNPID,(LN)	; Get DDP portal id
	CALL DLLDDP		; Ask driver to terminate
	 RET			; Error
	SETZRO DLLIU,(DL)	; Clear the line in use indicator
	MOVX T2,LS.OFF		; Save new line state as off
	STOR T2,LNSTA,(LN)	; ...
	JUMPE T1,RSKP		; If we got an MB return it
	DECR LNNBP,(LN)		; No longer posted
	CALL DNFMSG		; Return it to the free pool
	RETSKP

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDXMT - Transmit packet

;Still in IFN FTDDP

;DDDXMT - Send a packet to DDP driver
;
; Call: 
;	T3/ Message block
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;On error from driver
;	RETSKP			;On success

DDDXMT:	MOVX T1,DD.QUE		; Get function
	LOAD T2,LNPID,(LN)	; Get DDP portal id
	CALLRET DLLDDP		;

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDRED - Read parameter
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDSET - Set parameter
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDCLR - Clear parameter

;Still in IFN FTDDP

	XSWAPCD

DDDRED:	MOVEI T3,NF.RED		; Funtion will be in T3
	CALLRET DDDCPF		; Call common parameter routine

DDDSET: MOVEI T3,NF.SET
	CALLRET DDDCPF		; Call common parameter routine

DDDCLR: MOVEI T3,NF.CLR
;	CALLRET DDDCPF		; Call common parameter routine

DDDCPF:	STKVAR <FUNC>
	MOVEM T3,FUNC		; Save function (read,set,clear)
	MOVE P1,T2		; Save NTMAN function block
	LOAD T1,NFETY,(P1)	; Get the entity type
	CAIE T1,.NTCKT		; Is it circuit?
	IFSKP.
	  LOAD T1,NFEID,(P1)	; Yes, get the entity ID again
	  CALL DNDFDL		; Find the data link block
	   RNMXND		; Don't know of this link-Return succes/no data
	  LOAD LN,DLLNB,(DL)	; Address of line data block
	  XMOVEI T1,DDDCPT	; Use the circuit parameter table
	  MOVEI T2,DDDCPL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity type line?
	  IFSKP.
	    LOAD T1,NFEID,(P1)	; Get the entity ID
	    CALL DNDFLN		; Get the line data block for this line
	     RNMXER (NF.MPE)	; We don't support this device
	    XMOVEI T1,DDDLPT	; Get parameter table address
	    MOVEI T2,DDDLPL	;  and its length
	  ELSE.
	    RNMXER (NF.MPE)	; We don't support any others
	  ENDIF.
	ENDIF.
	MOVE T3,FUNC		; Recover requested function
	CALLRET NTPARM		; Call the common parameter processer
				; T1/ table
				; T2/ table's length
				; T3/ function

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDSHC - Show counters
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDSZC - Show and zero counters

;Still in IFN FTDDP

	XSWAPCD

DDDSHC:	SKIPA T3,[NF.COU]	;Load function code and skip
DDDSZC:	MOVX T3,NF.SZC		;Load function code
	STKVAR <FUNC>
	MOVEM T3,FUNC		;T3 will be used later
	MOVE P1,T2		;Move NF pointer to where it should be
	LOAD T1,NFETY,(P1)	;Get the entity type
	CAIE T1,.NTCKT		;Is it a circuit
	IFSKP.
	  LOAD T1,NFEID,(P1)	;Get circuit identifier
	  CALL DNDFDL		;Get the data link block block
	   RNMXND
	  XMOVEI T1,DDDCCT	;Address of circuit counter table
	  MOVEI T2,DDDCCL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity a line?
	  IFSKP.
	    RNMXND		; Return with no data
	  ELSE.
	    RNMXER (NF.OPF)	; We don't do any others
	  ENDIF.
	ENDIF.

;Call NTCTRS to do the real work

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

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDRTL - Return list
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDDCET - Check entity

;Still in IFN FTDDP

DDDRTL:
DDDCET:	RNMXER (NF.MPE)

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DLLDDP - Call DDP driver
	
;Still in IFN FTDDP

DLLDDP:	MOVX	T4,DD.DEC	; Say we are DECnet user
	SNCALL	(DDPDSP##,MCSEC1)
	  RET
	RETSKP
;Still in IFN FTDDP
	SUBTTL	DDPDLL - DDP (ANF-10) data link layer -- DDIPPI - DDP driver callback

;Still in IFN FTDDP

;DDIPPI - Interrupt from NETDDP
;
;Call:
;	T1/ Function code (DI.xxx)
;	T2/ Address of data link block
;	T3/ Function specific data

DDIPPI::CAXL T1,DI.ODN
	 CAXLE T1,DI.ICB
	BUG. (CHK,DDIIFD,DNADLL,SOFT,<Illegal function from DDP driver>,,<
	This BUG is not documented yet.
>,RTN)

	SAVEAC <DL,LN>		; Save DL and LN
	SKIPE DL,T2		; Get address of data link block
	LOAD LN,DLLNB,(DL)	; and line data block address
	MOVE T1,DDIDLI(T1)	; Get address of function specific routine
	CALLRET (T1)		; Dispatch to routine and return

DDIDLI:	IFIW	<DDIILL&777777>	; Unused
	IFIW	<DDIODN&777777>	; Output done
	IFIW	<DDIINC&777777>	; Input complete
	IFIW	<DDILSC&777777>	; Line state change
	IFIW	<DDIICB&777777>	; Initialize circuit block
IF1,<IFN <DI.MAX-<.-DDIDLI-1>>,<? Wrong number of entries in DDIDLI>>

DDIILL:	RET

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDIODN - Transmit done

;Still in IFN FTDDP

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address

DDIODN:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYS,(DL) ; Update bytes sent
	INCR DLDBS,(DL)		; Another packet sent
	MOVEI T1,DI.ODN		; Function is Output Done
	CALLRET DNDQUE		; Queue message up and return

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDIINC - Receive done

;Still in IFN FTDDP

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address

DDIINC:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYR,(DL) ; Update bytes received
	INCR DLDBR,(DL)		; Another packet received
	DECR LNNBP,(LN)		; Account for buffer received
	MOVEI T1,DI.INC		; Function is input complete
	CALL DNDQUE		; Queue message up
	CALLRET DDIPRB		; Post a buffer for driver

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDILSC - Line state change

;Still in IFN FTDDP

;Call:
;	T3/ New state
;	DL/ Data link block address
;	LN/ line data block address

DDILSC:	STOR T3,LNSTA,(LN)	; Save the new state
	TMNN DLLIU,(DL)		; Is Router using this line?
	IFSKP.
	  MOVEI T1,DI.LSC	; Yes, notify ROUTER
	  CALL DNDQUE
	ENDIF.
	LOAD T1,LNSTA,(LN)	; Get state again
	CAIN T1,LS.ON		; Is new state = on?
	 CALL DDIPRB		; Yes, post a buffer
	RET

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDIICB - Initialize circuit block

;Still in IFN FTDDP

;DDIICB - Create DDP line and circuit blocks
;
;Call:
;	T3/ Line id
;
;Returns:
;	RET on error
;	RETSKP on success

DDIICB:	SAVEAC <DL,LN>		; Save DL and LN
	MOVE T1,T3		; Get line id
	XMOVEI T2,DDDPLN	; Get address of prototype DDP line block
	CALL DNDCLN		; Create line block if needed
	 RET			; Can't, give error return
	LOAD T1,LNLID,(LN)	; Get line id
	TXZ T1,LILXC		; Make into circuit id
	CALL DNDCDL		; Create circuit block if needed
	 RET			; Can't, give error return
	MOVE T1,DL		; Give the DL block to our user
	RETSKP			; Return

;Still in IFN FTDDP
	SUBTTL DDPDLL - DDP (ANF-10) data link layer -- DDIPRB - Post receive buffer

;Still in IFN FTDDP

; Call: 
;	DL/ Data link block address
;	LN/ line data block address
;
; Uses: T1-T3

DDIPRB:	SAVEAC <MB>
	LOAD T1,LNNBP,(LN)		; Get number posted
	OPSTR <CAML T1,>,LNBNO,(LN)	; Enough posted?
	 RET
	LOAD T1,LNBSZ,(LN)		; Get the size to post for this link
	CALL DNGMSG			; Request a message block
	 RET				; None available, return
	MOVE MB,T1			; Save address of block
	XMOVEI T2,IN.MSD(MB)		; Point to the input MSD
	STOR T2,MBFMS,(MB)		; Store in forst MSD slot
	MOVE T3,MB			; Get MSD address for DDP driver
	MOVEI T1,DD.PRB			; Function is post receive buffer
	LOAD T2,LNPID,(LN)		; Get DDP portal id
	CALL DLLDDP			; Attempt to post the buffer
	 JRST [MOVE T1,MB		; Get message block address
	       CALL DNFMSG		; Free message block we couldn't post
	       RET]			;  and return
	INCR LNNBP,(LN)			; Account for buffer posted
	RET				; And return

>; END IFN FTDDP
	SUBTTL DMRDLL - DMR data link layer -- Dummy Routines

;Dummy routines if DMR circuits not supported

IFE FTDMR,<
DMDINI:
DMDSEC:
DMDDSP:	RET
>; END IFE FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDINI - Initialize lines

IFN FTDMR,<
;Call:
;	CALL DMDINI
;Returns:
;	RET always
;Side effects:
;	Creates LN blocks for any existing DMR lines

DMDINI:	RET		; DMRs initialize from DMIPPI

;Prototype DMR line block

DMDPLN:	$BUILD	(LN.LEN)
	  $SET (LNSTA,LS.ON)	; Default line state = ON
	  $SET (LNPRO,0)	; Default protocol type = DDCMP-POINT
	  $SET (LNCTY,0)	; Default circuit type = DDCMP-POINT
	  $SET (LNDBF,0)	; Default number of receive buffers = 0
	$EOB

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDSEC - Once a second code

;Still in IFN FTDMR

;Call:
;	DL/ Address of data link block
;	LN/ Address of line data block
;	CALL DMDSEC
;Returns:
;	RET always

DMDSEC:
REPEAT	0,<
	LOAD T1,LNSTA,(LN)	; Get line's state
	TMNE DLLIU,(DL)		; Is DECnet using the data link?
	CAIE T1,LS.ON		; Yes, is line running?
	 RET			; No, return
	CALLRET DMIPRB		; Try to post some buffers
>;We'll ask when we want one
	RET

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDDSP - Function dispatch

;Still in IFN FTDMR

;Call: (for non-network management functions)
;	T1/ Function code (DF.xxx)
;	T3-T4/ Function specific data
;	DL/ Address of data link block
;	LN/ Address of link table block
;	CALL DMDDSP
;Call: (for network management functions)
;	T1/ Function code (DF.xxx)
;	T2/ Address of NF block
;	CALL DMDDSP

DMDDSP:	MOVE CX,DMDDTB(T1)		; Get dispatch address
	CALLRET (CX)

DMDDTB:	IFIW <DMDOPN&777777>	; Open a portal/circuit
	IFIW <DMDCLS&777777>	; Close a portal/circuit
	IFIW <DMDXMT&777777>	; Transmit a packet
	IFIW <DMDSET&777777>	; Set a parameter
	IFIW <DMDCLR&777777>	; Clear a parameter
	IFIW <DMDRED&777777>	; Read paramater
	IFIW <DMDSHC&777777>	; Show counters
	IFIW <DMDSZC&777777>	; Show and zero counters
	IFIW <DMDILL&777777>	; Return list
	IFIW <DMDILL&777777>	; Map Node Address to Name
	IFIW <DMDILL&777777>	; Map Node Name to Address
	IFIW <DMDILL&777777>	; Check Entity Id
IF1,<IFN <DF.MAX-<.-DMDDTB-1>>,<? Wrong number of entries in DDMDTB>>

DMDILL:	RET

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDOPN - Open portal

;Still in IFN FTDMR

;DMDOPN - Open a data link layer port
;
; Call: 
;	T1/ Function (DF.OPN)
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;ON RESOURCE FAILURE
;	RETSKP			;On success with T1 = Line state
;
; Uses: T1-T3

DMDOPN:	MOVX T1,DC.FAL		; Get function (Assign line)
	LOAD T2,LNLID,(LN)	; Get line id
	MOVE T3,DL		; Callback ID (DL block)
	CALL DLLDMR		; Call DMRINT
	 RET			; Error
	SETONE DLLIU,(DL)	; Indicate circuit is using the line
	STOR T1,LNPID,(LN)	; Store portal id
	MOVX T1,DC.FIL		; Get function (Initialize protocol)
	LOAD T2,LNPID,(LN)	; Get portal id
	MOVE T3,DL		; Callback ID (DL block)
	CALL DLLDMR		; Call DMRINT
	 RET			; Error
	LOAD T1,LNSTA,(LN)	; Get current line state
	RETSKP			; Return

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDCLS - Close portal

;Still in IFN FTDMR

;DMDCLS - Close a circuit on a DMR line
;
; Call: 
;	DL/ Data link block address
;	LN/ Line tabel block address
;
; Return: 
;	RET			;On error
;	RETSKP			;On success
;

DMDCLS:	MOVX T1,DC.FHL		; Get DMR driver function
	LOAD T2,LNPID,(LN)	; Get DMR portal id
	CALL DLLDMR		; Ask driver to terminate
	 RET			; Error
	SETZRO DLLIU,(DL)	; Clear the line in use indicator
	MOVX T2,LS.OFF		; Save new line state as off
	STOR T2,LNSTA,(LN)	; ...
	RETSKP

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDXMT - Transmit packet

;Still in IFN FTDMR

;DMDXMT - Send a packet to DMR driver
;
; Call: 
;	T3/ Message block
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;On error from driver
;	RETSKP			;On success

DMDXMT:	MOVX T1,DC.FQB		; Get function
	LOAD T2,LNPID,(LN)	; Get DMR portal id
	CALLRET DLLDMR		;

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDRED - Read parameter
	SUBTTL DMRDLL - DMR data link layer -- DMDSET - Set parameter
	SUBTTL DMRDLL - DMR data link layer -- DMDCLR - Clear parameter

;Still in IFN FTDMR

	XSWAPCD

DMDRED:	MOVEI T3,NF.RED		; Funtion will be in T3
	CALLRET DMDCPF		; Call common parameter routine

DMDSET: MOVEI T3,NF.SET
	CALLRET DMDCPF		; Call common parameter routine

DMDCLR: MOVEI T3,NF.CLR
;	CALLRET DMDCPF		; Call common parameter routine

DMDCPF:	STKVAR <FUNC>
	MOVEM T3,FUNC		; Save function (read,set,clear)
	MOVE P1,T2		; Save NTMAN function block
	LOAD T1,NFETY,(P1)	; Get the entity type
	CAIE T1,.NTCKT		; Is it circuit?
	IFSKP.
	  LOAD T1,NFEID,(P1)	; Yes, get the entity ID again
	  CALL DNDFDL		; Find the data link block
	   RNMXND		; Don't know of this link-Return succes/no data
	  LOAD LN,DLLNB,(DL)	; Address of line data block
	  XMOVEI T1,DMDCPT	; Use the circuit parameter table
	  MOVEI T2,DMDCPL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity type line?
	  IFSKP.
	    LOAD T1,NFEID,(P1)	; Get the entity ID
	    CALL DNDFLN		; Get the line data block for this line
	     RNMXER (NF.MPE)	; We don't support this device
	    XMOVEI T1,DMDLPT	; Get parameter table address
	    MOVEI T2,DMDLPL	;  and its length
	  ELSE.
	    RNMXER (NF.MPE)	; We don't support any others
	  ENDIF.
	ENDIF.
	MOVE T3,FUNC		; Recover requested function
	CALLRET NTPARM		; Call the common parameter processer
				; T1/ table
				; T2/ table's length
				; T3/ function

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDSHC - Show counters
	SUBTTL DMRDLL - DMR data link layer -- DMDSZC - Show and zero counters

;Still in IFN FTDMR

	XSWAPCD

DMDSHC:	SKIPA T3,[NF.COU]	;Load function code and skip
DMDSZC:	MOVX T3,NF.SZC		;Load function code
	STKVAR <FUNC>
	MOVEM T3,FUNC		;T3 will be used later
	MOVE P1,T2		;Move NF pointer to where it should be
	LOAD T1,NFETY,(P1)	;Get the entity type
	CAIE T1,.NTCKT		;Is it a circuit
	IFSKP.
	  LOAD T1,NFEID,(P1)	;Get circuit identifier
	  CALL DNDFDL		;Get the data link block block
	   RNMXND
	  XMOVEI T1,DMDCCT	;Address of circuit counter table
	  MOVEI T2,DMDCCL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity a line?
	  IFSKP.
	    RNMXND		; Return with no data
	  ELSE.
	    RNMXER (NF.OPF)	; We don't do any others
	  ENDIF.
	ENDIF.

;Call NTCTRS to do the real work

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

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMR Parameters

;Still in IFN FTDMR

	XSWAPCD

DMDLPT:

PARAMETER (^D0,<NOSET!NOCLR>,,,,,<LOAD T2,LNSTA,(LN)>,,<Line state>)
PARAMETER(^D1110,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCON,(LN)>,<TRN>,<
	Controller>)
;PARAMETER(^D1111,,,,,<CALL DMDLCP>,<CALL DMDLRP>,<CALL DMDLSP>,<
;	Duplex>)
PARAMETER(^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNPRO,(LN)>,<TRN>,<
	Protocol>)

DMDLPL==.-DMDLPT


DMDCPT:

PARAMETER (^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCTY,(LN)>,<TRN>,<
	Circuit type>)

DMDCPL==.-DMDCPT

	XRESCD

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMR Counters

;Still in IFN FTDMR

	XSWAPCD

;Circuit counters maintained by the line

DMDCCT:

COUNTER (^D1000,^D32,<LOAD T1,DLBYR,(DL)>,<SETZRO DLBYR,(DL)>,,<
	Bytes received>)
COUNTER (^D1001,^D32,<LOAD T1,DLBYS,(DL)>,<SETZRO DLBYS,(DL)>,,<Bytes sent>)
COUNTER (^D1010,^D32,<LOAD T1,DLDBR,(DL)>,<SETZRO DLDBR,(DL)>,,<
	Data blocks received>)
COUNTER (^D1011,^D32,<LOAD T1,DLDBS,(DL)>,<SETZRO DLDBS,(DL)>,,<
	Data blocks sent>)

DMDCCL==.-DMDCCT

	XRESCD

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMDRTL - Return list
	SUBTTL DMRDLL - DMR data link layer -- DMDCET - Check entity

;Still in IFN FTDMR

DMDRTL:
DMDCET:	RNMXER (NF.MPE)

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DLLDMR - Call DMR driver
	
;Still in IFN FTDMR

DLLDMR==DMRDSP##

;Still in IFN FTDMR
	SUBTTL	DMRDLL - DMR data link layer -- DMIPPI - DMR driver callback

;Still in IFN FTDMR

;DMIPPI - Interrupt from DMRINT
;
;Call:
;	T1/ Function code (DC.Ixx)
;	T2/ Address of data link block
;	T3/ Function specific data

	INTERNAL DMIPPI
	XRENT DMIPPI
	CAXL T1,DC.IPU
	 CAXLE T1,DC.ICC
	BUG. (CHK,DMIIFD,DNADLL,SOFT,<Illegal function from DMR driver>,,<
	This BUG is not documented yet.
>,RTN)

	SAVEAC <DL,LN>		; Save DL and LN
	SKIPE DL,T2		; Get address of data link block
	LOAD LN,DLLNB,(DL)	; and line data block address
	MOVE T1,DMIDLI(T1)	; Get address of function specific routine
	CALLRET (T1)		; Dispatch to routine and return

DMIDLI:	IFIW	<DMIPRU&777777>	; Protocol up
	IFIW	<DMIPRD&777777>	; Protocol down
	IFIW	<DMIMAI&777777>	; Maint msg received
	IFIW	<DMISTR&777777>	; Start received
	IFIW	<DMIODN&777777>	; Output done
	IFIW	<DMIOND&777777>	; Output not done
	IFIW	<DMIINC&777777>	; Input complete
	IFIW	<DMIBFR&777777>	; Get buffer
	IFIW	<DMIICB&777777>	; Initialize circuit block
	IFIW	<DMIDCB&777777>	; Destroy circuit block
IF1,<IFN <DC.IMX-<.-DMIDLI-1>>,<? Wrong number of entries in DMIDLI>>

DMIDCB:
DMIILL:	RET
; Still FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMILSC - Line state change

DMISTR:
DMIPRU:	SKIPA T3,[LS.ON]	; Protocol is now up
DMIPRD:	 MOVEI T3,LS.OFF	; Protocol is down
	JRST DMILSC		; Line state changed

DMIMAI:	MOVEI T3,LS.SRV		; Service
;	JRST DMILSC		; We changed

DMILSC:	STOR T3,LNSTA,(LN)	; Save new state
	TMNN DLLIU,(DL)		; In use by router?
	IFSKP.
	  MOVEI T1,DI.LSC	; Yes, notify ROUTER
	  CALL DNDQUE
	ENDIF.
	RET

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMIODN - Transmit done

;Still in IFN FTDMR

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address


DMIOND:				; Output not done, who cares?
DMIODN:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYS,(DL) ; Update bytes sent
	INCR DLDBS,(DL)		; Another packet sent
	MOVEI T1,DI.ODN		; Function is Output Done
	CALLRET DNDQUE		; Queue message up and return

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMIINC - Receive done

;Still in IFN FTDMR

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address

DMIINC:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYR,(DL) ; Update bytes received
	INCR DLDBR,(DL)		; Another packet received
	MOVEI T1,DI.INC		; Function is input complete
	CALL DNDQUE		; Queue message up
	RET			; OK, done

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMIICB - Initialize circuit block

;Still in IFN FTDMR

;DMIICB - Create DMR line and circuit blocks
;
;Call:
;	T3/ Line id
;
;Returns:
;	RET on error
;	RETSKP on success

DMIICB:	SAVEAC <DL,LN>		; Save DL and LN
	MOVE T1,T3		; Get line id
	XMOVEI T2,DMDPLN	; Get address of prototype DMR line block
	CALL DNDCLN		; Create line block if needed
	 RET			; Can't, give error return
	LOAD T1,LNLID,(LN)	; Get line id
	TXZ T1,LILXC		; Make into circuit id
	CALL DNDCDL		; Create circuit block if needed
	 RET			; Can't, give error return
	RETSKP			; Return

;Still in IFN FTDMR
	SUBTTL DMRDLL - DMR data link layer -- DMIBFR - Provide receive buffer

;Still in IFN FTDMR

; Call: 
;	DL/ Data link block address
;	LN/ line data block address
;
; Uses: T1-T3

DMIBFR:	MOVE T1,T3			; Get the size to get for this link
	CALL DNGMSG			; Request a message block
	 JRST [SETZ T1,
		 RET]
	XMOVEI	T2,IN.MSD(T1)		;POINT SERVICE AT THE BUFFER
	MOVEM	T2,MB.FMS(T1)		;SO WE AREN'T CONFUSED LATER
	RET				; And return

>; END IFN FTDMR
	SUBTTL DTEDLL - DTE data link layer -- Dummy Routines

;Dummy routines if DTE circuits not supported

IFE FTDTE,<
DTDINI:
DTDSEC:
DTDDSP:	RET
>; END IFE FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDINI - Initialize lines

IFN FTDTE,<
;Call:
;	CALL DTDINI
;Returns:
;	RET always
;Side effects:
;	Creates LN blocks for any existing DTE lines

DTDINI:	SAVEAC <P1,P2>		; Save P1 and P2
	MOVSI P1,-CPUN##	; Set up CPU AOBJN counter
DTDIN1:	MOVE P2,[-3,,1]		; Set up DTE AOBJN counter
DTDIN2:	MOVEI T1,DD.CKE		; Ask DTE driver to check its existance
	HRLZ T2,P1		; Get CPU number
	HRR T2,P2		; And DTE number
	CALL DLLDTE		; Does this DTE exist?
	 JRST DTDIN3		; No, skip it
	MOVX T1,LILXC!FLD(LD.DTE,LIDEV) ; Build a line id for this DTE
	STOR P1,LIKON,+T1	; Store CPU number
	STOR P2,LIUNI,+T1	; And DTE number
	XMOVEI T2,DTDPLN	; Get address of prototype DTE line block
	CALL DNDCLN		; Create line block if needed
	 JRST DTDIN3		; Can't, skip this DTE
	LOAD T1,LNLID,(LN)	; Get line id
	TXZ T1,LILXC		; Make into circuit id
	CALL DNDCDL		; Create circuit block if needed
	 JFCL			; Error
DTDIN3:	AOBJN	P2,DTDIN2	; Loop back for all possible DTEs
	AOBJN	P1,DTDIN1	; Loop back for all possible CPUs
	RETSKP			; Return

; Prototype DTE line block

DTDPLN:	$BUILD	(LN.LEN)
	  $SET (LNSTA,LS.OFF)	; Default line state of off
	  $SET (LNPRO,8)	; Protocol type = QP2
	  $SET (LNCTY,8)	; Circuit type = QP2
	  $SET (LNDBF,1)	; Default number of receive buffers = 1
	$EOB

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDSEC - Once a second code

;Still in IFN FTDTE

;Call:
;	DL/ Address of data link block
;	LN/ Address of line data block
;	CALL DTDSEC
;Returns:
;	RET always

DTDSEC:	LOAD T1,LNSTA,(LN)	; Get line's state
	TMNE DLLIU,(DL)		; Is DECnet using the data link?
	CAIE T1,LS.ON		; Yes, is line running?
	 RET			; No, then don't attempt to post buffers
	CALLRET DTIPRB		; Try to post a buffer

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDDSP - Function dispatch

;Still in IFN FTDTE

;Call: (for non-network management functions)
;	T1/ Function code (DF.xxx)
;	T3-T4/ Function specific data
;	DL/ Address of data link block
;	LN/ Address of link table block
;	CALL DTDDSP
;Call: (for network management functions)
;	T1/ Function code (DF.xxx)
;	T2/ Address of NF block
;	CALL DTDDSP

DTDDSP:	MOVE CX,DTDDTB(T1)	; Get dispatch address
	CALLRET (CX)

DTDDTB:	IFIW <DTDOPN&777777>	; Open a portal/circuit
	IFIW <DTDCLS&777777>	; Close a portal/circuit
	IFIW <DTDXMT&777777>	; Transmit a packet
	IFIW <DTDSET&777777>	; Set a parameter
	IFIW <DTDCLR&777777>	; Clear a parameter
	IFIW <DTDRED&777777>	; Read paramater
	IFIW <DTDSHC&777777>	; Show counters
	IFIW <DTDSZC&777777>	; Show and zero counters
	IFIW <DTDILL&777777>	; Return list
	IFIW <DTDILL&777777>	; Map Node Address to Name
	IFIW <DTDILL&777777>	; Map Node Name to Address
	IFIW <DTDILL&777777>	; Check Entity Id
IF1,<IFN <DF.MAX-<.-DTDDTB-1>>,<? Wrong number of entries in DTDDTB>>

DTDILL:	RET

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDOPN - Open portal

;Still in IFN FTDTE

;DTDOPN - Open a data link layer port
;
; Call: 
;	T1/ Function (DF.OPN)
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;ON RESOURCE FAILURE
;	RETSKP			;On success with T1 = Line state
;
; Uses: T1-T3

DTDOPN:	MOVX T1,DD.OPN		; Get function
	LOAD T3,LNLID,(LN)	; Get line id
	LOAD T2,LIKON,+T3	; Get CPU number
	LOAD T3,LIUNI,+T3	; And DTE number
	HRLZS T2		; Construct CPU,,DTE
	HRR T2,T3		; ...
	MOVE T3,DL		; Callback ID (DL block)
	SETZ T4,		; Indicate no buffer
	CALL DLLDTE
	 RET			; Error
	SETONE DLLIU,(DL)	; Indicate circuit is using the line
	STOR T1,LNPID,(LN)	; Store portal id
	CALL DTIPRB		; Post a receive buffer
	LOAD T1,LNSTA,(LN)	; Get current line state
	RETSKP			; Return

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDCLS - Close portal

;Still in IFN FTDTE

;DTDCLS - Close a circuit on a DTE line
;
; Call: 
;	DL/ Data link block address
;	LN/ Line tabel block address
;
; Return: 
;	RET			;On error
;	RETSKP			;On success
;

DTDCLS:	MOVX T1,DD.CLS		; Get DTE driver function
	LOAD T2,LNPID,(LN)	; Get DTE portal id
	CALL DLLDTE		; Ask driver to terminate
	 RET			; Error
	SETZRO DLLIU,(DL)	; Clear the line in use indicator
	MOVX T2,LS.OFF		; Set new line state as off
	STOR T2,LNSTA,(LN)	; ...
	JUMPE T1,RSKP		; If we got an MB return it
	DECR LNNBP,(LN)		; No longer posted
	CALL DNFMSG		; Return it to the free pool
	RETSKP

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDXMT - Transmit packet

;Still in IFN FTDTE

;DTDXMT - Send a packet to DTE driver
;
; Call: 
;	T3/ Message block
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;On error from driver
;	RETSKP			;On success

DTDXMT:	MOVX T1,DD.QUE		; Get function
	LOAD T2,LNPID,(LN)	; Get DTE portal id
	CALLRET DLLDTE		;

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDRED - Read parameter
	SUBTTL DTEDLL - DTE data link layer -- DTDSET - Set parameter
	SUBTTL DTEDLL - DTE data link layer -- DTDCLR - Clear parameter

;Still in IFN FTDTE

	XSWAPCD

DTDRED:	MOVEI T3,NF.RED		; Funtion will be in T3
	CALLRET DTDCPF		; Call common parameter routine

DTDSET: MOVEI T3,NF.SET
	CALLRET DTDCPF		; Call common parameter routine

DTDCLR: MOVEI T3,NF.CLR
;	CALLRET DTDCPF		; Call common parameter routine

DTDCPF:	STKVAR <FUNC>
	MOVEM T3,FUNC		; Save function (read,set,clear)
	MOVE P1,T2		; Save NTMAN function block
	LOAD T1,NFETY,(P1)	; Get the entity type
	CAIE T1,.NTCKT		; Is it circuit?
	IFSKP.
	  LOAD T1,NFEID,(P1)	; Yes, get the entity ID again
	  CALL DNDFDL		; Find the data link block
	   RNMXND		; Don't know of this link-Return succes/no data
	  LOAD LN,DLLNB,(DL)	; Address of line data block
	  XMOVEI T1,DTDCPT	; Use the circuit parameter table
	  MOVEI T2,DTDCPL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity type line?
	  IFSKP.
	    LOAD T1,NFEID,(P1)	; Get the entity ID
	    CALL DNDFLN		; Get the line data block for this line
	     RNMXER (NF.MPE)	; We don't support this device
	    XMOVEI T1,DTDLPT	; Get parameter table address
	    MOVEI T2,DTDLPL	;  and its length
	  ELSE.
	    RNMXER (NF.MPE)	; We don't support any others
	  ENDIF.
	ENDIF.
	MOVE T3,FUNC		; Recover requested function
	CALLRET NTPARM		; Call the common parameter processer
				; T1/ table
				; T2/ table's length
				; T3/ function

	XRESCD

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDSHC - Show counters
	SUBTTL DTEDLL - DTE data link layer -- DTDSZC - Show and zero counters

;Still in IFN FTDTE

	XSWAPCD

DTDSHC:	SKIPA T3,[NF.COU]	;Load function code and skip
DTDSZC:	MOVX T3,NF.SZC		;Load function code
	STKVAR <FUNC>
	MOVEM T3,FUNC		;T3 will be used later
	MOVE P1,T2		;Move NF pointer to where it should be
	LOAD T1,NFETY,(P1)	;Get the entity type
	CAIE T1,.NTCKT		;Is it a circuit
	IFSKP.
	  LOAD T1,NFEID,(P1)	;Get circuit identifier
	  CALL DNDFDL		;Get the data link block block
	   RNMXND
	  XMOVEI T1,DTDCCT	;Address of circuit counter table
	  MOVEI T2,DTDCCL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity a line?
	  IFSKP.
	    RNMXND		; Return with no data
	  ELSE.
	    RNMXER (NF.OPF)	; We don't do any others
	  ENDIF.
	ENDIF.

;Call NTCTRS to do the real work

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

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTDRTL - Return list
	SUBTTL DTEDLL - DTE data link layer -- DTDCET - Check entity

;Still in IFN FTDTE

DTDRTL:
DTDCET:	RNMXER (NF.MPE)

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTE Parameters

;Still in IFN FTDTE

	XSWAPCD

DTDLPT:

PARAMETER (^D0,<NOSET!NOCLR>,,,,,<LOAD T2,LNSTA,(LN)>,,<Line state>)
PARAMETER (^D1105,,^D20,^D5,^D10,<STOR T2,LNBNO,(LN)>,<LOAD T2,LNBNO,(LN)>,<
	STOR T2,LNBNO,(LN)>,<Receive buffers>)
PARAMETER(^D1110,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCON,(LN)>,<TRN>,<
	Controller>)
PARAMETER(^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNPRO,(LN)>,<TRN>,<
	Protocol>)
PARAMETER(^D2500,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNBSZ,(LN)>,<TRN>,<
	Receive buffer size>)

DTDLPL==.-DTDLPT


DTDCPT:

PARAMETER (^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCTY,(LN)>,<TRN>,<
	Circuit type>)

DTDCPL==.-DTDCPT

	XRESCD

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTE Counters

;Still in IFN FTDTE

	XSWAPCD

;Circuit counters maintained by the line

DTDCCT:

COUNTER (^D1000,^D32,<LOAD T1,DLBYR,(DL)>,<SETZRO DLBYR,(DL)>,,<
	Bytes received>)
COUNTER (^D1001,^D32,<LOAD T1,DLBYS,(DL)>,<SETZRO DLBYS,(DL)>,,<Bytes sent>)
COUNTER (^D1010,^D32,<LOAD T1,DLDBR,(DL)>,<SETZRO DLDBR,(DL)>,,<
	Data blocks received>)
COUNTER (^D1011,^D32,<LOAD T1,DLDBS,(DL)>,<SETZRO DLDBS,(DL)>,,<
	Data blocks sent>)
COUNTER (^D1065,^D16,<LOAD T1,DLUBU,(DL)>,<SETZRO DLUBU,(DL)>,,<
	User buffer unavailable>)

DTDCCL==.-DTDCCT

	XRESCD

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DLLDTE - Call DTE driver
	
;Still in IFN FTDTE

	XRESCD

IFN FTOPS20,<
DLLDTE:	XCALLRET (MSEC1,DTEDSP)		; Call DTE driver
>; END IFN FTOPS20
IFN FTOPS10,<
DLLDTE:	SNCALL (DTEDSP##,MCSEC1)
	  RET
	RETSKP
>; END IFN FTOPS10

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTIPPI - DTE driver callback

;Still in IFN FTDTE

;DTIPPI - Interrupt from DTE driver for MCB functions
;
;Call:
;	T1/ Function code (DI.xxx)
;	T2/ Data link block address

	INTERNAL DTIPPI
	XRENT DTIPPI
	CAXL T1,DI.ODN
	 CAXLE T1,DI.ICB
	BUG. (CHK,DTIIFK,DNADLL,SOFT,<Illegal function code from DTE kontroller>,,<
	This BUG is not documented yet.
>,RTN)

	SAVEAC <DL,LN>		; Save DL and LN
	SKIPE DL,T2		; Get address of data link block
	LOAD LN,DLLNB,(DL)	; and line data block address
	MOVE T1,DTIDLI(T1)	; Get address of function specific routine
	CALLRET (T1)		; Dispatch to routine and return

DTIDLI:	IFIW	<DTIILL&777777>	; Unused
	IFIW	<DTIODN&777777>	; Output done
	IFIW	<DTIINC&777777>	; Input complete
	IFIW	<DTILSC&777777>	; Line state change
	IFIW	<DTIICB&777777>	; Initialize circuit block
IF1,<IFN <DI.MAX-<.-DTIDLI-1>>,<? Wrong number of entries in DTIDLI>>

DTIILL:	RET

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTIODN - Transmit done

;Still in IFN FTDTE

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address

DTIODN:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYS,(DL) ; Update bytes sent
	INCR DLDBS,(DL)		; Another packet sent
	MOVEI T1,DI.ODN		; Function is Output Done
	CALLRET DNDQUE		; Queue message up and return

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTIINC - Receive done

;Still in IFN FTDTE

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address

DTIINC:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYR,(DL) ; Update bytes received
	INCR DLDBR,(DL)		; Another packet received
	DECR LNNBP,(LN)		; Account for buffer received
	MOVEI T1,DI.INC		; Function is input complete
	CALL DNDQUE		; Queue message up
	CALLRET DTIPRB		; Post a buffer for driver

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTILSC - Line state change

;Still in IFN FTDTE

;Call:
;	T3/ New state
;	DL/ Data link block address
;	LN/ line data block address

DTILSC:	STOR T3,LNSTA,(LN)	; Save the new state
	TMNN DLLIU,(DL)		; Is Router using this line?
	IFSKP.
	  MOVEI T1,DI.LSC	; Yes, notify ROUTER
	  CALL DNDQUE
	ENDIF.
	LOAD T1,LNSTA,(LN)	; Get state again
	CAIN T1,LS.ON		; Is new state = on?
	 CALL DTIPRB		; Yes, post a buffer
	RET

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTIICB - Initialize circuit block

;Still in IFN FTDTE

;DTIICB - Create DTE line and circuit blocks
;
;Call:
;	T3/ Line id
;
;Returns:
;	RET on error
;	RETSKP on success

DTIICB:	SAVEAC <DL,LN>		; Save DL and LN
	MOVE T1,T3		; Get line id
	XMOVEI T2,DTDPLN	; Get address of prototype DTE line block
	CALL DNDCLN		; Create line block if needed
	 RET			; Can't, give error return
	LOAD T1,LNLID,(LN)	; Get line id
	TXZ T1,LILXC		; Make into circuit id
	CALL DNDCDL		; Create circuit block if needed
	 RET			; Can't, give error return
	RETSKP			; Return

;Still in IFN FTDTE
	SUBTTL DTEDLL - DTE data link layer -- DTIPRB - Post receive buffer

;Still in IFN FTDTE

; Call: 
;	DL/ Data link block address
;	LN/ line data block address
;
; Uses: T1-T3

DTIPRB:	SAVEAC <MB>
	LOAD T1,LNNBP,(LN)		; Get number posted
	OPSTR <CAML T1,>,LNBNO,(LN)	; Enough posted?
	 RET
	LOAD T1,LNBSZ,(LN)		; Get the size to post for this link
	CALL DNGMSG			; Request a message block
	 RET				; None available, return
	MOVE MB,T1			; Save address of block
	XMOVEI T2,IN.MSD(MB)		; Point to the input MSD
	STOR T2,MBFMS,(MB)		; Store in forst MSD slot
	MOVE T3,MB			; Get MSD address for DTE driver
	MOVEI T1,DD.PRB			; Function is post receive buffer
	LOAD T2,LNPID,(LN)		; Get DTE portal id
	CALL DLLDTE			; Attempt to post the buffer
	 JRST [MOVE T1,MB		; Get message block address
	       CALL DNFMSG		; Free message block we couldn't post
	       RET]			;  and return
	INCR LNNBP,(LN)			; Account for buffer posted
	RET				; And return

>; END IFN FTDTE
	SUBTTL KDPDLL - KDP data link layer -- Dummy Routines

;Dummy routines if KDP circuits not supported

IFE FTKDP,<
KDDINI:
KDDSEC:
KDDDSP:	RET
>; END IFE FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDINI - Initialize lines

IFN FTKDP,<
;Call:
;	CALL KDDINI
;Returns:
;	RET always
;Side effects:
;	Creates LN blocks for any existing KDP lines

KDDINI:	RET			; KDPs initialize from KDIPPI

;Prototype KDP line block

KDDPLN:	$BUILD	(LN.LEN)
	  $SET (LNSTA,LS.OFF)	; Default line state = OFF (KDPLDR must run)
	  $SET (LNPRO,0)	; Default protocol type = DDCMP-POINT
	  $SET (LNCTY,0)	; Default circuit type = DDCMP-POINT
	  $SET (LNDBF,0)	; Default number of receive buffers = 0
	$EOB

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDSEC - Once a second code

;Still in IFN FTKDP

;Call:
;	DL/ Address of data link block
;	LN/ Address of line data block
;	CALL KDDSEC
;Returns:
;	RET always

KDDSEC:	RET

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDDSP - Function dispatch

;Still in IFN FTKDP

;Call: (for non-network management functions)
;	T1/ Function code (DF.xxx)
;	T3-T4/ Function specific data
;	DL/ Address of data link block
;	LN/ Address of link table block
;	CALL KDDDSP
;Call: (for network management functions)
;	T1/ Function code (DF.xxx)
;	T2/ Address of NF block
;	CALL KDDDSP

KDDDSP:	MOVE CX,KDDDTB(T1)	; Get dispatch address
	CALLRET (CX)

KDDDTB:	IFIW <KDDOPN&777777>	; Open a portal/circuit
	IFIW <KDDCLS&777777>	; Close a portal/circuit
	IFIW <KDDXMT&777777>	; Transmit a packet
	IFIW <KDDSET&777777>	; Set a parameter
	IFIW <KDDCLR&777777>	; Clear a parameter
	IFIW <KDDRED&777777>	; Read paramater
	IFIW <KDDSHC&777777>	; Show counters
	IFIW <KDDSZC&777777>	; Show and zero counters
	IFIW <KDDILL&777777>	; Return list
	IFIW <KDDILL&777777>	; Map Node Address to Name
	IFIW <KDDILL&777777>	; Map Node Name to Address
	IFIW <KDDILL&777777>	; Check Entity Id
IF1,<IFN <DF.MAX-<.-KDDDTB-1>>,<? Wrong number of entries in DKDDTB>>

KDDILL:	RET

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDOPN - Open portal

;Still in IFN FTKDP

;KDDOPN - Open a data link layer port
;
; Call: 
;	T1/ Function (DF.OPN)
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;ON RESOURCE FAILURE
;	RETSKP			;On success with T1 = Line state
;
; Uses: T1-T3

KDDOPN:	MOVX T1,DC.FAL		; Get function (Assign line)
	LOAD T2,LNLID,(LN)	; Get line id
	MOVE T3,DL		; Callback ID (DL block)
	CALL DLLKDP		; Call KDPINT
	 RET			; Error
	SETONE DLLIU,(DL)	; Indicate circuit is using the line
	STOR T1,LNPID,(LN)	; Store portal id
	MOVX T1,DC.FIL		; Get function (Initialize protocol)
	LOAD T2,LNPID,(LN)	; Get portal id
	MOVE T3,DL		; Callback ID (DL block)
	CALL DLLKDP		; Call KDPINT
	 RET			; Error
	LOAD T1,LNSTA,(LN)	; Get current line state
	RETSKP			; Return

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDCLS - Close portal

;Still in IFN FTKDP

;KDDCLS - Close a circuit on a KDP line
;
; Call: 
;	DL/ Data link block address
;	LN/ Line tabel block address
;
; Return: 
;	RET			;On error
;	RETSKP			;On success
;

KDDCLS:	MOVX T1,DC.FHL		; Get KDP driver function
	LOAD T2,LNPID,(LN)	; Get KDP portal id
	CALL DLLKDP		; Ask driver to terminate
	 RET			; Error
	SETZRO DLLIU,(DL)	; Clear the line in use indicator
	MOVX T2,LS.OFF		; Save new line state as off
	STOR T2,LNSTA,(LN)	; ...
	RETSKP

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDXMT - Transmit packet

;Still in IFN FTKDP

;KDDXMT - Send a packet to KDP driver
;
; Call: 
;	T3/ Message block
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;On error from driver
;	RETSKP			;On success

KDDXMT:	MOVX T1,DC.FQB		; Get function
	LOAD T2,LNPID,(LN)	; Get KDP portal id
	CALLRET DLLKDP		;

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDRED - Read parameter
	SUBTTL KDPDLL - KDP data link layer -- KDDSET - Set parameter
	SUBTTL KDPDLL - KDP data link layer -- KDDCLR - Clear parameter

;Still in IFN FTKDP

	XSWAPCD

KDDRED:	MOVEI T3,NF.RED		; Funtion will be in T3
	CALLRET KDDCPF		; Call common parameter routine

KDDSET: MOVEI T3,NF.SET
	CALLRET KDDCPF		; Call common parameter routine

KDDCLR: MOVEI T3,NF.CLR
;	CALLRET KDDCPF		; Call common parameter routine

KDDCPF:	STKVAR <FUNC>
	MOVEM T3,FUNC		; Save function (read,set,clear)
	MOVE P1,T2		; Save NTMAN function block
	LOAD T1,NFETY,(P1)	; Get the entity type
	CAIE T1,.NTCKT		; Is it circuit?
	IFSKP.
	  LOAD T1,NFEID,(P1)	; Yes, get the entity ID again
	  CALL DNDFDL		; Find the data link block
	   RNMXND		; Don't know of this link-Return succes/no data
	  LOAD LN,DLLNB,(DL)	; Address of line data block
	  XMOVEI T1,KDDCPT	; Use the circuit parameter table
	  MOVEI T2,KDDCPL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity type line?
	  IFSKP.
	    LOAD T1,NFEID,(P1)	; Get the entity ID
	    CALL DNDFLN		; Get the line data block for this line
	     RNMXER (NF.MPE)	; We don't support this device
	    XMOVEI T1,KDDLPT	; Get parameter table address
	    MOVEI T2,KDDLPL	;  and its length
	  ELSE.
	    RNMXER (NF.MPE)	; We don't support any others
	  ENDIF.
	ENDIF.
	MOVE T3,FUNC		; Recover requested function
	CALLRET NTPARM		; Call the common parameter processer
				; T1/ table
				; T2/ table's length
				; T3/ function

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDSHC - Show counters
	SUBTTL KDPDLL - KDP data link layer -- KDDSZC - Show and zero counters

;Still in IFN FTKDP

	XSWAPCD

KDDSHC:	SKIPA T3,[NF.COU]	;Load function code and skip
KDDSZC:	MOVX T3,NF.SZC		;Load function code
	STKVAR <FUNC>
	MOVEM T3,FUNC		;T3 will be used later
	MOVE P1,T2		;Move NF pointer to where it should be
	LOAD T1,NFETY,(P1)	;Get the entity type
	CAIE T1,.NTCKT		;Is it a circuit
	IFSKP.
	  LOAD T1,NFEID,(P1)	;Get circuit identifier
	  CALL DNDFDL		;Get the data link block block
	   RNMXND
	  XMOVEI T1,KDDCCT	;Address of circuit counter table
	  MOVEI T2,KDDCCL	; and its length
	ELSE.
	  CAIE T1,.NTLIN	; Is entity a line?
	  IFSKP.
	    RNMXND		; Return with no data
	  ELSE.
	    RNMXER (NF.OPF)	; We don't do any others
	  ENDIF.
	ENDIF.

;Call NTCTRS to do the real work

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

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDDRTL - Return list
	SUBTTL KDPDLL - KDP data link layer -- KDDCET - Check entity

;Still in IFN FTKDP

KDDRTL:
KDDCET:	RNMXER (NF.MPE)

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDP Parameters

;Still in IFN FTKDP

	XSWAPCD

KDDLPT:

PARAMETER (^D0,<NOSET!NOCLR>,,,,,<LOAD T2,LNSTA,(LN)>,,<Line state>)
PARAMETER(^D1110,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCON,(LN)>,<TRN>,<
	Controller>)
PARAMETER(^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNPRO,(LN)>,<TRN>,<
	Protocol>)

KDDLPL==.-KDDLPT


KDDCPT:

PARAMETER (^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCTY,(LN)>,<TRN>,<
	Circuit type>)

KDDCPL==.-KDDCPT

	XRESCD

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDP Counters

;Still in IFN FTKDP

	XSWAPCD

;Circuit counters maintained by the line

KDDCCT:

COUNTER (^D1000,^D32,<LOAD T1,DLBYR,(DL)>,<SETZRO DLBYR,(DL)>,,<
	Bytes received>)
COUNTER (^D1001,^D32,<LOAD T1,DLBYS,(DL)>,<SETZRO DLBYS,(DL)>,,<Bytes sent>)
COUNTER (^D1010,^D32,<LOAD T1,DLDBR,(DL)>,<SETZRO DLDBR,(DL)>,,<
	Data blocks received>)
COUNTER (^D1011,^D32,<LOAD T1,DLDBS,(DL)>,<SETZRO DLDBS,(DL)>,,<
	Data blocks sent>)

KDDCCL==.-KDDCCT

	XRESCD

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- DLLKDP - Call KDP driver
	
;Still in IFN FTKDP

DLLKDP==KDPDSP##

;Still in IFN FTKDP
	SUBTTL	KDPDLL - KDP data link layer -- KDIPPI - KDP driver callback

;Still in IFN FTKDP

;KDIPPI - Interrupt from KDPINT
;
;Call:
;	T1/ Function code (DC.Ixx)
;	T2/ Address of data link block
;	T3/ Function specific data

	INTERNAL KDIPPI
	XRENT KDIPPI
	CAXL T1,DC.IPU
	 CAXLE T1,DC.ICC
	BUG. (CHK,KDIIFD,DNADLL,SOFT,<Illegal function from KDP driver>,,<
	This BUG is not documented yet.
>,RTN)

	SAVEAC <DL,LN>		; Save DL and LN
	SKIPE DL,T2		; Get address of data link block
	LOAD LN,DLLNB,(DL)	; and line data block address
	MOVE T1,KDIDLI(T1)	; Get address of function specific routine
	CALLRET (T1)		; Dispatch to routine and return

KDIDLI:	IFIW	<KDIPRU&777777>	; Protocol up
	IFIW	<KDIPRD&777777>	; Protocol down
	IFIW	<KDIMAI&777777>	; Maint msg received
	IFIW	<KDISTR&777777>	; Start received
	IFIW	<KDIODN&777777>	; Output done
	IFIW	<KDIOND&777777>	; Output not done
	IFIW	<KDIINC&777777>	; Input complete
	IFIW	<KDIBFR&777777>	; Get buffer
	IFIW	<KDIICB&777777>	; Initialize circuit block
	IFIW	<KDIDCB&777777>	; Destroy circuit block
IF1,<IFN <DC.IMX-<.-KDIDLI-1>>,<? Wrong number of entries in KDIDLI>>

KDIDCB:
KDIILL:	RET
; Still FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDILSC - Line state change

KDISTR:
KDIPRU:	SKIPA T3,[LS.ON]	; Protocol is now up
KDIPRD:	 MOVEI T3,LS.OFF	; Protocol is down
	JRST KDILSC		; Line state changed

KDIMAI:	MOVEI T3,LS.SRV		; Service
;	JRST KDILSC		; We changed

KDILSC:	STOR T3,LNSTA,(LN)	; Save new state
	TMNN DLLIU,(DL)		; In use by router?
	IFSKP.
	  MOVEI T1,DI.LSC	; Yes, notify ROUTER
	  CALL DNDQUE
	ENDIF.
	RET

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDIODN - Transmit done

;Still in IFN FTKDP

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address


KDIOND:				; Output not done, who cares?
KDIODN:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYS,(DL) ; Update bytes sent
	INCR DLDBS,(DL)		; Another packet sent
	MOVEI T1,DI.ODN		; Function is Output Done
	CALLRET DNDQUE		; Queue message up and return

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDIINC - Receive done

;Still in IFN FTKDP

;	T3/ MB address
;	DL/ Data link block address
;	LN/ line data block address

KDIINC:	MOVE T1,T3		; Get MB address
	CALL DNLENG		; Get message length
	OPSTRM <ADDM T1,>,DLBYR,(DL) ; Update bytes received
	INCR DLDBR,(DL)		; Another packet received
	MOVEI T1,DI.INC		; Function is input complete
	CALL DNDQUE		; Queue message up
	RET			; OK, done

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDIICB - Initialize circuit block

;Still in IFN FTKDP

;KDIICB - Create KDP line and circuit blocks
;
;Call:
;	T3/ Line id
;
;Returns:
;	RET on error
;	RETSKP on success

KDIICB:	SAVEAC <DL,LN>		; Save DL and LN
	MOVE T1,T3		; Get line id
	XMOVEI T2,KDDPLN	; Get address of prototype KDP line block
	CALL DNDCLN		; Create line block if needed
	 RET			; Can't, give error return
	LOAD T1,LNLID,(LN)	; Get line id
	TXZ T1,LILXC		; Make into circuit id
	CALL DNDCDL		; Create circuit block if needed
	 RET			; Can't, give error return
	RETSKP			; Return

;Still in IFN FTKDP
	SUBTTL KDPDLL - KDP data link layer -- KDIBFR - Provide receive buffer

;Still in IFN FTKDP

; Call: 
;	DL/ Data link block address
;	LN/ line data block address
;
; Uses: T1-T3

KDIBFR:	MOVE T1,T3			; Get the size to get for this link
	CALL DNGMSG			; Request a message block
	 JRST [SETZ T1,
		 RET]
	XMOVEI	T2,IN.MSD(T1)		;POINT SERVICE AT THE BUFFER
	MOVEM	T2,MB.FMS(T1)		;SO WE AREN'T CONFUSED LATER
	RET				; And return

>; END IFN FTKDP
	SUBTTL NIDLL - Ethernet data link layer -- Dummy Routines

;Dummy routines if ethernet circuits not supported

IFE FTNI,<
NIDINI:
NIDSEC:
NIDDSP:	RET
>; END IFE FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDINI - Initialize lines

IFN FTNI,<
;Call:
;	CALL NIDINI
;Returns:
;	RET always
;Side effects:
;	Creates LN blocks for any existing ethernet channels

NIDINI:	SAVEAC <UN>
	CALL GETUNB		; Try to allocate space for a UN block
	 RET
	MOVEI T1,4		; Length of buffer for channel list
	STOR T1,UNBSZ,(UN)	; Remember length for NISRV
	CALL DNGWDZ
	 CALLRET FREUNB
	STOR T1,UNBFA,(UN)
	MOVX T1,NU.RCL		; Function is "Return channel list"
	MOVE T2,UN		; Address of UN block
	CALL DLLNI
	 JRST NIDIN3		; Return error - (BUGINF)
	LOAD T1,UNBSZ,(UN)	; Get number of channels found
	JUMPE T1,NIDIN3		; Return if no ethernet channels
	MOVN P1,T1		; Create AOBJN for number of channels
	HRLZS P1		; ...
NIDIN1:	MOVX T1,LILXC!FLD(LD.ETH,LIDEV) ; Build a line id for this channel
	STOR P1,LIKON,+T1	; Store ethernet channel number
	XMOVEI T2,NIDPLN	; Get address of prototype NI line block
	CALL DNDCLN		; Create line block if needed
	 JRST NIDIN2		; Can't, skip this channel
	LOAD T1,LNLID,(LN)	; Get line id
	TXZ T1,LILXC		; Make into circuit id
	CALL DNDCDL		; Create circuit block if needed
	 JFCL			; Error
NIDIN2:	AOBJN P1,NIDIN1		; Loop back for all channels
NIDIN3:	LOAD T1,UNBFA,(UN)	; Free the buffer
	CALL DNFWDS
	CALL FREUNB		; Free the UN block
	RET

; Prototype NI line block

NIDPLN:	$BUILD (LN.LEN)
	  $SET (LNSTA,LS.ON)	; Default line state of on
	  $SET (LNPRO,6)	; Protocol type = Ethernet
	  $SET (LNCTY,6)	; Circuit type = Ethernet
	  $SET (LNDBF,6)	; Default number of receive buffers = 6
	$EOB

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDSEC - Once a second code

;Still in IFN FTNI

;Call:
;	DL/ Address of data link block
;	LN/ Address of line data block
;	CALL NIDSEC
;Returns:
;	RET always

NIDSEC:	LOAD T1,LNSTA,(LN)	; Get line's state
	TMNE DLLIU,(DL)		; Is DECnet using the data link?
	CAIE T1,LS.ON		; Yes, is line running?
	 RET			; No, return
	CALLRET NIDPRB		; Try to post some buffers

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDDSP - Function dispatch

;Still in IFN FTNI

;Call: (for non-network management functions)
;	T1/ Function code (DF.xxx)
;	T3-T4/ Function specific data
;	DL/ Address of data link block
;	LN/ Address of link table block
;	CALL NIDDSP
;Call: (for network management functions)
;	T1/ Function code (DF.xxx)
;	T2/ Address of NF block
;	CALL NIDDSP

NIDDSP:	SAVEAC <UN>
	MOVE T1,NIDDTB(T1)	; Get processor address
	CALLRET (T1)		;  and dispatch

NIDDTB:	IFIW <NIDOPN&777777>	; Open portal/circuit
	IFIW <NIDCLS&777777>	; Close portal/circuit
	IFIW <NIDXMT&777777>	; Transmit packet
	IFIW <NIDSET&777777>	; Set parameter
	IFIW <NIDCLR&777777>	; Clear parameter
	IFIW <NIDRED&777777>	; Read parameter
	IFIW <NIDSHC&777777>	; Show counters
	IFIW <NIDSZC&777777>	; Show and zero counters
	IFIW <NIDILL&777777>	; Return list
	IFIW <NIDILL&777777>	; Map Node Address to Name
	IFIW <NIDILL&777777>	; Map Node Name to Address
	IFIW <NIDILL&777777>	; Check Entity Id
IF1,<IFN <DF.MAX-<.-NIDDTB-1>>,<? Wrong number of entries in NIDDTB>>

NIDILL:	RET

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDOPN - Open portal

;Still in IFN FTNI

;NIDOPN - Open a data link layer port
;
; Call: 
;	T1/ Function (DF.OPN)
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;ON RESOURCE FAILURE
;	RETSKP			;On success with T1 = Line state
;
; Uses: T1-T3

NIDOPN:
;	CALL CHKADR		; Verify that the physical address is DECnet's
;	 RET			; It is not, return error
	CALL GETUNB		; Get a UN block
	 RET			; Return error
	XMOVEI T1,DNDNII	; Where DLL should call us back
	STOR T1,UNCBA,(UN)	; Give the driver tha callback address
	LOAD T1,LNLID,(LN)	; Get the line's ID
	LOAD T1,LIKON,+T1	; Get ethernet channel number
	STOR T1,UNCHN,(UN)	; Store in UN block
	STOR DL,UNUID,(UN)	; Associate DL block with callbacks
	MOVE T1,RTRPRO		; Router's protocol type
	STOR T1,UNPRO,(UN)
	SETONE UNPAD,(UN)	; Padding is to be done
	MOVEI T1,NU.OPN		; Function is  open a port
	MOVE T2,UN		; Address of UN block
	CALL DLLNI		; Call the driver
	 CALLRET FREUNB		;  Open failed!
	LOAD T1,UNPID,(UN)	; Get portal ID
	STOR T1,LNPID,(LN)	; Save for transmits later
	MOVEI T1,LS.OFF		; Assume line's state is off
	TMNE UNRUN,(UN)		; Is channel running?
	 MOVEI T1,LS.ON		; Yes, get proper line state
	STOR T1,LNSTA,(LN)	; Save state of channel

;Now enable the multi-cast address(es) we will use

;Do both if endnode so we get the routing messages

	MOVX T1,%RTRMA		; Multi-cast address "ALL ROUTERS"
	CALL NIFEMA		; Enable us to receive on this address
	 CALLRET FREUNB
	MOVE T2,DNDRNT		; Get Router's node type
	CAIE T2,RNT.NR		; Endnode?
	IFSKP.
	  MOVX T1,%RTEMA	; Yes, then enable to receive endnode multicast
	  CALL NIFEMA
	   CALLRET FREUNB
	ENDIF.

;Now post some buffers for the NI driver

	CALL NIDPRB		; This will post as many as we ask for
				;  or as many as it can
	SETONE DLLIU,(DL)	; Router is using the line
	CALL FREUNB
	LOAD T1,LNSTA,(LN)	; Return line state to Router
	RETSKP

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDCLS - Close portal

;Still in IFN FTNI

;NIDCLS - Close a circuit on an ethernet portal
;
; Call: 
;	DL/ Data link block address
;	LN/ Line tabel block address
;
; Return: 
;	RET			;On error
;	RETSKP			;On success

NIDCLS:	CALL GETUNB		; Get a UN block
	 RET
	LOAD T1,LNPID,(LN)	; Get the portal ID
	STOR T1,UNPID,(UN)	;  and put it in UN block for DLL
	MOVX T1,NU.CLO		; Function is "close portal"
	MOVE T2,UN		; UN block address
	CALL DLLNI
	 CALLRET FREUNB
	SETZRO DLLIU,(DL)	; Indicate Router is no longer using the line
	CALL FREUNB
	RETSKP

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDXMT - Transmit packet

;Still in IFN FTNI

;NIDXMT - Send a packet to NISRV
;
; Call: 
;	T3/ Message block
;	DL/ Data link block address
;	LN/ Line data block address
;
; Return: 
;	RET			;On error from driver
;	RETSKP			;On success

NIDXMT:	SAVEAC <P1,P2>
	DMOVE P1,T3		; Save these from destruction
				; P2 will have the next hop address
	CALL GETUNB
	 RET
	LOAD T1,LNPID,(LN)	; Get the portal ID
	STOR T1,UNPID,(UN)	;  and put it in UN block for DLL
	SETZRO UNBSZ,(UN)	; Byte count is zero for MSD buffers
	LOAD T1,MBFMS,(P1)	; Get address of MSD string
	STOR T1,UNBFA,(UN)
	STOR MB,UNRID,(UN)	; So we can easily recover MB address
	JUMPE P2,NIFXN1		; No nexthop, must be multicast
	MOVE T1,RTRHIO		; Build nexthop ethernet address
	STOR T1,UNDAD,(UN)	; Store the destination address
	STOR P2,UNDAD,+1(UN)	;
	JRST NIFXN2
NIFXN1:	LOAD T1,MBDS1,(MB)	; Destination in MB is the multicast address
	STOR T1,UNDAD,(UN)
	SETZRO UNDAD,+1(UN)	; This is always 0 anyway
NIFXN2:
IFN FTRTST,<
	JE RMTST,(MB),NIFXN3	; Test flag on?
	MOVEI P1,TSTBLK		; Yes, then get current time


  S1XCT <CALL UPDTCK>		; Must be executed in section 1 because of 
				;  RDTIME bug

	STOR T1,TRTTD,(P1)	; Save it for later computation
	LOAD T2,TRTAF,(P1)	;  and then compute the time latency
	SUB T1,T2		;  since we entered
	STOR T1,TRTED,(P1)	;  RTRFWD
NIFXN3:  >
	MOVX T1,NU.XMT		; Function is transmit message
	MOVE T2,UN		;
	CALL DLLNI
	 TRNA			; Fail but release the UN block first
	AOS (P)			; Indicate success
	CALLRET FREUNB		;  and return the UN memory

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDRED - Read parameter
	SUBTTL NIDLL - Ethernet data link layer -- NIDSET - Set parameter
	SUBTTL NIDLL - Ethernet data link layer -- NIDCLR - Clear parameter

;Still in IFN FTNI

	XSWAPCD

NIDRED:	MOVEI T3,NF.RED		; Read parameter
	CALLRET NIDCPF		; Call our common setup for parameters

NIDSET:	MOVEI T3,NF.SET		; Set parameter
	CALLRET NIDCPF		; Call our common setup for parameters

NIDCLR:	MOVEI T3,NF.CLR		; Clear parameter
;	CALLRET NIDCPF		; Call our common setup for parameters

NIDCPF:	STKVAR <FUNC>
	MOVEM T3,FUNC		; Save function (read,set,clear)
	MOVE P1,T2		; NTPARM wants function block here
	LOAD T1,NFETY,(P1)	; Get the entity type
	CAIE T1,.NTCKT		; Is it a circuit?
	IFSKP.
	  LOAD T1,NFEID,(P1)	; Get the entity ID
	  CALL DNDFDL		; Get the correct data link block
	   RNMXND		; Return success but no data
	  LOAD LN,DLLNB,(DL)	; Get line data block address
	  XMOVEI T1,NIDCPT	; Address of circuit parameter table
	  MOVEI T2,NIDCPL	;  And its length
	ELSE.
	  CAIE T1,.NTLIN	; Is it line?
	  IFSKP.
	    LOAD T1,NFEID,(P1)	; Get the entity ID
	    CALL DNDFLN		; Get the line data block for this line
	     RNMXER (NF.MPE)	; We don't support this device
	    XMOVEI T1,NIDLPT	; Address of line parameter table
	    MOVEI T2,NIDLPL	;  and its length
	  ELSE.
	    XMOVEI T1,NIDNPT	; Use Node parameter table
	    MOVEI T2,NIDNPL	;
	  ENDIF.
	ENDIF.
	MOVE T3,FUNC		; Recover requested function
	CALLRET NTPARM		; Call the common parameter processer
				; T1/ table
				; T2/ table's length
				; T3/ function

	XRESCD

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDSHC - Show counters
	SUBTTL NIDLL - Ethernet data link layer -- NIDSZC - Show and zero counters

;Still in IFN FTNI

	XSWAPCD

NIDSHC:	SKIPA T3,[NF.COU]	; Show counters
NIDSZC:	MOVEI T3,NF.SZC		; Show and zero counters
	STKVAR <FUNC,BUFADR>
	MOVEM T3,FUNC		; T3 will be used later
	CALL NIDLKW		; Get the interlock (wait if have too)
	MOVE P1,T2		; Save the NF block pointer
	LOAD T1,NFBLN,(P1)	; Get length of buffer
	AOJ T1,			; Plus one more for flags
	CALL DNGWDS		; Get a buffer of at least that size for NISRV
	 RNMXER (<NF.OPF>,<SETOM NIDLOK>)
	MOVEM T1,BUFADR		; Buffer address
	LOAD T1,NFETY,(P1)	; Get the entity type
	CAIE T1,.NTCKT		; Is it a circuit?
	IFSKP.
	  LOAD T1,NFEID,(P1)	; Get circuit identifier
	  CALL DNDFDL		; Get the data link block block
	  IFNSK.
	    MOVE T1,BUFADR	; Failed so free up the buffer we have
	    CALL DNFWDS
	    RNMXER (<NF.OPF>,<SETOM NIDLOK>)
	  ENDIF.
	  MOVE T1,FUNC		; Recover function
	  MOVE T2,BUFADR	;  Buffer address
	  AOJ T2,		; Step over flags
	  LOAD T3,NFBLN,(P1)	;   and length
	  CALL NIFRPC		; Read the portal counters
	  IFNSK.
	    MOVE T1,BUFADR
	    CALL DNFWDS
	    RNMXER (<NF.OPF>,<SETOM NIDLOK>)
	  ENDIF.
	ELSE.
	  CAIE T1,.NTLIN	; Is entity a line?
	  IFSKP.
	    MOVE T1,FUNC	; Get function (read or read/clear)
	    MOVE T2,BUFADR	;  Buffer address
	    AOJ T2,		; Step over flags
	    LOAD T3,NFBLN,(P1)	;   and length
	    CALL NIFRCC		; Read channel counters
	    IFNSK.
	      MOVE T1,BUFADR	; Error, free the buffer
	      CALL DNFWDS
	      RNMXER (<NF.OPF>,<SETOM NIDLOK>)
	    ENDIF.
	  ELSE.
	    MOVE T1,BUFADR	; Don't need this buffer any more
	    CALL DNFWDS
	    RNMXER (<NF.MPE>,<SETOM NIDLOK>)
	  ENDIF.
	ENDIF.
IFN FTOPS10,<
NIDSC1:	MOVEI T1,1		; Sleep a second
IFE FTXMON,<RGCALL (SLEEPF##)>	; ...
IFN FTXMON,<SNCALL (SLEEPF##,MCSEC0)>
	SKIPN RCCFLG		; Counters available yet?
	JRST NIDSC1		; No, loop back
>; END IFN FTOPS10
IFN FTOPS20,<
	MOVEI T1,NISCHK		; Scheduler test routine (uses only 18 bits)
	MDISMS			; Dismiss this process until NISRV returns data
>; END IFN FTOPS20
	SETOM NIDLOK		; Free the lock
	LOAD T1,NFETY,(P1)	; Get the entity type again ...
	CAIE T1,.NTCKT		; Is it a circuit?
	IFSKP.
	  XMOVEI T1,NIDCCT	; Address of circuit (portal) counter table
	  MOVEI T2,NIDCCL	; and its length
	ELSE.
	  XMOVEI T1,NIDLCT	; Address of line (channel) counter table
	  MOVEI T2,NIDLCL	;  and its length
	ENDIF.
	MOVE P2,BUFADR		; NTCTRS needs the buffer address
	SKIPE (P2)		; Any errors from NISRV?
	 JRST NIFSCE		; Yes..
	MOVE T3,FUNC
	AOJ P2,			; Step over flags
	CALL NTCTRS		; Now copy the data to NTMAN's buffer
	 JRST NIFSCE		; Did not successfully copy data
	MOVE T1,BUFADR		; Data copied, free the buffer
	CALL DNFWDS
	RETSKP

NIFSCE: MOVE T1,BUFADR		; Free the buffer
	CALL DNFWDS
	RNMXER (NF.OPF)		;  and return operation failure

	ENDSV.	

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- Ethernet Parameters

;Still in IFN FTNI

	XSWAPCD

NIDLPT:

PARAMETER (^D0,<NOSET!NOCLR>,,,,,<LOAD T2,LNSTA,(LN)>,,<Line state>)
PARAMETER (^D1105,,^D20,^D5,^D10,<STOR T2,LNBNO,(LN)>,<LOAD T2,LNBNO,(LN)>,<
	STOR T2,LNBNO,(LN)>,<Receive buffers>) ; This works for all but CI
PARAMETER (^D1110,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCON,(LN)>,<TRN>,<
	Controller>)
PARAMETER (^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNPRO,(LN)>,<TRN>,<
	Protocol>)
PARAMETER (^D1160,<NOSET!NOCLR!BEX>,,,,<TRN>,<CALL NMFRHA>,<TRN>,<
	Hardware address>)
PARAMETER(^D2500,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNBSZ,(LN)>,<TRN>,<
	Receive buffer size>)

NIDLPL==.-NIDLPT

NIDCPT:

PARAMETER (^D1112,<NOSET!NOCLR>,,,,<TRN>,<LOAD T2,LNCTY,(LN)>,<TRN>,<
	Circuit type>)

NIDCPL==.-NIDCPT


NIDNPT:
PARAMETER (^D10,<NOSET!NOCLR!BEX>,,,,<TRN>,<CALL NMFRPA>,<TRN>,<
	Physical address>)
NIDNPL==.-NIDNPT

	XRESCD

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- Ethernet counters

;Still in IFN FTNI

	XSWAPCD

NIDLCT:

COUNTER (^D0,^D16,<LOAD T1,CCSLZ,(P2)>,,,<Seconds since last zeroed>)
COUNTER (^D1000,^D32,<LOAD T1,CCBYR,(P2)>,,,<Bytes received>)
COUNTER (^D1001,^D32,<LOAD T1,CCBYX,(P2)>,,,<Bytes sent>)
COUNTER (^D1002,^D32,<LOAD T1,CCMBR,(P2)>,,,<Multicast bytes received>)
COUNTER (^D1010,^D32,<LOAD T1,CCDGR,(P2)>,,,<Data blocks received>)
COUNTER (^D1011,^D32,<LOAD T1,CCDGX,(P2)>,,,<Data blocks sent>)
COUNTER (^D1012,^D32,<LOAD T1,CCMDR,(P2)>,,,<
	Multicast data blocks received>)
COUNTER (^D1013,^D32,<LOAD T1,CCDXD,(P2)>,,,<
	Data blocks sent, initially deferred>)
COUNTER (^D1014,^D32,<LOAD T1,CCDX1,(P2)>,,,<
	Data blocks sent, single collision>)
COUNTER (^D1015,^D32,<LOAD T1,CCDXM,(P2)>,,,<
	Data blocks sent multiple collisions>)
COUNTER (^D1060,^D16,<LOAD T1,CCXMF,(P2)>,,<LOAD T1,CCXFM,(P2)>,<Send failures>)
COUNTER (^D1062,^D16,<LOAD T1,CCRCF,(P2)>,,<LOAD T1,CCRFM,(P2)>,<Receive failures>)
COUNTER (^D1063,^D16,<LOAD T1,CCUFD,(P2)>,,,<Unrecognized frame destination>)
COUNTER (^D1064,^D16,<LOAD T1,CCDOV,(P2)>,,,<Data overrun>)
COUNTER (^D1065,^D16,<LOAD T1,CCSBU,(P2)>,,,<System buffer unavailable>)
COUNTER (^D1066,^D16,<LOAD T1,CCUBU,(P2)>,,,<User buffer unavailable>)

NIDLCL==.-NIDLCT

;Ethenet portal counters

NIDCCT:

COUNTER (^D1000,^D32,<LOAD T1,PCBYR,(P2)>,,,<Bytes received>)
COUNTER (^D1001,^D32,<LOAD T1,PCBYX,(P2)>,,,<Bytes sent>)
COUNTER (^D1010,^D32,<LOAD T1,PCDGR,(P2)>,,,<Data blocks received>)
COUNTER (^D1011,^D32,<LOAD T1,PCDGX,(P2)>,,,<Data blocks sent>)
COUNTER (^D1065,^D16,<LOAD T1,PCUBU,(P2)>,,,<Use buffer unavailable>)

NIDCCL==.-NIDCCT

	XRESCD

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- DLLNI - Call Ethernet driver

;Still in IFN FTNI

	XRESCD

IFN FTOPS20,<
DLLNI:	XCALLRET (MSEC1,DLLUNI)	; Call NISRV wherever he may be
>; END IFN FTOPS20
IFN FTOPS10,<
DLLNI:	SNCALL (ETHSER##,MCSEC1)
	  RET
	RETSKP
>; END IFN FTOPS10

;Still in IFN FTNI
	SUBTTL Network management - Read portal counters

;Still in IFN FTNI

;Call:
;	T1/ Function (read/read and zero)
;	T2/ Buffer for counter data
;	T3/ Buffer length
;	DL/ Data link block

	XSWAPCD

NIFRPC:	STKVAR <FUNC,BUFADR,BUFLEN>
	MOVEM T1,FUNC
	MOVEM T2,BUFADR
	MOVEM T3,BUFLEN
	CALL GETUNB		; Get a UN block
	 RET
	LOAD T1,LNPID,(LN)	; Get the portal ID
	SKIPE T1		; Portal closed?
	IFSKP.
	  SETOM RCCFLG		; Yes, mark function as completed
	  CALLRET NMXGUD	; And return success
	ENDIF.
	STOR T1,UNPID,(UN)	; Store portal id
	STOR T1,UNSID,(UN)	; Portal to read buffers for
	MOVE T1,BUFADR		; Get address of buffer for NISRV
	STOR T1,UNBFA,(UN)
	MOVE T1,BUFLEN		;  and its length
	STOR T1,UNBSZ,(UN)
	MOVE T1,FUNC
	CAIE T1,NF.SZC		; Is function show and zero? 
	IFSKP.
	  SETONE UNZRO,(UN)	; Yes, set the to be zeroed flag
	ENDIF. 
	SETZM RCCFLG		; Clear read/clear complete flag
	MOVX T1,NU.RPC		; Function is read portal counters
	MOVE T2,UN
	CALL DLLNI
	 CALLRET NMXERR		; Failed with error in T1
	CALLRET NMXGUD		; Success - counters loaded into users buffer

	ENDSV.
	XRESCD

;Still in IFN FTNI
	SUBTTL Network management - Read channel counters

;Still in IFN FTNI

;Call:
;	T1/ Function to perform
;	T2/ Counter buffer address
;	T3/ Counter buffer length

	XSWAPCD

NIFRCC:	STKVAR <FUNC,BUFADR,BUFLEN>
	MOVEM T1,FUNC
	MOVEM T2,BUFADR
	MOVEM T3,BUFLEN
	CALL GETUNB		; Get a UN block
	 RET
	CALL NIFOIP		; Get the info portal ID
	 RET			; Can't get it
	STOR T1,UNPID,(UN)
	LOAD T1,NFETY,(P1)	; Get line ID
	LOAD T1,LIKON,+T1	; Get ethernet channel number
	STOR T1,UNSID,(UN)
	MOVE T1,BUFADR		; Get users buffer address
	STOR T1,UNBFA,(UN)
	MOVE T1,BUFLEN		;  and length of buffer
	STOR T1,UNBSZ,(UN)
	MOVE T1,FUNC
	CAIE T1,NF.SZC		; Is function show and zero? 
	IFSKP.
	  SETONE UNZRO,(UN)	; Yes, set the to be zeroed flag
	ENDIF. 
	SETZM RCCFLG		; Clear read/clear complete flag
	MOVX T1,NU.RCC		; Function is read channel counters
	MOVE T2,UN
	CALL DLLNI
	 CALLRET NMXERR		; Failed with error in T1
	CALLRET NMXGUD		; Success - Wait for counters to be loaded

	ENDSV.			;  into data buffer
	XRESCD

;Still in IFN FTNI
	SUBTTL  Network management - Get informational portal ID

;Still in IFN FTNI

;Call:
;	With nothing
;
;Uses:	T1-T4

	XSWAPCD

NIFOIP:	SKIPE T1,INFPID		; Do we have one already?
	 RETSKP			; Return it in T1
	SAVEAC <UN>
	CALL GETUNB
	 RET
	SETO T1,		; Get a -1
	STOR T1,UNPRO,(UN)	; Indicate we want an information portal
	SETZM $UNUID(UN)	; Zero user id
	XMOVEI T1,DNDNII	; Get callback routine address
	STOR T1,UNCBA,(UN)	; Save it
	MOVX T1,NU.OPN		; Open the portal
	MOVE T2,UN		; Get UN block addr into T2
	CALL DLLNI
	 CALLRET FREUNB		;  Sigh...
	LOAD T1,UNPID,(UN)	; Get informational portal ID
	MOVEM T1,INFPID		; Save for posterity
	CALL FREUNB
	MOVE T1,INFPID		; Return portal ID to caller
	RETSKP			; Return PID in T1

	XRESCD

;Still in IFN FTNI
	SUBTTL NIFCIP - Close the information portal

;Still in IFN FTNI

;Call:

NIFCIP:	SKIPN INFPID		; Is there an information portal open?
	 RET			; No
	SAVEAC <UN>
	CALL GETUNB		; Yes, get a UN block
	 RET
	MOVE T1,INFPID		; Get the portal ID for NISRV
	STOR T1,UNPID,(UN)
	MOVX T1,NU.CLO		; Function is "close portal"
	MOVE T2,UN
	CALL DLLNI
	 TRNA
	SETZM INFPID		; No longer have a portal open
	CALLRET FREUNB

;Still in IFN FTNI
	SUBTTL Network management - Read Ethernet addreses

;Still in IFN FTNI

;NMFRHA  - Read hardware address

	XSWAPCD

NMFRHA:	JSP T1,NIFRCI		; Read the address from the driver
	LOAD T1,UNHAD,(UN)	; Get hi-order of address
	LOAD T2,UNHAD,+1(UN)	;  and lo-order
	LOAD T3,NFBUF,(P1)	; Get the buffer address
	MOVEM T1,(T3)		;  and store the hardware address in it
	MOVEM T2,1(T3)
	RET

;Read current physical address

NMFRPA:	JSP T1,NIFRCI		; Read the physical address from the driver
	LOAD T1,UNCAR,(UN)	; Get hi-order of address
	LOAD T2,UNCAR,+1(UN)	;  and lo-order
	LOAD T3,NFBUF,(P1)	; Get the buffer address
	MOVEM T1,(T3)		;  and store the hardware address in it
	MOVEM T2,1(T3)
	RET

	XRESCD

;Still in IFN FTNI
	SUBTTL Network management - Read channel information

;Still in IFN FTNI

;Call:
;	P1/ NF argument block
	XSWAPCD

NIFRCI:	STKVAR <RETADR>
	MOVEM T1,RETADR
	CALL GETUNB		; Get a UN block
	 RET
	LOAD T1,NFETY,(P1)	; Get entity type
	CAIE T1,.NTNOD		; Is it node?
	IFSKP.
	  SETZ T1,		; Use channel 0
	ELSE.
	  LOAD T1,NFEID,(P1)	; Get entity ID
	  LOAD T1,LIKON,+T1	; Get ethernet channel number
	ENDIF.
	STOR T1,UNSID,(UN)
	SETZRO UNBSZ,(UN)	; Indicate no aux buffer
	MOVX T1,NU.RCI		; Function is read channel information
	MOVE T2,UN
	CALL DLLNI		; Call NISRV
	 CALLRET NMXERR		;  Error - T1 contains reason (???) NTMAN?
	MOVE T1,RETADR
	CALL (T1)		; Let caller copy the data he wants
	CALL FREUNB
	MOVX T1,NF.FCS		; Indicate success
				; Can't use NMXGUD here - Must be non-skip
	RET			; Success - Parameter loaded into users buffer

	XRESCD

;Still in IFN FTNI
	SUBTTL Network management - Read counters interlock

;Still in IFN FTNI

;NIDLKW - Interlock routine for process level callers
;
; Call:
;	T1/ Address of processor to call with the interlock
;
; Return:
;	RET
;
; Uses: T1
;
;This version of the interlock will spin on NIDLOK until it is freed.
;This is to be called only from process level, thus it will only spin
;if another process has the interlock.

NIDLKW: AOSN NIDLOK		; Test and set the interlock
	 RET			; Its free, continue processing
IFN FTOPS10,<
	MOVEI T1,1		; Sleep a second
	PUSHJ P,SLEEPF##	; ...
	JRST NIDLKW		; And try again
>; END IFN FTOPS10
IFN FTOPS20,<
	XMOVEI T1,NIDLST	; No, set up scheduler test
	MDISMS			; Wait for competing process to finish
	JRST NIDLKW		; Ok, lets try again

	RESCD
NIDLST:	SKIPL NIDLOK		; Is read counters lock free yet?
	 RET			; No, sleep on
	RETSKP			; Yes, wake up fork
NISCHK:	SKIPL RCCFLG		; Has data been returned yet?
	 RET			; No, sleep on
	RETSKP			; Yes, wake up fork
	XRESCD
>  ;End FTOPS20

NMXGUD:	AOS (P)			; Make a skip return
NMXERR:	SAVEAC <T1>		; Preserve error code if any
	CALL FREUNB
	RET

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIDPRB - Post receive buffers

;Still in IFN FTNI

;Call:
;	DL/ Data link block

NIDPRB:	SAVEAC <UN,MB>
	CALL GETUNB			; Get a fresh UN block
	 RET
	LOAD T1,LNPID,(LN)		; Get the portal ID
	STOR T1,UNPID,(UN)		;  and put it in UN block for DLL
	DO.
	  LOAD T1,LNNBP,(LN)		; Get number posted
	  OPSTR <CAML T1,>,LNBNO,(LN)	; Enough posted?
	   CALLRET FREUNB		; Yes, return
	  LOAD T1,LNBSZ,(LN)		; Get the size to post for this link
	  ADDI T1,%RTEHS		; Add overhead for Ethernet header
	  CALL DNGMSG			; Request a message block
	   CALLRET FREUNB		; None available
	  MOVE MB,T1			; Save message block address
	  STOR T1,UNRID,(UN)		; Store the MB as request ID
	  XMOVEI T1,+UD.MSD(MB)		; Get address of MSD
	  CALL DNF2WG##			; Fetch a two word global byte pointer
	  OPSTR <DMOVEM T1,>,UNBFA,(UN)	; Store buffer address
	  MOVEI T2,UNA.EV		; We are using EXEC virtual memory
	  STOR T2,UNADS,(UN)		; ...
	  LOAD T1,MDALL,+UD.MSD(MB)	; Get allocated length
	  STOR T1,UNBSZ,(UN)		; So the port knows what it can swallow
	  MOVEI T1,NU.RCV		; Function is post receive buffer
	  MOVE T2,UN			; Get address of UN block
	  CALL DLLNI			; Attempt to post the buffer
	   JRST [MOVE T1,MB		; Get message block address
	         CALL DNFMSG		; Free message block we couldn't post
	         CALLRET FREUNB]	;  and return
	  INCR LNNBP,(LN)		; Account for buffer posted
	  LOOP.				; Loop back
	ENDDO.

;Still in IFN FTNI
	SUBTTL NIFEMA - Enable multicast address

;Still in IFN FTNI

;Call:	T1/ Address to enable
;	UN/ UN block to use

NIFEMA:	STOR T1,UNDAD,(UN)	; Save the address for the port
	SETZRO UNDAD,+1(UN)	; Node address field is zero
	MOVX T1,NU.EMA		; Enable this address
	MOVE T2,UN
	CALLRET DLLNI

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- DNDNII - Ethernet driver callback

;Still in IFN FTNI

;DNDNII - Interrupt from NISRV
;
;Call:
;	T1/ Function code (NU.xxx)
;	T2/ UN block address

DNDNII:	CAXL T1,NU.OPN
	 CAXLE T1,NU.MAX
	BUG. (CHK,DNDIKF,DNADLL,SOFT,<Illegal function code from DLL kontroller>,,<
	This BUG is not documented yet.
>,RTN)

	SAVEAC <DL,LN,UN,P1,MB>
	MOVE UN,T2		; UN block address
	SKIPN DL,$UNUID(UN)	; From that get the data link block
	TDZA LN,LN		; If none, zero LN and skip
	LOAD LN,DLLNB,(DL)	; Point to line data block address
	MOVE T1,NIIDLI(T1)
	CALLRET (T1)		; Dispatch to correct routine

NIIDLI:	IFIW	<NIIILL&777777>	; Unused
	IFIW	<NIIILL&777777>	; Open portal (unused)
	IFIW	<NIICLS&777777>	; Close a Portal Callback
	IFIW	<NIIRCV&777777>	; Datagram Received Callback
	IFIW	<NIIXMT&777777>	; Datagram Sent Callback
	IFIW	<NIIILL&777777>	; Enable Multicast Callback
	IFIW	<NIIILL&777777>	; Disable multicast callback

	IFIW	<NIIILL&777777>	; Read channel list
	IFIW	<NIIRCI&777777>	; Read channel information
	IFIW	<NIIRCC&777777>	; Read channel counters
	IFIW	<NIISCA&777777>	; Set channel address

	IFIW	<NIIILL&777777>	; Read portal list
	IFIW	<NIIILL&777777>	; Read portal information
	IFIW	<NIIRPC&777777>	; Read portal counters

	IFIW	<NIIILL&777777>	; Read kontroller list
	IFIW	<NIIILL&777777>	; Read kontroller information
	IFIW	<NIIILL&777777>	; Read kontroller counters

NIIILL:	RET

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIICLS - Close done

;Still in IFN FTNI

;Call
;	UN/ UN block from driver
;	DL/ Address of DL block
;	LN/ Address of line block

NIICLS:	JUMPE LN,RTN			; Return if information portal
	SETZRO LNPID,(LN)		; Clear portal id
	RET				; And return

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIIRCV - Receive done

;Still in IFN FTNI

;Call
;	UN/ UN block returned by NISRV

NIIRCV:	DECR LNNBP,(LN)			; Another buffer used up
;** BUGCHK??
	LOAD MB,UNRID,(UN)		; Set up MB
	SKIPE T3			; Any errors?
	 JRST NIIRCE			; Yes, log event and return message
	LOAD T1,UNSAD,(UN)		; Get the high order source address
	STOR T1,MBSR1,(MB)		; Put it into the MB
	SETZ T1,
	LOAD T2,UNSAD,+1(UN)		; Get the low order (node address) in
	LSHC T1,^D8			;  string format.  Shift nn-2 into T1
	LSH T2,-^D20			; Right justify the area+2
	IOR T1,T2			;  and include the number
	STOR T1,MBSRC,(MB)		; Stuff it into the MB
	OPSTR <DMOVE T1,>,UNBFA,(UN)	; Fetch two word global byte pointer
	XMOVEI T3,+UD.MSD(MB)		; Get address of MSD
	CALL DNSBP##			; Store the byte pointer into the MSD
	LOAD T1,UNBSZ,(UN)		; Get the length received
	STOR T1,MDBYT,+UD.MSD(MB)	; Save as bytes written
	MOVEI T1,DI.INC			; Function is input complete
	MOVE T3,MB			; 
	LOAD T4,UNDAD,(UN)		; So Router knows destination multi-
					;  cast if running as endnode
	CALL DNDQUE			; Queue up the buffer for jiffy level
	CALLRET NIDPRB			; Post another buffer if possible

NIIRCE:	CAIE T3,UNLER%			; Length error?
	IFSKP.
	  MOVEI T1,DE%RCF		; Yes, say receive failed
	  CALL NIEVNT			; See if we need to log an event
	ENDIF.
;	CAIE T3,UNRAB%			; Buffer being returned because port	
					;  is shutting down?
; Remove next ; when NISRV etc is fixed
;	 CALL NIDPRB			; Post another buffer
	MOVE T1,MB			; Get message block address
	CALLRET DNFMSG			;  and return to free list

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIIXMT - Transmit done

;Still in IFN FTNI

NIIXMT:	SKIPN T3			; Any errors?
	IFSKP.
	  MOVEI T1,DE%XMF		; Transmit failed
	  CALL NIEVNT			; See if we need to log an event
	ENDIF.
	MOVEI T1,DI.ODN			; Function is Output Done
	LOAD T3,UNRID,(UN)		; Address of message block
	CALL DNDQUE			; Queue it up
	RET

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIIRPC - Read portal counters

;Still in IFN FTNI

NIIRPC:	SETOM RCCFLG		; Read counters is complete
	LOAD T1,UNBFA,(T2)	; Get buffer address
	MOVEM T3,-1(T1)		; Store flags ahead of data
	RET

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIIRCI - Read channel info

;Still in IFN FTNI

;Call:
;	UN/ UN block
;	DL/ DL block
;	LN/ Line data block address

NIIRCI:	MOVEI T3,LS.OFF		; Assume state is off
	TMNE UNRUN,(UN)		; Is new state "running"?
	 MOVEI T3,LS.ON		; Yes, change to on state
	LOAD T1,LNSTA,(LN)	; Get current state
	CAIN T1,(T3)		; Any change?
	 RET			; No, then done
	STOR T3,LNSTA,(LN)	; Save new state
	TMNN DLLIU,(DL)		; Is Router using this line?
	IFSKP.
	  CAIN T3,LS.ON	 	; Yes, is new state "running"?
	   CALL NIDPRB		; Yes, try to post buffers	  
	  MOVEI T1,DI.LSC	; Notify ROUTER for either state
	  CALL DNDQUE
	ENDIF.
	RET

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIISCA - Set channel address

;Still in IFN FTNI

;Call:
;	DL/ Data link block (if any)
;	LN/ Line data block address
;	UN/ UN block address


NIISCA:	LOAD T3,UNCAR,(UN)	; Get hi-order of new address
	LOAD T4,UNCAR,+1(UN)	; Get low order address
	DMOVEM T3,DCNNIA	; Save address
	JUMPE DL,RTN		; If no data link block then don't check for
				;  DECnet address
	TMNN DLLIU,(DL)		; Is Router using this data link?
	 RET			; No, not to worry then
	CAMN T3,RTRHIO		; YES, is the address still DECnet's?
	 CAME T4,RTRLOO		; Is it our local address?
	IFSKP.
	  TMNE LNCAD,(LN)	; Was it DECnet before?
	   RET			; Yes, then done
	  SETONE LNCAD,(LN)	; Indicate channel address is DECnet	  
	  MOVX T3,LS.ON		; Signal a line state change to on
	  MOVX T1,DI.LSC
	  CALL DNDQUE		; Notify Router
	ELSE.
	  TMNN LNCAD,(LN)	; Was it DECnet before?
	   RET			; No, then done
	  SETZRO LNCAD,(LN)	; Channel address is not DECnet's
	  MOVEI T1,DI.LSC	; Yes, notify ROUTER
	  MOVX T3,LS.OFF	; Report new line state is off
	  CALL DNDQUE
	ENDIF.
	RET

;Still in IFN FTNI
	SUBTTL NIDLL - Ethernet data link layer -- NIIRCC - Read channel counters

;Still in IFN FTNI

NIIRCC:	SETOM RCCFLG		; Read couters is complete
	LOAD T1,UNBFA,(T2)	; Get buffer address
	MOVEM T3,-1(T1)		; Store flags ahead of data
	CALLRET NIFCIP		; Close information portal and return


;Still in IFN FTNI
	SUBTTL - Ethernet event reporter

;Still in IFN FTNI

;Call:
;	T1/ Type
;	T3/ Reason
;	DL/ data link block
;

NIEVNT:	STKVAR <EVNTYP>
	MOVEM T1,EVNTYP
	MOVNI T2,NIETBL		; Length of event table
	HRLZ T2,T2
NIEVN1:	HLRZ T1,NIEVTB(T2)	; Get reason code
	CAIN T1,(T3)		; Is this the one?
	 JRST NIEVN2		; Yes, try to log the event
	AOBJN T2,NIEVN1		; No, try next
	RET
NIEVN2:	MOVE T1,EVNTYP		; Get type
	HRRZ T3,NIEVTB(T2)	; Get event reason
	LOAD T2,DLDID,(DL)	; Get entity ID
	CALLRET RTNEVT

;Data link layer events

DE%XMF==^D14		; Transmit failed
DE%RCF==^D15		; Receive failed

NIEVTB:	XWD UNEXC%,^D0		; Excessive collisions
	XWD UNCCF%,^D1		; Carrier check failed
	XWD UNSHT%,^D3		; Short circuit
	XWD UNOPN%,^D4		; Open circuit
	XWD UNLER%,^D5		; Length error (frame too long)
	XWD UNRFD%,^D6		; Remote failure to defer
NIETBL=.-NIEVTB

;Still in IFN FTNI
	SUBTTL Network Management Interface -- RTNEVT - Event Reporter

;Still in IFN FTNI

;RTNEVT - Report Router event
;
; Call:
;	T1/ Event type
;	T2/ Entity ID (we know the entity type from DNETAB)
;	T3/ Event specific word (REASON, STATUS, etc.)
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T4
;
;This calls something which will call network management to log this event
;which has taken place.  This routine is called by the EVENT macro.

	 EVTMLN==^D15		;Maximum length of event parameter data (bytes)

RTNEVT:	SAVEAC <P1,P2>
	DMOVE P1,T1
	MOVEM T3,DNDEVW		; Save the event argument

;Check if event should be thrown away

	MOVX T1,.NCDLL		; DNADLL event class
	STOR T1,FACCL,+T2
	STOR P1,FACTY,+T2	;  and event type to T2
	MOVX T1,EV.FIL		; Function code "filter event"
	CALL NMXEVT
	  RET			;   -throw it away
	SKIPN DNDECP		; Verify that EC pointer is non-zero
	  RET			;   -was zero, must have failed to initialize
; Continued...

;Still in IFN FTNI
;Still in IFN FTNI

;Ok, to log an event

	MOVX T1,NE.LEN+<<EVTMLN+3>/4> ;Get enough for arg block and
				; Maximum amount of event parameter data
	CALL DNGWDZ		;Get the words
	 BUG.(CHK,DNDCGV,DNADLL,SOFT,<Couldn't get memory for event arg block>,,<

Cause:	This BUG is not documented yet.

>,RTN)
	STOR P1,NECTY,(T1)	; Put the event type in the arg block
	STOR P2,NEEID,(T1)	; Put entity-id in event block
	MOVE P1,T1		; Save the pointer to the block
	MOVE T1,DNDECP		; Get EC pointer
	STOR T1,NEECP,(P1)	;  and store it in NE block
	MOVX T1,.NTLIN		; Get our entity type
	STOR T1,NEETP,(P1)
	MOVX T1,.NCDLL		; The event class is data link
	STOR T1,NECCL,(P1)	; Put in ne arg block
	XMOVEI T1,NE.LEN(P1)	; Make a fullword pointer to data
	STOR T1,NEDAT,(P1)	; Store pointer to it in arg block
	MOVE P2,[POINT 8,NE.LEN(P1)] ; Make up byte pointer to parameter data
	SETZ T4,		; Intialize count of bytes written
	CALL DNDRFR		; Write parameter strings into the data area
	STOR T4,NEDLN,(P1)	; Save count of bytes actually written
	MOVX T1,EV.LOG		; Function code is "log an event"
	MOVE T2,P1		;  NE pointer in T2
	CALL NMXEVT		; Call the event processor
	 TRN
	RET			; Return, NTMAN will deallocate NE block

DNDRFR:	MOVEI T1,^D16		; Parameter number - Failure reason
	MOVEI T2,2		; Get number of bytes
	CALL PUTNBT		; Insert the bytes swapped
	MOVEI T1,201		; Coded, single field, length 1 byte
	CALL PUTBYT		; Stuff it
	MOVE T1,DNDEVW		; Get the reason
	CALLRET PUTBYT		; Insert it and return

;Still in IFN FTNI
;Still in IFN FTNI

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

PUTNBT:	JUMPE T2,RTN		; Return if nothing left
	IDPB T1,P2		; Put the byte in the message
	LSH T1,-^D8		; Shift over to the next byte
	ADDI T4,1		; Update the count
	SOJA T2,PUTNBT		; Do the rest

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

PUTBYT:	IDPB T1,P2		; Install the byte
	AOJA T4,RTN		; Increment the number of bytes

;Still in IFN FTNI
	SUBTTL Miscellaneous routines

;Still in IFN FTNI

;GETUNB  - Get a UN block

GETUNB:	SYSPIF			; No interrupts please
	SKIPN UN,DNDUNQ		; Any free blocks on the queue?
	IFSKP.
	  MOVE T1,(UN)		; Yes, take the first one
	  MOVEM T1,DNDUNQ
	  SOS DNDUQL		;  and account for it
	  SYSPIN
	ELSE.
	  SYSPIN
	  MOVX T1,UN.LEN	; Get length of user NI block
	  CALL DNGWDS		; Try to get the words
	   RET			; Can't, return error
	  MOVE UN,T1		; Get address of UN block
	ENDIF.
	MOVEI T1,UN.LEN-1	; Clear UN block
	MOVE T2,UN		; ...
	XMOVEI T3,1(T2)		; ...
	SETZM (UN)		; Zero first word
	CALL XBLTA##		; Zero remainder
	RETSKP			; Return address in UN

;Free a UN block.
;This means put it on the queue of free UN blocks or return it to the
; the free pool if the queue is at the desired maximum.

FREUNB:	MOVE T1,DNDUQL		; Get the length of the UN block queue
	CAIG T1,6		; Is it greater than the desired maximum?
	IFSKP.
	  MOVE T1,UN		; Yes, the free the block
	  CALL DNFWDS
	ELSE.
	  SYSPIF
	  MOVE T1,DNDUNQ	; No, get the queue head
	  MOVEM T1,(UN)	 	;  and put this one at the head
	  MOVEM UN,DNDUNQ
	  AOS DNDUQL		; Add it into the count
	  SYSPIN
	ENDIF.
	RET

;Still in IFN FTNI
	SUBTTL	Miscellaneous routines -- Check Ethernet address

;Still in IFN FTNI

CHKADR:	DMOVE T1,DCNNIA		; Get the current address
	CAMN T1,RTRHIO		;  and see if it is DECnet's
	CAME T2,RTRLOO
	 RET			; No, then non-skip
	RETSKP

>; END IFN FTNI
	SUBTTL	Local storage for DNADLL

	RESDT

DNDINQ:	BLOCK QH.LEN		; Queue of interrupt callback functions
DLBQUE:	BLOCK QH.LEN		; Queue of data link blocks
LNBQUE:	BLOCK QH.LEN		; Queue of line data blocks
DNDUNQ:	BLOCK 1			; Queue of free UN blocks
DNDUQL:	BLOCK 1			; Length of UN block queue
DNDQBQ:	BLOCK 1			; Queue of free function blocks
DNDQBL:	BLOCK 1			; Length of QB queue
DNDRNT:	BLOCK 1			; Router's node type
RCCFLG:	EXP -1			; Read couter complete flag for NIDLL
NIDLOK:	EXP -1			; Interlock so only one counter reader
NIDLKO:	BLOCK 1			; Owner of interlock
DNDEVW:	BLOCK 1			; Event reason
DNDECP:	EXP 0			; Event communication pointer
INFPID:	EXP 0			; Informational portal ID
DCNNIA: BLOCK 2			; DECnet Ethernet address
	XRESCD

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