Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_1_19910112 - 7/ap23-mon/getsav.mac
There are 28 other files named getsav.mac in the archive. Click here to see a list.
; UPD ID= 8522, RIP:<7.MONITOR>GETSAV.MAC.3,   9-Feb-88 15:52:26 by GSCOTT
;TCO 7.1218 - Update copyright date.
; *** Edit 7278 to GETSAV.MAC by MRASPUZZI on 31-Mar-86, for SPR #21196
; Make edit 7125 put the JFN only into T1 (instead of fork handle,,JFN)
; Edit 7125 to GETSAV.MAC by MOSER on 12-Aug-85
; FIX XCT ONLY GET BUG 
;EDIT 7125 - ALWAYS SET UP T1 FOR CREADF/SREADF FIX XCT ONLY GET
; UPD ID= 2079, SNARK:<6.1.MONITOR>GETSAV.MAC.29,   3-Jun-85 14:38:02 by MCCOLLUM
;TCO 6.1.1406  - Update copyright notice.
; UPD ID= 1147, SNARK:<6.1.MONITOR>GETSAV.MAC.28,   4-Dec-84 10:41:37 by TBOYLE
;TCO 6.1.1070 (QAR 706281) Propogate FH%EPN when SS%EPN used at SSB10+...
; UPD ID= 4857, SNARK:<6.MONITOR>GETSAV.MAC.27,  20-Sep-84 09:16:05 by MOSER
;TCO 6.2220 - FIX ZERO COMPRESSED GET TO NOT CRASH IF BOGUS IOWDS
; UPD ID= 4802, SNARK:<6.MONITOR>GETSAV.MAC.26,  13-Sep-84 16:15:11 by MOSER
;TCO 6.2219 - SAVE ONLY PDVAS WITHIN RANGE OF PAGES SAVED
; UPD ID= 4711, SNARK:<6.MONITOR>GETSAV.MAC.25,  20-Aug-84 11:24:08 by LOMARTIRE
;TCO 6.2160 - Do not allow a GET on SRV:
;Also, fix error handling in previous edit.
; UPD ID= 4685, SNARK:<6.MONITOR>GETSAV.MAC.24,  14-Aug-84 10:11:09 by LEACHE
;In previous edit, leave last process error in T1
; UPD ID= 4632, SNARK:<6.MONITOR>GETSAV.MAC.23,  30-Jul-84 17:17:56 by MCCOLLUM
;TCO 6.2154 - Add ERJMP after PMAP in MAPIT to prevent NOSKTR BUGHLTs.
; UPD ID= 4520, SNARK:<6.MONITOR>GETSAV.MAC.22,  13-Jul-84 19:51:19 by PURRETTA
;Update copyright notice
; UPD ID= 3614, SNARK:<6.MONITOR>GETSAV.MAC.21,   1-Feb-84 09:45:08 by PURRETTA
;TCO 6.1962 - Check for errors returned by DVCHR at GTSVID.
;Make GTSVID return +1 failure, +2 sucess.
; UPD ID= 3488, SNARK:<6.MONITOR>GETSAV.MAC.19,  20-Jan-84 09:33:46 by CJOHNSON
;TCO 6.1902 - Make SSAVE properly fill out the EXE directory page with zeroes
; UPD ID= 2913, SNARK:<6.MONITOR>GETSAV.MAC.18,  20-Sep-83 15:20:32 by LOMARTIRE
;TCO 6.1781 - Do not allow a GET on a TTY or a PTY
; UPD ID= 2863, SNARK:<6.MONITOR>GETSAV.MAC.17,  23-Aug-83 15:37:22 by LOMARTIRE
;TCO 6.1709 - Make SSAVE save empty sections
; UPD ID= 2098, SNARK:<6.MONITOR>GETSAV.MAC.16,  28-Mar-83 17:38:46 by MURPHY
;Minor cleanup -- Change some numeric ACs to symbolic.
; UPD ID= 1646, SNARK:<6.MONITOR>GETSAV.MAC.15,  12-Jan-83 16:36:32 by CHALL.WIZARD
;TCO 6.1458 GET02- Fix CSKBUGs by not calling FUNLK twice on GETE2 error
;Also, create GETE0 and eliminate some literals which use GETE2.
; UPD ID= 1549, SNARK:<6.MONITOR>GETSAV.MAC.14,  21-Dec-82 08:08:57 by MOSER
;TCO 6.1409 - PREVENT FLKTIM-FLKNS
; UPD ID= 1470, SNARK:<6.MONITOR>GETSAV.MAC.13,  22-Nov-82 07:21:58 by GRANT
;TCO 6.1010 - Use MSEC1 instead of IFIW in SAVEB
; UPD ID= 1469, SNARK:<6.MONITOR>GETSAV.MAC.12,  19-Nov-82 14:26:24 by MOSER
;MORE TCO 6.1376
; UPD ID= 1465, SNARK:<6.MONITOR>GETSAV.MAC.11,  18-Nov-82 13:57:41 by MOSER
;TCO 6.1376 - PREVENT FLKTIM-FLKNS
; UPD ID= 167, SNARK:<5.MONITOR>GETSAV.MAC.10,  10-Sep-81 16:41:46 by MURPHY
;In CHKLOD - flush section from page ident before call to MSCANP
; UPD ID= 2211, SNARK:<5.MONITOR>GETSAV.MAC.9,  18-Jun-81 10:16:11 by OSMAN
;tco 5.1373 - Make GET into section 0 of self from section n of self
;work.  (merely turn on PM%EPN im MAPIT)
; UPD ID= 1651, SNARK:<5.MONITOR>GETSAV.MAC.8,   5-Mar-81 15:50:57 by OSMAN
;tco 5.1270 - Prevent SAVE from crashing the system
; UPD ID= 1536, SNARK:<5.MONITOR>GETSAV.MAC.7,   9-Feb-81 09:18:35 by OSMAN
;Remove SAVEAC in GTSVIF so that GET% with 0 in lh AC1 returns correct error code
; UPD ID= 1519, SNARK:<5.MONITOR>GETSAV.MAC.6,   5-Feb-81 09:08:17 by OSMAN
;Don't fill in PPC left half in VECSET, since a CONTINUE will crash system
; UPD ID= 1512, SNARK:<5.MONITOR>GETSAV.MAC.5,   3-Feb-81 11:03:37 by OSMAN
;CALL RESPMP before CHKLOD after MAPIT to correctly handle overlay checking
; UPD ID= 1442, SNARK:<5.MONITOR>GETSAV.MAC.4,  15-Jan-81 15:52:52 by FLEMMING
;add code for SMAP/RSMAP
; UPD ID= 1329, SNARK:<5.MONITOR>GETSAV.MAC.3,   1-Dec-80 16:27:58 by OSMAN
;Check for valid section number
; UPD ID= 1293, SNARK:<5.MONITOR>GETSAV.MAC.2,  19-Nov-80 10:15:54 by OSMAN
;Fix high page default

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

;GET AND SAVE ROUTINES

;AC USAGE:
; P5 - PTN OF UPT,,0
; P4 - File status save for execute-only GET
; P3 - UPPER LIMIT PAGE
; P2 - LOWER LIMIT PAGE
; P1 - FORK HANDLE,,JFN (AC1 ARG AFTER CHECKING)
; Q1, Q2, Q3 - Misc.
;  F - FLAGS FROM CALL
;If GT%ARG is on in AC1, AC2 points to an argument block, whose contents
;are interpreted as follows:
;ARG+.GFLAG/	flags
;	GT%LOW		;low address given
;	GT%HGH		;high address given
;	GT%BAS		;base given
;	GT%CSH		;cache this program
;	GT%CCH		;clear program cache
;ARG+.GLOW/	low address, used iff GT%LOW on
;ARG+.GHIGH/	high, used iff GT%HGH on
;ARG+.GBASE/	section to use, used iff GT%BAS on
;FPG0A USED AS WINDOW TO USER SPACE

.GET::	MCENT
	TRVAR <CHKPN,CHKQAN,LUS,NGPDVS,<PDVARG,1+.POADE>,PDVBLK,PDVPTR,GHNJFN,DIRPAG,WRKPAG,DIRLPR,DIRCNT,CURPAG,SPCCNT,<ENTVCW,2>,GBASE,OURFGS,ADVSEC,PROPTR,<PSAVEM,4>>

;ENTVCW/	saved entry vector (2 words)
;GBASE/		-1 or base section to load single-section exe file into.
;OURFGS/	our flags.
	GTFEXO==1B0		;Doing execute-only GET
	VRPRCF==1B1		;Process is virgin
;ADVSEC/	first process section found in exe file.
;PROPTR/	pointer to process address space
;PSAVEM/	saved PMAP ac's
	MOVX T1,VRPRCF		;Before we know better, process is virgin.
	MOVEM T1,OURFGS		;Store initial flags.
	SETOM LUS		;No computed section in SETP5 yet
	SETOM ADVSEC		;No advertised section from file known yet
	XCTU [HRRZ F,T1]	;Get user's flags.
	TXZ F,GT%JFN		;KEEP ONLY THE FLAGS
	TXZE F,GT%ARG		;ADDITIONAL FLAGS GIVEN?
	XCTUU [IOR F,.GFLAG(T2)] ;YES, GET REST OF FLAGS FROM ARG BLOCK
	MOVX T4,SC%WHL!SC%OPR	;YES.
	TXNN F,GT%ADR		;ALL OF THE ADDRESS SPACE?
	TDNN T4,CAPENB		;PRIVILEGED USER?
	TXZ F,GT%CSH		;NO. CAN'T ADD TO CACHE THEN
	TXNE F,GT%CCH		;CLEAR CACHE?
	JRST FLSCSH		;YES, GO DO IT
	MOVEI P3,777+FLD(MXSECN,VSECNO)_-9 ;HIGH PAGE DEFAULT IS LARGEST SECTION
	MOVEI P2,0		;LOW PAGE DEFAULT IS 0
	TXNE F,GT%ADR		;USER GIVING LIMITS?
	JRST [	XCTU [HLRZ P2,2] ;YES, GET THEM
		 XCTU [HRRZ P3,2]
		 JRST .+1]
	TXNE F,GT%LOW		;LOW ADDRESS GIVEN IN ARG BLOCK?
	XCTUU [MOVE P2,.GLOW(T2)] ;YES, GET IT
	TXNE F,GT%HGH		;HIGH ADDRESS GIVEN
	XCTUU [MOVE P3,.GHIGH(T2)] ;YES, GET IT FROM ARG BLOCK
	MOVNI T1,1		;FIRST ASSUME NO SPECIFIC BASE SECTION
	TXNN F,GT%BAS		;GET IT IF GIVEN
	JRST GET6		;NOT GIVEN
	XCTUU [SKIPL T1,.GBASE(T2)] ;IT'S GIVEN, PICK IT UP
	CAILE T1,MXSECN		;CHECK FOR LEGAL SECTION NUMBER
	ITERR (ARGX23)		;"INVALID SECTION NUMBER"
GET6:	MOVEM T1,GBASE		;REMEMBER BASE
GET01:
	CALL GTSVID		;CHECK PARAMETERS, ETC.
	 ITERR ()		;Failed, return error in T1
	MOVE P1,T4		;COPY JFN
	MOVEM T4,GHNJFN		;REMEMBER HANDLE,,JFN
	LDB T4,[POINTR T3,DV%TYP]  ;GET DEVICE TYPE
	CAIE T4,.DVTTY		;IS IT A TTY?
	CAIN T4,.DVPTY		;OR A PTY?
	ITERR(GETX1)		;YES, ILLEGAL
	CAIN T4,.DVSRV		;IS IT SRV:?
	ITERR(GETX1)		;YES, THIS IS ALSO ILLEGAL
	TXO T2,OF%RD		;Must have READ
	TLNN T3,777		;DISK?
	TXO T2,OF%EX		;Yes, request READ and EXECUTE
	OPENF			;OPEN FILE FOR INPUT (T1 set up by GTSVID)
	 JRST GET02		;Error-- try for just EXECUTE
	JRST GET03		;OK-- Go get the file
; Try to open the file for execute-only access

GET02:	MOVE T4,OURFGS		;GET OUR INTERNAL FLAGS
	TXNN T4,VRPRCF		;ERROR IF NOT A VIRGIN PROCESS
	 ITERR
	TLNN T3,777		;Error if not a disk
	TXNE F,GT%ADR		;Error if address limits given for execute-only
	 ITERR
	HRRZ T1,P1		;Get JFN
	TXZ T2,OF%RD		;Try to open file without READ (just EXECUTE)
	OPENF			; . . .
	 ITERR ()		;If we can't even get EXECUTE, give up now

; File is open for only EXECUTE access
; Try to make process execute-only

	CALL FLOCK		;Lock process structure
	HLRZ T1,P1		;Get specified process handle
	CALL STJFKR		;Convert RFH to JRFN
	 JRST GETE0		;Clean up and give error in AC T1
	CALL SETEXO		;Set process as execute-only
	 JRST [CALL FUNLK	;Process not virgin-- unlock process structure
	       MOVX T1,VRPRCF	;Remember process is not virgin
	       ANDCAM T1,OURFGS	;by turning off virgin flag
	       HRRZ T1,P1	;Get JFN back
	       TXO T1,CO%NRJ	;Don't release the JFN yet
	       CLOSF		;Close the file now
	        JFCL		; . . .
	       JRST GET01]	;Re-try the OPENF with READ and EXECUTE
				;This time return the OPENF error
	CALL FUNLK		;Unlock process structure
; The process is now execute-only
; Now set READ access and the execute-only GET bit (so PMAP works)

	NOINT			;Protect the use of READ access and
				; the execute-only GET bit
	MOVX T1,GTFEXO		;Remember that we are doing all this stuff so
				; we can undo it in the end
	IORM T1,OURFGS
	HRRZ T1,P1		;Get the JFN
	CALL SREADF		;Set READ access and restrict access to this process
	MOVE P4,T2		;Save old state of status
	CALL SETGXO		;Enable this process for execute-only GET

; File is open. Check out the process.

GET03:	CALL GTGLCK		;GET THE FORK LOCK
	MOVE T4,GHNJFN		;GET HANDLE AND JFN FOR GTSVIF
	CALL GTSVIF		;Setup PTN in P5
	 JRST GETE0		;Clean up and give error in AC T1

; Now do the GET

GET2:
    REPEAT 0,<			;OLD FAST GET CODE
	CALL TSTFST		;SEE IF A FAST GET CAN BE DONE
	 SKIPA			;COULDN'T
	JRST DONEG		;DID IT. ALL DONE THEN
    >				;END OF OLD FAST GET CODE
	HRRZ 1,P1		;JFN
	BIN			;GET IOWD COUNT,ADR
	 ERJMP GETE3		;FAILED. GO CLEAN UP AND GIVE ERROR RETURN
	MOVE Q2,2
	HLRZ 2,2
	CAIN 2,EXEFIL		;AN EXE FILE?
	JRST GETEXE		;YES. GO DO IT THEN
	TXNE F,GT%CSH		;DOING SPECIAL GET?
	JRST GETE1		;YES. MUST BE EXE FILE
	CAIL 2,1000		;CHECK FOR SHARE SAVE FILE
	CAIL 2,10000
	JRST .+2
	JRST SGET		;SHARE SAVE
	JUMPGE Q2,GET1		;POSITIVE MEANS END
;FILE IS ZERO-COMPRESSED

	ADDI Q2,1
	NOINT			;BE NOINT WHILE PAGE MAPPED
	CALL GET3		;SET MAP FOR PAGE
	JRST GET5

GET4:	TRNN Q2,777		;JUST CROSSED PAGE BOUNDARY?
	 CALL GET3		;YES, REMAP
