Google
 

Trailing-Edge - PDP-10 Archives - bb-d868b-bm_tops20_v3a_2020_dist - 3a-sources/qsrmem.mac
There are 28 other files named qsrmem.mac in the archive. Click here to see a list.
TITLE	QSRMEM -- Memory Manager for QUASAR
SUBTTL	Chuck O'Toole	12 Nov 76




;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1974, 1975, 1976, 1977, 1978 BY
;	DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

	SEARCH	QSRMAC		;PARAMETER FILE

	PROLOGUE(QSRMEM)	;GENERATE THE NECESSARY SYMBOLS
COMMENT\

	Entry points found in QSRMEM

M$INIT	Initialization entry point
M$ACQP	Acquire a page from the page map
M$RELP	Return page 'AP' to the page map
M$IPSN	Notification that IPCF send of page 'AP' is about to occur
M$NXPG	Get a non-existant page for an IPCF receive
M$IPRC	Notification that IPCF receive of a page has occurred
M$IPRM	Make room for IPCF page we couldn't fit in core
M$GCOL	A "Preventive" Garbage Collector for the Top Level
M$GFRE	Get a cell from the free list for queue 'H'
M$PFRE	Put cell 'AP' back on the free list for queue 'H'
M$RFRE	Remove cell 'AP' from the queue 'H' and return it to the free list
M$DLNK	Remove cell 'AP' from the queue 'H'
M$LINK	Link cell 'AP' into the queue 'H' before entry 'E'
M$ELNK	Link cell 'AP' at the end of the queue 'H'
M$FLNK	Link cell 'AP' at the front of the queue 'H'
M$MOVE	Move cell 'AP' from queue 'H' to queue 'S1'
M$PCNT	Determine whether 'S1' pages can be acquired
	STOPCDs found in QSRMEM

ASE	ADDRESSING SPACE EXHAUSTED
BPN	BAD PAGE NUMBER
CCC	CELL COUNTS CONFUSED
CCP	CANNOT CREATE PAGE
CGA	CANNOT GIVE ACCESS TO PAGE
CRC	CANNOT REDUCE CORE
FCE	FREE COUNT EXCEEDS FREINI
FCN	FREE COUNT NEGATIVE
MDL	MOVING DIFFERENT LEVEL
NTS	NOTHING TO SWAP OUT
PAF	PAGE ACCESS CHECK FAILED
PDF	PAGE DESTROY FAILED
PIF	PAGE IN FAILURE
PLL	PAGE LINK LOST
POF	PAGE OUT FAILURE
QSZ	QUEUE SIZE ZERO
RFP	RELEASING FREE PAGE
RNP	RECEIVED NON-EXISTANT PAGE
UPF	UNKNOWN PAGE FAULT TYPE

\

	LVL==M		;LEVEL (CLASS) FOR FREE SPACE
	LVLMAX==1	;MAXIMUM LEVEL.  ( 0 = IPCF, 1 = ANYTHING ELSE )

; FOLLOWING SYMBOLS REFERENCE "LVLTAB(LVL)"

	LVLNFC==777B35	; ORIGINAL FREE COUNT PER PAGE THIS CLASS
	LVLSIZ==777B26	; SIZE OF A QUEUE ENTRY THIS CLASS
	LVLFPG==777B17	; FIRST PAGE IN THE CHAIN FOR THIS CLASS
	LVLXXX==777B8	; UNUSED


	PADING==5	;NUMBER OF PAGES TO LEAVE FREE FOR EMERGENCIES
SUBTTL	Initialization Entry Point

;CALLED BY QUASAR AT FIRST START-UP TO INITIALIZE THE MEMORY MANAGER

	INTERN	M$INIT

M$INIT:	PUSHJ	P,.SAVE1##		;SAVE P1
	PUSHJ	P,I$MFFP##		;GET FIRST FREE PAGE
	MOVEM	S1,PAGSTA		;STORE FOR PAGE SEARCHING LOOPS
	MOVEI	S2,^D512		;NOW COMPUTE THE REMAINING ADDRESS SPACE
	SUB	S2,S1			;OFFSET FOR CODE
	MOVEM	S2,FREPGS		;SAVE FOR M$NXPG CHECK
	MOVEM	S2,FREINI		;SAVE INITIAL VALUE FOR CHECKS
	ZERO	ZERLOC			;START WITH A CLEAR PAGE MAP
	MOVE	S1,[ZERLOC,,ZERLOC+1]	;SET UP FOR THE BLT
	BLT	S1,ZERLAS		;CLEAR THE WHOLE THING

IFN FTUUOS,<	;TOPS10 INITIALIZATION
	MOVE	S1,[PFHEND,,PFHBGN]	;PAGE FAULT HANDLER POINTERS
	MOVE	S2,PFHBGN		;TOUCH THE FIRST LOCATION AND THE LAST
	MOVE	S2,PFHEND		;TO GUARANTEE PFH IS IN CORE
	MOVEM	S1,.JBPFH##		;NOW SET UP OUR OWN HANDLER
	MOVE	S1,[.STTVM,,0]		;SET VIRTUAL TIME TRAP = NONE
	SETUUO	S1,			;SET TO NOT GIVE TIMER TRAPS
	  JFCL				;OH WELL!!
	MOVE	AP,PAGSTA		;GET THE STARTING PAGE NUMBER
	DECR	AP			;IT GETS AOS'ED LATER
	MOVEM	AP,SWPSTA		;START OF THE SWAPPING TABLE
	PG2ADR	AP			;CONVERT TO ADDRESS
	MOVEI	AP,777(AP)		;LAST LOCATION WE KNOW ABOUT
	CORE	AP,			;DON'T CARE ABOUT ANYTHING ELSE
	  STOPCD(CRC,FATAL)		;++CANNOT REDUCE CORE
>  ;END OF IFN FTUUOS

	MOVEI	H,TBLHDR##		;START OF THE QUEUE HEADERS
	MOVEI	P1,NQUEUE##		;AND THE NUMBER OF THEM
INIT.1:	PUSHJ	P,SETLVL		;SET UP CLASS FOR THIS QUEUE
	LOAD	S1,.QHPAG(H),QH.SIZ	;SIZE OF AN ENTRY IN THIS QUEUE
	JUMPN	LVL,INIT.2		;JUMP IF NOT AN IPCF QUEUE (=0)
	ADD	S1,G$MPS##		;ADD IN MAXIMUM PACKET SIZE
	STORE	S1,.QHPAG(H),QH.SIZ	;UPDATE QUEUE SIZE
INIT.2:	LOAD	S2,LVLTBL(LVL),LVLSIZ	;CURRENT SIZE IN THIS LEVEL
	CAMGE	S2,S1			;THIS QUEUE LARGER THAN CURRENT
	  MOVE	S2,S1			;YES, GET S2 = LARGEST SIZE SO FAR
	STORE	S2,LVLTBL(LVL),LVLSIZ	;REMEMBER LARGEST IN CLASS
	SKIPN	S2			;ERROR CHECK IT NOW
	  STOPCD(QSZ,FATAL)		;++QUEUE SIZE ZERO
	MOVEI	S1,777			;FREE WORDS PER PAGE
	IDIVI	S1,(S2)			;COMPUTE FREE CELLS PER PAGE
	STORE	S1,LVLTBL(LVL),LVLNFC	;REMEMBER FOR FREE SPACE ROUTINES
	MOVEI	H,QHSIZE(H)		;STEP TO NEXT QUEUE HEADER
	SOJG	P1,INIT.1		;GET ALL QUEUES
	POPJ	P,			;AND RETURN
