Google
 

Trailing-Edge - PDP-10 Archives - bb-ev83b-bm - tcpip-sources/tcpsim.mac
There are 2 other files named tcpsim.mac in the archive. Click here to see a list.
SUBTTL	TCP Simulation Definitions - Full Duplex, Serially Reusable

IFN TCPF,<			; TCP Support Routines

; The TCP simulation routines require the following JSYS redefinitions
; for JSYSi to be simulated

REPEAT 0,<
	OPDEF	$GTJFN	[CALL TCPGJF]
	OPDEF	$OPENF	[CALL TCPOPN]
	OPDEF	$BIN	[CALL TCPBIN]
	OPDEF	$BOUT	[CALL TCPBOU]
	OPDEF	$SIN	[CALL TCPSIN]
	OPDEF	$SOUT	[CALL TCPSOU]
	OPDEF	$CLOSF	[CALL TCPCLO]
	OPDEF	$MTOPR	[CALL TCPMTP]
	OPDEF	$GTSTS	[CALL TCPGST]
	OPDEF	$JFNS	[CALL TCPJFS]
	OPDEF	$RFBSZ	[CALL TCPRBS]
	OPDEF	$SFBSZ	[CALL TCPSBS]
;	OPDEF	$CFIBF	[CALL TCPCIB]
;	OPDEF	$CFOBF	[CALL TCPCOB]
> ; End of REPEAT 0

; User defined sizes -- these are the defaults if the user has not
; previously specified them. If only one connection is to be used,
; TCPFB may be left undefined (or 0) and all storage for the
; connection will be allocated herein.  If multiple connections
; are to be used, TCPFB should be set to 1 (no storage will be
; allocated herein) and the user must allocate storage (a "nameCON"
; and "nameBUF" per connection, and a single "JFNTXS");  the macro
; TCPFIL may be used with a "NAME" of five or fewer characters.
; Note that TCPFIL must not be invoked below location 200.
; Note that the number of buffers per connection per direction
; (T.NDBF) and the size (words) of each buffer (T.BFSZ) should
; be specified before TCPFIL is invoked.

; The simulated GTJFN and OPENF have "non-standard" parameters,
; see the comments at the beginning of TCPGJF: and TCPOPN:,
; respectively.


IFNDEF T.NDBF,<T.NDBF==4>	; # of data buffers for each direction
IFNDEF T.BFSZ,<T.BFSZ==200>	; # of words in each

IFNDEF TCPFB,<TCPFB==0>		; 0 - use internal buffers
				; 1 - User will supply buffers
IFNDEF TCPOLD,<TCPOLD==0>	; Non-zero for old connection descriptor fmt

IFNDEF RXTMOT,<RXTMOT==^D120>	; Transmission timeout
IFNDEF RXPARS,<RXPARS==0>	; Standard algorithm

; Flags in LH of .TCPBF

;TCPMSK==TCP%ER!TCP%LE!TCP%PE!TCP%EC	; Mask of condition code bits

MSKSTR	(TCP$EC,.TCPBF,TCP%EC)	; Extract error code from buffer header
MSKSTR	(T$BSZ,0,OF%BSZ)

; Define offsets in (internal) connection file status block (nameCON:)

T.JFN==0			; Pseudo JFN (Address of this location)
T.CDB==T.JFN+1			; Connection descriptor block
; Above are user interface, below are internal to simulation routines
T.JCN==T.CDB+.TCPCS		; JCN of this connection, if open, or 0
T.BSZO==T.JCN+1			; Data buffer size, octets (constant)
T.BSZ==T.BSZO+1			; Simulated byte size (same for I & O)
T.STS==T.BSZ+1			; Simulated file status (combined)
  xx=GS%OPN			; Connection is open
  xx=GS%RDF			; Open for reading
  xx=GS%WRF			; Open for writing
  xx=GS%EOF			; Received FIN
  xx=GS%ERR			; Error detected

T.CUR==T.STS+1	; I & O		; Address of current buffer header
T.CNT==T.CUR+2	; I & O		; Bytes in current buffer
T.PTR==T.CNT+2	; I & O		; Byte pointer to current buffer
T.SVC==T.PTR+2	; I & O		; Saved count (TPC)
T.SVD==T.SVC+2	; I & O		; Saved data (TPD)

T.HDR==T.SVD+2	; I then O	; Ring headers begin here
T.SIZE==T.HDR			; Size of block exclusive of headers


DEFINE TCPFIL	(NAME),<
NAME'CON: BLOCK T.SIZE		; File control block followed by
	BLOCK 2*T.NDBF*.TCPBS	; Buffer headers
NAME'BUF: BLOCK 2*T.BFSZ*T.NDBF	; Data buffers
IFNDEF JFNTXS,<JFNTXS:BLOCK 60>	; Data area for JFNS simulation
IF2 <IFE JFNTXS-.,<   BLOCK 60>> ; If it was here on pass1, here on 2
> ; End of DEFINE TCPFIL