GET5:	JUMPL Q1,[TLNE Q1,(1B1)	;GET A LOAD ERROR?
		 JRST GETE5	;YES.
		 BIN		;NO. USE BIN TO SKIP THIS BLOCK
		  ERJMP GETE4	;FAILED. GO CLEAN UP AND GIVE ERROR RETURN
		 AOBJN Q2,GET4	;THROW WORD AWAY
		 JRST GET5A]
	HRRZ 2,Q2		;COMPUTE (NEG) NO. WDS LEFT ON PAGE
	ANDI 2,PGSIZ-1
	SUBI 2,PGSIZ
	HLRE 3,Q2		;GET (NEG) NO. WDS LEFT IN PTR
	JUMPGE T3,[MOVEI T1,GETX1
		   JRST GETE6]	;NOT NEGATIVE IS BAD FORMAT
	CAMGE 3,2		;GET 'MINIMUM'
	MOVE 3,2
	PUSH P,3		;SAVE IT
	MOVE 2,Q1		;CONSTRUCT IFIW FOR @
	TXO 2,IFIW
	MOVEI 2,@2		;CONSTRUCT PTR TO DEST
	HRLI 2,(<POINT 36,0>)
	SIN			;MOVE WORDS TO DEST
	 ERJMP GETE4		;FAILED. GO CLEAN UP AND GIVE ERROR RETURN
	POP P,2			;RECOVER STARTING COUNT
	SUB 3,2			;COMPUTE WORDS MOVED
	HRL 3,3			;PUT COUNT IN BOTH HALVES
	ADD Q2,3			;UPDATE AOBJN PTR
	JUMPL Q2,GET4		;LOOP UNLESS PTR EXHAUSTED
GET5A:	CALL GET3X		;CLEAN UP MAPPINGS
	JRST GET2		;NEXT BLOCK
;CHECK AND REMAP PAGE

GET3:	CALL GET3X		;UNDO PREVIOUS MAPPING IF ANY
	NOINT			;BE NOINT WHILE PAGES MAPPED
	LDB 2,[POINT 9,Q2,26]	;PAGE NUMBER NEXT REFERENCE
	CAMG 2,P3		;PAGE WITHIN LIMITS?
	CAMGE 2,P2
	JRST [	MOVSI Q1,(1B0)	;NO, SET FLAG TO PREVENT STORE
		 RET]
	MOVE 1,2		;PAGE NUMBER
	TXNN F,GT%NOV		;WANT TO AVOID OVERLAYS?
	JRST GET3N		;NO. GO ON THEN
	HLL 1,P1		;YES. GET FH
	RPACS			;GET ACCESS INFO FOR THIS PAGE
	TXNN 2,PA%PEX!PA%IND	;DOES THIS PAGE EXIST?
	JRST GET3N		;NO. GO ON THEN
	MOVSI Q1,(3B1)		;YES. GIVE ERROR
	RET			;AND DONE
GET3N:	HLL 1,P5		;GET PTN OF USER PAGE TABLE
	MOVE 2,[PTRW+FPG0A]
	CALL SETMPG		;SETUP WINDOW TO USER PAGE
	MOVE Q1,Q2		;MAKE @Q1 POINT TO FPG0A
	ANDI Q1,777000		; I.E. FPG0-USERPAGE
	MOVN Q1,5
	ADDI Q1,FPG0A
	HRLI Q1,Q2		;GIVE IT AN INDEX
	HRRZ 1,P1		;RESTORE JFN TO 1
	RET

;UNMAP WINDOW PAGE

GET3X:	SETZ 1,
	MOVEI 2,FPG0A
	CALL SETMPG
	OKINT
	CALLRET	CYGLCK		;CYCLE FORK LOCK AND RETURN

;HERE WHEN READ ENTRY VECTOR WORD (LAST WORD IN FILE)

GET1:	HLRZ A,Q2		;GET LENGTH OF ENTRY VECTOR
	HRRZ B,Q2		;AND ADDRESS
	CALL VECSET		;SET THE ENTRY VECTOR

; Common exit for all flavors of GET

DONEG:	CALL GETEND		;Clean up file, etc.
	JRST MRETN
;ROUTINE TO LOCK THE FORK LOCK WHILE PRESERVING THE INTEGRITY OF THE EXECUTE
;ONLY FORK, IF ANY.
;
;THE FORK LOCK IS LOCKED ALL DURING GET TO PREVENT CALLING FLOCK FROM A LOWER
;LEVEL ROUTINE WHILE NOINT. THE CYGLCK ENTRY IS USED TO UNLOCK THE GET LOCK
;AND RELOCK IT WHICH ALLOWS INTS DURING A GET

CYGLCK:	SAVEAC <T1,T2>		;SAVE ACS TRASHED BY SREADF
	CALL FUNLK		;UNLOCK FORK LOCK
GTGLCK:	MOVE T1,OURFGS		;GET OUR FLAGS
	TXNN T1,GTFEXO		;DOING EXECUTE-ONLY GET?
	CALLRET FLOCK		;NO JUST LOCK AND RETURN
	MOVE T2,P4		;GET OLD FILE STATUS
	HRRZ T1,P1		;[7125][7278] Get JFN only
	CALL CREADF		;RESTORE STATE
	CALL CLRGXO		;CLEAR XCT ONLY GET
	OKINT			;UNDO NOINT
	CALL FLOCK		;GET LOCK WHILE OKINT
	NOINT			;NOINT AGAIN
	HRRZ T1,P1		;[7125][7278] Get JFN only
	CALL SREADF		;ALLOW READ
	CALLRET SETGXO		;SET XCT ONLY GET AND RETURN
;COMMON ROUTINE TO SET THE ENTRY VECTOR
;
;Accepts:	A/	length of vector
;		B/	address

VECSET:	STKVAR <LENES,ADDES>
	MOVEM A,LENES
	MOVEM B,ADDES		;SAVE LENGTH AND ADDRESS
	CALL FLOCK
	HLRZ 1,P1		;FORK HANDLE
	CALL SETLFK		;MAP PSB
	MOVE B,LENES		;GET LENGTH
	MOVEM B,EVLNTH(A)	;STORE IT
	MOVE B,ADDES		;GET ADDRESS
	MOVEM B,EVADDR(A)	;STORE IT TOO
	CALL CLRLFK
	CALL FUNLK
	RET			;AND DONE
;COMMON SETUP FOR GET, SAVE, SSAVE
; Sets up:
;	T4/	User AC1 (XWD process handle, JFN)
;	P5/	XWD PTN of process, 0
;	T1, T2 for OPENF
;	T3/	Device characteristics

GTSVI:	STKVAR <<HLDT3,2>,<HLDT1,2>>
	CALL GTSVID		;Do device stuff, setup T4, T1, T2, T3
	 ITERR ()		;Failed, return error in T1
	DMOVEM T1,HLDT1		;GTSVIF clobbers ac's
	DMOVEM T3,HLDT3		;SAVE T3,T4 ACROSS CALL
	CALL GTSVIF		;Do process stuff, setup P5
	 ITERR ()		;Failed, return error in T1
	DMOVE T1,HLDT1		;RESTORE OPENF JFN AND MODE BITS
	DMOVE T3,HLDT3		;RESTORE T3
	RET			;Return from GTSVI

;Setup device stuff:  T4, T1, T2, T3
;Returns +1 (error code in T1), +2 (OK)

GTSVID:
	UMOVE T4,1		;SAVE ARG
	TRZ T4,770000		;FLUSH MISC BITS FROM JFN
	HRRZ T1,T4		;JFN
	DVCHR			;GET DEVICE CHARACTERISTICS
	 ERJMP [MOVE T1,LSTERR	;Get error returned by DVCHR
		RET]		;Return failure
	MOVE T3,T2		;Move device characteristics
	HRRZ T1,T4		;JFN
	MOVSI T2,(^D36B5)	;BYTE SIZE FOR BIN/BOUT XFER
	RETSKP			;Sucess, Return +2

;Setup process stuff:  P5
;Returns +1 (error code in T1), +2 (OK)

GTSVIF:	HLLZ T1,T4		;Get process handle from call
	MOVEI T2,0		;Convert page number to PC section
	CALL FKHPTX		;Convert RFH.0 to PTN.0 and check for execute-only
	 RET			;Bad process handle-- non-skip return
	MOVEM T1,P5		;Remember the PTN
	RETSKP			;Return +2 from GTSVIF
;SETP5 sets the left half of P5 for a particular section
;This routine is clever about not recalculating if not necessary, so
;don't worry about calling it wherever P5 might need updating
;
;Accepts:	T1/	section
;
;Returns+1:	T1/	error code (all set for ITERR)
;Returns+2:	P5 lh/	spt index of page table

SETP5:	CAMN T1,LUS		;Have we done calculation yet?
	RETSKP			;Yes, nothing to do.
	MOVEM T1,LUS		;No, remember which section we're calculating
SETP51:	LSH T1,9		;FKHPTX wants page number
	HLL T1,P1		;Get process handle
	MOVEI T2,0		;Convert page number to PC section
	CALL FKHPTX		;Get the info
	 JRST [	CAIE A,ARGX06	;Invalid section?
		RET		;No, other error, give error return
		MOVEI A,0	;Create private section
		HLL B,P1	;Get correct process to create section in
		HRR B,LUS	;Say which section to create
		MOVEI C,1	;One per customer
		SMAP%		;Create the section
		MOVE T1,LUS	;Get section number again
		JRST SETP51]	;Go get new section's index
	HLL P5,T1		;Remember spt index in lf P5
	RETSKP

; GETEND - clean up at end of GET

GETEND:	MOVE T2,OURFGS		;Get our flags
	TXNN T2,GTFEXO		;Doing execute-only GET?
	 JRST GETEN1		;No, just close file
	MOVE T2,P4		;Get old file status
	HRRZ T1,P1		;[7125][7278] Get JFN only
	CALL CREADF		;Restore state to what it was at OPENF
	CALL CLRGXO		;Disable execute-only GET
	OKINT			;Now we can be interrupted
GETEN1:
	HRRZ T1,P1		;Get JFN
	CLOSF			;Attempt to close file
	 JFCL			;Can't-- maybe pages still mapped
	CALLRET FUNLK		;UNLOCK FORK LOCK AND RETURN
; Errors during GET . . .

;ERROR DURING GET.  GO OKINT IF NOW NOINT AND SAVE ERROR CODE

GETE5:	MOVEI 1,GETX3		;SAY OVERLAY ERROR
GETE6:	MOVEM 1,LSTERR		;MAKE THIS THE ERROR
GETE4:	OKINT			;ALLOW INTERRUPTS
GETE3:	MOVE Q1,LSTERR		;GET MOST RECENT ERROR
	JRST GETE2		;GO CLEAN UP AND TAKE ERROR RETURN

;GETE0- USE THE ERROR CODE IN AC T1
;GETE1- UNEXPECTED END OF FILE DURING GET

GETE0:	SKIPA Q1,T1		;GIVE THE ERROR IN AC T1
GETE1:	MOVEI Q1,GETX1		;FORCE RETURN OF ILLEGAL FILE FORMAT ERROR

;AN ERROR OCCURRED. AC Q1 HAS THE ERROR CODE. CLEAN UP AND TAKE ERROR RETURN

GETE2:	CALL GETEND		;Close file and clean up
	SETZ 1,
	MOVEI 2,FPG0A
	CALL SETMPG		;ENSURE TEMP PAGE CLEARED
	CAIN Q1,IOX4		;DID WE GET END OF FILE?
	 ITERR (GETX1)		;YES. RETURN ILLEGAL SAVE FILE FORMAT
	MOVE 1,Q1		;NO. RETURN THE REAL ERROR
	ITERR
;THE FILE IS AN EXE FILE. THE FIRST BLOCK MUST BE A DIRECTORY IN
;ORDER FOR CONTROL TO REACH THIS CODE

;DEFINITIONS FOR EXE FILES

EXEEND==1777			;END OF ALL BLOCKS
EXEFIL==1776			;BLOCK TYPE FOR DIR AND FOR GETTING
				;TO THIS CODE
EXEVEC==1775			;ENTRY VECTOR BLOCK
EXEPDV==1774			;PDV BLOCK
EXEWRB==1B2			;WRITE REQUEST IN FLAG BITS
EXESHR==1B1			;PAGE IS SHARABLE
EXEZRO==1B3			;PAGE EXISTS BUT IS ZERO

;BITS IN Q1 REMEMBERED DURING PROCESSING

EXEDIR==1B0			;SAW A DIRECTORY BLOCK
EXVEC==1B1			;SAW AN ENTRY BLOCK
NDSK==1B2			;IF ON, NOT A DISK

;FIELD DEFINITIONS FOR THE BLOCK CONTENTS

DEFSTR(EXADR,,35,27)		;FILE PAGE ADDRESS
DEFSTR(EXSEC,,26,18)		;SECTION NUMBER (I.E. WHICH 256K ADDRESS SPACE)
DEFSTR(EXCNT,,8,9)		;COUNT FIELD
DEFSTR(EXPAG,,35,27)		;PROCESS PAGE ADDRESS
GETEXE:	SETZM SPCCNT
	MOVX Q1,EXEDIR		;SAW A DIR
	MOVEI Q2,0(Q2)		;GET COUNT OF DIR WORDS
	SOS Q2			;DISCOUNT THE HEADER
	LSH Q2,-1		;2 WORDS PER ENTRY
	MOVEM Q2,DIRCNT		;NUMBER OF PAIRS
	CALL CHECKG		;MUST BE ON DISK
	 SKIPA			;NOT DISK
	JRST DOEN1		;GO SEE ABOUT DIR ENTRIES
	TXNE F,GT%CSH		;SPECIAL GET?
	JRST GETE1		;YES. CAN'T DO IT THEN
	NOINT			;MUST BE NOINT FOR REMAINDER OF JSYS
	TXO Q1,NDSK		;NOT A DISK
	CALL ASGPGS		;GET DIR PAGE
	 JRST [MOVEI T1,GJFX22	;JSB MUST BE FULL
	       JRST GETE6]
	MOVEM A,WRKPAG		;REMEMBER SCRATCH PAGE FOR NON-DISK DEVICES
	CALL ASGPGS		;GET DIR PAGE
	 JRST [MOVEI T1,GJFX22	;JSB MUST BE FULL
	       JRST GETE6]
	MOVEM A,DIRPAG		;SAVE PAGE
	SOS A			;LOOPER WORD IS ONE LESS
	HRLI A,-1000		;# OF WORDS
	MOVEM A,DIRLPR		;SET UP LOOP VALUE
	OKINT			;ALLOW INTS NOW
	MOVE B,DIRPAG		;THE DIRECTORY PAGE
	HRLI B,(POINT ^D36,)	;MAKE IT A BYTE POINTER
	MOVNI C,777		;REST OF PAGE
	CALL SINN		;READ IT IN
	MOVEI A,1		;THE CURRENT PAGE
	MOVEM A,CURPAG		;REMEMBER THIS
DOEN1:	JUMPE Q2,DOCAT		;IF NO DIR ENTRIES, GO AWAY
;LOOP TO PROCESS ALL OF THE DIRECTORY ENTRIES. EACH ENTRY IS TWO
;WORDS THE FIRST OF WHICH DESCRIBES THE FILE AND THE SECOND,
;DESCRIBING THE PROCESS