SUBTTL	Free Space Management Subroutines

;ROUTINE TO GET A FREE CELL FOR A QUEUE
;CALL	H = QUEUE HEADER POINTER
;	PUSHJ	P,M$GFRE
;	  RETURNS AP = CELL ADDRESS
;DESTROYS S1,S2
;CALLS M$ACQP, BLDFRE

	INTERN	M$GFRE

M$GFRE:	SAVE	LVL			;SAVE CALLERS
	PUSHJ	P,SETLVL		;SET UP QUEUE CLASS
	SKIPN	LVL			;IPCF TYPE (=0)
	  PUSHJ	P,I$NOIN##		;YES, TURN OFF IPCF INTERRUPTS
	SKIPN	LVFCNT(LVL)		;ANY FREE CELLS LEFT FOR LEVEL?
	JRST	GFRE.3			;NO, GET A PAGE WORTH
	LOAD	AP,LVLTBL(LVL),LVLFPG	;GET FIRST PAGE IN THE CHAIN
	ZERO	S2			;CLEAR IN CASE NO FREE SPACE
GFRE.1:	LOAD	S1,PAGTBL(AP),PT.CNT	;CELLS REMAINING IN THIS PAGE
	JUMPE	S1,GFRE.2		;THIS PAGE IS FULL, TRY THE NEXT
	TRNE	S2,-1			;IS THIS THE FIRST PAGE WE'VE LOOKED AT
	 CAIGE	S1,(S2)			;NO, USE THE ONE WITH THE LEAST FREE SPACE
	  SKIPA				;THIS ONE IS BETTER (OR THE FIRST)
	   JRST	GFRE.2			;SKIP THIS PAGE
	HRLI	S2,(AP)			;SAVE PAGE NUMBER OF THE BEST
	HRRI	S2,(S1)			;AND ITS COUNT
GFRE.2:	LOAD	AP,PAGTBL(AP),PT.LNK	;GET CHAIN TO THE NEXT PAGE
	JUMPN	AP,GFRE.1		;LOOK AT THE NEXT PAGE IF ANY
	HLRZ	AP,S2			;GET THE NUMBER OF THE BEST PAGE
	JUMPN	AP,GFRE.4		;THERE WAS ONE, TAKE A CELL
	STOPCD	(LCC,FATAL)		;++LEVEL COUNT CONFUSED
GFRE.3:	PUSHJ	P,M$ACQP		;ACQUIRE A FRESH PAGE
	PUSHJ	P,BLDFRE		;GENERATE THE FREE SPACE IN THIS NEW PAGE
GFRE.4:	DECR	PAGTBL(AP),PT.CNT	;DECREMENT THE FREE COUNT FOR THIS PAGE
	SOS	LVFCNT(LVL)		;AND TOTAL FREE SPACE IN THIS CLASS
	PG2ADR	AP			;CONVERT TO AN ADDRESS
	HRRZ	S1,(AP)			;GET THE CELL FROM THIS PAGE
	LOAD	S2,.QELNK(S1),QE.PTN	;FIND THE NEXT
	HRRM	S2,(AP)			;DISSOLVE THE LINK
	MOVEI	AP,(S1)			;RETURN CELL ADDRESS IN AP
	POPJ	P,			;AND RETURN
;ROUTINES TO RETURN A CELL TO THE CORRECT FREE LIST
;CALL	AP = CELL ADDRESS (MAY BE DESTROYED)
;	H  = QUEUE HEADER POINTER
;	PUSHJ	P,M$PFRE (M$RFRE IF ENTRY MUST BE DE-LINKED)
;DESTROYS S1,S2,AP,TEMP
;CALLS M$DLNK, M$RELP

	INTERN	M$PFRE,M$RFRE

M$RFRE:	PUSHJ	P,M$DLNK		;DE-LINK THIS ENTRY BEFORE RETURNING IT
M$PFRE:	SAVE	LVL			;SAVE CALLERS
	PUSHJ	P,SETLVL		;SET UP QUEUE CLASS
	SKIPN	LVL			;IPCF TYPE (=0)
	  PUSHJ	P,I$NOIN##		;YES, TURN OFF IPCF INTERRUPTS
	MOVEI	S1,(AP)			;COPY THE CELL ADDRESS
	ADR2PG	S1			;CONVERT TO A PAGE NUMBER
	EXCH	AP,S1			;SET UP FOR VALIDITY CHECKS
	PUSHJ	P,VALPAG		;SEE IF 'AP' IS A GOOD PAGE NUMBER
	EXCH	AP,S1			;OK, GET THEM BACK IN THE RIGHT ORDER
	INCR	PAGTBL(S1),PT.CNT	;BUMP FREE COUNT FOR THIS PAGE
	AOS	LVFCNT(LVL)		;AND TOTAL FOR THIS CLASS
	LOAD	TEMP,PAGTBL(S1),PT.CNT	;GET THE COUNT NOW
	PG2ADR	S1			;BACK TO THE ADDRESS OF THIS PAGE
	HRRZ	S2,(S1)			;GET THE LINK TO THE FIRST FREE IN THIS PAGE
	HRRM	AP,(S1)			;STORE THIS ENTRY AS THE FIRST NOW
	STORE	S2,.QELNK(AP),QE.PTN	;LINK THIS ENTRY TO THE NEXT
	LOAD	S2,LVLTBL(LVL),LVLNFC	;GET THE COUNT IF THIS PAGE IS EMPTY
	CAMGE	S2,LVFCNT(LVL)		;MORE THAN 1 PAGE WORTH OF FREE SPACE
	  SETOM	DOGCOL			;YES, GARBAGE COLLECTION WILL WIN
	CAIE	TEMP,(S2)		;IS IT EMPTY NOW
	  POPJ	P,			;NO, JUST RETURN
	ADR2PG	AP			;BACK TO A PAGE NUMBER
	ZERO	S2			;CLEAR IN CASE THIS IS THE FIRST PAGE
	LOAD	S1,LVLTBL(LVL),LVLFPG	;GET FIRST PAGE IN THE CHAIN
PFRE.1:	CAIN	AP,(S1)			;FIND THE PAGE THAT POINTS TO ME
	  JRST	PFRE.2			;THIS IS IT
	MOVE	S2,S1			;COPY THE PAGE NUMBER
	LOAD	S1,PAGTBL(S2),PT.LNK	;LINK TO THE NEXT PAGE IN THE CHAIN
	JUMPN	S1,PFRE.1		;LOOK AT THIS ONE
	STOPCD	(PLL,FATAL)		;++PAGE LINK LOST
PFRE.2:	LOAD	S1,PAGTBL(AP),PT.LNK	;GET WHERE I POINT TO
	JUMPE	S2,PFRE.3		;JUMP IF I AM THE FIRST
	STORE	S1,PAGTBL(S2),PT.LNK	;REMOVE ME FROM THE CHAIN
	JRST	PFRE.4			;ADJUST COUNTS AND RETURN PAGE
PFRE.3:	PJUMPE	S1,.POPJ##		;RETURN IF I AM THE ONLY
	STORE	S1,LVLTBL(LVL),LVLFPG	;THAT IS NOW THE FIRST