IFE TCPFB,<TCPFIL DAT>		; Use internal buffers

; Internal Registers	;*****	Beware Order BIN/SIN	BOUT/SOUT
TIB==5			;***	;	     Data byte	Temp
TPD==6			;***	;	     - TCP packed data -
TOB==7			;***	;	     Temp	Data byte
TPC==10				; TPD bits   # left	# left
				; (32 to 0)  to read	to fill
TBC==11				; # Bits in  TIB	TOB
				; (T.BSZ to 0)
TFSB==12			; Points to T.JFN
TAC0==13	; *** LAST	; Points to "saved" AC0
SUBTTL	TCP Simulation - TCPGJF - Funny GTJFN

;IFE TCPFB			; Use internal buffers

; Set host address(es) and port(s) into connection block at
;		nameCON+T.CDB
; A/	0
; C/	TCP OPEN flags,,0	; Flags: TCP%FS, TCP%WT
;	$GTJFN (CALL TCPGJF)	; Simulate GTJFN & create connection
;Ret+1:	  error
;Ret+2:	OK


; or


;IFN TCPFB			; User supplies buffers

; Set host address(es) and port(s) into connection block at
;		nameCON+T.CDB
; A/	0,,Address of pseudo JFN variable (nameCON)
; B/	-#Buffers (-T.NDBF),,Location of buffers (nameBUF)
; C/	TCP OPEN flags,,Size of each buffer (T.BFSZ)
;	$GTJFN (CALL TCPGJF)	; Simulate GTJFN & create connection
;Ret+1:	  error
;Ret+2:	OK


TCPGJF:	TXNE A,GJ%SHT		; Decide if real or TCP call
	  JRST [GTJFN		; GJ%SHT set means real
		 SKIPA		; Error return
		  AOS (P)	; Ok return
		RET]

IFE TCPFB,<			; Use internal buffers
	MOVEI A,DATCON		; Connection data header address
	MOVE B,[-T.NDBF,,DATBUF] ; # & address of data buffers
	HRRI C,T.BFSZ		; Size of each buffer (words)
> ; End of TCPFB

	CALL TCPINI		; Save things, enter simulation context
SUBTTL TCP Simulation - TCPGJF, cont. - Initialize file conn. info.

	SKIPLE T.JFN(TFSB)	; Already been here?
	  JRST TCPGJI		; Yes

	SETZM T.JCN+1(TFSB)	; Zero nameCON & buffer headers
	HRRI D,T.JCN+1+1(TFSB)	; Build BLT word
	HRLI D,T.JCN+1(TFSB)

	HLRE TIB,B		; Find last buffer header end address
	MOVNS TIB		; # buffer headers, per direction
	ASH TIB,1		; Total, both directions
	IMULI TIB,.TCPBS	; Total words
	ADDI TIB,T.SIZE-1(TFSB)	; End address

	BLT D,(TIB)		; Clear everything

	HRRZ TBC,C		; Buffer size, words
	ASH TBC,2 		; Get size in (8-bit) bytes
	MOVEM TBC,T.BSZO(TFSB)	; Store for later use

	MOVEI D,T.HDR(TFSB)	; Location of first input ring header
	TXO D,TCP%DN		; Buffer belongs to program
	HRRI TPC,(TFSB)		; First do Input side
	HRLI TPC,-2		; Input, then output
	MOVEM B,TIB		; Save AOBJN count in left half

INIRNG:	HRRZM D,T.CUR(TPC)	; First buffer header
	HRRZM B,T.PTR(TPC)	; First data buffer for pointer
	HLL B,TIB		; Count of buffers in ring

INIRNL:	HLLZM D,.TCPBF(D)	; TCP%DN
	HRRZM B,.TCPBA(D)	; Set data buffer address into header
	MOVEM TBC,.TCPBC(D)	; Set byte count

	ADDI D,.TCPBS		; Step to next buffer header
	ADDI B,-1(C)		; Step to next data buffer (-1 for AOBJN)

	HRRM D,-.TCPBS(D)	; Point previous header to this one
	AOBJN B,INIRNL		; Loop thru all buffers

	MOVE TOB,T.CUR(TPC)	; First buffer header
	HRRM TOB,-.TCPBS(D)	; Close ring

	AOBJN TPC,INIRNG	; Back for output

	HRLI B,(POINT 32)	; Construct 4-byte pointer
	HLLM B,T.PTR(TFSB)	; To Input
	HLLM B,T.PTR+1(TFSB)	; To Output

TCPGJI:	HRRZM A,T.JFN(TFSB)	; Initialized (Pseudo JFN)
SUBTTL TCP Simulation - TCPGJN, cont., Establish the connection

	MOVEI A,T.CDB(TFSB)	; Connection descriptor block