DOENT:	CALL CYGLCK		;CYCLE FORK LOCK
	CALL GTWRD		;GET FILE POINTER
	MOVE Q3,B		;SAVE IT
	CALL GTWRD		;GET PROCESS POINTER
	MOVEM B,PROPTR		;REMEMBER PROCESS POINTER
	LOAD A,EXSEC,B		;GET PROCESS DESTINATION SECTION
	SKIPGE ADVSEC		;HAVE WE SET UP ADVERTISED SECTION YET?
	MOVEM A,ADVSEC		;NO, SO THIS IS IT
	CALL FIXSEC		;FIX SECTION NUMBER TO POSSIBLE ALTERNATE MAPPING
	STOR A,EXSEC,PROPTR	;MAKE POSSIBLY-MODIFIED PROCESS DESIGNATOR
	JE <EXEZRO,EXADR>,Q3,ALLZER ;IF NON-EX, IGNORE IT
	SETZ C,			;CLEAR ACCESS BITS
	TXO C,PM%RD!PM%EX	;TURN ON READ AND EXECUTE
	TXNE Q3,EXEWRB!EXESHR	;WANT SOME FLAVOR OF WRITE?
	TXO C,PM%CPY		;YES. GIVE HIM COPY ON WRITE
	LOAD A,EXPAG,PROPTR	;GET PROCESS PAGE ADDRESS
	LOAD D,EXCNT,PROPTR	;GET REPEAT COUNT
	ADD D,A			;CALCULATE LAST PAGE TO AFFECT
	MOVE B,A		;GET COPY OF FIRST PAGE IN GROUP
	XOR B,D			;COMPARE SECTIONS AT BEGINNING AND END OF RANGE
	TXNE B,VSECNO_-9	;MAKE SURE WE'RE DEALING WITH A SINGLE SECTION
	JRST [	MOVEI Q1,GETX5	;WE DON'T HANDLE SECTION-CROSSING.  REASONS:
		JRST GETE2]
				;1)MSCANP WON'T WORK RIGHT
				;2)THE GETX4 CHECK WOULD HAVE TO BE CHANGED
				;3)THE SMAP% IN SETP5 WOULD HAVE TO BE MORE CLEVER
				;4)RDPAGE WOULD BE SLOWED DOWN BY HAVING TO CALL
				;  SETP5 IN LOOP INSTEAD OF BEFORE IT
	CAML D,P2		;WITHIN BOUNDS?
	CAMLE A,P3		;?
	JRST ALLZER		;NO. DONT LOAD IT THEN
	LOAD B,EXADR,Q3		;STARTING FILE PAGE
	CAMLE D,P3		;VERIFY UPPER BOUND
	MOVE D,P3		;TOO LARGE. USE USER'S
	CAMGE A,P2		;VERIFY LOWER BOUND
	CALL [	ADD B,P2	;TOO LOW. COMPUTE PROPER FILE PAGE
		SUB B,A		;""
		MOVE A,P2	;AND USE USER'S LIMIT
		RET]		;AND PROCEED
	SUB D,A			;GET NEW REPEAT COUNT
	AOS D			;ONE MORE FOR GOOD MEASURE
	JXE Q3,EXADR,[	SETOM B	;IF ALL ZERO, MAKE DELETE ARG
			JRST DOENT0] ;AND PROCEED
	TXNE Q1,NDSK		;IS THIS A DISK?
	JRST [	CALL NTDISK	;NOT DISK
		JRST ALLZER]	;DONE
DOENT0:	EXCH A,B		;MAKE ARGS CORRECT
	CALL MAPIT		;GET IT MAPPED
	 JRST GETE0		;CLEAN UP AND GIVE ERROR IN AC T1
	CAMN T1,[-1]		;DOING UNMAP ONLY?
   REPEAT 0,<			;OLD FAST GET CODE
	JRST [	TXNN F,GT%CSH	;YES. A SPECIAL GET?
		JRST ALLZER	;NO. GO ON THEN
		MOVE A,D	;YES. GET COUNT
		CALL MAKPGS	;MAKE NON-EX POINTERS IN MAP
		JRST ALLZER]	;DONE
   >				;END OF OLD FAST GET CODE
	JRST ALLZER
	ADDM D,SPCCNT		;NO. ACCOUNT FOR PAGES
ALLZER:	SOSLE DIRCNT		;MORE TO DO?
	JRST DOENT		;YES. GO DO THEM
	JRST DOCAT		;ALL DONE
;FIXSEC changes a process section that was read from the exe file into the
;process section we really want to load that section into.
;
;Accepts:	A/	process section from file
;
;Returns+1:	A/	real process section to use instead

FIXSEC:	SKIPGE B,GBASE		;Is user relocating a one-section exe file?
	RET			;No relocation, so don't change section
	CAME A,ADVSEC		;Yes, make sure this file is only one section.
	JRST [	MOVEI Q1,GETX4	;Give error because attempt to relocate multiple sections
		JRST GETE2]
	MOVE A,GBASE		;Give caller the remapped section
	RET

;ROUTINE TO GET NEXT WORD FROM DIRECTORY

GTWRD:	TXNE Q1,NDSK		;A DISK?
	JRST GTWRD1		;NO
	MOVEI A,0(P1)		;JFN
	BIN 			;GET ENTRY
	 ERJMP GETE3		;FAILURE. GO CLEAN UP AND TAKE ERROR RETURN
	RET			;ALL DONE

GTWRD1:	MOVE A,DIRLPR		;CURRENT POINTER
	AOBJP A,DRBAD		;MORE?
	MOVE B,0(A)		;GET DATUM
	MOVEM A,DIRLPR		;SAVE POINTER
	RET			;AND DONE
;ROUTINE TO GET IN FILE PAGE FOR NON-DISK DEVICE AND TO PUT
;DATA IN PROPER USER'S PAGE
;ACCEPTS:
;	A/ FIRST PROCESS PAGE NUMBER
;	B/ FIRST FILE PAGE NUMBER
;	D/ REPEAT COUNT

NTDISK:	STKVAR <FPPN,FFPN,RPTCNT>
;FPPN -	  First process page number
;FFPN -   First file page number
;RPTCNT - Repeat count
	MOVEM A,FPPN		;SAVE ARGS
	MOVEM B,FFPN		;SAVE B
	MOVEM D,RPTCNT
	LOAD A,VSECNO_-9,FPPN	;GET SECTION WE'RE LOADING INTO
	CALL SETP5		;CALCULATE ITS INDEX FOR SETMPG
	 CALL DIRERR		;FAILED, CLEAN UP
	CALL CHKLOD		;CHECK FOR OVERLAYS
	 JRST DIREO3		;GIVE ERROR BECAUSE THERE ARE CONFLICTS
	MOVE B,FFPN		;GET FILE PAGE AGAIN
	CAME B,CURPAG		;AT PROPER FILE PAGE?
	JRST [	CAMG B,CURPAG	;NO. WANT TO MOVE FORWARD?
		JRST DRBAD	;NO. ERROR
		SUB B,CURPAG	;GET # OF PAGES TO WASTE
		ADDM B,CURPAG	;NEW CURRENT
		LSH B,^D9	;*512
		MOVNI C,0(B)	;NEGATIVE COUNT
		MOVEI B,B	;DUMMY POINTER
		MOVEI A,0(P1)	;JFN
		CALL SINN	;GET DATA
		JRST .+1]	;GO INLINE
RDPAGE:	MOVE A,FPPN		;GET CURRENT PROCESS PAGE
	HLL A,P1		;FORK HANDLE
	RPACS			;SEE ABOUT IT
	TLNE B,(1B5)		;NON-EX?
	TXNE B,PA%WT!PA%CPY	;HAVE WRITE ACCESS?
	SKIPA			;YES. ALLOW IT
	JRST WASTE		;NO. FORGET IT THEN
	TRZ A,VSECNO_-9		;SETMPG WANTS PAGE MODULO SECTION
	HLL A,P5		;GET PTN OF PROCESS'S UPT
	MOVE B,WRKPAG		;GET WORK PAGE
	TXO B,PA%WT		;NEED WRITE ACCESS
	CALL SETMPG		;DO MAPPING
WASTE:	MOVE B,WRKPAG
	HRLI B,(POINT ^D36,)	;MAKE IT A POINTER
	MOVNI C,1000		;COUNT
	CALL SINN		;GET PAGE
	SETZ A,			;FOR THE UNMAP
	MOVE B,WRKPAG		;THE PAGE ADDRESS
	CALL SETMPG		;UNMAP WORK PAGE
	AOS CURPAG		;HAVE STEPPED A PAGE
	AOS FPPN		;NEXT PROCESS
	AOS FFPN		;NEXT FILE PAGE
	SOSG RPTCNT		;MORE?
	RET			;NO
	JRST RDPAGE		;YES
;ROUTINE TO EXECUTE A SIN AND CHECK FOR EOF AND ERROR
;ACCEPTS:
;	B/ BP
;	C/COUNT

SINN:	MOVEI A,0(P1)		;GET JFN
	SIN			;SO THE SIN
	 ERJMP DIRERR		;FAILED. GO CLEAN UP AND TAKE ERROR RETURN
	RET			;OK
;LOOP TO READ THE NEXT BLOCK TYPE AND DISPATCH TO THE PROPER
;ACTION

DOCAT:	CALL CYGLCK		;CYCLE FORK LOCK
	CALL GTWRD		;GET NEXT DIR WORD
	MOVE Q2,B		;SAVE IT
	HLRZ B,Q2		;TYPE
	CAIN B,EXEPDV		;PDV BLOCK?
	JRST SETPDV		;YES, GO GET PDVAS
	CAIN B,EXEVEC		;VECTOR BLOCK?
	JRST SETVEC		;YES. GO DO IT
	CAIE B,EXEEND		;END OF INFORMATION?
	JRST SKPBLK		;NO. UNKNOWN BLOCK
	TXNE Q1,EXVEC		;AT THE END. SEEN A VECTOR TYPE YET?
	JRST DONEG1		;YES. ALL DONE THEN
	DMOVE A,[EXP 254000,0]	;NO, MAKE UP ONE
	CALL VECSET		;AND GO SET IT
DONEG1:	TXNE F,GT%CSH		;DOING A SPECIAL GET?
	CALL FSTENT		;YES. MAKE ENTRY IN CACHE THEN
	TXNN Q1,NDSK		;A DISK?
	JRST DONEG		;YES. ALL DONE
	MOVE A,WRKPAG		;NO. RELEASE STORAGE
	NOINT			;PROTECT STRUCTURES
	CALL RELPGS		;RELEASE
	MOVE A,DIRPAG		;THE OTHER PAGE
	CALL RELPGS
	OKINT			;ALLOW INTS
	HRRZ A,P1		;GET JFN
	MOVEI B,.MOFWF		;SPACE FORWARD OVER TAPE MARK
	MTOPR
	 ERJMP .+1		;DONT CARE ABOUT ERRORS
	JRST DONEG		;AND DONE
;UNKNOWN BLOCK TYPE. SKIP THE BLOCK

SKPBLK:	MOVEI D,-1(Q2)		;NUMBER TO SKIP
	JUMPE D,DOCAT		;IF NONE, ALL DONE
	CALL GTWRD		;GET ONE
	SOJG D,.-1		;DO ALL
	JRST DOCAT		;NO. GET NEXT CATEGORY

;ERROR OCCURRED IN READING THE FILE. SAVE ERROR CODE IN AC Q1 AND GO
;CLEAN UP

DRBAD:	MOVEI Q1,GETX1		;Say invalid file format
	JRST DIREO1		; . .

DIREO3:	SKIPA Q1,[GETX3]		;SAY HAD OVERLAY ERROR
DIRERR:	MOVE Q1,LSTERR		;GET LAST ERROR

;AN ERROR OCCURRED DURING READING OF THE FILE. AC Q1 CONTAINS THE ERROR CODE.
;CLEAN UP AND TAKE ERROR RETURN

DIREO1:	MOVE A,WRKPAG		;FREE WORK PAGE
	NOINT			;PROTECT STRUCTURES
	CALL RELPGS
	MOVE A,DIRPAG		;FREE DIR PAGE
	CALL RELPGS		;FREE THIS ONE TOO
	OKINT			;ALLOW INTS
	JRST GETE2		;AND GO ERROR OUT
;SETPDV handles a PDV block encountered in the file.  The PDVAs are
;added to the process's PSB.

SETPDV:	HRRZ T2,Q2		;ISOLATE LENGTH OF BLOCK
	SOJ T2,			;GET NUMBER OF PDVAS AVAILABLE
	HRRZM T2,NGPDVS		;REMEMBER HOW MANY
	HRRZM T2,.POCT2+PDVARG	;STORE FOR PDVOP% JSYS
	AOJ T2,			;LEAVE ROOM FOR HEADER
	NOINT			;DON'T ALLOW INTERRUPTS WHILE FREE SPACE TIED UP
	CALL ASGJFR		;GET SOME FREE SPACE FOR STORING PDVAS
	 CALL [	MOVEI Q1,MONX02	;FAILED, PRESUMABLY BECAUSE JSB FULL
		JRST GETE2]
	MOVEM T1,PDVBLK		;REMEMBER ADDRESS OF BLOCK
	MOVEM T1,PDVPTR		;REMEMBER POINTER TO WHERE NEXT PDVA GOES
	AOJ T1,			;STEP TO WHERE FIRST PDVA WILL BE PUT
	MOVEM T1,.PODAT+PDVARG	;TELL PDVOP% WHERE DATA BLOCK IS
SETPD1:	SOSGE NGPDVS		;ANY MORE PDVAS TO BE READ IN?
	JRST SETPD2		;NO
	CALL GTWRD		;YES, GET NEXT ONE
	AOS A,PDVPTR		;STEP POINTER TO WHERE IT GOES
	MOVEM B,(A)		;STORE THIS PDVA
	LOAD A,VSECNO,B		;GET SECTION NUMBER
	CALL FIXSEC		;CHANGE IT IN CASE NEEDED
	STOR A,VSECNO,@PDVPTR	;STORE UPDATED SECTION NUMBER
	JRST SETPD1		;LOOP TO READ IN ALL OF THEM
SETPD2:	MOVEI T1,.PODAT+1	;GET LENGTH OF ARGUMENT BLOCK
	MOVEM T1,.POCT1+PDVARG
	HLRZ T1,GHNJFN		;GET FORK WHOSE PDVAS ARE BEING SET
	MOVEM T1,.POPHD+PDVARG
	MOVEI T1,.POADD		;TELL PDVOP% TO ADD THESE PDVAS
	MOVEI T2,PDVARG		;TELL IT WHERE ARG BLOCK IS
	PDVOP%			;SET THE PDVAS
	 ERJMP [MOVE T2,PDVBLK	;FAILED, GET ADDRESS OF FREE SPACE BLOCK
		MOVEI T1,JSBFRE	;SAY WHICH POOL
		CALL RELFRE	;RELEASE THE FREE SPACE
		MOVE Q1,LSTERR	;GIVE WHATEVER ERROR PDVOP% DID
		JRST GETE2]
	MOVE T2,PDVBLK		;RELEASE FREE SPACE STORAGE
	MOVEI T1,JSBFRE
	CALL RELFRE
	OKINT			;ALLOW INTERRUPTS AGAIN
	JRST DOCAT		;GET TO NEXT PART OF EXE FILE

;ROUTINE TO SET THE ENTRY VECTOR FROM AN EXE VECTOR BLOCK

