Google
 

Trailing-Edge - PDP-10 Archives - tops10_tools_bb-fp64a-sb - 10,7/psthru/psthru.mac
There are 5 other files named psthru.mac in the archive. Click here to see a list.
	Title	PSTHRU - Program to do DECnet Phase III pass-through
	Subttl	General comments

Comment	&
;+
;.fig 20
;.center;PSTHRU.PLM - Documentation for PSTHRU
;.page
;Copyright (C) 1982
;.br
;Digital Equipment Corporation, Maynard, Massachusetts, U.S.A.
;.bl 
;This software is furnished under a license and may be used and copied only
;in accordance with the terms of such license and with the inclusion of the
;above copyright notice.  This software or any other copies thereof may not
;be provided or otherwise made available to any other person.   No title to
;and ownership of the software is hereby transferred.
;.bl 
;The information in this software is subject to change  without  notice and
;should not be construed as a commitment by Digital Equipment Corporation.
;.bl 
;Digital  assumes  no  responsibility  for  the  use  or reliability of its
;software on equipment which is not supplied by Digital.
;.page
;.title PSTHRU.PLM
;.subtitle General information
;.autop
;.lm 10
;.rm 70
;I.  General information
;	PSTHRU is a program which allows connects to be made from
;any accessable DECnet-10 or ANF-10 node through the host node to
;any other DECnet-10 or ANF-10 node which is visible to the host
;node.  The links need not be homogenous.
;	PSTHRU runs completely asynchronously and handles all incoming
;connects to the PSTHRU object (object 123 decimal for DECnet-10 or
;173 octal for ANF-10).  Only one job need be run per installation unless
;link quotas are such that one job cannot handle the required number of
;links.  One channel (of the appropriate type) is required for each side
;of a link (total of two channels per link).
;	PSTHRU should be started up via SYSJOB.INI.  If it is not logged
;in, then it will log in to [2,5].  It is recommended that it be run
;under [1,2], although no privileges are required unless the log file
;(see below) is to be written to an area to which only [1,2] has file
;access.
;	If the PATHological name PSTLOG exits and logging support is
;enabled, PSTHRU will log all connections, rejections, failures, and
;disconnects in the specified file.  If the specification is undefined
;and logging is enabled, then one message will be sent to ORION
;stating PSTHRU's inability to set logging.
;	PSTHRU is written to run under TOPS-10 7.02 monitors.
;DECnet support is required only if the DECnet interface is desired.
;The MACSYM universal file is required for the BEGSTR/ENDSTR macros.
;	Known restrictions:
;.lm 15
;.list 1
;.le
;ANF-10 supports only 32-bit modes (byte mode; I/O mode 3).  This
;is true both for heterogenous and homogenous connects.  This restriction
;is to provide a common "known" mode for communicating through
;the PSTHRU task to maintain correct resolution and byte counts.
;.le
;ANF-10 links take precedence over DECnet links when ambiguity
;is present (i.e. the same node name in both networks and the
;requested connection object exists on the ANF node).
;.le
;Error messages are always a la' DECnet style, even if DECnet
;support is not build in.
;.end list
;.lm 10
;II.  Assembly instructions
;.subtitle Assembly instructions
;	Feature tests:
;.lm 15
;.list 1
;.le
;FTDECNET:  if on (-1), include DECnet-10 support code.  If off (0)
;do not.  One of FTDECNET or FTANF10 must be on.
;.le
;FTANF10:  if on (-1) include ANF-10 support code.  If off (0),
;do not.  One of FTDECNET or FTANF10 must be on.
;.le
;FTDEBUG:  if on (-1) include a few extra consistency checks.
;.le
;FTLOG:  if on (-1) include support for producing log files.
;.le
;FTDETACH:  if on (-1), then DETach from the terminal on which
;the program is started.  This feature should be turned off only
;for debugging purposes.
;.end list
;.lm 10
;The default list of feature tests is to have FTDECNET, FTANF10,
;FTDETACH, and FTLOG all turned on and FTDEBUG turned off.
;	Required files:
;.lit
;
;	PSTHRU.MAC
;	UUOSYM.UNV
;	MACTEN.UNV
;	MACSYM.UNV
;	NETPRM.UNV
;	JOBDAT.UNV
;.end lit
;	Assembly instructions:
;.lit
;
;	.LOAD PSTHRU
;	.SSAVE
;.end lit
;-
&
	Subttl	Definitions/KBY

	Search	JOBDAT,UUOSYM,MACTEN,MACSYM,NETPRM
	TWOSEG	400000
	RELOC	400000
	.TEXT	^/LOCAL/SYMSEG:HIGH^
	SALL

;AC definitions:

F=0			;flags AC
			;LH: Link flags RH:  Global flags
T1=1			;Temp ACs
T2=2
T3=3
T4=4
C=5			;AC for character transfers
B=6			;Points to current psthru control block
S=7			;Current status of this link
P=17			;PDL pointer


;Fixed channels:

LOG==1			;Log output channel (if logging)
	Subttl	Feature tests

	ND	FTDECNET,-1
	ND	FTANF10,-1

	ND	FTDEBUG,0			;Debugging code

	ND	FTDETACH,-1			;Include DETACH code
	ND	FTLOG,-1			;Logging code

	IFE	FTANF10!FTDECNET,<
		PASS2
		PRINTX	?Can't set both FTANF10 and FTDECNET to zero
		END
	>
	Subttl	Miscellaneous definitions


	PDLSIZ==200				;PDL size
	ARRAY	PDL[PDLSIZ]

	ND	BUFNUM,6			;Number of initial buffers
	ND	BUFSIZ,200			;Buffer data size
	ND	QUEMAX,20			;Maximum write queue size
						;(See documentation below)
	ND	MAXRED,10			;Maximum number of reads to do
						;at a time
	ND	QUMLEN,^D132			;Max # of chars in error message
;+
;	There are a few additional assembly parameters which may also
;be changed:
;.lm 15
;.list 1
;.le
;BUFNUM (default=6):  This is the number of message buffers built at
;initialization time.  The list will be expanded as is necessary, but
;buffers are never returned to the free core list (only to the free buffer
;list).
;.le
;BUFSIZ (default=100):  This is the data size of a buffer for input.  Messages
;larger than this will be fragmented.  This is the size of the data portion
;of the buffer only and does not include any overhead words (currently
;there are three additional words).
;.le
;QUEMAX (default=20):  This parameter dictates the maximum number of buffers which may be
;queued up for writing on one side of a link.  The absolute maximum number
;is also governed by the MAXRED parameter and is equal to QUEMAX+MAXRED.
;At this point no data available interrupts will be acknowledged on the
;read side of the link until the write side's queue is empty.  Note that
;this restriction is held only as long as the link remains in
;RN state.  This is so that if the read side of the link is just
;slow and the write side is trying to disconnect, it will be able to.
;.le
;MAXRED (default=10):  This is the maximum number of buffers which will
;be read on a read request if infinited data were available.  See the
;documentation on QUEMAX for other effects of this parameter.
;.le
;QUMLEN (default=132):  This is the maximum length of any error message
;which will be sent to the operator.
;.end list
;.lm 10
;-

;Flag bits (Right half of F):

	FL$PI==1				;PI system is on
	FL$P2==2				;Part 2 of the core de-allocator
	FL$LOG==400000				;Logging enabled

;Version information

	VMAJOR==1				;Version 1
	VMINOR==0				;Minor version
	VEDIT==24				;Edit level

	LOC	.JBVER
	BYTE	(3)0(9)VMAJOR(6)VMINOR(18)VEDIT
	RELOC
	Subttl	Edit history
;+
;.page
;.subtitle Edit history
;.lm 10
;III.  Edit history
;.list 1
;.le
;(1) Design and implement original features.
;.le
;(2) Fix core allocation bug for one word
;.le
;(3) Fix bug where a NSP returns no data, but there is more to be read.
;This could cause a hang after such.
;.le
;(4) Fix NPDSTR:  doing a HLRI instead of a HRLI and CPYOBJ doing a
;HRLI T3,(POINT 8,,) instead of HRLI T3,(POINT 8,,35).  Symptom:
;causes connections to be made with junk object names (format type 1).
;.le
;(5) Change dump area to XPN:
;.le
;(6) Change default buffer size from 100 to 200 (octal)
;.le
;(7) Update for new DNET. UUO.
;.le
;(10) Make DECnet Send/Read Normal/Interrupt ABORT stopcodes instead of INFO.
;Make ABORT stopcodes work better.
;.le
;(11) Range check the size of fields in access control information
;.le
;(12) Make sure there's a <NUL> at the end of the string for the QUEUE. UUO
;so that the message sent to ORION doesn't have old garbage in it.
;Clean up DIE; remove a HALT that can't be executed and zero out
;the PPN and access stuff field of the LOOKUP block (which are the PPN
;and reserved fields of the SAVE. block) before each LOOKUP.
;.le
;(13) Edit 11 wasn't complete - changing SOSLE to SOSL causes a bit spray
;because a core block is allocated and pointed to before we decide
;the string is terminated.  Change it back to a SOSLE and then check
;to be sure the last character is a quote, eating one more and checking
;it if it is not.
;.le
;(14) Make NPD object numbers read octal instead of decimal.
;.le
;(15) Don't assume "last link" just because access control info is given;
;this allows users to specify access control info for the PSTHRU they
;want to connect to (it will have meaning for DECnet at least).
;.le
;(16) ANFEOF must clear T2 if it doesn't call ANFRD so we won't think
;there are any buffers to deallocate.
;.le
;(17) Restore ACs before dispatching to type-specific DIE processor
;(So ABT has the right ACs).  Don't restore on return, it is up to the
;type-specific processor to preserve those ACs it wants preserved.
;.le
;(20) Edit 5 incomplete - check on XPN when looking for new file name
;to dump on.
;.le
;(21) Make source NPD for ANF be the same as FAL:  Use "*" to fill
;in unused fields.
;.le
;(22) Change "Can't Put TSK_. Link into Idle State into a NODUMP
;STOPCD from an INFO STOPCD.  It happens too often and usually means
;nothing.
;.le
;(23) Make DCNDR not call DCNRD if aborting link; we probably can't
;read anything anyway.  Gives PDL overflow from recursive ABT stopcodes
;.le
;(24) Make SETCNB set format type 1 in the source descriptor PDB it sets up
;(this is only for passive connections and READ CONNECT INFO), to comply
;with pending DECnet changes.
;.end list
;-
	Subttl	Architecture Philosophies

Comment	&
;+
;.page
;.lm 10
;IV.  Architecture philosophies
;.subtitle Architecture philosphies
;	This section deals with the various implementation decisions
;that were made in creating the PSTHRU interface.
;	As was mentioned earlier, all connections are made in 32-bit mode.
;This decision was made to provide (also as mentioned earlier) a standard
;connect mode which would maintain proper resolution and byte size.  As you
;might also guess, it's due to ease of programming due to the DECnet interface
;which is byte oriented.
;	ANF connections take precedence over DECnet connections:  this
;is in conformance with the behaviour of SET HOSTES.
;	Note also that PSTHRU declares itself to the next succeeding
;part of the link as whatever it was connected to with.  This is in
;conformance to the behaviour of PSTHRU objects on other systems.
;	Below is a quote from the NFT program, reproduced here for your
;edification, which describes the philosophy of the ANF interface:
;.lm 15
;	As ANF-10 basically doesn't support any of the "wonderous" errata of
;DECnet such as user-id, password, ad nauseum, the TSK. monitor call
;was invented to give the user fuller control over the network logical
;link. In particular, in conjunction with versions 3 and later of the
;DECnet Compatible Port (in the DN8x), the "network process descriptor"
;was made available to the "user" in order to encode all that stuff in
;such a way that the DCP can convert it into something the DECnet side
;understood. The result is a very arcane NPD string (for which honesty
;requires that I not claim any credit) given to the TSK. in the active
;and passive init functions. The form of that NPD string, for the first
;time anywhere ever put in writing, is:
;.lit
;	<0>		;leading null byte
;	DST		;"destination" process identifier
;	<0>		;punctuation byte
;	SRC		;"source" process identifier
;	<0>		;punctuation byte
;	USERID		;user id string
;	<0>		;punctuation byte
;	PASSWORD	;password string for user id
;	<0>		;punctuation byte
;	ACCOUNT		;account string for user id
;	<0>		;punctuation byte
;	OPTDATA		;"optional user data" a la DECnet connect init
;	<0>		;punctuation byte
;
;.end lit
;DST and SRC both are one of the following forms:
;.lit
;
;	OBJ		;format 0: DECnet object type
;	OBJ.NAM		;format 1: DECnet object type and task name
;			;	   note the "." punctuation character
;
;.end lit
;OBJ is the object type as an ASCII octal number - e.g., "21" for DAP.
;NAM, USERID, PASSWORD, ACCOUNT, and OPTDATA are all "ASCII" character
;strings - i.e., 7-bit bytes (with no nulls).
;.lm 10
;.page

;-
&
	Subttl	Macros

	DEFINE	ASSUME	(SYM,VAL)<
	IF2,<
	IFN	<<SYM>-<VAL>>,<
	PRINTX	?Assumption wrong:  'SYM' not equal 'VAL'
	>
	>
>

	DEFINE	FALL	(ADDR)<
	IF2,<
	IFN	<.-ADDR>,<
	PRINTX	?Cannot FALL into 'ADDR'
	>
	>
>

	DEFINE	PRSERR	(MESSAGE,ADDR)<
	PUSHJ	P,ADDR
	CAI	[ASCIZ/'MESSAGE'/]
>

	DEFINE	STOPCD	(TYPE,MESSAGE)<
	IFNDEF	STOP,<
