SMFEX 
PRGM SMFEX; 
BEGIN 
# 
**        SMFEX - SCREEN MANAGEMENT FACILITY EXECUTIVE. 
* 
*         COPYRIGHT CONTROL DATA SYSTEMS INC.  1992.
* 
*         THE SMFEX PROGRAM IS THE EXECUTIVE OR MONITOR FOR THE SMFEX 
*         SUBSYSTEM.  THE FUNCTION PROVIDED BY THE SMFEX SUBSYSTEM IS 
*         AN ALTERNATE IMPLEMENTATION OF THE FULL SCREEN EDITOR (FSE) 
*         TO BE MEMORY RESIDENT, SERVICING LARGE USER COMMUNITIES 
*         WITH A REDUCTION IN SYSTEM SCHEDULING OVERHEAD COMPARED TO
*         THE CONVENTIONAL EXECUTION OF A UNIQUE JOB FOR EACH USER. 
* 
*         THE SMFEX PROGRAM IS RESPONSIBLE TO MANAGE THE
*         MULTI-TASKING TECHNIQUE WHICH ALLOWS THE EDITOR CODE TO BE
*         SHARED BY MULTIPLE USERS.  THE EDITOR CODE RESIDES IN 
*         SEVERAL SOURCE MODULES OUTSIDE OF THIS PROGRAM.  SMFEX AND
*         THE EDITING MODULES ARE COMBINED BY THE LOADER.  EDITING
*         MODULES REQUIRE SEPARATE COMPILATION WITH CERTAIN 
*         MODIFY-TIME DEFINITIONS CHANGED TO YIELD TWO SETS OF
*         RELOCATABLE MODULES, ONE SUITABLE FOR LOADER PROCESSING TO
*         YIELD FSE AND THE OTHER SUITABLE FOR LOADER PROCESSING TO 
*         YIELD SMFEX.
* 
# 
  
  DEF LISTCON #1#;
  
CONTROL PRESET;         # UNIVERSAL DEFINITIONS     # 
  
  
*CALL COMFMLT 
*CALL COMFFSE 
  
                        # SMFEX'S OWN DEFS          # 
  
*CALL COMFSMF 
  
#   SIC CODES.  IAFEX TALKS TO OUR BUFFER 0, SINGLE-USER FSE TALKS #
#   TO OUR BUFFER 1 WITH A DIFFERENT SET OF CODES.   #
  
STATUS IAF2SMF
  INIT, 
  MSGIN,
  OUTDONE,
  BREAK,
  HUNGUP, 
  CONNECT;
  
STATUS SMF2IAF
  INIT, 
  MSGOUT, 
  PROMPT, 
  LOGOUT; 
  
STATUS TASKST 
  EMPTY,
  DELAY,
  RECALLFET,
  START,
  SWAP, 
  RUN,
  PRELIM; 
  
#   TYPES OF STATISTICS COLLECTED   # 
STATUS INSTST 
  THNK, 
  RSPNS,
  NOTTI,
  NOTASK, 
  SWPE, 
  SWPD, 
  CLOCKTICK,
  PREEMPT,
  SWPTIM, 
  EDTCIO, 
  SESSION,
  TRANS,
  XSTTO,
  BREAKS, 
  HANGUPS,
  BGNEDT, 
  ENDEDT, 
  INPUT,
  XMIT, 
  KWIKTOC,
  TASKUTIL, 
  WORKIO, 
  DROPCPU;
  
DEF MOD4(ZZ) #((ZZ) LAN 3)#;
DEF MOD8(ZZ) #((ZZ) LAN 7)#;
DEF MOD32(ZZ) #((ZZ) LAN 31)#;
PAGE                         # EXTERNAL REF'S AND DEF'S    #
  
XDEF
  BEGIN 
  PROC SMFRCL;              PROC SMFDLY;
  PROC CLEARINT;             PROC VOLUNTEER;
  PROC TTITRAP;              PROC TTOTRAP;
  PROC FATALTRAP;            PROC CLEARQUE; 
  CONTROL IFEQ METERING,1;
    PROC BGNMETER;
    PROC WIOSTAT; 
  CONTROL FI; 
  ARRAY RENTSTK [1:MAXREENT];          # SUBROUTINE STACK  #
   BEGIN
    ITEM RSTK;
   END
  ITEM RSTKPTR; 
  END 
  
XREF PROC MOVEWD; 
  
XREF
  BEGIN 
  # FIRST COME COMPASS ROUTINES, FOR SPECIAL/OPTIMAL TRICKS # 
  FUNC FINDBIT;              FUNC NOSWDSZ;
  PROC SPREAD;               PROC GATHER; 
  PROC FLDLEN;               PROC FLDLENE;
  PROC SFMSSTF;  PROC SFMCSTF;  PROC TESTECS; 
  PROC READECS;              PROC WRITECS;
  PROC GETUTAB;              PROC SAVUTAB;
  # NOW COME THE STANDARD UOI SYMPLIB ROUTINES WE NEED #
                 PROC WRITEO;    PROC READO;
  PROC RECALL;               PROC SYSREQ; 
  PROC READ;                 PROC READC;
  PROC REWIND;               PROC STATUSP;
  PROC WRITER;               PROC REWRITR;
  PROC WRITEC;              PROC WRITEW;
  PROC MESSAGE;              PROC DISTCON;
  PROC RETERN;              PROC GETFLCE; 
  PROC ONSW;                 PROC RTIME;
  PROC SETRFLE;              PROC PROTEON;
  PROC EVICT; 
  PROC FILINFO;  PROC REQUEST;  PROC EESET; 
  PROC ENDRUN;              PROC ABORT; 
  # NOW COME ENTRIES TO FSE EDITOR #
  FUNC MIN;                  FUNC MAX;
  PROC PROCESS; PROC EDTINIT; PROC MAKEFET; PROC EDTTERM; 
  PROC PAUSEIO;  PROC RESUMIO;  PROC CHECKIO; 
  PROC TTSYNC;               PROC FATAL;
  PROC VDSYNCH; 
  END 
PAGE                         # COMMON DATA AREAS #
*CALL COMFDS1 
*CALL COMFVD2 
*CALL COMFDS2 
PAGE                         # EDITOR TABLES     #
                   # COMPILED HERE ONLY FOR CONTROL PRESET #
  
*CALL COMFTAB 
PAGE                         # SMFEX'S OWN DATA            #
  
  
ITEM TIMEOFDAY=0; 
ITEM SLICECOUNT=0;
ITEM OLDTIMEDAY=0;
ITEM NEWTIMEDAY=0;
ITEM IDLEDOWN B=FALSE;
ITEM STOPNOW B=FALSE; 
ITEM ACCEPTNONE B=FALSE;
ITEM ECSDOWN B=FALSE; 
ITEM SHUTDOWN=0;             # CONTROLS OPERATOR SHUTDON   #
ITEM COMPLETE B=FALSE;       # USED TO SHUT WHOLE THING DOWN         #
ITEM CURUSER;                # UTILITY VARIABLE  #
ITEM CURTASK;                # CURRENT TASK REFERENCE      #
ITEM LASTTASK=0;             # PREVIOUS TASK TO RUN        #
ITEM TASKSAVAIL=3;           # TASKS CURRENTLY AVAILABLE/IN USE      #
ITEM TASKSBUSY=0;            # NUMBER OF TASKS IN USE NOW  #
ITEM CHNGSTATUS;             # CURRENT TASKS CHANGE OF STATUS        #
ITEM REMAINTTI=NUMTTIBUF;    # HOW MANY BUFFERS AVAIL      #
ITEM DISPATLEN;              # LENGTH OF DISPATCHED DATA SEGMENT     #
ITEM SWAPLEN;                # LENGTH OF SWAPPED SEGMENT   #
ITEM SWAPPRUS;               # LENGTH SWAPPED IN PRU COUNT #
ITEM SWAPBUFLEN;             # LENGTH SWAP CIRCULAR BUFFS  #
ITEM MAXSWPPAG=0;            # HIGHEST (DISK ONLY) SWAP PAGE USED    #
ITEM MODELPTR;               # POINTS TO COPY OF VIRGIN DATA         #
ITEM MODELLEN;               # LENGTH OF VIRGIN COMMON COPY          #
ITEM FIELDLEN;
ITEM LOGGEDIN=0;
ITEM WRKFILLOCK B=FALSE;
ITEM ENABLEFLAG=0;
CONTROL IFEQ METERING,1;
  ITEM MAXLOGGED=0; 
  ITEM STATTIME=0;
CONTROL FI; 
CONTROL IFEQ ECSCODE,1; 
  ITEM ECSFIELD=0;
  ITEM ECSERRORS=0; 
CONTROL FI; 
ITEM STATUSMSG C(30)="0000 USERS 00 TASKS"; 
BASED ARRAY ANYFET;;
BASED ARRAY MOVFROM;; 
BASED ARRAY MOVTO;; 
  
ARRAY TTICONTROL [1:NUMTTIBUF]; 
  BEGIN 
  ITEM TTICTLWORD  = [NUMTTIBUF(0)];
  ITEM TTIBUFLEN   U(0,0,12); 
  ITEM TTIBUFLNK   U(0,12,12);
  ITEM TTIBUFADDR  U(0,42,18);
  END 
  
ARRAY SWAPBITS (NUMSWPBIT);;
ARRAY TTIBITS (NUMTTIBIT);; 
ARRAY SWAPGOOD (NUMSWPBIT);;
ARRAY SWAPBAD (NUMSWPBIT);; 
ARRAY TTIGOOD (NUMTTIBIT);; 
BASED ARRAY SWAPMASK;;
  
  
# NOTE THAT CURUSERTAB MUST MATCH USERTAB, WORD BY FIELD.  #
# NOTE ALSO THAT THE FORMATS OF THESE ARRAYS ARE HARDCODED #
# IN THE COMPASS SUBROUTINES GETUTAB AND SAVUTAB.          #
  
ARRAY CURUSERTAB (22);
  BEGIN 
  ITEM CURUSLOGGED B(0)=[FALSE];
  ITEM CURUSINQUE  B(1)=[FALSE];
  ITEM CURUSDROP  B(02)=[FALSE];     # CURRENT USER DROP FLAG # 
  ITEM CURUSDONE   B(3)=[FALSE];
  ITEM CURUSINTASK B(4)=[FALSE];
  ITEM CURUSSTART  B(5)=[FALSE];
  ITEM CURUSINTRPT B(6)=[FALSE];
  ITEM CURUSCRUNCH B(7)=[FALSE];
  ITEM CURUSLOSTDT B(8)=[FALSE];
  ITEM CURUSRCVRY  B(9)=[FALSE];
  ITEM CURUSOLDSND B(10)=[FALSE]; 
  ITEM CURUSVICTIM B(11)=[FALSE]; 
  ITEM CURUSSTMWRK B(12)=[FALSE]; 
  ITEM CURUSOKOUT  B(13)=[FALSE]; 
  ITEM CURUSSTKPTR (14)=[0];
  ITEM CURUSTASK   (15)=[0];
  ITEM CURUSTTIBUF (16)=[0];
  ITEM CURUSQUELNK (17)=[0];
  ITEM CURUSIAFNUM (18)=[0];
  ITEM CURUSFNTPTR (19)=[0];
  ITEM CURUSSWPPAG (20)=[0];
  ITEM CURUSLASTIM (21)=[0];
  END 
  
  
# NOTE COMMENTS ABOVE ON RESTRICTIONS AGAINST REDEFINITION  # 
# OF EITHER CURUSERTAB OR USERTAB.  # 
  
ARRAY USERTAB [1:NUMSMFUSR] S(3); 
  BEGIN 
  ITEM USERWORD0 (0)=[NUMSMFUSR(0)];
  ITEM USERWORD1 (1)=[NUMSMFUSR(0)];
  ITEM USERWORD2 (2)=[NUMSMFUSR(0)];
  ITEM USERFLAGS  U(0,0,18);
  ITEM USERLOGGED B(0,0,1);  # IS LOGGED FULLY IN          #
  ITEM USERINQUE  B(0,1,1);  # ALREADY IN SWAPIN QUEUE     #
  ITEM USERDROP   B(00,02,01);       # USER DROPPED PHONE CONNECT # 
  ITEM USERDONE   B(0,3,1);  # EDIT HAS COMPLETED          #
  ITEM USERINTASK B(0,4,1);  # ASSIGNED TO SOME TASK       #
  ITEM USERSTART  B(0,5,1);  # ONLY FOR RIGHT AFTER LOGGED #
  ITEM USERINTRPT B(0,6,1);  # WE MUST SOON SERVICE BREAK KEY        #
  ITEM USERCRUNCH B(0,7,1);  # USER IS FILE-CRUNCHING,LOW PRIO       #
  ITEM USERLOSTDT B(0,8,1);  # USER LOST EXCESS INPUT DATA #
  ITEM USERRCVRY  B(0,9,1);  # SWAPPING IN ONLY TO CHKPT   #
  ITEM USEROLDSND B(0,10,1); # MIDDLE OF SERIES OF SENDS   #
  ITEM USERVICTIM B(0,11,1); # VICTIMIZED BY SMFEX ECS ERROR         #
  ITEM USERSTMWRK B(0,12,1); # USE STIMULATION WRKFIL NAME #
  ITEM USEROKOUT  B(0,13,1); # O.K. TO PRODUCE TTY OUTPUT  #
  ITEM USERSTKPTR U(0,18,6); # REENTRANCY STACK LEVEL      #
  ITEM USERTASK   U(0,24,6); # WHICH TASK ARE WE IN        #
  ITEM USERTTIBUF U(0,30,6); # WHICH INPUT BUFFER WE OWN   #
  ITEM USERQUELNK U(0,36,12); #          LINKAGE IN SWAP QUEUE       #
  ITEM USERIAFNUM U(0,48,12); #          WHICH IAFEX PORT  #
  ITEM USERFNTPTR U(1,0,12);  #          TO SPEED UP CIO'S #
  ITEM USERSWPPAG U(1,12,12); #          WHICH SWAP PAGE   #
  ITEM USERLASTIM U(1,24,30); # LAST TIME STAMP            #
  ITEM USERJSN    C(2,0,4);   # JOB SEQUENCE NUMBER        #
  ITEM USEREJT    U(2,24,12); # JOB EJT ORDINAL            #
  END 
  
ARRAY TASKTAB [1:NUMTASKS] P(13); 
  BEGIN 
  ITEM TASKSTATUS (0)=[0];   # CURRENT TASK STATUS         #
  ITEM TASKFLAGS  (1)=[0];   # ALL BITS OFF      #
  ITEM TASKREQUE  B(1,0,1);  # USER NEEDS REPEATED SERVICE #
  ITEM TASKABORT  B(1,2,1);  # TASK IS ABORTING            #
  ITEM TASKSWPNOW B(1,3,1);  # CURRENTLY EXECING SWAP CODE #
  ITEM TASKSWPIN  B(1,4,1);  # DIRECTION OF SWAP IS IN     #
  ITEM TASKUSER   (2)=[0];             # WHICH USER IN THIS TASK     #
  ITEM TASKNXTTTO (3)=[0];             # WHERE TO APPEND NEXT OUTPUT #
  ITEM TASKTYPTTO (4)=[0];             # TRANSMIT CODE DESIRED       #
  ITEM TASKRCLADR (5)=[0];             # CURRENT RECALL FET          #
  ITEM TASKADDR   (6)=[0];             # ADDRESS OF TASK MEMORY      #
  ITEM TASKSTKPTR (7)=[0];             # DISPATCHING RSTKPTR         #
  ITEM TASKDSPADR (8)=[0];             # DISPATCH DATA START         #
  ITEM TASKTIME   (9)=[0];   # TIMEOFDAY SWAPPED IN        #
  ITEM TASKSWPFIL (10)=[0];  # WHICH SWAP FILE IN USE      #
  ITEM TASKSWPPRU (11)=[0];  # WHICH PRU IN THAT FILE      #
  ITEM TASKPULSE  (12)=[0];  # FAKE FET FOR CLOCK EVENTS   #
  END 
  
ARRAY SWAPQ (3);
  BEGIN 
  ITEM SWPQHEAD I(0)=[0]; 
  ITEM SWPQTAIL I(1)=[0]; 
  ITEM SWPQSIZE I(2)=[0]; 
  END 
  
ARRAY SSCINPUT [-1:O"100"]; ITEM SSCINP (0)=[O"102"(0)];
  
ARRAY IAFMSGWORD; 
  BEGIN 
  ITEM IAFHEADER;            # WHOLE WORD        #
  ITEM IAFFUNC  U(0,0,12)=[SMF2IAF"INIT"];
  ITEM IAFTERM  U(0,12,12)=[0]; 
  ITEM IAFPTR   U(0,24,18)=[0]; 
  ITEM IAFLEN   U(0,42,18)=[1]; 
  END 
ARRAY INITSSC [0:1]; ITEM INITSSCWORD=[0,0];
BASED ARRAY SSCBUFFER;
  BEGIN 
  ITEM SSCWORD; 
  ITEM SSCCOUNT   U(0,36,6);
  ITEM SSCTYPE    U(0,46,2);
  ITEM SSCSTATUS  U(0,48,12); 
  END 
  
ARRAY NULLFET(FETSIZ); ITEM NULLFETDT C(1,0,2); 
  
ARRAY SWPFETS [1:NUMSWPFET] S(FETSIZ);
  BEGIN 
  ITEM SWPFET;
  ITEM SWPFETDONE B(0,59,1);
  ITEM SWPFETDT  C(1,0,2);
  ITEM SWPFETR   B(1,12,1); 
  ITEM SWPFETL   U(1,36,6); 
  ITEM SWPFETIN  U(2,42,18);
  ITEM SWPFETOUT U(3,42,18);
  ITEM SWPFETCRI U(6,0,30); 
  ITEM SWPFETW   B(6,30,1); 
  ITEM SWPFETRR  U(6,31,29);
  END 
ARRAY DMBFET(FETSIZ);;
ARRAY FILINFPARMS (5);
  BEGIN 
  ITEM FILINFNAME C(0,0,7); 
  ITEM FILINFLENC U(0,42,18)=[O"050001"]; 
  ITEM FILINFSTAT U(1,12,42); 
  ITEM FILINFTTY  B(1,43,1);
  ITEM FILINFRMS  B(1,44,1);
  ITEM FILINFWRIT B(1,52,1);
  ITEM FILINFREAD B(1,53,1);
  ITEM FILINFFT   U(1,54,6);
  END 
PAGE                         # MINOR UTILITY ROUTINES      #
  
  
PROC INITBITMAP(MAP,NUMWORD,NUMBIT);
  BEGIN 
# 
**        INITBITMAP - INITIALIZE A BIT MAP ARRAY.
* 
*         INITBITMAP PREPARES BITMAPS FOR USAGE.  A BIT MAP CONSISTS
*         OF ANY NUMBER OF WORDS, EACH CONTAINING A 32-BIT MASK.
*         THE MASK IS POSITIONED 12 BITS RIGHT FROM THE SIGN BIT OF 
*         THE WORD.  THIS FORMAT ALLOWS SEARCHING FOR BITS WHICH ARE
*         STILL ON BY INSERTING A FLOATING POINT EXPONENT AND 
*         EXECUTING THE NORMALIZE INSTRUCTION.
* 
*         ENTRY  MAP - THE MAP TO INITIALIZE. 
*                NUMWORD - HOW MANY WORDS IN THE MAP. 
*                NUMBIT - HOW MANY TOTAL BITS IN THE MAP. 
* 
*         EXIT   MAP - SETUP. 
* 
*         CALLS  FORCEALLOC.
# 
  ARRAY MAP[0:999]; ITEM MAPWORD; 
  ITEM NUMWORD, NUMBIT; 
  ITEM TMP1;
  FOR TMP1=0 STEP 1 UNTIL NUMWORD-1 DO MAPWORD[TMP1]
    =O"00007777777777600000"; 
  FOR TMP1=NUMBIT+1 STEP 1 UNTIL NUMWORD*32 
    DO FORCEALLOC(MAP,TMP1,NUMWORD);
  END                          # OF INITBITMAP     #
  
  
PROC BUILDTASK(WHICH);
  BEGIN 
# 
**        BUILDTASK - ALLOCATE FIELD LENGTH AND INIT POINTERS.
* 
*         BUILDTASK ALLOCATES FIELD LENGTH FOR PER-TASK ARRAYS, AND 
*         MAPS OUT THE BASE ADDRESSES FOR THE ARRAYS.  BUILDTASK THEN 
*         COPIES THE SET OF POINTERS INTO THE TASK'S DISPATCHING AREA.
* 
*         ENTRY  WHICH - TASK NUMBER. 
*                FIELDLEN - CURRENT TOP OF FIELD LENGTH.
* 
*         EXIT   FIELDLEN - INCREASED.
*                TASKSPADR[WHICH] - ADDRESS OF TASK DISPATCH AREA.
*                TASK DISPATCH AREA - SETUP.
* 
*         CALLS  FLDLEN, MOVEWD.
* 
*         USES   POINTER WORDS FOR FOLLOWING ARRAYS - 
*                ARRAYEND, ARRAYSTART, AUDITSTAGE, BACKSTACK, 
*                BACKSTORE, BFPRU, CHGSTRING1, DISK, DISPRSTK,
*                FET, FKEYDEFS, LINEBUF, LOCSTRING1, LOCSTRING2,
*                MOVTO, OBF, READLIST, ROWCONTROL, SPLITCONTROL,
*                SWAPRSTK, TITLE1LIN, TITLE2LIN, TTOBUFFER. 
* 
*         NOTE   THIS CODE AND THE ARRAY LAYOUT IN COMFDAT ARE
*                EXTREMELY INTERDEPENDENT.
# 
  ITEM WHICH; 
  TASKADDR[WHICH]=FIELDLEN; 
  
  # FIRST ALLOCATE EDITOR ARRAYS, ORDER AND SIZE MATCHING COMFDAT # 
  P<ARRAYSTART>=FIELDLEN; 
  FIELDLEN=FIELDLEN+1;
  P<TABLEHEADR> = FIELDLEN; 
  FIELDLEN = FIELDLEN + 6;           # *TDU* TABLE LENGTH # 
  P<TABLEWORDS> = FIELDLEN; 
  FIELDLEN = FIELDLEN + O"272";      # *TDU* TABLE LENGTH # 
  P<SPLITCONTROL>=FIELDLEN; 
  FIELDLEN=FIELDLEN+2*SPLTCTLSIZ; 
  P<ROWCONTROL>=FIELDLEN; 
  FIELDLEN=FIELDLEN+2*(MAXROWS+1);
  P<BACKSTACK>=FIELDLEN;
  FIELDLEN=FIELDLEN+(BACKMAX+1);
  P<BACKSTORE>=FIELDLEN;
  FIELDLEN=FIELDLEN+(2*(TEMPIND+1));
  P<FKEYDEFS>=FIELDLEN; 
  FIELDLEN=FIELDLEN+NUMFKEYS; 
  P<AUDITSTAGE>=FIELDLEN; 
  FIELDLEN=FIELDLEN+AUDITSIZE+1;
  P<LOCSTRING1>=FIELDLEN; 
  FIELDLEN=FIELDLEN+STRWID; 
  P<LOCSTRING2>=FIELDLEN; 
  FIELDLEN=FIELDLEN+STRWID; 
  P<CHGSTRING1>=FIELDLEN; 
  FIELDLEN=FIELDLEN+STRWID; 
  P<FKEYSTRINGS>=FIELDLEN;
  FIELDLEN=FIELDLEN+(2*NUMFKEYS); 
  P<FKEYNUMB>=FIELDLEN; 
  FIELDLEN=FIELDLEN+POSFKEYS; 
  P<TITLE1LIN>=FIELDLEN;
  FIELDLEN=FIELDLEN+TTLLNLEN+1; 
  P<TITLE2LIN>=FIELDLEN;
  FIELDLEN=FIELDLEN+TTLLNLEN+1; 
  P<ARRAYEND>=FIELDLEN; 
  FIELDLEN=FIELDLEN+1;
  # END OF ALLOCATED EDITOR ARRAYS #
  
  # ALLOCATE BUFFERS AND CONTROL VECTORS #
  P<BFPRU>=FIELDLEN;
  FIELDLEN=FIELDLEN+BUFSIZE;
  P<SWAPRSTK>=FIELDLEN; 
  FIELDLEN=FIELDLEN+MAXREENT; 
  TASKDSPADR[WHICH]=FIELDLEN; 
  FIELDLEN=FIELDLEN+DISPATLEN;
  P<DISPRSTK>=FIELDLEN; 
  FIELDLEN=FIELDLEN+MAXREENT; 
  P<TTOBUFFER>=FIELDLEN;
  FIELDLEN=FIELDLEN+SIZTTOBUF+2;
  P<FET>=FIELDLEN;
  FIELDLEN=FIELDLEN+FETSIZ; 
  P<OBF>=FIELDLEN;
  FIELDLEN=FIELDLEN+OBFSIZE;
  P<DISK>=FIELDLEN; 
  FIELDLEN=FIELDLEN+DSKSIZ; 
  P<READLST>=FIELDLEN;
  FIELDLEN=FIELDLEN+LSTSIZE;
  FLDLEN(FIELDLEN+4); 
  P<LINEBUF>=LOC(LIN);
  P<MOVTO>=TASKDSPADR[WHICH]; 
  MOVEWD(DISPATLEN,DATASTART,MOVTO);
  END                          # OF BUILDTASK      #
  
  
