perm filename TCPSER.MA1[IP,NET] blob sn#702332 filedate 1983-03-05 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00074 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00010 00002		title	TCPSer  VTCPSr 
C00011 00003	\
C00012 00004		subttl	TCP states  S%Clos S%List S%SynS S%SyRP S%SyRA S%Estb S%Fin1 S%Fin2 S%Clsn S%TimW S%ClsW S%LAck
C00014 00005		subttl	macro for dispatching on different states  Dispat  ...min ...max ...flg ...x ...flg ...x
C00018 00006		subttl	defintions describing a TCP leader  TcpLen TCPIBH TCPIBf TCPObf TCPPnt TCPFlg TC%Urg TC%Ack TC%Psh TC%Rst TC%Syn TC%Fin TC%Low TC%ALL TC%Onc
C00021 00007		subttl	definitions  TC$ACK WndSiz StartT TCPUTT TCPRTT RTMin RTMax
C00022 00008		subttl	FMB 
C00025 00009		subttl	process incoming TCP message  TCPIn TCPIn0 TCPNCk RedCnt NewLst
C00032 00010		subttl	now parse options  OptnLp OptDis OptMax OptSeg OptDun NoOptn
C00035 00011	 here to process the message with DDB in tow.  FuturL NoFutr
C00039 00012		subttl	process a connection which has no DDB  NewCon NotExc
C00043 00013	ROUTINE TO CHECK LEGALITY OF AN EXEC Well Known Port.  WKPFND WKPFN1 WKPSRV WKPNUM WKPSKT WKPTFC
C00045 00014	 here to process one message.  this may be hot off the presses or it  PrcMsg
C00048 00015	 here to check for the segment starting in the window  WndFit
C00051 00016	 here to check for a segment finishing in the window  WndEnd FlsLp FlsBad
C00054 00017	 at this point we have a message which starts and ends within  InWind IsNext
C00057 00018	 deal with the urgent pointer, if there is one  TCPUrg
C00059 00019	 message chain is in P1. left half: first buffer, right half: last buffer.  TCPTxt TCPTx1
C00061 00020	 here to check for a FIN and handle it  TCPFin TCPFi0 TCPFi1
C00064 00021	FINF2:	movei	t1,2*MSL		 load up twice maximum segment life  FINF2 FINTW
C00066 00022	 here if we received a segment for a connection that doesn't exist  TryRst RstFls SndRst
C00068 00023	 here if we received a segment while listening for one  InLstn AckAck AckAc1
C00073 00024	 here if received a segment for a connection in SYN-SENT state  InSynS InSyn1 InSyn2
C00077 00025	
C00078 00026		subttl	returns  NoLead NoMess BadChk FlsOpt NoPort NoDDB NoITY AckErr UrgErr FinErr SeqBad BufFls FlsRst RstSRP RstBTL RstSRA RstEst RstCls FlsSyn
C00083 00027		subttl	routines to handle an ACK in various states  ACKSyR ACKEst AckEs1 ACKF1 ACKCln ACKLAc ACKTW
C00089 00028		subttl	AckUpd  ACKUpd NoAllc RetrLp RetrD0 RetrDn RetrNo
C00095 00029		subttl	deal with a message received before it should be  NotNxt FtrOrd FtrOr1 FtrRpl FtrOr2 FtrNew FtrSav
C00099 00030	 routine to get an FMB, return it in T1  GetFMB RelFMB FlsFMB FlsFM1
C00101 00031		subttl	GetISS  GetISS
C00102 00032		subttl	SecChk  SecChk
C00103 00033		subttl	TCPMak  TCPMak TCPMa1 MakCnt
C00110 00034		subttl	TCPRsp  TCPRsp
C00113 00035		subttl	TCPIFn  TCPIFn TCPIF1
C00116 00036		subttl	TCPICK  TCPICK
C00118 00037		subttl	TCPOCK  TCPOCK
C00120 00038		subttl	TCPTCk  TCPTCk
C00122 00039		subttl	TCPWUp  TCPWUp
C00124 00040		subttl	SetUrg  SetUrg
C00126 00041		subttl	TcpChk  TCPChk TCPCRT TCPRTR RtLoop RtNxt TCPCUT TCPUTO
C00132 00042	USER INTERFACE (IMPUUO)  CALL FLAGS IF.NWT IF.PRV IF.ALS TIMEOUT E .UUDEV .UUSTT .UUSKT .UUHST .UURMT .UULST PUUTIM sk.lcl FreOvr FreLsh FrePrt FreMin FreMch
C00135 00043	NOWAITS<  IMPUUO IMPUU1 ImpUU2 ImpUU3 ImpUU4
C00139 00044	MACRO FOR BUILDING THE DISPATCH TABLE  ZZ ZZ UU.PVN UU.PVI UU.ASD UU.NDB UU.INT UU.DNU uu.NUp
C00141 00045	THE DISPATCH TABLE  UUOTAB UUOLEN
C00143 00046	NOWAITS< It appears this code is never called.  ERRCHK
C00144 00047		DEFINE	ERRCOD(M,C) <  ERRLST ERRXIT ErrSet
C00149 00048	NOWAITS<  TRANS RAISS KILLS VERSS INISS IFRSTR
C00151 00049	NOWAITS<
C00153 00050	XSTSS:	PUSHJ	P,GETWDU##	RETURN NUMBER OF USER ARGS  XSTSS XSTSS0 XSTSS1 XSTSTB XSTBLN
C00156 00051	NOWAITS<  STATS STATS0 STATS9 STATS1
C00158 00052	NOWAITS< Perhaps WAITS should have something like this  ITTYS ITTYS1 ITTYS2
C00161 00053	HERE WITH DESIRED IMP DDB POINTED TO BY F  ITTYS3
C00162 00054	repeat 0,<	 should be simple  PIALS
C00164 00055	ROUTINE TO WAIT UNTIL THE CONNECTION BETWEEN A LOCAL TTY AND  XPWTS PPARS RPARS
C00167 00056	ROUTINES TO SET AND READ THE VARIOUS QUOTE AND ESCAPE CHARACTERS  PESCS RESCS ALLQUO ALLQU1
C00170 00057	SUBROUTINE FOR SETTING UP A SIMPLEX CONNECTION.  CONNS GotArp
C00174 00058	SUBROUTINE TO DROP A CONNECTION.  CLOSS PCLSSD ClsFls ClsEst
C00177 00059	SUBROUTINE TO DEASSIGN A DEVICE AFTER IT HAS HAD  DEASS
C00178 00060	SUBROUTINE TO PUT A SOCKET IN THE LISTENING STATE  LISTS Lists2 LISTS1
C00181 00061	SUBROUTINE TO GET A SOCKET REQUEST  REQUS PW1PJ1
C00183 00062	SUBROUTINE TO CONNECT A DUPLEX IMP CONNECTION TO  TALKS TalkOK
C00185 00063	repeat 0,<	 might be fun to do sometime.....  TRACS TRCTAB PINTS AINTS PECOS
C00189 00064	SUBROUTINE TO RETURN THE LOCAL HOST AND IMP PARAMETERS  PHSTS PWUPJ1
C00191 00065	REPEAT 0,<		THESE FUNCTIONS WERE NEVER DEBUGGED  PALLS PALLS1 PGVBS RSETS PNOPS PNOPS1 GETHS1
C00194 00066	NOWAITS< WAITS calls T10DDB to do some of this.  SETDDB SETDD3 SETDD0 SETDD2 SETDD1 SetDD4
C00198 00067	 subroutine to set up essential areas of a DDB  PrpDDB
C00200 00068	ROUTINE TO MAKE A LOCAL SOCKET NUMBER FOR A USER'S IMPUUO.  MAKMYS MAKMY0 MakM00 MakMy1 MakNxt MakFre
C00206 00069	ROUTINE TO ALLOCATE A FREE SOCKET RANGE  FRESKT FRESK1
C00208 00070	 subroutine to decide where to send a message on the ARPAnet to get  Target
C00209 00071	 subroutine to wait for state to arrive at an established state.  EstbWt EstbCl EstbSF EstbTm EstbEr EstbFl EstbW1
C00213 00072	SUBROUTINE TO WAIT FOR NCP ACTIVITY.  StWait StWai1 StWai2
C00215 00073	SUBROUTINE TO SET TCP state WAIT DONE.  CALLED AT INTERRUPT LEVEL.  TCPIOD
C00216 00074		$low  SktNum
C00217 ENDMK
C⊗;
	title	TCPSer ; VTCPSr ;⊗
	subttl	provan

	search	f,s
	search	NetDef		; network definitions
	search	MacTen		; search only if symbol not found in NetDef
				; MacTen needed for movx, txo, etc.

	sall

	$reloc
	$high

XP	VTCPSr,1		; first TCP version
comment	\

this module contains the support routines for the transmission
control protocol as defined in RFC-793

\
	subttl	TCP states ;⊗ S%Clos S%List S%SynS S%SyRP S%SyRA S%Estb S%Fin1 S%Fin2 S%Clsn S%TimW S%ClsW S%LAck


; first define the states we have for TCP

S%Clos==↑d0		;; closed (sometimes convenient, although usually
			;;		detected by absense of DDB)
			;; must ALWAYS be zero.  "closed" type states are
			;;	less than or equal to zero.
S%List==↑d1		;; listen
S%SynS==↑d2		;; SYN sent
S%SyRP==↑d3		;; SYN received, passive
S%SyRA==↑d4		;; SYN received, active (from S%SynS)
S%Estb==↑d5		;; established
S%Fin1==↑d6		;; FIN wait 1
S%Fin2==↑d7		;; FIN wait 2
S%Clsn==↑d8		;; Closing
S%TimW==↑d9		;; time wait
S%ClsW==↑d10		;; Close wait
S%LAck==↑d11		;; last ACK
	subttl	macro for dispatching on different states ; Dispat ;⊗ ...min ...max ...flg ...x ...flg ...x


; now define a macro to define a dispatch vector.  there are three
;  arguments.  the first is the register containing the state code.
;  the second is the location to jump to if a state comes which
;  is not defined in this table.  the third is a list of pairs of
;  entries: the state code and the instruction to execute.

;warning: the state pairs MUST begin on the same line as the second
;	argument.  state pairs MUST be separated from each other with
;	commas (between each pair) which MUST be on the same line as
;	the macro which FOLLOWs.

define	Dispat (AC,ErrLoc,StPair),
    <
	...min==777777			;; a high starting point
	...max==-1			;; and a low one
	define	Pair (state,instr),
	    <
		ifl <state>-...min,<	...min==<state>	>
		ifg <state>-...max,<	...max==<state>	>
	    >

	define	$$help(bogus),<	pair(bogus) >	;; your classic helper macro

	irp StPair,<		;; for each pair
		$$help(StPair)	;; expand the Pair macro with each pair
				;;  the as arguments.
	>

	;; code to check to see if the state is in the legal range
	cail	<AC>,...min		;; less than the lowest we know
	 caile	<AC>,...max		;; or greater than the highest
	  jrst	<ErrLoc>		;; go to the error handler

	define	Pair (state,instr),
	    <
		ife ...x-<state>,<	;; is this our state?
			instr		;; expand the instruction
			...flg==1	;; and tell that we did something
		>
	    >

	;; code for the actual dispatching
	xct	[
		 ...x==...min		;; start with lowest state
		 repeat ...max-...min+1,<	;; do every state in the range
		    ...flg==0		;; nobody's claimed this spot yet
		    irp StPair,<	;; go through all the pairs
			$$help(StPair)	;; expanding the Pair macro with each.
		    >
		    ife ...flg,<	;; if no one claimed to be this
			jrst	<ErrLoc>;; go to the error handler
		    >
		    ...x==...x+1	;; next place.
		 >
		]-...min(<AC>)		;; now correctly index the XCT
	purge	...min,...max,...x,...flg
    >	;; end of Dispat macro definition
	subttl	defintions describing a TCP leader ;⊗ TcpLen TCPIBH TCPIBf TCPObf TCPPnt TCPFlg TC%Urg TC%Ack TC%Psh TC%Rst TC%Syn TC%Fin TC%Low TC%ALL TC%Onc

; see RFC-793 for details of this header.


TcpLen==:5		; number ofwords in an TCP leader (not including options)

	$low		; define the storage needed

TCPIBH:	block	NBHLen			; buffer header.
TCPIBf:	block	TCPLen			; words needed for header

; the following block is used to create a TCP leader for output.
;  it is filled and then converted to 36 bit buffers all under ScnOff.
TCPObf:	block	NBHLen+TCPLen		; output buffer for forming leader

	$high		; back to protected code

TCPPnt:	point	8,TCPIBf		; pointer to start loading the
					;  header block from the stream.

; define the actual header fields.  position is the bit position of the
;  left most bit.
;
; 	name   word  position width

; TCP uses the standard ports, StdSP and StdDP.
;DefFd.	TCPSP,	0,	0,	16	; source port of message
;DefFd.	TCPDP,	0,	16,	16	; destination port
DefFd.	TCPSeq,	1,	0,	32	; sequence number
DefFd.	TCPAck,	2,	0,	32	; acknowledgement number
DefFd.	TCPOff,	3,	0,	4	; data offset from start of leader
					;  (length of total leader in words)
TCPFlg==3	; flags are in the third word
	TC%Urg==1b<↑d10>	; urgent flag
	TC%Ack==1b<↑d11>	; acknowledge flag
	TC%Psh==1b<↑d12>	; push flag
	TC%Rst==1b<↑d13>	; reset flag
	TC%Syn==1b<↑d14>	; syncronize sequence numbers
	TC%Fin==1b<↑d15>	; finished
	TC%Low==TC%Fin		; low order bit of group
	TC%ALL==TC%Urg!TC%Ack!TC%Psh!TC%Rst!TC%Syn!TC%Fin
	; bits which must be manually set each time they need to be sent.
	TC%Onc==TC%Urg!TC%Psh!TC%Rst!TC%Syn!TC%Fin
DefFd.	TCPWnd,	3,	16,	16	; window allocated
DefFd.	TCPChk,	4,	0,	16	; checksum of message
DefFd.	TCPUP,	4,	16,	16	; urgent pointer
	subttl	definitions ;⊗ TC$ACK WndSiz StartT TCPUTT TCPRTT RTMin RTMax

; flags in S during input
	TC$ACK==1b<↑d35>	; send an ACK at end of processing


; standard allocations and time-out times
	WndSiz==20*NBfByt		; number of bytes is normal window
	StartT==↑d60			; time we'll wait for server
					;  process to start up.
	TCPUTT==2*↑d60			; time to wait before declaring
					;  a connection dead in the water.
	TCPRTT==↑d5			; retransmission time (in seconds)
	RTMin==1			; minimum retranmission time (1 sec.)
	RTMax==↑d60			; maximum retranmission time (1 min.)
	subttl	FMB ;⊗

; the FMB, Future Message Block, is a block of information about a
;  message who's sequence number we are not ready to handle yet.
;  this block contains all the information necessary to process
;  the message, including the complete TCP header  for this message
;  and a pointer to buffers containing the message itself.


;;!------------------------------------|------------------------------------!
;;!									    !
;;!									    !
;;!			TCP header for this message (5 words)		    !
;;!									    !
;;!									    !
;;!------------------------------------|------------------------------------!
;;!   first buffer in message chain    |   last buffer in message chain	    !
;;!------------------------------------|------------------------------------!
;;!			pointer to next FMB in chain			    !
;;!------------------------------------|------------------------------------!
;;!		sequence number of first byte following this message	    !
;;!------------------------------------|------------------------------------!


bkini.		; have to start somewhere

	bknxt.	FMBTCL,TCPLen*ful.wd	; space TCP header
	bkoff.	FMBTCP			; grab offset into block for start
					;  of that field.

	bkdef.	FMBPnt			; buffer pointer (whole word)
	bknxt.	FMBFst,hlf.wd		; first buffer in message
	bknxt.	FMBLst,hlf.wd		; last buffer in message

	bknxt.	FMBNxt			; next buffer in chain
	bkoff.	FNxtOf			; grab offset, too

	bknxt.	FMBNBy			; sequence number of first byte
					;  of message which should follow.

bkend.	FMBLen				; get the length
	subttl	process incoming TCP message ;⊗ TCPIn TCPIn0 TCPNCk RedCnt NewLst


entry	TCPIn	; only load this module if IP calls this routine


TCPIn::
	move	p2,MsgLen(f)		; get length of message through IP
ifn FtChck,<	; doing checksum
	setz	p3,			; clear checksum
	move	t1,p2			; make sure to checksum length
					;  of TCP message before we
					;  convert it to length of segment.
	pushj	p,CSmHWd##		; checksum the length.
>
	subi	p2,TCPLen*4		; cut length by that amount
	jumpl	p2,NoLead		; not enough message to read in leader
	movei	t1,TCPIBH		; get pointer to input leader
	move	t2,ABfLst(f)		; get last buffer so far
	stor.	t1,NBHNxt,(t2)		; make us their next
	movem	t1,ABfLst(f)		; and make us last (for grins)
	move	t1,TCPPnt		; point at the storage block
	movei	t2,TCPLen*4		; length of leader in bytes
	stor.	t2,NBHCnt,TCPIBH	; store in buffer header
	pushj	p,GetLed##		; get the leader and checksum
	  jrst	NoLead			; not enough bytes for leader.


	; now read in the options and hold for later
	load.	t1,TCPOff,TCPIBf	; get "offset to data"
	subi	t1,TCPLen		; get words left to be read in leader
	jumple	t1,TCPIn0		; no options to read
	lsh	t1,wd2byt		; convert to bytes
	sub	p2,t1			; cut down message length again
	jumpl	p2,NoLead		; not enough in IP message for
					;  TCP leader indicated.
	pushj	p,GetMes##		; read in the options
	  jrst	NoLead			; message ended too soon.
	aos	TCPOpt##		; saw an option

TCPIn0:
	exch	t1,p2			; position length of message
					;  and put options in a safe place.
	movem	t1,MsgLen(f)		; save length of TCP message
	pushj	p,GetMes##		; copy T1 bytes in.
	  jrst	NoMess			; problem reading message

	move	p1,t1			; save new stream pointer for later.

ifn FtChck,<	; doing checksumming
	load.	t1,TCPChk,TCPIBf	; get the checksum from the leader
	jumpe	t1,TCPNCk		; this guy doesn't do checksums

	move	t1,RmtAdr(f)		; get their address.
	pushj	p,CSmWrd##		; add in that checksum.
	move	t1,LclAdr(f)		; our address
	pushj	p,CSmWrd##		; checksum it.
	move	t1,Protcl(f)		; get the protocol
	pushj	p,CSmHWd##		; checksum that half a word
	

	; bear in mind that the checksum we now have in P3 has, along with
	;  all the right stuff, its own one's complement.  therefore, what
	;  we really have is <checksum> + -<checksum>, which is 0.
	;  further, since <checksum> has some bit on (otherwise the
	;  sender isn't checksuming and we wouldn't be here), it can be
	;  shown that the brand of one's complement 0 we must have is
	;  the version with all 1's.  if that's what we have, we're ok.
	;  if not, the checksum failed.
	hrrzs	p3			; get just the checksum
	caie	p3,<1←↑d16>-1		; magic explained above
	  jrst	BadChk			; checsum is bad.