PFRE.4:	LOAD	S1,LVLTBL(LVL),LVLNFC	;GET A PAGE WORTH OF FREE SPACE
	MOVNS	S1			; - THAT
	ADDM	S1,LVFCNT(LVL)		;DECREMENT TOTAL FOR THIS CLASS
	PJRST	M$RELP			;RETURN THE PAGE AND RETURN
;THE GARBAGE COLLECTOR IS CALLED FROM THE TOP LEVEL AND IS "PREVENTIVE"
;	RATHER THAN "CORRECTIVE".  IT MUST NOT BE CALLED IN AN ATTEMPT
;	TO ACQUIRE MORE CORE IN CASE THERE IS NONE AVAILABLE SINCE THE
;	STATE OF QUEUES AND LISTS ARE UNCERTAIN AT THOSE TIMES.

	INTERN	M$GCOL

M$GCOL:	SKIPN	DOGCOL			;IS THERE ANYTHING TO COLLECT?
	POPJ	P,			;NO, JUST RETURN
	PUSHJ	P,I$NOIN##		;TURN OFF IPCF INTERRUPTS
	PUSHJ	P,.SAVE3##		;SAVE P1-P3
	$COUNT	(NGCC)			;COUNT GARBAGE COLLECTION CALLS
	MOVEI	P1,LVLMAX		;DRIVE LOOP FROM LVLMAX TO 0
GCOL.1:	MOVE	LVL,P1			;COPY CURRENT LEVEL NUMBER
	PUSHJ	P,CHKELM		;CHECK IF WE CAN ELIMINATE A PAGE
	JUMPE	S1,GCOL.4		;CAN'T, TRY THE NEXT LEVEL
	$COUNT	(NPGC)			;COUNT COLLECTED PAGES
	MOVEI	H,TBLHDR##		;THE START OF THE QUEUE HEADERS
	MOVEI	P2,NQUEUE##		;AND THE NUMBER OF QUEUES DEFINED
	MOVX	P3,PT.USE		;GET THE IN USE BIT FOR CHECKS
	MOVE	AP,S1			;COPY THE PAGE TO ELIMINATE
GCOL.2:	PUSHJ	P,SETLVL		;LEVEL OF THIS QUEUE
	SKIPE	.QHLNK(H)		;QUEUE EMPTY
	 CAME	LVL,P1			;NO, SAME CLASS AS FOR THIS PASS
	  JRST	GCOL.3			;EMPTY OR NOT SAME LEVEL, TRY NEXT QUEUE
	PUSHJ	P,DOELIM		;TRY TO ELIMINATE THE PAGE HERE
	TDNN	P3,PAGTBL(AP)		;PAGE GONE YET
	  JRST	GCOL.1			;YES, TRY FOR ANOTHER PAGE IN THIS CLASS
GCOL.3:	MOVEI	H,QHSIZE(H)		;BUMP TO THE NEXT QUEUE HEADER
	SOJG	P2,GCOL.2		;AND DO THAT ONE
	STOPCD(CCC,FATAL)		;++CELL COUNTS CONFUSED
GCOL.4:	SOJGE	P1,GCOL.1		;TRY THE NEXT CLASS (LEVEL)
	SETZM	DOGCOL			;WE'VE COLLECTED
	POPJ	P,			;THEN RETURN
;ROUTINES TO LINK AN ENTRY INTO A QUEUE
;CALL	AP = ENTRY TO LINK
;	H  = QUEUE HEADER POINTER
;	E  = SUCCESSOR OF THIS ENTRY (FOR ORDERING IF CALLING M$LINK)
;	PUSHJ	P,M$xxxx
;		WHERE xxxx IS
;			'LINK' TO LINK ENTRY BEFORE 'E'
;			'ELNK' TO LINK AT THE END OF THE QUEUE
;			'FLNK' TO LINK AT THE BEGINNING OF THE QUEUE
;DESTROYS S1

	INTERN	M$LINK,M$ELNK,M$FLNK

M$ELNK:	CAIN	H,HDRIPC##		;LINKING THE IPCF QUEUE
	  PUSHJ	P,I$NOIN##		;YES, TURN OFF IPCF INTERRUPTS
	LOAD	S1,.QHLNK(H),QH.PTL	;FIND THE LAST
	STORE	AP,.QHLNK(H),QH.PTL	;THIS IS NOW THE LAST
	STORE	S1,.QELNK(AP),QE.PTP	;POINT BACK AT THE OLD LAST
	ZERO	.QELNK(AP),QE.PTN	;LAST HAS NO NEXT
	JUMPE	S1,ELNK.1		;JUMP IF THIS IS THE FIRST
	STORE	AP,.QELNK(S1),QE.PTN	;LINK THE OLD LAST TO THIS ONE
	POPJ	P,			;AND RETURN
ELNK.1:	STORE	AP,.QHLNK(H),QH.PTF	;STORE AS FIRST IN QUEUE ALSO
	POPJ	P,			;AND RETURN

M$FLNK:	CAIN	H,HDRIPC##		;LINKING THE IPCF QUEUE
	  PUSHJ	P,I$NOIN##		;YES, TURN OFF IPCF INTERRUPTS
	LOAD	S1,.QHLNK(H),QH.PTF	;FIND THE FIRST
	STORE	AP,.QHLNK(H),QH.PTF	;THIS IS NOW THE FIRST
	STORE	S1,.QELNK(AP),QE.PTN	;POINT TO THE OLD FIRST
	ZERO	.QELNK(AP),QE.PTP	;FIRST HAS NO PREVIOUS
	JUMPE	S1,FLNK.1		;JUMP IF THIS IS THE LAST
	STORE	AP,.QELNK(S1),QE.PTP	;LINK THE OLD FIRST BACK TO THIS
	POPJ	P,			;AND RETURN
FLNK.1:	STORE	AP,.QHLNK(H),QH.PTL	;STORE THIS AS LAST IN QUEUE ALSO
	POPJ	P,			;AND RETURN

M$LINK:	JUMPE	E,M$ELNK		;USE THE SPECIAL CASE IF THIS IS TO BE THE LAST
	CAIN	H,HDRIPC##		;LINKING THE IPCF QUEUE
	  PUSHJ	P,I$NOIN##		;YES, TURN OFF IPCF INTERRUPTS
	LOAD	S1,.QELNK(E),QE.PTP	;LOAD SUCCESSORS PREVIOUS
	JUMPE	S1,M$FLNK		;JUMP IF THIS IS TO BE THE FIRST
	STORE	S1,.QELNK(AP),QE.PTP	;THAT IS MY PREVIOUS
	STORE	AP,.QELNK(S1),QE.PTN	;AND I AM ITS NEXT
	STORE	E,.QELNK(AP),QE.PTN	;POINT TO THE SUCESSOR
	STORE	AP,.QELNK(E),QE.PTP	;I AM ITS PREVIOUS
	POPJ	P,			;AND RETURN
;ROUTINE TO DISSOLVE THE LINKS OF AN ENTRY
;NORMALLY CALLED BEFORE THE CALL TO M$PFRE AND IS CALLED BY M$RFRE
;CALL	AP = CELL TO REMOVE
;	H  = QUEUE HEADER POINTER
;	PUSHJ	P,M$DLNK
;DESTROYS S1,S2

	INTERN	M$DLNK

