! !************************************************* !* * !* PSS X-25 Level 2 Protocol Handler * !* * !* XPROT * !* * !* Version 7.3 8 Feb 1982 * !* * !************************************************* !* %CONTROL 1 !* !******************************** !* * !* Declarations * !* * !******************************** %BEGIN !***** Configuration Information ***** !* %INCLUDE "ercm06.INC_SERS" %OWNINTEGER DCEDTE =0 ;!DCE !* !***** Constintegers ***** ! !*State Values %CONSTINTEGER WAITING FOR DISC =1 ;!DTE %CONSTINTEGER UA QUEUED 1 =2 ;!DTE & DCE %CONSTINTEGER SABM QUEUED =3 ;!DTE & DCE %CONSTINTEGER WAITING FOR UA =4 ;!DTE & DCE %CONSTINTEGER WAITING FOR SABM =5 ;!DCE %CONSTINTEGER UAQUEUED 2 =6 ;!DCE %CONSTINTEGER DISC QUEUED =7 ;!DTE & DCE %CONSTINTEGER LINK UP =0 ;!DTE & DCE %CONSTINTEGER DCE WAITING FOR UA =1 ;!DCE %CONSTINTEGER DTE WAITING FOR UA = 8 ;!DTE %CONSTINTEGER HELD DOWN =255 ;!DTE & DCE !*Substate values (Values of RSTATE) %CONSTINTEGER RRSENT = 0 %CONSTINTEGER RRPENDING = 1 %CONSTINTEGER REJSENT = 2 %CONSTINTEGER REJPENDING = 3 %CONSTINTEGER RNRSENT = 5 %CONSTINTEGER RNRPENDING = 6 !*Monitor Calls !* %CONSTINTEGER OK =0 %CONSTINTEGER LINE DOWN =1 %CONSTINTEGER LINE UP =2 %CONSTINTEGER QUERY =3 %CONSTINTEGER BUFFERS LOW = 5 %CONSTINTEGER SILO FULL = 7 %CONSTINTEGER BAD FR = 8 %CONSTINTEGER DMS = 9 %CONSTINTEGER INIT MSG = 10 %CONSTINTEGER MON CLOCK TICK = 11 %CONSTINTEGER BAD ADDRS RX = 12 %CONSTINTEGER SPURIOUS UFRAME RX = 13 %CONSTINTEGER RRS RX = 16 %CONSTINTEGER REJS RX = 17 %CONSTINTEGER RNRS RX = 18 %CONSTINTEGER IFRAMES RX = 19 %CONSTINTEGER ISEQERSS RX = 20 %CONSTINTEGER SABMS RX = 21 %CONSTINTEGER UAS RX = 22 %CONSTINTEGER DISCS RX = 23 %CONSTINTEGER FRMRS RX = 24 %CONSTINTEGER BADFRAMES RX = 25 %CONSTINTEGER NULL IFRAMES RX = 26 %CONSTINTEGER GROTTED 2 = 27 %CONSTINTEGER GROTTED 3 = 28 %CONSTINTEGER GROTTED 4 = 29 %CONSTINTEGER GROTTED 5 = 30 %CONSTINTEGER POLL FAILURE = 31 %CONSTINTEGER RRS TX = 32 %CONSTINTEGER REJS TX = 33 %CONSTINTEGER RNRS TX = 34 %CONSTINTEGER IFRAMES TX = 35 %CONSTINTEGER RETRIES TX = 36 %CONSTINTEGER SABMS TX = 37 %CONSTINTEGER UAS TX = 38 %CONSTINTEGER DISCS TX = 39 %CONSTINTEGER FRMRS TX = 40 %CONSTINTEGER MON DUP FAIL = 47 %CONSTINTEGER FUNNY INT = 48 %CONSTINTEGER BUFF FULL = 49 %CONSTINTEGER BAD ADDRESS = 50 %CONSTINTEGER SPURIOUS DATA = 51 %CONSTINTEGER GROTTED 1 = 52 %CONSTINTEGER SHORT FRAME = 54 %CONSTINTEGER FROM UPPER = 56 %CONSTINTEGER TO UPPER = 57 %CONSTINTEGER FUNNY STATE = 58 %CONSTINTEGER BAD FUNCTION = 59 %CONSTINTEGER BAD LINETYPE = 60 %CONSTINTEGER OUTPUT TX = 61 %CONSTINTEGER BAD ACK = 62 %CONSTINTEGER INPUT RX = 63 !* !Line and module state values for communicating with XGATE !* %CONSTINTEGER LINK ESTABLISHED =0 %CONSTINTEGER LINK DOWN =1 %CONSTINTEGER XPROT UP =2 !* !* X25 Frames !*Information transfer %CONSTINTEGER IFRAME =0 !*Supervisory %CONSTINTEGER RR =1 %CONSTINTEGER RNR =5 %CONSTINTEGER REJ =9 !*Unnumbered %CONSTINTEGER SABM =X'2F' %CONSTINTEGER SARM =X'0F' %CONSTINTEGER DISC =X'43' %CONSTINTEGER UA =X'63' %CONSTINTEGER FRMR =X'87' !* !* Values of flags etc !* %CONSTINTEGER CLEAR =0 %CONSTINTEGER SET =1 %CONSTINTEGER PENDING =1 %CONSTINTEGER SENT =2 %CONSTINTEGER PFSET =X'10' %CONSTINTEGER COMMAND =0 %CONSTINTEGER RESPONSE =1 %CONSTINTEGER INVALID =-1 %CONSTINTEGER SFMASK =31 %CONSTINTEGER DCE =0 %CONSTINTEGER DTE =1 %CONSTINTEGER INITIALISE =0 %CONSTINTEGER LINE INPUT =1 %CONSTINTEGER LINE OUTPUT =2 %CONSTINTEGER INPUT HERE =3 %CONSTINTEGER OUTPUT DONE =4 %CONSTINTEGER INPUT REQ =1 %CONSTINTEGER OUTPUT REQ =2 %CONSTINTEGER BOUNCE =3 %CONSTINTEGER PUT DOWN =4 %CONSTINTEGER PUT UP =5 %CONSTINTEGER MONIT = 6 %CONSTINTEGER DEAD =0 %CONSTINTEGER TIMED OUT =1 %CONSTINTEGER RESET = 2 %CONSTINTEGER SABM RECEIVED = 3 %CONSTINTEGER USER REQUEST =4 %CONSTINTEGER READ FAILS = 7 %CONSTINTEGER UFRAME IN DATA =6 %CONSTINTEGER DATA RETRIES =5 %CONSTINTEGER FULL UP = 8 %CONSTINTEGER DISC RECEIVED = 9 %CONSTINTEGER LINE TIMEOUT = 10 %CONSTINTEGER CRITICAL = 8 %CONSTINTEGER WMAX = 56 !* !***** Record Formats ***** %RECORDFORMAT QF(%RECORD (QF) %NAME LINK) %RECORDFORMAT XXF(%INTEGER DUMMY) %RECORDFORMAT PARF(%INTEGER TYPE, (%RECORD (XXF) %NAME B %OR %INTEGER ADDRESS), %C %INTEGER LEN) %RECORDFORMAT X25F(%BYTEINTEGER ADD, TYPE, OCTET1, OCTET2, OCTET3) %RECORDFORMAT M1F(%INTEGER A, B, C, D, E, F, G) %RECORDFORMAT MEF(%RECORD (MEF) %NAME LINK, %BYTEINTEGER LEN, TYPE, %C %STRING(2) DUMMY, %RECORD (X25F) X25) %RECORDFORMAT PE(%BYTEINTEGER SER, REPLY, %INTEGER A1, A2, A3) %RECORDFORMAT P2F(%BYTEINTEGER SER, REPLY, FN, LINE %C ,%RECORD (MEF) %NAME M,%BYTEINTEGER LEN, S1) !* !* !***** Monitoring Information ***** !* %OWNINTEGERARRAY MONCOUNT(0:63)=0(64) %CONSTBYTEINTEGERARRAY MONACTION(0:63) = %C 2, 2, 2, 5, 1, 1, 1, 3, 3, 1, 1, 1, 3, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 3, 2, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 3, 3, 2, 3, 3, 3, 1, 2, 1, 1, 1, 3, 3, 3, 4, 2, 4 !***** INTEGERS ***** !* %OWNINTEGER CLOCK0 =0 %OWNINTEGER CLOCK1 =0 %OWNINTEGER CLOCK2 =0 %OWNINTEGER CLOCK3 =0 %OWNINTEGER CLOCK4 =0 %OWNINTEGER CLOCK5 =0 %OWNINTEGER ADDRESS = 0 %OWNINTEGER PFBIT = 0 %OWNINTEGER COMRES = 0 %OWNINTEGER POLL =0 ;!"Pending" if we have a Poll bit to send !"Sent" when we have sent it, cleared when we receive a final bit %OWNINTEGER FINAL =0 ;!Set when far end sends us a Poll bit. !Cleared when we send back a Final bit %OWNINTEGER ISTATE= 0 ;!State of our end of the link %OWNINTEGER TSTATE= 0 ;!Set if far end has RNR up against us %OWNINTEGER RSTATE = 0 %OWNINTEGER ABORT REQ,ABORT REASON = 0 %OWNINTEGER WINDOW = 6 %OWNINTEGER AAA = 0 ;!Last message ackked by far end %OWNINTEGER EEE = 0 ;!Next message expected %OWNINTEGER TTT = 0 ;!Next message to send %OWNINTEGER XXX = 0 ;!High Water Mark !This is the most advanced frame for which a valid ack is possible !It is equal to TTT except during timer recovery (retransmission) !where TTT is the frame just sent and XXX is the most advanced frame %OWNINTEGER FFF = 0 ;!Last message sent %OWNINTEGER ACTIVE =0 %OWNINTEGER WPEND =0 ;!Writes pending %OWNINTEGER QMAX = 0 ;!Wpend high level watermark %OWNINTEGER MAX READS = 2 %OWNINTEGER T1 = 6 ;!6 Ticks = 2.5 - 3 secs %OWNINTEGER N2 = 20 %OWNINTEGER BIG LIMIT = 5 %OWNINTEGER INPUT EXP =0 ;!Blocks of input expected %OWNBYTEINTEGER LINE %OWNBYTEINTEGER LINE TYPE %OWNINTEGER I %OWNINTEGER TXINT = -6 %OWNINTEGER RXINT = -7 %OWNINTEGER BUFFERS HELD = 0 %OWNINTEGER QUIET IDLE = 1 %OWNINTEGER HOLD RNR = 0 ;!Hold RNR up for testing %OWNBYTEINTEGER MONBYTE = 1;!Controls what is monitored to where !* !****** NAMES ***** !* %RECORDFORMAT PF(%BYTE SER, REPLY, %INTEGER A, B, C) %INCLUDE "ercm06.INC_EXTS" %INCLUDE "ercm06.INC_VARIOUS" !***** Arrays ***** !* !* !***** Own and Const Arrays ***** !* %CONSTINTEGERARRAY INITSTATE(0:1)=DISC QUEUED(2) !* !***** Records and record names ***** !* %OWNRECORD (PE) P %OWNRECORD (P2F) %NAME P2 %OWNRECORD (PARF) PAR %CONSTRECORD (XXF) %NAME NULL == 0 %OWNRECORD (XXF) %NAME HANDLER ADDRESS == 1 %RECORDFORMAT BPF(%RECORD (MEF) %NAME M) %OWNRECORD (M1F) M1, M2 %RECORDFORMAT WDSE(%RECORD (MEF) %NAME M, %INTEGER LEN) %OWNRECORD (WDSE) %NAME WDESC %OWNRECORD (WDSE) %ARRAY WSPACE(0:31) %OWNRECORD (WDSE) %NAME IDESC %OWNRECORD (WDSE) ICURR %OWNRECORD (WDSE) IPOOL %OWNRECORD (WDSE) OM %OWNRECORD (BPF) %NAME IM %OWNRECORD (BPF) %NAME ME2 !***** Routine Specs ***** ! %ROUTINE PUSH(%RECORD (QF) %NAME HEAD,ITEM) !Places Item at the end of a circular queue %IF HEAD_LINK == NULL %THEN ITEM_LINK == ITEM %ELSESTART ITEM_LINK == HEAD_LINK_LINK HEAD_LINK_LINK == ITEM %FINISH HEAD_LINK == ITEM %END ;!of Push %RECORD (QF) %MAP POP(%RECORD (QF) %NAME HEAD) !Pops top item from circular queue %RECORD (QF) %NAME ITEM !Next statement traps empty (invalid) queue %IF HEAD_LINK == NULL %THEN ITEM == NULL %ELSESTART %IF HEAD_LINK_LINK == HEAD_LINK %START ;!One item on Queue ITEM == HEAD_LINK HEAD_LINK == NULL %ELSE ITEM == HEAD_LINK_LINK HEAD_LINK_LINK == ITEM_LINK %FINISH %FINISH %RESULT == ITEM %END ;!of Pop ! %ROUTINE DUP11E(%RECORD (PARF) %NAME P) %RECORDFORMAT DUP11F(%INTEGER RCS, RDB, TCS, TDB) %OWNRECORD (DUP11F) %NAME DUP == 1; ! SET UP BY PROT ON INITIALISE %RECORDFORMAT DESF(%INTEGER PT, %BYTEINTEGER STATE, S1, %C %INTEGER MAXLN, P1, FLAG, SEG, SA, VEC, INTNO) %RECORDFORMAT DES2F(%RECORD (DESF) RX, TX) %RECORDFORMAT PAR2F(%INTEGER TYPE, %RECORD (DUP11F) %NAME ADDRESS,LEN) %RECORDFORMAT R1F(%INTEGER N) %RECORDFORMAT R2F(%RECORD (DES2F) %ARRAYNAME DES) %RECORD (PAR2F) %NAME P2 %OWNRECORD (DES2F) %NAME DES %OWNRECORD (DES2F) %ARRAY %NAME DESX %RECORD (R1F) R1; %RECORD (R2F) %NAME R2 %CONSTINTEGER INITIALISE = 0 %CONSTINTEGER LINE INPUT = 1 %CONSTINTEGER LINE OUTPUT = 2 %CONSTINTEGER INPUT HERE = 3 %CONSTINTEGER OUTPUT DONE = 4 %CONSTINTEGER MARK = K'377' %CONSTINTEGER RSET=K'100',DSR=K'1000',DTR=2,RTS=4,CTS=K'20000' %CONSTINTEGER DCD=K'10000',RXEN=K'20',TXEN=K'20',DLEN=K'40' %CONSTINTEGER PARM = K'101062'; ! MODE=BYTE, NO CRC %CONSTINTEGER TSOM = K'400', TEOM = K'1000' %CONSTINTEGER DRS = K'400', SND = K'20', RCVEN = K'20' %OWNINTEGER TX REPLY, RX REPLY %ROUTINESPEC OCTAL(%INTEGER N) %SWITCH TYPESW(INITIALISE:OUTPUT DONE) %OWNINTEGER TYPE, F, CAD, OSEG, I, X %OWNINTEGER PAR, MID, PAD %OWNINTEGERARRAY RADDR(0:7) -> TYPE SW(P_TYPE) TYPE SW(INITIALISE): ! LEN IS NOW THE DEVICE NUMBER (0-2) MID = GET ID MAPHWR(3) P2 == P DUP == P2_ADDRESS %FOR I = 1, 1, 7 %CYCLE; ! FIND ABSOLUTE ADDRESSES RADDR(I) = MAP ABS(I<<13, 256, MID); ! MY ADDRESSES F = MAP ABS(I<<13, 0, MID); ! AND OFF AGAIN %REPEAT X = DUP ADDR(0) R2 == R1 R1_N = X&K'77'; ! JUST THE PAGE DISPLACEMENT DESX == R2_DES; DES == DESX(P_LEN) DES_RX_VEC = K'160000'!(P_ADDRESS&K'17777') DES_TX_VEC = DES_RX_VEC DUP_TCS = DUP_TCS!DRS; ! RESET THE DEVICE I = 0; ! TO FORCE A MOV #0, DUP_RDB = I; ! PARM = 0 DUP_RCS = DUP_RCS!DTR %WHILE DUP_RCS&DSR = 0 %CYCLE; %REPEAT DUP_RCS = DUP_RCS!RTS %WHILE DUP_RCS&CTS = 0 %CYCLE; %REPEAT %WHILE DUP_RCS&DCD = 0 %CYCLE; %REPEAT DUP_TCS = DUP_TCS!K'100'!SND DUP_TDB = TSOM %RETURN TYPE SW(OUTPUT DONE): ! TRANSMITTER TYPE = LINE OUTPUT %IF DES_TX_FLAG < 0 %OR TXREPLY = 0 %START !! TRANSMITTER ERROR PRINTSTRING("TX ERROR ") P_LEN = 1 %ELSE P_LEN = 0 %FINISH P_TYPE = LINE OUTPUT TXREPLY = 0 %RETURN TYPE SW(INPUT HERE): !! RECEIVER INTERRUPT X = DES_RX_FLAG %IF X < 0 %OR RX REPLY = 0 %START F = -2; ! FRAME ERROR ! %IF DUP_RSR&K'20' # 0 %THEN F = -3; ! SILO FULL FLT: ! DUP_RCR = 0; ! CLEAR DOWN ! DUP_RCR = K'10'; ! AND UP AGAIN %ELSE ! %IF DUP_RSR&K'1000' # 0 %THEN F = -1 %AND -> FLT ! WC OVERFLOW F = DES_RX_PT-CAD ! NUMBER OF BYTES TRANS ! %IF DUP_RSR&K'074000' # 0 %THEN F = F-1 %IF F> 252 %START PRINTSTRING("DUP NASTY:") OCTAL(DUP_RCS); SPACE; OCTAL(CAD); SPACE; OCTAL(F);NEWLINE %FINISH %FINISH P_TYPE = LINE INPUT P_ADDRESS = PAD; ! PASS BLOCK ADDRESS BACK P_LEN = F RX REPLY = 0 %RETURN TYPE SW(LINE INPUT): !! USER CALL !! READ REQUEST %IF RX REPLY # 0 %THEN -> ABORT RXREPLY = MID PAD = P_ADDRESS ! PAR = MAP ABS(PAD, P_LEN, RXREPLY) PAR = RADDR(PAD>>13) %IF PAR = 0 %THEN -> ABORT CAD = PAD&K'17777'!K'140000'; ! IN SEG NO 6 DES_RX_MAXLN = P_LEN DES_RX_SEG = PAR DES_RX_PT = CAD DUP_RCS = DUP_RCS!K'100'!RCVEN DES_RX_STATE = 0 %RETURN TYPE SW(LINE OUTPUT): !! OUTPUT REQUEST %IF TX REPLY # 0 %THEN -> ABORT TX REPLY = MID OSEG = P_ADDRESS ! PAR = MAP ABS(OSEG, P_LEN, TX REPLY) PAR = RADDR(OSEG>>13) %IF PAR = 0 %THEN -> ABORT DES_TX_SEG = PAR DES_TX_PT = P_ADDRESS&K'17777'!K'140000'; ! IN SEG NO 6 DES_TX_SA = P_LEN; ! LENGHT IN CHARS DES_TX_STATE = 1; ! TELL IT TO EXPECT INTS %RETURN ABORT: PRINTSTRING("DUP FAIL ") %CYCLE; %REPEAT %ROUTINE OCTAL(%INTEGER N) %INTEGER I PRINTSYMBOL((N >> I)&7+'0') %FOR I = 15, -3, 0 %END ;!of Octal %END ;!of DUP11E ! %ROUTINESPEC ABORT(%INTEGER REASON) %ROUTINESPEC ASK FOR BUFFER %ROUTINESPEC CLOCK INT %ROUTINESPEC DELETE READS %ROUTINESPEC FREE BUFFER(%RECORD (P2F) %NAME P) %ROUTINESPEC HANDLE INPUT %ROUTINESPEC HANDLE OUTPUT %ROUTINESPEC MONITOR(%RECORD (P2F) %NAME P,%INTEGER TYPE) %ROUTINESPEC REINITIALISE HARDWARE %ROUTINESPEC RESET LINE %ROUTINESPEC RETURN BUFFER(%RECORD (MEF) %NAME BUFF) %ROUTINESPEC START INPUT %ROUTINESPEC STOP(%INTEGER REASON) %ROUTINESPEC TO COMMS HW(%RECORD (PARF) %NAME PAR) %ROUTINESPEC TO XGATE(%INTEGER FLAG) !****** Others ***** !* %SWITCH GSW(OUTPUT REQ:MONIT) !* !****** END OF DECLARATIONS ***** !********************************** !* * !* Main Program * !* * !********************************** MAP VIRT(BUFFER MANAGER,5,4) MAP VIRT(BUFFER MANAGER,6,5) IM == M1 ; OM_M == M2 CHANGE OUT ZERO = T3 SER ;!Queued console I/O P2 == P P2_SER=0 ;!Accept all messages POFF(P2) ;!Wait. Expect an initialisation message !MON MONITOR(P, INIT MSG) LINE=P2_FN ;!We have only one line. Just record it and use !it for communicating with XGATE. PRINTSTRING("XPRO") ;WRITE(LINE,1) ;PRINTSTRING(" Running ") LINETYPE=P2_LINE&7 ;!0=DQS11E 1=DUP11E DCEDTE = P2_LINE >> 7 ;!0=DCE 1=DTE HANDLER ADDRESS==P2_M RXINT=P2_LEN!X'FF00' TXINT=P2_S1!X'FF00' %STOP %UNLESS 0 <= LINE <= 2 %STOP %UNLESS 0 <= LINE TYPE <= 1 ISTATE = INITSTATE(DCEDTE) REINITIALISE HARDWARE TO XGATE(XPROT UP) ALARM(25) ;!Half a second timer tick HANDLE OUTPUT ASK FOR BUFFER %CYCLE ;!Main loop P_SER=0 ;!Accept all messages POFF(P) ;!Wait %IF P_SER&X'80'#0 %START ;!Interrupt %IF P_SER=TXINT&X'FF' %THEN I=OUTPUT DONE %ELSE I=INPUT HERE PAR_TYPE=I TO COMMS HW(PAR) ;!Gets data length,pointer etc. %IF PAR_TYPE=LINE OUTPUT %START ACTIVE=0 HANDLE OUTPUT %ELSE !MON MONITOR(PAR, INPUT RX) HANDLE INPUT %FINISH %CONTINUE %FINISH %IF P_REPLY=0 %START ;!Clock tick !Interrupts: !0-9: Change monitoring level !A: Force a line abort !B:Generate a bad link-level address !E: Force a REJ !H: High Priority - tune for max throughput !L:Low priority !Q: Quiet: Suppress idle RR exchanges !U: Generate a spurious U-frame !R: Force a Transmitted sequence error !S: Force a reset of line by SABM !X: Generate a spurious frame (invalid) !Z: Hold up RNR !MON MONITOR(NULL, MON CLOCK TICK) CLOCK INT %IF INT = 'S' %START ABORT(RESET) %ELSEIF INT='A' ABORT(USER REQUEST) ;!Int A aborts line %ELSEIF INT='?' MONITOR(NULL,QUERY) ;!Int ? produces a status report %ELSEIF INT = 'L' ;!Low priority MAX READS = 3 ;BIG LIMIT = 10 ;T1 = 4 %ELSEIF INT = 'H' ;!High priority MAX READS = 6 ;BIG LIMIT = 4 %ELSEIF '0' <= INT <= '9' MONBYTE = INT - '0' %ELSEIF INT = 'Q' QUIET IDLE = 1 - QUIET IDLE %ELSEIF INT = 'Z' HOLD RNR = 1 - HOLD RNR ;!Force RNR for testing %FINISHELSE %CONTINUE INT = 0 %CONTINUE %FINISH %IF P_REPLY=BUFFER MANAGER %START ;!Buffer granted %IF P2_M_TYPE # 0 %THEN STOP(GROTTED 3) PUSH(IPOOL, P2_M) ;BUFFERS HELD = BUFFERS HELD + 1 INPUT EXP=INPUT EXP+1 ;!Count of available buffers %CONTINUE %FINISH !MON MONITOR(P, FROM UPPER) %IF OUTPUT REQ<=P2_FN<=PUT UP %THEN ->GSW(P2_FN) %ELSE ->FN INVALID GSW(OUTPUT REQ):!"Write to line" from higher level BUFFERS HELD = BUFFERS HELD + 1 %IF 0 # P2_M_TYPE # 64 %THEN STOP(GROTTED 2) %IF ISTATE=LINK UP %START ;!Ignore unless line is up !Xprot maintains a circular array of 24 records. These !point to buffers received from the higher level from the time !they arrive to the time they are acknowleged WPEND=WPEND+1 ;!Count writes outstanding %IF WPEND > QMAX %THEN QMAX = WPEND %IF WPEND=WMAX %START ;!Too many writes outstanding MONITOR(NULL,BUFFFULL) ABORT(FULL UP) %CONTINUE %FINISH WDESC==WSPACE(FFF) WDESC_M==P2_M WDESC_LEN=P2_LEN+2 FFF=(FFF+1)&SFMASK ;!Increment n(s): Sequence no of next frame HANDLE OUTPUT %ELSE ;!Link down FREE BUFFER(P) %FINISH %CONTINUE GSW(BOUNCE):!Force line bounce ABORT(USER REQUEST) %CONTINUE GSW(PUT DOWN):!Force and hold down ABORT(USER REQUEST) ISTATE=HELD DOWN %CONTINUE GSW(PUT UP):!Allow back up after a Put down and/or down-line load %IF INPUT EXP=0 %THEN ASK FOR BUFFER ISTATE=INITSTATE(DCEDTE) REINITIALISE HARDWARE %CONTINUE FN INVALID: MONITOR(P,BAD FUNCTION) !We Won't free the buffer (Better slowly gobble space than risk crashing BUFF) %CONTINUE GSW(MONIT):!Monitor Function INT = '?' %CONTINUE %REPEAT !* !**************************************** !* * !* Routines * !* * !**************************************** %ROUTINE ABORT(%INTEGER REASON) %INTEGER X,I %RETURN %IF ISTATE#LINK UP ABORT REASON = REASON %UNLESS REASON = 0 %IF ABORT REQ=0 %START ;!First try at aborting MONITOR(NULL,LINE DOWN) TO XGATE(LINK DOWN) %FINISH %IF ACTIVE=2 %START ;!Big block being transmitted ABORT REQ=1 %ELSE ABORT REQ=0 X = 0 %WHILE AAA#FFF %CYCLE ;!Clear up requests WDESC == WSPACE(AAA) RETURN BUFFER(WDESC_M) WDESC_M==NULL AAA=(AAA+1)&SFMASK X = X + 1 %REPEAT ISTATE=DISC QUEUED %IF ABORT REASON = RESET %THEN ISTATE = SABM QUEUED %IF ABORT REASON = SABM RECEIVED %THEN ISTATE = UA QUEUED 1 %IF ABORT REASON = DISC RECEIVED %THEN ISTATE = UA QUEUED 2 ABORT REASON = 0 DELETE READS HANDLE OUTPUT %FINISH %END ;!of abort %ROUTINE ASK FOR BUFFER !Asks for a big buffer P_SER=BUFFER MANAGER P_REPLY = OWN ID P_A1=0 P_A3=0 PON(P) %END ;!of ask for block %ROUTINE DELETE READS %WHILE %NOT IPOOL_M == NULL %CYCLE P2_M == POP(IPOOL) FREE BUFFER(P2) ;INPUT EXP = INPUT EXP-1 %REPEAT INPUT EXP=0 %END ;!of delete reads %ROUTINE FREE BUFFER(%RECORD (P2F) %NAME P) !Frees the block pointed at by P (P2)_M %UNLESS P_M == NULL %START ;!Safety check for NULL pointer P_SER=BUFFER MANAGER P_REPLY = OWN ID P_FN=1 BUFFERS HELD = BUFFERS HELD - 1 %IF 0 # P_M_TYPE # 64 %THEN STOP(GROTTED 4) PON(P) %FINISH %END ;!of free block %ROUTINE HANDLE OUTPUT %RECORD (X25F) %NAME X25 %RECORD (MEF) %NAME M %INTEGER TYPE %INTEGER LEN %INTEGER X %RETURN %IF ACTIVE#0 ;!Transmit channel busy %IF ABORT REQ#0 %THEN ABORT(DEAD) %IF ISTATE#0 %START ;!Link setup phase %IF ISTATE=SABM QUEUED %START ;!Have sent UA (DTE). Now send SABM TYPE=SABM ISTATE=WAITING FOR UA MONITOR(NULL, SABMS TX) ->SEND UNNUMBERED %FINISH %IF ISTATE=UAQUEUED 1 %START TYPE=UA ISTATE=LINK UP RESET LINE TO XGATE(LINK ESTABLISHED) MONITOR(NULL, UAS TX) ->SEND UNNUMBERED %FINISH %IF DCEDTE=DTE %START ;!We are a DTE %IF ISTATE=UAQUEUED 2 %START ;!We have had a DISC from DCE and !must send a UA in reply, with the P/F bit as received TYPE=UA ISTATE=SABM QUEUED MONITOR(NULL, SABMS TX) ->SEND UNNUMBERED %FINISH %IF ISTATE=DISC QUEUED %START ;!We have had either an abort !or a DISC while the link was up. Send a DISC back !and wait for DCE to repoll with DISCs TYPE=DISC ISTATE = DTE WAITING FOR UA ;!i.e. down MONITOR(NULL, DISCS TX) ->SEND UNNUMBERED %FINISH %ELSE ;!We are configured to be a DCE %IF ISTATE=UAQUEUED 2 %START TYPE=UA ISTATE=WAITING FOR SABM ;!For N2 * T1 seconds MONITOR(NULL, UAS TX) ->SEND UNNUMBERED %FINISH %IF ISTATE=DISC QUEUED %START TYPE=DISC ISTATE=DCE WAITING FOR UA MONITOR(NULL, DISCS TX) ->SEND UNNUMBERED %FINISH %FINISH %RETURN ;!If not in one of these states %ELSE ;!Link up %IF RSTATE = REJPENDING %START TYPE = REJ MONITOR(NULL,REJS TX) RSTATE = REJSENT ;!2 ->SEND SUPERVISORY %FINISH %IF RSTATE = RNRPENDING %START ;!6 TYPE = RNR MONITOR(NULL,RNRS TX) RSTATE = RNRSENT ;!5 ->SEND SUPERVISORY %FINISH %IF RSTATE = RRPENDING %OR (RSTATE = REJSENT %AND CLOCK0 >= T1) %OR FINAL = SET %START RSTATE = RRSENT %IF RSTATE = RRPENDING TYPE = RR MONITOR(NULL,RRS TX) ->SEND SUPERVISORY %FINISH INT R: %IF TTT#FFF %AND TSTATE=0 %AND (TTT-AAA)&7#WINDOW %START !Something to send/No RNR up from far end/No modulo count runout !respectively %IF TTT=XXX %OR CLOCK1 >= T1 %START !Branch past this if we are retransmitting and Clock1 is still running WDESC==WSPACE(TTT) ;!Pick up the top frame on the Q M==WDESC_M X25==M_X25 LEN=WDESC_LEN TYPE=(TTT&7)<<1 %IF XXX=TTT %START XXX=(XXX+1)&SFMASK MONITOR(NULL, IFRAMES TX) %ELSE POLL=PENDING MONITOR(NULL, RETRIES TX) %FINISH !If we are not retransmitting, keep high water mark abreast of TTT !If we are, this is a retransmission so has the poll bit set TTT=(TTT+1)&SFMASK ACTIVE=2 ;!Big block being transmitted CLOCK1=0 %IF INT = 'R' %THEN INT = 0 %AND ACTIVE=0 %AND ->INT R ;!Force a transmit sequence error ->SEND FRAME %FINISH %FINISH %FINISH %RETURN SEND SUPERVISORY: SEND UNNUMBERED: LEN=2 ACTIVE=1 X25 == OM_M SEND FRAME: !In LAPB, I,SABM,DISC are always commands. UA,FRMR are responses. ! RR,REJ,RNR can be either ! We will drive REJ as a reply. RR and RNR will be commands if sent as ! idle line polls and responses if prompted by an I or RR command ! Note they must set the poll bit. PFBIT=CLEAR ;!Default %IF TYPE&1=0 %OR TYPE=SABM %OR TYPE=DISC %THEN X=COMMAND %ELSE X=RESPONSE %IF TYPE = RR %OR TYPE = RNR %START X = COMRES %IF X=COMMAND %THEN POLL=PENDING %FINISH %IF TYPE&3 # 3 %THEN TYPE = EEE<<5 ! TYPE %IF X=COMMAND %AND POLL=PENDING %THEN PFBIT=PFSET %AND POLL=SENT %IF X=RESPONSE %AND FINAL=SET %THEN PFBIT=PFSET %AND FINAL=CLEAR !Calculate the address byte as follows: PSS algorithm is ! Commands DCE--> DTE Address=3 DTE--> DCE Address=1 ! Responses DCE--> DTE Address=1 DTE--> DCE Address=3 %IF X=COMMAND %THEN ADDRESS=1 %ELSE ADDRESS=3 !Addresses are the other way round for a DCE. %IF DCEDTE=DCE %THEN ADDRESS=4-ADDRESS CLOCK0=0 %IF INT = 'B' %THEN ADDRESS = 2 %AND INT = 0 ;!Force a bad address %IF INT = 'U' %THEN TYPE = UA %AND INT = 0 ;!Force a spurious UA %IF INT = 'X' %THEN TYPE = X'FD' %AND INT = 0;!Force a bad ctrl byte X25_ADD=ADDRESS X25_TYPE=TYPE ! PFBIT PAR_TYPE=LINE OUTPUT PAR_B==X25 PAR_LEN=LEN !MON MONITOR(PAR, OUTPUT TX) TO COMMS HW(PAR) %END ;!of handle output %ROUTINE HANDLE INPUT %RECORD (X25F) %NAME X25 %RECORD (WDSE) %NAME IMESS %RECORD (MEF) %NAME M %INTEGER R %INTEGER S %INTEGER X %INTEGER L %INTEGER TYPE M==ICURR_M ;!? X25==PAR_B ;!? MONITOR(NULL,BADADDRESS) %AND %RETURN %UNLESS X25==M_X25 ADDRESS = X25_ADD ;TYPE = X25_TYPE L = PAR_LEN %IF INT = 'E' %AND TYPE&1 = 0 %THEN L = -2 %AND INT = 0 !Force a received sequence error %IF ISTATE # HELD DOWN %START ;!Replace Read %IF INPUT EXP>0 %START CLOCK5 = 0 ;!?? ICURR_M==POP(IPOOL) INPUT EXP=INPUT EXP-1 %IF ICURR_M_TYPE=0 %THEN ICURR_LEN = 249 %ELSE ICURR_LEN = 57 %ELSE ICURR_M==IM ICURR_LEN=6 %FINISH %IF INPUT EXP<2 %THEN ASK FOR BUFFER ! START INPUT %finish %IF L<2 %START ;!Bad frame %IF L=-1 %START !MON MONITOR(NULL,DMS) CLOCK4=CLOCK4+1 %IF CLOCK4>24 %THEN ABORT(READ FAILS) %ELSEIF L=-2 MONITOR(NULL,BAD FR) %ELSEIF L=-3 MONITOR(NULL,SILOFULL) ;!Keep a count %FINISHELSE MONITOR(NULL, SHORT FRAME) ->NOISE %FINISH %IF ADDRESS&X'FD' # 1 %THEN MONITOR(NULL, BAD ADDRS RX) %AND -> NOISE !Check for bad address. Strictly speaking we should ignore these if we're a DCE PFBIT=TYPE&X'10' !Find out what sort of frame this is. We need to know if it is !a command or response (or invalid). X=INVALID ;!Guilty till proved innocent %IF TYPE&3#3 %START ;!S-frames and I-frames R=(TYPE>>5)&7 ;!Extract N(R) %IF TYPE&1=0 %START ;!I-frames. S=(TYPE>>1)&7 ;!Extract N(S) TYPE=IFRAME X=COMMAND %ELSE ;!Supervisory frames TYPE = TYPE & 15 ;!Mask out N(R) and P/F bit %IF TYPE=RR %OR TYPE=RNR %OR TYPE=REJ %START ;!Only ones we recognise %IF DCEDTE=DCE %THEN ADDRESS=4-ADDRESS ;!Watch out. %IF ADDRESS = 3 %THEN X = COMMAND %ELSE X = RESPONSE %FINISH %FINISH %ELSE ;!Unnumbered frames TYPE = TYPE & X'EF' ;!Mask out P/F bit %IF TYPE=SABM %OR TYPE=DISC %THEN X=COMMAND %IF TYPE=UA %OR TYPE=FRMR %THEN X = RESPONSE %FINISH %IF X=COMMAND %START COMRES = RESPONSE ;!Next frame is a response to this one unless there is a good reason otherwise %IF PFBIT=PFSET %START !Command with poll bit set requires an immediate RESPONSE FINAL=SET %IF RSTATE = RRSENT %OR RSTATE = RNRSENT %START %IF NO OF BUFF < BIG LIMIT %OR HOLD RNR # 0 %START !MON MONITOR(NULL, BUFFERS LOW) RSTATE = RNRPENDING %FINISHELSE RSTATE = RRPENDING %FINISH %FINISH %ELSE %IF X=RESPONSE %AND POLL=SENT %START %IF PFBIT=PFSET %THEN POLL = CLEAR %ELSESTART %IF DCEDTE = DCE %THEN MONITOR(NULL, POLL FAILURE) %FINISH %FINISH %IF X=INVALID %THEN MONITOR(NULL, BADFRAMES RX) %AND -> NOISE %FINISH ->NOISE %IF ABORT REQ#0 %IF ISTATE#LINK UP %START ->NOISE %IF ISTATE=HELD DOWN %IF TYPE=UA %AND ISTATE=WAITING FOR UA %THEN %START ISTATE = LINK UP RESET LINE TO XGATE(LINK ESTABLISHED) ->VALID %FINISH %IF TYPE=SABM %THEN ISTATE = UAQUEUED 1 %AND ->VALID %IF DCEDTE=DTE %START %IF TYPE=DISC %THEN ISTATE=UAQUEUED 2 %AND ->VALID %IF TYPE = UA %THEN ISTATE = SABM QUEUED %AND -> VALID %ELSE ;!DCE mode %IF TYPE=UA %THEN ISTATE=WAITING FOR SABM %AND ->VALID %IF TYPE=DISC %AND ISTATE # DCE WAITING FOR UA %AND %C ISTATE # DISC QUEUED %THEN ISTATE=UAQUEUED 2 %AND ->VALID %FINISH MONITOR(NULL,SPURIOUS UFRAMERX) ->NOISE %ELSE ;!LINK UP %IF TYPE&3=3 %START %IF TYPE=FRMR %START MONITOR(NULL, FRMRS RX) %ELSEIF TYPE = SABM ABORT(SABM RECEIVED) %ELSEIF TYPE = DISC ABORT(DISC RECEIVED) %FINISHELSE ABORT(UFRAME IN DATA) -> NOISE %FINISH %IF TYPE=IFRAME %START %IF S = EEE %START ;!Sequence OK %IF L > 2 %START P_SER=KERNEL SER P_REPLY = OWN ID P2_FN=INPUT REQ P2_LINE=LINE ;!Redundant here P2_M==M P2_M_LEN=L-2 ;!Length excluding address & control bytes %IF 0#P2_M_TYPE#64 %THEN STOP(GROTTED 1) !MON MONITOR(P,TO UPPER) BUFFERS HELD = BUFFERS HELD - 1 MONITOR(NULL,IFRAMES RX) ;!Just keep a count PON(P) X25==NULL ;!Avoid freeing buffer %FINISHELSE MONITOR(NULL, NULL IFRAMES RX) EEE=(EEE+1)&7 ;!Increment received sequence no. %IF NO OF BUFFNOISE %FINISH %FINISH %FINISH ! X=AAA %WHILE X&7#R %CYCLE %IF X=XXX %START MONITOR(NULL,BADACK) ABORT(RESET) ->VALID %FINISH X=(X+1)&SFMASK %REPEAT %UNLESS ACTIVE=2 %AND (XXX-X)&SFMASK <= (XXX-TTT)&SFMASK %START !Ignore ack if it includes one for block currently being transmitted %WHILE AAA#X %CYCLE ;!Free off ackked blocks WDESC == WSPACE(AAA) RETURN BUFFER(WDESC_M) MONITOR(NULL,IFRAMES TX) ;!Just count them WDESC_M==NULL WPEND=WPEND-1 ;!One less write outstanding !I have managed to convince myself that TTT could equal AAA !If we receive an ack for a frame while in mid-retransmit !If we dont do this, TTT and AAA could wind up inside-out. TTT = (TTT+1) & SFMASK %IF TTT = AAA AAA=(AAA+1)&SFMASK ;!Increment sequence no. of first frame not ackked XXX=TTT ;CLOCK1=0 ;WINDOW=6 ;!Leave Timer recovery condition CLOCK3=0 %REPEAT %IF TTT#XXX %THEN TTT=AAA ;!Retransmitting: retransmit first unackked frame %IF TYPE=REJ %START TTT = AAA ;XXX = TTT %IF RSTATE = RRSENT %OR RSTATE = REJSENT %THEN RSTATE = RR PENDING %FINISH !to the first one not ackked If a Reject or retransmission %FINISH %FINISH ! VALID: ! "Valid" here just means that the CRC was OK and it was a recognisable frame CLOCK2=0 ;!Reset validity timer - chops him if this gets too big. NOISE: RETURN BUFFER(M) %UNLESS X25==NULL %OR M==IM %RETURN %IF ISTATE=HELD DOWN ! HANDLE OUTPUT ! %END ;!of handle input %ROUTINE CLOCK INT ALARM(25) ;!Replace 1/2 second timer CLOCK0=CLOCK0+1 %IF ISTATE#0 %START %IF DCEDTE=DCE %START ;!We are configured to be the DCE %IF CLOCK0 >= T1-1 %AND ISTATE=DCE WAITING FOR UA %THEN %C ISTATE=DISC QUEUED %AND POLL = PENDING %IF CLOCK0>= T1*N2-1 %AND ISTATE=WAITING FOR SABM %THEN %C ISTATE = DISC QUEUED %AND POLL = CLEAR %ELSE ;!We are the DTE %IF CLOCK0 >= T1-1 %START %IF ISTATE=WAITING FOR UA %START %IF CLOCK3 <= N2 %START CLOCK3 = CLOCK3 + 1 ISTATE=SABM QUEUED POLL = PENDING %ELSE ISTATE = DISC QUEUED POLL = CLEAR %FINISH %ELSEIF ISTATE = DTE WAITING FOR UA %IF CLOCK3 <= N2 %START CLOCK3 = CLOCK3 + 1 ISTATE = DISC QUEUED POLL = PENDING %ELSE ISTATE = WAITING FOR DISC POLL = CLEAR %FINISH %FINISH %FINISH %FINISH %ELSE ;!Link up CLOCK4=0 %IF INPUT EXP>0 CLOCK2=CLOCK2+1 %IF CLOCK2 = N2*T1 %AND TTT # AAA %START !No data acknowledged by far end in 80 ticks. ABORT(LINE TIMEOUT) %FINISH !Clock 1 is knocked back whenever one of our frames is ackked or !we retransmit. If there is a frame unacknowleged (AAA#TTT) and it !hasnt been ackkecd for 5 seconds we wil retransmit !unless he has RNR up. %IF AAA#XXX %THEN CLOCK1=CLOCK1+1 %IF CLOCK1 >= T1 %AND AAA#TTT %AND TSTATE=0 %START !Enter Timer Recovery state CLOCK3=CLOCK3+1 TTT=AAA ;WINDOW=1 %IF CLOCK3 >= N2 %THEN ABORT(DATARETRIES) %AND %RETURN ;!Retry N2 times and give up %ELSE %IF CLOCK0 >= T1*2 %AND QUIET IDLE =0 %START ;!RCO-Defined line idle poll COMRES = COMMAND RSTATE = RNRPENDING %IF NO OF BUFFSW(MONACTION(TYPE)) SW(5):!Query command PRINTSTRING("LN=") ;WRITE(LINE, 1) %IF DCEDTE = DCE %THEN PRINTSTRING(" DCE") %ELSE PRINTSTRING(" DTE") PRINTSTRING(" BH=") ;WRITE(BUFFERS HELD,1) PRINTSTRING(" I=") ;WRITE(ISTATE,1) PRINTSTRING( "A=") ;WRITE(ACTIVE,1) ;PRINTSTRING(" CLKS:") WRITE(CLOCK0,2) ;WRITE(CLOCK1,2) ;WRITE(CLOCK2,2) ;WRITE(CLOCK3,2) WRITE(CLOCK4,2) ;WRITE(CLOCK5,2) NEWLINE %IF ISTATE=LINK UP %START PRINTSTRING(" R=") ;WRITE(RSTATE,1) PRINTSTRING(" T=") ;WRITE(TSTATE,1) PRINTSTRING("AETFX=") ;WRITE(AAA,1) ;WRITE(EEE,1) WRITE(TTT,1) ;WRITE(FFF,1); WRITE(XXX,1) NEWLINE %FINISH %FOR I = 16,1,25 %CYCLE ;WRITE(MONCOUNT(I), 1) ;%REPEAT SPACES(3) %FOR I = 32,1,40 %CYCLE ;WRITE(MONCOUNT(I), 1) ;%REPEAT NEWLINE -> LOG IT SW(4):!Frame sent to or received from hardware !MON LPAR == P !MON SELECTOUTPUT(1) !MON PRINTSYMBOL(6) ;PRINTSYMBOL(OWN ID) ;PRINTSYMBOL(TYPE) !MON PRINTSYMBOL(LPAR_TYPE) ;PRINTSYMBOL(LPAR_LEN) !MON PRINTSYMBOL(LPAR_B_DUMMY&255) ;PRINTSYMBOL(LPAR_B_DUMMY>>8) !MON SELECTOUTPUT(0) %RETURN SW(3):!Disaster - always log to .TT and disc J = 1 ;!Always to .TT SW(*): ;!All strays here SW(2):!Exception condition: Always log to disc, conditional to .TT J = J ! (MONBYTE&1) I = 1 ;!Always to disc LOG IT: SW(1):!Log a normal transaction I = I ! (MONBYTE&2) %IF J # 0 %START ;!_Log to .TT !Print a message on first 16 occurrences then every 16th !Note that I # 0 for monaction = 3 or monaction >= 1 and monbyte&2 # 0 %IF MONCOUNT(TYPE) < 16 %OR MONCOUNT(TYPE)&15 = 0 %OR I = 1 %START PRINTSTRING("*XPRO") ;WRITE(LINE, 1) ;WRITE(TYPE, 1) PRINTSTRING(" * 16") %IF MONCOUNT(TYPE) > 16 %AND I = 0 %IF TYPE = LINE DOWN %START PRINTSTRING(" Line Down at Level 2:") ;WRITE(ABORT REASON, 1) %ELSEIF TYPE = LINE UP PRINTSTRING(" Line Up at Level 2") %ELSEIF TYPE = BAD FR PRINTSTRING(" Bad Frame") %ELSEIF TYPE = FRMRS RX PRINTSTRING(" FRMR:") X25 == PAR_B WRITE(X25_OCTET1, 1) ;WRITE(X25_OCTET2, 1) ;WRITE(X25_OCTET3, 1) %FINISH NEWLINE %FINISH %FINISH !MON %IF I # 0 %START !MON SELECTOUTPUT(1) !MON %IF P == NULL %START !MON PRINTSYMBOL(2) ;PRINTSYMBOL(OWN ID) ;PRINTSYMBOL(TYPE) !MON %ELSE !MON PA == P !MON PRINTSYMBOL(10) ;PRINTSYMBOL(OWN ID) ;PRINTSYMBOL(TYPE) !MON %FOR I = 0,1,7 %CYCLE !MON PRINTSYMBOL(PA_A(I)) !MON %REPEAT !MON %UNLESS P_M == NULL %START !MON %IF (TYPE = TO UPPER %AND P_FN = INPUT REQ) %C !MON %OR (TYPE = FROM UPPER %AND P_FN = OUTPUT REQ) %START !MON PA == P_M !MON %IF 1 <= P_M_LEN <=19 %THEN J = P_M_LEN + 9 %ELSE J = 28 !MON !4 bytes (link,len,type) 5 bytes (3 pads,addr,ctrl) !MON PRINTSYMBOL(J) !MON %FOR I = 0,1,J-1 %CYCLE !MON PRINTSYMBOL(PA_A(I)) !MON %REPEAT !MON %FINISH !MON %FINISH !MON %FINISH !MON SELECTOUTPUT(0) !MON %FINISH SW(0):!Just a count MONCOUNT(TYPE) = MONCOUNT(TYPE) + 1 %RETURN %END ;!of Monitor %ROUTINE REINITIALISE HARDWARE !Grabs Interrupt service nos and sets/resets Hardware LINKIN(RXINT) ;LINKIN(TXINT) PAR_TYPE=INITIALISE PAR_B==HANDLER ADDRESS PAR_LEN = LINE ;!Line Number for Multiple DUPs. Must be 0 <= x <= 2 TO COMMS HW(PAR) ICURR_M==IM ;!? ICURR_LEN=6 ;!? ACTIVE = 0 ;ABORT REQ = 0 START INPUT %END ;!of reinitialise hw %ROUTINE RESET LINE TSTATE=0 ;!Assume no RNR from far end RSTATE = RRPENDING ;COMRES = COMMAND ;!Start off by sending a RR command FINAL = CLEAR !Zero transmit and receive sequence numbers AAA=0 EEE=0 FFF=0 TTT=0 XXX=0 CLOCK1=0 CLOCK2=0 CLOCK3=0 CLOCK4=0 %END ;!of reset line %ROUTINE RETURN BUFFER(%RECORD (MEF) %NAME BUFF) P2_M==BUFF !Free short buffers. Queue long buffers up to a limit !Unless we are short of space %IF BUFF_TYPE#0 %OR INPUT EXP > MAX READS %OR %C NO OF BUFF < CRITICAL %THEN FREE BUFFER(P2) %ELSESTART PUSH(IPOOL, BUFF) ;INPUT EXP = INPUT EXP +1 %FINISH %END ;!of return block %ROUTINE START INPUT %RECORDFORMAT PAR2F(%INTEGER A,B,C) PAR_TYPE=LINE INPUT ;!Put a read on PAR_B == ICURR_M_X25 PAR_LEN = ICURR_LEN %IF ICURR_LEN # 6 %AND 0 # ICURR_M_TYPE # 64 %THEN STOP(GROTTED 5) TO COMMS HW(PAR) %END ;!of start input %ROUTINE STOP(%INTEGER REASON) %RECORD (PE) P PRINTSTRING("Xpro") ;WRITE(LINE, 1) ;PRINTSTRING(" disaster") WRITE(REASON, 1) ;NEWLINE !Gracefully. (This may not be a good idea but we'll try it.) %CYCLE POFF(P) ;!Should be better than a tight loop - !a) Scheduler knows we're suspended b) won't fill poff queue %REPEAT %END ;!of Stop %ROUTINE TO COMMS HW(%RECORD (PARF) %NAME PAR) STOP(MON DUP FAIL) %IF PAR_B == NULL %AND PAR_TYPE # INITIALISE DUP11E(PAR) %END ;!of to comms hw %ROUTINE TO XGATE(%INTEGER FLAG) !Routine to send an 8-byte parameter area to XGATE giving it global !status, namely XPROT UP LINK ESTABLISHED/DOWN. !Differs from RCO-HDLC version in that LINK ESTABLISHED call passes !a Zero as the X25 address. P2_SER = KERNEL SER P2_REPLY = OWN ID P2_FN=OUTPUT REQ P2_LINE=LINE P2_M == NULL P_A3 = FLAG !MON MONITOR(P,TO UPPER) PON(P) %END ;!of to XGATE %ENDOFPROGRAM