PROC GENWRKNAM; 
  BEGIN 
# 
**        GENWRKNAM - GENERATE WORKFILE NAME FOR THIS USER. 
* 
*         GENWRKNAM DETERMINES THE CORRECT FILENAME FOR THE WORK- 
*         FILE FOR THE CURRENT USER.  THE FILE NAME IS UNIQUE FOR 
*         EACH USER AND IS GENERATED BY THE USER'S TLX CALL TO
*         CONNECT TO SMFEX, AND IS EQUAL TO "WK" PLUS JSN.
* 
*         ENTRY  CURUSIAFNUM - IAF TERMINAL NUMBER. 
*                USERJSN[CURUSER] - JSN OF THE USER'S JOB.
* 
*         EXIT   WORKNAM - CORRECT NAME.
# 
  C<0,2>WORKNAM="WK"; 
  C<2,4>WORKNAM=USERJSN[CURUSER]; 
  C<6,1>WORKNAM=0;
  END                         # OF GENWRKNAM      # 
  
  
CONTROL IFEQ METERING,1;
  PROC ADDVECT(VECTOR,DATA);
    BEGIN 
# 
**        ADDVECT - ADD DATA INTO A STATISTICS VECTOR.
* 
*         ADDVECT IS PART OF THE INSTRUMENTATION FEATURE OF SMFEX 
*         AND SERVES TO INCREMENT THE TOTALS AND ONE VALUE-DEPENDENT
*         COUNTER IN A SELECTED VECTOR.  THE VECTOR HAS ONE WORD FOR
*         TOTAL VALUE AND TOTAL EVENT COUNT, THEN FOUR WORDS EACH 
*         CONTAINING FOUR 15-BIT COUNTERS.  THE COUNTER TO BE 
*         INCREMENTED IS SELECTED BY THE DATA VALUE, WITH THE FIRST 
*         COUNTER FOR VALUES OF ZERO, THE SECOND FOR VALUES OF ONE, 
*         AND THE THIRD THRU SIXTEENTH COUNTER CORRESPONDING TO 
*         VALUES IN THE RANGE (2**(N-2)) .LE. VALUE .LT. (2**(N-1)).
* 
*         ENTRY  DATA - VALUE TO RECORD.
* 
*         EXIT   VECTOR - UPDATED.
# 
    ITEM TMP1, TMP2, TMP3 R;
    ARRAY VECTOR[0:4]; ITEM WORD; 
    ITEM DATA;
    B<0,30>WORD[0]=B<0,30>WORD[0]+DATA; 
    B<30,30>WORD[0]=B<30,30>WORD[0]+1;
    IF DATA LQ 0 THEN TMP1=0; 
    ELSE
      BEGIN 
      TMP3=DATA*1.0;
      TMP1=B<6,6>TMP3-15; 
      END 
    TMP1=MAX(TMP1,0); 
    TMP1=MIN(TMP1,15);
    TMP2=MOD4(TMP1)*15; 
    TMP1=(TMP1/4)+1;
    IF B<TMP2,15>WORD[TMP1] NQ O"77777" 
      THEN B<TMP2,15>WORD[TMP1]=B<TMP2,15>WORD[TMP1]+1; 
   END                        # OF ADDVECT        # 
CONTROL FI; 
PAGE                         # TRACE ROUTINES FOR DEBUG    #
  
  
DEF TCTL # CONTROL IFEQ TRACEFLAG,1; #; 
DEF TRCOUT(AA) # TCTL TRACEOUT(AA); CONTROL FI #; 
DEF TRCSTR(AA) # TCTL TRACESTR(AA); CONTROL FI #; 
DEF TRCDEC(AA) # TCTL TRACEDEC(AA); CONTROL FI #; 
DEF TRCFRC     # TCTL TRACEFRC;     CONTROL FI #; 
DEF TRCWORDS(AA,BB) # TCTL TRACEWORDS(AA,BB); CONTROL FI #; 
DEF TRCBOTH(AA,BB)  # TCTL TRACEBOTH(AA,BB);  CONTROL FI #; 
  
  
CONTROL IFEQ TRACEFLAG,1; 
  
  PROC TRACEOUT(TEXT);
    BEGIN 
    ARRAY TEXT[0:99]; ITEM TXTWORD; 
    ITEM INITED B=FALSE;
    ARRAY TRCFET (FETSIZ);; 
    ARRAY TRCLIN[0:20]; ITEM TRCLINE; 
    ITEM TRCPTR,TXTPTR; 
    ARRAY TRCBUFF [0:O"2000"];; 
  
    IF NOT INITED THEN
      BEGIN 
      INITED=TRUE;
      TRCPTR=0; 
      MAKEFET(TRCFET,"TRACE",TRCBUFF,O"2001");
      END 
    TXTPTR=0; 
    TRCLINE[TRCPTR]=TXTWORD[TXTPTR];
    WHYLE B<48,12>TXTWORD[TXTPTR] NQ 0 DO 
      BEGIN 
      TXTPTR=TXTPTR+1;
      TRCPTR=TRCPTR+1;
      TRCLINE[TRCPTR]=TXTWORD[TXTPTR];
      END 
    FOR TXTPTR=9 STEP -1 WHILE TXTPTR GQ 0
      AND C<TXTPTR,1>TRCLINE[TRCPTR] EQ ":" 
      DO C<TXTPTR,1>TRCLINE[TRCPTR]=" ";
    TRCPTR=TRCPTR+1;
    TRCLINE[TRCPTR]=0;
    IF TRCPTR GQ 07 THEN
      BEGIN 
      WRITEC(TRCFET,TRCLIN);
      TRCPTR=0; 
      END 
    RETURN; 
  
    ENTRY PROC TRACEWORDS(TEXT,NWORDS); 
  
    ITEM NWORDS;
  
    IF NOT INITED THEN
      BEGIN 
      INITED=TRUE;
      TRCPTR=0; 
      MAKEFET(TRCFET,"TRACE",TRCBUFF,O"2001");
      END 
    IF TRCPTR NQ 0 THEN 
      BEGIN 
      WRITEC(TRCFET,TRCLIN);
      TRCPTR=0; 
      END 
    WRITEW(TRCFET,TEXT,NWORDS); 
    IF B<48,12>TXTWORD[NWORDS-1] NQ 0 THEN WRITEW(TRCFET,0,1);
    RETURN; 
  
    ENTRY PROC TRACEFRC;
  
    IF TRCPTR NQ 0 THEN 
      BEGIN 
      WRITEC(TRCFET,TRCLIN);
      TRCPTR=0; 
      END 
    WRITER(TRCFET,1); 
  
    END                        # OF TRCOUT         #
  
  
  PROC TRACESTR(STR); 
    BEGIN 
  
    ITEM STR C(40); 
  
    ARRAY TEXT [0:4]; ITEM TXTWORD; 
    ITEM TMP1;
  
    TXTWORD[0]=0; 
    FOR TMP1=0 STEP 1 WHILE TMP1 LQ 39 AND C<TMP1,1>STR NQ "$" DO 
      BEGIN 
      C<MOD(TMP1,10),1>TXTWORD[TMP1/10]=C<TMP1,1>STR; 
      TXTWORD[1+TMP1/10]=0; 
      END 
    TRCOUT(TEXT); 
  
    END                        # OF TRCSTR         #
  
  
  PROC TRACEDEC(NUM); 
    BEGIN 
    ITEM NUM; 
    ITEM TMP1,TMP2,TMP3;
  
    TMP1=NUM; 
    TMP2=O"55555555555555330000"; 
    TMP3=7; 
    IF TMP1 LS 0 THEN 
      BEGIN 
      C<0,1>TMP2="-"; 
      TMP1=-TMP1; 
      END 
    WHYLE TMP1 NQ 0 DO
      BEGIN 
      C<TMP3,1>TMP2=O"33"+MOD(TMP1,10); 
      TMP3=TMP3-1;
      TMP1=TMP1/10; 
      END 
    TRCOUT(TMP2); 
  
    END                        # OF TRCDEC         #
  
  
  PROC TRACEBOTH(AA,BB);
    BEGIN 
    ARRAY AA;;
    ARRAY BB;;
    TRCSTR(AA); 
    TRCDEC(BB); 
    END                        # OF TRCBOTH        #
  
CONTROL FI; 
PAGE                         # UTIITIES FOR QUEUES, CHAINS, TABLES   #
  
  
PROC GETUSER; 
  BEGIN 
# 
**        GETUSER - MAKE TABLE ENTRIES ACCESSIBLE FOR CURRENT USER. 
* 
*         GETUSER UNPACKS THE DENSELY FORMATTED, INDEXABLE USER 
*         TABLE INTO THE SPARSE, NONINDEXABLE USER TABLE.  THE DENSE
*         TABLE PROVIDES CONSERVATION OF MEMORY CAPACITY, WHILE 
*         THE SPARSE TABLE PROVIDES CONSERVATION OF CODE SIZE AND 
*         TIMING FOR FREQUENT REFERENCES. 
* 
*         ENTRY  CURUSER - INDEX FOR WHICH USER.
*                USERTAB[CURUSER] - "DENSE PACK" FORMATTED TABLE. 
* 
*         EXIT   CURUSERTAB - EXPANDED COPY OF USERTAB. 
* 
*         CALLS  GETUTAB. 
# 
  BASED ARRAY PARM;;
  P<PARM>=LOC(USERTAB[CURUSER]);
  GETUTAB(PARM,CURUSERTAB); 
  END                         # OF GETUSER        # 
  
  
PROC PUTUSER; 
  BEGIN 
# 
**        PUTUSER - PRESERVE TABLE ENTRIES IN DENSE TABLE.
* 
*         PUTUSER IS CALLED TO COPY AND PACK THE SPARSELY FORMATTED,
*         NON-INDEXABLE USER TABLE INTO THE DENSELY FORMATTED,
*         INDEXABLE TABLE.  PUTUSER IS THUS THE OPPOSITE OF 
*         GETUSER.  NOTE THAT THE SPARSE TABLE IS TEMPORARY AND 
*         THE DENSE TABLE IS PERMANENT. 
* 
*         ENTRY  CURUSER - CURRENT USER.
*                CURUSERTAB - SPARSE TABLE. 
* 
*         EXIT   USERTAB[CURUSER] - PACKED VERSION OF TABLE.
* 
*         CALLS  SAVUTAB. 
# 
  BASED ARRAY PARM;;
  P<PARM>=LOC(USERTAB[CURUSER]);
  SAVUTAB(PARM,CURUSERTAB); 
  END                         # OF PUTUSER        # 
  
  
PROC GETCOMMON; 
  BEGIN 
# 
**        GETCOMMON - GET STORAGE FOR RIGHT TASK INTO COMMON. 
* 
*         TO CONSERVE CPU TIME REQUIRED FOR SHUFFLING OF THE COMMON 
*         BLOCK IN MULTI-TASKING, THE COMMON BLOCK IS LEFT VOLATILE 
*         WHEN ONE TASK GIVES UP THE CPU IN THE HOPE THAT THE SAME
*         TASK WILL BE THE NEXT TO USE THE CPU.  WHEN A DIFFERENT 
*         TASK IS NEXT ON THE CPU, THEN WE MUST PERFORM THE DEFERRED
*         COPYING OF COMMON TO/FROM TASK DISPATCHING AREAS. 
* 
*         ENTRY  LASTTASK - TASK WHICH MOST RECENTLY USED COMMON. 
*                    ZERO INDICATES COMMON IS NOT VOLATILE. 
*                CURTASK - TASK WHICH NEEDS COMMON. 
*                TASKSWPNOW[LASTTASK], TASKSWPIN[LASTTASK] -
*                    INDICATE IF VOLATILITY SUPPRESSED BY 
*                    CURRENTLY ACTIVE SWAPIN. 
* 
*         EXIT   LASTTASK - EQUALS CURTASK. 
*                COMMON BLOCK - SAVED IN LASTTASK IF NEEDED,
*                    UPDATED FOR CURTASK IF NEEDED. 
* 
*         CALLS  MOVEWD.
* 
*         USES   P<MOVFROM>, P<MOVTO>.
# 
  IF LASTTASK NQ 0 AND LASTTASK NQ CURTASK AND
    NOT (TASKSWPNOW[LASTTASK] AND TASKSWPIN[LASTTASK]) THEN 
    BEGIN 
    P<MOVTO>=TASKDSPADR[LASTTASK];
    MOVEWD(DISPATLEN,DATASTART,MOVTO);
    END 
  IF LASTTASK NQ CURTASK THEN 
    BEGIN 
    P<MOVFROM>=TASKDSPADR[CURTASK]; 
    MOVEWD(DISPATLEN,MOVFROM,DATASTART);
    END 
  LASTTASK=CURTASK; 
  END                         # OF GETCOMMON      # 
  
  
PROC PUTCOMMON; 
  BEGIN 
# 
**        PUTCOMMON - COPY COMMON INTO DISPATCHING AREA.
* 
*         PUTCOMMON IS CALLED WHEN THE LIVE COMMON BLOCK IS CONSIDERED
*         MORE CURRENT THAN THE COMMON IMAGE IN THE TASK DISPATCHING
*         AREA, AND REQUIRES IMMEDIATE COPYING. 
* 
*         ENTRY  CURTASK - CURRENT TASK.
*                TASKDSPADR[CURTASK] - ADDRESS OF DISPATCH AREA.
* 
*         EXIT   DISPATCH AREA - UPDATED FROM DATASTART.
* 
*         CALLS  MOVEWD.
* 
*         USES   P<MOVTO>.
# 
  # COPY DISPATCHABLE STORAGE FOR THIS TASK OUT OF COMMON # 
  P<MOVTO>=TASKDSPADR[CURTASK]; 
  MOVEWD(DISPATLEN,DATASTART,MOVTO);
  END                         # OF PUTCOMMON      # 
  
  
PROC KILL;
  BEGIN 
# 
**        KILL - ERASE USE FROM TABLES. 
* 
*         KILL IS CALLED WHEN DISCONNECTING A USER.  IT IS ASSUMED
*         THE USER IS NOT IN THE SWAPIN QUEUE.  THE USER MAY
*         HAVE TERMINAL INPUT BUFFERS, WHICH ARE RELEASED.  THEN
*         THE TABLE ENTRIES ARE ZEROED. 
* 
*         ENTRY  CURUSER - WHO TO KILL. 
*                CURUSIAFNUM - IAF TERMINAL NUMBER. 
* 
*         EXIT   USERTAB[CURUSER], ORDWORD[CURUSIAFNUM] - ZERO. 
*                CURUSERTAB - ZERO ALSO.
* 
*         CALLS  PURGETTI, PUTUSER, GETUSER.
# 
  PURGETTI; 
  USERWORD0[CURUSER]=0; 
  USERWORD1[CURUSER]=0; 
  USERWORD2[CURUSER]=0; 
  GETUSER;
  END                          # OF KILL #
  
  
PROC SCHEDULE;
  BEGIN 
# 
**        SCHEDULE - ASSURE USER WILL RUN IN A TASK SOON. 
* 
*         SCHEDULE IS USED WHENEVER WE RECEIVE A CODE FROM IAFEX, 
*         ASSURES THE USER WILL BE SCHEDULED FOR TASK SERVICE OR WILL 
*         BE ADEQUATELY SERVICED IF ALREADY IN A TASK.
* 
*         ENTRY  CURUSER - USER TO BE QUEUED. 
*                CURUSERTAB - ALL ENTRIES SETUP.
*                CURUSINTASK - WHETHER ALREADY SCHEDULED. 
*                CURUSINQUE - WHETHER ALREADY QUEUED. 
*                CURUSTASK - WHICH TASK IF CURUSINTASK TRUE.
* 
*         EXIT   USER QUEUED IF NOT ALREADY IN TASK.
*                TASKREQUE[] - SET IF IN TASK TO SIGNAL EVENT.
*                TASKPULSE[] - SET TO QUICKLY END ANY DELAY.
* 
*         CALLS  PUTINQ.
# 
  IF CURUSINTASK THEN 
    BEGIN 
    TASKREQUE[CURUSTASK]=TRUE; # ASSURE KEEPS RUNNING        #
    TASKPULSE[CURUSTASK]=3;    # ASSURE NO CLOCK DELAY LOOP  #
    END 
  ELSE IF NOT CURUSINQUE THEN PUTINQ; 
  
  END                          # OF SCHEDULE       #
  
  
PROC PUTINQ;
  BEGIN 
# 
**        PUTINQ - PUT CURRENT USER IN SCHEDULE QUEUE.
* 
*         PUTINQ LINKS THE CURRENT USER ONTO THE QUEUE OF JOBS
*         WHICH HAVE BEEN SCHEDULED FOR FUTURE TASK ASSIGNMENT. 
*         A NEW QUEUE LINKAGE IS STARTED IF THERE IS NO QUEUE 
*         ALREADY.
* 
*         ENTRY  CURUSER - CURRENT USER.
*                CURUSERTAB - ALL ENTRIES SETUP.
*                CURUSINQUE - MUST BE FALSE.
*                SWPQHEAD - ZERO IF NO QUEUE EXISTS, OR LINK TO HEAD
*                    OF QUEUE IF OTHER USERS ALREADY QUEUED.
*                SWPQTAIL - IF SWPQHEAD NON-TRIVIAL, LAST USER QUEUED.
*                SWPQSIZE - NUMBER OF USERS IN QUEUE. 
* 
*         EXIT   VIA MELT IF INVALID CONDITIONS.
*                SWPQHEAD - HEAD OF QUEUE.  (CURUSER IF NEW QUEUE)
*                SWPQTAIL - TAIL OF QUEUE NOW EQUALS CURUSER. 
*                USERQUELNK[OLD SWPQTAIL] - LINKS TO CURUSER IF THERE 
*                    WAS A PREVIOUS QUEUE.
*                CURUSINQUE - TRUE. 
*                SWPQSIZE - INCREMENTED.
* 
*         CALLS  MELT.
# 
  CONTROL IFGQ PARANOIA,5;
    IF CURUSINQUE THEN MELT("PUTINQ 1$"); 
  CONTROL FI; 
  IF SWPQHEAD EQ 0 THEN        # NEW QUEUE         #
    BEGIN 
    SWPQHEAD=CURUSER; 
    SWPQTAIL=CURUSER; 
    CURUSQUELNK=0;
    END 
  ELSE                         # APPEND TO QUEUE   #
    BEGIN 
    USERQUELNK[SWPQTAIL]=CURUSER; 
    CURUSQUELNK=0;
    SWPQTAIL=CURUSER; 
    END 
  CURUSINQUE=TRUE;
  SWPQSIZE=SWPQSIZE+1;
  
  END                          # OF PUTINQ         #
  
  
PROC TAKEOFFQ;
  BEGIN 
# 
**        TAKEOFFQ - TAKE CURRENT USER OFF QUEUE. 
* 
*         TAKEOFFQ REMOVES THE CURRENT USER FROM THE QUEUE OF USERS 
*         SCHEDULED FOR FUTURE TASK ASSIGNMENT.  THE CURRENT USER IS
*         REQUIRED TO BE IN THE QUEUE, BUT IS ALLOWED TO BE AT THE
*         HEAD, OR AT THE TAIL, OR ANYWHERE IN BETWEEN.  TAKEOFFQ 
*         THEREFORE CONTAINS LOGIC TO CLOSE QUEUE LINKAGE UP AROUND 
*         THE EXTRACTED USER. 
* 
*         ENTRY  CURUSER - CURRENT USER.
*                CURUSERTAB - ALL ENTRIES SETUP.
*                CURUSINQUE - MUST BE TRUE. 
*                SWPQHEAD - HEAD OF QUEUE.
*                SWPQTAIL - TAIL OF QUEUE.
*                SWPQSIZE - NUMBER OF USERS IN QUEUE. 
*                USERQUELNK[ALL IN QUEUE] - LINKAGE.
* 
*         EXIT   CURUSINQUE - FALSE.
*                CURUSQUELNK - ZEROED OUT.
*                SWPQTAIL - BACKED TO PREVIOUSLY NEXT-TO-LAST 
*                    USER IF CURUSER MATCHED SWPQTAIL.
*                SWPQHEAD - ADVANCED TO PREVIOUSLY SECOND USER IF 
*                    CURUSER MATCHED SWPQHEAD.  ZERO IF CURUSER WAS 
*                    ONLY USER IN QUEUE.
*                SWPQSIZE - DECREMENTED.
* 
*         CALLS  MELT.
# 
  ITEM TMP1,TMP2; 
  
  CONTROL IFGQ PARANOIA,5;
    IF NOT CURUSINQUE THEN MELT("TAKEOFFQ 1$"); 
  CONTROL FI; 
  IF SWPQHEAD EQ CURUSER THEN  # TAKE FROM FRONT   #
    BEGIN 
    IF SWPQHEAD EQ SWPQTAIL THEN SWPQTAIL=0;
    SWPQHEAD=CURUSQUELNK; 
    END 
  ELSE                         # FIND THEN EXTRACT #
    BEGIN 
    TMP1=SWPQHEAD;
    WHYLE TMP1 NQ CURUSER DO
      BEGIN 
      CONTROL IFGQ PARANOIA,5;
        IF TMP1 EQ 0 THEN MELT("TAKEOFFQ 2$");
      CONTROL FI; 
      TMP2=TMP1;
      TMP1=USERQUELNK[TMP1];
      END 
    IF TMP1 EQ SWPQTAIL THEN SWPQTAIL=TMP2; 
    USERQUELNK[TMP2]=USERQUELNK[TMP1];
    END 
  CURUSINQUE=FALSE; 
  CURUSQUELNK=0;
  SWPQSIZE=SWPQSIZE-1;
  
  END                          # OF TAKEOFFQ       #
  
  
PROC ALLOC(BITMAP,BITMASK,INDEX,LIMIT); 
  BEGIN 
# 
**        ALLOC - ALLOCATE RESOURCE VIA BITMAP. 
* 
*         SEARCH THE BITMAP FOR AN AVAILABLE ENTITY, RETURN THE INDEX 
*         FOR IT.  SET THE BIT AS UNAVAILABLE.  BITMAPS ARE 32-BITS 
*         PER WORD, CENTERED, SO THAT THE FINDBIT ROUTINE CAN USE 
*         OPTIMAL INSTRUCTIONS (E.G., NORMALIZE) TO FIND A BIT.  A
*         BIT IS ON TO SHOW AVAILABLE ENTITY. 
* 
*         ENTRY  BITMAP - THE BITMAP ARRAY TO SEARCH AND UPDATE.
*                BITMASK - ARRAY WITH "DONT USE" FLAGS. 
*                LIMIT - SIZE OF ARRAYS.
* 
*         EXIT   INDEX - ORDINAL OF ALLOCATED RESOURCE. 
*                BITMAP - UPDATED TO REFLECT ALLOCATION.
* 
*         CALLS  FINDBIT. 
# 
  ARRAY BITMAP[0:99]; ITEM BITWORD; 
  ARRAY BITMASK;; 
  ITEM INDEX, LIMIT;
  
  INDEX=FINDBIT(LIMIT,BITMAP,BITMASK);
  IF INDEX NQ -1 THEN B<12+MOD32(INDEX),1>BITWORD[INDEX/32]=0;
  INDEX=INDEX+1;               # ADJUST TO ORIGIN OF ONE     #
  
  END                          # OF ALLOC          #
  
  
PROC DEALLOC(BITMAP,INDEX,LIMIT); 
  BEGIN 