M$DLNK:	CAIN	H,HDRIPC##		;DELINKING THE IPCF QUEUE
	  PUSHJ	P,I$NOIN##		;YES, TURN OFF IPCF INTERRUPTS
	LOAD	S1,.QELNK(AP),QE.PTP	;GET LINKS OF THE ENTRY TO BE REMOVED
	LOAD	S2,.QELNK(AP),QE.PTN	;S1 = PREVIOUS, S2 = SUCCESSOR
	JUMPE	S1,DLNK.1		;JUMP IF REMOVING THE FIRST
	JUMPE	S2,DLNK.3		;JUMP IF REMOVING THE LAST
	STORE	S1,.QELNK(S2),QE.PTP	;LINK THE REMAINING ENTRIES TOGETHER
	STORE	S2,.QELNK(S1),QE.PTN	;TO COMPLETELY DISSOLVE THE OLD LINKS
	POPJ	P,			;AND RETURN
DLNK.1:	JUMPE	S2,DLNK.2		;JUMP IF REMOVING THE ONLY
	STORE	S2,.QHLNK(H),QH.PTF	;STORE SUCCESSOR AS THE NEW FIRST
	ZERO	.QELNK(S2),QE.PTP	;AND CLEAR ITS BACKWARD LINK
	POPJ	P,			;AND RETURN
DLNK.2:	ZERO	.QHLNK(H)		;IF REMOVING THE ONLY, CLEAR THE HEADER
	POPJ	P,			;AND RETURN
DLNK.3:	STORE	S1,.QHLNK(H),QH.PTL	;STORE PREVIOUS AS NEW LAST
	ZERO	.QELNK(S1),QE.PTN	;AND CLEAR ITS FORWARD LINK
	POPJ	P,			;AND RETURN


;ROUTINE TO MOVE A CELL FROM ONE QUEUE TO ANOTHER (PROCESSING TO USE, ETC..)
;CALL	H  = CURRENT QUEUE
;	AP = THE CELL TO MOVE
;	S1 = NEW QUEUE
;	PUSHJ	P,M$MOVE
;RETURNS AP & H UPDATED TO REFLECT NEW CELL
;DESTROYS S1,S2

	INTERN	M$MOVE

M$MOVE:	SAVE	LVL			;SAVE CALLERS
	PUSH	P,S1			;SAVE NEW HEADER
	PUSHJ	P,M$DLNK		;REMOVE CELL FROM OLD QUEUE
	PUSHJ	P,SETLVL		;DETERMINE LEVEL (CLASS) OF OLD QUEUE
	MOVE	S1,LVL			;SAVE LEVEL OF OLD QUEUE
	POP	P,H			;NEW HEADER
	PUSHJ	P,SETLVL		;FIND LEVEL OF NEW QUEUE
	CAIE	S1,(LVL)		;MOVING ACROSS LEVELS
	  STOPCD(MDL,FATAL)		;++MOVING DIFFERENT LEVEL
	LOAD	S1,.QHPAG(H),QH.SCH	;BASE SCHEDULER VECTOR FOR NEW QUEUE
	PJUMPE	S1,M$ELNK		;IF NO VECTOR TACK IT TO THE END
	PJRST	SCHLNK(S1)		;LINK IT IN AND RETURN
;ROUTINE TO BUILD THE FREE LIST IN A PAGE
;NORMALLY CALLED AFTER THE RETURN FROM M$ACQP
;CALL	AP = THE NEWLY ACQUIRED PAGE NUMBER
;	H  = QUEUE HEADER POINTER
;	LVL= QUEUE CLASS (LEVEL)
;	PUSHJ	P,BLDFRE
;DESTROYS S1,S2

BLDFRE:	PUSHJ	P,.SAVE2##		;SAVE P1,P2
	LOAD	S1,LVLTBL(LVL),LVLFPG	;GET FIRST PAGE IN THE CHAIN
	STORE	AP,LVLTBL(LVL),LVLFPG	;START WITH THE NEW ONE ACQUIRED
	STORE	S1,PAGTBL(AP),PT.LNK	;AND LINK NEW ONE TO OLD FIRST
	MOVEI	S1,(AP)			;COPY THE PAGE NUMBER
	PG2ADR	S1			;MAKE AN ADDRESS FROM IT
	MOVEI	S1,1(S1)		;WORD 0 IS THE LINK FOR FUTURE FREE SPACE
	MOVEM	S1,-1(S1)		;STORE AS A LINK TO WORD 1
	LOAD	S2,LVLTBL(LVL),LVLSIZ	;GET SIZE OF AN ENTRY IN THIS CLASS
	LOAD	P1,LVLTBL(LVL),LVLNFC	;GET NUMBER OF FREE CELLS IN A PAGE
	ADDM	P1,LVFCNT(LVL)		;INCLUDE NEW PAGE IN THE TOTAL
	STORE	P1,PAGTBL(AP),PT.CNT	;STORE THE COUNT OF FREE CELLS IN THIS PAGE
	SOJE	P1,BLDF.2		;DECREMENT FOR LOOP, JUMP IF ONLY ONE FITS
	MOVEI	P2,(S1)			;START COMPUTATIONS THE NEXT IN THIS PAGE
BLDF.1:	ADDI	P2,(S2)			;P2 = NEXT ENTRY
	STORE	P2,.QELNK(S1),QE.PTN	;LINK OF THIS POINTS TO NEXT
	MOVEI	S1,(P2)			;NOW POINT TO NEXT ENTRY
	SOJG	P1,BLDF.1		;AND LOOP FOR ALL ENTRIES - 1
BLDF.2:	ZERO	.QELNK(S1),QE.PTN	;LAST LINKS TO NOWHERE
	POPJ	P,			;AND RETURN

;ROUTINE TO ESTABLISH THE QUEUE CLASS (LEVEL) FOR QUEUE 'H'
;CALL	H = THE QUEUE HEADER
;	SAVE	LVL
;	PUSHJ	P,SETLVL
;RETURNS LVL = THE QUEUE CLASS (CALLER MUST SAVE IT FIRST)

SETLVL:	LOAD	LVL,.QHTYP(H),QH.IPC	;GET 'IPCF' QUEUE BIT
	TRC	LVL,1			;FLIP SO 0 = IPCF, 1 = ANYTHING ELSE
	POPJ	P,			;AND RETURN
;ROUTINE TO CHECK IF GARBAGE COLLECTION WILL ACCOMPLISH ANYTHING
;CALL	 LVL= CURRENT QUEUE CLASS (LEVEL)
;RETURNS S1 = PAGE THAT CAN BE ELIMINATED OR 0 IF CANT ELIMINATE ANYTHING

;THE DEPENDENCE ON THE METHODS USED BY M$PFRE AND M$GFRE MUST BE NOTED.
;	IN THE DETERMINATION OF THE PAGE TO BE ELIMINATED, THE PAGE WITH THE
;	"MOST" AMOUNT OF FREE SPACE IS USED, SINCE M$GFRE USES THE PAGE WITH
;	THE "LEAST" AMOUNT.  IF MULTIPLE PAGES HAVE THE SAME AMOUNT, M$GFRE USES
;	THE FIRST IN THE CHAIN, THEREFORE, TO ELIMINATE ONE, PICK THE LAST ONE.
;	M$PFRE (M$RFRE) WILL M$RELP A PAGE WHEN IT BECOMES COMPLETELY EMPTY.