IFN TCPOLD,<SKIPE TENEX		; TENEX has old CDB format
	      ADDI A,1>		; so adjust pointer accordingly
	HLL A,C(TAC0)		; Flags
	MOVX B,RXTMOT		; Retransmission timeout
	MOVX C,RXPARS		; Retransmission parameters
	OPEN
	  JRST TCPGJX
	MOVEM A,T.JCN(TFSB)	; Save JCN
	MOVX A,GS%OPN
	MOVEM A,T.STS(TFSB)	; Flag open
	AOS -2(TAC0)		; Skip return
	HRRZM TFSB,A(TAC0)	; To be returned
	SETZB TPD,TPC		; No packing state to be saved
	RET			; Go leave simulation context


; OPEN failed, analyze reason & ABORT JCN

TCPGJX:	HRRZ C,A		; Error code
	ANDI C,37		; Just error code
	HLRZS A			; Possible JCN

	JUMPE A,TCPGJY		; No JCN
	TXO A,TCP%JS
	ABORT			; Abort that JCN
	  JFCL
TCPGJY:
	SETOM T.JFN(TFSB)	; No useful connection

	MOVX B,GJFX24		; Default error code
	CAIN C,^D1		; Argument error
	  MOVX B,GJFX40		; "Undefined attribute"
	CAIN C,^D4		; Resource shortage
	  MOVX B,GJFX22		; "Insufficient resources"
	CAIN C,^D5		; Wild FH/FP only allowed if listening
	  MOVX B,GJFX31		; "Invalid wildcard"
	CAIN C,^D6		; Connection already exists (error??)
	  MOVX B,GJFX27		; "File already exists"
	CAIN C,^D7		; Rejected/reset
	  MOVX B,OPNX21		; "Connection refused"
	CAIN C,^D9		; Transmission timeout
	  MOVX B,NSPX17		; "Process aborted, timed out, or..."
	MOVEM B,A(TAC0)		; To be returned

	CALL SETERR		; Set process error code
	SETZB TPD,TPC
	RET
SUBTTL TCP Simulation - TCPOPN - Funny OPENF

; A/	nameCON
; B/	FLD(n,OF%BSZ)!OF%RD!OF%WR
;	CALL TCPOPN
;Ret+1:	  error
;Ret+2:	OK


TCPOPN:	TRNN A,777600		; JFN or TCP connection?
	  JRST [OPENF		; JFN
		 SKIPA		; Error return
		  AOS (P)	; Skip return
		RET]

	PUSH P,C		; Save a register

	MOVX C,DESX3		; "JFN is not assigned"
	SKIPLE T.JFN(A)		; Initialized? and
	 SKIPN T.JCN(A)		; Connection still open?
	  JRST TCPOPE		; No, error

	MOVE C,T.STS(A)		; Current status

	TXNE B,OF%RD		; Process READ bit
	 CALL TCPRED

	TXNE B,OF%WR!OF%APP	; Process WRITE bit
	 CALL TCPWRT

	MOVEM C,T.STS(A)	; Update status

	LOAD C,T$BSZ,+B		; Get byte size
	SKIPN C			; Zero means 36
	 MOVX C,^D36
	CAIL C,1		; Olny allow 1
	 CAILE C,^D36		; to 36
	  JRST TCPOPB		; Invalid size

	MOVEM C,T.BSZ(A)	; Set new size

	AOS -1(P)		; Skip return
	JRST TCPOPX

; Open errors

TCPOPB:	MOVX B,SFBSX2		; "Invalid byte size"
TCPOPE:	MOVX A,<.FHSLF>
	MOVE B,C		; Error code
	CALL SETERR		; Set process error code
	MOVE A,B		; Return code too
TCPOPX:
	POP P,C
	RET
SUBTTL TCP Simulation - TCPOPN - Transfer direction processing


TCPRED:	TXON C,GS%RDF		; Already reading?
	  RET			; No
				; Yes, do it again
	SETZM T.SVC(A)		; No partial word
	SETZM T.CNT(A)		; No more data in buffer
	RET


TCPWRT:	TXON C,GS%WRF		; Already writing?
	  RET			; No
				; Yes, do it again
	RET			; Should have done a PUSH to get
				; things synced
	SETZM T.SVC+1(A)	; No partial word
	SETZM T.CNT+1(A)	; No more data in buffer
	RET
SUBTTL TCP Simulation - TCPBIN - Simulate BIN

TCPBIN:	TRNN A,777600		; JFN or TCP connection?
	  JRST [BIN		; JFN
		ERCAL TERJMI
		RET]

	SETZ B,			; Return NULL byte on error
	SKIPN T.JCN(A)		; Connection ok?
	  CALL TERJMI	;RET	; Illegal inst. ;No, return null byte

	CALL TCPINI		; Save state

	MOVX D,^D<60*60>	; Give up after an hour

