Google
 

Trailing-Edge - PDP-10 Archives - cuspjul86upd_bb-jf24a-bb - 10,7/decnet/netpth/netpth.mac
There are 9 other files named netpth.mac in the archive. Click here to see a list.
	TITLE	NETPTH	Display the path from this node to any other
	SUBTTL	Stu Grossman	October 1982

IFNDEF FTTIME,<FTTIME=0>

	SEARCH	UUOSYM,MACTEN,SCNMAC

	.REQUEST REL:SCAN

	SALL			; Clean listings

	F=0
	T1=1
	T2=2
	T3=3
	T4=4
	P1=5
	P2=6
	P3=7
	P4=10
	P=17
	N==P3
	C==P4

	F.SEQL==1B0		; Scanner has seen an "="

	.HELPR==:.POPJ

	TWOSEG	400K		; Make us high and low

; Program version information

	NPHVER==4		; With DECnet phase IV
	NPHMIN==0		; Minor version
	NPHEDT==3		; Edit version
	NPHWHO==0		; Who last edited

	%%NPH==VRSN.(NPH)	; Full word version

	LOC	137
.JBVER::! EXP	%%NPH
	RELOC
	SUBTTL	Revision History


COMMENT	&

Edit	Description

1	6-Jun-84 by Bill Davenport

	Fixed to work with DECnet phase IV nodes.

2	19-Dec-84 by Carl Appellof

	Modified to print the other half of each circuit on the path.
	Put response time code under FTTIME, normally off.

3	31-Jan-85 by Bill Davenport

	Fixed up to preserve VERFLG when asking for the circuit back
	to a node.

&; End Revsion History
	SUBTTL	MACROS

; Handy macro for manipulating string blocks
	DEFINE	STRBLK(MAX<XX>,STRING)<
	XX==0
	IRPC	STRING,<XX==XX+1>
	XWD	XX,1+<MAX+3>/4
	IFB <STRING>,<BLOCK	<MAX+3>/4>
	CC==^D36-8
	XX==0
	IRPC	STRING,<
	XX==XX+"STRING"_CC
	CC==CC-8
	IFL CC,<CC==^D36-8
		XX
		XX==0>
			>
	IFN CC-<^D36-8>,XX
	>

	DEFINE	ERROR(PFX,TEXT)<
	JRST	[MOVX	T1,<SIXBIT |NET'PFX|> ;; Get Prog name, error prefix
		 MOVE	T2,["?",,[ASCIZ |TEXT|]] ;; Get lead char, error text
		 JRST	ERRDIE]		;; And go croak
	>

	DEFINE	WARN(PFX,TEXT)<
	PUSHJ	P,WRNRTN		;; Call the warning routine
	JUMP	[SIXBIT |NET'PFX|	;; Get Prog name, error prefix
		 "%",,[ASCIZ |TEXT|]]	;; Get lead char, error text
	>
	SUBTTL	Main program starts here

NETPTH:	JFCL				; Ignore CCL entry
	RESET				; Blow away the world
	SETZ	F,			; Clear all flags
	MOVE	P,STKPTR		; Set up stack
	SETZM	EXUNT			; Clear "exit when done" flag
	SETZM	X.NODE			; Clear executor node name storage

CONTIN:	PUSHJ	P,GETCMD		; Read a command line
	MOVEM	T1,CURNOD		; Make source be the current node
	MOVEM	T2,ENDNOD		; Save pointer to destination node
	MOVE	T1,T2			; Get pointer to end node
	PUSHJ	P,GETNNM		; Get node number of end node
	MOVEM	T1,ENDNUM		; Save it for later
	PUSHJ	P,GETXTR		; Get local node name
	MOVEM	T1,LCLNOD		; Save it for everybody else