SETVEC:	TXOE Q1,EXVEC		;SEEN ONE YET?
	JRST GETE1		;YES. BOMB HIM THEN
	HRRZ B,Q2		;GET COUNT
	CAIGE B,3		;PROPER TYPE?
	JRST SKPBLK		;NO. FORGET IT THEN
	PUSH P,B		;SAVE THE COUNT
	CALL GTWRD		;GET NUMBER OF WORDS IN ENTRY VECTOR
	MOVE Q2,B		;THE COUNT
	CALL GTWRD		;GET ADDRESS OF ENTRY VECTOR
	MOVE A,Q2		;GET LENGTH AND ADDRESS IN A AND B
	DMOVEM A,ENTVCW		;SAVE ENTRY VECTOR
	LOAD A,VSECNO,1+ENTVCW	;GET SECTION NUMBER FROM FILE'S ENTRY VECTOR
	CALL FIXSEC		;GET SECTION WHERE THIS EXE FILE IS BEING PUT
	STOR A,VSECNO,1+ENTVCW	;MAKE REAL ENTRY VECTOR
	DMOVE A,ENTVCW		;GET VECTOR BEING STORED
	CALL VECSET		;SET IT IN THE PROCESS
	POP P,Q2		;RESTORE THE COUNT
	MOVEI Q2,-2(Q2)		;GET COUNT MINUS TWO
	JRST SKPBLK		;SKIP THE REST
;ENCOUNTERED CODE FOR SHARED SAVE.  EACH PAGE TO BE PUT IN THE
;FORK IS INDICATED BY ONE WORD WHERE:
; B0-8: ACCESS BITS, B0=WRITE COPY
; B9-17: FORK PAGE NUMBER TO RECEIVE PAGE
; B18-35: FILE PAGE NUMBER OF SOURCE PAGE

;IN ORDER TO USE THE MULTI-PAGE PMAP AND SAVE TIME, THIS
;ROUTINE SCANS THE INPUT FOR SUCCESSIVE FORK/FILE PAGES AND
;DOES AS MANY AS POSSIBLE WITH EACH PMAP

SGET:	CAIE 2,1000		;CORRECT TYPE NUMBER?
	JRST GETE1		;NO, STRANGE FILE
	MOVEI Q3,0(Q2)		;SAVE COUNT
	CALL CHECKG		;MAKE SURE IS DISK
	 JRST [MOVEI Q1,SSAVX1	;MUST BE DISK
	       JRST GETE2]	;RETURN ERROR
	HRRZ 1,P1		;JFN
SGT5:	BIN			;GET FIRST WORD
	 ERJMP GETE3		;FAILED. GO CLEAN UP AND TAKE ERROR RETURN
SGT4:	CALL SGCHKL		;CHECK LIMITS
	 JRST [	SOJG Q3,SGT5	;NOT WITHIN LIMITS, SKIP PAGE
		JRST GET2]	;COUNT DONE
	PUSH P,2		;SAVE FIRST WORD OF THIS GROUP
	PUSH P,2		;SAVE COPY OF IT FOR INCREMENTING
	JRST SGT2		;ENTER LOOP TO SCAN FOR SEQUENTIAL PAGES

;GET NEXT INPUT WORD, SEE IF BOTH FORK AND FILE PAGE ARE 1
;BEYOND LAST INPUT WORD

SGT1:	CALL CYGLCK		;CYCLE FORK LOCK TO ALLOW INTS
	BIN
	 ERJMP GETE3		;FAILED. GO CLEAN UP AND TAKE ERROR RETURN
	CAMN 2,0(P)		;THIS PAGE SAME AS EXPECTED ONE?
	CALL SGCHKL		;AND WITHIN LIMITS?
	 JRST SGT3		;NO, MUST GO DO PREVIOUS INPUT
SGT2:	MOVE 2,[1,,1]		;BUMP FORK AND FILE PAGE NUMBERS
	ADDM 2,0(P)
	SOJG Q3,SGT1		;COUNT TOTAL PAGES
	POP P,4			;COUNT DONE, ALL INPUT NOW READ
	POP P,2			;RECOVER FIRST WORD OF GROUP
	SUBI 4,0(2)		;COMPUTE COUNT OF PAGES IN GROUP
	CALL SGTDO		;DO PMAP
	 JRST GETE0		;CLEAN UP AND GIVE ERROR IN AC T1
	JRST GET2		;CONTINUE WITH FILE

SGT3:	POP P,4			;RECOVER INCREMENTED PAGE NUMBERS
	EXCH 2,0(P)		;RECOVER FIRST WORD OF GROUP, SAVE NEXT
	SUBI 4,0(2)		;COMPUTE COUNT OF PAGES IN GROUP
	CALL SGTDO		;DO PMAP
	 JRST GETE0		;CLEAN UP AND GIVE ERROR IN AC T1
	POP P,2			;RECOVER LAST INPUT
	HRRZ 1,P1		;RESTORE JFN
	JRST SGT4		;GO START SCAN OF NEXT GROUP
;ROUTINE USED BY ABOVE TO SETUP AND DO PMAP

SGTDO:	MOVEI 1,0(2)		;FILE PAGE NUMBER
	LDB 3,[POINT 9,2,8]	;ACCESS
	LDB 2,[POINT 9,2,17]	;MEMORY PAGE
	ROT 3,-^D9
	TLZE 3,(1B0)		;WRITE COPY REQUEST?
	TLO 3,(PM%CPY)		;YES, SET APPROPRIATE BITS

;ENTRY FOR EXE FILES

MAPIT:	CALL SAVPMP		;SAVE PMAP ARGS
	LOAD A,VSECNO_-9,B	;GET SECTION NUMBER
	CALL SETP5		;UPDATE CORRECT INDEX FOR MSCANP IN CHKLOD
	 RETBAD			;FAILED, RETURN ERROR
	CALL RESPMP		;CHKLOD NEEDS PMAP ARGS
	CALL CHKLOD		;SEE IF OVERLAY CHECKS WANTED
	 RETBAD (GETX3)		;YES. AND IT DID
	CALL RESPMP		;RESTORE PMAP ARGS
	SKIPL T1		;DOING DELETE?
	HRL 1,P1		;NO, JFN TO LH
	HLL 2,P1		;FORK HANDLE TO LH
	HRRI 3,0(4)		;COUNT OF SUCCESSIVE PAGES TO DO
	TXO 3,PM%CNT!PM%EPN	;SAY DO MULTIPLE PAGES, GLOBAL PAGE NUMBER
	TXNE F,GT%PRL		;WANT PRELOADING?
	TXO T3,PM%PLD		;YES. TURN ON BIT FOR PMAP
;	TXNE F,GT%CSH		;DOING SPECIAL GET?
;	TXO T3,PM%IND		;YES. REQUEST INDIRECT POINTERS THEN
	PMAP
	 ERJMPR R		;IF PMAP GENERATES ITRAP, RETURN NICELY
	RETSKP

;SAVPMP and RESPMP are for saving and restoring the PMAP args

SAVPMP:	DMOVEM A,PSAVEM
	DMOVEM C,2+PSAVEM
	RET

RESPMP:	DMOVE A,PSAVEM
	DMOVE C,2+PSAVEM
	RET
;LOCAL ROUTINE TO CHECK PAGE AGAINST LIMITS

SGCHKL:	LDB 3,[POINT 9,2,17]	;GET PAGE NUMBER
	CAMG 3,P3		;TOO HIGH?
	CAMGE 3,P2		;OR TOO LOW?
	SOS 0(P)		;YES, CAUSE FAIL RETURN
	RETSKP

;LOCAL ROUTINE TO VERIFY FILE IS FROM DISK

CHECKG:	HRRZ 1,P1		;JFN
	DVCHR
	TLNE 2,777		;DISK FILE?
	RET			;NO
	RETSKP			;YES

;LOCAL ROUTINE TO CHECK FOR OVERLAYS
;
;Accepts:	T2 rh/	page number
;		T4 rh/	number of pages
;
;Returns+1:	some conflicts exist
;	+2:	no conflicts (all pages in range are nonexistent) or don't care

CHKLOD:	TXNN F,GT%NOV		;WANT TO PREVENT OVERLAYS?
	RETSKP			;NO. RETURN GOOD AND ALLOW MAP
	MOVEM T2,CHKPN		;REMEMBER ARGS
	MOVEM T4,CHKQAN
	MOVE T1,CHKPN		;STARTING PAGE IN PROCESS
	ANDX T1,LPGNO_-9	;ONLY LOCAL PAGE NUMBER HERE
	HLL T1,P5		;PTN OF THE PROCESS
	HRRZ T2,CHKQAN		;GET NUMBER OF PAGES
	CALL MSCANP		;SEE IF ANY EXIST IN THIS RANGE
	JUMPN 2,R		;YES. ERROR THEN
	RETSKP			;NO. ALLOW MAP
   REPEAT 0,<			;OLD FAST GET CODE

;TEST IF A FAST GET CAN BE DONE

;LOCAL DEFINITIONS FOR DATA STRUCTURE

DEFSTR (FGOFN,,35,12)		;THE OFN
DEFSTR (FGSPT,,23,12)		;THE PROTOTYPE MAP
DEFSTR (FGCNT,,11,11)		;THE SHARE COUNT
DEFSTR (FGVLD,,0,1)		;VALID BIT
	FGEVEC==1		;THE ENTRY VECTOR LENGTH
;    ;1+FGEVEC==2		;ENTRY VECTOR ADDRESS
	FGLLCK==3		;LOCK WORD

TSTFST:	SKIPE FSTTBL		;ANY ENTRIES IN THE CACHE?
	SKIPE P2		;YES. DOING ENTIRE ADDRESS SPACE?
	RET			;NO. CAN'T DO IT THEN
	TXNN F,GT%NOV		;CHECKING FOR OVERLAYS?
	CAIE P3,PGSIZ-1		;YES. UP TO THE END?
	RET			;NO. CAN'T DO IT
	CALL CHECKG		;ON DISK?
	 RET			;NO. CAN'T DO IT THEN
TSTFSN:	CSKED			;CRITICAL SECTION WHILE TABLE LOCKED
	LOCK FGLOCK		;LOCK UP THE TABLE
	CALL FNDOFN		;GET OFN FROM THE JFN
	 RETBAD (,<UNLOCK FGLOCK
		ECSKED>)
	MOVEI T4,FSTTBL+1	;START OF ENTRIES
	MOVE T3,FSTTBL		;COUNT
TSTFS0:	JE FGVLD,(T4),TSTFSX	;VALID?
	LOAD T2,FGOFN,(T4)	;GET OFN FROM TABLE
	CAIN T1,0(T2)		;SAME AS THE ONE IN THE JFN?
	JRST FSTGT1		;YES. GOT ONE
TSTFSX:	ADDI T4,FSN		;NEXT ENTRY
	SOJG T3,TSTFS0		;NO. DO THEM ALL
	RETBAD (,<UNLOCK FGLOCK
		ECSKED>)		;NOT FOUND

;FOUND A MATCHING ENTRY

FSTGT1:	AOS FGLLCK(T4)		;INCREMENT USE COUNT
	UNLOCK FGLOCK		;UNLOCK STRUCTURE LOCK
	NOINT			;DON'T GO NOINT BECAUSE USE COUNT IS UP
	ECSKED			;NOT CRITICAL WHEN TABLE UNLOCKED
	MOVE T1,T4		;COPY ENTRY
	CALLRET FSTGET		;DO FAST GET
;ROUTINE TO DO A FAST GET. AT THIS POINT
;FAST TABLE ENTRY IS LOCKED
;	T1/ ENTRY IN FAST GET TABLE
;	P1/ THE JFN


;RETURNS:	+1 SOME SORT OF FAILURE. MUST DO IT THE LONG WAY
;		+2 SUCCESS
;ONLY DOES A GET ON THE ENTIRE PROCESS SPACE.

FSTGET:	ACVAR <W1,W2,W3>	;WORK REGS
	STKVAR <WRKPG1,WRKPG2,SAVENT,<STRADR,2>,PSPTN,FSTOFN,FSTSPN,FSTSPP> ;SOME WORK LOCATIONS
	MOVEM T1,SAVENT		;SAVE TABLE INDEX
	LOAD T1,FGSPT,(T1)	;GET SPTN
	MOVEM T1,PSPTN		;SAVE SPTN OF PROTOTYPE MAP
	HLLZ T1,P1		;GET FORK HANDLE,,0
	MOVEI T2,0		;CONVERT PAGE NUMBER TO PC SECTION
	CALL FKHPTX		;GET PTN OF PROCESS
	JRST [	MOVE T1,SAVENT
		SOS FGLLCK(T1)	;REDUCE USE COUNT
		OKINT
		RET]		;SO FAIL
	HLLZ P5,T1		;SAVE SPTN
	CALL ASGPAG		;GET A PAGE FOR MAPPING FORKS' PT
	 JRST [	MOVE T1,SAVENT
		SOS FGLLCK(T1) ;FREE LOCK
		OKINT
		RET]
	MOVEM T1,WRKPG1		;SAVE PAGE
	CALL ASGPAG		;GET A PAGE TO MAP PROTOTYPE MAP
	 JRST [	MOVE T1,WRKPG1
		CALL RELPAG	;FAILED. RELEASE THIS
		MOVE T1,SAVENT
		SOS FGLLCK(T1)
		OKINT
		RET]
	MOVEM T1,WRKPG2		;SAVE THIS AS WELL

;NOW MAP THE FORK'S PT AND THE PROTOTYPE MAP

	MOVE T2,T1		;COPY PAGE
	MOVE T1,PSPTN		;GET PROTOTYPE MAP PAGE
	TXO T2,<PM%RD>		;ONLY NEED TO READ IT
	CALL SETMPG
	HLRZ T1,P5		;SPTN OF FORK'S PT
	MOVE T2,WRKPG1
	TXO T2,<PM%RD!PM%WT>
	CALL SETMPG		;MAP THIS AS WELL
	; ..
;PAGES ARE NOW MAPPED AND PROCESS VERIFIED. SCAN TABLE AND
;SET UP MAP

	MOVE T3,SAVENT		;GET FAST-GET ENTRY
	LOAD T1,FGOFN,(T3)	;GET THE OFN
	LOAD T2,FGCNT,(T3)	;GET THE COUNT
	MOVEM T1,FSTOFN		;SAVE OFN FOR LATER
	HRRZ T1,P1		;GET THE JFN
	CALL MJFCNT		;AND UPDATE THE JFN COUNT AS WELL
	 NOP
	MOVE W1,WRKPG2		;GET PROTOTYPE PT
	MOVE W2,WRKPG1		;GET FORK'S PAGE TABLE
	MOVE W3,IMMPTR		;GET IMMEDIATE PAGE POINTER
	TXO W3,UAAB		; TO CHECK FOR PLACE HOLDER
FSTSCN:	CALL FLOCK		;LOCK THE PROCESS STRUCTURE
FSTSC5:	SKIPE T1,0(W1)		;ANYTHING HERE?
	JRST FSTSC0		;YES. GO PROCESS IT
FSTS22:	ADDI W1,1		;NO. NEXT PAGE
	ADDI W2,1		;SAME HERE
FSTSC2:	TRNE W1,PGSIZ-1		;ALL DONE YET?
	JRST FSTSC5		;NO. GO DO NEXT ONE
	JRST FSTDON		;YES.
;FOUND A PAGE IN THE PROTOTYPE PAGE TABLE

FSTSC0:	SKIPE 0(W2)		;PROCESS HAVE A PAGE HERE?
	JRST FSTSC1		;YES. MUST DELETE FIRST
	CAMN T1,W3		;NO. IS THIS A PLACE-HOLDER?
	JRST FSTS22		;DONE.
	MOVEI T4,1		;INIT MAP COUNTER
	LOAD T2,IPPGN,0(W1)	;GET STARTING PAGE NUMBER
	MOVEM T2,FSTSPN		;AND SAVE IT
	MOVEM W2,FSTSPP		;SAVE STARTING PROCESS PAGE