# 
**        DEALLOC - UPDATED BITMAP TO MAKE RESOURCE AVAILABLE.
* 
*         DEALLOC IS THE COUNTERPART TO ALLOC.
* 
*         ENTRY  BITMAP - ARRAY CONTAINING ALLOCATION FLAGS.
*                INDEX - WHICH RESOURCE TO FREE UP. 
*                LIMIT - SIZE OF BITMAP.
* 
*         EXIT   BITMAP - UPDATED.
* 
*         CALLS  MELT.
# 
  ITEM INDEX, LIMIT;
  ARRAY BITMAP [0:99]; ITEM BITWORD;
  ITEM TMP1;
  TMP1=INDEX-1; 
  CONTROL IFGQ PARANOIA,5;
    IF TMP1 GQ LIMIT*32 OR TMP1 LS 0 THEN MELT("DEALLOC 1$"); 
    IF B<12+MOD32(TMP1),1>BITWORD[TMP1/32] EQ 1 
      THEN MELT("DEALLOC 2$");
  CONTROL FI; 
  B<12+MOD32(TMP1),1>BITWORD[TMP1/32]=1;
  END                          # OF DEALLOC        #
  
  
PROC FORCEALLOC(BITMAP,INDEX,LIMIT);
  BEGIN 
# 
**        FORCEALLOC - ALLOCATE SPECIFIC RESOURCE WITHOUT SEARCH. 
* 
*         FORCES A BIT AS UNAVAIL.  USED TO CUT OFF EXCESS BITS AT
*         END OF A BITMAP AND TO SHUT OFF ENTITIES THAT HAVE SUFFERED 
*         HARDWARE FAILURES.  (SUCH AS SEGMENTS OF ECS) 
* 
*         ENTRY  BITMAP - ARRAY OF ALLOCATION FLAGS.
*                INDEX - WHICH RESOURCE TO ALLOCATE.
*                LIMIT - SIZE OF BITMAP.
* 
*         EXIT   BITMAP - UPDATED.
* 
*         CALLS  MELT.
# 
  ITEM INDEX,LIMIT; 
  ARRAY BITMAP[0:99]; ITEM BITWORD; 
  ITEM TMP1;
  
  TMP1=INDEX-1; 
  CONTROL IFGQ PARANOIA,5;
    IF TMP1 GQ LIMIT*32 OR TMP1 LS 0 THEN MELT("FORCEALLOC 1$");
  CONTROL FI; 
  B<12+MOD32(TMP1),1>BITWORD[TMP1/32]=0;
  
  END                          # OF FORCEALLOC     #
  
  
PROC KILLECSBITS(BITMAP); 
  BEGIN 
# 
**        KILLECSBITS - FORCEALLOC ALL ECS SWAP PAGES.
* 
*         ENTRY  BITMAP - ARRAY OF ALLOCATION FLAGS TO UPDATE.
* 
*         BITMAP - UPDATED. 
* 
*         CALLS  FORCEALLOC.
# 
  ARRAY BITMAP;;
  ITEM TMP1;
  FOR TMP1=1 STEP 1 UNTIL NUMSWPECS 
    DO FORCEALLOC(BITMAP,TMP1,NUMSWPBIT); 
  END                          # OF KILLECSBITS    #
  
  
PROC PURGETTI;
  BEGIN 
# 
**        PURGETTI - GET RID OF ANY TERMINAL INPUT CHAINS.
* 
*         ENTRY  CURUSER - CURRENT USER.
*                CURUSTTIBUF - FIRST TTI BUFFER OR NULL.
*                REMAINTTI - HOW MANY BUFFERS STILL AVAIL.
*                TTIBUFLNK[ALL] - LINKAGES. 
*                TTIBITS - ALLOCATION BITMASK.
* 
*         EXIT   CURUSTTIBUF - NULL.
*                REMAINTTI - INCREMENTED. 
*                TTIBITS - UPDATED. 
* 
*         CALLS  DEALLOC. 
# 
  ITEM TMP1;
  TRCSTR("PURGETTI$");
  TMP1=CURUSTTIBUF; 
  WHYLE TMP1 NQ 0 DO
    BEGIN 
    CURUSTTIBUF=0;
    DEALLOC(TTIBITS,TMP1,NUMTTIBIT);
    REMAINTTI=REMAINTTI+1;
    TMP1=TTIBUFLNK[TMP1]; 
    END 
  END                          # OF PURGETTI       #
PAGE                         # MAJOR SYSTEM STATE ROUTINES #
  
  
PROC DROPSLICE; 
  BEGIN 
# 
**        DROPSLICE - RECALL CPU FOR A LITTLE WHILE.
* 
*         ENTRY  NO CONDITIONS. 
* 
*         EXIT   REAL TIME HAS BEEN DELAYED.
* 
*         CALLS  RECALL, INSTRMNT1. 
# 
  RECALL(0);
  CONTROL IFEQ METERING,1;
    INSTRMNT1(INSTST"CLOCKTICK"); 
  CONTROL FI; 
  
  END                         # OF DROPSLICE      # 
  
  
PROC DOTIMER; 
  BEGIN 
# 
**        DOTIMER - SUPERVISE TIME DEPENDENCIES.
* 
*         DOTIMER IS INTENDED TO BE CALLED ONCE PER MAIN LOOP.  THE 
*         CPU IS RECALLED ONCE TO ACHIEVE A SMALL DELAY IN REAL TIME, 
*         ABOUT 24 MILLISECONDS IF THE INSTALLATION RUNS WITH DEFAULT 
*         OPERATOR PARAMETERS, THE TIME OF DAY IS NOTED, AND CERTAIN
*         FUNCTIONS ARE PERFORMED IF THE TIME OF DAY HAS ADVANCED 
*         SUFFICIENTLY SINCE THE LAST TIME SUCH FUNCTIONS WERE
*         PERFORMED.
* 
*         ON A CYCLE OF APPROX FIVE TIMES PER SECOND, WE LOOK FOR 
*         OPERATOR COMMANDS AND SIGNAL RESTART OF ANY TASKS WHICH ARE 
*         DELAYING FOR THE PURPOSE OF ACHIEVING A QUICK RECYCLE OF
*         THE SAME TASK FOR THE SAME USER.
* 
*         ON A CYCLE OF APPROX TWO SECONDS, THE B-DISPLAY MESSAGE IS
*         UPDATED TO SHOW CURRENT LOAD PARAMETERS - NUMBER OF USERS 
*         IN THE SUBSYSTEM AND NUMBER OF TASKS CURRENTLY ASSIGNED TO
*         EXECUTE.
* 
*         ENTRY  TIMEOFDAY - TIME AT WHICH PREVIOUS CPU SLICE STARTED.
*                NEWTIMEDAY - EQUALS TIMEOFDAY OR TIME AT WHICH A TASK
*                WAS CONSIDERED FOR PREEMPTIVE SWAP, WHICHEVER LARGER.
*                SLICECOUNT - NUMBER OF CYCLES EXECUTED SO FAR. 
*                LOGGEDIN - NUMBER OF USERS.
*                TASKSBUSY - NUMBER OF TASKS UTILIZED.
*                ENABLEFLAG - ENABLE/DISABLE NEW USERS OR NO-OP.
* 
*         EXIT   OLDTIMEDAY - PREVIOUS VALUE FOR TIMEOFDAY. 
*                TIMEOFDAY, NEWTIMEDAY - CURRENT TIME.
*                SLICECOUNT - INCREMENTED.
*                SHUTDOWN, SENSE SWITCHES - REFER TO DOOPER ROUTINE.
*                TASKPULSE[ALL] - 1 IF 5-HERTZ CYCLE OCCURRED.
*                B-DISPLAY UPDATED IF 2-SECOND CYCLE. 
* 
*         CALLS  DROPSLICE, DOOPER, RTIME, SMFSSTF, SFMCSTF,
*                CHECKTIME(INTERNAL), MESSAGE.
# 
  # HANDLES TIME-OF-DAY DEPENDENT STUFF # 
  
  ITEM LAST5HZ=0, LAST2SEC=0; 
  ITEM TMP1, TMP2, BOOL B;
  
  PROC CHECKTIME(LAST,INTERVAL,FLAG); 
    BEGIN 
    ITEM LAST, INTERVAL, FLAG B;
    FLAG=FALSE; 
    IF TIMEOFDAY-LAST GQ INTERVAL THEN
      BEGIN 
      LAST=TIMEOFDAY; 
      FLAG=TRUE;
      END 
    END                       # OF CHECKTIME      # 
  
  
  # DOTIMER MAIN CODE STARTS HERE # 
  
  DROPSLICE;                         # RECALL FOR A WHILE  #
  OLDTIMEDAY=NEWTIMEDAY;
  RTIME(TIMEOFDAY); 
  B<0,30>TIMEOFDAY=0;          # DONT NEED UPPER PART        #
  NEWTIMEDAY=TIMEOFDAY; 
  SLICECOUNT=SLICECOUNT+1;
  
  IF ENABLEFLAG NQ 0 THEN 
    BEGIN 
    IF ENABLEFLAG GQ 0 AND SHUTDOWN EQ 0 THEN SFMSSTF;
    IF ENABLEFLAG LS 0 THEN SFMCSTF;
    ENABLEFLAG=0; 
    END 
  
  CHECKTIME(LAST5HZ,200,BOOL); # 5 TIMES PER SECOND          #
  IF BOOL THEN
    BEGIN 
    DOOPER;                          # LOOK FOR OPERATOR COMMANDS # 
    # ACTIVATE ANY TASKS THAT ARE SPINNING WHEELS # 
    FOR CURTASK=1 STEP 1 UNTIL NUMTASKS DO TASKPULSE[CURTASK]=3;
    END 
  
  CHECKTIME(LAST2SEC,2000,BOOL);       # ONCE EVERY TWO SECONDS      #
  IF BOOL THEN
    BEGIN 
    # DISPLAY LOAD FACTORS #
    TMP1=LOGGEDIN;
    FOR TMP2=3 STEP -1 UNTIL 0 DO 
      BEGIN 
      C<TMP2,1>STATUSMSG=MOD(TMP1,10)+O"33";
      TMP1=TMP1/10; 
      END 
    C<12,1>STATUSMSG=O"33"+MOD(TASKSBUSY,10); 
    C<11,1>STATUSMSG=O"33"+MOD(TASKSBUSY/10,10);
    C<28,2>STATUSMSG=0; 
    MESSAGE(STATUSMSG,1,1); 
    END 
  
  END                          # OF DOTIMER        #
  
  
PROC DOOPER;
  BEGIN 
# 
**        DOOPER - PERFORM OPERATOR FUNCTIONS.
* 
*         DOOPER SHOULD BE CALLED FROM TIME TO TIME TO SEE IF THE 
*         ENVIRONMENT INDICATES AN OPERATIONAL CHANGE IS OCCURRING. 
* 
*         THE FIRST ISSUANCE OF "IDLE,SMF." INDICATES THAT SMFEX
*         SHOULD ENROLL NO NEW USERS AND SHOULD TRANSITION EXISTING 
*         USERS BACK TO THEIR SINGLE-USER EDITORS WITH COMPLETE 
*         TRANSPARENCY.  THIS CAN BE DONE AS THE USERS CYCLE THROUGH
*         THE MAIN LOOP OF THE EDITOR.  WHEN THE USER COUNT REACHES 
*         ZERO, SMFEX WILL SHUT DOWN. 
* 
*         A SECOND ISSUE OF THE IDLE COMMAND REQUESTS THAT SMFEX GET
*         OUT OF THE SYSTEM MORE RAPIDLY, POSSIBLY AT THE EXPENSE OF
*         SOME USERS RECEIVING INTERRUPT WARNING MESSAGES BUT STILL 
*         WITH NO LOSS OF DATA.  SMFEX CAN STOP AS QUICKLY AS IT CAN
*         GET ANY CURRENT TASKS TO LEAVE THEIR INTERNAL CONTROL POINTS. 
* 
*         A THIRD ISSUE OF THE IDLE COMMAND REQUESTS EVEN MORE RAPID
*         SHUTDOWN.  ANY CURRENT TASKS WILL BE PAUSED AT THEIR NEXT 
*         CIO RECALL.  THUS SOME USER COMMANDS MAY NOT COMPLETE, BUT
*         NO EDIT SESSIONS WILL SUFFER REGRESSIVE DAMAGE. 
* 
*         ANY TIME DOOPER DETECTS EITHER THE SLOWDOWN MODE OR THE 
*         QUICK IDLE MODE, IT SETS THE ENABLEFLAG SO THAT DOTIMER 
*         WILL DISABLE THE ACCESSABILITY OF THE SUBSYSTEM.
* 
*         ENTRY  SHUTDOWN - PREVIOUS SHUTDOWN LEVEL IF ANY. 
*                    0 = NORMAL PROCESSING. 
*                    1 = ACCEPT NO USERS, KEEP RUNNING. 
*                    2 = SPECIAL FOR RECOVERY FUNCTION. 
*                    3 = SHUTDOWN WHEN TASKS BECOME EMPTY.
*                    4 = SHUTDOWN WHEN CIO CALLS COMPLETE.
*                SWAPGOOD, SWAPBAD - EXTENDED MEMORY CONTROLS.
* 
*         EXIT   SHUTDOWN - UPDATED.
*                MEM[0] - UPDATED TO ACKNOWLEDGE IDLE COMMAND.
*                ENABLEFLAG - FORCED NEGATIVE IF DETECT NEW NON-TRIVIAL 
*                    VALUE FOR SHUTDOWN FLAG. 
# 
  ITEM TMP1;
  
  IF SHUTDOWN EQ 2 THEN RETURN; 
  
  IF MEM[0] LAN O"100000" NQ 0 THEN  # IDLE,SMF. #
    BEGIN 
    IF IDLEDOWN THEN STOPNOW=TRUE;
    ELSE IF ACCEPTNONE THEN IDLEDOWN=TRUE;
    ELSE ACCEPTNONE=TRUE; 
    MEM[0]=MEM[0] LXR O"100000";     # ACKNOWLEDGE IDLE COMMAND # 
    ONSW(O"40");                     # SIGNAL SUBSYSTEM TERMINATION # 
    END 
  
  TMP1=0; 
  IF ACCEPTNONE THEN TMP1=1;
  IF IDLEDOWN THEN TMP1=3;
  IF STOPNOW THEN TMP1=4; 
  
  IF SHUTDOWN NQ TMP1 THEN
    BEGIN 
    IF TMP1 NQ 0 THEN ENABLEFLAG=-1;
    ELSE ENABLEFLAG=1;
    END 
  SHUTDOWN=TMP1;
  
  CONTROL IFEQ ECSCODE,1; 
    IF ECSDOWN THEN P<SWAPMASK>=LOC(SWAPBAD); 
    ELSE P<SWAPMASK>=LOC(SWAPGOOD); 
  CONTROL FI; 
  
  END                         # OF DOOPER         # 
PAGE                         # DOINPUT -- RECEIVE SIC MSG AND QUEUE  #
  
  
PROC DOINPUT; 
  BEGIN 
# 
**        DOINPUT - ACCEPT MESSAGES FROM IAFEX. 
* 
*         DOINPUT SUPERVISES ALL MESSAGE RECEIVED FROM IAF, AND 
*         SHOULD BE CALLED BY THE MAIN LOOP ONCE PER RECALL CYCLE.
*         IAF COMMUNICATES WITH SMF BY USING THE SCP/UCP FACILITY 
*         (IAF IS THE SCP AND SMF IS THE UCP) TO PLACE MESSAGES 
*         INSIDE THE SMF FIELD LENGTH.  THE ADDRESS OF THE SMF WINDOW 
*         FOR MESSAGES WAS ESTABLISHED BY SMF WHEN IT FIRST SENT A
*         MESSAGE TO IAF.  IAF CANNOT SEND ITS NEXT MESSAGE UNTIL SMF 
*         ACKNOWLEDGES THE CURRENT MESSAGE BY CLEARING THE HEADER 
*         WORD. 
* 
*         NOTE THAT THE DOINPUT ROUTINE PROCESSES RECEIPT OF
*         MESSAGES, BUT HAS NOTHING TO DO WITH SENDING MESSAGES TO
*         IAFEX.  THAT FUNCTION IS PROVIDED AS TASKS EXECUTE VIA
*         EXECUTIVE ROUTINES CALLED BY THE EDITOR MODULE.  DOINPUT
*         CAN TRANSMIT A MESSAGE TO IAFEX, BUT ONLY UNDER THE 
*         EMERGENCY SITUATION WHERE THE USER TABLE HAS OVERFLOWED, IN 
*         WHICH CASE THE USER BEING CONNECTED MUST BE IMMEDIATELY 
*         DISCONNECTED BEFORE ANY OTHER SMFEX PROCESSING CAN PROCEED. 
*         THIS SITUATION IS NORMALLY DISCOURAGED BY LOGIC TO DISABLE
*         SMFEX SUBSYSTEM ACCESSABILITY WHEN THE USER TABLE GETS
*         CLOSE TO THE FULL CONDITION.
* 
*         MESSAGES FROM IAF ARE OF TWO BASIC FORMS - FIRST, A 
*         QUICK-SHUTDOWN IS INDICATED BY NON-ZERO CONTENT APPEARING 
*         IN SSCINP[-1], AND SECOND, A PRODUCTION MESSAGE IS
*         INDICATED BY NON-ZERO CONTENT IN ONE OR MORE WORDS STARTING 
*         WITH SSCINP[0]. 
* 
*         PRODUCTION MESSAGES CAN APPEAR IN SIX VARIATIONS - (1)
*         INITIALIZATION OF IAF, (2) COMMAND TEXT, (3)
*         ACKNOWLEDGEMENT THAT OUTPUT HAS BEEN RECEIVED AND MORE CAN
*         BE ACCEPTED, (4) USER BREAK, (5) DETACHMENT OF TERMINAL 
*         FROM JOB, AND (6) CONNECTION OF A NEW USER INTO SMFEX.
* 
*         THE SECOND THRU FIFTH MESSAGE TYPES REQUIRE REFERENCE TO AN 
*         EXISTING USER WITHIN SMFEX.  THE SECOND MESSAGE TYPE
*         CARRIES ARBITRARY TEXT ALONG WITH THE HEADER WORD, AND THE
*         SIXTH MESSAGE TYPE CARRIES ONE WORD OF TEXT WITH THE HEADER 
*         WORD.  THE OTHER MESSAGE TYPES CONSIST SOLELY OF THE HEADER 
*         WORD. 
* 
*         COMMAND MESSAGES CAUSE SCHEDULING OF THE USER FOR TASK
*         PROCESSING, AND TO ENABLE THE RECEIPT OF ADDITIONAL 
*         MESSAGES FROM IAF WHILE THE TASK SWAPS IN, THE MESSAGE TEXT 
*         IS QUEUED IN ONE OF THE TERMINAL INPUT BUFFERS.  SINCE ONE
*         COMMAND MESSAGE MAY BE JUST A FRAGMENT OF THE ENTIRE
*         COMMAND TEXT, INPUT BUFFERS ARE LINKED TO THE USER AND TO 
*         EACH OTHER AS NEEDED. 
* 
*         SMFEX ATTEMPTS TO AVOID RUNNING OUT OF INPUT BUFFERS WITH 
*         "TRY HARDER" TASK SCHEDULING RULES, BUT IF BUFFERS DO GET 
*         EXHAUSTED, THEN DOINPUT SIMPLY NEGLECTS TO CLEAR AWAY THE 
*         IAF WINDOW AT SSCINP[0], THUS PREVENTING IAF FROM SENDING 
*         MORE MESSAGES UNTIL WE CATCH UP AND CAUSING FUTURE DOINPUT
*         CYCLES TO ONCE AGAIN ATTEMPT TO QUEUE THE INPUT.  IN THE
*         EXTREME CASE WHERE SMFEX FALLS BEHIND WITH IAFEX HOLDING 64 
*         MESSAGES IN ITS OWN QUEUE, THEN IAFEX WILL TREAT SMFEX AS A 
*         DERELICT AND REVERT ALL USERS BACK TO SINGLE-USER MODE. 
* 
*         USER BREAKS AND DETACHMENTS CAUSE TASK SCHEDULING WITH
*         APPROPRIATE FLAGS SET SO THE TASK WILL INTERRUPT NORMAL 
*         PROCESSING AND REACT ACCORDINGLY.  FOR DETACHMENT, IAF
*         DELAYS DETACH COMPLETION UNTIL SMFEX FINALLY TRANSMITS AN 
*         END-OF-EDIT MESSAGE.
* 
*         OUTPUT ACCEPTANCE MESSAGES CAUSE TASK SCHEDULING FOR
*         CONTINUED EXECUTION.
* 
*         CONNECTION MESSAGES CAUSE SMFEX TO ALLOCATE A NEW TABLE 
*         ENTRY FOR THE USER, SAVING THE JSN AND EJT ORDINAL PROVIDED 
*         IN THE SECOND WORD OF THE MESSAGE, WITH THE NEW USER
*         SCHEDULED FOR TASK EXECUTION SO THAT THE TRANSFERRED
*         WORKFILE CAN BE QUICKLY VALIDATED.
* 
*         ENTRY  SSCINP[-1] - AS CLEARED BY PREVIOUS DOINPUT, OR
*                    NEW SHUTDOWN MESSAGE FROM IAFEX. 
*                SSCINP[0] - AS CLEARED BY PREVIOUS DOINPUT, OR 
*                    HEADER OF NEW MESSAGE FROM IAFEX.
*                SHUTDOWN - WHETHER TO PROCESS INPUT NORMALLY.
*                USER AND TASK TABLES SETUP.
*                REMAINTTI - NUMBER OF INPUT QUEUE BUFFERS LEFT.
*                TTIBITS - ALLOCATION BITMASK FOR INPUT BUFFERS.
*                TTIBUFADDR[ALL] - WHERE THE INPUT BUFFERS ARE. 
*                TTIBUFLNK[ALL] - EXISTING LINKAGES OF GROUPED BUFFERS. 
*                LOGGEDIN - NUMBER OF USERS IN SMFEX. 
*                MAXLOGGED - HIGH WATER MARK OF LOGGEDIN. 
*                SWAP QUEUE - SETUP.
* 
*         EXIT   SSCINP[-1] - CLEARED.
*                SSCINP[0] - CLEARED UNLESS REMAINTTI WAS ZERO WITH 
*                    A COMMAND MESSAGE AWAITING QUEUING.
*                LOGGEDIN - INCREMENTED IF CONNECTION MESSAGE.
*                MAXLOGGED - HIGH WATER MARK. 
*                REMAINTTI - DECREMENTED IF COMMAND MESSAGE ARRIVED 
*                    AND REMAINTTI WAS NOT ALREADY ZERO.
*                SWAP QUEUE - UPDATED AS NEEDED.
*                TERMINAL INPUT BUFFERS - IF ALLOCATED, THEN LINKED TO
*                    USER TABLE ENTRY OR TO EXISTING BUFFERS. 
*                USER TABLE ENTRY - UPDATED AS NEEDED.
*                MEM[0] - SENSE SWITCH 6 SET AS NEEDED. 
*                ENABLEFLAG - FORCED NEGATIVE IF NEARING FULL TABLE.
* 
*         CALLS  MELT, INSTRMNT2, INSTRMNT1, ALLOC, MOVEWD, 
*                SCHEDULE, PURGETTI, SYSTEM, RECALL, GETUSER, PUTINQ, 
*                PUTUSER, MAX.
* 
*         USES   IAFHEADER, CURUSER.
# 
  ITEM TMP1,TMP2,TMP3;
  
  SWITCH RCVFNCSW RCVINIT,RCVMSGIN,RCVOUTDONE,RCVBREAK, 
                  RCVHUNGUP,RCVCONNECT; 
  ARRAY VALIDATE[0:5]; ITEM VLDTUSR B=[FALSE,4(TRUE),FALSE];
  ARRAY REJECTION [0:1]; ITEM REJECTMSG;
  ITEM EJT; 
  
  IF SSCINP[-1] NQ 0 THEN 
    BEGIN 
    STOPNOW=TRUE; 
    SSCINP[-1]=0; 
    ONSW(O"40");               # SIGNAL SUBSYSTEM TERMINATION # 
    END 
  
  IF SHUTDOWN GR 1 THEN RETURN; 
  
  IAFHEADER=SSCINP[0];
  IF IAFHEADER NQ 0 THEN         # WE HAVE RECEIVED SOMETHING  #
    BEGIN 
    IF VLDTUSR[IAFFUNC] THEN
      BEGIN 
      CURUSER=0;
      # SEARCH FOR USER AMONG RECENT TASK ASSIGNMENTS # 
      FOR TMP1=1 STEP 1 UNTIL TASKSAVAIL DO 
        BEGIN 
        IF TASKUSER[TMP1] NQ 0 AND USERIAFNUM[TASKUSER[TMP1]] 
          EQ IAFTERM THEN 
          BEGIN 
          CURUSER=TASKUSER[TMP1]; 
          GOTO RCVFOUND;
          END 
        END 
      # SEARCH FOR USER BY BRUTE FORCE #
      FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO
        BEGIN 
        IF USERIAFNUM[TMP1] EQ IAFTERM THEN 
          BEGIN 
          CURUSER=TMP1; 
          GOTO RCVFOUND;
          END 
        END 