CHKELM:	PUSHJ	P,.SAVET##		;SAVE THE T REGS
	LOAD	T1,LVLTBL(LVL),LVLFPG	;GET FIRST PAGE IN THE CHAIN
	JUMPE	T1,.FALSE##		;RETURN 0 IF EMPTY
	SETZB	T2,T3			;FOR FINDING FREE SPACE AND TOTALS
CHKE.1:	LOAD	T4,PAGTBL(T1),PT.CNT	;COUNT OF FREE CELLS IN THIS PAGE
	CAIGE	T4,(T2)			;THIS HAVE THE MOST YET
	  JRST	CHKE.2			;EQUAL CASE MUST BE CONSISTANT WITH M$GFRE
	MOVEI	S1,(T1)			;REMEMBER "BEST" PAGE
	MOVEI	T2,(T4)			;AND ITS CELL COUNT
CHKE.2:	ADDI	T3,(T4)			;INCLUDE IN THE TOTAL FREE
	LOAD	T1,PAGTBL(T1),PT.LNK	;FIND THE NEXT IN THE CHAIN
	JUMPN	T1,CHKE.1		;KEEP GOING IF THERE'S ANOTHER
	JUMPE	T3,.FALSE##		;RETURN 0 IF NO FREE CELLS LEFT
	CAME	T3,LVFCNT(LVL)		;COMPUTED AND RUNNING TALLIES AGREE
	  STOPCD(FNA,FATAL)		;++FREE TALLIES DO NOT AGREE
	LOAD	T1,LVLTBL(LVL),LVLNFC	;NUMBER OF FREE CELLS PER PAGE
	CAMG	T3,T1			;MOVE THAN 1 PAGE OF FREE SPACE
	  JRST	.FALSE##		;NO, CANNOT REDUCE
	POPJ	P,			;YES, RETURN WITH S1 = THE PAGE NUMBER
;SUBROUTINE TO ELIMINATE A PAGE FOR THE GARBAGE COLLECTOR
;CALL AP = THE PAGE TO ELIMINATE (OUTPUT FROM CHKELM)
;     H  = THE QUEUE HEADER
;     LVL= THE QUEUE LEVEL(CLASS)

DOELIM:	PUSHJ	P,.SAVET##		;SAVE ALL THE T REGS
	SAVE	E			;AND E
	SAVE	AP			;AND THE PAGE NUMBER
	MOVE	T1,AP			;SAVE THE PAGE NUMBER
	LOAD	T2,LVLTBL(LVL),LVLSIZ	;GET SIZE OF AN ENTRY THIS CLASS
	LOAD	E,.QHLNK(H),QH.PTF	;FIND FIRST IN THIS QUEUE
DOEL.1:	JUMPE	E,.POPJ##		;RETURN IF AT END OF LIST
	MOVEI	T3,(E)			;COPY CELL ADDRESS
	ADR2PG	T3			;CONVERT TO A PAGE NUMBER
	CAIE	T3,(T1)			;PAGE TRYING TO ELIMINATE
	  JRST	DOEL.2			;NO, TRY ANOTHER
	PUSHJ	P,M$GFRE		;YES, GET A NEW FREE CELL
	HRLI	T3,(E)			;SOURCE IS OLD ENTRY
	HRRI	T3,(AP)			;DESTINATION IS NEW ONE
	MOVEI	T4,(AP)			;NOW FIND HOW MUCH TO MOVE
	ADDI	T4,-1(T2)		;AS NEW + LENGTH OF ENTRY - 1
	BLT	T3,(T4)			;MOVE THE ENTRY TO ANOTHER PAGE
	MOVEI	T3,(E)			;COPY CELL ADDRESS AGAIN
	LOAD	E,.QELNK(E),QE.PTN	;FIND THE NEXT NOW
	PUSHJ	P,M$LINK		;PUT NEW IN CORRECT ORDER
	MOVEI	AP,(T3)			;NOW GET OLD CELL ADDRESS
	PUSHJ	P,M$RFRE		;AND UNLINK AND RETURN IT
	MOVX	AP,PT.USE		;THE IN USE BIT
	TDNN	AP,PAGTBL(T1)		;DID THE PAGE GO AWAY AFTER THAT RETURN
	  POPJ	P,			;YES, HAVE ELIMINATED IT
	JRST	DOEL.1			;NO, TRY ANOTHER CELL
DOEL.2:	LOAD	E,.QELNK(E),QE.PTN	;LINK TO THE NEXT ENTRY
	JRST	DOEL.1			;AND TRY THAT ONE
SUBTTL	Page Management Subroutines

;ROUTINE TO ACQUIRE A FRESH PAGE FROM THE PAGE MAP
;CALL	PUSHJ	P,M$ACQP
;	  RETURNS AP = PAGE NUMBER
;DESTROYS S1
;CALLS FNDPAG, PGSOUT, PGSDES, ANYOUT

	INTERN	M$ACQP

M$ACQP:	PUSHJ	P,I$NOIN##		;TURN OFF IPCF INTERRUPTS
	PUSHJ	P,FNDPAG		;FIND THE BEST AVAILABLE PAGE
	SKIPN	AP			;SKIP IF ONE FOUND
	  STOPCD(ASE,FATAL)		;++ADDRESSING SPACE EXHAUSTED

IFN FTJSYS,<

ACQP.1:	MOVX	S1,PT.USE!PT.WRK!PT.ADR	;GET ALL THE BITS
	IORM	S1,PAGTBL(AP)		;INCLUDE THEM
	PJRST	REDUCE			;REDUCE COUNT OF FREE CORE AND RETURN

>  ;END OF IFN FTJSYS

IFN FTUUOS,<

ACQP.1:	PUSHJ	P,REDUCE		;REDUCE COUNT OF FREE PAGES
	MOVX	S1,PT.USE		;GET THE INUSE BIT
	IORB	S1,PAGTBL(AP)		;SET IN USE, GET THE OTHERS
	TXNE	S1,PT.ADR		;IS IT ADDRESSABLE
	  POPJ	P,			;YES, RETURN (MAY PAGE FAULT IT IN LATER)
	PUSHJ	P,.SAVE3##		;SAVE P1-P3
ACQP.2:	MOVEI	P3,(AP)			;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	ACQP.3			;ANALYZE THE ERROR
	MOVX	S1,PT.WRK!PT.ADR	;IN THE WORKING SET, ADDRESSABLE
	IORM	S1,PAGTBL(AP)		;INCLUDE THE FLAG
	POPJ	P,			;AND RETURN
ACQP.3:	PUSH	P,AP			;SAVE THE PAGE WE'RE TRYING TO CREATE
	CAIE	P1,PAGNS%		;OUT OF SWAPPING SPACE
	  JRST	ACQP.4			;NO, LOOK AGAIN
	MOVEI	AP,5			;TAKE A QUICK NAP FIRST
	SLEEP	AP,			;IN CASE SOME FREES UP
	PUSHJ	P,PGSDES		;FREE SOME SWAPPING SPACE
	POP	P,AP			;RESTORE AP
	JRST	ACQP.2			;AND RETRY THE CREATE
ACQP.4:	CAIE	P1,PAGLE%		;MY LIMIT EXCEEDED
	  STOPCD(CCP,FATAL)		;++CANNOT CREATE PAGE
	PUSHJ	P,ANYOUT		;SWAP OUT ANYTHING
	POP	P,AP			;RESTORE AP
	JRST	ACQP.2			;RETRY THE CREATE

