Google
 

Trailing-Edge - PDP-10 Archives - BB-4172G-BM - language-sources/glxpfh.mac
There are 7 other files named glxpfh.mac in the archive. Click here to see a list.
TITLE GLXPFH - Page fault handler for the GALAXY library on the DECsystem-10
SUBTTL Dave Cornelius 16-Aug-79

;
;
;                COPYRIGHT (c) 1979
;                    DIGITAL EQUIPMENT CORPORATION
;
;     THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED
;     AND COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE
;     AND WITH THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE.   THIS
;     SOFTWARE  OR ANY OTHER COPIES THEREOF MAY NOT BE PROVIDED OR
;     OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON.  NO  TITLE  TO
;     AND OWNERSHIP OF THE SOFTWARE IS HEREBY TRANSFERRED.
;
;     THE INFORMATION  IN  THIS  SOFTWARE  IS  SUBJECT  TO  CHANGE
;     WITHOUT  NOTICE  AND SHOULD NOT BE CONSTRUED AS A COMMITMENT
;     BY DIGITAL EQUIPMENT CORPORATION.
;
;     DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY
;     OF  ITS  SOFTWARE  ON  EQUIPMENT  WHICH  IS  NOT SUPPLIED BY
;     DIGITAL.


;This module is loaded in the low segment of any program
;	using the GALAXY library code, GLXLIB.

	SEARCH	GLXMAC			;SEARCH GLXMAC FOR SYMBOL DEFS
	PROLOG(GLXPFH,PFH)

	PFHEDT==2			;EDIT LEVEL FOR MODULE
SUBTTL Table of Contents
SUBTTL	Revision History

COMMENT \

Revision History

EDIT	GCO	DESCRIPTION

1		Create GLXPFH from the remains of GLXINI

2		Don't throw out symbols until first timer trap
		And when we do throw 'em out, do it in one or two UUOs,
		not one PAGE. for each page thrown out

 \ ;end of revision history
SUBTTL	Fault handler data space

TOPS10 <	;THIS CODE EXISTS ONLY FOR THE TOPS10 MONITOR