RCVFOUND: 
      IF CURUSER LQ 0 OR CURUSER GR NUMSMFUSR THEN
        BEGIN 
        SSCINP[0]=0;
        GOTO AFTERRCV;
        END 
      GETUSER;
      END 
    GOTO RCVFNCSW[IAFFUNC]; 
  
RCVMSGIN: 
      CONTROL IFEQ TRACEFLAG,1; 
        P<MOVFROM>=LOC(SSCINP[1]);
        TMP1=IAFLEN-1;
      CONTROL FI; 
      TRCBOTH("MSGIN$",CURUSER);
      TRCWORDS(MOVFROM,TMP1); 
      CONTROL IFGQ PARANOIA,2;
        IF NOT CURUSLOGGED THEN MELT("DOIN 2$");
        IF IAFLEN GR SIZTTIBUF+1 THEN MELT("DOIN 22$"); 
      CONTROL FI; 
      IF IAFLEN LS 2 THEN 
        BEGIN 
        SSCINP[0]=0;
        GOTO AFTERRCV;
        END 
      CONTROL IFEQ METERING,1;
        INSTRMNT2(INSTST"INPUT",IAFLEN);
      CONTROL FI; 
      IF REMAINTTI LQ 0 THEN   # TRY ANOTHER DAY   #
        BEGIN 
        CONTROL IFEQ METERING,1;
          INSTRMNT1(INSTST"NOTTI"); 
        CONTROL FI; 
        GOTO PASTRCV; 
        END 
      ALLOC(TTIBITS,TTIGOOD,TMP2,NUMTTIBIT);
      CONTROL IFGQ PARANOIA,2;
        IF TMP2 EQ 0 THEN MELT("DOIN 4$");       # MUST SUCCEED      #
      CONTROL FI; 
      REMAINTTI=REMAINTTI-1;   # KEEP TRACK RESOURCE         #
      P<MOVFROM>=LOC(SSCINP[1]);
      P<MOVTO>=TTIBUFADDR[TMP2];
      TMP3=IAFLEN-1;
      MOVEWD(TMP3,MOVFROM,MOVTO); 
      TTIBUFLEN[TMP2]=TMP3; 
      TTIBUFLNK[TMP2]=0;
      IF CURUSTTIBUF EQ 0 THEN # START NEW CHAIN   #
        BEGIN 
        CURUSTTIBUF=TMP2; 
        CONTROL IFEQ METERING,1;
          TMP1=TIMEOFDAY-CURUSLASTIM; 
          INSTRMNT2(INSTST"THNK",TMP1); 
        CONTROL FI; 
        END 
      ELSE                     # ADD TO CHAIN      #
        BEGIN 
        TMP1=CURUSTTIBUF; 
        WHYLE TMP1 NQ 0 DO
          BEGIN 
          TMP3=TMP1;
          TMP1=TTIBUFLNK[TMP1]; 
          END 
        TTIBUFLNK[TMP3]=TMP2; 
        END 
      SCHEDULE; 
      GOTO ENDRCV;
  
RCVBREAK: 
      TRCBOTH("BREAK$",CURUSER);
      CONTROL IFEQ METERING,1;
        INSTRMNT1(INSTST"BREAKS");
      CONTROL FI; 
      IF NOT CURUSLOGGED THEN GOTO ENDRCV;
      CURUSINTRPT=TRUE; 
      CURUSCRUNCH=FALSE;     # IMPROVE SCHEDULEING PRIORITY          #
      PURGETTI; 
      SCHEDULE; 
      GOTO ENDRCV;
  
RCVHUNGUP:  
      TRCBOTH("HUNGUP$",CURUSER); 
      CONTROL IFEQ METERING,1;
        INSTRMNT1(INSTST"HANGUPS"); 
      CONTROL FI; 
      CURUSDROP=TRUE; 
      CURUSRCVRY=TRUE;
      GOTO RCVBREAK;
  
RCVINIT:  
      STOPNOW=TRUE; 
      GOTO AFTERRCV;
  
RCVOUTDONE:                # TTY OUTPUT COMPLETE         #
      TRCBOTH("TOCIN $",CURUSER); 
      CONTROL IFGQ PARANOIA,3;
        IF NOT CURUSLOGGED THEN MELT("DOIN 43$"); 
      CONTROL FI; 
      SCHEDULE; 
      GOTO ENDRCV;
  
RCVCONNECT: 
      TRCBOTH("CONNECT$",IAFTERM);
      IF IAFTERM GR MAXCONNECT THEN MELT("TABLE LIMIT$"); 
      CURUSER=0;
      FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO
        BEGIN 
        IF USERIAFNUM[TMP1] EQ IAFTERM THEN CURUSER=TMP1; 
        IF CURUSER EQ 0 THEN
          BEGIN 
          IF NOT USERLOGGED[TMP1] THEN CURUSER=TMP1;
          END 
        END 
      TRCBOTH("PICK USER$",CURUSER);
      IF CURUSER EQ 0 THEN
        BEGIN 
        # CRITICAL TABLE SPACE SHORTAGE - SEND LOGOUT FUNCTION TO  #
        # IAF IMMEDIATELY AND SERIALIZE UNTIL THIS IS ACCOMPLISHED #
        EJT=B<48,12>SSCINP[1];
TRYREJECT:  
        IAFFUNC=SMF2IAF"LOGOUT";
        IAFLEN=1; 
        IAFPTR=0; 
        REJECTMSG[0]=0; 
        REJECTMSG[1]=IAFHEADER; 
        P<SSCBUFFER>=LOC(REJECTION);
        SSCTYPE=O"3"; 
        SSCCOUNT=1; 
        SYSREQ("SSC",0,LOC(SSCBUFFER),IAFSSID); 
        IF SSCSTATUS LAN O"7776" EQ 4 THEN
          BEGIN 
          RECALL(0);
          GOTO TRYREJECT; 
          END 
        EESET((TTEQ*4096)+EJT); 
        GOTO AFTERRCV;
        END 
      USERFLAGS[CURUSER]=0; 
      GETUSER;
      CURUSLOGGED=TRUE; 
      CURUSSTART=TRUE;
      CURUSIAFNUM=IAFTERM;
      USERJSN[CURUSER]=C<0,4>SSCINP[1]; 
      USEREJT[CURUSER]=B<48,12>SSCINP[1]; 
      LOGGEDIN=LOGGEDIN+1;
      IF LOGGEDIN GQ NUMSMFUSR-2 THEN ENABLEFLAG=-1;
      CONTROL IFEQ METERING,1;
        MAXLOGGED=MAX(MAXLOGGED,LOGGEDIN);
      CONTROL FI; 
      PUTINQ; 
      GOTO ENDRCV;
  
ENDRCV: 
      CURUSLASTIM=TIMEOFDAY;
      SSCINP[0]=0;
PASTRCV:  
      PUTUSER;
AFTERRCV: 
  
    END                         # OF BUFFER0 HANDLER          # 
  
  END                          # OF DOINPUT        #
PAGE                         # RUNJOBS -- TRY TO DO SOME USER CODE   #
  
  
  
PROC RUNJOBS; 
  BEGIN 
# 
**        RUNJOBS - SUPERVISE TASK EXECUTION. 
* 
*         RUNJOBS CONTROLS TASK EXECUTION.  TASKS GET THE CPU ONLY
*         FROM RUNJOBS, AND ARE REQUIRED TO EXIT EACH TIME SLICE TO 
*         THE APPROPRIATE LABEL WITHIN RUNJOBS.  SINCE RUNJOBS IS THE 
*         SOLE PATH FOR TASKS TO RECEIVE CONTROL, IT IS NOT NECESSARY 
*         FOR RUNJOBS ITSELF TO USE REENTRANT ENTRY AND EXIT EVEN 
*         THOUGH ITS SUBROUTINES ARE THEMSELVES REENTRANT.
* 
*         NOTE THAT THERE IS NO ARCHITECTURAL PROTECTION AGAINST
*         RENEGADE TASKS.  IF A TASK LOOPS OR OVERWRITES MEMORY, THE
*         ENTIRE SUBSYSTEM WILL BE IMPACTED.  THEREFORE, MULTI-USER 
*         EDITING CODE MUST BE BUG-FREE.  EDITING CODE MUST ALSO BE 
*         SENSITIVE AND REASONABLE IN RESOURCE CONSUMPTION SINCE THIS 
*         SUBSYSTEM EXECUTES AT A VERY HIGH PRIORITY. 
* 
*         RUNJOBS PERFORMS TWO PRELIMINARY PHASES BEFORE INITIATING 
*         TASKS.  THE FIRST PRELIMINARY PHASE ACCOUNTS FOR THE LEVEL
*         OF UTILIZATION, BOTH FOR STATISTICAL REASONS AND TO ENABLE
*         SCHEDULING RULES TO "TRY HARDER".  THE SECOND PRELIMINARY 
*         PHASE CHECKS FOR SHUTDOWN CONDITIONS. 
* 
*         THE PRINCIPAL FUNCTION OF RUNJOBS IS TO ADVANCE THE STATUS
*         OF EACH AVAILABLE TASK.  WHILE THE SUBSYSTEM IS COMPILED
*         WITH A LARGE NUMBER OF TASKS IN EXISTENCE, THE NUMBER OF
*         TASKS AVAILABLE FOR EXECUTION IS DETERMINED AT SUBSYSTEM
*         STARTUP AND IS TYPICALLY MUCH SMALLER.
* 
*         EACH TASK CAN HAVE ANY OF THE FOLLOWING STATUSES -- 
* 
*         1.  IT MAY BE EMPTY.  AN EMPTY TASK WILL BE ASSIGNED TO A 
*         USER IF THERE ARE USERS IN THE SCHEDULING (SWAPIN) QUEUE
*         AND NO SHUTDOWN CONDITION EXISTS.  IN ASSIGNING A USER TO A 
*         PREVIOUSLY EMPTY TASK, THE TASK STATUS IS CHANGED TO
*         "START" OR "SWAPIN" BASED ON THE USER'S PROGRESS. 
* 
*         2.  IT MAY BE DELAYING.  A DELAYING TASK MISSES THE CPU FOR 
*         PRECISELY ONE SMFEX CPU RECALL CYCLE, AND TYPICALLY IS
*         WASTING REAL TIME WHILE POLLING ON COMPLETION OF AN I/O 
*         FUNCTION.  IN VIEW OF THE BRUTE FORCE REQUIRED TO RESTART A 
*         DELAYED TASK, THIS STYLE OF WAITING FOR I/O IS NOT USED 
*         OFTEN WITHIN THE EDITING CODE, BUT IT IS FULLY SUPPORTED BY 
*         THIS EXECUTIVE.  RUNJOBS RESTARTS A DELAYED TASK AS SOON AS 
*         IT IS DISCOVERED, BY CHANGING THE TASK STATUS TO "RUN". 
* 
*         3.  A TASK MAY BE IN AUTO RECALL.  THIS TASK WISHES TO MISS 
*         THE CPU UNTIL SMFEX DETECTS THAT THE SPECIFIED COMPLETION 
*         BIT HAS BEEN TOGGLED BY ANYBODY.  THIS IS THE METHOD
*         GENERALLY USED WITHIN THE EDITOR TO WAIT FOR I/O, SINCE IT
*         AVOIDS WASTING CPU TIME TO START UP A DELAYED TASK WHICH IS 
*         LIKELY TO DROP RIGHT BACK INTO DELAY.  ONCE SMFEX VERIFIES
*         THAT THE DESIRED EVENT IS COMPLETE, IT RESTARTS A RECALLED
*         TASK BY CHANGING THE TASK STATUS TO "RUN".
* 
*         4.  A TASK MAY BE IN "START" STATUS.  THIS TASK IS READY
*         FOR THE CPU, FOR A USER WHO HAS NOT YET EXECUTED ANY
*         MULTI-USER EDITING CODE.  THE TASK WILL GET THE CPU AT THE
*         EDITOR'S MAIN ENTRY POINT.  NO SWAP PAGE EXISTS YET, AND
*         DISPATCHING DATA IS IN MODEL FORMAT.
* 
*         5.  A TASK MAY BE IN "SWAPIN" STATUS.  THIS TASK HAS
*         PREVIOUSLY EXECUTED AND SWAPPED OUT.  THE TASK THEREFORE
*         WANTS THE CPU AT THE ENTRY POINT OF THE SWAPPING CODE.
*         SINCE THE SWAPOUT AND SWAPIN CODE ARE ONE AND THE SAME, 
*         TASK CPU DISPATCHING IS EFFECTED BY RETURNING TO THE SWAP 
*         CODE WHERE CONTROL WAS PREVIOUSLY SURRENDERED.  THAT SWAP 
*         CODE WILL READ IN THE SWAP CODE AND ALTER THE MEMORY IMAGE
*         FOR THE TASK ON THE FLY, THEN RETURN OUT THE THE EDITING
*         CODE WHICH HAD CAUSED THE PREVIOUS SWAPOUT. 
* 
*         6.  A TASK MAY BE IN "RUN" STATUS.  THIS TASK HAS BEEN
*         SWAPPED IN FOR SOME TIME, AND IS PRESENTLY COMPLETING A 
*         DELAY OR RECALL.  SINCE THE MEMORY IMAGE FOR THE TASK IS
*         INTACT, WE ARE REQUIRED ONLY TO MANIPULATE THOSE ARRAYS 
*         CRITICAL TO TASK DISPATCHING, AND RETURN TO THE POINT IN
*         THE EDITING CODE WHERE THE TASK HAD PREVIOUSLY PAUSED.
* 
*         EACH TIME A TASK GETS THE CPU, IT CAN FINISH ITS TIME SLICE 
*         IT THREE WAYS -- (1) THE ENTIRE EDIT SESSION MAY END, IN
*         WHICH CASE THE TASK EXITS VIA LABEL "AFTEREDIT" FOR CLEANUP 
*         PROCESSING, OR (2) THE TASK MAY COMPLETE A TRANSACTION, 
*         I.E., IT MAY FINISH SWAPPING OUT FOR TERMINAL I/O OR
*         RESOURCE BALANCING, IN WHICH CASE IT EXITS VIA LABEL
*         "AFTERTRANS", OR (3) IT MAY NEED TO DELAY/RECALL WITHIN ITS 
*         MEMORY IMAGE (I.E., WHILE STILL SWAPPED IN) FOR SOME DISK 
*         I/O OR OTHER SHORT-TERM EVENT, IN WHICH CASE IT EXITS VIA 
*         LABEL "AFTERSLICE". 
* 
*         IN SUMMARY, THERE ARE THREE WAYS FOR A TASK TO BE ASSIGNED
*         THE CPU (START OF SESSION, START OF TRANSACTION, OR RESTART 
*         AFTER DELAY) AND THREE WAYS FOR A TASK TO FORFEIT THE CPU 
*         (END OF SESSION, END OF TRANSACTION, DELAY), AND THESE
*         ENTRIES AND EXITS CAN BE MIXED IN ANY FASHION.  AS A
*         RESULT, RUNJOBS MUST VIOLATE A RULE OF CLEAN PROGRAMMING
*         PRACTISE, IN ALLOWING JUMPS INTO BLOCKS OF CODE (IF-THEN, 
*         NOT LOOPS) WHICH ARE NOT NECESSARILY ACTIVE.
* 
* 
*         ENTRY  SHUTDOWN - CONTROLS WHETHER TO EXECUTE AT ALL. 
*                SWPQHEAD - START OF SWAPIN QUEUE.
*                TASKSAVAIL - NUMBER OF TASKS WE CAN USE. 
*                TASK AND USER TABLES - SETUP.
*                MODELPTR, MODELLEN - MODEL FORMAT DISPATCH DATA. 
*                TIMEOFDAY - MILLISECOND CLOCK. 
*                LOGGEDIN - NUMBER OF USERS.
* 
*         EXIT   TASKSBUSY - NUMBER OF NON-EMPTY TASKS. 
*                COMPLETE - FORCED TRUE BASED ON SHUTDOWN.
*                LOGGEDIN - DECREMENTED FOR ANY SESSION COMPLETIONS.
*                ENABLEFLAG - NON-ZERO POSITIVE IF LOGGEDIN 
*                    DECREMENTS OUT OF NEAR-FULL CONDITION. 
*                USER AND TASK TABLES - UPDATED AS NEEDED.
* 
*         CALLS  INSTRMNT1, GETUSER, TAKEOFFQ, PUTUSER, GETCOMMON,
*                MOVEWD, GENWRKNAM, EDTINIT, PROCESS, EDTTERM,
*                MELT, ABTKILL, ABTPAUSE, RTRNWRKFIL, SENDLOGOUT, 
*                KILL, TRANSACT, PUTINQ, SPREAD, GATHER.
* 
*         USES   CURUSER, CURTASK, RSTKPTR, RENTSTK.
* 
*         NOTE   WHENEVER RUNJOBS INVOKES A TASK, BY CALLING
*                PROCESS, TRANSACT, OR RESUME, THE FOLLOWING
*                CONVENTIONS APPLY FOR PARAMETERS PASSED TO AND 
*                FROM THE EDITOR CODE --
* 
*                PASSED     CURTASK, CURUSER, RSTKPTR, RENTSTK, 
*                           WORKNAM, P<MANY ARRAYS>.
* 
*                RETURNED   ABORTED, RSTKPTR, RENTSTK,
*                           CHNGSTATUS, TASKRCLADR[CURTASK].
# 
  ITEM TMP1,TMP2; 
  XDEF LABEL AFTEREDIT; 
  XDEF LABEL AFTERTRANS;
  XDEF LABEL AFTERSLICE;
  XREF LABEL RESUME;
  
  TASKSBUSY=0;                 # COUNT UP TASK UTILIZATION   #
  FOR TMP1=1 STEP 1 UNTIL NUMTASKS DO  IF TASKSTATUS[TMP1] NQ 
    TASKST"EMPTY" THEN TASKSBUSY=TASKSBUSY+1; 
  CONTROL IFEQ METERING,1;
    IF TASKSBUSY GQ TASKSAVAIL AND SWPQHEAD NQ 0
      THEN INSTRMNT1(INSTST"NOTASK"); 
  CONTROL FI; 
  
  IF SHUTDOWN GR 1 THEN        # SEE IF SINUSES CLEARED      #
    BEGIN 
    IF TASKSBUSY EQ 0 THEN     # ALL ACTIVITY CEASED         #
      BEGIN 
      IF (SHUTDOWN GQ 2 AND SWPQHEAD EQ 0) OR 
        SHUTDOWN EQ 4 THEN COMPLETE=TRUE; 
      END 
    END 
  IF SHUTDOWN EQ 1 AND LOGGEDIN EQ 0 THEN COMPLETE=TRUE;
  
  
  FOR CURTASK=1 STEP 1 UNTIL TASKSAVAIL DO
    BEGIN 
  
    IF SWPQHEAD NQ 0 AND SHUTDOWN LQ 3
      AND TASKSTATUS[CURTASK] EQ TASKST"EMPTY" THEN 
      BEGIN 
      CURUSER=SWPQHEAD; 
      WHYLE CURUSER NQ 0 AND USERTTIBUF[CURUSER] EQ 0 
        DO CURUSER=USERQUELNK[CURUSER]; 
      IF CURUSER EQ 0 THEN
        BEGIN 
        CURUSER=SWPQHEAD; 
        WHYLE CURUSER NQ 0 AND USERCRUNCH[CURUSER]
          DO CURUSER=USERQUELNK[CURUSER]; 
        IF CURUSER EQ 0 THEN CURUSER=SWPQHEAD;
        END 
      TRCBOTH("ASGN SLT$",CURTASK);  TRCDEC(CURUSER); 
      GETUSER;
      CURUSCRUNCH=FALSE;
      CURUSINTASK=TRUE; 
      CURUSTASK=CURTASK;
      TAKEOFFQ; 
      TASKUSER[CURTASK]=CURUSER;
      TASKFLAGS[CURTASK]=0;    # CLEAR ALL         #
      TASKNXTTTO[CURTASK]=0;
      TASKTYPTTO[CURTASK]=0;
      IF CURUSSTART THEN TASKSTATUS[CURTASK]=TASKST"START"; 
      ELSE TASKSTATUS[CURTASK]=TASKST"SWAP";
      PUTUSER;
      END 
  
    IF TASKSTATUS[CURTASK] EQ TASKST"EMPTY" THEN TEST CURTASK;
  
    IF TASKSTATUS[CURTASK] EQ TASKST"DELAY" THEN
      BEGIN 
      TASKSTATUS[CURTASK]=TASKST"RUN";
      END 
  
    IF TASKSTATUS[CURTASK] EQ TASKST"RECALLFET" THEN
      BEGIN 
      IF B<59,1>MEM[TASKRCLADR[CURTASK]] EQ 1 THEN
        BEGIN 
        TASKSTATUS[CURTASK]=TASKST"RUN";
        END 
      END 
  
    IF TASKSTATUS[CURTASK] EQ TASKST"START" THEN
      BEGIN 
      CURUSER=TASKUSER[CURTASK];
      TRCBOTH("TASK STRT$",CURTASK);  TRCDEC(CURUSER);
      GETUSER;
      CONTROL IFEQ METERING,1;
        INSTRMNT1(INSTST"SESSION"); 
      CONTROL FI; 
      CURUSSTART=FALSE; 
      RSTKPTR=1;               # MAKE A NEW STACK  #
      GETCOMMON;               # SET ARRAY BASES   #
      P<MOVFROM>=MODELPTR;
      MOVEWD(MODELLEN,MOVFROM,DATAEND);        # VIRGIN EDIT DATA  #
      GENWRKNAM;
      TASKTIME[CURTASK]=TIMEOFDAY;       # SINCE NO SWAPIN   #
      IF NOT CURUSRCVRY THEN
        BEGIN 
        EDTINIT;
        PROCESS;
        EDTTERM;
        END 
AFTEREDIT:               # CONTROL FORCE HERE IF RCVRY #
      CONTROL IFEQ METERING,1;
        INSTRMNT1(INSTST"ENDEDT");
      CONTROL FI; 
      CURUSDONE=TRUE; 
      CONTROL IFEQ TRACEFLAG,1; 
        IF ABORTED THEN TRACESTR("ABORTEDIT$"); 
        ELSE TRACESTR("ENDEDIT$");
      CONTROL FI; 
      CONTROL IFEQ MELTONABT,1; 
        IF ABORTED THEN MELT("SUBTASK ABORT$"); 
      CONTROL FI; 
      RSTKPTR=1;
      IF ABORTED THEN 
        BEGIN 
        IF TASKABORT[CURTASK] THEN ABTKILL; 
        ELSE
          BEGIN 
          TASKABORT[CURTASK]=TRUE;
          ABTPAUSE; 
          TASKABORT[CURTASK]=FALSE; 
          END 
        END 
      RTRNWRKFIL;              # RETURN THE WORK FILE        #
      SENDLOGOUT;              # TRANSMIT LOGOUT FUNCTION    #
      LOGGEDIN=LOGGEDIN-1;
      IF LOGGEDIN EQ NUMSMFUSR-5 THEN ENABLEFLAG=1; 
      TASKSTATUS[CURTASK]=TASKST"EMPTY";
      LASTTASK=0; 
      KILL; 
      END 
  
    IF TASKSTATUS[CURTASK] EQ TASKST"SWAP" THEN 
      BEGIN 
      CURUSER=TASKUSER[CURTASK];
      TRCBOTH("TASK TRNS$",CURTASK);  TRCDEC(CURUSER);
      GETUSER;
      RSTKPTR=CURUSSTKPTR  ;   # HE REMEMBERS HIS PLACE      #
      GETCOMMON;
      FETFNT=CURUSFNTPTR; 
      GENWRKNAM;
      TRANSACT;                # RUNS AS SUBROUTINE OF USER  #
AFTERTRANS:  # CONTROL COMES HERE MANUALLY FROM TRANSACT/ENDTRANS # 
      CURUSSTKPTR=RSTKPTR;
      CURUSFNTPTR=FETFNT; 
      CURUSINTASK=FALSE;
      IF TASKREQUE[CURTASK] THEN PUTINQ;
      TASKSTATUS[CURTASK]=TASKST"EMPTY";
      LASTTASK=0; 
      PUTUSER;
      END 
  
    IF TASKSTATUS[CURTASK] EQ TASKST"RUN" THEN
      BEGIN 
      CURUSER=TASKUSER[CURTASK];
      TRCBOTH("TASK RUN$",CURTASK);  TRCDEC(CURUSER); 
      GETUSER;
      IF SHUTDOWN EQ 4 THEN    # SELF DESTRUCT     #
        BEGIN 
        TASKSTATUS[CURTASK]=TASKST"EMPTY";
        RETURN; 
        END 
      RSTKPTR=TASKSTKPTR[CURTASK];     # HE REMEMBERS PLACE          #
      GETCOMMON;
      MOVEWD(RSTKPTR-1,DISPRSTK,RENTSTK); 
      SPREAD(RSTKPTR,RENTSTK);
      IF CURUSINTRPT THEN USRBRK=1; 
      IF SHUTDOWN GR 2 THEN 
        BEGIN 
        USRBRK=1; 
        FORCENULL=TRUE; 
        END 
      GENWRKNAM;
      GOTO RESUME;             # TO RESUME JOB WHERE IT WAS  #