>  ;END OF IFN FTUUOS
;ROUTINE TO ACQUIRE A FRESH (UNADDRESSABLE) PAGE FROM THE PAGE MAP FOR IPCF
;CALL	PUSHJ	P,M$NXPG
;	  RETURNS AP = THE PAGE NUMBER AVAILABLE FOR IPCF RECEIVE
;			0 IF NONE AVAILABLE (NO CORE AVB)
;		NORMALLY, A CALL TO M$IPRC FOLLOWS THE RECEIVE AND THE PAGE IS INCLUDED
;			IN FACT, THE CALL TO M$IPRC IS REQUIRED TO KEEP FREPGS STRAIGHT
;DESTROYS S1
;CALLS FNDPAG, PGSOUT, PGSDES

	INTERN	M$NXPG

M$NXPG:	PUSHJ	P,I$NOIN##		;TURN OFF IPCF INTERRUPTS
	MOVE	S1,FREPGS		;GET FREE CORE LEFT
	CAIL	S1,PADING		;INSURE IPCF WON'T FILL OUR SPACE
	  JRST	NXPG.1			;GO TRY TO GET ONE
NXPG.0:	ZERO	AP			;RETURN A ZERO
	POPJ	P,			;THAT WILL SHUT IT OFF

IFN FTJSYS,<			;TOPS20 WILL REPLACE ANY EXISTING PAGE WITH THE MESSAGE

NXPG.1:	PUSHJ	P,FNDPAG		;ESSENTUALLY DUPLICATE M$ACQP
	PJUMPE	AP,.POPJ##		;RETURN 0 IF CANT GET ONE NOW
	MOVX	S1,PT.USE		;SET THE TEMP STATE
	IORM	S1,PAGTBL(AP)		;OF INUSE BUT NOT ADDRESSABLE
	POPJ	P,			;AND RETURN

>  ;END OF IFN FTJSYS

IFN FTUUOS,<			;TOPS10 REQUIRES A NON-EXISTANT PAGE

NXPG.1:	PUSHJ	P,NXPG.3		;FIND A COMPLETELY MISSING PAGE
	JUMPN	AP,NXPG.2		;TAKE THIS ONE
	PUSHJ	P,.SAVE3##		;SAVE P1-P3
	PUSHJ	P,PGSDES		;DESTROY ALL I CAN
	PUSHJ	P,NXPG.3		;NOW TRY TO FIND ONE
	PJUMPE	AP,.POPJ##		;RETURN 0 IF CANT GET ONE NOW
NXPG.2:	MOVX	S1,PT.USE		;SET THE TEMP STATE
	IORM	S1,PAGTBL(AP)		;OF INUSE BUT NOT ADDRESSABLE
	POPJ	P,			;AND RETURN

NXPG.3:	MOVE	AP,PAGSTA		;START AT THE FIRST AVAILABLE PAGE
NXPG.4:	CAIL	AP,^D512		;END OF THE ADDRESSING SPACE
	  JRST	NXPG.0			;YES, RETURN A ZERO
	MOVE	S1,PAGTBL(AP)		;GET THE TABLE ENTRY
	TXNN	S1,PT.USE!PT.WRK!PT.ADR	;IS THIS PAGE THERE
	  POPJ	P,			;NO BITS MEANS NON-EXISTANT
	AOJA	AP,NXPG.4		;WELL, TRY THE NEXT

>  ;END OF IFN FTUUOS
;ROUTINE TO RETURN A PAGE TO THE PAGE MAP
;CALL	AP = PAGE NUMBER
;	PUSHJ	P,M$RELP
;DESTROYS S1

	INTERN	M$RELP

M$RELP:	PUSHJ	P,VALPAG		;CONSISTENCY CHECK 'AP'
	MOVE	S1,PAGTBL(AP)		;GET THE FLAGS
	TXZN	S1,PT.USE		;CLEAR IN USE
	  STOPCD(RFP,FATAL)		;++RELEASING FREE PAGE
	TXNE	S1,PT.ADR		;IS THIS THE ONE IPCF'ED AWAY
	  JRST	RELP.1			;NO, GO FIX THE COUNTS
	ZERO	PAGTBL(AP)		;CLEAR THE ENTRY
	POPJ	P,			;AND RETURN
RELP.1:	MOVEM	S1,PAGTBL(AP)		;STORE NEW SETTINGS
	TXNE	S1,PT.WRK		;THIS PAGE LEFT IN CORE
	  AOS	AVBPGS			;YES, COUNT IT
	PJRST	INCLUD			;BUMP FREE PAGE COUNT AND RETURN

;ROUTINE TO SET NOTIFICATION OF A PAGE BEING IPCF'ed FROM OUR ADDRESSING SPACE
;CALL	AP = PAGE NUMBER
;	PUSHJ	P,M$IPSN
;DESTORYS S1

	INTERN	M$IPSN

M$IPSN:	PUSHJ	P,VALPAG		;CONSISTENCY CHECK 'AP'
	MOVX	S1,PT.WRK!PT.ADR	;CLEAR IN THE WORKING SET, ADDRESSABLE
	ANDCAM	S1,PAGTBL(AP)		;SO THAT WE DON'T GET CONFUSED
	PJRST	INCLUD			;BUMP FREE PAGE COUNT AND RETURN
;ROUTINE TO INCLUDE A PAGE RECEIVED BY IPCF INTO OUR ADDRESSING SPACE
;CALL	AP = THE PAGE NUMBER
;	PUSHJ	P,M$IPRC
;DESTROYS S1,S2

	INTERN	M$IPRC

IFN FTUUOS,<	;NOW NEED TO KNOW IF THE PAGE IS IN THE WORKING SET

M$IPRC:	PUSHJ	P,VALPAG		;CONSISTENCY CHECK 'AP'
	MOVEI	S2,(AP)			;FOR PAGE 'AP'
	HRLI	S2,.PAGCA		;CHECK ITS ACCESS BITS
	PAGE.	S2,			;SEE IF THE PAGE IS SWAPPED OUT
	  STOPCD(PAF,FATAL)		;++PAGE ACCESS CHECK FAILED
	MOVX	S1,PT.WRK!PT.ADR	;IN THE WORKING SET, ADDRESSABLE
	TXNE	S2,PA.GNE		;PAGE DOESN'T EXIST
	  STOPCD(RNP,FATAL)		;++RECEIVED NON-EXISTANT PAGE
	TXNE	S2,PA.GPO		;IS IT PAGED OUT
	  TXZ	S1,PT.WRK		;YES, CLEAR IN THE WORKING SET
	IORM	S1,PAGTBL(AP)		;INCLUDE THE FLAG(S)
	PJRST	REDUCE			;REDUCE COUNT OF FREE PAGES AND RETURN

>  ;END OF IFN FTUUOS

IFN FTJSYS,<

M$IPRC:	PUSHJ	P,VALPAG		;CONSISTENCY CHECK 'AP'
	MOVX	S1,PT.WRK!PT.ADR	;IN THE WORKING SET, ADDRESSABLE
	IORM	S1,PAGTBL(AP)		;INCLUDE THE FLAGS
	PJRST	REDUCE			;REDUCE COUNT OF FREE PAGES AND RETURN

