Google
 

Trailing-Edge - PDP-10 Archives - BB-F493Z-DD_1986 - 10,7/703mon/vmser.mac
Click 10,7/703mon/vmser.mac to see without markup as text/plain
There are 11 other files named vmser.mac in the archive. Click here to see a list.
TITLE	VMSER - VIRTUAL MEMORY SERVICE ROUTINE  V4636
SUBTTL	J. FLEMMING, T. WACHS 5 MAR 86
	SEARCH	F,S
	SALL
	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1973,1986>
;COPYRIGHT (C) 1973,1974,1975,1976,1977,1978,1979,1980,1982,1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.


XP VVMSER,4636    ;ASSEMBLY INSTRUCTIONS: VMSER,VMSER/C=F,S,VMSER
	ENTRY	VMSER
VMSER::


OPDEF	PAGE.	[CALLI	145]
OPDEF	MERGE.	[CALLI	173]
	SUBTTL	BYTE POINTERS

;THESE WILL HAVE TO BE CHANGED IF 30-BIT ADDRESSING IS EVER A REALITY

NVPNP1::POINT P2.SPN,PT2TAB(P1),<^L<P2.VPN>+P2.SPN-1> ;POINTER TO NEXT
NVPNP4::POINT P2.SPN,PT2TAB(P4),<^L<P2.VPN>+P2.SPN-1> ;POINTER TO NEXT
NVPNT1::POINT P2.SPN,PT2TAB(T1),<^L<P2.VPN>+P2.SPN-1> ;POINTER TO NEXT
NVPNT2::POINT P2.SPN,PT2TAB(T2),<^L<P2.VPN>+P2.SPN-1> ;POINTER TO NEXT
NVPNF::	POINT P2.SPN,PT2TAB(F),<^L<P2.VPN>+P2.SPN-1> ;POINTER TO NEXT
NVPNR::	POINT P2.SPN,PT2TAB(R),<^L<P2.VPN>+P2.SPN-1> ;POINTER TO NEXT

SUBTTL FILIO  INTERFACE WITH FILSER

;SUBROUTINE TO SCAN FOR THINGS TO DO FOR THE SWAPPER
;OPTIMIZES ALL SEEK REQUESTS, TRANSFER REQUESTS, STARTS SEEKS GOING
;STARTS DATA TRANSFER GOING IF ON UUO (CHAN 7) LEVEL
SWPSCN::
IFN FTMP,<
	SYSPIF
	SKIPL	T1,SCNOWN##	;IF NO CPU "OWNS" SCNCNT
	CAMN	T1,.CPCPN##	; OR IF THIS CPU "OWNS" IT
>
	AOSE	SCNCNT##	;SOMEONE ALREADY DOING THIS?
IFE FTMP,<
	PJRST	DOPOPJ##	;YES, GO AWAY (AT INTERRUPT LEVEL,
				; SWPSCN IS RUNNING AT UUO LEVEL)