; Simulate BIN by SIN of 1 byte to saved B on stack

	MOVE TBC,T.BSZ(TFSB)	; User byte size
	MOVEI B,B(TAC0)		; Build byte pointer to saved B
	DPB TBC,[POINT 6,B,5]	; Set byte position
	DPB TBC,[POINT 6,B,11]	; Set byte size

	MOVNI C,1		; Want one byte there

	CALLRET TCPBI		; TCP to user
SUBTTL TCP Simulation - TCPSIN - Simulate SIN

; Only C .LT. 0 case is simulated

TCPSIN:	TRNN A,777600		; JFN or TCP connection?
	  JRST [SIN		; JFN
		ERCAL TERJMP
		RET]

	SKIPGE C 		; Only neg count SIN supported
	 SKIPN T.JCN(A)		; Still open?
	  CALL TERJMP	; or I	; No, do nothing

	TLC B,777777
	TLCN B,777777
	  HRLI B,(POINT 7,0)

	CALL TCPINI		; Save state

	MOVX D,^D<60*60>	; Give up after an hour

	CALL TCPBI		; TCP to user

	MOVEM B,B(TAC0)		; B to be returned
	MOVEM C,C(TAC0)		; C too

	SETZ C,
	IDPB C,B		; End string with NULL

	RET			; To save/restore state
SUBTTL TCP Simulation - TCPBI - Unpack TCP into arbitrary size bytes

;  B/ Byte pointer,  C/ - byte count,  D/ Timeout, sec, TFSB
;		TIB			TPD
;	+----------------------+------------------+
;	!           0          !<----v---->! xxxx !
;	+----------------------+-----!------------+
;		TBC/ (T.BSZ)    TPC/ n
;
;	+----------------------+------------------+
;	!      0     !<---v--->!<--v-->! xxxxxxxx !
;	+-----------------!----+---!--------------+
;	      TBC/ (T.BSZ)-a  TPC/ n-a  where a=min(n,(T.BSZ))

TCPBI:	SETZ TIB,		; Clear user byte
	MOVE TBC,T.BSZ(TFSB)	; User byte size
	JUMPLE TPC,TCPBI3	; Need bits to unpack

TCPBI2:	CAML TPC,TBC		; Able to fill user byte?
	  JRST TCPBI6		; Yes, TPC .ge. TBC
	LSHC TIB,(TPC)		; Empty 32-bit word
	SUB TBC,TPC		; Bits still needed in byte (.GT.0)

TCPBI3:	SKIPLE TPC,T.CNT(TFSB)	; TCP bytes left in buffer
	  JRST TCPBI4		; Have some
	CALL GETBUF		; None, get another buffer
	MOVE TOB,T.STS(TFSB) 	; Get status bits
	TXNN TOB,GS%EOF+GS%ERR	; End of file? (or error)
	  JRST TCPBI3		; No, go get its actual byte count
	SETZ TPD,		; Yes, return NULL
	JRST TCPBI5

; Get next (4) bytes from TCP
TCPBI4:	SUBI TPC,4		; Assume 4 TCP bytes in buffer
	MOVEM TPC,T.CNT(TFSB)	; Update count remaining
	ILDB TPD,T.PTR(TFSB)	; Get 32-bits
	LSH TPD,4		; Left justify
	SKIPLE TPC		; Get all 4 TCP bytes?
TCPBI5:	  SETZ TPC,		; Yes
	ADDI TPC,4		; # There
	LSH TPC,^D3		; # bits therein

	JRST TCPBI2		; Go finish byte

; Have enough packed data to complete a user byte
TCPBI6:	LSHC TIB,(TBC)		; Complete byte
	SUB TPC,TBC		; Bits left in 32-bit word

	IDPB TIB,B		; Byte to user

	MOVE TOB,T.STS(TFSB)	; Get status
	TXNN TOB,GS%EOF+GS%ERR	; Unless EOF or error
	  AOJN C,TCPBI		; Loop if want another byte
	RET			; Go finish off
SUBTTL TCP Simulation - GETBUF - Get another buffer

; Simulation state - TFSB
;	MOVX D,<Timeout, seconds>
;	CALL GETBUF

GETBUF:	PUSH P,D		; Save registers
	PUSH P,C
	PUSH P,B

	MOVE B,T.CUR(TFSB) 	; Get pointer to current buffer head

; Finished with current buffer, ask for it to be refilled

GETBU0:	MOVE C,T.BSZO(TFSB)	; Reset maximum buffer count
	MOVEM C,.TCPBC(B)	; In buffer header

	MOVE A,T.JCN(TFSB) 	; Get the JCN
	RECV			; Start input on this buffer
	  DPB A,[POINT 8,.TCPBF(B),7] ; Save error code, process later

; Step to next buffer and begin processing it

	HRRZ B,0(B)		; Step to next buffer
	HRRZM B,T.CUR(TFSB)	; Make it be current