>  ;END OF IFN FTJSYS
;ROUTINE TO DETERMINE WHETHER A PARTICULAR NUMBER OF PAGES CAN BE
;	ACQUIRED.
;CALL	S1 = NUMBER OF PAGES
;	PUSHJ P,M$PCNT
;	  RETURN TRUE OR FALSE
;
;NOTE: UNLESS THE CALLER DISABLES INTERRUPTS THE NUMBER OF AVAILABLE
;	PAGES IS VERY VOLATILE.

	INTERN	M$PCNT

M$PCNT:	ADDI	S1,PADING		;INCLUDE BUILD IN PADING
	CAMLE	S1,FREPGS		;CAN WE GET IT?
	PJRST	.FALSE##		;NO, TELL HIM HE LOSES
	PJRST	.TRUE##			;YUP, WIN!!
;ROUTINE TO SEARCH THE PAGE MAP FOR THE BEST PAGE FOR M$ACQP TO USE

;CALL	PUSHJ P,FNDPAG
;	  RETURNS AP = THE PAGE OR 0 IF OUT OF ADDRESSING SPACE
;DESTROYS S1

FNDPAG:	MOVE	AP,PAGSTA		;FIRST AVAILABLE PAGE
FNDP.1:	CAIL	AP,^D512		;OFF THE END
	  JRST	FNDP.2			;YES, DIDN'T FIND ANY, RETURN 0
	MOVE	S1,PAGTBL(AP)		;GET THE TABLE ENTRY
	TXNE	S1,PT.USE		;THIS ONE IN USE
	  AOJA	AP,FNDP.1		;YES, DON'T EVEN BOTHER
	TXC	S1,PT.WRK!PT.ADR	;FLIP
	TXCN	S1,PT.WRK!PT.ADR	;BACK, P.S. IS IT INCORE AND FREE
	  JRST	FNDP.3			;CAN'T GET ANY BETTER THAN THAT
	SKIPN	AVBPGS			;WILL ANOTHER PASS FIND ANYTHING
	  POPJ	P,			;NO, TAKE THIS FREE PAGE NOW
	AOJA	AP,FNDP.1		;YES IT WILL, GO FIND IN CORE PAGE
FNDP.2:	TDZA	AP,AP			;NO PAGES, RETURN 0
FNDP.3:	SOS	AVBPGS			;DECREMENT COUNT OF IN CORE PAGES
	POPJ	P,			;AND RETURN


;CONSISTENCY CHECKS ON THE NUMBER OF FREE PAGES IN OUR ADDRESSING SPACE
;"REDUCE" DECREMENTS THE FREE PAGE COUNT , "INCLUD" ADDS A FREE PAGE
;
;INCLUD DESTROYS S1

REDUCE:	SOSGE	FREPGS			;DECREMENT COUNT OF FREE PAGES
	  STOPCD(FCN,FATAL)		;++FREE COUNT NEGATIVE
	POPJ	P,			;RETURN IF OK

INCLUD:	AOS	S1,FREPGS		;ADD A FREE PAGE
	CAMLE	S1,FREINI		;MORE THAN WE STARTED OUT WITH
	  STOPCD(FCE,FATAL)		;++FREE COUNT EXCEEDS FREINI
	POPJ	P,			;RETURN IF OK STILL

;CONSISTENCY CHECK ON 'AP', THE PAGE NUMBER ARGUMENT TO
;	M$RELP, M$IPSN, M$IPRC

VALPAG:	CAIG	AP,^D511		;OVER THE TOP OF THE WORLD
	 CAMGE	AP,PAGSTA		;OR UNDER THE INITIAL VALUE
	  STOPCD(BPN,FATAL)		;++BAD PAGE NUMBER
	POPJ	P,			;RETURN, 'AP' IS IN RANGE
SUBTTL	The Page Fault Handler

;THIS IS THE ROUTINE CALLED BY THE TOPS10 SYSTEM SO THAT A USER MAY PROCESS
;HIS OWN PAGE FAULTS.  .JBPFH CONTAINS THE STARTING ADDRESS OF THE ROUTINE
;AND IS CALLED WHEN EITHER A FAGE FAULT OCCURS OR THE .STTVM TIMER HAS EXPIRED

IFN FTUUOS,<	;THIS CODE EXISTS ONLY FOR THE TOPS10 MONITOR

PFHBGN:	JRST	PFHSTA			;ENTERED HERE
PFH.PC:	0				;PC AT TIME OF TRAP
PFH.PN:	0				;PAGE FAULT WORD
PFH.TI:	0				;VIRTUAL AGE OF THE PAGE FAULT HANDLER
PFH.RA:	0				;CURRENT PAGING RATE
	BLOCK	4			;ROOM FOR FUTURE EXPANSION

PFHSTA:	MOVEM	P,PFHS.P		;SAVE SOMEBODY'S P
	MOVE	P,[IOWD PF.PSZ,PF.PDL]	;GET MY OWN PDL
	PUSH	P,P1			;BEGIN TO SAVE SEVERAL REGISTERS
	PUSH	P,P2			;CAUSE I'M GOING TO CALL SUBROUTINES
	PUSH	P,P3			;FOR SWAPPING AND SEARCHING
	PUSH	P,AP			;AND THESE ARE THE ARGUMENTS
	LOAD	P2,PFH.PN,PF.HFC	;THE FAULT TYPE
	CAIG	P2,PFHDCT		;RANGE CHECK
	  JRST	PFHDSP(P2)		;DISPATCH THE FAULT
PFHDSP:	JRST	PFHILL			;PAGE FAULT CODE = 0 OR TOO BIG
	JRST	PFHGVA			;.PFHNA = 1 = NO ACCESS
	JRST	PFHSWI			;.PFHNI = 2 = NOT IN CORE
	JRST	PFHSWI			;.PFHUU = 3 = NOT IN CORE FOR UUO
	JRST	PFHXIT			;.PFHTI = 4 = IGNORE TIMER TRAPS
PFHDCT==.-PFHDSP-1			;DISPATCH LENGTH (0-n)

PFHILL:	STOPCD	(UPF,FATAL)		;++UNKNOWN PAGE FAULT TYPE

PFHSWI:	LOAD	P3,PFH.PN,PF.HPN	;GET THE PAGE THAT CAUSED THE FAULT
	MOVEI	P2,1			;ONLY 1 PAGE
	MOVE	P1,[.PAGIO,,P2]		;FUNCTION PAGE IO,,ARGUMENT BLOCK
	PAGE.	P1,			;SWAP IT IN
	  JRST	PFHSWF			;FAILED, SEE WHY
	MOVX	P1,PT.WRK		;THE WORKING SET BIT
	IORM	P1,PAGTBL(P3)		;PAGE IS NOW IN CORE
	JRST	PFHXIT			;DISMISS THE FAULT
PFHSWF:	CAIE	P1,PAGLE%		;IS MY CORE LIMIT EXCEEDED
	  STOPCD(PIF,FATAL)		;++PAGE IN FAILURE
	PUSHJ	P,ANYOUT		;SWAP OUT SOMETHING (ANYTHING)
	JRST	PFHSWI			;TRY THE SWAPIN NOW

;;;CODE IS CONTINUED ON THE NEXT PAGE AND STILL UNDER FTUUOS CONDITIONAL
;HERE TO GIVE ACCESS TO A PAGE (CAN ONLY HAPPEN IF LOADED VIA GET.SHR)