AFTERSLICE:              # CONTROL MANUALLY SENT HERE BY PAUSE   #
      GATHER(RSTKPTR,RENTSTK); # SAVE ALL SUBROUTINES        #
      MOVEWD(RSTKPTR-1,RENTSTK,DISPRSTK); 
      TASKSTATUS[CURTASK]=CHNGSTATUS; 
      TASKSTKPTR[CURTASK]=RSTKPTR;
      PUTUSER;
      END 
  
    END                         # OF POLLING LOOP   # 
  
  
  END                          # OF RUNJOBS        #
PAGE                         # INSTRUMENT -- COLLECT STATS #
  
  
CONTROL IFEQ METERING,1;
  PROC INSTRUMENT(TYPE,DATA,DATA2); 
    BEGIN 
# 
**        INSTRUMENT - TAKE STATISTICS. 
* 
*         INSTRUMENT, TOGETHER WITH ENTRY POINTS INSTRMNT1, 
*         INSTRMNT2, AND INSTRMNT3, COLLECTS STATISTICS REGARDING 
*         INTERNAL PERFORMANCE AND WORKLOAD CHARACTERISTICS.
* 
*         ENTRY  TYPE - SELECTS STATISTICAL CASE. 
*                DATA - ONE DATA ITEM, WHERE APPLICABLE.
*                DATA2 - ADDITIONAL DATA WHERE REQUIRED.
*                MAXLOGGED, LOGGEDIN, TIMEOFDAY, SLICECOUNT,
*                    BUILDCIO, ACCTTRANS, PALAST[0] - ADDITIONAL
*                    PARAMETERS FOR CERTAIN FUNCTION TYPES. 
* 
*         EXIT   STATWORD[ALL] - INCREMENTED OR SET.
*                DISTRSTM, DISTTKTM, DISTCICT, DISTTRCT, DISTFLSZ,
*                    DISTMGSZ - FREQUENCY DISTRIBUTIONS.
* 
*         CALLS  ADDVECT. 
# 
    ITEM DATA,DATA2,TYPE; 
  
    ENTRY PROC INSTRMNT1(TYPE); 
  
    ENTRY PROC INSTRMNT2(TYPE,DATA);
  
    ENTRY PROC INSTRMNT3(TYPE,DATA,DATA2);
  
    SWITCH INSTSW INSTTHNK,INSTRSPNS,INSTNOTTI, 
                  INSTNOTASK,INSTSWPE,INSTSWPD,INSTCLOCK, 
                  INSTPREEMPT,INSTSWPTIM,INSTEDTCIO,
                  INSTSESSION,INSTTRANS,INSTXSTTO,
                  INSTBREAK,INSTHANG,INSTBGNEDT,
                  INSTENDEDT,INSTINPUT,INSTXMIT,
                  INSTKWIK,INSTUTIL,INSTWORK,INSTDROP;
  
    GOTO INSTSW[TYPE];
  
INSTNOTTI:  
    STATWORD[1]=STATWORD[1]+1;
    RETURN; 
  
INSTNOTASK: 
    STATWORD[2]=STATWORD[2]+1;
    RETURN; 
  
INSTSESSION:  
    STATWORD[3]=STATWORD[3]+1;
    RETURN; 
  
INSTTRANS:  
    STATWORD[4]=STATWORD[4]+1;
    RETURN; 
  
INSTRSPNS:  
    STATWORD[5]=STATWORD[5]+DATA; 
    ADDVECT(DISTRSTM,DATA/16);
    RETURN; 
  
INSTTHNK: 
    STATWORD[6]=STATWORD[6]+DATA; 
    ADDVECT(DISTTKTM,DATA/1024);
    RETURN; 
  
INSTPREEMPT:               # WORDS 7, 8, 9  # 
    STATWORD[6+DATA]=STATWORD[6+DATA]+1;
    RETURN; 
  
INSTXSTTO:  
    STATWORD[10]=STATWORD[10]+1;
    RETURN; 
  
INSTBREAK:  
    STATWORD[11]=STATWORD[11]+1;
    RETURN; 
  
INSTHANG: 
    STATWORD[12]=STATWORD[12]+1;
    RETURN; 
  
INSTCLOCK:  
    STATWORD[13]=MAXLOGGED; 
    STATWORD[14]=LOGGEDIN;
    STATWORD[15]=TIMEOFDAY; 
    STATWORD[16]=SLICECOUNT;
    RETURN; 
  
INSTSWPE: 
    STATWORD[17]=STATWORD[17]+1;
    RETURN; 
  
INSTSWPD: 
    STATWORD[18]=STATWORD[18]+1;
    RETURN; 
  
INSTSWPTIM: 
    STATWORD[19]=STATWORD[19]+DATA; 
    RETURN; 
  
INSTEDTCIO: 
    STATWORD[20]=STATWORD[20]+DATA;   # CIO MSECS         # 
    STATWORD[21]=STATWORD[21]+DATA2;   # CIO COUNT         #
    IF DATA2 EQ 0 THEN STATWORD[22]=STATWORD[22]+1;  # NUM ZERO CIO  #
    IF DATA2 GQ 16 THEN 
      BEGIN 
      STATWORD[23]=STATWORD[23]+1;     # NUM CRUNCHERS     #
      STATWORD[24]=STATWORD[24]+DATA2; # CRUNCHY EFFORT    #
      END 
    ADDVECT(DISTCICT,DATA2);
    RETURN; 
  
INSTBGNEDT: 
    STATWORD[25]=STATWORD[25]+BUILDCIO;     # FILEBUILD CIOS    # 
    RETURN; 
  
INSTENDEDT: 
    ADDVECT(DISTTRCT,ACCTTRANS);
    ADDVECT(DISTFLSZ,PALAST[0]);
    RETURN; 
  
INSTINPUT:  
    STATWORD[26]=STATWORD[26]+DATA;    # NUM WORDS         #
    RETURN; 
  
INSTXMIT: 
    STATWORD[27]=STATWORD[27]+DATA;    # NUM WORDS         #
    STATWORD[28]=STATWORD[28]+DATA2;   # NUM BLOCKS        #
    ADDVECT(DISTMGSZ,DATA); 
    RETURN; 
  
INSTKWIK: 
    STATWORD[29]=STATWORD[29]+1;
    RETURN; 
  
INSTUTIL: 
    STATWORD[30]=STATWORD[30]+DATA; 
    RETURN; 
  
INSTWORK: 
    STATWORD[31+DATA]=STATWORD[31+DATA]+1;  # WORDS 31 THRU 41 #
    RETURN; 
  
INSTDROP: 
    STATWORD[42]=STATWORD[42]+1;
    RETURN; 
  
    END                        # OF INSTRUMENT     #
CONTROL FI; 
PAGE                         # USER-STATE CODE   #
                             # BASIC WAIT/INTERRUPT HOOKS  #
  
  
PROC SMFDLY;
  IOBEGIN(SMFDLY) 
# 
**        SMFDLY - INTERFACE FOR TASKS TO DELAY.
* 
*         EDITING CODE USE COMPILE-TIME MACROS TO REDEFINE THE
*         KEYWORD "DELAY" INTO A CALL TO "SMFDLY" FOR MULTI-USER
*         CODE.  SMFDLY INTERFACES TO THE PAUSE ROUTINE, WHICH IN 
*         TURN WORKS WITH RUNJOBS TO CONTROL THE TASK.
* 
*         ENTRY  NO CONDITIONS. 
* 
*         EXIT   AFTER DELAYING.
* 
*         CALLS  PAUSE. 
* 
*         USES   CHNGSTATUS.
# 
  CHNGSTATUS=TASKST"DELAY"; 
  PAUSE;
  IOEND                        # OF SMFDLY        # 
  
  
PROC SMFRCL(AFET);
  IOBEGIN(SMFRCL) 
# 
**        SMFRCL - INTERFACE FOR TASKS TO AUTO-RECALL ON AN EVENT.
* 
*         EDITING CODE USE COMPILE-TIME MACROS TO REDEFINE THE
*         KEYWORD "RECALL" INTO A CALL TO "SMFRCL" FOR MULTI-USER 
*         CODE.  SMFDLY INTERFACES TO THE PAUSE ROUTINE, WHICH IN 
*         TURN WORKS WITH RUNJOBS TO CONTROL THE TASK.
* 
*         NOTE THAT EDITING CODE IS NOT ALLOWED TO SPECIFY AUTO-RECALL
*         ON ANY RA+1 CALL.  INSTEAD, EDITING CODE MUST ISSUE 
*         RA+1 AND THEN CALL THIS ROUTINE TO RECALL UNTIL THE SYSTEM
*         FUNCTION IS COMPLETE. 
* 
*         ENTRY  AFET - COMPLETION WORD.
* 
*         EXIT   AFTER COMPLETION BIT IS FOUND TO BE ON.
* 
*         CALLS  PAUSE. 
* 
*         USES   CHNGSTATUS, TASKRCLADR[CURTASK]. 
# 
  ARRAY AFET;;
  TASKRCLADR[CURTASK]=LOC(AFET);
  CHNGSTATUS=TASKST"RECALLFET"; 
  PAUSE;
  IOEND                        # OF SMFRCL        # 
  
  
PROC CLEARINT;
  IOBEGIN(CLEARINT) 
# 
**        CLEARINT - ACKNOWLEGE USER BREAK. 
* 
*         EDITING CODE IS REQUIRED TO CALL THIS ROUTINE AS SOON AS
*         A USER BREAK HAS BEEN NOTICED, AND ALL EDITOR-SPECIFIC
*         RESPONSES HAVE BEEN PERFORMED.  CLEARINT CLEARS FLAGS 
*         INDICATING UNACKNOWLEGED INTERRUPT, SINCE ONLY EXECUTIVE
*         CODE CAN ACCESS SUCH FLAGS. 
* 
*         ENTRY  NO CONDITIONS. 
* 
*         EXIT   CURUSINTRPT - FALSE. 
*                BUFFERED OUTPUT PURGED.
*                TASKREQUE - FALSE UNLESS TASK RECOVERY IN EFFECT.
* 
*         CALLS  CLEARQUE.
# 
  IF CURUSINTRPT THEN TASKNXTTTO[CURTASK]=0;
  CLEARQUE; 
  CURUSINTRPT=FALSE;
  IOEND                        # OF CLEARINT       #
  
  
PROC CLEARQUE;
  BEGIN 
# 
**        CLEARQUE - ATTEMPT TO CLEAR TASKREQUE FLAG. 
* 
*         CLEARQUE CLEARS THE TASKREQUE FLAG UNLESS TASK RECOVERY IS
*         IN EFFECT.  TASKREQUE IS SET BY DOINPUT WHEN A USER NEEDS 
*         TO BE SCEDULED TO A TASK BUT IS ALREADY IN A TASK.  DOINPUT 
*         HAS NO WAY TO KNOW IF THE TASK ALREADY RUNNING WILL DETECT
*         OR IGNORE THE INPUT EVENT.  THUS, THE TASKREQUE FLAG IS 
*         SET.  IF THE EDITING CODE NEVER USES CLEARQUE TO SHOW 
*         ACKNOWLEGEMENT, THEN WHEN THE TASK SWAPS OUT THE RUNJOBS
*         ROUTINE WILL IMMEDIATELY PUT THE USER BACK IN THE SWAPIN
*         QUEUE.  IF THE EDITING CODE ACKNOWLEGES THE INPUT EVENT,
*         WHICH IS USUALLY A USER BREAK, THEN CLEARQUE GETS RID OF
*         THE RESCHEDULING AT END OF TRANSACTION. 
* 
*         THE TASKREQUE FLAG IS PRESERVED IF RECOVERY IS ON.
*         RECOVERY DEALS WITH JOB DETACHMENT AND SUBSYSTEM EXITS. 
* 
*         ENTRY  CURUSRCVRY - WHETHER RECOVERY IN EFFECT. 
*                TASKREQUE[CURTASK] - WHETHER RESCHEDULING PLANNED. 
* 
*         EXIT   TASKREQUE[CURTASK] - UPDATED.
# 
  TASKREQUE[CURTASK]=TASKREQUE[CURTASK] AND CURUSRCVRY; 
  END                          # OF CLEARQUE       #
  
  
PROC SPINWHEELS;
  IOBEGIN(SPINWHEELS) 
# 
**        SPINWHEELS - DELAY TASK UNTIL INPUT ARRIVES.
* 
*         SPINWHEELS ALLOWS A TASK TO HOLD ONTO ITS RESOURCES FOR 
*         BRIEF TIME INTERVALS IN EXPECTATION OF A USEFUL EXTERNAL
*         ARRIVING PROBABLY WITHIN A FRACTION OF A SECOND.  IF
*         RESOURCES ARE TIGHT WE TERMINATE THE WAIT SO THE CALLER CAN 
*         SWAP OUT. 
* 
*         ENTRY  TIMEOFDAY - CURRENT TIME.
*                TASKREQUE[CURTASK], TASKTIME[CURTASK], REMAINTTI,
*                    TASKAVAIL, TASKSBUSY - EVENT/RESOURCE DATA.
*                SPINTIME - TIME ALREADY SPENT SPINNING.
* 
*         EXIT   TIMEOFDAY - CURRENT TIME MAY HAVE ADVANCED.
*                SPINTIME - POSSIBLY INCREMENTED. 
* 
*         CALLS  OKTOWAIT(INTERNAL), SMFRCL.
* 
*         USES   SPINSTART, TASKPULSE[CURTASK]. 
# 
  
  FUNC OKTOWAIT B;
    BEGIN 
# 
**        OKTOWAIT - COMPUTE RULES FOR SPINWHEELS.
* 
*         OKTOWAIT DETERMINES THE EVENT/RESOURCE THRESHOLDS FOR THE 
*         SPINWHEELS ROUTINES.  A TASK MAY SPIN ITS WHEELS, HOLDING 
*         RESOURCES, UNTIL/UNLESS ANY OF THE FOLLOWING -- 
* 
*         1. THE DESIRED INPUT ARRIVES FROM IAFEX (TASKREQUE) 
* 
*         2. TWO SECONDS EXPIRE.
* 
*         3. ONE-HALF SECOND EXPIRES AND THERE ARE MORE USERS 
*            SCHEDULED FOR TASKS THAN TASKS LEFT TO TAKE THEM.
* 
*         4. REGARDLESS OF TIMING, LESS THAN HALF OF THE TERMINAL 
*            INPUT BUFFERS ARE LEFT OVER. (MOST CRITICAL RESOURCE)
* 
*         ENTRY AND EXIT - SEE OUTER PROCEDURE. 
# 
    OKTOWAIT=TRUE;
    IF TASKREQUE[CURTASK] OR TIMEOFDAY-TASKTIME[CURTASK]
      GR 2000 THEN OKTOWAIT=FALSE;
    IF NUMTTIBUF-REMAINTTI GR TASKSAVAIL-TASKSBUSY AND
      TIMEOFDAY-TASKTIME[CURTASK] GR 500 THEN OKTOWAIT=FALSE; 
    IF REMAINTTI LQ NUMTTIBUF/2 THEN OKTOWAIT=FALSE;
    END 
  
  
  # SPINWHEELS MAIN CODE STARTS HERE #
  
  SPINSTART=TIMEOFDAY;
  WHYLE OKTOWAIT DO 
    BEGIN 
    TASKPULSE[CURTASK]=2;          # SIMULATE BUSY FET #
    P<MOVTO>=LOC(TASKPULSE[CURTASK]); 
    SMFRCL(MOVTO);      # APPROX 05-HZ DELAY LOOP     # 
    END 
  SPINTIME=SPINTIME+TIMEOFDAY-SPINSTART;
  IOEND                       # OF SPINWHEELS     # 
PAGE                         # RELIABILITY/METERING HOOKS  #
  
  
PROC FATALTRAP; 
  IOBEGIN(FATALTRAP)
# 
**        FATALTRAP - INTERFACE FOR FAILING TASKS.
* 
*         FATALTRAP IS CALLED BY EDITING CODE WHICH DETECTS ITS OWN 
*         UNCORRECTABLE ALGORITHMIC INCONSISTENCY.  THIS INTERFACE
*         WAITS FOR ANY CIO CALL TO FINISH THEN BRANCHES DIRECTLY 
*         TO THE RUNJOBS EXECUTIVE. 
* 
*         ENTRY  FET - MAYBE ACTIVE.
* 
*         EXIT   FET - COMPLETE.
*                VIA AFTEREDIT LABEL. 
* 
*         CALLS  SMFRCL, AFTEREDIT. 
# 
  XREF LABEL AFTEREDIT; 
  SMFRCL(FET);        # WAIT IO DONE      # 
  GOTO AFTEREDIT;              # TERMINATE TASK    #
  END                          # OF FATALTRAP      #
  
  
PROC ABTPAUSE;
  IOBEGIN(ABTPAUSE) 
# 
**        ABTPAUSE - TRY TO CHECKPOINT WORKFILE AFTER ABORT.
* 
*         ABTPAUSE IS CALLED AS EDITING CODE BY THE RUNJOBS EXECUTIVE 
*         WHEN A TASK HAS ABORTED ITSELF.  THE INTENT IS TO ATTEMPT 
*         TO CHECKPOINT THE WORKFILE BEFORE TRANSITIONING BACK TO 
*         SINGLE-USER.  IF THIS CAN BE DONE, THEN THE SINGLE EDITOR 
*         WILL DETECT THE ABORTED FLAG IN THE DATA SEGMENT OF THE 
*         WORKFILE AND WILL KNOW THAT IT CANNOT PROCEED.  ABTPAUSE
*         MAY NOT SUCCEED, AND RUNJOBS IS RESPONSIBLE TO KEEP TRACK 
*         OF ABTPAUSE FAILURE AND INSTEAD CALL ABTKILL. 
* 
*         CALLS  PAUSEIO. 
# 
  PAUSEIO;
  IOEND                        # OF ABTPAUSE       #
  
  
PROC ABTKILL; 
  IOBEGIN(ABTKILL)
# 
**        ABTKILL - KILL OFF TASK AFTER DOUBLE ABORT. 
* 
*         THE RUNJOBS EXECUTIVE CALLS ABTKILL AS A LAST RESORT WHEN A 
*         TASK HAS DELIBERATELY ABORTED ITSELF, THEN HAS ABORTED
*         ITSELF A SECOND TIME WHEN ABTPAUSE ATTEMPTED TO CHECKPOINT
*         THE WORKFILE.  ABTKILL ATTEMPTS TO ZAP THE WORKFILE TO
*         CONTAIN EXACTLY ONE WORD OF ZEROES, WHICH WOULD NOT BE
*         ACCEPTED AS A VALID TRANSITION ONCE THE SINGLE-USER EDITOR
*         GETS BACK IN CONTROL.  THE INTENT IS TO BE AS CERTAIN AS
*         POSSIBLE THAT THE SINGLE EDITOR WILL NOT PICK UP AND
*         CONTINUE A HOPELESS EDIT SESSION. 
* 
*         CALLS  SMFRCL, WRITEO, REWRITR, SMFRCL. 
* 
*         USES   FET. 
# 
  SMFRCL(FET);
  FETRR=1;
  WRITEO(FET,0);
  REWRITR(FET,0); 
  SMFRCL(FET);
  IOEND                      # OF ABTKILL # 
  
  
CONTROL IFEQ METERING,1;
  
  PROC BGNMETER;
    BEGIN 
# 
**        BGNMETER - INSTRUMENT BEGINNING OF EDIT SESSION.
* 
*         BGNMETER ALLOWS THE SINGLE-USER EDITOR TO PASS DATA FROM
*         ITS FILE-BUILD PROCESS, INTO THE CORRESPONDING TASK IN
*         THE MULTI-USER EDITOR, WHICH CAN USE THIS INTERFACE TO
*         GLOBALLY RECORD THE DATA. 
* 
*         CALLS  INSTRMNT1. 
# 
    INSTRMNT1(INSTST"BGNEDT");
    END 
  
  
  PROC WIOSTAT(ORDINAL);
    BEGIN 
# 
**        WIOSTAT - WORKIO STATISTICS INTERFACE.
* 
*         WIOSTAT IS CALLED BY THE WORKFILE MANAGER AT NOTEWORTHY 
*         EVENTS SO THAT STATISTICS CAN BE INCREMENTED. 
* 
*         ENTRY  ORDINAL - WHICH ACCUMULATOR TO INCREMENT.
* 
*         CALLS  INSTRMNT2. 
# 
    ITEM ORDINAL; 
    INSTRMNT2(INSTST"WORKIO",ORDINAL);
    END 
  
CONTROL FI; 
PAGE                         # FNT MANAGEMENT    #
  
  
PROC RTRNWRKFIL;
  IOBEGIN(RTRNWRKFIL) 
# 
**        RTRNWRKFIL - RETURN THE WORKFILE FNT. 
* 
*         BEFORE SMFEX CAN TRANSITION A USER BACK TO THE SINGLE 
*         USER EDITOR, IT MUST GET RID OF ITS LOCAL FNT FOR THE 
*         USERS WORKFILE.  RTRNWRKFIL DOES THIS AND IS
*         DESIGNED TO BE CALLED BY RUNJOBS IN USER STATE JUST AFTER 
*         THE TASK HAS TERMINATED AT THE AFTEREDIT LABEL. 
* 
*         ENTRY  FET - SETUP. 
* 
*         EXIT   CURUSDONE - TRUE.
* 
*         CALLS  RETERN, SMFRCL.
# 
  RETERN(FET,0);
  CURUSDONE=TRUE;  # ASSURE NO RECOVERY          #
  SMFRCL(FET);
  
  IOEND                       # OF RTRNWRKFIL     # 
  
  
PAGE                         # LOGIN/LOGOUT CONTROL        #
  
  
PROC SENDLOGOUT;
  IOBEGIN(SENDLOGOUT) 
# 
**        SENDLOGOUT - TRANSMIT LAST OUTPUT AND ROLLIN SINGLE.
* 
*         SENDLOGOUT PERFORMS THE LAST PROCESSING WHEN A TASK 
*         COMPLETES ALL MULTI-USER PROCESSING.  SENDLOGOUT SHOULD 
*         BE CALLED ONLY BY RUNJOBS AFTER THE TASK HAS EXITED 
*         AT LABEL AFTEREDIT AND AFTER THE WORKFILE LOCAL FNT HAS 
*         BEEN RETURNED.  SENDLOGOUT ASSURES THAT ANY OUTPUT LEFT 
*         IN OUR BUFFERS IS SEND ON TO IAF, THEN TELLS IAF THE
*         USER IS LEAVING MULTI-USER MODE, THEN ISSUES AN EESET 
*         FUNCTION TO CAUSE ROLLIN OF THE SINGLE USER JOB.
* 
*         THE TLX FUNCTION USED BY THE SINGLE USER JOB TO ROLLOUT 
*         WILL BE RECALLED TO VERIFY THAT SMFEX HAS PROPERLY
*         DISPENSED WITH THE WORKFILE.  TLX THEN CONFORMS TO IAF
*         THAT THE MULTI-USER SESISON IS OVER.
* 
*         ENTRY  TASKNXTTTO[CURTASK] - INDICATES RESIDUAL OUTPUT. 
* 
*         EXIT   TASKNXTTTO[CURTASK] - ZERO.
* 
*         CALLS  TTOTRAP, TRANSMIT, EESET.
* 
*         USES   TTOADDR, TTOLEN, TTOTYPE.
# 
  IF TASKNXTTTO[CURTASK] NQ 0 THEN
    BEGIN 
    TTOADDR=0;
    TTOLEN=0; 
    TTOTYPE=TTOST"FORCE"; 
    TTOTRAP;
    END 
  TASKTYPTTO[CURTASK]=SMF2IAF"LOGOUT";
  TRANSMIT; 
  EESET((TTEQ*4096)+USEREJT[CURUSER]);
  
  IOEND                        # OF SENDLOGOUT     #
PAGE                         # TRANSMIT TO IAFEX #
  
  
PROC TRANSMIT;
  IOBEGIN(TRANSMIT) 