LOOP:	ADDI W1,1		;NEXT PAGE
	ADDI W2,1		;AND SAME HERE
	TRNN W1,PGSIZ-1		;AT THE END YET?
	JRST LOPMAP		;YES. DO MAPPING THEN
	SKIPE 0(W2)		;SOMETHING HERE IN THE PROCESS?
	JRST LOPMAP		;YES. DO MAPPING THEN
	LOAD T3,IPPGN,0(W1)	;NO. GET PAGE # THEN
	CAIE T3,1(T2)		;THE EXPECTED ONE?
	JRST LOPMAP		;NO. DO MAP NOW
	MOVE T2,T3		;YES. SAVE PAGE #
	ADDI T4,1		;ANOTHER PAGE IN THE COLLECTION
	JRST LOOP		;AND EXAMINE MEXT PAGE
;READY TO DO MAP

LOPMAP:	CALL GPAC		;GET USER ACCESS BITS FROM PAGE POINTER
	MOVE T3,T1		;COPY ACCESS BITS TO PROPER REG
	MOVE T1,FSTSPN		;GET STARTING PAGE # IN FILE
	HRL T1,FSTOFN		;FORM SOURCE ADDRESS
	MOVE T2,FSTSPP		;GET PROCESS PAGE ADDRESS
	SUB T2,WRKPG1		;COMPUTE PROCESS PAGE NUMBER
	HLL T2,P5		;GET PROCESS PAGE NUMBER
	CALL MSETPT		;MAP IT
	JRST FSTSC2		;AND PROCEED

;PROCESS HAS A PAGE IN THE SLOT.

FSTSC1:	DMOVEM W1,STRADR	;SAVE START OF SCAN
FSTSC4:	ADDI W2,1		;NEXT PAGE
	ADDI W1,1
	TRNN W2,PGSIZ-1		;AT THE END?
	JRST FSTSC3		;YES.
	SKIPE 0(W1)		;ANYTHING HERE?
	SKIPN 0(W2)		;YES. ANYTHING IN PROCESS?
	SKIPA			;NO. DO DELETER NOW
	JRST FSTSC4		;YES TO BOTH. CONTINUE SCAN
FSTSC3:	CALL FUNLK		;RELEASE PROCESS LOCK
	SUB W2,1+STRADR		;COMPUTE PAGES SCANNED
	SETOM T1
	MOVE T2,1+STRADR	;GET START OF RANGE
	SUB T2,WRKPG1		;GET STARTING PAGE NUMBER
	HLL T2,P1		;PROCESS HANDLE
	MOVE T3,W2		;COUNT
	TXO T3,PM%CNT		;SET REPEAT COUNT
	PMAP			;DELETE PAGES
	DMOVE W1,STRADR		;RESTORE START OF RANGE
	JRST FSTSCN		;AND GO AGAIN
;MAP ALL DONE. ADJUST COUNTS AND CLEAN UP
;INCREASE SHARE COUNT HERE
;

FSTDON:	CALL FUNLK		;RELEASE PROCESS LOCK
	MOVE T1,WRKPG1
	CALL RELPAG		;CLEAN UP
	MOVE T1,WRKPG2
	CALL RELPAG		;""
	HLLZ T1,P5		;GET IDENT
	MOVEI T2,PGSIZ
	CALL PREPG		;PRELOAD THE PAGES
	 NOP
	MOVE T2,SAVENT
	MOVE A,FGEVEC(T2)	;GET POSSIBLE ENTRY VECTOR LENGTH
	SKIPE B,1+FGEVEC(T2)	;HAVE A VECTOR TO SET?
	CALL VECSET		;YES. SET IT THEN
	MOVE T1,SAVENT
	SOS FGLLCK(T1)		;FREE LOCK
	OKINT			;MATCH NOINT DONE EARLIER
	RETSKP			;AND DONE

	ENDSV.			;END STKVAR
	ENDAV.			;END ACVAR
   >				;END OF OLD FAST GET CODE
;ROUTINE TO MAKE AN ENTRY IN THE FAST GET CACHE

FSTENT:	SAVEAC <P2>		;GET A WORK REG
	LOCK FGLOCK		;LOCK UP THE TABLE
	MOVE P2,[-FSMAX,,FSTTBL]
	SKIPE 0(P2)		;ANYTHING HERE?
	JRST [	AOBJN P2,.-1	;NO. ANY MORE TO DO?
		UNLOCK FGLOCK	;DONE WITH TABLE
		RET]		;NO. DONE THEN
	CALL FNDOFN		;FIND OFN PLEASE
	 RET			;OOPS. GIVE UP THEN
	MOVEM T1,0(P2)		;STASH OFN FOR LATER USE
	UNLOCK FGLOCK		;DONE WITH TABLE
	CALLRET UPSHR		;UP THE SHARE COUNT
   REPEAT 0,<			;OLD CODE
FSTENT:	STKVAR <SVX,FSTPAG>		;SAVE ENTRY INDEX HERE
	CALL ASGPAG		;GET A WORK PAGE
	 RET			;COULDN'T
	MOVEM T1,FSTPAG		;SAVE IT
	NOINT
	LOCK FGLOCK		;GET LOCK
	MOVEI T4,FSTTBL+1	;START OF THE TABLE
	HRLI T4,-FSMAX		;THE TABLE SIZE
FSTEN0:	JN FGVLD,(T4),FSTEN1	;IF VALID, CAN'T USE IT
	MOVEM T4,SVX		;FOUND ONE. SAVE IT
	HLLZ T1,P1		;GET FORK HANDLE,,0
	MOVEI T2,0		;USE PC SECTION
	CALL FKHPTN		;GET HANDLE
	 JRST FSTENX		;BOO HIS
	HLLZ P5,T1		;SAVE SPTN OF PROCESS PAGE TABLE
	NOSKED
	CALL ASSPTL		;GET AN SPT ENTRY
	OKSKED
	MOVE T4,SVX
	STOR T1,FGSPT,(T4)	;SAVE SPTN
	DMOVE T2,ENTVCW		;GET ENTRY VECTOR
	DMOVEM T2,FGEVEC(T4)	;SAVE IT
	CALL SWPIN4		;ASSIGN PAGE TO SPTN
	CALL FNDOFN		;GET OFN FOR THE GET FILE
	 JRST [	MOVE T4,SVX
		LOAD T1,FGSPT,(T4) ;GET SPTN
		CALL DESPT	;RELEASE IT
		JRST FSTENX]	;AND GO ERROR OUT
	MOVE T4,SVX		;GET BACK INDEX
	STOR T1,FGOFN,(T4)
	MOVEI T2,1		;INCREMENT OFN COUNT
	CALL MUPSHR		;DO IT NOW
	HLRE T2,T4		;GET THIS OFFSET
	ADDI T2,FSMAX+1
	CAMLE T2,FSTTBL		;THIS NEW HIGHEST ENTRY?
	MOVEM T2,FSTTBL		;YES. STORE IT
	MOVE T2,SPCCNT
	STOR T2,FGCNT,(T4)	;SAVE THE SHARE COUNT
	; ..
;FSTENT CONTINUED
;NOW COPY THE PT

	LOAD T1,FGSPT,(T4)	;GET BACK PT
	MOVE T2,[PTRW+FPG0A]
	CALL SETMPG		;MAP SPTN
	MOVE T2,FSTPAG		;GET PAGE
	HLRZ T1,P5		;GET SPT OF FORKS' UPT
	TXO T2,PTRW
	CALL SETMPG		;MAP THIS AS WELL
	HRL T1,FSTPAG		;SOURCE ADDRESS
	HRRI T1,FPG0A		;DESTINATION ADDRESS
	BLT T1,FPG0A+PGSIZ-1	;COPY MAP
	MOVE T1,FSTPAG		;GET WORK PAGE
	CALL RELPAG		;GET RID OF IT
	SETZM T1
	MOVEI T2,FPG0A
	CALL SETMPG		;UNMAP SPTN
	MOVE T4,SVX
	SETONE FGVLD,(T4)	;SAY ENTRY NOW VALID
FSTENX:	UNLOCK FGLOCK		;DONE WITH LOCK
	OKINT
	RET			;AND DONE WITH ROUTINE

;STEP TO NEXT ENTRY

FSTEN1:	ADDI T4,FSN-1
	AOBJN T4,FSTEN0		;DO THEM ALL
	JRST FSTENX		;NONE FOUEN

	ENDSV.			;END STKVAR
   >				;END OF OLD FAST GET CODE
;SPECIAL ROUTINE USED BY GET WHEN BUILDING A PROTOTYPE
;GET ADDRESS SPACE. WILL CREATE PRIVATE POINTERS TO UNASSIGNED
;ADDRESSES TO USE AS PLACE HOLDERS.

MAKPGS:	ASUBR<COUNT,ID>
	HLLZ T1,ID		;GET FORK HANDLE
	MOVEI T2,0		;USE PC SECTION
	CALL FKHPTX		;GET SPTN OF PROCESS PAGE TABLE
	 RETBAD ()
	HLRZS T1		;GET UPT INDEX
	NOINT
	MOVE T2,[PTRW+FPG0A]
	CALL SETMPG		;MAP PAGE TABLE
	HRRZ T3,ID		;GET FIRST PAGE
	MOVN T4,COUNT		;GET NUMBER TO DO
	HRL T3,T4
	MOVE T4,IMMPTR
	TXO T4,UAAB		;MAKE THEM ALL UNASSIGNED
MAKPG0:	MOVEM T4,FPG0A(T3)	;STORE ONE
	AOBJN T3,MAKPG0		;DO THEM ALL
	SETZM T1
	CALL SETMPG		;UNDO MAPPING
	OKINT
	RET			;AND DONE

;ROUTINE TO FIND THE OFN IN A JFN.
;	P1/ THE JFN
;RETURNS:	+1 ALWAYS. T1/ THE OFN

FNDOFN:	SAVEAC <F>		;SAVE FLAGS
	SETZM F
	HRLZ T1,P1		;GET JFN
	CALL JFNOFN		;GET OFN FOR PAGE 0
	 RET			;JFN WENT AWAY
	HLRZS T1		;YES. GET PT0 THEN
	RETSKP			;DONE
;USER REQUEST TO FLUSH THE CACHE

FLSCSH:	MOVX T4,SC%OPR!SC%WHL	;CHECK PRIVILEGES
	TDNN T4,CAPENB		;HAVE ENOUGH?
	ITERR (CAPX1)		;NO. DON'T DO IT THEN
FLSCS0:	LOCK FGLOCK		;LOCK THE CACHE
   REPEAT 0,<			;OLD CODE
FLSSC2:	SETZM P2
	SKIPN FSTTBL		;ANY ENTRIES?
	JRST FLSDON		;NO. ALL DONE THEN
	MOVN P1,FSTTBL		;YES. GET NUMBER
	HRLZS P1
	HRRI P1,FSTTBL+1	;SCAN TABLE
FLSCSL:	JE FGVLD,(P1),FLSX	;THIS ONE VALID?
	SKIPE FGLLCK(P1)	;YES. ANY USERS?
	AOJA P2,FLSX

;FOUND ONE TO DO

	LOAD T1,FGOFN,(P1)	;GET THE OFN
	CALL RELOFN		;RELEASE IT
	LOAD T1,FGSPT,(P1)	;GET SPTN
	CALL DESPT		;RELEASE IT
	SETZRO FGVLD,(P1)	;SAY ENTRY IS NOW FREE
FLSX:	ADDI P1,FSN-1		;NEXT ENTRY
	AOBJN P1,FLSCSL		;DO THEN ALL
	JUMPN P2,[UNLOCK FGLOCK
		MOVEI T1,^D500	;WAIT A WHILE
		DISMS
		JRST FLSCS0]	;AND TRY AGAIN
FLSDON:	SETZM FSTTBL		;NO ENTRIES NOW
   >				;END OF OLD CODE
	MOVE P2,[-FSMAX,,FSTTBL] ;SCAN WHOLE TABLE
FLSLOP:	SKIPN T1,0(P2)		;ANYTHING HERE?
	JRST FLSX		;NO.
	CALL RELOFN		;YES. NOT USING OFN ANYMORE
	SETZM 0(P2)		;AND NOT USING ENTRY
FLSX:	AOBJN P2,FLSLOP		;DO THEM ALL
	UNLOCK FGLOCK		;YES. UNLOCK THE LOCK
	MRETNG			;AND DONE
;SAVE, GIVEN LIST OF BLOCK DESCRIPTORS OR ONE BLOCK DESCRIPTOR
;AC USAGE:
; P5 - PTN OF UPT,,0
; P1 - DESCRIPTOR LIST
;  Q3 - CURRENT DESCRIPTOR

.SAVE::	MCENT
	TRVAR <SHNJFN>
	MOVE P1,2		;SAVE DESCRIPTOR
	CALL GTSVI
	MOVEM T4,SHNJFN		;SAVE HANDLE AND JFN
	CALL GTSVDV		;SEE IF THE DEVICE IS LEGAL (DISK, MAGTAPE OR DECTAPE)
	 ITERR (SAVX1)		;ILLEGAL DEVICE TO SAVE ONTO; ABORT THE JSYS
	HRRI 2,1B20		;WRITE FILE
	OPENF
	 ITERR			;OPENF FAILED. ABORT THE JSYS
	TLNE P1,-1		;SOMETHING IN LH?
	JRST SAVE3		;YES, IT'S ONE DESCRIPTOR
SAVE2:	UMOVE Q3,0(P1)		;GET NEXT DESCRIPTOR
	JUMPE Q3,SAVE1		;CHECK FOR END OF LIST OF DESCRIPTORS
	CALL SAVEB		;WRITE BLOCK
	AOJA P1,SAVE2

SAVE3:	MOVE Q3,P1
	CALL SAVEB
SAVE1:	HLRZ 1,SHNJFN		;FORK HANDLE
	CALL FLOCK		;LOCK THE FORK STRUCTURE
	CALL SETLFK
	PUSH P,EVLNTH(A)	;GET ENTRY VECTOR LENGTH
	PUSH P,EVADDR(A)	;GET ADDRESS
	CALL CLRLFK		;CLEAR PSB MAPPING
	CALL FUNLK		;UNLOCK FORKS
	POP P,B			;RECOVER ENTRY VECTOR ADDRESS
	POP P,C			;RECOVER ENTRY VECTOR LENGTH
	HRL B,C			;PUT THEM TOGETHER (THEY MIGHT NOT FIT THOUGH!)
	HRRZ 1,SHNJFN		;JFN
	BOUT			;WRITE ENTRY VECTOR POINTER
	CLOSF
	 JFCL
	JRST MRETN
;SAVE, PROCESS ONE BLOCK

SAVEB:	HLRZ Q2,Q3		;MAKE LH COUNT NEGATIVE
	TRZE Q2,400000		;LARGER THAN 377777?
	PUSH P,[MSEC1,,SAVEBL]	;YES, DO IN TWO PARTS
	MOVNI Q2,0(Q2)
	HRLI Q3,0(Q2)
	SETO 4,			;SAY NO PAGE NOW MAPPED
	NOINT			;BE NOINT WHILE PAGE MAPPED
