! ! TSBSP DRIVER ! copyright University of Kent at Canterbury 1982 ! stack=2000, streams=0 !version 2: 23:1:82 ! %CONTROL 1 %BEGIN %RECORDFORMAT NSF(%INTEGERARRAY DUM(1:3), %STRING (241) NAME) %RECORDFORMAT NSRF(%INTEGER D1,D2,CODE, %C ((%STRING (241) NAME) %ORC (%BYTEINTEGER FLAGS, ADDRESS, %INTEGER PORT, FUNCTION, %C %STRING (55) RESTOFNAME ))) %RECORDFORMAT MEF(%RECORD (MEF) %NAME LINK, %BYTEINTEGER LEN, TYPE, %C ( (%BYTEINTEGERARRAY PARAMS(0:251)) %ORC (%INTEGER ADDRESS, PORT, %C ( (%INTEGER RCOMM,TCOMM,%BYTEINTEGERARRAY DATA(0:243)) %ORC (%INTEGER COMMAND, REPLY PORT, FUNCTION, BSP PARAM LEN, %C %INTEGERARRAY BSP PARAM(1:2), %C %BYTEINTEGERARRAY TSPARAMS(0:235) ) %ORC (%RECORD (NSRF) NS REPLY ) %ORC (%RECORD (NSF) NS) )))) %RECORDFORMAT PEF(%BYTEINTEGER SER, REPLY, %C ( (%INTEGER A,B,C ) %ORC (%BYTEINTEGER A1,A2,B1,B2,C1,C2) %ORC (%BYTEINTEGER FN, S1, %C ( (%RECORD (MEF) %NAME MES, %BYTEINTEGER GATEPORT,TASKPORT) %ORC (%STRING (3) FACILITY) ) ) ) ) %RECORDFORMAT QF(%RECORD (MEF) %NAME LINK) %RECORDFORMAT BSPF(%INTEGER TTIMEOUT, RTIMEOUT, DEST PORT, %C %RECORD (MEF) %NAME HOLD BUF, %C %RECORD (MEF) %NAME O BUF Q, %C %BYTEINTEGER DEST, REPLY, TASK PORT, STATE, TSTATE, RSTATE, TSEQ, %C RSEQ, TGAH, RGAH, RDY COMMAND, FAULT COUNT, FWIN, OBUFQ LEN ) %INCLUDE "TSBSP_STAB"; !state table declarations %INCLUDE "TSBSP_TSCODES" %INCLUDE "TSBSP_DEIMOS" ! Values of BSP commands !------------------------ %CONSTINTEGER RDY=16_3000 %CONSTINTEGER NOTRDY=16_5000 %CONSTINTEGER RESET COMMAND=16_6300 %CONSTINTEGER CLOSE=16_6600 %CONSTINTEGER DATA COMMAND=16_A000 %CONSTINTEGER NODATA=16_C000 %CONSTINTEGER OPEN=16_6A00 %CONSTINTEGER OPENACK=16_6500 %CONSTINTEGER SSP REQUEST=16_6C00 !flag bits added into bsp DATA command %CONSTINTEGER CONTROL=8 %CONSTINTEGER EXPEDITED=16 %CONSTINTEGER ODD=1 %CONSTINTEGER PUSH FLAG=2 %CONSTINTEGER CLOSE REQ=4 !function code values to and from RING server !the values below may be added to the function codes %CONSTINTEGER RELEASEFLAG=16_80; !RELEASE BUFFER AFTER OUTPUT %CONSTINTEGER TELLFLAG=16_40; !NOTIFY AT END OF OUTPUT %CONSTINTEGER CS0FLAG=16_20 %CONSTINTEGER COMMAND MASK=16_1F; !TO MASK OUT VALUES ABOVE %CONSTINTEGER ENABLE PORT=0 %CONSTINTEGER XDATA=1 %CONSTINTEGER SSOUT=2; !NOT USED %CONSTINTEGER XRDY=3 %CONSTINTEGER XNOTRDY=4 %CONSTINTEGER XNODATA=5 %CONSTINTEGER XDISC ABANDON=6 %CONSTINTEGER XRESET=7 %CONSTINTEGER XCLOSEREQ=8 %CONSTINTEGER XEXPRDY=9 %CONSTINTEGER X DISC RESPONSE=10 %CONSTINTEGER X RESET RESPONSE=11 %CONSTINTEGER X DISC OK=12; !disconnect with zero code %CONSTINTEGER OUTPUT TRACE=16; !FORCE OUTPUT OF RING TRACE %CONSTINTEGER R OUTPUT DONE=0 %CONSTINTEGER TRANSFER ERROR=1 %CONSTINTEGER R INPUT DONE=2 %CONSTINTEGER INPUT ERROR=3 %CONSTINTEGER RING DOWN=4 %CONSTINTEGER LOOKUP=0 %CONSTINTEGER ADD NAME=1 %CONSTINTEGER REMOVE NAME=2 !function values for name server operations ! %CONSTINTEGER REVERSE LOOKUP=3 %CONSTINTEGER OWN NAME=4 !codes used for errors when connect fails %CONSTINTEGER NS ERROR=1 %CONSTINTEGER LOOKUP FAILED=2 %CONSTINTEGER NO PORTS=3 %CONSTINTEGER NO REPLY=4 !values for BSP receiver and transmitter states %CONSTINTEGER NESENT=1 %CONSTINTEGER ESENT=2 %CONSTINTEGER IDLE=3 ! service numbers and DEIMOS specific values %CONSTRECORD (*) %NAME NIL==0 %CONSTINTEGER BSP SER=16 %CONSTINTEGER RING SER=10 %CONSTINTEGER BUFFER MANAGER=17 %CONSTINTEGER NAME SERVER=15 %CONSTINTEGER T3 SER=21 %CONSTBYTEINTEGERNAME INT==8_160060 %CONSTBYTEINTEGERNAME OWN ID==8_160030 %CONSTBYTEINTEGERNAME CHANGE OUT ZERO==8_160310 %CONSTINTEGERNAME PKTS==8_100010; !monitoring locn in buff man space %CONSTINTEGERNAME SBR==8_100006 %CONSTINTEGERNAME BYT==8_100004 !function values to buffer manager %CONSTINTEGER REQUEST BUFFER=0 %CONSTINTEGER RELEASE BUFFER=1 %CONSTINTEGER LONG BLOCK=0 %OWNSTRING (3) %ARRAY FUNCTION NAME(1:25)="SM1","SM2","?","LP","?"(6),"SM", "CR","PR","?"(4),"ITP","?","TSR","INF","?"(4) !Various adjustable constants !---------------------------- %CONSTINTEGER BSLIMIT=32; !number of byte stream ports + 1 %CONSTINTEGER TIMEOUT=1; !normal timeout is (TIMEOUT+1)*TICKS/50 %CONSTINTEGER CONN TIMEOUT=5; !timeout on connect %CONSTINTEGER DISC TIMEOUT=1; !in addition to normal timeout %CONSTINTEGER IDLE TIMEOUT=15; !in data transfer state %CONSTINTEGER TICKS=100; !clock ticks per alarm call %CONSTINTEGER MAX PARAM LEN=127; !should be temporary restriction %CONSTINTEGER MAX B FNAMES=150; !space for facility names %CONSTINTEGER FAULT COUNT LIMIT=9; !before internal disconnect %CONSTINTEGER FWD WINDOW=2; !default output buffering %CONSTINTEGER MAX DATA SIZE=238;!bytes per block !miscellaneous data declarations !------------------------------- %OWNRECORD (PEF) P,OP; !input and output pon records %OWNINTEGER I %OWNSTRING (16) ME; !my name to be obtained from name server %OWNRECORD (BSPF) %NAME BSP %OWNINTEGER GPORT, NEWSTATE, TIME %OWNRECORD (MEF) %NAME HOLDBUF %OWNINTEGER MONIT=0; !monitoring state %EXTERNALSTRING (15) %FNSPEC ITOS(%INTEGER X, WIDTH) %ROUTINESPEC SWABA(%INTEGERNAME ADDRESS,%INTEGER COUNT) %ROUTINESPEC PUT HEX(%INTEGER X) %ROUTINESPEC PPON(%RECORD (PEF) %NAME P) %ROUTINESPEC PRINT BSP %ROUTINESPEC ERROR(%INTEGER N) %ROUTINESPEC IERROR(%INTEGER N, EVENT) %ROUTINESPEC PERROR(%INTEGER N) %ROUTINESPEC GET BUFFER %ROUTINESPEC FREE BUFFER(%RECORD (MEF) %NAME MES) %ROUTINESPEC TO NS(%INTEGER FN) %ROUTINESPEC SEND UP(%INTEGER FN) %ROUTINESPEC SEND UP BUFFER(%INTEGER FN) %ROUTINESPEC FREE BSP %ROUTINESPEC COPY N TO PARAMS(%INTEGER START, %INTEGER LEN, N) %ROUTINESPEC COPY PARAMS TO N(%BYTEINTEGERARRAYNAME TSP, %INTEGER TYPE, N) %ROUTINESPEC COPY PARAMS TO I(%RECORD (MEF) %NAME BUF, %INTEGER N) %ROUTINESPEC COPY I TO PARAMS(%RECORD (MEF) %NAME BUF, %INTEGER N) %ROUTINESPEC PREFIX(%STRING (*) %NAME S, P) %ROUTINESPEC SEND TO RING(%INTEGER FN) %ROUTINESPEC BUF TO RING(%INTEGER FN) %ROUTINESPEC SEND COMMAND TO RING(%INTEGER FN, SEQ) %ROUTINESPEC ADDFAC(%STRING (*) %NAME FAC) %ROUTINESPEC DELETE FAC(%STRING (*) %NAME FAC) %ROUTINESPEC FIND(%STRING (*) %NAME S, %INTEGERNAME REPLY TASK) %ROUTINESPEC DO CLEAR TIMEOUTS %ROUTINESPEC CLEAR QUEUES %ROUTINESPEC DO DISC N TO I %ROUTINESPEC DO RESET N TO I; !params are sent on later - ignore them %ROUTINESPEC DO ENABLE OUTPUT(%INTEGER N) %ROUTINESPEC PICK BSP PARAM(%INTEGERNAME MAXT, MAXR, %INTEGER NPARAMS, TYPE) %ROUTINESPEC DO PROCESS OPENACK %ROUTINESPEC DO DPROCESS OPENACK %ROUTINESPEC SETUP OPEN(%RECORD (MEF) %NAME B, %INTEGER COMMAND, REPLY PORT, %C X, BST, BSR) %ROUTINESPEC SEND OPEN NACK TO RING(%INTEGER CODE) %ROUTINESPEC DO CONN N FAILED %ROUTINESPEC DO INT OPEN NACK %ROUTINESPEC DO SEND OPEN ACK %ROUTINESPEC CONN I DISC(%INTEGER FN, ERR TYPE) %ROUTINESPEC DO NS MESS %ROUTINESPEC DG MESSAGE FOR I(%STRING (*) %NAME S) %ROUTINESPEC FORMAT DG AND SEND(%INTEGER COMMAND, PORT, FN, %STRING (*) %NAME S) %ROUTINESPEC DO DG REPLY ITON %ROUTINESPEC DO PROCESS DG %ROUTINESPEC SEND DG(%INTEGER FN CODE) %ROUTINESPEC DO DG REPLY NTOI %ROUTINESPEC DO PROCESS OPEN %ROUTINESPEC DO INT DISC %ROUTINESPEC SETUP DISC(%RECORD (MEF) %NAME BUF, %INTEGER COMMAND, TYPE) %ROUTINESPEC DO DISC I RESPONSE %ROUTINESPEC DO DISC I TO N %ROUTINESPEC DO RESET I RESPONSE %ROUTINESPEC DO RESET I TO N %ROUTINESPEC DO SEND LOOKUP; !to the name server %ROUTINESPEC DO Q DATA I %ROUTINESPEC START CONNECT(%INTEGER FN) %ROUTINESPEC SEND NOTRDY %ROUTINESPEC SEND RDY %ROUTINESPEC SEND NODATA %ROUTINESPEC SEND DATA AGAIN %ROUTINESPEC SEND DATA %ROUTINESPEC DO DATA ACK; !data has been acknowledged %ROUTINESPEC DO I NOTRDY %ROUTINESPEC DO I RDY %ROUTINESPEC PROCESS DATA %ROUTINESPEC DO I NODATA %ROUTINESPEC DO I DATA %ROUTINESPEC DO Q DATA N %ROUTINESPEC DO T TIMEOUT %ROUTINESPEC DO R TIMEOUT %ROUTINESPEC PERFORM(%INTEGER ACTION) %ROUTINESPEC STATE TABLE(%INTEGER EVENT) %ROUTINESPEC FROM BELOW %ROUTINESPEC FROM BUFF MAN %ROUTINESPEC FROM CLOCK %ROUTINESPEC FROM ABOVE %ROUTINESPEC FROM NS %ROUTINESPEC FIND MY NAME %RECORD (MEF) %MAP PULL !--------------------------------------------- %RECORD (MEF) %NAME PNT, LAST; !pull last item from the queue (FIFO) %IF BSP_OBUF Q == NIL %THEN LAST == NIL %ELSESTART %IF BSP_OBUF Q_LINK==NIL %START; !one item on q LAST==BSP_OBUF Q BSP_OBUF Q==NIL %FINISHELSESTART PNT==BSP_OBUF Q %WHILE %NOT PNT_LINK_LINK==NIL %CYCLE; PNT==PNT_LINK; %REPEAT LAST==PNT_LINK PNT_LINK==NIL %FINISH %FINISH %RESULT == LAST %END %ROUTINE SWABA(%INTEGERNAME ADDRESS,%INTEGER COUNT) !------------------------------------ !%INTEGER I !SWAB THE BYTES IN THE COUNT WORDS ADDRESSED BY ADDR ! %FOR I=1,1,COUNT %CYCLE ! ADDRESS=((ADDRESS>>8) & 16_FF)+(ADDRESS<<8) ! ADDRESS==INTEGER(ADDR(ADDRESS)+2) ! %REPEAT *=8_000321; !LOOP: SWAB (R1)+ *=8_005300; ! DEC R0 *=8_003375; ! BGT LOOP %END ! error handling routine !------------------------------------------------------------- %ROUTINE PUT HEX(%INTEGER X) !--------------------------- %INTEGER I,C SPACE %FOR I=3,-1,0 %CYCLE C=(X >> (I*4) ) & 15 %IF C>9 %THEN C=C-'0'+'A'-10 PRINTSYMBOL(C+'0') %REPEAT %END %ROUTINE PPON(%RECORD (PEF) %NAME P) !----------------------------------- %IF MONIT=0 %THEN %RETURN WRITE(P_SER, 3) WRITE(P_REPLY, 3) PUTHEX(P_A); PUTHEX(P_B); PUTHEX(P_C) NEWLINE %END %ROUTINE PRINT BSP !----------------- WRITE( BSP_REPLY, 4) WRITE( BSP_TASK PORT, 4) WRITE( BSP_DEST, 4) WRITE( BSP_DEST PORT, 4) WRITE( BSP_STATE, 4) WRITE(BSP_TSTATE, 4) WRITE(BSP_RSTATE, 4) WRITE(BSP_TSEQ, 4) WRITE(BSP_RSEQ, 4) WRITE(BSP_FAULT COUNT, 4) WRITE(BSP_TGAH, 4) WRITE(BSP_RGAH, 4) NEWLINE %END %ROUTINE ERROR(%INTEGER N) !------------------------- bsp errors PRINTSTRING("TSBSP: illegal bsp from ") WRITE(HOLD BUF_ADDRESS, 3) WRITE(N, 3) WRITE(BSP_RSTATE, 3) PUT HEX(HOLD BUF_RCOMM) WRITE(BSP_TSTATE, 3) PUT HEX(HOLD BUF_TCOMM) NEWLINE PRINT BSP %END %ROUTINE IERROR(%INTEGER N, EVENT) !--------------------------------- PRINTSTRING("TSBSP: state table error ") WRITE(N, 3); WRITE(EVENT, 3); NEWLINE PRINT BSP %END %ROUTINE PERROR(%INTEGER N) !-------------------------- PRINTSTRING("TSBSP: illegal parameters from task ") WRITE(P_REPLY, 3) WRITE(N, 3) WRITE(P_FN, 3); WRITE(P_GATE PORT, 3) WRITE(P_TASK PORT, 3) NEWLINE %END %ROUTINE GET BUFFER !------------------ OP_FN=REQUEST BUFFER; OP_C1=LONG BLOCK OP_SER=BUFFER MANAGER; OP_REPLY=OWN ID OP_S1=GPORT PPON(OP) PON(OP) %END %ROUTINE FREE BUFFER(%RECORD (MEF) %NAME MES) !-------------------------------------------- OP_FN=RELEASE BUFFER; OP_SER=BUFFER MANAGER OP_REPLY=OWN ID; OP_MES==MES PPON(OP) PON(OP) %END %ROUTINE TO NS(%INTEGER FN) !-------------------------- OP_FN=FN; OP_S1=GPORT OP_MES==HOLD BUF; HOLD BUF==NIL OP_SER=NAME SERVER; OP_REPLY=OWN ID PPON(OP) PON(OP) %END %ROUTINE SEND UP(%INTEGER FN) !---------------------------- OP_SER=BSP_REPLY; OP_REPLY=BSP SER OP_FN=FN; OP_TASK PORT=GPORT; !reverse port numbers !!!!!!!! OP_GATE PORT=BSP_TASK PORT PPON(OP) PON(OP) %WHILE INT='S' %CYCLE; %REPEAT %END %ROUTINE SEND UP BUFFER(%INTEGER FN) !----------------------------------- OP_MES==HOLD BUF HOLD BUF==NIL SEND UP(FN) %END !------------------------------------------------------------------------------- ! Routines to handle the allocation and freeing of ports ! uses a cyclic pointer for allocation %OWNINTEGER LAST ALLOC PORT=2 %OWNRECORD (BSPF) %ARRAY BS(1:BSLIMIT) %RECORD (BSPF) %MAP ALLOCATE PORT !-------------------------------- %INTEGER I %FOR I=2,1,BSLIMIT %CYCLE LAST ALLOC PORT=LAST ALLOC PORT+1 %IF LAST ALLOC PORT>BSLIMIT %THEN LAST ALLOC PORT=2 BSP==BS(LAST ALLOC PORT) %IF BSP_STATE=FREE %START GPORT=LAST ALLOC PORT CLEAR QUEUES; !used to initialise the BSP info %RESULT==BSP %FINISH %REPEAT GPORT=0 %RESULT==NIL %END %ROUTINE FREE BSP !---------------- BSP=0; !Clear the record BSP_STATE=FREE %END !End of declarations for the bs ports !------------------------------------------------------------------------------- !These routines are to convert from the internal representation of TS !parameters to and from the forms required for TSBSP (referred to as n !for network) and the TS interface (i). !The internal form is an array of strings. %OWNSTRING (MAXPARAMLEN) %ARRAY PARAM(1:4) %ROUTINE COPY N TO PARAMS(%INTEGER START, %INTEGER LEN, N) !---------------------------------------------------------------- !LEN is the network length , N the number of params %INTEGER I, SINDEX %ROUTINE COPY FRAG(%STRING (*) %NAME S) !-------------------------------------- %INTEGER L,I %IF BYTEINTEGER(START)=0 %THEN %RETURN; !Parameters terminated L=BYTEINTEGER(START)&63; !fragment length START=START+1 %FOR I=1,1,L %CYCLE %IF SINDEX<=MAX PARAM LEN %START CHARNO(S,SINDEX)=BYTEINTEGER(START) SINDEX=SINDEX+1 %FINISH START=START+1 %REPEAT %END %FOR I=1,1,N %CYCLE SINDEX=1; !index into string %WHILE 0 < BYTEINTEGER(START) < 64 %CYCLE COPY FRAG(PARAM(I)) %REPEAT COPY FRAG(PARAM(I)); !Last segment of the parameter %IF SINDEX>MAX PARAM LEN+1 %THEN SINDEX=1; !parameter overflowed LENGTH(PARAM(I))=SINDEX-1 %REPEAT %END %ROUTINE COPY PARAMS TO N(%BYTEINTEGERARRAYNAME TSP, %INTEGER TYPE, N) !--------------------------------------------------------------------- !TSP points to somewhere in HOLDBUF - increment it's length at the end !TYPE is the TS message type, N the number of paramaters %INTEGER INDEX,SINDEX,I,L,SAVE INDEX !INDEX is into the TSP array !SINDEX is into the paramater !TSPUT increments index but only stores the char if it hasn't overflowed %ROUTINE TSPUT(%INTEGER X) !------------------------- %IF INDEX<=235 %START TSP(INDEX)=X INDEX=INDEX+1 %FINISH %END %ROUTINE SCOPY(%STRING (*) %NAME S, %INTEGER LEN) !------------------------------------------------ %INTEGER I %FOR I=1,1,LEN %CYCLE TSPUT(CHARNO(S, SINDEX)) SINDEX=SINDEX+1 %REPEAT %END TSP(0)=TYPE INDEX=1 %FOR I=1,1,N %CYCLE SAVE INDEX=INDEX L=LENGTH(PARAM(I)) SINDEX=1 %WHILE L>63 %CYCLE TSPUT(63) SCOPY(PARAM(I), 63) L=L-63 %REPEAT TSPUT(L+128); !last fragment SCOPY(PARAM(I), L) %IF INDEX>235 %THEN INDEX=SAVE INDEX %AND %EXIT; !parameter overflowed ! set rest to null %REPEAT TSP(INDEX)=0; !end of parameters L=(INDEX+2) >> 1 SWABA(INTEGER(ADDR(TSP(0))), L) HOLD BUF_LEN=HOLD BUF_LEN + L %END %ROUTINE COPY PARAMS TO I(%RECORD (MEF) %NAME BUF, %INTEGER N) !------------------------------------------------------------- %INTEGER INDEX,I INDEX=0 %FOR I=1,1,N %CYCLE %IF INDEX+LENGTH(PARAM(I))+(N-I) > 249 %START; !no room BUF_PARAMS(INDEX)=0 INDEX=INDEX+1 %CONTINUE %FINISH STRING(ADDR(BUF_PARAMS(INDEX)))=PARAM(I) INDEX=INDEX+LENGTH(PARAM(I))+1 %REPEAT BUF_LEN=INDEX %END %ROUTINE COPY I TO PARAMS(%RECORD (MEF) %NAME BUF, %INTEGER N) !------------------------------------------------------------- %INTEGER I, INDEX %FOR I=1,1,N %CYCLE LENGTH(PARAM(I))=0 %REPEAT %IF BUF==NIL %THEN %RETURN INDEX=0 %FOR I=1,1,N %CYCLE %IF INDEX+BUF_PARAMS(INDEX) > BUF_LEN %START PRINTSTRING("TSBSP: corrupt parameters") PRINT BSP %CONTINUE %FINISH %IF BUF_PARAMS(INDEX) > MAX PARAM LEN %START PRINTSTRING("TSBSP: parameter too long"); NEWLINE PRINTSTRING(STRING(ADDR(BUF_PARAMS(INDEX)))); NEWLINE %FINISH %ELSE %START PARAM(I)=STRING(ADDR(BUF_PARAMS(INDEX))) %FINISH INDEX=INDEX+BUF_PARAMS(INDEX)+1 %REPEAT %END %ROUTINE PREFIX(%STRING (*) %NAME S, P) !-------------------------------------- %STRING (MAX PARAM LEN) T %IF LENGTH(S)+LENGTH(P) + 1 <= MAX PARAM LEN %START T=P."/".S S=T %FINISH %ELSE LENGTH(S)=0 %END !------------------------------------------------------------------------------- ! end of TS parameter shuffling routines !routines to handle messages to the ring driver %ROUTINE SEND TO RING(%INTEGER FN) !--------------------------------- OP_S1=GPORT OP_SER=RING SER OP_REPLY=OWN ID OP_FN=FN PPON(OP) PON(OP) %END %ROUTINE BUF TO RING(%INTEGER FN) !-------------------------------- HOLD BUF_ADDRESS=BSP_DEST HOLD BUF_PORT=BSP_DEST PORT OP_MES==HOLD BUF HOLD BUF==NIL SEND TO RING(FN) %END %ROUTINE SEND COMMAND TO RING(%INTEGER FN, SEQ) !---------------------------------------------- !used to send condensed transfers without buffers OP_B=BSP_DEST PORT OP_C1=BSP_DEST OP_C2=SEQ SEND TO RING(FN) %END !------------------------ ! end of ring communication routines !------------------------------------------------------------------------------- !routines to handle facility names !the names are stored in a byte array FNAMES each followed by the !relevant task id byte !NFNAMES is the number of stored names !NBFNAMES is the number of bytes occupied %OWNINTEGER NFNAMES=0 %OWNINTEGER NBFNAMES=0 %OWNBYTEINTEGERARRAY FNAMES(1:MAX B FNAMES) %ROUTINE ADDFAC(%STRING (*) %NAME FAC) !------------------------------------- %IF NBFNAMES+LENGTH(FAC)+2>MAX B FNAMES %START PRINTSTRING("TSBSP: facility name overflow ".FAC) NEWLINE %RETURN %FINISH STRING(ADDR(FNAMES(NBFNAMES+1)))=FAC NFNAMES=NFNAMES+1 NBFNAMES=NBFNAMES+LENGTH(FAC)+2 FNAMES(NBFNAMES)=P_REPLY; !store task id after the name %END %ROUTINE DELETE FAC(%STRING (*) %NAME FAC) !----------------------------------------- %INTEGER STRING START, I, K STRING START=1; !index into fnames %FOR I=1,1,NFNAMES %CYCLE %IF STRING(ADDR(FNAMES(STRING START)))=FAC %START; !found %FOR K=STRING START+LENGTH(FAC)+2,1,NBFNAMES %CYCLE; !shift down FNAMES(STRING START)=FNAMES(K) STRING START=STRING START+1 %REPEAT NBFNAMES=STRING START-1 NFNAMES=NFNAMES-1 %RETURN %FINISH STRING START=STRING START+FNAMES(STRING START)+2 %REPEAT %END %ROUTINE FIND(%STRING (*) %NAME S, %INTEGERNAME REPLY TASK) !----------------------------------------------------------------- !lookup name S in the facility name, if not found set the reply !task to 0 %INTEGER I, L, STRING START L=LENGTH(S) %FOR I=1,1,L %CYCLE %IF CHARNO(S,I)='.' %OR CHARNO(S,I)='/' %START LENGTH(S)=I-1; !set length down temporarily %EXIT %FINISH %REPEAT STRING START=1 %FOR I=1,1,NFNAMES %CYCLE; !look for name %IF STRING(ADDR(FNAMES(STRING START)))=S %START; !found it REPLY TASK=FNAMES(STRING START+FNAMES(STRING START)+1) LENGTH(S)=L; !restore full length %RETURN %FINISH STRING START=STRING START+FNAMES(STRING START)+2 %REPEAT REPLY TASK=0; !not known %END !end of routines to handle facility names !------------------------------------------------------------------------------- ! general purpose routines ! %ROUTINE DO CLEAR TIMEOUTS !---------------------- BSP_TTIMEOUT=0; BSP_RTIMEOUT=0 %END %ROUTINE CLEAR QUEUES !-------------------- !tidy up BSP record - free queued buffers, clear timeouts, reset counters %UNLESS BSP_HOLDBUF==NIL %THEN FREE BUFFER(BSP_HOLDBUF) %WHILE %NOT BSP_O BUFQ==NIL %CYCLE FREE BUFFER(PULL) %REPEAT BSP_OBUFQ LEN=0 DO CLEAR TIMEOUTS BSP_TGAH=0 BSP_RGAH=0 BSP_TSEQ=15 BSP_RSEQ=0 BSP_RSTATE=NESENT BSP_TSTATE=IDLE BSP_RDY COMMAND=X EXP RDY; !needed to implement expedited RDYs %END %ROUTINE DO DISC N TO I !------------------------ disconnect (CLOSE) from the ring %INTEGER PLEN, PSTART, I, REASON DO CLEAR TIMEOUTS SEND COMMAND TO RING(X DISC RESPONSE, 0); !disconnect response to ring REASON=HOLD BUF_RCOMM&63; !coded reason %FOR I=1,1,2 %CYCLE; !set params to nil LENGTH(PARAM(I))=0 %REPEAT %IF HOLD BUF_TCOMM&(16_FFFE)=DATA COMMAND+CONTROL %START PSTART=ADDR(HOLDBUF_DATA(0)) PLEN=HOLD BUF_LEN-1 %IF PLEN>0 %START SWABA(INTEGER(PSTART),PLEN) %IF BYTEINTEGER(PSTART)=18 %START; !TS disconnect COPY N TO PARAMS(PSTART+1, PLEN, 2) %FINISH %FINISH %FINISH COPY PARAMS TO I(HOLD BUF, 2) OP_S1=REASON SEND UP BUFFER(DISCONNECT) %END %ROUTINE DO RESET N TO I; !params are sent on later - ignore them !----------------------- CLEAR QUEUES OP_S1=HOLD BUF_RCOMM&63 OP_MES==NIL SEND UP(RESET) SEND COMMAND TO RING(X RESET RESPONSE, 0) %END %ROUTINE DO ENABLE OUTPUT(%INTEGER N) !------------------------------------ OP_S1=N SEND UP(ENABLE OUTPUT) BSP_TGAH=BSP_TGAH+N %END !---------------------------------------------------------------------------- ! routine to handle opens and openacks !---------------------------------------------------------------------------- %ROUTINE STOI(%INTEGERNAME ANS, %STRING (*) %NAME S) !----------------------------------- %INTEGER N,I,C N=0 %IF LENGTH(S)=0 %THEN %RETURN %FOR I=1,1,LENGTH(S) %CYCLE C=CHARNO(S, I) %IF '0'<=C<='9' %START N=N*10+C-'0' %FINISHELSE %RETURN; !error %REPEAT ANS=N %END %ROUTINE DECODE QUAL(%STRING (*) %NAME S, %INTEGERNAME FP, RP,FW, RW) !-------------------------------------------------------------------- %STRING (32) PAR %ROUTINE GET2(%STRING (*) %NAME S, %INTEGERNAME F,R) %STRING (32) S1, S2 %IF S->S1.("/").S2 %START STOI(F, S1) STOI(R, S2) %FINISH %END %ROUTINE GETPAR(%STRING (*) %NAME S) %IF S->("W=").S %THEN GET2(S, FW, RW) %ELSEC %IF S->("P=").S %THEN GET2(S, FP, RP) %END FP=MAX DATA SIZE; RP=MAX DATA SIZE FW=FWD WINDOW; RW=0; !reverse window not used %IF LENGTH(S)>32 %THEN %RETURN %IF S->PAR.(",").S %THEN GETPAR(PAR) GETPAR(S) %END %ROUTINE PICK BSP PARAM(%INTEGERNAME MAXT, MAXR, %INTEGER NPARAMS, TYPE) !------------------------------------------------------------------------ !pickup max transfer lengths and TS params from OPEN or OPENACK !TYPE is the TS type code for accept or connect %INTEGER I, L, N, PSTART, PLEN L=HOLD BUF_LEN %IF L<2 %THEN MAXT=-1 %ANDRETURN; !error MAXT=1024; !default values? MAXR=1024 %FOR I=1,1,NPARAMS %CYCLE; !set params to null LENGTH(PARAM(I))=0 %REPEAT %IF L=2 %THEN %RETURN N=HOLDBUF_BSP PARAM LEN; !number of parameters %IF L0 %THEN MAXT=HOLD BUF_BSP PARAM(1) %IF N>1 %THEN MAXR=HOLD BUF_BSP PARAM(2) PSTART=ADDR(HOLD BUF_BSP PARAM(N+1)) PLEN=L-(3+N) %IF PLEN>0 %START SWABA(INTEGER(PSTART), PLEN) %IF BYTEINTEGER(PSTART)# TYPE %THEN %RETURN; !ignore TS params COPY N TO PARAMS(PSTART+1, PLEN, NPARAMS) %FINISH %END %ROUTINE DO PROCESS OPENACK !-------------------------- %INTEGER RC, MAXR, MAXT %IF HOLD BUF_LEN<2 %THEN %RETURN; !too short DO CLEAR TIMEOUTS FREE BUFFER(BSP_HOLD BUF); !user params not needed any more, they were !kept in case the connect needed to be repeated BSP_HOLD BUF==NIL RC=HOLD BUF_FUNCTION; !return code %IF RC=0 %START; !connection made PICK BSP PARAM(MAXT, MAXR, 3, 17) %IF MAXT<0 %THEN ERROR(0) %AND %RETURN PARAM(2)="P=".ITOS(MAXT-4, -1)."/".ITOS(MAXR-4, -1) BSP_DEST PORT=HOLD BUF_REPLY PORT COPY PARAMS TO I(HOLD BUF, 3) SEND UP BUFFER(ACCEPT CALL) %RETURN %FINISH !connect failed - reply up as a disconnect PICK BSP PARAM(MAXT, MAXR, 2, 18) COPY PARAMS TO I(HOLD BUF, 2) OP_S1=RC SEND UP BUFFER(DISCONNECT) NEW STATE=FREE %END %ROUTINE DO DPROCESS OPENACK !--------------------------- !process openack for a connection which has been disconnected by !the task %IF HOLD BUF_LEN<2 %THEN %RETURN; !too short %IF HOLD BUF_FUNCTION # 0 %THEN NEW STATE=FREE %AND %RETURN; !Rejected anyway BSP_DEST PORT=HOLD BUF_REPLY PORT SEND COMMAND TO RING(X DISC ABANDON, 0); !the original disconnect params !have been discarded %END %ROUTINE SETUP OPEN(%RECORD (MEF) %NAME B, %INTEGER COMMAND, REPLY PORT, %C X, BST, BSR) !------------------------------------------------------------------------ !build open or openack buffer B_COMMAND=COMMAND B_REPLY PORT=REPLY PORT B_FUNCTION=X; !function code or reply code B_BSP PARAM LEN=2 B_BSP PARAM(1)=BST B_BSP PARAM(2)=BSR; !max buffer sizes B_LEN=5 %END %ROUTINE SEND OPEN NACK TO RING(%INTEGER CODE) !-------------------------------------------- SETUP OPEN(HOLD BUF, OPENACK, 0, CODE, 0, 0) COPY PARAMS TO N(HOLD BUF_TSPARAMS, 18, 2); !disconnect BUF TO RING( XDATA+RELEASE FLAG ) %END %ROUTINE DO CONN N FAILED !------------------------ !task has rejected a connect from the ring - send open nack %IF P_S1=0 %THEN P_S1=18; !error code must be non-zero COPY I TO PARAMS(HOLD BUF, 2) PREFIX(PARAM(1), ME) SEND OPEN NACK TO RING(P_S1) %END %ROUTINE DO INT OPEN NACK !------------------------ !abort a connection while waiting for accept (or disc) from task !because of protocol error from the ring PARAM(1)=ME PARAM(2)="Connect not accepted yet" SEND OPEN NACK TO RING(33); !protocol error %END %ROUTINE DO SEND OPEN ACK !------------------------ accept from the interface %INTEGER FP,RP,FW,RW COPY I TO PARAMS(HOLD BUF, 3) PREFIX(PARAM(1), ME); !recall address DECODE QUAL(PARAM(2), FP, RP, FW, RW) BSP_FWIN=RW LENGTH(PARAM(2))=0 SETUP OPEN(HOLD BUF, OPENACK, GPORT, 0, FP+4, RP+4) COPY PARAMS TO N(HOLD BUF_TS PARAMS, 17, 3) BUF TO RING( XDATA+RELEASE FLAG ) %END %ROUTINE CONN I DISC(%INTEGER FN, ERR TYPE) !--------------------------------- !disconnect reply to a connect from user task %OWNSTRING (23) %ARRAY CONN ERR(1:4)= %C "name server not working", %C "name not known", %C "no free connections", %C "out of order" %OWNINTEGERARRAY ERR CODE(1:4)=16,19,23,18 PARAM(1)=ME PARAM(2)=CONN ERR(ERR TYPE) %UNLESS HOLD BUF==NIL %THEN COPY PARAMS TO I(HOLD BUF,2) OP_S1=ERR CODE(ERR TYPE) %IF FN=CONNECT %THEN SEND UP BUFFER(DISCONNECT) %ELSEC SEND UP BUFFER(DATAGRAM REPLY) NEW STATE=FREE %END %ROUTINE DO NS MESS !------------------ !reply from name server - send off open unless the lookup failed %INTEGER FUNCTION CODE, OPERATION, FP, RP, FW, RW OPERATION=BSP_DEST; !connect or datagram %IF P_FN#0 %THEN CONN I DISC(OPERATION, NS ERROR) %AND %RETURN; !fail the connect %IF HOLD BUF_NS REPLY_CODE#0 %THEN CONN I DISC(OPERATION, LOOKUP FAILED) %ANDC %RETURN !lookup succeeded - send off the connect BSP_DEST=HOLD BUF_NS REPLY_ADDRESS BSP_DEST PORT=HOLD BUF_NS REPLY_PORT FUNCTION CODE=HOLD BUF_NS REPLY_FUNCTION COPY I TO PARAMS(BSP_HOLD BUF, 4) %IF OPERATION=DATAGRAM %THEN SEND DG(FUNCTION CODE) %AND %RETURN !connect and datagram from the user have been treated identically so far DECODE QUAL(PARAM(3), FP, RP, FW, RW) BSP_FWIN=FW PARAM(1)=HOLD BUF_NS REPLY_REST OF NAME PREFIX(PARAM(2), ME) LENGTH(PARAM(3))=0; !quality of service SETUP OPEN(HOLD BUF, OPEN, GPORT, FUNCTION CODE, RP+4, FP+4) COPY PARAMS TO N(HOLD BUF_TS PARAMS, 16, 4) BUF TO RING( XDATA+RELEASE FLAG ) BSP_TTIMEOUT=(TIME+CONN TIMEOUT)!1 %END %STRING (*) %MAP FIND PORT(%STRING (*) %NAME FACILITY, %INTEGERNAME FAIL) !------------------------------------------------------------------------ %INTEGER REPLY TASK %STRING (*) %NAME S FAIL=0 S=="" FIND(FACILITY, REPLY TASK) %IF REPLY TASK=0 %START FAIL=19 S=="invalid address" %FINISHELSESTART BSP==ALLOCATE PORT %IF BSP==NIL %START FAIL=17 S=="busy" %FINISH %FINISH %IF FAIL#0 %START; !use bs(1) to send the open nack GPORT=1 BSP==BS(1) %FINISH BSP_DEST=HOLD BUF_ADDRESS BSP_DEST PORT=HOLD BUF_REPLY PORT BSP_REPLY=REPLY TASK %RESULT==S %END %ROUTINE DG MESSAGE FOR I(%STRING (*) %NAME S) !--------------------------------------------- !datagram or reply from the network, copy the message into S by !inserting it's length preceding it and using a string copy %INTEGER L SWABA(INTEGER(ADDR(HOLD BUF_DATA(2) )), HOLD BUF_LEN-2) L=(HOLD BUF_LEN-2)<<1 - (HOLD BUF_COMMAND & 1) HOLD BUF_DATA(1)=L; !string length S=STRING(ADDR(HOLD BUF_DATA(1))) %END %ROUTINE FORMAT DG AND SEND(%INTEGER COMMAND, PORT, FN, %STRING (*) %NAME S) !------------------------------------------------------------------ %INTEGER L L=LENGTH(S) HOLD BUF_COMMAND=COMMAND+(L&1) HOLD BUF_REPLY PORT=PORT !copy string chars to DATA(2) onwards (the length goes in DATA(1) temporarily) STRING(ADDR( HOLD BUF_DATA(1)))=S HOLD BUF_FUNCTION=FN; !same as DATA(0:1) L=(L+1) >> 1 SWABA( INTEGER( ADDR( HOLD BUF_DATA(2) ) ), L) HOLD BUF_LEN=L+2 BUF TO RING(XDATA+RELEASE FLAG) %END %ROUTINE DO DG REPLY ITON !------------------------ COPY I TO PARAMS(HOLD BUF, 2) FORMAT DG AND SEND(OPENACK, 0, P_S1, PARAM(2)) %END %INTEGERFN PORT OPEN; !returns 1 if the destination specified in the !------------------- !open in HOLD BUF is already in use %INTEGER I %FOR I=2,1,BSLIMIT %CYCLE %IF BS(I)_DEST=HOLD BUF_ADDRESS %ANDC BS(I)_DESTPORT=HOLD BUF_REPLY PORT %THEN %RESULT=1 %REPEAT %RESULT=0 %END %ROUTINE DO PROCESS DG !--------------------- %INTEGER CODE %STRING (*) %NAME S, MES %IF HOLD BUF_LEN<2 %THEN %RETURN; !rubbish %IF PORT OPEN#0 %THEN %RETURN; !specified destination in use %IF 0< HOLD BUF_FUNCTION <26 %THEN S==FUNCTION NAME(HOLD BUF_FUNCTION) %ELSEC S=="**"; !error name MES==FIND PORT(S, CODE) %IF CODE #0 %START FORMAT DG AND SEND(OPEN ACK, 0, CODE, MES) %RETURN %FINISH DG MESSAGE FOR I(PARAM(4)); !get message into param 4 LENGTH(PARAM(1))=0; !dont know called address PARAM(2)=S LENGTH(PARAM(3))=0 COPY PARAMS TO I(HOLD BUF, 4) SEND UP BUFFER(DATA GRAM) BSP_STATE=WT DG REPLY I %END %ROUTINE SEND DG(%INTEGER FN CODE) !--------------------------------- FORMAT DG AND SEND(SSP REQUEST, GPORT, FN CODE, PARAM(4)) FREE BUFFER(BSP_HOLD BUF); !Not needed as there are no retransmissions BSP_HOLD BUF==NIL BSP_TTIMEOUT=(TIME+CONN TIMEOUT)!1 NEWSTATE=WT DG REPLY N %END %ROUTINE DO DG REPLY NTOI !------------------------ OP_S1=HOLD BUF_FUNCTION DG MESSAGE FOR I(PARAM(2)) LENGTH(PARAM(1))=0 COPY PARAMS TO I(HOLD BUF,2) SEND UP BUFFER(DATAGRAM REPLY) %END %ROUTINE DO PROCESS OPEN !----------------------- %INTEGER CODE, MAXT, MAXR %STRING (*) %NAME MES %IF HOLD BUF_LEN < 2 %THEN %RETURN; !too short !see if there's already a port allocated to this DEST,PORT if so the OPEN !is ignored. Not even an OPENNACK is sent %IF PORT OPEN#0 %THEN %RETURN; !destination in use PICK BSP PARAM(MAXT, MAXR, 4, 16) %IF MAXT < 0 %THEN %RETURN; !ignore it !MAXT and MAXR are the max block sizes,t => away from me PARAM(3)="P=".ITOS(MAXR-4, -1)."/".ITOS(MAXT-4, -1) !if function code set and called address not then pickup fixed name %IF LENGTH(PARAM(1))=0 %AND 0 < HOLD BUF_FUNCTION < 26 %THENC PARAM(1)=FUNCTION NAME(HOLD BUF_FUNCTION) MES==FIND PORT( PARAM(1), CODE) %IF CODE # 0 %START PARAM(1)=ME PARAM(2)=MES SEND OPEN NACK TO RING(CODE) %RETURN %FINISH !is sent off as well COPY PARAMS TO I(HOLD BUF, 4) SEND UP BUFFER(CONNECT) BSP_STATE=WT ACCI %END %ROUTINE DO INT DISC !------------------------------ CLEAR QUEUES OP_S1=42; !abandon by TS OP_MES==NIL SEND UP(DISCONNECT) SEND COMMAND TO RING(X DISC ABANDON, 0) BSP_TTIMEOUT=(TIME+DISC TIMEOUT)!1 %END %ROUTINE SETUP DISC(%RECORD (MEF) %NAME BUF, %INTEGER COMMAND, TYPE) !------------------------------------------------------------------- BUF_RCOMM=COMMAND BUF_TCOMM=DATA COMMAND+CONTROL BUF_LEN=1 COPY PARAMS TO N(BUF_DATA, TYPE, 2) %END %ROUTINE DO DISC I RESPONSE !-------------------------- OP_S1=1 OP_MES==NIL SEND UP(DISCONNECT) %END %ROUTINE DO DISC I TO N !---------------------- !send a disconnect on to the ring CLEAR QUEUES DO DISC I RESPONSE COPY I TO PARAMS(HOLD BUF, 2) PREFIX(PARAM(1),ME) %IF HOLD BUF==NIL %START; !no buffer from user %IF P_S1=0 %THEN SEND COMMAND TO RING(X DISC OK,0) %ELSEC SEND COMMAND TO RING(X DISC ABANDON, 0) %FINISHELSESTART SETUP DISC(HOLD BUF, CLOSE+P_S1, 18) BUF TO RING( XDATA+RELEASE FLAG ) %FINISH BSP_TTIMEOUT=(TIME+DISC TIMEOUT)!1 %END %ROUTINE DO RESET I RESPONSE !--------------------------- OP_S1=1; !response OP_MES==NIL SEND UP(RESET) %END %ROUTINE DO RESET I TO N !----------------------- ignore params for now CLEAR QUEUES DO RESET I RESPONSE SEND COMMAND TO RING(XRESET, 0) BSP_TTIMEOUT=(TIME+DISC TIMEOUT)!1 %END %ROUTINE DO SEND LOOKUP; !to the name server !---------------------- HOLD BUF_NS_NAME=STRING(ADDR(BSP_HOLD BUF_PARAMS(0))) TO NS(LOOKUP) %END %ROUTINE DO Q DATA I !------------------- !'data' command from task, ENABLE INPUT, PUT OUTPUT or EXPEDITED DATA %INTEGER L %IF P_FN=ENABLE INPUT %START BSP_RGAH=BSP_RGAH+P_S1 %IF BSP_STATE=DATA %AND BSP_RSTATE=NESENT %THEN SEND RDY %RETURN %FINISH %IF P_FN=PUT OUTPUT %START %IF BSP_TGAH<=0 %THEN %RETURN; !ignore it BSP_TGAH=BSP_TGAH - 1 L=HOLD BUF_LEN; !convert buffer for ring SWABA(HOLD BUF_DATA(0), (L+1)>>1 ) HOLD BUF_LEN=(L+3) >> 1; !bbp pkt count HOLD BUF_TCOMM=(L&1)+(P_S1 << 1); !odd byte flag + push HOLD BUF_LINK==BSP_O BUF Q BSP_O BUF Q==HOLD BUF BSP_OBUFQ LEN=BSP_OBUFQ LEN + 1 HOLD BUF==NIL %IF BSP_STATE=DATA %AND BSP_TSTATE=NESENT %START SEND DATA %FINISH %RETURN %FINISH %IF P_FN=EXPEDITED DATA %START; !not implemented yet PERROR(2) %RETURN %FINISH PERROR(4) %END %ROUTINE START CONNECT(%INTEGER FN) !--------------------- !allocate port and initialise record, ask for buffer to interrogate !name server BSP==ALLOCATE PORT %IF BSP==NIL %START !the low level routines need a BSP record so use BS(1) GPORT=0 BSP==BS(1) BSP_REPLY=P_REPLY BSP_TASK PORT=P_TASK PORT CONN I DISC(FN, NO PORTS) %RETURN %FINISH BSP_TASK PORT=P_TASK PORT BSP_REPLY=P_REPLY BSP_HOLD BUF==HOLD BUF HOLD BUF==NIL GET BUFFER BSP_STATE=WT BUF BSP_DEST=FN; !FN is either CONNECT or DATAGRAM, picked up later !on the reply from the name server %END !------------------------------------------------------------------------- ! Routines to handle BSP ie RDY NOTRDY DATA NODATA !------------------------------------------------------------------------- %ROUTINE SEND NOTRDY !------------------- SEND COMMAND TO RING(XNOTRDY, BSP_RSEQ) BSP_RSTATE=NESENT BSP_RTIMEOUT=0 %END %ROUTINE SEND RDY !---------------- SEND COMMAND TO RING(BSP_RDY COMMAND, BSP_RSEQ) BSP_RSTATE=ESENT BSP_RTIMEOUT=TIME %END %ROUTINE SEND NODATA !------------------- SEND COMMAND TO RING(XNODATA, BSP_TSEQ) BSP_TSTATE=NESENT BSP_TTIMEOUT=0 %END %ROUTINE SEND DATA AGAIN !----------------------- OP_MES==BSP_HOLD BUF SEND TO RING(XDATA) BSP_TSTATE=ESENT BSP_TTIMEOUT=TIME %END %ROUTINE SEND DATA !----------------- !need to take buffer from output queue, add the BSP bits and send !it. The buffer pointer is kept in BSP_HOLD BUF %RECORD (MEF) %NAME BUF BUF==PULL; !pull buffer from end of BSP output buffer q BSP_OBUFQ LEN=BSP_OBUFQ LEN - 1 BUF_ADDRESS=BSP_DEST BUF_PORT=BSP_DEST PORT BUF_RCOMM=0 BUF_TCOMM=BSP_TSEQ<<8 + DATA COMMAND + (BUF_TCOMM & 3) BSP_HOLD BUF==BUF SEND DATA AGAIN; !not really again, this is the first time %END %ROUTINE DO DATA ACK; !data has been acknowledged !------------------- FREE BUFFER(BSP_HOLD BUF) BSP_HOLD BUF==NIL %IF BSP_FAULTCOUNT>0 %THEN BSP_FAULT COUNT=BSP_FAULT COUNT-1 BSP_TTIMEOUT=0 %END %ROUTINE DO I NOTRDY !------------------- %INTEGER SQ,SQIN SQ=BSP_TSEQ SQIN=HOLD BUF_RCOMM>>8 & 15 %IF (SQ+1) & 15 = SQIN %AND BSP_TSTATE=ESENT %START DO DATA ACK BSP_TSTATE=IDLE %FINISH %END %ROUTINE DO I RDY !---------------- %INTEGER SQ,SQIN, ENABLES SQ=BSP_TSEQ SQIN=HOLD BUF_RCOMM >>8 & 15 %IF (SQ+1) & 15 = SQIN %START %IF BSP_TSTATE=ESENT %THEN DO DATA ACK %IF BSP_TSTATE=ESENT %OR BSP_TSTATE=IDLE %START !got a rdy so allow the user to send data ENABLES=BSP_FWIN - (BSP_TGAH + BSP_OBUFQ LEN) %IF ENABLES>0 %THEN DO ENABLE OUTPUT( ENABLES ) BSP_TSEQ=SQIN; !increment seq count %IF BSP_OBUFQ==NIL %START SEND NODATA %FINISHELSESTART SEND DATA %FINISH %FINISH %FINISHELSESTART %IF SQ=SQIN %START; !repeated RDY send data again %IF BSP_TSTATE=NESENT %START SEND NODATA %FINISHELSESTART SEND DATA AGAIN %FINISH %FINISH %FINISH %END %ROUTINE PROCESS DATA !-------------------- SWABA(HOLD BUF_DATA(0), HOLD BUF_LEN-1) HOLD BUF_LEN=(HOLD BUF_LEN << 1)-2-(HOLD BUF_TCOMM & 1); !byte length OP_S1=0; !assume data not pushed !to preserve compatability with BSP treat CLOSE REQ as push %IF HOLD BUF_TCOMM & (PUSH FLAG+CLOSE REQ) #0 %THEN OP_S1=1 BSP_RGAH=BSP_RGAH-1 SEND UP BUFFER(INPUT HERE) %END %ROUTINE DO I NODATA !------------------- %INTEGER SQIN SQIN=HOLD BUF_TCOMM>>8 & 15 %IF BSP_RSEQ=SQIN %AND BSP_RSTATE=ESENT %START BSP_RTIMEOUT=(TIME+IDLE TIMEOUT) ! 1; !idle timeout BSP_RSTATE=IDLE %FINISH %END %ROUTINE DO I DATA !----------------- %INTEGER SQIN SQIN=HOLD BUF_TCOMM>>8 & 15 %IF BSP_RSEQ=SQIN %ANDC (BSP_RSTATE=ESENT %OR BSP_RSTATE=IDLE) %START !increment seq number BSP_RSEQ=(SQIN+1) & 15 BSP_RTIMEOUT=0 %IF BSP_FAULT COUNT>0 %THEN BSP_FAULT COUNT=BSP_FAULT COUNT-1 %IF HOLD BUF_TCOMM & EXPEDITED # 0 %START BSP_RDY COMMAND=X EXP RDY %FINISHELSESTART BSP_RDY COMMAND=X RDY %FINISH %IF HOLD BUF_TCOMM & CONTROL=0 %THEN PROCESS DATA %IF BSP_RGAH>0 %START SEND RDY %FINISHELSESTART SEND NOTRDY %FINISH %RETURN %FINISH %IF BSP_RSEQ=(SQIN+1) & 15 %START; !repeated data %IF BSP_RSTATE=ESENT %START SEND RDY %FINISHELSESTART %IF BSP_RSTATE=NESENT %THEN SEND NOTRDY %FINISH %FINISH %END %ROUTINE DO Q DATA N !----------------- %INTEGER C C=HOLD BUF_RCOMM & 16_F000 %IF C#0 %START %IF C=RDY %START DO I RDY %FINISHELSESTART %IF C=NOTRDY %START DO I NOTRDY %FINISHELSESTART ERROR(3) %RETURN %FINISH %FINISH %FINISH C=HOLD BUF_TCOMM & 16_F000 %IF C#0 %START %IF C=DATA COMMAND %START DO I DATA %FINISHELSESTART %IF C=NODATA %START DO I NODATA %FINISHELSESTART ERROR(4) %RETURN %FINISH %FINISH %FINISH %END %ROUTINE DO T TIMEOUT !-------------------- %IF BSP_FAULTCOUNT=0 %START PRINTSTRING("TSBSP: transmitter timeout to ") WRITE(BSP_DEST,1) NEWLINE %FINISH BSP_FAULT COUNT=BSP_FAULT COUNT+1 %IF BSP_FAULT COUNT>FAULT COUNT LIMIT %START DO INT DISC BSP_STATE=WT DIN %FINISHELSESTART %IF BSP_TSTATE=ESENT %START SEND DATA AGAIN %FINISH %FINISH %END %ROUTINE DO R TIMEOUT !---------------- %IF BSP_RSTATE#IDLE %START %IF BSP_FAULT COUNT=0 %START PRINTSTRING("TSBSP: receiver timeout ") WRITE(BSP_DEST, 1) NEWLINE %FINISH BSP_FAULT COUNT=BSP_FAULT COUNT+1 %FINISH %IF BSP_FAULT COUNT>FAULT COUNT LIMIT %START DO INT DISC BSP_STATE=WT DIN %FINISHELSESTART %IF BSP_RSTATE=IDLE %OR BSP_RSTATE=ESENT %THEN SEND RDY %FINISH %END %ROUTINE PERFORM(%INTEGER ACTION) !-------------------------------- !called from the state table routine %SWITCH SW(1:MAX ACTION) ->SW(ACTION) SW(SEND LOOKUP): DO SEND LOOKUP %RETURN SW(CLEAR TIMEOUTS): DO CLEAR TIMEOUTS %RETURN SW(DISC I RESPONSE): DO DISC I RESPONSE %RETURN SW(Q DATA I): DO Q DATA I %RETURN SW(NS MESS): DO NS MESS %RETURN SW(PROCESS OPENACK): DO PROCESS OPENACK %RETURN SW(RETRANS CONN): %IF BSP_FAULT COUNT>FAULT COUNT LIMIT %START CONN I DISC(BSP_TSTATE, NO REPLY) %FINISHELSESTART GET BUFFER %FINISH %RETURN SW(D PROCESS OPENACK): DO D PROCESS OPENACK %RETURN SW(DISCI WT ACCN):; !process disci while waiting for accept from ring FREE BUFFER(BSP_HOLD BUF) BSP_HOLD BUF==NIL DO DISC I RESPONSE %RETURN SW(DISC I TO N): DO DISC I TO N %RETURN SW(INT DISC): DO INT DISC %RETURN SW(RESET N TO I): DO RESET N TO I %RETURN SW(RESET I TO N): DO RESET I TO N %RETURN SW(Q DATA N): DO Q DATA N %RETURN SW(DISC N TO I): DO DISC N TO I %RETURN SW(RESET I RESPONSE): DO RESET I RESPONSE %RETURN SW(RESET N RESPONSE): SEND COMMAND TO RING(X RESET, 0) %RETURN SW(CONN N FAILED): DO CONN N FAILED %RETURN SW(INT OPEN NACK): DO INT OPEN NACK %RETURN SW(SEND OPENACK): DO SEND OPENACK %RETURN SW(DISC TO I): OP_S1=42; !connection abandoned OP_MES==NIL SEND UP(DISCONNECT) %RETURN SW(DG REPLY NTOI): DO DG REPLY NTOI %RETURN SW(DG REPLY ITON): DO DG REPLY ITON %RETURN %END %ROUTINE STATE TABLE(%INTEGER EVENT) !------------------- %INTEGER ACTION, I %IF EVENT=0 %THEN IERROR(6, EVENT) %ANDRETURN %IF 0 < BSP_STATE <= MAX STATE %START I=(EVENT-1)*MAX STATE + BSP_STATE ACTION=ACTS( I ) NEW STATE=STATES( I ) %IF ACTION=ERROR ACTION %THEN IERROR(4, EVENT) %AND %RETURN %IF ACTION#NULL ACTION %THEN PERFORM(ACTION) %IF NEW STATE=FREE %START CLEAR QUEUES FREE BSP %FINISH BSP_STATE=NEW STATE %FINISHELSE IERROR(5, EVENT) %END %ROUTINE FROM BELOW !------------------ %INTEGER C !message from the ring handler %IF P_FN=R INPUT DONE %START HOLD BUF==P_MES P_MES==NIL GPORT=HOLD BUF_PORT BSP==BS(GPORT) !validate the input %IF BSP_STATE#FREE %AND BSP_DEST#HOLD BUF_ADDRESS %START ERROR(1) %RETURN %FINISH C=HOLD BUF_RCOMM & 16_FF00 %IF C=CLOSE %THEN STATE TABLE(DISC N) %ANDRETURN %IF C=RESET COMMAND %THEN STATE TABLE(RESET N) %ANDRETURN %IF GPORT=1 %START %IF C=OPEN %THEN DO PROCESS OPEN %IF C=SSP REQUEST %THEN DO PROCESS DG %RETURN %FINISH %IF C=OPENACK %THEN STATE TABLE(OPEN ACKN) %ANDRETURN STATE TABLE(DATAN); !assume it's data %FINISHELSESTART BSP==BS(P_S1) %IF P_FN=TRANSFER ERROR %THEN BSP_FAULT COUNT=BSP_FAULT COUNT+2 %FINISH %END %ROUTINE FROM BUFF MAN !--------------------- GPORT=P_S1 BSP==BS(GPORT) HOLD BUF==P_MES P_MES==NIL STATE TABLE(GOT BUFFER) %END %ROUTINE OUTBSPS !--------------- print info about all BS streams !The printing is done one line at a time on each clock tick !to avoid over loading the console buffering routine !INT is left at ? until it's all done %INTEGER I %OWNINTEGER PR INDEX=0 %IF PR INDEX=0 %START PRINDEX=2 PRINTSTRING(" task t-pt dest d-pt stat t-st r-st t-sq r-sq flts t-gh r-gh") NEWLINE %RETURN %FINISH %FOR I=PR INDEX,1,BSLIMIT %CYCLE BSP==BS(I) %IF BSP_STATE#FREE %START WRITE(I,2); PRINTSYMBOL(':') PRINT BSP PR INDEX=I+1; !for next entry %RETURN %FINISH %REPEAT PR INDEX=0; !ready for next time INT=0 %END %ROUTINE FROM CLOCK !------------------ %INTEGER I %IF INT='M' %THEN MONIT=1 %IF INT='O' %THEN MONIT=0 %IF INT='?' %THEN OUTBSPS %ELSE INT=0 ALARM(TICKS) TIME=TIME+1 %IF TIME=0 %THEN TIME=1; !time=0 implies time not set %FOR I=2,1,BSLIMIT %CYCLE GPORT=I BSP==BS(GPORT) %IF BSP_STATE=FREE %THEN %CONTINUE %IF BSP_TTIMEOUT#0 %AND TIME-BSP_TTIMEOUT>TIMEOUT %START BSP_TTIMEOUT=0 %IF BSP_STATE=DATA %THEN DO T TIMEOUT %ELSE STATETABLE(TIME OUTT) %FINISH %IF BSP_RTIMEOUT#0 %AND TIME-BSP_RTIMEOUT>TIMEOUT %START BSP_RTIMEOUT=0 %IF BSP_STATE=DATA %THEN DO R TIMEOUT %FINISH %REPEAT %END %ROUTINE FROM ABOVE !------------------ %STRING (*) %NAME FAC %INTEGER I %OWNBYTEINTEGERARRAY FA ACTION(0:DATAGRAM REPLY)=0, 0, ACCI, DISCI, DATAI, DATAI, DATAI, RESETI, 0, DGREPLY I %IF P_FN=ENABLE FACILITY %OR P_FN=DISABLE FACILITY %START %IF P_S1=0 %THEN FAC==P_FACILITY %ELSEC FAC==STRING(ADDR( P_MES_PARAMS(0) ) ) DELETE FAC(FAC) %IF P_FN=ENABLE FACILITY %THEN ADD FAC(FAC) %IF P_S1#0 %THEN FREE BUFFER(P_MES) %RETURN %FINISH GPORT=P_GATE PORT %IF P_FN<=DATAGRAM REPLY %AND GPORT<=BS LIMIT %START %UNLESS P_FN=ENABLE INPUT %ORC (P_FN=EXPEDITED DATA %AND P_S1#0) %THEN HOLD BUF==P_MES %IF GPORT=0 %START %IF P_FN=CONNECT %THEN START CONNECT(CONNECT) %ANDRETURN %IF P_FN=DATAGRAM %THEN START CONNECT(DATA GRAM) %ANDRETURN %IF P_FN=DISCONNECT %START; !special for disconnect before accept %FOR I=2,1,BSLIMIT %CYCLE BSP==BS(I) %IF BSP_TASK PORT=P_TASK PORT %START GPORT=I STATE TABLE(DISCI) %RETURN %FINISH %REPEAT %FINISH PERROR(1) %RETURN %FINISH BSP==BS(GPORT) %IF BSP_TASK PORT#0 %START %IF BSP_TASKPORT#P_TASK PORT %START PERROR(3) %RETURN %FINISH %FINISH %ELSE BSP_TASK PORT=P_TASK PORT STATE TABLE(FA ACTION(P_FN)) %FINISH %END %ROUTINE FROM NS !--------------- GPORT=P_S1 BSP==BS(GPORT) %IF P_FN=0 %THEN HOLD BUF==P_MES STATE TABLE(NS REPLY) %END %ROUTINE FIND MY NAME !-------------------- GET BUFFER P_SER=0; POFF(P) PPON(P) !must be reply from buff man HOLD BUF==P_MES TO NS(OWN NAME) P_SER=0; POFF(P) PPON(P) !must be reply from name server %IF P_FN#0 %START PRINTSTRING("TSBSP: name server not working") NEWLINE LENGTH(ME)=0 %RETURN %FINISH %IF LENGTH(P_MES_NS_NAME)>16 %THEN LENGTH(P_MES_NS_NAME)=16 ME=P_MES_NS_NAME PRINTSTRING("TSBSP: open on ".ME) NEWLINE FREEBUFFER(P_MES) %END !main program starts here !----------------------- MAP VIRT(BUFFER MANAGER, 5, 4) MAP VIRT(BUFFER MANAGER, 6, 5) LINKIN(BSP SER) %FOR I=1,1,BSLIMIT %CYCLE; !clear bsp records BS(I)=0 BS(I)_STATE=FREE %REPEAT CHANGE OUT ZERO=T3 SER; !output stream 0 to tty buffering task !find out the name of this machine from the name server FIND MY NAME !enable ports to the ring handler OP_FN=ENABLE PORT OP_B=1; !lower port limit OP_C=BSLIMIT OP_SER=RING SER; OP_REPLY=OWN ID PPON(OP) PON(OP) ALARM(TICKS); !clock tick every 2 seconds HOLD BUF==NIL %CYCLE %UNLESS HOLD BUF==NIL %THEN FREE BUFFER(HOLD BUF) %AND HOLDBUF==NIL P_SER=0; POFF(P); !wait for something %IF P_REPLY=0 %THEN FROM CLOCK %ANDCONTINUE PPON(P) %IF P_SER=BSP SER %THEN FROM ABOVE %ANDCONTINUE %IF P_REPLY=RING SER %THEN FROM BELOW %ANDCONTINUE %IF P_REPLY=BUFFER MANAGER %THEN FROM BUFF MAN %ANDCONTINUE %IF P_REPLY=NAME SERVER %THEN FROM NS %ANDCONTINUE PRINTSTRING("TSBSP: unexpected message -") WRITE(P_SER,3); WRITE(P_REPLY, 3); WRITE(P_FN, 3); NEWLINE %REPEAT %ENDOFPROGRAM