TCPNCk:	; here to skip over the checksum checks because sender is not
	;  checksumming the messages.
>
	; count all the bits in the flag word as message types to get some
	;  idea of what we're sending.
	movx	t1,TC%Low		; get lowest order bit
	setz	t2,			; and a count
RedCnt:	tdne	t1,TCPFlg+TCPIBf	; is that bit on in the flag word?
	  aos	TCPITy##(t2)		; yes.  count one more with
					;  that bit on.
	lsh	t1,1			; shift bit over one
	txne	t1,TC%All		; bit no longer in field?
	  aoja	t2,RedCnt		; still in flag field.  count on.

	move	t1,RmtAdr(f)		; source (foreign host address)
	load.	t2,StdSP,TCPIBf		; get his port
	load.	t3,StdDP,TCPIBf		; get my port
	move	t4,Protcl(f)		; get protocol
	move	p3,MsgLen(f)		; put length of this message
					;  somewhere where we can get
					;  it for the new DDB.
	push	p,f			; save current DDB, in case we fail
	pushj	p,FndDDB##		; scan network DDBs for the one
					;  that matches.
	  jrst	NewCon			; this is one we haven't heard of
	pop	p,(p)			; don't want that F any more.
NewLst:					; return here if we are now
					;  listening for it (exec port).
	movem	p3,MsgLen(f)		; remember the message length
					;  in the new DDB.
	subttl	now parse options ;⊗ OptnLp OptDis OptMax OptSeg OptDun NoOptn


	jumple	p2,NoOptn		; skip all this if no options
					;  were read in.
	push	p,p1			; preserve our actual message
	hlrz	p1,p2			; point at the first buffer of options
	push	p,p1			; save that for later
	setzb	p3,s			; clear count register and flags
OptnLp:	pushj	p,NxtByt##		; get next option
	  jrst	OptDun			; no more
	caig	t1,OptMax		; larger number than we know about?
	  jrst	@OptDis(t1)		; no.  handle it
	aos	TCEUOp##		; we don't understand this option.
	pushj	p,OptFls##		; flush the option
	  jrst	OptDun			; all done.
	jrst	OptnLp			; and try the next option

; dispatch table for options
OptDis:
		OptDun			; end of option list
		OptnLp			; noop
		OptSeg			; maximum segment size
OptMax==.-OptDis-1			; get highest option number we know.

OptSeg:	pushj	p,NxtByt##		; get next byte.
	  jrst	OptDun			; no next byte.  all done.
	move	t4,t1			; save count
	pushj	p,NxtByt##		; get first byte of length
	  jrst	OptDun			; ran out
	move	t3,t1			; save it
	pushj	p,NxtByt##		; get next byte
	  jrst	OptDun			; ran out again
	lsh	t3,↑d8			; shift first byte over to make room
	ior	t3,t1			; or in the other byte
	movem	t3,SndMax(f)		; save it it the DDB
	movei	t1,-4(t4)		; get length back, minus parts
					;  we read.
	pushj	p,NxtFls##		; flush any that are more than
					;  we needed.
	  jrst	OptDun			; nothing left in buffers
	jrst	OptnLp			; get next option

; here when all done reading options
OptDun:
	pop	p,t1			; get the pointer to the first buffer
	pushj	p,RelBuf##		; release the entire stream
	pop	p,p1			; recover message buffer pointer.

NoOptn:	; come here if there are no options to process
; here to process the message with DDB in tow. ;⊗ FuturL NoFutr

	movei	u,TCPIBf		; leader is still in the block.

	setz	s,			; clear flags word

	pushj	p,PrcMsg		; process this message

; scan the future queue for messages which can now be processed
FuturL:	skipn	t2,Future(f)		; get the start of the futures
	  jrst	NoFutr			; no futures
	load.	t1,TCPSeq,FMBTCP(t2)	; get sequence number from header
	camle	t1,RcvNxt(f)		; are we ready for this one yet?
	  jrst	NoFutr			; no.  newest future is too late.

	load.	t1,FMBNxt,(t2)		; get this one's next pointer
	movem	t1,Future(f)		; now that's the next one
	load.	p1,FMBPnt,(t2)		; get buffer pointer back
	load.	t1,FMBNBy,(t2)		; get byte just past this message
	caml	t1,RcvNxt(f)		; did we pass the message altogether?
	  jrst	[			; yes.  throw this one out.
		 pushj	p,RelFMB	; get rid of the FMB
		 pushj	p,BufFls	; release the buffers in the message
		 jrst	FuturL		; try the next future
		]
	aos	TCPFTU##		; count future message used
	load.	p2,FMBNBy,(t2)		; get the  sequence number of the
					;  next message after this one.
	load.	t1,TCPSeq,FMBTCP(t2)	; get sequence number
	sub	p2,t1			; compute the length of the message.
	movem	p2,MsgLen(f)		; remember that in the DDB
	movei	u,FMBTCP(t2)		; point at block with TCP leader.
    
	push	p,t2			; save FMB so we can delete it
	pushj	p,PrcMsg		; process this message
	pop	p,t2			; get back FMB

	pushj	p,RelFMB		; free FMB

	jrst	FuturL			; check for another future


NoFutr:	txzn	s,TC$ACK		; should we fire off an ACK?
	  popj	p,			; no.  return
	scnoff				; shut down interrupts
	skipg	State(f)		; have we been closed while we
					;  weren't looking?
	  pjrst	sonppj##		; yes.  forget it
	movx	t2,TC%Ack		; get ACK bit
	iorm	t2,SndBts(f)		; make sure it's set.
	pushj	p,SndMsg##		; yes.  tell IMPSER to get it sent or
					;  send it directly and return.
	  jfcl				; ignore error return
	pjrst	sonppj##		; interrupts back on and return
	subttl	process a connection which has no DDB ;⊗ NewCon NotExc


; handle a connection to a port which is not listening.
; port number is in T3.  old DDB (at this writing, always the pseudo
;	DDB) is on the stack.
NewCon:
	pop	p,f			; get back the DDB we've been given.
	movem	t2,RmtPrt(f)		; save his port
	movem	t3,LclPrt(f)		; and this port
	trne	t3,<-1←↑d8>		; anything on but the low 8 bits?
	  jrst	NotExc			; yes.  not an exec port.
	move	t1,t3			; position our port number better
	PUSHJ	P,WKPFND	;IS THIS SOCKET'S SERVICE IMPLEMENTED?
	  jrst	NoPort			; remember this "error"
	move	t4,t1			; save service offset
	MOVEI	J,0		;NO JOB NUMBER YET
	PUSHJ	P,DDBGET##	;TRY FOR FREE DDB
	  jrst	NoDDB			; can't get one
	PUSHJ	P,ITYGET##	;GET A PORT
	  jrst	NoITY			; can't get one
	MOVSI	u,TTYKBD!TTYPTR
	IORb	u,TTYLIN(F)	; SET TTY BITS, get ITY's LDB into U
	PUSHJ	P,TSETBI##	;CLEAR INPUT BUFFER
	PUSHJ	P,TSETBO##	;CLEAR OUTPUT BUFFER
	move	t1,t4			; position pointer to service.
	HRRO	T2,WKPSRV(T1)	;FETCH POINTER TO LOGICAL NAME
	POP	T2,DEVLOG(F)	;SET LOGICAL NAME INTO DDB
	LDB	T1,WKPTFC	;FETCH TTY FORCED COMMAND INDEX
	pushj	p,TTFORC##		;FORCE THE APPROPRIATE COMMAND
	pushj	p,PrpDDB		; set essential DDB words

	;now fill in the information we know
	move	t1,RmtAdr+PSDDDB##	; get the foreign host address.
	movem	t1,RmtAdr(f)		; and save it the real DDB
	move	t1,NetAdr+PSDDDB##	; get ARPA address
	movem	t1,NetAdr(f)		; save in the DDB
	load.	t1,StdSP,TCPIBf		; get the source port (his port)
	movem	t1,RmtPrt(f)		; save in DDB
	load.	t1,StdDP,TCPIBf		; get the destination port (my port)
	movem	t1,LclPrt(f)		; save in DDB
	movei	t1,S%List		; get state code "listen"
	movem	t1,State(f)		; make it this DDB's state

	pushj	p,NewLst		; go back a process this message
					;  as if nothing has happened.
	move	t2,State(f)		; now get the state
	caie	t2,S%List		; still listening?
	  popj	p,			; no.  just return.
	pushj	p,DDBFls##		; clear out DDB
	pjrst	DDBRel##		; and return it to free pool


NotExc:	movei	u,TCPIBf		; point at TCP leader
	move	p3,TCPFlg(u)		; get the flags from leader.
	jumple	p2,TryRst		; just reset if no options
	hlrz	t1,p2			; get the first buffer of options
	pushj	p,RelBuf##		; free the options.
	jrst	TryRst			; try to send a reset and
					;  return the buffers and return.
;ROUTINE TO CHECK LEGALITY OF AN EXEC Well Known Port. ;⊗ WKPFND WKPFN1 WKPSRV WKPNUM WKPSKT WKPTFC
;	MOVE	t1,[local port NUMBER]
;	PUSHJ	P,WKPFND
;	  ERROR--SERVICE NOT IMPLEMENTED
;	NORMAL--T1 CONTAINS INDEX INTO SERVER TABLE (WKPSRV)

WKPFND:	pushj	p,save2##		; get p1 and p2
	move	p2,t1			; save port number
	MOVSI	t1,-WKPNUM	;NUMBER OF SERVICES IMPLEMENTED
WKPFN1:	LDB	p1,WKPSKT	;FETCH SOCKET NUMBER OF THIS SERVICE
	CAMN	p1,p2		;MATCH?
	  JRST	CPOPJ1		;YES, GOOD RETURN, T1 is offset.
	AOBJN	t1,WKPFN1	;NO, TRY NEXT
	POPJ	P,		;ERROR--SERVICE NOT IMPLEMENTED