SAVEB1:	JUMPGE Q3,SAVUMP		;DONE, CLEAN UP
	MOVE Q2,Q3		;SETUP TO SCAN FOR NON-0'S
	LDB 1,[POINT 9,Q2,26]	;GET PAGE NUMBER
	CAIN 1,0(4)		;DIFFERENT FROM CURRENT MAPPING?
	JRST SAVE3A		;NO, SAME
	MOVEI 4,0(1)
	HLL 1,P5		;FORK HANDLE
	PUSH P,1
	SETZ 1,
	MOVEI 2,FPG0A
	CALL SETMPG		;REMOVE EXISTING CONTENTS OF WINDOW
	OKINT
	MOVE 1,0(P)
	CALL MRPACS		;SEE IF PAGE EXISTS
	POP P,2
	NOINT			;BE NOINT WHILE PAGE MAPPED
	TLNN 1,(PA%RD)		;CAN IT BE READ?
	JRST SAVE3B		;NO, FORGET IT
	MOVE 1,2
	MOVE 2,[PTRW+FPG0A]
	CALL SETMPG		;MAP PAGE
	SKIP FPG0A		;MAKE SURE IT IS READABLE
	 ERJMP SAVE3B		;WE LOST, NEXT PAGE
SAVE3A:	HRRZ Q1,Q2		;SETUP RELATIVE ADDRESS IN WINDOW
	ANDI Q1,PGSIZ-1
	ADDI Q1,FPG0A
	JRST SAVE8

SAVE3B:	ANDI Q2,777000
	ADDI Q2,1000		;TO NEXT PAGE
	SUBI Q2,0(Q3)
	HRLI Q2,0(Q2)		;COUNT OF WORDS SKIPPED IN BOTH HALVES
	ADD Q3,Q2			;UPDATE COUNT AND ADDRESS
	JRST SAVEB1

SAVEBL:	HRLI Q3,400000		;DID N-400000 BEFORE, DO LAST 400000 NOW
	NOINT
	JRST SAVEB1
SAVE12:	TRNN Q2,777		;NEW PAGE?
	JRST SAVE5		;YES
SAVE8:	SKIPN 0(Q1)		;NON-0 WORD?
	JRST SAVE6		;NO, GO CHECK FOR MORE
SAVE7:	ADDI Q1,1
	AOBJN Q2,SAVE12
	MOVE Q2,Q3		;END OF BLOCK
	CALL SAVED
	JRST SAVEB1

;WRITE ALL OR PART OF ONE PAGE.

SAVED:	HLRE 2,Q2		;COUNT OF WORDS JUST SCANNED
	ADD Q1,2			;RESET WINDOW POINTER
	HRRZ 1,SHNJFN		;JFN
	MOVE 2,6
	SUBI 2,1		;OUTPUT IOWD CNT,ADR
	BOUT
	 ERJMP SAVERR		;FAILED
	HLRE 3,Q2		;GET (NEG) COUNT
	PUSH P,3		;SAVE IT
	HRRZ 2,Q1		;GET STARTING ADR
	HRLI 2,(<POINT 36,0>)	;CONSTRUCT BYTE PTR TO IT
	SOUT			;OUTPUT THE BLOCK
	 ERJMP SAVERR		;FAILED
	POP P,2			;RECOVER STARTING COUNT
	SUB 3,2			;COMPUTE NO. WDS MOVED
	ADDM 3,Q1		;UPDATE WINDOW PTR
	HRL 3,3			;PUT COUNT IN BOTH HALVES
	ADDM 3,Q3		;UPDATE AOBJN PTR
	RET


SAVERR:	CALL SAVUMP		;UNMAP ANY PAGE AND GO OKINT
	MOVE T1,LSTERR
	ITERR ()		;DO ERROR

SAVUMP:	SETZ T1,		;REMOVE MAPPING
	MOVEI T2,FPG0A		;THAT WE HAD DONE
	CALL SETMPG		;DO IT
	OKINT			;CAN INTERRUPT NOW
	RET			;DONE
;FOUND 0 WORD, CHECK FOR MORE

SAVE6:	MOVEI 2,0(Q2)
	CAIN 2,0(Q3)		;FIRST WORD OF BLOCK OR PAGE?
	JRST SAVE9		;YES
	HLRZ 2,6
	SETCMI 3,0(Q2)
	CAIE 2,-1		;END OF BLOCK, OR
	TRNN 3,777		;END OF PAGE?
	JRST SAVE5		;YES, FINISH BLOCK
	SKIPE 1(Q1)		;SUCCESSIVE 0?
	JRST SAVE7		;NO, CONTINUE SCAN
SAVE5:	MOVNI Q2,0(Q2)		;COMPUTE NUMBER OF WORD TO DUMP
	ADDI Q2,0(Q3)		;(-CURRENT)+OLD = -DIFFERENCE
	MOVSI Q2,0(Q2)
	HRRI Q2,0(Q3)		;FIRST ADDRESS
	CALL SAVED		;WRITE BLOCK
	JRST SAVEB1

;SCAN OVER BLOCK OF 0'S

SAVE10:	TRNE Q2,777		;END OF PAGE?
	SKIPE 0(Q1)		;OR NON-0 WORD?
	JRST SAVE11		;YES
SAVE9:	ADDI Q1,1
	AOBJN Q2,SAVE10
SAVE11:	MOVE Q3,Q2		;UPDATE POINTER
	JRST SAVEB1		;RECHECK PAGE, ETC.
;GTSVDV - ROUTINE TO CHECK FOR LEGAL DEVICE FOR SAVE AND SSAVE

;ACCEPTS IN T3: DEVICE CHARACTERISTICS AS RETURNED IN T2 BY DVCHR

;RETURNS +1: ILLEGAL DEVICE
;	 +2: LEGAL DEVICE

;ALLOWS DISK, NUL, MAGTAPE AND DECTAPE
;CLOBBERS T4

GTSVDV:
	LDB T4,[POINTR (T3,DV%TYP)] ;GET THE DEVICE TYPE
	CAIE T4,.DVDSK		;IS IT A DISK?
	CAIN T4,.DVNUL		;OR NUL DEVICE?
	RETSKP			;YES. GOOD
	CAIE T4,.DVMTA		;NO. IS IT A MAGTAPE?
	CAIN T4,.DVDTA		;NO. IS IT A DECTAPE?
	RETSKP			;GOOD CHOICE.  TAKE THE SKIP RETURN
	RET			;NOT DISK, MAGTAPE OR DECTAPE. NONSKIP RETURN.
;SSAVE - CREATES EXE FILES

;ACCEPTS FROM USER:
;  1/ (FORK HANDLE,,JFN)
;  2/ EITHER (0,,ADDRESS OF TABLE) IF TABLE IS MORE THAN 1 WORD OR
;     TABLE WORD
;  3/ FLAGS - NOT IMPLEMENTED
;  A TABLE OF WORDS OF THE FORM
;       (- NUMBER OF PAGES IN GROUP,,ACCESS+1ST CORE PAGE)
;    EACH OF WHICH REPRESENTS A GROUP OF CONTIGUOUS PAGES

; CALL:	SSAVE

;RETURN + 1:ALWAYS
;           ITERR HANDLES ERRORS

;SAVES USER'S CORE IMAGE (LH T1) INTO FILE (RH T1)
;HEADER GOES INTO PAGES 0, 1, AND 2 (IF NEEDED)

;AC USAGE:
;  ALL REGISTERS EXCEPT Q3 ARE USED. SEE INDIVIDUAL ROUTINES FOR
;   COMMENTS

; STRINGS IN DIRECTORY WORDS.  IF COMMENT, DEFINED IN GET

	DEFSTR(EXFLG,,8,9)	;ACCESS BITS 1ST WORD
;	DEFSTR(EXPAG,,35,27)	;FILE PAGE NUMBER 1 ST WORD
;	DEFSTR(EXCNT,,8,9)	;REPEAT COUNT 2ND WORD
;	DEFSTR(EXADR,,35,27)	;CORE PAGE NUMBER 2ND WORD

; ACCESS BITS INPUT AND SHIFTED TO LH
	SSCPY==1B0		;COPY ON WRITE
	SSACC==1B1		;TAKE ACCESS FROM PAGE
	SSWRT==1B3		;ALLOW WRITE

; ACCESS FOR EXE FILE

	EXEWRB==1B2		;WRITABLE
	EXESHR==1B1		;SHARABLE

; BLOCK TYPES  (DEFINED IN GET)

;	EXEEND=1777		;END
;	EXEFIL=1776		;DIRECTORY
;	EXEVEC=1775		;ENTRY VECTOR
;	EXEPDV=1774		;PROGRAM DATA VECTORS

;NOTE: THE EXE FILE FORMAT ALLOWS UP TO THREE PAGES OF DIRECTORY.
;HOWEVER, SSAVE IS CURRENTLY LIMITED TO ONE PAGE.

	SSDATA==1		;FIRST DATA PAGE IN OUTPUT FILE
.SSAVE::MCENT
	CALL SSAVA		;CALL ROUTINE TO DO THE SSAVE
	 ITERR			;ERROR RETURN FROM SSAVA
	JRST MRETN		;RETURN FROM SSAVE
;SSAVA - DOES THE WORK FOR SSAVE

;ACCEPTS:
;  SAME AS FOR .SSAVE ABOVE

;CALL:	CALL SSAVA

;RETURN + 1:ERROR CODE IN T1
;       + 2:SUCCESS

;FUNCTION:
;  1) READS THE USER'S INPUT
;  2) SETS UP TO PROCESS EACH GROUP OF PAGES AND CALLS SSAVB
;  3) WRITES THE FILE HEADER INFO

;CALLED BY SSAVE

;AC USAGE:
;  P1/ LOCATION OF CURRENT ENTRY IN DIRECTORY
;  P3/ NEXT FILE PAGE
;  P4/ CURRENT TABLE WORD
;  T1-T4/ TEMPORARY STORAGE

;TEMPORARY STORAGE:
; SAVVEC - Saved entry vector (2 words)
; HANJFN - FORK HANDLE,,JFN (AC1 ARG AFTER CHECKING, ETC)
; PDVADR - ADDRESS OF SAVED PDV BLOCK FORMAT IS 
;          -PDVN,,PDVBLK+1
;	   PDVN WORDS OF PDVAS
; SAVENT - Entry vector
;  SSDIR - ADDRESS OF START OF SCRATCH PAGE IN WHICH DIRECTORY IS BUILT
;  SSAC2 - AC2 AS SPECIFIED BY THE USER'S CALLING ROUTINE
;  SSDEV - INDICATES TYPE OF OUTPUT DEVICE AND STATE OF PROCESSING
;  TADR  - PROCESS PAGE NUMBER
;  WSEC  - LAST SECTION SEEN
SSAVA:	TRVAR <<SAVVEC,2>,HANJFN,PDVSPC,PDVADR,SAVENT,SSDIR,SSAC2,SSCORE,SSDEV,TADR,WSEC>

	SETOM SSDEV  		;INITIALIZE TO INDICATE DISK
	SETOM WSEC		;SAY NO SECTION SEEN YET
	CALL GTSVI		;INPUT: USER'S T1/ (FORK HANDLE,,JFN)
				; OUTPUT: T1/JFN, T2/^D36B5,
				; T3/ DEVICE CHARACTERISTICS,
				; P5/PTN FOR PAGE 0 OF FORK,T4/FORK HANDLE,,JFN
	MOVEM T4,HANJFN		;REMEMBER HANDLE AND JFN
	CALL GTSVDV		;SEE IF THIS IS A LEGAL DEVICE
	 ITERR (SSAVX1)		;NO. RETURN AN ERROR AND ABORT THE JSYS
	TLNE T3,777		;LEGAL. DISK?
	AOS SSDEV		;NO. INDICATE FIRST PASS ON SEQUENTIAL
	HRRI T2,OF%WR		;WRITE ACCESS - LEFT HALF SET UP FOR
				; 36-BIT BYTES IN GTSVI
	OPENF			;OPEN OUTPUT FILE FOR WRITE
	 ITERR			;ERROR ON OPEN - CODE IN T1
	SKIPGE SSDEV		;WORKING ON DISK?
	JRST [	HRRZ T1,HANJFN	;YES. T1/JFN
		MOVEI T2,SSDATA*1000 ;T2/POINTER TO START OF 1ST DATA PAGE
		SFPTR		;SET FILE POINTER
		 ITERR		;FAILED. ERROR IN T1
		JRST .+1]
	NOINT
	CALL ASGPGS		;GET SCRATCH PAGE TO BUILD DIRECTORY IN
	 JRST [	OKINT
		ITERR(SSAVX3)]	;FAILED
	MOVEM T1,SSDIR		;SAVE ADDRESS OF FREE PAGE
	CALL ASGPGS		;GET A PAGE TO MAP USER'S PAGE INTO
	JRST [	MOVEI P4,SSAVX3	;FAILED. SET ERROR CODE
		JRST SSAV6]
	MOVEM T1,SSCORE		;SAVE ADDRESS OF THE PAGE
	OKINT			;NOW THA HANDLES ARE STORED, ALLOW ^C
	CALL GETOFF		;MAP IN THE FORK
	DMOVE T2,EVLNTH(T1)	;GET THIS FORK'S ENTRY VECTOR
	DMOVEM T2,SAVVEC	;SAVE ENTRY VECTOR WORD
	SETZM PDVADR		;ASSUME NO PDVS
	SETZM PDVSPC
; NOTE: THE FOLLOWING CODE BUILDS A BLOCK OF PDVAS IN FREE SPACE THE FORMAT OF
; THIS BLOCK IS DEPENDANT ON THE FREE SPACE MANAGERS DESIRE TO USE THE FIRST
; WORD OF THE RETURNED FREE SPACE. THIS CODE WOULD BE SOMEWHAT MORE CLEAR
; IF THAT WAS NOT TRUE SINCE IT IN NO WAY DEPENDS ON THE FREE SPACE HEADER
; AND CURRENTLY INDEXES OFFSET BY 1 TO AVOID TOUCHING THE HEADER

	SKIPN T2,PDVS(T1)	;ANY PDVS?
	IFSKP.
	  HRRZ T2,0(T2)		;GET SIZE OF PDV BLOCK
	  AOS T2		;+1 FOR FREE HEADER
	  MOVE P4,T1		;REMEMBER MAPPED FORK OFFSET
	  CALL ASGJFS		;GET FREE SPACE FOR BLOCK *GETOFF IS NOINT
	  JRST [CALL GEM	;UNLOCK
		MOVEI T1,SSAVX3	;ERROR CODE
		JRST SSAV5]
	  MOVEM T1,PDVADR	;SAVE BLOCK ADDRESS
	  MOVEI T3,1(T1)	;DEST IS PAST FREE HEADER
	  MOVE T2,PDVS(P4)	;SOURCE
	  HRRZ T1,0(T2)		;COUNT SHOULD NOT CHANGE WHILE GETOFF (FLOCK)
	  XBLT. T1		;SAVE PDV BLOCK LOCALLY
	  MOVE T1,PDVADR	;GET ADDRESS AGAIN
	  HRRZ T2,1(T1)		;GET COUNT OF BLOCK
	  ADDI T1,2		;GET PDV STG +2 (FIRST PDVA)
	  MOVNI T2,-1(T2)	;MAKE AOBJN WORD OF -<COUNT-1>
	  HRL T1,T2		; ,,PDVSTG+2 (FIRST PDVA)
	  MOVEM T1,-1(T1)	;SAVE AOBJN WORD IN WORD BEFORE FIRST PDVA
	ENDIF.
	CALL GEM		;UNLOCK THINGS
	MOVEI P3,SSDATA		;P3/START OF DATA AREA IN FILE
	;..
;HERE BEGINS A LOOP THAT IS PROCESSED ONCE FOR DISK AND TWICE FOR SEQUENTIAL
;DEVICES.  IN THE CASE OF DISK, SSAVB IS CALLED ONCE FOR EACH ENTRY IN THE
;CALLER'S TABLE.  SSAVB COPIES THE CORE PAGES ONTO DISK (IF NON-ZERO) AND
;BUILDS THE DIRECTORY.  WHEN ALL THE GROUPS HAVE BEEN DONE, THIS ROUTINE WRITES
;THE HEADER PAGES.  FOR A SEQUENTIAL DEVICE, THE FIRST PASS SIMPLY CREATES THE
;DIRECTORY AND WRITES THE HEADER PAGES, SINCE THEY PRECEDE ALL DATA.  SSAVB IS
;CALLED AGAIN TO WRITE THE DATA PAGES.

	;..
