;<PUP>PUPSEQ.MAC.50, 15-Dec-82 12:50:09, Edit by SCHOEN ; Open server socket in 8-bit mode (for MEIS people) ;<PUP>PUPSEQ.MAC.49, 2-Nov-82 20:44:38, Edit by SCHOEN ; Don't crash the server if PUPO fails at SNDPP1 ;<PUP>PUPSEQ.MAC.48, 26-Oct-82 16:46:42, Edit by SCHOEN ; Zero PUPFSK(CX) if connection can't be created at RNTS4C. Check for ; non-zero CONTAB(CX) before declaring a connection found at RNTS4F ;<PUP>PUPSEQ.MAC.45, 19-Oct-82 09:52:36, Edit by SCHOEN ; Try to create server fork before assigning buffers ; Start server fork only after all buffers have been assigned ; Forget about LFINIT table ; Clear sequin block before making fork ; Destroy timed out connections which are not logged in ;<SCHOEN.LEAF>PUPSEQ.MAC.3, 31-Jul-82 12:06:11, Edit by SCHOEN ; Catch PMAP failure in MAKFRK ;<PUP>PUPSEQ.MAC.35, 29-Jun-82 21:17:01, Edit by SCHOEN ; Destroy timed out connections which had been dallying ;<PUP>PUPSEQ.MAC.34, 10-Jun-82 12:31:54, Edit by SCHOEN ; Let OS do unmapping of a fork's pages in KILLFK ;<PUP>PUPSEQ.MAC.33, 15-May-82 19:37:13, Edit by SCHOEN ; Zero last locker BEFORE freeing lock in UNLOCK ;<PUP>PUPSEQ.MAC.28, 27-Apr-82 22:35:26, Edit by SCHOEN ; Don't resignal server fork on receipt of duplicate packet ; Make sure LEAFFK is handled as two half-words ; One more time -- filter out non-sequins at RNTSV4 ; Rework queues; dispense with PP queue ; SENSEQ sends data immediately, then queues it for retransmission on TX queue ; Clean up RELSEQ (correct some 8-bit arithmetic) ; Don't empty TX queue at BNTSV2 ; Replace SNDQ with SNDPUP ; Don't gratuitously send contents of TX queue at BNTSRV ;<PUP>PUPSEQ.MAC.22, 25-Apr-82 15:00:43, Edit by SCHOEN ; Go NOINT in SNDQ ; Lock BNTLCK only while in SNDQ ; Check for non-sequin packets at beginning of HDLSEQ (not HDLSQ3) ;<PUP>PUPSEQ.MAC.21, 23-Apr-82 12:07:02, Edit by SCHOEN ; Unlock queue lock words if dismissed waiting for queue to empty ;<PUP>PUPSEQ.MAC.20, 16-Apr-82 14:02:26, Edit by SCHOEN ; Improve handling of SEQOUT ; Remove routine to wait for lower fork init (doesn't work well on Tops-20) ; Instead, call SGLEAF when dropping duplicate packets ;<PUP>PUPSEQ.MAC.18, 11-Apr-82 22:32:42, Edit by SCHOEN ; Add routine to wait for server fork to initialize ;<PUP>PUPSEQ.MAC.17, 11-Apr-82 21:42:50, Edit by SCHOEN ; Make some tables global ;<PUP>PUPSEQ.MAC.16, 11-Apr-82 21:23:16, Edit by SCHOEN ; Log non-sequin pups being discarded at HDLSQ3 ;<PUP-TEMP>PUPSEQ.MAC.15, 11-Apr-82 00:34:05, Edit by SCHOEN ; Remove extraneous POP P,(P) from fail returns in SEQINI ;<PUP>PUPSEQ.MAC.14, 10-Apr-82 23:29:32, Edit by SCHOEN ; Stop treating SEQOUT as halfword -- count user's outstanding packets only ;<PUP>PUPSEQ.MAC.13, 10-Apr-82 20:51:28, Edit by SCHOEN ; PSQVAR, PSQPVR -> TOPVAR, TOPPVR so PUPUUO loads correctly ;<PUP>PUPSEQ.MAC.11, 6-Apr-82 12:39:22, Edit by SCHOEN ; Send SequinBroken on receipt of data Sequin for nonexistent connection ; Send SequinBroken when no free Sequins remain ; No longer timestamp buffers ; When freeing locks, make the last locker impossible. ;<PUP>PUPSEQ.MAC.8, 2-Apr-82 23:23:02, Edit by SCHOEN ; HDLSEQ checks that received packets are Sequins ;<PUP>PUPSEQ.MAC.3, 1-Apr-82 12:31:16, Edit by SCHOEN ; Don't search SYSDEF; LEAFSV.CCL includes SYS10X or SYST20 along ; with PUPDEF in compilation ;<PUP>PUPSEQ.MAC.2, 31-Mar-82 15:56:50, Edit by SCHOEN ; Shared variables via the GS macro defined in PUPDEF, rather than ; by local SHRVAR mechanism's TS macro ;<SCHOEN>PUPSEQ.MAC.71, 5-Mar-82 13:11:05, Edit by SCHOEN ; Log Sequin brokens (not DTYPE), zero CONTAB(CX) after connection has ; been cleaned up, not before! ;<SCHOEN>PUPSEQ.MAC.70, 26-Feb-82 08:49:38, Edit by SCHOEN ; Append log entry when killing connection due to timeout ;<SCHOEN>PUPSEQ.MAC.68, 3-Feb-82 20:31:04, Edit by SCHOEN ; Make MAPDAT map from file to core, rather than vs. ;<SCHOEN>PUPSEQ.MAC.64, 3-Feb-82 14:02:25, Edit by SCHOEN ; Clean stack correctly at SNDQ1 when there's no room to send ;<SCHOEN>PUPSEQ.MAC.63, 1-Feb-82 16:13:08, Edit by SCHOEN ; Make MAPDAT global (for PSVLEF routines), always map to <SYSTEM> ;<SCHOEN>PUPSEQ.MAC.61, 21-Jan-82 13:33:40, Edit by SCHOEN ; Keep track of number of active connections, do nothing when there ; are no active connections. ;<SCHOEN>PUPSEQ.MAC.60, 9-Jan-82 21:58:34, Edit by SCHOEN ; Make sure server is enabled when it starts ;<SCHOEN>PUPSEQ.MAC.59, 3-Jan-82 14:37:33, Edit by SCHOEN ; Do correct test in KILLFK on fork handle ;<SCHOEN>PUPSEQ.MAC.57, 3-Jan-82 13:55:48, Edit by SCHOEN ; Index off the correct AC to get fork handle in KILLFK ;<SCHOEN>PUPSEQ.MAC.55, 3-Jan-82 13:18:30, Edit by SCHOEN ; Kill dead connections from top fork only (fork handles are top fork relative) ;<SCHOEN>PUPSEQ.MAC.54, 2-Jan-82 13:36:48, Edit by SCHOEN ; [Tops-20] Don't call MAPDAT ;<SCHOEN>PUPSEQ.MAC.53, 15-Dec-81 10:48:41, Edit by SCHOEN ; Move responsibility for cleaning up a destroyed Sequin to BNTSV2 from ; RNTSV2 (race condition) ;<SCHOEN>PUPSEQ.MAC.50, 11-Dec-81 14:59:11, Edit by SCHOEN ; Send packet if no allocation at receiver iff packet contains no data ; Change Sequin state to BROKen after sending a SequinBroken ; Map data pages to LEAFSV.PMAP;1;P770000 ;<SCHOEN>PUPSEQ.MAC.48, 11-Dec-81 11:27:38, Edit by SCHOEN ; make sure server is enabled to OPENF socket 43!A (for restarts) ;<SCHOEN>PUPSEQ.MAC.47, 8-Dec-81 14:02:59, Edit by SCHOEN ; More fixes to 8-bit arithmetic (had 0-377 working, but not 377-0) ;<SCHOEN>PUPSEQ.MAC.45, 7-Dec-81 17:11:09, Edit by SCHOEN ; Timestamp buffers when they're filled (for debugging info) ;<SCHOEN>PUPSEQ.MAC.42, 5-Dec-81 15:45:04, Edit by SYSTEM ; Fix 8-bit arithmetic simulations in sequence filter ;<SCHOEN>PUPSEQ.MAC.33, 4-Dec-81 22:53:10, Edit by SCHOEN ; Make BNTSRV lock shared, create locks for RX, TX, and PP queues ;<SCHOEN>PUPSEQ.MAC.31, 19-Nov-81 23:47:20, Edit by SCHOEN ; Log source of Sequin connections ;<SCHOEN>PUPSEQ.MAC.29, 6-Nov-81 16:53:14, Edit by SCHOEN ; LOG crashes ; Don't enable server forks, disable after opening socket 43!A. ;<SCHOEN>PUPSEQ.MAC.20, 23-Sep-81 10:16:20, Edit by SCHOEN ; Mere mortals get user relative socket 43 if they try to start the... ; ...server title pupseq subttl Sequin protocol implementation for Tenex search pupdef,psqdef usevar topvar,toppvr,pshvar,pshpvr tenex,< search stenex > tops20,<search monsym> ; Eric Schoen ; SUMEX Computer Project ; Stanford University Medical Center ; Stanford, CA. ; August, 1981 ; Work on Leaf and Sequin implementations in Tenex ; and Tops-20 was funded by NIH Biotechnology Resouces ; Program under grant RR-00785 ; format of a packet buffer pblink==:0 ; link word: previous,,next buffer pbstat==:1 ; auxiliary word ; byte pointers into Sequin packet, indexed by PB pbhead==:2 pupLen::point 16,pbhead(pb),15 ; Pup length, bytes pupTCB::point 8,pbhead(pb),23 ; Pup Transport Control Byte PupTyp::point 8,pbhead(pb),31 ; Pup Type ; pbhead+1 (normally the Pup ID field) SeqAlc::point 8,pbhead+1(pb),7 ; Sequin Allocation byte SeqRec::point 8,pbhead+1(pb),15 ; Sequin Receive sequence SeqCon::point 8,pbhead+1(pb),23 ; Sequin control byte SeqSen::point 8,pbhead+1(pb),31 ; Sequin Send sequence ; pbhead+2 ppupdn::point 8,pbhead+2(pb),7 ; Pup Destination net ppupdh::point 8,pbhead+2(pb),15 ; Pup Destination host ppupd0::point 16,pbhead+2(pb),31; Pup Destination socket high ; pbhead+3 ppupd1::point 16,pbhead+3(pb),15; Pup Destination socket low ppupsn::point 8,pbhead+3(pb),23 ; Pup Source net ppupsh::point 8,pbhead+3(pb),31 ; Pup Source host ; pbhead+4 ppupss::point 32,pbhead+4(pb),31; Pup Source socket ; pbhead+5 pbcont==pbhead+5 .noint::push p,a tlne f,(debugf) jrst [move a,-1(p) soj a, movem a,.nocal ; save caller movei a,400000 skipl intdef jrst .+1 skpir jrst [type <INTDEF = -1 but interrupts OFF!%/> jrst .+1] jrst .+1] movei a,400000 aosg intdef dir pop p,a popj p, .okint::push p,a tlne f,(debugf) jrst [move a,-1(p) soj a, movem a,.okcal ; save caller address movei a,400000 skipge intdef jrst [type <Call to .OKINT with INTDEF = -1%/> jrst .+1] skpir jrst .+1 type <Call to .OKINT with INTDEF .ge. 0 and interrupts ON!%/> jrst .+1] movei a,400000 sosge intdef eir pop p,a popj p, ls intdef,1 ; interrupts on flag word ; -1 means interrupts on ls .nocal,1 ; last caller of .NOINT ls .okcal,1 ; last caller of .OKINT subttl Sequin Initialization ; Initialize Sequin connection management ; This routine should be called by the first user of the ; Sequin package. Its job to is set up connection tables ; for the Sequin connections which follow. ; call: pushj p,seqini ; a/ 0 to for sequin user, -1 for sequin server ; returns: +1, always seqini::movem a,server setzm contab move a,[contab,,contab+1] blt a,contab+nconn-1 setom bntlck ; make the background process lock free setzm bntlkr ; make the last locker be nonexistent movei a,pupbuf movem a,frepnt ; set up free space management movei a,freque ; assign the queues movei c,npupbf*3 skipge server ; are we a server? movei c,npupbf*nconn*3 ; yes, assign queues for everyone pushj p,mqueue skipl server ; are we a server? popj p, ; no, return here movei a,400000 rpcap move c,b ; enable what we've got epcap ifn ft10x,< hrroi b,[asciz/PUP:43!A./] > ifn ft20,< hrroi b,[asciz/PUP:43!A./] > trnn c,1b18!1b19 ; wheel or operator? ifn ft10x,< hrroi b,[asciz/PUP:43!U./] ; no, open user relative socket > ifn ft20,< hrroi b,[asciz/PUP:43!U./] > skipe soc44 hrroi b,[asciz/pup:44!A./] movsi a,1 ; yes, open server socket gtjfn ; get a jfn ercal [elog <Failed to get server socket: %1J%/> popj p,] move b,[107000,,300000] openf ercal [elog <Failed to open server socket: %1J%/> popj p,] movem a,srvjfn ; save server jfn push p,a pushj p,psiini ; init the psi's pop p,a ifn ft10x,< ; tenex, run disabled movei a,400000 rpcap trz c,1b18!1b19 ; not wheel or operator, now epcap > popj p, ls srvjfn ; JFN of port in server mode ls server ; server/user flag ls freque,3 ; free pool information block ; Initialize Sequin connection ; This routine resets the Send and Receive sequences, opens the Sequin ; connection, and creates the ring buffer of Sequin ; packets, thereby computing the receiver allocation. ; call: a/ JFN of raw pup port opened read/write ; sq/ Pointer to sequin data block (at least sqblen words long) ; cx/ Connection table index, if being called as server ; returns: +1, failure (no connections open) ; +2, success, cx/ Connection table index for this connection ; clobbers a,b,c conini::skipge server ; a server? jrst conin2 ; yes, have cx, sq already movsi cx,-nconn ; find a free connection conin1: skipn contab(cx) ; free connection if contab entry=0 jrst conin2 ; found one aobjn cx,conin1 popj p, ; fail if we can't find a free connection ; now have a connection table index. Assign buffers for this connection conin2: movei cx,(cx) ; clear left half movem sq,contab(cx) ; assign this table move p1,a ; save Pup jfn skipge server move p1,srvjfn movsi a,(sq) hrri a,1(sq) setzm (sq) blt a,sqblen-1(sq) ; clear out seqblk setom sendsq(sq) ; sendsq starts at -1 pushj p,makfrk ; Make server fork jrst [setzm contab(cx) ; free the connection popj p,] ; return movei a,freque ; from the free queue... movei c,npupbf ; make npupbfs on each queue movei b,(cx) ; get table address imuli b,lqutab ; compute index into table for connection movei b,txtab(b) ; get that address movem b,sqtxcu(sq) ; save it setom qulock(b) ; init the queue lock pushj p,aqueue ; make the queue popj p, movei b,(cx) imuli b,lqutab movei b,rxtab(b) movem b,sqrxcu(sq) setom qulock(b) pushj p,aqueue popj p, movsi a,npupbf ; init connection allocation movem a,seqall(sq) ; now fill in sequin data block with local and foreign port info move a,p1 ; retrieve pup jfn cvskt ; get local port params ercal jerr movem b,seqlnh(sq) ; save local net,,host movem c,seqlsk(sq) ; save local socket skipge server ; server? jrst [move a,pupfnh(cx); yes, get foreign stuff from cx tables movem a,seqfnh(sq) move a,pupfsk(cx) movem a,seqfsk(sq) jrst conin6] movei c,seqfnh(sq) ; get foreign port params hrli c,2 gdsts ercal jerr hrlm a,sqjfnx(sq) ; save jfn and connection index conin6: hrrm cx,sqjfnx(sq) movei a,leafSk ; default socket if necessary skipn seqfsk(sq) ; a socket specified? movem a,seqfsk(sq) ; no hlrz a,seqlnh(sq) ; see if local net specified jumpn a,conin5 hlrz c,seqfnh(sq) ; no, check foreign net jumpe c,conin5 ; jump if not specified either move a,[sixbit /puprou/] ; get routing table entry for net sysgt hrrz a,b ; table number hrli a,-1(c) ; entry getab ercal jerr trnn a,-1 ; are we directly connected? ldb c,[point 8,a,9] ; no, use net of gateway hrlm c,seqlnh(sq) ; establish local net move a,[connt,,filet] ; set up timeouts movem a,ltime(cx) ; local timeout move a,[connt,,filet←-1]; remote timeout movem a,ftime(cx) skipl server ; if not serving... jrst conin5 ; ... open the connection... hrrz a,leaffk(sq) movei b,leaf## pushj p,stfrk ; start server fork movei a,(cx) ; get real connection index log <Connection %1O opened from %2P> aos (p) ; and return if successful popj p, ; now open the Sequin connection conin5: setz a, ; send no data with this pup movei b,seqOpn pushj p,senSeq ; send a Sequin packet aos (p) ; increment return popj p, ; return subttl Sequin Send ; routine to send a Sequin packet ; call: a/length*,,address of data to send in packet (0 if none) ; b/SequinOp (-1 to default to most reasonable choice) ; sq/pointer to Sequin data block ; return: +1 always ; * length in 16 bit words senseq::push p,pb ; save packet buffer pointer push p,a ; save address push p,b ; save SequinOp move a,sqtxcu(sq) move b,qusize(a) ; get queue size camg b,qucnt(a) ; is there room? jrst [push p,a ; no, wait until there is movei a,↑d250 disms pop p,a jrst .-2] movei a,qulock(a) ; there's room, lock the queue pushj p,lock move a,sqtxcu(sq) ; recover queue pointer hlrz pb,(a) ; get pup pointer hrrz b,0(pb) ; get address of next buffer cain b,0 ; get a zero? jrst [dtype <Buffer chain points to nowhere!!%/%/> pushj p,jerr jrst .+1] hrlm b,(a) ; store as next available buffer aos 2(a) ; increment queue count pop p,b ; retrieve sequinOp move a,(p) ; retrieve a jumpe a,sensq1 ; minimum length if no data hlrz a,a ; get word length lsh a,1 ; convert to bytes sensq1: addi a,mnplen ; add header length dpb a,puplen ; save pup length setz a, ; clear transport control byte dpb a,puptcb movei a,seqtyp ; type is "sequin" dpb a,puptyp ; we run with 0 allocation for lockstep handling, since it is very easy for ; our 2020 to be overrun with packets from an IFS, even when Sequin is the ; only active process on the machine! setz a, repeat 0,< hlrz a,seqall(sq) ; store allocation byte > dpb a,seqalc move a,recvsq(sq) ; store receiver sequence dpb a,seqrec move a,sendsq(sq) ldb c,puplen ; get length caie c,MNPLEN ; any contents bytes? jrst [aoj a, ; yes, increment send sequence andi a,377 movem a,sendsq(sq) jrst sensq4] caige a,0 ; is this the first packet out on connection? aoj a, ; yes, go from -1 to 0 in send sequence sensq4: dpb a,seqsen skipl a,b ; get sequinOp into a jrst sensq2 ; caller supplied explicit op movei a,seqdat ; control is sequinData skipn 0(p) ; send a SequinNop if no data movei a,seqnop skipn seqsta(sq) ; unless connection is not open movei a,seqopn ; in which case, send a SequinOpen sensq2: dpb a,seqcon ; store control byte hlrz a,seqfnh(sq) ; store foreign net dpb a,ppupdn hrrz a,seqfnh(sq) ; store foreign host dpb a,ppupdh move a,seqfsk(sq) ; store foreign socket dpb a,ppupd1 ; low order rot a,-↑d16 dpb a,ppupd0 ; high order hlrz a,seqlnh(sq) ; store local net dpb a,ppupsn hrrz a,seqlnh(sq) ; store local host dpb a,ppupsh move a,seqlsk(sq) ; store local socket dpb a,ppupss ; move any data into Sequin packet pop p,a ; recover length,,address jumpe a,sensq5 ; skip this if no data to send hlrz b,a ; get length aoj b, idivi b,2 ; convert to PDP10 words hrl a,a ; set up to BLT data hrri a,pbcont(pb) addi b,pbcont-1(pb) blt a,(b) ; transfer data into packet buffer sensq5: tro f,tempf1 ; Remember that we have the tx queue locked ; skip the stack manipulation following ; send packet ; also here to retransmit packets from retransmit queue ; sq/ points to Sequin data block for this connection ; pb/ points to packet buffer for the Sequin data packet sensq3: movei b,(pb) pushj p,sndpup ; Send the pup off trze f,tempf1 jrst [move a,sqtxcu(sq) ; called from above? movei a,qulock(a) ; yes, unlock the queue pushj p,unlock pop p,pb ; restore packet buffer pointer popj p,] ; return popj p, ; return subttl Sequin Resend ; This routine is called to resend any packets in the retransmission ; queue which have not been acknowledged yet (i.e. on receipt of a ; SequinRestart packet). ; Only data containing packets are resent, control packets are ignored. ; sq/ pointer to sequin data block for this connection ; returns +1 always rsnseq: push p,pb ; save packet buffer pointer move a,sqtxcu(sq) ; lock the queue for consistency movei a,qulock(a) pushj p,lock trz f,tempf1 ; make sure we don't look like SENSEQ tlo f,(sqrtrn) ; say we're retransmitting hrrz pb,@sqtxcu(sq) ; pick up retransmission pointer hlrz a,@sqtxcu(sq) ; get transmission pointer? push p,a ; save on stack rsnsq1: camn pb,(p) ; reached transmission pointer? jrst rsnsq3 ; yes, return ldb a,puplen caile a,MNPLEN ; only resend data packets pushj p,sensq3 ; resend this packet rsnsq2: hrrz pb,0(pb) ; look at next packet buffer jrst rsnsq1 ; loop ; here when done rsnsq3: pop p,(p) ; remove transmission pointer entry pop p,pb tlz f,(sqrtrn) ; no longer retransmitting move a,sqtxcu(sq) movei a,qulock(a) pushj p,unlock ; unlock the queue popj p, ; return to caller subttl Sequin Release packets on retransmission queue ; This routine frees packets on the retransmission queue which have ; been acknowledged by the other process ; a/ receive sequence number from the acknowledgement ; sq/ pointer to sequin data block for this connection ; Returns +1 always, ; b/ number of freed buffers relseq: noint push p,c push p,d setz d, ; d counts number of freed buffers push p,pb ; don't kill pb setz b, push p,b ; (p) counts number of freed packets move pb,sqtxcu(sq) relsq0: hrrz pb,(pb) ; pick up retransmit queue pointer move b,sqtxcu(sq) ; compare with transmit queue pointer hlrz b,(b) cain b,(pb) ; if equal, we're done, else loop jrst relsq1 ; equal, leave ldb b,seqsen ; look at this packet's send sequence ; do 8-bit arithmetic camn a,b ; does recv'd ack = send seq of this packet? jrst relsq1 ; yes, we're done ldb b,puplen ; any data in this packet? caie b,mnplen aos (p) ; yes, increment released packet count aoja d,relsq0 relsq1: move b,sqtxcu(sq) hrrm pb,(b) ; store updated dequeue pointer move c,2(b) ; get queue item count subi c,(d) ; adjust for freed items movem c,2(b) ; save new value pop p,b ; get freed buffer count pop p,pb ; restore pb pop p,d pop p,c okint popj p, ; return subttl Sequin Receive ; this routine is called to receive Sequin packets ; Packets which are returned by this routine have already been ; input by an interrupt level routine. These packets are placed ; in a ring buffer, like the retransmit buffer. This routine returns ; these packets to the caller if and only if they contain data destined ; for some higher level process (i.e. Leaf). ; call: a/ location to store received packet in (assumed MXPBLN-MNPBLN long) ; sq/ pointer to sequin data block for this connection ; return: +1 failure: a/ -1 if timeout, ; other error codes as defined above, ; +2: success, packet stored in location pointed to by A ; a/ number of bytes stored ; clobbers c,d inpSeq::push p,a ; save address time addi a,sqtmin ; add timeout interval movem a,sqtime(sq) ; save timeout time ; loop here looking for packets inpSq1: move a,sqrxcu(sq) ; get pointer to receive queue skipn 2(a) ; anything in the queue? jrst inpSq0 ; no, wait a bit pop p,b ; retrieve storage address tlo b,(1b0) ; flag DQUEUE to return only data portion pushj p,dqueue ; get from queue subi a,mnplen ; remove overhead from packet length aos (p) ; return +2 popj p, ; here when the received packet queue is empty ; waits here for 250 ms, and then tries again (at inpSq1, above) ; if we timeout, return to caller =1 with a/ errTim (timeout) inpSq0: time ; get time camge a,sqtime(sq) ; timed out, yet? jrst [movei a,↑d250 ; no, loop disms jrst inpSq1] pop p,a ; clean stack movei a,errTim popj p, ; return +1 with timeout ; Interrupt level code to handle received packet ; call: input PUP in tmprec ; returns: +1 always ; a contains some error code (see PSQDEF.MAC) or zero ; Here when there is a packet in the received Sequin packet queue. ; Here, we dispatch on the packet type and perform any Sequin-level ; tasks which need to be performed. This code parallels the routine ; HandlePBI in IFSSequinSwap.bcpl (IFS Leaf/Sequin implementation). hdlSeq: movei pb,tmprec-2 ; point to input packet ldb a,seqCon ; get control field cain a,seqOpn ; if it's a sequinOpen skipa cain a,seqCls ; or a sequinClose movei a,seqDat ; make it look like a sequinData movem a,contro ; save control type tlnn f,(debugf) jrst hdlsq4 move a,recvsq(sq) move b,sendsq(sq) hrroi c,temp write c,<HDLSEQ: My send #%2O, recv #%1O; > ldb a,SeqSen ldb b,SeqRec write c,<packet send #%1O, recv #%2O> hrroi c,temp log <%3S> hdlsq4: ldb a,SeqSen ; get incoming send sequence move b,recvsq(sq) ; and expected send sequence pushj p,comSeq ; compare sequence numbers jrst sqFail ; +1: out-of-range, go die jrst sqRqRe ; +2: ahead, request retransmission jrst sqDupl ; +3: duplicate, drop packet jrst sqChng ; +4: previous, change control to restart ; if here, either OK or Sequin control changed to SequinRestart hdlSq1: ldb a,SeqAlc ; get other process's allocation hrrm a,seqAll(sq) ; save it ldb a,SeqRec ; now compare incoming recv sequence... move b,sendsq(sq) ; ... against my send sequence pushj p,comSeq jrst sqFail ; +1: out-of-range, go to die jrst hdlSq3 ; +2: ahead, fall through jrst sqDupl ; +3: duplicate, drop packet jfcl ; +4: previous, fall through skiple seqout(sq) ; +5: equal, adjust outstanding packet count sos seqout(sq) ; jrst hdlsq5 ; go handle states ; ... ; if here, sequence compare equal, duplicate, or previous ; in any case, release packets from the retransmission queue which ; are acknowledged by this packet, and process the data in the packet, ; if there is any hdlSq3: move a,contro cain a,SeqRes ; is control a Restart? jrst [skiple seqout(sq) sos seqout(sq) ; yes, adjust outstanding packet count jrst hdlsq5] ; and fall through ldb a,seqRec ; get receive sequence pushj p,relSeq ; release those packets this one acks move a,seqout(sq) ; update outstanding packet count caile a,0 ; don't if a is 0 or less sub a,b jumpl a,[push p,a movei a,(cx) elog <HDLSEQ: Overdecremented SEQOUT for connection %1O> pop p,a setzm seqout(sq) jrst .+1] movem a,seqout(sq) movei a,(cx) ; dtype <HDLSQ3: Freed %2O packets on connection %1O%/> ; dispatch on state of connection hdlsq5: move a,seqSta(sq) ; get connection state jrst @stattb(a) ; dispatch ; now dispatch on control field hdlsq2: move a,control jrst @ctrltb(a) ; dispatch ; dispatch table to handle sequin connection state stattb: stCLOS ; state is closed stOPEN ; state is open stDLLY ; state is dallying stBROK ; state is broken stDSTR ; state is destroyed stTIMD ; state is timed out ; dispatch table to handle different control types in Sequin packets ctrltb: hdlDat ; handle Sequin data hdlAck ; handle Sequin ack hdlNop ; handle Sequin nop hdlRes ; handle Sequin restart hdlChk ; handle Sequin check screwup ; can't get Sequin open in control hdlBrk ; handle Sequin break screwup ; can't get Sequin close in control hdlCld ; handle Sequin closed hdlDes ; handle Sequin destroy hdlDal ; handle Sequin dally hdlQui ; handle Sequin quit hdlBro ; handle Sequin broken subttl Sequin Receive utilities ; routine to compare sequence numbers ; call: a/first sequence number ; b/second sequence number ; Returns +1: (a-b) is out of range ; +2: (a-b) is between 1 and mxAhed ; +3: (a-b) is between -2 and -mxAhed ; +4: (a-b) is -1 ; +5: a=b ; Clobbers a,b comSeq: sub a,b ; compute a-b andi a,377 trne a,200 ior a,[-1,,777400] ; simluating 8-bit arithmetic movm b,a ; get magnitude of difference caile b,mxAhed ; too big a difference? popj p, ; yes, return +1 aos (p) ; return at least +2 caile a,0 ; a positive? popj p, ; yes, return +2 aos (p) ; no, return at least +3 camge a,[-1,,-1] ; a .ge. -1? popj p, ; no, return +3 aos (p) ; yes, return at least +4 caie a,0 ; a = 0? popj p, ; no, must be -1; return +4 aos (p) ; else return +5 popj p, ; here when sequence numbers are out of range sqFail: setz a, ; send a sequinBroken Op movei b,seqBro ; the world ends! pushj p,senSeq movei a,(cx) log <SQFAIL: Sequin broken on connection %1O%/> movei a,DSTR movem a,seqSta(sq) ; change state to BROKen movei a,errDes ; signal sequin broken popj p, ; return +1 ; here to request retransmission of all unacknowledged packets ; this routine sends a sequinRestart, and returns sqRqRe: setz a, ; send a sequinRestart movei b,seqRes pushj p,senSeq ; send it out movei a,(cx) dtype <SQRQRE: Sending sequinRestart for connection %1O%/> ; also here to return from HDLSEQ without queueing anything for Leaf rethdl: setz a, popj p, ; here when packet received is flagged as "duplicate" ; simply drop the packet sqDupl: movei a,(cx) dtype <SQDUPL: Dropping duplicate packet for connection %1O%/> jrst rethdl ; here when incoming packet is flagged as "previous" ; change control field to restart, and fall through sqChng: move a,control ; only make this a restart if control=data caie a,SeqDat jrst rethdl movei a,seqRes movem a,control ; store control movei a,(cx) dtype <SQCHNG: Incoming control becomes RESTART on connection %1O%/> jrst hdlsq1 ; fall back to hdlseq ; dispatch code from control and state dispatches here ; here to handle CLOSed state stCLOS: ldb a,seqCon ; get original control byte caie a,seqDat ; was it a data byte? cain a,seqOpn ; or an open request? jrst stTIMD ; then fall through jrst stOPEN ; else make believe state is open ; fall through ; here if state is timed out stTIMD: movei a,OPEN movem a,seqSta(sq) ; make state open ; fall through ; here if state is OPEN stOPEN: pushj p,strxtm ; set receive timer jrst hdlSq2 ; return ; here if state is broken stBROK: setz a, movei b,seqBro pushj p,senSeq ; send a sequin broken movei a,errBro ; say Sequin is broken popj p, ; return bad ; here if in dallying state stDLLY: move a,contro ; get control cain a,seqQui ; was it a quit? jrst stDLY1 ; yes, go die setz a, movei b,seqDal ; no, send a dally in return pushj p,senseq setz a, popj p, ; and wait for more packets stDLY1: movei a,DSTR ; say Sequin is destroyed movem a,seqSta(sq) movei a,errDes popj p, ; received a quit, return +1 ; here if in destroyed state stDSTR: dtype <STDSTR: Received packet while in DESTROYED state> movei a,errDes ; say sequin is destroyed popj p, ; return ; these routine handle the various control fields in the sequin packet ; here when control field is "destroy" hdlDes: movei a,DLLY ; make state = dallying movem a,seqSta(sq) setz a, ; respond with a dallying packet movei b,seqDal pushj p,senSeq ; send off the packet setz a, popj p, ; wait for a reply ; here to respond to a SequinDally hdlDal: setz a, movei b,seqQui ; respond with a SequinQuit pushj p,senSeq movei a,ErrDes ; say Sequin has been destroyed popj p, ; here to respond to a seqChk or seqNop hdlChk: hdlNop: setz a, movei b,seqAck ; reply with an ack pushj p,senSeq ; send it setz a, popj p, ; wait for more packets to come in ; here to respond to a SequinRestart hdlRes: pushj p,rsnseq ; resend the transmit queue setz a, popj p, ; wait for more packets ; here to respond to a sequinAck hdlAck: setz a, popj p, ; just keep waiting ; here to respond to a SequinData hdlDat: move a,seqSta(sq) ; get state caie a,OPEN ; are we open? jrst sqFail ; no, respond with sequinBroken, ret +1 movei b,seqAck ; prepare to respond with sequinAck ldb a,seqCon ; get real control field cain a,seqCls ; is it a sequinClose? jrst [movei a,CLOS ; make state = CLOSed movem a,seqSta(sq) movei b,seqCld ; prepare to respond with sequinClosed jrst .+1] ldb a,puplen ; get length of incoming pup caig a,MNPLEN ; greater than minimum? jrst [setz a, ; no, send an acknowledgement pushj p,senSeq ; send the reply setz a, popj p,] ; and wait for more input aos a,recvsq(sq) ; increment received sequence number andi a,377 movem a,recvsq(sq) ; save masked number pushj p,inSequ ; move the packet to the caller's space setz a, popj p, hdlBrk: hdlBro: hdlCld: hdlQui: jrst sqFail ; don't like receiving these here ; here to move data into caller's space ; pb/ pointer to packet buffer ; clobbers a,b,c ; returns +1 always inSequ: move a,sqrxcu(sq) movei b,pbhead(pb) ; yes, queue packet, else drop on floor skipn 2(a) ; is the queue empty? tro f,tempf1 ; yes, remember move c,1(a) ; get buffer size camle c,2(a) ; is there room? pushj p,nqueue ; yes, otherwise drop on floor trze f,tempf1 ; was the queue empty? pushj p,sgleaf ; yes, signal leaf to run popj p, ; return ; here to signal a leaf fork that a previously empty queue now has data in it ; call: pushj p,sgleaf ; cx/ connection index for this connection ; returns: +1, always ; clobbers a,b sgleaf: skipl leaffk(sq) ; only interrupt if B0 of leaffk is on popj p, move a,connfk(cx) ; get fork handle move b,[sigchn] iic popj p, subttl General utilities ; routines to manage queues ; a queue is a doubly-linked set of buffers of mamximum pup length, plus ; a couple of words of queue and buffer management overhead (see beginning ; of this file). Associated with each queue is a queue information block ; containing: ; enqueue,,dequeue pointers for the queue ; size of queue ; count of items in the queue ; routine to create a free buffer pool ; call: pushj p,mqueue ; a/ points to queue information word for this queue ; c/ number of buffers to assign ; returns: +1 always ; clobbers b,c mqueue: push p,p1 ; save a permanent location move p1,a ; point queue info pointer in a safe place movem c,1(p1) ; get queue size pushj p,asgfre ; get a buffer hrrm a,(p1) ; save dequeue pointer hrlm a,(p1) ; save enqueue pointer push p,a ; save address of first buffer soje c,mquex ; leave if all buffers reserved mque1: move b,a ; save address of this buffer pushj p,asgfre ; get next buffer hrrm a,(b) ; link it to previous buffer hrlm b,(a) ; link previous buffer to it sojn c,mque1 ; loop if more to do mquex: hlrz b,(p1) ; get enqueue pointer hrlm a,(b) ; make first buffer point back to last hrrm a,quemax ; save highest queue address pop p,b ; retrieve first buffer address hrrm b,(a) ; make last buffer point forward to first move a,p1 ; restore a setzm 2(a) ; make queue empty pop p,p1 popj p, ls quemax ; contains address of highest queue entry ; routine to assign buffers from free buffer pool to a process ; call: a/ address of free buffer pool information word ; b/ address of information word for buffers being assigned ; c/ number of buffers requested ; returns: +1, not enough available buffers ; +2, success, buffers assigned aqueue: push p,d ; save d push p,a ; save arguments push p,b push p,c hrrz a,(a) ; get address of first buffer in free pool movem a,(b) ; save in new info block hrlm a,(b) movem c,1(b) ; save queue size setzm 2(b) ; and make it initially empty move b,-2(p) ; get address of free pool info block move d,1(b) ; get free pool size subi d,(c) ; remove number of requested buffers jumpl d,aqueux ; fail if can't assigned requested number movem d,1(b) push p,a ; save address of first buffer in new queue caia ; enter assign loop aqueu1: hrrz a,(a) ; get free buffer pool header sojn c,aqueu1 ; loop until found last desired buffer hrrz c,(a) ; get address of new first buffer in free pool move b,(p) ; get address of first buffer hrrm b,(a) ; place in link word hlrz d,(b) ; get last buffer in free pool hrlm a,(b) ; place addr of last buffer in first entry move a,-3(p) ; get address of free pool info block hrrm c,(a) ; store new first buffer hrlm d,(c) ; store last free pool buffer in new first buf pop p,(p) aos -4(p) ; return successfully aqueux: pop p,c ; recovers acs pop p,b pop p,a pop p,d popj p, ; routine to return a queue of buffers to the free buffer pool ; call: a/ address of free pool information block ; b/ address of information block for buffer queue being deassigned ; returns: +1, always rqueue: push p,c ; save c push p,d ; and d push p,a ; also save arguments push p,b move d,1(b) ; get size of buffer queue being returned skipn 1(a) ; is free pool currently empty? jrst [movsi c,(b) ; yes, just move queue over hrri c,(a) blt c,2(a) setzm 1(b) jrst rqueux] hrrz c,(a) ; get address of first buffer in free pool hlrz c,(c) ; get address of last buffer in pool hrrz b,(b) ; get address of buffer in returning queue hrrm b,(c) ; link it into free pool hrlm c,(b) ; and link free pool into it caia ; enter loop rqueu1: hrrz b,(b) ; get next buffer in queue sojn d,rqueu1 ; loop until through all buffers hrrz a,(a) ; b has last buffer addr, get 1st in free pool hrlm b,(a) ; complete linking returned queue into free hrrm a,(b) move b,(p) move a,-1(p) exch d,1(b) ; zero queue size for returned queue setzm 2(b) ; make it empty, too addm d,1(a) ; add returned queue size to free pool size rqueux: pop p,b pop p,a pop p,d ; recover d and c pop p,c popj p, ; and return ; routine to place an item on queue ; call: a/address of queue information word ; b/address of pup to place on queue ; returns: +1 always ; if queue if full, then process waits until it can place item on queue nqueue: push p,pb ; don't clobber pb push p,a push p,b movei a,qulock(a) ; lock queue pushj p,lock move a,-1(p) movei pb,-pbhead(b) ; set up bytepointers for this packet move b,1(a) ; get size of queue camg b,2(a) ; is size > count? jrst [push p,a movei a,qulock(a) pushj p,unlock ; unlock while dismissed movei a,↑d250 ; wait 250 ms disms move a,(p) movei a,qulock(a) pushj p,lock ; relock when done waiting pop p,a jrst .-2] ; and try again ldb a,puplen ; compute length of move idivi a,4 caie b,0 aoj a, move b,-1(p) ; a has length in words, compute last address hlrz b,(b) ; set up BLT pointer addi b,pbhead-1(a) ; b has last address to BLT to now move a,-1(p) hlrz a,(a) ; get enqueue pointer addi a,pbhead ; point into data region of queue hrli a,pbhead(pb) blt a,(b) ; transfer onto queue hrrz b,-1(p) hlrz a,(b) ; update queue pointer hrrz a,(a) cain a,0 ; check for foul link jrst [elog <Buffer chain points nowhere!%/%/> pushj p,jerr jrst .+1] hrlm a,(b) pop p,b pop p,a aos 2(a) ; increment item count push p,a ; unlock queue movei a,qulock(a) pushj p,unlock pop p,a pop p,pb popj p, ; routine to dequeue item from queue ; call: a/ address of queue information word ; b/ address to write into ; B0 on means return data part only ; returns: +1 always (waits if queue is empty) dqueue: push p,p1 push p,pb ; save some acs push p,a push p,b movei a,qulock(a) ; lock the queue pushj p,lock move a,-1(p) hrrz pb,(a) ; get dequeue pointer hrrz b,2(a) ; get item count cain b,0 ; wait if it's zero jrst [push p,a movei a,qulock(a) pushj p,unlock ; unlock while dismissed movei a,↑d250 ; wait 250 ms disms move a,(p) movei a,qulock(a) pushj p,lock ; relock when done waiting pop p,a jrst .-2] ; and try again ldb a,puplen ; see how many words to transfer skipge 0(p) move p1,a ; if returning data only, save packet len idivi a,4 caie b,0 aoj a, move b,(p) ; a has len in wds, make BLT pointer hrli b,pbhead(pb) ; read following queue link word addi a,-1(b) ; b is source,,dest; get addr of last word skipge 0(p) ; want data part only? jrst [add b,[mnpbln-1,,0]; yes, adjust BLT stuff subi a,mnpbln-1 jrst .+1] blt b,(a) pop p,b pop p,a hrrz pb,(pb) ; update queue pointer cain pb,0 jrst [elog <Buffer chain points nowhere!%/%/> pushj p,jerr jrst .+1] hrrm pb,(a) sos 2(a) ; decrement queue item count push p,a ; save address of queue block movei a,qulock(a) ; compute address of lock pushj p,unlock ; unlock pop p,a ; restore address skipge b move a,p1 ; return packet length if necessary pop p,pb pop p,p1 popj p, ; return ; routine to mark out a buffer (MXPBLN+PBHEAD words) ; returns: +1 always, a/ address of buffer asgfre: move a,frepnt push p,a addi a,mxpbln+pbhead movem a,frepnt pop p,a popj p, ; routines to set timeouts for various connections ; routine to set timeout to keep connection alive ; call: cx/ connection index for this connection ; returns: +1 always, transparent to acs sttxtm: push p,a ; get now push p,b time hrrz b,ftime(cx) imuli b,↑d1000 ; convert to ms add a,b movem a,txtime(cx) ; save time to transmit in table pop p,b pop p,a popj p, ; routine to set timeout on received packets ; call: cx/ connection index for this connection ; returns: +1 always, transparent to acs strxtm: push p,a ; get now push p,b time hrrz b,ltime(cx) imuli b,↑d1000 ; convert to ms add a,b movem a,rxtime(cx) ; save time to receive by pop p,b pop p,a popj p, ; routine to set local timeout params (called by Leaf server) ; call: pushj p,stlctm ; a/ connection,,file timeout (seconds) ; cx/ connection table index ; returns: +1, always stlctm::movem a,ltime(cx) popj p, ; routine to set foreign timeout params (called by Leaf user) ; call: pushj p,stfntm ; a/ connection,,file timeout (seconds) ; cx/ connection table index ; returns: +1, always ; A SequinNop will be sent every time the file timeout expires. Thus, ; it is advisable to set the file timeout to be something less than the ; server's file timeout. The default timeout is 5 minutes, one-half the ; default timeout for the IFS. stfntm::movem a,ftime(cx) popj p, ; routine to create a server fork ; call: pushj p,makfrk ; cx/ connection index ; sq/ address of sequin data block ; returns: +1, failure, JSYS error code in 1 ; +2, success; leaffk(sq) containing fork handle of fork ; clobbers a,b,c,d makfrk: movsi a,(1b1) cfork ; create empty fork with superior's capenb popj p, tenex,< cfgrp ; in Tenex, do proxy logins for fork groups popj p, > hrrzm a,leaffk(sq) ; save fork index in sequin data block movem a,connfk(cx) ; and in table indexed by connection move d,[400000,,1000] ; stop PMAP loop when AC1 contains (d) movsi c,(1b2!1b4!1b9) ; map read, cw, execute hrlz b,a ; map from this fork to inferior movsi a,400000 makfk1: camn a,d ; done yet? jrst makfk3 ; yes, leave push p,b ; no, check if this page exists rpacs tlnn b,(1b5) ; page exists? jrst [push p,a ; make the page exist movei a,(a) lsh a,↑d9 setzm (a) ; touch it pop p,a jrst .+1] pop p,b ; yes, map to inferior pmap erjmp [movei a,400000 geter movei b,(b) elog <MAKFRK: PMAP failed: %2J> hrrz a,leaffk(sq) kfork movei a,(b) ; error number to A popj p,] caia makfk2: pop p,b aoj b, ; increment page numbers aoja a,makfk1 ; and loop ; here when done mapping pages... make certain pages read, write, exec makfk3: hll a,b hrri a,ishloc/1000 ; starting at ishloc move c,quemax lsh c,-↑d9 aoj c, subi c,-1(a) ; number of pages to change makfk4: rpacs tlne b,(1b5) ; page exists? jrst [movsi b,(1b2!1b3); yes, change to read, write spacs jrst .+1] aoj a, sojn c,makfk4 aos 0(p) popj p, ; ret +2 ; routine to start a Leaf server fork ; call: pushj p,stfrk ; a/ fork handle ; b/ start address ; returns +1, always stfrk: push p,b setz b, ; set fork acs sfacs pop p,b sfork aos (p) popj p, ; here to kill a server fork ; call: pushj p,killfk ; sq/ address of sequin data block ; cx/ connection table index ; returns: +1, always ; clobbers a,b,c killfk: hrrz a,connfk(cx) ; check consistency of fork handle caig a,400000 ; for debugging jrst [movei b,(cx) elog <Connection %2O claims fork handle %1O for server> popj p,] kfork setzm leaffk(sq) setzm connfk(cx) ; clear fork pointers popj p, ; returns ; lock and unlock utilities to insure contention-free program flow ; routine to lock a data structure ; call: pushj p,lock ; a/address of lock block (word 0 = lock, word 1 = fx of locker) ; returns: +1, always, when locked lock: aose (a) ; try to lock jrst [push p, a movei a,↑D100 disms pop p, a jrst .-1] movem fx,1(a) popj p, ; routine to unlock ; call: pushj p,unlock ; a/address of lock block unlock: push p,a move a,(a) ; get lock word camn a,[-1,,-1] ; is it locked? jrst [move a,(p) elog <Lock at %1O is not locked> jrst .+1] pop p,a came fx,1(a) ; do we own the lock? jrst [elog <Attempt to unlock lock at %1O by improper process %16O> pushj p,screwup] setzm 1(a) ; yes, make the last locker impossible setom (a) ; free lock popj p, ; routine to map data space into a thawed file so (enabled) others can look at ; what the server is doing mapdat::movsi a,1 ; get the .PMAP file ifn ft10x,< hrroi b,[asciz/<SYSTEM>LEAFSV.PMAP;1;P770000/] > ifn ft20,< hrroi b,[asciz/SYSTEM:LEAFSV.PMAP.1;P770000/] > gtjfn jrst mapdaf move p1,a move b,[44b5+1b19+1b20+1b25] ; open read, write, thawed, 36 bits openf jrst mapdaf move b,[400000,,ilsloc/1000] hrlz a,p1 hrri a,ilsloc/1000 movsi c,(1b2!1b3) ; map read/write move d,[400000,,600] ; stop here mapda1: move p2,b move b,a seto a, pmap ; delete the file page move a,b ; (hope this works on Tops-20) move b,p2 pmap ; map a page to core aoj b, ; increment core address came b,d ; reached end? aoja a,mapda1 ; nope, incr file address, loop mapda2: jrst (fx) ; here on failure mapdaf: log <Can't create .PMAP file: %1J> skipe a,p1 ; relase JFN if we've got one rljfn jfcl ; ignore it if we can't jrst (fx) subttl interrupt driven routines and background process ; These routines maintain the state of the server. RNTSRV is run at ; interrupt level to receive Pups. On receipt of a PUP, RNTSRV finds ; the connection it belongs to (or creates a new one), then calls the ; sequence number filter to determine where in the packet sequence the ; received Sequin belongs. Sequins which are indeed in sequence are ; queued in SQRXCU for the server fork (whatever it may be) below to ; handle. ; BNTSRV is run on a five second timer, and is responsible for garbage ; collecting dead or destroyed sequins, and also maintaining the state ; of each sequin connection. ; call: pushj p,bntsrv ; transparent to acs bntsrv::skipg nactcn ; are there any active connections? popj p, ; no, return exch p,bntpdl movem 16,bntacs+16 ; save acs 0-16 movei 16,bntacs blt 16,bntacs+15 move 16,bntacs+16 ; recover fx (=AC16) cail fx,0 ; are we the top fork here? jrst [pushj p,bntsv1 ; no, service this connection only tlze f,(defntf) ; need to reenter? jrst .-2 ; yes, do so jrst bntsv5] ; done, leave bntsv4: movsi cx,-nconn ; look for active connections bntsv0: skipe sq,contab(cx) ; is this connection alive? pushj p,bntsv1 ; yes, handle it aobjn cx,bntsv0 ; loop if more connections to scan tlze f,(defntf) ; want to recycle through this? jrst bntsv4 ; yes, go again bntsv5: movsi 16,bntacs ; done, recovers acs blt 16,16 exch p,bntpdl tlze f,(defntf) ; want BNTSRV again? jrst bntsrv ; loop through again popj p, gs bntlck ; Background service lock gs bntlkr ; Last locker ; this routine does all the work bntsv1: move a,seqSta(sq) ; get current state caie a,BROK ; if broken... cain a,DSTR ; ... or destroyed jrst bntsv2 ; clean up cain a,TIMD ; timed out? jrst [skipn b,rxtime(cx) ; see if connection timed out jrst .+1 hrrz a,ltime(cx) ; compute time of connection timeout imuli a,↑d1000 sub b,a hlrz a,ltime(cx) imuli a,↑d1000 add b,a push p,b ; get now time pop p,b caml a,b jrst [movei c,(cx) elog <BNTSV1: Connection %3O timed out> jrst bntsv2] ; connection timed out jrst .+1] ; check for time to send skipn txtime(cx) ; is there a send timeout? pushj p,sttxtm ; no, set one time caml a,txtime(cx) ; tx timeout yet? pushj p,sndnop ; yes, send a nop (if not server) ; check if receiver timed out skipn rxtime(cx) ; is there a received timeout? pushj p,strxtm ; no, set one move b,seqSta(sq) ; already timed out? cain b,TIMD ; if so, avoid following popj p, caml a,rxtime(cx) jrst [movei a,TIMD exch a,seqSta(sq) ; yes, say connection is timed out cain a,DLLY ; were we dallying on this connection? jrst [movei a,DSTR movem a,seqSta(sq); yes, destroy the connection popj p,] skipn usrnum##(cx) ; is the connection logged in? jrst [movei a,DSTR ; no, kill it movem a,seqSta(sq) popj p,] movei a,(cx) log <Short-term timeout on connection %1O> popj p,] popj p, ; return ; here to close a BROKen, DeSTRoyed, or TIMeD out connection ; sq, cx set up bntsv2: cail fx,0 ; Let only the top fork do this popj p, ; If not, then return movei a,(cx) log <BNTSV2: Closing connection %1O> setzm pupfsk(cx) ; clear address for server, also setzm pupfnh(cx) setzm rxtime(cx) setzm txtime(cx) skipge server jrst [pushj p,cleanf## ; clean JFNs pushj p,killfk ; kill server fork jrst bnts2a] ; don't close socket if server hlrz a,sqjfnx(sq) ; close jfn closf elog <BNTSV2: While closing port: %1J> bnts2a: movei a,freque ; return queues to free pool hrrz b,sqtxcu(sq) pushj p,rqueue hrrz b,sqrxcu(sq) pushj p,rqueue setzm contab(cx) ; clear connect index sosge nactcn ; decrement count of actives conns jrst [elog <BNTS2A: NACTCN overdecremented> setzm nactcn popj p,] popj p, ; Routine to send a PUP, updating allocations as it goes, if necessary. ; This routine will dismiss if the allocation does not exist at the ; receiver, hence, do not lock any critical data structures or call with ; interrupts off. ; call: b/ Address of packet buffer to send ; returns: +1 always ; Clobbers c,d sndpup: push p,a push p,b push p,pb ; save pb movei pb,(b) ; point to packet ldb a,puplen ; compute PDP10 word count addi a,3 idivi a,4 hrl b,a hrri b,pbhead(pb) ; point to start of packet data hlr a,sqjfnx(sq) skipge server ; if server hrr a,srvjfn ; use the server jfn hrli a,(1b0+1b1) ; don't block, generate checksum sndpp1: hrrz c,seqall(sq) ; get allocation for receiver cain c,0 ; is it 0? movei c,1 ; then make it 1 move d,seqout(sq) ; get outstanding packet count sub c,d ; get difference jumple c,[tlne f,(sqrtrn) ; retransmitting? jrst .+1 ; ignore allocation limits push p,a ; no allocation, must wait ldb a,puplen ; get length of pup caig a,mnplen ; don't disms if no contents bytes jrst [pop p,a jrst .+1] move a,sqtxcu(sq) movei a,qulock(a) push p,a ; unlock the queue while waiting pushj p,unlock movei a,↑d100 disms pop p,a pushj p,lock ; relock the queue pop p,a jrst sndpp1] ; try again now pupo elog <%1J> ldb a,puplen ; get length of PUP just sent caig a,MNPLEN ; any contents bytes? jrst sndpp2 ; no, don't mess with seqout tlnn f,(sqrtrn) ; retransmitting? aos seqout(sq) ; no, incr outstanding packet count sndpp2: pushj p,sttxtm ; reset the timeout timer for 5 minutes pop p,pb ; restore pb pop p,b pop p,a popj p, ; routine to place a sequinNop on the queue sndNop: skipge server ; only if not server popj p, push p,a push p,b setz a, ; send no data movei b,seqNop ; send a Nop pushj p,senSeq ; queue it pop p,b ; restore acs pop p,a popj p, ; routine called when a pup is received rntsrv: exch p,rntpdl movem 16,rntacs+16 ; save acs 0-16 movei 16,rntacs blt 16,rntacs+15 skipge server jrst [seto fx, ; we're the top fork pushj p,rntsv1 ; if server, get packet jrst rntsv5] movsi cx,-nconn ; loop through connections rntsv0: skipe sq,contab(cx) ; looking for those with pups waiting pushj p,rntsv1 ; do the work aobjn cx,rntsv0 rntsv5: movsi 16,rntacs ; done, recovers acs blt 16,16 exch p,rntpdl debrk ; leave interrupt routine ercal jerr ; in case something fails rntsv1: hrrz a,srvjfn ; assume we're a server skipl server ; are we a server? hlr a,sqjfnx(sq) ; no, get jfn from seqblk hrli a,(1b0+1b1) ; don't disms for I/O, gen checksum move b,[mxpbln,,tmprec] pupi ; attempt to input a pup jrst rntsv3 ; empty or error skipge server ; are we a server? jrst [pushj p,rntsv4 ; yes, get connection for this packet jrst rntsv1 ; failed, see if there's another jrst .+1] ; found the connection movei c,tmprec pushj p,HdlSeq ; Handle this packet cain a,errDes ; was the Sequin destroyed? jrst rntsv2 ; yes, go wait for send queue to empty jrst rntsv1 ; no, loop until input port is empty ; here when Sequin is classified as destroyed ; Make state=DSTR, let BNTSV2 clean up this connection rntsv2: movei a,DSTR movem a,seqsta(sq) ; make state=DeSTRoyed popj p, rntsv3: cain a,pupx3 ; empty? popj p, type <RNTSRV: PUPI failure %1J%/> popj p, ; routine to find connection index (or make new one) for received packet ; call: pushj p,rntsv4 ; TMPREC: containing received packet ; returns: +1, failed packet (can't open connection or not Sequin, etc) ; +2, packet received ok, with cx, sq set up rntsv4: push p,pb movei pb,tmprec-pbhead ldb a,puptyp ; is this a sequin packet? caie a,seqtyp jrst [log <RNTSV4: Discarding non-sequin packet> pop p,pb popj p,] ldb a,ppupss ; get source socket movsi cx,-nconn rnts4a: camn a,pupfsk(cx) ; did we find it? jrst rnts4d ; maybe, try net,,host aobjn cx,rnts4a ; loop until we find it ; here if not found; look for an empty slot to make a new connection in rnts4g: ldb a,seqcon ; get control byte caie a,seqOpn ; is it an open request? jrst rnts4x ; no, send a broken in return movsi cx,-nconn ; yes, look for a free slot rnts4b: skipn contab(cx) jrst rnts4c aobjn cx,rnts4b setz a, hrroi b,[asciz/Sorry, no Leaf server connections available; please try again later./] pop p,pb jrst errpup ; here if first Sequin received is not a SequinOpen rnts4x: pop p,pb jrst breksq ; send a sequin broken, return +1 ; here when a free slot found, make new connection rnts4c: movei cx,(cx) movei sq,(cx) imuli sq,sqblen movei sq,sqbtab(sq) movem sq,contab(cx) ldb a,ppupss movem a,pupfsk(cx) ldb a,ppupsh ldb b,ppupsn hrl a,b movem a,pupfnh(cx) move a,srvjfn hrlm a,sqjfnx(sq) pushj p,conini jrst [pop p,pb setzm pupfsk(cx) ; make sure connection is closed setz a, hrroi b,[asciz/Sorry, unable to make connection; please try again later./] jrst errpup] pop p,pb aos 0(p) dtype <LEAFSV: Sequin connection %6O opened%/> aos nactcn ; increment number of active conns popj p, ; here when socket matches; look for net,,host rnts4d: move c,a ; save pupfsk ldb a,ppupsh ldb b,ppupsn hrl a,b rnts4e: camn a,pupfnh(cx) jrst [camn c,pupfsk(cx) jrst rnts4f ; got it jrst .+1] aobjn cx,rnts4e jrst rnts4g ; here when found a match rnts4f: skipn sq,contab(cx) jrst [setzm pupfsk(cx) ; don't be mislead by half-created sos -1(p) ; ... sockets. Fail here. jrst .+1] pop p,pb ; connection is fully created aos (p) ; success return popj p, ls rntstk,psisiz ls bntstk,psisiz ; interrupt stacks ls rntpdl ls bntpdl ; interrupt stack pointers ; routine to send a SequinBroken back to sender when a Sequin cannot be ; created ; call: pushj p,breksq ; pb/ pointer to received packet ; returns: +1, always breksq: movei pb,tmprec-pbhead ldb a,ppupsn ; get source port ldb b,ppupsh ldb c,ppupss movei pb,brkpup-pbhead ; point to space to send break pup dpb a,ppupdn ; deposit dest port dpb b,ppupdh dpb c,ppupd1 ; low order bits rot c,-↑d16 ; rotate around high order bits dpb c,ppupd0 ; and deposit into Pup movei pb,tmprec-pbhead ; deposit source host bytes ldb a,ppupdn ldb b,ppupdh movei pb,brkpup-pbhead dpb a,ppupsn dpb b,ppupsh move a,srvjfn ; determine local net, host, socket cvskt ercal jerr dpb c,ppupss ; save socket movei pb,tmprec-pbhead ; set up sequin fields ldb a,seqrec ; receive sequence ldb b,seqSen ; get send sequence ldb c,puplen ; any data in packet? caile c,mnplen aoj b, ; yes, increment for return recv seq movei pb,brkpup-pbhead dpb a,seqSen dpb b,seqRec setz a, ; no Alloc dpb a,seqAll movei a,seqBro ; say sequin broken dpb a,seqCon movei a,mnplen dpb a,puplen movei a,seqtyp dpb a,puptyp hrr a,srvjfn ; use the server jfn hrli a,(1b0+1b1) ; don't block, generate checksum movei b,brkpup hrli b,mnpbln pupo elog <BREKSQ: PUPO error: %1J> log <BREKSQ: No room or Data Sequin rcvd for nonexistent connection> popj p, ls brkpup,MXPBLN ; a little longer for superstitious reasons ; routine to send an error pup to sender ; call: pushj p,errPup ; a/ Error subcode ; b/ Pointer to asciz string ; returns: +1, always errPup: push p,a tlc b,-1 tlcn b,-1 hrli b,(point 7) push p,b movei pb,tmprec-pbhead ; swap the ports ldb a,ppupsn ; get source port ldb b,ppupsh ldb c,ppupss movei pb,brkpup-pbhead ; point to space to send break pup dpb a,ppupdn ; deposit dest port dpb b,ppupdh dpb c,ppupd1 ; low order bits rot c,-↑d16 ; rotate around high order bits dpb c,ppupd0 ; and deposit into Pup movei pb,tmprec-pbhead ; deposit source host bytes ldb a,ppupdn ldb b,ppupdh movei pb,brkpup-pbhead dpb a,ppupsn dpb b,ppupsh move a,srvjfn ; determine local net, host, socket cvskt ercal jerr dpb c,ppupss ; save socket move a,tmprec+1 movem a,brkpup+1 ; transfer ID (sequin fields) move a,[tmprec,,brkpup+5] blt a,brkpup+↑d9 ; transfer original header pop p,b pop p,a ; recover string ptr and error code dpb a,[point 16,brkpup+↑d10,15] setz a, dpb a,[point 16,brkpup+↑d10,31] move a,[point 8,brkpup+↑d11,-1] ; string pointer setz d, errpu1: ildb c,b ; copy string jumpe c,errpu2 idpb c,a aoja d,errpu1 errpu2: movei pb,brkpup-pbhead movei a,mnplen+↑d12 ; min error pup len addi a,(d) ; add length of string trne a,1 ; if odd, increment aoj a, dpb a,puplen movei b,4 dpb b,puptyp movei b,3(a) ; compute # of pdp10 words lsh b,-2 hrlzs b hrr a,srvjfn ; use the server jfn hrli a,(1b0+1b1) ; don't block, generate checksum hrri b,brkpup pupo elog <ERRPUP: PUPO error: %1J> popj p, subttl PSI utilities ; routine to init psi's ; call: (p)/ jfn on port psiini::setom intdef move a,[iowd psisiz,bntstk] ; do this here because BNTSRV used to... movem a,bntpdl ; ... run at interrupt level move a,[iowd psisiz,rntstk] movem a,rntpdl move b,[levtab,,chntab] movei a,400000 ; init PSI's for this fork sir eir move b,[actchn] ; activate used channels aic move a,[↑d26,,↑d23] ; assign ↑Z interrupt to channel 23 ati move a,-1(p) ; set received pup interrupt movei b,24 move c,[36b5+1b11+36b17]; chan 1 is recvd interrupt mtopr popj p, ; PSI channel definitions define psi(ch,lev,disp),< actchn==actchn!1b<ch> reloc chntab+↑d<ch> lev,,disp > actchn==0 chntab::psi(1,3,rntsrv) ; receiver service psi(9,1,pdlovf) ; Pushdown overflow psi(11,1,daterr) ; Data error psi(15,1,illins) ; Illegal instruction psi(16,1,illred) ; Illegal read psi(17,1,illwrt) ; Illegal write psi(18,1,illxct) ; Illegal execute psi(20,1,illsiz) ; Machine size exceeded psi(23,1,kilsrv) ; ↑Z interrupt to kill server reloc chntab+↑d36 levtab::lev1pc lev2pc lev3pc ; Fatal errors pdlovf::jsr crashx asciz /Pushdown overflow/ daterr::jsr crashx asciz /IO data error/ illred::jsr crashx asciz /Illegal read/ illwrt::jsr crashx asciz /Illegal write/ illxct::jsr crashx asciz /Illegal execute/ illsiz::jsr crashx asciz /Machine size exceeded/ ; Common code for fatal error interrupts crashx: 0 push p,lev1pc ; Put trap pc on stack push p,b hrro b,crashx ; Make call pc into string ptr jrst screw1 jerr:: push p,b hrroi b,temp## write b,<%1J%/> hrroi b,temp## jrst screw1 screwup:: push p,b hrroi b,[asciz/A fatal error has occurred/] screw1: push p,a ; don't clobber any acs hrrz a,-2(p) ; get caller soj a, ; decrement for true address log <%2S at %1O%/%/> time ; get now subm a,crstim ; compute how long ago we last crashed exch a,crstim caige a,↑d<60*1000> ; longer than a minute? jrst [elog <Too-frequent crashes, aborting...> pushj p,dmplog## haltf jrst srvstt##] elog <Leaf server crashed, restarting...> pushj p,dmplog## jrst srvstt## ls crstim ; here to handle illegal instruction illins::push p,a ; Not sumex, try to do ERCAL push p,b hrrz a,lev1pc hllz b,(a) ; Get instruction after error camn b,[ERCAL] ; Is it an ERCAL? jrst [hrrz a,lev1pc ; Yes, simulate ERCAL to error routine hrrz b,(a) ; Get address of error routine movem b,psiret ; store it aoj a, ; Adjust return address from interrupt pop p,b exch a,(p) ; Store new return address jrst @psiret] ; Jump into error routine hllz b,-1(a) ; Not ERCAL, did a JSYS get us? camn b,[JSYS 0] ; Was it a JSYS? jrst [movei a,400000 geter movei a,(b) ; isolate error number hrroi b,temp write b,<Fatal JSYS error %1J> pop p,b ; mung stack pop p,a push p,lev1pc push p,b hrroi b,temp jrst screw1] pop p,b pop p,a ; Not JSYS, handle as with other errors jsr crashx asciz /Illegal instruction/ ls psiret,1 ; Location to hold address of error routine ; ↑Z interrupt to kill server kilsrv: movsi cx,-nconn ; break all connections setz a, movei b,seqBro ; send sequin brokens kilsr1: skipe sq,contab(cx) pushj p,senseq aobjn cx,kilsr1 pushj p,dmplog move a,srvjfn ; close the socket closf elog <KILSRV: Unable to close server socket: %1J> gtad type <%/%/Tenex Leaf Server halted at %1T> haltf jrst srvstt## subttl Storage ls soc44 ; 0 = use soc 43, -1 = use soc 44 for debugging ls bntacs,20 ; storage for background process acs ls rntacs,20 ; storage for recv interrupt acs ; tables indexed by CX gs contab,nconn ; connection table gs rxtime,nconn ; receiver timeout gs txtime,nconn ; transmitter timeout gs pupfsk,nconn ; table of foreign sockets, indexed by cx gs pupfnh,nconn ; table of foreign net,,host, index by cx gs ltime,nconn ; local timeout (connection,,filelock) gs ftime,nconn ; foreign timeout (connection,,filelock) gs connfk,nconn ; fork for leaf server, indexed by cx gs nactcn,1 ; holds number of active connections ls contro,1 ; control word from received packet ls frepnt,1 ; free space pointer for ASGFRE ls sndtmp,MXPBLN ; temporary send buffer ls tmprec,MXPBLN ; temporary receive buffer ls lev1pc,1 ls lev2pc,1 ls lev3pc,1 gs txtab,lqutab*nconn gs rxtab,lqutab*nconn gs sqbtab,sqblen*nconn end