COMMENT \
The fault handler will live in the realm of the library.
The code which handles the faults and determines what to do
is sharable, and part of the library high segment.
Since the monitor check to see if we are executing in the fault
handler is a range check of our PC agains the limits in .JBPFH,
the code should be as low as possible in the high segment.
We load it right after the entry vector.
Included as part of the fault handler (ie inside the high and low
bounds of the monitor's range check) is 10 words of 
dirty space.  Since the library is sharable, these 10 words
can't live in the high segment.
We put them in a page which is just below the library high segment.
Unfortunately, LINK doesn't support relocatable PSECTs, so
alot of this 'page manipulation' magic is done at link time.
The PSECTs whose origins must be defined (at LINK time) are:

If the library is impure (ie it will live in the lowseg), then
all of this PSECT nonsense is unneccessary, and not used.

Name	Location	Contents
.HIGH.	400000		The library sharable, reentrant code.
PFHDAT	377000		Fault handler data page (mostly blank)
DATA	600000		Local storage for library. Library symbol table

\

IFN GLXPURE,<				;Only if we're going to be sharable...
	.PSECT	PFHDAT			;Into dirty PFH data space
	PFHEST==32			;Estimated size of fault handler data
					;If this is wrong, assembly won't go.
					;If it is too high, we'll return the
					;space anyway.
	BLOCK	PAGSIZ-PFHEST		;Get to near top of page

>;End IFN GLXPURE

PFHDST:					;Start of the data spce

PFHBGN:	JRST	PFHSTA			;ENTERED HERE
PFH.PC:	0				;PC AT TIME OF TRAP
PFH.PN:	0				;PAGE FAULT WORD
PFH.TI:	0				;VIRTUAL AGE OF THE PAGE FAULT HANDLER
PFH.RA:	0				;CURRENT PAGING RATE
PFH.PV:	0				;Highest PSI,,Adr of PSI vec
	BLOCK	3			;ROOM FOR FUTURE EXPANSION

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

NICUCN:	BLOCK	1
NICCNT:	BLOCK	1
GVACNT:	BLOCK	1
TIMCNT:	BLOCK	1

IFN GLXPURE,<				;... Only for high segments
	PFHDSZ==.-PFHDST		;Size of the data space
PFHDEN:					;End of PFH data space
IFG <PFHDSZ-PFHEST>,<PRINTX ?PFH DATA SPACE TOO BIG, INCREASE PFHEST DEFINITION>
	.ENDPS				;Back to the library
>;End IFN GLXPURE

>;End TOPS10
SUBTTL The Page Fault Handler (TOPS-10 Only)

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

TOPS10 <
PFHSTA:
	MOVEM	P,PFHS.P		;SAVE SOMEBODY'S P
;*** Temporary
	MOVEM	P,PFHACS+P
	MOVEI	P,PFHACS
	BLT	P,PFHACS+P-1
;*** End Temporary
	MOVE	P,[IOWD PF.PSZ,PF.PDL]	;GET MY OWN PDL
	PUSH	P,P1			;BEGIN TO SAVE SEVERAL REGISTERS
	PUSH	P,P2			;CAUSE I'M GOING TO CALL SUBROUTINES
	PUSH	P,P3			;FOR SWAPPING AND SEARCHING
	PUSH	P,P4			;AND THESE ARE THE ARGUMENTS
	LOAD	P1,PFH.PC,RHMASK	;Get addr of fault
	CAIL	P1,PFHDST		;Skip if before fault handler
	CAILE	P1,PFHEND		;or was fault off end of handler
	SKIPA				;Fault outside of handle
	 HALT	.			;Oops! We're very confused
	LOAD	P2,PFH.PN,PF.HFC	;THE FAULT TYPE
	CAIG	P2,PFHDCT		;RANGE CHECK
	  JRST	PFHDSP(P2)		;DISPATCH THE FAULT
PFHDSP:	JRST	PFHILL			;PAGE FAULT CODE = 0 OR TOO BIG
	JRST	PFHGVA			;.PFHNA = 1 = NO ACCESS
	JRST	PFHSWI			;.PFHNI = 2 = NOT IN CORE
	JRST	PFHSWU			;.PFHUU = 3 = NOT IN CORE FOR UUO
	JRST	PFHTIM			;.PFHTI = 4 = VM TIMER TRAP
PFHDCT==.-PFHDSP-1			;DISPATCH LENGTH (0-n)

PFHILL:	$STOP(UPF,Unknown page fault type)

PFHSWU:	AOSA	NICUCN			;Count 'not in core for UUO' faults
PFHSWI:	AOS	NICCNT			;Count 'not in core' faults
	LOAD	P3,PFH.PN,PF.HPN	;GET THE PAGE THAT CAUSED THE FAULT
	MOVEI	P2,1			;ONLY 1 PAGE
	MOVE	P1,[.PAGIO,,P2]		;FUNCTION PAGE IO,,ARGUMENT BLOCK
	PAGE.	P1,			;SWAP IT IN
	  JRST	PFHSWF			;FAILED, SEE WHY
	MOVX	P1,PT.WRK		;THE WORKING SET BIT
	IORM	P1,PAGTBL##(P3)		;PAGE IS NOW IN CORE
	JRST	PFHXIT			;DISMISS THE FAULT
PFHSWF:	CAIE	P1,PAGLE%		;IS MY CORE LIMIT EXCEEDED
	  $STOP(PIF,Page in failure)
	PUSHJ	P,ANYOUT		;SWAP OUT SOMETHING (ANYTHING)
	JRST	PFHSWI			;TRY THE SWAPIN NOW

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

PFHGVA:	AOS	GVACNT
	LOAD	P3,PFH.PN,PF.HPN	;GET THE PAGE NUMBER WE CAN'T ACCESS
	TXO	P3,1B0			;SET TO ALLOW ACCESS
	MOVEI	P2,1			;ONLY ONE ARG
	MOVE	P1,[.PAGAA,,P2]		;GIVE ACCESS,,ARGS
	PAGE.	P1,			;SET ACCESS ALLOWED ON THAT PAGE
	  $STOP(CGA,Cannot give access to page)
	JRST	PFHXIT			;AND EXIT FROM THE PAGE FAULT HANDLER

;HERE ON A TIMER TRAP, SEE IF WE CAN CLEAN UP SOME CORE

PFHTIM:	AOS	TIMCNT
	SKIPE	INMEMF##		;SOMEBODY FOOLING WITH THE PAGE TABLE?
	JRST	PFHXIT			;Yes, don't screw 'em up
	PUSHJ	P,PGSDES		;NO, DESTROY ANY JUNK PAGES
	PUSHJ	P,PGSSWP		;And dump out any swappable pages

PFHXIT:	POP	P,P4			;RESTORE ACS
	POP	P,P3			;...
	POP	P,P2			;...
	POP	P,P1			;...
	MOVE	P,PFHS.P		;RESTORE THE OLD AC P
;*** Temporary
	$DATA	(PFHACS,20)
	PFH..X==0
REPEAT	20,<
	CAME	PFH..X,PFHACS+PFH..X
	PUSHJ	P,PFHDIE
	PFH..X==PFH..X+1
>
;*** End Temporary
	JRSTF	@PFH.PC			;DISMISS THE FAULT

;*** Temporary
PFHDIE:	OUTSTR	[ASCIZ/
?GLXPFH is confused. Please save this core image
/]
	$STOP	(PFH,GLXPFH IS CONFUSED)
	EXIT	1,
	POPJ	P,
;*** End Temporary
SUBTTL PGSDES - Routine to destroy unused pages

;CALL IS:	NO ARGUMENTS
;
;TRUE RETURN:	ALWAYS


PGSDES:	MOVE	P4,PAGSTA##		;THE FIRST AVAILABLE PAGE
PGSD.1:	CAIL	P4,MEMSIZ		;OFF THE END OF THE WORLD
	  POPJ	P,			;YES, RETURN
	MOVE	P1,PAGTBL##(P4)		;GET TABLE ENTRY FOR PAGE IN P4
	TXC	P1,PT.ADR		;NEED CHECK FOR BOTH SO FLIP
	TXNN	P1,PT.USE!PT.ADR	;USED OR NOT ADDRESSABEL
	  PUSHJ	P,DESTRY		;DESTROY THE PAGE (COULD BE PAGED OUT)
	AOJA	P4,PGSD.1		;AND CONTINUE LOOPING

DESTRY:	MOVEI	P3,(P4)			;WANT IT IN P3
	TXO	P3,1B0			;SET TO DESTROY
	MOVEI	P2,1			;1 ARGUMENT
	MOVE	P1,[.PAGCD,,P2]		;CREATE/DESTROY,,ARGUMENT
	PAGE.	P1,			;TRY TO DESTROY IT
	  $STOP(PDF,Page destroy failed)
	ZERO	P1			;CLEAR A REG
	EXCH	P1,PAGTBL##(P4)		;CLEAR PAGE TABLE ENTRY, GET OLD
	TXNE	P1,PT.WRK		;JUST DESTROY AN IN-CORE PAGE
	  SOS	AVBPGS##		;YES, REDUCE COUNTER ACCORDINGLY
	POPJ	P,			;RETURN
SUBTTL	PGSSWP - Swap out swappable pages
;PGSSWP - This routine will try to page out any pages
;	which are marked as swappable in PAGTAB but have managed to
;	crawl back into the working set.  This can happen for instance,
;	in the case of symbols thrown out during initialization
;	but faulted back in at a breakpoint by a DDT search of the symbol
;	table.
PGSSWP:
	PUSHJ	P,INDDT			;Fault from DDT itself?
	POPJ	P,			;Yes, don't throw out symbols

	SETZB	P4,SWPTBL		;Start at page 0, With none thrown out
PGSS.1:	CAIL	P4,MEMSIZ		;Off the end?
	PJRST	PGSS.2			;Yes, dump out any we found, and return
	MOVE	P1,PAGTBL(P4)		;Get entry for this page
	TXNE	P1,PT.WRK		;Is this one out already?
	TXNN	P1,PT.SWP		;No, is it swappable?
	AOJA	P4,PGSS.1		;Swapped, or not a candidate, skip it

;Found a  page to throw out, add it to the table
	AOS	P3,SWPTBL		;Get next slot number
	MOVEM	P4,SWPTBL(P3)		;Save page number to go out
	MOVX	P1,1B0			;Set to page OUT, not in!
	IORM	P1,SWPTBL(P3)		;Light in arg word for UUO
	MOVX	P1,PT.WRK		;Get the working set bit
	ANDCAM	P1,PAGTBL(P4)		;This page is out! (hopefully)
	CAIL	P3,SWPTLN		;Just filled up table?
	PUSHJ	P,PGSS.2		;Yes, throw out what we have
	AOJA	P4,PGSS.1		;Then try the next one

;A routine to page out whatever has been found as swappable
PGSS.2:	SKIPN	SWPTBL			;Anything to do?
	POPJ	P,			;No, just return
	MOVX	P1,<XWD .PAGIO,SWPTBL>	;Aim at the block we've built
	PAGE.	P1,			;Dump these pages out!
	CAXN	P1,PAGMI%		;Lost because we're confused?
	SKIPA				;Wins, keep going
	$STOP	(SPF,<Page out for symbols failed, error code ^O/P1/>)
	SETZM	SWPTBL			;Clear UUO counter
	POPJ	P,			;Return

;Data space for the symbol pager
	SWPTLN==^D25			;At most (usually) 25 pages of symbols
	$DATA	(SWPTBL,SWPTLN+1)	;UUO block, counted
SUBTTL	INDDT- Routine to determine if fault occurred inside DDT

;Returns .+1 if fault happened in DDT
;Returns .+2 if fault was outside DDT

INDDT:	SKIPN	P1,.JBDDT		;Get end,, start of DDT
	JRST	INDSKP			;No DDT, skip return
	HLRZ	P2,P1			;Isolate ending adrs
	HRRZS	P1			;Isolate starting adrs
	LOAD	P3,PFH.PC,RHMASK	;Get fault PC
	CAIL	P3,(P1)			;Below start of DDT
	CAILE	P3,(P2)			;Or above end
INDSKP:	AOS	(P)			;Not in DDT
	POPJ	P,			;Give skip return
SUBTTL ANYOUT - Routine to swap out anything possible 

; CALL IS:	NO ARGUMENTS
;
ANYOUT:	MOVE	P4,PAGSTA##		;FIRST MAKE A PASS LOOKING FOR ANY FREE

ANYO.1:	PUSHJ	P,ANYO.4		;PAGES THAT CAN BE DESTROYED
	JUMPE	P4,ANYO.2		;NONE, PAGE SOMETHING OUT
	TXNE	P1,PT.USE		;IS THIS IN-CORE PAGE USED
	  AOJA	P4,ANYO.1		;YES, LOOK FOR ANOTHER
	PJRST	DESTRY			;NO, DESTROY IT NOW AN SAVE A SWAP
ANYO.2:	AOS	P4,SWPSTA##		;START WHERE WE LEFT OFF
	PUSHJ	P,ANYO.4		;FIND A SWAPPED IN PAGE
	JUMPN	P4,ANYO.3		;SWAP IT OUT
	MOVE	P4,PAGSTA##		;RE-START AT THE FIRST
	PUSHJ	P,ANYO.4		;TRY NOW
	JUMPN	P4,ANYO.3		;FOUND ONE, SWAP IT OUT
	$STOP(NTS,Nothing to swap out)
ANYO.3:	MOVEM	P4,SWPSTA##		;REMEMBER FOR LATER
	MOVE	P3,P4			;COPY THE PAGE NUMBER
	TXO	P3,1B0			;SET TO SWAP OUT INSTEAD OF SWAP IN
	MOVEI	P2,1			;ONLY 1 PAGE FOR NOW
	MOVE	P1,[.PAGIO,,P2]		;FUNCTION SWAP,,ARGUMENT BLOCK
	PAGE.	P1,			;OUT IT GOES
	  $STOP(POF,Page out failure)
	MOVX	P1,PT.WRK		;GET THE WORKING SET BIT
	ANDCAM	P1,PAGTBL##(P3)		;NOT THERE ANY MORE
	POPJ	P,			;AND RETURN FOR ANY RETRIES

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


PFHEND:		;THE LAST LOCATION WITHIN THE PAGE FAULT HANDLER

 > ;END TOPS10 CONDITIONAL
SUBTTL SETPFH - Set up private page fault handler

;This is a no-op for TOPS-20 systems, for TOPS-10, we install
; our PFH.

;CALL IS:
;	M%INIT MUST be called before coming into SETPFH, since
;		we depend on PAGSTA being setup

;	No arguments
;
;TRUE RETURN:	Always

SETPFH::	
TOPS10 <
	$SAVE	<P1,P2>
	SETZM	INMEMF##		;CLEAR FLAG
	MOVE	S1,[PFHEND,,PFHBGN]	;GET START AND END OF PFH
	MOVES	PFHBGN			;Get it in core
	MOVEM	S1,.JBPFH##		;STORE LOCATION OF OUR PFH
	PUSHJ	P,MAPCOR		;Read the state of the working set
	MOVE	S1,[.STTVM,,^D1000]	;SET VIRTUAL TIMER TRAP (1 SEC)
	SETUUO	S1,			;
	  JFCL				;IGNORE ANY FAILURES
	MOVE	S1,PAGSTA##		;GET START OF FREE SPACE
	SUBI	S1,1			;BACK OFF BY ONE
	MOVEM	S1,SWPSTA##		;AND STORE IT FOR LATER USE
IFN GLXPURE,<
	MOVEI	S1,PFHDAT/PAGSIZ	;Page # of data page
	MOVX	S2,PT.INI		;Page in initial core image bit
	ANDCAM	S2,PAGTBL(S1)		;Allow returns of pieces of this page
	MOVEI	S1,PFHDST-PFHDAT	;Size of space at start of page
	MOVEI	S2,PFHDAT		;Get addr of dirty page
	PUSHJ	P,RETMEM		;Give back that wasted space
IFN PFHEST-PFHDSZ,<
	MOVEI	S1,PFHEST-PFHDSZ	;Size of that slot
	MOVEI	S2,PFHDEN		;Addr of small chunk above fault handler
	PUSHJ	P,RETMEM		;Give that back too
>
	SKIPN	S1,.HIGH.+.JBHSM	;Load high seg sym pointer
	JRST	SETP.1			;No high seg syms, try low seg
	PUSHJ	P,PAGSYM		;Page out the high seg symbols
	HLRE	S1,.HIGH.+.JBHSM	;Get neg length of high syms
	HRRZ	S2,.HIGH.+.JBHSM	;And get start adr
	SUB	S2,S1			;Compute the adr of first free
	TXNN	S2,PAGSIZ-1		;Symbols fit exactly on a page?
	JRST	SETP.1			;Just fit, don't return anything
	LOAD	S1,S2,PAGSIZ-1		;Get number used in this page
	SUBI	S1,PAGSIZ		;Make neg number free
	MOVNS	S1			;And now positive
	PUSHJ	P,RETMEM		;Give back that space
SETP.1:
>;End IFN GLXPURE

	SKIPE	S1,.JBSYM		;Load low seg symbol pointer
	PUSHJ	P,PAGSYM		;Page out the low seg symbols
> ;END TOPS10 CONDITIONAL
	$RETT				;All done
TOPS10<
;RETMEM is used to return pieces of memory which may have been part of
; the initial core image
;Call with S1 - length of block
;   S2 - adr of block
;Returns true, memory returned, page marked as not in initial allocation
RETMEM:	
	PUSH	P,S2			;Save adr of block
	ADR2PG	S2			;Get page number
	MOVX	TF,PT.INI		;Get 'in initial core image' bit
	ANDCAM	TF,PAGTBL(S2)		;Clear, so we can RMEM
	POP	P,S2			;Get back start of block
	PJRST	M%RMEM			;Give back the space
>;END TOPS10

TOPS10<
;PAGSYM takes a symbol table pointer in S1 <-len,,adr> and marks
; all pages which completely reside in the symbol table
; as swappable on a timer trap
PAGSYM:
	$SAVE	<P1,P2>
	HRRZ	P2,S1			;Copy adr of block
	HLRE	P1,S1			;Make negative length of table
	MOVNS	P1			;And now length of table
	ADD	P1,P2			;Figure first free after syms
	ANDI	P2,-PAGSIZ		;Get page number of first page of syms
PAGS.1:	ADDI	P2,PAGSIZ		;Bump to next page
	CAILE	P2,(P1)			;Off end of symbols?
	$RETT				;Yes, stop the page out war
	MOVE	S1,P2			;Get addr of page to move out
	ADR2PG	S1			;Make it a page number
	MOVX	S2,PT.SWP		;Get 'swappable anytime' bit
	IORM	S2,PAGTBL(S1)		;Mark it as such
	JRST	PAGS.1			;Try the next page
> ;END TOPS10 CONDITIONAL
SUBTTL	MAPCOR  -  Read the page map bits from the monitor

;This routine will ask the monitor for the state of the working
; set and will set or clear the PT.WRK bits in PAGTBL

TOPS10<
MAPCOR:
	$SAVE	<P1,P2>
	MOVX	S1,MAPLEN		;Length of table
	MOVEM	S1,BITMAP		;Count it for the monitor
	MOVE	S1,[XWD	.PAGWS,BITMAP]	;Read working set into BITMAP
	PAGE.	S1,			;Ask the monitor
	$STOP	(CDW,<Can't determine working set error = ^O/S1/>)
	MOVE	S1,[POINT 1,BITMAP+1]	;Aim at the 512 bits
	MOVSI	S2,-^D512		;Start at page 0
	MOVX	P1,PT.WRK		;Get our working set bit
MAPC.1:	ILDB	P2,S1			;Get this page's bit
	XCT	[ANDCAM	P1,PAGTBL(S2)	;Set or clear our bit
		IORM	P1,PAGTBL(S2)](P2) ;To match monitor's bit
	AOBJN	S2,MAPC.1		;Do 'em all
	$RETT

	MAPLEN==<<^D512+^D35>/^D36>
	$DATA	(BITMAP,MAPLEN+1)	;Bit block, counted
>;END TOPS10
SUBTTL	NODDT - Remove DDT.VMX routine for TOPS10

;This routine is intended to be used as an emergency tool
; in case a library gets saved with DDT in its high segment.
; In this case, any lowseg program which also has DDT.VMX in page 700
; will get page overlap errors when getting the library.
; It is not the intention that the library be saved in this state,
; however, if it happens, then this routine will be useful.
; The procedure is as follows:
; .GET GLXLIB	;The bad one, with DDT.VMX in it
; .DDT		;get into DDT.VMX
; NODDT$G	;Start it at this routine
; .SAVE		;Save the library without the symbols

TOPS10<
	EXTERN	.JBDDT

NODDT:	SKIPN	S1,.JBDDT		;Get the end,,start adr of DDT
	JRST	NODD.2			;No DDT to begin with!
	HRRZ	T2,S1			;Get start adr of DDT
	HLRZ	P1,S1			;And ending adr
	ADR2PG	T2			;Make starting page number
	ADR2PG	P1			;Make ending page number
	SUB	P1,T2			;Get number of pages in DDT
	TXO	T2,1B0			;Light the destroy page bit
	MOVEI	T1,1			;Count the args to PAGE. UUO
	MOVE	S2,[XWD .PAGCD,T1]	;Create/Destroy,, adr of arg list
NODD.1:	PAGE.	S2,			;Get rid of a page
	 JRST	NODDFA			;Complain!
	AOS	T2			;Bump to next page
	SOJGE	P1,NODD.1		;And try the rest of the pages
	SETZ	S1,			;Store 0 in protected DDT loc
	SETDDT	S1,			;And tell the monitor
	EXIT				;And go back to monitor

;Here is trying to destroy DDT, and it isn't here at all!
NODD.2:	OUTSTR	[ASCIZ/
% DDT not loaded
/]					;Inform the dumb user
	EXIT				;And leave 'em alone


;Here if the PAGE. UUO fails
NODDFA:	OUTSTR	[ASCIZ/
?Can't destroy DDT page
/]
	EXIT				;And die
>;End TOPS10
PFH%L:
	END