;TABLE OF DEFINED SERVICES AVAILABLE THROUGH EXEC WKP.
;   MACRO TO DEFINE A SERVICE:
;	SERVER	(PORT# , TTY FORCED COMMAND , LOGICAL NAME)


DEFINE SERVER(SKT,TFC,NAME) <
	↑D<SKT>B26 + TFC## ,, [SIXBIT\NAME\]
>

WKPSRV:	SERVER	(3,TTFCXF,FTPSRV)	;FILE TRANSFER PROTOCOL SERVER
	SERVER	(23,TTFCXH,NETUSR)	;TELNET SERVER
	server	(79,ttfcxg,FngSrv)	;(241) finger service
IFN FTPATT,<
	0		;SPACE TO PATCH IN NEW SERVICES
	0
>
	WKPNUM==.-WKPSRV	;NUMBER OF DEFINED SERVICES

WKPSKT:	POINT	9,WKPSRV(T1),8	;POINTER TO SERVICE SOCKET NUMBER
WKPTFC:	POINT	9,WKPSRV(T1),17	;POINTER TO TTY FORCED COMMAND INDEX
; here to process one message.  this may be hot off the presses or it ;⊗ PrcMsg
;  may be a message that's was received out of order and can only now
;  be processed, but it's ALWAYS called at IMP interrupt level.
; arguments:
;		F - DDB
;		U - pointer to block containing TCP leader for this message
;		P1 - buffer descriptor: <LH> first buffer, <RH> last buffer
;		length of message in bytes is in MsgLen(f)

; during this routine, P3 ALWAYS has the current flags from the TCP
;	leader (we sometimes change them), and P2 ALWAYS has the current
;	State, which should ALWAYS agree with State(f).
PrcMsg:
	move	p3,TCPFlg(u)		; get the flags from leader.

	move	p2,State(f)		; get state of this connection
	cain	p2,S%List		; waiting for anything?
	  jrst	InLstn			; yes
	cain	p2,S%SynS		; waiting for SYN ACK?
	  jrst	InSynS			; yes

	; this is a segment arriving at a previously established connection.

	move	t1,RcvWnd(f)		; get the receive window size
	move	t2,RcvNxt(f)		; get the beginning of the rec window
	load.	t3,TCPSeq,(u)		; get the sequence number of it
	move	t4,MsgLen(f)		; load up message length

	jumpn	t1,WndFit		; receive window is non-zero, so
					;  try to fit this one in.
	jumpn	t4,SeqBad		; can't handle it, it's too big
	came	t3,t2			; is it the one we are expecting?
	  jrst	SeqBad			; no.  sequence number out of range.
	move	t4,t3			; last byte is the first byte.
	jrst	InWind			; this is it.  process it.
; here to check for the segment starting in the window ;⊗ WndFit
WndFit:	add	t1,t2			; compute the end of the window
	add	t4,t3			; compute the end of the message
	; note: now T4 points one beyond the end of the current message,
	;	T1 points one beyond the end of the current window.

	camg	t4,t1			; does this message end within
					;  the window?
	  jrst	WndEnd			; yes.  do more checking.
	caml	t3,t1			; does it start before the end?
	  jrst	SeqBad			; no, it's way out of line.
	aos	TCPWET##		; count window end truncated
	move	t4,t1			; the end of the message is
					;  going to agree with the end
					;  of the window when we get done.
	sub	t1,t3			; compute the length we will accept:
					;  end of window less start of message.

	; now scan through stream until we've seen as many bytes as we
	;  are going to allow, then throw away everything else.
	hlrz	t2,p1			; get pointer to first buffer.

	pushj	p,SkpByt##		; skip past that many bytes.
					;  now pointing at unwanted bytes.
ifn debug,<	; is the code buggy?
	skipn	t2			; is there a buffer with this byte?
	  stopcd CPOPJ##,DEBUG,NEB,	;++ not enough bytes.
>

	hrr	p1,t2			; new last buffer in our pointer
	stor.	t1,NBHCnt,(p1)		; make this buffer have only as
					;  many bytes as we're prepared
					;  to see.
	load.	t1,NBHNxt,(p1)		; get pointer to next buffer
	pushj	p,RelBuf##		; release the rest of the stream
	zero.	t1,NBHNxt,(p1)		; zero out the link to the
					;  non-existent remains.

	; restore these two badly clobbered values
	move	t2,RcvNxt(f)		; get the beginning of the rec window
	load.	t3,TCPSeq,(u)		; get the sequence number of it

	; and charge on to check the end of the message.
; here to check for a segment finishing in the window ;⊗ WndEnd FlsLp FlsBad
WndEnd:	caml	t3,t2			; starts after the start of window?
	  jrst	InWind			; yes.  this message is all in window

	camg	t4,t2			; ends after start of window?
	  jrst	SeqBad			; yes.  we've already seen this.
		 			;  ACK may have been lost: make
					;  sure he KNOWS we saw this.

	aos	TCPWFT##		; count window front truncated
	push	p,f			; save real DDB
	movei	f,PSDDDB##		; get the pointer to the pseudo
					;  DDB for input.
	hlrz	t1,p1			; get first buffer
	hrrom	t1,IBfThs+PSDDDB##	; save as current buffer, untouched.
	setzm	IBfBC+PSDDDB##		; clear count.
	movei	p4,InByte##		; input from buffers which are already
					;  in 32 bit words.
	move	p1,t2			; get the start of window
	sub	p1,t3			; subtract starting sequence

FlsLp:	jsp	p4,(p4)			; get next byte
	  jrst	FlsBad			; someone miscounted.
	sojg	p1,FlsLp		; one more read.  loop.

	load.	t4,TCPSeq,(u)		; get start sequence number again.
	add	t4,MsgLen(f)		; compute ending sequence again.
	move	t1,t4			; position, now that T4 is restored.
	sub	t1,RcvNxt(f)		; subtract beginning of window
					; to get number of bytes to read.
	pushj	p,GetMes##		; go read it into fresh buffers.
	  jrst	FlsBad			; can't happen.  someone miscounted.

	move	p1,t1			; put message chain in proper place.
	pop	p,f			; get back real DDB address.
	jrst	IsNext			; this is the next message, so go.

; restore F
FlsBad:	pop	p,f			; clear stack
	hrrz	t1,IBfThs+PSDDDB##	; get next buffer to be input.
	jrst	RelBuf##		; release buffers and return
; at this point we have a message which starts and ends within ;⊗ InWind IsNext
;  the receive window.  now we must see if it is the next message to
;  be used.
;		T3 - sequence number of the first byte in message
;		T4 - sequence number of the next byte after this message
InWind:	came	t3,RcvNxt(f)		; is this the byte we want next?
	  jrst	NotNxt			; no.  save it until its time.

; now we have the next entry we need to process
IsNext:	txne	p3,TC%Rst		; reset coming in?
	  jrst	FlsRst			; yes.  reset connection.

	pushj	p,SecChk		; check security for this packet.
	  jrst	BufFls			; not good enough.

	txne	p3,TC%Syn		; incoming SYN?
	  jrst	FlsSyn			; yes.  can't be.  reset connection.

	txnn	p3,TC%ACK		; an ACK?
	  jrst	BufFls			; no.  can't be for us.  throw
					;  it away.

	; deal with an ACK differently depending on state
	Dispat (p2,ACKErr,<<S%SyRP,<pushj p,ACKSyR>>
			,<S%SyRA,<pushj p,ACKSyR>>
			,<S%Estb,<pushj p,ACKEst>>
			,<S%Fin1,<pushj p,ACKF1>>
			,<S%Fin2,<pushj p,ACKEst>>
			,<S%ClsW,<pushj p,ACKEst>>
			,<S%Clsn,<pushj p,ACKCln>>
			,<S%LAck,<pushj p,ACKLAc>>
			,<S%TimW,<pushj p,ACKTW>>
			>)
	  jrst	BufFls			; non-skip return from dispatch:
					;  discard message and return.
	; fall through to next page.
; deal with the urgent pointer, if there is one ;⊗ TCPUrg
TCPUrg:	; SYN-Sent state processing for incoming may join us at this point.

	; skip URG and text processing for states which can't have them.
	Dispat(p2,UrgErr,<<S%Estb,<jfcl>>
			,<S%Fin1,<jfcl>>
			,<S%Fin2,<jfcl>>
			,<S%ClsW,<jrst	TCPFin>>
			,<S%Clsn,<jrst	TCPFin>>
			,<S%LAck,<jrst	TCPFin>>
			,<S%TimW,<jrst	TCPFin>>
		>)

	txnn	p3,TC%Urg		; urgent bit set?
	  jrst	TCPTxt			; no.  process text
	load.	t1,TCPUP,(u)		; get the urgent pointer
	add	t1,t3			; add offset to sequence number
					;  to get sequence number after
					;  urgentness
	camg	t1,RcvUrg(f)		; is this more urgent than previously?
	  jrst	TCPTxt			; no.  just ignore it.

	movem	t1,RcvUrg(f)		; yes.  save the new urgent pointer.
	pushj	p,TTyUrg##		; do TTY urgent processing if
					;  necessary.
; message chain is in P1. left half: first buffer, right half: last buffer. ;⊗ TCPTxt TCPTx1
; note: can only get here in established or one of the FIN-wait states
TCPTxt:
	camg	t4,RcvNxt(f)	 	; it there any data here?
	  jrst	TCPFin			; nope.

	scnoff				; we are mucking with the
					;  stream, so protect our ass.
	SKIPE	T1,IBFLST(F)	;IS THERE ALREADY A STREAM?
	  jrst	[			; yes.
		 hlrz	t2,p1		; get first buffer of new message.
		 stor.	t2,NBHNxt,(T1)	; join the new message to the end of
					;  the old stream.
		 jrst	TCPTx1		; and continue
		]
	HLROM	p1,IBFTHS(F)	;NO, START ONE
TCPTx1:	HRRZM	P1,IBFLST(F)	;NEW END OF STREAM
	ScnOn				; ok.  let anyone have it.

	pushj	p,ImpNew##		; tell IO service about new data.

	exch	t4,RcvNxt(f)		; save the sequence number we
					;  expect next.
	sub	t4,RcvNxt(f)		; get negative number of words here
	addm	t4,RcvWnd(f)		; remove that many words from
					;  the window.

	txo	s,TC$ACK		; make sure to ACK this data
; here to check for a FIN and handle it ;⊗ TCPFin TCPFi0 TCPFi1
TCPFin:	txnn	p3,TC%Fin		; FIN set?
	  popj	p,			; no.  that's all for this message.
	skipe	RcvFin(f)		; have we received this FIN already?
	  jrst	TCPFi1			; yes.  skip initial FIN processing.
	aos	RcvNxt(f)		; no.  update next byte past FIN
	setom	RcvFin(f)		; remember we received a FIN
	pushj	p,ImpNew##		; tell input service about new
					;  informtaion.
	movsi	t1,ttyptr!ttykbd	; set up keyboard and printer bits
	scnoff				; shut down interrupts for
					;  these checks.
	cain	p2,S%Estb		; are we established?
	 tdnn	t1,ttylin(f)		; and are we dependent on the
					;  IMP for any TTY info?  (actually,
					;  should check for KBD and JOB or
					;  PTR and not JOB, but since
					;  we always set both PTR and
					;  KBD together, we don't have to.)
	  jrst	TCPFi0			; no to one or the other.
	movx	t1,TC%Fin		; set FIN bit
	iorm	t1,SndBts(f)		; set it in bits to be sent
	pushj	p,SndMsg##		; try to send a FIN in response.
	  jfcl				; ignore errors

	movei	p2,S%LAck		; skip straight to last ACK
	movem	p2,State(f)		; save the new state

TCPFi0:	scnon				; interrupts back on
TCPFi1:	txo	s,TC$ACK		; have to ACK this FIN.

	; skip if we want to stay in the same state, else load P2 with
	;  the new state and non-skip
	Dispat(p2,FinErr,<<S%Estb,<movei p2,S%ClsW>>
			,<S%Fin1,<movei	p2,S%Clsn>>
			,<S%Fin2,<pushj p,FINF2>>
			,<S%ClsW,<skipa>>
			,<S%Clsn,<skipa>>
			,<S%LAck,<skipa>>
			,<S%TimW,<pushj p,FINTW>>
		>)
	  movem	p2,State(f)		; store a new state
	popj	p,			; all done.
FINF2:	movei	t1,2*MSL		; load up twice maximum segment life ;⊗ FINF2 FINTW
	movem	t1,GTimer(f)		; time wait timer is running
	; RFC says "turn off other timers", but i see no timers here.
	setzm	DevLog(f)		; clear the logical name.  this
					;  makes it easier to spot
					;  someone trying to reuse this
					;  connection in a legitimate way.
	movei	p2,S%TimW		; change to time wait state
	popj	p,			;  return non-skip to set the
					;  new state.

FINTW:	; he must not know we're here yet.  just restart timer.
	movei	t1,2*MSL		; two times the longest time a
					;  packet can live
	movem	t1,GTimer(f)		; set the timer.
	pjrst	cpopj1##		; and don't change state
; here if we received a segment for a connection that doesn't exist ;⊗ TryRst RstFls SndRst
TryRst:	txnn	p3,TC%Rst		; reset on?
RstFls:	  pushj	p,SndRst		; no.  reply with a reset
	pjrst	BufFls			; and flush the buffers

; send a reset
SndRst:	movx	t1,TC%Rst		; get reset bit
	txnn	p3,TC%Ack		; ACK set?
	  txo	t1,TC%Ack		; no.  set in response.
	load.	t2,TCPSeq,(u)		; get sequence number
	add	t2,MsgLen(f)		; add the length
	txne	p3,TC%Syn		; is SYN set?
	  aos	t2			; yes.  length is one more
	txne	p3,TC%Fin		; is FIN set?
	  aos	t2			; yes.  remember to count that, too.
	movem	t2,RcvNxt(f)		; use that as the ACK field.
	setz	t1,			; send sequence 0 back to him.
	txne	p3,TC%Ack		; ack set?
	  load.	t1,TCPAck,(u)		; yes.  use ACK field for sequence.
	pushj	p,TCPRsp		; send it off, T1 and T2 are args.
	movx	t1,TC%All		; get all the bits
	andcam	t1,SndBts(f)		; clear them ALL.
	popj	p,			; return
; here if we received a segment while listening for one ;⊗ InLstn AckAck AckAc1
InLstn:	txne	p3,TC%Rst		; is this a reset?
	  jrst	BufFls			; can't be real.  flush message
	txnn	p3,TC%ACK		; acknowleging?
	 txnn	p3,TC%Syn		; or not SYNing?
	  jrst	RstFls			; we didn't say anything.
					;  respond RESET.

	; here when receiving a ligit incoming for our listen state.
	load.	t1,TCPSeq,(u)		; get sequence number
	movem	t1,RcvIRS(f)		; save in DDB
	aos	t1			; compute next message expected
	movem	t1,RcvNxt(f)		; save that as what is expected
	movem	t1,RcvRed(f)		; save this as sequence number
					;  last time we updated RcvWnd.
					; (we actually first "updated"
					;  it when we prepped the window.)

	pushj	p,GetISS		; decide on the initial send
					;  sequence number.
	movem	t1,SndISS(f)		; save ISS
	aos	t1			; account for SYN
	movem	t1,SndNxt(f)		; and save it.
	movem	t1,SndLWd(f)		; and make that the first message
					;  number for window calculating
	setzm	SndWnd(f)		; we have no idea how much we
					;  can send until we hear.


	; fill in defaults for passive open, just in case.
	move	t1,RmtAdr+PSDDDB##	; get the foreign host address.
	movem	t1,RmtAdr(f)		; and save it the real DDB
	move	t1,NetAdr+PSDDDB##	; get ARPA address
	movem	t1,NetAdr(f)		; save in the DDB
	load.	t1,StdSP,(u)		; get the source port (his port)
	movem	t1,RmtPrt(f)		; save in DDB
	load.	t1,StdDP,(u)		; get the destination port (my port)
	movem	t1,LclPrt(f)		; save in DDB

	movei	p2,S%SyRP		; change to syn-received, passive
	jrst	AckAc1			; and continue

AckAck:	; here from Syn-sent code, to pretend to be a listen.
	sos	SndNxt(f)		; pretend we didn't send anything
	movei	p2,S%SyRA		; change state to syn-received, active

AckAc1:	scnoff				; protect against unlikely race
	skipg	State(f)		; has this DDB been wiped while
					;  we were thinking?
	  pjrst	sonppj##		; yes.  just try to give up
	movem	p2,State(f)		; in DDB
	movx	t1,TC%Syn!TC%Ack	; get SYN bit and ACK the SYN we got
	iorm	t1,SndBts(f)		; set it in bits to be sent
	setzm	SndLst(f)		; force into retransmission queue.
	pushj	p,SndMsg##		; send message now.
	  jfcl				; ignore a error we can't help.
	scnon				; ok to interrupt now.

	skipn	t4,MsgLen(f)		; any text in this message?
	  pjrst	cpopj##			; no text.  just return.

	load.	t3,TCPSeq,(u)		; get the starting sequence number
	add	t4,t3			; compute the sequence number
					;  of the byte following this message.
	txz	p3,TC%Syn!TC%Ack	; don't reprocess SYN and ACK.
	jrst	NotNxt			; remember the text.
; here if received a segment for a connection in SYN-SENT state ;⊗ InSynS InSyn1 InSyn2
InSynS:	txnn	p3,TC%ACK		; is this an ACK?
	  jrst	InSyn1			; no
	load.	t1,TCPACK,(u)		; get the ACK number
	came	t1,SndNxt(f)		; is this the correct ACK?
	  jrst	TryRst			; no.  send a reset (unless reset)
InSyn1:	txnn	p3,TC%Rst		; is this a reset?
	  jrst	InSyn2			; no.  still processable
	txnn	p3,TC%ACK		; was ACK on?
	  jrst	BufFls			; no.  this isn't for us.
	; we flush this connection.  at call, we can check for closed.
	jrst	RstCls			; delete DDB and message and return.

InSyn2:	pushj	p,SecChk		; security check.  honk!  honk!
	  jrst	BufFls			; security isn't tight enough.

	txnn	p3,TC%Syn		; is this trying to get us together?
	  jrst	BufFls			; no.  must be from outer space
	load.	t4,TCPSeq,(u)		; get the sent sequence number
	movem	t4,RcvIRS(f)		; that's the first one we got
	movem	t4,RcvRed(f)		; save this as sequence number
					;  last time we updated RcvWnd.
					; (we didn't really know it at
					;  the time.)
	aos	t4			; we're expecting the next one
	movem	t4,RcvNxt(f)		; that's what we're expecting
					;  (after this SYN).  (now T4 is
					;  loaded as it must be for TCPUrg)
	txnn	p3,TC%Ack		; is this ACKing our SYN?
	  jrst	AckAck			; no.  now we send another SYN as
					;  if we were coming from a listen
					;  with the RcvNxt we just got.
					;  if this beats our other SYN,
					;  then all will proceed as if
					;  we had been listening and
					;  the earlier SYN will be discarded
					;  (not in window).  if the other
					;  SYN gets there first, this
					;  one will be discarded (not
					;  in window) and a proper ACK
					;  will be sent to us.  this
					;  ACK will appear to us to
					;  "ACK our SYN", taking us
					;  from Syn-Rcvd to established.
					;  And vice versa.
	pushj	p,ACKUpd		; yes.  go update the ACK stuff.
	movei	p2,S%Estb		; set state to ESTABLISHED
	movem	p2,State(f)		; in DDB
	pushj	p,TCPIOD		; wake up the job if needed.
	txo	s,TC$ACK		; remember to always ACK his ACK
	jrst	TCPUrg			; join ESTABLISHED processing
					;  at urgent pointer processing.
	subttl	returns ;⊗ NoLead NoMess BadChk FlsOpt NoPort NoDDB NoITY AckErr UrgErr FinErr SeqBad BufFls FlsRst RstSRP RstBTL RstSRA RstEst RstCls FlsSyn

; message ended before leader was read in
NoLead:	aos	TCELed##		; error with leader
	popj	p,			; return

; bytes ended before message or ran out of buffers while reading it
NoMess:	aos	TCEMes##		; count error reading message in
	jumple	p2,cpopj##		; return if no options
	hlrz	t1,p2			; get first buffer of options
	pjrst	RelBuf##		; release the options, too.

BadChk:	aos	TCEChk##		; checksum wrong.  count it
FlsOpt:	jumple	p2,BufFls		; just flush the buffers in no options
	hlrz	t1,p2			; get first buffer of options
	pushj	p,RelBuf##		; free them
	pjrst	BufFls			; flush out buffers and return

NoPort:	pushj	p,SndNSP##		; call ICMP to tell him we
					;  don't do that.
	aosa	TCEPrt##		; incoming to a exec port we
					;  don't watch.
NoDDB:	  aos	TCEDDB##		; couldn't get DDB when needed.
	jrst	FlsOpt			; go flush message and options

NoITY:	aos	TCEITY##		; couldn't get an ITY when i
					;  wanted one.
	pushj	p,DDBREL##		; RETURN THE DDB
	jrst	FlsOpt			; flush buffers and options and return



AckErr:
UrgErr:
FinErr:	stopcd	BufFls,DEBUG,SES,	;++ state error seen

; here to force an ACK if not handling a RESET and discard the message.
SeqBad:	aos	TCPMNW##		; count message not in window
	txnn	p3,TC%Rst		; a reset?
	  txo	s,TC$ACK		; get an ACK sent back.

; subroutine to release all the buffers in our message.
BufFls:	hlrz	t1,p1			; get first buffer of chain.
	pjrst	RelBuf##		; release the entire chain.

; here to flush the message and handle a reset
FlsRst:	dispat	(p2,RstCls,<<S%SyRP,<jrst	RstSRP>>
			,<S%SyRA,<jrst	RstSRA>>
			,<S%Estb,<jrst	RstEst>>
			,<S%Fin1,<jrst	RstEst>>
			,<S%Fin2,<jrst	RstEst>>
			,<S%ClsW,<jrst	RstEst>>
		   >)

; incoming SYN to an almost established connection from a listen
RstSRP:	pushj	p,DDBFls##		; clear our all data buffers
	pushj	p,ImpDev##		; is this controlling a job?
	  jrst	[			; this device is NOT an IMP?
		 stopcd	CPOPJ##,DEBUG,CNI	;++ connection not an IMP
		]
	  jrst	RstBTL			; not controlling a job: back
					;  to listen
	pjrst	DDBRel##		; this is an incoming
					;  connection to a server.
					;  flush it.
RstBTL:	movsi	p2,s%List		; get listen state
	movem	p2,State(f)		; back to listen state
	setz	s,			; no bits apply
	popj	p,			; try to get out of it

RstSRA:	skipa	s,[ xwd IOIMPM,0 ]	; set up S with improper mode
; incoming reset to an established connection
RstEst:	  movsi	s,IODERR		; set device error
	iorm	s,DevIOS(f)		; in DDB
RstCls:	setz	s,			; no bits are operative
	pushj	p,BufFls		; get rid of the data.
	pushj	p,DDBFls##		; clear DDB queues
	movsi	t1,AllWat		; get wait flags
	tdnn	t1,ImpIOS(f)		; waiting for anything?
	  pjrst	DDBRel##		; nope.  nothing to tell him,
					;  so just make the DDB disappear.
	pjrst	ImpWak##		; wake up this user and fly

; incoming SYN where there can't be one.  reset.
FlsSyn:	pushj	p,SndRst		; send a reset
	jrst	RstCls			; throw away DDB, queues and all.
	subttl	routines to handle an ACK in various states ;⊗ ACKSyR ACKEst AckEs1 ACKF1 ACKCln ACKLAc ACKTW

; all routines should skip return if this segment is still worthy
;  of consideration, non-skip return if this segment should be
;  discarded.

; ACK while in SYN-received state
ACKSyR:	load.	t2,TCPAck,(u)		; get ACK number for this message
	caml	t2,SndUna(f)		; has it been previously ACKed?
	 camle	t2,SndNxt(f)		; or is it ACKing something
					;  not sent yet?
	  jrst	[			; yes.  fucked up.
		 move	t1,t2		; get sequence number placed
		 movx	t2,TC%Rst	; reset is the bit we want
		 pjrst	TCPRsp		; queue it up to be sent and error
					;  return from AckSyR
		]
	movei	p2,S%Estb		; change state to ESTABLISHED
	movem	p2,State(f)		; in the DDB
	pushj	p,TCPIOD		; try to wake job
	jrst	ACKEs1			; now do established like processing

; ACK while in established state (also CLOSE-wait), as well as
;  part of the processing for FIN-wait-1, FIN-wait-2, and Closing.
ACKEst:	load.	t2,TCPAck,(u)		; get the ACK number
	camle	t2,SndNxt(f)		; ACKing data not yet sent?
	  jrst	[			; yes.  our friend seems confused.
		 txo	s,TC$ACK	; send an ACK with the fields
					;  properly set.
		 popj	p,		; perhaps that will straighten
					;  him out.
		]
AckEs1:	caml	t2,SndUna(f)		; any chance of progress made here?
	  pushj	p,ACKUpd		; yes.  update ACK information.

	pjrst	cpopj1##		; and continue processing


; ACK while in FIN-wait-1
ACKF1:	pushj	p,ACKEst		; do the common established processing
	  popj	p,			; this segment is no good
	skipe	RetrnQ(f)		; retransmission queue empty?
	  pjrst	cpopj1##		; no.  FIN hasn't been ACKed yet.
	movei	p2,S%Fin2		; yes: our FIN's been ACKed
	movem	p2,State(f)		; enter FIN-wait-2 state
	pjrst	cpopj1##		; continue processing

; ACK while in closing state
ACKCln:	pushj	p,ACKEst		; common established processing
	  popj	p,			; drop the segment
	skipe	RetrnQ(f)		; everything been ACKed
					;  (including our FIN)?
	  popj	p,			; no: discard segment
	movei	t1,2*MSL		; load up twice maximum segment life
	movem	t1,GTimer(f)		; time wait timer is running
	setzm	DevLog(f)		; don't let the logical name be
					;  used anymore.
	movei	p2,S%TimW		; change state to Time-wait
	movem	p2,State(f)		; and remember in DDB
	pjrst	cpopj1##		; still going on this segment

; ACK while in Last-ACK
ACKLAc:	pushj	p,ACKEst		; normal ACK processing.
					; (note: the specs indicate
					;  that this isn't necessary,
					;  but in last-ACK state, we
					;  can get ACKs of data which
					;  must be removed from the
					;  retransmission queue as always.)
	  popj	p,			; flush segment
	skipe	RetrnQ(f)		; everything's been ACKed,
					;  including our FIN?
	  popj	p,			; no.  keep waiting
	movx	t1,S%Clos		; set state to closed
	movem	t1,State(f)		; in DDB.
; legally, the following two lines should be in, but experience shows that
;  the user (well, me, anyway) expects the DDB to disappear at this point.
;	skipe	IBfThs(f)		; has everything been read?
;	  popj	p,			; no.  let input delete the DDB.
	pushj	p,DDBFls##		; yes.  the end of the connection.
	pjrst	DDBRel##		; give up DDB.

; ACK while in time-wait
ACKTW:	txnn	p3,TC%Fin		; is this a FIN?
	  popj	p,			; no.  it can't be legal.  ignore it.
	txo	s,TC$ACK		; ACK this FIN again: he didn't
					;  hear it last time.
	movei	t1,2*MSL		; time-wait max
	movem	t1,GTimer(f)		; set it
	popj	p,			; nothing more to do with this
	subttl	AckUpd ;⊗ ACKUpd NoAllc RetrLp RetrD0 RetrDn RetrNo

;++
; Functional description:
;
;	update information about how much data has been acknowleged
;	as received by the other host.  this update includes
;	remembering where the unacknowleged data now is, where the
;	end of the receive window is, and deleting any packets in
;	the retransmission queue that are entirely acknowleged.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,AckUpd
;		<always returns here>
;
; Input parameters:
;
;	F - DDB in question.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	TCP header, DDB
;
; Implicit outputs:
;
;	outgoing window information in DDB.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	modified data in DDB.  may delete some messages in the
;	retransmission queue.
;--


ACKUpd:	pushj	p,save3##		; get P1 and p2 and p3
	load.	t1,TCPWnd,(u)		; get window length
	move	p2,t1			; get copy
	lsh	p2,-2			; get 25% of window length.
	movem	p2,SndThr(f)		; that's our threshold for sending
	load.	p1,TCPACK,(u)		; get ACK number.
	add	t1,p1			; get highest sequence number
					;  we're allowed to send.
	push	p,t1			; save that value
	sub	t1,SndLWd(f)		; subtract off sequence number
					;  of the end of the window
					;  from the last window update
					;  to get the change in the window
	pop	p,SndLWd(f)		; put the last window end in
					;  the DDB from stack.
	addb	t1,SndWnd(f)		; update the window
	caml	t1,p2			; is the amount of window over
					;  the threshhold for sending?
	  pushj	p,AlcNew##		; yes.  wake job if waiting.

NoAllc:	camg	p1,SndUna(f)		; a real increase?
	  popj	p,			; no.
	movem	p1,SndUna(f)		; remember how much has been ACKed.

	move	t1,UTTime(f)		; get user timeout time.
	movem	t1,UTTimr(f)		; and reset it.

	setz	p2,			; remember haven't seen a fully
					;  ACKed message yet.
	scnoff				; protect BIB freeing code
	hrrz	t1,RetrnQ(f)		; get retransmission queue head.
	jumpe	t1,RetrD0		; this shouldn't happen....
RetrLp:
ifn debug,<	; debugging
	pushj	p,BIBChk##		; consistency check
>
	cam.	p1,BIBSeq,(t1),ge	; is this fully ACKing this one?
	  jrst	RetrDn			; no.  that's the lowest we
					;  have. stop scanning.
	load.	p2,BIBTim,(t1)		; seconds since sent
	load.	p3,BIBRTQ,(t1)		; get next BIB in queue
	pushj	p,RelBIB##		; dump that BIB.
	skipe	t1,p3			; position next BIB.  is one?
	  jrst	RetrLp			; yes.  loop.

RetrD0:	setzb	t1,RetrnQ(f)		; nothing left in the queue
RetrDn:
	hrrm	t1,RetrnQ(f)		; update pointer to new first buffer.

	scnon				; ok to interrupt us now.

	jumple	p2,RetrNo		; last BIB didn't have a
					;  real time in it.
	; smooth retransmission timeout time by computing
	;   (7/8*<old retran time> + 1/8*<round trip time for this segment>),
	; or	(7*<old> + <round trip>)/8, in this case.
	move	p1,RTTime(f)		; get standard retransmission time
	imuli	p1,7			; times 7
	addb	p2,p1			; total them
	addi	p2,4			; make sure to round up.
	ash	p2,-3			; now divide both by 8
	caige	p2,RTMin		; is it too small?
	  movei	p2,RTMin		; yes.  least legal time
	caile	p2,RTMax		; is it too big?
	  movei	p2,RTMax		; yes.  most legal time
	movem	p2,RTTime(f)		; set new timeout time in ticks.

RetrNo:
	; may need to send some information to user concerning the
	;	data we now know the other end received.


	popj	p,			; all done
	subttl	deal with a message received before it should be ;⊗ NotNxt FtrOrd FtrOr1 FtrRpl FtrOr2 FtrNew FtrSav

; P1 has a message pointer to message which cannot be accepted
;  until other messages before it arrive.  T4 has the sequence number
;  just after this message.
NotNxt:
	aos	TCPFTS##		; count future message seen
	load.	t3,TCPSeq,(u)		; get sequence number (chain is
					;  ordered by initial sequence number)

	movei	p4,Future-FNxtOf(f)	; get the start of the FMB chain.
					;  such that using FMBNxt will
					;  point at future pointer word.

FtrOrd:	load.	p2,FMBNxt,(p4)		; get next FMB in queue
	jumpe	p2,FtrNew		; found the end of the futures chain.
					;  get an FMB and save this in it.
	load.	t1,TCPSeq,FMBTCP(p2)	; get sequence number of this one.
	camg	t3,t1			; new starts after old?
	  jrst	FtrOr1			; no.  could precede or be together.
	cam.	t4,FMBNBy,(p2),g	; does new extend beyond the old?
	  jrst	BufFls			; no. new is duplicate. discard.
	move	p4,p2			; grab copy of this pointer in
					;  case it's the last.
	jrst	FtrOrd			; try next FMB.

FtrOr1:	came	t3,t1			; do they start at the same place?
	  jrst	FtrOr2			; no.  new one definitely
					;  starts first.
	cam.	t4,FMBNBy,(p2),g	; does the new one end after
					;  the old one?
	  jrst	BufFls			; nope.  old has everything the
					;  new one does.  kill new.
	; replace old one: new one consumes it.
FtrRpl:	load.	t1,FMBFst,(p2)		; get first buffer in old message
	pushj	p,RelBuf##		; free all buffers
	move	t1,p2			; position used but loved FMB
	jrst	FtrSav			; save all the data

FtrOr2:	cam.	t4,FMBNBy,(p2),l	; new one ends before old one?
	  jrst	FtrRpl			; nope.  completely consumes it.

; here to get an FMB and save the data in it
FtrNew:	pushj	p,GetFMB		; get a Future Message Block
	  jrst	BufFls			; no big deal.  flush buffer
					;  and go out normally.
	stor.	t1,FMBNxt,(p4)		; link to the rest of the stream
	stor.	p2,FMBNxt,(t1)		; whatever the next one was (may be
					;  zero), make sure it's our next.

; here to save the data in the FMB in t1
FtrSav:	movem	p3,TCPFlg(u)		; save the bits on this message.
					;  (we may have changed them)
	movei	t2,FMBTCP(t1)		; point at correct place in FMB
	hrl	t2,u			; BLT pointer to copy TCP header.
	blt	t2,FMBTCP+TCPLen-1(t1)	; copy the entire header into
					;  the FMB.
	stor.	p1,FMBPnt,(t1)		; save pointer to the buffer chain.
	stor.	t4,FMBNBy,(t1)		; save the sequence number of
					;  the next byte after this message.
	popj	p,
; routine to get an FMB, return it in T1 ;⊗ GetFMB RelFMB FlsFMB FlsFM1
GetFMB:	syspif				; turn off PIE for this
	movei	t2,<FMBLen+3>/4		; this many 4 word blocks in an FMB
	push	p,t4			; save T4
	pushj	p,Get4Wd##		; go get it.
	 skipa				; failed
	  aos	-1(p)			; skip return
	pop	p,t4			; restore T4
	pjrst	onpopj##		; interrupts on and return

; routine to return an FMB in T2 to free core.
RelFMB:	movei	t1,<FMBLen+3>/4		; this many 4 word blocks in an FMB
	pjrst	Giv4Wd##		; tell Core1 to take it back.


; routine to delete an FMB chain.  first FMB is in T1
FlsFMB::
	pushj	p,save1##		; get p1
	move	p1,t1			; start with buffer in correct place
FlsFM1:	load.	t1,FMBFst,(p1)		; get pointer to first buffer
					;  in message.
	pushj	p,RelBuf##		; release the buffer chain
	move	t2,p1			; position this FMB for release
	load.	p1,FMBNxt,(p1)		; get pointer to next FMB in chain
	pushj	p,RelFMB		; release this FMB
	jumpn	p1,FlsFM1		; loop if there's more

	popj	p,			; return
	subttl	GetISS ;⊗ GetISS

;++
; Functional description:
;
;	decide on the Initial Send Sequence number whenever we need one.
;
;
; Calling sequence:
;
;		pushj	p,GetISS
;		<always return here, ISS to use in T1>
;
; Input parameters:
;
;	none.
;
; Output parameters:
;
;	T1 - ISS
;
; Implicit inputs:
;
;	none.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	none.
;--


GetISS:	setz	t1,0			; just use zero for now.
	popj	p,
	subttl	SecChk ;⊗ SecChk

;++
; Functional description:
;
;	Classified.
;
;
; Calling sequence:
;
;	Classified.
;
; Input parameters:
;
;	Classified.
;
; Output parameters:
;
;	Classified.
;
; Implicit inputs:
;
;	Classified.
;
; Implicit outputs:
;
;	Classified.
;
; Routine value:
;
;	Classified.
;
; Side effects:
;
;	Classified.
;
;--


SecChk:	pjrst	cpopj1##		; security looks good.
	subttl	TCPMak ;⊗ TCPMak TCPMa1 MakCnt

;++
; Functional description:
;
;	put TCP leader (in 32 bit format) into fixed TCP output leader
;	buffer.  then link the buffer to the beginning of the
;	current output stream.  then send the message down to the
;	next level of protocol for further processing.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,TCPMak
;		<always returns here>
;
; Input parameters:
;
;	f - DDB for connection
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	data in DDB
;
; Implicit outputs:
;
;	data in DDB
;
; Routine value:
;
;	returns non-skip if can't get a buffer
;
; Side effects:
;
;	adds a buffer to the beginning of the current output stream.
;--


TCPMak::
	setzm	TCPOBf+NBHLen		; zero first word of leader.
	move	t2,[TCPOBf+NBHLen,,TCPOBf+NBHLen+1]	; set up blt
	blt	t2,TCPOBf+TCPLen+NBHLen-1	; clear to end

	move	t2,SndBts(f)		; get bit field from DDB somewhere
	movx	t1,TC%Onc		; get bits which should only be
					;  sent once.
	andcam	t1,SndBts(f)		; clear bits which we should
					;  not send again.
	andx	t2,TC%All		; make sure not to get stray bits.
	movsi	t1,ttyptr!ttykbd	; some brand of crosspatch bits
	tdne	t1,TtyLin(f)		; some kind of crosspatch?
	  txo	t2,TC%Psh		; yes.  make sure it's shoved through.

	move	t1,SndNxt(f)		; no.  get next sequence number
	stor.	t1,TCPSeq,NBHLen+TCPOBf	; save in leader
	move	t1,ObfByt(f)		; get byte count of this message
	addm	t1,SndNxt(f)		; update the current sequence
					;  that much.
	txne	t2,TC%Fin!TC%Syn	; FIN and SYN take up a sequence number.
	  aos	SndNxt(f)		; add it.

; enter here for out of sequence sending.  sequence number already set in
;	TCP leader, bits to be sent now in T2.
TCPMa1:
	; count all the bits in the flag word as message types to get some
	;  idea of what we're sending.
	movx	t1,TC%Low		; get lowest order bit
	setz	t3,			; and a count
MakCnt:	tdne	t2,t1			; is that bit on?
	  aos	TCPOTy##(t3)		; yes.  count one more with
					;  that bit on.
	lsh	t1,1			; shift bit over one
	txne	t1,TC%All		; bit no longer in field?
	  aoja	t3,MakCnt		; still in flag field.  count on.

	movem	t2,TCPFlg+NBHLen+TCPOBf	; set the bits wanted.

	movei	t1,TCPOBf		; point at the output leader space
	exch	t1,OBfFst(f)		; make us first, get old first
	stor.	t1,NBHNxt,TCPOBf	; link old first to us.
	move	t1,RmtPrt(f)		; get his port
	stor.	t1,StdDP,NBHLen+TCPOBf	; that's the destination port
	move	t1,LclPrt(f)		; get my port
	stor.	t1,StdSP,NBHLen+TCPOBf	; that's the source port
	move	t1,RcvNxt(f)		; get ACK number
	stor.	t1,TCPAck,NBHLen+TCPOBf	; into leader.
	move	t1,RcvWnd(f)		; current window
	stor.	t1,TCPWnd,NBHLen+TCPOBf	; in
	move	t1,SndUrg(f)		; current out going urgent pointer
	stor.	t1,TCPUP,NBHLen+TCPOBf	; save
	movei	t2,TCPLen		; get length (will need to
					;  compute when we perform options)
	stor.	t2,TCPOff,NBHLen+TCPOBf	; save that.
	lsh	t2,Wd2Byt		; convert from words to bytes
	stor.	t2,NBHCnt,TCPOBf	; save byte count for this buffer
	addm	t2,OBfByt(f)		; get a grand total in bytes.
	; save T2 for checksumming

	; one would add OPTIONS around here somewhere.

ifn FtChck,<	; doing checksums?
	move	t1,[point 16,NBHLen+TCPOBf]; starting pointer
	; number of bytes is in t2
	pushj	p,CSmWds##		; and checksum it.
	move	t1,RmtAdr(f)		; get remote address
	pushj	p,CSmWrd##		; add it to checksum
	move	t1,LclAdr(f)		; local address, too
	pushj	p,CSmWrd##		; add it in.
	move	t1,Protcl(f)		; and get protocol
	pushj	p,CSmHWd##		; and add it in as well
	move	t1,OBfByt(f)		; get byte count of message
					;  plus leader
	pushj	p,CSmHWd##		; add that to checksum, too.

	txc	p3,msk.hw		; send one's complement of the sum
	txnn	p3,msk.hw		; if zero, make it...
	  movei	p3,msk.hw		; ...the zero with all bits on
	stor.	p3,TCPChk,NBHLen+TCPOBf	; save the checksum in the leader.
>
ife FtChck,<	; not doing checksums
	zero.	t1,TCPChk,NBHLen+TCPOBf	; flag that we aren't checksumming
>
	pjrst	IpMake##		; call next level of protocol
	subttl	TCPRsp ;⊗ TCPRsp

;++
; Functional description:
;
;	routine to send a TCP response which is out of sequence from
;	the TCP stream.  for example, it could be a RESET or an
;	ACK to correct a bad sequence field.
;
;
; Calling sequence:
;
;		move	t1,<sequence to use>
;		move	t2,<bits>
;		move	f,<ddb>
;		pushj	p,TCPRsp
;		<always returns here>
;
; Input parameters:
;
;	T1 - sequence number to put on the message
;	T2 - bits which should be set in message
;	F - DDB
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	put a message in the output queue.
;--


TCPRsp:	
	scnoff				; STOP!

	push	p,p3			; save lots of things
	push	p,ObfFst(f)		; save
	push	p,ObfThs(f)		; save
	push	p,ObfBC(f)		; save
	push	p,ObfByt(f)		; save

	setzb	p3,OBfFst(f)		; pretend no first message
	setzm	OBfByt(f)		; no bytes in message
	stor.	t1,TCPSeq,TCPObf+NBHLen	; set desired sequence number
	pushj	p,TCPMa1		; call TCPMak properly
	  jfcl				; forget errors.

	pop	p,OBfByt(f)		; restore
	pop	p,OBfBC(f)		; restore
	pop	p,OBfThs(f)		; restore
	pop	p,OBfFst(f)		; restore
	pop	p,p3			; restore

	pjrst	sonppj##		; return to caller
	subttl	TCPIFn ;⊗ TCPIFn TCPIF1

;++
; Functional description:
;
;	check to see if this input stream has received a legitimate
;	FIN.  called after data is exhausted to see if there's any
;	more data coming or if this is EOF.  if we have received a FIN
;	for this connection, close it now.
;
;
; Calling sequence:
;
;		move	f,DDB
;		scnoff
;		pushj	p,TCPIFn
;		  <return here if EOF, FIN seen, connection closed,
;				interrupts on>
;		<return here if not EOF, FIN not yet seen,
;				interrupts still off>
;
; Input parameters:
;
;	F - DDB
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	returns non-skip if this connection is done doing input
;	(i.e., FIN received), else skip returns.  in non-skip return,
;	interrupts are enabled.
;
; Side effects:
;
;	will close the connection if a FIN has been seen.  turns
;	on interrupts if return is non-skip, else leave them off.
;
;--

TCPIFn::
	skipn	RcvFin(f)		; seen a FIN
	  pjrst	cpopj1##		; no.  still open for action.
	pushj	p,save1##		; get a scratch
	skiple	p1,State(f)		; state some kind of closed?
	  jrst	TCPIF1			; no.  check to see if we
					;  should release it, though.
	scnon				; allow DDBFls to handle interrupts
	pushj	p,DDBFls##		; clear this DDB
	pjrst	DDBRel##		; and let someone else use it.

TCPIF1:	cain	p1,S%ClsW		; in close wait?
	  pjrst	sonppj##		; yes.  this state may still need the
					;  DDB, so keep it.
					; no.  any other state the has
					;  received a FIN is of no use
					;  to user.   deassign the DDB.
	scnon				; interrupts are ok again.
	; detach IMP from terminal now.
	pushj	p,ItyRel##		; ditch ITY, if any.
	pushj	p,TTIDet##		; disconnect crosspatched IMP.
	pjrst	DDBDea##		; deassign DDB and return
	subttl	TCPICK ;⊗ TCPICK

;++
; Functional description:
;
;	check a connection to see if it is in a state where input is legal.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,TCPICK
;		<always returns here>
;
; Input parameters:
;
;	f - ddb
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	none.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	returns non-skip if the connection is NOT open for input.
;	returns skip if input is possible.
;
; Side effects:
;
;	none.
;--


TCPICK::
	pushj	p,save1##		; get p1
	move	p1,state(f)		; get state from DDB
	cain	p1,S%Estb		; is it well established?
	  pjrst	cpopj1##		; yes.  that's legal
	caie	p1,S%Fin1		; FIN wait 1?
	 cain	p1,S%Fin2		; or FIN wait 2?
	  aos	(p)			; yes.  he hasn't closed yet.
	popj	p,			; return.
	subttl	TCPOCK ;⊗ TCPOCK

;++
; Functional description:
;
;	check a connection to see if it is in a state where output
;	is legal.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,TCPOCK
;		<always returns here>
;
; Input parameters:
;
;	f - ddb
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	none.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	returns non-skip if the connection is NOT open for output.
;	returns skip if output is possible.
;
; Side effects:
;
;	none.
;--


TCPOCK::
	pushj	p,save1##		; get p1
	move	p1,state(f)		; get state from DDB
	caie	p1,S%Estb		; is it well established?
	 cain	p1,S%ClsW		; or in close wait?
	  aos	(p)			; yes.  he hasn't closed yet.
	popj	p,			; return.
	subttl	TCPTCk ;⊗ TCPTCk

;++
; Functional description:
;
;	check to see if the send threshhold has been exceeded.  if
;	there is enough real window available, it's ok to send more
;	data, otherwise (non-skip), avoid sending data until more
;	window appears.
;
;
; Calling sequence:
;
;		move	f,<ddb>
;		pushj	p,TCPTCk
;		  <returns here if not enough window>
;		<returns here if enough window to warrent sending more>
;
; Input parameters:
;
;	F - DDB
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	returns skip if there is enough window to allow sending more
;	data, else non-skip.
;
; Side effects:
;
;	none.
;--


TCPTCk::
	pushj	p,save1##		; get p1
	skiple	p1,SndWnd(f)		; any window?
	 camge	p1,SndThr(f)		; yes.  are we over the threshhold?
	  popj	p,			; no to either: avoid sending
	move	p1,State(f)		; get the connection state
	cail	p1,S%Estb		; at least established?
	  aos	(p)			; yes.  set for skip, is ok
	popj	p,			; no: pretend there's no window
					;  until we get into an
					;  established state.
	subttl	TCPWUp ;⊗ TCPWUp

;++
; Functional description:
;
;	update a window if the user has read some of the data waiting.
;
;
; Calling sequence:
;
;		move	f,DDB
;		scnoff
;		pushj	p,TCPWUp
;		<always returns here>
;
; Input parameters:
;
;	f - DDB
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB data
;
; Implicit outputs:
;
;	DDB data
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	none.
;--


TCPWUp::
	skipn	t1,IBfByt(f)		; get byte count read since
					;  last update
	  popj	p,			; none:  nothing to do.
	setzm	IBfByt(f)		; clear read byte count.
	addm	t1,RcvRed(f)		; update sequence number of
					;  bytes read.
	addb	t1,RcvHld(f)		; add up bytes we're holding
					;  back from window.
	camge	t1,RcvThr(f)		; are we over our threshhold?
	  popj	p,			; nope.  keep waiting
	setzm	RcvHld(f)		; not holding any now.
	addm	t1,RcvWnd(f)		; add freed bytes into window
	pushj	p,SndMsg##		; send the message
	  jfcl				; can't do much here
	popj	p,			; and return
	subttl	SetUrg ;⊗ SetUrg

;++
; Functional description:
;
;	set up TCP data to send an URG message next time out.
;	computes the current SndNxt (the value in DDB may be
;	out of date) and store is in SndUrg, then sets the URG
;	bit in the DDB.  note that we NEVER want to send this
;	now, because we want to add a data mark (for telnet) and
;	have it in this message.
;
;
; Calling sequence:
;
;		move	f,ddb
;		pushj	p,SetUrg
;		<always return here>
;
; Input parameters:
;
;	f - ddb
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB data
;
; Implicit outputs:
;
;	SndUrg in DDB
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	none.
;--



SetUrg::
	move	t1,SndNxt(f)		; get next sequence number
	add	t1,OBfByt(f)		; find real current sequence number
	movem	t1,SndUrg(f)		; make this the urgent pointer
	movx	t1,TC%Urg		; set urgent bit
	iorm	t1,SndBts(f)		; in DDB
	popj	p,			; and let it be sent with next
					;  message out.
	subttl	TcpChk ;⊗ TCPChk TCPCRT TCPRTR RtLoop RtNxt TCPCUT TCPUTO

;++
; Functional description:
;
;	subroutine to do various once a second checks to an IMP DDB.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,TCPChk##
;		<always returns here>
;
; Input parameters:
;
;	f - DDB of an IMP device.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB and queues
;
; Implicit outputs:
;
;	DDB and queues
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	may didle with output queues if it finds it needs to retransmit.
;	may delete DDB altogether, although DevSer will still have the
;	link to the next DDB.  (HINT: call this after doing everything else.)
;--


TCPChk::
	skiple	GTimer(f)		; general timer set to run?
	 sosle	GTimer(f)		; yes. has it expired?
	  jrst	TCPCRT			; no.  don't worry about it

	skiple	t1,State(f)		; get the state: ok if closed or error
	 cain	t1,S%TimW		; is it time-wait?
	  skipa				; either closed or time wait is ok.
	   jrst	TCPCRT			; must have changed under our nose.

	movx	t1,S%Clos		; set close state
	movem	t1,State(f)		; in DDB
; legally, the following two lines should be in, but experience shows that
;  the user (well, me, anyway) expects the DDB to disappear at this point.
;	skipe	IBfThs(f)		; anything left to input?
;	  popj	p,			; yes.  let input handle
					;  releasing DDB.
	pushj	p,DDBFls##		; clear out the DDB
	pjrst	DDBRel##		; return DDB to free pool


; here if not time-wait time-out
TCPCRT:	scnoff				; no interrupts here
	skipe	RetrnQ(f)		; anything waiting to retranmit?
	 skipg	t1,State(f)		; and is it some kind of active state?
	  pjrst	sonppj##		; no.  don't count if closed or idle.

; here if we need to retransmit for this DDB
TCPRTR:
	pushj	p,save2##		; get some scratches
	hrrz	p1,RetrnQ(f)		; get first BIB in
					;  retransmission queue.
RtLoop:	jumpe	p1,TCPCUT		; end of queue.  check user timeout
ifn debug,<	; debugging
	move	t1,p1			; position BIB
	pushj	p,BIBChk##		; consistency check
>
	skip.	t2,BIBTQ,(p1),n		; is this BIB already in the
					;  transmission queue?
	 skip.	p2,BIBTim,(p1),g	; no.  are we counting?
	  jrst	RtNxt			; not counting or already in TQ
	incr.	p2,BibTim,(p1)		; incr time
	camg	p2,RTTime(f)		; time to retransmit this one?
	  jrst	RtNxt			; no

	aos	TCPPRT##		; count a packet we had to retransmit.
	move	t1,p1			; position BIB pointer
	pushj	p,Go1822##		; put it in the transmission
					;  queue again.
	  jfcl				; can't do nothin'
ifn debug,<	; debugging
	move	t1,p1			; position BIB
	pushj	p,BIBChk##		; consistency check
>
RtNxt:	load.	p1,BIBRTQ,(p1)		; get next
	jrst	RtLoop			; and loop

TCPCUT:	scnon				; interrupts safe now
	sosg	UTTimr(f)		; user time-out expired?
	  jrst	TCPUTO			; yes.  go delete all queues in
					;  DDB and flag error.
	popj	p,			; no.  nothing timed out.

; here if user's timer time's out.
TCPUTO:	movsi	s,IODTER		; set data error
	iorm	s,DevIOS(f)		; set that in DDB
	pushj	p,DDBFls##		; strip all queues from this DDB
	movsi	t1,AllWat		; get wait flags?
	tdnn	t1,ImpIOS(f)		; waiting for anything?
	  pjrst	DDBRel##		; no.  flush DDB on the way out
	pjrst	ImpWak##		; yes.  wake up user on the way out.
SUBTTL USER INTERFACE (IMPUUO) ;⊗ CALL FLAGS IF.NWT IF.PRV IF.ALS TIMEOUT E .UUDEV .UUSTT .UUSKT .UUHST .UURMT .UULST PUUTIM sk.lcl FreOvr FreLsh FrePrt FreMin FreMch

COMMENT \
PROVIDES ABILITY FOR THE USER TO INITIATE IMP CONNECTIONS
UNDER PROGRAM CONTROL.

CALL:
	MOVE AC,[BYTE (8)FLAGS, (3)TIMEOUT, (7)CODE, (18)E ]
	CALL AC,[SIXBIT /IMPUUO/]
	  ERROR RETURN  --  CODE IN E+1
	OK RETURN

;NOTE THE CORRESPONDING CALLI UUO IS -5 AT HARVARD, -17 AT CMU,
; AND -4 AT AFAL SO DON'T USE IT.

FLAGS:	\
	IF.NWT==1B0	;IF SET, DON'T GO INTO IO WAIT FOR NCP ACTIVITY
	IF.PRV==1B1	;IF SET, ALLOW THE OPERATION EVEN IF THE USER
			;  DOESN'T OWN THE DEVICE (PRIVILEGED)
	IF.ALS==1B2	;IF SET, LOCAL SOCKET IS ABSOLUTE RATHER THAN
			;  JOB- OR USER-RELATIVE (PRIVILEGED)

COMMENT \
TIMEOUT:3 BIT CODE(T) STARTS A TIMEOUT OF M SECONDS
		M = 4 * 2↑T
		THUS, THE USER MAY SPECIFY A TIMEOUT FROM 8 TO 512 SECONDS.
		IF T = 0, THEN THE DEFAULT IS 30 SECONDS.


FORMAT OF THE ARGUMENT LIST: (EXCEPT AS OTHERWISE NOTED)

E:	SIXBIT /LOGICAL NAME/
	EXP STATUS/ERROR CODES
	EXP SOCKET NUMBER
	exp Foreign network/host/imp number		;[96bit]
	EXP FOREIGN SOCKET NUMBER
\

	.UUDEV==0
	.UUSTT==1
	.UUSKT==2
	.UUHST==3
	.UURMT==4

	.UULST==4		; length of block

PUUTIM:	POINT 3,P1,10		;POINTER TO GET TIMEOUT FIELD


; socket constants:
;	bottom 3 bits of a port are user controlled, that leaves 13 bits for
;	the program controlled part.  high bit is used to detect overflow.
	sk.lcl==7			; low 3 bits are user controlled
	FreOvr==10000			; how to detect wrap around
	FreLsh==3			; make room for low 3 bits
	FrePrt==400			; 0-377 are assigned.
	FreMin==FrePrt			; add on the value of the last ARPA
					;  assigned port to avoid these.
	FreMch==177770			; what bits are important for
					;  detecting ports in the same
					;  group of 8.
NOWAITS< ;⊗ IMPUUO IMPUU1 ImpUU2 ImpUU3 ImpUU4
IMPUUO::PUSHJ	P,SAVE4##	;SAVE P1, P2, P3, P4
	MOVE	P1,T1		;PERMANENT COPY OF USER STUFF
	HRR	M,P1		;REL ADDRESS OF ARG BLOCK
	LDB	T3,[POINT 7,P1,17] ;GET THE FUNCTION CODE
	MOVSI	T1,-UUOLEN	;SEARCH UUO TABLE
	MOVE	p4,UUOTAB(T1)	;GET THE TABLE ENTRY
	LDB	T2,[POINT 7,p4,17];GET THE CODE
	CAME	T2,T3		;THIS IT?
	AOBJN	T1,.-3		;NO
	JUMPGE	T1,ERRILU	;JUMP IF NOT THERE
	MOVEI	T1,JP.IMP	;TEST PRIVILEGES
	PUSHJ	P,PRVBIT##	;SUPER IMP?
	  JRST	IMPUU1		;YES
	TLZ	P1,(IF.PRV)	;NO--DISABLE PRIVILEGED IMPUUO FLAGS
	TLNE	p4,UU.PVI	;REQUIRED?
	JRST	ERRPRV		;YES--ERROR
	MOVEI	T1,JP.NET	;SETUP TO TEST NETWORK ACCESS PRIVILEGES
	TLNE	p4,UU.PVN	;NET PRIVILEGES REQUIRED?
	PUSHJ	P,PRVBIT##	;YES, GOT THEM?
	  JRST	IMPUU1		;YES OR NOT NEEDED
	JRST	ERRPRV		;NO

;HERE TO GO AHEAD WITH THE UUO DISPATCH
IMPUU1:	HRRZ	T1,P1		;ADDRESS CHECK THE ARGUMENTS
	CAIGE	T1,↑D16-.UULST	;IN ACS?
	JRST	ImpUU2		;YES, OK.
	PUSHJ	P,IADRCK##
	  JRST	ERRADR		;ADDRESS CHECK
	MOVEI	T1,.UULST(P1)
	PUSHJ	P,IADRCK##
	  JRST	ERRADR
ImpUU2:	tlnn	p4,uu.NUp	;(260) must have a working network?
	  jrst	ImpUU3		;(260) no.  don't check.
	skipe	OKFlag##	;(260) is it working?
	 skipe	StopFl##	;(260) yes.  are we coming down?
	  jrst	ErrNNU		;(260) either not up or going down
ImpUU3:	TLNE	p4,UU.DNU	;NEED TO SETUP DDB?
	  JRST	ImpUU4		;NO
	PUSHJ	P,SETDDB	;YES, DO IT
	  JRST	cpopj##		;ERROR
ImpUU4:	TLNN	p4,UU.INT	;INTERRUPTS ALLOWED?
	  ScnOff		;NO.  LET NOTHING INTERFERE
	PUSHJ	P,(p4)		;CALL THE ROUTINE
	 skipa			; non-skip return, please.
	  aos	(p)		; pass back the good return.
	tlnn	p4,uu.int	; did we shut down dangerous interrupts?
	  ScnOn			; yes.  allow them again.
	popj	p,		; return as set up

; register setup at the time of UUO dispatch:
;	f - IMPDDb
;	w - PDB
;	p2 - local port, if any
;	p4 - dispatch bits.  these must be preserved.
>;NOWAITS
;MACRO FOR BUILDING THE DISPATCH TABLE ;⊗ ZZ ZZ UU.PVN UU.PVI UU.ASD UU.NDB UU.INT UU.DNU uu.NUp

NOWAITS<
DEFINE U(C,DD,F)<
	ZZ==0
	IRP F,<
	ZZ==ZZ!UU.'F
	>

	.U'DD==↑D'C
	ZZ+↑D<C>  ,,  DD'S
>
>;NOWAITS

IFWAITS<
;JJW - generate external symbols giving table words for each code.
DEFINE U(C,DD,F)<
	ZZ==0
	IRP F,<
	ZZ==ZZ!UU.'F
	>
	.W'DD==:<ZZ+↑D<C>  ,,  DD'S>
>
>;IFWAITS

;THE DEFINITIONS OF THE VARIOUS BITS AND FIELDS
UU.PVN==(1B1)		;NETWORK PRIVILEGES REQUIRED
UU.PVI==(1B2)		;SUPER IMP PRIVILEGES REQUIRED
UU.ASD==(1B3)		;MUST CONSOLE ASSIGN AN IMP DEVICE
UU.NDB==(1B4)		;ALLOWED TO GET A FREE DDB
UU.INT==(1B5)		;INTERRUPTS NEED NOT BE DISABLED
UU.DNU==(1B6)		;DDB NOT USED (DON'T CALL SETDDB BEFOREHAND)
uu.NUp==(1b7)		;(260) network must be up to perform this UUO.

IFWAITS<INTERN	UU.PVN,UU.PVI,UU.ASD,UU.NDB,UU.INT,UU.DNU,UU.NUP>
;THE DISPATCH TABLE ;⊗ UUOTAB UUOLEN

IFWAITS<INTERN	UUOTAB>
UUOTAB:

NOWAITS<
	U 00,STAT,<>
>;NOWAITS
;	U 01,CONN,<PVN,ASD,NDB>
;	U 02,CLOS,<PVN,ASD>
	U 03,CONN,<PVN,ASD,NDB,NUp>	;(260)
	U 04,CLOS,<PVN,ASD,Int,NUp>	;(260)
	U 05,LIST,<PVN,ASD,NDB,NUp>	;(260)
	U 06,REQU,<PVN,ASD,NDB,NUp>	;(260)
	U 07,TALK,<PVN,ASD,NUp>		;(260)
;	U 08,TRAN,<PVN,ASD>
;(temp)	U 09,PINT,<PVN,ASD,NUp>		;(260)
;(temp)	U 10,AINT,<PVN,ASD,NUp>		;(260)
	U 11,VERS,<INT,DNU>
	U 12,DEAS,<PVN,ASD,Int>
	U 13,PHST,<INT,DNU>
;	U 14,CDDB,<>
;	U 15,PGVB,<PVN,ASD,NUp>		;(260)
NOWAITS<			;may change
	U 16,ITTY,<DNU>
>;NOWAITS
	U 17,XPWT,<PVN,ASD,INT,NUp>	;(260)
	U 18,PESC,<INT,DNU>
	U 19,RESC,<INT,DNU>
	U 20,PPAR,<PVN,ASD>
	U 21,RPAR,<PVN,ASD>
	U 22,XSTS,<DNU,Int>	; we turn off interrupt when we want
;(temp)	U 23,TRAC,<PVN,ASD>
;(temp)	U 24,PIAL,<PVN,ASD>

;(temp)	U 64,PNOP,<PVI,DNU,NUp>		;(260)
;(temp)	U 65,RSET,<PVI,DNU,NUp>		;(260)
;	U 66,PALL,<PVI,ASD,NUp>		;(260)
;	U 69,PECO,<PVI,DNU,NUp>		;(260)
	U 70,INIS,<PVI,Int,DNU>
	U 71,KILL,<PVI,INT,DNU>
	U 72,RAIS,<PVI,INT,DNU>
;	U 73,ERRO,<PVN,DNU>
IFN FTAIMP,<					;DK/OCT 75
;DO IMP IACCOUNTING
	U 81,IACT,<PVI,DNU>
>

	UUOLEN==.-UUOTAB
NOWAITS< ;It appears this code is never called. ;⊗ ERRCHK
;	ERROR CODES   --  RETURNED IN E+1 ON NON-SKIP RETURN


;HERE TO TEST FOR REMOTE HOST DOWN BEFORE DECLARING SYSTEM
;  FAILURE.
ERRCHK:	movx	t1,TrgDwn		; get target down flag
	tdne	t1,ImpIOS(f)		; is it set?
	  JRST	ERRDWN			; yes.  target was down
	JRST	ERRSOF			; no.  connection was reset.
>;NOWAITS
	DEFINE	ERRCOD(M,C) < ;⊗ ERRLST ERRXIT ErrSet
	E.'M==	.-ERRLST
ERR'M:	JSP	T1,ERRXIT
>

NOWAITS<
ERRLST:
	errcod	ILU,		ILLEGAL(UNIMPLEMENTED) UUO
	errcod	NSD,		NO SUCH DEVICE
	errcod	DNA,		DEVICE NOT AVAILABLE
	errcod	LNU,		LOGICAL NAME ALREADY IN USE
	errcod	STT,		STATE ERROR (WRONG STATE FOR THIS FUNCTION)
	errcod	SOF,		SOCKET OPENING failure (RESET)
	errcod	SYS,		SYSTEM ERROR
;	errcod	ABT,		A RFC WAS ABORTED
	ErrCod	CGT,		Can't get there from here
;	errcod	REQ,		THE REQUEST DOESNT MATCH YOUR RFC
	errcod	NES,		not enough internal buffer space
	errcod	SKT,		SOCKET NUMBER IN USE
	errcod	HST,		ILLEGAL HOST NUMBER
	errcod	DWN,		REMOTE HOST DOWN OR NOT ON NET
	errcod	ADR,		ADDRESS CHECK IN CALLI ARG LIST
	ERRCOD	TIM,		TIMEOUT
	ERRCOD	PAR,		PARAMETER SPECIFICATION ERROR
	ERRCOD	NCI,		TTY NOT CONNECTED TO IMP
	ERRCOD	QUO,		QUOTE OR ESCAPE ILLEGAL OR NOT DISTINCT
	ERRCOD	PRV,		NOT PRIVILEGED TO DO OPERATION
	ErrCod	NAI,		device is not an IMP
	ErrCod	NNU,		;(260) Network Not Up
	ErrCod	DUR,		destination unreachable (code in <lh>)
>;NOWAITS

IFWAITS<
ERRLST:
	errcod	ILU,		ILLEGAL(UNIMPLEMENTED) UUO
	errcod	SKT,		SOCKET NUMBER IN USE		(SIU in WAITS)
	errcod	CCS,		;Can't change socket numbers
	errcod	SYS,		SYSTEM ERROR
	errcod	NLA,		;No link available (won't ever happen)
	errcod	ILB,		;Illegal byte size
	ErrCod	NNU,		;(260) Network Not Up		(IDD in WAITS)
	errcod	GMM,		;Gender mismatch (won't happen)
	errcod	NSD,		NO SUCH DEVICE
	errcod	DNA,		DEVICE NOT AVAILABLE
	errcod	LNU,		LOGICAL NAME ALREADY IN USE
	errcod	STT,		STATE ERROR (WRONG STATE FOR THIS FUNCTION)
	errcod	SOF,		SOCKET OPENING failure (RESET)
;	errcod	ABT,		A RFC WAS ABORTED
	ErrCod	CGT,		Can't get there from here
;	errcod	REQ,		THE REQUEST DOESNT MATCH YOUR RFC
	errcod	NES,		not enough internal buffer space
	errcod	HST,		ILLEGAL HOST NUMBER
	errcod	DWN,		REMOTE HOST DOWN OR NOT ON NET
	errcod	ADR,		ADDRESS CHECK IN CALLI ARG LIST
	ERRCOD	TIM,		TIMEOUT
	ERRCOD	PAR,		PARAMETER SPECIFICATION ERROR
	ERRCOD	NCI,		TTY NOT CONNECTED TO IMP
	ERRCOD	QUO,		QUOTE OR ESCAPE ILLEGAL OR NOT DISTINCT
	ERRCOD	PRV,		NOT PRIVILEGED TO DO OPERATION
	ErrCod	NAI,		device is not an IMP
	ErrCod	DUR,		destination unreachable (code in <lh>)
>;IFWAITS

ERRXIT:	SUBI	T1,ERRLST+1
	ANDI	T1,-1		;GET RID OF LEFT HALF JUNK
; here to store an error code
ErrSet:
NOWAITS<
	HRRI	M,.UUSTT(P1)	;PUT ERROR CODE HERE
	PUSHJ	P,PUTWRD##
	  JRST	ADRERR##
>;NOWAITS
IFWAITS<
	DPB	T1,[POINT 6,STB##(F),35]	;JJW - store error in DDB
>;IFWAITS
	POPJ	P,
NOWAITS< ;⊗ TRANS RAISS KILLS VERSS INISS IFRSTR
	TRANS==	ERRILU		;ILLEGAL CODE


;SUBROUTINE TO PUT THE TEN ON THE NETWORK (PRIVILEGED)
RAISS:	TROA	T1,-1		;SET FLAG

;SUBROUTINE TO TAKE THE TEN OFF THE NETWORK SOFTLY. (PRIVILEGED)
KILLS:	MOVEI	T1,1		;SET FLAG
	HRREM	T1,IMPUP##
IFN FTAIMP,<
	JRST	IFRSTR		;INDICATE RESTART IN ACCT DATA
>
IFE FTAIMP,<
	JRST	CPOPJ1##
>


;SUBROUTINE TO RETURN THE CURRENT SOFTWARE VERSION NUMBERS
VERSS:	MOVE	T1,[VIMPSR##,,VIPSer##]	; IMP (1822) and IP versions
	pushj	p,PutWdu##		; store for user
	hrlzi	t1,VTCPSr		; TCP version
	pjrst	pw1pj1			; store that and skip return


;SUBROUTINE TO WIPE EVERYTHING (PRIVILEGED)
INISS:	PUSHJ	P,DINI+IMPDSP##	;DO 400 RESTART STUFF
IFN FTAIMP,<
IFRSTR:	SETZ	T1,		;PREPARE ENTRY FOR ACCTNG
	MOVEI	T2,17		;IDNICATE RESTART
	DPB	T2,IFTCOD	;IN T1
	PUSHJ	P,IFENTR	;MAKE ENTRY
>
	JRST	CPOPJ1##
>;NOWAITS
IFWAITS<
RAISS:
KILLS:
VERSS:
INISS:
>;IFWAITS
NOWAITS<
;SUBROUTINE TO RETURN EXTENDED STATUS OF AN IMP DEVICE.  MORE
;  ARGUMENTS MAY BE ADDED WITHOUT INVALIDATING EXISTING PROGRAMS.
;	MOVE	P1,[REL ADR OF ARGUMENT BLOCK]
;	PUSHJ	P,XSTSS
;	  ERROR--CODE IN T1
;	NORMAL RETURN--ARGUMENT BLOCK FILLED WITH STATUS INFO.

;BLOCK:	N		;NUMBER OF LOCATIONS THAT FOLLOW IN ARG BLOCK
			;  (0 IS SAME AS ↑O12)
;	SIXBIT	/DEV/
;	N-1 LOCATIONS FOR DATA TO BE RETURNED IN.  (IF N IS GREATER THAN
;		THE NUMBER OF WORDS PROVIDED BY THE MONITOR, THE REMAINDER
;		OF THE BLOCK WILL BE ZEROED).

; note: this UUO was massively changed by TCP
;CURRENTLY-DEFINED INDICES ARE:
;	0	.XSNUM	NUMBER OF WORDS THAT FOLLOW
;	1	.XSDEV	DEVICE NAME
;	2	.XSJob	owning job number
;	3	.XSIST	STATE of connection
;	4	.XSILS	LOCAL port NUMBER
;	5	.XSIHS	HOST
;	6	.XSIRS	REMOTE port NUMBER
;	7	.XSPrt	protocol
;	7	.XSRWn	INPUT window (how much we are giving him)
;	10	.XSSWn	OUTPUT window (how much he is giving us)
;	11	.XSIOS	RH I/O STATUS WORD (DEVIOS)
;	12	.XSRTT	current retranmission timeout time.
XSTSS:	PUSHJ	P,GETWDU##	;RETURN NUMBER OF USER ARGS ;⊗ XSTSS XSTSS0 XSTSS1 XSTSTB XSTBLN
	CAIGE	T1,2*<.UULST+1>	;WANT MORE THAN MINIMUM BLOCK?
	MOVEI	T1,2*<.UULST+1>	;NO, SUPPLY MINIMUM INFO
	ADDI	T1,(M)		;COMPUTE USER ADR OF LAST WORD OF BLOCK
	TRNN	T1,777760	;STILL IN AC'S?
	JRST	XSTSS0		;YES, IT'S OK
	TRNE	M,777760	;NO, ERROR IF STARTED IN AC'S
	PUSHJ	P,IADRCK##	;  OR IF WENT OUT OF BOUNDS
	  AOJA	P1,ERRADR
XSTSS0:	PUSH	P,T1		;SAVE USER ADR OF LAST WORD
	AOS	M,P1		;POINT TO DEVICE ARGUMENT
	PUSHJ	P,SETDDB	;SETUP IMP DDB
	  pjrst	tpopj##			; restore T1 for failure
	ScnOff			; make sure to get a consistent picture
	PUSHJ	P,STATS0	;RETURN SHORT STATUS, INCL. DEVICE NAME
	POP	P,P1		;GET BACK FINAL USER ADR
	movei	p2,1			; start with first entry in block.

;LOOP TO PLACE EXTENDED VALUES IN USER BLOCK
XSTSS1:	CAIG	P1,(M)		;ANY MORE SPACE IN USER BLOCK?
	  JRST	sonpj1##	;NO, SKIP RETURN TO USER
	CAILE	P2,XSTBLN	;YES, REACHED END OF STATUS INFO?
	TDZA	T1,T1		;YES, RETURN ZERO FOR REST OF BLOCK
	XCT	XSTSTB-1(P2)	;NO, GET NEXT ITEM
	PUSHJ	P,PUTWD1##	;STORE IN NEXT CELL IN USER BLOCK
	AOJA	P2,XSTSS1	;BACK FOR MORE

;TABLE FOR FETCHING EXTENDED STATUS INFORMATION.  NOTE THAT IT MAY BE
;  APPENDED TO, BUT MAY NOT BE REARRANGED OR ENTRIES DELETED WITHOUT
;  INVALIDATING EXISTING PROGRAMS

XSTSTB:	move	t1,Protcl(f)	; .XSPrt  protocol of this connection
	MOVE	T1,RcvWnd(F)	; .XSRWn  receive window size
	MOVE	T1,SndWnd(F)	; .XSSWn  send window size
	HRRZ	T1,DEVIOS(F)	; .XSIOS  DEVICE STATUS BITS
	move	t1,RTTime(f)	; .XSRTT  retransmission time

	XSTBLN==.-XSTSTB	;NUMBER OF EXTENDED STATUS ENTRIES
>;NOWAITS
IFWAITS<
XSTSS:
>;IFWAITS
NOWAITS< ;⊗ STATS STATS0 STATS9 STATS1
;SUBROUTINE TO RETURN THE STATUS OF A SIMPLEX CONNECTION
;  LOOKS AT IMPDEV(P1) AND LOW BIT OF IMPSKT(P1).
;CALL:
;	MOVE	P1,[REL ADDRESS OF ARGUMENT LIST
;	PUSHJ	P,STATS
;	  ERROR RETURN	...CODE IN T1
;	OK RETURN
STATS:	AOS	(P)		;PRESET SKIP RETURN

;CALLED FROM XSTSS (EXTENDED STATUS) ALSO.
STATS0:	HRRI	M,.UUDEV(P1)	;ADDRESS OF DEVICE NAME
	TLNE	P1,(IF.PRV)	;IF IMPORTANT PERSON,
	JRST	STATS9		;  GIVE HIM LOGICAL NAME
	LDB	T1,PJOBN##	;GET OWNERS JOB NUMBER
	MOVEI	T2,ASSCON
	TDNE	T2,DEVMOD(F)	;OWNED?
	CAME	T1,.CPJOB##	;BY THIS USER?
	  JRST	STATS1		;NO
STATS9:	SKIPE	T1,DEVLOG(F)	;LOGICAL NAME ASSIGNED?
	PUSHJ	P,PUTWDU##	;YES, RETURN IT

STATS1:	move	t1,state(f)		; get state
	LDB	T2,PJOBN##	;GET JOB NUMBER
	HRL	T1,T2			; put that in left half
	PUSHJ	P,PUTWD1##	;RETURN IT TOO
	move	t1,LclPrt(f)		; get local port
	PUSHJ	P,PUTWD1##	;RETURN THE port NUMBER
	move	t1,RmtAdr(f)		; get his address
	pushj	p,PutWd1##		; store it
	move	t1,RmtPrt(f)		; get his port
	PJRST	PUTWD1##	;GIVE IT TO THE USER AND RETURN
>;NOWAITS
NOWAITS< ;Perhaps WAITS should have something like this ;⊗ ITTYS ITTYS1 ITTYS2
;SUBROUTINE TO TRANSLATE BETWEEN IMPS AND CONTROLLING OR CONTROLLED TTYS.
;	MOVE	M,[REL ADR OF ARG BLOCK]
;	MOVE	P1,M
;	PUSHJ	P,ITTYS
;	  ERROR RETUR--CODE IN T1
;	OK RETURN

;THE RESULTS DEPEND ON THE CONTENTS OF THE BLOCK, AS FOLLOWS:
;	BEFORE				AFTER
;	------				-----
;BLOCK:	SIXBIT	/IMPN/		BLOCK:	SIXBIT	/IMPN/
;	0				FLAGS,,	TTY LINE #

;BLOCK:	0			BLOCK:	SIXBIT	/IMPN/
;	0,,	TTY LINE #		FLAGS,,	LINE # OF TTY CROSSPATCHED
;						TO IMPN.

;BLOCK:	0			BLOCK:	SIXBIT	/IMPN/
;	-1,,	TTY LINE #		FLAGS,,	LINE # OF TTY CONTROLLED
;						BY IMPN.

;FLAGS ARE:	BIT 0:	IMP CONTROLS TTY (I.E. TTY IS AN ITY)
;		BIT 1:	TTY PRINTER CROSSPATCHED TO IMP
;		BIT 2:	TTY KEYBOARD CROSSPATCHED TO IMP

ITTYS:	PUSHJ	P,GETWDU##	;GET FIRST ARGUMENT FROM USER
	JUMPE	T1,ITTYS1	;JUMP IF BLANK
	ScnOn			; let DDB stuff do it's stuff
				;  without these problems
	PUSHJ	P,SETDDB	;SETUP FOR DDB WORK
	  jrst	[		; error, not an IMP DDB
		ScnOff		; dispatch expects these off
		jrst	ErrNAI	; give the Not An Imp return
		]
	ScnOff			; shut down interrupts again
	JRST	ITTYS3		;OK, GO PROCESS USING THIS IMP

;HERE IF DEVICE NAME IS BLANK.  USE TTY NUMBER ARGUMENT.
ITTYS1:	PUSHJ	P,GETWD1##	;GET NEXT ARGUMENT
	MOVEI	T3,(T1)		;ISOLATE LINE NUMBER
	CAIL	T3,TTPLEN##	;LEGAL?
	JRST	ERRPAR		;NO
	HRRZ	U,LINTAB##(T1)	;YES, GET LDB POINTER FOR THAT LINE
	JUMPGE	T1,ITTYS2	;JUMP IF USER ASKING FOR CROSSPATCHED IMP
	CAIL	T3,ITYFST##	;NO, WANT CONTROLLING IMP.  IS THIS
	CAIL	T3,ITYFST##+ITYN## ;  AN ITY?
	JRST	ERRNCI		;NO
	SKIPA	F,ITYOFS##(T1)	;YES, GET ADR OF IMP CONTROLLING ITY
ITTYS2:	HRRZ	F,LDBIMP##(U)	;HERE TO GET ADR OF CROSSPATCHED IMP
	JUMPE	F,ERRNCI	;ERROR IF NO IMP CONNECTION TO TTY

	; fall into next page
;HERE WITH DESIRED IMP DDB POINTED TO BY F ;⊗ ITTYS3
ITTYS3:	MOVSI	U,TTYJOB+TTYPTR+TTYKBD ;BITS TO TEST FOR IMP CONNECTION
	TDON	U,TTYLIN(F)	;ARE ANY SET?  IF SO, SET U TO LDB
	JRST	ERRNCI		;NO--ERROR
	HRRI	M,(P1)		;RESET TO START OF USER ARGLIST
	MOVE	T1,DEVNAM(F)	;FETCH PHYSICAL IMP NAME
	PUSHJ	P,PUTWDU##	;RETURN IT
	LDB	T1,LDPLNO##	;FETCH LINE NO OF CONNECTED TTY
	HLL	T1,TTYLIN(F)	;RETURN FLAGS
	PJRST	PW1PJ1		;RETURN SECOND ARG AND SKIP
>;NOWAITS
repeat 0,<	; should be simple ;⊗ PIALS
;ROUTINE TO SET DESIRED ALLOCATION FOR AN OPEN INPUT CONNECTION
;	MOVE	P1,[ADDRESS OF ARGUMENT LIST]
;	PUSHJ	P,PIALS
;	  ERROR--CODE IN T1
;	NORMAL RETURN
;  THE .IBHST AND .IBRMT WORDS SPECIFY THE MESSAGE AND BIT ALLOCATIONS
;  TO BE USED SUBSEQUENTLY ON THE CONNECTION.  NOTE THAT THESE ARE
;  RESET TO SMALL VALUES BY THE 'TALK' OPERATION, SO 'PIAL' SHOULD
;  BE EXECUTED AFTER 'TALK'

PIALS:	PUSHJ	P,GETWD1##	;GET DESIRED MESSAGE ALLOCATION IN .IBHST
	CAIGE	T1,1		;AT LEAST 1?
	MOVEI	T1,1		;NO, MAKE IT 1
	CAILE	T1,.ALMSX	;WITHIN LIMIT?
	MOVEI	T1,.ALMSX	;NO, USE LIMIT
	DPB	T1,PIALMS	;STORE DESIRED ALLOCATION
	PUSHJ	P,GETWD1##	;NOW GET BIT ALLOCATION IN .IBRMT
	LDB	T2,PIBYTE	;GET CONNECTION BYTESIZE
	CAIGE	T1,(T2)		;AT LEAST ONE BYTE'S WORTH?
	MOVEI	T1,(T2)		;NO, MAKE IT SO
	CAILE	T1,.ALBTX	;WITHIN LIMIT?
	MOVEI	T1,.ALBTX	;NO, USE LIMIT
	DPB	T1,PIALBT	;STORE DESIRED BIT ALLOCATION
	JRST	CPOPJ1##	;OK RETURN
> ; end of repeat 0
;ROUTINE TO WAIT UNTIL THE CONNECTION BETWEEN A LOCAL TTY AND ;⊗ XPWTS PPARS RPARS
;   A CROSSPATCHED IMP IS BROKEN, EITHER BY THE ESCAPE HAVING BEEN TYPED
;   OR BY THE CONNECTION BEING CLOSED OR RESET.
;	MOVE	M,[REL ADR OF ARGUMENT BLOCK]
;	PUSHJ	P,XPTWS
;	  ERROR RETURN--CODE IN T1
;	OK RETURN AFTER WAITING FOR CROSSPATCH TO BE BROKEN

;JJW - calls to SETACT, WSYNC, CLRACT will have to be fixed for WAITS
XPWTS:	MOVSI	P1,TTYXWT	;SETUP WAITING-FOR-CROSSPATCH BIT
	IORM	P1,TTYLIN(F)	;SET IN DDB
	DPB	P1,PDVTIM##	;SET TIMER TO INFINITY
	MOVE	S,DEVIOS(F)	;GET I/O STATUS
	PUSHJ	P,SETACT##	;SET IOACT SO WSYNC WILL WORK
	MOVSI	T1,TTYKBD!TTYPTR ;BITS THAT MARK TTY-IMP CROSSPATCH
	TDNE	T1,TTYLIN(F)	;IS THE IMP CROSSPATCHED?
	PUSHJ	P,WSYNC##	;YES, WAIT UNTIL CROSSPATCH BROKEN
	ANDCAM	P1,TTYLIN(F)	;CLEAR WAITING-FOR-CROSSPATCH BIT
	PUSHJ	P,CLRACT##	;MAKE SURE IOACT IS CLEAR
	JRST	CPOPJ1		;OK RETURN


;ROUTINES TO SET AND READ THE USER-DEFINED CONNECTION PARAMETER WORD.
;   THIS WORD IS INTENDED FOR USE BY IMPCOM TO SAVE AND RESTORE ECHOING
;   CHARACTERISTICS, ETC.
;	MOVE	M,[REL ADR OF ARG BLOCK]
;	PUSHJ	P,PPARS (TO SET) OR RPARS (TO READ)
;	  ERROR--CODE IN T1
;	OK

;BLOCK:	SIXBIT	\IMPN\
;	EXP	PARAMETER WORD

PPARS:	HRRI	M,1(P1)		;GET USER PARAMETER
	PUSHJ	P,GETWDU##
	MOVEM	T1,USRPAR(F)	;STORE IN DDB
	JRST	CPOPJ1		;OK RETURN

RPARS:	HRRI	M,1(P1)		;POINT TO 2ND WORD OF PARAMETER BLOCK
	MOVE	T1,USRPAR(F)	;PICK UP PARAMETER WORD
	PJRST	PWUPJ1		;RETURN IT TO THE USER AND SKIP
;ROUTINES TO SET AND READ THE VARIOUS QUOTE AND ESCAPE CHARACTERS ;⊗ PESCS RESCS ALLQUO ALLQU1
;   FOR THE CONTROLLING TTY.
;	MOVE	M,[REL ADR OF ARG BLOCK]
;	PUSHJ	P,PESCS (TO SET) OR RESCS (TO READ)
;	  ERROR RETURN--CODE IN T1
;	OK RETURN

;BLOCK:	EXP	QUOTE CHARACTER
;	EXP	SHIFT CHARACTER
;	EXP	LOCAL ESCAPE CHARACTER
;	EXP	NETWORK ESCAPE CHARACTER

PESCS:	JSP	P2,ALLQUO	;DO THE FOLLOWING FOR EACH ARGUMENT
	PUSHJ	P,GETWDU##	;GET THE NEXT USER ARGUMENT
	HRRZ	T3,T1		;COPY THE CHARACTER
	PJRST	QUOCHK##	;CHECK IF LEGAL AND STORE IN LDB IF SO


RESCS:	JSP	P2,ALLQUO	;DO THE FOLLOWING FOR EACH ARGUMENT
	LDB	T1,LDPQTB##(T4)	;FETCH A QUOTE OR ESCAPE CHAR FROM THE LDB
	PJRST	PWUPJ1		;GIVE IT TO THE USER AND SKIP RETURN


;AUXILIARY ROUTINE TO CALL ANOTHER ROUTINE FOR EACH QUOTE OR ESCAPE
;   CHARACTER ARGUMENT
;	MOVE	P2,[ADDRESS OF ROUTINE TO CALL]
;	PUSHJ	P,ALLQUO
;	  ERROR RETURN--CODE IN T1
;	OK RETURN--CALL SUCCESSFULLY ITERATED OVER ALL CHARACTERS

;THE CALLEE IS PROVIDED WITH THE FOLLOWING AC'S SETUP:
;	U	THE TTY LDB ADDRESS
;	T4[RH]	THE QUOTE INDEX (INTO THE QUOTE POINTER TABLE)
;	M	UPDATED TO POINT TO NEXT USER ARGUMENT

ALLQUO:	SKIPE	U,TTYTAB##(J)	;FETCH THIS USER'S TTY DDB ADDRESS
	HRRZ	U,DDBLDB##(U)	;FOLLOW LINK TO LDB
	JUMPE	U,ERRDNA	;ERROR IF DETACHED OR NONEXISTENT
	MOVSI	T4,MNQUPT##	;SETUP -# OF QUOTE POINTERS,,0
ALLQU1:	PUSHJ	P,(P2)		;CALL GIVEN ROUTINE
	  JRST	ERRQUO		;ERROR RETURN--RETURN CODE
	AOBJP	T4,CPOPJ1	;INCREMENT INDEX.  DONE?
	AOJA	M,ALLQU1	;NO, DO ANOTHER ARGUMENT
;SUBROUTINE FOR SETTING UP A SIMPLEX CONNECTION. ;⊗ CONNS GotArp
;CALL:
;	MOVE	P1,[CODE,,RELATIVE ADDRESS OF ARGUMENT BLOCK]
;	MOVE	M,[REL ADDRESS OF ARGS (R) ]
;	PUSHJ	P,CONNS
;	ERROR RETURN	...CODE IN T1
;	OK RETURN

CONNS:	skiple	t1,State(f)		; get the state.  is it closed state?
	 cain	t1,S%List		; or listen?
	  skipa				; yes.
	   jrst	ErrStt			; wrong state for this.

	; set up DDB
NOWAITS<
	pushj	p,GetWd1##		; get host number (can be 32 bits)
>;NOWAITS
IFWAITS<
	EXCTUX	<MOVE T1,HLOC##(M)>
>;IFWAITS
	jumpe	t1,ErrHst		; can't be zero
	txnn	t1,NetMsk		; is there a network set?
	  txo	t1,ArpAdr		; no.  set ARPAnet address
	movem	t1,RmtAdr(f)		; store remote address
	skipe	NetAdr(f)		; need an arpanet address?
	  jrst	GotArp			; nope.  already read one off incoming
	pushj	p,Target		; find an ARPAnet address to
					;  try to get this sent.
	  jrst	ErrCGT			; can't get there from here:
					;  couldn't find a route.
	movem	t1,NetAdr(f)		; save that in DDB
GotArp:
NOWAITS<
	pushj	p,GetWd1##		; get remote port
>;NOWAITS
IFWAITS<
	EXCTUX	<MOVE T1,FSLOC##(M)>
>;IFWAITS
	andx	t1,<1←↑d16>-1		; trim down to 16 bits
	movem	t1,RmtPrt(f)		; save it in DDB
	PUSHJ	P,MAKMYS	;MAKE SOCKET
	  JRST	ERRSKT		;ILLEGAL
	pushj	p,prpDDB		; set required areas of DDB
	pushj	p,GetISS		; get an initial send sequence number
	movem	t1,SndISS(f)		; save it in the DDB
	movem	t1,SndNxt(f)		; and make it the current
					;  sequence number
	aos	t1			; account for SYN
	movem	t1,SndLWd(f)		; and make that the first message
	setzm	SndWnd(f)		; we have no idea how much we
					;  can send until we hear.
	setzm	SndLst(f)		; no last message yet (force
					;  this into retransmission queue)
	movx	t1,TC%Syn		; get SYN bit and ACK the SYN we got
	iorm	t1,SndBts(f)		; set it in bits to be sent
	pushj	p,SndMsg##		; send message now.
	  jrst	errNES			; give not enough space return

	movei	t1,S%SynS		; we've sent a SYN
	movem	t1,State(f)		; save our new state
	pushj	p,EstbWt		; wait for established (T1 is loaded)
	  popj	p,			; failed.  error already
					;  returned to user.
	jrst	cpopj1##		; we're established: skip return
;SUBROUTINE TO DROP A CONNECTION. ;⊗ CLOSS PCLSSD ClsFls ClsEst
;CALL:
;	PUSHJ	P,CLOSS
;	ERROR RETURN --  CODE IN T1
;	OK RETURN
CLOSS:
NOWAITS<
	SKIPGE	TTYLIN(F)	;JOB CONTROL?
	 TLNE	P1,(IF.PRV)	;YES, ENABLED SUPER-IMP PRIVILEGES?
	  JRST	PCLSSD		;NO JOB CONTROL OR CORRECT PRIVILEGES
	PUSHJ	P,PRVJ##	;TEST FOR LOGIN, LOGOUT
	  jrst	PCLSsd		;OK TO SUICIDE
	JRST	ERRDNA		;NOT AVAILABLE TO CASUAL PROG.
>;NOWAITS

PCLSSD:	move	t1,State(f)		; get state of the connection
	dispat	(t1,cpopj1##,<<S%List,<jrst	ClsFls>>
			     ,<S%SynS,<jrst	ClsFls>>
			     ,<S%SyRP,<jrst	ClsEst>>
			     ,<S%SyRA,<jrst	ClsEst>>
			     ,<S%Estb,<jrst	ClsEst>>
			     ,<S%ClsW,<jrst	ClsEst>>
		    >)

ClsFls:	pushj	p,DDBFls##		; flush DDB
	pushj	p,DDBRel##		; return it to free pool
	pjrst	cpopj1##		; and give a good return

ClsEst:	movx	t1,TC%Fin		; set FIN bit
	iorm	t1,SndBts(f)		; set it in bits to be sent
	scnoff				; no interrupts
	pushj	p,SndMsg##		; send message now.
	  jrst	[			; failed
		 scnon			; enable interrupts
		 pjrst	errNES		; not enough buffer space for message
		]
	scnon				; interrupts ok now.

	move	t1,State(f)		; get state again
	movei	t2,S%Fin1		; assume it's establish, so
					;  we're going to FIN-wait-1
	caie	t1,S%Estb		; are we established?
	  movei	t2,S%LAck		; no, close-wait.  goto last-ACK
	movem	t2,State(f)		; save the new state
	pjrst	cpopj1##		; and give a good return
;SUBROUTINE TO DEASSIGN A DEVICE AFTER IT HAS HAD ;⊗ DEASS
;  BOTH SIDES CLOSED.
;CALL:
;	PUSHJ	P,DEASS
;	  ERROR RETURN	...CODE IN T1
;	OK RETURN...	DEVICE DEASSIGNED
DEASS:	skiple	t1,State(f)		; get state
	  jrst	ERRSTT			; not a closed state.  not allowed.
	PUSHJ	P,DDBFls##	;NOW RELEASE IT
	pushj	p,DDBRel##		; back to free pool
	JRST	CPOPJ1##	;SKIP RETURN
;SUBROUTINE TO PUT A SOCKET IN THE LISTENING STATE ;⊗ LISTS Lists2 LISTS1
;THE SOCKET MUST BE CLOSED, LISTENING, OR IN RFC IN STATE.
;CALL:
;	PUSHJ	P,LISTS
;	ERROR RETURN --  CODE IN T1
;	OK RETURN

LISTS:
NOWAITS<
	PUSHJ	P,GETWD1##	;GET remote host
>;NOWAITS
IFWAITS<
	EXCTUX	<MOVE T1,HLOC##(M)>
>;IFWAITS
	jumpe	t1,Lists2		; don't munge it if he wants default.
	txnn	t1,NetMsk		; does it have a network number?
	  txo	t1,ArpAdr		; no.  default to our network.
Lists2:	movem	t1,RmtAdr(f)		; and save it
NOWAITS<
	PUSHJ	P,GETWD1##	;GET REMOTE SOCKET NUMBER
>;NOWAITS
IFWAITS<
	EXCTUX	<MOVE T1,FSLOC##(M)>
>;IFWAITS
	andx	t1,<1←↑d16>-1		; trim down to 16 bits
	movem	t1,RmtPrt(f)		; and save it
	move	t1,State(f)		; get current state
	cain	t1,S%List		; already listening?
	  JRST	LISTS1			; YES.  don't clobber port we have.
	caie	t1,S%Clos		; closed?
	  jrst	ErrStt			; nope.  must have slipped to
					;  a more advanced state while
					;  he wasn't looking (that's
					;  what he gets for using
					;  Listen instead of Request).
	PUSHJ	P,MAKMYS	;MAKE A port
	  JRST	ERRSKT		;ILLEGAL
LISTS1:
	pushj	p,PrpDDB		; prepare DDB for action
	MOVEI	T1,S%List		; this is Listen state now
	movem	t1,State(f)		; new state
	JRST	CPOPJ1##
;SUBROUTINE TO GET A SOCKET REQUEST ;⊗ REQUS PW1PJ1
;IF THERE IS NONE IN YET, THE JOB WAITS FOR ONE.
;CALL:
;	PUSHJ	P,REQUS
;	ERROR RETURN -- CODE IN T1
;	OK RETURN

REQUS:	PUSHJ	P,LISTS		;MAKE SURE LISTENING OR RFC IN
	  POPJ	P,		;ERROR!!
	JUMPL	P1,CPOPJ1	;NO WAIT IF FLAG ON
	movei	t1,S%List		; waiting to get out of listen state
	PUSHJ	P,EstbWt		; wait to get into established
					;  or better.
	  popj	p,			; error return (error code
					;  already given to user)
NOWAITS<
	hrri	m,.uuhst(p1)		; point at host word
	move	t1,RmtAdr(f)		; get host we accepted
	pushj	p,PutWdu##		; store host
>;NOWAITS
IFWAITS<
	move	t1,RmtAdr(f)		; get host we accepted
	EXCTUU	<MOVEM T1,HLOC##(M)>
>;IFWAITS
	move	t1,RmtPrt(f)		; get remote port number

NOWAITS<
;HERE TO STORE IN THE NEXT WORD OF THE USER'S BLOCK, THEN SKIP RETURN
PW1PJ1:	PUSHJ	P,PUTWD1##	;RETURN IT
	JRST	CPOPJ1##	;OK RETURN
>;NOWAITS
IFWAITS<
	EXCTUU	<MOVEM T1,FSLOC##(M)>
	JRST	CPOPJ1##
>;IFWAITS
;SUBROUTINE TO CONNECT A DUPLEX IMP CONNECTION TO ;⊗ TALKS TalkOK
;  THE USER'S LOCAL TELETYPE.
;CALL:
;	MOVE	P1,[ADDRESS OF ARGUMENT LIST]
;	PUSHJ	P,TALKS
;	  ERROR RETURN	...CODE IN T1
;	OK RETURN...	TELETYPE CONNECTED
TALKS:
	skipe	IBfThs(f)		; anything waiting to be read?
	  jrst	TalkOK			; yes.  always legal to crosspatch.
	move	t1,State(f)		; get state
	CAIE	T1,S%Estb		; established?
	 cain	t1,S%ClsW		; or close wait?
	  jrst	TalkOK			; yes.  data can still flow
	caie	t1,S%Fin1		; Fin-1?
	 cain	t1,S%Fin2		; or 2?
	  jrst	TalkOK			; yes.  he can still send to us
	JRST	ERRSTT			; bad state to crosspatch

TalkOK:	SKIPGE	TTYLIN(F)	;JOB-CONTROLLING IMP?
	   JRST	ERRDNA		;YES, DON'T ALLOW, ELSE WIERD LOOP
	MOVSI	T1,(IECHO)	;SET UP FOR TWEAK
	MOVE	T2,[ANDCAM T1,TELOWD(F)];NORMALLY A CLEAR
	skipGE	P1		;BUT SOMETIMES NOT IF /ECHO SWITCH USED
	  HRLI	T2,(IORM T1,(F));  (ASSUMING HERE FROM IMPCOM)
	XCT	T2		;DO IT
	PUSHJ	P,IMPTTY##	;SET UP THE CONNECTION
;	PUSHJ	P,TLNSET	;SPECIFY SMALL ALLOCATIONS FOR TTY'S
	JRST	CPOPJ1##	;  AND RETURN
repeat 0,<	; might be fun to do sometime..... ;⊗ TRACS TRCTAB PINTS AINTS PECOS

;SUBROUTINE TO ENABLE/DISABLE SENDING THE TRACE BIT ON ALL OUTPUT
;  MESSAGES THRU THIS SOCKET.
;	MOVE	P1,[ADDRESS OF USER ARGUMENT LIST]
;	PUSHJ	P,TRACS
;	  ERROR--CODE IN T1
;	OK RETURN -- TRACE ENABLED OR DISABLED

;BLOCK:	SIXBIT	/DEV/
;	EXP	SWITCH (0 TO DISABLE, NONZERO TO ENABLE)

TRACS:	TRNN	P2,1		;CAN ONLY DO THIS FOR OUTPUT CONNECTIONS
	JRST	ERRPAR		;OOP
	HRRI	M,.UUSTT(P1)	;OK, POINT TO TRACE SWITCH
	PUSHJ	P,GETWDU##	;GET IT FROM USER CORE
	JUMPE	T1,.+2		;JUMP IF TURNING OFF
	MOVEI	T1,1		;ON
	MOVSI	T2,(TRCENB)	;SET OR CLEAR TRACE ENABLE BIT IN DDB
	XCT	TRCTAB(T1)	;ANDCAM OR IORM
	JRST	CPOPJ1##	;SKIP RETURN TO USER

TRCTAB:	ANDCAM	T2,ostat(F)	;[96bit] DISABLE
	IORM	T2,ostat(F)	;[96bit] ENABLE

;still in repeat 0
;SUBROUTINE TO SEND AN INTERRUPT ON THE SPECIFIED SOCKET
;CALL:
;	MOVE	P1,[ADDRESS OF ARGUMENT LIST]
;	PUSHJ	P,PINTS
;	ALWAYS RETURN HERE
PINTS:	PUSHJ	P,GETSTT	;GET THE STATE
	CAIE	T1,.ISOPN	;OPEN?
	JRST	ERRSTT		;NO
	PUSHJ	P,GETHST	;GET THE HOST NUMBER
	PUSHJ	P,NDBSTU	;SET UP NCP UUO DDB
	TRNN	P2,1		;MY RECEIVE SOCKET?
	PUSHJ	P,PINR		;YES, SEND "INR"
	TRNE	P2,1
	PUSHJ	P,PINS		;NO, SEND "INS"
	PUSHJ	P,OUTXX		;SEND IT
	JRST	CPOPJ1##	;OK RETURN

> ; end of one repeat 0
REPEAT 0,<
;SUBROUTINE TO SPECIFY THE USERS TRAP ADDRESS FOR INCOMING
;  INTERRUPTS.(NOT FULLY IMPLEMENTED)
;CALL:
;	MOVE	P1,[ADDRESS OF ARGUMENT LIST]
;	PUSHJ	P,AINTS
;	  ERROR RETURN	...CODE IN T1
;	OK RETURN...	ADDRESS DEPOSITED IN DDB
AINTS:	PUSHJ	P,GETSTT	;GET STATE
	CAIE	T1,.ISOPN	;BETTER BE OPEN
	JRST	ERRSTT		;IT ISNT
	PUSHJ	P,GETWD1##	;GET HOST NUMBER FIELD(DISPATCH ADDRESS)

	HRRZS	T1
	PUSHJ	P,SETINT	;SET IT IN THE DDB
	JRST	CPOPJ1##	;OK RETURN

;STILL IN REPEAT 0
;SUBROUTINE TO SEND A "ECO" MESSAGE AT UUO LEVEL(PRIVILEGED)
PECOS:	PUSHJ	P,GETHS1	;GET, TEST HOST NUMBER
	  JRST	ERRHST		;FOUL-UP
	PUSHJ	P,NDBSTU	;SET UP UUO DDB FOR NCP
	PUSHJ	P,PECO		;SEND IT
	PUSHJ	P,OUTXX
	JRST	CPOPJ1##
> ;END REPEAT 0
;SUBROUTINE TO RETURN THE LOCAL HOST AND IMP PARAMETERS ;⊗ PHSTS PWUPJ1
;	PARAMETERS:
;	    In .IbDev (.UUDev):
;		bits 1-8:	# OF ITY'S IN SYSTEM
;		bits 9-17:	# OF IMPS
;(246)		right half:	tty number of first ITY.
;	    In .IbStt (.UUStt):
;		BIT 0:	1 IF IMP IS NOT READY
;	    In .IbHst (.UUHst)
;		bits 18-35:	LOCAL HOST'S NETWORK ADDRESS
PHSTS:
	hrlzi	t1,itimpl##	;[96bit] get the ity/imp count
	hrri	t1,ityfst##	;(246) and the first ITY number.
	pushj	p,putwdu##	;[96bit] put in first word of block
	setz	t1,		;[96bit] (more imp status can go in
				;	  around here somewhere.)
	skipn	okflag##	;[96bit] imp up?
	  tlo	t1,400000	;[96bit] no: set flag
	pushj	p,putwd1##	;[96bit] put that in the second word
	move	t1,IPAddr##	;[96bit] get my site number
	hrri	m,.uuhst(p1)	;[96bit] point to host word

;HERE TO RETURN A WORD TO THE USER'S BLOCK, THEN SKIP RETURN
PWUPJ1:	PUSHJ	P,PUTWDU##	;RETURN IT
	JRST	CPOPJ1		;OK RETURN
REPEAT 0,<		;THESE FUNCTIONS WERE NEVER DEBUGGED ;⊗ PALLS PALLS1 PGVBS RSETS PNOPS PNOPS1 GETHS1
;HERE TO SEND AN "ALL" TYPE MESSAGE(PRIVILEGED)
PALLS:	PUSHJ	P,GETSTT	;GET STATE
	CAIE	T1,.ISOPN	;OPEN?
	JRST	ERRSTT		;NO
	PUSHJ	P,GETWD1##	;GET MESSAGES
	MOVE	P3,T1
	PUSHJ	P,GETWD1##	;GET BITS
	MOVE	T2,P3
	TRNN	P2,1		;MY SEND?
	JRST	PALLS1		;NO
	ADDM	T1,OALBIT(F)
	ADDM	T2,OALMES(F)
	PUSHJ	P,IMPALL##	;TELL IMP SERVICE
	JRST	CPOPJ1##

;HERE TO SEND "ALL" TO REMOTE HOST
PALLS1:	MOVNS	T1
	ADDM	T1,IALBIT(F)	;DECREMENT INPUT ALLOCATION COUNTERS
	MOVNS	T2		;  SO THEY WILL BE INCREASED AT CLOCK
	ADDM	T2,IALMES(F)	;  OR INTERRUPT LEVEL.
	JRST	CPOPJ1##

;STILL IN REPEAT 0
;HERE TO SEND A "GVB" MESSAGE TO RE-INITIALIZE ALLOCATION.
PGVBS:	LDB	T1,PIHOST	;GET HOST NUMBER
	PUSHJ	P,NDBSTU	;SET UP AN NCP DDB
	PUSHJ	P,PGVB		;BUILD THE MESSAGE
	PUSHJ	P,OUTXX		;SEND IT
	JRST	CPOPJ1##
> ;END REPEAT 0

repeat 0,<	; can't do this in TCP
;ROUTINE TO RESET A SPECIFIED HOST (PRIVILEGED)
RSETS:	PUSHJ	P,GETHS1	;GET AND TEST HOST NUMBER
	  JRST	ERRHST		;NO GOOD
	PUSH	P,T1		;SAVE IT
	PUSHJ	P,HSTCLR	;WIPE THE HOST LOCALLY
	POP	P,T1		;GET BACK HOST NUMBER
	PJRST	PNOPS1		;CAUSE 'RST' TO BE SENT BY QUEUEING A NOP

;HERE TO SEND A "NO-OP" TO THE SPECIFIED HOST
PNOPS:	PUSHJ	P,GETHS1	;GET AND TEST HOST NUMBER
	  JRST	ERRHST		;ERROR
PNOPS1:	PUSHJ	P,NDBSTU	;SET UP A DDB
	PUSHJ	P,PNOP		;FORM THE MESSAGE
	PUSHJ	P,OUTXX		;SEND IT
	JRST	CPOPJ1##	;RETURN

;SUBROUTINE TO GET THE HOST FIELD  AND TEST IT.
GETHS1:
	hrri	m,.uuhst(p1)	;[96bit] set to host word
	pushj	p,g.uuht	;[96bit] get host word
	jumpg	t1,cpopj1##	;[96bit] greater than 0 is OK.
	popj	p,		;[96bit] 0 is not OK.

> ; end of repeat 0
NOWAITS< ;WAITS calls T10DDB to do some of this. ;⊗ SETDDB SETDD3 SETDD0 SETDD2 SETDD1 SetDD4
;SET UP A DDB FOR UUO WORK
;CALL:
;	MOVE	P1,[ XWD CODE, REL ADDRESS OF ARGUMENT LIST]
;	MOVE	M,[RELADR(R)]
;	MOVE	J,JOB NUMBER
;	PUSHJ	P,SETDDB
;	ERROR RETURN -- CODE IN T1
;	OK RETURN

SETDDB:	PUSHJ	P,GETWDU##	;GET UUO DEVICE NAME
	JUMPE	T1,SETDD1	;JUMP IF NONE
	PUSHJ	P,DEVSRG##	;FIND DEVICE
	  JRST	SETDD1		;NO SUCH DEVICE
	HLRZ	T1,DEVNAM(F)	;PHYSICAL DEVICE NAME
	CAIE	T1,(SIXBIT -IMP-);AN IMP?
	  JRST	SETDD2		;NO
	LDB	T1,PJOBN##	;GET OWNER'S JOB NUMBER
	CAMN	T1,J		;SAME?
	  JRST	SETDD3		;YES
	TLNE	P1,(IF.PRV)	;NO, SPECIAL ACTION?
	 TLOA	P1,(IF.NWT)	;YES, FORCE NOWAIT OPTION
SETDD3:	  TLNN	p4,UU.ASD	;MUST ASSIGN DEVICE?
	   JRST	SETDD0		;NO. DON'T ASSIGN IT
	PUSHJ	P,GETWDU##	;GET DEVICE NAME FOR ASSASG		DK/MAR 75
	MOVEI	T2,ASSCON	;ASSIGN BY CONSOLE
	PUSHJ	P,ASSASG##
	  JRST	ERRDNA		;CANT HAVE IT
	skipg	State(f)		; is it a closed DDB?
	  PUSHJ	P,CLRIMP##		; yes.  CLEAR THE DDB
SETDD0:	HRRI	M,.UUSKT(P1)	;POINT AT LOCAL SOCKET NUMBER
	PUSHJ	P,GETWDU##	;GET IT
	andx	t1,<1←↑d16>-1		; trim down to 16 bits
	MOVE	P2,T1		;PUT IN PROPER AC
	JRST	CPOPJ1##	;RETURN

;HERE WHEN THE DEVICE IS NOT AN IMP
SETDD2:	PUSHJ	P,GETWDU##	;GET DEVICE NAME AGAIN
	CAMN	T1,DEVLOG(F)	;WAS IT THE LOGICAL NAME FOR THIS IMP?
	  JRST	ERRLNU		;YES, CAN'T ALLOW IT.

;HERE WHEN CANT FIND THE SPECIFIED DEVICE
SETDD1:	TLNE	p4,UU.NDB	;ALLOWED TO GET FREE DDB?
	PUSHJ	P,DDBGET##	;GET A DDB
	  JRST	ERRNSD		;NO OR NONE
	PUSHJ	P,GETWDU##	;GET DEVICE NAME AGAIN
	JUMPE	T1,SetDD4		;SPECIFIED?
	CAME	T1,[SIXBIT\IMP\] ;AND NOT 'IMP'?
	  MOVEM	T1,DEVLOG(F)	;YES, ASSIGN LOGICAL NAME
SetDD4:	PUSHJ	P,SETDVL##	;MARK DDB AS BELONGING TO JOB (J)	DK/MAR 75
				;AND ADD TO LOGICAL NAME TABLE		DK/MAR 75
	MOVE	T1,DEVNAM(F)	;PICK UP PHYSICAL NAME
	PUSHJ	P,PUTWDU##	;GIVE HIM THE PHYSICAL NAME
	JRST	SETDD0		;AND SET IT UP
>;NOWAITS
; subroutine to set up essential areas of a DDB ;⊗ PrpDDB
PrpDDB:	movei	t1,.iptcp		; get TCP protocol number for IP
	movem	t1,Protcl(f)		; save in DDB
	move	t1,IPAddr##		; get my site number
	movem	t1,LclAdr(f)		; that's the source address
	movei	t1,TCPRTT		; get standard retransmission time
	movem	t1,RTTime(f)		; save that in DDB
	movei	t1,TCPUTT		; get user timeout time (can't
					;  be set by user yet).
	movem	t1,UTTime(f)		; put that in DDB as timeout set.
	movem	t1,UTTimr(f)		; and set timeout time now.
	movei	t1,WndSiz		; get size of standard window.
					; (this should be more flexible.)
	movem	t1,RcvWnd(f)		; initialize window size.
	lsh	t1,-1			; get 1/2 of size
	movem	t1,RcvThr(f)		; that's our window treshhold
	setzm	RcvHld(f)		; we're not holding back any bytes yet
	popj	p,			; return
;ROUTINE TO MAKE A LOCAL SOCKET NUMBER FOR A USER'S IMPUUO. ;⊗ MAKMYS MAKMY0 MakM00 MakMy1 MakNxt MakFre
;	MOVE	P1,[IMPUUO ARGUMENT WORD]
;	MOVE	P2,[LOCAL SOCKET AS SUPPLIED BY USER]
;	MOVE	J,[JOB NUMBER]
;	MOVE	F,[IMP DDB ADDRESS]
;	PUSHJ	P,MAKMYS
;	  ERROR--DUPLICATE OR UNAVAILABLE LOCAL SOCKET NUMBER
;	OK--FULL LOCAL SOCKET NUMBER IN LclPrt(f)

MAKMYS:	TLNN	P1,(IF.ALS)		;USER WANT ABSOLUTE LOCAL SOCKET?
	  jrst	MakFre			; no.  grab a free socket
;JJW - study the following code carefully.  I don't trust it.
	pushj	p,save4##		; get lots of registers
	move	p3,RmtPrt(f)		; target port
	move	p4,RmtAdr(f)		; target host
	PUSH	P,F		;SAVE DDB POINTER
	MOVEI	T4,(F)		;MAKE A COPY AND CLEAR SOCKET USE FLAG
	HRRZ	F,TTYTAB##(j)	;GET TTY DDB FOR THIS JOB
	PUSHJ	P,CTLJBD##	;FIND CONTROLLING JOB
	move	t2,t1			; save controlling job number
	MOVEI	F,IMPDDB##	;SEARCH ALL DDB'S
NOWAITS<
	MOVEI	T3,IMPN##
>;NOWAITS
MAKMY0:	skiple	State(f)		; ignore closed DDBs
	 CAIN	F,(T4)			; mustn't be our DDB
	  jrst	MakNxt			; try next one
	came	p4,RmtAdr(f)		; is this aimed at the target site?
	  jrst	MakNxt			; this isn't very informative
	came	p2,LclPrt(f)		; does the local port match ours?
	  jrst	MakM00			; no.  try remote match
	move	t1,RmtPrt(f)		; load remote port
	cain	t1,1(p3)		; is the remote port this our
					;  controlling port?
	  jrst	MakMy1			; yes.  this is a related connection
	came	t1,p3			; is the remote port the same?
	  jrst	MakNxt			; no.  examine next DDB
	move	t1,State(f)		; get the state
	caie	t1,S%TimW		; is it time wait?
	  jrst	FPopj##			; no.  this is a functioning
					;  connection, in use.
	ldb	t1,PJobN##		; get the owning job
	caie	t1,(j)			; do we own it?
	  jumpn	t1,FPopj##		; no, someone else does
	pushj	p,DDBRel##		; flush the one which is
					;  waiting to time out.  this
					;  isn't quite legal, but
					;  someone knows she wants to
					;  reuse this connection, so go
					;  ahead and let them.  chances
					;  are they're reusing it
					;  because they know they can.
	jrst	MakNxt			; but we still need to check
					;  for a related socket before
					;  we approve this connection.

MakM00:	MOVE	T1,LclPrt(F)		; get local port
	camn	p3,RmtPrt(f)		; are the remote sockets the same?
	 caie	t1,1(p2)		; and are the local sockets related?
	  JRST	MakNxt			; no to one.  not related
MakMy1:	LDB	T1,PJOBN##		; get owning job
	CAIe	T1,(J)			; is it ours?
	 cain	t1,(t2)			; or our parent's?
	  tloa	T4,-1			; yes.  mark we saw a related
					;  socket that belongs to us.
	   jrst	FPopj##			; oops.  someone else owns a
					;  related socket.
MakNxt:	HLRZ	F,DEVSER(F)		;CHAIN TO NEXT DDB
NOWAITS<
	SOJG	T3,MAKMY0		;MORE?
>;NOWAITS
IFWAITS<
	CAIE F,IMP.NX##			;MORE?
	 JRST MAKMY0
>;IFWAITS

	TLNn	P1,(IF.PRV)		; is he prived?
	 pjmpge	t4,FPopj##		; no.  does he own a relative?
					;  if not, give error return.
	movem	p2,LclPrt(t4)		; save this port in the DDB
	pjrst	FPopj1##		; skip return: either has privs
					;  to do anything, or knows a
					;  related socket.

;JJW - The following 2 comment lines appear to be out-of-date.
;HERE IF USER-SUPPLIED ARGUMENT IS NEGATIVE, MEANING WANT A FREE SOCKET
;  RANGE ALLOCATED.
MakFre:	PUSHJ	P,FRESKT	;FIND A FREE SOCKET
	ANDI	P2,SK.LCL	;MASK USER-SPECIFIED PORTION
	IORb	P2,T1		;BUILD COMPLETE SOCKET
	movem	p2,LclPrt(f)		; save this port in the DDB
NOWAITS<				;JJW - this is done in IMPUUO.FAI.
	hrri	m,.uuSkt(p1)		; point at local port word
	pushj	p,PutWdu##		; tell user what the local port
					;  we assigned is (it's in T1)
>;NOWAITS
	JRST	CPOPJ1##	;GIVE NORMAL RETURN
;ROUTINE TO ALLOCATE A FREE SOCKET RANGE ;⊗ FRESKT FRESK1
;	PUSHJ	P,FRESKT
;	ALWAYS RETURN HERE, WITH FIRST SOCKET IN RANGE IN T1.

FRESKT:	AOS	T1,SKTNUM	;ADVANCE SOCKET NUMBER GENERATOR
	txne	t1,FreOvr		; overflowing out of field?
	  setzb	t1,SktNum		; yes.  zero it.
	LSH	T1,FRELSH	;POSITION THE BITS
	ADDx	T1,FREMIN	;OFFSET FROM START
	MOVE	T2,T1		;MAKE A COPY
NOWAITS<
	MOVEI	T3,IMPN##	;START IMP COUNTER
>;NOWAITS
	MOVEI	T4,IMPDDB##	;SEARCH ALL IMP DDB'S
FRESK1:	xor	t2,LclPrt(t4)		; compare with local port
	txnn	t2,FreMch		; is it a match?
	  JRST	FRESKT		;YES, DISCARD AND TRY AGAIN
	HLRZ	T4,DEVSER(T4)	;LOOP THRU ALL DDB'S
NOWAITS<
	SOJG	T3,FRESK1
>;NOWAITS
IFWAITS<
	CAIE T4,IMP.NX##
	 JRST FRESK1
>;IFWAITS
	POPJ	P,		;HERE WHEN FOUND FREE SOCKET RANGE.
; subroutine to decide where to send a message on the ARPAnet to get ;⊗ Target
;  it to some host in the internet.
; call:
;	move	t1,<IP network address>
;	pushj	p,Target
;	  <return here if we couldn't figure out a way>
;	<return here with T1 = ARPAnet address>

Target:	pushj	p,save1##		; get p1
	ldb	p1,[pointr t1,NetMsk]	; get network address
	caie	p1,.ArpaN		; arpanet?
	  popj	p,			; too bad, that's the only one
					;  i know.
	txz	t1,NetMsk		; flush the network number
	pjumpn	t1,cpopj1##		; and just return that as the target.
	popj	p,			; just the network number was
					;  on.  not funny.
; subroutine to wait for state to arrive at an established state. ;⊗ EstbWt EstbCl EstbSF EstbTm EstbEr EstbFl EstbW1
;  Established and Close-Wait are both considered established.
; call:
;	move	f,DDB
;	move	t1,<"current" wait state (what we're waiting to get out of)>
;	pushj	p,EstbWt
;	  <return here is failed to get to established state, T1 has state,
;			DDB has been cleared out>
;	<here if in Established or in Close-Wait, T1 has state>

IFWAITS<ENTRY	ESTBWT>
EstbWt:
	pushj	p,StWait		; wait for a change in state.
	caie	t1,S%Estb		; made it to being established?
	 cain	t1,S%ClsW		; or even further: incoming closed?
	  jrst	cpopj1##		; yes.  good return.
	caie	t1,S%SyRA		; are we in SYN received?  (we've
					;   been diverted from SYN sent.)
	 cain	t1,S%SyRP		; either version is ok.
	  jrst	EstbW1			; one or the other.  check again.

	; failed.  decide why before junking the DDB
	jumpl	t1,EstbEr		; ICMP got an error indication.
	MOVE	T2,IMPIOS(F)		; get flags
	jumpe	t1,EstbCl		; closed can't be timeout

	MOVEI	T3,TIMFLG		; timeout flag
	ANDCAM	T3,IMPIOS(F)		; CLEAR TIMFLG
	TRNe	T2,TIMFLG		; CHECK FOR TIMEOUT...
	  JRST	EstbTm			; timeout it is.  return it to
					;  user and non-skip to caller.
	movsi	t3,IODTer		; IO data error?
	tdne	t3,DevIOS(f)		; is that set?
	  jrst	EstbTm			; yes.  this is a timeout
					;  detected by data level (user
					;  timeout).
EstbCl:	txnn	t2,TrgDwn		; target host down?
	  jrst	EstbSF			; no.  some bizarre system failure
	pushj	p,ErrDwn		; target down error
	jrst	EstbFl			; flush DDB, etc.

EstbSF:	pushj	p,ErrSof		; system failure error to user
	jrst	EstbFl			; ditch the DDB and return bad
					;  to caller.

EstbTm:	pushj	p,ErrTim		; report timeout error to user
	jrst	EstbFl			; flush DDB and return to caller

EstbEr:	hrlzs	t1			; get unreachable type in left half
	hrri	t1,errDUR-errLst	; get the proper error code in
					;  the right half.
	pushj	p,ErrSet		; go put the code in place
	; now flush the DDB and return to caller

EstbFl:	scnon				; interrupts to DDBFls
	pushj	p,DDBFls##		; zap buffers
	scnoff				; reset interrupts as expected
	pjrst	DDBRel##		; return DDB to free pool.

EstbW1:
	MOVEI	T3,TIMFLG		; get the time out flag
	ANDCAM	T3,IMPIOS(F)		; make sure it's cleared.
	jrst	EstbWt			; yes.  wait to leave that state.

;SUBROUTINE TO WAIT FOR NCP ACTIVITY. ;⊗ StWait StWai1 StWai2
;CALL:
;  WAITS FOR A CHANGE IN THE STATE.  IT IS UP TO THE CALLING
;  ROUTINE TO DETERMINE IF THE NEW CODE IS PROPER.
;	MOVE	T1,STATE CODE
;	MOVE	F,DDB ADDRESS
;	PUSHJ	P,StWait
;	RETURN HERE WITH NEW STATE IN T1
StWait:	HRLM	T1,(P)		;SAVE THE CODE
StWai1:	MOVSI	S,StatWT	; waiting for a change of state
	IORM	S,IMPIOS(F)	;SET IO ACTIVE
	IORB	S,DEVIOS(F)	;COPY FOR DEVIOS
	HLRZ	T2,(P)		;GET TEST CODE
	CAmE	T2,State(f)		; correct state?
	  JRST	StWai2		;NO.  we're done.
	MOVEI	T1,TIMFLG	;TIMED OUT?
	TDNE	T1,IMPIOS(F)
	  JRST	StWai2		;YES.
	LDB	T1,PUUTIM	;GET USER WAIT CODE
	CAIGE	T1,1		;NULL?
	  MOVEI	T1,3		;YES--DEFAULT (30 SECONDS)
	PUSHJ	P,IMPWAT##	;WAIT
	ScnOff
	JRST	StWai1		;TRY AGAIN

;HERE IF WAIT SATISFIED
StWai2:	PUSHJ	P,IMPWK1##	;CLEAR FLAGS
	TLNE	p4,UU.INT	;INTERRUPTS ALLOWED?
	  ScnOn			;YES
	move	t1,State(f)		; get state
	popj	p,			; and return
;SUBROUTINE TO SET TCP state WAIT DONE.  CALLED AT INTERRUPT LEVEL. ;⊗ TCPIOD
;  CLOBBERS T1.   SAVES ALL OTHER ACS.
;CALL:
;	MOVE	F,[DATA BLOCK ADDRESS]
;	PUSHJ	P,NCPIOD
;	ALWAYS RETURNS HERE
TCPIOD:	movsi	t1,StatWt	; state wait bit.
	TDNN	T1,IMPIOS(F) 	;WAITING?
	  POPJ	P,		;NO
	PJRST	IMPWAK##	;WAKE THE JOB
	$low ;⊗ SktNum

; storage i need

SktNum:	0		; number of last free port assigned.


	$high
	$LIT
	END