SSAV7:	MOVE P1,SSDIR		;P1/START OF DIRECTORY AREA.
	UMOVE T1,2		;T1 / INPUT AC2 - TABLE WORD OR ADDRESS OF TABLE
	MOVEM T1,SSAC2		;SAVE INPUT AC2 IN TEMPORARY STORAGE
	JUMPL T1,SSAV3		;NEGATIVE IF SINGLE DESCRIPTOR

;TABLE HAS MORE THAN ONE WORD.  T1 POINTS TO CURRENT WORD

SSAV2:	CALL GETENT		;GET NEXT TABLE ENTRY
	MOVEM T1,SSAC2		;REMEMBER ADDRESS OF NEXT ENTRY
	JUMPE P4,SSAV1		;0 INDICATES END OF TABLE
	CALL SSAV0		;DO THIS SET OF PAGES
	MOVE T1,SSAC2		;GET ADDRESS OF NEXT ENTRY
	JRST SSAV2		;GO GET THE NEXT TABLE WORD

;TABLE HAS ONE WORD. T1 CONTAINS IT.

SSAV3:	MOVEI T1,T2		;SAY TABLE WORD IS IN T2
	CALL GETENT		;GET ENTRY
	CALL SSAV0		;DO THIS SET OF PAGES
;ALL GROUPS HAVE BEEN PROCESSED.  SSDEV INDICATES STATE:
; -1: WORKING ON DISK.  DIRECTORY HAS BEEN BUILT IN SCRATCH PAGE,
;     AND DATA PAGES HAVE BEEN WRITTEN.  WRITE DIRECTORY AND QUIT.
;  0: FIRST PASS ON A SEQUENTIAL DEVICE.  DIRECTORY HAS BEEN BUILT
;     IN SCRATCH PAGE.  WRITE IT AND GO START OVER TO WRITE THE
;     DATA PAGES
;  1: SECOND PASS ON A SEQUENTIAL DEVICE.  DIRECTORY WAS WRITTEN ON
;     FIRST PASS, DATA PAGES ON SECOND.  QUIT.

;NOTE:WHEN SSAVE SUPPORTS MULTIPLE-PAGE DIRECTORIES, THIS CODE
;SHOULD, WHEN SSDEV=0, DETERMINE THE NUMBER OF PAGES IN THE
;DIRECTORY.  IF IT IS LESS THAN 3, IT SHOULD REDUCE ALL THE FILE
;PAGE NUMBERS IN THE DIRECTORY APPROPRIATELY AND SET P3 TO POINT
;TO THE FIRST PAGE AFTER THE DIRECTORY (I.E,. 1 OR 2)

			;..
SSAV1:	SKIPLE SSDEV	;SECOND PASS FOR SEQUENTIAL?
	JRST SSAV8	;YES. ALL DONE. FILE POINTER IS AT END
	SKIPE SSDEV	;NO. PROCESSING DISK?
	JRST [	HRRZ T1,HANJFN ;YES. T1/JFN
		RFPTR	;GET PRESENT FILE POINTER
		 JRST SSAV5 ;FAILED
		MOVEM T2,P4 ;SAVE FILE POINTER
		SETZ T2, ;POINT TO BEGINNING OF FILE
		SFPTR	;SET FILE POINTER TO START DIRECTORY
		 JRST SSAV5 ;FAILED
		JRST .+1]

			;WRITE THE DIRECTORY BLOCK

	SUB P1,SSDIR		;SUBTRACT START OF SCRATCH PAGE FROM CURRENT LOC
	MOVE T2,P1		;NUMBER OF SUBGROUPS DONE *2
	JUMPE T2,SSAV4		;DON'T WRITE DIRECTORY IF NO SUBGROUPS
	ADDI T2,1		;NUMBER OF WORDS IN DIRECTORY (INCLUDES TITLE)
	HRLI T2,EXEFIL		;T2/ (TYPE NO.,,NUMBER WORDS)
	HRRZ T1,HANJFN		;T1/ JFN FOR FILE
	BOUT			;WRITE FIRST WORD OF DIRECTORY
	HRRZ T3,P1		;T3/ (-NUMBER SUBGROUPS IN DIRECTORY)
	MOVNS T3
	MOVX T2,<POINT 36,0>	;T2/POINTER FOR 36-BIT BYTES
	HRR T2,SSDIR		; IN SCRATCH PAGE
	SOUT			;WRITE DIRECTORY
	;..
;ENTRY VECTOR BLOCK

	;..
	HRRI T2,3		;WORD COUNT FOR ENTRY VECTOR BLOCK
	HRLI T2,EXEVEC		;T2/(TYPE NO,,NUMBER WORDS)
	BOUT			;WRITE FIRST WORD OF ENTRY VECTOR BLOCK
	MOVE T2,SAVVEC		;T2/ ENTRY VECTOR LENGTH
	HRRZ T1,HANJFN		;JFN FOR OUTPUT FILE
	BOUT			;WRITE ENTRY VECTOR COUNT
	MOVE T2,1+SAVVEC	;ADDRESS OF ENTRY VECTOR FOR OUTPUT
	BOUT			;WRITE ENTRY VECTOR ADDRESS

;PDV BLOCK

	SKIPN T2,PDVSPC		;PDVS TO DO?
	JRST NOPDVS
	AOS T2			;COUNT HEADER TOO
	HRLI T2,EXEPDV		;BLOCK TYPE IS PDV
	BOUT			;WRITE BLOCK HEADER
	MOVE T3,PDVADR		;GET AOBJN WORD TO PDVAS
	MOVE T3,1(T3)
	SETZ T4,		;COUNT FOR SANITY
	DO.
	  SKIPGE T2,0(T3)	;GET PDVA
	  AOS T4		;COUNT IT WRITTEN PDVAS
	  TXZE T2,1B0		;WAS IT MARKED GOOD (UNMARK IT)
	  BOUT			;WRITE GOOD PDVS
	  AOBJN T3,TOP.		;CHECK ALL OF THEM
	ENDDO.
	CAME T4,PDVSPC		;SAME NUMBER
	ITERR (SSAVX5)		;NO!!!

;END BLOCK

NOPDVS:	HRRZ T1,HANJFN		;GET JFN
	HRLI T2,EXEEND
	HRRI T2,1		;T2/ (TYPE NUMBER,,NUMBER WORDS)
	BOUT			;WRITE END BLOCK
;HEADER WRITTEN (DIRECTORY, ENTRY VECTOR, ETC.).  EITHER WORKING
;ON DISK (SSDEV=-1) OR FIRST PASS FOR SEQUENTIAL DEVICE (SSDEV=0)

SSAV4:	SKIPGE SSDEV		;WORKING ON DISK?
	JRST [	HRRZ T1,HANJFN	;YES. T1/JFN
		MOVE T2,P4	;T2/ORIGINAL FILE POINTER (AFTER DATA)
		SFPTR		;SET FILE POINTER AFTER ALL EXISTING PAGES
		 JRST SSAV5	;ERROR FROM SFPTR - CODE IN T1
		JRST SSAV8]	;DONE. GO CLOSE THE FILE

;FIRST PASS FOR SEQUENTIAL DEVICE.  WRITE THE REST OF THE PAGE AND
;GO REPEAT THE BUILDING PROCESS TO WRITE THE DATA

	HRRZ T1,HANJFN		;T1/JFN FOR FILE
	RFPTR%			;How far have we gone?
	 MOVEI T2,5		;If it fails, assume 5 words
	MOVEI T4,-1000		;Get count for a full page
	ADDI T4,(T2)		;Halfword add words already written
	SETZM T2		;T2/DATA TO BE WRITTEN
	HRLZ T4,T4		;TO LEFT HALF FOR COUNTING
	JUMPGE T4,SSAV10	;DON'T WRITE IF PAGE IS FULL
SSAV9:	BOUT			;WRITE A WORD
	AOBJN T4,SSAV9		;CONTINUE UNTIL PAGE COMPLETED

SSAV10:	MOVEI P3,SSDATA		;YES. P3/ADDRESS OF 1ST DATA PAGE
	AOS SSDEV		;INDICATE SECOND PASS
	JRST SSAV7		;GO WRITE THE DATA PAGES