>
IFN FTMP,<
	JRST	[SYSPIN
		 PJRST DOPOPJ##]
	MOVE	T1,.CPCPN##
	MOVEM	T1,SCNOWN##
	SYSPIN
>
	DSKON
	PUSHJ	P,SAVE3##	;NO, NOW WE ARE
	JUMPE	J,SWPSC1	;GO IF UUO LEVEL
	SOS	P3,CHNCFS##(P1)	;UPDATE CHANNEL FAIRNESS COUNT FOR SWAPPING
	HRR	P3,CHNQUE##(P1)	;RH NON-0 IF FILE TRANSFER WAITING
SWPSC1:	PUSH	P,J		;SAVE J
	PUSH	P,U		;SAVE U
SWPSC2:	SETZB	P1,T2		;INDICATE FIRST CALL TO NEXT
	HRLOI	T1,377777	;SET ALL UNITS IN A.S.L. TO HUGE
				; TIMES TO BLOCK OR TIMES TO CYLINDER
	MOVEI	T4,TWCOD##
SWPSC3:	SKIPG	T3,SWPTAB##(T2)	;UNIT IN ASL?
	JRST	SWPSC4
	CAME	T1,UNISWD##(T3)	;TIME ALREADY INFINITY?
	CAME	T4,UNISTS##(T3)	;NO, IS UNIT IN TRANSFER WAIT?
	SKIPA	U,T1		;NO, MAKE TIME = INFINITY
	MOVEI	U,-1		;YES, INSURE SWPPIK WILL FIND IT EVEN IF SWPSCN DOESN'T
	MOVEM	U,UNISWD##(T3)	;SAVE LARGE TIME-TO-BLOCK OR CYLINDER
IFN FTDUAL,<
	SKIPE	T3,UNI2ND##(T3)	;IN 2ND PORT TOO
	MOVEM	U,UNISWD##(T3)
>
SWPSC4:	CAIGE	T2,SWPMAX##	;END OF THE ACTIVE SWAPPING LIST?
	AOJA	T2,SWPSC3	;NO, LOOK AT ALL UNITS IN THE ASL
	DSKOFF
SWPSC5:	PUSHJ	P,NEXT		;GET NEXT REQUEST FROM SWPLST
	  JRST	SWPS22		;DONE
	JUMPL	U,SWPS10	;DISK (CPU) GONE, ERROR
	HRRZ	T2,UNICDA##(U)	;DDB UNIT IS ATTACHED TO
	CAIE	T2,SWPDDB##	;IF NOT THE SWAPPER
	JUMPN	T2,SWPSC5	;LET FILE IO HAPPEN
	HRR	J,UNIKON##(U)	;SET UP J POINTING TO  KONTROLLER
IFN FTDUAL,<
	PUSHJ	P,RHBP##	;NEED TO REREAD HOME BLOCKS?
	  JRST	SWPSC5		;YES, DON'T SWAP ON THIS UNIT
	CAME	J,-1(P)		;IGNORE BUSY IF INT ON THIS KON
	SKIPL	KONBSY##(J)	;KON BUSY?
	JRST	SWPSC6		;IDLE, USE PRIME PORT
	SKIPN	T3,UNI2ND##(U)	;BUSY, DUAL-PORTED DRIVE?
	JRST	SWPSC6		;NO, WAIT FOR PRIME PORT
	HRR	J,UNIKON##(T3)	;YES, GET 2ND KON
	CAME	J,-1(P)		;IGNORE BUSY IF INT ON THIS KON
	SKIPL	KONBSY##(J)	;KON BUSY?
	HRRZ	U,UNI2ND##(U)	;IDLE, USE 2ND PORT
	HRR	J,UNIKON##(U)	;GET (NEW) KON
SWPSC6:
IFN FTMP,<
	PUSHJ	P,SWPCP		;SET U IF OTHER CPU
	  JFCL
>>;END FTDUAL&FTMP
	CAMN	J,-1(P)		;IF NOT ON INTERRUPT LEVEL FOR THIS CHAN,
	JRST	SWPSC7
	HRRZ	T2,KONCHN##(J)
	SKIPE	DIADSK##
	CAME	T2,DIACHN##	;IGNORE REQUEST IF SHUTTING DOWN CHAN

	SKIPGE	KONBSY##(J)	;IS KONTROLLER IDLE?
	JRST	SWPSC5		;NO, TRY NEXT SWPLST ENTRY
	JRST	SWPSC8		;YES, THIS REQUEST IS OK
SWPSC7:	JUMPGE	P3,SWPSC8	;GO UNLESS BEING FAIR
IFN FTDUAL,<
	SKIPE	T4,UNI2ND##(U)	;POSITIONS WAITING ON SECOND PORT?
	SKIPN	UNIQUE##(T4)
>
	SKIPE	UNIQUE##(U)	;OR PRIME PORT?
	JRST	SWPSC5		;YES, DON'T DO THIS REQUEST
SWPSC8:	SKIPG	KONPOS##(J)	;CAN DO THINGS FOR THIS REQUEST
	JRST	SWPS12		;UNIT DOESNT POSITION
	MOVE	T4,T1		;POSITIONING UNIT - SAVE BLOCK NUMBER
	PUSHJ	P,@KONCCM##(J)	;COMPUTE DISTANCE TO BLOCK
	JUMPE	T1,SWPS11	;GO IF UNIT ON CYLINDER
	MOVMS	T1		;NOT ON CYL, GET + DISTANCE
	SKIPE	T2,UNISTS##(U)	;IS UNIT IDLE
	CAIN	T2,O2COD##	; OR OFF-LINE (NEED THIS TO GET TO BADUNI)
	JRST	SWPSC9		;YES, SAVE THIS REQUEST
	CAIN	T2,PWCOD##	;NO, IN POSITION WAIT?
	CAML	T1,UNISWD##(U)	;YES, IS THIS DISTANCE SHORTER?
	JRST	SWPSC5		;NO, TRY NEXT REQUEST
SWPSC9:	MOVEI	T2,PWCOD##	;MAKE SURE UNIT IS IN POSITION WAIT
	PUSHJ	P,SETUDB	;SET UNISWA, ETC.
	JRST	SWPSC5		;AND TEST NEXT SWPLST ENTRY
;HERE AFTER NUS STOPCD

SWPS10:	PUSHJ	P,SWPCD1	;GIVE SWAP READ ERROR
	JRST	SWPSC5		;AND PUSH ON
;HERE IF UNIT ON-CYLINDER
SWPS11:	MOVE	T1,T4		;RESTORE BLOCK NUMBER

;HERE IF UNIT DOESN'T POSITION OR ON CYLINDER
SWPS12:	CAMN	J,-1(P)		;INTERRUPT LEVEL FOR THIS KON?
	JRST	SWPS13		;YES
	SKIPL	KONBSY##(J)	;NO, IS KON BUSY?
	JRST	SWPS14		;NO, KON IDLE
	JRST	SWPSC5		;YES, KON BUSY
SWPS13:	TRNE	P3,-1		;TRANSFERS WAITING?
	JUMPL	P3,SWPSC5	;YES, IGNORE IF BEING FAIR
SWPS14:
IFN FTDUAL,<
	HRRZ	T2,UNICHN##(U)	;FILE TRANSFERS WAITING ON THIS CHAN?
	SKIPE	CHNQUE##(T2)
	SKIPN	T2,UNI2ND##(U)	;AND DUAL PORTED?
	JRST	SWPS16		;NO, OK LET IT HAPPEN
	HRR	J,UNIKON##(T2)	;YES, IS OTHER KON BUSY?
	CAME	J,-1(P)
	SKIPL	KONBSY##(J)
	JRST	SWPS15		;IDLE, LET SWAP HAPPEN
	HRRZ	T2,KONCUA##(J)	;BUSY, IS IT DOING A SWAP?
	MOVE	T3,UNISTS##(T2)
	HRRZ	T4,UNICDA##(T2)
	CAIN	T3,TCOD##
	CAIE	T4,SWPDDB##
	JRST	SWPS15		;NOT A SWAP
	JRST	SWPSC5		;DON'T TIE UP BOTH CHANNELS DOING SWAPS
				;LEAVE ONE CHANNEL OPEN FOR FILE I/O
SWPS15:	HRR	J,UNIKON##(U)	;GET KON BACK
SWPS16:>
IFN FTMP,<
	MOVE	T2,KONCAM##(J)
	TDNE	T2,.CPBIT##
>
	PUSHJ	P,@KONLTM##(J)	;COMPUTE TIME TO BLOCK
	  MOVEI	T1,0		;UNIT OFF-LINE
	SKIPE	T2,UNISTS##(U)	;IS UNIT IDLE
	CAIN	T2,PWCOD##	; OR POSITION WAIT?
	JRST	SWPS17		;YES, THIS IS THE BEST TIME TO BLOCK
	CAIN	T2,TWCOD##	;NO, TRANSFER WAIT?
	CAMLE	T1,UNISWD##(U)	;YES, IS THIS BEST?
	JRST	SWPSC5		;NO, TEST NEXT ENTRY
SWPS17:	MOVEI	T2,TWCOD##	;SET UNIT TO TRANSFER WAIT
	PUSHJ	P,SETUDB	;SET UNISWA, ETC.
IFN FTCIDSK,<
	SKIPL	KONMX##(J)	;MULTIPLE XFERS?
	JRST	SWPSC5		;NO
	PUSHJ	P,SWPCH		;PARANOIA
	  JRST	SWPSC5
	SKIPL	KONCNA##(J)	;CREDITS?
	PUSHJ	P,THSBLK	;YES, GET BLOCK NUMBER
	  JRST	SWPSC5		;MEMTAB MESSED UP
IFN FTMP,<
	PUSHJ	P,SWPCD		;ON THIS CPU?
	  JRST	SWPS18		;NO, SET SO DSKTIC WILL FIND IT
>
	MOVEM	T1,DEVBLK##(F)	;SAVE IN SWPDDB
	MOVEM	U,DEVUNI##(F)	;SAVE U IN DDB
	PUSH	P,P1		;SAVE OUR PLACE
	MOVE	P1,KONCHN##(J)	;CHAN LOC
	PUSHJ	P,STRTIO##	;GO CRANK UP THE IO
	POP	P,P1		;WHERE WERE WE?
	DSKOFF			;STRTIO ENABLED INTERRUPTS
IFN FTMP,<
	JRST	SWPSC5
SWPS18:	HRRZM	J,.C0SWP##(T4)	;WAKE UP THE OTHER CPU
>
>; END IFN FTCIDSK
IFE FTCIDSK,<
	JRST	SWPSC5		;JUST RESCAN IF NO CI DISKS
>
IFN FTMP,<
SWPS20: SETOM	.C0SWP##(T4)	;COME BACK AT DSKTIC
>
SWPS21:	PUSHJ	P,IDLEPW##	;SET IDLE OR PW
	JRST	SWPS26
;HERE WHEN ALL OF SWPLST HAS BEEN SCANNED. START SEEKS GOING
SWPS22:	MOVEI	P2,0		;START AT 1ST UNIT IN ASL
SWPS23:	SKIPG	U,SWPTAB##(P2)	;GET UNIT
	JRST	SWPS26		;NOT THERE, TRY NEXT
	DSKOFF			;NO DISK INTERRUPTS HERE
	MOVE	T1,UNISTS##(U)	;STATE OF UNIT
	MOVE	T2,UNISWD##(U)	;DISTANCE OR TIME OF BEST
	CAME	T2,[377777,,-1];IS ANYTHING THERE?
	CAIE	T1,PWCOD##	;YES, POSITION WAIT?
	JRST	SWPS26		;NO
	HRR	J,UNIKON##(U)	;KONTROLLER
IFN FTCIDSK,<
	SKIPGE	KONMX##(J)	;IF IT'S PW, WE DIDN'T PUT IT THERE
	JRST	SWPS26
>
IFN FTDUAL,<
	CAME	J,-1(P)		;IGNORE BUSY IF INT ON THIS KON
	SKIPL	KONBSY##(J)	;KON BUSY?
	JRST	SWPS24		;IDLE, USE PRIME PORT
	SKIPN	T3,UNI2ND##(U)	;BUSY, DUAL-PORTED DRIVE?
	JRST	SWPS24		;NO, WAIT FOR PRIME PORT
	HRR	J,UNIKON##(T3)	;YES, GET 2ND KON
	CAME	J,-1(P)		;IGNORE BUSY IF INT ON THIS KON
	SKIPL	KONBSY##(J)	;KON BUSY?
	HRRZ	U,UNI2ND##(U)	;IDLE, USE 2ND PORT
	HRR	J,UNIKON##(U)	;GET (NEW) KON
SWPS24:>
	MOVE	P1,UNISWA##(U)	;GET SWPLST ENTRY
IFN FTMP,<
	PUSHJ	P,SWPCD		;UNIT ON THIS CPU?
	  JRST	SWPS20		;NO,COME BACK AT DSKTIC
>
	CAMN	J,-1(P)		;ON INTERRUPT LEVEL?
	JRST	SWPS25		;YES, KONTROLLER ISNT REALLY BUSY
	MOVSI	T2,KOPPWX##
	AND	T2,KONPOS##(J)	;T2 NON-0 IF KON CAN START SEEKS WHILE BUSY
	SKIPE	DIADSK##	;IF DIAG IS GOING
	SETZ	T2,		;REQUIRE KDB TO BE IDLE

	SKIPGE	KONBSY##(J)	;IS KONTROLLER NOW BUSY?
	JUMPE	T2,SWPS21	;YES, TOUGH LUCK
SWPS25:
IFN FTDUAL,<
	HRL	P1,U		;SAVE U (2ND PORT OF DUAL-PORTED DRIVE)
>
	PUSHJ	P,THSBLK	;NO, GET BLOCK AND UNIT AGAIN
	  JRST	SWPS26		;MEMTAB IS MESSED UP
IFN FTDUAL,<
	HLRZ	U,P1		;RESTORE U
>
	MOVEM	T1,DEVBLK##(F)	;SAVE BLOCK IN SWPDDB FOR UNIPOS
	MOVEM	U,DEVUNI##(F)	;SAVE UNIT IN SWPDDB
	MOVE	P1,KONCHN##(J)	;IN CASE STRPOS CALLS STRTIO
IFN FTDUAL,<
	SKIPE	T1,UNI2ND##(U)	;TO DETECT SPURIOUS INTERRUPTS
	SETZM	UNICDA##(T1)
	SETOM	UNICDA##(U)	;SAVE F IN ACTIVE UDB
>
	PUSHJ	P,STRPOS##	;GO START THE SEEK
SWPS26:	CAIGE	P2,SWPMAX##	;AT END OF SWPTAB?
	AOJA	P2,SWPS23	;NO, TEST NEXT UNIT IN ASL

;HERE WHEN ALL UNITS HAVE BEEN STARTED SEEKING
	DSKON
	SKIPN	-1(P)		;INTERRUPT LEVEL?
	JRST	SWPPI1		;NO, START IO IF A UNIT IN TW
	PJRST	SWPPI6		;YES, EXIT HOLDING SCNLOK (SWPPIK
				;WILL BE CALLED SOON)

;ROUTINE TO SAVE STUFF IN UDB
;ENTER T1=UNISWD, T2=UNISTS, P1=UNISWA
SETUDB:	MOVEM	T1,UNISWD##(U)	;SAVE TIME
	MOVEM	P1,UNISWA##(U)	;SAVE SWPLST POSITION
IFN FTCIDSK,<
	SKIPL	KONMX##(J)
>
	MOVEM	T2,UNISTS##(U)	;SAVE PW/TW
IFN FTDUAL,<
	SKIPN	T3,UNI2ND##(U)	;IS THERE A SECOND PORT?
	POPJ	P,		;NO
	MOVEM	T1,UNISWD##(T3)	;YES, SET UP THAT UDB TOO
	MOVEM	P1,UNISWA##(T3)
IFN FTCIDSK,<
	SKIPL	KONMX##(J)
>
	MOVEM	T2,UNISTS##(T3)
>
	POPJ	P,
;SUBROUTINE TO SCAN ALL UNITS IN A.S.L., START TRANSFER ON BEST
SWPPIK::
IFN FTMP,<
	SKIPL	T1,SCNOWN##
	CAMN	T1,.CPCPN##	;IF THIS CPU "OWNS" SCNCNT
>
	SKIPE	SCNCNT##	;SOMEONE ALREADY HERE?
	POPJ	P,		;YES, WAIT A WHILE
	PUSHJ	P,SAVE2##	;SAVE P1,P2
	PUSH	P,J		;SAVE J
	PUSH	P,U
	HRRZ	P2,P1		;SAVE LOC OF CHAN DB
	MOVSI	T1,KOPBSY##	;SET EVERYTHING IDLE SO FILIO WILL DO SOMETHING
	ANDCAM	T1,KONBSY##(J)	; (THEY REALLY ARE, HAVEN'T YET BEEN MARKED)
SWPPI1:	SETZB	T1,P1		;INITIALIZE
	HRLOI	T2,377777	;INITIALIZE BEST-SO-FAR
	DSKOFF
SWPPI2:	SKIPG	U,SWPTAB##(T1)	;GET ASL ENTRY
	JRST	SWPPI4
	HRR	J,UNIKON##(U)	;SET KONTROLLER
IFN FTDUAL,<
	CAIE	P2,@KONCHN##(J)	;IGNORE BUSY IF INT ON THIS CHAN
	SKIPGE	@KONCHN##(J)	;CHAN BUSY?
	JRST	SWPPI3		;IDLE, USE PRIME PORT
	SKIPN	T3,UNI2ND##(U)	;BUSY, DUAL-PORTED DRIVE?
	JRST	SWPPI3		;NO, WAIT FOR PRIME PORT
	HRR	J,UNIKON##(T3)	;YES, GET 2ND KON
	CAIE	P2,@KONCHN##(J)	;IGNORE BUSY IF INT ON THIS CHAN
	SKIPGE	@KONCHN##(J)	;CHAN BUSY?
	HRRZ	U,UNI2ND##(U)	;IDLE, USE 2ND PORT
	HRR	J,UNIKON##(U)	;GET (NEW) KON
SWPPI3:>
IFN FTMP,<
	PUSHJ	P,SWPCP		;ON THIS CPU?
	  JRST	[SKIPLE .C0SWP##(T4) ;NO, ALREADY QUEUED AN IO ON OTHER CPU?
		 JRST SWPPI4	 ;YES, IGNORE THIS UNIT
		 JRST .+1]	 ;NO, GET 1 TO START ON OTHER CPU
>
IFE FTMP&FTDUAL,<
	MOVE	T3,UNISTS##(U)	;STATE OF UNIT
>
IFN FTMP&FTDUAL,<
	SKIPG	T3,SWPTAB##(T1)	;GET UNISTS FROM THE UNIT WHICH IS IN
	JRST	SWPPI4		;?
	MOVE	T3,UNISTS##(T3)	; SWPTAB (MIGHT HAVE CHANGED U TO 2ND PORT)
>
	CAIE	P2,@KONCHN##(J)
	SKIPGE	@KONCHN##(J)	;IS CHANNEL IDLE?
	CAIE	T3,TWCOD##	;YES, UNIT IN TRANSFER WAIT?
	JRST	SWPPI4		;NO, TEST NEXT UNIT
	CAMLE	T2,UNISWD##(U)	;YES, IS THIS BEST SO FAR?
	PUSHJ	P,SWPCH		;YES, CAN WE DO THIS IO (FRAG'D LIST)?
	  JRST	SWPPI4		;NO, TRY NEXT
	MOVE	T2,UNISWD##(U)	;YES, SAVE TIME
	MOVE	P1,UNISWA##(U)	;SAVE SWPLST POSITION
IFN FTDUAL,<
	HRL	P1,U		;SAVE ACTUAL UNIT
>
SWPPI4:	CAIGE	T1,SWPMAX##	;END OF ASL?
	AOJA	T1,SWPPI2	;NO, TRY NEXT UNIT
	JUMPE	P1,SWPPI5	;RETURN IF NOTHING TO DO

;HERE WHEN BEST TRANSFER HAS BEEN FOUND FOR SOME CHANNEL
	PUSHJ	P,THSBLK	;GET BLOCK NUMBER
	  JRST	[SETZM (P1)	;MEMTAB MESSED UP
		 JRST SWPPI5]
IFN FTDUAL,<
	HLRZ	U,P1		;GET ACTUAL UNIT BACK
>
	HRR	J,UNIKON##(U)	;SET UP KONTROLLER
IFN FTMP,<
	PUSHJ	P,SWPCD		;ON THIS CPU?
	  JRST	SWPPI7		;NO, SET SO DSKTIC WILL FIND IT
>
	MOVEM	T1,DEVBLK##(F)	;SAVE IN SWPDDB
	MOVEM	U,DEVUNI##(F)	;SAVE U IN DDB
	MOVE	P1,KONCHN##(J)	;AND CHAN LOC
	SKIPGE	KONBSY##(J)	;IS KONTROLLER BUSY?
	JRST	SWPPI8		;YES, CANT USE IT NOW
	SETZM	(P1)		;CHAN IS BUSY
IFN FTDUAL,<
	SKIPE	T1,UNI2ND##(U)
	SETZM	UNICDA##(T1)
	SETOM	UNICDA##(U)
>
	PUSHJ	P,STRTIO##	;GO CRANK UP THE IO
	CAIN	P2,@KONCHN##(J)	;YES, INT ON THIS CHAN?
	TLO	P2,-1		;YES, FLAG THAT CHAN IS NOW BUSY
	JRST	SWPPI1		;LOOK FOR OTHER CHAN
;HERE WHEN THROUGH
SWPPI5:	DSKON
	SOSL	SCNCNT##	;DECREMENT SCNCNT. DID INTERRUPT LEVEL TRY?
	JUMPGE	P2,SWPSC2	;YES, SCAN AGAIN (UNIT IN ASL MAY HAVE CHANGED STATE)
				;DON'T RETRY IF STARTED XFER. COULDN'T TELL
				;IF KON WAS REALLY BUSY.
	SKIPGE	P2		;STARTED XFER ON THIS CHAN?
	AOS	-2(P)		;YES, TELL FILSER SOMETHING HAPPENED
IFN FTCIDSK,<
;NOTE THAT FOR A CI TYPE DISK WE END UP LYING TO FILIO.
;WE TELL HIM THAT WE DIDN'T START A TRANSFER WHEN ACTUALLY WE DID.
;THIS WILL CONVINCE HIM THAT IT'S OK TO START A FILE.
>
IFN FTMP,<
	SETOM	SCNOWN##
>
	MOVE	J,-1(P)		;RESTORE J
	JUMPE	J,SWPPI6	;IF ON INTERRUPT LEVEL,
	MOVE	P1,KONCHN##(J)	;GET CHAN LOC
	SKIPGE	T1,CHNCFS##(P1);RESTORE CHNCFT IF IT COUNTED
	MOVE	T1,CHNIFS##(P1); BELOW 0
	MOVEM	T1,CHNCFS##(P1)

SWPPI6:	POP	P,U		;RESTORE U
	PJRST	JPOPJ##		;AND EXIT

IFN FTMP,<
SWPPI7:	HRRZM	J,.C0SWP##(T4)	;.CPSWP GREATER THAN 0
>
SWPPI8:	DSKON
	JRST	SWPPI1

;SUBROUTINE TO SEE IF A SWAP REQUEST MAY BE STARTED
;CALLED AT INTERRUPT LEVEL BY FILIO ON A POSITION-DONE INTERRUPT AND BY SWPPIK
;NON-SKIP RETURN IF CAN'T START IT NOW (FRAGMENTED, ANOTHER FRAGMENT IN PROGRESS)
;SKIP RETURN IF IO CAN BE STARTED
;PRESERVES T1,T2
SWPCH:
SWPCHK::HLRZ	T3,UNISWA##(U)	;FRAGMENTED?
	JUMPE	T3,CPOPJ1##	;NO, OK TO START
	HRL	T3,(T3)
	MOVSS	T3		;YES, GET START OF CHAIN
SWPCH1:	SKIPN	T4,(T3)		;GET FRAGMENT POINTER
	PJRST	CPOPJ1##	;DONE - OK TO START
	JUMPL	T4,[HRR T3,T4
		    JRST SWPCH1]
	TLNE	T4,(SL.IOP)	;IN PROGRESS?
	CAMN	T3,UNISWA##(U)	;YES, IS IT THE ONE WE WANT TO START?
	AOJA	T3,SWPCH1	;THIS ONE IS OK, CHECK NEXT
	POPJ	P,		;IO ALREADY HAPPENING - QUEUE THIS TILL LATER


IFN FTMP,<
;RETURNS CPOPJ IF ON OTHER CPU, CAUSES SWAP READ ERROR IF OTHER CPU IS DEAD
; RETURN CPOPJ1 IF ON THIS CPU
;PRESERVES T1,T2
SWPCD:	PUSHJ	P,SWPCP		;ON THIS CPU?
	  CAIA			;NO
	JRST	CPOPJ1##	;YES, ON THIS CPU RETURN
	MOVSI	T3,(CR.DET)	;CPU IS DETACHED BIT
	TDNN	T3,.C0RUN##(T4)	;CPU DETACHED?
	POPJ	P,
>
SWPCD1:	PUSHJ	P,SAVT##	;PRESERVE T1,T2 - SAVE T4
	PUSHJ	P,SW2LC		;INDEX INTO SW2LST
	MOVE	T4,SW2LST##(T1)	;ORIGINAL SWPLST ENTRY
	TLO	T4,(SL.ERR+SL.IOP) ;ERROR + I/O IN PROGRESS
	PJRST	DONE1		;SET IT DONE
IFN FTMP,<
;RETURNS CPOPJ IF ON OTHER CPU, CPOPJ1 IF ON THIS
;PRESERVES T1,T2
SWPCP:	MOVE	T4,KONCAM##(J)	;CPU THAT THIS KONTROLLER IS ON
	TDNE	T4,.CPBIT##	;THIS CPU?
	JRST	CPOPJ1##	;YES, DO THE I/O NOW
IFN FTDUAL,<
	SKIPN	T3,UNI2ND##(U)	;SECOND PORT
	JRST	SWPCP1
	HRRZ	T3,UNIKON##(T3)
	MOVE	T3,KONCAM##(T3)	;2ND PORT ON THIS CPU?
	TDNN	T3,.CPBIT##
	JRST	SWPCP1		;NO
	HRRZ	U,UNI2ND##(U)	;SWITCH TO OTHER PORT
	HRR	J,UNIKON##(U)	;KONTROLLER FOR SECOND PORT
	JRST	CPOPJ1##	;DO I/O NOW RETURN
SWPCP1:
>
	PUSH	P,T1		;SAVE AC
	PUSH	P,T2
	PUSHJ	P,GETCAM##	;BUILD COMBINED CPU BIT MASK
	PUSHJ	P,CAMCPU##	;PICK BEST CPU
	JRST	TTPOPJ##
> ;END IFN FTMP
;SUBROUTINE TO GET THE NEXT ENTRY FROM SWPLST
;CALL WITH P1=0 ON FIRST ENTRY
;SUBSEQUENTLY CALL WITH P1,P2=NUMBERS RETURNED FROM THE LAST CALL
;RETURNS CPOPJ IF NO MORE ENTRIES
;RETURNS CPOPJ1 WITH P1 POINTING AT NEXT ENTRY,
; U=LOC OF UNIT DATA BLOCK, T1=PHYSICAL DSK BLOCK NUMBER
NEXT:	JUMPN	P1,NEXT2	;FIRST CALL?
	PUSHJ	P,UUOLVL##	;IF AT UUO LEVEL
	  JRST	NEXT1		;AVOID CHANGING THE UBR
	HRRZ	P1,.USSLX	;BY FINDING ONLY THE ENTRY FOR THIS JOB
	MOVE	T1,SWPLST##(P1)
	TLNE	T1,(SL.IOP!SL.IOD) ;ALREADY STARTED?
	POPJ	P,		;YES, GO AWAY
	MOVEI	P2,1
	MOVEI	P1,SWPLST##-1(P1)
	JUMPL	T1,NEXT9	;IF FRAGMENTED
	JRST	NEXT7
NEXT1:	MOVEI	P1,SWPLST##-1	;YES, INITIALIZE P1
	SKIPN	P2,SPRCNT##	;ONLY LOOK AT SWAPS IF THERE ARE ANY
	SKIPA	P2,SLECNT##	;P2=NUMBER OF ENTRIES IN SWPLST
	ADD	P2,SWPCNT##
NEXT2:	JUMPE	P2,CPOPJ##	;NON-SKIP RETURN IF NO MORE ENTRIES
NEXT3:	SKIPA	T2,[TLNN T1,(SL.IOP)]	;ASSUME NOT A FRAGMENTED ENTRY
NEXT4:	MOVE	T2,[TLNE T1,(SL.IOP)]	;IF IT IS A FRAGMENTED ENTRY
NEXT5:	SKIPN	T1,1(P1)	;IS THERE ANOTHER ENTRY IN SWPLST?
	JRST	NEXT11		;MAYBE (IF WE'RE IN A FRAGMENT CHAIN)
	SKIPE	SPRCNT##	;IF ANY SWAPS WAITING,
	TLNE	T1,(SL.SIO)	;IF THIS IS A SWAP REQUEST
	SKIPA	T4,SINCNT##	; TEST IT
	JRST	NEXT11		;PAGE REQUEST - IGNORE FOR NOW
	JUMPL	T1,NEXT9	;SWAP, IS IT A FRAGMENT POINTER?
	JUMPE	P2,NEXT6	;IGNORE SINCNT IF FRAGMENT IN PROGRESS
	TLNE	T1,(SL.DIO)	;NO, IF AN OUTPUT REQUEST
	JUMPG	T4,NEXT11	; IGNORE IT IF ANY INPUT REQUESTS WAITING
NEXT6:	XCT	T2		;IS IT USABLE?(SL.IOP=0 IF NOT IN A FRAGMENT CHAIN
				; , SL.IOP=1 IF IN A FRAGMENT CHAIN)
	TLNE	T1,(SL.IOD)	; AND SL.IOD=0
	AOJA	P1,NEXT13	;NO, TRY NEXT
	TLZN	P1,400000	;FIRST TIME FOR THIS ENTRY?
	TLNN	P1,-1		;NO, IN A FRAGMENT?
	JRST	NEXT7		;NO, USE THIS ENTRY
	MOVSI	T4,(SL.MAP)	;MAP INFO IN THIS ENTRY BIT
	TLNN	T1,(SL.DIO)	;YES, SWAPPING OUT?
	JRST	NEXT7		;NO, ALL IS WELL
	SKIPE	2(P1)		;YES, LAST ENTRY IN THE FRAGMENT?
	TDNE	T4,1(P1)	;MAP INFO THIS ENTRY AS BAD AS LAST
	JRST	NEXT11		;YES, CANT USE IT (CANT WRITE UPMP TILL ALL
				; DSK ADDRESSES ARE FILLED IN)
NEXT7:
IFN FTMP&FTKL10,<
	TLNE	T1,(SL.DIO)	;IF PAGING OUT
	TLNE	T1,(SL.SIO)	;(NOT SWAPPING)
	JRST	NEXT8
	PUSHJ	P,SW2LC
	TLNE	P1,-1
	SUBI	T1,1
	HRR	J,SW3LST##+1(T1) ;GET JOB NUMBER
	PUSHJ	P,CHKCSH##	;IS IT RIGHT WRT CACHE?
	  SKIPA	T1,1(P1)	;YES, DO IT
	JRST	NEXT11		;NO, TRY SOMEONE ELSE
>
NEXT8:	PUSHJ	P,SWPMEM	;YES, GET DSK ADR, UNIT
	  JRST	NEXT11		;SWPLST IS ZERO!
	TLNN	P1,-1		;A MAIN ENTRY?
	SUBI	P2,1		;YES, DECREMENT NO OF ENTRIES LEFT
	AOJA	P1,CPOPJ1##	;AND SKIP-RETURN
;HERE ON A POINTER TO A FRAGMENT
NEXT9:	HLRZ	T3,P1		;T3 NON-0 IF NOT FIRST LINK (FATHER NOT SWPLST ENTRY)
	JUMPN	T3,NEXT10	;GO IF NOT FIRST TIME IN THIS FRAGMENT
	TLNE	T1,(SL.IOD!SL.IOP) ;I/O ALREADY STARTED OR DONE?
	JRST	NEXT11		;DONE, ERROR, OR ACTIVE, NEXT ENTRY
	MOVE	T4,T1		;SAVE THIS PIECE
	PUSHJ	P,SW2LC		;WE THINK WE FOUND A USABLE ENTRY
	MOVE	T4,SW2LST##+1(T1) ;TO SEE WHAT WE MUST LOOK FOR
	MOVE	T1,1(P1)	;OK - RESTORE SWPLST ENTRY TO T1
	MOVSI	P1,400001(P1)	;SAVE POINT IN SWPLST WHERE WE TURNED DOWN
	SOJL	P2,CPOPJ##	;COUNT THE ENTRY
NEXT10:	HRRI	P1,-1(T1)	;SET RH(P1) FOR 1ST THING IN CHAIN
	JUMPN	T3,NEXT5	;T2 SET UP RIGHT IF NOT 1ST LINK
	JUMPE	T4,NEXT3	;IF ANOTHER FRAG THIS ENTRY NOT
				;BEING DONE, ANY PART IS OK
	SETZ	P2,		;MAKE SURE THIS ENTRY IS PICKED
	JRST	NEXT4


;HERE WHEN AT END OF CHAIN
NEXT11:	JUMPLE	P2,CPOPJ##	;RETURN IF ALL ITEMS IN SWPLST SCANNED
	TLNN	P1,-1		;NOT END, IN A FRAGMENT?
	AOJA	P1,NEXT12	;NO, TRY NEXT SWPLST ENTRY
	HLRZS	P1		;RESTORE MAIN LOC IN SWPLST IF FRAGMENT
	TRZ	P1,400000	;BIT IS ON IF FOUND NOTHING
NEXT12:	CAIL	P1,SW2LST##	;REACHED END?
	POPJ	P,		;YES (DUAL CPU, OTHER CPU IN ZERSLE)
	JRST	NEXT3

NEXT13:	TLNE	P1,-1		;FRAGMENT?
	JRST	NEXT5		;YES, P2 ALREADY DECREMENTED
	SOJG	P2,NEXT5	;NO, DECR P2 AND TRY NEXT
	POPJ	P,		;ALL DONE - RETURN

;SUBROUTINE TO GET THE BLOCK NUMBER FOR THIS SWPLST ENTRY
;CALL P1= LOC OF ENTRY
;RETURNS CPOPJ1 WITH U SET UP, T1= BLOCK NUMBER
;RETURNS CPOPJ IF THINGS ARE MESSED UP
THSBLK:	SKIPN	T1,(P1)		;GET SWPLST ENTRY
	STOPCD	CPOPJ##,DEBUG,NSE,	;++NO SWPLST ENTRY
;	PJRST	SWPMEM		;FALL INTO SWPMEM

;SUBROUTINE TO OBTAIN THE DISK ADDRESS FROM MEMTAB
;ENTER WITH T1=SWPLST ENTRY
;RETURN CPOPJ1 WITH U, T1 SET TO DISK ADDRESS
;RETURNS CPOPJ IF THINGS ARE MESSED UP
SWPMEM:	SE1ENT			;MUST BE IN SECTION 1 TO REFERENCE MEMTAB
	LDB	T1,[POINT 13,T1,26]  ;GET PHYSICAL PAGE NUMBER
	SSX	T1,MS.MEM	;MEMTAB SECTION
	SKIPN	T1,MEMTAB(T1)	;GET DSK ADDRESS
	STOPCD	CPOPJ##,DEBUG,MIZ,	;++MEMTAB IS ZERO
SWPADR::LDB	U,SSUNT1##	;GET SWAP UNIT
	MOVE	U,SWPTAB##(U)
	JUMPL	U,CPOPJ1##	;UNIT GONE, BUT WE EXPECTED IT
	SKIPE	U
	SKIPG	UNISLB##(U)
	STOPCD	.,STOP,SBZ,	;++SWAP BLOCK ZERO
	LDB	T1,SLKNT1##	;1ST LOGICAL K
	ROT	T1,BLKSPP##	;CONVERT TO BLOCKS
	ADD	T1,UNISLB##(U)	;PLUS STARTING BLOCK NUMBER
	JRST	CPOPJ1##	;RETURN
;SUBROUTINE TO BE CALLED WHEN IO IS FINISHED. ENTERED AT INTERRUPT LEVEL FROM FILIO
;CALLED WITH P1=LOC OF SWPLST ENTRY, T1= NUMBER OF BLOCKS TRANSFERRED, P2=ERROR BITS
DONE::	MOVE	T2,(P1)		;SWPLST ENTRY
	MOVEI	T3,UNIICT##(U)	;SET CORRECT UNIT WORD TO
	TLNN	T2,(SL.IPC)	;IPC AND PAGE QUEUES ARE PAGING I/O
	TLNN	T2,(SL.SIO)	; COUNT THE IO (SWAP OR PAGE, IN OR OUT)
	MOVEI	T3,UNIPCI##(U)
	TLNE	S,IO
	ADDI	T3,1
	ADDM	T1,(T3)
	PUSHJ	P,SW2LC		;GET INDEX INTO 2ND TABLE
	MOVE	T4,SW2LST##(T1)	;ORIGINAL SWPLST ENTRY
	TRNE	S,IODERR+IODTER+IOIMPM	;IO ERROR?
	TLO	T4,(SL.ERR)	;YES, SET BIT
	TRNE	P2,IOCHMP+IOCHNX ;CHAN-DETECTED ERROR?
	TLO	T4,(SL.CHN)	;YES
DONE1:	HLRZ	T3,P1		;ORIGINAL SWPLST ENTRY IF A FRAGMENT
	LDB	T2,[POINT 9,(P1),35] ;NUMBER OF PAGES LEFT IN ENTRY
	TLNN	T4,(SL.ERR+SL.CHN) ;ABORT IF ERROR
	JUMPN	T2,DONE7
	JUMPE	T3,DONE2	;NO, GO IF NOT IN A FRAGMENT
	MOVSI	T2,(SL.IOP)	;IN A FRAGMENT - CLEAR THE IOP BIT
	ANDCAM	T2,(T3)		; IN THE SWPLST ENTRY ITSELF
DONE2:	TLZE	T4,(SL.IOP)	;CLEAR IN-PROGRESS BIT
	TLOE	T4,(SL.IOD+SL.DFM) ;SET DONE BIT
	STOPCD	.+1,DEBUG,SBW,	;++SWPLST BITS WRONG
	SETZM	SW2LST##(T1)
	MOVEM	T4,(P1)		;SAVE UPDATED SWPLST ENTRY
	TLNE	T4,(SL.CHN+SL.ERR)
	HRRZM	U,SWPEUJ##	;SAVE ADR OF UNIT
	JUMPE	T3,DONE6	;FINISHED IF NONE
	TLNE	T4,(SL.ERR+SL.CHN)  ;IF AN ERROR,
	JRST	DONE5		; DONT WASTE TIME BY CONTINUING
	SKIPL	T1,(T3)		;GET ADR OF START OF CHAIN
	STOPCD	DONE5,DEBUG,NRF,	;++SWPLST NOT REALLY FRAGMENTED
DONE3:	SKIPN	T2,(T1)		;LOOK AT NEXT ENTRY IN FRAGMENT CHAIN
	JRST	DONE5		;WHOLE CHAIN DONE IF AT END
	JUMPGE	T2,DONE4	;CHECK IT IF NOT ANOTHER POINTER
	MOVE	T1,T2		;GO TO NEXT PART OF CHAIN
	JRST	DONE3
DONE4:	TLNN	T2,(SL.IOD)	;THIS FRAGMENT DONE?
	POPJ	P,		;NO, DONT WAKE UP SWAPPER
	AOJA	T1,DONE3	;YES, TEST NEXT ENTRY OF FRAGMENT CHAIN
;HERE IF THE ENTIRE SWPLST FRAGMENT CHAIN IS FINISHED
DONE5:	HLLZS	T4		;INDICATE DONE, ERROR BIT IF ON
	IORB	T4,(T3)
	HLRZS	P1		;POINT TO SWPLST
DONE6:	HRLOI	T1,377777	;SET UNISWD HUGE
IFN FTDUAL,<
	SKIPE	T2,UNI2ND##(U)
	MOVEM	T1,UNISWD##(T2)
>
	MOVEM	T1,UNISWD##(U)
	TLNE	T4,(SL.IPC)	;IF IPC PAGING
	TLNN	T4,(SL.DIO)	; OUT
	JRST	SWPINT##
	TLNN	T4,(SL.SIO)	;DON'T IF REALLY SWAPPING REPLACABLE QUEUE
	AOS	IPCCNT##	;BUMP A COUNT
	PJRST	SWPINT##	;KICK THE SWAPPER
;HERE IF THE SWPLST ENTRY WASN'T ENTIRELY PROCESSED
DONE7:	SKIPN	T3		;IN A FRAGMENT?
	MOVEI	T3,(P1)		;NO, PICK UP ADDR OF ENTRY
	MOVSI	T1,(SL.IOP)	;CLEAR THE IO IN PROGRESS BIT
	ANDCAM	T1,(T3)		; SO NEXT WILL CONSIDER THIS ENTRY AGAIN
	POPJ	P,		;AND RETURN
;SUBROUTINE TO SET UP AN IOLIST FOR A SWAP/PAGE OPERATION
;ENTER P1=LOC OF SWPLST ENTRY, T3= MAX NUMBER OF BLOCKS TO DO
;EXIT P3=LOC OF 1ST IOWD, T1= NUMBER OF BLOCKS IN LIST
THIS::	PUSHJ	P,GETSWJ	;GET JOB NUMBER
	  PUSHJ	P,SVEUB##	;SAVE MAPPING IN EFFECT
	PUSHJ	P,SAVE4##	;SAVE P'S
	LSH	T3,-2		;CONVERT BLOCKS TO PAGES
	MOVE	P4,(P1)		;GET SWPLST ENTRY
	LDB	P2,[POINT SL.SCT,P4,<^L<SL.CNT>+SL.SCT-1>] ;NUMBER OF PAGES REQUIRED
	MOVE	T2,P2
	CAMLE	P2,T3		;NEED MORE THAN WE CAN DO?
	MOVE	P2,T3		;YES, SETTLE FOR LESSER AMOUNT
	SUB	T2,P2		;AMOUNT LEFT AFTER THIS OPERATION FINISHED
	SKIPN	P2		;REALLY ANYTHING TO DO?
	STOPCD	.,STOP,ZPS,	;++ZERO PAGE SWAP
	PUSH	P,T2
	TLO	P4,(SL.IOP)	;INDICATE IO IN PROGRESS
	PUSHJ	P,SW2LC
	SKIPN	SW2LST##(T1)	;IF THE FIRST TIME,
	MOVEM	P4,SW2LST##(T1)	; SAVE THE SWPLST ENTRY IN DUPLICATE TABLE
	DPB	T2,[POINT SL.SCT,P4,<^L<SL.CNT>+SL.SCT-1>] ;UPDATE SWPLST ENTRY WITH NEW COUNT
	MOVEM	P4,(P1)
	MOVE	T2,P2		;NUMBER OF PAGES WE'LL DO
	LSH	T2,2		;CONVERT TO BLOCKS
	EXCH	T2,(P)
	PUSH	P,T2		;SAVE (RETURN NUMBER TO FILIO)
	DSKOFF			;PROTECT AGAINST S BEING CHANGED AT
				; INTERRUPT LEVEL
	TLNE	P4,(SL.DIO)	;INPUT OR OUTPUT?
	TLOA	S,IO		;OUTPUT
	TLZ	S,IO		;INPUT
	TRZ	S,IODTER+IODERR+IOIMPM
	MOVEM	S,DEVIOS(F)	;SAVE UPDATED S
IFN FTKL10,<
	SETZ	P1,		;WE NEED AN IOWD
	PUSHJ	P,GCH4WD##	;GET A LOW-CORE BLOCK FOR THE IOLIST
	  JRST	THIS21		;CANT GET ONE!
	MOVE	P1,T1		;ADR IN P1
	HRLI	P1,-3		;CAN SAVE 3 IOWD'S WITH NO HASSLE
	MOVEM	T1,-5(P)	;RETURN INITIAL IOWD LOC IN P2

>
	PUSH	P,U		;SAVE U
IFN FTKS10,<
	MOVE	P1,CHNIMR##(P3)	;GET ADDRESS OF INITIAL MAPPING REGISTER
	MOVEI	T4,UBAEXP	;GET INITIAL MAPPING REGISTER ADDRESS
	SUBM	P1,T4		;DISPLACEMENT OF THIS DEVICES FIRST
	IMULI	T4,UBAMUL	;MAKE INTO AN ELEVEN STYLE ADDRESS
	MOVEM	T4,CHNIEA##(P3)	;AND SAVE AS INITIAL ELEVEN ADDRESS
	HLL	P1,CHNUBA##(P3)	;GET UBA NUMBER
	PUSH	P,CHNMRC##(P3)	;SAVE FOR LATER
>
	MOVEI	U,0		;U=0 FOR 18-BIT CHAN
IFN FTKL10,<
	SKIPGE	T2,CHB22B##(P3)
	MOVEI	U,1		;22-BIT CHAN
	TLNE	T2,CP.RH2##!CP.KLP##	;RH20 OR KLIPA?
	MOVEI	U,2		;YES
>
	TLNN	P4,(SL.SIO)	;SWAPPING?
	TLO	U,1		;NO, PAGING, REMEMBER THAT (SEC # FROM MEMTAB)
IFN FTIPCF,<
	TLNN	P4,(SL.IPC)	;IPCF?
>
	CAILE	J,JOBMAX##	;HIGH SEG?
	TLOA	U,400000	;INDICATE BY U NEGATIVE
	PUSHJ	P,[EXCH	P1,-10(P)
		   PUSHJ P,GETVPN
		   EXCH	P1,-10(P)
		   POPJ	P,	]
	LDB	P4,[POINT 13,P4,26]	;PHYSICAL PAGE TO START IO AT
	JUMPL	U,THIS3		;PROCEED IF HIGH SEG, IPCF, OR PAGING QUEUE
	HRRZ	T2,JBTUPM##(J)	;SWAPPING THE UPMP?
	CAIE	T2,(P4)
	JRST	THIS4		;NO
	TLO	U,500000	;YES, NO NEXT PAGE+"THIS IS UPT" BIT
	TLNN	S,IO		;INPUT?
	TLO	U,200000	;YES, FLAG INPUT OF UPT
	SKIPGE	U
THIS3:	SETO	P3,		;BE SURE NON-ZERO AT THIS15 IF WE WANT IT TO BE

;HERE WITH P4=PAGE NUMBER OF THE BEGINNING PAGE OF THE IO
;AND P3=POINTER TO VIRTUAL SLOT IF SWAPOUT
THIS4:
IFN FTKL10,<
	MOVE	T2,[400000+T1,,1] ;SET TO COUNT CONTIGUOUS PAGES (IOWD FOR KL)
>
IFN FTKS10,<
	POP	P,T2		;RESTORE SAVED UMR COUNT
>
THIS5:
IFN FTXMON,<
	XJRST	 [MCSEC1+.+1]	;ENTER SECTION 1 FOR PAGTAB/MEMTAB REFERENCES
>
	MOVE	T1,P4		;STARTING PAGE
THIS6:	SSX	P4,MS.MEM	;SET SECTION INDEX FOR PAGTAB/MEMTAB
	SKIPGE	MEMTAB(P4)	;IS THIS LAST PAGE IN FRAGMENT?
	SOJN	P2,@[0,,THIS27]	;YES, SOMEONE IS CONFUSED IF NOT LAST PAGE
	JUMPL	U,THIS10	;GO IF HI-SEG
	LDB	T4,[POINT MT.SAD,MEMTAB(P4),<^L<MT.DAD>+MT.SAD-1>] ;DSK ADR OF THIS PAGE
	TLNN	S,IO		;IF SWAPPING OUT,
	JRST	THIS7		;NO, DON'T CHANGE PAGE MAP
	MOVE	T3,(P3)		;GET CURRENT MAP CONTENTS
	AND	T3,[PM.BTS-PM.COR]	;SAVE THE BITS
	IOR	T4,T3
	MOVEM	T4,(P3)		;SAVE ADDR IN UPMP
THIS7:	JUMPE	P2,THIS11	;GO IF LAST PAGE
	LDB	T4,NVPNP4			;GET NEXT VIRTUAL PAGE #
	CAIN	T4,.UPMVP/PAGSIZ## ;UPMP?
	TLO	U,500000	;YES, DONT WIPE OUT SLOT IN MAP
THIS9:	TLNN	S,IO		;INPUT?
	TLZ	U,400000	;YES, STOP ON COUNT EXHAUSTED
	PUSHJ	P,GMPTR		;GET POINTER
	MOVE	P4,(T4)		;GET POINTER
	TLZ	P4,(PM.NAD)	;CLEAR BITS
	SKIPA	P3,T4		;POINTER FOR NEXT DEPOSIT
THIS10:	HRRZ	P4,PAGTAB(P4)	;HI-SEG GETS NEXT PAGE FROM PAGTAB
IFN FTKL10,<
	HRRZ	T4,U		;LEFT HALF = 0 FOR INDEXING
	CAIN	P4,@T2		;PAGES CONTIGUOUS?
	CAML	T2,MAXCNT(T4)	;YES, BELOW MAX SIZE OF AN IOWD?
	SOJA	P2,THIS11	;NO, MAKE THE IOWD
	CAMN	T2,[400000+T1,,27] ;DON'T BUILD AN IOWD
	SOJA	P2,THIS11	; THAT LOOKS LIKE AN RH20 GOTO WORD
	SOJLE	P2,THIS11	;MAKE THE IOWD IF DONE
	AOJA	T2,THIS6	;PAGE IN IOWD - COUNT AND TRY NEXT PAGE
>
;HERE TO FINISH THE IOWD/MAPPING REGISTER
THIS11:
IFN FTXMON,<
	JRST	@[0,,.+1]	;RETURN TO SECTION 0
>
IFN FTKL10,<
	IMULI	T2,-1000	;COMPUTE WORDCOUNT
	LSH	T1,P2WLSH##	;CONVERT BEGINNING PAGE NUMBER TO ADDRESS
	TRNN	U,2		;IF NOT AN RH20,
	SOJA	T1,THIS12	; ADDR-1
	MOVNS	T2		;RH20 - ADDR RATHER THAN ADR-1
	TRO	T2,RH2TRX##	; +WDCNT, TRA BIT
THIS12:	DPB	T2,PNTCNT(U)
	MOVEM	T1,(P1)		;SAVE IOWD IN LOCORE BLOCK
	ADDI	T1,1		;ADDRESS OF 1ST WORD TO TRANSFER
	AND	T1,MSK22B##(U)	;GET ADDRESS
	JUMPLE	P2,THIS13	;DONE IF P2=0
	AOBJN	P1,THIS4	;MORE TO DO, GET NEXT CHUNK
	TRNE	U,2		;IF AN RH20
	TRO	U,4		; TELL KLSER
	PUSHJ	P,GETMOR##	;NEED ANOTHER 4-WORD BLOCK
	  JRST	THIS20		;NONE AVAILABLE
	TRZ	U,4		;CLEAR KLSER'S RH20-BIT
	JRST	THIS4		;GOT IT, DO NEXT CHUNK
>; END FTKL10

IFN FTKS10,<
	TRO	T1,UNB36B!UNBVBT ;SET 36 BIT MODE AND VALID BIT
	WRIO	T1,(P1)		;LOAD THE MAPPING REGISTER
	ADDI	P1,1		;POINT TO NEXT MAPPING REGISTER
	SOJLE	P2,THIS13	;DONE IF P2=0
	SOJG	T2,THIS5	;IF UMR AVAILABLE, CONTINUE
	MOVE	T2,-7(P)	;OOPS, RAN OUT OF MAPPING REGISTERS
	ADDM	P2,(T2)		;INCREASE # PAGES LEFT TO DO
	LSH	P2,2
	ADDM	P2,-1(P)	;UPDATE # BLOCKS LEFT AFTER THIS IO
	MOVNS	P2
	ADDM	P2,-2(P)	;UPDATE # BLOCKS DONE THIS OPERATION
	SOJA	P1,THIS13	;FINISH UP, START THE IO
>; END FTKS10


;HERE WHEN ALL REQUIRED IOWDS HAVE BEEN BUILT
THIS13:
IFN FTKL10,<
	SETZM	1(P1)		;TERMINATE THE LIST
	AOS	T3,P1		;POINT AT TERMINATION WORD
	TRNN	U,2		;RH20?
	JRST	THIS14		;NO
	MOVSI	T1,RH2LST##	;YES, INDICATE LAST IOWD
	IORB	T1,-1(P1)
	LDB	T2,[POINT 11,T1,13]	;WORDCOUNT
	TLZ	T1,077760	;CLEAR WDCNT FIELD
	ADD	T1,T2		;ADDR + N
	JRST	THIS15		;STORE IN CHNTCW AND CONTINUE
THIS14:	HLRO	T1,-1(P1)	;WORD COUNT OF LAST IOWD
	LDB	T2,ADRPT2##(U)	;WHERE DATA ASSOCIATED WITH LAST IOWD
	ASH	T1,@ASH22B##(U)	; WILL START
	SUBM	T2,T1		;WHERE LAST DATA ITEM WILL GO
	DPB	P1,ADRPT4##(U)	;WHERE TERMINATION WORD WILL BE FETCHED FROM
THIS15:	MOVE	T2,-5(P)	;ADDRESS OF THE CHANNEL DATA BLOCK
	MOVEM	T1,CHNTCW##(T2)	;STORE WHAT CHANNEL TERMINATION WORD SHOULD LOOK LIKE
>
IFN FTKS10,<
	MOVE	T2,-5(P)	;ADDRESS OF THE CHANNEL DATA BLOCK
	HLL	P1,CHNIMR##(T2)	;RESTORE LEFT HALF
	SUB	P1,CHNIMR##(T2)	;GET NUMBER OF MAPPING REGISTERS USED
	LSH	P1,P2WLSH##+2	;MAKE INTO WORDS, 11 STYLE
	MOVEM	P1,CHNTCW##(T2)	;STORE ADDRESS OF LAST WORD TO BE TRANSFERRED
	SUB	P1,CHNIEA##(T2)	;COMPUTE TOTAL NUMBER OF BYTES IN TRANSFER
	MOVEM	P1,CHNBTC##(T2)	; AND PUT IT HERE
>
	SKIPN	-1(P)		;WILL THIS BE THE LAST IO?
	SETZB	P3,P4		;YES
	TLNE	U,200000	;UPMP SWAP IN?
	JRST	THIS16		;YES
	MOVE	P1,-7(P)	;ORIGINAL SWPLST ENTRY ADDRESS
	MOVSI	T1,(SL.IPC)	;PAGING QUEUE BIT
	TLNE	S,IO		;INPUT?
	TDNE	T1,(P1)		;NO, PAGING QUEUE
	JRST	THIS16		;NO
	CAIG	J,JOBMAX##	;JOB?
	PUSHJ	P,SETVPN	;YES, SET VPN IN .USLPS OR TABLE
THIS16:	JUMPE	P3,THIS18	;GO IF NO MORE TO DO
	DPB	P4,[POINT SL.SPN,(P1),<^L<SL.PPN>+SL.SPN-1>]
	JUMPL	J,THIS17	;IF SWAPPING "IP" QUEUE
	TLNN	U,200000	;OR THIS IS UPMP SWAP IN
	CAILE	J,JOBMAX##	; OR A HIGH SEGMENT,
	JRST	THIS17		;DON'T STORE SINCE A RANDOM JOB IS ADDRESSABLE
THIS17:	HLRZS	P1		;SWPLST ENTRY FOR MAIN WORD
	JUMPE	P1,THIS18	;GO IF NOT IN A FRAGMENT
	MOVSI	T1,(SL.IOP)
	IORM	T1,(P1)		;INDICATE IN A FRAGMENT FOR NEXT
				; (SO WILL ONLY PICK THIS PIECE)
THIS18:	TLNE	S,IO		;IN OR OUT?
	TLZN	U,100000	;;OUT, JUST SWAP THE UPT?
	JRST	THIS19		;NO
	SKIPE	.USLPS		;DID WE STOP JUST BEFORE THE UPT?
	JRST	THIS19		;YES, WE'LL GET IT NEXT TIME THEN
	MOVE	T1,NLUPMP##+.UMUPT	;YES,GET POINTER TO NULL UPMP
	MOVEM	T1,.UPMP+.UMUPT
THIS19:	POP	P,U		;RESTORE U
	POP	P,(P)		;FIX STACK
	POP	P,T1		;NO OF BLOCKS TO TRANSFER
	SKIPE	DINITF##
	POPJ	P,
	TLNN	S,IO
	ADDM	T1,.CPSBI##	;COUNT TOTAL SWAPPING BLOCKS
	TLNE	S,IO
	ADDM	T1,.CPSBO##
	POPJ	P,		;AND RETURN
;HERE IF WE RAN OUT OF LOWER CORE BLOCKS
THIS20:	HRRZ	T1,-7(P)
	ADDM	P2,(T1)		;INCREASE # PAGES LEFT TO DO
	LSH	P2,2
	ADDM	P2,-1(P)	;UPDATE # BLOCKS LEFT AFTER THIS IO
	MOVNS	P2
	ADDM	P2,-2(P)	;UPDATE # BLOCKS DONE THIS OPERATION
	SOJA	P1,THIS13	;FINISH UP, START THE IO

;HERE IF THERE WASNT A LOWER-CORE BLOCK TO START AT
THIS21:	ADD	P4,P2		;MAKE P4 THE SWPLST ENTRY AGAIN
	TLZ	P4,(SL.IOP)
	HRRZ	P1,-6(P)	;RESET P1
	MOVEM	P4,(P1)		;SAVE BACK IN SWPLST
	POP	P,(P)		;CLEAR GARBAGE OFF LIST
	SETZB	T1,-4(P)	;INDICATE 0 BLOCKS TRANSFERRED
	PJRST	T2POPJ##	;RETURN TO FILIO

;HERE ON VARIOUS ERRORS

THIS27:	STOPCD	THIS13,DEBUG,IPM, ;++ILLEGAL POINTER IN MEMTAB
;SUBROUTINES TO MANIPULATE VIRTUAL PAGE NUMBERS FOR SWAPOUT

;GET/SETVPN:
;		MOVE	P1,SWPLST-POINTER	;SWPLST,,FRAG OR 0,,SWPLST
;		PUSHJ	P,{GET|SET}VPN		;CALL ROUTINE
;		<ONLY RETURN, FOR GETVPN - P3 CONTAINS VIRTUAL POINTER>

GETVPN:	MOVE	P3,.USLPS	;NO, GET POINTER FROM CURRENT UPT
	TLNN	P1,-1		;FRAGMENTED SWPLST ENTRY?
	POPJ	P,		;NO, ALL IS WELL
	PUSHJ	P,FRGIDX	;GET FRAGMENT INDEX
	ADDI	P3,(T1)		;POINTER INTO TABLE
	MOVE	P3,(P3)		;AND ENTRY
	POPJ	P,

SETVPN:	MOVEI	T2,.USLPS	;NO, STORE FOR CURRENT JOB
	TLNN	P1,-1		;FRAGMENTED?
	JRST	SETVP5		;NO
	PUSHJ	P,FRGIDX	;COMPUTE A FRAGMENT INDEX
	MOVE	T2,(T2)		;RELOCATE
	ADD	T2,T1		;PLACE TO STORE
SETVP5:	MOVEM	P3,(T2)		;STORE
	POPJ	P,

;ROUTINE TO COMPUTE FRAGMENT TABLE OFFSET
;CALL WITH P1=SWPLST POINTER (SW2LC FORMAT)
;EXIT WITH T1=OFFSET INTO FRAGMENT VIRTUAL TABLE

FRGIDX:	PUSH	P,T2		;GET AN AC
	SETZ	T1,		;COUNTER
	HLRZ	T2,P1		;GET START OF CHAIN
	HRRZ	T2,(T2)		;FIRST FRAGMENT
FRGID2:	CAIN	T2,(P1)		;IS THIS THE RIGHT ENTRY?
	JRST	T2POPJ##	;YES, RETURN
	AOS	T1		;NO, SET FOR NEXT
	SKIPL	1(T2)		;POINTER TO NEXT
	AOJA	T2,FRGID2	;NO
	HRRZ	T2,1(T2)	;GET NEXT BLOCK
	JRST	FRGID2		;START LOOKING THERE

;SUBROUTINE TO GET SUFFICIENT FREE CORE TO BUILD THE VPN TABLE
;CALL:
;	MOVE	T1,FRAGMENT TABLE
;	PUSHJ	P,BLDVPN
;	  <NOT ENOUGH FREE CORE>
;	<NORMAL RETURN, POINTER TO TABLE IN T2>

BLDVPN::
	SETZ	T2,		;T2 COUNTS WORDS
	JUMPGE	T1,CPOPJ1##	;NOT REALLY FRAG'D?
	HRRZS	T1
BLDVP1:	SKIPN	T3,(T1)		;DONE?
	JRST	BLDVP4		;YES
	JUMPL	T3,[HRRZ T1,T3	;POINTER TO ANOTHER ENTRY
		    JRST BLDVP1] ;LOOP BACK
	AOS	T2		;ONE MORE WORD WE NEED
	AOJA	T1,BLDVP1	;LOOP

BLDVP4:	AOS	T2		;ONE MORE WORD FOR COUNT
	PUSH	P,T2		;SAVE # OF WORDS
	PUSHJ	P,GETWDS##	;GET THE CORE
	  JRST	T2POPJ##	;NOT THERE
	POP	P,(T1)		;REMEMBER THE COUNT
	AOS	T2,T1		;AND RETURN +1 AS THE ADDRESS
	MOVE	T1,-1(T2)	;GET COUNT AGAIN
	TRZE	T1,3		;ROUND TO NEAREST MULTIPLE OF 4
	ADDI	T1,4		;TO ENSURE LINK WORD IS ZERO IN CASE
	ADDI	T1,-1(T2)	;THIS EVER GETS USED FOR SWPLST ENTRY
	SETZM	-1(T1)		;ZAP IT
	JRST	CPOPJ1##	;GOOD RETURN

;SUBROUTINE TO RETURN THE VPN TABLE POINTED TO BY C(T1)

RTNVPN::
	MOVE	T1,-1(T2)	;GET COUNT
	SOS	T2		;ADDRESS
	PJRST	GIVWDS##
;SUBROUTINE TO SET UP J FROM SWPLST ENTRY
;ENTER P1 POINTING TO SWPLST
;EXIT CPOPJ1 IF A HIGH SEG, CPOPJ IF LOW
GETSWJ:	PUSHJ	P,SW2LC		;GET INDEX OF SWPLST ENTRY
	HRRZ	J,SW3LST##(T1)	;GET JOB NUMBER
	MOVE	T1,SWPLST##(T1)	;GET MAIN SWPLST ENTRY
	TLC	T1,(SL.SIO+SL.IPC) ;SWAPPING OUT THE REPLACABLE QUEUE?
	TLNN	T1,(SL.SIO+SL.IPC)
	TLOA	J,-1		;YES, INDICATE THAT BY SETTING J NEGATIVE
	CAILE	J,JOBMAX##	;HIGH SEG?
	AOS	(P)		;HIGH SEG OR PAGING QUEUE RETURN
	POPJ	P,		;BACK


;SUBROUTINE TO COMPUTE INDEX INTO SW2LST TABLE
;ENTER P1=ADDRESS IN SWPLST (OR FRAGMENT, ADR IN LH)
;EXIT T1=INDEX
;PRESERVES T2-T4
SW2LC::	TLNN	P1,-1		;POINTER TO A FRAGMENT?
	SKIPA	T1,P1		;NO, USE MAIN ADDRESS
	HLRZ	T1,P1		;YES, GET SWPLST LOCATION
	TRZ	T1,400000	;SIGN BIT OF P1 MIGHT BE ON
	SUBI	T1,SWPLST##	;MAKE INTO AN INDEX
	POPJ	P,		;AND RETURN
SUBTTL SWPSER INTERFACE WITH THE SWAPPER

;SUBROUTINE TO SETUP SWPLST FOR SWAP OUT
BOSLST::PUSHJ	P,SAVE4##	;SAVE ACS
	CAILE	J,JOBMAX##	;HI SEG?
	JRST	BOSLS5		;YES, DO IT DIFFERENTLY
	PUSHJ	P,SVEUB##	;LOW SEG, MAKE JOB ADDRESSABLE
	MOVEM	T2,.USLPS	;VPN POINTER
	SKIPL	.USBTS		;GET BITS
	TDZA	T2,T2		;NOT EXTENDED
	MOVEI	T2,MXSECN	;MAX SECTION TO TRY
	MOVEM	T2,.USSPT	;SECTION TO DO
	MOVE	T1,JBTADR##(J)	;SAVE JBTADR
	HLLM	T1,.USREL	; FOR SWAP-IN
	MOVE	T2,JBTSTS##(J)
	TLNE	T2,JXPN
	MOVEM	T1,.JDAT+JOBDPG##
	MOVE	T1,.JDAT+.JBPFH## ;GET JOBPFH
	MOVEM	T1,.USPFH	;AND SAVE IN UPMP FOR BISLST (MIGRATING JOB)
	SKIPGE	U,JBTSWP##(J)	;FRAGMENTED?
	JRST	BOSLS1		;YES
	LDB	T1,IMGOUT##	;NO, GET SIZE TO SWAP OUT
	LDB	T2,NFYPGS##	;ADD 1 PAGE FOR UPMP
	ADD	T1,T2
	LDB	J,NZSSCN##	;GET NUMBER OF NZS MAPS
	ADDI	J,(T1)
	PUSHJ	P,SETOMT	;SETOMT WILL SET UP MEMTAB
	MOVE	J,.USJOB	;RESTORE JOB #
	MOVEI	T1,(P3)		;FIRST PHYSICAL PAGE
	LSH	T1,SL.SCT	;POSITION IT
	LDB	T2,IMGOUT##	;NUMBER OF PAGES
	LDB	T3,NFYPGS##	;PLUS 1 FOR UPMP
	ADD	T2,T3
	LDB	T3,NZSSCN##	;# OF MAPS AGAIN
	ADDI	T2,(T3)		;ADD IN
	DPB	T2,[POINT SL.SCT,T1,<^L<SL.CNT>+SL.SCT-1>]	;IN T1
	HLRZ	T4,P3		;VIRTUAL PAGE #
	PUSHJ	P,GMPTR		;GET POINTER
	MOVEM	T4,.USLPS	;NOT FRAGMENTED
	JRST	BOSLS4		;SET LH BITS AND STORE T1 IN SWPLST
;HERE IF LOW SEG SWAP OUT AND DISK SPACE IS FRAGMENTED
BOSLS1:	MOVE	P1,JBTSWP##(J)
;	TLZ	P1,FRGSEG
	PUSH	P,.USLPS	;INITIALIZE POINTER TO VPN TABLE
	MOVSI	P2,(SL.DIO+SL.SIO)	;SWPLST DESCRIPTORS
	PUSHJ	P,NXTFRG	;SET UP U FOR THIS FRAGMENT
	  STOPCD	.+1,DEBUG,O1F,	;++ONLY 1 FRAGMENT
	PUSHJ	P,SETOMT	;SET UP MEMTAB
BOSLS2:	DPB	P3,[POINT SL.SPN,P2,<^L<SL.PPN>+SL.SPN-1>] ;STORE 1ST PAGE NUMBER OF FRAG
	HLRZ	T4,P3		;VPN OF FIRST VIRTUAL PAGE
	PUSHJ	P,GMPTR		;GENERATE A POINTER
	MOVEM	T4,@(P)		;STORE
	AOS	(P)		;INCREMENT
	MOVEM	P2,-1(P1)	;STORE THE SWPLST FRAGMENT IN TABLE
	PUSHJ	P,NXTFRG	;GET U FOR NEXT FRAG
	  JRST	BOSLS3		;LAST FRAGMENT
	MOVEI	P3,0		;P3=0 0N CALL TO SETOMT
	PUSHJ	P,[SE1ENT	;DO THIS IN SECTION 1
		JRST SETOM8]	;SET UP MEMTAB FOR THIS FRAG
	JRST	BOSLS2		;AND TRY NEXT
BOSLS3:	POP	P,(P)		;FIX STACK
	PUSHJ	P,[SE1ENT	;DO THIS IN SECTION 1
		SSX F,MS.MEM	;MAKE A REAL POINTER TO MEMTAB
		LDB  U,[POINT MT.SAD,MEMTAB(F),<^L<MT.DAD>+MT.SAD-1>]
		POPJ P,]
	MOVE	J,.USJOB	;RESTORE J
	MOVE	T1,JBTSWP##(J)	;ADDRESS OF FRAGMENT/SWPLST TABLE
;HERE WHEN DONE, T1=SWPLST ENTRY, U=DSK ADR OF UPMP
BOSLS4:	HRRZS	M		;IS THE UPMP THE LAST PAGE?
	CAIE	M,.UPMVP/PAGSIZ##
	STOPCD	.,STOP,UNL	;++UPMP NOT LAST
	MOVEM	U,JBTSWP##(J)	;SAVE UPMP ADDR IN JBTSWP
	TLO	T1,(SL.DIO+SL.SIO) ;INDICATE SWAP OUT
	MOVSI	T2,(UP.PGB)	;INDICATE PAGES WILL BE GIVEN BACK
	ANDCAM	T2,.USBTS
	PJRST	MAKSLE		;STORE IN SWPLST AND RETURN

;HERE IF HIGH SEG SWAP OUT
BOSLS5:	LDB	P1,IMGOUT##	;SIZE OF OUTPUT IMAGE (NO PAGES TO SWAP)
	HRLI	P1,(SL.SIO+SL.DIO) ;SWPLST DESCRIPTORS
	PJRST	BHSLST		;GO DO IT
;SUBROUTINE TO SETUP SWPLST FOR SWAP IN OF UPMP
BUSLST::SE1ENT			;ENTER SECTION 1
	CAILE	J,JOBMAX##	;HI SEG?
	JRST	BISL11		;YES, DO DIFFERENTLY
	PUSHJ	P,SVEUB##	;MAKE THE JOB ADDRESSABLE
	MOVE	U,JBTSWP##(J)	;NO, GET DSK ADDR OF UPMP
	HRRZ	T1,JBTUPM##(J)	;PAGE NUMBER OF UPMP
	TLO	U,(MT.LEF)	;INDICATE ONLY 1 FRAGMENT
	SSX	T1,MS.MEM	;MAKE MEMTAB ADDRESSABLE
	MOVEM	U,MEMTAB(T1)	;MEMTAB FOR UPMP
	LDB	T2,JBYMAP##	;ADDRESS OF THE MAP
	EXCH	U,T2		;PUT IN U
	TLZ	T2,(PM.NAD)	;CLEAR BITS
	SOS	T2
	CAME	U,T2		;CONTIGUOUS?
	JRST	BUSLS2		;NO, ALAS
	HRR	T1,PAGTAB(T1)
	MOVEI	T2,.UPMVP/PAGSIZ##
	DPB	T2,NVPNT1	;STORE POINTER TO NEXT WHICH IS UPT
	MOVEM	U,MEMTAB(T1)
	HRRZS	T1		;CLEAR SECTION NUMBER FROM LEFT HALF
	LSH	T1,11
	ADDI	T1,2		;NUMBER OF PAGES
	TLO	T1,(SL.SIO)	;SWAPPING UPMP IO (PREVENT FNDSLE FROM FINDING IT)
BUSLS1:	LDB	T2,JBYLSA##	;NUMBER OF 1ST PAGE FOR JOB
	MOVEM	T2,JBTSWP##(J)	;SAVE IN JBTSWP
	AOS	(P)		;GOOD RETURN
	JRST	MAKSLE		;SET UP SWPLST AND RETURN
BUSLS2:	PUSH	P,T1		;SAVE PAGTAB/MEMTAB POINTER
	HRLI	T1,(<SL.SIO_-SL.SCT>) ;CLEAR SECTION #, SET BIT
	LSH	T1,SL.SCT	;POSITION
	AOS	T1		;ONE PAGE
	PUSH	P,T1		;SAVE IT
	MOVEI	T2,1		;GET A CORE BLOCK
	PUSHJ	P,GET4WD##	;FOR THE SWPLST ENTRY
	  JRST	TPOPJ##		;OOPS
	POP	P,1(T1)		;STORE THE ENTRY
	TLO	U,(MT.LEF)	;TURN ON
	POP	P,T2		;PAGTAB POINTER
	HRR	T2,PAGTAB(T2)	;NEXT PAGE IN CHAIN
	MOVEM	U,MEMTAB(T2)	;STORE DISK ADDR IN MEMTAB
	HRLI	T2,(<SL.SIO_-SL.SCT>) ;CLEAR SECTION #, SET BIT
	LSH	T2,SL.SCT	;AND MAKE THE FRAGMENT ENTRY
	AOS	T2
	MOVEM	T2,(T1)		;MAP ENTRY
	SETZM	2(T1)		;NO MORE
	SETZM	3(T1)
	HRLI	T1,(SL.SIO!SL.FRG) ;SET FRAGMENTED ENTRY
	JRST	BUSLS1
;SUBROUTINE TO SET UP SWPLST FOR SWAP IN OF JOB
BISLST::PUSHJ	P,SAVE4##	;SAVE SOME ACS
	SETZB	P3,.USLPS	;CLEAR SLOT IF LEFT FROM FRAGMENTED SWAPOUT
	SKIPL	JBTSWP##(J)	;IF NOT FRAGMENTED,
	JRST	BISLS1		;GO DIRECTLY TO SETIMT
	MOVSI	T1,FRGSEG	;FRAG'D
	ANDCAM	T1,JBTSWP##(J)	;CLEAR BIT IN JBTSWP
	DMOVE	T1,.USHSE
	LSH	T1,W2PLSH##
	LSH	T2,W2PLSH##
	EXCH	T1,.USTMP
	EXCH	T2,.USTMP+1
	DMOVE	T3,.USTMP+2	; TO SETIMT
	MOVE	M,.USTMP+4
	MOVE	F,.USTMP+5
	MOVEI	U,SETIMX	;WE'LL CALL SETIMX THIS TIME
	JRST	BISLS2

BISLS1:	SKIPGE	P1,.USBTS	;EXTENDED USER (UP.BIG=1B0)
	TLNN	P1,(UP.NZS)	;BIG USER, TIME TO DO NON-ZERO SECTIONS?
	TDZA	P1,P1		;SECTION 0, DO IT
	MOVEI	P1,MXSECN	;START WITH SECTION 37 OTHERWISE
	MOVEM	P1,.USSPT	;SAVE AS SECTION TO START I/O AT
	MOVEI	U,SETIMT	;WHERE TO GO
BISLS2:	SETZ	P1,		;FIRST CALL
	PUSHJ	P,(U)		;SET UP UPMP FOR THIS FRAG, OR WHOLE JOB
BISLS3:	  SKIPA	T1,P1		;DONE - GET 1ST VIRT,,1ST PHYS
	JRST	BISLS4		;ANOTHER FRAG TO DO
	TLZ	T1,-1		;1ST PHYSICAL PAGE
	LSH	T1,SL.SCT	;POSITION IT
	TLO	T1,(SL.SIO)	;SWAPPING IO
	DPB	P3,[POINT SL.SCT,T1,<^L<SL.CNT>+SL.SCT-1>]	;NUMBER OF PAGES TO DO
	JRST	BISLS7		;SET UP JBTADR AND CALL MAKSLE
;HERE WHEN FRAGMENTED
BISLS4:	MOVEI	P4,0		;INDICATE 1ST CALL
	PUSHJ	P,GT4MR		;GET A 4-WORD BLOCK
	  JRST	BISL12		;CANT GET ONE
	HRRZ	P2,P4		;SAVE 1ST ADR OF 4-WD BLOCKS
BISLS5:	HRLI	P3,(SL.SIO)	;SWAPPING IO
	DPB	P1,[POINT SL.SPN,P3,<^L<SL.PPN>+SL.SPN-1>]	;1ST PHY PAGE
	MOVEM	P3,(P4)		;SAVE SWPLST ENTRY IN 4-WD BLOCK
	JUMPL	P2,BISLS6	;GO IF LAST FRAG
	SETZB	P1,P3		;NOT LAST, SETIMX WANTS P1,P3=0
	PUSHJ	P,SETIMX	;SET UP UPMP FOR THIS FRAG
	  TLO	P2,400000	;LAST FRAGMENT - P2 NEGATIVE
	AOBJN	P4,BISLS5	;GO DO NEXT IF 4-WD BLOCK NOT EXHAUSTED
	PUSHJ	P,GT4MR		;GET ANOTHER BLOCK
	  JRST	BISL12		;NONE AVAILABLE
	JRST	BISLS5		;GET ONE - CONTINUE

;HERE WHEN LAST FRAGMENT DONE
BISLS6:	SETZM	1(P4)		;TERMINATE LIST
	HRRZ	T1,P2		;ADR OF START OF 4-WD BLOCKS
	TLO	T1,FRGSEG(SL.SIO)	;FRAGMENTED SWAP
;HERE AT END OF FRAG (OR WHOLE JOB)
BISLS7:	HLRZ	M,.USREL	;LH (JBTADR) ON SWAP-OUT
	JUMPE	M,BISLS8	;ALREADY STORED
	HRRZS	.USREL		;CLEAR LH (.UPREL)
	HRLM	M,JBTADR##(J)	;RESTORE (NON-SHARABLE HS CASE)
BISLS8:	MOVE	R,JBTADR##(J)	;MAKE SURE R IS RIGHT
	SKIPGE	JBTSWP##(J)	;FRAGMENT END?
	JRST	BISLS9		;YES, RAN OUT OF STUFF ON FRAGMENTED SWAP
	MOVSI	T2,(UP.NZS)	;BIT TO CHECK
	XORB	T2,.USBTS	;CHANGE STATE
	JUMPGE	T2,BISLS9	;NORMAL USER
	TLNE	T2,(UP.NZS)	;BIT JUST GO ON?
	HRRZM	F,.USTMP+5	;SAVE POINTER TO NEXT
	MOVEM	T2,.USBTS	;SAVE STATE OF BIT
BISLS9:	SKIPLE	T2,JBTSGN##(J)	;IS THERE A NON-SHARABLE HI-SEG?
	TLNE	T2,SHRSEG
	JRST	BISL10		;NO, GO MAKE THE SWPLST ENTRY
IFN FTXMON,<
	HRRZS	T2		;CLEAR JUNK
>
	LDB	T3,[POINT 9,JBTADR##(T2),8]	;YES
	SKIPE	JBTADR##(T2)	;HIGHEST ADR IN HIGH SEG
	ADDI	T3,1
	DPB	T3,JBYHSS##	;SAVE AS HI SEG SIZE
	LDB	T4,[POINT 9,JBTUPM##(T2),8]	;HI-SEG OFFSET
	PUSHJ	P,MAPHI
BISL10:	JUMPG	P3,MAKSLE		;MAKE SWPLST ENTRY (NORMAL)

;HERE IF REALLY DON'T NEED TO SWAP ANYTHING.  NOTE THAT THIS CAN ONLY
;HAPPEN ON THE THIRD SWAP OPERATION OF AN NZS PROGRAM AS SECTION 0 ALWAYS
;HAS PAGE 0 TO SWAP IN.

	MOVSI	P2,(SL.IOD)
	IORB	P2,T1		;MARK AS DONE
	PUSHJ	P,MAKSLE	;MAKE NEW SWPLST ENTRY
	MOVEI	P1,(T2)		;INDEX TO SWXLST
	AOS	(P)		;SKIP AOS OF SWAPPER COUNT
	SOS	SPRCNT##	;FIX COUNTS UP
	SOS	SINCNT##
	MOVEI	F,SWPDDB##	;RESTORE F
	PJRST	SWPINA##	;AND CONTINUE

;HERE FOR HI SEG
BISL11:	PUSHJ	P,SAVE2##	;SAVE ACS
	LDB	P1,IMGOUT##	;NO OF PAGES TO SWAP IN
	HRLI	P1,(SL.SIO)	;SWAPPING IO
	MOVE	T1,JBTSWP##(J)	;GET DISK ADDR
	PUSHJ	P,BLDVPN	;GET CORE FOR TABLE, IF NECESSARY
	  POPJ	P,		;OOPS
	AOS	(P)		;GOOD RETURN
	PJRST	BHSLST		;SET UP SWPLST FOR HI SEG

;HERE WHEN NOT ENOUGH 4-WD BLOCKS ARE AVAILABLE
BISL12:	DMOVEM	T1,.USTMP	;SAVE WORKING ACS IN  UPMP
	DMOVEM	T3,.USTMP+2
	MOVEM	M,.USTMP+4
	HRRZM	F,.USTMP+5
	MOVSI	T1,FRGSEG	;INDICATE HAVE DONE A PART OF WHAT WE NEED
	IORM	T1,JBTSWP##(J)
	JUMPE	P4,BISLS3	;FINISH UP THIS PART AND CALL MAKSLE
	SOJA	P4,BISLS6	;FINISH UP
;SUBROUTINE TO SETUP SWPLST FOR HIGH SEGMENT SWAP IN OR OUT
;CALLING SEQUENCE:
;
;	MOVE	P1,HIGH SEGMENT SIZE
;	HRLI	P1,SWPLST ATTRIBUTES (I.E. DIRECTION OF I/O)
;	MOVEI	T2,ADDR+1 OF CORE BLOCK FOR FRAGMENT TABLE COPY (IF FRAG'D)
;	PUSHJ	P,BHSLST
;ALWAYS RETURNS CPOPJ
BHSLST:	MOVE	T3,T2		;SAVE CORE FOR FRAG COPY (IF ANY)
	LDB	T2,JBYHSA##	;1ST PAGE
	SKIPGE	T1,JBTSWP##(J)	;DSK ADR OF 1ST PAGE
	JRST	BHSLS1		;FRAGMENTED
	HRRZ	T3,P1		;SIZE
	MOVEI	T4,STOHDA	;ROUTINE TO STORE DISK ADDRESS IN MEMTAB
	PUSHJ	P,SETXMT	;SET UP FOR SWAP
	LDB	T1,JBYHSA##	;1ST PAGE
	LSH	T1,SL.SCT	;POSITION IT FOR SWPLST
	IOR	T1,P1		;ADD ATTRIBUTES, SIZE
	JRST	MAKSLE		;SET UP SWPLST AND RETURN
BHSLS1:	PUSHJ	P,SAVE4##	;SAVE ACS
	SOS	P4,T3		;GET CORE ADDRESS (WE DON'T NEED COUNT,
				;BUT DO NEED EXTRA WORD FOR TERMINATING ZERO)
	MOVE	P2,T1		;ADDRESS OF DISK ADDRESS FRAGMENT TABLE
	MOVEI	P3,STOHDA	;ROUTINE TO STORE DISK ADDRESS IN MEMTAB
	PUSHJ	P,BFSLST	;SETUP SWPLST FRAGMENT TABLE, MAKE SWPLST ENTRY, AND RETURN
	HRRM	P4,JBTSWP##(J)	;STORE NEW SWPLST TABLE
	POPJ	P,
;SUBROUTINE TO STORE DISK ADDRESS IN MEMTAB, CALL WITH T1=DISK ADDRESS,
; T2=MEMTAB INDEX, ALWAYS RETURNS CPOPJ
STOHDA:	MOVEM	T1,MEMTAB(T2)	;STORE DISK ADDRESS
	POPJ	P,		;AND RETURN
;SUBROUTINE TO SETUP SWPLST TO SWAP OUT PAGES ON "IN" QUEUE
;CALLING SEQUENCE:
;	PUSHJ	P,BPSLST
;ALWAYS RETURNS CPOPJ, I/O STARTED IF POSSIBLE

BMSLST::SKIPA	T4,[STODAU]	;SPECIAL ENTRY FOR UUO LEVEL CALLS
BPSLST::MOVEI	T4,STODA	;NORMAL ROUTINE TO CHANGE DSK ADDRS
	SKIPE	PAGIPQ##	;CAN'T DO ANYTHING IF I/O IS IN PROGRESS
	POPJ	P,		;RETURN DOING NOTHING IF I/O IS IN PROGRESS
	PUSHJ	P,SAVE1##	;SAVE AN AC
	SKIPN	U,PAGSNC##	;ANYTHING ON THE SLOW SWAPPING SPACE "IN" QUEUE?
	JRST	BPSLS1		;NO, TRY "IN" QUEUE
	MOVEI	P1,PAGSNQ##	;YES, OUTPUT IT FIRST
	TLO	U,(PG.SLO)	;ALLOCATE FROM SLOW SWAPPING SPACE
	JRST	BPSLS2		;GO START THE I/O
BPSLS1:	SKIPN	U,PAGINC##	;ANYTHING ON THE "IN" QUEUE?
	POPJ	P,		;NOTHING TO DO
	MOVEI	P1,PAGINQ##	;OUTPUT "IN" QUEUE
BPSLS2:	PUSHJ	P,SAVJW##	;SAVE J,W
	MOVE	W,T4		;SAVE T4 (DON'T NEED W ANYWAY)
	SETZ	J,		;USE JOB 0
	PUSHJ	P,SWPSPC##	;GET SWAPPING SPACE
	  POPJ	P,		;NONE AVAILABLE, TRY AGAIN LATER
	MOVE	J,-2(P)		;RESTORE J
	DMOVE	T2,(P1)		;"IN" QUEUE HEADER
	DMOVEM	T2,PAGIPQ##	;BECOMES "IP" QUEUE HEADER
	CAIE	P1,PAGIPQ##	;ARE WE RE-DOING THE IP QUEUE?
	SETZB	T3,(P1)		;"IN" QUEUE IS EMPTY
	EXCH	T3,1(P1)	;GET NUMBER OF PAGES ON "IP" QUEUE
	MOVE	T4,W		;RESTORE T4
	JUMPL	T1,BPSLS3	;JUMP IF SWAPPING SPACE IS FRAGMENTED
	.CREF	FRGSEG
	PUSHJ	P,SETXMT	;SETUP MEMTAB AND COPY DISK ADDRESSES TO MAPS
	MOVE	T1,PAGIPQ##	;FIRST PAGE
	LSH	T1,SL.SCT	;POSITION FOR SWPLST ENTRY
	IOR	T1,PAGIPC##	;COUNT
	TLO	T1,(SL.SIO+SL.DIO+SL.IPC)
	PUSHJ	P,MAKSLE	;MAKE A SWPLST ENTRY
	JRST	BPSLS4		;START I/O AND RETURN
;HERE WHEN SWAPPING SPACE IS FRAGMENTED
BPSLS3:	PUSHJ	P,SAVE4##	;SAVE ACS
	MOVE	P1,T3		;COUNT TO P1
	HRLI	P1,(SL.SIO+SL.DIO+SL.IPC)
	MOVE	P2,T1		;ADDRESS OF FRAGMENT TABLE
	MOVEI	P3,(T4)		;ADDRESS OF ROUTINE TO STORE IN MEMTAB
	SETZ	P4,		;NO FRAG TABLE COPY
	PUSHJ	P,BFSLST	;SETUP SWPLST
BPSLS4:	PUSHJ	P,INTLVL##	;AT INTERRUPT LEVEL?
	  HRRM	T2,.USSLX	;NO, BE SURE WE FIND THIS SWPLST ENTRY
	AOS	SPRCNT##	;ONE MORE SWAP IN PROGRESS
	PJRST	SQOUT##		;START THE OUTPUT
;SUBROUTINE TO STORE DISK ADDRESS IN MEMTAB AND IN MAP THAT THE PAGE BELONGS TO
; CALL WITH T1=DISK ADDRESS, T2=MEMTAB INDEX, RETURNS CPOPJ, T1 AND T2 INTACT
STODA:	PUSHJ	P,SAVE4##	;SAVE ACS
	MOVE	P1,J		;SAVE J
	DMOVE	P2,T1		;SAVE DISK ADDRESS AND MEMTAB INDEX
	EXCH	T1,MEMTAB(T2)	;STORE DISK ADDRESS, GET JOB NUMBER+VIRTUAL PAGE NUMBER
	TLZE	T1,(MT.IPC)	;AN IPCF PAGE?
	JRST	STODIP		;YES, HANDLE IT
	MOVE	P4,T1		;SAVE THAT
	LDB	J,[POINT MT.SJB,P4,<^L<MT.JOB>+MT.SJB-1>] ;GET JOB NUMBER OF JOB PAGE BELONGS TO
	PUSHJ	P,SVEUB##	;MAKE MAPS ADDRESSABLE
	LDB	T1,[POINT <MT.SPN+MT.SSN>,P4,<^L<MT.VSN>+MT.SPN+MT.SSN-1>] ;GET PAGE NUMBER
	PUSHJ	P,GTPME		;GET MAP CONTENTS
	AND	T2,[PM.BTS]	;EXTRACT ACCESS BITS
	IOR	T2,MEMTAB(P3)	;ADD IN DISK ADDRESS
	MOVEM	T2,(T4)		;STORE IT IN THE MAP
	DPB	P4,[POINT P2.SPN,PT2TAB(P3),<^L<P2.VPN>+P2.SPN-1>] ;REMEMBER
				;WHERE IT CAME FROM
	DPB	J,[POINT MT.SJB,MEMTAB(P3),<^L<MT.JOB>+MT.SJB-1>] ;AND SET JOB #
STODA1:	MOVE	J,P1		;RESTORE J
STODA2:	DMOVE	T1,P2		;AND T1, T2
	POPJ	P,		;AND RETURN

;HERE FOR IPCF PAGE

STODIP:	DPB	T1,[POINT P2.SPN,PT2TAB(T2),<^L<P2.VPN>+P2.SPN-1>] ;SET LOW
				;ORDER BITS
	ROT	T1,-P2.SPN	;DEPOSIT
	DPB	T1,[POINT <^D18-P2.SPN>,MEMTAB(T2),<^L<MT.JOB>+MT.SJB-1>]
				;HIGH ORDER BITS
	ROT	T1,P2.SPN	;PUT BACK
	MOVSI	P4,(MT.IPC)	;LEAVE MT.IPC ON TOO
	IORM	P4,MEMTAB(T2)
	MOVSI T2,<1000_<^D36-^D18-IP.PLN##-1>> ;THIS IS A PAGE
	IOR	T2,P2		;YES, GET DISK ADDRESS BACK
	TLO	T2,IP.DSK##_-^D18 ;MARK IT AS A DISK ADDRESS FOR IPCSER
	MOVEM	T2,(T1)		;STORE IT IN THE PACKET
	.CREF	MT.IPA		;IPCF PACKET ADDRESS
	JRST	STODA1		;AND EXIT
;SUBROUTINE TO DO THE SAME THING AS STODA, BUT CAN BE CALLED AT
;UUO LEVEL.  IS SLOWER, BUT DOESN'T CALL SVEUB.  CANNOT BE CALLED
;AT NON-UUO LEVEL

STODAU::PUSHJ	P,SAVE4##	;SAVE WORLD
	DMOVE	P2,T1		;MOVE TO A BETTER PLACE
	EXCH	T1,MEMTAB(T2)	;SAVE DISK ADDR, GET WHERE TO PUT IT
	TLZE	T1,(MT.IPC)	;IPCF PAGE?
	JRST	STODIP		;YES, DO THAT
	MOVE	P4,T1		;LOCATION WORD
	LDB	T1,[POINT MT.SJB,P4,<^L<MT.JOB>+MT.SJB-1>] ;JOB IT GOES IN
	DPB	T1,[POINT MT.SJB,MEMTAB(P3),<^L<MT.JOB>+MT.SJB-1>] ;PRESERVE
	HRRZ	T2,JBTUPM##(T1)	;UPMP FOR THAT JOB
	HRLI	T2,(<PM.DCD>B2+PM.WRT+PM.PUB) ;DON'T BOTHER CACHING IT
	MOVEM	T2,.UPMP+.UMTMP
	PUSHJ	P,CLPTZO	;SET UP THE MAPPING
	EXP	.TEMP
	LDB	T1,[POINT MT.SSN,P4,<^L<MT.VSN>+MT.SSN-1>] ;SECTION # PAGE IS IN
	HRR	T2,.TEMP+SECTAB(T1) ;SECTION MAP POINTER FOR THAT SECTION
	TLZ	T2,(PM.CSH)	;BE SURE IT'S OFF
	MOVEM	T2,.UPMP+.UMTMP ;MAP THAT
	PUSHJ	P,CLPTZO
	EXP	.TEMP		;CLEAR PAGING
	LDB	T1,[POINT MT.SPN,P4,<^L<MT.VPN>+MT.SPN-1>] ;PAGE TO CHANGE
	MOVSI	T2,(PM.BTS)	;GET BITS
	ANDM	T2,.TEMP(T1)	;SAVE ONLY THEM
	MOVE	T2,MEMTAB(P3)	;GET DISK ADDRESS
	TLZ	T2,(-1^!MT.DAD)	;KEEP ONLY ADDRESS
	IORM	T2,.TEMP(T1)	;AND PUT IT IN
	DPB	P4,[POINT P2.SPN,PT2TAB(P3),<^L<P2.VPN>+P2.SPN-1>]
	DMOVE	T1,P2
	POPJ	P,
;SUBROUTINE TO BUILD A FRAGMENTED SWPLST ENTRY
; CALL WITH P1=ATTRIBUTES,,SIZE, P2=ADDRESS OF DISK ADDRESS FRAGMENT TABLE,
; P3=ROUTINE TO CALL TO SETUP MEMTAB FOR A FRAGMENT, ALWAYS RETURNS CPOPJ

; P4=ADDRESS TO COPY ORIGINAL FRAGMENT TABLE TO (CONTIGUOUS BLOCK)
; OR ZERO IF NONE (COPY IS USED FOR HIGH SEG)
BFSLST:	PUSHJ	P,SAVE4##	;SAVE ACS
	PUSH	P,J		;NXTFRG CLOBBERS J
	EXCH	P2,P1		;ATTRIBUTES,,SIZE
	PUSH	P,P1		;SAVE LOC OF BLOCK
	HLLM	P2,(P)		;AND BITS
BFSLS1:	DPB	T2,[POINT SL.SPN,P2,<^L<SL.PPN>+SL.SPN-1>] ;SAVE PAGE NUMBER IN P2
	PUSHJ	P,NXTFRG	;SET U FOR THIS FRAGMENT
	  JRST	BFSLS2		;DONE
	JUMPE	P4,BFSLS4	;NO COPYING TO DO
	MOVE	T1,-1(P1)	;GET ORIGINAL ENTRY
	MOVEM	T1,(P4)		;COPY IT
	AOS	P4
BFSLS4:	MOVEM	P2,-1(P1)	;SAVE SWPLST ENTRY
	LDB	T3,[POINT SL.SCT,P2,<^L<SL.CNT>+SL.SCT-1>] ;SIZE
	MOVE	T1,U		;1ST DSK ADR
	MOVE	T4,P3		;ROUTINE TO CALL TO STORE IN MEMTAB
	PUSHJ	P,SETXMT	;SET UP FOR THIS FRAG
	JRST	BFSLS1		;OK, DO NEXT FRAG
BFSLS2:	POP	P,T1		;RESTORE 1ST ADR OF 4-WD BLOCKS
	TLO	T1,FRGSEG	;INDICATE FRAGMENTED
	POP	P,J		;RESTORE J
	SKIPE	P4		;IF COPY OF FRAGMENT LIST
	SETZM	(P4)		;THEN TERMINATE COPY
	JRST	MAKSLE		;CALL MAKSLE AND RETURN
;SUBROUTINE TO SET UP MEMTAB FOR A SWAP
;CALL WITH T1=1ST DSK ADR, T2=1ST PAGE NUMBER, T3=NUMBER OF PAGES, T4=ROUTINE
; TO CALL TO STORE DISK ADDRESSES
;ALWAYS RETURNS CPOPJ
SETXMT:	SE1ENT			;ENTER SECTION 1
	PUSHJ	P,SAVE2##
	SETZ	P1,		;NEED ZERO FOR SECTION #
	MOVE	P2,T4		;ADDRESS OF ROUTINE TO CALL TO STORE DISK ADDRESS
SETXM1:	SSX	T2,MS.MEM	;SECTION NUMBER FOR PAGTAB/MEMTAB
	PUSHJ	P,(P2)		;SAVE DISK ADDRESS (CLEARING REST)
	MOVE	T4,T2		;GET NEXT PAGE OF SEGMENT
	TRNE	T4,-1		;LEGAL PREDESESSOR?
	SKIPGE	T2,PAGTAB(T2)
	STOPCD	.,STOP,PFC,	;++PAGE ON FREE CORE LIST
	ADDI	T1,1		;INCR. DSK ADR BY 1
	SOJG	T3,SETXM1	;GO DO NEXT PAGE
	MOVSI	T1,(MT.LEF)
	IORM	T1,MEMTAB(T4)	;INDICATE LAST PAGE IN MEMTAB
	POPJ	P,		;AND RETURN
;SUBROUTINE TO MOVE PAGES FROM "IP" QUEUE TO "OUT" QUEUE.
;CALL WITH P1=LOC OF SWPLST ENTRY,  P2=SWPLST ENTRY

IP2OUT::SE1ENT			;MUST BE IN SECTION 1 TO LOOK AT PAGTAB/MEMTAB
	PUSHJ	P,SAVE2##	;SAVE ACS
IP2OU1:	SKIPN	P1,PAGIPQ##	;GET FIRST PAGE ON "IP" QUEUE
	STOPCD	IP2OU6,DEBUG,IPE, ;++IN PROGRESS QUEUE EMPTY
	TLNE	P2,(SL.ERR!SL.CHN!SL.CHK) ;ANY ERRORS?
	JRST	IP2ERR		;YES, PUT THE QUEUE BACK
IP2OU2:	SSX	P1,MS.MEM	;PAGTAB SECTION
	MOVE	P2,P1		;SAVE PREDECESSOR
	LDB	T1,[POINT PT.SJB,PAGTAB(P1),<^L<PT.JOB>+PT.SJB-1>] ;GET POSSIBLE WAITING JOB NUMBER
	JUMPE	T1,IP2OU3	;JUMP IF THERE IS NONE
	S0PSHJ	STPIOD##	;GET THE JOB STARTED AGAIN
	HRRZS	PAGTAB(P1)	;AND CLEAR JOB NUMBER
IP2OU3:	MOVE	T3,MEMTAB(P1)	;MEMTAB ENTRY
	TLNN	T3,(MT.GPB)	;DOES THE PAGE NEED TO BE GIVEN BACK?
	JRST	IP2OU4		;NO, NEXT PAGE
	LDB	T2,[POINT MT.SAD,T3,<^L<MT.DAD>+MT.SAD-1>]
	PUSHJ	P,DLTDSP	;DELETE PAGE FROM DISK
	HRRZ	T2,P2		;PHYSICAL PAGE NUMBER
	MOVEI	T3,PAGIPQ##	;"IP" QUEUE HEADER
	PUSHJ	P,RMVPFQ	;REMOVE THE PAGE FROM THE "IP" QUEUE
	PUSHJ	P,DLTPGC	;DELETE THE PAGE FROM CORE
	SKIPE	PAGIPQ##	;EMPTIED THE ENTIRE QUEUE?
	JRST	IP2OU1		;NO, START OVER
	JRST	IP2OU5		;YES, DELETE SWPLST ENTRY AND RETURN
IP2OU4:	SKIPE	P1,PAGTAB(P1)	;NEXT PAGE, EQUAL LAST PAGE?
	JRST	IP2OU2		;YES, FINISH UP
	DMOVE	T1,PAGIPQ##	;GET FIRST PAGE, COUNT
	EXCH	T1,PAGOUQ##	;MAKE IT FIRST PAGE ON "OUT" QUEUE
	MOVEM	T1,PAGTAB(P2)	;LINK FORMER "OUT" QUEUE AT END OF FORMER "IP" QUEUE
	SKIPE	T1
	HRRM	P2,@[IW MS.MEM,PT2TAB(T1)]
	ADDM	T2,PAGOUC##	;UPDATE NUMBER OF PAGES ON "OUT" QUEUE
	SETZM	PAGIPQ##	;"IP" QUEUE IS NOW EMPTY
	SETZM	PAGIPC##	; ...
IP2OU5:	MOVE	P1,-2(P)	;GET SWPLST INDEX BACK
	PUSHJ	P,DLTSLX	;DELETE SWPLST ENTRY
IP2OU6:
IFN FTMP,<
	PUSHJ	P,SBSCD##	;MUST HAVE THE SCHEDULER INTERLOCK HERE
>
	MOVEI	P1,1		;FLAG
	HRRE	J,JBTCQ##-PQIOQ## ;TAKE EVERYONE OUT OF PQIOW
	JUMPLE	J,IP2OU9	;DONE HERE
IP2OU7:	PUSHJ	P,SSEC0##
IP2OU8:	MOVEI	T1,RNQ##	;REQUEUE HIM
	SYSPIF
	PUSHJ	P,WAKOKE##	;PUT HIM IN THE RUN QUEUE
	  JFCL
	HRRE	J,JBTCQ##(J)	;NEXT JOB
	JUMPG	J,IP2OU8
IP2OU9:	SOJL	P1,CPOPJ##	;DONE IF LOOKED AT BOTH QUEUES
	HRRE	J,JBTCQ##-PQIOQ##-MXQUE##
	JUMPG	J,IP2OU7
	POPJ	P,

;HERE IF ERRORS IN THE SWPLST ENTRY

IP2ERR:	SSX	P1,MS.MEM		;SET RIGHT SECTION
	LDB	T1,[POINT P2.SPN,PT2TAB(P1),<^L<P2.VPN>+P2.SPN-1>] ;GET "VPN"
	MOVSI	T3,(MT.GPB)		;REALLY NEED THIS PAGE?
	TDNE	T3,MEMTAB(P1)		;?
	JRST	[MOVEI	T3,PAGIPQ##	;NO, JUST FLUSH IT
		 HRRZ	T2,P1		;...
		 HRRZ	P1,PAGTAB(P1)	;GET SUCCESSOR (IF ANY)
		 PUSHJ	P,RMVPFQ	;REMOVE THE PAGE
		 JRST	IP2ERX	]	;CONTINUE
	MOVSI	T3,(MT.IPC)		;IPCF PAGE?
	TDNE	T3,MEMTAB(P1)		;IS IT?
	JRST	IP2EIP			;YES, TREAT DIFFERENTLY
	DPB	T1,[POINT MT.SPN+MT.SSN,MEMTAB(P1),<^L<MT.VSN>+MT.SPN+MT.SSN-1>]
IP2ERE:	HRRZ	P1,PAGTAB(P1)		;NEXT PAGE ON IP QUEUE
IP2ERX:	JUMPN	P1,IP2ERR		;CONTINUE
	MOVE	P1,-2(P)		;DELETE THIS ENTRY
	PUSHJ	P,DLTSLX
	MOVEI	P1,PAGIPQ##		;HEADER TO "TRANSFER"
	MOVE	U,PAGIPC##		;AMOUNT OF SWPSPC TO GET
	MOVEI	T4,STODA		;ROUTINE
	PJRST	BPSLS2			;TRY, TRY AGAIN

IP2EIP:	LDB	J,[POINT MT.SJB,MEMTAB(P1),<^L<MT.JOB>+MT.SJB-1>]
	LSH	J,P2.SPN		;SHIFT
	IOR	T1,J			;FULL 18-BIT ADDR
	HRRM	T1,MEMTAB(P1)		;SAVE IPCF ADDR
	SETZ	J,
	DPB	J,[POINT MT.SJB,MEMTAB(P1),<^L<MT.JOB>+MT.SJB-1>]
	JRST	IP2ERE			;CONTINUE WITH QUEUE
;SUBROUTINE TO MAKE AN ENTRY IN SWPLST
;CALLING SEQUENCE:
;
;	MOVE	T1,SWPLST ENTRY
;	PUSHJ	P,MAKSLE
;ALWAYS RETURNS CPOPJ

MAKSLE::MOVSI	T2,MJBTMX##	;MAX NUMBER OF SWPLST ENTRIES
	DSKOFF
IFN FTMP,<
	SYSPIF
>
MAKSL1:	SKIPE	SWPLST##(T2)	;THIS SLOT AVAILABLE?
	AOBJN	T2,MAKSL1	;NO
	JUMPL	T2,MAKSL2	;FOUND A SLOT IF T2 NEG
	STOPCD	CPOPJ##,DEBUG,SLF,	;++SWPLST FULL
MAKSL2:	MOVEM	J,SW3LST##(T2)	;SAVE J
	MOVEM	T1,SWPLST##(T2)	;SAVE SWPLST ENTRY
	MOVE	T1,SWPOUT##	;SWPOUT (SCHED1) HAS TO BE SAVED
	HRLM	T1,SW3LST##(T2)	; IN CASE OF MULTIPLE SWAP-OUTS
	AOS	SLECNT##	;INCREMENT SWAP-LIST-ENTRY COUNT
MAKSL3:
IFN FTMP,<
	SYSPIN
>
	POPJ	P,		;AND RETURN

;SUBROUTINE TO FIND A SWPLST ENTRY
;RETURNS CPOPJ IF NO ENTRIES (EVERYTHING IN SWPLST IS FOR PAGING)
;RETURNS CPOPJ1 IF FOUND, P1=INDEX , J=JOB NUMBER, T1=1ST PAGE, P2=ENTRY
FNDSLE::
IFN FTMP,<
	SYSPIF			;MAKE SURE SOME OTHER CPU DOESN'T MAKE A SWPLST ENTRY
				; AT INTERRUPT LEVEL
>
	SKIPN	P3,SLECNT##	;GET COUNT OF SWPLST ENTRIES
	STOPCD	MAKSL3,DEBUG,SLZ,	;++SLECNT IS ZERO
	MOVEI	P1,0		;START AT 1ST THING IN SWPLST
FNDSL1:	SKIPN	P2,SWPLST##(P1);GET THE ENTRY
FNDSL2:	AOJA	P1,FNDSL1	;NOT HERE - TRY NEXT
	TLNE	P2,(SL.DFM)	;DON'T FIND A UPMP ENTRY
	JRST	FNDSL3		;IT WILL BE HANDLED AT INTERRUPT LEVEL
	TLNE	P2,(SL.SIO)	;SWAPPER ENTRY?
	TLNN	P2,(SL.IOD)	;YES, DONE?
FNDSL3:	SOJG	P3,FNDSL2	;NO, TRY NEXT
	JUMPE	P3,ONPOPJ##	;DIDNT FIND ONE IF T1=0
	MOVE	T1,SW3LST##(P1)	;SWPOUT,,JOB NUMBER
	TLNE	P2,(SL.DIO)	;DON'T CHANGE SWPOUT IF INPUT
	HLRZM	T1,SWPOUT##	;RESTORE SWPOUT FOR SCHED1
	HRRZ	J,T1		;SET UP J
	LDB	T1,[POINT 13,P2,26]	;1ST PHYSICAL PAGE
IFN FTMP,<
	SYSPIN			;SLECNT AND SWPLST ARE CONSISTANT
>
	PJRST	CPOPJ1##	;TAKE SKIP-RETURN
;SUBROUTINE TO DELETE THE SWPLST ENTRY
;ENTER WITH P1=INDEX
;ALWAYS RETURNS CPOPJ
DLTSLX::
IFN FTMP,<
	PUSH	P,F		;PROBABLY NOT NECESSARY
	MOVEI	F,SWPDDB##	;FOR PIOFF
	PIOFF			;GUARD AGAINST INTERPROCESSOR RACE
	PUSHJ	P,DLTSLE	;DELETE SWPLST ENTRY AND DECREMENT SLECNT
	PION
	JRST	FPOPJ##		;RESTORE F AND RETURN
>
DLTSLE::SKIPN	T1,SWPLST##(P1)	;GET ENTRY
	STOPCD	CPOPJ##,DEBUG,SIE, ;++SWPLST IS EMPTY
	TLNE	T1,(SL.IOD)	;IO DONE FOR THIS ENTRY?
	TLNN	T1,(SL.SIO)	;SWAPPER?
	JRST	DLTSL1		;PAGER - LEAVE SWPCNT ALONE
	SOSGE	SWPCNT##	;DECREMENT SWPCNT
	STOPCD	DLTSL2,DEBUG,SIN, ;++SWPCNT IS NEGATIVE
DLTSL1:	SOS	SLECNT##	;DECREMENT NUMBER OF ITEMS IN SWPLST
	SETZM	SWPLST##(P1)	;CLEAR OUT THE OLD ENTRY
	JUMPGE	T1,CPOPJ##	;DONE IF NOT FRAGMENTED
				;FALL INTO RTNBLK

;SUBROUTINE TO RETURN 4-WORD CORE BLOCKS USED IN SWPLST
;ENTER WITH T1=SWPLST ENTRY
RTNBLK:	PUSHJ	P,SAVE1##	;SAVE AC
	MOVE	P1,T1		;ORIGINAL ENTRY
RTNBL1:	HRRZ	T2,P1		;ADR OF 4-WD BLOCK
	SKIPG	T1,3(P1)	;NEXT BLOCK CONTIGUOUS WITH THIS?
	SKIPA	P1,T1		;NO, SAVE ITS ADR
	ADDI	P1,4		;YES
	MOVEI	T1,1		;DELETE 1 BLOCK
	PUSHJ	P,GIV4WD##
	JUMPE	P1,CPOPJ##	;DONE IF LINK=0
	JRST	RTNBL1		;NOT DONE, RETURN NEXT BLOCK


DLTSL2:	SETZM	SWPCNT##	;ZERO SWPCNT SO WONT KEEP GETTING HERE
	POPJ	P,		;AND RETURN
;SUBROUTINE TO GET 4 WORDS OF MONITOR FREE CORE
;ENTER P4=0 1ST TIME, P4=PREVIOUS P4 ON FOLLOWING CALLS
;RETURNS CPOPJ IF NO BLOCKS AVAILABLE
;RETURNS CPOPJ1 IF GOT ONE, P4=AOBJN WORD FOR BLOCK
GT4MR:	PUSHJ	P,SAVT##	;SAVE A BUNCH OF ACS
	MOVEI	T2,1		;GET 1 BLOCK
	PUSHJ	P,GET4WD##
	  POPJ	P,SETOMT	;CANT GET IT - POPJ
	SETZM	3(T1)		;GOT IT, ZERO THE LINK
	JUMPE	P4,GT4M2	;IF NOT 1ST CALL,
	CAIE	T1,1(P4)	;BLOCK CONTIGUOUS WITH PREVIOUS?
	JRST	GT4M1		;NO
	HRLI	P4,-4		;YES, CAN USE 4 WDS
	JRST	CPOPJ1##	; (LINK OF LAST, FIRST 3 OF THIS)
GT4M1:	HRROM	T1,(P4)		;POINT LAST WD OF PREVIOUS BLOCK AT THIS
GT4M2:	MOVE	P4,T1		;ADR OF THIS BLOCK
	HRLI	P4,-3		;CAN USE 3 WORDS
	JRST	CPOPJ1##	;TAKE GOOD RETURN

;SUBROUTINE TO SET UP BITS IN S FROM SWPLST
GETIOS::MOVEI	S,0
	TLNE	P2,(SL.ERR)	;ERROR?
	TRO	S,IODERR	;YES
	TLNE	P2,(SL.CHN)	;CHAN ERROR?
	TRO	S,IOCHMP	;YES
	TLNE	P2,(SL.CHK)	;CHECKSUM ERRORS
	TRO	S,IOIMPM	;YES
	TLNE	P2,(SL.DIO)	;OUTPUT?
	TLO	S,IO		;YES
	POPJ	P,		;RETURN
;ROUTINE TO RETURN NEXT PAGE
;RETURNS RH(M)=VIRT PAGE, T4=POINTER TO MAP SLOT
;PRESERVE T1-T3,M ACCROSS CALLS (AFTER FIRST, CALL NXTSW2)
;BITS 3-17 OF M ARE PRESERVED FOR USE BY THE CALLER
;AND BITS 0-2 OF M (AS WELL AS RH(M) ARE FOR USE BY THIS ROUTINE

NXTWSB::SKIPGE	.USBTS		;NOT A BIG USER?
	SKIPG	T2,.USSPT	;SECTION ZERO?
	JRST	[MOVEI	T2,WSBTAB ;YES
		 JRST	NXTWS1	] ;DO THE OLD WAY
	HRRZS	T2		;CLEAR LEFT HALF
	IMULI	T2,WSBTBL	;COMPUTE WHICH WSBTAB TO USE
	MOVEI	T2,.WSBNZ-WSBTBL(T2)
NXTWS1:	AND	M,[77777,,0]	;CLEAR ALL BITS WE USE
NXTWS2::MOVEI	T1,^D36		;36 BITS PER WORD
	SKIPE	T3,(T2)		;GET THE WSBTAB ENTRY
NXTWS3::JFFO	T3,NXTWS4	;FIND FIRST BIT
	ADDI	M,(T1)		;REST OF WORD 0 - COUNT VIRT PAGE IN M
	HRRZ	T3,M		;GET VPN
	TLNN	M,200000	;TRANSLATED FUNNY SPACE #S YET?
	CAIG	T3,HLGPNO+1+<FYSSIZ/PAGSIZ##>+MXSECN
	CAIL	T3,.UPMVP/PAGSIZ##+MXSECN
	SKIPA	T3,[<.UPMVP/PAGSIZ##>+MXSECN+1] ;RETURN INVALID #
	AOJA	T2,NXTWS2	;AND TRY NEXT WSBTAB WORD
	HRRI	M,(T3)		;M RETURNS PAGE #, NOT T3
	POPJ	P,
NXTWS4:	LSH	T3,1(T4)	;SHIFT OUT BITS THROUGH BIT JUST FOUND
	SUBI	T1,1(T4)	;ACCOUNT FOR NO OF BITS SCANNED
	ADDI	M,1(T4)		;INCR VIRTUAL PAGE
	TLON	M,400000	;1ST TIME?
	SUBI	M,1		;YES, WE'RE 1 TOO HIGH
	HRRZ	T4,M		;GET VIRTUAL PAGE IN T4
	CAIG	T4,HLGPNO	;STILL IN USER SPACE?
	JRST	[MOVE	T4,.USSPT ;GET SECTION #
		 LSH	T4,S2PLSH## ;MAKE FULL PAGE #
		 TRO	T4,(M)
		 JRST	GMPTR ]
	TLOE	M,200000	;NO, TRANSLATE ALREADY?
	JRST	GMPTR		;ALREADY TRANSLATED, GO TO GMPTR
	HRRI	M,<.MCFV/PAGSIZ##>-<HLGPNO+1>(M) ;TRANSLATE #
	HRRZ	T4,M		;RESET T4
				;FALL INTO GTMPTR
;SUBROUTINE TO SETUP A BYTE POINTER TO A HALF WORD IN THE MAP
;ENTER WITH T4=VIRTUAL PAGE NUMBER FOR WHICH THE BYTE PNTR IS TO BE COMPUTED
;PRESERVES T1-T3
GMPTR::	CAIGE	T4,<.MCFV/PAGSIZ##>
	JRST	[ADD T4,[UMAPSN+UMAPS]
		 POPJ P,]
	ADDI	T4,.UPMP-<.MCFV/PAGSIZ##>+.UMORG
	CAILE	T4,.UPMP+.UMUPT		;SECTION MAP?
	MOVEI	T4,SECTAB-.UMORG-<<.UPMVP/PAGSIZ##>-<.MCFV/PAGSIZ##>>(T4)
	POPJ	P,
;SUBROUTINE TO SETUP MEMTAB FOR A SWAP OUT FROM THE WORKING SET TABLE
; AND THE UPMP
;CALLING SEQUENCE:
;
;	MOVSI	J,NUMBER OF PAGES ALLOCATED CONTIGUOUSLY ON THE DISK
;	HRRI	U,STARTING PAGE NUMBER ON THE DISK
;	PUSHJ	P,SETOMT	;FIRST CALL
;	PUSHJ	P,SETOM8	;ALL SUBSEQUENT CALLS
;ALWAYS RETURNS CPOPJ, T1-T3,M,F,P4,R MUST BE PRESERVED FOR SUBSEQUENT CALLS

SETOMT:	SE1ENT			;ENTER SECTION 1
	SETZB	P3,F		;FIRST TIME FOR ANYTHING
	MOVSI	P4,(UP.MAP)	;CLEAR BIT IN
	ANDCAM	P4,.USBTS	;.USBTS INDICATING MAP INFO
	SKIPE	R,.USSPT	;EXTENDED SECTION
	JRST	SETO18		;YES
SETOM1:	SETZB	R,.USSPT	;BE SURE THAT'S WHAT WE'RE DOING
SETOM2:	MOVEI	P4,.UPMVP/PAGSIZ##	;MAX PAGE HERE IS THE UPMP
SETOM3:	PUSHJ	P,NXTWSB	;GET 1ST PAGE OF WORKING SET
SETOM4:	CAIGE	P4,(M)		;LEGAL PAGE #?
	JRST	SETO16		;NO, GO TO NEXT SECTION
	JUMPN	F,SETO10	;MUST BE JUST A SECTION CYCLE
	MOVE	F,(T4)		;GET MAP ENTRY
	TLZE	F,(<PM.DCD>B2)	;ACCESS ALLOWED ON?
	TLZA	F,(PM.NAD)	;YES, CLEAR BITS
	TLZA	F,(<PM.NAD^!<PM.COR!PM.AAB>>) ;ACCESS ALLOWED OFF, SEE IF EXPAND
	JUMPN	F,SETOM5	;PROCEED WITH PARANOIA IF DCD IS ON
	TLZN	F,(PM.AAB)	;AAB ON?
	TLZN	F,(PM.COR)	;NO, COR MUST BE ON TOO IF NOT EXPANSION
	CAIA			;AAB ON OR COR OF, EXPANSION
	JUMPN	F,SETOM5	;FOUND A PAGE TO START AT
	PUSHJ	P,NXTWS3	;MUST BE A NZS SECTION W/O P0
	JRST	SETOM4		;AND COMPARE MORE
SETOM5:	SKIPGE	P3,.USSPT	;GET SECTION
	TDZA	P3,P3		;MAPS
	LSH	P3,^D18+S2PLSH## ;LEFT HALF PAGE #
	TLO	P3,(M)		;VPN
	HRR	P3,F		;P3=1ST VIRT,,1ST PHYS
SETOM6:	SSX	F,MS.MEM	;SET SECTION NUMBER
	MOVEM	U,MEMTAB(F)	;SAVE DISK ADDRESS IN MEMTAB
	MOVEI	T4,(M)
	CAIE	T4,<.UPMVP-1>/PAGSIZ##
	JRST	SETOM7
	MOVE	T4,.USJOB
	DPB	U,JBYMT4##	;SET MAP
SETOM7:	SOJLE	J,SETO15	;DONE
	AOS	U
SETOM8:	JUMPE	P4,SETO22	;JUST DID A MAP
SETOM9:	PUSHJ	P,NXTWS3	;NOT LAST, GET NEXT WS BIT
	CAIGE	P4,(M)		;LEGAL PAGE?
	JRST	SETO16		;NO, NEXT SECTION
SETO10:	MOVE	T4,(T4)		;PHYSICAL PAGE
	TDNN	T4,[PM.ADR]	;ANY DISK ADDRESS?
	JRST	SETOM8		;NO, IGNORE PAGE
	TLZN	T4,(<PM.DCD>B2)
	JRST	SETO14		;NO
SETO11:	JUMPN	P3,SETO12	;IF 1ST TIME,
	SKIPGE	P3,.USSPT
	TDZA	P3,P3
	LSH	P3,^D18+S2PLSH##
	TLO	P3,(M)
	TRO	P3,(T4)		; AND PHYS PAGE IN P3
	JRST	SETO13
SETO12:	TRNE	M,-1^!HLGPNO	;FUNNY PAGE?
	SKIPA	R,M		;YES
	DPB	M,[POINT 9,R,35] ;ELSE R HAS SECTION, MAKE VPN
	DPB	R,NVPNF		;AND SET NEXT VPN
SETO13:	MOVE	F,T4		;F=PHYS PAGE
	JRST	SETOM6		;AND CONTINUE

;HERE IF PM.ACC OFF - EITHER EXPANDING (INSDSK) OR IT REALLY SHOULD BE OFF
SETO14:	TLNN	T4,(PM.AAB)	;IS AAB OFF?
	TLNN	T4,(PM.COR)	;YES, IS COR ON?
	JRST	SETOM8		;SOME FLAVOUR OF EXPANSION, IGNORE
	JRST	SETO11


;HERE AT FRAGMENT'S END
SETO15:	PUSH	P,T1		;SAVE T1
	HLRZ	T1,P3		;FIRST PAGE
	DPB	T1,NVPNF	;POINT LAST TO IT
	MOVSI	T1,(MT.LEF)
	IORM	T1,MEMTAB(F)	;SET LAST
	MOVSI	T1,(UP.MAP)	;MAP INFO THIS FRAGMENT?
	TDNE	T1,.USBTS	;?
	TLO	P2,(SL.MAP)	;YES, SET SWPLST BIT TOO
	ANDCAM	T1,.USBTS	;CLEAR BIT
	JRST	TPOPJ##		;RETURN

;HERE AT THE END OF A SECTION
SETO16:	SKIPG	.USSPT		;SECTION 0 OR MAPS?
	JRST	SETO19		;YES, DO MAPS THEN OR RETURN
SETO17:	SOSGE	R,.USSPT	;COUNT DOWN TO DO NEXT SECTION
	JRST	SETO20		;TIME TO START DOING MAPS
SETO18:	SKIPN	P4,.UPMP+SECTAB(R)
	JRST	SETO17
	TRNN	R,-1		;SECTION 0?
	SKIPA	P4,[.UPMVP/PAGSIZ##-1-1] ;YES, STOP AT MAP THEN
	MOVEI	P4,HLGPNO	;MAX FOR NON-ZERO SECTIONS
	LSH	R,S2PLSH##	;MAKE IT A SECTION # WITH NO PAGE #
	JRST	SETOM3		;START THE SECTION

;HERE WHEN DOING MAPS

SETO19:	JUMPE	P4,SETO21	;DOING MAPS
	CAIN	P4,.UPMVP/PAGSIZ## ;JUST DO UPMP?
	POPJ	P,		;HUH? PROBABLY A UNL THEN
SETO20:
SETO21:	MOVEM	T4,.USTMP	;SAVE RELEVANT VARIABLES
	MOVEM	M,.USTMP+1	;WHICH WILL BE DESTROYED
SETO22:	MOVSI	P4,(UP.MAP)	;SET WE'RE DOING MAPS
	IORM	P4,.USBTS	;SET IT FOR BOSLST
	SKIPL	P4,.USSPT	;FIRST TIME?
	MOVSI	P4,-<MXSECN+1>	;YES
	AOBJP	P4,SETO24	;TIME TO DO SECTION 0
SETO23:	SKIPE	T4,.UPMP+SECTAB(P4);SAVE A LITTLE TIME IN THIS
	TLNE	T4,(<PM.ACD^!PM.DCD>B2) ;INDIRECT POINTER?
	AOBJN	P4,SETO23
	JUMPGE	P4,SETO24	;DONE
	MOVEM	P4,.USSPT	;SAVE POINTER TO CURRENT MAP
	MOVEI	M,.UPMVP/PAGSIZ##(P4)	;PAGE # OF MAP
	MOVEI	T4,(M)		;POINTER
	PUSHJ	P,GMPTR		;GET MAP
	SETZ	P4,		;FLAG TO COME BACK HERE
	HRRZS	R		;AND THE SECTION #
	JRST	SETO10

;HERE TO DO MAP AND UPMP (FINISH SECTION 0)

SETO24:	MOVE	T4,.USTMP	;RESTORE RELEVANT VARIABLES
	MOVE	M,.USTMP+1
	MOVEI	P4,.UPMVP/PAGSIZ## ;NOW UPMP IS LAST
	SETZM	.USSPT		;REALLY SECTION 0 NOW
	JRST	SETO10
;SUBROUTINE TO SETUP THE UPMP WITH PHYSICAL CORE ADDRESSES FROM PAGTAB,
; THE UPMP, THE WORKING SET TABLE, AND THE ACCESS ALLOWED TABLE AND SETUP
; MEMTAB FOR A SWAP IN
;CALLING SEQUENCE:
;
;	PUSHJ	P,SETIMT	;FIRST CALL
;	PUSHJ	P,SETIMX	;ALL SUBSEQUENT CALLS
;RETURNS CPOPJ1 WHEN A NON-CONTIGUOUS DISK ADDRESS IS FOUND. RETURNS
; CPOPJ WHEN THE ENTIRE UPMP HAS BEEN PROCESSED.
;RETURNS P3=# OF PAGES, P1=1ST VIRT,,1ST PHYS
;PRESERVE T1-T4,M,F OVER CALLS TO SETIMX
;BITS IN THE LEFT HALF OF P3 IN BELOW (USED ONLY LOCALLY):
;	1B0	JUST CHANGED SECTION, STILL CONTIGUOUS)
;	1B14	SECTION CHANGE REQUIRED ON NEXT MEMTAB ENTRY
;	1B15	NO I/O TO DO YET
;	1B16	NEED TO ADVANCE PHYSICAL PAGE FOR STORE INTO P1
;	1B17	MAY BE NO PAGES IN REQUEST

SETIMT:	SE1ENT			;ENTER SECTION 1
	PUSH	P,J		;SAVE J
	SKIPE	T1,.USSPT	;SECTION ZERO?
	JRST	SETI20		;SET UP FOR FIRST CALL IN NZS
	LDB	F,JBYLSA##	;FIRST PHYSICAL ADDRESS
SETIM1:	PUSHJ	P,NXTWSB	;GET 1ST PAGE IN WORKING SET
	HRRZ	R,M
	CAILE	R,.UPMVP/PAGSIZ##+MXSECN
	JRST	SETI13
	TLZ	P3,4		;ACTUALLY DOING SOMETHING I/OWISE
	TLZE	P3,400000	;CYCLING THROUGH SECTIONS?
	JRST	SETIM5		;YES, THIS WAS A LOGICAL CALL TO NXTNW3
	MOVE	U,(T4)		;GET MAP WORD
	TLZ	U,(PM.NAD)	;CLEAR EXTRANEOUS BITS
	JUMPE	U,SETI11	;CONTINUE
	JUMPN	P1,SETIM3	;IF JUST CYCLING THROUGH SECTIONS
	SKIPGE	P1,.USSPT
	TDZA	P1,P1
	LSH	P1,S2PLSH##+^D18
	TLO	P1,(M)
	HRR	P1,F		;1ST VIRTUAL,,1ST PHYSICAL
	JRST	SETIM3		;PROCEED
SETIM2:	MOVE	U,(T4)		;GET DSK ADR
	TLZ	U,(PM.NAD)	;MAKE SURE EXTRANEOUS BITS ARE 0
	JUMPE	U,SETIM8	;MAY NOT BE ANY PAGES IN THIS SECTION
SETIM3:	MOVE	J,(T4)		;GET OLD PAGE MAP ENTRY
	AND	J,[PM.BTS]	;KEEP ONLY ACCESSIBILITY BITS
	TLNN	J,(PM.AAB)	;IN AABTAB?
	TRNE	M,<-1^!HLGPGS>	;OR IS IT A MONITOR PAGE?
	TLOA	J,(<PM.DCD>B2)
	TLZ	J,(<PM.ACD>B2)	;NO ACCESS ALLOWED
	TLO	J,(PM.COR)	;NOW A CORE ADDRESS
	ADDI	J,(F)
	MOVE	R,F
	SSX	R,MS.MEM	;SET SECTION NUMBER FOR MEMTAB
	TLNN	F,-1		;THIS PAGE HAVE A DISK ADDRESS?
	MOVEM	U,MEMTAB(R)	;YES, SAVE IN MEMTAB
	MOVEM	J,(T4)		;SAVE IN MAP
SETIM4:	PUSHJ	P,NXTWS3	;GET NEXT PAGE, M=VIRT, T4=MAP POINTER
	HRRZ	R,M
	CAILE	R,.UPMVP/PAGSIZ##+MXSECN
	JRST	SETI12
	CAIE	R,.UPMVP/PAGSIZ##;UPT?
	CAIN	R,.UPMVP/PAGSIZ##-1 ;MAP?
	JRST	SETIM4		;YES
SETIM5:	MOVE	J,(T4)		;DISK ADDR OF NEXT PAGE
	TLZ	J,(PM.NAD)	;CLEAR ACCESS BITS ETC.
	JUMPE	J,SETIM8	;GO IF NO DISK ADDR
	JUMPN	P1,SETIM6	;IF 1ST CALL,
	SKIPGE	P1,.USSPT	;SECTION #
	TDZA	P1,P1
	LSH	P1,S2PLSH##+^D18
	TLNN	P3,2		;NEED TO ADVANCE PHYSICAL PAGE?
	HRR	P1,F		;NO, GET CURRENT 1ST PHYS
SETIM6:	SKIPN	U		;FIND FIRST PAGE YET?
	SOSA	U		;NO, BE SURE WE THINK IT'S CONTIGUOUS
	SUB	U,J
	HLRZ	J,F		;PAGE NUMBER OF LAST PAGE WITH A DSK ADR
	TLZN	F,-1		;IF IT WAS LAST PAGE,
	MOVE	J,F		;GET IT FROM F
	AOJN	U,SETI15	;GO IF NOT CONTIGUOUS
	MOVEI	R,(P3)		;CHECK ONLY RIGHT HALF
	CAIL	R,776		;CAN ONLY STORE NINE BITS
	SOJA	U,SETI15	;FRAGMENT; BE SURE THAT U'S NON-ZERO
SETIM7:	SKIPGE	U,.USSPT	;CURRENT SECTION
	TDZA	U,U
	LSH	U,S2PLSH##	;MAKE SECTION #
	TRO	U,(M)		;SET PART OTHER THAN SECTION
	HRRZ	R,J
	SSX	R,MS.MEM	;MAKE PAGTAB/MEMTAB ADDRESSBLE
	DPB	U,NVPNR		;STORE
	HRR	R,F		;FOR INDEXING INTO PAGTAB
	HRR	F,PAGTAB(R)	;NEXT PHYSICAL PAGE
	TLZE	P3,2		;NEED TO STORE "ADVANCED" PHYSICAL IN P1?
	HRRI	P1,(F)		;YES, DO IT NOW
	AOJA	P3,SETIM2	;COUNT THE PAGE, TEST NEXT
;HERE IF NEXT PAGE HAS NO DISK ADDRESS
SETIM8:	PUSH	P,T2		;SAVE T2 FOR NXTWSB
	MOVE	T2,F		;FOR INDEXING INTO PAGTAB
	SSX	T2,MS.MEM	;SET SECTION NUMBER
	HRRZ	T2,PAGTAB(T2)	;THIS PHYSICAL PAGE
	PUSHJ	P,ZPAGE		;ZERO THE PAGE
	POP	P,T2		;RESTORE T2
	TLNN	F,-1		;FIRST SUCH PAGE?
	HRLS	F		;YES, SAVE IN LH(F)
SETIM9:	MOVE	R,F		;TO GET PAGTAB ENTRY
	SSX	R,MS.MEM	;SET SECTION
	HRR	F,PAGTAB(R)	;STEP TO NEXT PHYSICAL PAGE
SETI10:	JUMPN	P1,SETIM3	;CONTINUE, U=DSK ADR IF NOT 1ST CALL TO SETIMX
	JUMPE	U,SETIM3	;ALSO IF FIRST PAGE OF SECTION IS ABZ,
				;THEN AVOID KAF
	JRST	SETIM2		;1ST CALL TO SETIMX - GET U FROM BYTE PNTR

;HERE SAME AS SETIM8, BUT FOR FIRST TIME THROUGH MAIN SETIM LOOP

SETI11:	TRNN	P3,-1		;ANY PAGES ALREADY IN REQUEST?
	TLO	P3,3		;NO, THERE MAY BE NONE, FLAG PHYSICAL ADVANCE
	PUSH	P,T2		;SAVE T2 FOR NXTWSB
	HRRZI	T2,(F)		;PAGE TO ZAP
	PUSHJ	P,ZPAGE		;ZERO THE PAGE
	POP	P,T2		;RESTORE T2
	JRST	SETI10
;HERE FOR UPMP
SETI12:	TLO	P3,400010	;FLAG A POSSIBLE SECTION CYCLE
SETI13:	SKIPE	.USSPT		;DOING SECTION 0?
	JRST	SETI16		;NO, INCREMENT SECTION #
SETI14:	SOS	-1(P)		;RETURN CPOPJ
	PUSHJ	P,SETHSA	;UPDATE JBTHSA IF NECESSARY
	TLNE	F,-1		;ENDING ON EXPANDING PAGES?
	HLRZ	J,F		;YES, LOAD J WITH LAST PAGE WE DID I/O TO

;HERE AT END OF THIS FRAGMENT
SETI15:	HLRZ	U,P1		;NEXT VPN
	MOVE	R,J		;USE LAST PAGE I/O WAS DONE TO
	SSX	R,MS.MEM	;SET SECTION
	DPB	U,NVPNR		;SET NEXT VIRTUAL PAGE
	MOVSI	U,(MT.LEF)
	IORM	U,MEMTAB(R)
	TLZ	P3,400012	;BE SURE THEY'RE CLEAR
	TLZN	P3,5		;WENT THROUGH SETI11 (COUNT OFF BY ONE)?
				;OR NOT DONE ANY I/O?
	AOJA	P3,JPOPJ1##	;NO, COUNT THE PAGE AND RETURN
	JRST	JPOPJ1##	;NO PAGES IN REQUEST


;HERE IF CONTIGUITY CROSSES A SECTION BOUNDARY
SETI16:
SETI17:	SOSG	T1,.USSPT	;DECREMENT SECTION WORKING ON
	JRST	SETI19		;DONE IF THIS WAS LAST
SETI18:	SKIPN	.UPMP+SECTAB(T1) ;SECTION EXIST?
	JRST	SETI17		;NO, TRY NEXT
	JRST	SETIM1		;CONTINUE FRAGMENT

SETI19:	SETZ	T1,		;RESTORE SECTION 0
	MOVEM	T1,.USSPT
	JRST	SETI14

;HERE ON CALL TO SETIMT FOR NON-ZERO SECTIONS

SETI20:	MOVE	F,.USTMP+5	;WHERE TO START
	SSX	F,MS.MEM	;PAGTAB SECTION
	HRRZ	F,PAGTAB(F)	;GET POINTER TO NEXT PHYSICAL PAGE
				;(AND FLAG TO STORE U IN MEMTAB)
	TLO	P3,4		;IN CASE WE DON'T DO ANY I/O
	JRST	SETI18		;FIND FIRST REAL SECTION

;HERE FOR SUBSEQUENT CALLS
SETIMX:	SE1ENT			;ENTER SECTION 1
	PUSH	P,J		;SAVE J
	SSX	F,MS.MEM	;SET SECTION
	HRRZ	F,PAGTAB(F)	;STEP TO NEXT PHYSICAL PAGE
				;(AND STORE INTO MEMTAB AT SETIM2)
;	JUMPN	P1,SETIM3	;CONTINUE, U=DSK ADR IF NOT 1ST CALL TO SETIMX
				;(THE ABOVE JUMPN CAN'T EVER HAPPEN ANY MORE)
	SKIPGE	P1,.USSPT	;SECTION #
	TDZA	P1,P1
	LSH	P1,S2PLSH##+^D18
	TLO	P1,(M)		;..
	HRR	P1,F
	JRST	SETIM2
;SUBROUTINE TO SETUP AN AOBJN POINTER TO BE USED TO SETUP MEMTAB -
; I.E. XWD -NUMBER OF CONTIGUOUS PAGES ON THE DISK, STARTING
; PAGE ON THE DISK, FROM THE FRAGMENT TABLE POINTED TO BY JBTSWP.
; IT ALSO SETS UP THE JOB NUMBER AND NUMBER OF PAGES
; ENTRIES IN THE SWPLST ENTRY WHICH IS BEING BUILT.
;CALLING SEQUENCE:
;
;	MOVE	P1,POINTER TO THE FRAGMENT TABLE (1ST CALL)
;	MOVEI	P2,SWPLST DESCRIPTORS
;	PUSHJ	P,NXTFRG
;RETURNS CPOPJ IF THIS IS THE LAST ENTRY IN THE FRAGMENT TABLE, CPOPJ1
; IF NOT, P1 POINTS TO THE NEXT ENTRY

NXTFR1:	HRRZ	P1,(P1)		;GET NEXT FRAG TABLE ENTRY
NXTFRG:	SKIPN	J,(P1)		;GET NO OF DSK PAGES
	POPJ	P,		;DONE IF 0
	JUMPL	J,NXTFR1	;POINTER TO ANOTHER ADR IF NEG
	LDB	J,FRGCP1##	;FRAGMENT COUNT
	DPB	J,[POINT SL.SCT,P2,<^L<SL.CNT>+SL.SCT-1>]	;SAVE NUMBER OF PAGES
	LDB	U,FRGAP1##	;FRAGMENT ADDRESS
	AOJA	P1,CPOPJ1##	;GOOD RETURN
;SUBROUTINE TO FIX THINGS UP AFTER A SWAP-WRITE ERROR
MAPBAK::TDZA T1,T1		;NORMAL ENTRY (DON'T CALL XPANDH WHEN DONE)
MAPBAX::MOVEI	T1,UP.JXP	;CALL XPANDH WHEN DONE
	CAILE	J,JOBMAX##
	JRST	DLTSLX
	PUSHJ	P,FNDPDS##	;GET PDB
	HRRZ	S,.PDSCX##(W)	;GET OLD SAVCTX POINTER (IF THERE IS)
	HLLZS	.PDSCX##(W)	;..
	MOVE	T3,.CPMAP##	
	HRRZ	T2,JBTUPM##(J)	;POINT UPT BACK AT ITSELF
	TLO	T2,(<PM.DCD>B2+PM.WRT+PM.PUB+PM.COR+IFE FTMP,<PM.CSH>)
	CONO	PI,PI.OFF	;DON'T LET INTERRUPT LEVEL CHANGE THIS
	MOVEM	T2,.ECKSM/PAGSIZ##(T3)	;SINCE IT POINTED AT NLUPMP
	CLRPT	.ECKSM		
	MOVEM	T2,.ECKSM+.UMUPT	;POINT IT BACK AT ITSELF
	CONO	PI,PI.ON	;OK TO TURN THE PIS ON
	PUSHJ	P,SVEUB##	;MAKE SURE RIGHT STUFF GETS SAVED
	IORM	T1,.USBTS	;SET FLAG FOR WHEN DONE
	PUSHJ	P,SAVCTS##	;SAVE CONTEXT
	  STOPCD .,STOP,OOC	;++OUT OF CORE BLOCKS
	JRST	MAPB12
;HERE AT UUO LEVEL
	PUSHJ	P,MAPBK0	;DO THE REAL WORK
	PJRST	USCHE1##	;CONTINUE NOW
MAPBK0:	PUSHJ	P,SAVE4##	;SAVE ACS
	MOVE	P3,.UPMP+SECTAB+0 ;MAKE SURE REAL SECTION 0 PTR (NOT DISK)
IFN FTMP,<
	PUSHJ	P,MLSCSH##	;CACHED?
	  TLZ	P3,(PM.CSH)	;NO
>
	MOVEM	P3,.UPMP+.UMUPM
	PUSHJ	P,CLPTZO	;FIX PAGING MEMORY
	EXP	.UPMAP
	SE1ENT			;MUST BE IN SECTION 1
	MOVE	J,.USJOB	;DON'T TRUST J
	LDB	P3,JBYLSA##	;YES, SET TO SCAN MEMTAB CHAIN
	ADJSP	P,1		;ALLOCATE STACK SPACE TO REM LAST PAGE
	MOVEI	J,200		;BEFORE CALLING SCDCHK
	SETZM	.USSPT
MAPBK1:	PUSHJ	P,NXTWSB	;GET 1ST PAGE IN WORKING SET
MAPBK2:	HRRZ	P4,M		;UPMP?
	SKIPE	.USSPT		;SECTION 0?
	CAIG	P4,HLGPNO	;NO, THIS IS MAX
	CAILE	P4,.UPMVP/PAGSIZ##+MXSECN
	JRST	MAPBK7		;END THIS SECTION
	MOVE	P2,(T4)		;GET DSK ADR
	TRNE	P2,-1		;EVEN IF PM.COR WILL BE OFF, COULD BE ABZ
	TLNE	P2,(PM.COR)	;IS IT A DISK ADDRESS?
	JRST	MAPBK4		;NO, LOOK AT NEXT PAGE
	HLRZ	P1,P2		;SAVE ACCESS AND SOFTWARE BITS
	TLZ	P2,(PM.NAD)	;KEEP ONLY ADDR IN P2
	ANDI	P1,(PM.BTS)	;AND ONLY BITS IN P1
	HRRZM	P3,(P)		;SAVE PAGE WE START AT
MAPBK3:	SSX	P3,MS.MEM	;MAKE GLOBAL INDEX
	MOVE	P4,MEMTAB(P3)	;MEMTAB FOR THIS PAGE
	TLZ	P4,(<-1^!MT.DAD>)
	CAME	P2,P4		;DSK ADR THE SAME?
	JRST	MAPBK5		;NO
	HRLI	P3,<(PM.COR)>(P1)
	TLNN	P3,(PM.AAB)	;IN AABTAB?
	TRNE	M,<-1^!HLGPGS>	;OR IS IT A MONITOR PAGE?
	TLO	P3,(<PM.DCD>B2)
	MOVEM	P3,(T4)		;SAVE MAP ENTRY IN UPMP
MAPBK4:	PUSHJ	P,NXTWS3	;GET NEXT PAGE IN WORKING SET
	JRST	MAPBK2		;DO SO NOW
MAPBK5:	HRRZ	P3,PAGTAB(P3)	;STEP TO NEXT PAGE OF JOB
	CAMN	P3,(P)		;BEEN HERE BEFORE?
	JRST	MAPBK4		;YES, MUST BE EXPANDING INTO THIS PAGE
	JUMPN	P3,MAPB10	;CONTINUE
	MOVE	P3,J		;SAVE COUNT
	MOVE	J,.USJOB	;RESTORE JOB #
	LDB	J,JBYLSA##	;FIRST PAGE
	EXCH	J,P3		;CONTINUE
MAPB10:	SOJG	J,MAPBK3	;CONTINUE
	MOVE	J,.USJOB	;WAIT
	PUSHJ	P,SCDCHK##	;CHECK
	MOVEI	J,200
	JRST	MAPBK3
;HERE FOR NEXT SECTION

MAPBK7:	SKIPL	.USBTS		;EXTENDED?
	JRST	MAPBK9		;RESTORE INDEX AND FINISH UP
MAPBK8:	AOS	T1,.USSPT	;NEXT SECTION
	CAILE	T1,MXSECN	;MAX?
	JRST	MAPBK9		;RESTORE INDEX AND FINISH UP
	SKIPE	P2,.UPMP+SECTAB(T1) ;SECTION EXIST?
	TLNE	P2,(<PM.ACD^!PM.DCD>B2) ;IMMEDIATE POINTER?
	JRST	MAPBK8		;NO
	JRST	MAPBK1		;DO THIS SECTION
MAPBK9:	HRRZS	.USREL
	MOVE	J,.USJOB	;RESTORE JOB #
	PUSHJ	P,FNDPDS##	;GET PDB
	HRRZ	P1,.PDSCX##(W)	;GET CONTEXT BLOCK POINTER
	HRRZ	P1,(P1)		;AND OLD POINTER (IF ANY)
	PUSHJ	P,RESCTS##	;GET RID OF THIS SAVCTX BLOCK
	DMOVEM	T1,.CPPC##	;SAVE PC
	MOVSI	T1,NSWP!NSHF	;RESTORE STATE OF NSWP,NSHF
	AND	T3,T1
	ANDCAM	T1,JBTSTS##(J)
	IORM	T3,JBTSTS##(J)
	HRRM	P1,.PDSCX##(W)
	MOVEI	P1,UP.JXP	;CALL XPANDH FOR SWAPPER?
	TDNE	P1,.USBTS	;?
	PUSHJ	P,XPANDH##	;YES, FORCE JOB OUT NOW
	ANDCAM	P1,.USBTS
	JRST	TPOPJ##		;FIX STACK AND RETURN

;HERE AFTER SETTING UP AT CLOCK LEVEL TO RUN MAPBAK AT UUO LEVEL

MAPB12:	MOVSI	T1,NSHF		;MAKE SURE SWAPPER DOESN'T FIDDLE WITH US
	IORM	T1,JBTSTS##(J)	;(SET NSHF BUT NOT NSWP)
	MOVSI	T1,SWP!SHF!JXPN!NSWP	;SO JOB WILL RUN
	ANDCAM	T1,JBTSTS##(J)	;CLEAR SO THE SCHEDULER KNOWS IT
	PUSHJ	P,REQUE##	;PUT INTO A QUEUE
	S0JRST	DLTSLX		;RETURN SWPLST ENTRY
;SUBROUTINE TO SET/CLEAR IO IN S FOR SWAPPER
;CALLED IN ERROR RECOVERY SINCE DEVIOS CAN CHANGE IN SWPDDB
;CALL WITH U SET UP

SWPINS::MOVE	T1,UNISWA##(U)	;POINTER TO SWPLST
	MOVE	T1,(T1)		;GET SWPLST ENTRY
	TLNN	T1,(SL.DIO)	;INPUT?
	TLZA	S,IO		;YES
	TLO	S,IO		;NO
	POPJ	P,

;SUBROUTINE TO DETERMINE IF A JOB HAS A PAGE OR PAGES ON EITHER THE "IN" QUEUE
; OR THE "SN" QUEUE. CALL WITH J=JOB NUMBER, SKIPS IF JOB ISN'T ON EITHER QUEUE
;PRESERVES ALL ACS (SINCE CALLED FROM THE SWAPPER, THAT'S THE ONLY SAFE THING TO DO)

JOBNQ::	PUSHJ	P,SVEUB##	;SEE IF JOB IS VIRTUAL
	SKIPN	.USVRT		;IS HE?
	  JRST	CPOPJ1##	;NO, CAN'T HAVE PAGES ON ANY QUEUE
	PUSHJ	P,SAVT##	;SAVE ALL ACS
	MOVEI	T1,PAGINQ##	;LOOK AT "IN" QUEUE FIRST
	PUSHJ	P,JOBNPQ	;SEE IF JOB HAS PAGES ON THAT QUEUE
	  POPJ	P,		;YES
	MOVEI	T1,PAGSNQ##	;NOW LOOK AT "SN" QUEUE
	PUSHJ	P,JOBNPQ
	  POPJ	P,		;ON THE SN QUEUE
	MOVEI	T1,PAGIPQ##	;CHECK THE IP QUEUE AS WELL
;FALL INTO JOBNPQ

;SUBROUTINE TO SEE IF A JOB HAS ANY PAGES ON A PAGE QUEUE
;CALL WITH T1 = ADDRESS OF QUEUE, SKIPS IF JOB ISN'T ON THE QUEUE

JOBNPQ:	SKIPN	T1,(T1)		;NOT ON QUEUE IF QUEUE IS EMPTY
	JRST	CPOPJ1##	;NOT ON QUEUE RETURN
	SE1ENT			;MUST BE IN SECTION 1 TO REFERENCE PAGTAB/MEMTAB
JOBNP1: SSX	T1,MS.MEM
	LDB	T2,[POINT MT.SJB,MEMTAB(T1),<^L<MT.JOB>+MT.SJB-1>] ;GET JOB NUMBER FROM QUEUE
	HLL	T2,MEMTAB(T1)	;AND BITS
	TLNN	T2,(MT.IPC)	;IF THIS IS AN IPCF PAGE, DON'T WORRY
	CAIE	J,(T2)
	SKIPA	T1,PAGTAB(T1)	;GET NEXT PAGE
	POPJ	P,		;YES, INDICATE THAT TO CALLER
	JUMPE	T1,JOBNP1	;CHECK IF IT EXISTS
	JRST	CPOPJ1##	;NOT ON THE QUEUE
;SUBROUTINE TO CLEAR A JOB'S PAGES IN ANY PAGING QUEUE (ANY IN QUEUE OR
;THE IP QUEUE).  CALLED BY CORE 0 PROCESSING
JOBFNQ::
	PUSHJ	P,SVEUB##	;NEED TO MAP JOB
	SE1ENT			;MUST BE IN SECTION 1
JOBFN2:	SKIPN	.USVRT		;IS JOB VIRTUAL?
	POPJ	P,		;NO, NOTHING TO DO
	SKIPN	PAGINC##	;ANYTHING ON THE FAST "IN" QUEUE?
	JRST	JOBFN3		;NO
	MOVEI	T1,PAGINQ##	;POINT THERE
	PUSHJ	P,JOBNPQ	;SEE IF ANY PAGES ON THAT QUEUE FOR THIS JOB
	  SKIPA	T1,MEMTAB(T1)	;YES, MUST PROCESS IT
	JRST	JOBFN3		;NO PAGES ON IN QUEUE, PROCEED
	ANDI	T1,MT.VSN+MT.VPN ;SAVE JUST PAGE #
	PUSHJ	P,DLTPAG	;GET RID OF THE PAG
	JRST	JOBFN2		;AND LOOK FOR MORE
JOBFN3:	SKIPN	PAGSNC##	;ANYTHING ON THE SLOW QUEUE?
	JRST	JOBFN6		;NO
	SKIPN	.USVRT		;ONLY CONTINUE IF STILL VIRTUAL
	POPJ	P,		;DONE IF NOT
	MOVEI	T1,PAGSNQ##	;SLOW QUEUE
	PUSHJ	P,JOBNPQ	;FIND ANYTHING THERE
	  SKIPA	T1,MEMTAB(T1)	;YES, MUST PROCESS IT
	JRST	JOBFN6		;NO MORE HERE
	ANDI	T1,MT.VSN+MT.VPN
	PUSHJ	P,DLTPAG	;GET RID OF IT
	JRST	JOBFN3		;CONTINUE LOOKING
JOBFN6:	SKIPE	PAGIPC##	;ANY PAGES ON IN PROGRESS QUEUE?
	SKIPN	.USVRT		;AND IS JOB STILL VIRTUAL?
	POPJ	P,		;NO TO ONE; ALL DONE
	MOVEI	T1,PAGIPQ##	;POINT TO IP QUEUE
	PUSHJ	P,JOBNPQ	;FIND PAGE THERE
	  CAIA			;FOUND ONE
	POPJ	P,		;DONE
	MOVSI	T2,(MT.GPB)	;GIVE PAGE BACK
	IORM	T2,MEMTAB(T1)
	JRST	JOBFN6
;SUBROUTINE TO REMOVE ALL DISK PAGES FOR JOB.  CALLS SCDCHK, SO
;MUST BE RUN AT UUO LEVEL
DSKFLS::
	SKIPN	.USVRT		;VIRTUAL
	POPJ	P,		;NO, CAN'T HAVE ANY OF THESE THEN
	SE1ENT
	SETZM	.USSPT		;START AT S0
DSKFL0:	PUSHJ	P,NXTNWS	;GET FIRST NON-WORKING SET PAGE
DSKFL1:	TRNE	M,-1^!HLGPNO	;DONE THIS SECTION?
	JRST	DSKFL6		;YES
	PUSHJ	P,DSKFLX	;REMOVE PAGE
	PUSHJ	P,NXTNW3	;CONTINUE LOOKING
	JRST	DSKFL1
DSKFL6:	SKIPL	.USBTS		;EXTENDED USER?
	POPJ	P,		;NO, DONE
	PUSH	P,.USSPT	;IN CASE SOMETHING HAPPENS
	PUSHJ	P,SCDCHK##
	POP	P,.USSPT
	AOS	T1,.USSPT	;NEXT SECTION
DSKFL7:	CAILE	T1,MXSECN	;AT TOP
	POPJ	P,		;YES, DONE
	SKIPE	T2,.UPMP+SECTAB(T1) ;YES, DOES SECTION EXIST?
	TLNE	T2,(<PM.ACD^!PM.DCD>B2) ;AND INDIRECT
	AOJA	T1,DSKFL7
	MOVEM	T1,.USSPT	;SAVE
	JRST	DSKFL0
;HERE TO DELETE PAGE WE FOUND
DSKFLX:	PUSHJ	P,SAVT##	;SAVE TS FOR NXTNWS
	MOVE	T1,.USSPT	;GET SECTION
	LSH	T1,S2PLSH##
	IORI	T1,(M)
	JRST	DLTPAG
;SUBROUTINE TO RETURN THE NEXT PAGE NOT IN THE WORKING SET
;RETURNS CPOPJ, WITH RH(M)=VIRT PAGE, T4=CONTENTS OF MAP SLOT
;ZERO PAGES AND PM.ZER PAGES ARE IGNORED
;NOTE THAT THIS ROUTINE ASSUMES ALL PER-PROCESS PAGES ARE IN THE WORKING SET
;PRESERVE T1-T3, M ACROSS CALLS (CALL NXTNW3 AFTER FIRST CALL)
;LIKE NXTWSB, BITS 3-17 OF M ARE PRESERVED FOR USE BY THE CALLER
;AND BITS 0-2 OF M (AS WELL AS RH(M) ARE FOR USE BY THIS ROUTINE
NXTNWS::
	SKIPGE	.USBTS		;NOT A BIG USER?
	SKIPG	T2,.USSPT	;SECTION POINTER
	JRST	[MOVEI	T2,WSBTAB
		 JRST	NXTNW1	] ;SECTION ZERO; OLD WAY
	HRRZS	T2
	IMULI	T2,WSBTBL
	MOVEI	T2,.WSBNZ-WSBTBL(T2)
NXTNW1:	AND	M,[77777,,0]	;CLEAR ALL BITS WE USE
NXTNW2:	MOVEI	T1,^D36		;36 BITS IN A WORD
	SETCM	T3,(T2)		;BITS FOR EVERY NON-WS PAGE
NXTNW3::
	JFFO	T3,NXTNW4	;FIND A 1-BIT
	ADDI	M,(T1)		;REST OF WORD 0, STEP TO NEXT
	MOVEI	T4,(M)		;GET PAGE #
	CAILE	T4,HLGPNO	;IN USER SPACE?
	TDZA	T4,T4		;NO, OFF THE END (SEE NOTE TOP OF THIS ROUTINE)
	AOJA	T2,NXTNW2
	POPJ	P,
NXTNW4:	LSH	T3,1(T4)	;CLEAR THE BIT
	SUBI	T1,1(T4)	;COUNT THE BIT
	ADDI	M,1(T4)		;...
	TLON	M,400000	;FIRST TIME?
	SUBI	M,1		;YES, WE'RE 1 TOO HIGH
	MOVEI	T4,(M)		;VIRTUAL PAGE #
	CAILE	T4,HLGPNO	;IN USER SPACE?
	TDZA	T4,T4		;NO, RETURN ZERO
	SKIPA	T4,.USSPT
	POPJ	P,
	LSH	T4,S2PLSH##
	TRO	T4,(M)
NXTNW5:	PUSHJ	P,GTPM4		;GET MAP CONTENTS
	JUMPE	T4,NXTNW3	;UNALLOCATED PAGE
	TDNE	T4,[PM.ADR^!PM.ZER] ;ABZ PAGE?
	PUSHJ	P,NXTHS		;SEE IF 0 OR HIGH SEG PAGE
	  JRST	NXTNW3		;0 OR PM.ZER, TRY NEXT
	TLNN	T4,(PM.SSP!PM.OIQ) ;QUEUE PAGE?
IFN FTPEEKSPY,<
	TLNE	T4,(PM.SPY) ;SPY PAGE?
	JRST	NXTNW3		;YES, DON'T FIND IT
>
	POPJ	P,		;"REAL" PAGE, RETURN IT

;SUBROUTINE TO SEE IF MAP CONTENTS IS 0 OR IN A HIGH SEG
;PRESERVES ALL ACS
NXTHS:	JUMPE	T4,CPOPJ##	;IF MAP IS NON-0
	PUSHJ	P,SAVT##	;SAVE ALL ACS
	HRRZ	T1,M		;PAGE TO TEST
	PJRST	TPNSHS		;GO SEE IF IT IS IN A HIGH SEG
;SUBROUTINE TO TEST IF A DISK ADDRESS IS ON A UNIT BEING REMOVED
;ENTER T4=C(MAP SLOT)  -  DSK ADR
;EXIT CPOPJ IF UNIT GOING DOWN, CPOPJ1 IF NOT
;PRESERVES T1-T3
TSTUN::
IFN FTPEEKSPY,<
	TLNE	T4,(PM.SPY)	;SPY PAGE?
	JRST	CPOPJ1##	;YES, IT'S NOT ON A BAD UNIT
>
	LDB	T4,SSUNT4##	;GET SWAP UNIT
	SKIPLE	T4,SWPTAB##(T4)	;UNIT DATA BLOCK LOC
	SKIPGE	UNIFKS##(T4)	;IF NEGATIVE, GOING DOWN TOO
	JRST	CPOPJ##		;UNIT ALREADY DOWN
	MOVE	T4,UNIUST##(T4)	;STATUS
	TLNN	T4,UNPNNA##+UNPRSS## ;UNIT GOING DOWN?
	AOS	(P)		;NO, SKIP RETURN
	POPJ	P,		;RETURN

;SUBROUTINE TO COMPLEMENT A BIT IN WSBTAB
;ENTER RH(M) = VIRTUAL PAGE NUMBER
;PRESERVES T1-T3
;NOTE THAT .USSPT MUST NOT BE AN INDIRECT SECTION
BTCOM::
	PUSH	P,T1		;SAVE T1
	PUSH	P,T2		;AND T2
	SKIPG	T1,.USSPT	;CURRENT SECTION
	TDZA	T1,T1		;IS REALLY S0
	LSH	T1,S2PLSH##	;CONVERT TO PAGE #
	ADDI	T1,(M)
	PUSHJ	P,TSWST		;COMPUTE WSBTAB BIT, POSITION
	  JFCL
	XORM	T2,(T4)		;FLIP THE BIT IN WSBTAB
	JRST	TTPOPJ##	;NO
;SUBROUTINE TO ALLOCATE MONITOR PAGES
;CALLING SEQUENCE:
;	MOVE	T1,FIRST PAGE TO ALLOCATE
;	MOVE	T2,NUMBER OF PAGES TO ALLOCATE
;	MOVE	T3,SIGN BIT=FLAG 0=CACHED,NON-ZERO=UNCACHED (KL ONLY)
;	PUSHJ	P,CREMPG
;	  RETURNS HERE IF NO VIRTUAL CORE
;	RETURNS HERE WHEN THE PAGES HAVE BEEN ALLOCATED
;STOPS IF THE PAGES ALREADY EXIST, CALLERS RESPONSIBILITY TO
; CHECK TO SEE THAT THE PROGRAM ISN'T GETTING TO BIG

CREMPG:
IFN FTMP,<
	PUSHJ	P,GGVMM##	;GET THE MM
>
GETMPG::
	PUSHJ	P,SAVE3##	;SAVE P1-P3
IFN FTKL10,<
	MOVE	P3,T3		;SAVE CACHED/UNCACHED FLAG
>
	CAMLE	T2,VIRTAL##	;MAKE SURE THERE'S ENOUGH SPACE
	POPJ	P,		;FAIL
	DMOVEM	T1,P1		;SAVE STARTING PAGE NUMBER, NUMBER OF PAGES
	MOVN	T3,T2		;MINUS THE NUMBER OF PAGES OF INCREASE
	ADDM	T3,VIRTAL##	;DECREMENT TOTAL SYSTEM VIRTUAL MEMORY AVAILABLE
CREMP1:	MOVE	T3,P2		;NUMBER OF PAGES TO ALLOCATE
	PUSHJ	P,FRDCRW##	;ENOUGH CORE IN CORE TO SATISFY REQUEST?
	  JRST	CREMP5		;NO, ALLOCATE CORE ON THE DISK
	SE1ENT			;ENTER SECTION 1 TO ADDRESS PAGTAB
	PUSH	P,J		;SAVE J, COULD POINT AT A KDB
	MOVE	J,.CPJOB##	;CURRENT JOB
	MOVE	T3,[MCPCNT]	;ONE IN COUNT OF FUNNY PAGES BYTE
	IMULI	T3,(P2)		;TIMES THE NUMBER OF FUNNY PAGES TO ALLOCATE
	ADDM	T3,JBTPDB##(J)	;UPDATE NUMBER OF FUNNY PAGES OWNED BY THIS JOB
	PUSHJ	P,SCPAGS##	;FIND THE LAST PAGE ALLOCATED TO THIS JOB
	MOVE	T2,P2		;NUMBER OF PAGES REQUESTED
	PUSHJ	P,ADPAGS##	;ALLOCATE THAT NUMBER
	EXCH	P1,T1		;P1 = FIRST PHYSICAL PAGE ALLOCATED, T1 = VIRTUAL PAGE
IFN FTLOCK,<
	LDB	T2,JBYLSA##	;PAGE 0
	SSX	T2,MS.MEM	;PAGTAB SECTION NUMBER
	HLR	P3,PAGTAB(T2)	;LOCK BITS
>
	SSX	P1,MS.MEM	;PAGTAB SECTION
	JRST	CREMP3		;GO ADD THE PAGES TO THE JOB'S MAP
CREMP2:	SSX	P1,MS.MEM	;SET PAGTAB SECTION NUMBER
	HRRZ	P1,PAGTAB(P1)	;NEXT PHYSICAL PAGE
CREMP3:
IFN FTLOCK,<
	HRLM	P3,PAGTAB(P1)	;LOCK BITS
>
IFN FTKL10,<
	JUMPGE	P3,CREMP4	;JUMP IF CACHED PAGE
	PUSHJ	P,WSBIT		;SETUP FOR CALL TO INSMPU
	MOVE	T3,P1
	HRLI	T3,(<PM.DCD>B2+PM.WRT+PM.PUB+PM.COR)

	PUSHJ	P,INSMPU	;PUST THE PAGE IN THE JOB'S MAP
	CAIA			;SKIP
CREMP4:>
	PUSHJ	P,INSMPC	;INSERT THE PAGE IN THE JOB'S MAP CACHED
	ADDI	T1,1		;NEXT VIRTUAL PAGE
	SOJG	P2,CREMP2	;LOOP OVER ALL PAGES ALLOCATED
	CLRPGT	(0)		;CLEAR MEMORY OF PREVIOUS MAP
	JRST	JPOPJ1##	;RESTORE J AND RETURN
CREMP5:	PUSH	P,J		;SAVE J, COULD POINT AT A KDB
	MOVE	J,.CPJOB##	;CURRENT JOB
IFN FTLOCK,<
	MOVSI	T2,NSHF!NSWP	;JOB IS LOCKED BITS
	TDNN	T2,JBTSTS##(J)	;IS THE JOB LOCKED?
	JRST	CREMP6		;NO
IFN FTMP,<
	PUSHJ	P,DWNMM##	;RETURN THE MEMORY MANAGEMENT RESOURCE
>
	PUSH	P,T1		;YES, SAVE STARTING PAGE NUMBER
	MOVEI	T1,1		;SLEEP 1 SECOND SINCE CANNOT SWAP OUT JOB TO ALLOCATE CORE
	PUSHJ	P,SLEEPF##	;ZZZZZZ
	POP	P,T1		;RESTORE STARTING PAGE NUMBER
IFN FTMP,<
	PUSHJ	P,UPMM##	;RE-GET THE MM RESOURCE
>
	POP	P,J
	JRST	CREMP1		;AND START OVER FROM SCRATCH
>
IFE FTLOCK,<
	SETZ	T2,		;FOR CALL TO IOWAT BELOW
>
CREMP6:	PUSH	P,S		;SAVE S (FILSER IS PARTICULAR ABOUT THAT)
	PUSH	P,F		;AND F
	PUSHJ	P,IOWAT		;WAIT FOR ALL I/O TO STOP SINCE JOB WILL BE SWAPPED
	POP	P,F		;RESTORE F
	POP	P,S		;AND S
	LDB	T2,NFYPGS##	;NUMBER OF FUNNY PAGES CURRENTLY ALLOCATED TO THE JOB
	ADD	T2,P2		;PLUS NUMBER OF PAGES OF INCREASE
	DPB	T2,IFYPGS##	;NUMBER OF PAGES REQUIRED WHEN SWAPPED BACK IN
CREMP7:
IFN FTKL10,<
	PUSHJ	P,[JUMPG P3,INSDMC ;IF CACHED
		   JRST   INSDMU   ] ;IF NOT
>
IFN FTKS10,<
	PUSHJ	P,INSDMU	;INSERT PAGE (NEEDN'T WORRY ABOUT CACHE)
>
	ADDI	T1,1		;NEXT VIRTUAL PAGE
	SOJG	P2,CREMP7	;NEXT PAGE
	PUSHJ	P,XPANDH##	;MARK JOB AS EXPANDING
	POP	P,J		;RESTORE J
IFN FTMP,<
	PUSHJ	P,REMMM##	;GIVE UP MM NOW THAT XJOB AND JXPN AGREE
>
	AOS	(P)		;EVERYTHING IS OK
	PJRST	WSCHED##	;AND WAIT UNTIL THE JOB IS BACK IN CORE
;ROUTINE TO REQUEST ALLOCATION OF MONITOR PAGES AT CLOCK LEVEL.
;WHEN ALLOCATION IS COMPLETE (MAY TAKE A WHILE) THE CALLER WILL
;BE CALLED AT THE ADDRESS THEY SPECIFIED.  THIS ROUTINE CAN BE
;CALLED ON ANY CPU BUT WILL ONLY RECALL ON THE BOOT CPU.
;CALL:
;	T1/ SECTION #,,# PAGES REQUIRED
;	T2/ ADDRESS OF ROUTINE TO CALL WHEN ALLOCATION COMPLETE
;	T3-T4/ ARGUMENTS FOR CALLED ROUTINE (RETURNED IN T3-T4)
;	PUSHJ	P,GETCLP
;RETURN:
;	CPOPJ ALWAYS

	CLPLNK==0		;LINK TO NEXT REQUEST IN CHAIN
	CLPPGS==1		;SECTION NUMBER,,NUMBER OF PAGES TO ALLOCATE
	CLPRTN==2		;ROUTINE TO CALL WHEN ALLOCATION COMPLETE
	CLPARG==3		;ARGUMENTS TO ROUTINE
	CLPADR==5		;VIRTUAL ADDRESS OF PAGES ALLOCATED

	CLP4WD==2		;NUMBER OF 4 WORD BLOCKS FOR REQUEST

GETCLP::PUSHJ	P,SAVE4##	;SAVE P1-P4
	DMOVE	P1,T1		;SAVE CALLER'S ARGUMENTS
	DMOVE	P3,T3		;...
	MOVEI	T2,CLP4WD	;GET MEMORY FOR REQUEST BLOCK
	S0PSHJ	GET4WD##	;ASK
	  STOPCD .,STOP,CAC,	;++CAN'T ALLOCATE CLOCK LEVEL PAGES
	SETZM	CLPLNK(T1)	;CLEAR LINK WORD
	DMOVEM	P1,CLPPGS(T1)	;SAVE ARGUMENTS
	DMOVEM	P3,CLPARG(T1)	;...
	SYSPIF			;PREVENT RACES
	MOVEM	T1,@CLPBOT##	;LINK NEW ENTRY ONTO END OF LIST
	MOVEM	T1,CLPBOT##	;MAKE THIS ENTRY THE NEW END
	JRST	ONPOPJ##	;RESTORE PI'S AND RETURN
;ROUTINE TO LOOP THROUGH THE CLP LIST AND ALLOCATE PAGES.
;CAN ONLY BE CALLED ON THE BOOT CPU.

CRECLP::SE1ENT			;MUST RUN IN NZS
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	P1,CLPTOP##	;GET FIRST ENTRY IN LIST
	MOVE	T1,CLPPGS(P1)	;GET NUMBER OF PAGES REQUESTED
IFN FTMP,<
	JUMPL	T1,CRECL1	;JUMP IF WAITING FOR THE CACHE OR DATAO PAG,
	PUSHJ	P,GETMM##	;MUST HAVE THE MM
	  POPJ	P,		;CAN'T GET IT, TRY AGAIN LATER
>; END IFN FTMP
	HRRZ	T3,T1		;NUMBER OF PAGES TO ALLOCATE
	PUSHJ	P,FRDCR##	;SEE IF THE CORE IS AVAILABLE IN CORE
IFE FTMP,<
	  POPJ	P,		;NOT NOW, WAIT A WHILE
>; END IFE FTMP
IFN FTMP,<
	  PJRST	GIVMM##		;NOT NOW, WAIT A WHILE (GIVE UP MM)
>; END IFN FTMP
	PUSHJ	P,SAVR##	;SAVE R (JUST SUPERSTITION I HOPE)
	HLRZ	R,CLPPGS(P1)	;SECTION TO ALLOCATE FROM
IFE FTXMON,<
	SKIPE	R		;ANY SECTION NUMBER?
	PUSHJ	P,SVDCN##	;YES, ENTER DECNET CONTEXT
>; END IFE FTXMON
	HRRZ	T2,T1		;NUMBER OF PAGES TO ALLOCATE
	PUSHJ	P,CREMNS	;ALLOCATE PAGES, SETUP MAPS
IFE FTMP,<
	  STOPCD CPOPJ##,DEBUG,FDL, ;++FRDCR LIED
>; END IFE FTMP
IFN FTMP,<
	  STOPCD GIVMM##,DEBUG,FDL, ;++FRDCR LIED
	PUSHJ	P,GIVMM##	;NOW OK TO RETURN THE MM
>; END IFN FTMP
	MOVEM	T1,CLPADR(P1)	;SAVE VIRTUAL ADDRESS OF PAGES JUST ALLOCATED
IFN FTMP,<
	HRROS	CLPPGS(P1)	;INDICATE WAITING ON CACHE AND PAGING MEMORY
				; ON OTHER CPUS
	HRROI	T1,[MOVE  T2,.CPCSN##-.CPCDB##(P1) ;BUMP CACHE SWEEP NUMBER
		    ADDI  T2,2	; ON ALL OTHER CPUS
		    MOVEM T2,.CPCSW##-.CPCDB##(P1) ;...
		    POPJ  P,]	;RETURN FROM CPUAPP
	PUSHJ	P,CPUAPP##	;CAUSE ALL CPUS TO SWEEP CACHE AND DO A DATAO PAG,
CRECL1:	PUSHJ	P,MLSCSH##	;SEE IF MONITOR LOW SEGMENT CACHED (ONLY 1 CPU RUNNING)
	  CAIA			;NO
	JRST	CRECL2		;YES, DON'T HAVE TO WORRY ABOUT OTHER CPUS
	HRROI	T1,[SKIPGE .CPOK##-.CPCDB##(P1) ; (ONLY CHECK IF CPU ALIVE AND WELL)
		    SKIPN  .CPCSW##-.CPCDB##(P1) ;HAVE WE SWEPT YET?
		    CAIA	;HAVE SWEPT OR CAN'T BECAUSE CPU HAS DIED
		    SETO   T3,	;FLAG SOMEONE STILL NEEDS TO SWEEP
		    POPJ   P,]	;RETURN FROM CPUAPP
	SETZ	T3,		;ASSUME ALL CPUS ARE DONE
	PUSHJ	P,CPUAPP##	;HAVE ALL CPUS SWEPT AND CLEARED PAGING MEMORY?
	JUMPN	T3,CPOPJ##	;NO, STILL HAVE TO WAIT A WHILE
>; END IFN FTMP
CRECL2:	MOVE	T1,CLPADR(P1)	;GET VIRTUAL ADDRESS OF MEMORY ALLOCATED
	HRRZ	T2,CLPPGS(P1)	;GET NUMBER OF PAGES REQUESTED
	DMOVE	T3,CLPARG(P1)	;GET ARGUMENTS FOR ROUTINE
	PUSHJ	P,@CLPRTN(P1)	;CALL THE NOTIFICATION ROUTINE
	SYSPIF			;PREVENT RACES
	MOVE	T1,CLPLNK(P1)	;GET LINK TO NEXT BLOCK
	JUMPN	T1,CRECL3	;JUMP IF THERE IS ONE
	MOVEI	T2,CLPTOP##	;NOTHING, RESET EMPTY QUEUE
	MOVEM	T2,CLPBOT##	;...
CRECL3:	MOVEM	T1,CLPTOP##	;MAKE NEXT NEW FIRST
	SYSPIN			;OK TO INTERRUPT AGAIN
	MOVEI	T1,CLP4WD	;NUMBER OF 4-WORD CHUNKS
	MOVE	T2,P1		;ADDRESS OF THEM
	S0JRST	GIV4WD##	;RETURN THE CHUNK AND RETURN
;SUBROUTINE TO ALLOCATE AND MAKE ADDRESSABLE PAGES IN A NON-ZERO SECTION
;CALLING SEQUENCE:
;	MOVEI	R,SECTION NUMBER PAGES ARE TO BE ALLOCATED IN
;		  IF (R) = 0, ALLOCATE IN FUNNY SPACE
;	MOVEI	T2,NUMBER OF PAGES TO BE ALLOCATED
;	PUSHJ	P,CREMNS
;	  ...			;RETURNS HERE IF NOT POSSIBLE TO ALLOCATE PAGES
;	...			;RETURNS HERE WITH T1 = VIRTUAL ADDRESS OF PAGES ALLOCATED
;MUST BE CALLED IN SECTION 1 IF ALLOCATING NZS SPACE

CREMNS::
	JUMPE	R,CREFSP	;IF (R) = 0, ALLOCATE A PAGE OF FUNNY SPACE
	PUSHJ	P,SAVE4##	;SAVE WORKING ACS
	MOVE	P1,T2		;SAVE NUMBER OF PAGES REQUESTED
;VERIFY THAT ALLOCATING A PHYSICAL PAGE WON'T PREVENT ANY JOB FROM BEING SWAPPED
; IN AND ADJUST CORMAX IF NEED BE TO KEEP ANY JOB FROM GETTING TO BIG AFTER A PAGE
; IS ALLOCATED. ACCOUNT FOR SECTION MAP IF IT MUST BE ALLOCATED.
;	INSERT CODE HERE
IFN FTXMON,<
	MOVE	T1,R		;SECTION TO ALLOCATE FROM
	ADD	T1,.CPEPT##	;ADDRESS OF THE EPT
	SKIPE	T2,SECTAB(T1)	;SECTION ALREADY EXIST?
	JRST	CREMN1		;YES, JUST FIND THE FREE PAGES
;HERE WHEN A PAGE MUST BE ALLOCATED FOR A SECTION MAP
	MOVEI	T3,1		;ALLOCATE 1 PAGE
	PUSHJ	P,GETPHP	;GET A PAGE OF PHYSICAL MEMORY
	MOVE	T2,T1		;PAGE NUMBER
	MOVE	T1,R		;SECTION NUMBER
	MOVEM	T2,SPTCOM##-<(MS.FMD)>(T1)	;STORE IN SPT SLOT
	ADDI	T1,SECTAB	;WHERE TO STORE
	PUSHJ	P,STMAPS	;STORE PAGE NUMBER IN THE SECTION MAP
>;END IFN FTXMON
IFE FTXMON,<
	MOVE	T2,.CPEPT##	;EPT FOR THIS CPU
	MOVE	T2,SECTAB+0(T2)	;CURRENT S0 MAP
>;END IFE FTXMON
CREMN1:	HRLI	T2,(<PM.DCD>B2+PM.WRT+PM.PUB+PM.COR)
	MOVEM	T2,.UPMP+.UMTMP
	CLRPGT	(0)
	PUSH	P,T2		;SAVE FOR LATER
	MOVSI	T1,-<HLGPNO+2>	;MAX # OF SLOTS+1 (FOR PRE-INCREMENT)
CREMN2:	HRRZ	P2,T1		;FIRST FREE SLOT SEEN
	MOVEI	T3,0		;NUMBER OF CONTIGUOUS FREE SLOTS SEEN SO FAR
CREMN3:	CAIL	T3,(P1)		;FOUND ENOUGH FREE SLOTS YET?
	JRST	CREMN4		;YES
	AOBJP	T1,TPOPJ##	;SECTION FULL, CAN'T ALLOCATE HERE
	SKIPE	.TEMP-1(T1)	;NEXT SLOT FREE?
	JRST	CREMN2		;NO, LOOK HIGHER UP
	AOJA	T3,CREMN3	;YES, SEE IF ENOUGH SLOTS HAVE BEEN FOUND
CREMN4:	MOVE	T3,P1		;NUMBER OF PAGES TO ALLOCATE
	PUSHJ	P,GETPHP	;GET THE PAGES
	POP	P,.UPMP+.UMTMP ;MAKE THE SECTION MAP ADDRESSABLE
	CLRPGT	(0)		;FLUSH PAGING MEMORY
	MOVE	P3,P2		;STARTING MAP SLOT (NEEDED FOR VIRTUAL ADDRESS)
	HRRZ	P4,T1		;FIRST PHYSICAL PAGE ALLOCATED
CREMN5:	MOVE	T2,P4		;PAGE NUMBER TO T2
	MOVEI	T1,.TEMP(P2)	;WHERE TO STORE IN THE SECTION MAP
	PUSHJ	P,STMAPE	;MAKE THE MAP ENTRY
	SSX	P4,MS.MEM	;PAGTAB SECTION
	HRRZ	P4,PAGTAB(P4)	;NEXT PAGTAB ENTRY
	TRNE	P4,-1		;LAST PAGE?
	AOJA	P2,CREMN5	;NO, LOOP MAKING ENTRIES FOR EVERY PAGE ALLOCATED
	CLRPGT	(0)		;CLEAR PAGING MEMORY
	MOVE	T1,P3		;FIRST SLOT ALLOCATED
	LSH	T1,P2WLSH##	;ADDRESS WITHIN THE SECTION
	SSXE	T1,(R)		;FULL VIRTUAL ADDRESS OF CORE ALLOCATED
	JRST	CPOPJ1##	;GIVE WON RETURN
;ROUTINE TO CREATE A PAGE IN FUNNY SPACE
; CALL WITH T1=BITS,,HEADER
;RETURNS CPOPJ IF NONE AVAILABLE, CPOPJ1 IF AVAILABLE, T1 = VIRTUAL ADDRESS OF PAGE

CREFSP::
	PUSH	P,T1		;SAVE BITS,,HEADER
	MOVEI	T1,.MCFV/PAGSIZ## ;VIRTUAL PAGE NUMBER OF FIRST FUNNY PAGE
CREFS1:	PUSHJ	P,TSWST		;DOES IT EXIST
	  JRST	[MOVE T2,-1(P)	;NO, CHECK IF RESTRICTED PAGE
		 CAIN T1,<<.MCFV+NWMCP>/PAGSIZ##>-1 ;THE NEXT TO THE LAST FUNNY PAGE
		 TLNE T2,200000	;YES, ARE WE ALLOWED TO GIVE IT OUT
		 JRST CREFS2	;OK TO CREATE THIS PAGE
		 AOJA T1,.+1]	;CANNOT GIVE IT OUT, STOP LOOP
	CAIGE	T1,<.MCFV+NWMCP>/PAGSIZ## ;OFF THE END OF FUNNY SPACE
	AOJA	T1,CREFS1	;LOOK FOR A NON-EXISTANT PAGE
	JRST	TPOPJ##		;NONE AVAILABLE
CREFS2:	MOVEI	T2,1		;NUMBER OF PAGES TO CREATE
	PUSH	P,T1		;SAVE VIRTUAL PAGE NUMBER
IFN FTKL10,<
	MOVE	T3,-1(P)	;GET SIGN BIT FOR UNCACHED
>
	PUSHJ	P,CREMPG	;CREATE THE PAGE
	  JRST	TTPOPJ##	;FAIL IF NO VIRTUAL SPACE
	POP	P,T1		;RESTORE VIRTUAL PAGE NUMBER
	SUBI	T1,<.MCFV-.FPMC>/PAGSIZ## ;CONVERT TO PER-PROCESS ADDRESS
	LSH	T1,P2WLSH##	;...
	JRST	T2POJ1##	;POP OFF JUNK AND GIVE GOTIT RETURN
;SUBROUTINE TO GET PHYSICAL PAGES OF MEMORY
; CALL WITH T3 = NUMBER OF PAGES TO ALLOCATE
; CALLERS RESPONSABILITY TO CHECK TO SEE IF ALLOCATING PAGES REQUESTED WILL
; LEAVE ENOUGH ROOM FOR THE BIGGEST JOB TO SWAP IN
; RETURNS WITH T1 = FIRST PAGE ALLOCATED
; MUST OWN THE MM BEFORE CALL

GETPHP::PUSHJ	P,FRDCR##	;MAKE ROOM IF POSSIBLE
	  CAIA			;DIDN'T MAKE IT, WAIT A WHILE
	JRST	GETPH1		;PAGES ARE AVAILABLE, ALLOCATE THEM
	PUSHJ	P,UUOLVL##	;IF NOT AT UUO LEVEL, STOP (CALLER IS CONFUSED)
	  STOPCD CPOPJ##,DEBUG,NUL ;++NOT AT UUO LEVEL
IFN FTMP,<
	PUSHJ	P,DWNMM##	;RETURN MM BEFORE SLEEPING
>
	PUSH	P,T3		;SAVE NUMBER OF PAGES REQUESTED
	MOVEI	T1,1		;SLEEP A TIC
IFN FTXMON,<
	PUSHJ	P,SLEEPF##	;ZZZZZZ
>
IFE FTXMON,<
	RGCALL	SLEEPF##
>
IFN FTMP,<
	PUSHJ	P,UPMM##	;GET THE MM BACK
>
	POP	P,T3		;RESTORE SIZE OF REQUEST
	JRST	GETPHP		;AND TRY AGAIN
GETPH1:	SE1ENT			;ENTER SECTION 1
	MOVE	T2,T3		;NUMBER OF PAGES TO GET
	LSH	T3,P2WLSH##	;NUMBER OF WORDS BEING ALLOCATED
	MOVNS	T3		;MAKE NEGATIVE
	ADDM	T3,MAXMAX##	;DECREASE MAX CORMAX
	ADDB	T3,RMCMAX##	;ADJUST REAL CORMAX
	CAMGE	T3,CORMAX##	;LOWER THAN CORMAX?
	MOVEM	T3,CORMAX##	;YES, DECREASE CORMAX AS WELL
	PUSHJ	P,GTPAGS##	;GET THE NEEDED PAGES
	MOVE	T3,T1		;RETURN FIRST PAGE ALLOCATED IN T1
	SSX	T3,MS.MEM	;PAGTAB SECTION
GETPH2:	HRRZ	T2,T3		;PAGE NUMBER OF PAGE TO BE ZEROED
	PUSHJ	P,ZPAGE		;ZERO THE PAGE
	MOVSI	T2,MONTRB+TNCSHB;MARK THIS PAGE AS A MONITOR PAGE
	IORB	T2,PAGTAB(T3)	;LITE THE BIT AND GET NEXT PAGE NUMBER
	TRNN	T2,-1		;LAST PAGE ALLOCATED?
	JRST	GETPH3		;SET UP PT2TAB AND RETURN
	HRR	T3,T2		;NO, NEXT PAGE NUMBER
	JRST	GETPH2		;LOOP TO MARK AND ZERO ALL PAGES ALLOCATED
GETPH3:	HRRZ	T2,LMPAG##	;GET LAST ALLOCATED MONITOR PAGE
	HRRZM	T3,LMPAG##	;SET LAST PAGE ALLOCATED
	SSX	T2,MS.MEM	;PAGTAB SECTION
	HRRM	T1,PAGTAB(T2)	;SET FORWARD LINK
	HRRZM	T2,@[IW MS.MEM,PT2TAB(T1)] ;SET BACK LINK
	POPJ	P,
;SUBROUTINE TO STORE A PAGE NUMBER IN A MAP (CAUSES ALL CPUS TO SEE NEW MAPPING)
; ENTER AT STMAPS TO STORE IN SECTION TABLE, STMAPE TO STORE IN SECTION MAP
;CALL WITH T1= MAP OFFSET, T2 = NEW PHYSICAL PAGE # (OR ZERO IF
;CLEARING)

IFN FTXMON,<
STMAPS:	TLOA	T1,400000	;REMEMBER ENTRY POINT
>
STMAPE:	TLZ	T1,400000	;REMEMBER ENTRY POINT
	PUSHJ	P,SAVE4##	;SAVE ACS
IFN FTXMON,<
	SKIPGE	T1		;STORING SECTION POINTER?
	SKIPA	P2,[<POINT 36,(P4),35>] ;YES, BYTE POINTER TO EPT INDEXED BY .CPEPT
>
	MOVSI	P2,(POINT 36,0,35) ;BYTE POINTER TO STORE IN SECTION MAP
	SKIPN	P3,T2		;IF ZERO, STORE A ZERO
	JRST	STMAP1
	HRLI	P3,(<PM.DCD>B2+PM.WRT+PM.CSH) ;ACCESSIBLE, WRITABLE, CACHED
	IOR	P3,.CPKPM##	;ALSO LITE "KEEP ME" IF THERE IS AN MCA25

	HRRI	P2,(T1)		;OFFSET INTO SECTION TABLE OR MAP
IFN FTPEEKSPY,<
	SKIPL	T1		;SECTION POINTER?
	TLO	P3,(PM.PUB)	;NO, PAGES MUST BE PUBLIC FOR SPYING
>
IFN FTMP,<
	JUMPL	T1,STMAP1	;SECTION POINTERS ALWAYS HAVE CACHE BIT ON
	PUSHJ	P,MLSCSH##	;MONITOR LOW SEGMENT CACHED?
	  TLZ	P3,(PM.CSH)	;NO, CACHE MUST BE OFF
>
STMAP1:
	MOVEI	T1,STMPEC##	;ROUTINE TO STORE ENTRY IN ALL CPU'S MAPS
	PUSHJ	P,UUOLVL##	;MM AND SLEEPING (WAITING) ARE THE CALLER'S
	 S0JRST	CPUAPP##	; RESPONSIBILITY IF NOT AT UUO LEVEL
IFN FTMP,<
	PUSHJ	P,DIFMM##	;GIVE UP THE MM IF OWNED SINCE STMPEC MAY SLEEP
>
	S0PSHJ	CPUAPP##	; AND INSURE THAT ALL CPUS SEE THE NEW MAPPING
				; DO IT
IFN FTMP,<
	PJRST	UIFMM##		;RESTORE THE MM IF OWNED UPON ENTRY
>
IFE FTMP,<
	POPJ	P,		;RETURN
>
;SUBROUTINE TO ALLOCATE FUNNY SPACE IN WORDS RATHER THAT CHUNKS
;	T2/ NUMBER OF WORDS DESIRED
;	PUSHJ	P,GTFWDC	;GET FUNNY WORDS, CACHED
;	  -OR-
;	PUSHJ	P,GTFWDU	;GET FUNNY WORDS, UNCACHED
;RETURN CPOPJ IF SPACE IS NOT AVAILABLE
;	CPOPJ1 IF IT IS, T1 = ADDRESS

GFWDCH::HRRZS	.USFCU		;CLEAR LARGEST HOLE
	PUSHJ	P,GTFWDC	;GET THE SPACE
	  CAIA			;NOT AVAILABLE
	JRST	CPOPJ1##	;GOT IT
	HLRZ	T2,.USFCU	;GET SIZE OF BIGGEST HOLE
	POPJ	P,		; AND FAIL

IFN FTKL10,<
GFWDUD::SKIPA	T1,[GTFWDU]	;TRY REGULAR ENTRY POINTS FIRST
GFWDCD::MOVEI	T1,GTFWDC	;...
	PUSHJ	P,(T1)		;CALL CORRECT ROUTINE
>
IFE FTKL10,<
GFWDUD::
GFWDCD::PUSHJ	P,GTFWDC	;ALL THE SAME ON KI/KS
>
	  CAIA			;NO FUNNY SPACE AVAILABLE
	JRST	CPOPJ1##	;GIVE GOOD RETURN
	MOVE	T1,[600000,,.USFCD] ;TRY RESTRICTED SPACE
	JRST	GTFWD1		;AND HOPE THAT THERE IS SOME

GTFWDU::
IFN FTKL10,<
	SKIPE	[CPUN##-1]	;ALL SPACE IS CACHED IF ONLY 1
	SKIPA	T1,[400000,,.USFCU] ;ELSE GET UNCACHED HEADER
>
GTFWDC::MOVEI	T1,.USFCC	;POINT TO CACHED LIST HEAD
GTFWD1:	PUSHJ	P,SAVR##	;SAVE R
	MOVEI	R,(MDSEC0)	;SECTION 0 SPACE (REALLY FUNNY SPACE)
	JRST	GTFWD2		;JOIN COMMON CODE
IFE FTXMON,<
GFWNZD::SKIPA	T1,[400000,,(MS.DCN)] ;"DECNET" CORE FOR THE KS
GFWNZO::SKIPA	T1,[400000,,0]	;"ONCE-ONLY" CORE FOR THE KS
	PUSHJ	P,SVDCN##	;IF DECNET, POINT TO DECNET CORE
>
IFN FTXMON,<
GFWNZ2::SKIPA	T1,[400000,,(MS.SAT)]	;SECTION 2 CORE
GFWNZD::			;(NAME FOR DECNET TO CALL)
GFWNZS::MOVE	T1,[400000,,(MS.MEM)]	;GENERAL NZS SPACE
	SE1ENT			;MUST BE IN SECTION 1 TO ALLOCATE NZS SPACE
>
IFN FTMP,<
	PUSHJ	P,MMOWN##	;WE OWN MM ALREADY?
	  PUSHJ	P,GGVMM##	;NO--MUST HAVE THE MM TO LINK THROUGH FREE CORE CHAINS
>
	PUSHJ	P,SAVR##	;SAVE R
	MOVE	R,T1		;COPY SECTION # TO R
	MOVE	T1,NZSFCH##(R)	;HEADER FOR SPACE (ALWAYS UNCACHED)
GTFWD2:	PUSH	P,T1		;SAVE HEAD (FLAG ACTUALLY)
GTFWD3:
IFE	FTXMON,<
	MOVEI	T3,(T1)		;REMEMBER LINK
>
IFN FTXMON,<
	MOVE	T3,T1		;REMEMBER LINK
	SKIPE	R		;SECTION ZERO ALWAYS STORES SECTION
	CAME	T3,NZSFCH##(R)	;IF NOT THE FIRST TIME IN NON-ZERO SECTION
	SSXE	T1,(R)		; SET SECTION INDEX TO FUNNY SPACE SECTION
>
	HLL	T3,T1		;SET PREDECESSOR SECTION
	JUMPE	R,GTFWD4	;NON-ZERO SECTION HAS END POINTER
	CAMN	T1,NZSFCE##(R)	;THIS LAST BLOCK?
	  JRST	GTFWD7		;YES
	HRRZ	T1,(T1)		;POINT TO NEXT BLOCK
	JRST	GTFWD5
GTFWD4:	HRRZ	T1,0(T1)	;T1 = CURRENT CHUNK ADDRESS
	JUMPE	T1,GTFWD7	;IF 0, NO HOLE AVAILABLE, GET SOME MORE
GTFWD5:	SSXE	T1,(R)		;ADDRESS CORRECT SECTION
	HLRZ	T4,0(T1)	;SIZE OF THIS HOLE
	CAIGE	T4,(T2)		;LARGE ENOUGH FOR REQUEST
	JRST	[JUMPN R,GTFWD3	;NO BIGGEST HOLE IN NZS SPACE
		 HRLZ T4,T4	;NO, MOVE SIZE OF THIS HOLE
		 CAMLE T4,.USFCU ;BIGGEST YET
		 HLLM T4,.USFCU ;YES, REMEMBER FOR TMPCOR
		 JRST	GTFWD3]	; AND LOOK AT THE NEXT
	MOVEM	T1,(P)		;SAVE CHUNK ADDRESS
	CAIN	T4,(T2)		;AN EXACT SIZE MATCH
	JRST	[HRR T4,0(T1)	;YES, GET POINTER TO NEXT
		 HRRM T4,0(T3)	;RE-LINK PREVIOUS
		 JUMPE	R,TPOPJ1##
		 JRST	GTFWD6]	;CHECK IF THIS WAS LAST
	HRL	T4,0(T1)	;GET LINK TO NEXT CHUNK
	ADDI	T1,(T2)		;ADDRESS OF REMAINDER THIS CHUNK
	HRRM	T1,0(T3)	;STORE LINK TO REMAINDER
	SUBI	T4,(T2)		;SIZE OF REMAINDER
	MOVSM	T4,0(T1)	;STORE NEW LENGTH,OLD LINK IN REMAINDER
	JUMPE	R,TPOPJ1##	;SECTION ZERO, WE ARE DONE
	MOVE	T3,T1		;NEW LAST BLOCK
GTFWD6:	POP	P,T1		;RESTORE ADDRESS
	CAMN	T1,NZSFCE##(R)	;JUST ALLOCATE LAST BLOCK?
	  MOVEM	T3,NZSFCE##(R)	;STORE IT
	JRST	CPOPJ1##	;DONE
GTFWD7:	PUSH	P,T2		;SAVE AMOUNT REQUESTED
	MOVE	T1,-1(P)	;BITS,,HEADER
	MOVEI	T2,1		;ALWAYS CREATE ONLY 1 PAGE
	PUSH	P,R		;SAVE R
	PUSHJ	P,CREMNS	;CREATE A PAGE IN FUNNY SPACE OR NON-ZERO SECTION
	  JRST	GTFWD8
	POP	P,R
	JRST	GTFWD9		;A WINNER
GTFWD8:	POP	P,R
IFN FTKL10,<
	POP	P,T2		;RESTORE AMOUNT REQUESTED
	POP	P,T1		;RESTORE HEADER USED
	SKIPE	[CPUN##-1]	;ONLY ONE TRY IF ALL IS CACHED
	JUMPGE	T1,GTFWDU	;TRY FOR UNCACHED SPACE IF NO CACHED
	POPJ	P,		;ELSE OUT OF FUNNY SPACE
>
IFE FTKL10,<JRST TTPOPJ##>	;ONLY 1 TRY ON KI/KS
GTFWD9:	POP	P,T4		;RESTORE AMOUNT REQUESTED
	POP	P,T3		;RESTORE FREE SPACE HEADER AND FLAG
	MOVEI	T2,PAGSIZ##	;AMOUNT JUST ACQUIRED
	CAILE	T4,PAGSIZ##	;REQUESTING MORE THAN 1 PAGE
	JRST	GTFW10		;YES, MERGE THIS PAGE AND TRY AGAIN
	SUB	T2,T4		;AMOUNT LEFT OVER AFTER SATISFYING REQUEST
	JUMPE	T2,CPOPJ1##	;EXACT AMOUNT, RETURN NOW
	PUSH	P,T1		;SAVE ADDRESS OF NEWLY ACQUIRED CHUNK
	ADD	T1,T4		;ADDRESS OF RESIDUE
	EXCH	T1,T2		;WANT ARGS IN OTHER ORDER
	PUSHJ	P,GVFWD3	;GIVE IT BACK (T3 ALREADY SET UP)
	JRST	TPOPJ1##	;AND GIVE GOOD RETURN TO CALLER
GTFW10:	PUSH	P,T3		;SAVE HEADER AND FLAGS
	PUSH	P,T4		;SAVE ORIGINAL AMOUNT REQUESTED
	TLO	T3,100000	;LIGHT BIT SAYING NOT TO RETURN FULL PAGES
	EXCH	T1,T2		;WANT LENGTH AND ADDRESS IN THE OTHER ORDER
	PUSHJ	P,GVFWD3	;RETURN FULL PAGE TO THE FREE LIST
	POP	P,T2		;RESTORE ORIGINAL WORDS REQUESTED
	POP	P,T1		;RESTORE FREE SPACE HEADER AND FLAGS
	JRST	GTFWD2		;AND TRY TO SATISFY THE REQUEST NOW
;SUBROUTINE TO RETURN WORDS ACQUIRED BY GTFWDC, GTFWDU, OR GFWNZS
;CALL:	T1/ WORDS TO RETURN
;	T2/ ADDRESS
;	PUSHJ	P,GVFWDS

IFE FTXMON,<
GVFNZD::PUSHJ	P,SAVR##	;SAVE R
	MOVEI	R,(MS.DCN)	;FLAG DECNET CONTEXT
	SETZ	T4,		;BUT STILL S0
	PUSHJ	P,SVDCN##	;MAP DECNET
	JRST	GVFWDC
GVFNZO::
> ;END IFE FTXMON
IFN FTXMON,<
GVFNZD::
>
GVFWDS::PUSHJ	P,SAVR##	;SAVE TEMP USED FOR SECTION INDEX
GVFWD1:
IFN FTXMON,<
	MOVEI	R,(MDSEC0)	;ASSUME WE ARE TALKING ABOUT SECTION 0 SPACE
	HLRZ	T4,T2		;SECTION NUMBER
>
IFE FTXMON,<
	SETZB	R,T4		;ONCE ONLY OR FUNNY SPACE IS SECTION 0
>
	CAML	T2,LOCORE##	;ONCE-ONLY CORE?
IFN FTXMON,<
	TLZE	T2,-1		;NZS SPACE?
	TRNA			;ONCE ONLY OR NZS
>
	JRST	GVFWD2		;NO, SECTION ZERO FUNNY SPACE
GVFWDC:	CAIGE	T4,NZSFCL##	;KNOW ABOUT THIS SECTION?
	SKIPN	T3,NZSFCH##(T4)	;?
	STOPCD	CPOPJ##,DEBUG,RWS, ;++RETURNING SPACE TO WRONG SECTION
IFN FTXMON,<
	SKIPE	R,T4		;GET SECTION #
	SE1ENT			;IF RETURNING NZS CORE
	TLO	R,400000	;FLAG NOT FUNNY SPACE
>
IFE FTXMON,<
	MOVSI	R,400000	;FLAG NOT FUNNY SPACE BUT STILL SECTION ZERO
>
IFN FTMP,<
	PUSHJ	P,MMOWN##	;WE OWN MM ALREADY?
	  PUSHJ	P,GGVMM##	;NO--MUST INTERLOCK CHAIN CHASING
>
	MOVE	T3,NZSFCH##(R)	;GET PROPER HEADER
	JRST	GVFWD3		;JOIN COMMON CODE
GVFWD2:	MOVE	T4,T2		;COPY ADDRESS OF PIECE
	LSH	T4,W2PLSH##	;CONVERT TO PAGE NUMBER
	CAIGE	T4,.LPMC/PAGSIZ## ;RANGE CHECK IT
	CAIGE	T4,.FPMC/PAGSIZ## ;...
	STOPCD	CPOPJ##,DEBUG,PFR, ;++PIECE OUT OF FREE RANGE
	CAIN	T4,<.LPMC/PAGSIZ##>-1 ;IS THIS THE RESTRICTED PAGE
	JRST	[MOVE T3,[600000,,.USFCD] ;YES, USE THIS HEADER
		 JRST GVFWD3]	;ENTER COMMON CODE
	ADDI	T4,<.MCFV-.FPMC>/PAGSIZ## ;CONVERT TO VIRTUAL ADDRESS
	PUSHJ	P,GTPM4		;GET PAGE MAP ENTRY
	SKIPN	T4
	STOPCD	CPOPJ##,DEBUG,FPE, ;++FUNNY PAGE MUST EXIST
IFN FTKL10,<
	TLNN	T4,(PM.CSH)	;A CACHED PAGE
	SKIPA	T3,[400000,,.USFCU] ;GET UNCACHED LIST HEADER
>
	MOVEI	T3,.USFCC	;GET CACHED LIST HEADER
GVFWD3:	SPUSH	T3		;PUSH LEFT HALF
	TLZ	T3,100000	;CLEAR POSSIBLE FLAG.
	TLNE	R,400000	;FUNNY SPACE?
	CAME	T3,NZSFCH##(R)	;IF NOT THE FIRST TIME
IFN FTXMON,<
	SSXE	T3,(R)		; RELOCATE TO DATA SECTION
>
IFE FTXMON,<
	HRRZS	T3		;ALL IS REALLY S0
>
	JUMPE	R,GVFWD4	;ZERO TERMINATION FOR SECTION ZERO
	CAMN	T3,NZSFCE##(R)	;ELSE IS THIS LAST?
	  JRST	[SPOP	T3
		 JRST	GVFWD5]	;YES
GVFWD4:	HRRZ	T4,0(T3)	;GET FIRST CHUNK IN FREE LIST
	SPOP	T3		;RESTORE LEFT HALF
	TLNN	R,400000	;FUNNY SPACE?
	  JUMPE	T4,GVFWD5	;SECTION ZERO IS ZERO TERMINATED
	CAIN	T4,(T2)		;ALREADY IN FREE LIST
	STOPCD	CPOPJ##,DEBUG,PFL	;++PIECE ON FREE LIST
	CAIL	T4,(T2)		;KEEP CHUNKS IN ADDRESS ORDER
	JRST	GVFWD5		;IT GOES HERE
	HRR	T3,T4		;REMEMBER LINK
	JRST	GVFWD3		;AND LOOK AT THE NEXT
GVFWD5:	PUSH	P,T3		;SAVE PREVIOUS (AND FLAGS)
	MOVEI	T4,(T3)		;ADDRESS OF PREVIOUS
	JUMPL	R,GVFWD6	;ALWAYS COMBINE NZS
	CAIE	T4,.USFCD	;RESTRICTED SPACE HEADER
	CAIN	T4,.USFCC	;OR CACHED HEADER THE PREVIOUS?
	JRST	GVFWD8		;YES, CANNOT COMBINE (LH OF HDR IS NOT A LENGTH)
	JUMPE	R,GVFWD7	;SKIP SOME IF FUNNY SPACE
GVFWD6:	HLL	T4,NZSFCH##(R)	;COMPARE WITH NZS HEADER FOR RIGHT SECTION
IFN FTKL10,<
	CAME	T4,NZSFCH##(R)
	TLZA	T4,-1		;CLEAR LEFT HALF, PRESERVE NON-SKIP
	TLZA	T4,-1		;CLEAR LEFT HALF PRESERVE SKIP
GVFWD7:	CAIN	T4,.USFCU	;UNCACHED HEADER
>
IFE FTKL10,<
	CAMN	T4,NZSFCH##(R)
	TLZA	T4,-1		;CLEAR LEFT HALF, PRESERVE NON-SKIP
	TLZA	T4,-1		;CLEAR LEFT HALF PRESERVE SKIP
>
	JRST	GVFWD8		;IF NZS HEADER, CANNOT COMBINE
IFE FTKL10,<
GVFWD7:
>
IFE FTXMON,<
	TLZ	T3,-1		;CLEAR JUNK
>
IFN FTXMON,<
	SSXE	T3,(R)		;PROPER SECTION FOR REFERENCES BELOW
>
	HLRZ	T4,0(T3)	;LENGTH OF PREVIOUS PIECE
	ADDI	T4,(T3)		;FIRST WORD NOT IN PREVIOUS CHUNK
	CAILE	T4,(T2)		;BETTER BE
	STOPCD	TPOPJ##,DEBUG,FOP, ;++FUNNY ADDRESS OVERLAPS PREVIOUS
	CAIE	T4,(T2)		;PIECES CONTIGUOUS
	JRST	GVFWD8		;NO, CANNOT COMBINE
	MOVEI	T2,(T3)		;YES, ADDRESS OF LARGER CHUNK
	HLRZ	T4,0(T3)	;SIZE OF PREVIOUS
	ADD	T1,T4		;SIZE OF COMBINED PIECES
GVFWD8:
IFN FTXMON,<
	SSXE	T2,(R)		;SET FOR PROPER SECTION ADDRESSING
>
IFE FTXMON,<
	HRRZS	T2
>
	HRLM	T1,0(T2)	;SIZE OF THIS CHUNK
	HRRZ	T4,0(T3)	;PREVIOUS LINK TO NEXT
	HRRM	T4,0(T2)	;LINK THIS TO NEXT
	CAME	T2,T3		;PREV=THIS (CONCATENATION)
	HRRM	T2,0(T3)	;NO, LINK PREV TO THIS
	JUMPE	R,GVFWD9
	CAMN	T3,NZSFCE##(R)	;LAST?
	MOVEM	T2,NZSFCE##(R)	;WE ARE NOW LAST
GVFWD9:	HRRZ	T3,T2		;ADDRESS OF CURRENT
	ADD	T3,T1		;+SIZE OF CURRENT
	SKIPN	T4		;WAS LAST ADDRESS ZERO?
	JUMPE	R,GVFW10	;YES, END OF LIST IF SECTION ZERO
	CAMN	T2,NZSFCE##(R)	;ARE WE NOW LAST?
	JRST	GVFW10		;YES, THERE IS NO NEXT
	CAILE	T3,(T4)		;BETTER  BE
	STOPCD	.,JOB,FON,	;++FUNNY ADDRESS OVERLAPS NEXT
	CAIE	T3,(T4)		;BUTT UP AGAINST NEXT ONE
	JRST	GVFW10		;NO, CANNOT COMBINE THAT SIDE
	HRLZS	T1		;SIZE OF CURRENT,,0
IFN FTXMON,<
	SSXE	T4,(R)		;PROPER SECTION (STILL CLEAR IF IFE FTXMON)
>
	ADD	T1,0(T4)	;COMBINED SIZE,,NEXT'S LINK
	MOVEM	T1,0(T2)	;STORE NEW INFO
	HLRZS	T1		;ISOLATE SIZE OF NEW, LARGER PIECE
	JUMPE	R,GVFW10	;NOTHING MORE IF SECTION 0
	CAMN	T4,NZSFCE##(R)	;WAS THIS LAST?
	MOVEM	T2,NZSFCE##(R)	;WE ARE LAST NOW
GVFW10:	POP	P,T3		;RESTORE ORIGINAL PREVIOUS (AND FLAGS)
	TLNE	T3,100000	;SUPRESS RECLAMATION OF FULL PAGES
	POPJ	P,		;YES, ALL DONE NOW
	TLCE	R,400000	;FUNNY SPACE?
	JUMPE	R,CPOPJ		;NO, CAN'T RECLAIM ONCE-ONLY CORE
	TLC	R,400000	;PUT BIT BACK RIGHT
	PUSHJ	P,INTLVL##	;ONLY RECLAIM AT UUO LEVEL
	  CAIGE	T1,PAGSIZ##	;CAN WE RECLAIM A FULL CHUNK
	POPJ	P,		;NO, RETURN NOW
	PUSHJ	P,SAVE4##	;YES, SAVE SOME REGS
	DMOVE	P1,T1		;SAVE LENGTH, ADDRESS OF PIECE
	TRZ	T1,PAGSIZ##-1	;MAX WORDS THAT CAN BE RETURNED
	ADDI	T2,PAGSIZ##-1	;AND ROUND ADDRESS TO CHUNK BOUNDRY
	TRZ	T2,PAGSIZ##-1	;...
	MOVE	P4,P2		;ORIGINAL ADDRESS
	ADD	P4,P1		;P4 = FIRST WORD NOT IN PIECE
	MOVE	T4,T2		;START OF CHUNK THAT CAN BE RETURNED
	ADD	T4,T1		;T4 = FIRST WORD THAT WON'T BE RETURNED
GVFW11:	CAMGE	P4,T4		;RETURNABLE SPACE COMPLETELY CONTAINED
	JRST	[SUBI T1,PAGSIZ## ;NO, LESS WORDS TO RETURN
		 JUMPE T1,CPOPJ## ;"AND THEN THERE WERE NONE"
		 SUBI T4,PAGSIZ## ;ADJUST ENDING ADDRESS ( +1 )
		 JRST GVFW11]	;AND LOOK AT THE SMALLER PIECE
	MOVEI	P3,(P2)		;GET RIGHT HALF
	CAIE	P3,(T3)		;CURRENT = PREVIOUS (CONCATENTATION)
	JRST	[JUMPE	R,GVFW15	;GO IF FUNNY SPACE
		 CAME	T3,NZSFCH##(R)	;AT BEGINNING?
IFN FTMP,<
		 SSXE	T3,(R)		;NO, RELOCATE
>
IFE FTMP,<
		 HRRZS	T3
>
		 JRST	GVFW15]		;AND UNLINK PIECE
	JUMPE	R,GVFW12	;SECTION ZERO
	HRR	T3,NZSFCH##(R)	;ASSUME NZS CORE BEING RETURNED
	JRST	GVFW14		;NON-ZERO SECTION
GVFW12:	HRRI	T3,.USFCC	;YES, MUST FIND NEW PREVIOUS
IFN FTKL10,<
	SKIPGE	T3		;CACHED OR UNCACHED HEADER NEEDED
	HRRI	T3,.USFCU	;UNCACHED
>
	TLNE	T3,200000	;RESTRICTED SPACE
	HRRI	T3,.USFCD	;YES
GVFW13:
IFN FTXMON,<
	SSXE	T3,(R)		;SET TO ADDRESS PROPER SECTION
>
IFE FTXMON,<
	HRRZS	T3
>
GVFW14:	HRRZ	P3,0(T3)	;GET LINK TO NEXT
	CAIN	P3,(P2)		;POINT TO THIS ONE
	JRST	GVFW15		;YES, T3 = NEW PREVIOUS
	HRR	T3,P3		;COPY PREVIOUS
	JRST	GVFW13		;AND GO LOOK AGAIN
GVFW15:	SPUSH	P2		;SAVE LEFT HALF
IFN FTXMON,<
	SSXE	P2,(R)		;SET PROPER SECTION
>
IFE FTXMON,<
	HRRZS	P2
>
	HRRZ	P3,0(P2)	;GET ADDRESS OF NEXT CHUNK
	JUMPE	R,GVFW16	;SECTION ZERO
	CAMN	P2,NZSFCE##(R)	;WERE WE THE LAST?
	MOVEM	T3,NZSFCE##(R)	;PREDECESSOR NOW IS
GVFW16:	SPOP	P2		;RESTORE LEFT HALF
	HRRM	P3,0(T3)	;DE-LINK THE LARGER PIECE
	MOVE	P1,T2		;START OF REMOVED PIECE
	SUB	P1,P2		;COMPUTE LENGTH OF L.H. EXCESS
	MOVE	P3,P4		;1ST WORD NOT IN THE PIECE
	SUB	P3,T4		;COMPUTE LENGTH OF R.H. EXCESS
	MOVE	P4,T4		;AND WHERE IT STARTS
	PUSH	P,P1		;SAVE REMAINDER INFORMATION
	PUSH	P,P2		;...
	DMOVE	P1,T1		;COPY AMOUNT TO RETURN
	LSHC	P1,W2PLSH##	;P1 = NUMBER TO RETURN, P2 = PAGE NUMBER OF FIRST
GVFW17:	MOVE	J,.USJOB	;GET JOB NUMBER WHO OWNS THESE PAGES
	PUSHJ	P,DLTMPG	;DELTETE THIS PAGE
	AOS	P2		;STEP TO NEXT PAGE TO RETURN
	SOJG	P1,GVFW17	;GET THEM ALL
	CLRPGT	(0)		;WE HAVE CHANGED THE MAP
	POP	P,T2		;RESTORE LEFT HAND REMAINDER INFORMATION
	POP	P,T1		;...
	SKIPE	T1		;WAS THERE ANY
	PUSHJ	P,GVFWD1	;YES, GIVE IT BACK
	JUMPE	P3,CPOPJ##	;DONE IF NO RIGHT HAND REMAINDER
	DMOVE	T1,P3		;GET LENGTH AND ADDRESS OF IT
	JRST	GVFWD1		;GIVE IT BACK AND RETURN
;SUBROUTINE TO RETURN A FUNNY PAGE, MUST BE A SUBROUTINE SO THAT
; IT CAN CALL THE COROUTINE GGVMM
; CALL WITH P2 = VIRTUAL PAGE NUMBER

DLTMPG:	MOVE	T1,P2		;IN CASE A NZS PAGE
	PJUMPN	R,DNZSPG	;JUMP IF A NZS PAGE
IFN FTMP,<
	PUSHJ	P,GGVMM##	;GET MM RESOURCE SINCE DIDDLING CORE ALLOCATION TABLES
				; WILL BE RETURNED UPON EXIT
>
DLTFSP::
	MOVN	T1,[MCPCNT]	;DECREMENT COUNT OF FUNNY PAGES OWNED
	ADDM	T1,JBTPDB##(J)	;...
	MOVEI	T1,<.MCFV-.FPMC>/PAGSIZ##(P2) ;CONVERT TO VIRTUAL PAGE NUMBER
	PUSHJ	P,TSWST		;GET BIT FOR WSBTAB
	  STOPCD .+1,DEBUG,PNW,	;++PAGE NOT IN WORKING SET
	ANDCAM	T2,(T4)		;NO LONGER IN WORKING SET
	PUSHJ	P,GTPME		;GET PAGE MAP ENTRY FOR IT
	SETZM	(T4)		;ZAP THE PAGE MAP ENTRY
	TLZ	T2,(PM.NAD)	;DOWN TO PHYSICAL PAGE NUMBER
	PUSHJ	P,LKPSF		;FIX UP PAGTAB
	PJRST	DLTPGC		;AND DELETE THE PAGE,
				; RETURN MM RESOURCE AND RETURN TO CALLER

;SUBROUTINE TO RETURN A PAGE IN A NON-ZERO SECTION TO FREE CORE OR ONCE-ONLY CORE
; CALL WITH T1 = VIRTUAL PAGE NUMBER (MUST OWN THE MM RESOURCE)
;MUST BE IN SECTION 1

DNZSPG:	LSHC	T1,-^D9		;ISOLATE SECTION NUMBER
	LSH	T2,-^D27	;RIGHT JUSTIFY PAGE NUMBER
IFE FTXMON,<
	MOVEI	T1,0		;MAKE SURE SECTION IS RIGHT
>	ADD	T1,.CPEPT##	;ADDRESS OF EPT + SECTION NUMBER
	SKIPN	T3,SECTAB(T1)	;GET ENTRY FROM SECTION MAP
	STOPCD	CPOPJ##,DEBUG,NXS, ;++NON-EXISTANT SECTION
	MOVEM	T3,.UPMP+.UMTMP ;MAKE THE SECTION MAP ADDRESSABLE
	CLRPGT	(0)		;MAKE NEW MAPPING VISABLE
	MOVEI	T1,.TEMP(T2)	;ADDRESS OF THE POINTER SLOT (FOR STMAPE)
	SKIPN	T2,.TEMP(T2)	;GET PAGE POINTER
	STOPCD	CPOPJ##,DEBUG,RNP, ;++RETURNING NON-EXISTANT PAGE
	TLZ	T2,(PM.NAD)	;ISOLATE PHYSICAL PAGE NUMBER
;	SE1ENT			;(RIGHT NOW, DNZSPG IS ALWAYS CALLED HERE)
	PUSH	P,T1		;SAVE MAP OFFSET
	HRRZ	T1,@[IW MS.MEM,PT2TAB(T2)]
	CAMN	T2,LMPAG##	;IS THIS THE LAST MONITOR PAGE?
	HRRZM	T1,LMPAG##	;YES, NEW LAST PAGE
	PUSHJ	P,LKPSF		;REMOVE PAGE FROM MONITOR'S CHAIN
	PUSHJ	P,DLTPGC	;RETURN THE PAGE TO FREE CORE
	MOVEI	T1,PAGSIZ##	;NUMBER OF WORDS BEING FREED
	ADDM	T1,MAXMAX##	;UPDATE MAXMAX TO REFLECT THAT
	ADDM	T1,RMCMAX##	;UPDATE REAL MAXIMUM FOR CORMAX
	POP	P,T1		;RESTORE PAGE OFFSET
	MOVEI	T2,0		;SET TO ZERO THE MAP SLOT
	PJRST	STMAPE		;ZERO THE MAP SLOT AND RETURN
	SUBTTL	COMCON	INTERFACE WITH COMMAND DECODER

;PRIVILEGED COMMAND/UUO TO SET THE SYSTEM-WIDE VM LIMIT
SETVMX::PUSHJ	P,CORLGL##	;GET CORE ARGUMENT
	ADDI	T2,1
	LSH	T2,W2PLSH##	;CONVERT TO PAGES
SETVM1::CAMLE	T2,VIRTAL##	;BIGGER THAN VIRTAL?
	MOVE	T2,VIRTAL##	;YES, USE VIRTAL
	CAMGE	T2,VMTOTL##	;BIGGER THAN TOTAL VM NOW IN USE?
	MOVE	T2,VMTOTL##	;YES, USE CURRENT AMOUNT IN USE
	MOVEM	T2,VMCMAX##	;SAVE SYSTEM WIDE VM LIMIT
	PJRST	CPOPJ1##	;AND RETURN

;PRIVILEGED UUO TO SET THE ABSOLUTE PER-USER MAXIMA
SETUVL::HRR	M,T2		;LOC OF ARGUMENT
	TLZ	M,MXSECN	;(SETUUO IS LOCAL)
	PUSHJ	P,FLTSX		;MAKE SURE WONT FAULT
	  JRST	ECOD0##		;PAGE-FAULT AT NON-UUO LEVEL????!!??
	PUSHJ	P,GETWDU##	;GET ARG
	PUSHJ	P,CHKLIM	;MAKE SURE VALUES ARE OK
	  JRST	ECOD0##		;NOT - NON SKIP
SETVLM::
	MOVEM	T1,.PDMVL##(W)	;MVPL,,MPPL
	SKIPN	.PDCVL##(W)	;IF NOT ALREADY SET,
	JRST	SETUV2		;SAVE AS CURRENT LIMITS TOO
	PJRST	CPOPJ1##	;AND GOOD RETURN

;NON-PRIVILEGED UUO TO SET INTERVAL FOR VIRTUAL-TIME INTERRUPT
SETVTM::PUSHJ	P,FNDPDB##	;GET PDB FOR TARGET JOB
	  JRST	ECOD0##		;CAN'T SET IF NO PDB
	IMUL	T2,TICSEC##	;CONVERT TO TICS-PER-INTERRUPT
	IDIVI	T2,^D1000
	HRRZM	T2,.PDTMC##(W)	;SAVE IN UPMP
	HRRM	T2,.PDTMI##(W)
	PJRST	CPOPJ1##	;AND RETURN

;SUBROUTINE TO CHECK CORE LIMITS
;ENTER T1=LIMITS
;EXIT CPOPJ IF TOO BIG, CPOPJ1 IF OK
;RETURNS T3=RH(T1) WITHOUT GUIDELINE BIT
CHKLIM:	HLRZ	T2,T1
	HRRZ	T3,T1
	TRZ	T3,400000	;CLEAR LIMIT BIT
	CAIG	T2,<MXSECN+1>*<HLGPNO+1>	;OK?
	CAILE	T3,<MXSECN+1>*<HLGPNO+1>
	POPJ	P,		;NO, NON-SKIP
	JUMPE	T3,CPOPJ1##	;0 IS ALWAYS OK
	CAIL	T3,4		;AT LEAST 4 PAGES?
	PJRST	CPOPJ1##	;YES, SKIP
	TRZ	T3,377777	;CLEAR
	TRO	T3,4		;MAKE IT FOUR PAGES
	JRST	CPOPJ1##	;SKIP RETURN
;NON-PRIVILEGED COMMAND TO SET CURRENT PER-USER LIMITS
SETUVC::SKIPA	T1,[POINT 18,.PDMVL##(W),17]
SETUPC::MOVE	T1,[POINT 18,.PDMVL##(W),35]
	PUSHJ	P,SAVE2##	;SAVE AN AC
	MOVE	P1,T1		;SAVE POINTER TO MAX VALUE FOR THIS ARG
	PUSHJ	P,FNDPDB##	;GET PDB LOC
	  POPJ	P,		;NONE-RETURN
	MOVE	T1,[-2,,[SIXBIT /GUIDEL/
			 SIXBIT /LIMIT/]]
	PUSHJ	P,TXTARG##
	  TDZA	P2,P2
	MOVE	P2,T1
	PUSHJ	P,CORARG##	;GET THE CORE ARGUMENT
	  PJRST	NOTENF##
	ADDI	T1,1		;CONVERT TO PAGES
	LSH	T1,W2PLSH##
	PUSHJ	P,CHKLIM	;LEGAL?
	  PJRST	COMERA##
	LDB	T2,P1		;YES, GET MAX VALUE FOR THIS ARG
	CAILE	T1,(T2)		;TRYING TO SET IT TOO HIGH?
	JRST	COMERA##	;YES
	HRRI	P1,.PDCVL##	;NO, SET TO STORE NEW VALUE
	DPB	T1,P1		;SAVE CURRENT LIMIT
	TRNN	T1,-1		;SETTING LIMIT/GUIDELINE TO ZERO?
	MOVEI	P2,0		;YES, CLEAR LIMIT BIT
	TLNN	P1,770000	;PHYSICAL?
	DPB	P2,[POINT 1,.PDCVL##(W),18]
	PJRST	CPOPJ1##	;AND TAKE GOOD RETURN


;NON-PRIVILEGED UUO TO SET PER-USER CURRENT LIMITS
SETUV1::HRR	M,T2		;ADR OF ARGUMENT
	TLZ	M,MXSECN	;(SETUUO IS LOCAL)
	PUSHJ	P,FLTSX
	  POPJ	P,		;PAGE-FAULT AT NON-UUO LEVEL???!!!
	PUSHJ	P,GETWDU##	;GET ARGUMENT
	PUSHJ	P,CHKLIM	;LEGAL?
	  POPJ	P,		;NO, RETURN
	MOVE	T2,.PDMVL##(W)	;YES, GET MAX LIMIT
	HRRZ	T4,T2		;MAKE SURE ARGUMENTS ARE BELOW MAX LIMITS
	CAILE	T2,<MXSECN+1>*<HLGPNO+1>
	HRLI	T2,<MXSECN+1>*<HLGPNO+1>
	TRO	T2,-1		;PUT LARGE NUMBER IN IRRELEVANT RIGHT-HALF
	CAMG	T1,T2
	CAMLE	T3,T4
	POPJ	P,		;TRYING TO SET THEM TOO BIG
SETUV2:	TLNE	T1,-1		;OK, SAVE WHAT HE ASKED FOR
	.HLLM	T1,.PDCVL##(W)	;CVPL
	TRNE	T1,-1
	.HRRM	T1,.PDCVL##(W)	;CPPL
	SKIPN	T1		;IF WHOLE THING ZERO,
	.MOVEM	T1,.PDCVL##(W)	;  DO IT ANYWAY
	PJRST	CPOPJ1##
;ROUTINE TO DETERMINE IF A PAGE EXISTS AND IF SO IF REFERENCING IT
; WILL CAUSE A PAGE FAULT. ENTER T2 = VIRTUAL ADDRESS
; EXIT CPOPJ IF PAGE DOESN'T EXIST
; CPOPJ1 IF PAGE EXISTS BUT IS PAGED OUT OR HAS ACCESS ALLOWED OFF,
; CPOPJ2 IF PAGE EXISTS AND IS IN CORE,  OR IS ALLOCATED BUT ZERO
;RETURNS MAP CONTENTS FOR THE ADR IN T4
;PRESERVES T2.

FLTTC::	MOVE	T4,T2		;GET ADR
	LSH	T4,W2PLSH##+P2SLSH## ;GET SECTION
	JUMPE	T4,FLTTC1	;CONTINUE IF S0
	PUSH	P,T2		;SAVE T2
	PUSHJ	P,RSECT4	;GET REAL SECTION #
	  JUMPN	T4,T2POPJ##	;DOESN'T EXIST
	POP	P,T2		;RESTORE T2
FLTTC1:	MOVE	T4,T2		;GET WORD ADDRESS AGAIN
	LSH	T4,W2PLSH##	;PAGE #
	SE1ENT
	SKIPN	T4,@[IW UMAPSN,UMAPS(T4)] ;GET ENTRY (SECTION EXISTS SO CAN'T FAULT)
	POPJ	P,		;TOO BAD PAGE DOESN'T
	TLNE	T4,(<PM.ACD>B2)	;IN CORE?
	  JRST	CPOPJ2##	;YES
	TLZ	T4,(PM.NAD)	;KEEP ONLY ADDRESS
	CAMN	T4,[PM.ZER]	;ABZ PAGE?
	AOS	(P)		;YES, ACCESS ALLOWED IS ON
	JRST	CPOPJ1##	;DONE
;HERE TO DEPOSIT IN AN ALLOCATED BUT ZERO PAGE
; S = WHAT TO DEPOSIT, JOBEXM = WHERE
CREPAG::PUSHJ	P,CLRASA##	;CLEAR JS.ASA IN CASE OF ERROR AND SO USER'S
				; ACS WILL GET RESTORED WHEN MONITOR JOB IS STARTED
	JUMPL	P4,CREPA2	;IF RUNNING, REQUIRE A CONTROL C
	JSP	T2,SAVCTX##
;RETURN HERE AT UUO LEVEL
	MOVEM	S,.JDAT+JOBFDV## ;SAVE WHAT TO DEPOSIT
	MOVE	T2,.JDAT+JOBEXM## ;WHERE TO DEPOSIT
	TLZ	T2,400000	;CLEAR EXAMINE LAST FLAG
	MOVEI	T3,1		;NUMBER OF PAGES TO CREATE FOR THE DEPOSIT
	PUSHJ	P,MAKPGS	;CREATE THE PAGE FOR THE DEPOSIT
	  JRST	CREPA1		;LOST, PHYSICAL LIMIT IS SET TOO LOW
	MOVE	T1,.JDAT+JOBFDV## ;WHAT
	MOVE	T2,.JDAT+JOBEXM## ;WHERE
	TLZ	T2,400000	;CLEAR EXAMINE LAST FLAG
	EXCTXU	<MOVEM T1,(T2)>	;STORE IT AWAY FOR HIM
	POPJ	P,
CREPA1:	PUSHJ	P,TTYFUW##	;FIND HIS TTY AND RESTORE J
	PUSHJ	P,PRQM##	;"?"
	MOVEI	T1,OUTBMS##	;"OUT OF BOUNDS"
	PJRST	CONMES##	;PRINT THAT
CREPA2:	SKIPGE	.JDAT+JOBEXM##	;EXAMINE LAST FLAG ON?
	TLO	T2,400000	;YES, SET IT AGAIN
	MOVEM	T2,.JDAT+JOBEXM## ;STORE WHAT USER TYPED SINCE CAN'T DO IT NOW
	MOVEI	T1,RUNERR##	;"PLEASE TYPE CONTROL C FIRST"
	PJRST	ERRMES##	;PRINT THAT AND RETURN TO COMCON
;SUBROUTINE TO RETURN # OF VIRTUAL PAGES ALLOCATED TO A USER'S JOB
;CALL WITH:
;	UBR SETUP TO USER PAGE MAP PAGE
;	MOVEI	J,JOB #
;	PUSHJ	P,VMSIZE
;	RETURNS HERE WITH LOWSEG SIZE IN T1 AND HISEG SIZE IN T2
;
;	JOB'S UPMP IS IN CORE WHEN VMSIZE IS CALLED. LOWSEG SIZE
;	  INCLUDES THE JOB'S PAGE MAP PAGE.
;
VMSIZE::SETZB	T1,T2		;ASSUME USER DID CORE 0
	SKIPN	JBTADR##(J)	;ANY CORE ASSIGNED?
	POPJ	P,		;NO, RETURN ZEROS
	PUSHJ	P,LOSIZ		;GET THE LOW SEG SIZE
	MOVEI	T1,UPMPSZ##(T3)	; +1 FOR UPMP
	ADD	T1,.USMEM	; + NO OF NON-CONTIGUOUS PAGES
	SKIPLE	T3,JBTSGN##(J)	;HAVE A HI SEG?
	TLOA	T3,(1B0)	;MAKE A USABLE INDEX OUT OF COPY OF JBTSGN
	TRNA			;NO HISEG
	HLRZ	T2,JBTADR##(T3)	;YES, GET ITS SIZE
	ADDI	T2,1
	LSH	T2,W2PLSH##	;CONVERT TO PAGES
	POPJ	P,		;AND RETURN
;SUBROUTINE TO COMPUTE THE LOW SEG SIZE
;ENTER J=JOB NUMBER
;EXIT T3= LOW SEG SIZE IN PAGES
;PRESERVES ALL ACS (EXCEPT T3)

;CALL HISIZ TO COMPUTE HI SEG SIZE, ALWAYS RETURNS CPOPJ1
HISIZ:	HLRZ	T3,JBTADR##(J)	;USE JBTADR
	AOS	T3		;ROUND UP
	LSH	T3,W2PLSH##	;MAKE PAGES
	JRST	CPOPJ1##	;AND RETURN

LOSIZ:	SKIPN	T3,.USREL	;VIRTUAL?
	HLRZ	T3,JBTADR##(J)	;NO, USE JBTADR
	ADDI	T3,1
	LSH	T3,W2PLSH##	;CONVERT TO PAGES
	ADD	T3,.USNCR	;ADD # OF NZS PAGES
	POPJ	P,		;AND RETURN
	SUBTTL	ERRCON	ERROR HANDLING MODULE

;SUBROUTINE TO COMPUTE A VIRTUAL PAGE NUMBER WITHIN A JOB'S
; ADDRESS SPACE BASED ON AN ABSOLUTE PAGE NUMBER KNOWN TO BE IN THE
; JOB'S MAP
;CALLING SEQUENCE:
;	MOVE	J,JOB NUMBER
;	MOVE	T3,PHYSICAL PAGE NUMBER TO BE USED IN VIRTUAL PAGE CALCULATION
;	MAKE THE JOB'S MAP ADDRESSABLE
;	PUSHJ	P,PHYPVP	;CONVERT PHYSICAL PAGE NUMBER TO VIRTUAL PAGE NUMBER
;ALWAYS RETURNS POPJ,T1=VIRTUAL PAGE NUMBER WITHIN THE
; LOW SEGMENT OR RELATIVE PAGE NUMBER WITHIN HIGH SEGMENT

PHYPVP::SE1ENT		;TO TOUCH MAPS
	SETZB	T1,T4		;START AT VIRTUAL PAGE 0
	PUSHJ	P,GMPTR		;GET A BYTE POINTER TO THE MAP
PHYPV1:	MOVE	T2,(T4)		;GET MAP CONTENTS
	AOS	T4		;INCREMENT
	TLZ	T2,(PM.NAD)	;KEEP ONLY PAGE #
	CAME	T3,T2		;IS THIS THE ABSOLUTE PAGE IN QUESTION?
	AOJA	T1,PHYPV1	;NO, LOOK AT THE NEXT PAGE IN THE MAP
	PUSHJ	P,TSWST		;IN THE WORKING SET?
	CAIA			;NO, SEE IF IN THE HIGH SEG
	POPJ	P,		;IT'S IN THE WORKING SET
	PUSHJ	P,TPNHS		;YES, IS THIS PAGE IN THE HIGH SEGMENT?
	  SKIPA	J,JBTSGN##(J)	;YES, GET HIGH SEGMENT NUMBER
	AOJA	P1,PHYPV1	;NO, NOT A REAL PAGE THEN (DISK ADDR?)
	LDB	T2,JBYHSO##	;GET ORIGIN PAGE OF THE HIGH SEGMENT
	SUBI	T1,(T2)		;COMPUTE RELATIVE PAGE WITHIN HIGH SEGMENT
	POPJ	P,		; AND RETURN
	SUBTTL	CORE1	INTERFACE WITH THE CORE ROUTINES

;SUBROUTINE TO TURN ON BITS IN WSBTAB WHEN A JOB IS EXPANDING
;ENTER T1=HIGHEST RELATIVE PAGE DESIRED
;PRESERVES T1
CLRWS::	PUSHJ	P,SAVE2##	;SAVE P1 & P2
	MOVSI	P1,(ANDCAM T1,(T4)) ;SET ARGUMENT FOR ADJWS1
	JRST	ADJWS1		;CLEAR BITS IN WSBTAB

ADJWS::	PUSHJ	P,SAVE2##	;SAVE P1 & P2
	MOVSI	P1,(IORM T1,(T4)) ;SET ARG  FOR ADJWS1
	MOVEI	T3,30		;MAKE SURE UPMP BIT ON IN WSBTAB
	IORM	T3,WSBTAB+16
ADJWS1:	PUSHJ	P,SAVT##
	MOVEI	T3,(T1)		;GET ARG SET
	ANDI	T3,HLGPNO	;ONLY KEEP PAGE NUMBER
ADJWS2:	MOVEI	P2,MXSECN_S2PLSH## ;ISOLATE SECTION #
	AND	P2,T1
	LSH	P2,P2SLSH##
ADJWS3:	SKIPGE	.USBTS		;NOT A BIG USER
	SKIPN	T4,P2		;SECTION BEING DONE
	SKIPA	T4,[WSBTAB-.WSBNZ+WSBTBL]
	IMULI	T4,WSBTBL
	ADDI	T4,.WSBNZ-WSBTBL
DOBIT:	MOVEI	T1,^D36		;SET UP FIRST PART OF MASK
	PUSHJ	P,BITMSK##
DOBIT1:	XCT	P1		;SET OR CLEAR BITS
	JUMPLE	T3,DOBIT2	;GO IF THROUGH
	PUSHJ	P,BITMS2##	;SET MASK FOR NEXT PART
	JRST	DOBIT1		;AND CONTINUE
DOBIT2:	SOSGE	T1,P2		;ANOTHER SECTION TO DO?
	POPJ	P,		;NO, DONE
	SKIPN	.UPMP+SECTAB(T1);SECTION EXIST?
	JRST	DOBIT2		;NO, KEEP LOOKING (SEC 0 MUST EXIST FOR NOW)
	MOVEI	T3,HLGPNO	;DO ALL PAGES IN PRECEDING SECTIONS
	JRST	ADJWS3

;SUBROUTINE TO COMPUTE THE TOTAL VIRTUAL SIZE OF A JOB
;ENTER WITH JOB ADDRESSABLE
;EXIT T2= JOB SIZE IN PAGES (EXCLUDES UPMP SIZE)
;PRESERVES ALL ACS BUT T2
VRTSZ:	PUSH	P,T1		;SAVE T1
	MOVE	T1,.USJOB	;JOB NUMBER
	SKIPN	T2,.USREL	;NON-CONTIGUOUS?
	HLRZ	T2,JBTADR##(T1)	;NO, GET JBTADR
	SKIPLE	T1,JBTSGN##(T1)	;IS THERE A NON-SHARABLE HI-SEG?
	TLNE	T1,SHRSEG
	JRST	VRTSZ1		;NO
	HRRZS	T1
	HLRZ	T1,JBTADR##(T1)	;YES, GET ITS SIZE
	ADD	T2,T1		;LOW+HIGH SIZES
VRTSZ1:	LSH	T2,W2PLSH##
	ADD	T2,.USNCR	;AND NZS PAGES
	ADD	T2,.USMEM	;+NO OF NON-CONTIG PAGES
	AOJA	T2,TPOPJ##	;AND RETURN
;SUBROUTINE CALLED BY CORE1 ON A CORE UUO/COMMAND
;ALLOCATES SPACE (ALLOCATED BUT ZERO) IF A JOB IS ALREADY VIRTUAL
; OR IS GOING VIRTUAL BY THIS CORE OPERATION
;ENTER T3=AMOUNT OF INCREASE -1 ; T1=NEW HIGHEST ADDRESS
;EXIT CPOPJ IF ASKING FOR TOO MUCH
;EXIT CPOPJ1 IF ALLOCATED ON DSK
;EXIT CPOPJ2 OTHERWISE, T1=HIGHEST ADR, T3=AMOUNT OF INCREASE-1
VIRCHK::CAIG	J,JOBMAX##	;LOW SEGS MUST CALL KISER TO
	JUMPE	R,CPOPJ2##	; GET INITIAL CORE (GETMIN)
	TLNE	J,SHRSEG	;A SHARABLE HIGH SEGMENT?
	JRST	CPOPJ2##	;YES, LET KISER HANDLE CORE ALLOCATION
	PUSHJ	P,UUOLVL##	;UUO LEVEL?
	  TDZA	T3,T3		;NO, MUST BE SECTION 0 THEN
	XSFM	T3		;GET THE SECTION #
	TRNE	T3,MXSECN	;NON-ZERO SECTION?
	JRST	VIRCH6		;YES, HANDLE DIFFERENTLY
	CAIG	J,JOBMAX##	;A HIGH SEGMENT?
	SKIPN	T2,.USREL	;NO, HIGHEST CONTIGUOUS ADDRESS IN THE LOW SEGMENT
	HLRZ	T2,R		;HIGHEST ADDRESS IN THE SEGMENT
	MOVE	T4,T1		;HIGHEST ADDRESS REQUESTED
	SUB	T4,T2		;MINUS CURRENT HIGHEST ADDRESS
	JUMPLE	T4,VIRCH9	;DEALLOCATING
	LSH	T4,W2PLSH##	;CONVERT TO NUMBER OF PAGES OF INCREASE
	DMOVEM	T1,.USTMP	;SAVE REQUESTED SIZE, CURRENT SIZE
	CAIG	J,JOBMAX##	;A HIGH SEGMENT?
	JRST	VIRCH1		;NO
	EXCH	J,.USJOB	;JOB NUMBER
	PUSHJ	P,HSVAD##	;COMPUTE THE EXTENT OF THE HIGH SEGMENT
	EXCH	J,.USJOB	;RESTORE HIGH SEGMENT NUMBER
	MOVE	T1,T2		;UVA OF HIGH SEGMENT
	ADD	T1,.USTMP	;CURRENT HIGEST ADDRESS IN THE HIGH SEGMENT
	ADD	T2,.USTMP+1
VIRCH1:	CAMG	T4,VIRTAL##	;IS ENOUGH SWAPPING SPACE AVAILABLE?
	PUSHJ	P,CKNZ1		; AND ARE ALL PAGES REQUESTED NOT IN-USE?
	  POPJ	P,		;NO, ERROR RETURN
	MOVE	T1,.USTMP	;RESTORE THE HIGEST ADDRESS REQUESTED
	MOVEM	T4,.USTMP	;SAVE THE NUMBER OF PAGES OF INCREASE
	CAIG	J,JOBMAX##	;A HIGH SEGMENT?
	PUSHJ	P,FNDPDS##	;NO, FIND THE PDB FOR THIS JOB
	PUSHJ	P,GSIZT		;DOES IT FIT (PHSYCIALLY, VIRTUALY, NOT AT ALL)?
	  JRST	VIRCH2		;MAYBE VIRTUALLY
	  SKIPGE P1		;VIRTUALLY IF NOT PHYSICAL ONLY
	JRST	VIRCH3		;PHYSICALLY
;HERE IF JOB ALREADY VIRTUAL OR HAS TO GO VIRTUAL
VIRCH2:	JUMPL	P1,CPOPJ##	;PHYSICAL-ONLY IF P1 NEGATIVE
	JRST	VIRCH5		;NO, LOW SEG VIRTUAL CORE
;HERE IF CAN GET PHYSICAL CORE
VIRCH3:	CAIG	J,JOBMAX##	;HIGH SEG?
	JRST	VIRCH4		;NO
	SKIPN	T2,.USHSE	;CREATING?
	MOVE	T2,.USHSS	;YES, THEN START AT THE VERY BEGINNING
	ADD	T1,.USHSS	;HIGHEST PAGE DESIRED
	TRO	T1,PG.BDY	;WORD ADDRESS
VIRCH4:	TDZA	T3,T3		;CLEAR
VIRCH5:	MOVSI	T3,(PG.SLO)	;ON DISK
	SUBI	T1,(T2)		;# OF PAGES TO EFFECT
	SKIPN	R		;IF CREATING FROM NOTHING,
	AOSA	T1		;INCLUDE 0TH PAGE
	AOS	T2		;ELSE START AT FIRST NON-EX PAGE
	LSHC	T1,W2PLSH##	;CONVERT TO PAGES
	HLL	T2,T3
	MOVNM	T1,.JDAT+JOBUAL##
	MOVEM	T2,.JDAT+JOBUAL##+1
	SETZM	.JDAT+JOBUAL##+6 ;NO SECOND UUO
	CAIG	J,JOBMAX##	;LOW SEG?
	JRST	VIRC14		;DO THE UUOS
;HERE TO SET UP THE REMAP FOR A NON-SHARABLE HIGH SEG
	HRRZM	T2,.JDAT+JOBUAL##+4 ;SOURCE PAGE
	HRRZM	T2,.JDAT+JOBUAL##+5 ;NEW HS ORIGIN (APPEND)=IN PLACE
	MOVE	J,.USJOB	;POINT TO LOW SEG
	MOVNM	T1,.JDAT+JOBUAL##+3 ;(NEGATIVE INDICATES APPEND)
	MOVEI	T1,3		;# OF ARGS
	MOVEM	T1,.JDAT+JOBUAL##+2 ;SET IN ARG BLOCK
	MOVE	T1,[.PAGRM,,JOBUAL##+2] ;SET ARG POINTER
	MOVEM	T1,.JDAT+JOBUAL##+6
	JRST	VIRC20
;HERE TO DO CORE UUO FOR A NON-ZERO SECTION

VIRCH6:	CAILE	J,JOBMAX##	;JOB OR HIGH SEG?
	JRST	VIRCH7		;HIGH SEG
	LSH	T1,W2PLSH##	;CONVERT TO HIGHEST PAGE DESIRED
	AOS	T1		;INCLUDE P0
	MOVNM	T1,.JDAT+JOBUAL##	;# OF PAGES TO "CREATE"
	LSH	T3,S2PLSH##	;CHANGE SECTION # TO PAGE 0 IN THAT SECTION
	HRLI	T3,(PG.IDC)	;DON'T CARE ABOUT PAGES THAT ALREADY EXIST
	MOVEM	T3,.JDAT+JOBUAL##+1 ;ARG
	SETZM	.JDAT+JOBUAL##+6 ;NO SECOND UUO
	PJRST	VIRC14		;FINISH UP

;HERE FOR HIGH SEG

VIRCH7:	MOVE	T3,[.PAGRM,,JOBUAL##+2] ;A REMAP WILL BE DONE
	MOVEM	T3,.JDAT+JOBUAL##+6 ;POINTER TO BLOCK
	MOVE	J,.USJOB
	LDB	T3,JBYSG2##
	XSFM	T4
	CAIN	T3,(T4)		;IS HIGH SEG IN THIS SECTION?
	SKIPN	R		;YES, DOES IT ALREADY EXIST?
	SKIPA	T2,[377000]	;DOESN'T EXIST OR WRONG SECTION, CREATE
	SKIPA	T2,.USHSE	;APPENDING, CURRENT END OF HIGH SEG
	SKIPA	T3,[400000]
	MOVE	T3,.USHSS
	ADDI	T1,(T3)
	LDB	T3,JBYSG2##	;RESTORE SECTION #
	HRLI	T1,(T4)		;SECTION #
	HRLI	T2,(T4)		;HERE ALSO
	LSHC	T1,W2PLSH##	;PAGES
	TLZN	T2,-1		;"CORE 0"
	SOS	T1		;YES, ONE MORE PAGE TO DELETE
	SUBM	T2,T1		;-# OF PAGES TO CREATE/REMAP
	JUMPL	T1,VIRCH8	;NEGATIVE, ALL IS WELL
	SETZM	.JDAT+JOBUAL##	;NO FIRST UUO FOR DELETE
	MOVEI	T2,1		;ONLY ONE ARG FOR DELETE REMAP
	MOVEM	T2,.JDAT+JOBUAL##+2 ;# OF ARGS
	MOVEM	T1,.JDAT+JOBUAL##+3 ;SET # OF PAGES (APPEND)
	PJRST	VIRC20
VIRCH8:	MOVEM	T1,.JDAT+JOBUAL##
	CAIN	T3,(T4)		;IS HIGH SEG IN THIS SECTION?
	SKIPN	R		;YES, DOES IT ALREADY EXIST?
	MOVNS	T1		;CREATE
	AOS	T2
	MOVEM	T2,.JDAT+JOBUAL##+1 ;CREATE ARG TOO
	HRRZS	T2		;INCASE DELETE
	MOVEM	T2,.JDAT+JOBUAL##+4 ;"FROM" REMAP PAGE
	MOVEM	T2,.JDAT+JOBUAL##+5 ;"TO" REMAP PAGE
	MOVEI	T2,3
	MOVEM	T2,.JDAT+JOBUAL##+2 ;# OF ARGS
	MOVEM	T1,.JDAT+JOBUAL##+3 ;SET # OF PAGES (APPEND)
	JRST	VIRC20
;HERE TO DEALLOCATE STORAGE
VIRCH9:	CAIG	J,JOBMAX##	;JOB?
	JRST	VIRC10		;YES, DO AS LOW SEG STUFF
	ASH	T4,W2PLSH##	;CONVERT TO PAGES
	JUMPE	T4,VIRC18	;NOTHING TO DO
	MOVNM	T4,.JDAT