# 
**        TRANSMIT - TRANSMIT A FUNCTION CODE/MESSAGE TO IAFEX. 
* 
*         TRANSMIT IS USED FOR THE ISSUANCE OF ALL CODES TO IAFEX 
*         EXCEPT FOR THE SPECIAL CASE WHERE DOINPUT NEEDS TO
*         DISCONNECT A TERMINAL IN SERIALIZED OPERATION FOR 
*         OVERFLOWED TABLES.  TRANSMIT EXECUTES IN USER MODE, THUS
*         THE EXECUTIVE HAS NO WAY TO SEND MESSAGES TO IAFEX EXCEPT 
*         FOR THE CASE ALREADY MENTIONED. 
* 
*         EACH TASK HAS ITS OWN OUTPUT BUFFER WHICH IS USED TO
*         ACCUMULATE AND BATCH OUTPUT TEXT TOGETHER TO MINIMIZE CALLS 
*         TO TRANSMIT.  ONCE TRANSMIT IS CALLED, IT USES THE SCP/UCP
*         FACILITY TO SEND THE TASK'S BUFFER TO IAF.  IF THE SCP/UCP
*         FUNCTIONS INDICATE THAT THE MESSAGE COULD NOT BE SENT TO
*         IAF ON THIS RECALL CYCLE, THEN THE TASK IS DELAYED
*         (ALLOWING OTHER TASKS TO EXECUTE IN PARALLEL) AND THE 
*         SCP/UCP OPERATION WILL BE TRIED AGAIN.
* 
*         ENTRY  TASKTYPTTO[CURTASK] - FUNCTION CODE FOR IAFEX. 
*                TASKNXTTO[CURTASK] - AMOUNT OF TEXT TO GO WITH 
*                    FUNCTION CODE. 
*                CURUSIAFNUM - IAFEX TERMINAL NUMBER. 
*                TTOWORDS, TTOBLOCKS - STATISTICAL ACCUMULATORS.
*                CURUSLASTIM, TIMEOFDAY - ALSO FOR STATISTICS.
*                CURUSRCVRY, CURUSINTRPT - INDICATE PURGE BUFFER. 
* 
*         EXIT   TTOWORDS, TTOBLOCKS - INCREMENTED. 
*                CURUSLASTIM, TIMEOFDAY - UP TO DATE. 
* 
*         CALLS  MELT, SYSTEM, INSTRMNT2, SMFDLY. 
* 
*         USES   IAFHEADER, P<SSCBUFFER>. 
# 
  ITEM TMP1;
  
  TRCSTR("NTR TRSMT$"); 
  CONTROL IFEQ TRACEFLAG,1; 
    TMP1=TASKNXTTTO[CURTASK]; 
    TRACEBOTH("NXTTTO$",TMP1);
    TMP1=TASKTYPTTO[CURTASK]; 
    TRACEBOTH("TYPTTO$",TMP1);
  CONTROL FI; 
  
  CONTROL IFGQ PARANOIA,2;
    IF TASKNXTTTO[CURTASK] GR SIZTTOBUF THEN MELT("TRANSMIT 1$"); 
    IF CURUSIAFNUM GR MAXCONNECT THEN MELT("TRANSMIT 2$");
  CONTROL FI; 
  
  IF CURUSINTRPT OR CURUSRCVRY THEN 
    BEGIN 
    TASKNXTTTO[CURTASK]=0;
    IF SHUTDOWN EQ 2 OR TASKTYPTTO[CURTASK] EQ SMF2IAF"MSGOUT" OR 
      TASKTYPTTO[CURTASK] EQ SMF2IAF"PROMPT" THEN 
      BEGIN 
      TASKTYPTTO[CURTASK]=0;
      IORET 
      END 
    END 
  
  IAFFUNC=TASKTYPTTO[CURTASK];
  IAFLEN=TASKNXTTTO[CURTASK]+1; 
  IAFPTR=0; 
  IAFTERM=CURUSIAFNUM;
  TTOBUFF[1]=IAFHEADER; 
  CONTROL IFEQ METERING,1;
    TTOWORDS=TTOWORDS+IAFLEN; 
    TTOBLOCKS=TTOBLOCKS+1;
  CONTROL FI; 
  
TRYTOSEND:                   # BACK HERE IF RETRY NEEDED   #
  
  TTOBUFF[0]=0; 
  P<SSCBUFFER>=LOC(TTOBUFFER);
  SSCTYPE=O"3"; 
  SSCCOUNT=IAFLEN;
  SYSREQ("SSC",0,LOC(SSCBUFFER),IAFSSID); 
  IF SSCSTATUS LAN O"7776" EQ 0 THEN   # NO ERROR          #
    BEGIN 
    CONTROL IFEQ TRACEFLAG,1; 
      ITEM TMP2,TMP3; 
      IAFHEADER=TTOBUFF[1];    # SINCE MAY HAVE LOST CTL     #
      TMP1=IAFFUNC; 
      TMP2=IAFTERM; 
      TMP3=IAFLEN-1;
      P<MOVFROM>=LOC(TTOBUFF[2]); 
      TRACEBOTH("FUNCOUT$",TMP1); 
      TRACEDEC(TMP2); 
      IF IAFLEN GR 1 THEN TRACEWORDS(MOVFROM,TMP3); 
    CONTROL FI; 
    CONTROL IFEQ METERING,1;
      IF NOT CURUSOLDSND THEN 
        BEGIN 
        TMP1=TIMEOFDAY-CURUSLASTIM; 
        INSTRMNT2(INSTST"RSPNS",TMP1);
        END 
    CONTROL FI; 
    IF TASKTYPTTO[CURTASK] EQ SMF2IAF"MSGOUT" THEN CURUSOLDSND=TRUE;
    ELSE CURUSOLDSND=FALSE; 
    CURUSLASTIM=TIMEOFDAY;
    IORET 
    END 
  ELSE IF SSCSTATUS LAN O"7776" EQ 4 THEN   # IAF BUSY NOW      # 
    BEGIN 
    SMFDLY; 
    GOTO TRYTOSEND; 
    END 
  ELSE MELT("CANT TALK TO IAFEX$"); 
  
  IOEND                        # OF TRANSMIT       #
PAGE                         # VOLUNTEER TO SURRENDER TASK #
  
  
PROC VOLUNTEER; 
  IOBEGIN(VOLUNTEER)
# 
**        VOLUNTEER - SEE IF THIS TASK SHOULD SLOW DOWN.
* 
*         VOLUNTEER IS USED TO PREVENT A TASK FROM EXCESSIVE RESOURCE 
*         USAGE.  EDITING CODE SHOULD CALL VOLUNTEER PERIODICALLY 
*         INSIDE ANY PROCESS WHICH IS POTENTIALLY LONG-RUNNING.  NOTE 
*         THAT ANY CODE IN THE WORKFILE MANAGER (THE WORKIO MODULE) 
*         IS A "MUST COMPLETE" FUNCTION - THAT MEANS THAT VOLUNTEER 
*         CAN BE CALLED BY ACTUAL EDITING CODE BUT CANNOT BE CALLED 
*         FROM WORKIO ITSELF. 
* 
*         VOLUNTEER FIRST CONSIDERS WHETHER THE TASK IS USING 
*         EXCESSIVE CPU TIME ON AN INSTANTANEOUS BASIS.  THE
*         OPERATING SYSTEM REAL TIME MILLISECOND CLOCK IS FRESHLY 
*         ACCESSED, AND THE CURRENT TIME IS COMPARED TO THE TIME
*         TAKEN AT THE START OF THE RECALL CYCLE TO SEE IF MORE THAN
*         A FEW MILLISECONDS OF CPU HAVE BEEN USED.  A CALCULATION
*         ATTEMPTS TO SELECT A THRESHOLD SUCH THAT SMFEX WILL NEVER 
*         USE MORE THAN ABOUT HALF OF THE CPU.  THIS MEANS THAT FOR 
*         AN INSTALLATION WHICH INTENDS THAT SUBSTANTIALLY ALL OF THE 
*         CPU SHOULD BE USED FOR TEXT EDITING, SMFEX MAY BE 
*         UNSUITABLE AND THE SINGLE-USER EDITORS MAY BE BETTER. 
* 
*         IF THE TASK IS CURRENTLY USING MORE THAN A FEW MILLISECONDS 
*         OF CPU, IT DELAYS, AND THE REMAINDER OF THE VOLUNTEER 
*         ALGORITHM WILL CONTINUE ON A LATER RECALL CYCLE.
* 
*         THE REMAINDER OF THE VOLUNTEER ALGORITHM CONSIDERS WHETHER
*         A TASK HAS RESIDED IN ITS SWAPPED-IN MEMORY IMAGE FOR TOO 
*         MUCH REAL TIME.  WE CALCULATE THE REAL TIME FROM THE TIME 
*         AT WHICH THE TASK SWAPPED IN UNTIL THE PRESENT TIME.  IF
*         THE TIME DELTA EXCEEDS THE "VOLTIME" THRESHOLD AND THERE IS 
*         QUEUED TERMINAL OUTPUT, THE TASK WILL SWAP OUT SO THE 
*         OUTPUT CAN BE SENT.  IF THE TIME DELTA EXCEEDS THE
*         THRESHOLD AND THERE ARE MORE USERS AWAITING SWAPIN THAN THE 
*         NUMBER OF TASKS FREE TO SWAP THEM IN, THEN THIS TASK WILL 
*         SWAPOUT SO ANOTHER USER CAN SWAPIN.  IF THE NUMBER OF 
*         TERMINAL INPUT BUFFERS IS DOWN TO APPROXIMATELY ONE FOURTH
*         OF THE TOTAL, THEN THIS TASK WILL SWAP OUT REGARDLESS OF
*         WHETHER WE HAVE EXCEEDED THE THRESHOLD. 
* 
*         THUS, THE "VOLTIME" THRESHOLD REPRESENTS THE AMOUNT OF TIME 
*         A TASK IS ASSURED THAT IT CAN REMAIN SWAPPED IN.  IF THE
*         TASK IS NOT PRODUCING OUTPUT AND THERE IS NO EXCESS QUEUE 
*         FOR SWAPIN, THE TASK CAN EXCEED THE TRESHOLD.  BUT IF THERE 
*         IS A CRITICAL RESOURCE SHORTAGE (INPUT BUFFERS) THEN ALL
*         BETS ARE OFF AND WE SWAP THIS TASK AS SOON AS IT HAS
*         VOLUNTEERED, WITHOUT ALLOWING THE NORMAL GUARANTEED TIME. 
* 
*         ENTRY  TIMEOFDAY - TIME AT WHICH THIS RECALL CYCLE STARTED. 
*                OLDTIMEDAY - TIME FOR START OF PREVIOUS RECALL CYCLE,
*                    OR TIME OF VOLUNTEER EXECUTION WITHIN PREVIOUS 
*                    RECALL CYCLE IF VOLUNTEER WAS CALLED.
*                TASKTIME[CURTASK] - WHEN THIS TASK SWAPPED IN. 
*                TASKNXTTTO[CURTASK] - WHETHER OUTPUT IS PENDING. 
*                TASKSAVAIL - TOTAL USABLE TASKS. 
*                TASKSBUSY - NUMBER OF TASKS USED NOW.
*                SWPQSIZE - NUMBER OF USERS AWAITING SWAPIN.
*                REMAINTTI - NUMBER OF UNUSED INPUT BUFFERS.
* 
*         EXIT   TIMEOFDAY - UP TO DATE.
*                TASKNXTTTO[CURTASK] - CLEARED IF SWAPPED.
*                TASKTIME[CURTASK] - NEW VALUE IF SWAPPED.
*                CURUSCRUNCH - TRUE IF SWAP FOR OVERTIME BUT NO OUTPUT. 
*                TASKREQUE[CURTASK] - TRUE IF SWAP NOT FOR OUTPUT.
* 
*         CALLS  RTIME, MIN, MAX, SMFDLY, INSTRMNT1, INSTRMNT2, 
*                TTSYNC, TTOTRAP. 
* 
*         USES   TTOADDR, TTOLEN, TTOTYPE.
# 
  ITEM DELTA, SURRENDER;
  
  RTIME(NEWTIMEDAY);
  NEWTIMEDAY=B<24,36>NEWTIMEDAY;
  DELTA=MAX(50,MIN(10,(TIMEOFDAY-OLDTIMEDAY)/2)); 
  IF NEWTIMEDAY-TIMEOFDAY GR DELTA THEN 
    BEGIN 
    SMFDLY; 
    CONTROL IFEQ METERING,1;
      INSTRMNT1(INSTST"DROPCPU"); 
    CONTROL FI; 
    END 
  
  # NEXT CODE IS NON-REENTRANT, MUST RUN INSTANTANEOUSLY #
  
  DELTA=TIMEOFDAY-TASKTIME[CURTASK];
  SURRENDER=0;
  
  # TRY TO MOVE IF SITTING ON TYPABLE OUTPUT FOR A LONG TIME #
  IF TASKNXTTTO[CURTASK] NQ 0 AND DELTA GQ VOLTIME*4
    THEN SURRENDER=1; 
  
  # ALSO TRY TO MOVE IF SHORT ON TASKS FOR A SHORT TIME # 
  IF TASKSAVAIL-TASKSBUSY LS SWPQSIZE AND DELTA GQ VOLTIME
    THEN SURRENDER=2; 
  
  # MOVE IF CRITICAL RESOURCE SHORTAGE REGARDLESS OF TIME # 
  IF REMAINTTI LQ (NUMTTIBUF+3)/4 
    THEN SURRENDER=3; 
  
  # END OF INSTANTANEOUS ALGORITHM #
  
  IF SURRENDER NQ 0 THEN       # FACE WRITING ON WALL        #
    BEGIN 
    TRCBOTH("PREEMPT$",CURUSER);
    CONTROL IFEQ METERING,1;
      INSTRMNT2(INSTST"PREEMPT",SURRENDER); 
    CONTROL FI; 
    IF SCREENMODE THEN
      BEGIN                          # IF IN SCREEN MODE #
      VDSYNCH;                       # MUST PRECEED TASKNXTTTO CHECK #
      END                            # SINCE OUTPUT MAY BE FLUSHED #
    ELSE
      BEGIN                          # LINE MODE #
      TTSYNC;                        # MUST PRECEED TASKNXTTTO CHECK #
      END                            # SINCE OUTPUT MAY BE FLUSHED #
    IF TASKNXTTTO[CURTASK] EQ 0 THEN
      BEGIN                          # IF NO OUTPUT QUEUED FOR USER # 
      CURUSCRUNCH=TRUE;              # NO OUTPUT, JUST CRUNCHING #
      TASKREQUE[CURTASK]=TRUE;       # SHOULD BE REQUEUED # 
      END 
    TTOADDR=0;
    TTOLEN=0; 
    TTOTYPE=TTOST"FORCE"; 
    TTOTRAP;
    END 
  
  IOEND                        # OF VOLUNTEER      #
PAGE                         # TTY INPUT HOOK    #
  
  
PROC TTITRAP; 
  IOBEGIN(TTITRAP)
# 
**        TTITRAP - PROVIDE EDITOR CODE WITH INPUT TEXT.
* 
*         TTITRAP IS CALLED BY THE EDITOR IN LIEU OF CIO READ 
*         FUNCTIONS AGAINST AN INPUT FET.  CALLING TTITRAP INSTEAD OF 
*         CIO IS DONE BY CONDITIONAL COMPILATION OF CODE IN THE 
*         TERMIO MODULE.  THE EDITOR IS NOT ALLOWED TO OBTAIN INPUT 
*         BY ANY MEANS EXCEPT TO CALL TERMIO AND LET IT DECIDE WHICH
*         MECHANISM IS APPROPRIATE FOR THE VERSION OF THE EDITOR. 
* 
*         IN ADDITION TO PROVIDING INPUT, TTITRAP ALSO PROVIDES A 
*         MEANS TO PURGE ANY UNUSED INPUT FROM THE BUFFERS CHAINS.
*         THIS IS INDICATED BY A NEGATIVE VALUE IN THE TTILEN 
*         PARAMETER.
* 
*         WHEN INPUT IS DESIRED, TERMIO USES A POSITIVE VALUE IN THE
*         TTILEN PARAMETER TO INDICATE THE MAXIMUM NUMBER OF WORDS IT 
*         CAN ACCEPT.  THE WORKING BUFFER IS POINTED TO BY THE
*         TTIADDR PARAMETER.  THE TTINEXT PARAMETER IS MAINTAINED BY
*         THIS ROUTINE TO KEEP TRACK OF WHAT PORTIONS OF THE CURRENT
*         BUFFER CHAIN HAVE ALREADY BEEN TRANSMITTED. 
* 
*         INPUT FROM IAFEX IS MAINTAINED BY SMFEX, UNTIL TTITRAP IS 
*         READY TO PASS ON TO THE EDITING CODE, IN A CHAIN OF ONE OR
*         MORE TERMINAL INPUT BUFFERS.  ONE BUFFER IS CHAINED FOR 
*         EACH MESSAGE FRAGMENT SENT BY IAFEX.  TTITRAP MAINTAINS THE 
*         TTILAST PARAMETER, WHICH IS THE TEXT OF THE LAST WORD 
*         PASSED TO TERMIO, TO KNOW WHETHER THE PREVIOUS TTITRAP
*         CYCLE ENCOUNTERED A ZERO BYTE TERMINATOR.  THIS ENABLES 
*         TTITRAP TO KNOW WHETHER THE EDITING TASK IS IN THE MIDDLE 
*         OF A SERIES OF FRAGMENTS OR IS AWAITING THE FIRST FRAGMENT
*         OF A MESSAGE. 
* 
*         IF TTITRAP DETERMINES THAT THE TASK IS IN THE MIDDLE OF A 
*         SERIES OF FRAGMENTS, BUT DOES NOT YET HAVE THE NEXT 
*         FRAGMENT FROM IAFEX, TTITRAP WILL ATTEMPT TO KEEP THE TASK
*         DELAYING IN MEMORY, USING THE SPINWHEELS FUNCTION.  THIS IS 
*         DONE BECAUSE THERE IS AN EXCELLENT PROBABILITY THAT THE 
*         NEXT INPUT FRAGMENT WILL ARRIVE FROM IAFEX WITHIN A FEW 
*         MILLISECONDS.  ON THE OTHER HAND, WHEN THE EDITING TASK 
*         ASKS FOR THE FIRST FRAGMENT OF A MESSAGE AND NO TEXT HAS
*         ARRIVED YET FROM IAFEX, IT IS LIKELY THAT SEVERAL SECONDS 
*         WILL PASS BEFORE THE INPUT ARRIVES, SO THAT TASK IS ALLOWED 
*         TO SWAPOUT. 
* 
*         ONCE THE TASK ADVANCES THRU TTITRAP BEYOND ANY WHEELSPIN OR 
*         ANY SWAPOUT, TTITRAP ASSEMBLES AS MUCH INPUT TEXT AS IT 
*         CAN, UNTIL EITHER THE TTILEN PARAMETER IS EXHAUSTED OR THE
*         END OF ONE BUFFER IS REACHED.  TEXT FROM THE NEXT BUFFER OF 
*         A CHAIN OF SEVERAL FRAGMENTS CANNOT BE PROCESSED UNTIL THE
*         NEXT CALL TO TTITRAP.  THIS MEANS THAT THE SIZE OF EACH 
*         FRAGMENT ALLOWED BY IAFEX AND SMFEX MUST BE ADEQUATE FOR
*         COMPLETE INPUTS WHEN THE EDITOR IS USED IN LINE MODE.  THIS 
*         RESTRICTION IS IN EFFECT BECAUSE THE LINE EDITOR REQUESTS 
*         INPUT IN COMPLETE LINE IMAGES.  THE SCREEN EDITOR, HOWEVER, 
*         ASKS FOR INPUT ONE WORD AT A TIME, SO MULTIPLE FRAGMENTS
*         CAN BE PROCESSED WITH MULTIPLE TTITRAP CALLS. 
* 
*         WHEN TTITRAP DECIDES TO SWAP THE TASK OUT, IT CALLS PAUSEIO 
*         IN THE WORKFILE MANAGER.  THIS IS DONE BECAUSE WORKIO HOLDS 
*         SOME TRANSIENT RESOURCES, SUCH AS ITS CIRCULAR BUFFER, AND
*         NEEDS THE OPPORTUNITY TO COMPLETE ITS USAGE OF THESE
*         RESOURCES.
* 
* 
*         ENTRY  TTILEN - NEGATIVE MEANS PURGE UNUSED INPUT.
*                         POSITIVE IS TASKS CAPACITY FOR TEXT.
*                TTIADDR - WHERE THE TEXT SHOULD GO.
*                CURUSTTIBUF - FIRST BUFFER LINKAGE.
*                TTINEXT - WHERE TTITRAP PREVIOUS EXTRACTED TEXT. 
*                TTILAST - LAST WORD OF TEXT FROM PREVIOUS TTITRAP. 
*                RUNTIME - FOR STATISTICS.
*                TIMEOFDAY, TASKTIME[CURTASK] - FOR STATISTICS. 
*                ACCTCIO, ACCTTRANS - STATISTICS/ACCOUNTING.
*                TTOWORDS, TTOBLOCKS - FOR STATISTICS.
*                TTIBUFADDR[ALL] - SETUP. 
*                TTIBUFLNK[ALL] - LINKAGES. 
*                TTIBUFLEN[ALL] - FRAGMENT SIZES. 
*                TTIBITS - ALLOCATION BITMASK FOR BUFFERS.
* 
*         EXIT   TTINEXT - INCREMENTED. 
*                CURUSTTIBUF - ADVANCED/CLEARED VIA LINKAGE.
*                TIMEOFDAY - UP TO DATE.
*                TASKTIME[CURTASK] - TIME OF SWAPOUT IF ANY.
*                RUNTIME, ACCTCIO, ACCTTRANS - INCREMENTED. 
*                SPINTIME, RUNTIME, CIOCOUNT - CLEARED IF SWAPPED.
*                CURUSOKOUT - TRUE. 
*                TTILEN - DECREMENTED.
*                TTILAST - UPDATED. 
*                REMAINTTI - INCREMENTED IF BUFFER EMPTIED. 
*                TTOWORDS, TTOBLOCKS - CLEARED IF SWAPPED.
* 
*         CALLS  PURGETTI, SPINWHEELS, CLEARQUE, PAUSEIO, INSTRMNT1,
*                INSTRMNT2, INSTRMNT3, TTOTRAP, MIN, MOVEWD, DEALLOC. 
* 
*         USES   TTOLEN, TTOTYPE. 
# 
  ITEM TMP1, TMP2, TMP3;
  ITEM QUESTION1  I=O"7100 0013 0000 0000 0000";  # PROMPT ONE #
  ITEM QUESTION2  I=O"7155 0013 0000 0000 0000";  # PROMPT TWO #
  ITEM XPTINCTL   I=O"0006 4704 0015 0000 0000";  # XPARENT INPUT # 
  
  TRCSTR("NTR ITRP$");
  
  IF TTILEN LS 0 THEN          # PURGE ANY BUFFERS #
    BEGIN 
    IF CURUSTTIBUF NQ 0 THEN PURGETTI;
    TTINEXT=0;
    IORET 
    END 
  
  IF CURUSTTIBUF EQ 0 AND B<48,12>TTILAST NQ 0 THEN 
    BEGIN                    # DELAY SINCE MORE INPUT DUE ASAP       #
    SPINWHEELS;              # DELAY TILL INPUT ARRIVES OR TIMEOUT   #
    CLEARQUE; 
    END 
  
  IF NOT CURUSOKOUT THEN             # FIRST TRANSACTION #
    BEGIN 
    CURUSOKOUT=TRUE;
    IF SCREENMODE THEN
      BEGIN 
      ROWPAINT[COMMANDROW]=TRUE;
      END 
    ELSE
      BEGIN 
      TTOADDR=LOC(QUESTION1); 
      TTOLEN=1; 
      TTOTYPE=TTOST"NORMAL";
      TTOTRAP;
      END 
    END 
  
  IF CURUSTTIBUF EQ 0 THEN   # NORMAL TRANSACTION SO SWAPOUT         #
    BEGIN 
    PAUSEIO;                   # LET WORKIO PP'S DROP        #
    RUNTIME=TIMEOFDAY-TASKTIME[CURTASK]+RUNTIME;
    TASKTIME[CURTASK]=TIMEOFDAY;
    ACCTCIO=ACCTCIO+CIOCOUNT; 
    ACCTTRANS=ACCTTRANS+1;
    CONTROL IFEQ METERING,1;
      INSTRMNT2(INSTST"TASKUTIL",RUNTIME);
      INSTRMNT3(INSTST"EDTCIO",RUNTIME-SPINTIME,CIOCOUNT);
      INSTRMNT1(INSTST"TRANS"); 
    CONTROL FI; 
    IF NOT CURUSINTRPT THEN 
      BEGIN                     # NO FRESH BREAK, CAN PROMPT  # 
      TTOADDR=LOC(QUESTION2); 
      TTOLEN=1; 
      IF SCREENMODE THEN             # IF SCREEN MODE # 
        BEGIN 
        IF TABTYPHEAD[0] THEN TTOLEN=0;  # IF TYPEAHEAD USED #
        ELSE TTOADDR=LOC(XPTINCTL);  # NO TYPEAHEAD # 
        END 
      TTOTYPE=TTOST"PROMPT";
      TTOTRAP;
      END 
    SPINTIME=0; 
    RUNTIME=0;
    CIOCOUNT=0; 
    CONTROL IFEQ METERING,1;
      INSTRMNT3(INSTST"XMIT",TTOWORDS,TTOBLOCKS); 
      TTOBLOCKS=0;
      TTOWORDS=0; 
    CONTROL FI; 
    END 
                             # NOW WE KNOW WE HAVE SOMETHING         #
  
  MEM[TTIADDR]=0;            # ASSURE DEFAULT NULL LINE    #
  WHYLE CURUSTTIBUF NQ 0 AND TTILEN GR 0 DO 
    BEGIN 
    TMP1=CURUSTTIBUF; 
    P<MOVFROM>=TTIBUFADDR[TMP1]+TTINEXT;
    P<MOVTO>=TTIADDR; 
    TMP2=TTIBUFLEN[TMP1]-TTINEXT; 
    TMP3=MIN(TMP2,TTILEN);
    MOVEWD(TMP3,MOVFROM,MOVTO); 
    TTINEXT=TTINEXT+TMP3;    # INCREMENT SOURCE ADDR       #
    TTILEN=TTILEN-TMP3;      # DECREMENT CALLERS COUNT     #
    TTILAST=MEM[LOC(MOVFROM)+TMP3-1];    # SHOWS WHETHER EOL SEEN    #
    IF TTINEXT GQ TTIBUFLEN[TMP1] THEN
      BEGIN                     # RELEASE THIS BUFFER, POINT NEXT    #
      TTINEXT=0;
      DEALLOC(TTIBITS,TMP1,NUMTTIBIT);
      REMAINTTI=REMAINTTI+1;
      CURUSTTIBUF=TTIBUFLNK[TMP1];
      END 
    END 
  
  IOEND                        # OF TTITRAP        #