PFHGVA:	LOAD	P3,PFH.PN,PF.HPN	;GET THE PAGE NUMBER WE CAN'T ACCESS
	TXO	P3,1B0			;SET TO ALLOW ACCESS
	MOVEI	P2,1			;ONLY ONE ARG
	MOVE	P1,[.PAGAA,,P2]		;GIVE ACCESS,,ARGS
	PAGE.	P1,			;SET ACCESS ALLOWED ON THAT PAGE
	  STOPCD(CGA,FATAL)		;++CANNOT GIVE ACCESS TO PAGE
PFHXIT:	POP	P,AP			;RESTORE AP
	POP	P,P3			;...
	POP	P,P2			;...
	POP	P,P1			;...
	MOVE	P,PFHS.P		;RESTORE THE OLD AC P
	JRSTF	@PFH.PC			;DISMISS THE FAULT

PFHS.P:	BLOCK	1			;SAVED P
PF.PDL:	BLOCK	^D12			;PDL FOR USE IN THE PAGE FAULT HANDLER
PF.PSZ==.-PF.PDL			;SIZE OF THE LIST

;ROUTINE TO DESTROY AS MANY PAGES THAT ARE SWAPPED OUT OR UNUSED
;CALL	PUSHJ	P,PGSDES
;	RETURNS AFTER DESTROYING AS MANY AS POSSIBLE
;DESTORYS AP,P1-P3 WITHOUT SAVING

PGSDES:	MOVE	AP,PAGSTA		;THE FIRST AVAILABLE PAGE
PGSD.1:	CAIL	AP,^D512		;OFF THE END OF THE WORLD
	  POPJ	P,			;YES, RETURN
	MOVE	P1,PAGTBL(AP)		;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,DESTRY		;DESTROY THE PAGE (COULD BE PAGED OUT)
	AOJA	AP,PGSD.1		;AND CONTINUE LOOPING

DESTRY:	MOVEI	P3,(AP)			;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
	  STOPCD(PDF,FATAL)		;++PAGE DESTROY FAILED
	ZERO	P1			;CLEAR A REG
	EXCH	P1,PAGTBL(AP)		;CLEAR PAGE MAP ENTRY, GET FLAGS
	TXNE	P1,PT.WRK		;JUST WIPE OUT AN INCORE PAGE
	  SOS	AVBPGS			;YES, REDUCE COUNTERS ACCORDINGLY
	POPJ	P,			;RETURN


;;;CODE IS CONTINUED ON THE NEXT PAGE AND STILL UNDER FTUUOS CONDITIONAL
;ROUTINE TO SWAP OUT ANYTHING POSSIBLE SO THAT A PAGEIN CAN OCCUR
;CALL	PUSHJ	P,ANYOUT
;	RETURNS AFTER A PAGE HAS BEEN SUCCESSFULLY PAGED OUT
;DESTROYS AP, P1-P3 WITHOUT SAVING
;COULD EXIT THROUGH DESTRY

	INTERN	M$IPRM

M$IPRM:	PUSHJ	P,.SAVE3##		;SAVE P1-P3
	SAVE	AP			;AND AP AND FALL INTO ANYOUT
ANYOUT:	MOVE	AP,PAGSTA		;FIRST MAKE A PASS LOOKING FOR ANY FREE
ANYO.1:	PUSHJ	P,ANYO.4		;PAGES THAT CAN BE DESTROYED
	JUMPE	AP,ANYO.2		;NONE, PAGE SOMETHING OUT
	TXNE	P1,PT.USE		;IS THIS IN-CORE PAGE USED
	  AOJA	AP,ANYO.1		;YES, LOOK FOR ANOTHER
	PJRST	DESTRY			;NO, DESTROY IT NOW AN SAVE A SWAP
ANYO.2:	AOS	AP,SWPSTA		;START WHERE WE LEFT OFF
	PUSHJ	P,ANYO.4		;FIND A SWAPPED IN PAGE
	JUMPN	AP,ANYO.3		;SWAP IT OUT
	MOVE	AP,PAGSTA		;RE-START AT THE FIRST
	PUSHJ	P,ANYO.4		;TRY NOW
	JUMPN	AP,ANYO.3		;FOUND ONE, SWAP IT OUT
	STOPCD	(NTS,FATAL)		;++NOTHING TO SWAP OUT
ANYO.3:	MOVEM	AP,SWPSTA		;REMEMBER FOR LATER
	MOVEI	P3,(AP)			;COPY THE PAGE NUMBER
	TXO	P3,1B0			;SET TO SWAP OUT INSTEAD OF SWAP IN
	MOVEI	P2,1			;ONLY 1 PAGE FOR NOW
	MOVE	P1,[.PAGIO,,P2]		;FUNCTION SWAP,,ARGUMENT BLOCK
	PAGE.	P1,			;OUT IT GOES
	  STOPCD(POF,FATAL)		;++PAGE OUT FAILURE
	MOVX	P1,PT.WRK		;GET THE WORKING SET BIT
	ANDCAB	P1,PAGTBL(P3)		;NOT THERE ANY MORE, GET NEW FLAGS
	TXNN	P1,PT.USE		;WAS SOMEBODY USING IT
	  SOS	AVBPGS			;NO, REDUCE COUNT OF INCORE FREE PAGES
	POPJ	P,			;AND RETURN FOR ANY RETRIES

ANYO.4:	CAIL	AP,^D512		;OVER THE TOP
	  JRST	ANYO.5			;YES, RETURN A ZERO
	MOVE	P1,PAGTBL(AP)		;GET THE STATUS FLAGS
	TXNE	P1,PT.ADR		;IS IT ADDRESSABLE
	 TXNN	P1,PT.WRK		;YES, IS IT IN THE WORKING SET
	  AOJA	AP,ANYO.4		;TRY THE NEXT PAGE
	POPJ	P,			;YES, A CANDIDATE FOR SWAP OUT
ANYO.5:	ZERO	AP			;RETURN 0 IF NONE FOUND
	POPJ	P,			;RETURN

SWPSTA:	BLOCK	1			;PAGE NUMBER FOR START OF SWAP OUT SEARCH

PFHEND:		;THE LAST LOCATION WITHIN THE PAGE FAULT HANDLER


>  ;END OF IFN FTUUOS
SUBTTL	Constants and Other Things

	XLIST			;FORCED OUT LITERAL POOL HERE
	LIT
	LIST
	SALL

FREPGS:	BLOCK	1		;COUNT OF FREE PAGES LEFT IN ADDRESSING SPACE
FREINI:	BLOCK	1		;THE INITIAL VALUE OF FREPGS FOR CONSISTENCY CHECKS
PAGSTA:	BLOCK	1		;THE STARTING POINT FOR PAGSRC SEARCHES

ZERLOC:				; BEGIN BLOCK ZERO'ED AT INITIALIZATION

AVBPGS:	BLOCK	1		;COUNT OF INCORE PAGES THAT AREN'T USED
DOGCOL:	BLOCK	1		;-1 IF GARBAGE COLLECTION WILL DO ANY GOOD
LVLTBL:	BLOCK	LVLMAX+1	;LH = MAX ENTRY SIZE, RH = FIRST PAGE IN CHAIN
LVFCNT:	BLOCK	LVLMAX+1	;CURRENT FREE CELL COUNTS PER LEVEL(CLASS)
PAGTBL:: BLOCK	^D512		;THE PAGE MAP

ZERLAS==.-1			; END OF THE BLOCK ZERO'ED AT ONCE ONLY

	END