;<134-TENEX>FREE.MAC;39 28-APR-75 12:14:22 EDIT BY CLEMENTS ;<134-TENEX>FREE.MAC;38 28-APR-75 11:32:15 EDIT BY CLEMENTS ;<134-TENEX>FREE.MAC;37 24-APR-75 14:15:03 EDIT BY CLEMENTS ;FREE.REL;36 13-FEB-73 18:57:03 EDIT BY CLEMENTS ; ADDED JBCLCK IN ASGPAG ;FREE.MAC;35 21-NOV-72 7:26:46 EDIT BY WALLACE ;FREE.MAC;34 21-NOV-72 7:14:26 EDIT BY WALLACE ;FREE.MAC;33 28-SEP-72 14:51:38 EDIT BY TOMLINSON ;FREE.MAC;32 28-SEP-72 14:20:45 EDIT BY TOMLINSON ; Relfre returns biggest block found if failure ;FREE.MAC;31 29-JUN-72 9:56:22 EDIT BY TOMLINSON SEARCH STENEX,PROLOG TITLE FREE ; Storage routines SUBTTL R.s.tomlinson EXTERN BHC EXTERN HSHLUK,MAPDIR,RESAC,SAVAC,SKPRET,USTDIR,XPAND0 EXTERN BUGCHK,BUGHLT,MSTKOV USE SWAPPC ; Assign space in free storage region ; Call: RH(A) ; Location of free storage header ; LH(A) ; Index field for references to a and pointers ; ; I.e. @a references first word of header ; B ; Size of block needed ; PUSHJ P,ASGFRE ; Return ; +1 ; Not enough space ; +2 ; Ok, in a, the location of the block (absolute) ; Clobbers a,b,c,d ; Calling routine must take measures to prevent loss of free storage ; Space by inhibiting psi's until the space assigned ; Has been accounted for ; Free storage header format is: ; 0 ; Lh points to first free block ; 1 ; Lock ; 2 ; Space counter ; 3 ; Most common block size ; 4 ; Lh has max top of free storage ; Rh has min bottom ; 5 ; Temp 2 ; 6 ; Temp 3 ASGFRE::MOVEI C,@A ; Get origin of header CAMLE B,2(C) ; Any possibility of success? POPJ P, ; No. return immediately LOCK 1(C) ; Lock this free storage list PUSH P,B ; Save desired block size PUSH P,[0] ; BIGEST BLOCK SEEN SO FAR MOVEI B,377777 MOVEM B,5(C) ; Initial best block size SETZM 6(C) ; Initial location of best block MOVE B,A ; Start with the header word HLLZ C,A ; Initialize index field MOVEI A,@A ASGFR1: HLR C,@B ; Get pointer to next block TRNN C,777777 JRST ASGFR2 ; No more free blocks to examine HRRZ D,@C ; Get size of the block CAMLE D,0(P) MOVEM D,0(P) CAMN D,-1(P) ; Is it the right size? JRST ASGFR3 ; Just right use it CAML D,-1(P) ; Too small CAML D,5(A) ; Or bigger than best? JRST ASGFR4 ; Yes, ignore it MOVEM D,5(A) ; This one is better MOVEM B,6(A) ASGFR4: MOVE B,C ; Step to next block JRST ASGFR1 ; And repeat ASGFR2: SKIPN B,6(A) ; Did we find anything? JRST [ UNLOCK 1(A) ; No. unlock and return POP P,B ; Make transparent to b on error SUB P,BHC+1 POPJ P,] MOVE D,-1(P) ; Get desired size HLR C,@B ; Get pointer to block to be used HRRM D,@C ; Convert to desired size ADD D,C ; Pointer remainder of block HRLM D,@B ; Point prev to remainder HLLZ B,@C ; Get next HLLM B,@D ; Point remainder to it MOVE B,5(A) SUB B,-1(P) ; Size of remainder HRRM B,@D ; To header of remainder ASGFR5: SUB P,BHC+1 MOVN B,0(P) ADDM B,2(A) ; Reduce count of space left UNLOCK 1(A) MOVEI A,@C ; Get origin of block HRROS (A) ; Set lh to ones HRRZ B,(A) ; Get rh HRRZI C,2(A) HRLI C,1(A) ADD B,A HRRZS B SETZM -1(C) CAIGE B,(C) BLT C,-1(B) ; Zero the block POP P,B AOS (P) POPJ P, ASGFR3: HLL D,@C HLLM D,@B ; Point predecessor to successor JRST ASGFR5 ; Release free storage block ; Call: A ; Location of free storage header (like asgfre) ; B ; Location of the block to be returned ; PUSHJ P,RELFRE ; Clobbers b,c,d RELFRE::HLLZ C,A SUBI B,@C HLL B,A PUSH P,A MOVEI A,@A LOCK 1(A) HRRZ D,0(A) JUMPE D,RELFR0 ; Jump if old style free block HLRZ D,4(A) HRRZ A,4(A) CAILE D,0(B) CAILE A,0(B) JRST RELFRA MOVEI A,@0(P) JRST RELFR0 RELFRA: BUG(CHK,) MOVEI A,@0(P) UNLOCK 1(A) POP P,A POPJ P, RELFR0: PUSH P,B MOVE B,-1(P) RELFR1: HLR C,@B ; Get loc of next block TRNN C,777777 JRST RELFR2 ; End of list CAML C,0(P) JRST RELFR2 ; Or above block being returned MOVE B,C JRST RELFR1 RELFR2: CAME C,0(P) ; Releasing a block already released? JRST RELFRB UNLOCK(<1(A)>) BUG(CHK,) POP P,B POP P,A POPJ P, RELFRB: HRRZ D,@0(P) ADDM D,2(A) ; Augment count of remaining storage ADD D,0(P) ; Get end of block being returned CAME D,C ; Same as following block location? JRST RELFR3 ; No HRRZ D,@C ; Get length of following block ADDM D,@0(P) ; Augment length of block being returned HLLZ D,@C ; Get loc of successor of successor HLLM D,@0(P) RELFR5: MOVE C,0(P) HRLM C,@B HRRZ D,@B ; Length of predecessor ADD D,B ; End of predecessor CAME D,C ; Same as new block JRST RELFR4 ; No, done MOVE C,@C HLLM C,@B HRRZS C ADDM C,@B RELFR4: UNLOCK 1(A) POP P,B POP P,A POPJ P, RELFR3: HRLM C,@0(P) ; Point returned block to successor JRST RELFR5 ; Assign a page in job area ; Call: PUSHJ P,ASGPAG ; Return ; +1 ; None available ; +2 ; Success ; A ; Address of origin of page ASGPAG::LOCK(JBCLCK) ;LOCK THE PAGE ASSIGNMENT MOVSI C,-4 ; Four words of bits ASGPG1: MOVE A,JBCOR(C) JFFO A,ASGPG2 ; Any bits? AOBJN C,ASGPG1 ; No, try next word UNLOCK(JBCLCK) POPJ P, ; No words left ASGPG2: MOVN B,A+1 MOVSI A,400000 ROT A,(B) ANDCAM A,JBCOR(C) ; Mark as used UNLOCK(JBCLCK) MOVEI A,(C) IMULI A,^D36 SUB A,B LSH A,9 ADDI A,PJMA ; Origin of job mapped area JRST SKPRET ; Return page ; Call: A ; Location of page ; PUSHJ P,RELPAG RELPAG::SUBI A,PJMA LSH A,-9 IDIVI A,^D36 MOVSI C,400000 MOVNS A+1 ROT C,(A+1) IORM C,JBCOR(A) ; Clear the bit POPJ P, ; Assign free storage in directory ; Call: B ; Size of block needed ; PUSHJ P,ASGDFR ; Return ; +1 ; Not enough room ; +2 ; Ok, in a, the location of the block ; Clobbers a,b,c,d ASGDFR::MOVE A,[XWD E,DIRFRE-DIRORG] PUSH P,E MOVEI E,DIRORG PUSHJ P,ASGFRE JRST ASGDF1 ; No room, try garbage collection ASGDF3: AOS -1(P) ASGDF2: POP P,E POPJ P, ASGDF1: PUSHJ P,GCDIR MOVE A,[XWD E,DIRFRE-DIRORG] PUSHJ P,ASGFRE JRST ASGDF4 ; Still no room JRST ASGDF3 ; Now ok ASGDF4: MOVE A,FRETOP SUB A,DIRFRE+2 ADDI A,2(B) ; Where fretop must be if moved up + PUSH P,B ; Save b CAMG A,SYMBOT ; Will this overlap symtab? JRST ASGDF5 ; No PUSHJ P,XPAND0 ; Move symtab up JRST [ POP P,B JRST ASGDF2] ; Fail SKIPA A,[-10] ; Space to leave for symtab after xpand ASGDF5: MOVNI A,2 ; Space to leave if no xpand ADD A,SYMBOT ; Target fretop MOVE B,FRETOP HRLM A,DIRFRE+4 MOVEM A,FRETOP ; New fretop SUB A,B ; Length of additional storage MOVEM A,DIRORG(B) ; Make block header MOVEI B,DIRORG(B) ; Convert to absolute PUSHJ P,RELDFR ; Let reldfr put back the storage POP P,B MOVE A,[XWD E,DIRFRE-DIRORG] PUSHJ P,ASGFRE JRST ASGDF2 JRST ASGDF3 ; Release free storage in directory ; Call: B ; Location of the block to be released ; PUSHJ P,RELDFR ; Returns +1 always ; Clobbers a,b,c,d RELDFR::MOVE A,[XWD E,DIRFRE-DIRORG] PUSH P,E MOVEI E,DIRORG PUSHJ P,RELFRE POP P,E POPJ P, ; Assign job storage ; Call: B ; Size of block needed ; PUSHJ P,ASGJFR ; Return ; +1 ; Not enough room ; +2 ; Success. location of block in b ASGJFR::MOVEI A,JSBFRE PUSHJ P,ASGFRE ; Attempt to assign JRST ASGJF1 ; Not enough AOS (P) ; Success POPJ P, ASGJF1: PUSH P,B PUSH P,C PUSHJ P,ASGPAG ; Get another page of job storage JRST ASGJF2 ; No pages left MOVEI B,1000 HRROM B,(A) ; Make a free block out of it MOVEI B,1000(A) HLRZ C,4+JSBFRE CAMGE C,B HRLM B,4+JSBFRE MOVE B,A MOVEI A,JSBFRE PUSHJ P,RELFRE ; Release the new block POP P,C POP P,B JRST ASGJFR ; Try again ASGJF2: POP P,C POP P,B POPJ P, ; Fail ; Garbage collect a directory ; Call: PUSHJ P,GCDIR ; Transparent EXTERN BITS GCDIR:: PUSHJ P,SAVAC ; Sav ac's ADD P,[XWD 5,5] ; Make room for temps on stack JUMPGE P,MSTKOV PUSHJ P,ASGPAG ; Get a page for bit table BUG(HLT,) HRLI A,A ; Index by a MOVEM A,0(P) ; Used to reference bit table HRLZ B,A HRRI B,1(A) ; Prepare blt pointer SETZM 0(A) BLT B,777(A) ; And zero page JSP D,GCSCAN ; Initialize scanner GCDI0: JSP D,0(D) ; Co-routine jump JRST GCDI2 ; No more JRST GCDI0 ; Pointer only CAMGE E,FRETOP CAIGE E,DIFREE-DIRORG JRST GCDI0 ; Not pointer to dynamic area HRRZ A,DIRORG(E) ; Get length of block ADDI A,-1(E) ; Last word of block IDIVI A,^D36 ; Separate bit and word MOVEM A,-1(P) ; Save word number MOVN C,BITS(B) ; Mask of bits up to and including last MOVE A,E IDIVI A,^D36 ; Separate first word into bit and word MOVE B,BITS(B) ; Get bit for beginning of block LSH B,1 ; Bit to left of desired bits SOS B ; Gives bits to right GCDI1: CAMN A,-1(P) ; Last word of bits? AND B,C ; Yes, reduce bits IORM B,@0(P) ; Or into bit word CAMN A,-1(P) ; Done? JRST GCDI0 ; Yes, next block SETO B, ; All ones for next word AOJA A,GCDI1 ; No ; Bit table is now formed, prepare for compaction GCDI2: MOVE A,FRETOP ADDI A,^D35 IDIVI A,^D36 ; End of bit table MOVEM A,-1(P) MOVEI A,DIFREE-DIRORG MOVEM A,-2(P) ; Current top of compacted storage SOS A ; Start scanning below good stuff MOVEM A,-3(P) ; In order to not miss anything GCDI3: MOVE A,-3(P) ; Get current loc IDIVI A,^D36 ; Separate MOVN B,BITS(B) ; Get mask of ones to ignore GCDI4: ANDCA B,@0(P) ; Get ones in bit table JFFO B,GCDI5 ; Find first one and jump CAMGE A,-1(P) ; No ones. end check AOJA A,GCDI4 ; Not end, try next. note b has 0 MOVE A,-2(P) ; Top of compacted storage MOVE B,FRETOP ; Top of dynamic area SUB B,A ; Recompute length of free area MOVEM B,DIRFRE+2 JUMPE B,[HRRZS DIRFRE ; Empty free list JRST GCDID] MOVEM B,DIRORG(A) HRLM A,DIRFRE GCDID: HRRZ A,0(P) ; Page address PUSHJ P,RELPAG ; Release SUB P,[XWD 5,5] ; Flush stack PUSHJ P,RESAC ; Restore ac's POPJ P, ; Found a one, now look for a zero GCDI5: MOVN B,BITS(B+1) ; Get mask of bits to left inc first 1 MOVEM B+1,-4(P) MOVE C,A IMULI C,^D36 ADDM C,-4(P) ; Location of first good word GCDI6: ANDCB B,@0(P) ; Get zeros, ignore those to left JFFO B,GCDI7 ; Find "zeroes" CAMGE A,-1(P) AOJA A,GCDI6 ; Try next word, note b=0 MOVEI B+1,^D36 GCDI7: IMULI A,^D36 ADD A,B+1 MOVEM A,-3(P) ; New bottom of garbage MOVE B,-2(P) ; Get "to" CAMN B,-4(P) ; Equals "from"? JRST GCDI9 ; Move not needed HRL B,-4(P) ; And "from" ADD B,[XWD DIRORG,DIRORG] SUB A,-4(P) ; Length of block ADDI A,(B) ; Compute end address BLT B,-1(A) ; Blt it ; State now is: ; -2(P) ; Top of previous compact storage ; -3(P) ; End of good block+1 ; -4(P) ; Beg of good block JSP D,GCSCAN ; Set up scan GCDI8: JSP D,0(D) ; Get a pointer JRST GCDI9 ; Done JFCL CAMGE E,-3(P) ; Beyond end CAMGE E,-4(P) ; Or before beg JRST GCDI8 ; Needs no adjustment ADD E,-2(P) ; Adjust by new bottom SUB E,-4(P) ; Minus old bottom JRST GCDI8 GCDI9: MOVE A,-3(P) ; End SUB A,-4(P) ; Minus beg = length ADDM A,-2(P) ; New top of compacted storage JRST GCDI3 ; Loop to find next good block ; The gc scanner ; This routine knows where all the good things are ; And operates as a coroutine with the garbage collector ; Started up with ; JSP D,GCSCAN ; For each datum, ; JSP D,(D) ; Returns ; +1 ; Nothing left ; +2 ; Possible pointer for updating ; +3 ; Pointer to good stuff to be retained ; Uses f,num,dev ; Returns datum in e GCSCAN: MOVE NUM,SYMBOT ; Start with first symbol table entry JSP D,(D) GCSCN1: CAML NUM,SYMTOP ; Entire table scanned? JRST GCSCN4 ; Yes. no skip return HLRZ E,DIRORG(NUM) ; Get pointer to string JSP D,2(D) HRLM E,DIRORG(NUM) ; Put back updated pointer HRRZ E,DIRORG(NUM) ; Get pointer to data block SKIPG DIRNUM JRST GCSCN5 TRZE E,700000 AOJA NUM,GCSCN1 ; No blocks for current uses of rh JSP D,2(D) HRRM E,DIRORG(NUM) GCSCN3: MOVE DEV,E ; Save root of fdb's GCSCN2: MOVE F,E ; Point to current fdb HRRZ E,FDBCTL+DIRORG(F) ; Pointer to name JSP D,2(D) HRRM E,FDBCTL+DIRORG(F) HLRZ E,FDBEXT+DIRORG(F) JSP D,2(D) HRLM E,FDBEXT+DIRORG(F) MOVE E,FDBACT+DIRORG(F) TLNN E,777777 JSP D,2(D) MOVEM E,FDBACT+DIRORG(F) HRRZ E,FDBVER+DIRORG(F) ; Pointer to next version JSP D,2(D) HRRM E,FDBVER+DIRORG(F) ; Update JUMPN E,GCSCN2 ; Scan all versions HRRZ E,FDBEXT+DIRORG(DEV) JSP D,2(D) ; Update HRRM E,FDBEXT+DIRORG(DEV) JUMPN E,GCSCN3 ; Scan all extensions AOJA NUM,GCSCN1 ; Scan all of the symtab GCSCN4: HRRZ E,DIRSAV JSP D,2(D) MOVEI E,-DIRORG(E) JSP D,2(D) MOVEI E,DIRORG(E) HRRM E,DIRSAV HRRZ E,DIRSCN JSP D,1(D) MOVEI E,-DIRORG(E) JSP D,1(D) MOVEI E,DIRORG(E) HRRM E,DIRSCN HRRZ E,DIRLOC JSP D,1(D) MOVEI E,-DIRORG(E) JSP D,1(D) MOVEI E,DIRORG(E) HRRM E,DIRLOC JRST (D) ; Subindex scanner GCSCN5: MOVE DEV,E ; Remember directory number SETZB E,F ; Zero mask and value PUSHJ P,CHGHSH ; Update hash entry bits masked 1 JSP D,2(D) ; Return pointer MOVSI F,7777 PUSHJ P,CHGHSH ; This really updates it MOVE DEV,E ; Remember pointer to ddb HLRZ E,DIRORG+DDBNAM(DEV) ; Pointer to password JSP D,2(D) HRLM E,DIRORG+DDBNAM(DEV) HRRZ E,DIRORG+DDBNAM(DEV) ; Pointer to name JSP D,2(D) HRRM E,DIRORG+DDBNAM(DEV) AOJA NUM,GCSCN1 CHGHSH: PUSH P,A ; Save temps PUSH P,B PUSH P,C PUSH P,DIRNUM ; Remember where to come back to MOVE A,DEV ; Get directory number to look up PUSHJ P,HSHLUK BUG(HLT,) HRLZS E XOR E,DIRORG(B) ; Get difference AND E,F ; Retain bits to change XORB E,DIRORG(B) ; Change bits, keep result HLRZS E ANDI E,7777 PUSHJ P,USTDIR POP P,A PUSHJ P,MAPDIR ; Return to proper subindex POP P,C ; Restore temps POP P,B POP P,A POPJ P, IFN 0,< ; Put item onto deallocation list ; Call: LH(A) ; Routine to call to deallocate the item ; RH(A) ; Item identifier (address usually) ; PUSHJ P,PUTITM ; Items put on the deallocation are automatically deallocated whenever ; A psi occurs and the user's program changes the pc such that ; The monitor routine in progress does not complete PUTITM::PUSH P,B ; Free up some ac's PUSH P,A PUTIT0: MOVE A,INTLVL ; Get current interrupt level SKIPE B,ITMHD(A) ; Get the correct item list header JRST PUTIT1 PUSH P,A ; No header, create one MOVEI A,PSBFRE MOVEI B,6 PUSHJ P,ASGPAG ; Assign a block of psb free storage JSR BUGHLT POP P,B MOVEM A,ITMHD(B) ; Point the header to the block HRLI A,1(B) HRRI A,2(B) SETZM 1(B) BLT A,6(B) ; Clear the block PUTIT1: HRLI B,5 AOS B ; Make aobjn pointer PUTIT2: SKIPN (B) ; Search for an empty slot JRST PUTIT3 ; Found AOBJN B,PUTIT2 MOVE B,INTLVL ; No empty slots MOVEI A,0 EXCH A,ITMHD(B) ; Clear header, get old header HRLI A,RELITB ; Make into an item word PUSHJ P,PUTITM ; Call self, making first thing on JRST PUTIT0 ; New block the old block. try again PUTIT3: POP P,A MOVEM A,(B) POP P,B POPJ P, ; Release all items on interrupt level specified in a ; Call: A ; Interrupt level ; PUSHJ P,RELITM RELITM::PUSH P,ITMHD(A) SETZM ITMHD(A) POP P,A JUMPN A,RELITB POPJ P, RELITB: PUSH P,A PUSH P,B HRLI A,-5 AOS A RELIT1: SKIPN B,(A) JRST RELIT2 PUSH P,A HRRZ A,B HRLZS B PUSHJ P,(B) POP P,A RELIT2: AOBJN A,RELIT1 MOVE B,-1(P) MOVEI A,PSBFRE PUSHJ P,RELFRE POP P,B POP P,A POPJ P, > END