PAGE                         # TTY OUTPUT AND SWAPOUT HOOK #
  
  
PROC TTOTRAP; 
  IOBEGIN(TTOTRAP)
# 
**        TTOTRAP - QUEUE/ISSUE OUTPUT, SWAP AS NEEDED. 
* 
*         TTOTRAP IS CALLED BY EDITOR CODE IN LIEU OF CIO WRITE 
*         FUNCTIONS AGAINST AN OUTPUT FET.  EDITING CODE IS REQUIRED
*         TO INTERFACE ALL OUTPUT THRU THE TERMIO MODULE, WHICH USES
*         CONDITIONAL COMPILATION TO SELECT THE RIGHT TECHNIQUE OF
*         OUTPUT FOR THE VERSION OF THE EDITOR. 
* 
*         TTOTRAP IS ALSO USED BY THE VOLUNTEER AND TTITRAP ROUTINES
*         OF SMFEX.  TTOTRAP PROVIDES SWAPOUT LOGIC FOR THOSE CALLERS 
*         TOGETHER WITH ISSUANCE OF RESIDUAL OUTPUT.
* 
*         TTOTRAP IS CONTROLLED PRIMARILY BY THE TTOTYPE PARAMETER. 
*         THE "NORMAL" VALUE INDICATES THAT THE CALLER'S INTENT IS TO 
*         OFFER SOME OUTPUT TEXT.  TTOTRAP QUEUES THIS TEXT INTO THE
*         TASK'S UNIQUE OUTPUT BUFFER AS POSSIBLE, CALLING THE
*         TRANSMIT ROUTINE TO SEND TO IAFEX WHENEVER THE BUFFER IS
*         FULL.  FOR THIS TYPE OF COMMUNICATION TO IAFEX, TTOTRAP 
*         THEN USES THE SPINWHEELS FUNCTION TO HOLD ONTO THE TASK FOR 
*         A FEW EXTRA MILLISEONDS, SINCE THERE IS ABOUT AN EVEN 
*         CHANCE THAT IAFEX WILL QUICKLY RESPOND AND ASK SMFEX TO 
*         CONTINUE TASK EXECUTION.  IF IAFEX FAILS TO RESPOND WITHIN
*         THE LIMITATIONS OF THE SPINWHEELS ROUTINE, THEN TTOTRAP 
*         ASSUMES IT WILL BE SEVERAL SECONDS BEFORE IAFEX AND THE 
*         NETWORK CAN ACCEPT ANY OTHER OUTPUT FOR THIS USER, AND
*         TTOTRAP SWAPS THE TASK OUT. 
* 
*         THE SECOND BASIC VALUE FOR THE TTOTYPE PARAMETER IS 
*         "PROMPT".  THIS IS USED WHEN TTITRAP CALLS TTOTRAP. 
*         TTOTRAP IS EXPECTED TO IMMEDIATELY TRANSMIT ANY RESIDUAL
*         QUEUE OF OUTPUT TEXT TO IAFEX, THEN SWAP THE TASK OUT.
*         TTOTRAP ASSUMES THAT THE CALLER (TTITRAP) HAS ALREADY MADE
*         A DETERMINATION AS TO LOW PROBABILITY OF RAPID REPLY FROM 
*         IAFEX.
* 
*         THE THIRD AND LAST BASIC FUNCTION OF TTOTRAP IS THE "FORCE" 
*         VALUE OF THE TTOTYPE PARAMETER.  THIS IS USED BY VOLUNTEER
*         WHEN IT IS DETERMINED THAT SOME RESIDUAL TEXT HAS BEEN
*         QUEUED IN THE TASK'S OUTPUT BUFFER FOR AN EXCESSIVE REAL
*         TIME DELAY, AND OUGHT TO BE SENT TO THE USER'S TERMINAL.
*         WHEN TTOTRAP PROCESSES THE "FORCE" FUNCTION, IT TRANSMITS 
*         THE BUFFER CONTENT TO IAF AND PROCEEDS TO SWAP THE TASK 
*         WITHOUT ANY USAGE OF THE SPINWHEELS FUNCTION. 
* 
*         WHEN TTOTRAP DECIDES TO SWAPOUT, IT MUST CALL PAUSEIO IN
*         THE WORKFILE MANAGER.  THIS IS BECAUSE WORKIO DEALS WITH
*         SOME TRANSIENT RESOURCE, SUCH AS ITS CIRCULAR BUFFER, AND 
*         NEEDS THE OPPORTUNITY TO COMPLETE ITS USAGE OF SUCH 
*         RESOURCES.
* 
*         ENTRY  TTOTYPE - FUNCTION CODE. 
*                TTOADDR - ADDRESS OF TEXT. 
*                TTOLEN - LENGTH OF TEXT. 
*                TASKNXTTTO[CURTASK] - AMOUNT OF TEXT ALREADY QUEUED. 
*                TTOBUFFER - ADDRESS OF THIS TASK'S BUFFER. 
*                ALL SPINWHEELS PARAMETERS SETUP. 
* 
*         EXIT   TTOLEN - DECREMENTED.
*                TTOADDR - INCREMENETED.
*                TASKNXTTTO[CURTASK] - AMOUNT OF TEXT NOW IN BUFFER.
*                RUNTIME - INCREMENTED IF SWAPOUT.
*                ACCTOUT - INCREMENTED. 
* 
*         CALLS  MIN, MOVEWD, MELT, INSTRMNT1, TRANSMIT,
*                SPINWHEELS, CLEARQUE, PAUSEIO, ENDTRANS. 
* 
*         USES   TTOQUIT, TASKTYPTTO[CURTASK].
# 
  ITEM TMP1       I;                 # TEMPORARY STORAGE #
  ITEM TMP2       I;                 # TEMPORARY STORAGE #
  ITEM ENDBLOCK   I=O"0014 0000 0000 0000 0000";  # BLOCK TERMINATOR #
  
  TRCSTR("NTR OTRP$");
  
  IF NOT CURUSOKOUT THEN IORET     # FIRST TRANS ONLY  #
  
  P<MOVFROM>=TTOADDR; 
  ACCTOUT=ACCTOUT+TTOLEN;            # ACCOUNT FOR OUTPUT # 
  
  TTOQUIT=FALSE;
  WHYLE NOT TTOQUIT DO         # SEND ALL TEXT     #
    BEGIN 
    TMP1=SIZTTOBUF-TASKNXTTTO[CURTASK]-1;  # AVAILABLE ROOM # 
    TMP2=MIN(TMP1,TTOLEN);     # AMOUNT TO SEND    #
    P<MOVFROM>=TTOADDR; 
    P<MOVTO>=LOC(TTOBUFFER)+2+TASKNXTTTO[CURTASK];
    MOVEWD(TMP2,MOVFROM,MOVTO); 
    TTOLEN=TTOLEN-TMP2; 
    TTOADDR=TTOADDR+TMP2; 
    TASKNXTTTO[CURTASK]=TASKNXTTTO[CURTASK]+TMP2; 
    TASKTYPTTO[CURTASK]=0;     # DETERMINE TRANSMIT          #
    CONTROL IFGQ PARANOIA,4;
      IF TASKNXTTTO[CURTASK] GR SIZTTOBUF THEN MELT("TTOTRAP 1$");
    CONTROL FI; 
    CONTROL IFEQ METERING,1;
      IF TASKNXTTTO[CURTASK] GQ SIZTTOBUF-1 
        THEN INSTRMNT1(INSTST"XSTTO");
    CONTROL FI; 
    IF (TTOTYPE EQ TTOST"FORCE" AND TASKNXTTTO[CURTASK] NQ 0) 
      OR TASKNXTTTO[CURTASK] GQ (SIZTTOBUF-1) 
      THEN TASKTYPTTO[CURTASK]=SMF2IAF"MSGOUT"; 
    IF TTOTYPE EQ TTOST"PROMPT" AND TTOLEN EQ 0 
      THEN TASKTYPTTO[CURTASK]=SMF2IAF"PROMPT"; 
    IF TASKTYPTTO[CURTASK] NQ 0 THEN     # TRANSMIT          #
      BEGIN 
      P<MOVFROM>=LOC(ENDBLOCK);      # ADD 0014 CONTROL BYTE #
      P<MOVTO>=LOC(TTOBUFFER)+2+TASKNXTTTO[CURTASK];
      MOVEWD(1,MOVFROM,MOVTO);
      TASKNXTTTO[CURTASK]=TASKNXTTTO[CURTASK]+1;
      TRANSMIT;                      # TRANSMIT BUFFER TO IAFEX # 
      TASKNXTTTO[CURTASK]=0;
      IF TTOTYPE NQ TTOST"FORCE" AND TASKTYPTTO[CURTASK]
        EQ SMF2IAF"MSGOUT" THEN          # ATTEMPT CONTINUE  #
        BEGIN 
        SPINWHEELS;            # LOOK FOR QUICK ACKNOWLEGE   #
        IF TASKREQUE[CURTASK] THEN       # CAN CONTINUE      #
          BEGIN 
          TRCSTR("KWIK TOCIN$");
          CLEARQUE;                      # ACKNOWLEDGE       #
          TASKTYPTTO[CURTASK]=0;         # AVOID SWAPOUT     #
          CONTROL IFEQ METERING,1;
            INSTRMNT1(INSTST"KWIKTOC"); 
          CONTROL FI; 
          END 
        END 
      END 
    IF TASKTYPTTO[CURTASK] NQ 0 OR TTOTYPE NQ TTOST"NORMAL" THEN
      BEGIN 
      PAUSEIO;
      RUNTIME=RUNTIME+TIMEOFDAY-TASKTIME[CURTASK];
      ENDTRANS; 
      END 
    IF TTOLEN LQ 0 THEN TTOQUIT=TRUE; 
    END 
  
  IOEND                        # OF TTOTRAP        #
PAGE                         # SWAPPING ROUTINE  #
  
  
PROC SWAP;
  IOBEGIN(SWAP) 
# 
**        SWAP - SWAP THIS TASK IN OR OUT.
* 
*         THE SWAP ROUTINE READS OR WRITE THE SWAP PAGE FOR THE 
*         CURRENTLY EXECUTING TASK.  FOR A SWAPIN, WE READ THE PAGE 
*         ALREADY ASSIGNED TO THE USER.  FOR A SWAPOUT, WE ASSIGN A 
*         PAGE.  THE FIRST PRIORITY IN PAGE SELECTION IS EXTENDED 
*         MEMORY IF AVAILABLE, HEALTHY, AND NOT YET FILLED UP.  THE 
*         LOWER PRIORITY IS TO SELECT A DISK PAGE.  DISK PAGES ARE
*         PHASED OR INTERLEAVED ROUND-ROBIN ACROSS THE SEVERAL SWAP 
*         DATABASES.
* 
*         THE SWAP ROUTINE CAN RECOVER FROM A PARITY ERROR IN AN
*         EXTENDED MEMORY TRANSFER, BUT DOES NOT ATTEMPT TO HANDLE
*         DEVICE ERRORS ON DISKS.  WHEN AN ERROR IS DETECTED FROM 
*         EXTENDED MEMORY, A USER BEING SWAPPED IN WILL HAVE HIS TASK 
*         ABORTED, WHILE A USER BEING SWAPPED IS UNHARMED.  ERROR 
*         RECOVERY INCLUDES THE FAULT-OUT OF THE BAD PAGE,
*         DETERMINATION WHETHER THE ENTIRE EXTENDED MEMORY FIELD
*         LENGTH SHOULD BE ABANDONED DUE TO WIDESPREAD ERRORS, AND
*         FOR SWAPOUT WE ALSO ALLOCATE ANOTHER PAGE AND TRY THE SWAP
*         AGAIN.
* 
*         WHILE SWAP IS EXECUTING, THE DISPATCHING AREA IN THE COMMON 
*         BLOCK IS CONSIDERED TO BE IRRELEVANT.  THEREFORE, THE 
*         ROUTINE WHICH MANAGES THE BRUTE-FORCE SHUFFLING OF THE
*         COMMON BLOCK (SUBROUTINE GETCOMON) NEEDS TO KNOW WHEN 
*         SWAPPING IS IN EFFECT.  THIS SWAP ROUTINE IS THUS REQUIRED
*         TO TURN THE TASKSWPNOW FLAG ON AT ENTRY TIME AND TO TURN IT 
*         OFF AT EXIT TIME.  NO REENTRANT EVENTS CAN OCCUR BETWEEN
*         THE ACTUAL ENTRY OR EXIT AND THE SETTING OF THE TASKSWPNOW
*         FLAG. 
* 
*         ENTRY  TASKSWPIN[CURTASK] - DIRECTION OF SWAP.
*                TIMEOFDAY - CURRENT. 
*                SWAPBITS - ALLOCATION BITMASK. 
*                P<SWAPMASK> - ENABLES/DISABLES EXTENDED MEMORY PAGES.
*                CURUSSWPPAG - USERS PAGE FOR SWAPIN. 
*                TASKADDR[CURTASK] - MEMORY ADDRESS FOR SWAP PAGE.
*                SWAPLEN - LENGTH OF A PAGE IN WORDS. 
*                SWAPPRUS - LENGTH OF A PAGE IN SECTORS.
*                ECSERRORS - PREVIOUS EXTENDED MEMORY ERRORS. 
*                SWPFETDONE[ALL] - INTERLOCKS ON DISK FILES.
*                SWPFET[ALL] - THE DISK FET'S.
*                MAXSWPPAG - HIGHEST DISK PAGE CREATED TO DATE. 
* 
*         EXIT   TIMEOFDAY - CURRENT. 
*                SWAPBITS - UPDATED.
*                SWAPGOOD - EXTENDED MEMORY ERRORS FAULTED HERE.
*                ECSERRORS - INCREMENTED IF ERRORS. 
*                MAXSWPPAG - INCREMENTED IF HIGHWATERMARK RAISED. 
*                TASKTIME[CURTASK] - TIMEOFDAY. 
*                STATUSMSG - MAY CONTAIN ERROR MSG. 
*                CURUSSWPPAG - PAGE SELECTED FOR SWAPOUT. 
*                CURUSVICTIM - TRUE IF ECS PARITY ERROR ON SWAPIN.
* 
*         CALLS  ALLOC, MELT, INSTRMNT1, READECS, WRITECS,
*                FORCEALLOC, SMFDLY, MAKEFET, READ, WRITER, REWRITR,
*                SMFRCL, INSTRMNT2. 
* 
*         USES   TASKSWPNOW[CURTASK], TASKSWPFIL[CURTASK],
*                TASKSWPPRU[CURTASK], P<MOVFROM>, P<MOVTO>, SWPFET. 
# 
  ITEM TMP1,TMP2; 
  
  # SWAP IS REQUIRED TO SET TASKSWPNOW BEFORE ANY DELAYS OCCUR #
  
  TRCSTR("NTR SWP$"); 
  TASKSWPNOW[CURTASK]=TRUE; 
  TASKTIME[CURTASK]=TIMEOFDAY;
  
SWAPSTART:  
  
  IF NOT TASKSWPIN[CURTASK] THEN         # ALLOC PAGE        #
    BEGIN 
    ALLOC(SWAPBITS,SWAPMASK,TMP1,NUMSWPBIT);
    IF TMP1 EQ 0 THEN MELT("SWPO 2$");
    CURUSSWPPAG=TMP1;          # SAVE INVIOLATE    #
    END 
  
  TRCBOTH("PAGE$",CURUSSWPPAG); 
  
  IF CURUSSWPPAG LQ NUMSWPECS THEN       # ECS SWAP          #
    BEGIN 
    TRCSTR("SWPECS$");
    CONTROL IFEQ ECSCODE,0; 
      CONTROL IFGQ PARANOIA,1;
        MELT("SWP 3$"); 
      CONTROL FI; 
    CONTROL FI; 
    CONTROL IFEQ ECSCODE,1; 
      CONTROL IFEQ METERING,1;
        INSTRMNT1(INSTST"SWPE");
      CONTROL FI; 
      P<MOVFROM>=TASKADDR[CURTASK]; 
      P<MOVTO>=(CURUSSWPPAG-1)*SWAPLEN; 
      IF TASKSWPIN[CURTASK] THEN READECS(SWAPLEN,MOVTO,MOVFROM,TMP1); 
      ELSE WRITECS(SWAPLEN,MOVFROM,MOVTO,TMP1); 
      CONTROL IFGQ ECSERRMAX,0; 
        IF TMP1 NQ 0 THEN      # ECS ERROR, RETRY OR KILL    #
          BEGIN 
          ECSERRORS=ECSERRORS+TMP1; 
          IF ECSERRORS GQ ECSERRMAX THEN
            BEGIN 
            ECSDOWN=TRUE; 
            C<22,6>STATUSMSG="EM OFF";
            END 
          # FAULT OUT THIS PAGE TO PROTECT FUTURE USERS # 
          FORCEALLOC(SWAPGOOD,CURUSSWPPAG,NUMSWPBIT); 
          IF TMP1 GQ 3 AND TASKSWPIN[CURTASK] THEN CURUSVICTIM=TRUE;
          IF NOT TASKSWPIN[CURTASK] THEN GOTO SWAPSTART;
          END 
      CONTROL FI; 
      CONTROL IFLS ECSERRMAX,0; 
        IF TMP1 NQ 0 THEN MELT("ECS HARDWARE$");
      CONTROL FI; 
    CONTROL FI; 
    END 
  ELSE                         # DISK SWAPPING     #
    BEGIN 
    TASKSWPFIL[CURTASK]=1+MOD(CURUSSWPPAG-1-NUMSWPECS,NUMSWPFET); 
    TASKSWPPRU[CURTASK]=1+(CURUSSWPPAG-1-NUMSWPECS)/NUMSWPFET*SWAPPRUS; 
    TRCBOTH("SWPDSK$",TASKSWPFIL[CURTASK]); 
    TRCDEC(TASKSWPPRU[CURTASK]);
    CONTROL IFEQ METERING,1;
      INSTRMNT1(INSTST"SWPD");
    CONTROL FI; 
    WHYLE NOT SWPFETDONE[TASKSWPFIL[CURTASK]] DO SMFDLY;
    TMP1=TASKSWPFIL[CURTASK];  # VALID TILL SMFRCL          # 
    C<0,6>TMP2="SMFSWP";
    C<6,1>TMP2=O"33"+TMP1-1;
    P<ANYFET>=LOC(SWPFET[TMP1]);
    P<MOVFROM>=TASKADDR[CURTASK]; 
    MAKEFET(ANYFET,TMP2,MOVFROM,SWAPBUFLEN);
    SWPFETR[TMP1]=TRUE; 
    SWPFETRR[TMP1]=TASKSWPPRU[CURTASK]; 
    IF TASKSWPIN[CURTASK] THEN READ(ANYFET,0);
    ELSE
      BEGIN 
      SWPFETIN[TMP1]=SWPFETIN[TMP1]+SWAPLEN;
      IF CURUSSWPPAG GR MAXSWPPAG THEN
        BEGIN 
        MAXSWPPAG=CURUSSWPPAG;
        SWPFETRR[TMP1]=LOC(DUMB); 
        WRITER(ANYFET,0); 
        END 
      ELSE REWRITR(ANYFET,0); 
      END 
    SMFRCL(ANYFET); 
    END 
  
  # ONCE TASKSWPNOW IS CLEARED, SWAP CANNOT DELAY ANYMORE # 
  
  TASKSWPNOW[CURTASK]=FALSE;
  CONTROL IFEQ METERING,1;
    TMP1=TIMEOFDAY-TASKTIME[CURTASK]; 
    INSTRMNT2(INSTST"SWPTIM",TMP1); 
  CONTROL FI; 
  TASKTIME[CURTASK]=TIMEOFDAY;
  
  IOEND                        # OF SWAP           #
PAGE                         # USER/SYSTEM-STATE CHANGING CODE       #
  
  
PROC PAUSE; 
  IOBEGIN(PAUSE)
# 
**        PAUSE - INTERFACE TO EXECUTIVE TO START/END TIME SLICE. 
* 
*         PAUSE IS THE ROUTINE USED BY USER-MODE ROUTINES TO END A
*         SLICE ON THE CPU, AND BY THE EXECUTIVE TO RESUME EXECUTION
*         WHERE THE PREVIOUS TIME SLICE ENDED.  THUS, PAUSE EXITS TO
*         LABEL AFTERSLICE AND PROVIDES THE LABEL RESUME, WHICH 
*         RUNJOBS WILL JUMP TO JUST IN FRONT OF THE DEFINITION OF THE 
*         AFTERSLICE LABEL. 
# 
  XDEF LABEL RESUME;
  XREF LABEL AFTERSLICE;
  GOTO AFTERSLICE;
  
RESUME:                      # HERE FOR NEW TIMESLICE      #
  
  IOEND                        # OF PAUSE          #
  
  
PROC TRANSACT;
  BEGIN 