; Print out banner
	PUSHJ	P,.TCRLF##		; Skip a line before banner
	MOVEI	T1,[ASCIZ |[Routing path from |] ; Get header
	PUSHJ	P,.TSTRG##		; Type it out
	MOVE	T1,CURNOD		; Get name of first node
	SETZ	T2,			; No enforced field length
	PUSHJ	P,.TNNAM		; Type it out
	PUSHJ	P,.TSPAC##		; Space over
	MOVE	T1,CURNOD		; Get node address
	PUSHJ	P,.TNNAD		; Type it out
	MOVEI	T1,[ASCIZ | to |]	; Type out predicate
	PUSHJ	P,.TSTRG##		; . . .
	MOVE	T1,ENDNOD		; Get end node name
	SETZ	T2,			; No enforced field length
	PUSHJ	P,.TNNAM		; Type it out too
	PUSHJ	P,.TSPAC##		; Space over
	MOVE	T1,ENDNOD		; Get node address
	PUSHJ	P,.TNNAD		; Type it out
	PUSHJ	P,.TRBRK##		; Type out closing bracket
	PUSHJ	P,.TCRLF##		; Type a crlf
	PUSHJ	P,.TCRLF##		; Type another line
	MOVEI	T1,[ASCIZ | From        	  Via        Back	Thru		Cost	Hops|]
	PUSHJ	P,.TSTRG##		; Type out column headers
	PUSHJ	P,.TCRLF##		; <CRLF>
	SUBTTL	Main loop

; Starting with the current node, find out what circuit is used to get to
; the end node, and then find out which node is connected to the other end
; of that circuit.  When we find out the node on the other other end of the
; circuit, make that the current node, and start over.  We stop when the
; current node becomes the end node.

	MOVE	T1,CURNOD		; Get pointer to current node spec
	PUSHJ	P,OPNLNK		; Go open link to foreign NML
	  JRST	NSPERR			; Can't even link to ourselves!
LOOP:
	MOVE	T1,CURNOD		; Get current node name
	MOVEI	T2,^D7			; Enforced field length
	PUSHJ	P,.TNNAM		; Type it out
	MOVE	T1,CURNOD		; Get current node address
	PUSHJ	P,.TNNAD		; Type it out
	MOVEI	T1,[ASCIZ /	=>/]
	PUSHJ	P,.TSTRG##		;Type an arrow
	MOVE	T1,ENDNUM		;Get terminal node's number
	PUSHJ	P,GETCKT		; Get circuit between here and there
	MOVE	P1,T1			; Save it for later
	MOVEI	T2,^D8			; Enforced field length
	PUSHJ	P,.TCASC		; Type it out
	MOVEI	T1,[ASCIZ | / |]
	PUSHJ	P,.TSTRG##		;Type a pipe
	MOVE	T1,CURNOD		;Get current node
	PUSHJ	P,GETNNM		;Get number from it
	MOVEM	T1,CURNUM		;Save it for later
	MOVE	T1,P1			; Get circuit name back
	PUSHJ	P,GETNOD		; Get node on other end of the circuit
	MOVEM	T1,CURNOD		; Make it the new current node
	PUSHJ	P,CLSLNK		;Don't need this link any more
	PUSH	P,VERFLG		;Save DECnet version info for a bit
	MOVE	T1,CURNOD		;So open new one
	PUSHJ	P,OPNLNK		;to next node on the chain.
	  JRST	[MOVEI	T1,[ASCIZ /????????/]
		 PUSHJ	P,.TSTRG##
		 JRST	NOLNK]
	MOVE	T1,CURNUM		;Get number of previous node on chain
	DMOVE	P1,COST			;Save cost and hops from orig. circuit
	PUSHJ	P,GETCKT		;Ask new node how to go backwards
	MOVEI	T2,^D8			;Enforced field length
	PUSHJ	P,.TCASC		;Type back circuit name
NOLNK:	MOVEI	T1,[ASCIZ /<=	/]
	PUSHJ	P,.TSTRG##
	MOVE	T1,CURNOD		;Get new node again
	MOVEI	T2,^D7			; Enforced field length
	PUSHJ	P,.TNNAM		; Type out the foreign node name
	MOVE	T1,CURNOD		; Get current node address
	PUSHJ	P,.TNNAD		; Type it out
	SKIPE	T1,LASERR		;Any error on last connect?
	  JRST	NSPERR			;Yes, quit now and tell why.
	PUSHJ	P,.TTABC##
	MOVE	T1,P1			;Get cost of forward circuit
	PUSHJ	P,.TDECW##		;Type it out.
	PUSHJ	P,.TTABC##
	MOVE	T1,P2			;and HOPS
	PUSHJ	P,.TDECW##		; too
IFN FTTIME,<
	PUSHJ	P,.TTABC##		; Type a tab
	MOVE	T1,CITIME		; Get length of CI
	PUSHJ	P,TIMPNT		; Print it out
	PUSHJ	P,.TTABC##		; Type another tab
	PUSHJ	P,TIMAVG		; Get average of responses
	PUSHJ	P,TIMPNT		; Print out the average resp time
> ;END IFN FTTIME
	PUSHJ	P,.TCRLF##		; Finish up with a <CRLF>
	MOVE	T1,VERFLG		; Get version of next node
	EXCH	T1,(P)			; Save; get back previous version
	MOVEM	T1,VERFLG		; And set for later comparison

	MOVE	P1,CURNOD		; Get pointer to current node
	MOVE	P2,ENDNOD		; Get pointer to end node
	ILDB	T1,P1			; Get low order part of destnode addr
	ILDB	T2,P1			; And high order part of addr
	LSH	T2,^D8			; Shift into position
	IOR	T1,T2			; And combine
	ILDB	T2,P2			; Get low order part of end node addr
	ILDB	T3,P2			; And high order part of addr
	LSH	T3,^D8			; Shift into position
	IOR	T2,T3			; And combine
	SKIPG	VERFLG			; Was current node less than phase IV ?
	 ANDI	 T2,1777		; Yes, mask off area stuff
	POP	P,VERFLG		; Reset next node's version
	CAME	T1,T2			; Same network address?
	 JRST	 LOOP			; No, start over
	DMOVE	T1,P1			; Now check node names
	PUSHJ	P,CMPSTR		; Compare them
	 JRST	 LOOP			;  Different, start over
	PUSHJ	P,CLSLNK		; Close the final network link
	PUSHJ	P,.TCRLF##		; Add another CRLF for good effect

	JRST	CONTIN			;  No, go back to command mode
	SUBTTL	GETCMD	Read a command line from the user

; This routine will read in command lines of the form:
;
; Destination=Source
; 	     =Source		; Destination defaults to executor node
; Destination			; Source defaults to executor node
; Destination=			; Same as above
;
; Destination and source must both be reachable nodes.  If they are unknown,
; unreachable, or improperly formatted, an appropriate message will be printed,
; and a new command line will be requested.
; Switches (when implememted) may be inserted anywhere except in the middle of
; a node name.
;
; Call:	PUSHJ	P,GETCMD		; Takes no arguments
;	<Only return>			; T1 & T2 contain byte pointers to the
;					; node names in standard NML format.
;					; Ie: <Low#><High#><Length><Name>
;

GETCMD:

; Here after user typed continue (after ^Zing out)
REDNO5:	MOVE	T1,ISCARG		; Get arg for .ISCAN
	PUSHJ	P,.ISCAN##		; Init SCAN

; Here after blank line
REDNO1:	SKIPN	EXUNT			; Do we wnat to exit ???
	 JRST	 REDNO2			;  No, do command scanning
	EXIT	1,			; Yes, quit now
	SETZM	EXUNT			; Reset flag
	JRST	REDNO5			; And re-init line

; Here to scan new command line
REDNO2:	SETZM	N.DEST			; Clear this too
	SETZM	N.SRC			; Clear this too
	SETOM	DEFFLG			; Allow one node name to be defaulted
	MOVE	T1,PSCARG		; Get arg for .PSCAN
	PUSHJ	P,.PSCAN##		; Init the line
	 JFCL
	MOVEI	T1,[ASCIZ |NETPTH>|]	; Get prompt
	PUSHJ	P,.TSTRG##		; Type it out

	TXZ	F,F.SEQL		; Indicate no "=" seen yet
REDNO0:	PUSHJ	P,REDNOD		; Go read one name
	SKIPN	N.SPEC			; Did we parse a node spec?
	 JUMPLE	 C,REDEOL		;  No, end of line also?
	CAIE	C,"="			; Did we get an =??
	 JRST	 REDNOF			;  No, go see what we got
	TXOE	F,F.SEQL		; Have we been here before?
	 ERROR	 (MES,More than one equals sign in command string)
	MOVE	T1,[POINT 8,N.SPEC]	; Get BP to node id
	MOVE	T2,[POINT 8,N.DEST]	; Get BP to destination area
	PUSHJ	P,CPYNOD		; Copy N.SPEC to N.DEST
	JRST	REDNO0			; And try again

;Here when character in C is not "="
REDNOF:	SKIPLE	C			; Did line terminate properly?
	 PUSHJ	 P,REDJEL		;  No, go eat it up
	MOVE	T1,[POINT 8,N.SPEC]	; Get BP to node id
	MOVE	T2,[POINT 8,N.SRC]	; Get ptr to source area
	TXNN	F,F.SEQL		; Have we seen an = sign yet?
	 MOVE	 T2,[POINT 8,N.DEST]	;  No, this is just a source spec
	PUSHJ	P,CPYNOD		; Copy N.SPEC to N.SRC or N.DEST

REDEOL:	CAMN	C,[.CHEOF]		; Did user type a ^Z ???
	 SETOM	 EXUNT			;  Yes, flag exit on end of processing
	TXNN	F,F.SEQL		; Did we see an equals sign?
	 SKIPE	 N.DEST			;  No, was a destination specified?
	  SKIPA				;   Yes
	 JRST	 REDNO1			;  No, quit now

; Install defaults
	MOVE	T1,[POINT 8,N.SRC]	; Get BP to source
	SKIPN	N.SRC			; Did we get a source node?
	 PUSHJ	 P,DEFXTR		;  No, default it to the executor
	MOVE	T2,[POINT 8,N.SRC]	; Get pointer to source area
	PUSHJ	P,CPYNOD		; Copy the default for source node
	MOVE	T1,[POINT 8,N.DEST]	; Get BP to dest
	SKIPN	N.DEST			; Did we get a destination node?
	 PUSHJ	 P,DEFXTR		;  No, default to the executor
	MOVE	T2,[POINT 8,N.DEST]	; Get pointer to destination area
	PUSHJ	P,CPYNOD		; Copy default for destination node

	MOVE	T1,[POINT 8,N.SRC]	; Get BP to source node name
	MOVE	T2,[POINT 8,N.DEST]	; Get BP to dest node name
	PUSHJ	P,CMPNOD		; Are they the same??
	 TRNA				;  No, alls well
	  ERROR	 (SDS,Source and destination nodes are the same)
	MOVE	T1,[POINT 8,N.SRC]	; Get BP to source node name
	MOVE	T2,[POINT 8,N.DEST]	; Get BP to dest node name
	POPJ	P,			; And return happily

REDJEL:	JUMPLE	C,.POPJ			; Did line end properly??
	 WARN	 (JEL,Junk at end of line - ignored)
	PUSHJ	P,.TICHR##		; Read a char
	JUMPG	C,.-1			; Loop till we hit eol
	POPJ	P,
	SUBTTL	REDNOD	Read node names for the command scanner

; Read one node name or number, checking for validity, skipping spaces, etc.
; Return name in N.SPEC as a standard DECnet node name string.
;
; Call:	PUSHJ	P,REDNOD		; Go read a name or number
;	 Only return; node name in N.SPEC, terminating character in C.
;

REDNOD:	PUSHJ	P,.SAVE2##		; Save some Ps
	SETZM	N.SPEC			; Clear number and name length fields
	PUSHJ	P,.TIAUC##		; Get a character
	CAIE	C," "			; Is it a space?
	 PUSHJ	 P,.REEAT##		;  No, put it back

; Loop around gathering up node name.
	MOVE	P1,[POINT 8,N.SPEC,7+8+8] ; Get BP to 1st byte of name
	MOVEI	P2,6			; Set up maximum name length
	JRST	REDNO8			; Enter loop at correct point
REDNO4:	IDPB	C,P1			; Save the char
REDNO8:	PUSHJ	P,.TIAUC##		; Get a char
	PUSHJ	P,.TICAN##		; Is it alphanumeric??
	 JRST	 REDNO6			;  No, we are done
	CAIGE	C,"A"			; Are we alphabetic??
	 CAIE	 P2,6			;  No, digit - is it first??
	  TRNA				;   No to either, go do normal stuff
	   JRST	   REDNO7		;    Yes, go do special stuff
	SOJGE	P2,REDNO4		; Loop back for entire node name
	CAMN	P2,[-1]			; First time through??
	 WARN	 (NLS,Node name longer than six characters)
	JRST	REDNO8			; Go eat extraneous characters

; Here if 1st char of node name is a digit.  User wants to use #
REDNO7:	PUSHJ	P,.DECNC##		; Read in decimal node number
	JUMPE	N,REDNOB		; Zero node numbers are naughty
	DPB	N,[POINT 8,N.SPEC,7]	; Install the low order byte
	LSH	N,-8			; Get the high order byte
	DPB	N,[POINT 8,N.SPEC,7+8]	; Install the high order byte
	JRST	REDNOA			; Go map name and give skip return

; Here to install length of node name
REDNO6:	SKIPGE	P2			; Did user type too much?
	 SETZ	 P2,			;  Yes, name gets truncated
	MOVEI	T1,6			; Get max node name length
	SUB	T1,P2			; Compute length of name
	JUMPE	T1,REDRET		; Return now if nothing was parsed
	DPB	T1,[POINT 8,N.SPEC,7+8+8] ; Install length

; Here to map the name to a number, and determine reachability
REDNOA:	MOVE	T1,[POINT 8,N.SPEC]	; Get pointer to node spec
	PUSHJ	P,MAPIT			; Map number into name (or vice versa)
	 JRST	 REDNOE			;  Unknown component

;Here just before returning to caller.  Eat trailing spaces.
REDRET:	CAIN	C," "			; Was terminating character a space???
	 PJRST	 .TIAUC##		;  Yes, we hate spaces
	POPJ	P,			; And return

; Here when name to number mapping operation fails
REDNOE:	CAIN	P2,6			; Did user give a node number??
REDNOB:	 ERROR	 (INN,Illegal node number) ;  Yes, give correct message
	ERROR	(UKN,Unknown node name)
	SUBTTL	GETXTR	Return exector node name

; Call:	PUSHJ	P,GETXTR		; No args
;	<Return +1 always, T1/ BP to executor node ID>
;

DEFXTR:	AOSE	DEFFLG			; Have we been here before??
	 ERROR	 (NNS,No node names specified) ;  Yes, no good
GETXTR:	MOVE	T1,[POINT 8,X.NODE]	; Get pointer
	SKIPE	X.NODE			; Have we been here before??
	 POPJ	 P,			;  Yes, we're all set
	MOVEI	T2,9			; Get length of node name buffer
	MOVEM	T2,NTREX+.NTBYT		; Stuff it
	MOVEI	T2,NTREX		; Get arg for NTMAN.
	NTMAN.	T2,			; Read the executor ID
	 HALT	 .			;  Silly program error
	PUSHJ	P,MAPIT			; Go do mapping if necessary
	 WARN	 (EDN,Executor node does not have a name)
	POPJ	P,			; Return
	SUBTTL	GETCKT	Get the circuit between current node and ENDNOD

; This routine will return the circuit between CURNOD and ENDNOD.  The
; circuit id will be a CASCII string pointed to by T1.
;
; Call:	PUSHJ	P,GETCKT
;	<Return +1 always>	T1/ ASCIZ byte pointer to circuit id

GETCKT:	SKIPG	VERFLG			; Connected to v3 NML or earlier ??
	 ANDI	 T1,1777		; Yes, clear area number junk
	MOVE	T2,[POINT 8,CKTNIC,7+8+8] ; Get pointer to NICE message
	IDPB	T1,T2			; Install low order byte
	LSH	T1,-^D8			; Get high order part of node number
	IDPB	T1,T2			; Stuff it

	MOVEI	T1,CKTNLN		; Get length of NICE message
	MOVE	T2,[POINT 8,CKTNIC]	; Get pointer to NICE message
	PUSHJ	P,SNDMSG		; Go send the message

	SETOM	RSPNFG			; Set Response About Node flag
	PUSHJ	P,REDRSP		; Read the response
	DMOVEM	T1,PTRHLD		; Put pointers in a safe place
	MOVEI	T3,^D820		; COST parameter
	PUSHJ	P,FNDPRM		; Try to find it.
	 SETO	T1,			; Not there, use 0.
	MOVEM	T1,COST			; Save it
	DMOVE	T1,PTRHLD		; Start of message again.
	MOVEI	T3,^D821		; HOPS
	PUSHJ	P,FNDPRM		; Find it too.
	 SETO	T1,
	MOVEM	T1,HOPS			; Save it too.
	DMOVE	T1,PTRHLD		; Restore pointer to message again.
	MOVEI	T3,^D822		; Output Circuit parameter number
	PUSHJ	P,FNDPRM		; Go look for circuit id
	 ERROR	 (URN,Unreachable node)
	POPJ	P,			;Return with T1 pointing to circuit id
	SUBTTL	GETNOD	Get the node at the other end of the circuit in T1

; This routine will get the node that is on the other side of the circuit in
; CIRBUF on the node CURNOD.  The node will be returned in SIXBIT in T1.
;
; Call:	MOVE	T1,[BP to CASCII ckt id]
;	PUSHJ	P,GETNOD
;	<Return +1 Always>	T1/ SIXBIT node id

GETNOD:	PUSHJ	P,.SAVE1##		; Save a P
	MOVE	P1,T1			; Save pointer to circuit
	DMOVE	T1,PTRHLD		; Restore pointers from last response
	MOVEI	T3,^D830		; Adjacency parameter
	PUSHJ	P,FNDPRM		; Go look for adjacent node
	 JRST	 NODNM3			;  No such parameter, do usual thing
	MOVE	T1,T2			; Get second part of parameter
	MOVE	T2,[POINT 8,N.SPEC]	; Get pointer to handy area
	SETZ	T3,			; Clear an AC
	IDPB	T3,T2			; Zero first byte of node number
	IDPB	T3,T2			; Zero second byte of node number
	PUSHJ	P,CPYCAS		; Copy parameter to a safe place
	MOVE	T1,[POINT 8,N.SPEC]	; Get BP for the mapping function
NODNM4:	PUSHJ	P,MAPIT			; Map the node name
	 ERROR	 (UDS,Unknown destination node name)
	MOVE	T1,[POINT 8,N.SPEC]	; Return pointer to node spec
	POPJ	P,			; ...

; Here when we have to do a TELL curnod SHOW CIRCUIT ckt STATUS
NODNM3:	MOVEI	T2,.NTCKT		; Get default entity type
	SKIPGE	VERFLG			; Are we talking to version 2 NML??
	 MOVEI	 T2,.NTLIN		;  Yes, use lines instead
	DPB	T2,[POINT 3,NODNIC,7+8]	; Install entity type in NICE message
	MOVE	T2,[POINT 8,NODNIC,7+8]	; Get pointer to NICE message
	MOVE	T1,P1			; Get back pointer to ckt id
	PUSHJ	P,CPYCAS		; Copy the circuit id into the msg

	ILDB	T1,P1			; Get length of circuit id
	ADDI	T1,3			; Correct for all overhead
	MOVE	T2,[POINT 8,NODNIC]	; Get fresh pointer to NICE
	PUSHJ	P,SNDMSG		; Send message

	SETZM	RSPNFG			; Clear response node flag
	PUSHJ	P,REDRSP		; Read the response message

	MOVEI	T3,^D800		; Get parameter number for ADJACENT
					;  NODE parameter
	PUSHJ	P,FNDPRM		; Get node number into T1
	 ERROR	 (CFA,Can't find Adjacent Node parameter)
	MOVE	T2,[POINT 8,N.SPEC]	; Get pointer to handy holding place
	IDPB	T1,T2			; Save low part of node number
	LSH	T1,-8			; Get high order part into place
	IDPB	T1,T2			; Install that too
	SETZ	T1,			; Clear an ac
	IDPB	T1,T2			; Zero length name so far
	MOVE	T1,[POINT 8,N.SPEC]	; Get pointer back again
	JRST	NODNM4			; Map it and do final fixups
	SUBTTL	FNDPRM	Get the value of a parameter

; This routine will search through a string of NICE parameter ids and values,
; and return the value of a certain parameter.  This routine is called with
; T1 containing the number of NICE bytes, T2 contains a byte pointer to a
; block of NICE bytes, and T3 contains the parameter number.
;
; On return T1 (and possibly T2) will contain the value of the parameter.  If
; the parameter is a binary number, the AC will contain the number (as a real
; PDP-10 integer).  If the parameter value is a byte string, AC will contain
; an ILDB pointer to a CASCII string.  For coded multiples, only the first
; two values will be returned, T1 and T2 will contain them.

FNDPRM:	PUSHJ	P,.SAVE1##		; Get a more permanent AC
	MOVE	P1,T3			; And save parameter number
	PUSHJ	P,NICSET		; Set up for NICBYT

; Here for each new parameter we find
NEWPRM:	PUSHJ	P,NICBYT		; Get low byte of parameter number
	 POPJ	 P,			;  Can't find parameter, let caller know
	MOVE	T2,T1			; Save low byte in T2
	PUSHJ	P,NICBYT		; Get high byte of paramter number
	 HALT	 .			; Halt for now
	LSH	T1,8			; Put high part in place
	IORI	T1,(T2)			; Install low part
	CAME	T1,P1			; Did we get the right parameter??
	 JRST	 NXTPRM			;  No, skip to next parameter
	PUSHJ	P,REDPRM		; Go get the parameter value in T1&T2
	JRST	.POPJ1			; And give skip return

; Here when we have to skip over the data portion of this parameter value
NXTPRM:	PUSHJ	P,SKPPRM		; Skip over the data portion of parm
	JRST	NEWPRM			; And try again
	SUBTTL	NICBYT, NICPTR, NICSET	NICE byte string manipulation routines

; Call this to set up for calls to NICBYT

NICSET:	DMOVEM	T1,NICCNT		; Put BP and count in safe place
	POPJ	P,

; Call this for each byte to be read from NICE message

NICBYT:	SOSGE	NICCNT			; Any bytes left??
	 POPJ	 P,			;  No, give skip return
	ILDB	T1,NICBPT		; Yes, get one
.POPJ1:	AOS	(P)			; And give
.POPJ:	POPJ	P,			;  skip return

; Get current ILDB pointer to NICE string

NICPTR:	MOVE	T1,NICBPT		; Get current NICE pointer
	POPJ	P,
	SUBTTL	NICE parameter control routines

; SKPPRM - Skip over the data portion of a parameter

SKPPRM:	PUSHJ	P,NICBYT		; Get DATA TYPE field
	 HALT	 .
	TRNN	T1,200			; Coded field??
	 JRST	 UNCOD			;  No, do image data stuff
	TRNE	T1,100			; Single field?
	 JRST	 CMULT			;  No, go do Coded Multiple stuff

; Here for Coded Single fields
	ANDI	T1,77			; Make just byte count
BYTSKP:	MOVE	T2,T1			; Get count into a safe place
SCLOP:	JUMPLE	T2,.POPJ		; Jump if no more bytes left
	PUSHJ	P,NICBYT		; Get a byte
	 HALT	 .			;  ???
	SOJA	T2,SCLOP		; Jump if not done

; Here for skipping over Coded Multiples
CMULT:	ANDI	T1,77			; Make field count
CMLOP:	PUSH	P,T1			; Save it better
	PUSHJ	P,SKPPRM		; Call ourselves recursivly
	POP	P,T1			; Restore count
	SOJG	T1,CMLOP		; Skip multiple fields
	POPJ	P,			; And return triumphant!

; Here for uncoded (image) fields
UNCOD:	TRNN	T1,100			; ASCII image field?
	 JRST	 NOASC			;  No, go do numbers
AIMAGE:	PUSHJ	P,NICBYT		; Get byte count
	 POPJ	 P,			;  Ok if short terminated
	JRST	BYTSKP			; and go to the string skipper

; Here for binary numbers.  If length is 0, then treat like ASCII image
; otherwise just jump off to BYTSKP.

NOASC:	ANDI	T1,17			; Mask to data length
	JUMPE	T1,AIMAGE		; Zero implies image field
	JRST	BYTSKP			; Go to string skipper
	SUBTTL	REDPRM	Read a parameter from a NICE message

; The parameter value will either returned in T1 or T1 will contain a byte
; pointer to a CASCII string.  For coded multiple, T1 will be as above for the
; first parameter and T2 will be similar, except it will be for the second
; parameter.

REDPRM:	PUSHJ	P,NICBYT		; Get DATA TYPE byte
	 HALT	 .
	MOVE	T2,T1			; Put it into a safe place
	LSH	T1,-6			; Reduce it to top two bits
	JRST	@FMTTAB(T1)		; And call the right routine

FMTTAB:	EXP	GETBIN			; 000 Binary number
	EXP	GETASC			; 100 ASCII image
	EXP	GETCSN			; 200 Coded single
	EXP	GETCML			; 300 Coded multiple

; This routine will build a number from a string of eight bit bytes.  The
; bytes are stored backwards (least significant byte first), and must be
; reversed before assembly.

GETBIN:	ANDI	T2,17			; Reduce to data length
	JUMPN	T2,GETBI1		; Jump if not image field
	PUSHJ	P,NICBYT		; Get length of field
	 HALT	 .			;  ???
	MOVE	T2,T1			; Put length in a safe place

GETBI1:	PUSHJ	P,NICBYT		; Get a byte from the number
	 HALT	.
	PUSH	P,T1			; Save one byte of number
	SETZ	T1,			; Clear receiving buffer
	SOSLE	T2			; Skip if no more bytes
	 PUSHJ	 P,GETBI1		;  Get rest of number
	LSH	T1,8			; Make room for more
	IOR	T1,0(P)			; Bring in that byte
	ADJSP	P,-1			; Fix the stack
	POPJ	P,			; And return

; This routine will get an IDLB pointer to a CASCII string

	GETASC==NICPTR			; Get byte pointer from byte getter

; Here to get the value of a Coded Single into T1

GETCSN:	ANDI	T2,77			; Reduce to data size
	JRST	GETBI1			; And jump into the fray

; Here to get Coded Multiples

GETCML:	ANDI	T2,77			; Reduce to size
	CAILE	T2,2			; .LE. two items??
	 MOVEI	 T2,2			;  No, force it down to two
	MOVE	T3,T2			; Get count into a safe place
	PUSHJ	P,REDPRM		; Read the first parameter
	CAIE	T3,2			; Another parameter?
	 POPJ	 P,			;  No, just return
	PUSH	P,T1			; Save it for later
	PUSHJ	P,REDPRM		; Read the second parameter
	MOVE	T2,T1			; Put second param into T2
	POP	P,T1			; Put first parameter into T1
	POPJ	P,			; And return
	SUBTTL	REDRSP	Read a response message from the network

; This routine will read a response from the network, and skip over the
; response data (everything except the meat of the message).  It will also
; call NICSET to set up for reading the data.

NICPUT:	SOSGE	RSPCNT			; Any room left?
	 POPJ	 P,			;  No, no skip return
	IDPB	T1,RSPPTR		; Yes, install the byte
	JRST	.POPJ1			; And skip return

REDRSP:	MOVEI	T1,RSPLEN		; Set up length of buffer
	MOVEM	T1,RSPCNT		; Save it
	MOVE	T1,[POINT 8,RSPBUF]	; Get pointer to buffer
	MOVEM	T1,RSPPTR		; Save that too
	PUSHJ	P,REDMSG		; Read first message
	PUSHJ	P,NICSET		; Set up pointers
	PUSHJ	P,NICBYT		; Get first byte of message
	 HALT	 .			;  !?!?
	CAIE	T1,1			; Single complete response???
	 JRST	 REDR.1			;  No, try for multiple
	PUSHJ	P,EATHDR		; Eat the common header info
	PUSHJ	P,EATEID		; Eat up entity id
	PUSHJ	P,MOVRSP		; Move remaining part of response
REDR.2:	MOVEI	T1,RSPLEN		; Get response buffer length
	SUB	T1,RSPCNT		; Compute number of bytes in buffer
	MOVE	T2,[POINT 8,RSPBUF]	; Get pointer to response stuff
	POPJ	P,			; And return

REDR.1:	CAIE	T1,2			; Start of multiple response??
	 HALT	 .			;  Sigh
	SKIPN	NICCNT			; Any header stuff left???
	 JRST	 RSPLOP			;  No, go read next message
	PUSHJ	P,EATHDR		; Yes, eat common part of header
	SKIPE	NICCNT			; Message should be finished
	 HALT	 .			;  But it's not!
RSPLOP:	PUSHJ	P,REDMSG		; Read message from the network
	PUSHJ	P,NICSET		; Set up for calls to NICBYT

	PUSHJ	P,NICBYT		; Get Return Code
	 HALT	 .
	CAIN	T1,1			; Success return??
	 JRST	PRSSUC			;  Yes, parse as such
	CAIN	T1,2			; Seperated responses???
	 HALT	 .			;  Not legal here
	CAIN	T1,3			; Or, is it this one?
	 HALT	 .			;  I don't understand this one yet
	CAIE	T1,200			; End of multiple responses?
	 HALT	 .			;  No, die horribly
	SKIPE	NICCNT			; Did message end properly??
	 PUSHJ	 P,EATHDR		;  No, go eat the header
	JRST	REDR.2			; Yes, finish up the buffer and return

PRSSUC:	PUSHJ	P,EATHDR		; Eat common part of header
	PUSHJ	P,EATEID		; Eat entity id
	PUSHJ	P,MOVRSP		; Move response into response buffer
	JRST	RSPLOP			; And go back for more

MOVRSP:	PUSHJ	P,NICBYT		; Get a byte from the network buffer
	 POPJ	 P,			;  No more, quit now
	PUSHJ	P,NICPUT		; Install it in the response buffer
	 HALT	 .			;  No more room
	JRST	MOVRSP			; And start over

EATHDR:	PUSHJ	P,NICBYT		; Read first half of error detail
	 HALT	 .			;  Missing error detail
	PUSHJ	P,NICBYT		; Read second half of error detail
	 HALT	 .			;  Missing half of error detail?!?
	PUSHJ	P,NICBYT		; Get number of bytes in error message
	 HALT	 .			;  Should at least be a length here
	SKIPE	T1			; Message length should be zero
	 HALT	 .			;  Error message?
	POPJ	P,			; And return now

EATEID:	SKIPN	RSPNFG			; Is this for a node id??
	 JRST	 AIMAGE			;  No, skip this
	PUSHJ	P,NICBYT		; Yes, skip low order byte of number
	 HALT	 .			;  Node number missing?!?
	PUSHJ	P,NICBYT		; Skip high order byte of number
	 HALT	 .			;  Missing half of number??
	PUSHJ	P,NICBYT		; Get start of node id
	 HALT	 .			;  No node id length??
	TRZ	T1,200			; Clear the "executor" bit
	PJRST	BYTSKP			; And go skip some bytes
	SUBTTL	MAPIT	Map from node number to node name

; Call:	MOVx	T1,BP to node spec
;	PUSHJ	P,MAPIT
;	 <+1 return if node is unknown>
;	<+2 return if success>
; In either case T1/ original BP

MAPIT:	MOVEM	T1,NTMAP+.NTBPT		; Set up pointer to info to be mapped
	MOVEI	T2,9			; Get length of MAPBUF
	MOVEM	T2,NTMAP+.NTBYT		; Stuff it
	MOVEI	T2,NTMAP		; Get arg for NTMAN.
	NTMAN.	T2,			; Read mapping from monitor
	 TRNA				;  Oops, something went wrong...
	JRST	.POPJ1			; Give skip return
	CAME	T2,[NEURC%]		; Unrecognised component?
	 HALT	 .			;  No, croak fatally
	POPJ	P,			; Yes, let user handle this one
	SUBTTL	OPNLNK & CLSLNK	Open and close NICE links to a foreign NML

; This routine will open a NICE link to the node in CURNOD

OPNLNK:	SETZM	LASERR			;NO ERROR CODE YET
	ILDB	T2,T1			; Skip first byte
	ILDB	T2,T1			; Skip second byte of node number
	ILDB	T2,T1			; Get length of node name
	SKIPN	T2			; Is it legal?
	 HALT	 .			;  !?! Zero node name?
	HRLM	T2,CNODE		; Save length of name in string block

	MOVE	T3,[POINT 8,CNODE+1]	; Get pointer to destination
OPNLN1:	ILDB	T4,T1			; Get byte from node name
	IDPB	T4,T3			; Stuff it into the NSP. block
	SOJG	T2,OPNLN1		; Loop till done

IFN FTTIME,<
	PUSHJ	P,TIMRST		; Reset timing data base
	PUSHJ	P,TIMBEG		; Start CI timer
> ;END FTTIME
	MOVEI	T1,ENTACT		; Get addr for NSP.
	NSP.	T1,			; Open up link to foreign node
	 JRST	 OPNLNE			;  Print nice error mess
IFN FTTIME,<
	PUSHJ	P,TIMEND		; Stop timer
	MOVEM	T1,CITIME		; Save amount of time for this CI
> ;END FTTIME
	HRRZ	T1,ENTACT+.NSACH	; Get channel number
	MOVEM	T1,NETCHN		; Save it for SNDMSG and REDMSG

	MOVEM	T1,RCCBLK+.NSACH	; Install channel number in CC block
	MOVEI	T1,RCCBLK		; Get arg for NSP.
	NSP.	T1,			; Read Connect Confirm Data
	  JRST	OPNLNE			; Report NSP. error
	LDB	T1,[POINT 8,CCDATA+1,7]	; Get version number of foreign NML
	SUBI	T1,3			;Make NML v3 = 0
	MOVEM	T1,VERFLG		; Remember which version of NML
	JRST	.POPJ1
OPNLNE:	MOVEM	T1,LASERR
	POPJ	P,

CLSLNK:	MOVE	T1,NETCHN		; Get network channel number
	MOVEM	T1,DSCBLK+.NSACH	; Install it in NSP. block
	MOVEI	T1,DSCBLK		; Get pointer to NSP. arg block
	NSP.	T1,			; Do Synchronus Disconnect
	  JRST	NSPERR			; Report NSP. error
	MOVE	T1,NETCHN		; Get network channel number
	MOVEM	T1,RELBLK+.NSACH	; Install it in NSP. block
	MOVEI	T1,RELBLK		; Get arg for releasing channel
	NSP.	T1,			; Drop the channel
	  JRST	NSPERR			; Report NSP. error
	POPJ	P,
	SUBTTL	SNDMSG and REDMSG	Network I/O routines

; Call:	MOVEI	T1,length-of-message
;	MOVE	T2,[BP to message]
;	PUSHJ	P,SNDMSG
;	 <Return +1 Always>

SNDMSG:	DMOVEM	T1,SNDBLK+.NSAA1	; Install byte count and byte pointer
	MOVE	T1,NETCHN		; Get network channel number
	MOVEM	T1,SNDBLK+.NSACH	; Install it
IFN FTTIME,<
	PUSHJ	P,TIMBEG		; Start timing response
> ;END IFN FTTIME
	MOVEI	T1,SNDBLK		; Get address of arg block
	NSP.	T1,			; Send the data
	  JRST	NSPERR			; Report NSP. error
	POPJ	P,			; Return happily

; REDMSG will read data from the network.  It will return T1 with a byte count,
; and T2 with a byte pointer.  T1 and T2 will be supplied by me, and ignored
; if they are set up.


REDMSG:	MOVE	T1,NETCHN		; Get channel number for network
	MOVEM	T1,RCVBLK+.NSACH	; Install it
	DMOVE	T1,[MSGLEN		; Length of my buffer
		    POINT 8,MSGBUF]	; Pointer to my buffer
	DMOVEM	T1,RCVBLK+.NSAA1	; Install in NSP. arg block
	MOVEI	T1,RCVBLK		; Get arg for NSP.
	NSP.	T1,			; Read data from network
	  JRST	NSPERR			; Report NSP. error
IFN FTTIME,<
	PUSHJ	P,TIMEND		; Stop timing response
> ;END FTTIME
	MOVEI	T1,MSGLEN		; Get size of message buffer
	SUB	T1,RCVBLK+.NSAA1	; Compute number of bytes received
	MOVE	T2,[POINT 8,MSGBUF]	; Get pointer to message buffer
	POPJ	P,
	SUBTTL	Miscellaneous and sundry routines

; .TNNAM  Type out the standard style node name pointed to by T1
.tnnam:	ibp	t1
	ibp	t1
	pjrst	.tcasc

; .TNNAD  Type out standard node address pointed to by T1
.TNNAD:	PUSH	P,T1			; Save byte pointer
	ILDB	T1,(P)			; Get first byte of address
	ILDB	T2,(P)			; And second byte
	LSH	T2,^D8			; Combine in T1
	IOR	T1,T2			; ...
	MOVEM	T1,(P)			; Save
	MOVEI	T1,"("			; Delimit node address
	PUSHJ	P,.TCHAR##		; ...
	LDB	T1,[POINT 6,(P),25]	; Get area number
	JUMPE	T1,TNNAD1		; Skip if no area number
	PUSHJ	P,.TDECW##		; Type in decimal
	MOVEI	T1,"."			; Add delimiter
	PUSHJ	P,.TCHAR##		; ...
TNNAD1:	LDB	T1,[POINT 10,(P),35]	; Get node address
	PUSHJ	P,.TDECW##		; Type in decimal
	MOVEI	T1,")"			; Add delimiter
	PUSHJ	P,.TCHAR##		; ...
	ADJSP	P,-1			; Clean stack
	POPJ	P,			; And return
; .TCASC  Type out a counted ASCII string pointed to by BP in T1
; Enforced field length is in T2
.TCASC:	PUSHJ	P,.SAVE3##		; Save several Ps
	DMOVE	P1,T1			; Put BP and count in a safe place
	ILDB	P3,P1			; Get number of chars into P3
	SUB	P2,P3			; Calculate number of pad characters
TCASC1:	JUMPE	P3,TCASC2		; Jump if no more bytes
	ILDB	T1,P1			; Get another byte
	PUSHJ	P,.TCHAR##		; Type it out
	SOJA	P3,TCASC1		; Bop the count and start over
TCASC2:	JUMPLE	P2,.POPJ		; Return if no more pads needed
	PUSHJ	P,.TSPAC##		; Pad with a space
	SOJA	P2,TCASC2		; Loop for all needed pads

; CASSIX - Convert counted ASCII to SIXBIT

CASSIX:	ILDB	T2,T1			; Get byte count
	CAILE	T2,6			; More than six bytes
	 MOVEI	 T2,6			;  Yes, make it six
	SETZ	T3,			; Clear receiving buffer
CASSI1:	JUMPLE	T2,CASSI3		; Return when byte count hits zero
	LSH	T3,6			; Make room for next byte
	ILDB	T4,T1			; Get a byte from the string
	SUBI	T4," "			; Make it SIXBIT
	IOR	T3,T4			; Install new byte
	SOJA	T2,CASSI1		; Do it all over again

CASSI3:	MOVE	T1,T3			; Get data into right AC
CASSI2:	SKIPE	T1			; Prevent infinite loops
	TLNE	T1,770000		; Left justified yet??
	 POPJ	 P,			;  Yes, we're all done
	LSH	T1,6			; No, try again
	JRST	CASSI2			; . . .
	SUBTTL	Various string movement and comparison routines

; GETNNM get the node number from the node spec pointed to by T1
GETNNM:	ILDB	T2,T1			; Get low order part of number
	ILDB	T1,T1			; Get high order part
	LSH	T1,8			; Get high part into place
	IORI	T1,(T2)			; Make it a whole number
	POPJ	P,

; CPYNOD - Copy a node spec from one place to another.  T1 is source, T2 is
; destination.

CPYNOD:	ILDB	T3,T1			; Get low order byte of node number
	IDPB	T3,T2			; Install it in dest
	ILDB	T3,T1			; Get high order byte of node number
	IDPB	T3,T2			; Install that too

; CPYCAS - Copy a counted ascii string from one place to another.  T1 is
; source, T2 is destination.

CPYCAS:	ILDB	T4,T1			; Get byte count
	IDPB	T4,T2			; Install it
CPYCA1:	JUMPE	T4,.POPJ		; Return if done
	ILDB	T3,T1			; Get a byte
	IDPB	T3,T2			; Stuff it
	SOJA	T4,CPYCA1		; Loop till done

CMPNOD:	ILDB	T3,T1			; Get low order number from string 1
	ILDB	T4,T2			; Get low order number from string 2
	CAME	T3,T4			; Are they the same??
	 POPJ	 P,			;  No, give failure return

CMPSTR:	PUSHJ	P,.SAVE1##		; Get a safe register
	ILDB	P1,T1			; Get length of string 1
	ILDB	T3,T2			; Get length of string 2
	CAME	P1,T3			; Are they the same length??
	 POPJ	 P,			;  No, give failure return
CMPST1:	JUMPE	P1,.POPJ1		; Jump if strings are identical
	ILDB	T3,T1			; Get a byte from string 1
	ILDB	T4,T2			; Get a byte from string 2
	CAME	T3,T4			; Are they the same???
	 POPJ	 P,			;  Nope, fail
	SOJA	P1,CMPST1		; Yes, try next byte

IFN FTTIME,<
	SUBTTL	Time Accounting Routines

; These routines provide a facility for computing the average real time that
; it takes for an event to occur.  There is a routine for resetting the time
; data base, there is one for starting the timing of an event, there is one
; for finishing the timing of an event, and there is a routine for returning
; the average timing of the event.

; Call TIMRST with no args to reset the time data base
TIMRST:	SETZM	TIMTOT			; Clear time totals
	SETZM	TIMCNT			; Clear time count
	SETZM	TIMSTR			; Non-zero means we are timing now
	POPJ	P,			; And return

; Call TIMBEG to begin a timing interval
TIMBEG:	MSTIME	T1,			; Get time into T1
	MOVEM	T1,TIMSTR		; Set time of starting interval
	POPJ	P,			; And just return

; Call TIMEND to end a timing interval, return with T1/ time interval
TIMEND:	SKIPN	TIMSTR			; Were we timing an interval???
	 POPJ	 P,			;  No, quit
	MSTIME	T1,			; Get current time
	SUB	T1,TIMSTR		; Compute length of this interval
	ADDM	T1,TIMTOT		; Add that to the total
	AOS	TIMCNT			; Increment teh number of intervals
	SETZM	TIMSTR			; Indicate we have stopped timing
	POPJ	P,

; Call TIMAVG to return the average amount of time spent for all intervals
TIMAVG:	MOVE	T1,TIMTOT		; Get total amount of time
	IDIV	T1,TIMCNT		; Compute average
	POPJ	P,			; Return

; TIMPNT will print out the time interval contained in T1 in units of seconds
TIMPNT:	PUSHJ	P,.SAVE1##		; Save one
	ADDI	T1,^D5			; Round up to 100'ths of seconds
	IDIVI	T1,^D1000		; Convert milliseconds to seconds
	MOVE	P1,T2			; Save remainder
	PUSHJ	P,.TDECW##		; Type out seconds
	MOVEI	T1,"."			; Get decimal point
	PUSHJ	P,.TCHAR##		; Print that too
	MOVE	T1,P1			; Get back remainder
	IDIVI	T1,^D10			; Convert milliseconds to 100'ths
	PJRST	.TDECW##		; Type out fraction
> ;END FTTIME
	SUBTTL	WRNRTN and ERRDIE  Error handling routines

WRNRTN:	PUSHJ	P,.PSH4T##		; Save all the important acs
	MOVE	T3,@-4(P)		; Get the instruction after PUSHJ to us
	LDB	T4,[POINT 9,T3,8]	; Get the opcode
	CAIE	T4,JUMP_-^D27		; Is it a jump?
	 HALT	 .			; No, die horribly
	MOVE	T1,0(T3)		; Get first arg for .ERMSG
	MOVE	T2,1(T3)		; Get second arg for .ERMSG
	PUSHJ	P,.ERMSG##		; Call the processor
	PUSHJ	P,.TCRLF##		; Tie it off with a CRLF
	PUSHJ	P,.POP4T##		; Restore the world
	POPJ	P,

ERRDIE:	PUSHJ	P,.ERMSG##		; Issue the error message
	PUSHJ	P,.TCRLF##		; Followed by a CRLF
	PUSHJ	P,.CLRBF##		; Clear typeahead
	JRST	NETPTH			; And start over
	SUBTTL	NSP. Error handling

NSPERR:	PUSHJ	P,.SAVE1##		; Save an AC
	MOVE	P1,T1			; Save error from NSP. uuo
	MOVE	T1,['NETNSP']		; Get message prefix
	MOVE	T2,["?",,[ASCIZ |NSP. error |]] ; Get error prefix
	PUSHJ	P,.ERMSG##		; Print message
	CAILE	P1,MAXERR		; Do we know this error code?
	 SETZ	 P1,			;  No, use general unknown
	HRR	T1,NSPERC(P1)		; Get address of error text
	PUSHJ	P,.TSTRG##		; Type it out
	PUSHJ	P,.TCRLF##		; Type a <CRLF>
	JRST	NETPTH			; And restart gracefully
DEFINE ERRMAC(code,text),<
	IF1,<IFN code-<.-NSPERC>,<
		PRINTX ?NSP. error code out of order in NSPERC table>>
	ERRMC1(\code,text)
>
DEFINE ERRMC1(code,text),<[ASCIZ |(code) text|]>

NSPERC:	ERRMAC 0,     <Unknown Error, code in AC 1>
	ERRMAC NSABE%,<Argument Block Format Error>
	ERRMAC NSALF%,<Allocation failure>
	ERRMAC NSBCN%,<Bad channel number>
	ERRMAC NSBFT%,<Bad format type in process block>
	ERRMAC NSCFE%,<Connect Block format error>
	ERRMAC NSIDL%,<Interrupt data too long>
	ERRMAC NSIFM%,<Illegal flow control mode>
	ERRMAC NSILF%,<Illegal function>
	ERRMAC NSJQX%,<Job quota exhausted>
	ERRMAC NSLQX%,<Link quota exhausted>
	ERRMAC NSNCD%,<No connect data to read>
	ERRMAC NSPIO%,<Percentage input out of bounds>
	ERRMAC NSPRV%,<No Privileges to Perform Function>
	ERRMAC NSSTB%,<Segment size too big>
	ERRMAC NSUKN%,<Unknown node name>
	ERRMAC NSUXS%,<Unexpected State: Unspecified>
	ERRMAC NSWNA%,<Wrong number of arguments>
	ERRMAC NSWRS%,<Function called in wrong state>

;New error codes (to be re-ordered):

	ERRMAC NSCBL%,<Connect block length error>
	ERRMAC NSPBL%,<Process block length error>
	ERRMAC NSSBL%,<String block length error>
	ERRMAC NSUDS%,<Unexpected State: Disconnect Sent>
	ERRMAC NSUDC%,<Unexpected State: Disconnect Confirmed>
	ERRMAC NSUCF%,<Unexpected State: No Confidence>
	ERRMAC NSULK%,<Unexpected State: No Link>
	ERRMAC NSUCM%,<Unexpected State: No Communication>
	ERRMAC NSUNR%,<Unexpected State: No Resources>

;Error codes which correspond to DECnet disconnect codes.

	ERRMAC NSRBO%,<Rejected by Object>
	ERRMAC NSDBO%,<Disconnected by Object>
	ERRMAC NSRES%,<No Resources at Remote Node>
	ERRMAC NSUNN%,<Unrecognized Node Name>
	ERRMAC NSRNS%,<Remote Node Shut Down>
	ERRMAC NSURO%,<Unrecognized Object>
	ERRMAC NSIOF%,<Invalid Object Name Format>
	ERRMAC NSOTB%,<Object Too Busy>
	ERRMAC NSABM%,<Abort by Management>
	ERRMAC NSABO%,<Abort by Object>
	ERRMAC NSINF%,<Invalid Node Name Format>
	ERRMAC NSLNS%,<Local Node Shut Down>
	ERRMAC NSACR%,<Access Control Rejection>
	ERRMAC NSNRO%,<No Response from Object>
	ERRMAC NSNUR%,<Node Unreachable>
	ERRMAC NSNLK%,<No Link>
	ERRMAC NSDSC%,<Disconnect Complete>
	ERRMAC NSIMG%,<Image Field Too Long>
	ERRMAC NSREJ%,<Unspecified Reject Reason>

	ERRMAC NSBCF%,<Bad combination of NS.EOM & NS.WAI flags>
	ERRMAC NSADE%,<Address Error>
MAXERR==.-NSPERC-1
	SUBTTL	Pure data and Low segment

STKPTR:	IOWD	STKLEN,STACK		; Set up stack from this word

	XLIST				; Turn off listing for a moment
	LIT				; Generate literals in hiseg
	LIST				; Turn on listing after lits

	RELOC	0

; Argument block for mapping node numbers into node names
NTMAP:	EXP	.NTLST
	EXP	0
	EXP	0
	EXP	.NTMAP
	EXP	0
	EXP	0
	POINT	8,MAPBUF
	EXP	0
	EXP	0

MAPBUF:	BLOCK	<9+3>/4

; Argument block for reading the executor node name
NTREX:	EXP	.NTLST
	EXP	0
	EXP	0
	EXP	.NTREX
	EXP	0
	EXP	0
	POINT	8,X.NODE
	EXP	0
	EXP	0

; Argument block for reading the status of a node
NTRCH:	EXP	.NTLST
	EXP	.NTNOD
	EXP	0
	EXP	.NTSHO
	EXP	.NTSTA
	EXP	0
	POINT	8,RCHBUF
	EXP	0
	EXP	0

	RCHBLN==30
RCHBUF:	BLOCK	<RCHBLN+3>/4

NICBUF:	BLOCK	10			; Buffer for NICE
	NICLEN==<.-NICBUF>*4

; The following is an NSP. arg block for doing an Enter Active to another NML

ENTACT:	NS.WAI+.NSFEA_^D18+3		; Enter Active, Please Wait
	EXP	0			; Channel number
	EXP	CBLK			; Connect Block pointer

; The following block is used to read the connect confirm data

RCCBLK:	XWD	.NSFRC,5		; Read connect data function
	EXP	0			; Channel number
	EXP	CCDATA			; Pointer to string block
	EXP	0
	EXP	0

CCDATA:	STRBLK	^D16			; Connect Confirm Data string block

; The next block is used for sending data over the network

SNDBLK:	NS.EOM+NS.WAI+.NSFDS_^D18+4	; Blocking data send and end of message
	EXP	0			; Channel number goes here
	EXP	0			; Byte count goes here
	EXP	0			; Byte pointer goes here

; This block is used for receiving data from the network (via NSP.)

RCVBLK:	NS.WAI+.NSFDR_^D18+4		; Blocking data receive
	EXP	0			; Channel number
	EXP	0			; Byte count
	EXP	0			; Byte pointer

; This is a disconnect block

DSCBLK:	XWD	.NSFSD,3		; Synchronus disconnect
	EXP	0			; Channel number goes here
	EXP	0			; No disconnect data

; This is a channel release block

RELBLK:	XWD	.NSFRL,2		; Release this channel
	EXP	0			; Channel number goes here

MSGBUF:	BLOCK	^D50			; Lotsa room for network data
	MSGLEN==<.-MSGBUF+3>*4		; # of 8 bit bytes
RSPCNT:	BLOCK	1			; Number of bytes left in response buffer
RSPPTR:	BLOCK	1			; BP into buffer
RSPBUF:	BLOCK	^D100			; Buffer for total response
	RSPLEN==<.-RSPBUF+3>*4

; The following is a connect block for opening the network connections

CBLK:	EXP	CBLEN			; .NSCNL
	EXP	CNODE			; .NSCND
	EXP	CSOURC			; .NSCSD
	EXP	CDEST			; .NSCDD
	EXP	CUSER			; .NSCUS
	EXP	CPASS			; .NSCPW
	EXP	CACC			; .NSCAC
	EXP	CUDATA			; .NSCUD
	CBLEN==.-CBLK

CNODE:	STRBLK	6			; Max six chars for node names
CDEST:	EXP	3			; .NSDFL
	EXP	0			; .NSDFM
	EXP	.OBNIC			; .NSDOB
CSOURC:	EXP	5
	EXP	1,0,0			; Format 1, no object type or PPN
	EXP	SRCSTR			; Address of my name
CUSER==<CPASS==<CACC==0>>		; No USER-ID, PASSWORD, or ACCOUNT
CUDATA:	XWD	3,CUDLEN		; Special string block for NML
	BYTE	(8) 4,0,0		;  version number
	CUDLEN==.-CUDATA

SRCSTR:	STRBLK	^D6,<NETPTH>		; My name

; The following is NICEese for SHOW NODE xxx STATUS
CKTNIC:	BYTE	(8) ^D20,1_4+.NTNOD,0,0,0
CKTNLN==5	; Number of bytes in this NICE message

; The following is NICese for SHOW CIRCUIT xxx STATUS
NODNIC:	BYTE	(8) ^D20,1_4+.NTCKT  ; Circuit id goes here
	EXP	0,0,0,0			;  and here, and here, and here

; The following two locations must be kept together
NICCNT:	BLOCK	1			; Remaining byte count for NICE message
NICBPT:	BLOCK	1			; NICE byte pointer
NICRTN:	BLOCK	1			; Driver for getting NICE bytes

LASERR:	BLOCK	1		; Last NSP. error from OPNLNK
VERFLG:	BLOCK	1		;-1=Phase 2, 0=phase 3, 1=phase 4
RSPNFG:	BLOCK	1			; Parsing a response for a NODE entity
NETCHN:	BLOCK	1			; Channel for NSP.'s
LCLNOD:	BLOCK	1			; Name of the local node
CURNOD:	BLOCK	1			; Name of current node
CURNUM:	BLOCK	1		; Number of current node
EXUNT:	BLOCK	1			; Exit when done flag

; The following two locs must be consecutive
ENDNOD:	BLOCK	1			; Name of node we are seeking
ENDNUM:	BLOCK	1			; Number of end node
IFN FTTIME,<
TIMTOT:	BLOCK	1			; Total amount of time elapsed so far
TIMCNT:	BLOCK	1			; Number of intervals
TIMSTR:	BLOCK	1			; Real time at start of this interval
CITIME:	BLOCK	1			; Length of this CI
> ;END FTTIME
PTRHLD:	BLOCK	2			; Holding area for response pointer
					;  used betwixt GETCKT and GETNOD
DEFFLG:	BLOCK	1			; =0 means we have defaulted already

;next two must be together
COST:	BLOCK	1			;COST of a circuit
HOPS:	BLOCK	1			;and HOPS

X.NODE:	BLOCK	3			; Executor node name goes here
N.SPEC:	BLOCK	3			; Node name goes here
N.DEST:	BLOCK	3			; Holds destination node spec
N.SRC:	BLOCK	3			; Holds source node spec
STACK:	BLOCK	50
	STKLEN==.-STACK
	SUBTTL	SCAN control blocks

; Control block for .ISCAN

ISCARG:	XWD	ISCLEN,ISCBLK			; T1 for call to .ISCAN
ISCBLK:	EXP	0
	EXP	0
	EXP	0
	EXP	0
	EXP	0
	EXP	0
	ISCLEN==.-ISCBLK

PSCARG:	XWD	PSCLEN,PSCBLK
PSCBLK:	XWD	0,0
	XWD	0,0
	XWD	0,0
	XWD	0,0
	PSCLEN==.-PSCBLK

	END	NETPTH