Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_3_19910112 - mit/monitor/getsav.mac
There are 28 other files named getsav.mac in the archive. Click here to see a list.
; 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) 1976,1977,1978,1979 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

	 SEARCH PROLOG
	 TTITLE GETSAV

 ;GET AND SAVE ROUTINES

 ;AC DEFINITIONS USED HEREIN

 DEFAC (FX,Q3)			;FORK INDEX

 ;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)
 ;  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
	 SAVEAC <Q1,Q2,FX> ;NEED TO USE ALL OF THE Q REGS
	 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.
	 MOVE P1,T4		;COPY JFN
	 MOVEM T4,GHNJFN		;REMEMBER HANDLE,,JFN
	 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 [	CALL FUNLK		;Invalid process handle-- unlock process structure
		 MOVE 5,T1		;Save the error code
		 JRST GETE2]		;Clean up and give error
	 CALL SETEXO		;Set process as execute-only
	  JRST [	CALL FUNLK		;Process is 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:
	 MOVE T4,GHNJFN		;GET HANDLE AND JFN FOR GTSVIF
	 CALL GTSVIF		;Setup PTN in P5
	  JRST [	MOVE 5,T1		;Error-- Save the error code
		 JRST GETE2]		;Clean up and return error

 ; 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 6,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 6,GET1		;POSITIVE MEANS END

 ;FILE IS ZERO-COMPRESSED

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

 GET4:	TRNN 6,777		;JUST CROSSED PAGE BOUNDARY?
	 CALL GET3		;YES, REMAP
 GET5:	JUMPL 5,[TLNE 5,(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 6,GET4	;THROW WORD AWAY
		 JRST GET5A]
	 HRRZ 2,6		;COMPUTE (NEG) NO. WDS LEFT ON PAGE
	 ANDI 2,PGSIZ-1
	 SUBI 2,PGSIZ
	 HLRE 3,6		;GET (NEG) NO. WDS LEFT IN PTR
	 CAMGE 3,2		;GET 'MINIMUM'
	 MOVE 3,2
	 PUSH P,3		;SAVE IT
	 MOVE 2,5		;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 6,3			;UPDATE AOBJN PTR
	 JUMPL 6,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,6,26]	;PAGE NUMBER NEXT REFERENCE
	 CAMG 2,P3		;PAGE WITHIN LIMITS?
	 CAMGE 2,P2
	 JRST [	MOVSI 5,(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 5,(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 5,6		;MAKE @5 POINT TO FPG0A
	 ANDI 5,777000		; I.E. FPG0-USERPAGE
	 MOVN 5,5
	 ADDI 5,FPG0A
	 HRLI 5,6		;GIVE IT AN INDEX
	 HRRZ 1,P1		;RESTORE JFN TO 1
	 RET

 ;UNMAP WINDOW PAGE

 GET3X:	SETZ 1,
	 MOVEI 2,FPG0A
	 CALL SETMPG
	 OKINT
	 RET
 
 ;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

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

GTSVID:
	UMOVE T4,1		;SAVE ARG
	TRZ T4,770000		;FLUSH MISC BITS FROM JFN
	HRRZ T1,T4		;JFN
	DVCHR			;GET DEVICE CHARACTERISTICS
	MOVE T3,T2		;Move device characteristics
	HRRZ T1,T4		;JFN
	MOVSI T2,(^D36B5)	;BYTE SIZE FOR BIN/BOUT XFER
	RET

;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
	CALL CREADF		;Restore state to what it was at OPENF
	CALL CLRGXO		;Disable execute-only GET
	OKINT			;Now we can be interupted
GETEN1:
	HRRZ T1,P1		;Get JFN
	CLOSF			;Attempt to close file
	 JFCL			;Can't-- maybe pages still mapped
	RET			;Return from GETEND
; Errors during GET . . .

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

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

;UNEXPECTED END OF FILE DURING GET

GETE1:	MOVEI 5,GETX1		;FORCE RETURN OF ILLEGAL FILE FORMAT ERROR

;AN ERROR OCCURRED. AC 5 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 5,IOX4		;DID WE GET END OF FILE?
	 ITERR (GETX1)		;YES. RETURN ILLEGAL SAVE FILE FORMAT
	MOVE 1,5		;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 6,0(6)		;GET COUNT OF DIR WORDS
	SOS 6			;DISCOUNT THE HEADER
	LSH 6,-1		;2 WORDS PER ENTRY
	MOVEM 6,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
	 ITERR (GJFX22,OKINT)	;JSB MUST BE FULL
	MOVEM A,WRKPAG		;REMEMBER SCRATCH PAGE FOR NON-DISK DEVICES
	CALL ASGPGS		;GET DIR PAGE
	 ITERR (GJFX22,OKINT)	;JSB MUST BE FULL
	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 6,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 GTWRD		;GET FILE POINTER
	MOVE FX,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>,FX,ALLZER ;IF NON-EX, IGNORE IT
	SETZ C,			;CLEAR ACCESS BITS
	TXO C,PM%RD!PM%EX	;TURN ON READ AND EXECUTE
	TXNE FX,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,FX		;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 FX,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 [	MOVE 5,1	;GET THE ERROR
		JRST GETE2]	;AND GO ERROR OUT
	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 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(6)		;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 5 AND GO 
;CLEAN UP

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

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

;AN ERROR OCCURRED DURING READING OF THE FILE. AC 5 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 6,-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 FX,0(Q2)		;SAVE COUNT
	CALL CHECKG		;MAKE SURE IS DISK
	 ITERR(SSAVX1)		;MUST BE DISK
	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 FX,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:	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 FX,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 [	MOVE 5,1	;GET THE ERROR
		JRST GETE2]	;AND GIVE IT
	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 [	MOVE 5,1	;GET THE ERROR
		JRST GETE2]	;AND GIVE IT
	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
	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
;	* P6/ FORK HANDLE,,0 (* NO LONGER TRUE.  P6 IS NOW TRVAR POINTER.
;		HOWEVER, I COULDN'T FIND ANY REFERENCES IN FSTGET TO
;		P6, OR I WOULD HAVE FIXED THEM!  E.O.  7/24/80)


;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
;  7 - 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 7,0(P1)		;GET NEXT DESCRIPTOR
	JUMPE 7,SAVE1		;CHECK FOR END OF LIST OF DESCRIPTORS
	CALL SAVEB		;WRITE BLOCK
	AOJA P1,SAVE2

SAVE3:	MOVE 7,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 6,7		;MAKE LH COUNT NEGATIVE
	TRZE 6,400000		;LARGER THAN 377777?
	PUSH P,[IFIW!SAVEBL]		;YES, DO IN TWO PARTS
	MOVNI 6,0(6)
	HRLI 7,0(6)
	SETO 4,			;SAY NO PAGE NOW MAPPED
	NOINT			;BE NOINT WHILE PAGE MAPPED
SAVEB1:	JUMPGE 7,SAVUMP		;DONE, CLEAN UP
	MOVE 6,7		;SETUP TO SCAN FOR NON-0'S
	LDB 1,[POINT 9,6,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 5,6		;SETUP RELATIVE ADDRESS IN WINDOW
	ANDI 5,PGSIZ-1
	ADDI 5,FPG0A
	JRST SAVE8

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

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

;WRITE ALL OR PART OF ONE PAGE.

SAVED:	HLRE 2,6		;COUNT OF WORDS JUST SCANNED
	ADD 5,2			;RESET WINDOW POINTER
	HRRZ 1,SHNJFN		;JFN
	MOVE 2,6
	SUBI 2,1		;OUTPUT IOWD CNT,ADR
	BOUT
	 ERJMP SAVERR		;FAILED
	HLRE 3,6		;GET (NEG) COUNT
	PUSH P,3		;SAVE IT
	HRRZ 2,5		;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,5		;UPDATE WINDOW PTR
	HRL 3,3			;PUT COUNT IN BOTH HALVES
	ADDM 3,7		;UPDATE AOBJN PTR
	RET


SAVERR:	CALL SAVUMP		;UNMAP ANY PAGE AND GO OKINT
	ITERR (,<MOVE T1,LSTERR>)	;THEN GIVE THE 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(6)
	CAIN 2,0(7)		;FIRST WORD OF BLOCK OR PAGE?
	JRST SAVE9		;YES
	HLRZ 2,6
	SETCMI 3,0(6)
	CAIE 2,-1		;END OF BLOCK, OR
	TRNN 3,777		;END OF PAGE?
	JRST SAVE5		;YES, FINISH BLOCK
	SKIPE 1(5)		;SUCCESSIVE 0?
	JRST SAVE7		;NO, CONTINUE SCAN
SAVE5:	MOVNI 6,0(6)		;COMPUTE NUMBER OF WORD TO DUMP
	ADDI 6,0(7)		;(-CURRENT)+OLD = -DIFFERENCE
	MOVSI 6,0(6)
	HRRI 6,0(7)		;FIRST ADDRESS
	CALL SAVED		;WRITE BLOCK
	JRST SAVEB1

;SCAN OVER BLOCK OF 0'S

SAVE10:	TRNE 6,777		;END OF PAGE?
	SKIPE 0(5)		;OR NON-0 WORD?
	JRST SAVE11		;YES
SAVE9:	ADDI 5,1
	AOBJN 6,SAVE10
SAVE11:	MOVE 7,6		;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 FX 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)
; PDVSPC - Room needed for PDV storage, 0 for none, n+1 for header plus n 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,SAVENT,SSDIR,SSAC2,SSCORE,SSDEV,TADR,WSEC>
;TADR/	process page number

	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
	SKIPE T2,PDVS(T1)	;ANY PDVS?
	MOVE T2,(T2)		;GET HEADER OF PDV BLOCK
	HRRZM T2,PDVSPC		;REMEMBER HOW MUCH SPACE TO LEAVE FOR PDV BLOCK
	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

	CALL GETOFF		;MAP IN FORK
	SKIPN T4,PDVS(T1)	;ARE THERE ANY PDVS?
	JRST [	CALL GEM	;UNLOCK THINGS
		JRST NOPDVS]	;SKIP REST OF PDV STUFF
	HRRZ T2,(T4)		;GET SIZE OF PDV BLOCK IN PSB
	CAMLE T2,PDVSPC		;IS IT WITHIN RESERVED BOUNDS?
	CALL [	CALL GEM	;UNLOCK THINGS
		ITERR (SSAVX5)]	;"NUMBER OF PDVS GREW DURING SSAVE"
	HRRZ T1,HANJFN		;GET JFN
	HRLI T2,EXEPDV		;SHOW THAT THIS IS THE PDV SECTION
	BOUT			;WRITE EXEPDV,,LENGTH
	HRRZ T3,T2		;ISOLATE LENGTH OF BLOCK
	MOVNI T3,-1(T3)		;GET NEGATIVE NUMBER OF PDVAS TO BE STORED
	MOVEI T2,1(T4)		;GET ADDRESS OF FIRST PDVA
	HRLI T2,444400		;MAKE BYTE POINTER TO FULL WORDS
	SOUT			;WRITE THE PDVAS INTO THE OUTPUT FILE
	CALL GEM		;UNLOCK THINGS

;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
	SETZM T2		;T2/DATA TO BE WRITTEN
	MOVEI T4,-1000+5	;NEGATIVE SPACE AVAILABLE FOR DIRECTORY
	ADD T4,P1		;NEGATIVE SPACE REMAINING
	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
	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
	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
; 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
	SETZ P2,		;CLEAR COUNT OF PAGES IN SUBGROUP
	SETZM SSFLAG		;CLEAR FIRST TIME FLAG
	HLRO Q1,SSWORD		;-COUNT OF PAGES
	;..
;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
	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
		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
	MOVEI T2,0		;USE PC SECTION IF SECTION NUMBER IS ZERO
	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 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),[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:	SETZ P2,		;CLEAR COUNT OF PAGES IN GROUP
	RET

	TNXEND
	END