# 
**        TRANSACT - TRANSITION FROM EXECUTIVE TO USER FOR SWAPIN.
* 
*         TRANSACT IS USED BY THE RUNJOBS EXECUTIVE, JUST BEFORE
*         RUNJOBS DEFINES THE AFTERTRANS LABEL, TO SWITCH FROM
*         EXECUTIVE MODE TO USER MODE AND PERFORM SWAPINS.  TRANSACT
*         THEN REPAIRS THE COMMON BLOCK DISPATCHING AREA, REPAIRS THE 
*         RETURN JUMP WORDS IN ALL ACTIVE SUBROUTINE ENTRY POINTS,
*         CHECKS FOR CERTAIN INTERRUPT CONDITIONS, AND EXITS VIA A
*         JUMP TO THE MORETRANS LABEL IN THE ENDTRANS ROUTINE.  THIS
*         TYPE OF EXIT IS USED BECAUSE THE TASK IS PRESUMED TO HAVE 
*         HAVE COMPLETED ITS PREVIOUS SWAPPED-IN TRANSACTION BY 
*         CALLING ENDTRANS, AND ENDTRANS IS RESUMED TO HAVE JUMPED TO 
*         THE EXECUTIVE JUST IN FRONT OF THE DEFINITION OF LABEL
*         MORETRANS.
* 
*         ENTRY  MAY BE CALLED ONLY BY RUNJOBS TRANSACTION SECTION. 
*                CURUSINTRPT - AN INTERRUPT MUST BE COMMUNICATED TO 
*                    USER-MODE CODE AFTER SWAPIN IS COMPLETE. 
*                SHUTDOWN - CAUSES SYNTHESIS OF INTERRUPTS. 
*                CURUSRCVRY - FSE JOB DETACHING.
*                RSTKPTR - SIZE OF REENTRANCY STACK.
* 
*         EXIT   VIA LABEL MORETRANS. 
*                RENTSTK - REENTRANCY STACK.
*                COMMON BLOCK AND SUBROUTINE ENTRIES REBUILT. 
*                USRBRK - 1 IF ANY INTERRUPT PASSED TO USER.
*                CURUSVICTIM - TRUE IF SWAPIN RUINED USER DATA. 
*                FORCESINGLE, FORCENULL - ADDITIONAL INTERRUPTS.
*                WORKFILE CHECKPOINTED AND EXIT VIA LABEL AFTEREDIT 
*                    IF CURUSRCVRY IS ON. 
* 
*         CALLS  SWAP, DEALLOC, GETCOMMON, CHECKIO, MOVEWD, SPREAD. 
* 
*         USES   SWAPRSTK, TASKSWPIN[CURTASK].
# 
  XREF LABEL AFTEREDIT; 
  XREF LABEL AFTERTRANS;
  XREF LABEL MORETRANS; 
  
  TRCSTR("NTR TRNSCT$");
  TASKSWPIN[CURTASK]=TRUE;
  
  SWAP; 
  # ONCE SWAP RETURNS, WE CANNOT REENTER UNTIL AFTER GETCOMMON #
  IF SHUTDOWN NQ 2 THEN DEALLOC(SWAPBITS,CURUSSWPPAG,NUMSWPBIT);
  CURUSSWPPAG=0;
  LASTTASK=0;                # RECONSTRUCT COMMON AFTER SWAP         #
  GETCOMMON;
  # END OF TIME-CRITICAL SEQUENCE # 
  
  IF CURUSINTRPT THEN USRBRK=1; 
  IF CURUSVICTIM THEN 
    BEGIN 
    SMFVICTIM=TRUE; 
    ERRSTRING="ECS HARDWARE$";
    END 
  IF SHUTDOWN NQ 0 THEN FORCESINGLE=TRUE; 
  IF SHUTDOWN GR 1 THEN 
    BEGIN 
    CURUSINTRPT=TRUE; 
    USRBRK=1; 
    FORCENULL=TRUE; 
    END 
  IF CURUSRCVRY THEN           # CHKPT AND END EDIT          #
    BEGIN 
    FORCENULL=TRUE; 
    IF SHUTDOWN EQ 2 THEN 
      BEGIN 
      USRBRK=0; 
      CURUSINTRPT=FALSE;
      END 
    IF NOT CURUSDONE THEN 
      BEGIN                    # IF WORKFILE NOT YET CHECKPOINTED # 
      CHECKIO;                 # CHECKPOINT WORKFILE #
      END 
    GOTO AFTEREDIT; 
    END 
  MOVEWD(RSTKPTR-1,SWAPRSTK,RENTSTK); 
  SPREAD(RSTKPTR,RENTSTK);
  
  #   OUR RETURN GOES NOT TO CALLER BUT TO USER   # 
  GOTO MORETRANS;            # LET GATHERED CODE RETURN TO USER      #
  
  END                          # OF TRANSACT       #
  
  
PROC ENDTRANS;
  IOBEGIN(ENDTRANS) 
# 
**        ENDTRANS - END A TRANSACTION WITH SWAPOUT.
* 
*         ENDTRANS IS CALLED ONLY BY TTOTRAP, FOR THE PURPOSE OF
*         SWAPPING THE CURRENT TASK OUT.  ENDTRANS CONSOLIDATES DATA
*         FROM SUBROUTINE ENTRY POINTS AND FROM THE DISPATCHABLE
*         COMMON BLOCK, THEN SWAPS THE MEMORY IMAGE OUT.  ENDTRANS
*         THEN EXITS BY JUMPING TO LABEL AFTERTRANS IN THE RUNJOBS
*         EXECUTIVE.  ENDTRANS PROVIDES THE DEFINITION OF LABEL 
*         MORETRANS SO THAT WHEN RUNJOBS CALLS TRANSACT TO SWAP THIS
*         USER BACK IN, TRANSACT CAN JUMP TO THE ADDRESS AT WHICH THE 
*         TASK HAD FORFEITED CONTROL. 
* 
*         ENDTRANS IS ALLOWED TO BE CALLED ONLY BY TTOTRAP BECAUSE
*         THE TASK CANNOT SWAPOUT WITH ANY RESIDUAL OUTPUT LEFT IN
*         THE BUFFER OWNED BY THE TASK. 
* 
*         ENTRY  ONLY FROM TTOTRAP. 
*                RSTKPTR - DEPTH OF REENTRANCY STACK. 
*                RENTSTK - THE REENTRANCY STACK.
* 
*         EXIT   VIA LABEL AFTERTRANS.
* 
*         CALLS  GATHER, MOVEWD, PUTCOMMON, SWAP, MELT. 
* 
*         USES   SWAPRSTK, TASKSWPIN[CURTASK].
# 
  XREF LABEL AFTERTRANS;
  XDEF LABEL MORETRANS; 
  
  TRCSTR("NTR NDTRNS$");
  GATHER(RSTKPTR,RENTSTK);
  MOVEWD(RSTKPTR-1,RENTSTK,SWAPRSTK); 
  
  # MUST EXECUTE INSTANTLY FROM PUTCOMMON INTO START OF SWAP #
  PUTCOMMON;                   # MAKE COMMON SWAPPABLE       #
  TASKSWPIN[CURTASK]=FALSE; 
  SWAP; 
  # END OF TIME-CRITICAL SEQUENCE # 
  
  CONTROL IFGQ PARANOIA,3;
    IF TASKNXTTTO[CURTASK] NQ 0 THEN MELT("NTRNS 2$");
  CONTROL FI; 
  GOTO AFTERTRANS;
  
  # RETURN GOES BACK TO USER #
MORETRANS:  
  
  IOEND                        # OF ENDTRANS       #
PAGE                         # INITIALIZATION AND TERMINATION        #
  
  
PROC INITMON; 
  BEGIN 
# 
**        INITMON - INITIALIZE SMFEX. 
* 
*         INITMON IS THE PRESET ROUTINE FOR SMFEX.  THE FOLLOWING 
*         FUNCTIONS ARE PERFORMED --
* 
*         THE PARAMETERS CRACKED BY THE OPERATING SYSTEM AT RA+2 ARE
*         CHECKED FOR RECOVERY MODE OR FOR A NUMBER OF TASKS TO BE
*         FORMATTED.  THE SHUTDOWN FLAG IS SET FOR RECOVERY MODE, AND 
*         THE TASKSAVAIL FLAG IS SET AND FORCED WITHIN REASONABLE 
*         BOUNDS FOR A NUMERIC PARAMETER. 
* 
*         MISCELLANEOUS POINTERS AND LENGTH FIELDS ARE COMPUTED.  THE 
*         FIELD LENGTH IS THEN INCREASED TO MAKE ROOM FOR THE MODEL 
*         FORMAT OF THE COMMON BLOCK.  THE FIELD LENGTH IS INCREASED
*         TO ALLOCATE THE TERMINAL INPUT BUFFERS.  THE BITMAPS USED 
*         FOR ALLOCATION OF SMFEX RESOURCES ARE INITIALIZED.  THEN
*         EACH TASK IS BUILT OUT OF INCREASED FIELD LENGTH. 
* 
*         TASK CONSTRUCTION IS DELEGATED TO THE BUILDTASK ROUTINE.
*         THE COMMON BLOCK IS USED TO CONSTRUCT THIS TASK'S POINTERS
*         FOR A NUMBER OF PER-TASK ARRAYS.  THEN COMMON BLOCK IS THEN 
*         COPIED INTO THE EXTENDED FIELD LENGTH SO EACH TASK CAN
*         CONTAIN ITS OWN BASE ADDRESSES FOR ARRAYS.
* 
*         THE EXTENDED MEMORY FIELD LENGTH IS DETERMINED BY SEEING
*         HOW MUCH MEMORY IS DESIRED FOR THE COMPILE-TIME 
*         CONFIGURATION AND HOW MUCH IS ACTUALLY AVAILABLE FROM THE 
*         RUNNING SYSTEM.  THE ALLOCATION BITMAPS ARE MODIFIED IF 
*         NEEDED TO ALLOW FOR THE NUMBER OF ECS SWAP PAGES ACTUALLY 
*         OBTAINED.  EXTENDED MEMORY SWAPPING IS COMPLETELY DISABLED
*         IF THE RUNNING SYSTEM CANNOT PROVIDE ANY SECONDARY FIELD
*         LENGTH AT ALL.
* 
*         FOR SUBSYSTEM RECOVERY, A SHORTENED INITMON ALGORITHM TAKES 
*         EFFECT.  WE ASSUME THE PREVIOUS SMFEX JOB STEP EXECUTED AN
*         IDENTICAL VERSION OF THIS PROGRAM (I.E., SMFEX HAS NOT BEEN 
*         SYSEDITED WHILE RUNNING) AND THAT THE TERMINATION OF THE
*         PREVIOUS SMFEX JOB STEP EXITED INTO A "DMB" FORMAT DUMP 
*         FILE.  THE DUMP FILE IS SCANNED TO OBTAIN THE USER TABLE. 
*         WE CHECK ALL USER TABLE ENTRIES FOR RECOVERABLE TASKS AND 
*         QUEUE THOSE USERS FOR SWAPIN.  RECOVERABLE TASKS ARE THOSE
*         WHICH EXIST ON A SWAP PAGE BUT NOT IN MEMORY.  FOR
*         SUBSYSTEM RECOVERY, THE INITMON ALGORITHM TERMINATES AT 
*         THIS POINT IN THE FLOW. 
* 
*         FOR TRUE SUBSYSTEM INITIATION, THE REMAINDER OF THE INITMON 
*         ALGORITHM IS AS FOLLOWS --
* 
*         ALL DISK SWAP FILES ARE EVICTED.
* 
*         THE NEGATIVE FIELD LENGTH IS BOOSTED TO ITS ANTICIPATED 
*         HIGH WATER MARK BY ALLOCATING THEN RETURNING A LARGE GROUP
*         OF TOKEN FILES.  THIS IS DONE BECAUSE THE TLX PP ROUTINE
*         WHICH TRANSITIONS USERS FROM SINGLE USER MODE TO MULTI USER 
*         MODE IS UNABLE TO INCREASE OUR NEGATIVE FIELD LENGTH TO 
*         MAKE ROOM FOR LOCAL FNT'S FOR THE TRANSFERRED WORKFILES.
*         TLX TREATS OUR PRE-ALLOCATED NEGATIVE FIELD LENGTH AS A 
*         LIMITATION AS TO WHETHER A PARTICULAR SINGLE USER EDITOR
*         JOB WILL BE ACCEPTED INTO MULTI USER MODE.
* 
*         FINALLY, COMMUNICATIONS ARE ESTABLISHED WITH IAFEX VIA THE
*         SCP/UCP FACILITY.  SMFEX IS THE UCP AND IAFEX IS THE SCP. 
# 
  ITEM TMP1,TMP2,TMP3,TMP4; 
  
  P<CORE>=0;
  IF B<00,01>MEM[CSMR] EQ 0 THEN     # IF SYSTEM CHARACTER SET = 63 # 
    BEGIN 
    XLTINTXP[O"00"]=O"4045";         # COLON = PERCENT #
    XLTINTXP[O"63"]=O"4072";         # PERCENT = COLON #
    XLTDSPXP[O"00"]=O"4045";         # COLON = PERCENT #
    XLTDSPXP[O"63"]=O"4072";         # PERCENT = COLON #
    XLTDSPINT[O"00"]=O"0063";        # COLON = PERCENT #
    XLTDSPINT[O"63"]=O"0000";        # PERCENT = COLON #
    XLTXPINT[O"45"]=O"0000";         # PERCENT = COLON #
    XLTXPINT[O"72"]=O"0063";         # COLON = PERCENT #
    XLTINTDSP[O"00"]=O"0055";        # COLON = BLANK #
    END 
  
  SFMCSTF;
  
  IF C<0,7>MEM[2] EQ "RECOVER" THEN SHUTDOWN=2; 
  ELSE
    BEGIN 
    TMP1=0; 
    TMP2=0; 
    TMP3=C<0,1>MEM[2]-O"33";
    WHYLE TMP2 LQ 6 AND TMP3 GQ 0 AND TMP3 LQ 9 DO
      BEGIN 
      TMP1=TMP1*10+TMP3;
      TMP2=TMP2+1;
      TMP3=C<TMP2,1>MEM[2]-O"33"; 
      END 
    IF TMP1 GQ 2 AND TMP1 LQ NUMTASKS THEN TASKSAVAIL=TMP1; 
    END 
  
  P<SWAPMASK>=LOC(SWAPGOOD);
  SWAPLEN=LOC(SWAPEND)-LOC(DATASTART);
  DISPATLEN=LOC(DISPATEND)-LOC(DATASTART);
  FIELDLEN=B<42,18>MEM[O"65"];
  MODELPTR=FIELDLEN;
  MODELLEN=LOC(SWAPEND)-LOC(DATAEND); 
  FIELDLEN=MODELPTR+MODELLEN; 
  FLDLEN(FIELDLEN+4); 
  P<MOVTO>=MODELPTR;
  MOVEWD(MODELLEN,DATAEND,MOVTO); 
  FOR TMP3=1 STEP 1 UNTIL NUMTTIBUF DO
    BEGIN 
    TTIBUFADDR[TMP3]=FIELDLEN;
    FIELDLEN=FIELDLEN+SIZTTIBUF;
    FLDLEN(FIELDLEN+4); 
    END 
  FOR TMP1=1 STEP 1 UNTIL MAXREENT DO RSTK[TMP1]=LOC(RSTK[TMP1]); 
  INITBITMAP(SWAPBITS,NUMSWPBIT,NUMSWPECS+NUMSWPDSK); 
  INITBITMAP(TTIBITS,NUMTTIBIT,NUMTTIBUF);
  INITBITMAP(SWAPGOOD,NUMSWPBIT,NUMSWPECS+NUMSWPDSK); 
  INITBITMAP(SWAPBAD,NUMSWPBIT,NUMSWPECS+NUMSWPDSK);
  INITBITMAP(TTIGOOD,NUMTTIBIT,NUMTTIBUF);
  KILLECSBITS(SWAPBAD); 
  MEM[USRBASE]=LOC(USERTAB);   # FOR EASY C DISPLAY          #
  MEM[SLTBASE]=LOC(TASKTAB);
  CONTROL IFEQ METERING,1;
    MEM[STTBASE]=LOC(INSTDATA); 
  CONTROL FI; 
  FOR CURTASK=1 STEP 1 UNTIL TASKSAVAIL DO BUILDTASK(CURTASK);
  SWAPLEN=SWAPLEN+TASKDSPADR[1]-TASKADDR[1];
  SWAPPRUS=((SWAPLEN+O"100")/O"100"); 
  SWAPBUFLEN=1+SWAPPRUS*O"100"; 
  FLDLEN(FIELDLEN+4); 
  
  CONTROL IFEQ ECSCODE,1; 
    # REQUEST ECS FIELD LENGTH/ SEE IF IT IS THERE #
    GETFLCE(ECSFIELD);         # GET VALIDATION LIMIT        #
    TESTECS(TMP1);             # GET EXISTENCE/ENABLED       #
    IF B<0,12>ECSFIELD NQ 0 AND TMP1 NQ 0 THEN
      BEGIN 
      # ALLOCATE DESIRED OR AVAILABLE ECS FIELD LENGTH #
      ECSFIELD=MIN(NUMSWPECS*SWAPLEN,512*B<0,12>ECSFIELD);
      FLDLENE(ECSFIELD);
      SETRFLE((ECSFIELD+511)/512);
      PROTEON;               # SINCE ABORT RECOVERY ADVANCES         #
      # TURN OFF ANY SWAP PAGES BEYOND AVAILABLE FIELD LENGTH # 
      TMP1=ECSFIELD/SWAPLEN;
      FOR TMP2=TMP1+1 STEP 1 UNTIL NUMSWPECS
        DO FORCEALLOC(SWAPGOOD,TMP2,NUMSWPBIT); 
      END 
    ELSE KILLECSBITS(SWAPGOOD);   # TURN OFF ALL ECS SWAP PAGES # 
  CONTROL FI; 
  CONTROL IFEQ ECSCODE,0; 
    KILLECSBITS(SWAPGOOD);
  CONTROL FI; 
  
  IF SHUTDOWN EQ 2 THEN 
    BEGIN 
    TERMMON;                   # SHUT OFF ANY INPUT          #
    P<MOVFROM>=FIELDLEN;
    MAKEFET(DMBFET,"ZZZZDMB",MOVFROM,O"1001");
    FIELDLEN=FIELDLEN+O"1001";
    FLDLEN(FIELDLEN+4); 
    REWIND(DMBFET,1); 
    READ(DMBFET,0); 
    TMP1=0; 
    TMP4=0; 
    FOR TMP2=0 STEP 1 UNTIL O"61" DO
      BEGIN                  # UNTIL RIGHT (CM) CONTROL WORD         #
      READO(DMBFET,TMP1,TMP4);         # ACCESS NEXT CONTROL WORD    #
      IF TMP4 NQ 0 THEN MELT("RCVR 1$");
      END 
    IF C<0,2>TMP1 NQ "CM" THEN MELT("RCVR 2$"); 
    FOR TMP2=1 STEP 1 UNTIL LOC(USERTAB) DO 
      BEGIN                            # CONSUME LOWER FIELD LENGTH  #
      READO(DMBFET,TMP1,TMP4);
      IF TMP4 NQ 0 THEN MELT("RCVR 3$");
      END 
    # FINALLY READ IN THE USER TABLE #
    FOR TMP2=1 STEP 1 UNTIL 3*NUMSMFUSR DO
      BEGIN 
      P<MOVTO>=LOC(USERTAB)+TMP2-1; 
      READO(DMBFET,MOVTO,TMP4); 
      IF TMP4 NQ 0 THEN MELT("RCVR 4$");
      END 
    REWIND(DMBFET,1);        # IN CASE TAKE ANOTHER DUMP   #
    FOR CURUSER=1 STEP 1 UNTIL NUMSMFUSR DO 
      BEGIN                     # UPDATE AND QUEUE CHECKPT    # 
      GETUSER;
      IF CURUSINTASK THEN CURUSDONE=TRUE; 
      CURUSINQUE=FALSE; 
      CURUSTTIBUF=0;
      CURUSQUELNK=0;
      CURUSINTASK=FALSE;
      IF CURUSLOGGED AND CURUSSWPPAG NQ 0 AND NOT CURUSDONE THEN
        BEGIN 
        TRCBOTH("RECOVER$",CURUSER);
        CURUSRCVRY=TRUE;
        CURUSINTRPT=TRUE; 
        PUTINQ; 
        END 
      PUTUSER;
      END 
    FOR TMP1=1 STEP 1 UNTIL NUMSWPFET DO SWPFETDONE[TMP1]=TRUE; 
    TRCSTR("END INITMON RCVR$");
    RETURN;                  # SO THAT NOTHING ELSE IS DONE          #
    END 
  
  FOR TMP1=1 STEP 1 UNTIL NUMSWPFET DO
    BEGIN 
    CONTROL IFGR NUMSWPFET,8; BADSTUFF; CONTROL FI; # CODE DEPENDENT #
    C<0,6>TMP2="SMFSWP";
    C<6,1>TMP2=TMP1+O"32";
    P<ANYFET>=LOC(SWPFET[TMP1]);
    P<MOVFROM>=TASKADDR[1]; 
    MAKEFET(ANYFET,TMP2,MOVFROM,SWAPBUFLEN);
    READ(ANYFET,1);          # ASSURE FNT ALLOCATE #
    EVICT(ANYFET,1);         # ASSURE NO OLD DATA # 
    END 
  
  
    PROC GENNULL; 
      BEGIN 
      C<0,2>TMP2="NE";
      TMP3=TMP1;
      FOR TMP4=6 STEP -1 UNTIL 2 DO 
        BEGIN 
        C<TMP4,1>TMP2=O"33"+MOD(TMP3,10); 
        TMP3=TMP3/10; 
        END 
      MAKEFET(NULLFET,TMP2,CORE,O"101");
      END                       # OF GENNULL        # 
  
  
    FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO
      BEGIN                  # MANY NULL FILES TO ENLARGEN NFL       #
      GENNULL;
      READ(NULLFET,1);       # ASSURE FNT ALLOCATE #
      END 
    FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO
      BEGIN                  # THEN RELEASE THE FNT SPACE  #
      GENNULL;
      RETERN(NULLFET,1);
      END 
  IAFPTR=LOC(SSCINP[0]);
  INITSSCWORD[1]=IAFHEADER; 
  
  P<SSCBUFFER>=LOC(INITSSC);
  SSCSTATUS=-1; 
  # DELAY UNTIL IAF HANDSHAKE WORKS OR IDLE COMMAND DETECTED #
  WHYLE SSCSTATUS LAN O"7776" NQ 0 AND MEM[0] LAN O"100000" EQ 0 DO 
    BEGIN 
    SSCWORD=O"0000 0000 0000 0103 0000";
    SYSREQ("SSC",0,LOC(SSCBUFFER),IAFSSID); 
    RECALL(0);
    END 
  
  SFMSSTF;
  TRCSTR("END INITMON$"); 
  
  END                          # OF INITMON        #
  
  
PROC TERMMON; 
  BEGIN 
# 
**        TERMMON - TERMINATE SMFEX NORMALLY. 
* 
*         CALLS  SFMCSTF. 
# 
  SFMCSTF;
  TRCSTR("COMPLETE$");
  TRCFRC; 
  END                         # OF TERMMON        # 
  
  
PROC MELT(STR); 
  BEGIN 
# 
**        MELT - TERMINATE SMFEX ABNORMALLY.
* 
*         ENTRY  STR - ERROR MESSAGE FOR INTERNAL ABORT CONDITION.
*                COMPLETE - IF ALREADY SET, THEN MELT FUNCTIONS AS
*                    AN INTERFACE TO PRODUCE A DAYFILE MESSAGE THEN 
*                    ALLOW NORMAL TERMINATION.  NORMALLY, COMPLETE
*                    IS FALSE AND MELT ABORTS THE JOB STEP. 
* 
*         EXIT   VIA LABELS ABORT OR ENDRUN.
* 
*         CALLS  MESSAGE, TERMMON.
# 
  ITEM STR C(30); 
  ITEM STR2 C(30);
  ITEM TMP1,TMP2; 
  
  TRCSTR(STR);
  TRCFRC; 
  STR2=STR; 
  TMP2=0; 
  FOR TMP1=0 STEP 1 UNTIL 29 DO 
    BEGIN 
    IF TMP2 EQ 0 AND C<TMP1,1>STR2 EQ "$" THEN TMP2=TMP1+1; 
    END 
  FOR TMP1=29 STEP -1 UNTIL TMP2 DO C<TMP1,1>STR2=0;
  MESSAGE(STR2,0,1);
  TERMMON;
  IF COMPLETE THEN ENDRUN;
  ELSE ABORT; 
  
  END                          # OF MELT #
PAGE
  
# 
**        SMFEX - MAIN ALGORITHM. 
* 
*         THE SMFEX MAIN ALGORITHM INITIALIZES THE SUBSYSTEM, THEN
*         LOOPS ON THE THREE MAIN PROCESSES UNTIL A COMPLETION FLAG 
*         IS DETECTED.  THE SUBSYSTEM IS THEN TERMINATED. 
* 
*         THE THREE PROCESSING PHASES PERFORM TIME-DEPENDENT FUNCTIONS, 
*         RECEIVE AND QUEUE MESSAGES FROM IAFEX, AND EXECUTE TASKS AND
*         REPLY TO IAFEX. 
* 
*         CALLS  INITMON, DOTIMER, DOINPUT, RUNJOBS, INITMON. 
# 
ITEM TMP1;
  
INITMON;                     # GET EVERYTHING SET UP       #
  
WHYLE NOT COMPLETE DO        # MAIN LOOP         #
  BEGIN 
  DOTIMER;                   # RECALL CPU, HANDLE TIMER AND OPERATOR #
  DOINPUT;                   # PROCESS INPUT FROM IAFEX    #
  RUNJOBS;                   # EXECUTE ANY TASKS THAT CAN  #
  END                         # OF MAIN LOOP      # 
  
TERMMON;                     # CLOSE UP SHOP     #
ENDRUN; 
  
  
END TERM
