PROC FSEWORK;
BEGIN
#
*** FSEWORK - WORKFILE ACCESS METHOD FOR FULL SCREEN EDITOR.
*
* COPYRIGHT CONTROL DATA SYSTEMS INC. 1992.
#
DEF LISTCON #0#;
CONTROL EJECT; # UNIVERSAL DEFINITIONS #
*IFCALL SINGLE,COMFSGL
*IFCALL ONLY,COMFONL
*IFCALL MULTI,COMFMLT
*CALL COMFFSE
CONTROL IFEQ MULTI,1;
DEF RECALL(AA) #SMFRCL(AA)#;
DEF DELAY #SMFDLY#;
XREF PROC SMFRCL;
XREF PROC SMFDLY;
CONTROL FI;
CONTROL IFEQ SINGLE,1;
XREF PROC RECALL;
DEF DELAY #RECALL(0)#;
CONTROL FI;
XREF # SYSTEM PROCS #
BEGIN
PROC WRITE;
PROC REWRITE;
PROC RPHR;
PROC RPHRLS;
PROC SKIPEI;
PROC REWIND;
PROC READ;
PROC WRITER;
PROC WRITEW;
PROC REWRITR;
CONTROL IFEQ SINGLE,1;
PROC EVICT;
CONTROL FI;
END
XREF # COMPASS PROCS #
BEGIN
PROC MOVEWD;
PROC EXCHWD;
FUNC MOVELN;
FUNC LINESZ;
END
XREF
BEGIN
*CALL COMFXSB
END
XDEF
BEGIN
*CALL COMFXWK
END
# COMMON DATA #
CONTROL IFEQ MULTI,1;
XREF ARRAY RENTSTK [1:MAXREENT]; # SUBROUTINE STACK #
BEGIN
ITEM RSTK;
END
XREF ITEM RSTKPTR;
XREF PROC WIOSTAT;
CONTROL FI;
*CALL COMFDS1
*CALL COMFVD2
*CALL COMFDS2
PAGE
PROC INITFET(BUFF,LEN); # INITIALIZE FET #
BEGIN
#
** INITFET - INITIALIZE FILE ENVIRONMENT TABLE.
*
* INITFET IS SPECIALIZED FOR THE WORKFILE MANAGER. THE FET
* IS ASSUMED TO BE MAPPED BY THE BASED ARRAY "FET". THE
* LENGTH OF THE FET IS ASSUMED TO BE AT LEAST SEVEN WORDS,
* AND RANDOM I/O FLAGS WILL BE SET.
*
* ENTRY BUFF - THE ARRAY PROVIDED AS A BUFFER.
* LEN - INTEGER LENGTH OF BUFFER.
*
* EXIT FET - INITIALIZED FOR RANDOM I/O.
* QUARTER - INITIALIZED AS APPROX LEN/4.
#
ARRAY BUFF;;
ITEM LEN;
QUARTER=(LEN+O"300")/4;
FETNAM = WORKNAM;
FETCOD = 0;
FETDUN = TRUE;
FETR = TRUE;
FETW = FALSE;
FETOUT = LOC(BUFF); FETLIM = LOC(BUFF) + LEN;
FETL = 2;
FETFIR = LOC(BUFF); FETIN = LOC(BUFF);
END
FUNC BUFAVAIL I;
BEGIN
#
** BUFAVAIL - FUNCTION TO COMPUTE BUFFER FREE SPACE.
*
* BUFAVAIL COMPUTES THE SPACE AVAILABLE IN THE CIRCULAR
* BUFFER ASSIGNED TO THE FET MAPPED BY THE BASED ARRAY
* "FET".
*
* ENTRY FET - FET.
*
* EXIT BUFAVAIL - WORDS OF SPACE STILL AVAILABLE FOR DATA.
#
IF FETIN LS FETOUT THEN BUFAVAIL=FETOUT-FETIN;
ELSE BUFAVAIL=(FETLIM-FETFIR)-(FETIN-FETOUT);
END
FUNC BUFUSAGE I;
BEGIN
#
** BUFUSAGE - FUNCTION TO COMPUTE BUFFER UTILIZATION.
*
* BUFUSAGE IS THE COUTERPART TO BUFAVAIL, COMPUTING THE
* NUMBER OF WORDS ALREADY USED IN THE CIRCULAR BUFFER
* ASSIGNED TO THE FET MAPPED BY BASED ARRAY "FET".
*
* ENTRY FET - FET.
*
* EXIT BUFUSAGE - RESULT.
#
IF FETIN GQ FETOUT THEN BUFUSAGE=FETIN-FETOUT;
ELSE BUFUSAGE=(FETLIM-FETFIR)-(FETOUT-FETIN);
END
FUNC LSTUSAGE I;
BEGIN
#
** LSTUSAGE - COMPUTE UTILIZATION OF READ LIST.
*
* LSTUSAGE COMPUTES THE NUMBER OF WORDS USED IN THE READ
* LIST.
*
* ENTRY RDIN, RDOUT - BOUNDS FOR PORTION OF LIST USED.
* LSTSIZE - SIZE OF LIST.
*
* EXIT LSTUSAGE - RESULT.
#
LSTUSAGE=RDIN-RDOUT;
IF RDIN LS RDOUT THEN LSTUSAGE=LSTSIZE-1-RDOUT+RDIN;
END
FUNC LSTAVAIL I;
BEGIN
#
** LSTAVAIL - COMPUTE SPACE AVAILABLE IN READ LIST.
*
* LSTAVAIL COMPUTES THE NUMBER OF WORDS AVAILABLE FOR USE
* IN THE RANDOM READ LIST.
*
* ENTRY - RDIN, RDOUT, LSTSIZE - CONTROL READ LIST.
*
* EXIT LSTAVAIL - RESULT.
*
* CALLS LSTUSAGE.
#
LSTAVAIL=LSTSIZE-1-LSTUSAGE;
END
PROC RDWBUF(LENGTH);
BEGIN
#
** RDWBUF - READ WORDS FROM BUFFER WITH NO I/O.
*
* RDWBUF FUNCTIONS AS A SPECIALIZED READW MACRO. A STANDARD
* FET IS ASSUMED AND THE DATA TRANSFER WORKING BUFFER IS
* ASSUMED TO BE MAPPED BY A STANDARD BASED ARRAY. MOST
* IMPORTANT, THIS ROUTINE IS GUARANTEED TO NEVER CALL CIO.
* THE CALLER IS RESPONSIBLE TO ASSURE THAT THE CIRCULAR
* BUFFER HAS ENOUGH DATA TO SATISFY THE REQUEST.
*
* ENTRY LENGTH - AMMOUNT OF DATA NEEDED.
* FET - FET AND CIRCULAR BUFFER HAVE ENOUGH DATA.
* TOO - MAPPED TO USER'S WORKING BUFFER.
*
* EXIT FET - INDICATES DATA TRANSFERRED.
* TOO - DATA IS TRANSFERRED.
*
* CALLS MOVEWD, TRAGIC, BUFUSAGE.
*
* USES FROM.
#
ITEM LENGTH, X1;
IF BUFUSAGE LS LENGTH OR LENGTH LS 0 THEN
BEGIN
TRAGIC(" WORKFILE BUFFER EMPTY ON READ.$");
END
P<FROM>=FETOUT;
IF FETIN GR FETOUT OR FETLIM-FETOUT GR LENGTH THEN
BEGIN
MOVEWD(LENGTH,FROM,TOO);
FETOUT=FETOUT+LENGTH;
END
ELSE
BEGIN
X1=FETLIM-FETOUT;
MOVEWD(X1,FROM,TOO);
P<TOO>=LOC(TOO)+X1;
X1=LENGTH-X1;
P<FROM>=FETFIR;
MOVEWD(X1,FROM,TOO);
FETOUT=FETFIR+X1;
END
END
PROC WTWBUF(LENGTH);
BEGIN
#
** WTWBUF - WRITE WORDS TO BUFFER WTH NO I/O.
*
* WTWBUF PERFORMS THE ROLE OF THE WRITEW MACRO, BUT IS
* SPECIALIZED TO USE AN ASSUMED FET AND WORKING BUFFER, AND
* IS GUARANTEED TO NEVER PERFORM ANY CIO CALLS. THE CALLER
* IS RESPONSIBLE TO ASSURE THE CIRCULAR BUFFER HAS ROOM TO
* ACCEPT THE DATA.
*
* ENTRY LENGTH - NUMBER OF WORDS.
* FET - THE CIRCULAR BUFFER HAS ROOM.
* FROM - MAPPED ONTO WORKING BUFFER.
*
* EXIT FET - THE CIRCULAR BUFFER HAS DATA.
*
* CALLS BUFAVAIL, TRAGIC, MOVEWD.
*
* USES TOO.
#
ITEM LENGTH, X1;
IF BUFAVAIL LS LENGTH OR LENGTH LS 0 THEN
BEGIN
TRAGIC(" WORKFILE BUFFER FULL ON WRITE.$");
END
P<TOO>=FETIN;
IF FETOUT GR FETIN OR FETLIM-FETIN GR LENGTH THEN
BEGIN
MOVEWD(LENGTH,FROM,TOO);
FETIN=FETIN+LENGTH;
END
ELSE
BEGIN
X1=FETLIM-FETIN;
MOVEWD(X1,FROM,TOO);
P<FROM>=LOC(FROM)+X1;
X1=LENGTH-X1;
P<TOO>=FETFIR;
MOVEWD(X1,FROM,TOO);
FETIN=FETFIR+X1;
END
END
PROC WAIT;
IOBEGIN(WAIT)
#
** WAIT - WAIT (RECALL) FOR I/O TO FINISH.
*
* WAIT DELAYS THIS TASK UNTIL THE FET IS FOUND TO INDICATE
* COMPLETION OF A PREVIOUS I/O OPERATION. WAIT IS A NO-OP
* IF THE FET ALREADY INDICATES COMPLETION.
*
* ENTRY FETDUN - SHOWS WHETHER DELAYS REALLY NEEDED.
*
* EXIT FETDUN - TRUE.
*
* CALLS RECALL.
#
IF NOT FETDUN THEN
BEGIN
RECALL(FET);
END
IOEND
PROC DISOWNCACHE(PARM);
BEGIN
#
** DISOWNCACHE - RELEASE CACHE FRAME FROM OWNERSHIP.
*
* DISOWNCACHE IS USED TO RELEASE A PAGE FROM OWNERSHIP BY
* A LEVEL OF THE TREE STRUCTURE. RELEASE OF A PAGE MAKES
* IT ELIGIBLE TO BE FLUSHED OUT OF MEMORY (AND POSSIBLY
* REWRITTEN BACK TO DISK) ANYTIME THE FLUSHCACHE ROUTINE
* IS NEEDED. DISOWNCACHE ALSO SETS THE AGE FOR THE PAGE
* SO THAT FLUSHCACHE CAN DO ANY REWRITES IN THE MANDATORY
* ORDER OF RELEASE. THE GLOBAL AGE COUNTER IS INCREMENTED.
*
* ENTRY PARM - TREE LEVEL.
* CACHEOWNED[PARM] - TRUE FOR NON-TRIVIAL OPERATON.
* AGECOUNT - HIGHEST AGE YET ASSIGNED TO ANY PAGE.
*
* EXIT AGECOUNT - INCREMENTED.
* CACHEOWNED[PARM] - FALSE.
* CACHEAGE[PARM] - SETUP.
#
ITEM PARM;
IF PARM LS 0 OR NOT CACHEOWNED[PARM] THEN RETURN;
CACHEOWNED[PARM]=FALSE;
AGECOUNT=AGECOUNT+1;
CACHEAGE[PARM]=AGECOUNT;
END # OF DISOWNCACHE #
FUNC COUNTCACHE;
BEGIN
#
** COUNTCACHE - DETERMINE HOW MANY CACHE FRAMES ARE NOT USED.
*
* COUNTCACHE TELLS THE CALLER HOW MANY CACHE FRAMES ARE
* NEITHER OWNED BY ANY TREE LEVEL NOR CONTAIN DATA, WHICH
* HAS BEEN ALTERED IN THE CACHE BUT NOT YET ON DISK.
*
* ENTRY CACHEOWNED[ALL] - SETUP.
* CACHEDIRTY[ALL] - SETUP.
*
* EXIT COUNTCACHE - RESULT.
#
ITEM I, J;
J=0;
FOR I=0 STEP 1 UNTIL MAXCACHE DO IF NOT
(CACHEDIRTY[I] OR CACHEOWNED[I]) THEN J=J+1;
COUNTCACHE=J;
END # OF COUNTCACHE #
PROC SEARCHCACHE(M);
BEGIN
#
** SEARCHCACHE - DETERMINE BEST CACHEFRAME TO TAKE OVER.
*
* SEARCHCACHE TESTS ALL CACHE FRAMES FOR THOSE WHICH ARE
* AVAILABLE TO BE CLAIMED AND USED FOR DIFFERENT DATA THAN
* PRESENTLY IN MEMORY. THE OLDEST AVAILABLE FRAME IS
* CONSIDERED THE BEST CANDIDATE FOR RECYCLE. A FRAME
* IS AVAILABLE IF IT IS NEITHER CURRENTLY OWNED BY ANY TREE
* LEVEL NOR CONTAINS DATA ALTERED IN MEMORY BUT NOT ON DISK.
*
* ENTRY CACHEOWNED[ALL] - SETUP.
* CACHEDIRTY[ALL] - SETUP.
* CACHEAGE[ALL] - SETUP FOR UNOWNED, UNDIRTY FRAMES.
*
* EXIT M - CHOSEN FRAME.
*
* CALLS TRAGIC.
#
ITEM K,L,M;
M=-1;
K=999999999;
FOR L=0 STEP 1 UNTIL MAXCACHE DO
BEGIN
IF CACHEAGE[L] LS K AND
NOT (CACHEDIRTY[L] OR CACHEOWNED[L]) THEN
BEGIN # SEARCH OLDEST AVAIL PAGE #
M=L;
K=CACHEAGE[L];
END
END
IF M LS 0 THEN TRAGIC(" ALL WORKFILE BUFFERS ARE FULL.$");
END # OF SEARCHCACHE #
PROC ALLOCCACHE;
BEGIN
#
** ALLOCCACHE - LAY CLAIM TO A CACHE FRAME.
*
* ALLOCCACHE IS CALLED TO CLAIM A FRAME OF MEMORY IN THE
* DISK CACHE, AND ATTACH IT TO A PARTICULAR TREE LEVEL.
* THE DISK ADDRESS OF THE DISK DATA IS SAVED IN THE HEADER
* WORD OF THE CACHE FRAME, SO IT CAN BE MATCHED FOR
* PURPOSES OF AVOIDING DISK READS.
*
* ENTRY PL - CURRENT TREE LEVEL.
* PADA[PL] - DISK ADDRESS ASSIGNED FOR REAL DATA.
*
* EXIT PACACHE[PL] - WHICH CACHE FRAME WAS SELECTED.
* BFDA[PL] - DISK ADDRESS IS STORED IN CACHE FRAME.
*
* CALLS SEARCHCACHE.
#
ITEM TMP1;
SEARCHCACHE(TMP1);
PACACHE[PL]=TMP1;
CACHEOWNED[TMP1]=TRUE;
BFDA[TMP1]=PADA[PL];
END # OF ALLOCCACHE #
PROC RECLAIMCACHE;
BEGIN
#
** RECLAIMCACHE - IDENTIFY CACHE FRAME WRONGLY RELEASED.
*
* RECLAIMCACHE IS USED TO MATCH A TREE LEVEL TO A CACHE
* FRAME WHICH CONTAINS THE VIRTUAL DISK SECTOR FOR THE
* TREE LEVEL, AND REESTABLISH OWNERSHIP. THIS IS DONE
* BECAUSE THERE ARE SITUATIONS WHERE A PAGE MUST BE
* TEMPORARILY RELEASED FROM OWNERSHIP SO IT CAN BE WRITTEN
* TO DISK, BUT FORFEITURE OF OWNERSHIP IS NOT INTENDED
* FOR ANY OTHER REASON. RECLAIMCACHE CAN BE CALLED AFTER
* A SEQUENCE OF DISOWNCACHE AND FLUSHCACHE, AND THE TREE
* LEVELS ARE THEN AS THEY WERE BUT DATA ALTERATIONS ARE
* UP TO DATE ON DISK. REMEMBER THAT THE ORDER IN WHICH PAGES
* CAN BE UPDATED TO DISK DEPENDS ON THE TREE HIERARCHY, SO
* IT IS SOMETIMES NECESSARY TO GO THRU THIS SEQUENCE WITH ONE
* TREE LEVEL IN ORDER TO ACHIEVE THE ACTUAL PURPOSE OF
* WRITING A DIFFERENCE TREE LEVEL TO DISK.
*
* ENTRY PL - CURRENT TREE LEVEL.
* PALVL - DEEPEST VALID TREE LEVEL.
* CACHEOWNED[ALL], PACACHE[ALL], BFDA[ALL] - SETUP.
*
* EXIT CACHEOWNED[PL THRU PALVL] - TRUE.
*
* CALLS TRAGIC.
*
* USES TPL.
#
FOR TPL=PL STEP 1 UNTIL PALVL DO
BEGIN
IF CACHEOWNED[PACACHE[TPL]] THEN TEST TPL;
IF BFDA[PACACHE[TPL]] NQ PADA[TPL] THEN
BEGIN
TRAGIC(" WORKFILE BUFFER CONTAINS WRONG TEXT.$");
END
CACHEOWNED[PACACHE[TPL]]=TRUE;
END
END # OF RECLAIMCACHE #
PAGE
PROC MOVEIO;
BEGIN
#
** MOVEIO - INITIATE ANY CIO CALLS AS NEEDED.
*
* MOVEIO IS CALLED ANY TIME CIO CALLS ARE KNOWN TO BE
* NECESSARY, AND CAN ALSO BE CALLED ANY TIME CIO CALLS MIGHT
* BE DESIRABLE. MOVEIO BECOMES A NO-OP IF A CIO CALL IS
* ALREADY IN PROGRESS, OR IF NO CIO CALL IS NEEDED. THE
* CALLER IS RESPONSIBLE TO PREPARE THE FET AND CIRCULAR
* BUFFER IN ALL WAYS EXCEPT THE CHOICE OF CIO FUNCTION CODE.
*
* WRITES ARE INITIATED WHEN THE IOWRITE FLAG HAS BEEN
* PREPARED TO INDICATE THAT WRITES ARE NEEDED, THE FET IS NOT
* PREVIOUSLY INTERLOCKED, AND THE CIRCULAR BUFFER CONTAINS
* SOME DATA. READS ARE INITIATED WHEN THE IOREAD FLAG HAS
* BEEN PREPARED TO INDICATE THAT READS ARE NEEDED, THE FET IS
* NOT PREVIOUSLY INTERLOCKED, AND THE RDIN/RDOUT POINTERS
* INDICATE THERE IS A NON-TRIVIAL LIST OF PRUS STILL AWAITING
* READS.
*
* MOVEIO ALSO PERFORMS THE FUNCTION OF RECOGNIZING THAT ALL
* DESIRED I/O HAS BEEN COMPLETED, SUCH AS ALL WRITEABLE DATA
* REMOVED FROM CIRCULAR BUFFER OR ALL READABLE PRUS REMOVED
* FROM READ LIST. UNDER THESE CONDITIONS, MOVEIO CLEARS THE
* IOXXXX FLAGS SO THAT NO FURTHER DESIRE FOR CIO CALLS WILL
* BE INDICATED, UNTIL AND UNLESS SOMEONE TURNS THE FLAGS ON
* AGAIN.
*
* IN THE CASES WHERE THE CIRCULAR BUFFER CONTAINS DATA TO BE
* WRITTEN, THE ACTUAL CIO FUNCTION CODE IS CHOSEN FROM FOUR
* POSSIBILITIES BASED ON EXTENSION/ALTERATION OF THE FILE AND
* NEED OR NON-NEED FOR END-OF-RECORD TO BE WRITTEN. WHEN THE
* DISK IS TO BE READ, THE FUNCTION CODE IS ALWAYS "RPHRLS",
* AKA "READ PRUS RANDOMLY BY LIST".
*
* NOTE THAT MOVEIO NEVER WAITS FOR ANY CIO TO COMPLETE, THUS
* MOVEIO IS NOT A REENTRANT ROUTINE.
*
* ENTRY FETDUN - WHETHER ANY PREVIOUS I/O IS DONE.
* IOACT - WHETHER I/O SHOULD BE DONE SOMETIME SOON.
* IOWRITE - WHETHER I/O NEEDED IS A WRITE.
* IOREAD - WHETHER I/O NEEDED IS A READ.
* IOAPEND - WHETHER WRITE IS TO EXTEND FILE OR ALTER.
* IOWRTEOR - WHETHER WRITE IS TO PRODUCE EOR.
* FETIN, FETOUT - INDICATE WHETHER UNWRITTEN DATA IS
* STILL IN THE BUFFER.
* RDIN, RDOUT - INDICATE WHETHER THERE ARE PRUS
* IDENTIFIED IN READ LIST AS DESIRABLE TO READ,
* BUT NOT YET READ IN.
* FETLA, FETLAW - CURRENT POSITION IN READ LIST.
* RDLIST[ALL] - SETUP.
* CIOCOUNT - ACCOUNTING ACCUMULATOR.
* MAXDA, LASTDA - USED TO DETERMINE VALIDITY OF
* WRITE PARAMETERS, WHEN CONSISTENCY CHECKING
* IS ENABLED IN THE SOURCE CODE.
* FETCRI - USED WITH MAXDA AND LASTDA FOR VALIDITY.
*
* EXIT FETDUN - FALSE IF A CIO CALL STARTED UP.
* CIOCOUNT - INCREMENTED IF CIO CALLED.
* IOACT, IOREAD, IOWRITE, IOAPEND - CLEARED IF AND
* ONLY IF ALL MOVEIO DETERMINES THAT ALL
* PREVIOUSLY REQUESTED I/O HAS COMPLETED.
*
* CALLS WRITE, REWRITE, WRITER, REWRITR, RPHRLS, TRAGIC.
#
ITEM I;
SWITCH CALLWRITE WRTFUNC,RWRTFUNC,WRTRFUNC,RWRTRFUNC;
IF IOACT AND FETDUN THEN
BEGIN
IF IOWRITE AND FETIN NQ FETOUT THEN # WRITE NOT DONE #
BEGIN
CIOCOUNT = CIOCOUNT + 1;
I=1;
IF IOAPEND THEN I=0;
IF IOWRTEOR THEN I=I+2;
IOWRTEOR=FALSE;
GOTO CALLWRITE[I];
WRTFUNC:
WRITE(FET,0);
RETURN;
RWRTFUNC:
FETEP = TRUE; # SET ERROR PROCESSING BIT #
REWRITE(FET,0);
FETEP = FALSE; # CLEAR ERROR PROCESSING BIT #
IF FETAT EQ O"22" THEN
BEGIN # IF FATAL ERROR #
TRAGIC(" WORKFILE BUFFER EMPTY ON REWRITE.$");
END
RETURN;
WRTRFUNC:
WRITER(FET,0);
RETURN;
RWRTRFUNC:
FETEP = TRUE; # SET ERROR PROCESSING BIT #
REWRITR(FET,0);
FETEP = FALSE; # CLEAR ERROR PROCESSING BIT #
IF FETAT EQ O"22" THEN
BEGIN # IF FATAL ERROR #
TRAGIC(" WORKFILE BUFFER EMPTY ON REWRITER. $");
END
RETURN;
END
ELSE IF IOREAD AND RDIN NQ RDOUT THEN # READ NOT DONE #
BEGIN
IF FETLA EQ LOC(RDLIST[RDLIM])
THEN FETLAW = LOC(RDLIST[0]);
IF FETLA NQ LOC(RDLIST[RDIN]) THEN
BEGIN
CIOCOUNT = CIOCOUNT + 1;
RPHRLS(FET,0);
END
END
ELSE # READ AND WRITE DONE #
BEGIN
CONTROL IFGQ PARANOIA,5;
IF IOWRITE THEN # CHECK WRITE CRI #
BEGIN
IF IOAPEND THEN
BEGIN
IF FETCRI NQ MAXDA+1 THEN
BEGIN
TRAGIC(" FILE SIZE INCORRECT.$");
END
END
ELSE
BEGIN
IF FETCRI NQ LASTDA+1 THEN
BEGIN
TRAGIC(" RANDOM ADDRESS INCORRECT.$");
END
END
END
CONTROL FI;
IOACT = FALSE;
IOWRITE = FALSE; IOAPEND = FALSE;
IOREAD = FALSE;
END
END
END
PAGE
PROC PAUSEIO;
IOBEGIN(PAUSEIO)
#
** PAUSEIO - AWAIT AND/OR ENFORCE CIO COMPLETION.
*
* PAUSEIO IS CALLED FOR ONE OF THREE REASONS: (1) THE CALLER
* HAS STARTED SOME I/O VIA MOVEIO AND NEEDS TO WAIT FOR IT TO
* FINISH, OR (2) THE CALLER NEEDS SOME OLD I/O TO FINISH SO
* THAT THE FET CAN BE USED TO INITIATE NEW DESIRED I/O, OR
* (3) THE CALLER NEEDS SOME OLD I/O TO FINISH, TO FREE THE
* CIRCULAR BUFFER AND READ LIST SO THOSE MEMORY RESOURCES CAN
* BE MANAGED AND POSSIBLY RE-ASSIGNED.
*
* PAUSEIO THEREFORE GUARANTEES THAT ANY WRITEABLE DATA IN THE
* CIRCULAR BUFFER REALLY GETS WRITTEN, AND THAT ANY INITIATED
* READS ARE FINISHED WITH THE DATA DISCARDED AND THE CIRCULAR
* BUFFER EMPTY. PAUSEIO IS ALLOWED TO HANDLE READS WHICH
* HAVE BEEN REQUESTED BUT NOT YET INITIATED BY SIMPLE
* CANCELLATION. THUS, PAUSEIO IS MEANINGFUL TO THE TYPE 1
* CALLER ONLY FOR THE WRITE DIRECTION - THE READ DIRECTION IS
* A THROWAWAY WHILE THE WRITE DIRECTION HAS ASSURANCE OF
* COMPLETION.
*
* SINCE THE TYPE 3 CALLER OFTEN DOES NOT KNOW HOW (OR IS NOT
* ALLOWED) TO DETERMINE IF THE MEMORY RESOURCES ARE ALREADY
* AVAILABLE FOR REASSIGNMENT, PAUSEIO CAN BE CALLED CASUALLY,
* AND WILL NO-OP ITSELF IF THE DESIRED CONDITIONS ARE ALREADY
* TRUE.
*
* PAUSEIO ALSO ZEROES OUT FETCHGUIDE. THIS IS USED TO CONTROL
* THE QUANTITY OF DATA PREFETCHED AS A FUNCTION OF HOW MUCH
* SUCCESS WE HAVE HAD IN UTILIZING PREFETCHED DATA IN RECENT
* HISTORY. A PAUSEIO CALL IS REGARDED AS AN ADMISSION THAT
* ANY CURRENT PREFETCHING HAS LOST ALL RELEVANCE.
*
* ENTRY IOREAD - WHETHER WE WERE PREVIOUSLY TRYING TO READ
* SOMETHING WHICH MUST NOW BE THROWN AWAY.
* IOWRITE - WHETHER WE HAD PREVIOUSLY REQUESTED THAT
* SOMETHING BE WRITTEN, WHICH MUST NOW BE BROUGHT
* TO FASTEST POSSIBLE COMPLETION.
* RDIN, RDOUT - BRACKET ACTIVE SEGMENT OF READ LIST.
* FETIN, FETOUT - BRACKET CIRCULAR BUFFER DATA.
*
* EXIT IOREAD, IOWRITE, IOACT - GUARANTEED FALSE.
* FET - CIRCULAR BUFFER GUARANTEED EMPTY.
* RDLIST[ALL], RDIN, RDOUT - GUARANTEED EMPTY.
* FETCHGUIDE - ZEROED.
*
* CALLS WAIT, MOVEIO.
#
ITEM I, J, K;
FETCHGUIDE=0;
IF IOREAD THEN
BEGIN
WAIT; # FOR ACTIVE TRANSFER TO FINISH #
FOR I = RDIN WHILE I NQ RDOUT DO # CLEAN OUT READLIST #
BEGIN
I = I - 1;
IF I LS 0 THEN I = RDLIM - 1;
RDLIST[I] = 0;
END
FETOUT = FETIN; # ABANDON CIRCULAR BUFFER CONTENT #
IOACT = FALSE;
IOREAD = FALSE;
END
ELSE IF IOWRITE THEN
BEGIN
WHYLE IOWRITE DO
BEGIN
MOVEIO;
WAIT;
END
END
IOEND
PAGE
PROC SETREAD((DA));
BEGIN
#
** SETREAD - ADD DISK ADDRESS TO READ LIST.
*
* SETREAD PERFORMS FET INITIALIZATION AS NEEDED, TO ASSURE
* THAT THE BUFFER AND FET ARE TURNED AROUND TO THE INPUT
* DIRECTION, AND APPENDS THE SPECIFIED DISK ADDRESS ONTO THE
* READ LIST. THE CALLER IS RESPONSIBLE TO ASSURE THAT
* SETREAD WILL NOT ENCOUNTER A FULL READ LIST. THE CALLER IS
* ALLOWED TO CALL SETREAD FOR AN ADDRESS ALREADY LISTED, IN
* WHICH CASE SETREAD NO-OPS ITSELF. THE USER IS REQUIRED TO
* PROVIDE AN IN-BOUNDS DISK ADDRESS, AND TO HAVE USED PAUSEIO
* TO COMPLETE ANY PREVIOUS OUTPUT ACTIVITIES.
*
* ENTRY DA - SECTOR NUMBER FOR DESIRED PRU.
* IOREAD - WHETHER FET ALREADY INITIALIZED FOR INPUT.
* IOWRITE, MAXDA - WHEN CONSISTENCY CHECKING IS ENABLED,
* THESE ARE USED TO VERIFY VALIDITY.
* RDIN, RDOUT - BRACKET ACTIVE SEGMENT OF READ LIST.
* RDLIST[ALL] - SETUP FOR EXISTING DISK ADDRESSES
* AND WITH ZEROES IN EXCESS LIST AREAS.
*
* EXIT IOREAD, IOACT - TRUE.
* FET AND CIRCULAR BUFFER - VALID FOR MOVEIO CALL.
* RDIN, RDOUT - UPDATED FOR ENLARGED READ LIST.
* RDLIST[NEW RDIN-1] - DISK ADDRESS STORED HERE.
*
* CALLS TRAGIC, INITFET.
#
ITEM DA;
ITEM P;
CONTROL IFGQ PARANOIA,5;
IF DA LQ 0 OR DA GR MAXDA THEN
BEGIN
TRAGIC(" OUT OF BOUNDS PRU ADDRESS ON READ (1).$");
END
IF IOWRITE THEN TRAGIC(" WORKFILE BUFFER FULL ON READ.$");
CONTROL FI;
IF NOT IOREAD THEN
BEGIN
INITFET(DISK,DSKSIZ);
RDIN = 0;
RDOUT = 0;
FETLAW = LOC(RDLIST[0]);
IOACT = TRUE;
IOREAD = TRUE;
END
FOR P = RDOUT WHILE RDLIST[P] NQ DA DO
BEGIN
IF P EQ RDIN THEN
BEGIN
RDLIST[RDIN] = DA;
RDIN = RDIN + 1;
IF RDIN EQ RDLIM THEN RDIN = 0;
CONTROL IFGQ PARANOIA,5;
IF RDIN EQ RDOUT THEN TRAGIC(" RPHRLS LIST BUFFER IS FULL.$");
CONTROL FI;
END
ELSE
BEGIN
P = P + 1;
IF P EQ RDLIM THEN P = 0;
END
END
END
PAGE
PROC PREFETCH((DD));
BEGIN
#
** ARRANGE READ LIST FOR SEVERAL PREFETCHES.
*
* PREFETCH SHOULD BE CALLED ONCE PER SECTOR PROCESSED, BY
* THOSE PROCESSES WHICH ACT UPON POTENTIALLY LARGE SERIES OF
* DISK SECTORS IN EITHER THE FORWARD OR BACKWARD LOGICAL
* ORDER OF TEXT IN THE FILE. THE LOGICAL ORDER CAN BE QUITE
* DIFFERENT FROM THE PHYSICAL ORDER DUE TO THE TREE LINKAGES.
*
* PREFETCH DETERMINES SEVERAL DISK ADDRESSES WHOSE NEED IS
* ANTICIPATED ON BEHALF OF THE CALLER, AND USES SETREAD TO
* ARRANGE THAT SUCH SECTORS CAN BE READ IN THE LOGICAL ORDER.
* IT IS THE CALLING PROCESSES RESPONSIBILITY TO OCCASIONALLY
* CALL MOVEIO FOR ACTUAL INITIATION OF CIO.
*
* TO MINIMIZE OVERHEAD, PREFETCH CHOOSES A DEPTH OF
* PREFETCHING BASED ON APPARENT RECENT SUCCESS IN UTILIZING
* PREFETCHED DATA, AND SUCCESSIVELY UPDATES THE DEPTH OF
* PREFETCH. THIS DEPTH INDICATOR MAY BE CLEARED BY PAUSEIO
* IN RECOGNITION OF THE ABANDONMENT OF A READ, OR BY THE
* CALLER WHEN A LOGICAL BREAK IN SEQUENTIAL PROCESSING IS
* KNOWN TO OCCUR.
*
* TO MINIMIZE OVERHEAD, PREFETCH ATTEMPTS TO PRODUCE EITHER
* ZERO OR SEVERAL NEW ENTRIES IN THE READ LIST, AND ATTEMPTS
* TO NO-OP ITSELF WHEN IT IS RECOGNIZED THAT ONLY ONE OR A
* FEW ENTRIES COULD BE PRODUCED. PREFETCH MUST KEEP THE
* DEPTH OF PREFETCHING WITHIN THE LEGAL BOUNDS OF THE SETREAD
* ROUTINE.
*
* THE DEPTH OF PREFETCHING IS LIMITED TO THE SMALLEST OF THE
* FOLLOWING FACTORS - (1) THE FETCHGUIDE QUANTITY, WHICH
* REPRESENTS RECENT SUCCESS, (2) THE LEGAL LIMITS IMPOSED BY
* SETREAD, AND (3) ONE AND A HALF TIMES THE SIZE OF THE
* CIRCULAR BUFFER. PREFETCHING IS SET UP ONLY WHEN THE
* EXISTING QUEUE IS LESS THAN HALF THE DESIRED DEPTH. FOR
* LARGER QUEUES, IT IS ANTICIPATED THE QUEUE WILL BE SHRUNK
* BACK DOWN AT SOME FUTURE CALL TO PREFETCH, AT WHICH TIME
* A LARGE BATCH OF QUEUE ENTIRES CAN BE PRODUCED.
*
* ONCE PREFETCH DECIDES TO SET UP A BATCH OF READ LIST QUEUE
* ENTRIES, IT IDENTIFIES THE SECTORS TO BE READ AS ALL DATA
* (BOTTOM) LEVEL PRUS, POINTED TO BY THE NEXT-TO-BOTTOM
* DIRECTORY, LOGICALLY ADJACENT TO THE CURRENT DATA SECTOR IN
* THE SPECIFIED DIRECTION. PREFETCH ALSO CAN QUEUE THE NEXT
* LOGICALLY ADJACENT NEXT-TO-BOTTOM DIRECTORY, BY LOOKING
* ANOTHER LEVEL TOWARDS THE ROOT OF THE TREE.
*
* ENTRY FETCHGUIDE - CURRENT GUIDELINE FOR DEPTH.
* RDIN, RDOUT - DESCRIBE CURRENT READ LIST.
* DSKSIZ - CIRCULAR BUFFER SIZE.
* PALVL - DEEPEST TREE LEVEL CURRENTLY VALID.
* PACACHE[ALL] - CACHE FRAMES AT EACH TREE LEVEL.
* BFPRU[ALL] - DISK ADDRESSES FOR EACH FRAME.
* PAFINAL[ALL] - LAST WORDS IN EACH SECTOR OF TREE.
* IOWRITE - WHETHER FET INTERLOCKED FOR OUTPUT.
*
* EXIT RDLIST[ALL], RDIN, RDOUT - UPDATED.
* FETCHGUIDE - INCREMENTED UNLESS DIRECTORY INCLUDED.
*
* CALLS LSTAVAIL, LSTUSAGE, MIN, MAX, SETREAD.
*
* USES P<PRU> WITH RESTORATION.
#
ITEM DD,N;
ITEM DSTOP;
ITEM DONE B;
ITEM TMPPRU;
ITEM TMPPL,TMPD;
ITEM I;
IF FETCHGUIDE LS 0 OR IOWRITE THEN RETURN;
FETCHGUIDE=FETCHGUIDE+2;
N=LSTAVAIL;
N=MIN(N-3,3*DSKSIZ/O"200");
N=MIN(N,FETCHGUIDE);
IF LSTUSAGE LQ N/2 THEN
BEGIN
TMPPRU = P<PRU>;
DONE = FALSE;
FOR TMPPL = PALVL-1 STEP -1 WHILE TMPPL GQ 0 AND NOT DONE DO
BEGIN
TMPD = PADISP[TMPPL] + DD;
P<PRU> = LOC(BFPRU[PACACHE[TMPPL]]);
IF DD GR 0 THEN DSTOP = MIN(PAFINAL[TMPPL]+1,TMPD+N);
ELSE DSTOP = MAX(0,TMPD-N);
IF DSTOP EQ TMPD+N*DD THEN DONE = TRUE;
ELSE FETCHGUIDE=-1;
N=1;
FOR TMPD = TMPD STEP DD WHILE TMPD NQ DSTOP DO
BEGIN
FOR I=0 STEP 1 UNTIL MAXCACHE DO
BEGIN
IF BFDA[I] EQ DIRDA[TMPD] THEN TEST TMPD;
END
SETREAD(DIRDA[TMPD]);
END
END
P<PRU> = TMPPRU;
END
END
PAGE
PROC READPA;
IOBEGIN(READPA)
#
** READPA - READ NEXT DEEPER TREE PATH SECTOR.
*
* READPA IS USED TO ACCESS THE SECTOR OF DIRECTORY OR DATA
* FOR A SPECIFIED LEVEL OF THE TREE. THE HIGHER LEVELS OF
* THE TREE MUST ALREADY BE PROPERLY FILLED IN. ANY RESIDUAL
* SECTOR IN THE CURRENT TREE LEVEL MUST BE PROPERLY RELEASED
* FROM OWNERSHIP, SO THAT READPA WILL NOT NEED TO CONCERN
* ITSELF WITH THE UPDATING OF ALTERED DATA TO DISK. THE
* CALLER DOES NOT NEED TO SETUP THE FET AND CIRCULAR BUFFER
* FOR INPUT, AND READPA WILL TAKE CARE OF THAT IF NEEDED.
*
* WHEN POSSIBLE, READPA WILL ACCESS THE DESIRED DISK SECTOR
* BY SIMPLY IDENTIFYING IT IN CACHE AND CLAIMING OWNERSHIP OF
* THE RIGHT CACHE FRAME. WHEN THE CACHE DOES NOT PROVIDE THE
* NECESSARY DATA, READPA WILL READ IT FROM DISK. IF THE FET
* AND CIRCULAR BUFFER ARE PREVIOUSLY INTERLOCKED FOR PENDING
* OUTPUT, READPA WILL ASSURE THAT THE UNFINISHED WRITES GET
* DONE. IF THE FET IS ALREADY SETUP FOR INPUT BUT HAS THE
* WRONG DISK ADDRESS COMING IN AT THE FRONT OF THE CIRCULAR
* BUFFER, READPA WILL DISCARD SUCH INCORRECT INPUT AND
* REINITIALIZE TO INPUT THE RIGHT DISK ADDRESS. IF THE FET
* IS ALREADY SET UP FOR INPUT WITH THE RIGHT SECTOR IN OR
* COMING INTO THE CIRCULAR BUFFER, READPA WILL MAKE USE OF
* THAT CIO ACTIVITY.
*
* IF READPA READS THE SECTOR FROM DISK, IT CLEANS UP THE
* RANDOM READ LIST AFTERWARDS, ATTEMPTING TO SHUFFLE THE
* ACTIVE SEGMENT OF THE READ LIST TO MAXIMIZE THE REMAINING
* LIST CAPACITY BEFORE WRAPAROUND. READPA ALSO CHECKS TO SEE
* IF THE BUFFER IS SUFFICIENTLY EMPTY WITH A SUFFICIENTLY
* FULL PREFETCH LIST SO THAT ANTICIPATORY READS SHOULD BE
* STARTED.
*
* ENTRY TEMP - SECTOR NUMBER.
* PL - CURRENT PATH LEVEL.
* PACACHE[ALL] - CACHE FRAMES CURRENTLY OWNED.
* CACHEOWNED[ALL] - SETUP.
* BFDA[ALL] - IDENTIFY SECTOR ADDRESSES EACH FRAME.
* BFHEAD[ALL] - HEADER WORDS FOR EACH CACHED SECTOR.
* ACCTPRU - PRU TRANSFER COUNT.
* IOWRITE - WHETHER FET ALREADY SET FOR WRITES.
* RDIN, RDOUT - READ LIST BRACKETING.
* RDLIST[ALL] - SETUP.
* IOREAD - WHETHER SOME READ ALREADY SCHEDULED.
* FETCHGUIDE - PREFETCH QUANTITY GUIDELINE.
* QUARTER - APPROX ONE FOURTH BUFFER SIZE.
*
* EXIT PACACHE[PL] - INDICATES CACHE FRAME WITH SECTOR.
* CACHEOWNED[PACACHE[PL]] - TRUE.
* ANY OLD CACHE FRAMES MAY HAVE BEEN FLUSHED TO
* DISK AND/OR REASSIGNED IN MEMORY.
* PAHEAD[PL] - HEADER WORD.
* IOWRITE - FALSE IF NOT CACHE HIT.
* IOREAD - POSSIBLY CHANGED.
* RDOUT - ADVANCED IF DISK READ DONE.
* RDIN - RDIN AND RDOUT POSSIBLY RELOCATED IF READ
* DONE AND CIO TERMINATED.
* ACCTPRU - INCREMENTED IF NO CACHE HIT.
* FETCHGUIDE - POSSIBLY NEW VALUE.
* ANTICIPATORY CIO POSSIBLY CALLED.
*
* CALLS TRAGIC, COUNTCACHE, FLUSHCACHE, DISOWNCACHE,
* WIOSTAT, PAUSEIO, ALLOCCACHE, SETREAD, MOVEIO,
* DELAY, RECALL, RDWBUF, BUFUSAGE.
*
* USES P<TOO>.
#
ITEM X1;
CONTROL IFGQ PARANOIA,5;
IF PADIRTY[PL] THEN TRAGIC(" ALTERED PRU WAS NOT REWRITTEN.$");
CONTROL FI;
IF COUNTCACHE LQ 2 THEN FLUSHCACHE;
# FIRST RELEASE CURRENTLY OWNED SECTOR IF ANY #
DISOWNCACHE(PACACHE[PL]);
FOR X1=0 STEP 1 UNTIL MAXCACHE DO
BEGIN # TRY TO FIND IN CACHE #
IF BFDA[X1] EQ TEMP THEN # GOT IT #
BEGIN
CONTROL IFGQ PARANOIA,5;
IF CACHEOWNED[X1] THEN
BEGIN
TRAGIC(" WORKFILE BUFFER ALLOCATED TWICE.$");
END
CONTROL FI;
PACACHE[PL]=X1;
CACHEOWNED[X1]=TRUE;
PAHEAD[PL] = BFHEAD[X1];
CONTROL IFEQ MULTI,1;
WIOSTAT(1); # ACCUMULATE CACHE HITS #
CONTROL FI;
IORET
END
END
CONTROL IFEQ MULTI,1;
WIOSTAT(2); # ACCUMULATE SECTORS READ #
ACCTPRU=ACCTPRU+1;
CONTROL FI;
IF IOWRITE THEN # DO ANY PENDING WRITES #
BEGIN
PAUSEIO;
CONTROL IFEQ MULTI,1;
WIOSTAT(3); # ACCUMULATE FLUSHED WRITES #
CONTROL FI;
END
CONTROL IFGQ PARANOIA,5;
IF TEMP LQ 0 OR TEMP GR MAXDA THEN
BEGIN
TRAGIC(" OUT OF BOUNDS PRU ADDRESS ON READ (2).$");
END
CONTROL FI;
PADA[PL] = TEMP;
ALLOCCACHE;
IF IOREAD AND RDLIST[RDOUT] NQ TEMP THEN
BEGIN
PAUSEIO;
CONTROL IFEQ MULTI,1;
WIOSTAT(4); # ACCUMULATE READ REJECTS #
CONTROL FI;
END
IF NOT IOREAD THEN
BEGIN
SETREAD(TEMP);
FETCHGUIDE=2;
END
WHYLE FETIN EQ FETOUT DO
BEGIN
CONTROL IFEQ MULTI,1;
WIOSTAT(10); # ACCUMULATE NOT ALREADY IN BUFFER #
CONTROL FI;
MOVEIO;
CONTROL IFEQ MULTI,1;
WAIT;
CONTROL FI;
CONTROL IFEQ SINGLE,1;
IF NOT FETDUN THEN DELAY;
CONTROL FI;
END
P<TOO> = LOC(BFPRU[PACACHE[PL]]);
RDWBUF(O"100");
PAHEAD[PL] = BFHEAD[PACACHE[PL]];
CONTROL IFGQ PARANOIA,5;
IF TEMP NQ RDLIST[RDOUT] THEN
BEGIN
TRAGIC(" RANDOM READ LIST INCORRECT.$");
END
IF PADA[PL] NQ TEMP THEN TRAGIC(" PRU CONTENT INCORRECT.$");
IF PADIRTY[PL] THEN
BEGIN
TRAGIC(" STATUS FLAGS SHOULD HAVE BEEN CLEARED.$");
END
CONTROL FI;
RDLIST[RDOUT] = 0;
IF RDOUT EQ RDLIM-1 THEN RDOUT = 0;
ELSE RDOUT = RDOUT + 1;
IF FETDUN AND RDIN EQ RDOUT THEN
BEGIN
RDIN=0;
RDOUT=0;
FETCHGUIDE=ABS(FETCHGUIDE);
END
IF BUFUSAGE LQ QUARTER THEN MOVEIO;
IOEND
PAGE
PROC ALLOC;
BEGIN
#
** ALLOC - ASSIGN DISK ADDRESS.
*
* ALLOC IS CALLED WHEN A NEW SECTOR MUST BE CREATED. THE
* NEXT PRU OFF THE LOGICAL END OF THE FILE IS CHOSEN. NOTE
* THAT THE LOGICAL END OF THE FILE REPRESENTS THOSE ADDRESSES
* WHICH HAVE BEEN ALLOCATED. THE PHYSICAL END OF THE FILE
* REPRESENTS THOSE ADDRESSES ACTUALLY WRITTEN ONTO THE FILE,
* AND CAN BE A SMALLER NUMBER THAN THE LOGICAL END.
*
* ENTRY PL - CURRENT PATH LEVEL.
* DANEXT - ALLOCATION THRESHOLD.
*
* EXIT PADA[PL] - SETUP.
* DANEXT - INCREMENTED.
*
* CALLS TRAGIC.
#
CONTROL IFGQ PARANOIA,5;
IF PADIRTY[PL] THEN TRAGIC(" OLD SECTOR MUST BE WRITTEN.$");
CONTROL FI;
PADA[PL] = DANEXT;
DANEXT = DANEXT + 1;
END
PROC DEALLOC;
BEGIN
#
** DEALLOC - DISCARD DISK SECTOR.
*
* DEALLOC IS USED TO DISCARD A DISK SECTOR WHICH CAN NO
* LONGER BE USED. THIS CAN BE NEEDED WHEN A SECTOR HAS
* OVERFLOWED AND HAS BEEN SPLIT INTO TWO NEW SECTORS. IF THE
* ROOT SECTOR NEEDS TO BE SPLIT, A COMPLETE NEW TREE LEVEL IS
* CREATED WITH NEW SECTORS, THE ROOT SECTOR IS MODIFIED TO
* BECOME A DIRECTORY FOR THE NEW LEVEL, AND NO DEALLOCATION
* IS NEEDED. IF A NON-ROOT SECTOR NEEDS TO BE SPLIT EXACTLY
* AT (JUST AFTER) ITS EXISTING LAST FIELD, THEN THAT SECTOR
* IS PRESERVED WITHOUT DEALLOCATION AND A NEW SECTOR IS
* CREATED FOR THE OVERFLOW CONTENT. BUT IF A NON-ROOT SECTOR
* MUST BE SPLIT SOMEWHERE IN THE MIDDLE OF ITS EXISTING
* CONTENT, IT MUST BE DEALLOCATED AND BE REPLACED BY TWO
* COMPLETELY NEW SECTORS.
*
* THE REASON FOR DEALLOCATION IS THAT NOTHING MUST BE ALLOWED
* TO BE POINTED TO UNTIL AND UNLESS IT ALREADY EXISTS, AS
* UPDATED TO DISK ALTHOUGH NOT NECESSARILY AS CACHED IN
* MEMORY. SINCE A NON-ROOT SECTOR IS POINTED TO BE
* HIGHER-LEVEL DIRECOTRIES, IT CANNOT BE TOTALLY REFORMATTED
* IN PLACE, THEREFORE IT IS LEFT AS IS AND NEW SECTORS
* REPLACE IT. THE DIRECTORIES WHICH POINT TO THE DISCARDED
* SECTOR WILL EVENTUALLY BE UPDATED THEMSELVES. UNTIL THEN,
* THOSE HIGHER DIRECTORIES MAY BE OUT OF DATE BUT AT LEAST
* POINT TO A VALID DATA STRUCTURE.
*
* ENTRY PL - CURRENT TREE LEVEL.
*
* EXIT PADA[PL] - CLEARED AWAY.
*
* CALLS TRAGIC.
#
CONTROL IFGQ PARANOIA,5;
IF PL EQ 0 OR PADA[PL] EQ 0 THEN
BEGIN
TRAGIC(" SECTOR CANNOT BE DEALLOCATED.$");
END
CONTROL FI;
PADA[PL] = 0;
PADIRTY[PL] = FALSE;
END
PROC SETWRITE(PARM);
BEGIN
#
** SETWRITE - SCHEDULE EXTENSION/ALTERATION OF FILE.
*
* SETWRITE INITIALIZES THE FET FOR OUTPUT. IT SETS THE IOACT
* FLAG TO INDICATE THAT I/O IS SCHEDULED, SETS IOWRITE TO
* INDICATE THAT THE I/O SHALL BE AN OUTPUT FUNCTION, AND
* SETS OR CLEARS IOAPEND ACCORDING TO WHETHER THE SECTOR TO
* BE WRITTEN IS WITHIN THE CURRENT PHYSICAL BOUNDARIES OF THE
* FILE OR REPRESENTS AN EXTENSION OF PHYSICAL SIZE.
*
* ENTRY PARM - DISK ADDRESS TO BE WRITTEN.
* MAXDA - CURRENT PHYSICAL SIZE OF FILE.
*
* EXIT FET - INITIALIZED WITH EMPTY CIRCULAR BUFFER.
* IOACT - TRUE.
* IOWRITE - TRUE.
* IOAPEND - TRUE OR FALSE BASED ON PARM AND MAXDA.
* LASTDA - SET TO KEEP TRACK OF WHERE WE ARE IN FILE.
* FETRR - SETUP.
*
* CALLS INITFET.
#
ITEM PARM;
INITFET(DISK,DSKSIZ);
IOACT = TRUE;
IOWRITE = TRUE;
IF PARM GR MAXDA THEN
BEGIN
IOAPEND = TRUE;
FETRR = LOC(DUMB);
END
ELSE
BEGIN
IOAPEND = FALSE;
FETRR = PARM;
LASTDA = PARM - 1;
END
END
PAGE
PROC TRYWRITE(PARM,FLAG);
BEGIN
#
** TRYWRITE - TRANSMIT SECTOR TO CIRCULAR BUFFER.
*
* TRYWRITE ATTEMPTS TO TRANSMIT A SECTOR TO THE CIRCULAR
* BUFFER. IT MAY FAIL TO DO SO IF THE CIRCULAR BUFFER ALREADY
* CONTAINS SECTORS FOR WHICH THIS SECTOR IS NOT CONTIGUOUS.
* THE CALLER SHOULD CALL PAUSEIO WHEN TRYWRITE FAILS, AND
* THEN CALL THIS ROUTINE AGAIN. TRYWRITE CANNOT FAIL IF
* PAUSEIO IS USED.
*
* ENTRY P<FROM> - POINTS TO TEXT OF SECTOR.
* PARM - DISK ADDRESS.
* MAXDA - PHYSICAL FILE SIZE.
* LASTDA - MOST RECENT DISK ADDRESS TRANSMITTED.
*
*
* EXIT P<FROM> - POSSIBLY DESTROYED.
* MAXDA - INCREMENTED IF FILE EXTENDED.
* LASTDA - INCREMENTED IF CONTIGUOUS EARLIER OUTPUT.
* FET - GUARANTEED SETUP FOR OUTPUT WITH THIS SECTOR
* OF TEXT IN CIRCULAR BUFFER. EXISTING FET SETUP
* AND CIRCULAR BUFFER CONTENT, IF ANY, ARE NOT
* DISTURBED WITH REGARD TO EARLIER OUTPUT.
* FLAG - TRUE IF THIS SECTOR TRANSMITTED. FALSE IF
* UNABLE TO TRANSMIT DUE TO DISCONTIGUITY WITH
* EARLIER OUTPUT.
* IOACT, IOWRITE - TRUE.
* IOAPEND - SETUP.
*
* CALLS SETWRITE, WTWBUF.
#
ITEM PARM, FLAG B, ZERO = 0; # ZERO IS A CONSTANT #
FLAG = TRUE;
IF NOT IOWRITE THEN SETWRITE(PARM);
IF IOAPEND THEN
BEGIN
IF MAXDA+1 LS PARM THEN # ADD PADDING SECTOR #
BEGIN
MAXDA = MAXDA + 1;
P<FROM>=LOC(ZERO);
WTWBUF(O"100");
END
ELSE IF MAXDA+1 EQ PARM THEN # ADD REAL SECTOR #
BEGIN
MAXDA = MAXDA+1;
WTWBUF(O"100");
END
ELSE
BEGIN
FLAG = FALSE;
END
END
ELSE
BEGIN
IF LASTDA+1 EQ PARM AND PARM LQ MAXDA THEN
BEGIN
LASTDA = LASTDA+1;
WTWBUF(O"100");
END
ELSE
BEGIN
FLAG = FALSE;
END
END
END
PAGE
PROC FLUSHCACHE;
IOBEGIN(FLUSHCACHE)
#
** FLUSHCACHE - UPDATE ALTERED CACHE FRAMES TO DISK.
*
* FLUSHCACHE IS OBLIGATED TO ASSURE THAT ALL ALTERED, UNOWNED
* CACHE PAGES BECOME UNALTERED IN CACHE. THIS REQUIRES THAT
* SUCH PAGES BE TRANSMITTED TO THE CIRCULAR BUFFER. THIS IN
* TURN CAN BE DONE ONLY IN ORDER OF AGE AND IN ADDRESS ORDER.
*
* UNALTERED FRAMES DO NOT NEED TO BE UPDATED TO DISK. OWNED
* FRAMES CANNOT YET BE UPDATED - IN THE AGE-RESTRICTED ORDER
* OF UPDATE, OWNED PAGES CAN BE THOUGHT OF AS HAVING THE
* WORST AGE CATEGORY.
*
* EACH SECTOR CAN BE TRANSMITTED TO THE CIRCULAR BUFFER IF
* THE BUFFER AND FET ARE AVAILABLE FOR OUTPUT, AND IF THE
* BUFFER IS EITHER EMPTY OR HAS ONE OR MORE SECTORS ALREADY IN
* IT FOR WHICH THE NEW SECTOR IS CONTIGOUS IN ADDRESS. IF THE
* FET IS ALREADY CLAIMED FOR INPUT PROCESSING, PAUSEIO CAN BE
* CALLED TO STOP AND DISCARD SUCH INPUT. FOR SPANNED
* ADDRESSES, WE MUST CALL PAUSEIO. THIS LOGIC IS PROVIDED BY
* INSPECTING THE FLAG RETURNED BY TRYWRITE, AND CALLING
* TRYWRITE A SECOND TIME WITH PAUSEIO, WHEN TRYWRITE FAILED.
*
* BECAUSE THE ALLOCATION OF NEW SECTORS AT THE LOGICAL END OF
* THE FILE CAN RUN SEVERAL UNITS AHEAD OF THE ACTUAL
* EXTENDING OF THE PHYSICAL END OF THE FILE, AND BECAUSE THE
* AGE-DEPENDENT ORDERING MIGHT REQUIRE A HIGH ADDRESS SECTOR
* TO BE WRITTEN BEFORE A LOWER ADDRESS SECTOR MAY BE LEGALLY
* WRITTEN, A SITUATION CAN ARISE WHERE A SECTOR MUST BE
* WRITTEN SEVERAL ADDRESSES BEYOND THE CURRENT PHYSICAL END
* OF FILE. WHEN THIS OCCURS, FLUSHCACHE RECOGNIZES THE
* SITUATION AND PADS THE PHYSICAL END OUT TO WITHIN ONE
* SECTOR OF THE SECTOR THAT MUST BE NEXT WRITTEN. THE
* INTERMEDIATE SECTORS THUS GET A DISK IMAGE WHICH IS NOT UP
* TO DATE IN ANY WAY WHATSOEVER. THIS IS NOT A PROBLEM - THE
* UP TO DATE FRAMES FOR SUCH PADDING WILL EVENTUALLY BE
* RELEASED FOR ACTUAL DISK UPDATE. THE CONTENT OF PADDING
* SECTORS IS INNOCUOUS, TO FOLLOW THE RULE THAT NOTHING CAN
* BE POINTED TO ON DISK UNTIL THE OBJECT ITSELF IS UP TO DATE
* ON DISK.
*
* ENTRY CACHEDIRTY[ALL] - SETUP.
* CACHEAGE[ALL] - SETUP.
* CACHEOWNED[ALL] - SETUP.
* BFDA[ALL] - SETUP.
* IOREAD - WHETHER OBSOLETE INPUT SCHEDULED.
* IOACT - WHETHER ANY PREVIOUS I/O SCHEDULED.
* FETDUN - FET INTERLOCK.
* IOAPEND - WHETHER A PREVIOUS WRITE EXTENDS.
* QUARTER - APPROX ONE FOURTH BUFFER CAPACITY.
* ACCTPRU - SECTOR TRANSFER ACCUMULATOR.
* MAXDA - FILE PHYSICAL BOUNDARY.
*
* EXIT ALL ALTERED AND UNOWNED CACHE FRAMES OUTPUT TO DISK
* OR BUFFERED FOR OUTPUT.
* IOREAD - FORCED FALSE IF ANY OUTPUT NEEDED.
* IOACT, IOWRITE, IOAPEND - POSSIBLY TRUE.
* CACHEDIRTY[ALL] - FALSE WHERE CACHEOWNED IS FALSE.
* ACCTPRU - INCREMENTED FOR ANY WORK DONE.
* MAXDA - INCREMENTED IF FILE EXTENDED.
*
* CALLS WIOSTAT, PUSHTEMP, POPTEMP, PAUSEIO, BUFAVAIL,
* MOVEIO, WAIT, DELAY, TRYWRITE, TRAGIC.
*
* USES P<FROM>, TEMP(RESTORED).
#
ITEM TMP1, TMP2, BOOL B;
CONTROL IFEQ MULTI,1;
WIOSTAT(5); # ACCUMULATE CACHE FLUSHES #
CONTROL FI;
PUSHTEMP;
FLUSHLOOP:
TMP1=999999999;
TEMP=-1;
FOR TMP2=0 STEP 1 UNTIL MAXCACHE DO
BEGIN # SEARCH FOR OLDEST DIRTY #
IF CACHEDIRTY[TMP2] AND CACHEAGE[TMP2] LS TMP1
AND NOT CACHEOWNED[TMP2] THEN
BEGIN
TEMP=TMP2;
TMP1=CACHEAGE[TMP2];
END
END
IF TEMP LS 0 THEN # NO MORE FLUSH NEEDED #
BEGIN
POPTEMP;
IORET
END
CACHEDIRTY[TEMP]=FALSE;
IF BFDA[TEMP] EQ 0 THEN GOTO FLUSHLOOP;
IF IOREAD THEN # COMPLETE ANY INPUT #
BEGIN
PAUSEIO;
CONTROL IFEQ MULTI,1;
WIOSTAT(6); # ACCUMULATE READ REJECTS #
CONTROL FI;
END
WRITELOOP:
WHYLE IOACT AND BUFAVAIL LS O"100" DO
BEGIN # NEED ROOM IN BUFFER #
MOVEIO;
CONTROL IFEQ MULTI,1;
WAIT;
CONTROL FI;
CONTROL IFEQ SINGLE,1;
IF NOT FETDUN THEN DELAY;
CONTROL FI;
END
CONTROL IFEQ MULTI,1;
WIOSTAT(7); # ACCUMULATE SECTORS WRITTEN #
ACCTPRU=ACCTPRU+1;
CONTROL FI;
P<FROM>=LOC(BFPRU[TEMP]);
TRYWRITE(BFDA[TEMP],BOOL);
IF NOT BOOL THEN
BEGIN # ONE FAILURE ALLOWED, NOT TWO #
PAUSEIO;
CONTROL IFEQ MULTI,1;
WIOSTAT(8); # ACCUMULATE DISCONTIGUOUS WRITES #
CONTROL FI;
P<FROM>=LOC(BFPRU[TEMP]);
TRYWRITE(BFDA[TEMP],BOOL);
IF NOT BOOL THEN TRAGIC(" UNABLE TO WRITE FROM BUFFER.$");
END
# EITHER ADD MORE PADDING FOR THIS FLUSH OR FLUSH ANOTHER #
IF IOAPEND AND MAXDA LS BFDA[TEMP] THEN
BEGIN
CONTROL IFEQ MULTI,1;
WIOSTAT(9); # ACCUMULATE PADDING SECTORS #
CONTROL FI;
GOTO WRITELOOP;
END
IF BUFAVAIL LQ QUARTER THEN MOVEIO;
GOTO FLUSHLOOP;
IOEND # OF FLUSHCACHE #
PROC CLOSEOUT;
IOBEGIN(CLOSEOUT)
#
** CLOSEOUT - FINISH UPDATE OF DISK.
*
* WORKFILE CLOSEOUT CONDITIONS HAVE BEEN DETECTED. UNDER
* CLOSEOUT CONDITIONS, WE MUST WRITE NOT ONLY THE ZERO-LEVEL
* SECTOR, BUT ALSO THE DATA SEGMENT (RIGHT AFTER ROOT SECTOR)
* FOLLOWED BY EOR THEN ARRAY SEGMENT THEN A SECOND EOR.
*
* THE CLOSEOUT ROUTINE WRITES THE TWO BINARY SEGMENTS, BUT
* REQUIRES THAT THE CALLER FIRST FLUSH ALL TREE STRUCTURE
* SECTORS TO DISK OR AT LEAST TO THE CIRCULAR BUFFER. THE
* CALLER SHOULD USE THE CLEAN ROUTINE WITH PL=0 TO MEET THAT
* REQUIREMENT. THE CLOSEOUT ROUTINE DEPENDS ON THE RULE THAT
* THE ROOT SECTOR OF THE TREE STRUCTURE MUST BE THE LAST
* (CHRONOLOGICALLY) TO BE FREED FOR UPDATE TO DISK, AND
* DEPENDS ON THE RULE THAT THE ROOT SECTOR IS AT DISK ADDRESS
* 1 AND THEREFORE IS CONTIGUOUS WITH NO PREVIOUSLY RELEASED
* SECTORS. THUS, CLOSEOUT EXPECTS THAT ALL SECTORS EXCEPT
* THE ROOT HAVE BEEN ACTUALLY WRITTEN TO DISK, AND THE ROOT
* SECTOR IS EXPECTED TO BE SCHEDULED FOR UPDATE BUT STILL IS
* PRESENT IN THE CIRCULAR BUFFER.
*
* CLOSEOUT INTERCEPTS THE ISSUANCE OF CIO FUNCTION CODES FOR
* THE UPDATE OF THE ROOT SECTOR, AND AFTER APPENDING THE
* SCALAR DATA SEGMENT ONTO THE ROOT SECTOR IN THE CIRCULAR
* BUFFER, CLOSEOUT CALLS PAUSEIO (AND THUS MOVEIO) SUCH THAT
* ONE WRITE WITH END OF RECORD OCCURS. THEN CLOSEOUT
* CONSTRUCTS A NEW CIRCULAR BUFFER IMAGE FOR THE ARRAY
* SEGMENT, AND USES PAUSEIO A SECOND TIME TO WRITE ANOTHER
* MULTIPLE SECTOR RECORD.
*
* ENTRY CLEAN HAS BEEN CALLED WITH PL=0.
* IOAPEND - WHETHER FILE PRESENTLY EMPTY.
* LASTDA, MAXDA - LOGICAL AND PHYSICAL FILE SIZES.
* DATALEN, ARRAYLEN - LENGTHS OF BINARY SEGMENTS.
* DATAPRUS, ARRAYPRUS - LENGTHS IN SECTORS.
*
* EXIT FILE COMPLETELY DRAINED TO DISK, FET INACTIVE.
* LASTDA, MAXDA - ONE OF THESE TWO IS INCREMENTED.
*
* CALLS FLUSHCACHE, WTWBUF, PAUSEIO, INITFET.
*
* USES P<FROM>, IOWRITE, IOWRTEOR, IOAPEND, IOACT.
#
FLUSHCACHE; # PUT SECTOR 1 (ROOT PRU) INTO BUFFER #
IOWRTEOR=TRUE; # WRITE ROOT PRU PLUS DATA SEGMENT #
P<FROM>=LOC(DATASTART);
WTWBUF(DATALEN);
IF IOAPEND THEN MAXDA=MAXDA+DATAPRUS;
ELSE LASTDA=LASTDA+DATAPRUS;
PAUSEIO;
INITFET(DISK,DSKSIZ); # NOW SET UP FOR ARRAYS #
IOACT=TRUE;
IOWRITE=TRUE;
IF FETCRI GR MAXDA THEN
BEGIN
IOAPEND=TRUE;
FETRR=LOC(DUMB);
MAXDA=MAXDA+ARRAYPRUS;
END
ELSE
BEGIN
IOAPEND=FALSE;
FETRR=FETCRI;
LASTDA=LASTDA+ARRAYPRUS;
END
IOWRTEOR=TRUE;
P<FROM>=LOC(ARRAYSTART);
WTWBUF(ARRAYLEN);
PAUSEIO;
IOEND # OF CLOSEOUT #
PAGE
PROC CLEAN;
IOBEGIN(CLEAN)
#
** CLEAN - DISCONNECT SECTOR FROM LOWEST TREE LEVEL.
*
* CLEAN IS CALLED WHENEVER A ROUTINE WISHES TO DISCARD THE
* SECTOR PRESENTLY ASSOCIATED WITH A TREE LEVEL. THIS CAN BE
* DONE ONLY IN THE REVERSE ORDER OF THE TREE HIERARCHY, IE,
* THE BOTTOM LEVEL FIRST AND THE ROOT LEVEL LAST. THE EFFECT
* OF CALLING CLEAN IS TO DISCONNECT THE SECTOR FROM THE TREE
* STRUCTURE BY FREEING THE CACHE FRAME FROM OWNERSHIP, FIRST
* DISCONNECTING ANY TREE LEVELS LOWER THAN THE CURRENT LEVEL.
*
* IF THE TREE LEVEL WAS PREVIOUSLY FLAGGED AS ALTERED, THE
* FREED CACHE FRAME IS SIMILARLY FLAGGED. ALL HEADER WORD
* DATA IS COPIED FROM THE TREE CONTROL VECTOR TO THE SECTOR
* IMAGE IN CACHE.
*
* ENTRY PL - LEVEL TO BE FREED.
* PALVL - DEEPEST ACTIVE TREE LEVEL.
* PAHEAD[ALL] - HEADER WORDS IN TREE VECTOR.
* PACACHE[ALL] - SETUP.
* CACHEOWNED[ALL] - SETUP.
* PADIRTY[ALL] - ALTERATION FLAG.
*
* EXIT THIS LEVEL AND ALL DEEPER LEVELS FREED.
* CACHEOWNED[ANY] - FORCED TRUE FOR THOSE FREED.
* CACHEDIRTY[ANY] - FORCED TRUE FOR THOSE FREED
* WITH PADIRTY TRUE.
* CACHEAGE[ANY] - INITIALIZED FOR THOSE FREED.
*
* CALLS DROPIT(INTERNAL), DISOWNCACHE.
*
* USES TPL.
#
PROC DROPIT;
BEGIN
IF CACHEOWNED[PACACHE[TPL]] THEN BFHEAD[PACACHE[TPL]]=PAHEAD[TPL];
DISOWNCACHE(PACACHE[TPL]);
END # OF DROPIT #
# START OF MAIN CODE #
IF PADIRTY[PL] THEN
BEGIN
FOR TPL = PALVL STEP -1 UNTIL PL DO
BEGIN
IF PADIRTY[TPL] THEN
BEGIN
PADIRTY[TPL]=FALSE;
CACHEDIRTY[PACACHE[TPL]]=TRUE;
DROPIT;
END
ELSE DROPIT;
END
END
ELSE
BEGIN
FOR TPL=PALVL STEP -1 UNTIL PL DO DROPIT;
END
IOEND
PROC CLEANALL;
IOBEGIN(CLEANALL)
#
** CLEANALL - CLEAN ENTIRE TREE STRUCTURE AND DATA SEGEMENTS.
*
* CLEANALL IS USED TO INITIATE CLOSEOUT OF THE WORKFILE
* MANAGER. THE ENTIRE TREE STRUCTURE IS CLEANED WITH ANY
* ALTERED SECTORS WRITTEN TO DISK, AND THE SCALAR AND ARRAY
* DATA SEGMENTS ARE UPDATED.
*
* ENTRY CACHEOWNED[ALL] - SETUP.
* PACACHE[ALL] - SETUP.
* PADIRTY[ALL] - SETUP.
* PAHEAD[ALL] -SETUP.
* PALVL - DEEPEST TREE LEVEL.
*
* EXIT PL - ZERO.
* CACHEDIRTY[ALL] - FALSE.
* FET AND CIRCULAR BUFFER - COMPLETE/EMPTY.
* PADIRTY[ALL] - FALSE.
*
* CALLS CLEAN, CLOSEOUT, RECLAIMCACHE.
#
PADIRTY[0]=TRUE;
PL=0;
CLEAN;
CLOSEOUT;
RECLAIMCACHE;
IOEND # OF CLEANALL #
PAGE
PROC BK;
IOBEGIN(BK)
#
** BK - BACK UP ONE LINE.
*
* BK IS ORGANIZED INTO THREE OPERATIONAL PHASES. THE FIRST
* PHASE DETERMINES WHETHER THE CURRENT TREE PATH ALREADY
* CONTAINS THE SECTOR WITH THE DESIRED LINE OF TEXT, AND IF
* NOT, IT CLEANS AWAY AS MANY TREE LEVELS AS NECESSARY, FROM
* THE BOTTOM TOWARDS THE ROOT, UNTIL WE ARE IN A DIRECTORY
* WHOSE SPHERE OF INFLUENCE INCLUDES THE DESIRED LINE.
*
* THE SECOND PHASE ASSURES THAT WE HAVE A FULL DEPTH TREE
* STRUCTURE CONTAINING THE RIGHT SECTOR AT THE DATA (BOTTOM)
* LEVEL. IF THE FIRST PHASE WAS NON-TRIVIAL, THEN THE SECOND
* PHASE WILL ALSO HAVE REAL WORK TO PERFORM, WORKING BACK
* TOWARD DEEP TREE LEVELS, READING IN EACH SECTOR NEEDED.
* THE SECOND PHASE IS A NO-OP IF THE FIRST PHASE WAS A NO-OP
* AND IF THE TREE ALREADY EXISTS ALL THE WAY DOWN TO THE DATA
* LEVEL. HOWEVER, THE FIRST PHASE COULD HAVE BEEN A NO-OP
* FOR AN INCOMPLETE TREE PATH, IN WHICH CASE THE SECOND PHASE
* MUST DEEPEN THE TREE PATH.
*
* THE THIRD PHASE SCANS THE DATA SECTOR TO FIND THE RIGHT
* LINE IMAGE. THE INTERNAL LINE IMAGE FORMAT USES THE SIGN
* BIT TO INDICATE WORDS WHICH ARE THE LAST WORDS OF LINES.
*
* ENTRY CURRENT - CURRENT LINE.
* PALVL - DEEPEST TREE LEVEL.
* ALL PATH AND CACHE CONTROLS SETUP.
* P<MVLNBUF> - WHERE TO PUT LINE OF TEXT.
* DISP, CURNW - DESCRIBE WORD OFFSET AND LENGTH OF
* PREVIOUS CURRENT LINE.
*
* EXIT CURRENT - DECREMENTED.
* MVLNBUF - CONTAINS LINE OF TEXT.
* PALVL - NEW VALUE FOR DEEPEST TREE LEVEL.
* ALL PATH CONTROLS - MAY CONTROL DIFFERENT SECTORS
* FOR DEEPER TREE LEVELS.
* ALL CACHE CONTROLS - MAY CONTROL DIFFERENT SECTORS.
* FET, CIRCULAR BUFFER, READ LIST - MAY CONTROL
* A PREFETCH OPERATION.
* CURNW - SIZE OF NEW LINE.
* DISP - OFFSET OF NEW LINE.
*
* CALLS TRAGIC, CLEAN, PUSHTEMP, READPA, POPTEMP, PREFETCH,
* MOVELN.
*
* USES TEMP(RESTORED), P<PRU>, PL.
#
IF CURRENT LQ 0 THEN TRAGIC(" BAK BEFORE START OF FILE.$");
CURRENT = CURRENT - 1;
FOR PL = PALVL WHILE PAFIRST[PL] GR CURRENT DO
BEGIN
CLEAN;
PL = PL - 1;
END
P<PRU> = LOC(BFPRU[PACACHE[PL]]);
DISP = PADISP[PL] - 1;
CONTROL IFGQ PARANOIA,5;
IF DISP LQ 0 THEN TRAGIC(" DIRECTORY BLOCK POINTER TOO SMALL.$");
CONTROL FI;
PADISP[PL] = DISP;
IF PADIR[PL] THEN
BEGIN
FOR PL = PL WHILE PADIR[PL] DO
BEGIN
PL = PL + 1;
PALVL = PL;
PUSHTEMP;
TEMP=DIRDA[DISP];
READPA;
POPTEMP;
PAFIRST[PL] = CURRENT - DIRNL[DISP] + 1;
PALAST[PL] = CURRENT;
P<PRU> = LOC(BFPRU[PACACHE[PL]]);
DISP = PAFINAL[PL];
PADISP[PL] = DISP;
END
PREFETCH(-1);
END
# NOTE THIS CODE REQUIRES INTERNAL CHARSET USAGE OF SIGN BIT #
FOR DISP = DISP - 1 WHILE DISP NQ 0 AND B<0,1>DATWORD[DISP] EQ 0
DO DISP = DISP - 1;
PADISP[PL] = DISP + 1;
P<FROM> = LOC(BFPRU[PACACHE[PALVL]]) + PADISP[PALVL];
CURNW = MOVELN(FROM,MVLNBUF);
IOEND
PAGE
PROC FW;
IOBEGIN(FW)
#
** FW - ADVANCE ONE LINE.
*
* FW IS ORGANIZED INTO THREE OPERATIONAL PHASES. THE FIRST
* PHASE DETERMINES WHETHER THE CURRENT TREE PATH ALREADY
* CONTAINS THE SECTOR WITH THE DESIRED LINE OF TEXT, AND IF
* NOT, IT CLEANS AWAY AS MANY TREE LEVELS AS NECESSARY, FROM
* THE BOTTOM TOWARDS THE ROOT, UNTIL WE ARE IN A DIRECTORY
* WHOSE SPHERE OF INFLUENCE INCLUDES THE DESIRED LINE.
*
* THE SECOND PHASE ASSURES THAT WE HAVE A FULL DEPTH TREE
* STRUCTURE CONTAINING THE RIGHT SECTOR AT THE DATA (BOTTOM)
* LEVEL. IF THE FIRST PHASE WAS NON-TRIVIAL, THEN THE SECOND
* PHASE WILL ALSO HAVE REAL WORK TO PERFORM, WORKING BACK
* TOWARD DEEP TREE LEVELS, READING IN EACH SECTOR NEEDED.
* THE SECOND PHASE IS A NO-OP IF THE FIRST PHASE WAS A NO-OP
* AND IF THE TREE ALREADY EXISTS ALL THE WAY DOWN TO THE DATA
* LEVEL. HOWEVER, THE FIRST PHASE COULD HAVE BEEN A NO-OP
* FOR AN INCOMPLETE TREE PATH, IN WHICH CASE THE SECOND PHASE
* MUST DEEPEN THE TREE PATH.
*
* THE THIRD PHASE IDENTIFIES THE CORRECT LINE IMAGE WITHIN
* THE DATA SECTOR. THE MECHANISM USED DEPENDS ON WHETHER THE
* SECOND PHASE WAS ACTUALLY PERFORMED, SINCE FOR A NO-OP THE
* DISP AND CURNW PARAMETERS FOR THE PREVIOUS CURRENT LINE
* PROVIDE ALL INFORMATION REQUIRED TO ADVANCE ONE LINE.
*
* ENTRY CURRENT - CURRENT LINE.
* PALVL - DEEPEST TREE LEVEL.
* ALL PATH AND CACHE CONTROLS SETUP.
* P<MVLNBUF> - WHERE TO PUT LINE OF TEXT.
* DISP, CURNW - DESCRIBE WORD OFFSET AND LENGTH OF
* PREVIOUS CURRENT LINE.
*
* EXIT CURRENT - INCREMENTED.
* MVLNBUF - CONTAINS LINE OF TEXT.
* PALVL - NEW VALUE FOR DEEPEST TREE LEVEL.
* ALL PATH CONTROLS - MAY CONTROL DIFFERENT SECTORS
* FOR DEEPER TREE LEVELS.
* ALL CACHE CONTROLS - MAY CONTROL DIFFERENT SECTORS.
* FET, CIRCULAR BUFFER, READ LIST - MAY CONTROL
* A PREFETCH OPERATION.
* CURNW - SIZE OF NEW LINE.
* DISP - OFFSET OF NEW LINE.
*
* CALLS TRAGIC, CLEAN, PUSHTEMP, READPA, POPTEMP, PREFETCH,
* MOVELN.
*
* USES TEMP(RESTORED), P<PRU>, PL.
#
IF CURRENT GQ PALAST[0] THEN TRAGIC(" FWD BEYOND END OF FILE.$");
CURRENT = CURRENT + 1;
FOR PL = PALVL WHILE PALAST[PL] LS CURRENT DO
BEGIN
CLEAN;
PL = PL - 1;
END
P<PRU> = LOC(BFPRU[PACACHE[PL]]);
DISP = PADISP[PL];
IF PADIR[PL] THEN
BEGIN
DISP = DISP + 1;
CONTROL IFGQ PARANOIA,5;
IF DISP GR PAFINAL[PL] THEN
BEGIN
TRAGIC(" DIRECTORY BLOCK POINTER TOO LARGE (1).$");
END
CONTROL FI;
PADISP[PL] = DISP;
FOR PL = PL WHILE PADIR[PL] DO
BEGIN
PL = PL + 1;
PALVL = PL;
PUSHTEMP;
TEMP=DIRDA[DISP];
READPA;
POPTEMP;
PAFIRST[PL] = CURRENT;
PALAST[PL] = CURRENT + DIRNL[DISP] - 1;
P<PRU> = LOC(BFPRU[PACACHE[PL]]);
DISP = 1;
PADISP[PL] = 1;
END
PREFETCH(+1);
END
ELSE PADISP[PL] = DISP + CURNW;
P<FROM> = LOC(BFPRU[PACACHE[PALVL]]) + PADISP[PALVL];
CURNW = MOVELN(FROM,MVLNBUF);
IOEND
PAGE
PROC POSR;
IOBEGIN(POSR)
#
** POSR - POSITION TO A RANDOMLY SELECTED LINE.
*
* POSR IS ORGANIZED INTO THREE OPERATIONAL PHASES. THE FIRST
* PHASE DETERMINES WHETHER THE CURRENT TREE PATH ALREADY
* CONTAINS THE SECTOR WITH THE DESIRED LINE OF TEXT, AND IF
* NOT, IT CLEANS AWAY AS MANY TREE LEVELS AS NECESSARY, FROM
* THE BOTTOM TOWARDS THE ROOT, UNTIL WE ARE IN A DIRECTORY
* WHOSE SPHERE OF INFLUENCE INCLUDES THE DESIRED LINE.
*
* THE SECOND PHASE ASSURES THAT WE HAVE A FULL DEPTH TREE
* STRUCTURE CONTAINING THE RIGHT SECTOR AT THE DATA (BOTTOM)
* LEVEL. IF THE FIRST PHASE WAS NON-TRIVIAL, THEN THE SECOND
* PHASE WILL ALSO HAVE REAL WORK TO PERFORM, WORKING BACK
* TOWARD DEEP TREE LEVELS, READING IN EACH SECTOR NEEDED.
* THE SECOND PHASE IS A NO-OP IF THE FIRST PHASE WAS A NO-OP
* AND IF THE TREE ALREADY EXISTS ALL THE WAY DOWN TO THE DATA
* LEVEL. HOWEVER, THE FIRST PHASE COULD HAVE BEEN A NO-OP
* FOR AN INCOMPLETE TREE PATH, IN WHICH CASE THE SECOND PHASE
* MUST DEEPEN THE TREE PATH.
*
* THE THIRD PHASE SCANS THE DATA SECTOR TO FIND THE RIGHT
* LINE IMAGE. THE INTERNAL LINE IMAGE FORMAT USES THE SIGN
* BIT TO INDICATE WORDS WHICH ARE THE LAST WORDS OF LINES.
*
* ENTRY NEWCURL - DESIRED LINE.
* PALVL - DEEPEST TREE LEVEL.
* ALL PATH AND CACHE CONTROLS SETUP.
* P<MVLNBUF> - WHERE TO PUT LINE OF TEXT.
* DISP, CURNW - DESCRIBE WORD OFFSET AND LENGTH OF
* PREVIOUS CURRENT LINE.
*
* EXIT CURRENT - MATCHES NEWCURL.
* MVLNBUF - CONTAINS LINE OF TEXT.
* PALVL - NEW VALUE FOR DEEPEST TREE LEVEL.
* ALL PATH CONTROLS - MAY CONTROL DIFFERENT SECTORS
* FOR DEEPER TREE LEVELS.
* ALL CACHE CONTROLS - MAY CONTROL DIFFERENT SECTORS.
* FET, CIRCULAR BUFFER, READ LIST - MAY CONTROL
* A PREFETCH OPERATION.
* CURNW - SIZE OF NEW LINE.
* DISP - OFFSET OF NEW LINE.
*
* CALLS TRAGIC, CLEAN, PUSHTEMP, READPA, POPTEMP, PREFETCH,
* MOVELN.
*
* USES TEMP(RESTORED), P<PRU>, PL.
#
IF NEWCURL LS 0 OR NEWCURL GR PALAST[0] THEN
BEGIN
TRAGIC(" LINE NOT FOUND IN FILE.$");
END
PUSHTEMP;
CURRENT = NEWCURL;
FOR PL=PALVL WHILE
NOT(CURRENT GQ PAFIRST[PL] AND CURRENT LQ PALAST[PL]) DO
BEGIN
CLEAN;
PL = PL - 1;
END
P<PRU> = LOC(BFPRU[PACACHE[PL]]);
FOR PL = PL WHILE PADIR[PL] DO
BEGIN
TEMP = PAFIRST[PL];
FOR DISP = 1 WHILE CURRENT GQ TEMP+DIRNL[DISP] DO
BEGIN
TEMP = TEMP + DIRNL[DISP];
DISP = DISP + 1;
END
CONTROL IFGQ PARANOIA,5;
IF DISP GR PAFINAL[PL] THEN
BEGIN
TRAGIC(" DIRECTORY BLOCK POINTER TOO LARGE (2).$");
END
CONTROL FI;
PADISP[PL] = DISP;
PL = PL + 1;
PALVL = PL;
PAFIRST[PL] = TEMP;
PALAST[PL] = TEMP + DIRNL[DISP] - 1;
PUSHTEMP;
TEMP=DIRDA[DISP];
READPA;
POPTEMP;
P<PRU> = LOC(BFPRU[PACACHE[PL]]);
END
TEMP = PAFIRST[PL];
FOR DISP = 1 WHILE CURRENT NQ TEMP DO
BEGIN
# NOTE THIS CODE REQUIRES INTERNAL CHARSET USAGE OF SIGN BIT #
IF B<0,1>DATWORD[DISP] NQ 0 THEN TEMP = TEMP + 1;
DISP = DISP + 1;
END
CONTROL IFGQ PARANOIA,5;
IF DISP GR PAFINAL[PL] THEN
BEGIN
TRAGIC(" DIRECTORY BLOCK POINTER TOO LARGE (3).$");
END
CONTROL FI;
PADISP[PL] = DISP;
P<FROM> = LOC(BFPRU[PACACHE[PALVL]]) + PADISP[PALVL];
CURNW = MOVELN(FROM,MVLNBUF);
POPTEMP;
IOEND
PAGE
PROC FWD;
IOBEGIN(FWD)
#
** FWD - EXTERNAL INTERFACE FOR FORWARD MOVEMENT.
*
* FWD INTERFACES TO THE FW ROUTINE TO ADVANCE ONE LINE
* FROM THE CURRENT POSITION. FWD ALSO CONTROLS THE BASE
* ADDRESS FOR MVLNBUF AND ACCUMULATES STATISTICS.
*
* ENTRY P<LINEBUF> - WHERE TO PUT TEXT OR ZERO FOR INVISIBLE
* POSITIONING FUNCTION.
* CURRENT - CURRENT LINE.
*
* EXIT CURRENT - INCREMENTED.
* LINEBUF - TEXT MOVE HERE IF BASE ADDRESS NONZERO.
*
* CALLS FW.
*
* USES P<MVLNBUF>.
#
CONTROL IFEQ MULTI,1;
WIOSTAT(0); # ACCUMULATE LINE ACCESES #
CONTROL FI;
P<MVLNBUF>=LOC(LINEBUF);
FW;
P<MVLNBUF>=0;
IOEND
PROC BAK;
IOBEGIN(BAK)
#
** FWD - EXTERNAL INTERFACE FOR BACKWARD MOVEMENT.
*
* BAK INTERFACES TO THE BK ROUTINE TO REGRESS ONE LINE
* FROM THE CURRENT POSITION. BAK ALSO CONTROLS THE BASE
* ADDRESS FOR MVLNBUF AND ACCUMULATES STATISTICS.
*
* ENTRY P<LINEBUF> - WHERE TO PUT TEXT OR ZERO FOR INVISIBLE
* POSITIONING FUNCTION.
* CURRENT - CURRENT LINE.
*
* EXIT CURRENT - DECREMENTED.
* LINEBUF - TEXT MOVE HERE IF BASE ADDRESS NONZERO.
*
* CALLS BK.
*
* USES P<MVLNBUF>.
#
CONTROL IFEQ MULTI,1;
WIOSTAT(0); # ACCUMULATE LINE ACCESES #
CONTROL FI;
P<MVLNBUF>=LOC(LINEBUF);
BK;
P<MVLNBUF>=0;
IOEND
PROC POS;
IOBEGIN(POS)
#
** POS - EXTERNAL INTERFACE FOR RANDOM MOVEMENT.
*
* POS INTERFACES TO THE POSR ROUTINE TO GO TO ANY LINE
* FROM THE CURRENT POSITION. POS ALSO CONTROLS THE BASE
* ADDRESS FOR MVLNBUF AND ACCUMULATES STATISTICS.
*
* ENTRY P<LINEBUF> - WHERE TO PUT TEXT OR ZERO FOR INVISIBLE
* POSITIONING FUNCTION.
* NEWCURL - DESIRED LINE.
* CURRENT - PREVIOUS CURRENT LINE.
*
* EXIT CURRENT - MATCHES NEWCURL.
* LINEBUF - TEXT MOVE HERE IF BASE ADDRESS NONZERO.
*
* CALLS POSR.
*
* USES P<MVLNBUF>.
#
CONTROL IFEQ MULTI,1;
WIOSTAT(0); # ACCUMULATE LINE ACCESES #
CONTROL FI;
P<MVLNBUF>=LOC(LINEBUF);
IF NEWCURL EQ CURRENT-1 THEN BK;
ELSE IF NEWCURL EQ CURRENT+1 THEN FW;
ELSE IF NEWCURL NQ CURRENT THEN POSR;
ELSE # JUST READOUT SAME LINE #
BEGIN
P<FROM> = LOC(BFPRU[PACACHE[PALVL]]) + PADISP[PALVL];
DUMB = MOVELN(FROM,LINEBUF);
END
P<MVLNBUF>=0;
IOEND # OF POS #
PAGE
PROC SPLITTOP;
IOBEGIN(SPLITTOP)
#
** SPLITTOP - SPLIT ROOT SECTOR TO MAKE NEW LEVEL.
*
* SPLITTOP IS USED WHEN THE ROOT SECTOR OVERFLOWS. THE ROOT
* SECTOR MUST ALWAYS EXIST AT DISK ADDRESS 1, THUS THE METHOD
* USED TO SPLIT IT IS DIFFERENT FROM THAT USED FOR ANY OTHER
* SECTOR. THE EXISTING CONTENTS OF THE ROOT SECTOR ARE
* PLACED IN TWO NEW SECTORS, AND THE ROOT SECTOR IS
* RECONSTRUCTED AS A DIRECTORY POINTING TO THESE TWO NEW
* SECTORS. THUS THE OVERALL TREE STRUCTURE IS DEEPENED BY
* ONE LEVEL.
*
* HOWEVER, SPLITTOP DOES NOT PROVIDE ALL THIS ALGORITHM.
* SPLITTOP DOES PRODUCE A NEW TREE LEVEL WITH A REBUILT ROOT,
* BUT STOPS SHORT OF FITTING THE OVERFLOWED TEXT INTO A PAIR
* OF SECTORS. SPLITTOP PLACES THE PRE-OVERFLOW SECTOR
* CONTENT INTO A SINGLE SECTOR AT THE NEW INTERMEDIATE TREE
* LEVEL, AND ESTABLISHES THIS AS THE CURRENT TREE PATH LEVEL.
* THE CALLER IS THEN RESPONSIBLE TO USE ONE OF THE OTHER
* SECTOR SPLITTING ALGORITHMS TO GET THE TEXT ACTUALLY SPLIT
* INTO TWO SECTORS. THESE OTHER ALGORITHMS WILL UPDATE THE
* NEW ROOT TO POINT TO THE OVERFLOW SECTOR.
*
* ENTRY PL - MUST EQUAL ZERO.
* PADEPTH[0] - INDICATE HIGH WATER MARK OF TREE DEPTH.
* PALVL - CURRENT DEEPEST TREE LEVEL ON PATH.
* ALL PATH CONTROLS - SETUP.
* CACHE - AT LEAST ONE FRAME MUST BE AVAILABLE TO
* BE CLAIMED.
*
* EXIT PL - ONE.
* PALVL - INCREMENTED.
* PADEPTH[0] - INCREMENETED.
* ALL PATH CONTROLS - SHUFFLED TO DEEPER LEVELS.
* CACHE - ONE FRAME ALLOCATED FOR NEW LEVEL 0.
* ROOT SECTOR CACHE FRAME - BUILT AS DIRECTORY WITH
* ONE ENTRY POINTING TO INTERMEDIATE SECTOR.
* PATH LEVEL 1 - BUILT AS INTERMEDIATE TREE LEVEL
* USING SAME CACHE FRAME AS FORMER ROOT LEVEL,
* BUT ASSIGNED A NEWLY ALLOCATED DISK ADDRESS.
* MARKED AS ALTERED SO NEW DISK ADDRESS CAN BE
* EVENTUALLY WRITTEN TO DISK.
*
* CALLS TRAGIC, ALLOC, ALLOCCACHE, MOVEWD.
*
* USES P<PRU>.
#
IF PADEPTH[0] GQ MAXDEPTH THEN TRAGIC(" FILE TOO LARGE.$");
PADEPTH[0] = PADEPTH[0] + 1;
FOR PL = PALVL STEP -1 UNTIL 0 DO # SLIDE PATH UP #
BEGIN
PAFIRST[PL+1] = PAFIRST[PL];
PALAST[PL+1] = PALAST[PL];
PADISP[PL+1] = PADISP[PL];
PAHEAD[PL+1] = PAHEAD[PL];
PACACHE[PL+1] = PACACHE[PL];
END
PL = 1; # STEP UP PL & PALVL #
PALVL = PALVL + 1;
PATOP[1] = FALSE; # FIX UP OLD TOP #
PADIRTY[1] = FALSE;
PADA[1] = 0;
ALLOC;
ALLOCCACHE;
PADIRTY[1] = TRUE;
P<FROM> = LOC(BFPRU[PACACHE[0]]) + 1;
P<TOO> = LOC(BFPRU[PACACHE[1]]) + 1;
MOVEWD(PAFINAL[0],FROM,TOO);
PADATA[0] = FALSE; # BUILD NEW TOP #
PADIR[0] = TRUE;
PADIRTY[0] = TRUE;
PAFINAL[0] = 1;
P<PRU> = LOC(BFPRU[PACACHE[0]]);
DIRDA[1] = PADA[1];
DIRNL[1] = PALAST[1] - PAFIRST[1] + 1;
PADISP[0] = 1;
IOEND
PAGE
PROC SPLITAPEND;
IOBEGIN(SPLITAPEND)
#
** SPLITAPEND - SPLIT SECTOR JUST AFTER CURRENT CONTENT.
*
* SPLITAPEND IS USED TO OVERFLOW ANY NON-ROOT SECTOR UNDER
* THE CONDITION THAT THE OVERFLOW TEXT BELONGS JUST AFTER ALL
* OF THE TEXT ALREADY CONTAINED IN THE SECTOR. THUS, THE
* SECTOR REMAINS JUST AS IT IS, AND A NEW SECTOR IS
* CONSTRUCTED WHICH CONTAINS PRECISELY THE OVERFLOW TEXT.
* SINCE NO EXISTING TEXT LEAVES THE SECTOR, THERE IS NO NEED
* TO DISCARD IT AND BUILD TWO COMPLETELY NEW SECTORS. THUS,
* SPLITAPEND IS MORE EFFICIENT THAN SPLITBEFORE OR
* SPLITAFTER.
*
* THE NEWLY BUILT SECTOR BECOMES THE CURRENT SECTOR FOR THIS
* LEVEL OF THE TREE. THE ORIGINAL SECTOR IS RELEASED FROM
* OWNERSHIP.
*
* SPLITAPEND REQUIRES THAT AT LEAST ONE CACHE FRAME IS
* AVAILABLE FOR ALLOCATION.
*
* SPLITAPEND FINISHES ITS PROCESSING BY SETTING UP PARAMETERS
* FOR THE POSSIBLE CALLING OF ANY SPLIT ROUTINE FOR THE NEXT
* HIGHER LEVEL. NOTE THAT ALL SPLIT ROUTINES ANTICIPATE THAT
* THE CALLER WILL ITERATE SO LONG AS PARAMETERS INDICATE THAT
* MORE SPLITTING NEEDS TO BE DONE. SINCE EACH SPLIT ROUTINE
* DECREMENTS THE CURRENT TREE LEVEL, THE EFFECT IS TO WORK
* FROM THE BOTTOM OF THE TREE TOWARDS THE ROOT, SPLITTING
* UNTIL A LEVEL IS REACHED WHERE EVERYTHING FITS.
*
* ENTRY P<NEW> - LOCATION OF OVERFLOW TEXT.
* NWNEW - NUMBER OF WORDS IN OVERFLOW TEXT.
* PL - CURRENT LEVEL IN TREE PATH.
* ALL PATH CONTROLS SETUP.
* ALL CACHE CONTROLS SETUP.
*
* EXIT PL - DECREMENTED.
* SECTOR AT ORIGINAL TREE LEVEL REBUILT TO CONTAIN
* OVERFLOW TEXT.
* PREVIOUS SECTOR AT ORIGINAL TREE LEVEL RELEASED.
* NWNEW, NEWBEFORE, NWAFTER - SET FOR ITERATION.
* NEWDA, NEWNL - SET FOR ITERATION.
* DISP, PADISP[NEW PL] - INCREMENTED FOR ITERATION.
*
* CALLS ALLOC, ALLOCCACHE, MOVEWD.
*
* USES P<TOO>.
#
CLEAN; # CLEAN OLD BLOCK #
ALLOC; # BUILD NEW BLOCK #
ALLOCCACHE;
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + 1;
MOVEWD(NWNEW,NEW,TOO);
PAFINAL[PL] = NWNEW;
PADIRTY[PL] = TRUE;
PADISP[PL] = 1; # FIX UP PATH #
PAFIRST[PL] = FIRST;
PALAST[PL] = LAST;
NEWDA[0] = PADA[PL]; # BUILD NEW DIR ENTRY #
NEWNL[0] = PALAST[PL] - PAFIRST[PL] + 1;
NWNEW = 1;
PL = PL - 1; # SET UP NEXT PASS #
NWBEFORE = PADISP[PL];
NWAFTER = PAFINAL[PL] - PADISP[PL];
DISP = PADISP[PL] + 1;
PADISP[PL] = DISP;
IOEND
PAGE
PROC SPLITBEFORE;
IOBEGIN(SPLITBEFORE)
#
** SPLITBEFORE - SPLIT SECTOR JUST BEFORE INSERTION POINT.
*
* SPLITBEFORE IS USED TO OVERFLOW ANY NON-ROOT SECTOR UNDER
* THE CONDITION THAT THE OVERFLOW TEXT MUST BE INSERTED
* SOMEWHERE IN THE MIDDLE OF THE TEXT ALREADY CONTAINED IN
* THE SECTOR, AND NEW TEXT WOULD NOT FIT WITHIN THE FIRST
* SECTOR OF THE NEW PAIR. THUS, THE SECTOR IS SPLIT JUST IN
* FRONT OF THE INSERTION POINT, AND THE SECOND SECTOR
* RESULTING FROM THE SPLIT WILL START WITH THE OVERFLOW TEXT
* AND CONTINUE WITH THE REMAINING TEXT SPLIT OUT OF THE
* ORIGINAL SECTOR.
*
* SINCE SOME EXISTING TEXT MUST BE REMOVED FROM THE CURRENT
* SECTOR, IT IS NOT POSSIBLE TO KEEP USING THE SAME SECTOR,
* UNDER THE RULE THAT NOTHING CAN BE POINTED TO WITHOUT FIRST
* EXISTING ON DISK. THEREFORE, TWO COMPLETELY NEW SECTORS
* ARE CREATED AND THE ORIGINAL SECTOR BECOMES A FRAGMENT.
* SINCE THE INSERTED TEXT GOES INTO THE SECOND SECTOR OF THE
* NEW PAIR, THAT IS THE SECTOR WHICH IS LEFT AS THE CURRENT
* SECTOR FOR THIS TREE PATH LEVEL.
*
* SPLITBEFORE REQUIRES THAT AT LEAST ONE CACHE FRAME IS
* AVAILABLE FOR ALLOCATION. THE EXISTING CACHE FRAME FOR
* THIS TREE LEVEL IS RECYCLED TO PROVIDE THE SECOND SECTOR OF
* CAPACITY. THE EXISTING CACHE FRAME IS FIRST SUBJECTED TO
* THE NORMAL "CLEAN" ROUTINE TO ASSURE THAT ANY PENDING
* CHANGES ARE FLUSHED TO DISK.
*
* SPLITBEFORE FINISHES ITS PROCESSING BY SETTING UP PARAMETERS
* FOR THE POSSIBLE CALLING OF ANY SPLIT ROUTINE FOR THE NEXT
* HIGHER LEVEL. NOTE THAT ALL SPLIT ROUTINES ANTICIPATE THAT
* THE CALLER WILL ITERATE SO LONG AS PARAMETERS INDICATE THAT
* MORE SPLITTING NEEDS TO BE DONE. SINCE EACH SPLIT ROUTINE
* DECREMENTS THE CURRENT TREE LEVEL, THE EFFECT IS TO WORK
* FROM THE BOTTOM OF THE TREE TOWARDS THE ROOT, SPLITTING
* UNTIL A LEVEL IS REACHED WHERE EVERYTHING FITS.
*
* ENTRY P<NEW> - LOCATION OF OVERFLOW TEXT.
* NWNEW - NUMBER OF WORDS IN OVERFLOW TEXT.
* PL - CURRENT LEVEL IN TREE PATH.
* ALL PATH CONTROLS SETUP.
* ALL CACHE CONTROLS SETUP.
*
* EXIT PL - DECREMENTED.
* SECTOR AT ORIGINAL TREE LEVEL REBUILT TO CONTAIN
* OVERFLOW TEXT AND SPLIT-OFF ORGINAL TEXT.
* PREVIOUS SECTOR AT ORIGINAL TREE LEVEL RELEASED.
* NWNEW, NEWBEFORE, NWAFTER - SET FOR ITERATION.
* NEWDA, NEWNL - SET FOR ITERATION.
* DISP, PADISP[NEW PL] - INCREMENTED FOR ITERATION.
*
* CALLS ALLOC, ALLOCCACHE, MOVEWD, LAST.
*
* USES P<TOO>.
#
P<FROM> = LOC(BFPRU[PACACHE[PL]]) + 1; # SAVE OLD DATA #
MOVEWD(PAFINAL[PL],FROM,OBF);
DEALLOC; # FREE UP OLD BLOCK #
ALLOC; # BUILD OFF BLOCK #
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + 1;
MOVEWD(NWBEFORE,OBF,TOO);
PAFINAL[PL] = NWBEFORE;
ODA = PADA[PL]; # REMEMBER THIS #
ONL = FIRST - PAFIRST[PL];
PADIRTY[PL] = TRUE; # GET RID OF OFF BLOCK #
CLEAN;
ALLOC; # BUILD NEW BLOCK #
ALLOCCACHE;
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + 1;
MOVEWD(NWNEW,NEW,TOO);
P<FROM> = LOC(OBF) + DISP-1;
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + NWNEW + 1;
MOVEWD(NWAFTER,FROM,TOO);
PAFINAL[PL] = NWNEW + NWAFTER;
PADIRTY[PL] = TRUE;
PADISP[PL] = PADISP[PL] - NWBEFORE; # FIX UP PATH #
PAFIRST[PL] == FIRST;
PALAST[PL] = PALAST[PL] + NL;
LAST = PALAST[PL];
NEWDA[0] = ODA; # BUILD NEW DIR ENTRIES #
NEWNL[0] = ONL;
NEWDA[1] = PADA[PL];
NEWNL[1] = PALAST[PL] - PAFIRST[PL] + 1;
NWNEW = 2;
PL = PL - 1; # SET UP NEXT PASS #
NWBEFORE = PADISP[PL] - 1;
NWAFTER = PAFINAL[PL] - PADISP[PL];
DISP = PADISP[PL] + 1;
PADISP[PL] = DISP;
IOEND
PAGE
PROC SPLITAFTER;
IOBEGIN(SPLITAFTER)
#
** SPLITAFTER - SPLIT SECTOR JUST AFTER INSERTION POINT.
*
* SPLITAFTER IS USED TO OVERFLOW ANY NON-ROOT SECTOR UNDER
* THE CONDITION THAT THE OVERFLOW TEXT MUST BE INSERTED
* SOMEWHERE IN THE MIDDLE OF THE TEXT ALREADY CONTAINED IN
* THE SECTOR, AND NEW TEXT WOULD FIT WITHIN THE FIRST
* SECTOR OF THE NEW PAIR. THUS, THE SECTOR IS SPLIT JUST
* BEYOND THE INSERTED TEXT, AND THE SECOND SECTOR
* RESULTING FROM THE SPLIT WILL START WITH THE REMAINING
* TEXT SPLIT OUT OF THE ORGINAL SECTOR.
*
* SINCE SOME EXISTING TEXT MUST BE REMOVED FROM THE CURRENT
* SECTOR, IT IS NOT POSSIBLE TO KEEP USING THE SAME SECTOR,
* UNDER THE RULE THAT NOTHING CAN BE POINTED TO WITHOUT FIRST
* EXISTING ON DISK. THEREFORE, TWO COMPLETELY NEW SECTORS
* ARE CREATED AND THE ORIGINAL SECTOR BECOMES A FRAGMENT.
* SINCE THE INSERTED TEXT GOES INTO THE FIRST SECTOR OF THE
* NEW PAIR, THAT IS THE SECTOR WHICH IS LEFT AS THE CURRENT
* SECTOR FOR THIS TREE PATH LEVEL.
*
* SPLITAFTER REQUIRES THAT AT LEAST ONE CACHE FRAME IS
* AVAILABLE FOR ALLOCATION. THE EXISTING CACHE FRAME FOR
* THIS TREE LEVEL IS RECYCLED TO PROVIDE THE SECOND SECTOR OF
* CAPACITY. THE EXISTING CACHE FRAME IS FIRST SUBJECTED TO
* THE NORMAL "CLEAN" ROUTINE TO ASSURE THAT ANY PENDING
* CHANGES ARE FLUSHED TO DISK.
*
* SPLITAFTER FINISHES ITS PROCESSING BY SETTING UP PARAMETERS
* FOR THE POSSIBLE CALLING OF ANY SPLIT ROUTINE FOR THE NEXT
* HIGHER LEVEL. NOTE THAT ALL SPLIT ROUTINES ANTICIPATE THAT
* THE CALLER WILL ITERATE SO LONG AS PARAMETERS INDICATE THAT
* MORE SPLITTING NEEDS TO BE DONE. SINCE EACH SPLIT ROUTINE
* DECREMENTS THE CURRENT TREE LEVEL, THE EFFECT IS TO WORK
* FROM THE BOTTOM OF THE TREE TOWARDS THE ROOT, SPLITTING
* UNTIL A LEVEL IS REACHED WHERE EVERYTHING FITS.
*
* ENTRY P<NEW> - LOCATION OF OVERFLOW TEXT.
* NWNEW - NUMBER OF WORDS IN OVERFLOW TEXT.
* PL - CURRENT LEVEL IN TREE PATH.
* ALL PATH CONTROLS SETUP.
* ALL CACHE CONTROLS SETUP.
*
* EXIT PL - DECREMENTED.
* SECTOR AT ORIGINAL TREE LEVEL REBUILT TO CONTAIN
* OVERFLOW TEXT AND SPLIT-OFF ORGINAL TEXT.
* PREVIOUS SECTOR AT ORIGINAL TREE LEVEL RELEASED.
* NWNEW, NEWAFTER, NWAFTER - SET FOR ITERATION.
* NEWDA, NEWNL - SET FOR ITERATION.
* DISP, PADISP[NEW PL] - INCREMENTED FOR ITERATION.
*
* CALLS ALLOC, ALLOCCACHE, MOVEWD, CLEAN, DEALLOC.
*
* USES P<TOO>, FIRST, LAST.
#
P<FROM> = LOC(BFPRU[PACACHE[PL]]) + 1; # SAVE OLD DATA #
MOVEWD(PAFINAL[PL],FROM,OBF);
DEALLOC; # FREE UP OLD BLOCK #
ALLOC; # BUILD OFF BLOCK #
P<FROM> = LOC(OBF) + DISP-1;
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + 1;
MOVEWD(NWAFTER,FROM,TOO);
PAFINAL[PL] = NWAFTER;
ODA = PADA[PL]; # REMEMBER THIS #
ONL = PALAST[PL] + NL - LAST;
PADIRTY[PL] = TRUE; # GET RID OF OFF BLOCK #
CLEAN;
ALLOC; # BUILD NEW BLOCK #
ALLOCCACHE;
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + 1;
MOVEWD(NWBEFORE,OBF,TOO);
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + NWBEFORE + 1;
MOVEWD(NWNEW,NEW,TOO);
PAFINAL[PL] = NWBEFORE + NWNEW;
PADIRTY[PL] = TRUE;
FIRST = PAFIRST[PL]; # FIX UP PATH #
LAST == PALAST[PL];
LAST = LAST + NL;
NEWDA[0] = PADA[PL]; # BUILD NEW DIR ENTRIES #
NEWNL[0] = PALAST[PL] - PAFIRST[PL] + 1;
NEWDA[1] = ODA;
NEWNL[1] = ONL;
NWNEW = 2;
PL = PL - 1; # SET UP NEXT PASS #
NWBEFORE = PADISP[PL] - 1;
NWAFTER = PAFINAL[PL] - PADISP[PL];
DISP = PADISP[PL] + 1;
IOEND
PAGE
PROC CHANGE; # CHANGE TREE #
IOBEGIN(CHANGE)
#
** CHANGE - APPLY CHANGES TO TREE STRUCTURE.
*
* CHANGE IS THE PRINCIPAL ALGORITHM USED TO MANIPULATE THE
* TREE STRUCTURE. CHANGE IS DESIGNED TO BE CALLED BY THE
* INS, REP, AND DEL ENTRY POINTS. THE ALGORITHM WORKS IN
* THREE PHASES. THE FIRST PHASE TAKES CARE OF CASES WHERE AN
* INSERTION OF A LINE OR THE REPLACEMENT OF A LINE WITH AN
* ENLARGED VERSION WILL CAUSE THE CURRENT DATA SECTOR (THE
* BOTTOM LEVEL OF THE TREE STRUCTURE) TO OUTGROW THE SECTOR
* CAPACITY AND CAUSE SPLITTING INTO TWO SECTORS. THIS CAUSES
* ADDITIONAL DIRECTORY ENTRIES TO BE REQUIRED IN THE HIGHER
* TREE LEVELS, WHICH CAN IN TURN CAUSE ONE OR MORE DIRECTORY
* LEVELS TO BE SPLIT INTO MULTIPLE SECTORS. IN THE EXTREME
* CASE, THE ROOT LEVEL OF THE TREE MAY OVERFLOW, IN WHICH
* CASE A NEW INTERMEDIATE LEVEL OF THE TREE MUST BE CREATED,
* WITH THE ROOT SECTOR CONVERTED INTO A DIRECTORY FOR THE
* INTERMEDIATE LEVEL.
*
* AFTER THE FIRST PHASE HAS EXHAUSTED ITS ITERATION, THE
* SECOND PHASE TAKES OVER, IDENTIFYING ANY TREE LEVEL
* STARTING WITH THE DATA LEVEL WHICH HAS BECOME EMPTY AS THE
* RESULT OF DELETIONS. THE SECOND PHASE IS THUS MUTUALLY
* EXCLUSIVE OF THE FIRST PHASE.
*
* THE THIRD PHASE IS A CLEANUP OPERATION. WHILE EACH OF THE
* FIRST TWO PHASES MAY BE A NO-OP, (AND AT LEAST ONE OF THE
* FIRST TWO PHASES WILL INDEED BE A NO-OP), THE THIRD PHASE
* ALWAYS IS IN EFFECT. THE CLEANUP OPERATION CONSISTS OF THE
* APPLICATION OF LEFT-OVER CHANGES. LEFTOVER CHANGES MAY
* CONSTITUTE THE ENTIRE CHANGE IF NEITHER PRELIMINARY PHASE
* WAS NEEDED, OR IF ONE OF THE FIRST TWO PHASES WAS USED,
* THEN IT WILL HAVE LEFT RESIDUAL CHANGES TO BE APPLIED ONE
* LEVEL CLOSER TO THE ROOT THAN THE LAST LEVEL AFFECTED BY
* THE SPLITTING OR COLLAPSING.
*
* ACTUAL APPLICATION OF CHANGES, WHETHER PERFORMED BY THE
* THIRD PHASE OF THE CHANGE ROUTINE, OR WITHIN ONE OF THE
* SPLIT ROUTINES SELECTED BY THE FIRST PHASE OF THE CHANGE
* ROUTINE, SIMPLY CONSIST OF OPENING UP A GAP AT THE RIGHT
* MEMORY LOCATION, OF ZERO, POSITIVE, OR NEGATIVE LENGTH, AND
* MOVING IN THE TEXT OF THE CHANGE, WHICH CAN CONSIST OF ZERO
* OR MORE WORDS.
*
* THE CLEANUP PHASE ALSO INCREMENTS OR DECREMENTS THE COUNT
* OF LINES BRACKETED BY EACH LEVEL OF THE TREE.
*
* ENTRY PALVL - DEEPEST LEVEL IN TREE STRUCTURE.
* PL - CURRENT TREE LEVEL MUST EQUAL PALVL.
* NL - CHANGE IN NUMBER OF LINES IN WORKFILE.
* NWBEFORE - NUMBER OF WORDS BEFORE POINT OF CHANGE.
* NWAFTER - NUMBER OF WORDS AFTER POINT OF CHANGE.
* NWNEW - NUMBER OF WORDS IN CHANGED TEXT.
* DISP - POINTS JUST AFTER CURRENT LINE.
* LINEBUF - CONTAINS TEXT OF CHANGE.
* ALL PATH CONTROLS - SETUP.
* ALL CACHE CONTROLS - SETUP.
*
* EXIT PL - ZERO.
* PALVL - INCREMENTED/DECREMENTED IF TREE DEPTH CHANGED.
* DISP, CURNW - BRACKET NEW CURRENT LINE.
* ALL PATH, CACHE CONTROLS - UPDATED.
* FET AND CIRCULAR BUFFER USED IF NEEDED.
*
* CALLS COUNTCACHE, FLUSHCACHE, SPLITTOP, SPLITAPEND,
* SPLITBEFORE, SPLITAFTER, RECLAIMCACHE, DEALLOC, CLEAN,
* MOVEWD.
*
* USES P<NEW>, P<FROM>, P<TOO>, FIRST, LAST, NWNEW,
* NWBEFORE, NWAFTER, NL.
#
P<NEW> = LOC(LINEBUF); # SET THINGS UP #
FIRST = CURRENT + NL;
LAST = FIRST;
CURNW = NWNEW;
FOR PL = PL WHILE NWBEFORE+NWNEW+NWAFTER GR 63 DO
BEGIN # DO ALL SPLITS #
IF COUNTCACHE LQ 2 THEN FLUSHCACHE;
IF PL EQ 0 THEN SPLITTOP;
IF NWBEFORE EQ PAFINAL[PL] THEN SPLITAPEND;
ELSE IF NWBEFORE+NWNEW LQ 63 THEN SPLITAFTER;
ELSE SPLITBEFORE;
RECLAIMCACHE;
P<NEW> = LOC(NDIR);
END
FOR PL = PL WHILE NWBEFORE+NWNEW+NWAFTER EQ 0 DO
BEGIN # DO ALL COLLAPSING #
DEALLOC;
CLEAN; # TO FREE UP CACHE FRAME #
PALVL = PALVL - 1;
PL = PL - 1;
NWBEFORE = PADISP[PL] - 1;
NWAFTER = PAFINAL[PL] - PADISP[PL];
DISP = PADISP[PL] + 1;
END
P<FROM> = LOC(BFPRU[PACACHE[PL]]) + DISP; # SLIDE AFTER #
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + NWBEFORE +NWNEW + 1;
MOVEWD(NWAFTER,FROM,TOO);
P<TOO> = LOC(BFPRU[PACACHE[PL]]) + NWBEFORE + 1; # MOVE IN NEW #
MOVEWD(NWNEW,NEW,TOO);
PAFINAL[PL] = NWBEFORE + NWNEW + NWAFTER;
PADIRTY[PL] = TRUE;
PALAST[PL] = PALAST[PL] + NL; # FIX UP PATH #
FOR PL = PL-1 STEP -1 UNTIL 0 DO # UPDATE HIGHER DIRS #
BEGIN
P<PRU> = LOC(BFPRU[PACACHE[PL]]);
DIRNL[PADISP[PL]] = DIRNL[PADISP[PL]] + NL;
PADIRTY[PL] = TRUE;
PALAST[PL] = PALAST[PL] + NL;
END
CURRENT = CURRENT + NL; # UPDATE CURRENT #
IOEND
PAGE
PROC INS;
IOBEGIN(INS)
#
** INS - INTERFACE FOR INSERTION OF LINES.
*
* ENTRY CURRENT - CURRENT LINE.
* LINEBUF - LINE IMAGE.
* ALL PATH AND CACHE CONTROLS SETUP.
*
* EXIT CURRENT - INCREMENETED.
* ALL PATH AND CACHE CONTROL UPDATED.
*
* CALLS LINESZ, CHANGE, WIOSTAT.
*
* USES PL, DISP, NL, NWBEFORE, NWNEW, NWAFTER, DISP.
#
CONTROL IFEQ MULTI,1;
WIOSTAT(0); # ACCUMULATE LINE ACCESES #
CONTROL FI;
PL = PALVL; # STANDARD INITCHANGE #
DISP = PADISP[PL];
NL = 1;
NWBEFORE = DISP + CURNW - 1;
NWNEW = LINESZ(LINEBUF);
NWAFTER = PAFINAL[PL] - DISP - CURNW + 1;
DISP = DISP + CURNW;
PADISP[PL] = DISP;
CHANGE;
IOEND
PROC REP;
IOBEGIN(REP)
#
** REP - INTERFACE FOR REPLACEMENT OF LINES.
*
* ENTRY CURRENT - CURRENT LINE.
* LINEBUF - LINE IMAGE.
* ALL PATH AND CACHE CONTROLS SETUP.
*
* EXIT CURRENT - UNCHANGED.
* ALL PATH AND CACHE CONTROL UPDATED.
*
* CALLS LINESZ, CHANGE, WIOSTAT.
*
* USES PL, DISP, NL, NWBEFORE, NWNEW, NWAFTER, DISP.
#
CONTROL IFEQ MULTI,1;
WIOSTAT(0); # ACCUMULATE LINE ACCESES #
CONTROL FI;
PL = PALVL; # STANDARD INITCHANGE #
DISP = PADISP[PL];
NL = 0;
NWBEFORE = DISP - 1;
NWNEW = LINESZ(LINEBUF);
NWAFTER = PAFINAL[PL] - DISP - CURNW + 1;
DISP = DISP + CURNW;
CHANGE;
IOEND
PROC DEL;
IOBEGIN(DEL)
#
** DEL - INTERFACE FOR DELETION OF LINES.
*
* ENTRY CURRENT - CURRENT LINE.
* ALL PATH AND CACHE CONTROLS SETUP.
* DELETCTL - POST-POSITIONING INCREMENT.
*
* EXIT CURRENT - INCREMENTED IF DELETCTL WAS ONE.
* LINEBUF - NEW CURRENT LINE IS READ UP.
* ALL PATH AND CACHE CONTROL UPDATED.
* DELETCTL - FORCED ZERO IF DELETED LAST LINE.
*
* CALLS CHANGE, WIOSTAT, POSR.
*
* USES PL, DISP, NL, NWBEFORE, NWNEW, NWAFTER, DISP,
* P<MVLNBUF>, NEWCURL.
#
CONTROL IFEQ MULTI,1;
WIOSTAT(0); # ACCUMULATE LINE ACCESES #
CONTROL FI;
PL = PALVL; # STANDARD INITCHANGE #
DISP = PADISP[PL];
NL = -1;
NWBEFORE = DISP - 1;
NWNEW = 0;
NWAFTER = PAFINAL[PL] - DISP - CURNW + 1;
DISP = DISP + CURNW;
CHANGE;
IF PALAST[0] LQ CURRENT THEN DELETCTL=0;
CURRENT=CURRENT+DELETCTL;
NEWCURL=CURRENT;
P<MVLNBUF>=LOC(LINEBUF);
POSR;
P<MVLNBUF>=0;
IOEND
PAGE
PROC INIT;
BEGIN
#
** INIT - INITIALIZE MEMORY CELLS FOR WORKIO.
*
* ENTRY NO CONDITIONS.
*
* EXIT P<LINEBUF> -POINTS TO LIN ARRAY.
* ALL ELEMENTS OF PATH ARRAY - NOMINAL.
* ALL CACHE CONTROLS - NOMINAL.
* READ LIST (RDLIST, RDIN, RDOUT) - EMPTY.
* FLAGS (IOACT, IOREAD, IOWRITE) - FALSE.
* ARRAYLEN, DATALEN, ARRAYPRUS, DATAPRUS - INITIALIZED.
* SEGEMENTVER - ZEROED OUT.
#
P<LINEBUF>=LOC(LIN);
FOR PL = 1 STEP 1 UNTIL MAXDEPTH DO # INIT PATH #
BEGIN
PAFIRST[PL] = 0;
PALAST[PL] = 0;
PADISP[PL] = 0;
PAHEAD[PL] = 0;
END
FOR PL = 0 STEP 1 UNTIL MAXCACHE DO # INIT CACHE CTL #
BEGIN
CACHEOWNED[PL]=FALSE; CACHEDIRTY[PL]=FALSE;
CACHEAGE[PL]=-1;
BFHEAD[PL]=0;
END
FOR RDIN = 0 STEP 1 UNTIL RDLIM DO RDLIST[RDIN] = 0;
IOACT = FALSE; # INIT IO #
IOAPEND = FALSE;
IOWRITE = FALSE;
IOREAD = FALSE;
CURNW = 0;
SEGMENTVER=0;
ARRAYLEN=LOC(ARRAYEND)-LOC(ARRAYSTART);
ARRAYPRUS=(ARRAYLEN+O"77")/O"100";
DATALEN=LOC(DATAEND)-LOC(DATASTART);
DATAPRUS=(DATALEN+O"77")/O"100";
END
PAGE
PROC RESUMIO;
IOBEGIN(RESUMIO)
#
** RESUMIO - RESUME EDITOR STATUS FROM LEFTOVER WORKFILE.
*
* RESUMIO ATTEMPTS TO VALIDATE A FILE AS CONTAINING THE TEXT
* AND STATUS LEFT BY A PREVIOUS EDIT SESSION, OR BY A
* SINGLE/MULTI USER TRANSITION. SINCE THE SCALAR AND ARRAY
* DATA SEGMENTS CONTAIN NEARLY ALL EDITOR DATA, SUCCESSFUL
* EXECUTION OF RESUMIO CHANGES EVERYTHING.
*
* THE FOLLOWING CONDITIONS ARE REQUIRED TO VALIDATE AN
* APPARENT WORKFILE AS A LEGITIMATE BASIS FOR RESUMPTION --
*
* 1. THE FIRST WORD MUST BE FORMATTED AS A ROOT SECTOR
* HEADER WORD. THE DISK ADDRESS FIELD MUST BE ONE AND
* THE TOP FLAG MUST BE ON AND THE DIRECTORY AND DATA
* FLAGS MUST BE UNEQUAL.
*
* 2. THE FIRST RECORD OF THE FILE MUST BE OF LENGTH
* 64 WORDS LONGER THAN THE SCALAR DATA SEGMENT, AND
* THE SECOND RECORD OF THE FILE MUST EXACTLY MATCH
* THE LENGTH OF THE ARRAY SEGMENT.
*
* 3. THE SEGMENTLEN1 AND SEGMENTLEN2 FIELDS OF THE SCALAR
* SEGMENT MUST MATCH THE ABOVE LENGTHS, AND THE
* SEGMENTVER FIELD MUST MATCH THE VERSION NUMBER
* COMPILED INTO THIS PROGRAM.
*
* AFTER READING UP ALL EDITOR DATA, THE FOLLOWING WORKIO
* CONTROLS ARE RE-INITIALIZED - THE PATH VECTOR IS REFRESHED
* TO MATCH THE CURRENT LINE, THE SCHEDULING FLAGS (IOACT,
* IOREAD, ETC.) ARE CLEARED, MAXDA AND DANEXT ARE BASED ON
* FILE SIZE, AND SEGMENTVER IS CLEARED. THE WORKFILE IS THEN
* IMMEDIATELY CHECKPOINTED SO THAT IT CANNOT BE RESUMED
* AGAIN, THUS INTERLOCKING IT.
*
* ENTRY NO CONDITIONS.
*
* EXIT EVERYTHING IN THE WHOLE EDITOR IS CHANGED.
* IORESUMED - SUCCESS/FAILURE INDICATOR.
*
* CALLS INIT, INITFET, BUFUSAGE, ALLOCCACHE, RDWBUF, READ,
* WAIT, TRAGIC, SKIPEI, POSR, CLEANALL.
#
INIT;
IORESUMED=FALSE;
INITFET(DISK,DSKSIZ);
REWIND(FET,0); # TRY TO READ PRU 1 #
WAIT;
READ(FET,0);
WAIT;
IF BUFUSAGE GR O"100" THEN
BEGIN
PL=0;
PADA[0]=1;
ALLOCCACHE;
P<TOO>=LOC(BFPRU[PACACHE[0]]);
RDWBUF(O"100");
PAHEAD[0] = BFHEAD[PACACHE[0]];
END
IF PADA[0] EQ 1 AND PATOP[0]
AND BOOLDIFF(PADIR[0],PADATA[0]) THEN
BEGIN
# READ FIRST FOUR WORDS TO VERIFY SEGMENT DESCRIPTORS #
P<TOO>=LOC(DATASTART);
NWNEW=BUFUSAGE;
NWNEW=MIN(NWNEW,4);
RDWBUF(NWNEW);
IORESUMED=FALSE;
IF SEGMENTVER NQ IOVERSION OR SEGMENTLEN1 NQ DATALEN
OR SEGMENTLEN2 NQ ARRAYLEN THEN IORET
# SEGMENT DESCRIPTORS VALID, READ REST OF SCALAR SEGMENT #
P<TOO>=LOC(DATASTART)+4;
NWNEW=BUFUSAGE;
NWNEW=MIN(DATALEN-4,NWNEW);
RDWBUF(NWNEW);
READ(FET,0);
WAIT;
# READ ARRAY SEGMENT #
P<TOO>=LOC(ARRAYSTART);
NWNEW=BUFUSAGE;
NWNEW=MIN(ARRAYLEN,NWNEW);
RDWBUF(NWNEW);
IF ABORTED THEN TRAGIC(ERRSTRING);
SKIPEI(FET,0);
WAIT;
MAXDA=FETCRI-1;
DANEXT=FETCRI;
PADISP[0] = 1; # SET UP PATH[0] #
PAFIRST[0] = 0;
PALAST[0] = -1;
P<PRU> = LOC(BFPRU[PACACHE[0]]);
IF PADIR[0] THEN
BEGIN
FOR DISP = 1 STEP 1 UNTIL PAFINAL[0]
DO PALAST[0] = PALAST[0] + DIRNL[DISP];
END
ELSE # NOTE INTERNAL CHARSET SIGN BIT USAGE #
BEGIN
FOR DISP = 1 STEP 1 UNTIL PAFINAL[0]
DO IF B<0,1>DATWORD[DISP] NQ 0 THEN PALAST[0] = PALAST[0] + 1;
END
IORESUMED=TRUE; # RESET FLAGS READ WITH DATA #
IOACT=FALSE;
IOREAD=FALSE;
IOWRITE=FALSE;
IOAPEND=FALSE;
PALVL = 0; # BUILD REST OF PATH #
NEWCURL=0;
P<MVLNBUF>=LOC(LINEBUF);
POSR;
P<MVLNBUF>=0;
SEGMENTVER=0; # INTERLOCK FILE ONWESHIP #
CLEANALL;
END
IOEND
PAGE
CONTROL IFEQ SINGLE,1;
PROC INITIO;
IOBEGIN(INITIO)
#
** INITIO - FORMAT A NEW WORKFILE.
*
* INITIO IS USED TO BUILD A WORKFILE FROM SCRATCH OR TO
* DISCARD OLD WORKFILE CONTENT AND REFRESH IT.
*
* ENTRY NO CONDITIONS.
*
* EXIT FILE INITIALIZED.
* ALL PATH CONTROLS NOMINAL.
* ALL CACHE CONTROL NOMINAL.
* MAXDA - ZERO
* DANEXT - FIRST ALLOCATABLE DISK ADDRESS RIGHT AFTER
* ROOT SECTOR, AND SCALAR AND ARRAY DATA SEGMENTS.
* IORESUMED - TRUE TO SHOW WORKIO ACTIVE.
* SEGMENTVER - ZERO IN MEMORY AND ON DISK TO INTERLOCK.
*
* CALLS INITFET, EVICT, WAIT, INIT, ALLOCCACHE, CLEANALL.
#
INITFET(DISK,DSKSIZ);
EVICT(FET,0);
WAIT;
INIT;
MAXDA = 0; # SET MAXDA AND DANEXT #
DANEXT = 2+DATAPRUS+ARRAYPRUS;
PAHEAD[0] = 0; # BUILD TOP BLOCK #
PADATA[0] = TRUE;
PATOP[0] = TRUE;
PADIRTY[0] = TRUE;
PADA[0] = 1;
PAFINAL[0] = 1;
PL=0;
ALLOCCACHE;
P<PRU> = LOC(BFPRU[PACACHE[0]]);
DATWORD[1] = NULLIN;
CURNW = 1;
PADEPTH[0] = 0;
PADISP[0] = 1; # SET UP PATH #
PAFIRST[0] = 0;
PALAST[0] = 0;
PALVL = 0;
CURRENT = 0;
IORESUMED=TRUE; # SHOW WORKIO ALIVE #
SEGMENTVER = 0; # INTERLOCK FILE OWNERSHIP #
CLEANALL;
IOEND
CONTROL FI;
PAGE
PROC CHECKIO;
IOBEGIN(CHECKIO)
#
** CHECKIO - CHECKPOINT WORKFILE.
*
* CHECKIO IS USED TO ASSURE THAT ALL MEMORY DATA IS FULLY
* WRITTEN TO DISK. THIS CAN BE USED TO PREPARE FOR END OF
* JOB STEP OR FOR TRANSITION BETWEEN SINGLE AND MULTIPLE USER
* VERSIONS OF THE EDITOR. BY CHECKPOINTING THE WORKFILE, IT
* BECOMES POSSIBLE TO EXECUTE THE RESUMIO ROUTINE AND FULLY
* RESTORE THE EDITOR STATUS.
*
* ENTRY IORESUMED - SHOWS WHETHER WORKFILE MANAGER EVER ACTIVE.
* CURRENT - CURRENT LINE ORDINAL.
*
* EXIT SEGMENTVER, SEGMENTLEN1, SEGMENTLEN2 - SETUP.
* CACHE FLUSHED, FET AND CIRCULAR BUFFER COMPLETE.
*
* CALLS CLEANALL.
#
IF NOT IORESUMED THEN IORET # WORKIO NEVER ALIVE #
SAVECURL=CURRENT;
SEGMENTVER = IOVERSION; # SHOW VALID WORKFILE FORMAT #
SEGMENTLEN1 = DATALEN;
SEGMENTLEN2 = ARRAYLEN;
CLEANALL; # WRITE OUT WHOLE PATH #
IOEND
END TERM