!********************************************************** !* * !* PSS X-25 Level 3 Protocol Handler * !* * !* XGATE * !* * !* Version 8.15 29 Mar 1982 * !* * !********************************************************** !* %CONTROL 1 %BEGIN !************************************ !* * !* Declarations * !* * !************************************ !* !****** Constintegers ****** !* !*Link states %CONSTINTEGER ESTABLISHED = 0 %CONSTINTEGER DOWN = 1 %CONSTINTEGER RESTARTING = 2 %CONSTINTEGER GFI = X'10' !*State values %CONSTINTEGER IDLE = 0 ;!Fixed %CONSTINTEGER WTACI = 1 ;!Fixed %CONSTINTEGER WTACN = 2 ;!Fixed %CONSTINTEGER ESTB = 3 ;!Fixed %CONSTINTEGER WAIT DATA = 4 ;!Fixed %CONSTINTEGER WTDSI = 7 %CONSTINTEGER WTDSN = 8 %CONSTINTEGER WTDSN2 = 9 %CONSTINTEGER WDACI =10 !Tstate and Rstate values %CONSTINTEGER CLEAR = 0 %CONSTINTEGER WW SET = 1 %CONSTINTEGER PS SET = 2 %CONSTINTEGER SET = 1 %CONSTINTEGER ACKPENDING = 1 !Monitor calls %CONSTINTEGER OK =0 %CONSTINTEGER LINE DOWN = 1 %CONSTINTEGER LINE UP = 2 %CONSTINTEGER QUERY = 3 %CONSTINTEGER BAD PROCESS = 4 %CONSTINTEGER BAD OUTSTATE = 5 !6-13 Used for Connect rejection monitoring %CONSTINTEGER MON PROCESS RUNNING = 6 %CONSTINTEGER MON BAD PARAM = 7 %CONSTINTEGER MON NO FREE LCNS = 8 !14-21 Used for incoming call rejection monitoring %CONSTINTEGER BAD FN = 22 %CONSTINTEGER BAD INSTATE = 23 %CONSTINTEGER DATA OUTSIDE CONNECT = 24 %CONSTINTEGER MON NO PROCS = 25 %CONSTINTEGER RRS RX = 26 %CONSTINTEGER REJS RX = 27 %CONSTINTEGER RNRS RX = 28 %CONSTINTEGER DATAS RX = 29 %CONSTINTEGER DSEQERRS RX = 30 %CONSTINTEGER CALLS RX = 31 %CONSTINTEGER ACCEPTS RX = 32 %CONSTINTEGER CLEARS RX = 33 %CONSTINTEGER CLEAR CONFS RX = 34 %CONSTINTEGER INTS RX = 35 %CONSTINTEGER INT CONFS RX = 36 %CONSTINTEGER RESETS RX = 37 %CONSTINTEGER RESET CONFS RX = 38 %CONSTINTEGER RESTARTS RX = 39 %CONSTINTEGER RESTART CONFS RX = 40 %CONSTINTEGER RRS TX = 41 %CONSTINTEGER REJS TX = 42 %CONSTINTEGER RNRS TX = 43 %CONSTINTEGER DATAS TX = 44 %CONSTINTEGER RETRIES TX = 45 %CONSTINTEGER CALLS TX = 46 %CONSTINTEGER ACCEPTS TX = 47 %CONSTINTEGER CLEARS TX = 48 %CONSTINTEGER CLEAR CONFS TX = 49 %CONSTINTEGER INTS TX = 50 %CONSTINTEGER INT CONFS TX = 51 %CONSTINTEGER RESETS TX = 52 %CONSTINTEGER RESET CONFS TX = 53 %CONSTINTEGER RESTARTS TX = 54 %CONSTINTEGER RESTART CONFS TX = 55 %CONSTINTEGER BAD ACK = 56 %CONSTINTEGER MON TO ACCT = 57 %CONSTINTEGER FROM UP = 58 %CONSTINTEGER TO UP = 59 %CONSTINTEGER FROM LOW = 60 %CONSTINTEGER TO LOW = 61 %CONSTINTEGER BAD BLOCK = 62 %CONSTINTEGER MON CALL COLLISION = 63 %INCLUDE "ERCM06.INC_CONFIG" %INCLUDE "ERCM06.INC_VARIOUS" %INCLUDE "ERCM06.INC_XGTFNS" !TS Functions %CONSTINTEGER TS CONNECT = 16 %CONSTINTEGER TS ACCEPT = 17 %CONSTINTEGER TS DISCONNECT = 18 !*X25 Functions %CONSTINTEGER INCOMING CALL = 11 ;!From DCE %CONSTINTEGER CALL REQUEST = 11 ;!To DCE %CONSTINTEGER CALL CONNECTED = 15 ;!From DCE %CONSTINTEGER CALL ACCEPTED = 15 ;!To DCE %CONSTINTEGER CLEAR INDICATION = 19 ;!From DCE %CONSTINTEGER CLEAR REQUEST = 19 ;!To DCE %CONSTINTEGER CLEAR CONFIRMATION = 23 ;!Covers DTE & DCE !Data and interrupt %CONSTINTEGER DCE DATA = 0 ;!From DCE %CONSTINTEGER DTE DATA = 0 ;!To DCE %CONSTINTEGER INTERRUPT = 35 ;!Both ways %CONSTINTEGER INTERRUPT CONFIRMATION = 39 ;!Both ways !Flow control and reset %CONSTINTEGER RR = 1 ;!Both ways %CONSTINTEGER RNR = 5 ;!Both ways %CONSTINTEGER REJ = 9 ;!To DCE %CONSTINTEGER RESET INDICATION = 27 ;!From DCE %CONSTINTEGER RESET REQUEST = 27 ;!To DCE %CONSTINTEGER RESET CONFIRMATION = 31 ;!Covers DCE & DTE !Restart %CONSTINTEGER RESTART INDICATION =251 ;!From DCE %CONSTINTEGER RESTART REQUEST =251 ;!To DCE %CONSTINTEGER RESTART CONFIRMATION =255 ;!Covers DCE & DTE %CONSTINTEGER FAST SELECT BIT =128 %CONSTINTEGER RESTRICTED RESPONSE BIT = 64 !* !Commands to and from lower level %CONSTINTEGER LINE INPUT = 1 %CONSTINTEGER LINE OUTPUT = 2 %INCLUDE "ERCM06.INC_DISCQUALS" %INCLUDE "ERCM06.INC_SERS" !*Various consts !Note LCN = 255 is used as a flag %CONSTINTEGER MAX WRITES = 24 %CONSTINTEGER DTE = 0 %CONSTINTEGER DCE = 1 %CONSTINTEGER TX = 7 %CONSTINTEGER RX = 3 %CONSTINTEGER FAST = 1 %CONSTINTEGER NOT NEGOTIABLE = -9 %CONSTINTEGER XPROT HELLO = 2 !* !****** End of constintegers ****** !* !****** Recordformats ****** %INCLUDE "ERCM06.INC_FORMATS" %RECORDFORMAT TIMEF(%BYTEARRAY A(0:3)) %RECORDFORMAT FACTABF(%STRING(15) FACILITY, %BYTE SER) %RECORDFORMAT FACF(%BYTEARRAY A(0:255) %OR %STRING (255) S) %RECORDFORMAT LINEF(%RECORD (QF) %NAME LINK, %BYTE %C PROCNO,STATE,WRITES LEFT,LCGN,SER,LINE NO,DCEDTE,%RECORD (QF) CALL Q, %C %BYTEARRAY LCNTAB(0:NO OF LCNS - 1)) %RECORDFORMAT PROCF(%RECORD (QF) %NAME LINK, %C %BYTE PROCNO, STATE, TASK ID, TASK PORT, FAC, QUIETTIME, ISTATE, TSFLAG, %C %RECORD (TIMEF) TIME, %C %RECORD (LINEF) %NAME LINELINK,%RECORD (QF) OUTQ, %C %BYTE LCGN,LCN,AAA,EEE,TTT,CCC,TSTATE,SUBSTATE,RC,WW IN,WW OUT,PS IN,PS OUT, %RECORD (CVF) CV) !* !****** Monitoring information ****** !* %CONSTINTEGER MAXMON = 64 %CONSTBYTEINTEGERARRAY MONACTION(0:MAXMON)= %C 2, 2, 2, 4, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 3, 2, 2 %OWNBYTEINTEGER DCEDTE = 0 %OWNINTEGER SHUTTERS UP = 0 %OWNBYTEINTEGER MONBYTE = 3 ;!all messages to disc. %OWNINTEGERARRAY MONCOUNT(0:MAXMON) = 0(MAXMON+1) !****** Integers ****** !* !No. of writes we have left out of WRITEMAX (currently 24) %OWNINTEGER I,R,S,M,INDEX = 0 %OWNSTRING(2) FAST SELECT = "**" %OWNSTRING(1) ZEROSTR = "*" !* !Upper level refers to Protocol conversion modules. Lower refers to level 2 !protocol handlers and Buffer Manager. %CONSTRECORD (QF) %NAME NULL == 0 %OWNRECORD (LINEF) %NAME LINE %OWNRECORD (PROCF) %NAME PROCESS %OWNRECORD (PF) P !****** Records and Recordarrays ****** %OWNRECORD (QF) BUSY Q,FREE Q = 0 %CONSTINTEGER FACMAX = 6 %OWNINTEGER FACTOT = 0 %OWNRECORD (FACTABF) %ARRAY FACLIST(0:FACMAX-1) %OWNINTEGER BUFFERS HELD = 0 !&&1 omit next line %OWNRECORD (TIMEF) CURTIME = 0 %OWNRECORD (PROCF) %ARRAY PROCTAB(1:NO OF PROCS) !* !****** Routine Specs ****** !* %ROUTINE PUSH(%RECORD (QF) %NAME Q,NEW) Q_COUNT = Q_COUNT + 1 %IF Q_LINK == NULL %START Q_LINK == NEW NEW_LINK == NEW %ELSE NEW_LINK == Q_LINK_LINK Q_LINK_LINK == NEW Q_LINK == NEW %FINISH %END %RECORD (QF) %MAP POP(%RECORD (QF) %NAME Q) %RECORD (QF) %NAME OLD %IF Q_LINK == NULL %THEN OLD == NULL %ELSESTART %IF Q_LINK_LINK == Q_LINK %START ;!One element only on Q OLD == Q_LINK Q_LINK == NULL %ELSE OLD == Q_LINK_LINK Q_LINK_LINK == OLD_LINK %FINISH Q_COUNT = Q_COUNT - 1 %FINISH %RESULT == OLD %END %INCLUDE "ERCM06.INC_EXTS" %ROUTINESPEC INCREMENT(%INTEGER TXRX, LENGTH) %EXTERNALSTRING (15) %FNSPEC ITOS(%INTEGER NO, WIDTH) %INTEGERFNSPEC NEXT FREE LCN(%INTEGER DCEDTE) %INTEGERFNSPEC ACKNOWLEDGE(%BYTE SEQNO) %ROUTINESPEC ASK FOR BUFFER(%BYTE FN) %ROUTINESPEC MOVE(%INTEGER LEN, FROM, TO) %ROUTINESPEC PULL(%RECORD (QF) %NAME Q,ITEM) %ROUTINESPEC REJECT CALL(%INTEGER QUALIFIER) %ROUTINESPEC REJECT CONNECT(%INTEGER FN, QUALIFIER) %ROUTINESPEC RELEASE PROCESS %STRING (255) %FNSPEC SUB STRING(%RECORD (MEF) %NAME MES, %INTEGER NO) !&&1 omit next line %ROUTINESPEC SUBTRACT TIMES %ROUTINESPEC BUFFER ARRIVED %INTEGERFNSPEC FIND(%INTEGER ELEMENT, %INTEGERARRAYNAME LIST) %STRING (15) %FNSPEC FORM FACILITIES(%INTEGER FLAGS) %ROUTINESPEC FREE BUFFER(%RECORD (MEF) %NAME MES) %ROUTINESPEC HANDLE TS PACKET %ROUTINESPEC HANDLE CLOCK TICK %ROUTINESPEC HANDLE OUTQ %ROUTINESPEC HANDLE LINE INPUT %ROUTINESPEC HANDLE LINE OUTPUT %INTEGERFNSPEC INTERPRET FACILITIES(%INTEGER FN, %BYTEARRAYNAME F) %ROUTINESPEC MONITOR(%RECORD (PF) %NAME P, %INTEGER TYPE) %ROUTINESPEC PACK BCD STRING(%STRING(255) S,%BYTEARRAYNAME A, %C %INTEGERNAME INDEX) %ROUTINESPEC PACKCHAR(%BYTE CHAR, %RECORD (MEF) %NAME MES) %INTEGERFNSPEC PACKETLENGTH(%INTEGER FN) %ROUTINESPEC PACK STRING(%STRING(255)%NAME S,%RECORD (MEF) %NAME MES) %ROUTINESPEC RESTART PROCESSES %INTEGERFNSPEC STOI(%STRING (15) S) %ROUTINESPEC STOP(%INTEGER REASON) %ROUTINESPEC TO ACCOUNT(%RECORD (MEF) %NAME MES, %INTEGER FN) %ROUTINESPEC TO LOWER(%RECORD (MEF) %NAME MES, %INTEGER FN) %ROUTINESPEC TO UPPER(%RECORD (MEF) %NAME MES, %INTEGER FN) %STRING (15) %FNSPEC UNPACK BCD STRING(%BYTEARRAYNAME A,%INTEGER NO) %INTEGERFNSPEC WP TO FACS(%STRING (31) FACS, %STRING (31) %NAME PSSFACS) !*************************************** !* * !* Main Program * !* * !*************************************** LINKIN(XGATE SER) ;LINKIN(FROM XPROT);!dentify ourself to DEIMOS for receiving messages CHANGE OUT ZERO = T3 SER ;!Use buffered console I/O MAP VIRT(BUFFER MANAGER,5,4) ;!Get access to buffer pool MAP VIRT(BUFFER MANAGER,6,5) PRINTSTRING("XGAT Running") ;NEWLINE CHARNO(FAST SELECT, 1) = 1 ;CHARNO(FAST SELECT, 2) = FAST SELECT BIT CHARNO(ZEROSTR, 1) = 0 %FOR I=1,1,NO OF PROCS %CYCLE ;!Initialise our main tables PROCTAB(I)_PROCNO = I ;!Give each process a process no. PROCTAB(I)_STATE = IDLE PUSH(FREE Q,PROCTAB(I)) %REPEAT POFF(P) ;!Wait for configuration message from loader DCEDTE = P_FN ;!Are we a DCE or a DTE? MONBYTE = P_PROCESS ;!Set monitoring level SHUTTERS UP = P_C2 ;!This enables us to come up refusing connections ALARM(100) ;!Set off a 2-second clock tick %CYCLE ;!We never leave this loop P_SER = 0 ;!Accept anything POFF(P) ;!Wait for a message %IF P_SER = XGATE SER %START ;!A message from the higher levels (PCMs) HANDLE TS PACKET %ELSEIF P_REPLY = 0 ;!Clock tick message HANDLE CLOCK TICK %ELSEIF P_REPLY = BUFFER MANAGER ;!A buffer request has been granted BUFFER ARRIVED %ELSEIF P_FN = LINE INPUT ;!Something from the comms line handler HANDLE LINE INPUT %FINISHELSE HANDLE LINE OUTPUT %REPEAT !**************************************** !* * !* Routines * !* * !**************************************** !* %INTEGERFN ACKNOWLEDGE(%BYTE SEQNO) !Update AAA from incoming N(R) unless we have a bad ack %BYTE X,ACKS X = PROCESS_AAA ;ACKS = 0 %WHILE X&7 # SEQNO %CYCLE %RESULT = BAD ACK %IF X = PROCESS_TTT ACKS = ACKS + 1 X=(X+1)&7 %REPEAT %IF ACKS # 0 %START P_S1 = ACKS TO UPPER(NULL, ACK) %FINISH PROCESS_AAA = SEQNO %RESULT = 0 %END ;!of Acknowledge %ROUTINE ASK FOR BUFFER(%BYTE REASON) !This routine fires off a buffer request to Buffer Manager %RECORD (PF) P P_SER = BUFFER MANAGER ;P_REPLY = OWN ID P_FN = REQUEST BUFFER P_PROCESS = PROCESS_PROCNO P_LEN = SHORT P_C2 = REASON PON(P) %END ;!of Ask For Buffer %ROUTINE BUFFER ARRIVED !A previously issued buffer request has been granted. !P_C2 was set up in ASK FOR BUFFER and tells us why we !asked for it. %INTEGER L %RECORD (MEF) %NAME MES %BYTENAME STATE BUFFERS HELD = BUFFERS HELD + 1 STOP(99) %IF 0 # P_M_TYPE # 64 ;!It got grotted MES == P_M MES_L = 0 PROCESS == PROCTAB(P_PROCESS) STATE == PROCESS_STATE LINE == PROCESS_LINELINK %UNLESS P_C2 = RESTART INDICATION %IF P_C2 = RR %AND 3 <= STATE <= 4 %START ;!Value 1 TO LOWER(MES, RR) %ELSEIF P_C2 = RESTART INDICATION ;!Value 251 !Note PROCESS points to LINE process LINE == PROCESS %IF LINE_STATE = RESTARTING %THEN TO LOWER(MES, RESTART INDICATION) %C %ELSE FREE BUFFER(MES) %ELSEIF P_C2 = CLEAR REQUEST ;!Value 19 FREE BUFFER(POP(PROCESS_OUTQ)) %WHILE PROCESS_OUTQ_COUNT # 0 %IF STATE = ESTB %OR STATE = WAIT DATA %OR STATE = WTACN %START TO LOWER(MES, CLEAR REQUEST) ;INCREMENT(TX, 0) STATE = WTDSN2 ;PROCESS_QUIETTIME = 6 %ELSEIF STATE = WTACI TO LOWER(MES, CLEAR REQUEST) ;INCREMENT(TX, 0) STATE = WTDSN %ELSE FREE BUFFER(MES) %FINISH %ELSEIF P_C2 = INTERRUPT %AND 3 <= STATE <= 4 ;!Value 35 TO LOWER(MES, INTERRUPT) INCREMENT(TX, 0) %ELSEIF P_C2 = DISCONNECT ;!Value 3 PROCESS_SUBSTATE = PROCESS_SUBSTATE & (\2) !&&1 omit next line SUBTRACT TIMES PACKCHAR(0, MES) PACKSTRING(PROCESS_CV_S, MES) P_S1 = PROCESS_CV_REASON %IF STATE = ESTB %OR STATE = WTACI %OR STATE = WDACI %START TO UPPER(MES, DISCONNECT) STATE = WTDSI %ELSEIF STATE = WTACN %OR STATE = WTDSN2 %OR STATE = WAIT DATA TO UPPER(MES, DISCONNECT) RELEASE PROCESS %ELSEIF STATE = WTDSN TO UPPER(MES, DISCONNECT) PROCESS_TASKPORT = 0 ;!Decouple process from higher level %ELSE FREE BUFFER(MES) %FINISH !&&1 omit %ELSEIF P_C2 = CHECKPOINT %AND 3 <= STATE <= 4 ;!Value 45 SUBTRACT TIMES MES_S = PROCESS_CV_S TO ACCOUNT(MES, CHECKPOINT) !&&1 mark %ELSEIF P_C2 = CALL ACCEPTED ;!Value 15 PROCESS_SUBSTATE = PROCESS_SUBSTATE & (\1) MES_DATA(0) = 0 ;!Address lengths (=0) MES_DATA(1) = 0 MES_L = 2 TO LOWER(MES, CALL ACCEPTED) %IF PROCESS_TSFLAG = 1 %START ASK FOR BUFFER(TS ACCEPT) ;PROCESS_SUBSTATE = PROCESS_SUBSTATE ! 4 %FINISHELSE HANDLE OUTQ %ELSEIF P_C2 = TS ACCEPT ;!Value 17 PROCESS_SUBSTATE = PROCESS_SUBSTATE & (\4) MES_L = 1 ;MES_FN = X'80' MES_DATA(0) = TS ACCEPT TO LOWER(MES, DCE DATA) PROCESS_TTT = (PROCESS_TTT + 1)&7 HANDLE OUTQ %ELSE FREE BUFFER(MES) %FINISH %END ;!of Buffer Arrived %INTEGERFN FIND(%INTEGER ELEMENT, %INTEGERARRAYNAME LIST) !ry to find ELEMENT in LIST. Return the !position of ELEMENT if it exists, -1 if not. %INTEGER I %FOR I = 0,1, LIST(-1) - 1 %CYCLE %IF LIST(I) = ELEMENT %THENRESULT = I %REPEAT %RESULT = -1 %END ;!of Find %STRING (15) %FN FORM FACILITIES(%INTEGER FLAGS) %STRING (15) FACS !Takes two sets of information: 1) The current facilities !values and 2) which of these are to be passed on. !It then creates a facilities field of the form "W=m/n,P=m/n" FACS = "" %IF FLAGS & WW SET # 0 %START ;!We want to specify a window size FACS = "W=".ITOS(PROCESS_WW IN, -1)."/".ITOS(PROCESS_WW OUT, -1) FACS = FACS."," %IF FLAGS & PS SET # 0 %FINISH %IF FLAGS & PS SET # 0 %START ;!Specify packet size !Note PSS transfers packet size as n, where 2**n = size FACS = FACS."P=".ITOS(1< FOUND LINE %IF P_PROCESS = LINE_LINENO LINE == LINE_LINK %REPEAT %FINISH MONITOR(P, BAD PROCESS) FREE BUFFER(MES) %RETURN FOUND LINE: LCGN = MES_OCTET1&15;!Received LCGN %IF FN = RESTART CONFIRMATION %THEN -> SW RESTART CONF %IF FN = RESTART INDICATION %THEN -> SW RESTART ;!Multi-process command %IF FN = INCOMING CALL %THEN ->SW INCOMING CALL ;!No process yet. !There ought to be a process for this message on the circular !List queued off LINE_CALL Q %IF LINE_CALL Q_COUNT # 0 %START PROCESS == LINE_CALL Q %FOR I = 1,1,LINE_CALL Q_COUNT %CYCLE PROCESS == PROCESS_LINK %IF PROCESS_LCGN = LCGN %AND PROCESS_LCN = MES_LCN %THEN -> FOUND IT ;!Correct LCGN and LCN %REPEAT %FINISH %IF FN # CLEAR CONFIRMATION %THEN %C MONITOR(P,BAD INSTATE) ;!Should have been a process if it wasnt an incoming call FREE BUFFER(MES) %RETURN FOUND IT: STATE == PROCESS_STATE %IF FN&3 #3 %START ;!Data,RR,RNR, (REJ) NR = FN>>5 ;FN = FN&X'1F' ;!Remove R: Next block expected %IF FN&1=0 %START ;!Data NS = (FN>>1)&7 ;!Block sequence no. M = FN&X'10' FN = DCE DATA %FINISH %FINISH %IF FIND(FN, INFNS) >= 0 %THEN -> SW(FN) ;!Just check its a valid X-25 function MONITOR(P,BAD FN) FREE BUFFER(MES) %RETURN SW INCOMING CALL:!Can occur in any state but only IDLE accepted %IF LINE_STATE = ESTABLISHED %START !Set up a process if one is available and does not exist already %IF LINE_CALL Q_COUNT # 0 %START PROCESS == LINE_CALL Q %FOR I = 1, 1, LINE_CALL Q_COUNT %CYCLE PROCESS == PROCESS_LINK %IF PROCESS_LCGN = LCGN %AND PROCESS_LCN = MES_LCN %START ;!LCGN and LCN clash - Call collision !PSS X-25 Call collision: DCE accepts the Call Request !and a) forgets about its Incoming Call then b) rejects the CONNECT !which generated it. Ultimately it will retry on another LCN !DTE just ditches the Incoming Call as it knows the DCE !will handle it. !If the other process is not waiting for a call accepted !(Call connected) then it is an error and should be ditched MONITOR(P, MON CALL COLLISION) %IF LINE_DCEDTE = DTE %OR PROCESS_STATE # WTACN %START FREE BUFFER(MES) %RETURN %ELSE P_S1 = CALL COLLISION TO UPPER(NULL, DISCONNECT) RELEASE PROCESS %EXIT ;!Should not be TWO active processes with the same LCN %FINISH %FINISH %REPEAT %FINISH %IF LINE_LCNTAB(MES_LCN) = 0 %START ;!Make sure he isnt using a busy stream PROCESS == POP(FREE Q) %UNLESS PROCESS == NULL %START ;!There is a process available !Zero the entire process but save its process number I = PROCESS_PROCNO ;PROCESS=0 ;PROCESS_PROCNO = I PUSH(LINE_CALL Q,PROCESS) ;!Queue it on busy process q STATE == PROCESS_STATE ;PROCESS_LCGN = LCGN PROCESS_CV_L = 15 ;!Set up call statistics record PROCESS_LCN = MES_LCN ;LINE_LCNTAB(MES_LCN) = 1 PROCESS_LINELINK == LINE %IF LINE_LINE NO # SERCLINE %START PROCESS_WW IN = 2 ;PROCESS_WW OUT = 2 %ELSE PROCESS_WW IN = 1 ;PROCESS_WW OUT = 1 %FINISH PROCESS_TIME = CURTIME !TSTATE = Clear, OUTQ == NULL implicitly !Extract DCE & DTE addresses, facilities and Call User Data field !From Incoming call packet CALLER = UNPACK BCD STRING(DATA,1) CALLED = UNPACK BCD STRING(DATA, 0) %IF SHUTTERS UP # TRUE %START !Subaddresses. If a subaddress is present and has a value between !20 and 30 we take it as the intended value. Otherwise we assume it !refers to PCMA (The triple-X handler) %IF LENGTH(CALLED) = 14 %THEN RC = (DATA(7)>>4)*10+DATA(7)&15 %ELSE RC = 0 RC = PCMA SER %UNLESS 10 <= RC <= 30 DTEL = (DATA(0)>>4 + DATA(0)&15 + 1)>>1 ;!Length of DTE addresses FACL = DATA(DTEL+1) ;!Length of facilities field I = INTERPRET FACILITIES(INCOMING CALL, DATA) %IF I >= 0 %AND FACTOT > 0 %START CUDFL = MES_L - FACL - DTEL - 5 !Three bytes header + Length of DTE/DCE addresses + Facilities length = 5 INCREMENT(RX, CUDFL) CUDFL = 31 %IF CUDFL > 31 FACS = FORM FACILITIES(I) MOVE(CUDFL, ADDR(DATA(DTEL+FACL+2)), ADDR(CUDF)+1) LENGTH(CUDF) = CUDFL !We now try to work out what PCM he wants. Try the CUDF first PROCESS_TASK ID = 0 %IF CUDFL # 0 %START ;!There is Call user data %FOR I = 0,1,FACTOT-1 %CYCLE %IF CUDF -> S.(FACLIST(I)_FACILITY).T %THEN PROCESS_TASK ID = FACLIST(I)_SER %REPEAT %FINISH !If TASK ID is still 0 we didnt find a match, so look at subaddresses %IF PROCESS_TASK ID = 0 %START %FOR I = 0,1,FACTOT-1 %CYCLE %IF FACLIST(I)_SER = RC %THEN PROCESS_TASK ID = RC %AND %EXIT %REPEAT %FINISH !Assume its T.S. if & only if its FTP PROCESS_TSFLAG = 1 %IF PROCESS_TASK ID = FTP SER %IF PROCESS_TASK ID # 0 %START !Send Connect up to PCM MES_L = 0 CALLER = ITOS(LINE_LINENO, -1).".".CALLER %UNLESS LINE_LINENO = 0 PACKCHAR(0, MES) ;!Called PACKSTRING(CALLER,MES) ;!Caller PACKSTRING(FACS, MES) ;!Facilities/Quality PACKSTRING(CUDF, MES) ;!Call User Data/Explanatory Text TO UPPER(MES, CONNECT) PROCESS_STATE = WTACI %ELSE PROCESS_STATE = WTDSN MES_DATA(0) = 0 ;MES_DATA(1) = PROT NOT SUPPORTED TO LOWER(MES, CLEAR INDICATION) ;INCREMENT(TX, 0) %FINISH %ELSE PROCESS_STATE = WTDSN MES_DATA(0) = 0 ;MES_DATA(1) = FACS NOT NEGOTIABLE TO LOWER(MES, CLEAR INDICATION) ;INCREMENT(TX, 0) %FINISH %ELSE PROCESS_STATE = WTDSN MES_DATA(0) = 0 ;MES_DATA(1) = NO USER SERVICE TO LOWER(MES, CLEAR INDICATION) ;INCREMENT(TX, 0) %FINISH %ELSE ;!No free processes !Note: a clear confirmation will come back with no process REJECT CALL(GATEWAY FULL) %FINISH %FINISHELSE REJECT CALL(LCN CONFLICT) %FINISHELSE FREE BUFFER(MES) ;!Ignore Calls while restarting or down %RETURN SW(CALL CONNECTED):!Reply to call request to Network !Work out user data length for stats %IF MES_L >= 5 %START DTEL = (DATA(0)>>4 + DATA(0) & 15 + 1)>>1 FACL = DATA(DTEL + 1) INCREMENT(RX, MES_L - DTEL - FACL - 5) %FINISH !Expect in states WTACN or WTDSN2 %IF STATE = WTACN %START ;!Call now successfully established. I = INTERPRET FACILITIES(CALL CONNECTED, DATA) STATE = ESTB FACS = FORM FACILITIES(I) MES_L = 0 PACKCHAR(0, MES) ;!Recall Address %IF LENGTH(FACS) # 0 %THEN PACKSTRING(FACS, MES) %ELSE %C FREE BUFFER(MES) %AND MES == NULL TO UPPER(MES, ACCEPT CALL) HANDLE OUTQ %FINISHELSE FREE BUFFER(MES) %RETURN SW(RNR):!Level 3 RNR PROCESS_TSTATE = SET ;!Remote RNR set against us. MONITOR(NULL,RNRS RX) ->END1 SW(REJ):!Level 3 REJ. Shouldn't get this. RC = REJS RX ->CHOP SW(RR):!Level 3 RR PROCESS_TSTATE = CLEAR ;!Clear remote RNR if set against us. MONITOR(NULL,RRS RX) END1: %IF 3 <= STATE <= 4 %START RC = ACKNOWLEDGE(NR) %IF RC # 0 %THEN -> CHOP HANDLE OUTQ %FINISH FREE BUFFER(MES) %RETURN CHOP: MONITOR(P, RC) PROCESS_CV_REASON = PACKET LEVEL ERROR TO LOWER(MES, CLEAR INDICATION) ;INCREMENT(TX, 0) PROCESS_STATE = WDACI %RETURN SW(DCE DATA):!Level 3 data block. INCREMENT(RX, MES_L-3) ;!Increment will catch case where mes_l-3 < 0 %IF 3 <= STATE <= 4 %START RC = ACKNOWLEDGE(NR) %IF RC # 0 %THEN -> CHOP %IF NS = PROCESS_EEE %START ;!Level 3 sequence OK MONITOR(NULL, DATAS RX) PROCESS_EEE = (PROCESS_EEE + 1) & 7 P_S1 = 1 - M >> 4 ;!Push it if asked to MES_L = MES_L - 3 ;!User data length only %IF MES_L = 0 %START ;!Ignore any zero-length blocks we may receive PROCESS_CCC = (PROCESS_CCC + 1) & 7 TO LOWER(MES, RR) %ELSEIF MES_OCTET1 < 128 ;!Ordinary data and Push TO UPPER(MES, INPUT HERE) %ELSEIF PROCESS_TSFLAG = 0 ;!Control data, not T.S. TO UPPER(MES, CONTROL INPUT HERE) %ELSE ;!Control data, T.S. !We don't implement this yet so discard it (nasty isnt it) PROCESS_CCC = (PROCESS_CCC + 1) & 7 TO LOWER(MES, RR) %FINISH HANDLE OUTQ %ELSE ;!Sequence error - shouldn't happen MONITOR(P, DSEQERRS RX) PROCESS_CV_REASON = PACKET LEVEL ERROR TO LOWER(MES, CLEAR INDICATION) ;INCREMENT(TX, 0) PROCESS_STATE = WDACI %FINISH %FINISHELSE FREE BUFFER(MES) %RETURN SW(CLEAR CONFIRMATION):!Expect in states WTDSN,WTDSN2 MONITOR(NULL,CLEAR CONFS RX) ->SWC SW(CLEAR INDICATION):!Expect in any state MONITOR(NULL,CLEARS RX) PROCESS_CV_REASON = CALL CLEARED ;PROCESS_CV_CAUSE = DATA(0) ;PROCESS_CV_DIAGS = DATA(1) SWC: !&&1 omit %IF MES_L > 5 %START ;!Fastselect or extended formats may have user data !DCE Clear Confirmation doesnt have the Cause & Diags fields %IF FN = CLEAR CONFIRMATION %AND LINE_DCEDTE = DTE %THEN I = 0 %ELSE I = 2 DTEL = (DATA(I)>>4 + DATA(I) & 15 + 1)>>1 FACL = DATA(DTEL + 3) INCREMENT(RX, MES_L - DTEL - FACL - I - 5) %FINISH !&&1 mark I = INTERPRET FACILITIES(FN, DATA) FREE BUFFER(POP(PROCESS_OUTQ)) %WHILE PROCESS_OUTQ_COUNT # 0 !We always reply immediately to a clear if we reply at all. !Either way, we have finished with the LCN at the end of this section LINE_LCNTAB(PROCESS_LCN) = 0 %IF STATE = WTDSN %START FREE BUFFER(MES) %IF PROCESS_SUBSTATE & 2 # 0 %START !We are in the process of disconnecting a call by timeout STATE = WTDSN2 ;PROCESS_QUIETTIME = 6 PROCESS_LCN = 255 %FINISHELSE RELEASE PROCESS %ELSE %IF 1 <= STATE <= 4 %START !Evidence is that this packet is charged for in advance by PSS TO LOWER(MES, CLEAR CONFIRMATION) ;INCREMENT(TX, 0) %FINISHELSE FREE BUFFER(MES) ASK FOR BUFFER(DISCONNECT) ;PROCESS_SUBSTATE = PROCESS_SUBSTATE ! 2 PROCESS_LCN = 255 ;!Decouple process from this LCN/LCGN %FINISH %RETURN SW(INTERRUPT):!Action in state ESTB only MONITOR(NULL,INTS RX) INCREMENT(RX, 0) TO LOWER(MES,INTERRUPT CONFIRMATION) INCREMENT(TX, 0) %IF STATE = ESTB %START %IF MES_L > 3 %THEN X = DATA(0) %ELSE X = 'A' !Interrupt user data (one byte only) P_S1 = X TO UPPER(NULL,EXPEDITED DATA) %FINISH %RETURN SW(INTERRUPT CONFIRMATION): MONITOR(NULL, INT CONFS RX) INCREMENT(RX, 0) PROCESS_ISTATE = 0 ;!Clear interrupt pending condition FREE BUFFER(MES) %RETURN SW(RESET INDICATION): PRINTSTRING("Reset") ;WRITE(DATA(0), 1) ;PRINTSYMBOL('/') WRITE(DATA(1), 1) ;NEWLINE MONITOR(P, RESETS RX) !Not a lot we can do for user, so chop connection at this point PROCESS_CV_REASON = CALL RESET ;PROCESS_CV_CAUSE = DATA(0) ;PROCESS_CV_DIAGS = DATA(1) TO LOWER(MES, CLEAR REQUEST) STATE = WDACI %RETURN SW RESTART:!Clear down Network side (all processes) completely and !Interface (upper) side tidily. MONITOR(NULL,RESTARTS RX) %IF LINE_STATE = RESTARTING %START P_LEN = 0 TO ACCOUNT(NULL, HELLO) ;!Tell Account line is up LINE_STATE = ESTABLISHED FREE BUFFER(MES) %ELSE MES_FN = RESTART CONFIRMATION !Dont use TO LOWER as we dont have a process I = P_SER ;P_SER = P_REPLY ;P_REPLY = I ;!Send it back whence it came P_FN = LINE OUTPUT !P_M == MES already P_LEN = 3 MONITOR(P, RESTART CONFS TX) PON(P) RESTART PROCESSES %FINISH %RETURN SW RESTART CONF: MONITOR(NULL, RESTART CONFS RX) %IF LINE_STATE = RESTARTING %START P_LEN = 0 TO ACCOUNT(NULL, HELLO) ;!Tell Account line is up LINE_STATE = ESTABLISHED %FINISH FREE BUFFER(MES) %RETURN SW(RESET CONFIRMATION): SW(*): STOP(97) %END ;!of HANDLE LINE INPUT %ROUTINE HANDLE LINE OUTPUT %INTEGER I MONITOR(P, FROM LOW) %IF P_LEN = XPROT HELLO %START ;!Init message at Task startup (one only per task) LINE == POP(FREE Q) ;!Get a line process %UNLESS LINE == NULL %START ;!Got one OK. I = LINE_PROCNO ;LINE = 0 ;LINE_PROCNO = I PUSH(BUSY Q, LINE) LINE_STATE = DOWN LINE_SER = P_REPLY LINE_LINE NO = P_PROCESS LINE_DCEDTE = (DCEDTE>>LINE_LINENO) & 1 LINE_LCGN = 4 LINE_WRITES LEFT = MAX WRITES %FINISHELSE STOP(MON NO PROCS) %ELSE ;!Line Up/Down or write ack I = NO OF PROCS %WHILE LINE_LINE NO # P_PROCESS %AND I # 0 %CYCLE LINE == LINE_LINK I = I - 1 %REPEAT %IF LINE_LINE NO # P_PROCESS %THEN MONITOR(P, BAD PROCESS) %ELSESTART %IF P_LEN = 1 %START ;!Link down message TO ACCOUNT(NULL, HELLO) MONITOR(NULL,LINE DOWN) RESTART PROCESSES LINE_STATE = DOWN %ELSE %IF LINE_STATE = DOWN %START PRINTSTRING("Line ".ITOS(LINE_LINENO,-1)." Up") ;NEWLINE MONITOR(NULL,LINE UP) PROCESS == LINE ;!For benefit of ASK FOR BUFFER ASK FOR BUFFER(RESTART INDICATION) LINE_STATE = RESTARTING %FINISH %FINISH %FINISH %FINISH %END ;!of HANDLE LINE OUTPUT %ROUTINE HANDLE OUTQ !Send off what we can from the output queue %RECORD (MEF) %NAME MES %RETURN %UNLESS 3 <= PROCESS_STATE <= 4 %RETURN %IF PROCESS_SUBSTATE & 5 # 0 %WHILE PROCESS_TSTATE = CLEAR %AND PROCESS_OUTQ_COUNT # 0 %C %AND (PROCESS_TTT - PROCESS_AAA)&7 < PROCESS_WW OUT %CYCLE MES == POP(PROCESS_OUTQ) TO LOWER(MES,DTE DATA) ;INCREMENT(TX, MES_L) PROCESS_TTT = (PROCESS_TTT+1)&7 %REPEAT %IF PROCESS_STATE = WAIT DATA %AND PROCESS_OUTQ_COUNT = 0 %THEN %C ASK FOR BUFFER(CLEAR REQUEST) %END ;!of HANDLE OUTQ %ROUTINE HANDLE TS PACKET %OWNBYTEARRAY FS(0:2) = 2, 1, 128 ;!Fast select facility %STRING(31) CALLER, CALLED, CUDF, FACS %RECORD (MEF) %NAME MES %BYTENAME STATE %BYTE R,S %INTEGER FLAGS %INTEGER FN,LCN,LINENO,L,I,J %STRING(7) LS %SWITCH SW(ACCEPT CALL:PUT CONTROL OUTPUT) MES == P_M ;FN = P_FN %UNLESS MES == NULL %OR FN = ENABLE FACILITY %OR FN = DISABLE FACILITY %START BUFFERS HELD = BUFFERS HELD + 1 STOP(94) %IF 0 # MES_TYPE # 64 %FINISH MONITOR(P, FROM UP) %IF FN = CONNECT %OR FN = DATAGRAM %THEN -> SW CONNECT %IF FN = ENABLE FACILITY %OR FN = DISABLE FACILITY %THEN ->SW ASSIGN FACILITY !Process numbers: Processes are identified by two process numbers, !but usually GATE PORT. In the case of CONNECT or DISCONNECT before !ACCEPT CALL this is not known, so GATE PORT is 0 and the process is !identified by the sender's process TASK PORT %IF 1 <= P_GATE PORT <= NO OF PROCS %START !He correctly specified one of our processes PROCESS == PROCTAB(P_GATE PORT) ->FOUND IT %ELSE %IF BUSYQ_COUNT # 0 %OR P_GATE PORT # 0 %START !He specified his process only and there are processes active LINE == BUSY Q %FOR I = 1, 1, BUSY Q_COUNT %CYCLE LINE == LINE_LINK %IF LINE_CALL Q_COUNT # 0 %START PROCESS == LINE_CALL Q %FOR J = 1, 1, LINE_CALL Q_COUNT %CYCLE PROCESS == PROCESS_LINK %IF PROCESS_TASK PORT=P_TASK PORT %THEN ->FOUND IT %REPEAT %FINISH %REPEAT %FINISH %FINISH !Failed to find a process corresponding to specified proc.no MONITOR(P,BAD PROCESS) FREE BUFFER(MES) %RETURN FOUND IT: STATE == PROCESS_STATE LINE == PROCESS_LINELINK ->SW(FN) %IF ACCEPT CALL <= FN <= PUT CONTROL OUTPUT MONITOR(P, BAD FN) STOP(BAD FN) SW CONNECT: CALLED = SUBSTRING(MES, 1) LS = "S" %UNLESS CALLED -> LS.(".").CALLED ;!Pick Line number off front LINENO = CHARNO(LS, 1) %IF LINENO = 'S' %START LINENO = SERCLINE %ELSEIF LINENO = 'P' LINENO = PSSLINE %ELSEIF '0' <= LINENO <= '9' LINENO = LINENO - '0' %ELSE MONITOR(NULL, MON BAD PARAM) REJECT CONNECT(FN, BAD PARAM) %RETURN %FINISH %IF BUSY Q_COUNT # 0 %START LINE == BUSY Q %FOR I = 1, 1, BUSY Q_COUNT %CYCLE LINE == LINE_LINK %IF LINE_LINENO = LINENO %AND LINE_STATE = ESTABLISHED %THEN -> OK %REPEAT %FINISH REJECT CONNECT(FN, NETWORK DOWN) %RETURN OK: !Scan calls on this line to ensure this is not a duplicate request %IF LINE_CALL Q_COUNT # 0 %START PROCESS == LINE_CALL Q %FOR I = 1,1,LINE_CALL Q_COUNT %CYCLE PROCESS == PROCESS_LINK %IF PROCESS_TASK ID = P_REPLY %AND PROCESS_TASK PORT = P_TASK PORT %START ;!Duplicate request MONITOR(NULL, MON PROCESS RUNNING) REJECT CONNECT(FN, PROCESS RUNNING) %RETURN %FINISH %REPEAT %FINISH LCN = NEXT FREE LCN(LINE_DCEDTE) %IF LCN >= 0 %START ;!Got a free LCN PROCESS == POP(FREE Q) %UNLESS PROCESS == NULL %START ;!All OK. !Record the connection and send off a Call Request I = PROCESS_PROCNO ;PROCESS=0 ;PROCESS_PROCNO = I PUSH(LINE_CALL Q,PROCESS) PROCESS_CV_L = 15 !Tstate = Clear, OUTQ_COUNT = 0 implicitly %IF LINE_LINENO # SERCLINE %START PROCESS_WW IN = 2 ;PROCESS_WW OUT = 2 ;!Window sizes %ELSE PROCESS_WW IN = 1 ;PROCESS_WW OUT = 1 %FINISH PROCESS_LINELINK == LINE ;!Reverse link for speed PROCESS_TASK PORT = P_TASK PORT PROCESS_LCGN = LINE_LCGN PROCESS_LCN = LCN PROCESS_TASK ID = P_REPLY !Assume its T.S. if and only if its FTP PROCESS_TSFLAG = 1 %IF PROCESS_TASK ID = FTP SER CALLER = SUBSTRING(MES, 2) FACS = SUBSTRING(MES, 3) ;!Facilities/Quality CUDF = SUBSTRING(MES, 4) ;!Call User Data/Explanatory Text I = WP TO FACS(FACS, FACS) %IF I >= 0 %START ;!All OK %IF LENGTH(CUDF) > 16 %THEN FACS = FAST SELECT.FACS %IF FN = DATAGRAM %THEN CHARNO(FACS, 2) = CHARNO(FACS, 2) ! RESTRICTED RESPONSE BIT MES_DATA(0) = LENGTH(CALLER)<<4+LENGTH(CALLED)&15 INDEX = 2 ;!Index counts in quartets not bytes PACK BCD STRING(CALLED.CALLER,MES_DATA,INDEX) STRING(ADDR(MES_DATA(INDEX>>1))) = FACS INDEX = INDEX + LENGTH(FACS)<<1 + 2 MOVE(LENGTH(CUDF), ADDR(CUDF)+1, ADDR(MES_DATA(INDEX>>1))) INDEX = INDEX + LENGTH(CUDF)<<1 MES_L = INDEX>>1 ;!Length of data + header (Used by TO LOWER) I = INTERPRET FACILITIES(CALL REQUEST, MES_DATA) PROCESS_TIME = CURTIME TO LOWER(MES,CALL REQUEST) ;INCREMENT(TX, LENGTH(CUDF)) PROCESS_STATE = WTACN %ELSE MONITOR(NULL, MON BAD PARAM) LINE_LCNTAB(LCN) = 0 REJECT CONNECT(FN, BAD PARAM) RELEASE PROCESS %FINISH %ELSE LINE_LCNTAB(LCN) = 0 REJECT CONNECT(FN, GATEWAY FULL) %FINISH %ELSE MONITOR(NULL, MON NO FREE LCNS) REJECT CONNECT(FN, NO FREE LCNS) %FINISH %RETURN SW(PUT CONTROL OUTPUT):!Control data (for transmission with QBIT = 1 MES_FN = X'80' -> SW CTRL OUTPUT SW(PUT OUTPUT):!Data block MES_FN = 0 SW CTRL OUTPUT: %IF LINE_STATE # DOWN %AND STATE = ESTB %START ;!Data only valid in established call PUSH(PROCESS_OUTQ,MES) !We ignore the PUSH bit in P_S1 since all data is acknowleged !end-to-end anyway !MES_L contains length of Level 3 user data HANDLE OUTQ ;!See if we can send anything. %ELSE MONITOR(P, DATA OUTSIDE CONNECT) !Line down FREE BUFFER(MES) %FINISH %RETURN SW(ACK): %IF PROCESS_STATE = ESTB %START PROCESS_CCC = (PROCESS_CCC + P_S1) & 7 ASK FOR BUFFER(RR) %FINISH %RETURN SW(ACCEPT CALL):!States WTACI, WDACI %IF STATE = WTACI %START !Connection now fully established. Log his process number and PROCESS_STATE = ESTB PROCESS_TASK PORT = P_TASK PORT %IF MES == NULL %START ASK FOR BUFFER(CALL ACCEPTED) PROCESS_SUBSTATE = PROCESS_SUBSTATE ! 1 %ELSE I = WP TO FACS(SUBSTRING(MES, 3), FACS) STOP(96) %IF I < 0 MES_DATA(0) = 0 STRING(ADDR(MES_DATA(1))) = FACS MES_L = LENGTH(FACS) + 2 TO LOWER(MES, CALL ACCEPTED) %IF PROCESS_TSFLAG = 1 %START ASK FOR BUFFER(TS ACCEPT) ;PROCESS_SUBSTATE = PROCESS_SUBSTATE ! 4 %FINISH %FINISH %FINISHELSE FREE BUFFER(MES) ;!Otherwise just ignore it (WDACI, WTDSI) %RETURN SW(DISCONNECT):! FREE BUFFER(MES) PROCESS_CV_REASON = P_S1 %IF PROCESS_CV_REASON = 0 %IF STATE = ESTB %AND PROCESS_OUTQ_COUNT # 0 %START STATE = WAIT DATA ;PROCESS_QUIETTIME = 6 %ELSE %IF STATE=ESTB %OR STATE=WTACN %OR STATE=WTACI %THEN %C ASK FOR BUFFER(CLEAR REQUEST) %ELSESTART FREE BUFFER(POP(PROCESS_OUTQ)) %WHILE PROCESS_OUTQ_COUNT # 0 %IF STATE = WTDSI %THEN RELEASE PROCESS %ELSE STATE = WTDSN2 %AND PROCESS_QUIETTIME = 6 %FINISH %FINISH %RETURN SW(EXPEDITED DATA): %IF PROCESS_ISTATE = 0 %START ASK FOR BUFFER(INTERRUPT) PROCESS_ISTATE = 1 ;!Block further interrupts or we may get reset %FINISH %RETURN SW ASSIGN FACILITY: !First get facility string from P or P_M (According to P_S1) %IF P_S1 = 0 %START FACS = P_FACILITY %ELSE FACS = MES_S BUFFERS HELD = BUFFERS HELD + 1 FREE BUFFER(MES) %FINISH !Note that once assigned the facility has a table entry for ever !Next, look throught table and see if we know about this one %IF FACTOT # 0 %START %FOR I = 0, 1, FACTOT-1 %CYCLE %IF FACLIST(I)_FACILITY = FACS %THEN -> ASSIGNED ALREADY %REPEAT %FINISH !Never heard of it. Make a new table entry if we can. I = FACTOT %RETURN %IF FACTOT = FACMAX FACTOT = FACTOT + 1 FACLIST(I)_FACILITY = FACS ASSIGNED ALREADY: !Non-zero SER indicates is is enabled. Zero SER = disabled %IF FN = ENABLE FACILITY %THEN FACLIST(I)_SER = P_REPLY %ELSE FACLIST(I)_SER = 0 %RETURN %END ;!of from higher level %ROUTINE INCREMENT(%INTEGER TXRX, LENGTH) %INTEGER I,SEGS !Inefficient but it'll do for now !Increment segment count. PSS counts 0-64 bytes = 1 segment !65-128 bytes = 2 segments etc. (Obviously not devised by !a programmer) %IF LENGTH < 0 %THEN MONITOR(P, 64) %AND LENGTH = 0 %IF LENGTH = 0 %THEN SEGS = 1 %ELSE SEGS = (LENGTH-1)>>6 + 1 !We dont expect SEGS to exceed about 4. Algorithm breaks down !for segs >= 255. I = PROCESS_CV_OUR SEGS(TXRX) + SEGS ;!Add on segs PROCESS_CV_OUR SEGS(TXRX) = I ;!Jam bottom 8 bits into Our Segs %IF I > 255 %START ;!There was an overflow (can only be 1 bit) %FOR I = TXRX-1, -1, TXRX-3 %CYCLE PROCESS_CV_OUR SEGS(I) = PROCESS_CV_OUR SEGS(I) + 1 ;!Add to next more significant byte %EXIT %IF PROCESS_CV_OUR SEGS(I) & 255 # 0 ;!Stupid compiler !Carry on if we overflowed again %REPEAT %FINISH %END ;!of Increment %INTEGERFN INTERPRET FACILITIES(%INTEGER FN, %BYTEARRAYNAME F) !Integerfn interprets facilities field of incoming packets %CONSTINTEGERARRAY CODELIST(-1:7) = 8, %C 0,1,3,4,X'42',X'43',X'C1',X'C2' %INTEGER I,P,DTEL %SWITCH SW(-1:7) %IF FN = CLEAR INDICATION %THEN DTEL = 2 %ELSE DTEL = 0 DTEL = DTEL + (F(DTEL)>>4 + F(DTEL)&15 + 1)>>1 I = 0 P = DTEL+2 %WHILE P < F(DTEL+1) + DTEL + 2 %CYCLE ->SW(FIND(F(P), CODELIST)) SW(2):!Closed User Group (Not implemented) SW(0):!National Options P = P + 2 ;!Ignore field %CONTINUE SW(*):!Unknown facilities - they might be trying to commit us to something SW(1):!Fast select and reverse Charging %IF F(P+1)&X'FE' # X'80' %THEN %RESULT = NOT NEGOTIABLE !Covers reverse charging and restricted response fast selecrt PROCESS_FAC = FAST P = P + 2 %CONTINUE SW(4):!Packet Size Negotiation !Not our problem PROCESS_PS IN = F(P+1) PROCESS_PS OUT = F(P+2) I = I ! PS SET P = P + 3 %CONTINUE SW(5):!Window size negotiation PROCESS_WW IN = F(P+1) PROCESS_WW OUT = F(P+2) I = I ! WW SET P = P + 3 %CONTINUE SW(6):!Call Duration !Duration always comes before stats MOVE(4, ADDR(F(P+2)), ADDR(PROCESS_CV_PSS CT(0))) P = P + 6 MOVE(8, ADDR(F(P+2)), ADDR(PROCESS_CV_PSS SEGS(0))) P = P + 10 PROCESS_CV_L =27 %CONTINUE %REPEAT %RESULT = I %END ;!of Interpret Facilities %ROUTINE MONITOR(%RECORD (PF) %NAME P,%INTEGER TYPE) %RECORDFORMAT PFA(%BYTEARRAY A(0:27)) %RECORD (PROCF) %NAME PR %RECORD (LINEF) %NAME LN %RECORD (PFA) %NAME PA %INTEGER I,J,K %SWITCH SW(0:4) STOP(TYPE) %UNLESS 0 <= TYPE <= MAXMON MONCOUNT(TYPE) = MONCOUNT(TYPE) + 1 I = 0 ;J = 0 ->SW(MONACTION(TYPE)) SW(4):!Query Command PRINTSTRING("BH=") ;WRITE(BUFFERS HELD, 1) PRINTSTRING(" CT=") ;%FOR I = 0,1,3 %CYCLE ;WRITE(CURTIME_A(I), 1) ;%REPEAT NEWLINE %IF BUSY Q_COUNT # 0 %START LN == BUSY Q %FOR I = 1,1,BUSY Q_COUNT %CYCLE LN == LN_LINK WRITE(LN_PROCNO,1) ;PRINTSYMBOL('/') %IF LN_DCEDTE = DCE %THEN PRINTSTRING(" DCE") %ELSE PRINTSTRING(" DTE") PRINTSTRING(" S=") ;WRITE(LN_STATE, 1) PRINTSTRING(" ID=") ;WRITE(LN_SER, 1) PRINTSTRING(" L=") ;WRITE(LN_LINENO, 1) PRINTSTRING(" LG=") ;WRITE(LN_LCGN, 1) NEWLINE %IF LN_CALL Q_COUNT # 0 %START PR == LN_CALLQ %FOR J = 1,1,LN_CALL Q_COUNT %CYCLE PR == PR_LINK SPACES(3) WRITE(PR_PROCNO, -1) ;PRINTSYMBOL('/') PRINTSTRING(" ST=") ;WRITE(PR_STATE,-1) PRINTSYMBOL('/') ;WRITE(PR_TSTATE, -1) PRINTSTRING(" ID=") ;WRITE(PR_TASK ID,-1) PRINTSYMBOL('.') ;WRITE(PR_TASK PORT, -1) PRINTSTRING(" LC=") ;WRITE(PR_LCGN,-1) PRINTSYMBOL('.') ;WRITE(PR_LCN, -1) PRINTSTRING(" QU=") ;WRITE(PR_QUIETTIME, -1) PRINTSTRING(" OQ=") ;WRITE(PR_OUTQ_COUNT, -1) PRINTSTRING(" AETC=") ;WRITE(PR_AAA, -1) WRITE(PR_EEE,-1) ;WRITE(PR_TTT,-1) WRITE(PR_CCC,-1) PRINTSTRING(" CV") %IF PR_CV_L > 0 %AND PR_CV_L <= 27 %START %FOR K = 0,1,PR_CV_L %CYCLE ;WRITE(PR_CV_A(K),1) ;%REPEAT %FINISH NEWLINE %REPEAT %FINISH %REPEAT %FINISH ->LOG IT SW(3):!Disaster: Always Log to .TT and disc J = 1 ;!Always to .TT SW(*):!All strays here LOG IT:!And query command SW(2):!Exception condition: Always log to disc, conditional to .TT J = J ! (MONBYTE&1) I = 1 ;!Always to disc SW(1):!Log a normal transaction I = I ! (MONBYTE&2) %IF J # 0 %START ;!Log to .TT PRINTSTRING("*XGAT") ;WRITE(TYPE,1) ;NEWLINE %FINISH %IF I # 0 %START SELECTOUTPUT(1) %IF P == NULL %START PRINTSYMBOL(2) ;PRINTSYMBOL(XGATE SER) ;PRINTSYMBOL(TYPE) %ELSE PA == P !Monbyte bit 2**2 set indicates we dont want data monitored %IF MONBYTE&4 # 0 %START -> END %IF (TYPE = TO UP %OR TYPE = FROM UP) %AND (P_FN = DATA %OR P_FN = ACK) -> END %IF (TYPE = TO LOW %OR TYPE = FROM LOW) %AND P_M_FN&3 # 3 %FINISH PRINTSYMBOL(10) ;PRINTSYMBOL(XGATE SER) ;PRINTSYMBOL(TYPE) %FOR I = 0,1,7 %CYCLE PRINTSYMBOL(PA_A(I)) %REPEAT %UNLESS P_M == NULL %OR P_FN = ENABLE FACILITY %OR P_FN = DISABLE FACILITY %START PA == P_M J = P_M_L + 4 %IF (TYPE = TO UP %OR TYPE = FROM UP) %AND (P_FN = DATA %OR P_FN = CONTROL DATA) %THEN J = J + 8 %IF (TYPE = TO LOW %OR TYPE = FROM LOW) %THEN J = J + 5 J = 28 %UNLESS 1 <= J <= 28 PRINTSYMBOL(J) %FOR I = 0,1,J-1 %CYCLE PRINTSYMBOL(PA_A(I)) %REPEAT %FINISH END: %FINISH SELECTOUTPUT(0) %FINISH SW(0): %RETURN %END ;!of MONITOR %ROUTINE MOVE(%INTEGER LEN,FROM,TO) ! ! 'Assembler Routine' to emulate EMAS MOVE. ! Note: 1. No action if LEN<=0 ! 2. Registers 1,2 and 3 used. ! %label Loop, Return ! *MOV_LEN,1 ;! Load the length *BLE_Return ;! Return if less than or equal to zero *MOV_FROM,2 ;! Load the FROM address *MOV_TO,3 ;! Load the TO address ! ! Loop to move LEN bytes FROM -> TO ! Loop: *MOVB_(2)+,(3)+ ;! Move the byte *DEC_1 ;! Decrement length count *BNE_Loop ;! Continue if length not exhausted Return: %return %END %INTEGERFN NEXT FREE LCN(%INTEGER DCEDTE) %INTEGER I %IF DCEDTE = DTE %START %FOR I = NO OF LCNS-1, -1, 0 %CYCLE %IF LINE_LCNTAB(I) = 0 %THEN LINE_LCNTAB(I) = 1 %AND %RESULT = I %REPEAT %ELSE %FOR I = 0, 1, NO OF LCNS-1 %CYCLE %IF LINE_LCNTAB(I) = 0 %THEN LINE_LCNTAB(I) = 1 %AND %RESULT = I %REPEAT %FINISH %RESULT = -1 %END ;!of Next free LCN %ROUTINE PACK BCD STRING(%STRING(255) S,%BYTEARRAYNAME A, %C %INTEGERNAME INDEX) !This routine takes a decimal number as a character string and packs it into A !in packed BCD (two digits per byte) starting ! at A(INDEX) and updating INDEX as it goes. INDEX is a global !variable used for this purpose. Trailing quartets are set to zero. !This routine is intended primarily for packing X-25 addresses %ROUTINESPEC PACK BCD CHAR(%BYTE NO) %INTEGER I %IF LENGTH(S) > 0 %START %FOR I=1,1,LENGTH(S) %CYCLE PACK BCD CHAR(CHARNO(S,I) - '0') %REPEAT %FINISH INDEX=(INDEX+1)&X'FE' ;!Rounds INDEX up to the nearest byte boundary %RETURN %ROUTINE PACK BCD CHAR(%BYTE NO) %INTEGER P P=INDEX>>1 %IF INDEX&1=0 %THEN A(P)=NO<<4 %ELSE A(P) = A(P)+NO&15 INDEX = INDEX + 1 %END ;!of PACK BCD CHAR %END ;!of PACK BCD STRING %ROUTINE PACKCHAR(%BYTE CHAR, %RECORD (MEF) %NAME MES) MES_A(MES_L) = CHAR MES_L = MES_L + 1 %END ;!of Pack Char %INTEGERFN PACKETLENGTH(%INTEGER FN) !Returns the length of the control fields in an X-25 packet. !Call request, Data are not catered for %IF FN = 19 %OR FN = 27 %OR FN = 251 %THENRESULT = 5 ;!Clear & conf, Reset & conf, Restart & conf. %IF FN = 35 %THEN %RESULT = 4 ;!Interrupt %RESULT = 3 %END ;!of PACKETLENGTH %ROUTINE PACK STRING(%STRING (255) %NAME S,%RECORD (MEF) %NAME MES) !Add string S as a substring to MES_A, taking the end of MES_A !From a length supplied in MES_L STRING(ADDR(MES_A(MES_L))) = S MES_L = MES_L + LENGTH(S) + 1 %END ;!of PACKSTRING %ROUTINE PULL(%RECORD (QF) %NAME Q,ITEM) !Pulls the specified item off a circular queue with header at Q %RECORD (QF) %NAME P %IF Q_LINK == NULL %THEN %RETURN ;!No elements (Why??) Q_COUNT = Q_COUNT - 1 %IF Q_LINK_LINK == Q_LINK %THEN Q_LINK == NULL %AND %RETURN ;!One element on Q P == Q %CYCLE P == P_LINK %REPEATUNTIL P_LINK == ITEM P_LINK == P_LINK_LINK %IF Q_LINK == ITEM %THEN Q_LINK == P ;!If we removed Q head, reposition it %END %ROUTINE REJECT CONNECT(%INTEGER FN, QUALIFIER) !Reject CONNECT outright %BYTE SER SER = P_SER ;P_SER = P_REPLY ;P_REPLY = SER ;!Send it back whence it came %IF FN = CONNECT %THEN P_FN = DISCONNECT %ELSE P_FN = DATAGRAM REPLY FREE BUFFER(P_M) P_M == NULL P_S1 = QUALIFIER MONITOR(P, TO UP) PON(P) %END %ROUTINE REJECT CALL(%INTEGER QUALIFIER) !Reject incoming call outright !Dont use TO LOWER as we dont have a process %BYTE SER P_M_FN = CLEAR REQUEST P_M_DATA(0) = 0 ;!Clearing cause = DTE Clearing P_M_DATA(1) = QUALIFIER ;!Diagnostics SER = P_SER ;P_SER = P_REPLY ;P_REPLY = SER P_FN = LINE OUTPUT P_LEN = 5 MONITOR(P, TO LOW) BUFFERS HELD = BUFFERS HELD - 1 STOP(88) %IF 0 # P_M_TYPE # 64 PON(P) %END ;!of REJECT CALL %STRING (255) %FN SUB STRING(%RECORD (MEF) %NAME MES, %INTEGER NO) %INTEGER I,L %UNLESS MES == NULL %OR MES_L<=0 %OR NO<=0 %START L=0 %WHILE NO>1 %CYCLE L=L+MES_A(L)+1 %RESULT = "" %IF L >= MES_L NO = NO - 1 %REPEAT %RESULT = STRING(ADDR(MES_A(L))) %FINISHELSE %RESULT = "" %END ;!of SUB STRING !&&1 omit next routine %ROUTINE SUBTRACT TIMES %INTEGER I,J %BYTEARRAYNAME T,ST %BYTE BORROW T == PROCESS_CV_OUR CT ST == PROCESS_TIME_A !Calculate call duration. T(3) = (CURTIME_A(3) - ST(3)) & 255 ;!Stupid compiler %IF T(3)&255 < 60 %THEN BORROW = 0 %ELSE T(3) = T(3) + 60 %AND BORROW = 1 T(2) = (CURTIME_A(2) - ST(2) - BORROW) & 255 ;!Stupid compiler %IF T(2)&255 < 60 %THEN BORROW = 0 %ELSE T(2) = T(2) + 60 %AND BORROW = 1 T(1) = (CURTIME_A(1) - ST(1) - BORROW) & 255 ;!Stupid compiler %IF T(1)&255 < 24 %THEN BORROW = 0 %ELSE T(1) = T(1) + 24 %AND BORROW = 1 T(0) = CURTIME_A(0) - ST(0) - BORROW %FOR I = 0,1,3 %CYCLE J = T(I)//10 T(I) = J<<4 + (T(I)-J*10) %REPEAT %END ;!of Subtract times %ROUTINE RELEASE PROCESS %INTEGER I !Tidy up process. %IF PROCESS_STATE # IDLE %START ;!Safeguard against duff queues !Clear out in- and out-bound data queues FREE BUFFER(POP(PROCESS_OUTQ)) %WHILE PROCESS_OUTQ_COUNT # 0 PROCESS_STATE = IDLE PULL(LINE_CALL Q,PROCESS) ;PUSH(FREE Q,PROCESS) %FINISH %END ;!of RELEASE PROCESS %ROUTINE RESTART PROCESSES %INTEGER I %BYTENAME STATE %RECORD (MEF) %NAME MES %RECORD (PROCF) %NAME PR %IF LINE_CALL Q_COUNT # 0 %START PROCESS == LINE_CALL Q_LINK %FOR I = 1,1,LINE_CALL Q_COUNT %CYCLE PR == PROCESS_LINK STATE == PROCESS_STATE PROCESS_CV_REASON = CALL RESTARTED %IF STATE = WTDSN %THEN RELEASE PROCESS %ELSESTART ASK FOR BUFFER(DISCONNECT) %IF PROCESS_SUBSTATE & 2 = 0 PROCESS_SUBSTATE = PROCESS_SUBSTATE ! 2 %FINISH LINE_LCNTAB(PROCESS_LCN) = 0 ;!Free off LCN PROCESS_LCN = 255 ;!Decouple process from LCN/LCGN PROCESS == PR %REPEAT %FINISH %END ;!of RESTART PROCESSES %INTEGERFN STOI(%STRING (15) S) %INTEGER N,I N=0 %IF LENGTH(S) > 0 %START %FOR I = 1,1,LENGTH(S) %CYCLE N = N*10 + CHARNO(S,I)-'0' %REPEAT %FINISH %RESULT = N %END ;!of Stoi %ROUTINE STOP(%INTEGER REASON) PRINTSTRING("*XGAT ") ;WRITE(REASON, 1) ;NEWLINE MONITOR(NULL, QUERY) %CYCLE %REPEAT %END ;!of Stop %ROUTINE TO ACCOUNT(%RECORD (MEF) %NAME MES, %INTEGER FN) P_SER = ACCOUNT SER P_REPLY = XGATE SER P_FN = FN P_M == MES %UNLESS P_M == NULL %START BUFFERS HELD = BUFFERS HELD - 1 STOP(92) %IF 0 # P_M_TYPE # 64 %FINISH %IF FN = HELLO %START P_S1 = XGATE SER P_C2 = LINE_LINENO %ELSE P_GATE PORT = PROCESS_TASK ID ;P_TASK PORT = PROCESS_TASK PORT %FINISH MONITOR(P, MON TO ACCT) PON(P) %END ;!of To Account %ROUTINE TO LOWER(%RECORD (MEF) %NAME MES, %INTEGER FN) %RECORD (PF) P %BYTE LEN !Find the length of the Packet. For most packets this is fixed !but for Data,interrupt and connect packets it must be supplied. Length !supplied is Level 3 data length + 3 bytes control %IF FN = DTE DATA %OR FN = CALL REQUEST %OR FN = CALL ACCEPTED %THEN %C LEN = MES_L+3 %ELSE LEN = PACKETLENGTH(FN) %AND MES_L = LEN !Last statement (after %AND) redundant except for Monitor %IF FN < 250 %START ;!Not Restarts or Restart Confirmations MES_OCTET1 = GFI + PROCESS_LCGN %IF FN = DCE DATA %THEN MES_OCTET1 = MES_OCTET1 ! (MES_FN & X'80') !Q-bit buried in bit 2**7 of MES_FN MES_LCN = PROCESS_LCN ;!Bottom byte only %IF FN&3 # 3 %START ;!Data, RR, RNR, REJ FN = FN ! PROCESS_CCC<<5 %IF FN&1 = 0 %THEN FN = FN ! PROCESS_TTT<<1 !We dont use the M bit %FINISH %ELSE MES_OCTET1 = GFI MES_LCN = 0 %FINISH %IF FN = CLEAR REQUEST %OR FN = RESET REQUEST %OR FN = RESTART REQUEST %START MES_DATA(0) = 0 MES_DATA(1) = 0 %FINISH MES_FN = FN P_SER = LINE_SER P_REPLY = XGATE SER P_FN = LINE OUTPUT P_PROCESS = LINE_LINE NO P_M == MES P_LEN = LEN MONITOR(P, TO LOW) BUFFERS HELD = BUFFERS HELD - 1 STOP(89) %IF 0 # P_M_TYPE # 64 PON(P) %END ;!of To Lower %ROUTINE TO UPPER(%RECORD (MEF) %NAME MES, %INTEGER FN) P_SER = PROCESS_TASK ID P_REPLY = XGATE SER P_FN = FN P_GATE PORT = PROCESS_PROCNO P_TASK PORT = PROCESS_TASK PORT P_M == MES MONITOR(P, TO UP) %UNLESS MES == NULL %START BUFFERS HELD = BUFFERS HELD - 1 STOP(93) %IF 0 # P_M_TYPE # 64 %FINISH PON(P) %END ;!of TO UPPER %STRING (15) %FN UNPACK BCD STRING(%BYTEARRAYNAME A, %INTEGER NO) !Retrieves X-25 addresses. These are presented as: !Length(caller),Length(called),called,caller. Lengths are 1 !quartet each. NO=1 gives us the length and contents of caller ! NO=0 " " " " " " " called %BYTEFNSPEC NEXT BCD CHAR %INTEGER I,L,INDEX %STRING(255) S L=(A(0)>>(NO*4))&15 ;!Assumes >>0 is valid. INDEX = (A(0)&15)*NO + 2 %IF L>0 %START %FOR I=1, 1, L %CYCLE CHARNO(S,I) = NEXT BCD CHAR + '0' %REPEAT LENGTH(S) = L %RESULT = S %FINISHELSERESULT = "" %BYTEFN NEXT BCD CHAR %BYTE B %IF INDEX&1 = 0 %THEN B = A(INDEX>>1)>>4 %ELSE B = A(INDEX>>1)&15 INDEX = INDEX + 1 %RESULT = B %END ;!of NEXT BCD CHAR %END ;!of UNPACK BCD STRING %INTEGERFN WP TO FACS(%STRING (31) FACS, %STRING (31) %NAME PSSFACS) !Take a facilities field presented as "W=m/n,P=m/n" and !1) validate it, 2) pull out facilities values, and 3) set flags !to indicate which were specified. Thi function is essentially !the opposite of Form Facilities !*** NOTE: Reverse charging not completely implemented *** %BYTEFNSPEC SHIFT(%INTEGER N) %STRING (15) P,W,M,N,C %INTEGER IM, IN, L, FLAGS W = "" ;P = "" ;C = "" ;L = 0 ;FLAGS = 0 %IF LENGTH(FACS) > 21 %START ;!Max = "W=7/7,P=8192/8192,C=R" (21 chars) %RESULT = -8 %ELSEIF FACS -> W.(",").P ;!Both W and P specified %RESULT = -1 %UNLESS W -> ("W=").W %RESULT = -2 %UNLESS P -> ("P=").P %ELSEIF FACS -> ("W=").W %ELSEIF FACS -> ("P=").P %ELSEIF FACS -> ("C=").C %ELSEIF LENGTH(FACS) # 0 %RESULT = -3 %FINISH %IF LENGTH(W) # 0 %START %RESULT = -4 %UNLESS W -> M.("/").N IM = STOI(M) ;IN = STOI(N) %RESULT = -5 %UNLESS 0 <= IM <= 7 %AND 0 <= IN <= 7 PROCESS_WW IN = IM ;PROCESS_WW OUT = IN CHARNO(PSSFACS, 1) = X'43' ;CHARNO(PSSFACS, 2) = IM ;CHARNO(PSSFACS, 3) = IN L = 3 %FINISH %IF LENGTH(P) # 0 %START %RESULT = -6 %UNLESS P -> M.("/").N IM = STOI(M) ;IN = STOI(N) IM = SHIFT(IM) ;IN = SHIFT(IN) %RESULT = -7 %UNLESS IM # 0 %AND IN # 0 PROCESS_PS IN = IM ;PROCESS_PS OUT = IN CHARNO(PSSFACS, L+1) = X'42' ;CHARNO(PSSFACS, L+2) = IM ;CHARNO(PSSFACS, L+3) = IN L = L + 3 %FINISH %IF C = "R" %START PROCESS_RC = TRUE CHARNO(PSSFACS, L+1) = 1 ;CHARNO(PSSFACS, L+2) = 1 L = L + 2 %FINISH LENGTH(PSSFACS) = L %RESULT = 0 %BYTEFN SHIFT(%INTEGER N) !Function to convert from an integer packet size to form n !where 2**n = packet size. Function just shifts right till either !we get bored or we are left with the topmost bit. I'm sure there !is a clever way to do this in assembler. %INTEGER I %FOR I = 0,1,15 %CYCLE ;!Its a 16-bit integer %RESULT = I %IF N = 1 ;!Gotcha N = N >> 1 %REPEAT %RESULT = 0 ;!Signifies invalid packet size. (NB: size=1 does also) %END ;!of Shift (in WP to Facs) %END ;!of WP to Facs %ENDOFPROGRAM