GETBU1:	MOVE C,.TCPBF(B)	; Get flags of this new buffer
	TXNN C,TCP%ER!TCP%DN	; Error code or done yet?
	  JRST GETBU2		; No, wait
	TXNE C,TCP%ER		; Error?
	  JRST GETERR		; Yes

; Buffer ok to process, see if it has any data

	MOVE C,T.BSZO(TFSB)	; Total bytes
	SUB C,.TCPBC(B)		; Less bytes not xferred
	JUMPE C,GETBU0		; If none, get next buffer

; Setup to read data from buffer

	MOVEM C,T.CNT(TFSB)	; Set bytes left in current buffer
	MOVE C,.TCPBA(B) 	; Get buffer address
	HRLI C,(POINT 32)
	MOVEM C,T.PTR(TFSB)	; Set byte pointer

	POP P,B
	POP P,C
	POP P,D
	RET
SUBTTL TCP Simulation - GETBUF - Wait for buffer, or error

; Wait for next buffer of data to arrive

GETBU2:	MOVE D,B-D(P)		; Timeout count
	MOVX A,^D1000		; One second
GETBU3:	DISMS			; Wait a second
	MOVE C,.TCPBF(B)	; Get flags of buffer
	TXNN C,TCP%ER!TCP%DN	; Error code or done yet?
	  SOJG D,GETBU3		; No, haven't timedout, wait
	JUMPG D,GETBU1		; If count remains, go look again
				; Waited too long, give up

; Error code returned

GETERR:	MOVE D,T.STS(TFSB)	; Current status

	LOAD C,TCP$EC,(B)	; Extract error code
	CAIE C,^D12		; Is it "Connection Closing"?
	 TXOA D,GS%EOF+GS%ERR	; No, so it is a real error
	  TXO D,GS%EOF		; Closing, so only set EOF bit

;	TXZ D,GS%OPN		; No longer open
	JFCL
	MOVEM D,T.STS(TFSB)	; Save status

	SKIPE A,T.JCN(TFSB)	; Get back JCN of connection
	 ABORT			; And abort it
	  JFCL			; Shouldn't fail...

	SETZM T.JCN(TFSB)	; Closed, no more JCN

	POP P,B
	POP P,C
	POP P,D
	RET
SUBTTL TCP Simulation - TCPBOU - Simulate BOUT for TCP connection

TCPBOU:	TRNN A,777600		; JFN or TCP connection?
	  JRST [BOUT		; JFN
		ERCAL TERJMI
		RET]

	SKIPN T.JCN(A)		; Connection still open?
	  CALL TERJMI		; No.

	CALL TCPINO		; Save state

; Change BOUT to transparent SOUT of 1 byte from TOB

	MOVE TOB,B		; Data
				; Forget pointer will skip into TCPBO
	MOVNI C,1		; One byte

	CALLRET TCPBO1		; User to TCP
SUBTTL TCP Simulation - TCPSOU - Simulate SOUT

; C .LT. 0 case processed directly, others converted to it by scanning


TCPSOU:	TRNN A,777600		; JFN or TCP connection?
	  JRST [SOUT		; JFN
		ERCAL TERJMP
		RET]

	SKIPN T.JCN(A)		; Connection still open?
	  CALL TERJMP	; or I	; No, do nothing

	TLC B,777777
	TLCN B,777777
	  HRLI B,(POINT 7,0)

	CALL TCPINO		; Save state

	JUMPL C,TCPSO4		; Exact count case all set

; Count characters

	MOVNS C			; Positive (max) count into
	HRLZS C			; AOBJN counter
	SKIPN C 		; C.EQ.0 or C was .GT.0
	 TLOA C,770000		; C.EQ.0, big count and zero D
	  SKIPA			; C was .GT.0, now has count, D set
	   SETZ D,		; C.EQ.0 - search til NUL

TCPSO2:	ILDB A,B		; Get next character
	CAME A,D		; What we're looking for?	
	  AOBJN C,TCPSO2	; No, look further

	SKIPGE C		; Stopped by count or character?
	 SKIPN D		; Character, and searching for NUL?
	  SKIPA			; Count, or NUL
	   ADDI C,1		; Non-NUL character should be sent
	MOVNI C,(C)		; Set C to - number of characters

	MOVE B,B(TAC0)		; Restore original pointer
	SKIPN C			; Anything to output?
	  RET			; No characters, done, go save/restore
TCPSO4:

; Have the exact count

	CALL TCPBO		; User to TCP

	MOVEM B,B(TAC0)		; B to be returned
	MOVEM C,C(TAC0)		; C too

	RET			; All done, go save/restore
SUBTTL TCP Simulation - TCPBO - Pack arbitrary bytes into TCP

;  B/ Byte pointer,  C/ Negative byte count, TFSB

