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