Google
 

Trailing-Edge - PDP-10 Archives - BB-LW55A-BM_1988 - 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, 1988.
;	ALL RIGHTS RESERVED.
;
;	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 THAT IS NOT SUPPLIED BY DIGITAL.

	SEARCH	GLXMAC			;SUBSYSTEMS SYMBOLS
	PROLOG(GLXMEM,MEM)		;PRODUCE PROLOG CODE



	MEMMAN==:0			;Maintenance edit number
	MEMDEV==:61			;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. Entry Points found in GLXMEM . . . . . . . . . . . . .   2
;    2. Revision History . . . . . . . . . . . . . . . . . . .   4
;    3. Global Storage . . . . . . . . . . . . . . . . . . . .   5
;    4. M%INIT - Initialize the memory system  . . . . . . . .   6
;    5. System PFH interface . . . . . . . . . . . . . . . . .   7
;    6. NODDT - Remove DDT.VMX routine for TOPS10  . . . . . .   8
;    7. PAGFRE - Determine if a given page is free or not  . .   9
;    8. M%GPAG - Acquire one free page full of zeroes (address  10
;    9. M%AQNP - Acquire several free pages full of zeroes . .  11
;   10. CREPAG - Routine to create a page  . . . . . . . . . .  12
;   11. M%NXPG - Acquire the number of a free page for IPCF re  13
;   12. M%RLNP - Release contiquous free pages . . . . . . . .  14
;   13. M%IPSN - Inform that page is about to be sent via IPCF  15
;   14. M%GMEM - Allocate a chunk of memory  . . . . . . . . .  16
;   15. APMEM - Routine to add one page to the chunk pool  . .  17
;   16. PGCOLL - Routine to remove whole pages from chunk free  18
;   17. M%RMEM - Routine to de-allocate a memory chunk . . . .  19
;   18. Consistency checking routines  . . . . . . . . . . . .  20
;   19. M%CLNC - Routines for cleaning up core . . . . . . . .  21
;   20. M%IPRM - Routine to find a free page for an IPCF recei  22
;   21. 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.
*****	Release 5.0 -- begin maintenance edits	*****

45	Increment maintenance edit level for version 5 of GALAXY.

46	5.1233		31-Mar-86
	Change VALPAG to check that the returned page number is greater
than or equal to PAGSTA.

47	5.1234		1-Apr-86
	Use P3 rather than S2 when finding the largest number of contiguous
pages in routine M%FPGS. S2 is trashed by routine FNDPAG

*****	Release 6.0 -- begin development edits	*****

60	6.1037		26-Oct-87
	Move sources from G5: to G6:

61	6.1225		8-Mar-88
	Update copyright notice.

\   ;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

;**;[47]At M%FPGS:+0L  replace 4 lines with 3 lines  JCR  4/1/86

M%FPGS:	PUSHJ	P,.SAVE3		;[47]Save a couple of regs
	MOVE	S1,PAGSTA		;[47]First page to start the search
	SETZB	P2,P3			;[47]Initialize counters
	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
;**;[47]At TRY:+6L replace 5 lines with 5 lines  JCR  4/1/86
	CAMLE	P2,P3			;[47]No, current contig. count largest?
	MOVE	P3,P2			;[47]Yes, so make it overall largest
	MOVEI	P2,MEMSIZ		;[47]Get the number of pages 
	SUB	P2,S1			;[47]not yet searched for
	CAMLE	P3,P2			;[47]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
;**;[47]At FINISH:+0L replace 4 lines with 5 lines  JCR  4/1/86
FINISH:	CAMLE	P2,P3			;[47]Current contig. count the largest?
	MOVE	P3,P2			;[47]Yes, make it the overall largest
FINIT:  MOVE	S1,FREPGS		;[47]Pick up the number
	MOVE	S2,P3			;[47]Pick up the contig. block size
	$RETT				;[47]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

;**;[46]At VALPAG:+0L Replace 1 line with 2 lines  JCR  3/31/86

VALPAG:	CAIGE	S1,MEMSIZ		;[46]Page number must be within
	CAMGE	S1,PAGSTA		;[46]bounds
	  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