;		TPD			TOB
;	+--------------------------+-----------------+
;	!0000!<---v--->!packed data! xxxxx !<---v--->!
;	+---------!----------------+------------!----+
;	     TPC/ n			 T.BSZ/ n

;	+--------------------------+-----------------+
;	!0000!<---v--->!packed data!<---v--->! xxxxx !
;	+---------!----------------+----!------------+
;	     TPC/ n		 TBC/ (T.BSZ)

;	+--------------------------+-----------------+
;	!0000!<-v->!  packed data  !<-v->! xxxxxxxxx !
;	+-------!------------------+--!--------------+
;	  TPC/ n-a	     TBC/ (T.BSZ)-a  where a=min(n,(T.BSZ))


TCPBO:	ILDB TOB,B		; Get next user data byte

TCPBO1:	MOVE TBC,T.BSZ(TFSB)	; User byte size
	MOVN TIB,TBC		; For left-justify
	LSH TOB,^D36(TIB)	; Drop unused bits on left

	JUMPLE TPC,TCPBO4	; Need word to pack
TCPBO2:	CAMLE TPC,TBC		; Able to fill 32-bit word?
	  JRST TCPBO6		; No
	LSHC TPD,(TPC)		; Fill 32-bit word
	SUB TBC,TPC		; Bits left in byte for next word
	MOVE TIB,T.CNT+1(TFSB)	; Bytes used in output buffer

	CAML TIB,T.BSZO(TFSB)	; Buffer full?
	  CALL SNDBUF		; Yes, get another

	IDPB TPD,T.PTR+1(TFSB)	; 4 TCP bytes into send buffer
	MOVX TIB,4
	ADDM TIB,T.CNT+1(TFSB)	; Into buffer
;	SETZ TPD,
TCPBO4:	MOVEI TPC,^D32		; 4 empty TCP bytes
	JUMPN TBC,TCPBO2	; Back if more bits in user byte
	JRST TCPBO8		; Done with this byte, see if another


TCPBO6:	LSHC TPD,(TBC)		; Finish byte
	SUB TPC,TBC		; Room left in 32-bit word
;	SETZ TBC,

TCPBO8:	AOJN C,TCPBO		; Loop if more bytes to process?

	RET
SUBTTL TCP Simulation - SNDBUF - Send a buffer and set up the next one

; Simulation state - TFSB
;	CALL SNDBUF

SNDBUF:	PUSH P,D
	PUSH P,C
	PUSH P,B

	MOVE B,T.CUR+1(TFSB) 	; Pointer to current buffer head
	MOVE C,T.CNT+1(TFSB) 	; Get # of bytes in buffer to be sent
	MOVEM C,.TCPBC(B)	; Into buffer header

SNDBU1:	SKIPN A,T.JCN(TFSB) 	; Get JCN
	  JRST SNDERR
	MOVX C,RXTMOT		; Time out in 2 minutes
	MOVX D,RXPARS 		; Standard rexmit
	SEND			; Send the buffer
	  JRST [ANDI A,40!37	; Error code
		CAIE A,00+^D4	; Temporary resource shortage?
		  JRST SNDERR	; No.  Send error
		MOVX A,^D1000
		DISMS
		JRST SNDBU1]	; Try again

; Move to next buffer, make sure last send completed ok

	MOVE B,.TCPBF(B)	; Link to next buffer header
	HRRZM B,T.CUR+1(TFSB)	; Make it current
SNDBU2:	MOVE C,.TCPBF(B)	; Get (old) flags of this buffer
	TXNE C,TCP%ER		; Error?
	  JRST SNDERR		; Yes, handle that
	TXNN C,TCP%DN		; Done?
	  JRST SNDBU3		; No, have to wait

; Last send completed ok, setup pointers & zero count before filling it

	HRRZS .TCPBF(B)		; Clear all flags for next time
	MOVE C,.TCPBA(B) 	; Get data buffer address
	HRLI C,(POINT 32)
	MOVEM C,T.PTR+1(TFSB)	; Write pointer
	SETZM T.CNT+1(TFSB)	; Empty buffer

	POP P,B
	POP P,C
	POP P,D
	RET
SUBTTL TCP Simulation - SNDBUF - Wait for data to be sent, or error


; Wait til buffer has been sent (window must be zero)

SNDBU3:	MOVX A,^D1000
	DISMS
	JRST SNDBU2


SNDERR:	SKIPE A,T.JCN(TFSB)	; Get JCN
	 ABORT			; Reset connection
	  JFCL

	SETZM T.JCN(TFSB)	; Connection closed, JCN gone
	MOVX C,GS%ERR		; Remember error
	MOVEM C,T.STS(TFSB)

	POP P,B
	POP P,C
	POP P,D
	RET
SUBTTL TCP Simulation - TCPRBS, TCPSBS - Simulate RFBSZ and SFBSZ

