Trailing-Edge
-
PDP-10 Archives
-
tops20_v6_1_tcpip_distribution_tp_ft6
-
galaxy-sources/glxmem.mac
There are 26 other files named glxmem.mac in the archive. Click here to see a list.
TITLE GLXMEM -- Memory Manager for GALAXY Programs
SUBTTL Preliminaries
;
;
; COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION
; 1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985
;
; THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
; AND COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE
; AND WITH THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS
; SOFTWARE OR ANY OTHER COPIES THEREOF MAY NOT BE PROVIDED OR
; OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. NO TITLE TO
; AND OWNERSHIP OF THE SOFTWARE IS HEREBY TRANSFERRED.
;
; THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE
; WITHOUT NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT
; BY DIGITAL EQUIPMENT CORPORATION.
;
; DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY
; OF ITS SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY
; DIGITAL.
SEARCH GLXMAC ;SUBSYSTEMS SYMBOLS
PROLOG(GLXMEM,MEM) ;PRODUCE PROLOG CODE
MEMMAN==:27 ;Maintenance edit number
MEMDEV==:37 ;Development edit number
VERSIN (MEM) ;Generate edit number
EXTERNAL OTSEDT,SCNEDT,INTEDT
EXSYM2==:OTSEDT+SCNEDT+INTEDT+MEMEDT ;Calculate part of GLXVRS
SUBTTL Entry Points found in GLXMEM
ENTRY M%INIT ;Initialization entry point
ENTRY M%ACQP ;Acquire a page from the page map (page number is returned)
ENTRY M%RELP ;Return page to the page map (by page number)
ENTRY M%GPAG ;Get a free page (address is returned)
ENTRY M%RPAG ;Return a page to the page map (by address)
ENTRY M%IPSN ;Notification that IPCF send of page is about to occur
ENTRY M%NXPG ;Get a non-existant page for an IPCF receive
ENTRY M%IPRC ;Notification that IPCF receive of a page has occurred
ENTRY M%IPRM ;Make room for IPCF page we couldn't fit in core
ENTRY M%AQNP ;Acquire N contiguous pages from the map
ENTRY M%RLNP ;Release pages acquired from M%AQNP
ENTRY M%CLNC ;Clean Core For Cusps About To Go Dormant
ENTRY M%FPGS ;Return free page counter
ENTRY M%GMEM ;Allocate a chunk of memory
ENTRY M%RMEM ;De-allocate a chunk of memory
ENTRY PAGFRE ;Called from I%INIT
SUBTTL Table of Contents
; Table of Contents for GLXMEM
;
;
; Section Page
; 1. Preliminaries. . . . . . . . . . . . . . . . . . . . . 1
; 2. Entry Points found in GLXMEM . . . . . . . . . . . . . 2
; 3. Table of contents. . . . . . . . . . . . . . . . . . . 3
; 4. Revision History . . . . . . . . . . . . . . . . . . . 4
; 5. Global Storage . . . . . . . . . . . . . . . . . . . . 5
; 6. M%INIT - Initialize the memory system. . . . . . . . . 6
; 7. System PFH interface . . . . . . . . . . . . . . . . . 7
; 8. NODDT - Remove DDT.VMX routine for TOPS10. . . . . . . 8
; 9. PAGFRE - Determine if a given page is free or not. . . 9
; 10. M%GPAG - Acquire one free page full of zeroes (address) 10
; 11. M%ACQP - Acquire one free page full of zeroes (page number) 10
; 12. M%AQNP - Acquire several free pages full of zeroes . . 11
; 13. FNDPAG - Find first free page. . . . . . . . . . . . . 11
; 14. CREPAG - Routine to create a page. . . . . . . . . . . 12
; 15. M%NXPG - Acquire the number of a free page for IPCF reception 13
; 16. M%RLNP - Release contiquous free pages . . . . . . . . 14
; 17. M%RELP - Release a single page to the free pool (by page number) 14
; 18. M%RPAG - Release a single page to the free pool (by address) 14
; 19. M%FPGS - Return number of free pages . . . . . . . . . 14
; 20. M%IPSN - Inform that page is about to be sent via IPCF 15
; 21. M%IPRC - Inform that page has been created via IPCF. . 15
; 22. M%GMEM - Allocate a chunk of memory. . . . . . . . . . 16
; 23. APMEM - Routine to add one page to the chunk pool . . 17
; 24. PGCOLL - Routine to remove whole pages from chunk free pool 18
; 25. M%RMEM - Routine to de-allocate a memory chunk . . . . 19
; 26. Consistency checking routines. . . . . . . . . . . . . 20
; 27. M%CLNC - Routines for cleaning up core . . . . . . . . 21
; 28. M%IPRM - Routine to find a free page for an IPCF receive 22
; 29. End. . . . . . . . . . . . . . . . . . . . . . . . . . 23
SUBTTL Revision History
COMMENT \
27
Don't reset PT.ADR when restarting so GLXMEM doesnt stopcode.
***** Release 4.2 -- begin maintenance edits *****
***** Release 5.0 -- begin development edits *****
35 5.1002 28-Dec-82
Move to new development area. Clean up edit organization. Update TOC.
36 5.1132 9-Apr-84
Calculate part of GLXVRS here to get around the Polish stack limitation
of MACRO.
37 5.1177 6-Nov-84
Change M%FPGS to also return the size of the maximum block of
contiguous free pages.
\ ;End of Revision History
SUBTTL Global Storage
$GDATA VRTPRV ;VIRTUAL PRIVS FLAG (0 = NONE, 1 = YES)
$GDATA PAGTBL,MEMSIZ ;PAGE MAP OF ALL PAGES
$DATA PAGSTA ;STARTING POINT FOR PAGSRC PAGES
$DATA MEMBEG,0 ;START OF ZEROABLE DATA FOR GLXMEM
$DATA AVBPGS ;COUNT OF RELP'D BUT IN CORE PAGES
$DATA FREPGS ;COUNT OF FREE PAGES IN ADR SPACE
$DATA FREWDS ;FREINI, EXPRESSED AS WORDS NOT PAGES
$DATA DICT,DCT.MX+1 ;CHUNK DICTIONARY OF FREQUENTLY USED SIZES
$DATA PANFLG ;FLAG FOR CHUNK MANAGER
$DATA APCNT ;COUNTER FOR COLLECT OR ADD PAGE TEST
$DATA CNT.AP ;COUNTER: PAGES ADDED TO FREE CHUNK POOL
$DATA CNT.DD ;COUNTER: TIMES DICT HAD TO BE DUMPED
$DATA CNT.PC ;COUNTER: NUMBER OF PAGES GARBAGE COLLECTED
$DATA CNT.CL ;COUNTER: TIMES RECLAIMED MEMORY
$DATA MEMEND,0 ;END OF ZEROABLE DATA FOR GLXMEM
;HERE COMES NON-ZEROABLE $DATA SPACE
$DATA MEMFLG ;-1 WHEN M%INIT HAS BEEN CALLED
$DATA FREINI ;INITIAL VALUE OF FREPGS FOR CHECKING
SUBTTL M%INIT - Initialize the memory system
; M%INIT HAS THE TASK OF PUTTING THE PAGE TABLE AND PAGE COUNTERS INTO
; A DETERMINED STATE.
; CALL IS: NO ARGUMENTS
;
;TRUE RETURN: ALWAYS
M%INIT: MOVE S1,[MEMBEG,,MEMBEG+1] ;SETUP BLT PTR TO ZERO $DATA SPACE
SETZM MEMBEG ;ZERO OUT FIRST LOCATION
BLT S1,MEMEND-1 ;AND DO THE REST
SKIPGE MEMFLG ;HAVE WE BEEN HERE BEFORE (RESTART)?
JRST INIT.3 ;YES, RESTORE INITIAL STATE
SETOM MEMFLG ;NO, DETERMINE INITIAL STATE
TOPS10 <MOVEI S1,0> ;START AT PAGE 0
TOPS20 <HRRZ S1,.JBFF##> ;START FROM FIRST FREE
ADR2PG S1 ;CONVERT TO PAGE NUMBER
SETOM PAGSTA ;NO PAGE MARKED AS FIRST FREE YET
INIT.1: PUSHJ P,PAGFRE ;IS THIS PAGE FREE?
JUMPN S2,INIT.2 ;IF ITS IN USE, MARK IT AS SUCH
AOS FREINI ;INCREMENT COUNT OF FREE PAGES
SKIPGE PAGSTA ;HAVE WE ALREADY FOUND ONE FREE PAGE?
MOVEM S1,PAGSTA ;NO, SO SET IT UP NOW
INIT.2: STORE S2,PAGTBL(S1),PT.INI ;SAVE INITIAL BIT (1=PART OF ORIGINAL)
STORE S2,PAGTBL(S1),PT.USE ;SET THE PAGE STATUS UP TOO
STORE S2,PAGTBL(S1),PT.ADR ;AND ADDRESSABLE
CAIE S1,MEMSIZ-1 ;LOOP FOR ALL PAGES
AOJA S1,INIT.1 ;MARKING FREE AND INUSE
JRST INIT.6 ;THEN DO COMMON SET UP
INIT.3: MOVEI S1,0 ;START AT PAGE 0
INIT.4: LOAD S2,PAGTBL(S1),PT.INI ;GET INITIAL IN-USE BIT
STORE S2,PAGTBL(S1),PT.USE ;RESET 'IN USE' BIT FOR THIS PAGE
CAIE S1,MEMSIZ-1 ;ARE WE DONE?
AOJA S1,INIT.4 ;NO, SO DO NEXT PAGE
INIT.6: MOVE S1,FREINI ;GET NUMBER OF FREE PAGES
MOVEM S1,FREPGS ;STORE CURRENT NUMBER OF FREE PAGES
PG2ADR S1 ;CONVERT TO WORDS
MOVEM S1,FREWDS ;AND STORE THAT TOO
MOVX S1,DDCNT ;RE-SET THE DICT DUMP COUNT
MOVEM S1,APCNT ;FOR M%GMEM AND M%RMEM
PUSHJ P,M%CLNC ;CLEAN UP CORE
ZERO AVBPGS ;CLEAR COUNT OF AVAILABLE PAGES
PUSHJ P,PFHINI ;SET UP THE PAGE FAULT HANDLER
$RETT ;ALL DONE, RETURN NOW
SUBTTL System PFH interface
; System page fault handler (PFH) initialization
;
PFHINI: SETZM VRTPRV ;ASSUME NO VIRTUAL PRIVS
TOPS10 < ;TOPS-10 ONLY
MOVX TF,IB.NPF ;GET 'DON'T SET UP GLXPFH' BIT
SKIPN .JBPFH ;ALREADY SET UP?
TDNE TF,IIB##+IB.FLG ;USER WANT PFH ?
POPJ P, ;NO - JUST RETURN
HRROI TF,.GTMVL ;SET UP AC
GETTAB TF, ;READ MAXIMUM CORE LIMITS
SETZ TF, ;CAN'T
TLNN TF,-1 ;HAVE VM PRIVS ?
POPJ P, ;NOPE - JUST RETURN
AOS VRTPRV ;YES - REMEMBER IT
PFHI.1: MOVE S2,[.STTVM,,^D1000] ;REQUEST A VIRTUAL TIMER TRAP
SETUUO S2, ;AFTER 1000 MILLISECONDS
JFCL ;LOSER
POPJ P, ;RETURN
> ;END OF TOPS10 CONDITIONAL
TOPS20 < ;TOPS-20 ONLY
AOS VRTPRV ;ALWAYS HAVE VIRTUAL PRIVS ON THE -20
POPJ P, ;RETURN
> ;END OF TOPS-20 CONDITIONAL
SUBTTL NODDT - Remove DDT.VMX routine for TOPS10
TOPS10 < ;TOPS-10 ONLY
;This routine is intended to be used as an emergency tool
; in case a library gets saved with DDT in its high segment.
; In this case, any lowseg program which also has DDT.VMX in page 700
; will get page overlap errors when getting the library.
; It is not the intention that the library be saved in this state,
; however, if it happens, then this routine will be useful.
; The procedure is as follows:
; .GET GLXLIB ;The bad one, with DDT.VMX in it
; .DDT ;get into DDT.VMX
; NODDT$G ;Start it at this routine
; .SAVE ;Save the library without the symbols
;
;This routine is also useful to remove VMDDT after a component
;has been attached to, and DDTed. It will remove VMDDT and continue
;the program at .JBOPC (the interrupted PC)
;
; NODDT$G ;Remove VMDDT and restart @.JBOPC
NODDT:: SKIPN S1,DDTADR ;Get the end,,start adr of DDT
JRST NODD.2 ;No DDT to begin with!
HRRZ T2,S1 ;Get start adr of DDT
HLRZ P1,S1 ;And ending adr
ADR2PG T2 ;Make starting page number
ADR2PG P1 ;Make ending page number
SUB P1,T2 ;Get number of pages in DDT
TXO T2,1B0 ;Light the destroy page bit
MOVEI T1,1 ;Count the args to PAGE. UUO
MOVE S2,[XWD .PAGCD,T1] ;Create/Destroy,, adr of arg list
NODD.1: PAGE. S2, ;Get rid of a page
JRST NODDFA ;Complain!
AOS T2 ;Bump to next page
SOJGE P1,NODD.1 ;And try the rest of the pages
SETZ S1, ;Store 0 in protected DDT loc
SETDDT S1, ;And tell the monitor
NODDTE: SKIPN .JBOPC ;Old PC around?
EXIT ;No--Just exit
JRSTF @.JBOPC ;Yes--Resume program
;Here is trying to destroy DDT, and it isn't here at all!
NODD.2: OUTSTR [ASCIZ/
% DDT not loaded
/] ;Inform the dumb user
JRST NODDTE ;And leave 'em alone
;Here if the PAGE. UUO fails
NODDFA: OUTSTR [ASCIZ/
?Can't destroy DDT page
/]
JRST NODDTE ;And die
> ;End of TOPS-10 conditional
SUBTTL PAGFRE - Determine if a given page is free or not
;CALL S1/PAGE NUMBER OF PAGE IN QUESTION
;
;TRUE RETURN: S2/0 ;IF PAGE IS FREE
; OR
; S2/1 ;IF PAGE IS IN USE
TOPS10 <
PAGFRE: MOVE S2,S1 ;GET ARGUMENT (PAGE NR.)
HRLI S2,.PAGCA ;GET PAGE ACCESS
PAGE. S2, ;LOOK UP PAGE ACESS CODE
$STOP(PEF,Page existence check failed)
TXNE S2,PA.GNE ;DOES PAGE EXIST?
TDZA S2,S2 ;NO, RETURN 0, I.E. PAGE IS FREE
MOVX S2,1 ;YES, MARK IT AS IN USE
$RETT ;IN EITHER CASE, RETURN
> ;END TOPS10 CONDITIONAL
TOPS20 <
PAGFRE: HRLI S1,.FHSLF ;CHECK FOR THIS PROCESS
RPACS ;THE PAGE ACCESSABLITY
ERCAL [$STOP(PEF,Page existence check failed)]
TXNN S2,PA%PEX ;DOES PAGE EXIST?
TDZA S2,S2 ;NO, RETURN THAT PAGE IS FREE
MOVX S2,1 ;OTHERWISE MARK ITS USE
ANDI S1,-1 ;PUT JUST PAGE NUMBER BACK INTO S1
$RETT ;AND RETURN
> ;END TOPS20 CONDITIONAL
SUBTTL M%GPAG - Acquire one free page full of zeroes (address)
;This routine is called to acquire one free page (zeroed).
;
;Call: no arguments
;
;T Ret: S1/ address of first word of page acquired
M%GPAG: PUSHJ P,M%ACQP ;GET A PAGE
PG2ADR S1 ;CONVERT PAGE NUMBER TO ADDRESS
$RETT ;AND RETURN
SUBTTL M%ACQP - Acquire one free page full of zeroes (page number)
;THIS ROUTINE IS CALLED TO ACQUIRE A SINGLE FREE PAGE
;
;TRUE RETURN: S1/PAGE NUMBER OF ACQUIRED PAGE
;
;FALSE RETURN: NEVER, STOP CODE "ASE" INSTEAD
;
M%ACQP: MOVEI S1,1 ;WILL ASK FOR 1 PAGE
SKIPG AVBPGS ;ANY "GOOD" PAGES AVAILABLE?
JRST M%AQNP ;NO, TAKE ANY AVAILABLE PAGE
MOVE S1,PAGSTA ;GET STARTING POINT FOR SEARCH
ACQP.1: CAIL S1,MEMSIZ ;OFF THE TOP OF MEMORY
$STOP(CAC,Count of Available Pages Confused)
MOVE S2,PAGTBL(S1) ;GET PAGE FLAGS
TXC S2,PT.ADR ;WANT TO TEST FOR ON
TXNE S2,PT.USE!PT.ADR ;IS IT FREE AND IN-CORE NOW
AOJA S1,ACQP.1 ;NOT THE BEST, TRY THE NEXT
PJRST CREPAG ;EXIT, CREATING AND ZEROING PAGE "S1"
SUBTTL M%AQNP - Acquire several free pages full of zeroes
;CALL IS: S1/NUMBER OF PAGES DESIRED (MAYBE 1 TO MEMSIZ, BUT NOT 0)
;
;TRUE RETURN: S1/PAGE NUMBER OF FIRST PAGE ACQUIRED
;
;FALSE RETURN: NEVER, STOP CODE "ASE" INSTEAD
M%AQNP: SKIPG S1 ;WANTS 1 OR MORE PAGES, RIGHT?
$STOP(RZP,Request for zero pages) ; NO, SO STOP NOW
PUSHJ P,.SAVE3 ;SAVE A COUPLE FIRST
MOVE P1,S1 ;GET THE NUMBER REQUESTED
AQNP.0: MOVE S1,PAGSTA ;FIRST PAGE TO TRY FOR
MOVEI P2,-1(P1) ;COPY THE COUNT FOR THE LOOP BELOW
PUSHJ P,FNDPAG ;GET A PAGE
JUMPF AQNP.3 ;IF NO PAGES, TRY TO ROB CHUNK POOL
JUMPE P2,AQNP.2 ;DONE IF ONLY WANTS ONE
AQNP.1: MOVE P3,S1 ;SAVE THAT NUMBER
PUSHJ P,[ AOJA S1,FNDPAG] ;TRY NEXT PAGE NOW
JUMPF AQNP.3 ;IF FAILS, TRY COLLECTING
CAIE S1,1(P3) ;ARE THEY CONTIGUOUS?
MOVEI P2,(P1) ;NO, RESET LOOP COUNT
SOJG P2,AQNP.1 ;GET MORE IF REQUIRED
AQNP.2: PUSHJ P,CREPAG ;CREATE PAGE "S1"
SOJLE P1,.RETT ;RETURN IF ALL DONE
SOJA S1,AQNP.2 ;ELSE BACK DOWN TO THE NEXT ONE
AQNP.3: SKIPE PANFLG ;CALL FROM M%GMEM?
JRST S..ASE ;YES, ITS ALL OVER
PUSHJ P,PGCOLL ;COLLECT PAGES FROM CHUNK ODD SIZE POOL
JUMPT AQNP.0 ;IF WE GOT A PAGE, TRY ALL OVER
$STOP(ASE,Addressing space exhausted) ;ELSE, REALLY NO CORE LEFT
SUBTTL FNDPAG - Find first free page
;CALL IS: S1/ STARTING POINT FOR THE SEARCH
;TRUE RETURN: S1/ FIRST FREE PAGE
;FALSE RETURN: COULD NOT FIND A FREE PAGE
FNDPAG: CAIL S1,MEMSIZ ;ONLY WANT PAGES THAT ARE UNUSED
$RETF ;IF CAN'T FIND ONE, FAIL RETURN
LOAD S2,PAGTBL(S1),PT.USE ;THIS ONE USED
JUMPE S2,.RETT ;NO, TAKE THIS ONE
AOJA S1,FNDPAG ;TRY ANOTHER
SUBTTL CREPAG - Routine to create a page
TOPS20 <
CREPAG: MOVE S2,PAGTBL(S1) ;GET OLD SETTINGS
TXNE S2,PT.ADR ;ADDRESSABLE
SOS AVBPGS ;DECREMENT COUNT OF "GOOD" PAGES
MOVX S2,PT.USE!PT.ADR ;GET ALL THE BITS
IORM S2,PAGTBL(S1) ;INCLUDE THEM
PUSHJ P,REDUCE ;REDUCE COUNT OF FREE CORE
PJRST .ZPAGN ;RETURN, ZEROING THE PAGE
> ;END TOPS20 CONDITIONAL
TOPS10 <
CREPAG: PUSHJ P,REDUCE ;REDUCE COUNT OF FREE PAGES
MOVX S2,PT.USE ;GET THE INUSE BIT
IORB S2,PAGTBL(S1) ;SET IN USE, GET THE OTHERS
TXNE S2,PT.ADR ;IS IT ADDRESSABLE
JRST [SOS AVBPGS ;YES--DECREMENT "GOOD" PAGES
PJRST .ZPAGN] ;AND RETURN ZEROING PAGE
PUSHJ P,.SAVE4 ;SAVE P1-P4
CREP.1: MOVE P3,S1 ;ARGUMENT FOR CREATE A PAGE
MOVEI P2,1 ;ONLY 1 ARGUMENT
MOVE P1,[.PAGCD,,P2] ;FUNCTION CREATE/DESTROY,,ARGUMENTS
PAGE. P1, ;TRY THE CREATE
JRST CREP.2 ;ANALYZE THE ERROR
MOVX S2,PT.ADR ;ADDRESSABLE
IORM S2,PAGTBL(S1) ;INCLUDE THE FLAG
PJRST .ZPAGN ;RETURN, ZEROING THE PAGE
CREP.2: PUSH P,S1 ;SAVE THE PAGE WE'RE TRYING TO CREATE
CAIE P1,PAGNS% ;OUT OF SWAPPING SPACE
JRST CREP.3 ;NO, LOOK AGAIN
MOVEI S1,5 ;TAKE A QUICK NAP FIRST
SLEEP S1, ;IN CASE SOME FREES UP
PUSHJ P,M%CLNC ;FREE SOME SWAPPING SPACE
POP P,S1 ;RESTORE PAGE NUMBER
JRST CREP.1 ;AND RETRY THE CREATE
CREP.3: CAIE P1,PAGLE% ;MY LIMIT EXCEEDED
$STOP(CCP,Cannot create page) ;
PUSHJ P,M%IPRM ;SWAP OUT ANYTHING
SKIPT ;CHECK FOR ERRORS
$STOP (NFP,<No free pages>)
POP P,S1 ;RESTORE PAGE NUMBER
JRST CREP.1 ;RETRY THE CREATE
> ;END TOPS10 CONDITIONAL
SUBTTL M%NXPG - Acquire the number of a free page for IPCF reception
;CALL NO ARGUMENTS
;
;TRUE RETURN: S1/THE PAGE NUMBER AVAILABLE FOR IPCF RECEIVE
;FALSE RETURN: S1/?, NO PAGES AVAILABLE AT THIS TIME, TRY LATER
;
; AFTER THE RECEIVE, A CALL TO M%IPRC IS REQUIRED.
M%NXPG:
NXPG.0: MOVE S1,FREPGS ;GET COUNT OF FREE PAGES
CAILE S1,IPCPAD ;ENOUGH TO HANDLE AN INCOMING MESSAGE
JRST NXPG.1 ;YES, GO TAKE ONE
PUSHJ P,PGCOLL ;NO, TAKE ONE FROM THE FREE SPACE
JUMPF .RETF ;CAN'T GET ONE, RETURN FALSE
JRST NXPG.0 ;TRY UNTIL ENOUGH FREE
TOPS20 < ;TOPS20 WILL REPLACE ANY EXISTING PAGE WITH THE MESSAGE
NXPG.1: MOVE S1,PAGSTA ;WHERE TO START LOOKING
PUSHJ P,FNDPAG ;GRAB AN AVAILABLE PAGE
SKIPT ;GREAT IF WE GOT ONE
$STOP(CFC,Count of Free Pages Confused)
MOVX S2,PT.USE ;SET THE TEMP STATE
IORB S2,PAGTBL(S1) ;OF INUSE BUT NOT ADDRESSABLE
TXNE S2,PT.ADR ;TAKE ONE OF THE "NICE" PAGES?
SOS AVBPGS ;YES, REDUCE COUNT OF THEM NOW
MOVX S2,PT.ADR ;GET THESE FLAGS
ANDCAM S2,PAGTBL(S1) ;CLEAR THE FLAGS TO BE SAFE
$RETT ;AND TAKE A GOOD RETURN
> ;END TOPS20 CONDITIONAL
TOPS10 < ;TOPS10 REQUIRES A NON-EXISTANT PAGE
NXPG.1: PUSHJ P,NXPG.3 ;FIND A COMPLETELY MISSING PAGE
JUMPT NXPG.2 ;TAKE THIS ONE IF WE CAN
PUSHJ P,M%CLNC ;DESTROY ANY PAGES I CAN
PUSHJ P,NXPG.3 ;NOW TRY TO FIND ONE
SKIPT ;GREAT IF WE GOT ONE
$STOP(CFC,Count of Free Pages Confused)
NXPG.2: MOVX S2,PT.USE ;SET THE TEMP STATE
IORM S2,PAGTBL(S1) ;OF INUSE BUT NOT ADDRESSABLE
$RETT ;RETURN OUR SUCCESS
NXPG.3: MOVE S1,PAGSTA ;START AT THE FIRST AVAILABLE PAGE
NXPG.4: CAIL S1,MEMSIZ ;END OF THE ADDRESSING SPACE
$RETF ;YES, RETURN A FAILURE
MOVE S2,PAGTBL(S1) ;GET THE TABLE ENTRY
TXNN S2,PT.USE!PT.ADR ;IS THIS PAGE THERE
$RETT ;NO BITS MEANS OK TO USE IT
AOJA S1,NXPG.4 ;WELL, TRY THE NEXT
> ;END TOPS10 CONDITIONAL
SUBTTL M%RLNP - Release contiquous free pages
;CALL IS: S1 / NUMBER TO RELEASE
; S2 / THE FIRST PAGE
;
;TRUE RETURN: ALWAYS
M%RLNP: PUSHJ P,.SAVE2 ;SAVE P1 AND P2
DMOVE P1,S1 ;COPY THE ARGS OVER
RLNP.1: SOJL P1,.RETT ;DECR THE COUNT AND RTN ON 0
MOVE S1,P2 ;GET THE PAGE NUMBER
PUSHJ P,M%RELP ;RELEASE IT
AOJA P2,RLNP.1 ;AND LOOP
SUBTTL M%RELP - Release a single page to the free pool (by page number)
SUBTTL M%RPAG - Release a single page to the free pool (by address)
;CALL IS: S1/ PAGE NUMBER TO RELEASE
;
;TRUE RETURN: ALWAYS
M%RPAG: ADR2PG S1 ;CONVERT ADR TO PAGE
; AND FALL INTO M%RELP
M%RELP: PUSHJ P,VALPAG ;CONSISTENCY CHECK PAGE NUMBER
MOVE S2,PAGTBL(S1) ;GET THE FLAGS
TXNE S2,PT.ADR ;IS THIS THE ONE IPCF'ED AWAY
JRST RELP.1 ;NO, GO FIX THE COUNTS
ZERO PAGTBL(S1) ;CLEAR THE ENTRY
$RETT ;AND RETURN
RELP.1: PUSHJ P,.SAVE1 ;SAVE AN AC
MOVEI P1,PAGTBL(S1) ;SAVE ADDRESS OF PAGTBL ENTRY
PUSHJ P,INCLUD ;BUMP FREE PAGE COUNT
AOS S1,AVBPGS ;BUMP COUNT OF AVAILABLE PAGES
CAILE S1,PAGAVL ;EXCEED WORKING SET SIZE
PUSHJ P,M%CLNC ;YES..CLEANUP MEMORY
RELP.2: TXZ S2,PT.USE ;CLEAR IN USE
MOVEM S2,(P1) ;SAVE THE ENTRY IN PAGE TABLE
$RETT ;NOW RETURN..
;THIS WILL ALWAYS LEAVE ONE PAGE
;FOR AVBPGS
SUBTTL M%FPGS - Return number of free pages
;CALL NO ARGUMENTS
;
;TRUE RETURN: ALWAYS, S1/THE NUMBER OF PAGES FREE
; S2/SIZE OF MAXIMUM BLOCK OF CONTIGUOUS FREE PAGES
M%FPGS: PUSHJ P,.SAVE2 ;Save a couple of regs
MOVE S1,PAGSTA ;First page to start the search
MOVEI P2,0 ;Initialize current contiguous counter
MOVEI S2,0 ;Initialize largest found contiguous
PUSHJ P,FNDPAG ;Find the first free page
JUMPF FINISH ;There are none
MOVE P1,S1 ;Save free page number for later
AOS P2 ;Increment current contiguous counter
TRY: PUSHJ P,[AOJA S1,FNDPAG] ;Pick up the next free page
JUMPF FINISH ;No free pages left so finish
CAIN S1,1(P1) ;A contiguous free page?
JRST [AOS P1 ;Yes,increment both
AOS P2 ;counters
JRST TRY] ;Try for another
CAMLE P2,S2 ;No, current contiguous count largest?
MOVE S2,P2 ;Yes, so make it overall largest
MOVEI P2,MEMSIZ ;Get the number of pages
SUB P2,S1 ;not yet searched for
CAMLE S2,P2 ;Continue the search for largest block?
JRST FINIT ;No
MOVEI P2,0 ;Yes, initialize current contiguous
MOVE P1,S1 ;Save free page number for later
JRST TRY ;Check for next contiguous block
FINISH: CAMLE P2,S2 ;Current contiguous count the largest?
MOVE S2,P2 ;Yes, so make it the overall largest
FINIT: MOVE S1,FREPGS ;PICK UP THE NUMBER
$RETT ;AND RETURN
SUBTTL M%IPSN - Inform that page is about to be sent via IPCF
; CALL IS: S1/ PAGE NUMBER OF IPCF'ED PAGE
;
;TRUE RETURN: ALWAYS
M%IPSN: PUSHJ P,VALPAG ;CONSISTENCY CHECK PAGE NUMBER
MOVX S2,PT.ADR ;CLEAR ADDRESSABLE
ANDCAM S2,PAGTBL(S1) ;SO THAT WE DON'T GET CONFUSED
PJRST INCLUD ;BUMP FREE PAGE COUNT AND RETURN
SUBTTL M%IPRC - Inform that page has been created via IPCF
;CALL IS: S1/PAGE NUMBER THAT RECEIVE CREATED
;
;TRUE RETURN: ALWAYS
TOPS10 < ;NOW NEED TO KNOW IF THE PAGE IS IN THE WORKING SET
M%IPRC: PUSHJ P,VALPAG ;CONSISTENCY CHECK PAGE NUMBER
PUSH P,S1 ;SAVE THE PAGE NUMBER
HRLI S1,.PAGCA ;CHECK ITS ACCESS BITS
PAGE. S1, ;SEE IF THE PAGE IS SWAPPED OUT
$STOP(PAF,Page access check failed)
MOVX S2,PT.ADR ;ADDRESSABLE
TXNE S1,PA.GNE ;PAGE DOESN'T EXIST
$STOP(RNF,Received non-existent page)
POP P,S1 ;RESTORE PAGE NUMBER
IORM S2,PAGTBL(S1) ;INCLUDE THE FLAG(S)
PJRST REDUCE ;REDUCE COUNT OF FREE PAGES AND RETURN
> ;END TOPS10 CONDITIONAL
TOPS20 <
M%IPRC: PUSHJ P,VALPAG ;CONSISTENCY CHECK PAGE NUMBER
MOVX S2,PT.ADR ;ADDRESSABLE
IORM S2,PAGTBL(S1) ;INCLUDE THE FLAGS
PJRST REDUCE ;REDUCE COUNT OF FREE PAGES AND RETURN
> ;END TOPS20 CONDITIONAL
SUBTTL M%GMEM - Allocate a chunk of memory
;CALL IS: S1/ NUMBER OF WORDS WANTED
;
;TRUE RETURN: S1/ NUMBER OF WORDS OBTAINED
; S2/ ADDRESS OF FIRST WORD
M%GMEM: PUSHJ P,.SAVE2 ;GET TWO WORK REGISTERS
CAMG S1,FREWDS ;IN RANGE OF AVAILABLE SPACE?
SKIPG S1 ;OR SILLY NUMBER?
$STOP(RNW,Ridiculous number of words requested)
CAIG S1,DCT.MX ;IF REQUIRED SIZE .GT. DICTIONARY
SKIPN S2,DICT(S1) ;OR IF DICTIONARY ENTRY IS 0
JRST GMEM.0 ;GO TRY THE ODD SIZE POOL
MOVE P1,0(S2) ;GET FORWARD LINK OF CHOSEN BLOCK
MOVEM P1,DICT(S1) ;STORE INTO HEAD AS NEXT TO CHOSE
PJRST .ZCHNK ;RETURN ZEROING CHUNK
GMEM.0: MOVE P1,S1 ;SAVE THE REQUIRED BLOCK LENGTH
MOVE S1,CNT.AP ;GET FREE POOL ALLOCATED PAGE COUNT
CAIL S1,CNK.PM ;WITHIN BOUNDS OF PAGES
PUSHJ P,[PUSHJ P,PGCOLL ;NO,,GARBAGE COLLECT
SETZM CNT.AP ;CLEAR ADDED PAGE COUNT
POPJ P, ] ;RETURN
MOVE S1,P1 ;RESTORE REQUIRED BLOCK LENGTH
GMEM.1: MOVEI S2,DICT ;START WITH HEADER OF ODD LIST
GMEM.2: MOVE P1,S2 ;REMEMBER WHO POINTS TO CURRENT
HRRZ S2,0(P1) ;S2 IS NOW CURRENT BLOCK
JUMPE S2,GMEM.4 ;IF 0, WE HAVE REACHED END OF THE ROAD
HLRZ P2,0(S2) ;GET SIZE OF CURRENT BLOCK
CAMGE P2,S1 ;IS IT SUFFICIENT FOR REQUEST?
JRST GMEM.2 ;NO, SO TRY NEXT BLOCK
GMEM.3: HRL S2,0(S2) ;GET LINK OF CURRENT BLOCK
HLRM S2,0(P1) ;MAKE PREV LINK BE WHAT WAS OUR LINK
HRRZS S2 ;ISOLATE CURRENT BLOCKS ADDRESS
CAMN P2,S1 ;IS THIS AN EXACT MATCH ON SIZE?
PJRST .ZCHNK ;YES, RETURN, ZEROING CHUNK
PUSH P,S1 ;SAVE NUMBER OF WORDS
PUSH P,S2 ;SAVE ADDRESS
ADD S2,S1 ;GET FIRST WORD TO RETURN
SUBM P2,S1 ;NUMBER OF WORDS TO RETURN
PUSHJ P,M%RMEM ;RETURN THE EXTRA WORDS
POP P,S2 ;RESTORE ADDRESS OF BLOCK
POP P,S1 ;RESTORE NUMBER OF WORDS
PJRST .ZCHNK ;YES, RETURN, ZEROING CHUNK
GMEM.4: MOVEI P2,1(S1) ;START WITH NEXT DICT SLOT
GMEM.5: CAILE P2,DCT.MX ;IS THIS STILL INSIDE DICTIONARY?
JRST GMEM.6 ;TIME FOR MORE MEMORY
SKIPN S2,DICT(P2) ;ANYTHING IN THIS DICTIONARY SLOT ???
AOJA P2,GMEM.5 ;NO, TRY NEXT LARGEST
MOVEI P1,DICT(P2) ;P1 IS CELL POINTING TO CHOSEN
JRST GMEM.3 ;EXIT RETURNING EXTRA MEMORY
GMEM.6: PUSH P,S1 ;SAVE SIZE WANTED
PUSHJ P,APMEM ;TRY TO FIX UP FREE CHUNK POOL
POP P,S1 ;RESTORE THE SIZE
JRST GMEM.1 ;AND TRY AGAIN
SUBTTL APMEM - Routine to add one page to the chunk pool
;CALL IS: No arguments
;TRUE RETURN: Always
APMEM: PUSHJ P,.SAVE2 ;GET SOME INDICES
SETOM PANFLG ;DON'T WANT ANYTHING TO GO TO DICT
SOSL APCNT ;TIME TO DUMP THE DICTIONARY?
JRST APME.4 ;NO, JUST GET A PAGE
MOVEI P1,DCT.MX ;GET MAXIMUM DICTIONARY ENTRY
APME.1: MOVE P2,DICT(P1) ;GET START OF LINKED LIST FOR SIZE
SETZM DICT(P1) ;CLEAR IT OUT
APME.2: SKIPN S2,P2 ;DO WE HAVE A VALID ADDRESS?
JRST APME.3 ;NO, PROCESS NEXT SIZE
MOVE S1,P1 ;SET SIZE OF CHUNK UP
MOVE P2,0(P2) ;GET LINK TO NEXT BLOCK
PUSHJ P,M%RMEM ;AND RETURN IT
JRST APME.2 ;REPEAT FOR POSSIBLE NEXT BLOCK
APME.3: SOJG P1,APME.1 ;DO FOR ENTIRE DICTIONARY
MOVEI P1,DDCNT ;SET COUNTER AGAIN
MOVEM P1,APCNT ;RESET IT
SETZM PANFLG ;CLEAR PANIC LEVEL FLAG
INCR CNT.DD ;DUMPED DICTIONARY AGAIN
$RETT ;AND RETURN
APME.4: AOS CNT.AP ;BUMP THE ADDED PAGE COUNT
PUSHJ P,M%ACQP ;ACQUIRE A PAGE
SETZM PANFLG ;CLEAR PANIC LEVEL FLAG
MOVE S2,S1 ;GET PAGE NUMBER
PG2ADR S2 ;CONVERT TO AN ADDRESS
MOVEI S1,PAGSIZ ;AND THE SIZE
PJRST M%RMEM ;RETURN, RETURNING TO FREE POOL
SUBTTL PGCOLL - Routine to remove whole pages from chunk free pool
;This routine is called to remove, from the odd-size pool of the chunk
;manager, whole pages so that they are available to routines needing
;whole, page-aligned areas of memory.
;CALL IS: No arguments
;TRUE RETURN: A page has been freed
;FALSE RETURN: No page could be removed
PGCOLL: AOS CNT.CL ;BUMP NUMBER OF TIMES COLLECTED
SETZM APCNT ;FORCE DICTIONARY DUMP
PUSHJ P,APMEM ;TO INSURE FREE POOL IS ALL IN ODD SIZE
PUSHJ P,.SAVE4 ;NEED LOTS OF SCRATCH SPACE
SETZM CNT.PC ;CLEAR COUNT OF PAGES GOTTEN
MOVEI P1,DICT ;SEED HEAD OF LIST AS PREVIOUS
PGCO.1: HRRZ P2,0(P1) ;GET ADDR OF NEXT CHUNK
JUMPE P2,PGCO.2 ;IF 0 LINK, WE ARE AT END
HLRZ P3,0(P2) ;GET LENGTH OF THIS CHUNK
MOVE S1,P2 ;GET ADDRESS OF CHUNK
ADDI S1,PAGSIZ-1 ;AND ROUND UP TO
TRZ S1,PAGSIZ-1 ; PAGE BOUNDARY
MOVE P4,P2 ;COPY ADDRESS OF THIS CHUNK
ADD P4,P3 ;COMPUTE FIRST ADDR NOT IN THIS CHUNK
MOVE S2,S1 ;GET START OF CHUNK
ADDI S2,PAGSIZ ;ADDR OF PAGE STARTING HERE
CAMGE P4,S2 ;IS SIZE OF PAGE WITH BOUNDS OF CHUNK?
JRST [ MOVE P1,P2 ;NO, SO STEP TO NEXT CHUNK
JRST PGCO.1 ] ;AND TRY AGAIN
MOVE P4,S2 ;REMEMBER END ADDRESS OF PAGE SIZE CHUNK
HRRZ S2,0(P2) ;GET ADDR OF NEXT CHUNK IN CHAIN
HRRM S2,0(P1) ;DE-LINK THIS CHUNK
SUB S1,P2 ;COMPUTE LN. OF LEFT HAND OVERFLOW
MOVE S2,P2 ;AND ADDRESS LH OVERFLOW STARTS AT
SUBI P3,PAGSIZ(S1) ;ADJUST COUNT TO REFLECT 1 PAGE+LH
SKIPE S1 ;IF THERE IS ANY LEFT HAND TO RETURN
PUSHJ P,M%RMEM ;DO SO NOW
DMOVE S1,P3 ;GET SIZE, ADDR OF RH OVERFLOW
SKIPE S1 ;IF THERE IS ANY RH OVERFLOW,
PUSHJ P,M%RMEM ;RETURN IT NOW
INCR CNT.PC ;COUNT PAGES COLLECTED IN THIS MANNER
MOVE S1,P4 ;GET END ADDRESS OF PAGE SIZE CHUNK
SUBI S1,PAGSIZ ;IT STARTS HERE
PUSHJ P,M%RPAG ;RETURN THE PAGE
JRST PGCO.1 ;TRY TO GET SOME MORE
PGCO.2: SKIPG CNT.PC ;DID WE GET ANY
$RETF ;NO..RETURN FALSE
$RETT ;YES..RETURN TRUE
SUBTTL M%RMEM - Routine to de-allocate a memory chunk
;CALL IS: S1/ SIZE OF CHUNK BEING RETURNED
; S2/ ADDRESS OF CHUNK BEING RETURNED
;
;TRUE RETURN: ALWAYS
M%RMEM: PUSHJ P,.SAVE2 ;GET SOME WORK SPACE
PUSHJ P,VALADR ;VALIDATE THE ADDRESS
SKIPG S1 ;REASONABLE AMOUNT BEING RETURNED?
$STOP(ZWR,Zero words of memory returned)
SKIPE PANFLG ;ARE WE IN PANIC MODE?
JRST RMEM.1 ;YES, DON'T RETURN TO DICTIONARY
CAIL S1,DCT.MN ;LESS THAN SMALLEST OR
CAILE S1,DCT.MX ;GREATER THAN MAXIMUM IN DICT?
JRST RMEM.1 ;YES, RETURN TO ODD-SIZE POOL
MOVE P1,DICT(S1) ;GET LINK OF HEADER
HRRZM P1,0(S2) ;MAKE IT CURRENT BLOCK'S HEADER
HRRZM S2,DICT(S1) ;AND MAKE HEADER POINT TO CURRENT
$RETT ;RETURN NOW
RMEM.1: MOVEI P1,DICT ;GET PREV SET UP
RMEM.2: HRRZ P2,0(P1) ;GET PREV'S LINK
SKIPE P2 ;IF CURRENT IS 0 OR
CAIL P2,0(S2) ; ITS ADDRESS IS PAST ADDR OF RETURN BLK
JRST RMEM.3 ; THEN RETURN BLOCK HERE
MOVE P1,P2 ;MAKE PREV=CURRENT
JRST RMEM.2 ;CONTINUE
RMEM.3: HLRZ P2,0(P1) ;GET SIZE OF PREVIOUS
ADD P2,P1 ;ADD SIZE PLUS ADDRESS
CAIE P2,0(S2) ;DOES THIS PUT IT AT CURRENT BLOCK?
JRST RMEM.4 ;NO, CANNOT COMBINE
MOVE S2,P1 ;CONCATENATE PREV AND CURRENT
HLRZ P2,0(P1) ;GET SIZE OF PREVIOUS AGAIN
ADD S1,P2 ;MAKE A COMBINED SIZE
RMEM.4: HRLM S1,0(S2) ;STORE SIZE OF CURRENT BLOCK
HRRZ P2,0(P1) ;GET PREV'S FORWARD LINK
HRRM P2,0(S2) ;MAKE IT CURRENT'S FORWARD LINK
CAME S2,P1 ;UNLESS PREV=CURRENT (FROM CONCATENATION)
HRRM S2,0(P1) ;MAKE PREV'S FORWARD LINK POINT TO CURR.
MOVE P1,S2 ;GET ADDRESS OF CURRENT BLOCK
ADD P1,S1 ;ADD SIZE TO THAT
CAIE P1,0(P2) ;DO WE BUTT UP AGAINST NEXT?
$RETT ;NO, CANNOT COMBINE, RETURN NOW
HRLZS S1 ;YES, POSITION SIZE OF CURRENT
ADD S1,0(P2) ;MAKE COMBINED SIZE,,LINK TO NEW NEXT
MOVEM S1,0(S2) ;STORE NEW SIZE AND LINK
$RETT ;RETURN
SUBTTL Consistency checking routines
;"REDUCE" DECREMENTS THE FREE PAGE COUNT , "INCLUD" ADDS A FREE PAGE
;
REDUCE: SOSGE FREPGS ;DECREMENT COUNT OF FREE PAGES
$STOP(FCN,Free count negative)
$RETT ;RETURN IF OK
INCLUD: AOS S1,FREPGS ;ADD A FREE PAGE
CAMLE S1,FREINI ;MORE THAN WE STARTED OUT WITH
$STOP(FCE,Free count exceeds FREINI)
$RETT ;RETURN IF OK
;VALADR VALIDATES THE RANGE OF MEMORY THAT STARTS AT ADDR IN S2
; AND CONTINUES FOR THE NUMBER OF WORDS IN S1
VALADR: PUSH P,S1 ;SAVE INPUT ARGUMENTS
PUSH P,S2 ;FROM M%RMEM
EXCH S1,S2 ;GET ADDRESS IN S1, SIZE IN S2
ADR2PG S1 ;CONVERT TO A PAGE NUMBER
VALA.1: PUSHJ P,VALPAG ;VALIDATE IT
SUBI S2,PAGSIZ ;HAVE ACCOUNTED FOR ONE PAGE
SKIPLE S2 ;DONE ENTIRE CHUNK?
AOJA S1,VALA.1 ;NO, DO THE NEXT PAGE
POP P,S2 ;RESTORE S2 (ADDRESS)
POP P,S1 ;RESTORE S1 (SIZE)
$RETT ;AND RETURN
;VALPAG VALIDATES THE PAGE NUMBER IN AC S1
VALPAG: CAIL S1,MEMSIZ ;RANGE CHECK THE PAGE NUMBER
JRST S..BPN ;OUT OF RANGE OF PAGE TABLE
PUSH P,S2 ;SAVE CALLER'S AC
MOVE S2,PAGTBL(S1) ;GET THE PAGE TABLE ENTRY FOR PAGE
TXNN S2,PT.INI ;PART OF INITIAL CORE IMAGE?
TXNN S2,PT.USE ;OR NOT IN USE?
$STOP(BPN,Bad page number ^O/S1/) ;YES, STOP NOW
POP P,S2 ;RESTORE CALLER'S S2
$RETT ;RETURN IF OK
SUBTTL M%CLNC - Routines for cleaning up core
;CALL IS: NO ARGUMENTS
;
;TRUE RETURN: ALWAYS
M%CLNC:
TOPS10 < ;TOPS-10 ONLY
PUSHJ P,.SAVE4 ;SAVE P1-P4
MOVE P4,PAGSTA ;THE FIRST AVAILABLE PAGE
CLNC.1: CAIL P4,MEMSIZ ;OFF THE END OF THE WORLD
$RETT ;YES, RETURN
MOVE P1,PAGTBL(P4) ;GET THE TABLE ENTRY
TXC P1,PT.ADR ;NEED CHECK FOR BOTH SO FLIP
TXNN P1,PT.USE!PT.ADR ;USED OR NOT ADDRESSABEL
PUSHJ P,KILPAG ;DESTROY THE PAGE (COULD BE PAGED OUT)
AOJA P4,CLNC.1 ;AND CONTINUE LOOPING
KILPAG: MOVEI P3,(P4) ;WANT IT IN P3
TXO P3,1B0 ;SET TO DESTROY
MOVEI P2,1 ;1 ARGUMENT
MOVE P1,[.PAGCD,,P2] ;CREATE/DESTROY,,ARGUMENT
PAGE. P1, ;TRY TO DESTROY IT
$STOP(PKF,Page kill failed)
ZERO P1 ;CLEAR A REG
EXCH P1,PAGTBL(P4) ;CLEAR PAGE TABLE ENTRY, GET OLD FLAGS
SOS AVBPGS ;ONE LESS "GOOD" PAGE
$RETT ;RETURN
> ;END OF TOPS-10 CONDITIONAL
TOPS20 <$RETT> ;TOPS-20 ONLY
SUBTTL M%IPRM - Routine to find a free page for an IPCF receive
; M%IPRM will find a free page for an IPCF receive. To do this
; correctly in all cases, we create a page on disk, fault it into
; core, and destroy it. This leaves us a slot in our working set
; to receive an IPCF packet.
; Call: $CALL M%IPRM
;
; TRUE return: a page free in the working set
; FALSE return: can't find a free page
;
M%IPRM:
TOPS10 < ;TOPS-10 ONLY
SKIPN VRTPRV ;HAVE VIRTUAL PRIVS?
JRST IPRM.E ;NOPE - THEN WE CAN'T FIND A FREE PAGE
PUSHJ P,.SAVE3## ;SAVE SOME ACS
PUSHJ P,M%NXPG ;GET A NON-EXISTANT PAGE NUMBER
JUMPF IPRM.E ;CAN'T
MOVE P1,[.PAGCD,,P2] ;SET UP UUO
MOVEI P2,1 ;ONE WORD ARGUMENT
MOVE P3,S1 ;GET THE PAGE NUMBER
TXO P3,PA.GCD ;CREATE THE PAGE ON DISK
PAGE. P1, ;CREATE THE PAGE
JRST IPRM.E ;CAN'T
MOVE P1,S1 ;GET THE PAGE NUMBER
PG2ADR P1 ;CONVERT TO AN ADDRESS
MOVE S2,(P1) ;PAGE FAULT IT INTO CORE
MOVE P1,[.PAGCD,,P2] ;SET UP UUO
MOVEI P2,1 ;ONE WORD ARGUMENT
MOVE P3,S1 ;GET THE PAGE NUMBER
TXO P3,PA.GAF ;LITE THE DESTROY BIT
PAGE. P1, ;DESTROY THE PAGE
SKIPA ;CAN'T
$RETT ;RETURN
IPRM.E: $RETE (NFP) ;?NO FREE PAGES
> ;END OF TOPS-10 CONDITIONAL
TOPS20 <$RETT> ;TOPS-20 ONLY
SUBTTL End
MEM%L:
END