;GETENT gets the entry for a set of pages
;
;Accepts:	T1/	address in user space of table word
;
;Returns+1:	P4/	table word except for page number
;		TADR/	page number
;		T1/	address of next entry (varies wrt SS%EPN
GETENT:	UMOVE P4,(T1)		;GET FIRST PART OF TABLE WORD
	LOAD T2,SS%FPN,P4	;FIRST ASSUME NON-EXTENDED ENTRY
	TXNE P4,SS%EPN		;IS THIS AN EXTENDED ENTRY?
	UMOVE T2,1(T1)		;YES, GET PAGE NUMBER FROM SECOND WORD OF PAIR
	MOVEM T2,TADR		;REMEMBER PAGE NUMBER
	AOJ T1,			;FIRST ASSUME ONE-WORD ENTRY
	TXNE P4,SS%EPN
	AOJ T1,			;FIX POINTER FOR DOUBLE-WORD ENTRY
	RET

;SSAV0 does a group of pages

SSAV0:	MOVE T2,SSDIR		;PASS ADDRESS OF DIRECTORY SCRATCH  PAGE
	MOVE T3,SSCORE		;PASS ADDRESS OF CORE SCRATCH PAGE
	MOVE T4,SSDEV		;PASS DEVICE FLAG
	CALL SSAVB		;GO PROCESS THIS GROUP OF PAGES
	 JRST SSAV5		;ERROR FROM SSAVB - CODE IN T1
	RET

;GETOFF maps in the PSB of the fork being saved.
;
;Returns+1:	FLOCK locked
;		page mapped
;		T1/	offset

GETOFF:	CALL FLOCK		;PREVENT FORK STRUCTURE FROM CHANGING
	HLRZ T1,HANJFN		;GET FORK HANDLE
	CALLRET SETLFK		;MAP IN THE FORK TO SOMEWHERE

;This little GEM is used to unwind mappings and locks.

GEM:	CALL CLRLFK		;CLEAR MAPPING
	CALLRET FUNLK		;UNLOCK FORK STRUCTURE
;ALL OUTPUT DONE.  CLOSE FILE AND RELEASE SCRATCH PAGES

SSAV8:	HRRZ T1,HANJFN		;JFN
	CLOSF			;CLOSE FILE
	 JFCL
	MOVE T1,SSDIR		;T1/ADDRESS OF DIRECTORY SCRATCH PAGE
	NOINT			;NO INTERRUPTS WHILE RELEASING PAGES
	CALL RELPGS		;RELEASE THE SCRATCH PAGE
	MOVEI T1,JSBFRE		;ADDR OF JSB FREE HEADER
	SKIPE T2,PDVADR		;ADDRESS OF PDV STORAGE IF ANY
	CALL RELFRS		;RELEASE IT
	MOVE T1,SSCORE		;T1/ADDRESS OF CORE SCRATCH PAGE
	CALL RELPGS		;RELEASE THE PAGE
	OKINT			;ALLOW INTERRUPTS
	RETSKP			;SUCCESSFUL RETURN

;ERROR OCCURRED AFTER EITHER OR BOTH PAGES WERE OBTAINED.  RELEASE THEM.
;THE FAILING ROUTINE SET THE ERROR CODE IN T1.

SSAV5:	MOVEM T1,P4		;SAVE THE ERROR CODE
	NOINT			;NO INTERRUPTS WHILE RELEASING PAGES
	MOVEI T1,JSBFRE		;ADDR OF JSB FREE HEADER
	SKIPE T2,PDVADR		;ADDRESS OF PDV STORAGE
	CALL RELFRS		;RELEASE IT
	MOVE T1,SSCORE		;T1/ADDRESS OF CORE SCRATCH PAGE
	CALL RELPGS		;RELEASE THE PAGE

SSAV6:	MOVE T1,SSDIR		;T1/ADDRESS OF DIRECTORY SCRATCH PAGE
	CALL RELPGS		;RELEASE THE PAGE
	MOVE T1,P4		;RESTORE THE ERROR CODE
	OKINT
	ITERR			;ERROR RETURN
;SSAVB - PROCESSES ONE WORD FROM USER TABLE

;ACCEPTS:
; T2/STARTING ADDRESS OF SCRATCH PAGE TO CONTAIN DIRECTORY
; T3/STARTING ADDRESS OF SCRATCH PAGE TO CONTAIN USER'S CORE PAGE
; T4/-1 OR 1 IF DATA PAGES SHOULD BE WRITTEN. 0 IF BUILD DIRECTORY ONLY
; P1/ADDRESS OF NEXT ENTRY IN THE DIRECTORY
; P3/ NEXT FILE PAGE
; P4/ TABLE WORD AS ENTERED (ACCESS IN 18-26) (SAVED IN SSWORD)
; HANJFN/ FORK HANDLE,,JFN

;CALL:	CALL SSAVB

;RETURN + 1:ERROR - CODE IN T1
;       + 2:SUCCESS

; SSAVB PROCESSES ONE GROUP OF PAGES AS REQUESTED BY ONE WORD IN
;  THE INPUT TABLE. CONTIGUOUS PAGES FORM ONE SUBGROUP (AND
; THUS ONE ENTRY IN THE DIRECTORY) UNTIL A PAGE DIFFERS FROM ITS
; PREDECESSOR IN ONE OF THE FOLLOWING WAYS:
;  A. PAGE N EITHER
;     1. IS ALL ZERO
;     2. DOES NOT EXIST, OR
;     3. IS NOT READABLE
;     AND N-1 IS NONE OF THE ABOVE
;  B. PAGE N-1 IS ANY OF THE ABOVE AND PAGE N IS NONE
;  C. PAGES N-1 AND N HAVE DIFFERENT ACCESS
; AT THAT POINT THE DIRECTORY ENTRY FOR N-1 IS COMPLETED AND A
; NEW SUBGROUP BEGINS WITH N.

;EACH DIRECTORY ENTRY IS TWO WORDS AS FOLLOWS:
;  - - - - - - - - - - - - - - - - - - - -
;  !ACCESS	  	!FILE PAGE NUMBER!
;  !REPEAT COUNT	!CORE PAGE NUMBER!
;  - - - - - - - - - - - - - - - - - - - -

;WHERE THE PAGE NUMBERS EACH OCCUPY 27 BITS AND REPRESENT THE STARTING PAGES
;IN A GROUP.  THE REPEAT COUNT IS 1 LESS THAN THE NUMBER OF PAGES
;IN A GROUP

;AC USAGE:
; P1/ADDRESS IN SCRATCH PAGE OF FIRST WORD OF CURRENT DIRECTORY ENTRY
; P2/ NUMBER OF PAGES PROCESSED THIS SUBGROUP
; P4/ ADDRESS OF FIRST WORD OF SCRATCH PAGE FOR DIRECTORY
; P5/ ACCESS TO GO WITH PAGE IN FILE
; Q1/ -NUMBER OF PAGES IN GROUP AS INPUT BY USER
; Q2/ NUMBER OF PAGES IN THIS SECTION PROCESSED THIS SUBGROUP
; T1-T3/ TEMPORARY STORAGE

;TEMPORARY STORAGE:
; SSWORD - TABLE WORD CURRENTLY BEING DONE (SPECIFIED BY USER IN CALL)
; SSFLAG - FIRST TIME FLAG (0 ON FIRST ENTRY TO ROUTINE)
; SSCORE - SCRATCH PAGE FOR MAPPING USER'S CORE PAGE
; SSDEV -  CONTENTS OF T4 AS PASSED ON ENTRY
SSAVB:	STKVAR <SSFLAG,SSWORD,SSCORE,SSDEV,SSPACC>
	MOVEM T3,SSCORE		;SAVE ADDRESS OF CORE SCRATCH PAGE
	MOVEM T4,SSDEV		;SAVE DEVICE FLAG
	TXNN P4,1B0		;MAKE SURE COUNT IS NEGATIVE
	ITERR(SSAVX2)		;LOSE - RETURN CODE IN T1
	MOVEM P4,SSWORD		;SAVE TABLE WORD
	MOVEM T2,P4		;P4/ADDRESS OF DIRECTORY SCRATCH PAGE
	SETZB P2,Q2		;CLEAR COUNT OF PAGES IN SUBGROUP AND SECTION
	SETZM SSFLAG		;CLEAR FIRST TIME FLAG
	HLRO Q1,SSWORD		;-COUNT OF PAGES
	SKIPN T3,PDVADR		;PDVS TO DO?
	JRST SSB10		;NO
	MOVE T3,1(T3)		;GET AOBJN WORD TO PDVAS
	HRR T1,TADR		;FIRST PAGE IN RANGE
	MOVN T2,Q1		;GET COUNT OF PAGES
	ADD T2,T1		;ADD FIRST PAGE SO T2 = LAST PG + 1
	LSH T1,PGSFT		;AS ADDRESS
	LSH T2,PGSFT
	SOS T2			;T2 IS LIMIT FOR PDV
	MOVX T4,1B0		;MARKER VALUE FOR GOOD PDVA
PDVLOP:	CAMG T1,0(T3)		;IS PDV BIGGER THAN LOWER
	CAMGE T2,0(T3)		;YES, IS IT SMALLER THAN UPPER
	IFSKP.
	  TDNN T4,0(T3)		;YES AND YES. ALREADY COUNT THIS PDVA?
	  AOS PDVSPC		;NO, SO COUNT IT NOW
	  IORM T4,0(T3)		;MARK IT AS GOOD
	ENDIF.
	AOBJN T3,PDVLOP		;AND LOOP
	HLRO Q1,SSWORD		;RESTORE Q1
	;..
;SSB10 BEGINS LOOP THROUGH ALL PAGES IN A GROUP AS DESCRIBED BY ONE
;WORD IN INPUT TABLE. ENDS BEYOND SSAVB5.

	;..
SSB10:	HLLZ T1,HANJFN		;FORK HANDLE TO LH
	HRR T1,TADR		;CORE PAGE TO RH OF T1
	MOVX T2,SS%EPN		;CHECK TO SEE IF EXTENDED PAGE NUMBERS WANTED
	TDNE T2,SSWORD		;WERE THEY?
	TLO T1,FH%EPN		; YES, SPECIFY THAT IN FORK HANDLE
	RMAP			;GET STATE OF THIS MEM PAGE INTO AC 2
	ERJMP [	MOVE T1,LSTERR	;FAILED, SEE WHY
		CAIE T1,ARGX06	;NO SUCH SECTION?
		ITERR		;UNKNOWN ERROR, SO BOMB OUT
		ADDI P2,777	;KEEP COUNT OF PAGES FOR EXE DIRECTORY UP-TO-DATE
		ADDI Q1,777	;SKIP THIS ENTIRE NONEXISTENT SECTION OF 1000 PAGES
		SOS Q2		;DECREMENT SECTION COUNT
		MOVEI T1,777
		ADDM T1,TADR	;UPDATE NEXT PAGE NUMBER TO BE IN NEXT SECTION
		JRST SSB1]	;GO SEE IF DONE
	MOVEM T2,SSPACC		;SAVE ACCESS BITS FOR PAGE
	MOVX P5,<EXEWRB>_-^D27	;SET WRITABLE IN CASE ALL-ZERO OR DOESN'T EXIST
				; (NINE-BIT FIELD RIGHT-JUSTIFIED)
	TXNN T2,PA%PEX		;DOES IT EXIST?
	JRST SSAVB6		;NO. TREAT AS IF ALL-ZERO
	TXNN T2,PA%RD		;YES. READABLE?
	JRST SSAVB6		;NO, TREAT AS IF ALL-ZERO
				;MAP THE USER'S PAGE INTO CORE SCRATCH PAGE
	HLL T1,HANJFN		;USER'S FORK HANDLE
	HRR T1,TADR		;CORE PAGE
	MOVX T2,SS%EPN		;CHECK TO SEE IF EXTENDED PAGE NUMBERS WANTED
	TDNE T2,SSWORD		;WERE THEY?
	TLO T1,FH%EPN		; YES, SPECIFY THAT IN FORK HANDLE
	MOVEI T2,0
	CALL FKHPTN		;INPUT: T1/ (FORK HANDLE,,PAGE)
	  ITERR ()		;ILL FORMED RFH
				; OUTPUT: T1/ (PTN,,PAGE)
	MOVE T2,SSCORE		;T2/ADDRESS OF CORE SCRATCH PAGE
	TXO T2,PM%RD		;READ ACCESS
	CALL SETMPG		;INPUT: T1/ (PTN,,PAGE)
				; T2/(ACCESS ,,MONITOR ADDRESS)
				; SETS UP INDIRECT POINTER TO USER PAGE
	SKIP SSCORE		;IS PAGE REALLY THERE?
	 ERJMP SSAVB6		;NO

; CHECK PAGE FOR ALL ZEROES. GO TO SSAVB6 IF ZERO, SSAVB2 OTHERWISE

	MOVSI T1,-1000		;SET UP COUNTERS
	HRR T1,SSCORE		;POINT TO START OF SCRATCH PAGE
SSAVB9:	MOVE T2,(T1)		;GET DATA
	 ERJMP SSAVB6		;IF PAGE GOES AWAY, ASSUME IT WAS NEVER THERE
	JUMPN T2,SSAVB2		;PAGE IS NON-0 IF SOMETHING WITHIN IT IS.
	AOBJN T1,SSAVB9		;KEEP LOOKING
	TXO P5,<EXEZRO>_-^D27	;SET ALL-ZERO BIT

;CURRENT PAGE ALL ZERO, NOT READABLE, OR DOESN'T EXIST (NOT MAPPED)
;LOOK AT DIRECTORY FOR CURRENT GROUP.  IF SAME AS THIS PAGE, ADD TO
;CURRENT GROUP.  OTHERWISE, START NEW GROUP

	;..
SSAVB6:	SKIPN SSFLAG		;FIRST TIME THROUGH?
	JRST [	SETOM SSFLAG	;YES. SET THE FLAG
		JRST SSAVB1]	; AND GO SET UP THE DIRECTORY
	CALL SCOMPR		;SEE IF NEW SUBGROUP NEEDED
	 JRST SSAVB1		;START NEW SUBGROUP
	JUMPE T1,SSAVB5		;T1 ZERO IF WORKING ON GROUP OF ALL ZEROES
	CALL SSAVC

; SET UP DIRECTORY FOR ALL-ZERO BLOCK

SSAVB1:	SETZM (P1)		;SET FILE PAGE NUMBER IN FIRST WORD TO 0
	STOR P5,EXFLG,(P1)	;MOVE ACCESS TO FIRST WORD
	MOVE T1,TADR
	STOR T1,EXPAG,1(P1) 	;MOVE CORE PAGE NO TO 2ND WORD
	JRST SSAVB5
;CURRENT PAGE NOT ALL ZERO

;NOTE: THIS BLOCK ASSUMES CERTAIN BIT PATTERNS FOR INPUT TO SSAVE
; AND OUTPUT FROM RPACS.  CHANGING EITHER WILL AFFECT THIS CODE.

SSAVB2:	MOVE T2,SSPACC		;T2/ACCESS FOR USER'S PAGE
	HRL T3,SSWORD		;MOVE ACCESS TO LH OF T3
	TXNE T3,SSACC		;ACCESS PER CORE PAGE REQUESTED?
	JRST [	TXZ T2,SSCPY	;YES. 'AND' ACCESS REQUESTED (LH T3)
				; WITH PAGE ACCESS (LH T2)
		TXNE T2,PA%CPY	;MOVE COPY ON WRITE BIT TO BIT 0 IN T2
		TXO T2,SSCPY	; TO CORRESPOND TO T3
		AND T2,T3	;AND INTO T2
		JRST SSB12]	;T2 CONTAINS ACCESS TO BE GIVEN
	MOVE T2,T3		;NO. SAVE ACCESS REQUEST IN T2
SSB12:	SETZ T3,
	TXNE T2,SSWRT		;WRITE?
	JRST [	TXO T3,EXESHR!EXEWRB ;YES. SET SHARABLE AND WRITABLE
		JRST SSB11]
	TXNE T2,SSCPY		;NO. COPY ON WRITE?
	TXO T3,EXEWRB		;YES. SET WRITABLE
SSB11:	LOAD P5,EXFLG,T3	;SAVE RIGHT-JUSTIFIED IN P5

;LOOK AT DIRECTORY FOR CURRENT GROUP.  IF SAME AS THIS PAGE, ADD TO
;CURRENT GROUP.  OTHERWISE, START NEW GROUP.

	SKIPN SSFLAG		;FIRST TIME THROUGH?
	JRST [	SETOM SSFLAG	;YES. SET FIRST TIME FLAG
		JRST SSAVB3]	;GO SET UP DIRECTORY
	CALL SCOMPR		;DO COMPARISON
	 JRST SSAVB3		;NEW SUBGROUP DESIRED
	JUMPN T1,SSAVB4		;T1 NOT ZERO IF WORKING ON GROUP OF
				; NON-ZERO
	CALL SSAVC

; SET UP DIRECTORY FOR NON-ZERO BLOCK

SSAVB3:	STOR P5,EXFLG,(P1)	;MOVE ACCESS TO FIRST WORD
	STOR P3,EXADR,(P1) 	;MOVE FILE PAGE NO. TO 1ST WORD
	MOVE T1,TADR
	STOR T1,EXPAG,1(P1) 	;MOVE CORE PAGE NO TO 2ND WORD
	;..
;COPY NON-ZERO PAGE TO FILE (TADR POINTS TO CORE PAGE, P3 TO FILE PAGE)

	;..
SSAVB4:	SKIPE SSDEV		;ARE WE WRITING THE DATA PAGES?
	JRST [	HRRZ T1,HANJFN	;YES.T1/JFN
		MOVE T2,[POINT ^D36,0]	;T2/(36 BIT BYTES,,
		HRR T2,SSCORE	;START OF CORE SCRATCH PAGE)
		MOVNI T3,1000	;T3/NEGATIVE BYTE COUNT
		SOUT		;WRITE THE PAGE
		 ERJMP SSAVB0	;FAILED
		JRST .+1]
	AOS P3			;POINT TO NEXT FILE PAGE

;PREPARE FOR NEXT PAGE

SSAVB5:	SETZ T1,		;T1/0 FOR UNMAP
	MOVE T2,SSCORE		;T2/ADDRESS OF CORE SCRATCH PAGE
	CALL SETMPG		;UNMAP THE CORE SCRATCH PAGE
SSB1:	AOS P2			;INCREMENT PAGE COUNT FOR SUBGROUP
	AOS Q2			;INCREMENT PAGES FROM SECTION COUNT
	AOS TADR		;INCREMENT CORE PAGE NO.
	AOJL Q1,SSB10		;INCREMENT DECREASING PAGE COUNT.
				; DONE IF ZERO

; ALL PAGES FOR ONE ENTRY IN INPUT TABLE ARE DONE

	CALL SSAVC		;FINISH LAST DIRECTORY ENTRY AND REINITIALIZE
	RETSKP			;SUCCESS

;SCOMPR used to compare consecutive pages of memory and
;tell whether a new subgroup is needed.
;
;Returns+1:	new subgroup needed
;	+2:	no new subgroup needed

SCOMPR:	LOAD T1,EXFLG,(P1) 	;PUT ACCESS BITS INTO T1 RIGHT-JUSTIFIED
	LOAD T2,VSECNO_-9,TADR	;SEE WHICH SECTION WE'RE DEALING WITH
	CAME T2,WSEC		;DID WE JUST CROSS A SECTION BOUNDARY?
	JRST [	EXCH T2,WSEC	;YES, WORRY.  (SEE GETX5 COMMENT FOR WHY!)
		JUMPL T2,.+1	;DON'T WORRY MUCH IF THIS IS FIRST SECTION
		CALLRET SSAVC]	;DON'T CROSS SECTION BOUNDARY IN DIRECTORY ENTRY
	CAME T1,P5		;IS ACCESS FOR THIS PAGE SAME AS FOR
				; CURRENT GROUP?
	CALLRET SSAVC		;START NEW SUBGROUP
	LOAD T1,EXADR,(P1) 	;YES. LOOK AT FILE PAGE NUMBER IN 1ST WORD
	RETSKP			;SKIP TO SAY NEW SUBGROUP NOT NEEDED

;HERE IF SOUT FAILED WRITING THE .EXE FILE

SSAVB0:	SETZ T1,		;UNMAPPING
	MOVE T2,SSCORE		;ADDRESS OF PAGE TO UNMAP
	CALL SETMPG		;UNMAP THE WINDOW
	HRRZ T1,LSTERR		;GET ERROR CODE RETURNED BY SOUT
	ITERR			;RETURN ERROR TO CALLER
;SSAVC - CLEANS UP THE DIRECTORY WHEN A SUBGROUP ENDS

;ACCEPTS:
; P4/ADDRESS OF SCRATCH PAGE
;  P1/ ADDRESS OF CURRENT ENTRY IN DIRECTORY
;  P2/ NUMBER OF PAGES IN THIS SUBGROUP

;CALL:	CALL SSAVC

;CALLED BY SSAVB

;THIS ROUTINE FILLS IN THE REPEAT COUNT IN THE DIRECTORY ENTRY
;FOR THE CURRENT GROUP.  THE OTHER FIELDS HAVE BEEN SET IN SSAVB
;WHEN THE GROUP BEGAN.

SSAVC:	JE <EXEZRO,EXADR>,(P1),[
		CAIE Q2,1000	;HAS THE ENTIRE EMPTY SECTION BEEN SEEN?
		JRST SSAVC0	;NO, CLEAR ENTRY
		MOVX CX,EXEWRB!EXEZRO  ;SET FIRST WORD OF ENTRY
		MOVEM CX,(P1)
		MOVEI P2,1	;SET PAGE COUNT
		JRST .+1	;WRITE ENTRY
	SSAVC0:	SETZM 0(P1)	;IF ALLOCATED BUT ZERO
		SETZM 1(P1) 	;CLEAR ENTRY
		JRST SSAVC1]	;AND WRAP UP
	SOS P2			;DECREMENT PAGE COUNT
	STOR P2,EXCNT,1(P1) 	;SAVE  IN 2ND WORD
	ADDI P1,2		;POINT TO NEXT LOCATION IN DIRECTORY
	MOVE T1,P4		;START OF DIRECTORY SCRATCH PAGE
	ADDI T1,772		;LAST WORD OF SCRATCH PAGE
	SUB T1,PDVSPC		;LEAVE ROOM FOR PDV LIST
	CAML P1,T1		;P1 POINTS TO FIRST OF PAIR - IS IT
				; BEYOND END?
	ITERR(SSAVX4)		;YES. RETURN ERROR

SSAVC1:	SETZB P2,Q2		;CLEAR COUNT OF PAGES IN GROUP AND SECTION
	RET
	TNXEND
	END