TCPRBS:	TRNN A,777600		; JFN or TCP connection?
	  JRST [RFBSZ		; JFN
		 SKIPA		; Error return
		  AOS (P)	; Skip return
		RET]
	PUSH P,B
	SKIPN T.JCN(A)		; Open?
	  JRST $DESX3
	MOVE B,T.STS(A)
	TXNN B,GS%OPN
	  JRST $DESX5

	MOVE B,T.BSZ(A)		; Current size
	MOVEM B,(P)		; To be returned
	JRST TCPXBX		; Leave

TCPSBS:	TRNN A,777600		; JFN or TCP connection?
	  JRST [SFBSZ		; JFN
		 SKIPA		; Error return
		  AOS (P)	; Skip return
		RET]
	PUSH P,B
	SKIPN T.JCN(A)		; Open?
	  JRST $DESX3
	MOVE B,T.STS(A)
	TXNN B,GS%OPN
	  JRST $DESX5

	TXC B,GS%RDF!GS%WRF
	TXCN B,GS%RDF!GS%WRF
	  JRST $SFBX1		; Should be one or other

	SKIPN B,(P)		; Desired size
	  MOVX B,^D36		; Zero is a word
	CAIL B,1		; Better be 1-36
	 CAILE B,^D36
	  JRST $SFBX2		; Or error
	MOVEM B,T.BSZ(A)	; Set new size

TCPXBX:	AOS -1(P)		; Skip return
	POP P,B
	RET

$SFBX1:	SKIPA A,[SFBSX1]
$SFBX2:	  MOVX A,SFBSX2
	JRST TCPXBZ

$DESX3:	SKIPA A,[DESX3]
$DESX5:	  MOVX A,DESX5
TCPXBZ:	POP P,B
	RET			; Error return
SUBTTL TCP Simulation - TCPGST, TCPMTP - Simulate GTSTS and MTOPR

TCPGST:	TRNN A,777600		; JFN or TCP connection?
	  JRST [GTSTS		; JFN
		ERCAL TERJMP
		RET]
	MOVE B,T.STS(A)		; The status word
	SKIPE T.JCN(A)		; Connection ok?
	 TXOA B,GS%OPN		; Yes, say open
	  TXZ B,GS%OPN		; No, say not open
	RET


; Simulate MTOPR for a TCP connection

TCPMTP:	TRNN A,777600		; JFN or TCP connection?
	  JRST [MTOPR		; JFN
		ERCAL TERJMI
		RET]
	SKIPE T.JCN(A)		; Connection ok?
	 CAIE B,.MOSND		; Only MTOPR 21 is simulated
	  RET			; Ignore others
	CALL TCPINO		; Get output state

TCPSNL:	MOVE C,T.CNT+1(TFSB)	; See if buffer is full
	CAME C,T.BSZO(TFSB)
	  JRST TCPSN2		; No, so any partial byte will fit in it

	CAIE TPC,0		; Have any data bits beyond full buffer
	 CAIL TPC,^D32		; Empty count
	  JRST TCPSN4		; No, just send (full) buffer

	CALL SNDBUF		; First send full buffer
TCPSN2:
	CAIE TPC,0		; Any remaining bits?
	 CAIL TPC,^D32		; Empty count
	  JRST TCPSN4		; No, all set

	LSH TPD,(TPC)		; Pack data left
	IDPB TPD,T.PTR+1(TFSB)	; Into TCP buffer

	MOVNS TPC
	ADDI TPC,^D32+7		; Bits used + 7
	LSH TPC,-3		; Bytes used
	ADDM TPC,T.CNT+1(TFSB)	; In TCP remaining bits
	SETZB TPC,TPD		; No bits left
TCPSN4:
	MOVE B,T.CUR+1(TFSB) 	; Get pointer to current buffer
	MOVX C,TCP%PU
	IORM C,.TCPBF(B)	; Mark the buffer as PUSH
	CALLRET SNDBUF		; Send it
SUBTTL TCP Simulation - TCPCLO/CLOSET - Simulate CLOSF

; A/	(CO%WCL!)nameCON

TCPCLO:	TRNN A,777600		; JFN or TCP connection?
	  JRST [CLOSF		; JFN
		 SKIPA
		  AOS (P)	; Skip return
		RET]


	SKIPN T.JCN(A)		; Be sure it's still open
	  RET			; Already closed or aborted

	CALL TCPINO		; Get output state

	MOVX TOB,1		; Assume no errors, skip return

	SKIPN T.CNT+1(TFSB)	; Have any data
	 SKIPE T.SVC+1(TFSB)	; or bits?
	  SKIPA			; Yes, send them
	   JRST TCPCLS		; No, just close

	MOVE B,T.STS(TFSB) 	; Get status
	TXNE B,GS%WRF	 	; Sending?
	  CALL TCPSNL		; Send final buffer if any
TCPCLS:				; TPC and TPD should be zero here
	MOVE A,T.JCN(TFSB)