;STOPCD types:

	STOP==0				;Stop the PSTHRU process (don't reload)
	RELOAD==1			;Reload
	ABORT==2			;Abort the link
	INFO==3				;Just type a message on the CTY
	NODUMP==4			;Just type message, don't even dump
>
	PUSHJ	P,DIE
	CAI	TYPE,[ASCIZ\'MESSAGE'\]
>
;+
;.lm 10
;V.  Program messages and stop codes
;.subtitle Program messages and stop codes
;	PSTHRU is built to notify the operator about various problems
;which can occur.  These are listed below in detail.
;	In general, most messages are accompanied by a STOPCD dump.
;The program dumps on its default path (usually [1,2]).  The name of
;the first dump file is PSDMP_.EXE.  If creating a new dump would
;overwrite a previous dump, then the last SIXBIT character of the name
;will be incremented by one until a unique name is found.  The increment
;process will continue through the next high order letters if that is
;necessary.  Thus, the second dump to be written would have the name
;PSDMP_!_.EXE.  Each STOPCD dump is accompanied by a message to the
;operator which will also appear in ORION's log file.
;	If any UUO necessary for the dump fails during stopcode
;processing, then the program will halt and have to be dumped and
;restarted manually.
;	Five types of STOPCDs are recognized:
;.lm 15
;.list 1
;.le
;STOP - The problem is a serious system failure and the program will
;not continue.  This usually occurs if some UUO fails which never should.
;The program will not try to reload itself due to the fact that the
;failure probably won't go away.
;.le
;RELOAD - A serious internal error occured, but it is probably the
;fault of the PSTHRU program itself.  The program will reload, which
;will cause all existing links to be aborted.
;.le
;ABORT - The current link in which this problem occured will be aborted.
;PSTHRU continues after the link is aborted.
;.le
;INFO - Something happened which may be worth knowing about, but is
;probably not serious.  PSTHRU continues from the dump point as
;if nothing happened.
;.le
;NODUMP - Something happened which may be worth knowing about, but it's
;not even serious enough to warrant a dump file.  PSTHRU continues normally.
;.end list
;.lm 10
;The following is a list of the stopcodes which can occur.
;.list 1
;-
	Subttl	Psthru control block
;definitions for a psthru control block
;Prefix:  PC

	BEGSTR	PC

		HWORD	LNK		;pointer to other link block
		HWORD	NXT		;pointer to next block on chain

		FIELD	FLG,^D18
			BIT	ANF	;flags word:  this is an ANF link
			BIT	DCN	;  "     " :  this is a DECnet link
			BIT	PW	;this link is waiting for the PSTHRU msg
			BIT	LL	;This is the last link
			BIT	CW	;This link waiting for connect confirm
			BIT	STF	;Suspended transfer in progress
			BIT	AL	;Abort this link (when I/O done)
			BIT	STP	;STOP input on this link when in RN
			BIT	INI	;This side initiated the link (for logging)
			BIT	MAB	;Monitor abort:  catastrophic error
					;is causing link to abort
		HWORD	CDT		;Pointer to connect/disconnect data

		HWORD	BUF		;Buffer in use by this PCB
		HWORD	SIZ		;size of this PCB

		HWORD	POL		;Next PCB in the poll queue
		HWORD	CNT		;Count of characters of buffer in BUF

		HWORD	IDQ		;Interrupt data queue

		WORD	BPT		;Save of byte pointer of current buffer

		ASSUME	.NSAA3,.TKAA3
		WORD	ARG,.NSAA3+1	;arg blocks for NSP. and TSK.

		WORD	BCO,3		;Output buffer control block for TSK.

		WORD	BCI,3		;Input buffer control block for TSK.

		WORD	NOD		;Node name (SIXBIT)


	ENDSTR
	Subttl	Buffer Block

	BEGSTR	BF
		HWORD	LNK		;Link to next buffer block
		FIELD	CNT,18		;Count field
			BIT EOM		;Sign bit is eom bit

		WORD	BPT		;Byte pointer (used for TSK.)

		WORD	BKP		;Bookeeping (used for TSK.)

		WORD	DAT,BUFSIZ	;The Data in the buffer
	ENDSTR	BF
	Subttl	Initialization

PSTHRU:	JFCL
	RESET
	MOVE	P,[IOWD PDLSIZ,PDL]
	MOVE	T1,[-LGNLEN,,LGNARG]
	LOGIN	T1,
	  JFCL				;Oh well
	MOVE	T1,[.STPRV,,T2]
	MOVEI	T2,.STCCW
	SETZ	T3,
	SETUUO	T1,			;Clear capabilities
	  JFCL
IFN	FTANF10,<
	MOVEI	T3,.GTLOC
	GETTAB	T3,
	SETZ	T3,
	MOVEM	T3,MYNODE
	MOVE	T1,[.NDRNN,,T2]
	MOVEI	T2,2
	NODE.	T1,
	  SETZ	T1,
	MOVEM	T1,ANFNAM		;Save our ANF name
>
IFN	FTDECNET,<
	MOVEI	T1,DCNBLK
	DNET.	T1,
	  SETZM	DCNNAM
>
	MOVEI	F,FL$LOG		;Assume we're logging
IFN	FTDETACH,<
	MOVSI	T1,-1
	ATTACH	T1,
	  JFCL
>
	MOVE	T1,.JBFF
	MOVEI	T4,BUFNUM		;NUMBER OF BUFFERS
	MOVEM	T1,BUFPTR		;POINTER TO BUFFERR BLOCK
	MOVEI	T3,BUFNUM*BF.LST(T1)	;WHERE THEY END
	CORE	T3,
	  STOPCD STOP,<Can't get core for initial buffers>
;+
;.lm 10
;.le
;Message:  "Can't get core for initial buffers"
;.break
;Type:  STOP
;	The CORE UUO to set up the initial BUFNUM buffers failed.
;-
BUFALC:	MOVEI	T2,BF.LST(T1)		;ADDRESS OF SECOND BUFFER
	HRLZM	T2,BF.LNK(T1)		;LINK THEM
	MOVE	T1,T2			;AND POINT TO SECOND
	SOJG	T4,BUFALC
	SETZM	(T1)			;NO MORE BUFFERS
	MOVEI	T1,BF.LST(T1)		;FIRST FREE
	MOVEM	T1,.JBFF
	IORI	T3,777
	MOVEM	T3,HICORE
	MOVEI	T1,PIVEC		;SET UP PSISER
	PIINI.	T1,
	  STOPCD STOP,<Can't initialize PSISER>
;+
;.lm 10
;.le
;Message:  "Can't initialize PSISER"
;.br
;Type:  STOP
;	The PIINI. UUO to set up the priority software interrupt system
;failed.  See AC T1 for the error code.
;-
IFN	FTDECNET,<
	MOVE	T1,[PS.FAC+[	.PCNSP	;NSP TRAPS
			    NSPOFF,,0
				Z    ]]	;SET THEM UP
	PISYS.	T1,
	  STOPCD STOP,<Can't set up PSISER for NSP.>
;+
;.lm 10
;.le
;Message:  "Can't set up PSISER for NSP."
;.br
;Type:  STOP
;	The PISYS. UUO to add NSP_. conditions to the priority software
;interrupt system failed.  Error code in T1.
;-
	PUSHJ	P,GETPCB		;GET A CONTROL BLOCK
	MOVEI	B,(T1)
	PUSHJ	P,DCNENT		;DO ENTER PASSIVE
	  STOPCD INFO,<Can't set initial DECnet link up>
;+
;.lm 10
;.le
;Message:  "Can't set initial DECnet link up"
;.br
;Type:  INFO
;	Routine DCNENT does an ENTER PASSIVE function to set up a DECnet
;link.  It returned non-skip, which means the function failed.  Error
;code to the NSP_. UUO in T1.  Note that PSTHRU will not be accepting
;any DECnet connects if this error occurs.
;-
>
IFN	FTANF10,<
	PUSHJ	P,GETPCB
	MOVEI	B,(T1)
	PUSHJ	P,ANFENT		;DO ANF ENTER PASSIVE
	  STOPCD INFO,<Can't set initial ANF link up>
;+
;.lm 10
;.le
;Message:  "Can't set initial ANF link up"
;.br
;Type:  INFO
;	Routine ANFENT does an ENTER PASSIVE function to set up an ANF
;link.  It returned non-skip, which means the function failed.
;-
>
DOSLP:	PUSHJ	P,PION			;TURN ON PSISER
	SETZ	T1,
	HIBER	T1,
	  JFCL
	PUSHJ	P,PIOFF			;PI should be off for this stuff
IFN	FTDECNET,<
	SKIPE	DPLQUE			;DECnet queue?
	  PUSHJ	P,DCNFRC		;Force an interrupt
>
IFN	FTANF10,<
	SKIPE	APLQUE			;ANF queue?
	  PUSHJ	P,ANFFRC		;Same for it
>
	JRST	DOSLP
	Subttl	DECnet interrupt routine

IFN	FTDECNET,<

NSPINT:	MOVE	B,BLKPTR			;POINT TO LIST OF BLOCKS
	MOVE	S,NSPPSI+.PSVIS			;GET THE CHANNEL #
FNDNSP:	HRRZ	T2,PC.ARG+.NSACH(B)		;GET CHANNEL # OF THIS BLOCK
	HLL	F,PC.FLG(B)
	TXNE	F,PCDCN				;Is this DECnet?
	CAIE	T2,(S)				;Is channel same?
	  SKIPA					;Wrong link
	JRST	HAVNSP				;YES
	HRRZ	B,PC.NXT(B)			;POINT TO NEXT BLOCK
	JUMPN	B,FNDNSP			;CHECK IT
	JRST	NOSTR				;Check for queued PCBs

HAVNSP:	LDB	T1,[POINT 6,S,^L<NS.STA>+5]	;GET CURRENT STATE
	CAMN	B,DCNCWL			;Is this the CW link?
	CAIN	T1,.NSSCW			;Yes, is it going into CW?
	  SKIPA					;Going into CW or wrong link
	SETZM	DCNCWL				;Isn't CW any more
	MOVEM	S,PC.ARG+.NSACH(B)		;Save this status
	PUSHJ	P,@DCNSTB(T1)			;CALL ROUTINE
	  JRST	NOSTR				;Don't update
	HLLM	F,PC.FLG(B)
	HLLM	S,PC.ARG+.NSACH(B)		;STORE UPDATE STATUS
DCNFRC:
NOSTR:	SKIPN	B,DPLQUE			;See if any PCBs to poll
	  JRST	DISNSP				;No, dismiss the interrupt
	HLL	F,PC.FLG(B)			;Get flags
	HLRZ	T1,PC.POL(B)			;Else de-link this one
	HRRZM	T1,DPLQUE			;Making DPLQUE point ahead
	HRRZS	PC.POL(B)
	MOVE	T1,[NS.WAI!<.NSFRS,,.NSACH+1>]	;Poll the link
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  JRST	NOSTR				;Toss this one if fail
	MOVE	S,PC.ARG+.NSACH(B)		;Update S
	JRST	HAVNSP
DISNSP:	SKIPE	DCNCWL				;Is there still a link in CW?
	  JRST	DODBK				;Yes
	PUSHJ	P,GETPCB			;Get a PCB
	MOVEI	B,(T1)
	PUSHJ	P,DCNENT			;Put one there
	  JFCL					;Oh well
DODBK:	DEBRK.
	  STOPCD STOP,<DEBRK. not implemented>
;+
;.lm 10
;.le
;Message:  "DEBRK_. not implemented"
;.br
;Type:  STOP
;	A DEBRK_. UUO failed with the non-skip return.
;-
	POPJ	P,
	Subttl	DECnet State Dispatch Table

;still under FTDECNET

	DEFINE	DCNVEC	(STATE,ADDR)<

		IFN	<.-DCNSTB-.NSS'STATE>,<
		PRINTX	%DECnet State Vector Table is out of phase
		>
		EXP	ADDR
	>
DCNSTB:	EXP	CPOPJ			;NULL INTERRUPT

	DCNVEC	CW,CPOPJ		;CONNECT WAIT
	DCNVEC	CR,DCNCR		;CONNECT RECEIVE
	DCNVEC	CS,CPOPJ		;CONNECT SENT
	DCNVEC	RJ,DCNRJ		;REJECT
	DCNVEC	RN,DCNRN		;RN
	DCNVEC	DR,DCNDR		;DISCONNECT RECEIVED
	DCNVEC	DS,DCNDS		;DISCONNECT SENT
	DCNVEC	DC,DCNDC		;DISCONNECT CONFIRMED
	DCNVEC	CF,DCNCF		;NO CONFIDENCE
	DCNVEC	LK,DCNLK		;NO LINK
	DCNVEC	CM,DCNCM		;NO COMMUNICATION
	DCNVEC	NR,DCNNR		;NO RESOURCES

IFN	<.-DCNSTB-.NSSMX-1>,<
	PRINTX	%DECnet State Vector Table is incomplete
>
	Subttl	DECnet Connect Received

;still under FTDECNET

DCNCR:	PUSHJ	P,SETCNB		;Set up to read connect data
	MOVE	T1,[.NSFRI,,.NSAA1+1]
	MOVEM	T1,PC.ARG+.NSAFN(B)	;..
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  JRST	RETCNC			;Oh, well, no node to save
	MOVE	T1,PC.ARG+.NSAA1(B)	;Address of Connect block to T1
	MOVE	T1,.NSCND(T1)		;String block for node name
	HRLI	T1,(POINT 8,,35)	;Point to data part
	MOVE	T2,[POINT 6,PC.NOD(B),]	;Where to put name
	MOVEI	T3,6			;Maximum number of chars
CPYNOD:	ILDB	C,T1			;Get a character
	JUMPE	C,RETCN1		;Done
	ANDI	C,177			;Mask to seven bits
	MOVEI	C,<'0'-"0">(C)		;Convert to six bits
	IDPB	C,T2			;Dump character
	SOJG	T3,CPYNOD
RETCN1:	MOVE	T1,PC.ARG+.NSAA1(B)	;Point to connect block
	MOVE	T2,.NSCSD(T1)		;Point to the source descriptor
	HRRM	T2,PC.CDT(B)		;Save it for later
	SETZM	.NSCSD(T1)		;Don't return the block yet
RETCNC:	PUSHJ	P,GIVBAK		;Return all the blocks, and, in the immortal words
	SETZM	PC.ARG+.NSAA1(B)	;Of WEM, "Its not polite to point"
	MOVE	T1,[.NSFAC,,.NSACH+1]	;ACCEPT THE CONNECT
	MOVEM	T1,PC.ARG+.NSAFN(B)	;..
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  JRST	REJCNC			;OH WELL
	TXO	F,PCPW			;SET THE LINK TO PW STATE
	JRST	CPOPJ1

REJCNC:	MOVE	T1,[.NSFRJ,,.NSACH+1]
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  STOPCD INFO,<DECnet REJECT failed>
;+
;.lm 10
;.le
;Message:  "DECnet REJECT failed"
;.br
;Type:  INFO
;	A NSP_. function to reject an incoming connect failed.  PSTHRU
;should only be trying to reject a connection if it could not create
;an additional channel to have an ever-present channel in connect wait,
;or if accepting the connection failed.  Error code for the NSP_. UUO
;in T1.
;-
	MOVE	T1,[.NSFRL,,.NSACH+1]
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  JFCL
	MOVEI	T1,(B)
	JRST	FREPCB
	Subttl	DECnet Disconnect Received


;still under FTDECNET

DCNDR:
IFN	FTLOG,<
	PUSHJ	P,LOGDCN			;Log the disconnect
>
	HRRZ	T1,PC.CDT(B)			;If any connect data around
	JUMPE	T1,NOCDDR			;(None)
	HLLZS	PC.CDT(B)			;Clear out pointer to it
	HRRZ	T2,.NSASL(T1)			;Return it
	PUSHJ	P,CORFRE			;now
NOCDDR:	HLRZ	T1,PC.LNK(B)			;See if another side
	JUMPE	T1,DCNDR1			;None
	TXNE	F,PCPW!PCMAB			;Don't read anything if in PW
	  TDZA	T2,T2				;Didn't read anything
	PUSHJ	P,DCNRD				;Read everything
	JUMPE	B,CPOPJ
	PUSH	P,T1				;And save pointers to it
	PUSH	P,T2				;..
	MOVE	T1,[NS.WAI!<.NSFRD,,.NSAA2+1>]	;Read the disconnect data
	MOVEM	T1,PC.ARG+.NSAFN(B)		;..
	MOVEI	T1,5
	PUSHJ	P,CORGET			;Get string block for it
	MOVEI	T2,5
	MOVEM	T2,.NSASL(T1)			;Set length
	MOVEM	T1,PC.ARG+.NSAA1(B)
	MOVEI	T3,(T1)				;Save in T3 for later
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  STOPCD INFO,<DECnet Read Disconnect Information failed>
;+
;.lm 10
;.le
;Message:  "DECnet Read Disconnect Information failed"
;Type:  Info
;	The _.NSFRD function failed.  Error code in T1.
;-
DCNDR1:	MOVE	T1,[<.NSFRL,,.NSACH+1>]		;Release the channel
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  STOPCD INFO,<DECnet RELEASE failed>
;+
;.lm 10
;.le
;Message:  "DECnet RELEASE failed"
;.br
;Type:  INFO
;	The _.NSFRL function failed.  Usually this means the channel was
;already gone.  Error code in T1.
;-
	MOVEI	T1,(B)				;Free the PCB
	HLRZ	B,PC.LNK(B)
	PUSHJ	P,FREPCB
	JUMPE	B,CPOPJ				;If no other side
	HRRZS	PC.LNK(B)			;Clear link
	POP	P,T2				;Restore data pointers
	POP	P,T1
	TXNE	F,PCPW				;Was this link in PW?
	  JRST	RELLNK				;Abort link and release
	HLL	F,PC.FLG(B)			;Set flags accurately
	FALL	DSCLNK				;Disconnect the link

>						;End FTDECNET
	Subttl	Disconnect other side

;Enter here to disconnect the link in B.  T3 points to disconnect data
;T1=pointer to interrupt data to queue to outgoing side (if any)
;T2=pointer to normal data to queue
;(i.e. T1&T2 set to call DCNWR)

DSCLNK:	HRRM	T3,PC.CDT(B)	;Store disconnect data, if any
	TXO	F,PCAL		;Set to abort link on I/O done
	PUSH	P,T1		;Save pointers in case CW
	PUSH	P,T2
	TXZE	F,PCSTP		;Clear STOP if aborting link
	  PUSHJ	P,POLQUE	;And put it on the polling queue
	TXZN	F,PCCW		;Clear it's in CW now (let data through)
	  JRST	NOCW		;Not, keep buffers
	SETZM	(P)		;No new data either
	SETZM	-1(P)
	HLRZ	T1,PC.BUF(B)	;Destroy all buffers now
	JUMPE	T1,NOCW		;Unless there aren't any (should be @least 1)
	HLRZ	T2,BF.LNK-BF.DAT(T1)	;Remember the next
	PUSHJ	P,FREBUF	;Free this one
	MOVEI	T1,(T2)		;Make next current
	JUMPN	T1,.-3		;And free it
	HRRZS	PC.BUF(B)	;**No stale pointers!**
NOCW:	POP	P,T2
	POP	P,T1		;Restore data pointers
	HLLM	F,PC.FLG(B)	;Won't be stored by HAVNSP since no <SKP>
IFN	FTDECNET,<
	TXNE	F,PCDCN		;DECnet?
	  PJRST	DCNWR		;Write remaining data
>
IFN	FTANF10,<
	TXNE	F,PCANF		;ANF?
	  PJRST	ANFWR
>
	STOPCD	RELOAD,<Link isn't DECnet or ANF>
;+
;.lm 10
;.le
;Message:  "Link isn't DECnet or ANF"
;.br
;Type:  RELOAD
;	PSTHRU can't determine whether a link is of type DECnet or
;ANF.  This implies the internal data base has been trashed.
;-

;Here to unconditionally abort link in B

RELLNK:	IFN	FTANF10,<
	TXNE	F,PCANF				;ANF?
	  JRST	ANFREL				;Yes
>
IFN	FTDECNET,<
	TXNN	F,PCDCN
>
	  STOPCD  RELOAD,<Link isn't DECnet or ANF>
;Note that this was documented earlier.
IFN	FTDECNET,<
	MOVE	T1,[.NSFAB,,.NSACH+1]
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  SKIPA					;Do release if can
	JRST	RELLN1
	MOVE	T1,[.NSFRL,,.NSACH+1]
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  STOPCD INFO,<DECnet RELEASE failed>
;Note that this was documented earlier.
RELLN1:	MOVEI	T1,(B)
	SETZ	B,
	PJRST	FREPCB
>

IFN	FTANF10,<
ANFREL:	HRLZ	T1,PC.ARG+.TKACH(B)	;Channel
	HRRI	T1,.FOREL
	MOVEM	T1,FLPBLK+.FOFNC
	MOVE	T1,[.FOFNC+1,,FLPBLK]
	FILOP.	T1,
	  STOPCD INFO,<ANF RELEASE failed>
;+
;.lm 10
;.le
;Message:  "ANF RELEASE failed"
;.br
;Type:  INFO
;	A FILOP. to release an ANF channel failed.  Probably the channel
;was already released.  The error code is in T1.
;-
	MOVEI	T1,(B)
	SETZ	B,
	PJRST	FREPCB
>
	Subttl	DECnet error states

;If in CW, send error message as if NSP. failed
;Else, treat as DR state

IFN	FTDECNET,<

DCNNR:						;No resources
DCNLK:						;No link
DCNCF:						;No confidence
DCNCM:						;No communication
	TXNN	F,PCCW				;In CW?
	  JRST	DCNDR				;No, as if a disconnect happened
	SETZM	PC.ARG+.NSAA2(B)		;Be sure GIVBAK doesn't
	JRST	DCNRJ				;Treat like connection failure
	Subttl	DECnet state RN

;still under FTDECNET
;for data transfer

DCNRN:
DCNDS:	TXZE	F,PCCW			;Connect wait?
	  PUSHJ	P,PARSER		;Finish up completing the links
DCNRNR:	SETZB	T1,T2			;Default no data
	TXNN	F,PCSTP			;Stop input?
	PUSHJ	P,DCNRD			;Read data on block pointed to by B
	JUMPE	B,CPOPJ
	PUSH	P,T1			;Save interrupt data
	PUSH	P,T2			;Save normal data
	TXNN	F,PCPW			;Parse?
	  JRST	DCNRN1			;No
	MOVEI	T1,(T2)			;else set byte pointer for it
	JUMPE	T1,DCNRN1		;No parse if no data
	PUSHJ	P,PARSE
	SETZM	(P)			;All buffers already transferred
DCNRN1:	SETZB	T1,T2			;Nothing additional to queue
	SKIPE	B			;If link aborted during parse
	PUSHJ	P,DCNWR			;Write what we can from this link
	POP	P,T2			;Restore normal
	POP	P,T1			;And interrupt data
	JUMPE	B,CPOPJ
	HLLM	F,PC.FLG(B)		;Store B
	HLRZ	T3,PC.LNK(B)
	JUMPE	T3,BUFEAT		;Throw away buffers if no other side
	HRRZI	B,(T3)
	HLL	F,PC.FLG(B)		;And flags
IFN	FTANF10,<
	LDB	T3,[POINT	1,F,^L<PCANF>]
	PUSHJ	P,@[	DCNWR
			ANFWR](T3)
>
IFE	FTANF10,<
	PUSHJ	P,DCNWR
>
	JUMPE	B,CPOPJ			;If no link, then don't update
	HLLM	F,PC.FLG(B)		;Point back to original block
	HLRZ	B,PC.LNK(B)
	HLL	F,PC.FLG(B)
	JRST	CPOPJ1

	Subttl	Reject state

;still under FTDECNET

DCNRJ:
IFN	FTLOG,<
	PUSHJ	P,LOGREJ			;Log a REJECT
>
	HLRZ	T1,PC.LNK(B)			;Is there another side?
	JUMPE	T1,DCNRJ1			;No
	MOVE	T1,[NS.WAI!<.NSFRD,,.NSAA1+1>]
	MOVEM	T1,PC.ARG+.NSAFN(B)		;Read the disconnect data
	MOVEI	T1,5
	PUSHJ	P,CORGET
	MOVEI	T2,5
	HRRM	T2,.NSASL(T1)			;Set length
	MOVEM	T1,PC.ARG+.NSAA1(B)
	MOVEI	T3,(T1)
	MOVEI	T1,PC.ARG(B)
	SETZ	T2,				;Preset T2
	NSP.	T1,
	  TROA	T2,400000(T1)			;Flag an error not a reason
	MOVE	T2,PC.ARG+.NSAA2(B)		;Get reason
	PUSH	P,T2				;Save it
DCNRJ1:	MOVE	T1,[<.NSFRL,,.NSACH+1>]
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  STOPCD INFO,<DECnet RELEASE failed>
;Note that this was documented earlier.
	MOVEI	T1,(B)
	HLRZ	T4,PC.BUF(B)			;Get buffer
IFN	FTDEBUG,<
	SKIPN	T4
	  STOPCD RELOAD,<No buffer>
;Message:  "No buffer"
;.br
;Type:  RELOAD
;	This STOPCD only occurs if FTDEBUG is turned on.  While
;processing a REJECT, we could not find the buffer which we would normally
;have queued to either write the answer back message or the PSTHRU message
;to the next link.
;-
>
	HLRZ	B,PC.LNK(B)			;Point to other half
	SKIPE	B				;If no other side, eat buffers
	HRRZS	PC.BUF(T1)			;Don't let BUFEAT this time
	PUSHJ	P,FREPCB
	JUMPE	B,CPOPJ				;Return if nothing else
	HRRZS	PC.LNK(B)			;No stale links!
	HLL	F,PC.FLG(B)			;Get flags
	MOVE	S,PC.ARG+.NSACH(B)		;And last known status
	HRRM	T3,PC.CDT(B)			;Save the disconnect data
	POP	P,T2				;Restore T2
	TRZN	T2,400000			;Error or reason?
	  SKIPA	T2,DCNRSN(T2)			;A reason
	MOVE	T2,DCNERR(T2)			;An error
	FALL	ABTLNK				;Get the message and abort

>						;End FTDECNET
	Subttl	Abort Link

;Here from PRSERx to abort a link after sending a reason message
;T4=pointer to buffer to use
;T2=byte pointer to string
;PC.CDT(B) points to disconnect data, if any
;PC.BUF(B) is buffer

ABTLNK:
	PUSH	P,T4				;Save buffer pointer
	HRLI	T4,(POINT 8,,)			;Make data pointer
	HRLI	T2,(POINT 7,,)			;Make pointer for this
	MOVEI	C,2				;Failure code
	IDPB	C,T4				;Put in
	MOVEI	C,"("
	IDPB	C,T4
	MOVSI	T3,-6				;Maximum size of name
	PUSH	P,T2				;Save pointer
IFN	FTANF10,<
	MOVEI	T2,ANFNAM			;Assume ANF
>
IFN	FTANF10&FTDECNET,<
	TXNN	F,PCANF				;Is it?
>
IFN	FTDECNET,<
	MOVEI	T2,DCNNAM			;No, make DECnet
>
	HRLI	T2,(POINT 6,,)
CPYNAM:	ILDB	C,T2
	JUMPE	C,NAMDON
	MOVEI	C,"0"-'0'(C)			;Convert from sixbit
	IDPB	C,T4
	AOBJN	T3,CPYNAM
NAMDON:	HRRZI	T3,6(T3)			;Clear left half; include ^B(::)<SP>
	MOVEI	C,":"
	IDPB	C,T4
	IDPB	C,T4
	MOVEI	C,")"
	IDPB	C,T4
	MOVEI	C," "
	IDPB	C,T4
	POP	P,T2				;Restore message byte pointer
CPYMSG:	ILDB	C,T2
	JUMPE	C,CPYDON
	IDPB	C,T4
	AOJA	T3,CPYMSG
CPYDON:	POP	P,T2				;Restore buffer pointer
	TRO	T3,BFEOM			;Set EOM
	HRRM	T3,BF.CNT-BF.DAT(T2)		;Store # of characters
	SETZ	T1,				;No interrupt data
	HRRZ	T3,PC.CDT(B)			;Get the connect data
	PJRST	DSCLNK				;Disconnect the link
	Subttl	Disconnect confirmed


IFN	FTDECNET,<

DCNDC:	HLRZ	T1,PC.LNK(B)			;Is this a funny phase-II DC?
	JUMPN	T1,DCNRJ			;Yes, treat like reject
	MOVE	T1,[<.NSFRL,,.NSACH+1>]
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,				;Release the channel
	  STOPCD INFO,<DECnet RELEASE failed>
;Note that this was documented earlier.
	MOVEI	T1,(B)
	PJRST	FREPCB				;And release the core

>		;END OF FTDECNET
	Subttl	ANF-10 interrupt routine

COMMENT	&
			***NOTE***

	In order to appear similar to the DECnet code, .TKACH stores
the channel in the right half of the word and the PSI interrupt bits
in the left half (as also does AC S).  It is OK to leave the junk bits
around for the TSK. since it calls SETUF (P1=channel) which does a
HRRZS P1 upon entry.  This will not necessarily be true.

							&

IFN	FTANF10,<

ANFINT:	HLRZ	S,ANFPSI+.PSVIS			;Get channel
	HRL	S,ANFPSI+.PSVFL			;And reason
	MOVE	B,BLKPTR			;Point to list of blocks
FNDANF:	HRRZ	T2,PC.ARG+.TKACH(B)		;Get the TSK. channel
	HLL	F,PC.FLG(B)			;Flags for this link
	TXNE	F,PCANF				;ANF link?
	CAIE	T2,(S)				;Yes, right channel?
	  SKIPA					;Not right link or wrong channel
	JRST	HAVANF				;Found the culprit
	HRRZ	B,PC.NXT(B)			;Point to next
	JUMPN	B,FNDANF			;Check it out
	JRST	NOSTRA				;Pitch it if don't know who

HAVANF:	MOVEM	S,PC.ARG+.TKACH(B)		;Save current status
	CAMN	B,ANFCWL
	  SETZM	ANFCWL				;If this is the CW link
HAVAN2:	PUSHJ	P,DOANF				;Ponder what to do
ANFFRC:	SKIPN	B,APLQUE			;ANF polling queue
	  JRST	NOSTRA				;None
	HLRZ	T1,PC.POL(B)			;Take this one off
	MOVEM	T1,APLQUE
	HRRZS	PC.POL(B)
	MOVE	S,PC.ARG+.TKACH(B)
	HLL	F,PC.FLG(B)
	JRST	HAVAN2
NOSTRA:	SKIPE	ANFCWL				;Is there still a link in CW?
	  JRST	DODBKA				;No, do the DEBRK.
	PUSHJ	P,GETPCB			;Get a PCB for new link
	MOVEI	B,(T1)
	PUSHJ	P,ANFENT
	  JFCL
DODBKA:	DEBRK.
	  STOPCD STOP,<DEBRK. not implemented>
;Note that this was documented earlier.
	POPJ	P,

DOANF:	TLNE	S,PS.RDO				;Off-line interrupt?
	  JRST	ANFOFL					;Yes, go handle it
	TLNE	S,PS.REF				;End of file?
	  JRST	ANFEOF					;Yes, close down link
	TLNE	S,PS.ROL				;On-line interrupt?
	  PUSHJ	P,ANFONL				;Yes, see what kind
	SETZ	T2,					;So don't queue junk
	TXNN	F,PCSTP					;STOP bit set?
	PUSHJ	P,ANFRD					;Read data on this link
	TXNN	F,PCPW					;In PSTHRU wait?
	  JRST	ANFNPW					;No
	SKIPE	T1,T2					;Call PARSE if anything
	PUSHJ	P,PARSE
	JUMPE	B,CPOPJ					;If the parse failed
	SETZ	T2,					;String has been processed
ANFNPW:	PUSH	P,T2					;Save buffer string
	SETZB	T1,T2
	PUSHJ	P,ANFWR					;Start I/O on this link
	SETZ	T1,
	POP	P,T2					;Restore buffer chain
	JUMPE	B,BUFEAT				;If was queued for abort
							;then partner is gone
							;anyway
	HLLM	F,PC.FLG(B)				;Switch to partner link
	HLRZ	B,PC.LNK(B)				;..
	JUMPE	B,BUFEAT				;If none, toss input
	HLL	F,PC.FLG(B)
	ASSUME	.NSACH,.TKACH				;**must be equal**
	MOVE	S,PC.ARG+.NSACH(B)
	MOVEI	T3,ANFWR				;Assume ANF
IFN	FTDECNET,<
	TXNN	F,PCANF					;Is it?
	  MOVEI	T3,DCNWR				;Nope
>
	PUSHJ	P,(T3)
	JUMPE	B,CPOPJ
	HLLM	F,PC.FLG(B)				;Store flags
	POPJ	P,
	Subttl	ANF off-line interrupt

;still under FTANF10

ANFOFL:	TXZN	F,PCCW			;Was link in CW?
	  JRST	ANDRDC			;DR or DC maybe
	TXZ	F,PCANF			;Was, then is a reject
	HLLM	F,PC.FLG(B)		;Update the permanent copy too
	HRLZ	T1,PC.ARG+.TKACH(B)	;Get the channel
	SETZM	PC.ARG+.TKACH(B)	;And don't remember it here
	HRRI	T1,.FOREL		;Release function
	MOVEM	T1,FLPBLK+.FOFNC	;Set function
	MOVE	T1,[.FOFNC+1,,FLPBLK]
	FILOP.	T1,
	  STOPCD INFO,<ANF RELEASE failed>
;Note that this was documented earlier.
	JRST	DCNACT			;Now try DECnet enter active

ANDRDC:	TLNE	S,PS.RID!PS.ROD!PS.REF	;If any of these, then
	  JRST	ANFEOF			;Treat like EOF
	PJRST	ANFREL			;Else like a DC
	Subttl	ANF On-line interrupt

;Still under FTANF10

ANFONL:	TXZN	F,PCCW				;Was link in CW?
	  JRST	ANFCR				;No, connect received then
	HLLZS	PC.CDT(B)			;Clear connect data ptr
	PUSHJ	P,GIVBAK			;Yes, return DECnet blocks
	PUSHJ	P,ZAPARG			;Clear all traces to them
	PJRST	PARSER				;Finish up the string

ANFCR:	MOVEI	T1,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	PUSHJ	P,CORGET			;Get block to read NPDs
	HRLI	T1,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	MOVEM	T1,PC.ARG+.TKAA3(B)		;Store where it needs to be
	HLRZS	T1				;Get length again
	PUSHJ	P,CORGET			;Get another block
	HRLI	T1,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	MOVEM	T1,PC.ARG+.TKAA2(B)		;This one too
	MOVEI	T1,.TKFRS			;Function read status
	MOVEM	T1,PC.ARG+.TKAFN(B)		;Set it
	MOVEI	T1,PC.ARG(B)
	HRLI	T1,.TKAA3+1			;Length of arg block
	TSK.	T1,				;Read ths status
	  JFCL					;Oh well
	MOVEI	T1,<<<^D16/4>+1>+<.NSDPN+1>>	;Get core for DECnet process blk
	PUSHJ	P,CORGET			;Get it
	MOVEI	T2,.NSDPN+1			;Set its length
	MOVEM	T2,.NSDFL(T1)			;..
	ASSUME	<<^D16/4>+1>,<.NSDPN+1>		;Save an instruction here
	MOVEM	T2,.NSDPN+1(T1)			;Link in name block
	ADDI	T2,(T1)				;Point to name block
	MOVEM	T2,.NSDPN(T1)			;Save it
	HRRM	T1,PC.CDT(B)			;Save pointer to it here
	HRRZ	T1,PC.ARG+.TKAA2(B)		;Point to NPD
	MOVEI	T2,.TKNPN(T1)			;Make byte pointer of T2
	HRLI	T2,(POINT 7,,)			;..
	MOVEI	T4,2				;Number of <NUL>s to pass
FNDNUL:	SOSGE	.TKNLN(T1)			;Count characters
	  JRST	NOCDAC				;None
	ILDB	C,T2				;Get a character of it
	JUMPN	C,FNDNUL			;Look for a <NUL>
	SOJG	T4,FNDNUL			;Look for the nth (n=2) one
	SETZ	T4,				;Clear number AC
OBJNUM:	SOSGE	C,.TKNLN(T1)
	  AOJA	C,HOBJ1				;Have an object (C=0 here)
	ILDB	C,T2				;Get a character
	SKIPE	C				;A <NUL>
	CAIN	C,"."				;Or end of this field?
	  JRST	HOBJ1				;Yes
	LSH	T4,3				;Octal
	ADDI	T4,-"0"(C)			;..
	JRST	OBJNUM				;Back for more

HOBJ1:	HRRZ	T3,PC.CDT(B)			;Point to connect block
	MOVEM	T4,.NSDOB(T3)			;Set object
	JUMPE	C,HOBJ3				;Also if so
	MOVEI	T4,1				;Set format 1
	MOVEM	T4,.NSDFM(T3)
	MOVE	T3,.NSDPN(T3)			;Get process name
	PUSH	P,T3				;Save pointer
	HRLI	T3,(POINT 8,,35)		;Point there
	MOVSI	T4,-^D16/4			;Be defensive
CPYOBJ:	SOSGE	.TKNLN(T1)			;Here too
	  JRST	HOBJ2				;Figure we're done
	ILDB	C,T2				;Get character from NPD
	JUMPE	C,HOBJ2				;Done on <NUL>
	IDPB	C,T3				;Put in here
	AOBJN	T4,CPYOBJ			;Copy more
HOBJ2:	POP	P,T3
	HRLM	T4,.NSASL(T3)			;Set length
	JRST	HOBJ3				;Done
NOCDAC:	HRRZ	T1,PC.CDT(B)			;Else flag none
	HRRZI	T2,<<^D16/4>+1+<.NSDPN+1>>	;Free the block
	PUSHJ	P,CORFRE
	HLLZS	PC.CDT(B)			;And forget it
HOBJ3:	HRRZ	T1,PC.ARG+.TKAA3(B)		;Remote NPD
	MOVE	T4,.TKNND(T1)			;And the remote node number
	MOVEI	T2,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	PUSHJ	P,CORFRE			;Zap block
	HRRZ	T1,PC.ARG+.TKAA2(B)		;Free local NPD too
	MOVEI	T2,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	PUSHJ	P,CORFRE			;Zap it too
	MOVEI	T3,2				;Arg block for NODE.
	MOVE	T1,[.NDRNN,,T3]			;Find the name
	NODE.	T1,
	  SETZ	T1,				;Oops
	MOVEM	T1,PC.NOD(B)			;Store the node name
	TXO	F,PCPW				;Link into PW state
	HLLM	F,PC.FLG(B)			;Set in permanent copy too
	POPJ	P,				;And return
	PJRST	ZAPARG				;Return killing stale pointers
	Subttl	ANF End-of-file interrupt

;Still under FTANF10
;Like DCNDR

ANFEOF:
IFN	FTLOG,<
	PUSHJ	P,LOGDCN			;Log the disconnect
>
	TXNE	F,PCMAB!PCPW			;If here, don't do anything
	  TDZA	T2,T2				;Didn't read anything
	PUSHJ	P,ANFRD				;Read all data possible
	PUSH	P,T2				;Save pointer to it
	HLRZ	T1,PC.LNK(B)			;Remember partner
	PUSH	P,T1				;Save it
	PUSHJ	P,ANFREL			;Release us
	POP	P,B				;Get partner
	POP	P,T2				;Restore buffer string
	SETZ	T1,				;No interrupt data
	JUMPE	B,BUFEAT			;If none
	HRRZS	PC.LNK(B)			;No stale links here
	SETZB	T1,T3				;No interrupt or disconnect data
	HLL	F,PC.FLG(B)			;Get flags
	PJRST	DSCLNK				;Disconnect the link

>						;End FTANF10
	Subttl	Subroutines

;PIOSAV:  Routine to turn off the PSI if it wasn't
;and restore it upon exit from the subroutine


PIOSAV:	TRNN	F,FL$PI			;Was it on?
	  POPJ	P,			;No, nothing to remember
	PUSHJ	P,PIOFF			;Turn it off
	POP	P,(P)			;OK to do this with PIs off
	PUSHJ	P,@1(P)			;Call subroutine
	SKIPA
	AOS	(P)
					;fall thru to PION
;PION: Routine to turn on PSI
;Call:
;
;	PUSHJ	P,PION
;	  <only return>			;all ACs saved

PION:	TROE	F,FL$PI			;Was it off?
	  POPJ	P,			;No
	PUSH	P,T1
	MOVSI	T1,(PS.FON)
	PISYS.	T1,
	  STOPCD STOP,<Can't turn PSISER on>
;+
;.lm 10
;.le
;Message:  "Can't turn PSISER on"
;.br
;Type:  STOP
;	A PISYS_. UUO to turn the priority software interrupt system back
;on failed.  Error code in T1.
;-
	JRST	TPOPJ

;PIOFF: Complementary routine of above
;called as above

PIOFF:	TRZN	F,FL$PI			;Was PI on?
	  POPJ	P,			;No
	PUSH	P,T1
	MOVSI	T1,(PS.FOF)
	PISYS.	T1,
	  STOPCD STOP,<Can't turn PSISER off>
;+
;.lm 10
;.le
;Message:  "Can't turn PSISER off"
;.br
;Type:  STOP
;	A PISYS_. UUO to turn the priority software interrupt system off
;failed.  Error code in T1.
;-
	JRST	TPOPJ
;Find predecessor of block in T1 (searching BLKPTR)
;Return with it in T1
;All ACs respected

GETPRD:	PUSH	P,T1
	PUSH	P,T2				;Save ACs
	MOVEI	T2,BLKPTR			;Point to start of list
	ASSUME	PC.LNK,0			;Assume PC.LNK is zero
PRDLP:	HRRZ	T1,PC.LNK(T2)			;**NOTE** PC.LNK==0
	EXCH	T2,T1
	CAME	T2,-1(P)			;one we want?
	JRST	PRDLP
	POP	P,T2
	POP	P,(P)
	POPJ	P,

;Routine to eat buffer chains
;T1=pointer to interrupt data, t2=pointer to normal data

BUFEAT:	PUSHJ	P,SAVT
	PUSH	P,T2			;Save normal pointer
	JUMPE	T1,BUFEAN		;Eat normal data
BUFEAI:	HLRZ	T3,BF.LNK-BF.DAT(T1)
	HRRZI	T2,5+1			;Size of interrupt data block
	PUSHJ	P,CORFRE
	HRRZI	T1,(T3)
	JUMPN	T1,BUFEAI
BUFEAN:	POP	P,T1
	JUMPE	T1,CPOPJ
BUFEN1:	HLRZ	T2,BF.LNK-BF.DAT(T1)
	PUSHJ	P,FREBUF
	HRRZI	T1,(T2)
	JUMPN	T1,BUFEN1
	POPJ	P,
;Zap .NSAAx args in block pointed to by B (So GIVBAK won't try to return junk))
;Uses T1

ZAPARG:	HRLI	T1,PC.ARG+.NSAA1(B)
	HRRI	T1,PC.ARG+.NSAA1+1(B)
	SETZM	-1(T1)
	BLT	T1,PC.ARG+.NSAA3(B)
	POPJ	P,
	Subttl	Routines to insert "B" into the polling queue

;Routines to put the PCB pointed to by B on the polling list
;All ACs respected

;Arbitrary queue, based on B
IFN	FTDECNET&FTANF10,<
POLQUE:	PUSH	P,T1			;Save T1
	HLL	T1,PC.FLG(B)
	LDB	T1,[POINT 1,T1,^L<PCDCN>]
	PUSHJ	P,@[ANFPOL
		    DCNPOL](T1)		;Call proper routine
	JRST	TPOPJ
>

IFE	FTDECNET&FTANF10,<
POLQUE:
>

;DECnet queue:
IFN	FTDECNET,<
DCNPOL:	PUSH	P,T1			;Save T1
	MOVE	T1,DPLQUE		;Get first in queue
	HRRZM	B,DPLQUE		;Make us first
	HRLM	T1,PC.POL(B)		;And him second
	SETO	T1,
	WAKE	T1,
	  JFCL
	JRST	TPOPJ			;Restore T1 and return
>

;ANF queue:
IFN	FTANF10,<
ANFPOL:	PUSH	P,T1			;Save T1
	MOVE	T1,APLQUE		;Get first in queue
	HRRZM	B,APLQUE		;Make us first
	HRLM	T1,PC.POL(B)		;And him second
	SETO	T1,
	WAKE	T1,
	  JFCL
	JRST	TPOPJ			;Restore T1 and return
>
	Subttl	Numeric Conversion

;Enter at DECIN1 with initial value in T1 already set
;Exit with T1=#

DECIN:	SETZ	T1,
DECIN1:	PUSHJ	P,NXTCHR
	  POPJ	P,
	CAIL	C,"0"
	CAILE	C,"9"
	POPJ	P,
	IMULI	T1,^D10
	ADDI	T1,-"0"(C)
	JRST	DECIN1
	Subttl	Routine to read data for B
;Read all available data for the link whose PCB is pointed to by B
;Return with T1=pointer to interrupt data buffer string (zero if none)
;Return with T2=pointer to normal data string (zero if none)
;S must also already be set up and is updated
;All T ACs used

IFN	FTDECNET,<
DCNRD:	SETZ	T1,
	TXNE	S,NS.IDA			;Interrupt data available?
	  PUSHJ	P,DCNRDI			;Yes, read it
	JUMPE	B,CPOPJ				;Done
	PUSH	P,T1				;Store it
	PUSH	P,[0]				;Clear place on stack
	MOVEI	T4,BF.DAT-BF.LNK(P)		;Fake first buffer
	MOVEI	T2,MAXRED			;Maximum number of reads to do
DCNRDN:	TXNN	S,NS.NDA			;Normal data available?
	  JRST	DCNRDR				;No return
	PUSHJ	P,GETBUF			;Get a buffer
	MOVEI	T3,(T1)				;Save it
DCNRDO:	HRLI	T1,(POINT 8,,)			;Make a byte pointer
	MOVEM	T1,PC.ARG+.NSAA2(B)
	MOVEI	T1,BUFSIZ*4			;4 characters per word
	MOVEM	T1,PC.ARG+.NSAA1(B)
	MOVE	T1,[<.NSFDR,,.NSAA2+1>]		;Read normal
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)			;Point to args
	NSP.	T1,				;Read the data
	  STOPCD ABORT,<DECnet READ NORMAL failed>
;+
;.lm 10
;.le
;Message:  "DECnet READ NORMAL failed"
;.br
;Type:  ABORT
;	A NSP_. function to read normal data from a DECnet link
;failed.  The error code is in T1.  Data may have been lost.
;-
	JUMPE	B,DCNRDR			;In case link was aborted
	MOVE	S,PC.ARG+.NSACH(B)		;Update S
	MOVEI	T1,BUFSIZ*4			;Compute # of characters read
	SUB	T1,PC.ARG+.NSAA1(B)		;..
	JUMPGE	T1,DCNRD3			;Go
	MOVEI	T1,(T3)				;Free the buffer
	TXNE	S,NS.NDA			;Still data to be read?
	  JRST	DCNRDO				;Yes, toss this and read it
	PUSHJ	P,FREBUF
DCNRDR:	POP	P,T2
	POP	P,T1
	MOVSS	T2				;Normal data is reversed
	POPJ	P,				;Return with strings

DCNRD3:	HRLM	T3,BF.LNK-BF.DAT(T4)
	HRRZI	T4,(T3)				;Point it to this buffer
	MOVE	T3,PC.ARG+.NSAFN(B)		;Get function again
	TXNE	T3,NS.EOM			;EOM?
	TXO	T1,BFEOM			;Yes, set it
	HRRM	T1,BF.CNT-BF.DAT(T4)
	LDB	T3,[POINT 6,S,^L<NS.STA>+5]	;Get current state
	CAIE	T3,.NSSRN			;RN state?
	  JRST	DCNRDN				;No, no limit on reads then
	SOJGE	T2,DCNRDN			;Only read this many
	PUSHJ	P,DCNPOL			;Else put this on polling queue
	JRST	DCNRDR				;Return
;Routine for interrupt data
;Returns with T1 pointing to interrupt data string
;Still under FTDECNET

DCNRDI:	PUSH	P,[0]			;Default none
	MOVEI	T4,BF.DAT-BF.LNK(P)	;Start address to store in
RIDA1:	TXNN	S,NS.IDA		;Interrupt data available?
	  JRST	RIDAR			;No, return
	MOVEI	T1,5+1			;Interrupt data in 16 chars max+header
	PUSHJ	P,CORGET		;Get the mini-buffer
	MOVEI	T1,BF.DAT(T1)		;Point to data area
	MOVEI	T2,5			;Size of data part of message
	HRRZM	T2,.NSASL(T1)		;Which is a string block
	MOVEM	T1,PC.ARG+.NSAA1(B)	;Buffer addr
	MOVE	T3,[<.NSFIR,,.NSAA1+1>]	;The function
	MOVEM	T3,PC.ARG+.NSAFN(B)
	MOVEI	T3,PC.ARG(B)
	NSP.	T3,			;Read the data
	  STOPCD ABORT,<DECnet READ INTERRUPT failed>
;+
;.lm 10
;.le
;Message:  "DECnet READ INTERRRUPT failed"
;.br
;Type:  ABORT
;	A NSP_. UUO to read interrupt data failed.  Error code in T3.
;Some data may have been lost.
;-
	JUMPE	B,TPOPJ			;Done
	HLL	S,PC.ARG+.NSACH(B)	;Update S
	HLRZ	T3,.NSASL(T1)		;Get characters read
	JUMPN	T3,RIDA2		;Proceed if there are any
	MOVEI	T1,BF.LNK-BF.DAT(T1)	;Else free the buffer
	MOVEI	T2,5+1
	PUSHJ	P,CORFRE		;..
RIDAR:	POP	P,T1
	MOVSS	T1,			;It was reversed
	POPJ	P,

RIDA2:	HRLZM	T1,BF.LNK-BF.DAT(T4)	;Point previous to us
	HRRZI	T4,(T1)			;Make us predecessor of next
	JRST	RIDA1
	Subttl	Routine to write all data for B

;Write all buffers possible.  Enter with T1=interrupt data buffer string
;Enter with T2=normal data buffer string (either are zero if none).
;Queue the buffers up and crank up any I/O possible.
;Still under FTDECNET

DCNWR:	JUMPE	T1,QUENRM			;Queue the normal data
	PUSH	P,[0]				;Count number of entries
	MOVEI	T4,PC.IDQ+BF.DAT-BF.LNK(B)	;Do interrupt queue first
	HLRZ	T3,BF.LNK-BF.DAT(T4)		;Get next item in queue
	JUMPE	T3,.+4				;Done if no more
	MOVEI	T4,(T3)				;Else remember us
	AOS	(P)				;Count another buffer
	JRST	.-4				;And go to next
	HRLM	T1,BF.LNK-BF.DAT(T4)		;Point him to us
	POP	P,T1				;Count of buffers (not incl added)
	CAIG	T1,QUEMAX
	  JRST	QUENRM				;OK here
	HLRZ	T1,PC.LNK(B)			;If there is another side
	JUMPE	T1,QUENRM
	HLL	T3,PC.FLG(T1)
	TXO	T3,PCSTF
	HLLM	T3,PC.FLG(T1)			;Then stop input on it
QUENRM:	PUSH	P,[0]				;Initialize buffer count
	MOVEI	T1,PC.BUF+BF.DAT-BF.LNK(B)	;Initialize pointer
QNM1:	HLRZ	T3,BF.LNK-BF.DAT(T1)		;Get link to next
	JUMPE	T3,QNM2				;Done
IFN	FTDEBUG,<
	CAIN	T2,(T3)
	  STOPCD RELOAD,<DECnet buffer queued for write twice>
;+
;.lm 10
;.le
;Message:  "DECnet buffer queued for write twice"
;.br
;Type:  RELOAD
;	The same buffer was queued for write twice on a given link.
;This only occurs under FTDEBUG.
;-
>
	AOS	(P)				;Count buffer
	MOVEI	T1,(T3)				;Point to next
	JRST	QNM1				;Find end
QNM2:	HRLM	T2,BF.LNK-BF.DAT(T1)		;Link up buffers
	POP	P,T1				;Count
	CAIG	T1,QUEMAX
	  JRST	DEQN1
	HLRZ	T1,PC.LNK(B)
	JUMPE	T1,DEQN1
	HLL	T2,PC.FLG(T1)
	TXO	T2,PCSTP
	HLLM	T2,PC.FLG(T1)
DEQN1:	TXNE	F,PCCW				;Is link in CW?
	  POPJ	P,				;Don't dq anything if in CW
	HLRZ	T1,PC.IDQ(B)			;Get interrupt data queue ptr
	JUMPE	T1,DEQN2			;If none
	MOVEM	T1,PC.ARG+.NSAA1(B)		;Set this block as arg
	MOVE	T1,[<.NSFIS,,.NSAA1+1>]		;..
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  STOPCD ABORT,<DECnet SEND INTERRUPT failed>
;+
;.lm 10
;.le
;Message:  "DECnet SEND INTERRUPT failed"
;.br
;Type:  ABORT
;	A NSP_. function to send interrupt data failed.  The error code
;is in T1.  Some data may be lost.
;-
	JUMPE	B,CPOPJ				;Done
	MOVE	T1,PC.ARG+.NSAA1(B)		;Point to block again
	HLRZ	T2,.NSASL(T1)			;Get number written
	JUMPN	T2,DEQN2			;Didn't finish
	HLRZ	T2,BF.LNK-BF.DAT(T1)		;Else free this buffer
	HRLM	T2,PC.IDQ(B)			;Pointing queue to sucessorr
	MOVEI	T2,5+1
	PUSHJ	P,CORFRE
	JRST	DEQN1				;And send more
DEQN2:	TXZN	F,PCSTF				;Suspended transfer?
	  JRST	DEQN3				;No, just get the rest
	MOVE	T3,PC.BPT(B)			;Get byte pointer
	MOVEM	T3,PC.ARG+.NSAA2(B)
	MOVE	T3,PC.CNT(B)
	JRST	DEQN6				;Send data
DEQN3:	HLRZ	T3,PC.BUF(B)
	JUMPE	T3,DEQN7			;None to write
	HRLI	T3,(POINT 8,,)
	MOVEM	T3,PC.ARG+.NSAA2(B)
	HRRZ	T3,BF.CNT-BF.DAT(T3)
DEQN6:	MOVE	T1,[<.NSFDS,,.NSAA2+1>]
	TXZE	T3,BFEOM
	TXO	T1,NS.EOM
	MOVEM	T3,PC.ARG+.NSAA1(B)
	MOVEM	T1,PC.ARG+.NSAFN(B)		;Function send normal
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  STOPCD ABORT,<DECnet SEND NORMAL failed>
;+
;.lm 10
;.le
;Message:  "DECnet SEND NORMAL failed"
;.br
;Type:  ABORT
;	The NSP_. UUO to send normal data failed.  Error code in T1.
;Some data may have been lost.
;-
	JUMPE	B,CPOPJ				;Return if link aborted
	HRRZ	T3,PC.ARG+.NSAA1(B)		;Get count
	JUMPE	T3,DCNWR7			;Free buffer and process more
	MOVE	T1,PC.ARG+.NSAFN(B)		;Else was EOM set?
	TXNE	T1,NS.EOM
	TXO	T3,BFEOM
	MOVEM	T3,PC.CNT(B)
	MOVE	T3,PC.ARG+.NSAA2(B)
	MOVEM	T3,PC.BPT(B)			;Save aborted byte pointer
	TXO	F,PCSTF				;Suspended transfer
	POPJ	P,				;Return
DCNWR7:	HLRZ	T1,PC.BUF(B)
	HLRZ	T2,BF.LNK-BF.DAT(T1)
	HRLM	T2,PC.BUF(B)
	PUSHJ	P,FREBUF
	JRST	DEQN3

DEQN7:	HLRZ	T1,PC.LNK(B)			;Put other side
	JUMPE	T1,DEQN8			;(if there is one)
	EXCH	T1,B				;temporarily this side
	HLL	T2,PC.FLG(B)			;on proper polling queue
	TXZE	T2,PCSTP			;if input was stopped
	  PUSHJ	P,POLQUE
	HLLM	T2,PC.FLG(B)
	MOVE	B,T1
DEQN8:	TXZN	F,PCAL				;Aborting link?
	  POPJ	P,				;No, just return
	HLLM	F,PC.FLG(B)			;Update flags
	HRRZ	T3,PC.CDT(B)			;Get disconnect data (if any)
	MOVEM	T3,PC.ARG+.NSAA1(B)		;Put in correct place
	MOVE	T1,[.NSFSD,,.NSAA1+1]		;Get function
	MOVEM	T1,PC.ARG+.NSAFN(B)		;Set it
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,				;Send the disconnect
	  PUSH	P,[RELLNK]			;Kill it unconditionally
	HLLZS	PC.CDT(B)			;Get rid of disconnect data
	SKIPN	T1,PC.ARG+.NSAA1(B)		;Get addr of it if any
	  POPJ	P,				;None
	HRRZ	T2,.NSASL(T1)			;Get length
	PJRST	CORFRE				;And free it
>						;End FTDECNET
	Subttl	ANF Read data routine

;The (ANF) block pointed to by "B" is TSK.d to return all buffer
;strings, just like the DECnet routine.  Note that since ANF doesn't
;support a concept analagous to DECnet's interrupt data, T1 will always
;be returned as zero.

IFN	FTANF10,<
ANFRD:	PUSH	P,[0]				;Default no buffer
	MOVEI	T4,BF.DAT-BF.LNK(P)		;And point T4 to into it
	MOVEI	T3,MAXRED			;Maximum number of reads
ANFRD1:	PUSHJ	P,GETBUF			;Get a buffer
	MOVEI	T2,BF.BPT-BF.DAT(T1)		;Point to the buffer pointer
	HRLI	T2,BUFSIZ+1			;This is how big
	MOVEM	T2,(T2)				;Point buffer to itself
	HRRZM	T2,PC.BCI+.BFADR(B)		;Set the header up
	HRLI	T1,(BF.VBR)			;Also set virgin bit
	HLLM	T1,PC.BCI+.BFADR(B)
	HRLI	T1,(POINT 8,,)			;Byte mode
	HLLZM	T1,PC.BCI+.BFPTR(B)		;Set the pointer
	SETZM	PC.BCI+.BFCTR(B)		;Remember we ate it all
	MOVEI	T2,.TKFIN			;IN function
	MOVEM	T2,PC.ARG+.TKAFN(B)		;Set it
	MOVEI	T2,PC.ARG(B)
	HRLI	T2,.TKAA1+1			;The size
	TSK.	T2,
	  JRST	ANFRD5				;Failed, go see why
	HRLM	T1,BF.LNK-BF.DAT(T4)		;Point previous to us
	MOVEI	T4,(T1)				;And we are now current
	MOVE	T1,PC.BCI+.BFCNT(B)		;Get count of words
	MOVE	T2,PC.ARG+.TKAA1(B)		;Get DAP type
	CAIN	T2,DC.DAR			;EOR?
	TRO	T1,BFEOM			;Yes, set EOM bit
	HRRZM	T1,BF.LNK-BF.DAT(T4)		;Set the data
	MOVEM	T1,BF.BPT-BF.DAT(T4)		;Remember it
	TLNE	S,PS.RDO!PS.REF			;If link trying to shut down,
	  JRST	ANFRD1				;Read all
	SOJG	T3,ANFRD1			;Get more
	PUSHJ	P,ANFPOL			;If quota exceeded for polling
	MOVEM	S,PC.ARG+.TKACH(B)		;Be sure status is stored
	HLLM	F,PC.FLG(B)			;And flags
	JRST	ANFRD6

;Here if the TSK. failed

ANFRD5:	SETZM	PC.BCI+.BFADR(B)		;Clear the stale pointer
	PUSHJ	P,FREBUF			;Free the buffer
ANFRD6:	POP	P,T2				;Restore buffer string
	HLRZS	T2				;Make RH pointer
	SETZ	T1,				;No interrupt data
	POPJ	P,				;Done
	Subttl	ANF write routine

;Enter with T2=pointer to buffer string
;Using non-blocking I/O, all buffers are queued to ANF.
;If the last buffer is written and the abort bit is on, destroy
;the link.

ANFWR:	TLNE	S,PS.ROD			;Output done?
	SKIPG	PC.BCO+.BFCNT			;Maybe
	  JRST	ANFWR0				;Nope
	TXZ	F,PCSTF				;No longer suspended
	HRRZ	T1,PC.BCO(B)			;Point to the buffer
	MOVEI	T1,BF.DAT-BF.BPT(B)		;..
	PUSHJ	P,FREBUF
ANFWR0:	JUMPE	T2,ANFWR2			;Don't bother if nothing to q
	SETZ	T3,				;Initialize buffer count
	MOVEI	T4,PC.BUF+BF.DAT-BF.LNK(B)	;Point T4 to where to look
ANFWR1:	HRLI	T4,(T4)				;LH=predecessor
	HLR	T4,BF.LNK-BF.DAT(T4)		;RH=successor
	TRNE	T4,-1				;If there is one
	  AOJA	T3,ANFWR1
	HLRZS	T4				;Go to predecessor
	HRLM	T2,BF.LNK-BF.DAT(T4)		;We are the successor
	CAIG	T3,QUEMAX
	  JRST	ANFWR2
	HLRZ	T1,PC.LNK(B)			;Stop input if necessary
	JUMPE	T1,ANFWR2
	HLL	T2,PC.FLG(T1)
	TXO	T2,PCSTP
	HLLM	T2,PC.FLG(T1)
ANFWR2:	TXNE	F,PCCW				;In CW?
	  POPJ	P,				;Don't write if in CW
	TXNE	F,PCSTF				;If a suspended transfer,
	  JRST	ANFWR9				;See if we should restart it
ANFWR3:	HLRZ	T1,PC.BUF(B)			;Get the buffer
	JUMPE	T1,ANFWR8			;Check for abort
	HLRZ	T2,BF.LNK-BF.DAT(T1)		;Get next in q
	HRLM	T2,PC.BUF(B)			;Make it first
	MOVEI	T2,BF.BPT-BF.DAT(T1)		;Point to BPT word
	HRLI	T2,BUFSIZ+1			;Set use bit and size
	MOVEM	T2,(T2)				;Set BPT part of buffer
	HRRZM	T2,PC.BCO+.BFADR(B)		;Set address of buffer
	MOVSI	T3,(BF.VBR!BF.IBC)		;Set the virgin bit
	HLLM	T3,PC.BCO+.BFADR(B)
	HRLI	T1,(POINT 8,,)			;Make a byte pointer
	HRRZS	T2,BF.CNT-BF.DAT(T1)		;Get the count, clear link
	HRRM	T2,PC.CNT(B)			;Save as is in case need to restart
	MOVEI	T3,DC.DAT			;Assume no EOM
	TRZE	T2,BFEOM			;Clear EOM bit if DECnet
	  MOVEI	T3,DC.DAR			;Assume EOR then
	MOVEM	T3,PC.ARG+.TKAA1(B)		;Set DAP word for the OUT
	MOVEI	T3,BUFSIZ			;# of words in buffer
	SUBI	T3,(T2)				;minus number used
	SETOM	PC.BCO+.BFCTR(B)		;So can tell when monitor is done
	ADJBP	T2,T1				;Set new byte pointer
	HLLZM	T2,PC.BCO+.BFPTR(B)		;Set the byte pointer
ANFWR4:	MOVEI	T1,.TKFOT
	MOVEM	T1,PC.ARG+.TKAFN(B)		;Function out
	MOVEI	T1,PC.ARG(B)
	HRLI	T1,.TKAA1+1			;Size
	TXZE	F,PCSTF				;Is this suspended?
	  JRST	ANFWR5
	TSK.	T1,				;Do the dummy out
	  JFCL
	MOVEM	T2,PC.BCO+.BFPTR(B)		;Now do the "real" out
	SETOM	PC.BCO+.BFCNT(B)		;..
ANFWR5:	TSK.	T1,				;..
	  JRST	ANFWR7
ANFWR6:	HRRZS	T1,PC.BCO+.BFADR(B)		;Get the pointer
	MOVEI	T1,BF.DAT-BF.BPT(T1)
	PUSHJ	P,FREBUF			;Release the buffer
	JRST	ANFWR3				;And try next

;Here if TSK. fails

ANFWR7:	CAIE	T1,TKUDW%			;UUO failed?
	  JRST	TSKERR				;No, abort link
	HRL	T1,PC.ARG+.TKAA1(B)		;Get file status
	TLNE	T1,IO.BKT!IO.IMP		;Any errors?
	  JRST	TSKERR				;Abort link if so
	TXO	F,PCSTF				;Set suspended tranfer bit
	HLLM	F,PC.FLG(B)
	POPJ	P,				;Wait for done interrupt

ANFWR8:	SETZM	PC.BCO+.BFADR(B)		;No buffer here
	HLRZ	T1,PC.LNK(B)			;Queue empty,
	JUMPE	T1,ANFWRN			;If there is another side
	EXCH	T1,B				;Put in B temporarily
	HLLZ	T2,PC.FLG(B)			;Then clear the STP bit
	TXZE	T2,PCSTP
	PUSHJ	P,POLQUE			;put on polling queue
	HLLM	T2,PC.FLG(B)			;on its side
	MOVE	B,T1				;Get B back
ANFWRN:	TXZN	F,PCAL				;Aborting link?
	  POPJ	P,				;No, just return then
ANFREE:	MOVEI	T1,.TKFEI			;Put link in idle
	MOVEM	T1,PC.ARG+.TKAFN(B)
	MOVEI	T1,PC.ARG(B)
	HRLI	T1,.TKACH+1
	TSK.	T1,				;Do the TSK.
	  STOPCD NODUMP,<Can't put TSK. link into IDLE state>
;+
;.lm 10
;.le
;Message:  "Can't put TSK_. link into IDLE state"
;.br
;Type:  NODUMP
;	The TSK_. function to set a TSK link into idle state failed.  The
;error code is in T1.  Actually, this can occur under normal circumstances
;if the other side released the link already.  Since this stopcode seems
;to occur with alarming regularity, it is assembled as a NODUMP STOPCD.
;It can be patched to a INFO STOPCD if a dump is actually desired.
;-
	POPJ	P,				;Then don't release link yet


ANFWR9:	TLNN	S,PS.ROD			;Got an output done interrupt?
	  POPJ	P,				;Nope
	HRRZ	T1,PC.CNT(B)
	MOVEI	T2,DC.DAT
	TXNE	T1,BFEOM
	MOVEI	T2,DC.DAR
	MOVEM	T2,PC.ARG+.TKAA1(B)		;Set EOM correctly
	JRST	ANFWR4				;Do a TSK. and exit

TSKERR:	STOPCD ABORT,<I/O error on TSK. link>
;+
;.lm 10
;.le
;Message:  "I/O error on TSK_. link"
;.br
;Type:  ABORT
;	A TSK link suffered an I/O error.
;-
	POPJ	P,				;Return
>						;End FTANF10
	Subttl	String Parse Routines
;Enter with T1 pointing to buffer (which contains count)
;All temp ACs trashed here
;Exit with T1 pointing to next buffer on string, this one 

PARSE:	PUSH	P,T1				;Save buffer addr
	HLRZ	T1,PC.LNK(B)			;Already got one?
	JUMPN	T1,PARSEX
	PUSHJ	P,GETPCB			;Get a PCB
	HRLM	T1,PC.LNK(B)			;Set up link words
	HRLM	B,PC.LNK(T1)
PARSEX:	HRRZI	T2,PC.BUF+BF.DAT-BF.LNK(B)	;In case message is fragmented
	HLL	T2,BF.LNK-BF.DAT(T2)
	MOVSS	T2
	TRNE	T2,-1				;Reached end yet?
	  JRST	.-3				;No
	POP	P,T3				;Get buffer addr
	MOVSS	T2
	HRLM	T3,BF.LNK-BF.DAT(T2)
	MOVEI	T4,BFEOM			;Now see if EOM yet
PARSEY:	TDNE	T4,BF.CNT-BF.DAT(T3)
	  JRST	PARSEZ				;Yes, complete parse
	HLL	T3,BF.LNK-BF.DAT(T3)
	MOVSS	T3
	TRNE	T3,-1
	  JRST	PARSEY
	MOVSI	T2,(PCCW)			;Make sure nothing gets qd
	HLLM	T2,PC.FLG(T1)
	POPJ	P,				;No EOM, don't start parsing yet
PARSEZ:	TXZ	F,PCPW				;No longer waiting for string
	HLLM	F,PC.FLG(B)			;Save current flags
	HLRZ	T2,PC.BUF(B)			;Transfer buffer to here
	HRRZS	PC.BUF(B)
	HRLM	T2,PC.BUF(T1)
	HRRZ	T2,PC.CDT(B)			;Transfer connect data
	HRRM	T2,PC.CDT(T1)
	HLLZS	PC.CDT(B)			;..
	MOVEM	T3,PC.CNT(T1)			;Set count
	MOVEI	B,(T1)				;Set B,F for new block
	HRRZS	F
	HLRZ	T1,PC.BUF(B)			;Start of buffer
	HRLI	T1,(POINT 8,,)			;Make byte pointer
	HRRZ	T3,BF.CNT-BF.DAT(T1)		;Get the count
	ANDCMI	T3,BFEOM
	MOVEM	T3,PC.CNT(B)			;Store count
	MOVEM	T1,PC.BPT(B)			;And byte pointer
	PUSHJ	P,NXTCHR			;Get a character
	PRSERR	<Ran out of characters parsing string>,PRSER3
;HERE TO PARSE THE NODE NAME

	SETZ	T1,				;Clear T1
NODA:	PUSHJ	P,NXTCHR
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	CAIE	C,""""				;Access control info?
	CAIN	C,":"				;End?
	  JRST	NODB
	LSH	T1,6
	IORI	T1,-<"0"-'0'>(C)
	JRST	NODA
NODB:	JUMPE	T1,NODD
NODC:	TLNE	T1,770000			;Left justify the node name
	  JRST	NODD
	LSH	T1,6
	JRST	NODC
NODD:	MOVEM	T1,PC.NOD(B)			;Save the node name
;Here to parse access control information if it exists

	CAIE	C,""""				;Is there access control info?
	  JRST	NOACI				;No
	MOVEI	T1,.NSCUD+1			;Get a connect block
	PUSHJ	P,CORGET
	MOVEM	T1,PC.ARG+.NSAA1(B)		;Save where it is useful
	MOVEI	T3,.NSCUD+1			;And put length in
	MOVEM	T3,.NSCNL(T1)			;..
	MOVEI	T3,.NSCUS(T1)			;Point T3 to username area
	PUSH	P,[4]				;Count of things to scan
PARSE1:	MOVEI	T1,^D11				;Get core block for it
	PUSHJ	P,CORGET
	MOVEM	T1,(T3)
	HRLI	T1,(POINT 8,,35)		;Where to store characters
	MOVSI	T4,-<^D39+1>			;These are 39 character fields
PARSE2:	PUSHJ	P,NXTCHR			;Get next character
	 PRSERR	<Ran out of characters parsing string>,PRSER2
	CAIE	C," "				;End of user name?
	CAIN	C,""""				;??
	  JRST	PARSE3				;Yes
	IDPB	C,T1
	AOBJN	T4,PARSE2			;Continue as can
	 PRSERR	<Too many characters in access control field>
;Note the above works because 40 bytes are actually allocated
PARSE3:	HRLI	T4,^D11				;Size in words
	MOVSM	T4,@(T3)			;Store
	SOSLE	(P)				;More things to parse?
	CAIN	C,""""				;And not end of string?
	  JRST	PARSE4				;Nothing more or end
	AOJA	T3,PARSE1			;Continue

PARSE4:	POP	P,(P)				;Clear junk
	CAIN	C,""""				;Was last character quote?
	  JRST	PARS4A				;Yes
	PUSHJ	P,NXTCHR			;Else next one should be
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	CAIE	C,""""				;Is it?
	 PRSERR	<Access control information doesn't end with a quote>,PRSER3
PARS4A:	PUSHJ	P,NXTCHR
	 PRSERR	<Ran out of characters parsing string>,PRSER3
;Now parse destination
;Fill in rest of fields as well
NOACI:	CAIE	C,":"				;Colon?
	 PRSERR	<Node terminator not "::">,PRSER3
	PUSHJ	P,NXTCHR
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	CAIE	C,":"
	 PRSERR	<Node terminator not "::">,PRSER3
	PUSHJ	P,NXTCHR
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	MOVEI	T1,.OBPST			;Default not last
	CAIE	C,""""				;Are we?
	  JRST	PARSE9				;No
	PUSHJ	P,NXTCHR
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	CAIL	C,"0"
	CAILE	C,"9"				;Numeric type?
	  JRST	PARSE5				;No
	MOVEI	T1,-"0"(C)			;Set starting value
	PUSHJ	P,DECIN1
	PUSHJ	P,NXTCHR			;Eat the "="
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	TXO	F,PCLL				;Last
	JRST	PARSE9				;And finish up

PARSE5:	SETZ	T1,				;Clear the object name
PARSE6:	LSH	T1,7				;Shift over
	IORI	T1,(C)				;Move in character
	PUSHJ	P,NXTCHR
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	CAIE	C,"="
	JRST	PARSE6
	LSH	T1,1				;Must be at least one more
PARSE7:	TLNE	T1,774000			;Left justify it
	  JRST	PARSE8
	LSH	T1,7
	JRST	PARSE7
PARSE8:
	CAME	T1,[ASCII/TASK/]
	 PRSERR	<Final destination isn't numeric or TASK=>,PRSER3
;Here to parse an object name

PARSED:	TXO	F,PCLL			;This is the last link
	MOVE	T1,PC.ARG+.NSAA1(B)	;Is there already a connect block?
	JUMPN	T1,PARSD0		;Yes
	MOVEI	T1,.NSCUD+1		;Get the connect block
	PUSHJ	P,CORGET		;Get core for it
	MOVEI	T2,.NSCUD+1		;Mark its length
	MOVEM	T2,.NSCNL(T1)		;..
	MOVEM	T1,PC.ARG+.NSAA1(B)	;Store the block addr
PARSD0:	MOVEI	T2,(T1)			;Point T2 to the connect block
	MOVEI	T1,.NSDPN+1		;Get the destination object block
	PUSHJ	P,CORGET		;Get the core block
	MOVEM	T1,.NSCDD(T2)		;Save it
	MOVEI	T3,.NSDPN+1		;Set length
	MOVEM	T3,.NSDFL(T1)		;Save it
	MOVEI	T2,(T1)			;Now point T2 at process block
	MOVEI	T1,5			;Get core for process name
	PUSHJ	P,CORGET		;Get block
	MOVEM	T1,.NSDPN(T2)		;Set address
	MOVEI	T3,1			;Format type 1
	MOVEM	T3,.NSDFM(T2)		;Set it
	MOVEI	T3,5			;Set length
	HRRZM	T3,.NSASL(T1)		;..
	PUSH	P,T1			;Save pointer
	HRLI	T1,(POINT 8,,35)	;Make byte pointer
	MOVEI	T3,^D16			;Max characters in name
OBJA:	PUSHJ	P,NXTCHR
	 PRSERR	<Ran out of characters parsing string>,PRSER2
	CAIE	C,"/"			;Start of optional data?
	CAIN	C,""""			;End of string?
	  JRST	OBJB			;Yes, done
	IDPB	C,T1
	SOJGE	T3,OBJA			;More characters
	 PRSERR	<Object name more than 16 characters>,PRSER2

OBJB:	SUBI	T3,16+2			;Convert # of characters
	MOVNS	T3
	POP	P,T1
	HRLM	T3,.NSASL(T1)		;Set count
;Here with object type parsed if object name (block filled in)
;or with T1=object number if a numeric object

PARSE9:	SKIPE	T2,PC.ARG+.NSAA1(B)		;Already have connect block?
	  JRST	PARSD1				;Yes
	PUSH	P,T1				;Save T1
	MOVEI	T1,.NSCUD+1
	PUSHJ	P,CORGET
	MOVEM	T1,PC.ARG+.NSAA1(B)		;Save it
	MOVEI	T2,(T1)
	MOVEI	T1,.NSCUD+1
	MOVEM	T1,.NSCNL(T2)
	POP	P,T1
PARSD1:	TXNE	F,PCLL				;Is this the last link?
	CAIE	C,"/"				;Yes, was data specified?
	  JRST	NOOPT				;No, no optional data
	MOVEI	T1,^D16/4+1			;Get data
	PUSHJ	P,CORGET			;Get memory for it
	MOVEM	T1,.NSCUD(T2)			;Store it
	MOVEI	T4,^D16/4+1			;Set length
	HRRM	T4,.NSASL(T1)			;..
	MOVSI	T4,-^D16			;Max number of chars
	MOVEI	T3,(T1)				;Point T3 to block so
	HRLI	T3,(POINT 8,,35)		;As to make a byte pointer
OPT1:	PUSHJ	P,NXTCHR			;Get a character
	 PRSERR	<Ran out of characters parsing string>,PRSER3
	CAIN	C,""""				;End of string?
	  JRST	OPT4				;Yes
	IDPB	C,T3				;Put character in
	AOBJN	T4,OPT1				;Get more
	 PRSERR	<Too many characters in connect data>,PRSER3

OPT4:	HRLM	T4,.NSASL(T1)			;Store the character count
NOOPT:	SKIPE	.NSCDD(T2)			;Already set destination?
	  JRST	PARSD2				;Yes
	PUSH	P,T1				;Save object type
	MOVEI	T1,.NSDOB+1
	PUSHJ	P,CORGET
	MOVEI	T3,.NSDOB+1
	MOVEM	T3,.NSDFL(T1)
	POP	P,.NSDOB(T1)
	MOVEM	T1,.NSCDD(T2)			;Set it if none
PARSD2:	HRRZ	T1,PC.CDT(B)
	JUMPN	T1,PRSD2A			;If connect data, use it
	MOVEI	T1,.NSDOB+1			;Set source
	PUSHJ	P,CORGET
	MOVEI	T3,.OBPST
	MOVEM	T3,.NSDOB(T1)
	MOVEI	T3,.NSDOB+1
	MOVEM	T3,.NSDFL(T1)
PRSD2A:	MOVEM	T1,.NSCSD(T2)
	MOVEI	T1,3
	PUSHJ	P,CORGET			;Node name block
	HRRZM	T1,.NSCND(T2)
	MOVE	T2,PC.NOD(B)				;Get name
	PUSH	P,T1
	HRLI	T1,(POINT 8,,35)
PARSD3:	SETZ	T3,
	ROTC	T2,6
	MOVEI	T3,"0"-'0'(T3)
	IDPB	T3,T1
	AOS	@(P)
	JUMPN	T2,PARSD3
	POP	P,T1
	MOVEI	T3,3
	HRLM	T3,.NSASL(T1)
	MOVSS	.NSASL(T1)
IFN	FTANF10,<
	PUSHJ	P,TRYANF		;See if can do ANF connect
	  JRST	TSKWAT			;Maybe, wait for it to happen
>
DCNACT:
IFE	FTDECNET,<
	SETZ	T1,			;In case FTDECNET is off
>					;Fake error code 0 from NSP.
IFN	FTDECNET,<
	TXO	F,PCDCN			;No, this is a DECnet link then
	MOVEI	T1,PC.ARG(B)
	MOVE	T2,[<.NSFEA,,.NSAA1+1>]
	MOVEM	T2,.NSAFN(T1)
	NSP.	T1,
>
	  JRST	CNCFAL
IFN	FTDECNET,<
	TXO	F,PCCW			;Set this link to CW
	HLLZS	PC.CDT(B)		;Don't double-point to this
	PUSHJ	P,GIVBAK		;Return connect block etc.
	PUSHJ	P,DNPION
	  STOPCD INFO,<Can't add DECnet channel to the PI system>
;+
;.lm 10
;.le
;Message:  "Can't add DECnet channel to the PI system"
;.br
;Type:  INFO
;	Routine DNPION which does the NSP_. UUO to add a DECnet channel
;to the priority software interrupt system didn't skip.  Note that the affected
;link half is probably dead.

;-
	PUSHJ	P,ZAPARG		;Zap argument block so GIVBAK won't try
	HLLM	F,PC.FLG(B)		;Restore previous context & save this one
	HLRZ	B,PC.LNK(B)
	HLL	F,PC.FLG(B)
	POPJ	P,
>					;End FTDECNET
;Here to finish up the connects after the connect to the
;other side is completed

PARSER:
IFN	FTLOG,<
	PUSHJ	P,LOGCON		;Log the connection
>
	TXNN	F,PCLL			;Last link?
	  PJRST	SNDPST			;No, send the pass-thru string
	HLRZ	T2,PC.BUF(B)		;Get buffer
	HLRZ	T1,BF.LNK-BF.DAT(T2)	;Get next buffer
	HRRZS	BF.LNK-BF.DAT(T2)	;De-link this buffer
	HRLM	T1,PC.BUF(B)		;Store buffer
	HLLM	F,PC.FLG(B)		;Point back to initiating link
	HLRZ	B,PC.LNK(B)
	HRLM	T2,PC.BUF(B)		;Move buffer back
	HLL	F,PC.FLG(B)
	MOVEI	T1,1			;Send success string
	HRLI	T2,(POINT 8,,7)
	DPB	T1,T2			;Put in character
	TXO	T1,BFEOM		;EOM to be sent
	HRRZM	T1,BF.LNK-BF.DAT(T2)	;Count of 1
	HLLM	F,PC.FLG(B)		;Switch context back to original block
	HLRZ	B,PC.LNK(B)
	HLL	F,PC.FLG(B)
	POPJ	P,			;Return


SNDPST:
IFN	FTANF10&FTDECNET,<
	TXNE	F,PCANF			;Is this link ANF?
	  JRST	ANFPST			;Yes, can't do fancy forward pointing
>
IFN	FTDECNET,<
	MOVNI	T2,2			;Back up pointer 2 places
	ADJBP	T2,PC.BPT(B)
	MOVEM	T2,PC.BPT(B)
	MOVEI	T3,1
	IDPB	T3,T2			;Make a legal message
	MOVE	T2,PC.CNT(B)		;Get count
	ADDI	T2,2			;Account for character added etc.
	TXO	T2,BFEOM		;Set EOM
	MOVEM	T2,PC.CNT(B)
	TXO	F,PCSTF			;Fake a suspended transfer
	POPJ	P,			;And return
>
IFN	FTANF10,<
ANFPST:	MOVEI	T1,2
	ADD	T1,PC.CNT(B)		;Get the count
	HLRZ	T2,PC.BUF(B)		;Get beginning of buffer
	PUSH	P,T1			;Save unadulterated count
	TXO	T1,BFEOM		;and set EOM bit
	HRRM	T1,BF.CNT-BF.DAT(T2)	;Store the new count
	POP	P,T1			;Restore count
	HRLI	T2,(POINT 8,,)		;Make the pointer
	MOVEI	C,1			;Set correct first character
	IDPB	C,T2
	LDB	C,PC.BPT(B)		;Get character
	SOJA	T1,.+2			;Decrement count for "1"
	ILDB	C,PC.BPT(B)		;all other times increment
	IDPB	C,T2			;Store character
	SOJG	T1,.-2			;Decrenent count
	POPJ	P,			;Done
>

	Subttl	ANF routines

;Routine after calling TRYANF and getting success (non-skip) return
;to do minor bookeeping while we wait for confirm or reject

IFN	FTANF10,<
TSKWAT:	TXO	F,PCCW!PCANF		;Set link to CW
	HLLM	F,PC.FLG(B)		;Set flags
	HLRZ	B,PC.LNK(B)		;Point back
	HLL	F,PC.FLG(B)		;Restore those flags
	POPJ	P,			;And return
;Routine to try an ANF connection
;Enter:	PC.NOD(B)=node name (sixbit).
;Exit:  All T ACs preserved always.  CPOPJ if should wait for confirm or
;	reject from the TSK. Exit CPOPJ1 if must try DECnet (no such ANF
;	node, for example).

;Still under FTANF10
TRYANF:	PUSHJ	P,SAVT			;Save ACs
	MOVE	T1,PC.ARG+.NSAA1(B)	;Can't do format type 2,
	MOVE	T1,.NSCDD(T1)		;So fail now if it is
	MOVE	T1,.NSDFM(T1)
	CAIN	T1,2			;Is it?
	  JRST	CPOPJ1			;Yes, do DECnet connect
	MOVEI	T1,2			;Length of arg block
	MOVE	T3,[.NDRNN,,T1]		;And where it is
	MOVE	T2,PC.NOD(B)		;Get node name
	NODE.	T3,			;Is there such a node?
	  JRST	CPOPJ1			;No, restore ACs and return
	MOVEI	T1,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	PUSHJ	P,CORGET		;Get maximum needed for NPD
	MOVEM	T3,.TKNND(T1)		;Store node number
	MOVEI	T4,.TKNPN(T1)		;And byte pointer to string
	HRLI	T4,(POINT 7,,)		;..
	PUSHJ	P,NPDNUL		;Do a <NUL>
	MOVE	T2,PC.ARG+.NSAA1(B)	;Point to connect block
	MOVE	T2,.NSCDD(T2)		;And destination process block
	PUSHJ	P,NPDOBJ		;Do object
	MOVEI	T3,.OBPST		;Get our number
	HRRZ	T2,PC.CDT(B)		;Get connect dat if any
	JUMPE	T2,NOCDTA
	PUSHJ	P,NPDOBJ		;Do this object to
	JRST	OBJDON			;Done
NOCDTA:	PUSHJ	P,NPDOCT		;And put in
	PUSHJ	P,NPDNUL		;Do the <NUL>
OBJDON:	MOVE	T2,PC.ARG+.NSAA1(B)	;Get connect block again
	MOVEI	T3,.NSCUS+1(T2)
	CAMG	T3,.NSCNL(T2)
	SKIPN	T2,.NSCUS(T2)		;Username?
	  SKIPA
	PUSHJ	P,NPDSTR		;Yes, output it
	PUSHJ	P,NPDNUL		;Finish it off
	MOVE	T2,PC.ARG+.NSAA1(B)
	MOVEI	T3,.NSCPW+1
	CAMG	T3,.NSCNL(T2)
	SKIPN	T2,.NSCPW(T2)		;Any password?
	  SKIPA
	PUSHJ	P,NPDSTR
	PUSHJ	P,NPDNUL		;Yes, put in and nul it
	MOVE	T2,PC.ARG+.NSAA1(B)	;Likewise do account
	MOVEI	T3,.NSCAC+1
	CAMG	T3,.NSCNL(T2)
	SKIPN	T2,.NSCAC(T2)
	  SKIPA
	PUSHJ	P,NPDSTR
	PUSHJ	P,NPDNUL
	MOVE	T2,PC.ARG+.NSAA1(B)
	MOVEI	T3,.NSCUD+1		;Be sure the is some
	CAMG	T3,.NSCNL(T2)
	SKIPN	T2,.NSCUD(T2)		;Optional data?
	  SKIPA
	PUSHJ	P,NPDSTR		;Yes, do it
	PUSHJ	P,NPDNUL		;And add it in
	HRLI	T1,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	PUSH	P,T1			;Save pointer to NPD
	MOVEI	T1,.TKAA2+1		;Get block for args
	PUSHJ	P,CORGET		;Get it
	POP	P,.TKAA2(T1)		;Store NPD in correct place
	PUSH	P,T1			;Save arg block pointer
	MOVE	T2,[MYNODL,,MYNODE]
	MOVEM	T2,.TKAA1(T1)		;Set local NPD
	MOVE	T2,[FO.ASC!.FOSIO]
	MOVEM	T2,FLPBLK+.FOFNC	;Open TSK.
	HRLZI	T2,PC.BCO(B)
	HRRI	T2,PC.BCI(B)
	MOVEM	T2,FLPBLK+.FOBRH
	MOVE	T2,[FLPLEN,,FLPBLK]
	FILOP.	T2,
	  JRST	NOANF			;Oh well, try DECnet
	LDB	T2,[POINT 9,FLPBLK+.FOFNC,^L<FO.CHN>+9-1]
	MOVEM	T2,PC.ARG+.TKACH(B)	;Remember the channel
	PUSHJ	P,ANPION
	  STOPCD INFO,<Can't add ANF channel to PI system>
;+
;.lm 10
;.le
;Message:  "Can't add ANF channel to the PI system"
;.br
;Type:  INFO
;	ANPION, which does the appropriate PISYS_. UUO to add a
;new ANF channel to the priority software interrupt system, failed
;and didn't skip.  Note that the affected link half is probably
;dead.
;-
	MOVEM	T2,.TKACH(T1)		;And here
	MOVEI	T2,.TKFEA		;enter Active
	MOVEM	T2,.TKAFN(T1)
	HRLI	T1,.TKAA2+1		;Length of arg block
	TSK.	T1,			;Do the enter active
	  JRST	NOANF			;Oh well
	SOS	-1(P)			;Set non-skip return
NOANF:	POP	P,T4			;Restore block addr
	MOVE	T1,.TKAA2(T4)		;Get NPD addr
	MOVEI	T2,<<2*<3+1+^D16>>+<3*^D39>+^D16+7>/5+1+.TKNPN
	PUSHJ	P,CORFRE
	MOVEI	T1,(T4)
	MOVEI	T2,.TKAA2+1
	AOS	(P)			;Skip return
	TXO	F,PCCW			;Set the link to CW
	HLLM	F,PC.FLG(B)		;And link is in wait state
	PJRST	CORFRE			;Free core and return
;Subroutines to be used by the above

;Routine to put a <NUL> into the NPD string

NPDNUL:	SETZ	C,			;Get a <NUL>
					;Fall into NPDCHR

;Routine to place character in C in the NPD where T4 is the byte pointer
;to the next byte in the NPD; T1 is the address of the NPD

NPDCHR:	AOS	.TKNLN(T1)		;Increment length	
	IDPB	C,T4			;And put byte in
	POPJ	P,			;Return

;Routine to put an octal number given in T3 in ASCII format into the
;NPD string.  Implied inputs:  T1, T4 set up for NPDCHR calls

NPDOCT:	PUSHJ	P,SAV3			;Save T3
NPDOC1:	LDB	C,[POINT 3,T3,35]	;Get low order digit
	HRLM	C,(P)
	LSH	T3,-3			;And shift out character
	SKIPE	T3
	PUSHJ	P,NPDOC1
	HLRZ	C,(P)
	MOVEI	C,"0"(C)
	PJRST	NPDCHR

;Routine to put string from string block pointed to by T2 into the
;NPD pointed to by T1 and byte pointed to by T4

NPDSTR:	HLRZ	T3,.NSASL(T2)
	JUMPE	T3,CPOPJ
	HRLI	T2,(POINT 8,,35)	;Make a byte pointer
NPDST1:	ILDB	C,T2
	PUSHJ	P,NPDCHR
	SOJG	T3,NPDST1
	POPJ	P,			;return

;Routine to put NPD object pointed to by DECnet process block in T2
;into NPD string pointed to by T4.  Uses T2, T3, C.
;Updates pointer and count in .TKNLN(T1)

NPDOBJ:	MOVE	T3,.NSDOB(T2)		;Get object number
	PUSHJ	P,NPDOCT		;Output the number
	HRRZ	T3,.NSDFL(T2)		;Get length
	CAILE	T3,.NSDOB+1		;Can block have anything else?
	SKIPN	.NSDFM(T2)		;Format 0 or 1?
	  PJRST	NPDNUL			;Format 0 or can't have anything
	MOVE	T2,.NSDPN(T2)		;Get name string block
	MOVEI	C,"."			;Put in punctuation
	PUSHJ	P,NPDCHR		;..
	PUSHJ	P,NPDSTR		;Do the string
	PJRST	NPDNUL			;Punctuate
>			;end FTANF10
;Errors in the PSTHRU string
;Enter at PRSER2 with 1 junk item on the stack
;Enter at PRSER3 with nothing on the stack

PRSER2:	POP	P,-1(P)			;Clear stack
PRSER3:	POP	P,T2
	MOVE	T2,@T2
PRSER4:	PUSHJ	P,GIVBAK		;Return affiliated blocks
	HLRZ	T1,PC.LNK(B)
	HLRZ	T4,PC.BUF(B)		;Point back to buffer
	HRRZS	PC.BUF(B)		;Clear it out of here
	HRRZS	PC.LNK(T1)		;Destroy the link
	EXCH	T1,B
	PUSH	P,T2
	PUSHJ	P,FREPCB		;Return the PCB
	HLRZ	T2,BF.LNK-BF.DAT(T4)
	JUMPE	T2,PRSER6		;If any
PRSER5:	SETZ	T1,
	PUSHJ	P,BUFEAT

PRSER6:	POP	P,T2			;Restore T2
	HLL	F,PC.FLG(B)		;Point to this links flags
	PJRST	ABTLNK			;And abort the link

CNCFAL:
IFN	FTLOG,<
	PUSHJ	P,LOGFAL		;Log connection failure
>
	HLLZS	PC.CDT(B)		;Don't point
	MOVE	T2,DCNERR(T1)
	JRST	PRSER4
	Subttl	Logging

;The logging routines preserve all ACs.
;Assume one of the partners of the link to be logged is in B,
;Also assume P is set up
;Do not log anything if both sides of the link don't exist

IFN	FTLOG,<
;Log a disconnect

LOGDCN:	PUSHJ	P,SAVT				;Save ACs
	JSP	T1,LOGEVT			;The string addr in T1
	ASCIZ	/Link broken from /	;..

;Log a connection

LOGCON:	PUSHJ	P,SAVT				;Save the world
	JSP	T1,LOGEVT
	ASCIZ	/Link established from /	;..

;Log a reject

LOGREJ:	PUSHJ	P,SAVT
	JSP	T1,LOGEVT
	ASCIZ	/Connection rejected from /

;Log a connection failure

LOGFAL:	PUSHJ	P,SAVT
	JSP	T1,LOGEVT
	ASCIZ	/Connection failed from /

LOGEVT:
	PUSHJ	P,PIOSAV			;So core doesn't get messed up
	MOVSI	T2,(PCLNK)			;Do both sides exist?
	TDNE	T2,PC.LNK(B)			;??
	TRNN	F,FL$LOG			;Logging?
	  POPJ	P,				;No
	PUSH	P,.JBFF				;Keep monitor from messing up core
	MOVE	T2,[DEFLOG,,LOGLEB]
	BLT	T2,LOGLEB+3			;Clean log area
	MOVE	T2,[LOGLEN,,LOGBLK]		;Open the channel
	SETZM	LOBFH+.BFADR
	SETZM	LOBFH+.BFPTR
	SETZM	LOBFH+.BFCTR			;Zap buffer header to
	FILOP.	T2,
	  JRST	LOGOFF				;Turn logging off
	PUSHJ	P,DSKSTR			;Output string to disk
	MOVEI	T4,(B)				;Set B to the initiating side
	MOVSI	T1,(PCINI)
	TDNN	T1,PC.FLG(B)			;Is this it?
	HLRZ	T4,PC.LNK(B)			;No, must be other side
	PUSHJ	P,DSKNOD			;Output source node& chan
	MOVEI	T1,[ASCIZ/ to /]
	PUSHJ	P,DSKSTR			;Output this too
	HLRZ	T4,PC.LNK(T4)			;Other side
	PUSHJ	P,DSKNOD			;Output the node name
	MOVEI	T1,[ASCIZ/ at /]
	PUSHJ	P,DSKSTR
	MSTIME	T1,
	PUSHJ	P,DSKTIM			;Output time
	DATE	T1,
	PUSHJ	P,DSKDAT
	MOVEI	T1,[ASCIZ/
/]
CLSLOG:	PUSHJ	P,DSKSTR
	CLOSE	LOG,				;Close the LOG file
	RELEAS	LOG,				;Free the channel
	POP	P,T1
	MOVEM	T1,.JBFF			;Reset core boundaries
	CORE	T1,
	  JFCL
	POPJ	P,				;And return

LOGOFF:	POP	P,.JBFF				;Since FILOP failed
	TRZ	F,FL$LOG			;Don't try to log any more
	STOPCD	NODUMP,<Couldn't open log file, logging turned OFF>
;+
;.lm 10
;.le
;Message:  "Couldn't open log file, logging turned OFF"
;.br
;Type:  NODUMP
;	PSTHRU couldn't open the log file.  This usually means the
;appropriate logical name, PSTLOG, hasn't been defined.  Logging
;of events will not occur.
;-
	POPJ	P,
;Utility log routines
;Still under FTLOG

;Routine to output node and channel name from block pointed to by T4 (not B!)

DSKNOD:	MOVE	T1,PC.NOD(T4)			;Get node name
	PUSHJ	P,SIXDSK			;Output
	MOVSI	T3,(PCDCN!PCANF)		;Are any bits set?
	TDNN	T3,PC.FLG(T4)			;?
	  JRST	CPOPJ				;No channel then
	MOVEI	T1,[ASCIZ/ (DECnet-/]
	MOVSI	T3,(PCDCN)			;Default DECnet
	TDNN	T3,PC.FLG(T4)
	MOVEI	T1,[ASCIZ/ (ANF-/]
	PUSHJ	P,DSKSTR			;Output
	ASSUME	.NSACH,.TKACH
	HRRZ	T1,PC.ARG+.NSACH(T4)
	MOVEI	T2,DECDSK			;Assume DEcnet again
	TDNN	T3,PC.FLG(T4)
	MOVEI	T2,OCTDSK			;Nope, ANF
	PUSHJ	P,(T2)				;Output in proper format
	MOVEI	C,")"
	PJRST	DSKCHR				;Finish up

;Routine to output ASCIZ string pointed to by T1 to the log channel

DSKSTR:	HRLI	T1,(POINT 7,,)			;Make byte pointer
DSKST1:	ILDB	C,T1				;Get character
	JUMPE	C,CPOPJ				;Done
	PUSHJ	P,DSKCHR			;Output the character
	JRST	DSKST1

DSKCHR:	SOSGE	LOBFH+.BFCNT
	  JRST	DOLOUT
	IDPB	C,LOBFH+.BFPTR
	POPJ	P,				;Return

DOLOUT:	OUT	LOG,
	  JRST	DSKCHR
	STOPCD	INFO,<Logging output failed>
;+
;.lm 10
;.le
;Message:  "Logging output failed"
;.br
;Type:  INFO
;	An OUT UUO to the log file failed.  The log file will be closed.
;-
	JRST	CLSLOG

;Output a decimal number to the log file.  Number in T1, all Ts preserved

DECDSK:	PUSHJ	P,SAVT
	JUMPGE	T1,DCDS1		;If non-negative
	MOVEI	C,"-"
	PUSHJ	P,DSKCHR
	MOVNS	T1
DCDS1:	IDIVI	T1,^D10
	HRLM	T2,(P)
	SKIPE	T1
	PUSHJ	P,DCDS1
	HLRZ	C,(P)			;Get character
	MOVEI	C,"0"(C)
	PJRST	DSKCHR

;Output octal number to the log file.  Number in T1, all Ts preserved

OCTDSK:	PUSHJ	P,SAVT
OCTLPD:	LDB	C,[POINT 3,T1,35]
	HRLM	C,(P)
	LSH	T1,-3
	SKIPE	T1
	PUSHJ	P,OCTLPD
	HLRZ	C,(P)
	MOVEI	C,"0"(C)
	PJRST	DSKCHR

;Output 15-bit date in T1 to log file.  Saves Ts

DSKDAT:	PUSHJ	P,SAVT
	MOVEI	T3,(T1)
	IDIVI	T3,^D31
	MOVEI	T1,1(T4)
	PUSHJ	P,DECDSK
	IDIVI	T3,^D12
	MOVEI	T1,MONTAB(T4)
	PUSHJ	P,DSKSTR
	MOVNI	T1,^D64(T3)
	PJRST	DECDSK

;Output MSTIME in T1 into the log file.  Saves Ts

DSKTIM:	PUSHJ	P,SAVT
	IDIVI	T1,^D1000
TIMLP:	IDIVI	T1,^D60
	PUSH	P,T2
	SKIPE	T1
	PUSHJ	P,TIMLP
	POP	P,T1
	CAIL	T1,^D10				;Need extra leading zero?
	  JRST	NOL0				;No
	MOVEI	C,"0"
	PUSHJ	P,DSKCHR
NOL0:	PUSHJ	P,DECDSK
	MOVEI	C,":"
	PJRST	DSKCHR

;Output SIXBIT to LOG file.  Preserves Ts

SIXDSK:	JUMPE	T1,CPOPJ			;If nothing to do
	PUSHJ	P,SAVT
SIXDLP:	SETZ	T2,
	ROTC	T1,6				;Get a character
	MOVEI	C,"0"-'0'(T2)			;Convert to ASCII
	PUSHJ	P,DSKCHR
	JUMPN	T1,SIXDLP			;Back for more
	POPJ	P,
>
	Subttl	Routines to set up Enter Passive links

;subroutine to do a DECnet Enter Passive
;Call:
;	MOVEI	B,<address of passive link block>
;	PUSHJ	P,DCNENT
;	  <failed, reason code in T1>
;	<success>
;Uses T1-T4

IFN	FTDECNET,<
DCNENT:	MOVE	T1,[.NSFEP,,.NSAA1+1]
	MOVEM	T1,PC.ARG+.NSAFN(B)		;SET FUNCTIONS
	PUSHJ	P,SETCNB			;Set up connect block
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  POPJ	P,
	PUSHJ	P,PIOSAV			;No interrupts here
	PUSHJ	P,GIVBAK			;Give back all blocks
	HRLI	T1,(PCDCN!PCINI)		;THIS IS A DECnet LINK
	HLLM	T1,PC.FLG(B)			;SET IT SUCH
	PUSHJ	P,DNPION
	  POPJ	P,
	MOVEM	B,DCNCWL
	AOS	(P)
	PJRST	ZAPARG				;Clear argument block
;Routine to set up a CONNECT block and all its affiliated sub-blocks
;for a DECnet Enter Passive (or Read Connect Data)
;Uses T1-T4.
;Puts block in PC.ARG+.NSAA1(B)
;Still under FTDECNET

SETCNB:	MOVEI	T1,.NSCUD+1			;Length of connect block
	PUSHJ	P,CORGET
	MOVEM	T1,PC.ARG+.NSAA1(B)		;Set it in arg block
	MOVEI	T2,.NSCUD+1
	MOVEM	T2,.NSCNL(T1)			;LENGTH OF CONNECT BLOCK
	MOVEI	T2,(T1)				;Point T2 at connect block
	MOVEI	T1,.NSDOB+1
	PUSHJ	P,CORGET
	MOVEM	T1,.NSCDD(T2)
	MOVEI	T3,.OBPST			;PSTHRU OBJECT TYPE
	MOVEM	T3,.NSDOB(T1)			;SET IT
	MOVEI	T3,.NSDOB+1
	MOVEM	T3,.NSDFL(T1)
	MOVEI	T1,.NSDPN+1
	PUSHJ	P,CORGET			;Now get source block
	MOVEM	T1,.NSCSD(T2)
	MOVEI	T3,.NSDPN+1
	EXCH	T1,T3
	MOVEM	T1,.NSDFL(T3)
	MOVEI	T1,1				;Dummy format
	MOVEM	T1,.NSDFM(T3)
	MOVEI	T1,<^D16/4>+1
	PUSHJ	P,CORGET
	MOVEM	T1,.NSDPN(T3)			;In case a name
	MOVEI	T3,<^D16/4>+1
	MOVEM	T3,.NSASL(T1)
	MOVEI	T1,3
	PUSHJ	P,CORGET			;Name block
	MOVEM	T1,.NSCND(T2)
	MOVEI	T3,3
	MOVEM	T3,.NSASL(T1)
	MOVEI	T1,5				;Get connect data block
	PUSHJ	P,CORGET
	MOVEI	T3,5
	MOVEM	T3,.NSDFL(T1)
	MOVEM	T1,.NSCUD(T2)
	POPJ	P,				;Return
>
;subroutine to do an ANF10 enter passive

IFN	FTANF10,<

ANFENT:	MOVSI	T1,(FO.ASC)		;Assign extended channel
	HLLM	T1,FLPBLK+.FOFNC	;Set function
	HRLZI	T1,PC.BCO(B)
	HRRI	T1,PC.BCI(B)		;Get buffer headers
	MOVEM	T1,FLPBLK+.FOBRH	;Set them
	MOVE	T1,[FO.ASC!.FOSIO]
	MOVEM	T1,FLPBLK+.FOFNC
	MOVE	T1,[FLPLEN,,FLPBLK]
	FILOP.	T1,			;Open the channel
	  POPJ	P,			;Failed
	LDB	T1,[POINT 9,FLPBLK+.FOFNC,^L<FO.CHN>+9-1]
	MOVEM	T1,PC.ARG+.TKACH(B)	;Set the channel number for TSK.
	PUSHJ	P,ANPION		;Turn on PSISER
	  POPJ	P,			;Failed
	MOVE	T1,[MYNODL,,MYNODE]
	MOVEM	T1,PC.ARG+.TKAA1(B)	;Set it
	MOVE	T1,[WLDNPL,,WLDNPD]
	MOVEM	T1,PC.ARG+.TKAA2(B)	;Set other side
	MOVEI	T1,.TKFEP
	MOVEM	T1,PC.ARG+.TKAFN(B)
	MOVEI	T1,PC.ARG(B)
	HRLI	T1,.TKAA2+1
	TSK.	T1,
	  JRST	ENTFAI				;Failed
	AOS	(P)
	MOVEM	B,ANFCWL			;This is the CW link
	HRLI	T1,(PCANF!PCINI)
	HLLM	T1,PC.FLG(B)			;Set ANF bit in flags
ENTFAI:	PJRST	ZAPARG				;Kill stale pointers
>
	Subttl	Parsing I/O

;Routine to get next character from buffer chain pointed to
;by PC.BPT (count PC.CNT) or PC.BUF, all (B).
;Return with character in C, translated to upper case if necessary
;and also stripped of parity

NXTCHR:	SOSGE	PC.CNT(B)		;Any more in this buffer?
	  JRST	NEWBUF			;No
	ILDB	C,PC.BPT(B)		;Else get a character
	ANDI	C,177			;Mask to 7 bits
	CAIL	C,"a"			;In the lower case range?
	CAILE	C,"z"			;??
	  JRST	CPOPJ1			;No, just get next character
	ANDCMI	C,<"a"-"A">		;Yes, translate
	JRST	CPOPJ1			;And return

NEWBUF:	PUSHJ	P,SAVT			;Save ACs
	PUSHJ	P,PIOSAV		;Turn PSISER off
	HLRZ	T1,PC.BUF(B)		;Get buffer pointer
	MOVEI	T2,BFEOM		;Was this end of message?
	TDNE	T2,BF.CNT-BF.DAT(T1)	;??
	  JRST	CPOPJ			;Yes, lose
	HLRZ	T3,BF.LNK-BF.DAT(T1)	;Else get next buffer
	PUSHJ	P,FREBUF		;Free this one
	HRLM	T3,PC.BUF(B)		;Make this one current
	HRLI	T3,(POINT 8,,)		;Make the byte pointer
	MOVEM	T3,PC.BPT(B)
	HRRZ	T3,BF.CNT-BF.DAT(T3)	;Set the count
	ANDCMI	T3,BFEOM
	MOVEM	T3,PC.CNT(B)
	JRST	NXTCHR
	Subttl	Add new links to PSISER status

IFN	FTANF10,<

ANPION:	PUSHJ	P,SAVT				;Save ACs
	MOVE	T1,[PS.FAC!T2]			;The arg pointer
	HRRZ	T2,PC.ARG+.TKACH(B)		;The channel
	MOVE	T3,[ANFOFF,,PS.RID!PS.REF!PS.RIE!PS.ROE!PS.RDF!PS.RQE!PS.RRC!PS.ROD!PS.RDO!PS.ROL]
	SETZ	T4,
	PISYS.	T1,
	  POPJ	P,				;Failed
	JRST	CPOPJ1				;Success
>

IFN	FTDECNET,<

DNPION:	PUSH	P,T1
	MOVE	T1,[.NSFPI,,.NSAA1+1]
	MOVEM	T1,PC.ARG+.NSAFN(B)
	MOVEI	T1,(NS.STA!NS.NDR!NS.IDR!NS.NDA!NS.IDA)
	MOVEM	T1,PC.ARG+.NSAA1(B)
	MOVEI	T1,PC.ARG(B)
	NSP.	T1,
	  JRST	TPOPJ
	JRST	TPOPJ1
>
	Subttl	Core management routines

comment	&
	The following strategy is what will be implemented at the
current time:
	1.  Blocks will be variable length
	2.  The allocator will first check a linked free list
	    of blocks.
	3.  If a free block doesn't exist on the free list, the allocator
	    will see if a block will fit between .JBFF and HICORE.  If so,
	    the block will be allocated here.
	4.  If a block won't fit, the HICORE will be expanded via a CORE
	    UUO to increase.
	5.  The de-allocator will return blocks to the free list.  Currently,
	    it will not shrink core size.
	6.  The de-allocator will merge blocks together.
&
;ROUTINE TO ALLOCATE A PCB.  RETURNS POINTER IN T1; USES T1-T2
;Enter with size of PCB desired in T1 at GETPC1 if non-standard
;sized PCB is desired, else enter at GETPCB.

GETPCB:	MOVEI	T1,PC.LEN
GETPC1:	PUSH	P,T1
	PUSHJ	P,CORGET
	POP	P,PC.SIZ(T1)
	MOVE	T2,BLKPTR
	HRRZM	T2,PC.NXT(T1)
	MOVEM	T1,BLKPTR
	POPJ	P,
;ROUTINE TO FREE A PCB BLOCK.  POINTER TO BLOCK IN T1

FREPCB:	PUSH	P,T1		;Save T1
	SKIPN	T1,PC.BCO+.BFADR(T1)	;Get ANF buffer if any
	  JRST	FREPC1			;None
	MOVEI	T1,BF.DAT-BF.BPT(T1)	;Point to data area
	PUSHJ	P,FREBUF		;Free the buffer
FREPC1:	MOVE	T1,(P)			;Restore T1
	HLRZ	T2,PC.BUF(T1)	;Free normal buffers
	HLRZ	T1,PC.IDQ(T1)	;Free interrupt buffers eaten up
	PUSHJ	P,BUFEAT
	MOVE	T1,(P)		;Restore block pointer
IFN	FTDECNET,<
	SKIPN	T2,DPLQUE	;Check DECnet queue first
	  JRST	CHKAPL		;None, check ANF queue
	CAME	T1,T2		;Same as head?
	  JRST	CKDCN1
	HLRZ	T2,PC.POL(T1)
	MOVEM	T2,DPLQUE	;Make successor us
	JRST	POLCHK		;Done
CKDCN1:	HRLI	T2,(T2)		;Predecessor is us
	HLR	T2,PC.POL(T2)	;Successor is current
	TRNN	T2,-1		;Any?
	  JRST	CHKAPL		;No
	CAIE	T1,(T2)		;Is it us?
	  JRST	CKDCN1		;No
	HLR	T2,PC.POL(T2)	;Pred,,succ to current block
	MOVSS	T2		;Succ,,Pred
	HLLM	T2,PC.POL(T2)	;Take us out
	JRST	POLCHK		;Done
>
CHKAPL:
IFN	FTANF10,<
	SKIPN	T2,APLQUE	;Anything here?
	  JRST	POLCHK		;No
	CAME	T1,T2		;Are we at the head?
	  JRST	CKAPL1		;No
	HLRZ	T2,PC.POL(T1)
	MOVEM	T2,APLQUE
	JRST	POLCHK		;Done if we were at the head
CKAPL1:	HRLI	T2,(T2)		;Predecessor is us
	HLR	T2,PC.POL(T2)	;Successor is currnet
	TRNN	T2,-1
	  JRST	POLCHK		;Done
	CAIE	T1,(T2)		;Us?
	  JRST	CKAPL1		;No
	HLR	T2,PC.POL(T2)	;Pred,,succ
	MOVSS	T2,
	HLLM	T2,PC.POL(T2)
>
POLCHK:	HRRZ	T1,PC.CDT(T1)	;Get connect/disconnect data if any
	JUMPE	T1,NODSC
	HRRZ	T2,.NSASL(T1)	;Get length if there is
	PUSHJ	P,CORFRE	;Deallocate it
NODSC:	MOVE	T1,(P)		;Get block addr again
	PUSHJ	P,GETPRD	;Get the predecessor
	POP	P,T2		;Get the block back
	HRL	T2,PC.LNK(T2)	;Get successor to us
	HLRM	T2,PC.LNK(T1)	;Point predecessor to him
	HRRZI	T1,(T2)		;Get block addr again
	HRRZ	T2,PC.SIZ(T1)
	PJRST	CORFRE
;Routine to give back the connect block pointed to by
;PC.ARG+.NSAA1(B).  Gives back all affiliated blocks first.


GIVBAK:	PUSHJ	P,SAVT
	SKIPN	T4,PC.ARG+.NSAA1(B)
	  POPJ	P,			;Nothing to return
	HRRZ	T3,.NSCNL(T4)		;Length of the block
	SOJLE	T3,GIVBK5		;-1=max offset
	HRLI	T4,T3			;Index by it
GIVBK1:	SKIPN	T1,@T4			;If no block there
	  JRST	GIVBK4			;Don't give one back
	CAIE	T3,.NSCSD		;Source block?
	CAIN	T3,.NSCDD		;Destination block?
	  SKIPA				;Yes, may be more blocks here
	JRST	GIVBK3			;No, no blocks on it then
	HRRZ	T2,.NSDFL(T1)		;Does this block contain a string block?
	CAIGE	T2,.NSDPN+1		;??
	  JRST	GIVBK3			;No
	PUSH	P,T1			;Save process block pointer
	SKIPE	T1,.NSDPN(T1)		;Get the string block if there is one
	PUSHJ	P,CORFRE		;Free it as well
	POP	P,T1
GIVBK3:	HRRZ	T2,(T1)			;Get length
	PUSHJ	P,CORFRE
GIVBK4:	SOJG	T3,GIVBK1		;Loop over all blocks
GIVBK5:	MOVEI	T1,(T4)			;Now kill connect block
	HRRZ	T2,.NSCNL(T1)
	PJRST	CORFRE
	Subttl	Core Allocation

;Core allocator
;Enter with T1=size of block desired
;Exit with T1=pointer to block.

CORGET:	PUSHJ	P,PIOSAV		;PI OFF if necessary
	PUSH	P,T2			;Save the world
	PUSH	P,T3
	PUSH	P,T4
	SKIPN	T2,FRELST		;Any blocks on the free list?
	JRST	CORG20			;No, make the block
	SETZB	T3,T4			;Not remembering any block
	HRLI	T2,FRELST		;Remember precessor
CORG1:	PUSH	P,(T2)			;Push size of block
	HLRZS	(P)			;Move to right half
	CAMN	T1,(P)			;Same size as we want?
	JRST	CORG7			;Yes, allocate it
	CAML	T1,(P)			;Do we want smaller than this block?
	JRST	CORG6			;No, don't remember it
	JUMPE	T3,CORG4		;If not remembering anything, then this
	CAMG	T4,(P)			;Else is this closer to the size than previous remembered?
	JRST	CORG6			;No, remember previous then
CORG4:	MOVE	T3,T2			;Remember this block & its predecessor
	MOVE	T4,(P)			;And its size
CORG6:	POP	P,(P)			;Fix stack
	HRLI	T2,(T2)			;Remember precessor
	HRR	T2,(T2)			;Point to successor
	TRNE	T2,-1			;If there is one
	JRST	CORG1			;There is, check it out
	JUMPE	T3,CORG20		;If didn't find anything
	MOVE	T2,T3			;Point T2 to block we want
	PUSH	P,(T2)			;And push its size
	HLRZS	(P)			;on the right side
CORG7:	CAMN	T1,(P)			;Size the same as we want?
	JRST	CORG9			;Yes, just de-link it then
	PUSH	P,(P)			;Duplicate size of desired block
	SUBM	T1,(P)			;No, subtract out what we want
	MOVNS	(P)			;Leaving size left
	HRRZI	T3,(T2)			;Start of the block as it is
	ADDI	T3,(T1)			;Where it will now start
	POP	P,(T3)			;Put new size in
	MOVSS	(T3)			;left half, of course
	HRL	T3,(T2)			;Get successor
	HLRM	T3,(T3)			;and place in new block
	MOVSS	T2			;Point to predecessor
	HRRM	T3,(T2)			;Point it to new block
	HRRZM	T1,(P)			;Put desired size on
	HLRZ	T1,T2			;Return with new block in T1
	JRST	CORG30			;Cleared, of course

CORG9:	HRRZI	T1,(T2)			;Point T1 to desired block
	HRRZ	T3,(T2)			;Get successor
	MOVSS	T2			;Get predecessor
	HRRM	T3,(T2)			;Link to new sucessor
	JRST	CORG30			;Clear it and return

CORG20:	PUSH	P,T1			;Save size
	MOVE	T2,.JBFF		;Point to first free location
	ADDI	T2,(T1)			;The address which must be allocated to
	CAMLE	T2,HICORE		;Does that far exist?
	JRST	CORG23			;No, go allocate it
	MOVE	T1,.JBFF		;Point to block
	MOVEM	T2,.JBFF		;Update first free
	JRST	CORG30			;Clear and return

CORG23:	MOVE	T1,.JBFF		;Point to new block
	MOVEM	T2,.JBFF		;Store pointer to new
	IORI	T2,777			;To next page
	MOVEM	T2,HICORE		;Save max
	CORE	T2,			;Get it
	  STOPCD RELOAD,<CORE UUO failed>
;+
;.lm 10
;.le
;Message:  "CORE UUO failed"
;.br
;Type:  RELOAD
;	A CORE UUO to create more free core or buffers failed.
;-

CORG30:	EXCH	T1,(P)			;Exchange size for address
	HRRZ	T2,(P)			;Make BLT pointer
	HRLI	T2,1(T2)		;..	
	SETZM	(T2)			;Clear first location
	SOJLE	T1,CORG40		;Skip the BLT if only one word
	MOVSS	T2
	ADD	T1,(P)			;Last location(+1)
	BLT	T2,(T1)			;Clear core
CORG40:	POP	P,T1
	POP	P,T4
	POP	P,T3
	POP	P,T2
	POPJ	P,
	Subttl	Core De-allocator
;Enter with T1=address of block to free
;T2=# of words to free
;All ACs respected

CORFRE:	IFN	FTDEBUG,<
	SKIPE	T1
	SKIPN	T2
	  STOPCD RELOAD,<Returning junk core block>
;+
;.lm 10
;.le
;Message:  "Returning junk core block"
;.br
;Type:  RELOAD
;	This is only enabled under FTDEBUG.  The core de-allocator
;was called with either a base address of zero or a length of zero
;to return to the free core pool.
;-
>
	PUSHJ	P,PIOSAV
	TRZ	F,FL$P2			;First pass
	PUSHJ	P,SAVT
	SETZM	(T1)			;No links here yet!
	ADDI	T2,(T1)			;Compute end address(+1)
	HRLI	T3,FRELST
	HRR	T3,FRELST		;Get free list pointer
	TRNN	T3,-1
	  JRST	CORF10
CORF1:	HLRZ	T4,(T3)			;Get size of block
	ADDI	T4,(T3)			;Compute its last addr(+1)
	CAME	T4,T1			;Does that block end at us?
	  JRST	CORF4			;No
	SUBI	T2,(T1)			;Get length again
	TRON	F,FL$P2			;Pass 2?
	  JRST	CORF2			;No, don't return yet
	MOVSS	T2			;Put in left half
	ADDM	T2,(T3)			;Make that block bigger
	POPJ	P,			;And return

CORF2:	HLRZ	T4,(T3)			;Get length of existing block
	ADDI	T2,(T4)			;Add length of new block
	MOVEI	T1,(T3)			;Point T1 at new larger block
CORF3:	HRR	T3,(T3)			;Point T3 at successor
	MOVSS	T3			;Get predecessor
	HLRM	T3,(T3)			;Point it at sucessor
	SETZM	(T1)			;No links yet to new block
	ADDI	T2,(T1)
	MOVSS	T3			;Point T3 at current block
	JRST	CORF9			;And continue scan

CORF4:	CAIE	T2,(T3)			;Does our block end at his?
	  JRST	CORF7			;No
	SUBI	T2,(T1)			;Get length again
	HLRZ	T4,(T3)			;And length of this block
	ADDI	T2,(T4)			;Get length of new block
	TRON	F,FL$P2			;Pass 2?
	  JRST	CORF3			;No, de-link and continue search
	HRLM	T2,(T1)			;Store in proper place
	HRRZ	T2,(T3)			;Get successor block
	HRRM	T2,(T1)			;And point us to him
	MOVSS	T3			;Get predecessor
	HRRM	T1,(T3)			;And point him to us
	POPJ	P,			;Return

CORF7:	HRLI	T3,(T3)			;Remember us as prececessor
	HRR	T3,(T3)			;Get successor
CORF9:	TRNE	T3,-1			;See if there is one
	  JRST	CORF1			;Yes, check it
CORF10:	MOVSS	T3			;No, point back to last block
	HRRM	T1,(T3)			;Save
	SUBI	T2,(T1)			;Get real length again
	HRLM	T2,(T1)			;Store it
	POPJ	P,
	Subttl	Buffer Handling routines

;Note that the header word is "invisible" to the calling routines -
;i.e. GETBUF and FREBUF return/expect T1 to point to the data area of
;the buffer.

;Allocate a buffer:  Uses T1

GETBUF:	PUSHJ	P,PIOSAV		;No interrupts here
	PUSH	P,T2
	SKIPE	T1,BUFPTR		;See if any cached buffers
	  JRST	HAVBUF			;Yes, win
	MOVEI	T1,BF.LST		;Else allocate one from free core
	PUSHJ	P,CORGET

HAVBUF:	HLRZ	T2,BF.LNK(T1)		;Link to next
	HRRZM	T2,BUFPTR		;Update
	SETZM	BF.LNK(T1)		;Clear this word
	MOVEI	T1,BF.DAT-BF.LNK(T1)	;Return pointer to data
	POP	P,T2			;Restore T2
	POPJ	P,


;Free a buffer:  address in T1

FREBUF:	IFN	FTDEBUG,<
	CAM	(T1)			;See if it exists
>
	PUSHJ	P,PIOSAV
	MOVEI	T1,BF.LNK-BF.DAT(T1)
IFN	FTDEBUG,<
	PUSH	P,T2
	MOVE	T2,BUFPTR
	JUMPE	T2,DEB2
DEB1:	CAIN	T1,(T2)
	HALT	.
	HLRZ	T2,(T2)
	JUMPN	T2,DEB1
DEB2:	POP	P,T2
>
	EXCH	T1,BUFPTR
	ASSUME	BF.LNK,0
	HRLZM	T1,@BUFPTR
	POPJ	P,
	Subttl	STOPCD processor

;+
;.end list
;-

DIE:	MOVEM	17,CRSACS+17			;Save ACs in dump
	MOVE	17,[0,,CRSACS]
	BLT	17,CRSACS+16
	MOVE	17,CRSACS+17			;Restore AC 17
	LDB	T1,[POINT 4,@(P),12]		;Get the AC field
	CAIL	T1,NODUMP			;One of the no-dump STOPCDs?
	  JRST	DIDDMP				;Yes
	MOVE	T1,[SIXBIT/PSDMP/]
	OPEN	[	.IODMP			;See if the file exists
		 SIXBIT/XPN/
			Z	]
	  HALT	.				;Stop if can't dump anywhere
FNDDMP:	MOVEM	T1,DMPFIL
	SETZM	DMPPPN
	SETZM	DMPPPN+1			;Be sure these are zapped
	LOOKUP	DMPFIL
	  JRST	DODMP
	AOJA	T1,FNDDMP			;Look again
DODMP:	HLLZS	DMPEXT
	SETZM	DMPPPN
	SETZM	DMPPPN+1
	SETZM	DMPCOR
	MOVEI	T1,DMPDEV
	SAVE.	T1,
	  HALT	.				;If can't dump, then halt
	MOVE	P,CRSACS+P			;Restore P since SAVE.
DIDDMP:	HRRZI	T1,@(P)				;Point to ASCIZ string
	HRRZI	T1,@(T1)
	HRLI	T1,(POINT 7,,)
	MOVSI	T2,-QUMLEN*5			;Queue message length
	MOVE	T3,[POINT 7,QUEMSG+.QBMAS]	;Where to copy to
CPYERR:	ILDB	C,T1				;Get character
	IDPB	C,T3
	JUMPE	C,DOQUE				;Done, do the QUEUE.
	AOBJN	T2,CPYERR			;Copy unless can't
DOQUE:	HRRZI	T2,(T2)				;Number of bytes
	JUMPE	T2,NOMSG			;Don't bother if nothing
	IDIVI	T2,5				;Convert to words
	SKIPE	T3				;Round up if necessary
	  AOJ	T2,
	AOJ	T2,				;Now include length
	HRLM	T2,QUEBLK+.QUARG
	MOVE	T1,[.QUARG+2,,QUEBLK]
	QUEUE.	T1,
	  HALT	.				;If that fails
NOMSG:	LDB	T1,[POINT 4,@(P),12]		;Get STOPCD type
	MOVE	T1,[[HALT	.]		;Save dispatch:  STOP
		    RLD				;Reload
		    ABT				;Abort
		    CPOPJ			;Informational
		    CPOPJ	 ](T1)		;Informational w/o dump
	MOVEM	T1,DIEPRC			;Save dispatch
	MOVSI	17,CRSACS			;Restore ACs
	BLT	17,16
	MOVE	17,CRSACS+17
	PJRST	@DIEPRC				;Process and return


;Reload processor

RLD:	HRROI	T1,.GTRDV			;Device run from
	GETTAB	T1,
	  MOVSI	T1,'SYS'			;Assume SYS: if fail
	MOVEM	T1,RUNDEV			;Store it
	HRROI	T1,.GTRFN			;File name
	GETTAB	T1,
	  MOVE	T1,[SIXBIT/PSTHRU/]		;Default
	MOVEM	T1,RUNFIL			;Set it
	SETZM	RUNEXT				;That also
	HRROI	T1,.GTRDI			;Directory
	GETTAB	T1,
	  SETZ	T1,				;Default if can't find
	PUSH	P,[0]				;First two words are zero
	HRRZM	P,RUNPPN			;P points to path block
	PUSH	P,[0]				;..
	PUSH	P,T1				;Put PPN in
	MOVE	T1,[%LDSFD]			;Maximum number
	GETTAB	T1,				;of SFDs
	  MOVEI	T1,5				;"normal" default
	MOVNS	T1
	MOVSI	T1,(T1)				;Make aobjn
	ASSUME	.GTRS1,<<.GTRS0+1>>
	ASSUME	.GTRS2,<<.GTRS1+1>>
	ASSUME	.GTRS3,<<.GTRS2+1>>
	ASSUME	.GTRS4,<<.GTRS3+1>>
SFDLP:	HRROI	T2,.GTRS0(T1)
	GETTAB	T2,
	  SETZ	T2,
	PUSH	P,T2				;Save it
	AOBJN	T1,SFDLP
	MOVEI	T1,RUNBLK
	RUN	T1,
	  HALT	.				;If fail

;Abort processor

ABT:
	PUSHJ	P,SAVT				;Save the Ts (just in case)
IFN	FTDECNET,<
	MOVEI	T1,DCNDR			;Assume DECnet
>
IFN	FTDECNET&FTANF10,<
	HLL	F,PC.FLG(B)			;Must be the link in B
	TXNN	F,PCDCN				;DECnet?
>
IFN	FTANF10,<
	MOVEI	T1,ANFOFL			;No, must be ANF
>
	TXO	F,PCMAB				;We are aborting link
	HLLM	F,PC.FLG(B)
	PUSHJ	P,(T1)				;Call disconnect routine
	  JFCL					;Ignore any skip
	SETZ	B,				;This link doesn't exist
	POPJ	P,				;Anymore
	Subttl	Returns

TPOPJ1:	POP	P,T1
CPOPJ1:	AOSA	(P)
TPOPJ:	POP	P,T1
CPOPJ:	POPJ	P,
	Subttl	AC saving routines

SAVT:	EXCH	T1,(P)
	PUSH	P,T2
	PUSH	P,T3
	PUSH	P,T4
	PUSH	P,[REST]		;Do it this way so no interrupt
	PUSH	P,T1			;stuff clobbers the stack
	MOVE	T1,-5(P)
	POPJ	P,
REST:	SKIPA
	AOS	-4(P)
	POP	P,T4
	POP	P,T3
	POP	P,T2
	POP	P,T1
	POPJ	P,


SAV3:	EXCH	T3,(P)
	PUSH	P,[RES3]
	PUSH	P,T3
	MOVE	T3,-2(P)
	POPJ	P,
RES3:	SKIPA
	AOS	-1(P)
	POP	P,T3
	POPJ	P,
	Subttl	Message table

	DEFINE	ERRMSG(TEXT)<
	[ASCIZ/'TEXT'/]
>

DCNERR:	ERRMSG	<ANF connection failed and FTDECNET is off>
	ERRMSG	<Argument error>
	ERRMSG	<Allocation failure>
	ERRMSG	<Bad channel>
	ERRMSG	<Bad format type>
	ERRMSG	<Connect block format error>
	ERRMSG	<Interrupt data too long>
	ERRMSG	<Illegal flow control mode>
	ERRMSG	<Illegal function>
	ERRMSG	<Job quota exhausted>
	ERRMSG	<Link quota exhausted>
	ERRMSG	<No connect data to read>
	ERRMSG	<Percentage input out of bounds>
	ERRMSG	<No privileges>
	ERRMSG	<Segment size too big>
	ERRMSG	<Unknown node name>
	ERRMSG	<Unexpected state: Unspecified>
	ERRMSG	<Wrong number of arguments>
	ERRMSG	<Function called in wrong state>
	ERRMSG	<Connect block length error>
	ERRMSG	<Process block length error>
	ERRMSG	<String block length error>
	ERRMSG	<Unexpected state:  Disconnect sent>
	ERRMSG	<Unexpected state:  Disconnect confirmed>
	ERRMSG	<Unexpected state:  No confidence>
	ERRMSG	<Unexpected state:  No link>
	ERRMSG	<Unexpected state:  No communication>
	ERRMSG	<Unexpected state:  No resources>
	ERRMSG	<Connect rejected>
	ERRMSG	<Rejected or disconnected by object>
	ERRMSG	<No resources>
	ERRMSG	<Unrecognized node name>
	ERRMSG	<Remote node shut down>
	ERRMSG	<Unrecognized object>
	ERRMSG	<Invalid object name format>
	ERRMSG	<Object too busy>
	ERRMSG	<Abort by network management>
	ERRMSG	<Abort by object>
	ERRMSG	<Invalid node name format>
	ERRMSG	<Local node shut down>
	ERRMSG	<Access control rejection>
	ERRMSG	<No response from object>
	ERRMSG	<Node unreachable>
	ERRMSG	<No link>
	ERRMSG	<Disconnect complete>
	ERRMSG	<Image field too long>
	ERRMSG	<Unspecified reject reason>
	ERRMSG	<Bad flag combination>
	ERRMSG	<Address check>
;Disconnect reason table:

DCNRSN:	ERRMSG	<Rejected or disconnected by object>
	ERRMSG	<No resources>
	ERRMSG	<Remote node shut down>
	ERRMSG	<Unrecognized object>
	ERRMSG	<Invalid object name format>
	ERRMSG	<Object too busy>
	ERRMSG	<>
	ERRMSG	<Abort by network management>
	ERRMSG	<Abort by object>
	ERRMSG	<Local node shut down>
	REPEAT	<^D34-^D12>,<
	ERRMSG	<>
	>
	ERRMSG	<Access control rejection>
	ERRMSG	<>
	ERRMSG	<>
	ERRMSG	<>
	ERRMSG	<>
	ERRMSG	<No response from object>
	ERRMSG	<Node unreachable>
	ERRMSG	<>
	ERRMSG	<No link>
	ERRMSG	<Disconnect complete>
	ERRMSG	<image field too long>
	Subttl	Miscellaneous data

MONTAB:	ASCII/-Jan/
	ASCII/-Feb/
	ASCII/-Mar/
	ASCII/-Apr/
	ASCII/-May/
	ASCII/-Jun/
	ASCII/-Jul/
	ASCII/-Aug/
	ASCII/-Sep/
	ASCII/-Oct/
	ASCII/-Nov/
	ASCII/-Dec/

IFN	FTLOG,<
DEFLOG:	SIXBIT/PSTHRU/			;Virgin LEB for logging
	SIXBIT/LOG/
	Z
	30,,5730
>
	Subttl	Impure storage

	RELOC	0

;The following is for the PSI system

PIVEC:					;START OF PSI VECTORS
IFN	FTDECNET,<
	NSPOFF==.-PIVEC			;THE FIRST IS THE NSP. VECTOR
NSPPSI:	EXP	NSPINT			;THE NSP INTERRUPT ROUTINE
	Z
	Z
	Z
>

IFN	FTANF10,<
	ANFOFF=.-PIVEC
ANFPSI:	EXP	ANFINT
	Z
	Z
	Z
>

HICORE:	BLOCK	1			;Highest available location
FRELST:	BLOCK	1			;Pointer to free PCBs
BLKPTR:	BLOCK	1			;Pointer to PCBs in use
BUFPTR:	BLOCK	1			;Pointer to message buffers

IFN	FTDECNET,<
DPLQUE:	BLOCK	1			;Poll queue for DECnet
>
IFN	FTANF10,<
APLQUE:	BLOCK	1			;Poll queue for ANF
>


FLPBLK:	.FOSIO				;Super I/O (just do OPEN)
	UU.DMR!UU.IBC!UU.AIO!.IOBYT		;Byte mode I/O
	SIXBIT/TSK/			;Device TSK
	Z				;Buffer header pointers
	Z				;Number of buffers

	FLPLEN==.-FLPBLK

IFN	FTANF10,<
;**NOTE** that these to things MUST be contigues as MYNODE is the node
;number for the NPD described here***
	ASSUME	.OBPST,^D123
MYNODE:	Z
LOCNPD:	^D15
	BYTE	(7)0,"1","7","3",0,"*",0,"*",0,"*",0,"*",0,"*",0	;**TEMP**
	MYNODL==.-MYNODE

WLDNPD:	-1
	5
	BYTE	(7)0,"1","7","3","*"
	WLDNPL==.-WLDNPD
>

IFN	FTLOG,<
LOGBLK:	LOG,,.FOAPP
	.IOASC
	SIXBIT/PSTLOG/
	LOBFH,,0
	Z					;Default number of buffers
	LOGLEB					;LOOKUP/ENTER block
	LOGLEN==.-LOGBLK

LOBFH:	BLOCK	3
LOGLEB:	BLOCK	4

>

LGNARG:	2,,5
	Z
	SIXBIT/SYSJOB/
	Z
	0
	LGNLEN==.-LGNARG

IFN	FTDECNET,<
DCNCWL:	BLOCK	1				;Current CW DECnet link
>

IFN	FTANF10,<
ANFCWL:	BLOCK	1
>

IFN	FTANF10,<
ANFNAM:	BLOCK	1				;Our ANF node name
>

IFN	FTDECNET,<
DCNBLK:	<DN.FLE!<.DNLNN,,.DNNMS+1>>		;For DNET.
	BLOCK	.DNNMS
	DCNNAM=DCNBLK+.DNNMS			;Local node name
>

CRSACS:	BLOCK	20				;AC saving block

DIEPRC:	BLOCK	1				;Address of type specific processor

DMPDEV:	SIXBIT/XPN/
DMPFIL:	Z
DMPEXT:	SIXBIT/EXE/
DMPPPN:	Z
	Z
DMPCOR:	Z

QUEBLK:	.QUWTO					;Write to Operator
	1					;Central
	Z					;No response
	.QBMSG					;Type of arg block
	QUEMSG					;Where it is

QUEMSG:	BLOCK	QUMLEN

RUNBLK:
RUNDEV:	Z					;Device to run from
RUNFIL:	Z					;File to run from
RUNEXT:	Z					;Extension
	Z					;Zero word
RUNPPN:	Z					;Pointer to PATH block
RUNCOR:	Z					;core assignment
	Subttl	End

	END	PSTHRU