;	MOVE B,A(TAC0)		; Flags in left half
;	TXNE B,CO%WCL		; Wait?
;	 TXO A,TCP%WT		; Yes
	CLOSE			; Tell other end to close it up
	  SETZ TOB,		; Error return

	MOVX D,<<RXTMOT*2+10>/10> ; Short timeout for GETBUF here
	MOVX B,<-^D8,,0>	; Allow 8 buffers
TCLOOP:	CALL GETBUF		; Start input
	SKIPN A,T.JCN(TFSB)	; Is it closed yet?
	  JRST TCXT		; Yes, done
	AOJN B,TCLOOP		; No, try next buffer

	ABORT			; Tried all 10, give up
	  SETZ TOB,		; Error return
TCXT:	SETZB TPC,TPD		; Nothing remaining

	MOVX B,GS%OPN
	ANDCAM B,T.STS(TFSB)	; No longer open
	SETZM T.JCN(TFSB)	; No JCN either

	ADDM TOB,-2(TAC0)	; Possible skip return
	RET			; Go save/restore state
SUBTTL TCP Simulation - TCPJFS - Simulate JFNS

TCPJFS:	TRNN A,777600		; JFN or TCP connection?
	  JRST [JFNS		; JFN
		  ERCAL TERJMI
		RET]
	PUSH P,A
	HRROI A,JFNTXS
	JFNS
	  ERJMP TERJMA
	POP P,A
	PUSH P,B
	PUSH P,C
	HRROI B,JFNTXS
	SETZ C,
	$SOUT
	POP P,C
	POP P,B
	RET

; Simulate error return to ERJMP/ERCAL

TERJMA:	MOVE A,(P)		; Restore A & leave garbage
TERJMP:	SETZM (P)		; Return if neither
TERJMI:	PUSH P,A		; Get a reg, ITRAP if neither
	HLRZ A,@-2(P)		; Get instruction at return address
	CAIN A,(ERJMP)
	  JRST TEJMP
	CAIN A,(ERCAL)
	  JRST TECAL
	SKIPE -1(P)		; Neither, what to do
	  0			; Make illegal instruction
	JRST TEXIT		; Continue


TECAL:	MOVE A,@-2(P)		; ERCAL instruction
	HLL A,-2(P)		; Fake return to get there (user flags)
	AOS -2(P)		; Return from ERCAL
	PUSH P,(P)		; Move saved A down
	MOVEM A,-2(P)		; Fake return to get there
	JRST TEXIT

TEJMP:	MOVE A,@-2(P)		; ERJMP instruction
TERET:	HRRM A,-2(P)		; Fake return to ERJMP
TEXIT:	POP P,A			; Restore A
	POP P,(P)		; Drop flag
	RET


SETERR:	MOVX A,<.FHSLF>
	SETER			; Set system error message for ERSTR
	ERJMP .+1		; TENEX does skip return, TOPS20 doesn't
	RET
SUBTTL TCP Simulation - TCPINI, TCPINO - Enter/Exit Simulation Context

;	Return from simulated instruction
; P---)	Return from CALL TCPINx
; Becomes
;	Return from simulated instruction
;	Return from CALL TCPINx
;	I(0)/O(1) flag
;	Saved A-TAC0
; P---)	Return to restore code


TCPINI:	PUSH P,[0]		; Input entry
	SKIPA
TCPINO:	  PUSH P,[1]		; Output entry

TNR==TAC0-A+1			; Number of registers saved

	ADD P,[TNR,,TNR]	; Reserve room on stack
	MOVEM TAC0,(P)		; Save last
	HRLI TAC0,TAC0-TNR+1	; First register
	HRRI TAC0,1-TNR(P)	; Address for first
	BLT TAC0,-1(P)		; Save registers

	MOVEI TAC0,-TAC0(P)	; (Pseudo) saved AC0

	HRRZI TFSB,(A)		; Address of T.JFN
	ADD TFSB,-TNR(P)	; Bump by one if Output
	MOVE TPC,T.SVC(TFSB)	; Restore last state
	MOVE TPD,T.SVD(TFSB)
	SUB TFSB,-TNR(P)	; Point to T.JFN

	CALL @-1-TNR(P)		; Continue

	ADD TFSB,-TNR(P)	; Bump by one if Output
	MOVEM TPC,T.SVC(TFSB)	; And bits left in it
	MOVEM TPD,T.SVD(TFSB)	; Save partial 32-bit word
;	SUB TFSB,-TNR(P)	; Point to T.JFN

	HRLI TAC0,1-TNR(P)	; Restore registers
	HRRI TAC0,1+TAC0-TNR
	BLT TAC0,TAC0

	SUB P,[TNR+1+1,,TNR+1+1] ; Remove registers & flag & entry PC
	RET			; Back to user after simulation

TLITS:;	LIT
	XLIST
	LIT
	LIST

> ; End of IFN TCPF conditional
	SUBTTL