perm filename SAISGC.FAI[S,AIL]1 blob sn#102552 filedate 1974-05-22 generic text, type T, neo UTF8
COMPIL(SGC,<STRNGC,STRGC,STCLER,SGINS,SGREM,%SPGC1,%ARSR1,.SONTP>
	   ,<.SGCIN,GOGTAB,X11,CORGET,CORREL,CORINC,X22,CORBIG,SPRPDA,INSET>
	   ,<STRING GARBAGE COLLECTOR ROUTINES>
	   ,<%SPGC,%STRMRK,%ARRSRT>)
NOLOW <
MLT←←=16  BKSZ←←5*MLT+1		;BKSZ must always be so related to MLT
↑.CORERR:
	CORERR	<NO CORE FOR ALLOCATON>
HERE (STRGC)
	EXCH	A,-1(P)		;THE DESIRED A IS HERE
	MOVE	USER,GOGTAB
	MOVEM	RF,RACS+RF(USER);SAVE F REGISTER WHERE GC CAN FIND.
	PUSHJ	P,STRNGC	;COLLECT TRASH
	SUB	P,X22		;BACK UP STACK
	MOVNS	A
	ADDM	A,REMCHR(USER)
	MOVE	A,1(P)		;GET ORIGINAL "A" BACK
	JRST	2,@2(P)		;RETURN
HEREFK(.SONTP,..SONT)
	BEGIN 	SONTP
DEFINE CANON (ADR,AC)<
	LDB	TEMP,[POINT 3,ADR,5]	;4,5,6,7,0,1 FROM POSITION
	IMULI	AC,5			;ADDR IN CHARS
	ADD	AC,BPTBL(TEMP)		;0,1,2,3,4,5 EXTRA CHARS
>
	MOVE	USER,GOGTAB
	PUSH	P,A
	PUSH	P,B
	PUSH	P,C
	PUSH	P,D
	MOVE	A,-5(P)	;CNT
	ADDM	A,REMCHR(USER)	;TEST GCING LATER
	HRRZ	D,-1(SP)	;LOAD LENGTH
	HRRZ	B,(SP)
	CANON	<(SP)>,B	;STANDARD FORM 
	ADD	B,D		;ADD LENGTH
	HRRZ	C,TOPBYT(USER)
	CANON	<TOPBYT(USER)>,C
	CAMN	B,C		;SAME??
ISONTP:	SKIPLE	REMCHR(USER)	;GC NEEDED??
	JRST	NOTONT		;MAY WIND UP COPYING
XIT:	POP	P,D		;FINISH
	POP	P,C
	POP	P,B
	POP	P,A		
	SUB	P,X22
	JRST	@2(P)
NOTONT:				;ALWAYS GET ENOUGH TO COPY STRING
	HRRZ	D,-1(SP)	;GET LENGTH OF STRING
	ADD	A,D
	ADDM	D,REMCHR(USER)
	SKIPG	REMCHR(USER)	;REALLY GC ??
	JRST	CPYSTR		;NO REAL NEED
	PUSHJ	P,STRNGC	;GARBAGE IS COLLECTED
	HRRZ	B,(SP)
	CANON	<(SP)>,B	;ON TOP NOW ??
	ADD	B,D
	HRRZ	C,TOPBYT(USER)	
	CANON	<TOPBYT(USER)>,C
	CAME	B,C		;WELL
	JRST	CPYSTR		;NO, MUST COPY
	MOVN	D,D		;GIVE BACK CHARS GET FROM NOT COPYING
	ADDM	D,REMCHR(USER)	;
	JRST	XIT		;DONE
CPYSTR:	
	SKIPE	SGLIGN(USER)	;NEED FW BNDRY??
	PUSHJ	P,INSET		;YES
	MOVE	B,TOPBYT(USER)	;NEW STRING BP
	EXCH	B,(SP)
	JUMPE	D,XIT		;DONE ??
	ILDB	C,B		;COPY CHARS
	IDPB	C,TOPBYT(USER)
	SOJG	D,.-2		;
	JRST	XIT		;DONE
BPTBL:	4
	5
	0
	0
	0
	1
	2
        3				;MAP
BEND SONTP
HERE(STRNGC)
	MOVE	USER,GOGTAB
	CAME	RF,RACS+RF(USER)	;ALL RUNTIMES SHOULD BOTH
	 ERR	 <DRYROT -- RF (R12) not saved in RACS at STRNGC>
	MOVEM	RF,RACS+RF(USER)	;WILL RESTORE AFTER SORTING ROUTINES
	SKIPN	SGCTIME(USER)	;User can
	 JRST	 SGC1
	MOVEI	TEMP,0		;TIME SG STARTS
NOTENX <
	CALL6	(TEMP,MSTIME)
	MOVNM	TEMP,SGCTIME(USER)
>;NOTENX
TENX <
	MOVEM	1,SGCTIME(USER)		;SAVE 1 & 2
	MOVE	TEMP,2			;"TIME" GIVES TIME IN 1, DIVISOR
	JSYS	TIME			;TO GET SECONDS IN 2 (ALWAYS
	MOVNS	1			;1000 SO FAR)
	EXCH	1,SGCTIME(USER)		;BUT SHOULD THIS REALLY BE TIME
	MOVE	2,TEMP			;OF DAY, NOT BILLABLE RUNTIME?
>;TENX
SGC1:	MOVEM	11,SGACS+11(USER)
	MOVEI	11,SGACS(USER)
	BLT	11,SGACS+10(USER)
	AOS	TEMP,SGCCNT(USER)	;COUNT TIMES THROUGH GC
	MOVNM	TEMP,SGCCNT(USER)	;INDICATE THAT GC IS IN PROGRESS
	SKIPN	.SGCINT
	 JRST	 NOTRP
	PUSH	P,A			;SIZE OF REQUEST
	PUSH	P,0			;CONVENTION IS 4 PARAMS
	PUSH	P,SGCCNT(USER)
	PUSH	P,0			;SO PUSH SOME [CENSORED] UP
	PUSHJ	P,@.SGCINT
NOTRP:
	HRRZ	TEMP,TOPBYTE(USER) ;MAKE SURE DIDN'T OVERFLOW
	CAMG	TEMP,STTOP(USER)
	CAMGE	TEMP,ST(USER)
	 ERR	 <TOPBYTE out of range at STRNGC -- will continue>,1
CALSG:	MOVEI	T,SGROUT(USER)		;GET LINKED LIST OF ROUTINE NAMES
	PUSH	P,T			;SAVE FIRST POINTER
	PUSH	P,[SGSORT]		;PROVIDE ACCESS TO SORTING ROUTINE
↑CALSGL:
	SKIPN	T,@-1(P)		;GO DOWN LIST UNTIL DONE
	JRST	ALLCOL			;DONE
	HRRZM	T,-1(P)			;SAVE NEW POINTER
	PUSHJ	P,@-1(T)		;CALL GENERATOR ROUTINE
	MOVE	RF,RACS+RF(USER)	;GET GOOD F BACK, ASSUMING GOOD USER
	JRST	CALSGL			;DO MORE THAN ONCE
ALLCOL:	SUB	P,X22			;Remove temp, SGSORT address
SGSWEP:	MOVEI	C,BKSZ
	PUSHJ	P,CORGET
STCORERR: ERR  <String garbage collector can't get core>
	MOVEM	B,STBUCK(USER)
	MOVE	B,STLIST(USER)		;Loop through all string spaces, 
	SETZM	SGCNUM(USER)		;Strings handled count (not incl. const.)
SPCLUP:	PUSHJ	P,SRTSPC		; sorting.  When through, .LIST
	SKIPE	B,.NEXT(B)		; in the header of each space
	 JRST	 SPCLUP			; will be the sorted dscrptr lst.
	MOVE	B,STBUCK(USER)	;Release the buckets (STBUCK=OFFSET, see blow).
	PUSHJ	P,CORREL
	MOVE	B,STLIST(USER)
	MOVE	C,B
	PUSHJ	P,DSTSET
SWPLUP:	PUSHJ	P,SOURCE	;Identify a source "nest", return params
	 JRST	 SWPDUN		; and adjust descriptors, no-skip when done
	PUSHJ	P,DEST		;Identify a destination location, move the
	JRST	SWPLUP		; source nest there, and re-create all 
SWPDUN:
	HLRZ	D,STREQD(USER)		;Requested char count +
	ADD	D,SGACS+A(USER)		; STREQD (see p. 2) char count.
	MOVE	E,D
GRANT:	ADD	D,REMCHR(USER)		;Granted, if total required
	JUMPL	D,GRANTED		; space exists in last DEST
	PUSHJ	P,WASTE			;Add up wasted space in DEST being left.
	MOVE	A,C			;Save space being abondoned
	SKIPN	C,.NEXT(C)		; space.  Otherwise, move
	 JRST	 EXPSTR			; to next space, if any, and
GRTSET:	PUSHJ	P,DSTSET		; continue to try to grant 
	MOVE	D,E			; request
	JRST	GRANT
EXPSTR:	HLRZ	C,STINCR(USER)		;STINCR (see p. 2) char count.
	CAML	E,C			;Is there going to be room?
	 ERR	 <String space expansion: request too big>
	HRRZ	C,STINCR(USER)		;STINCR  word count, + .HDRSIZ
	PUSHJ	P,CORGET
	 JRST	 [PUSHJ P,CORBIG	;If for some reason we can't get
		  MOVEI B,.HDRSIZ+1(C)	; STINCR words, make sure that
		  IMULI B,5		; a new block can at least satisfy
		  CAMGE B,E		; the request + STREQD.
		   ERR <String GC: no core to expand string space>
		  PUSHJ P,CORGET	;Will do, get it
		   ERR <DRYROT -- unexpected STRNGC core problem>
		  JRST .+1]
	MOVEI	B,.HDRSIZ(B)		;Adjust pointer to leave header,
	SUBI	C,.HDRSIZ		; set up header area parameters,
	MOVEM	C,.STTOP(B)		; link to previous area
	MOVEM	C,.SIZE(B)
	ADDM	B,.STTOP(B)
	SETZM	.NEXT(B)
	SETZM	.LIST(B)
	MOVEM	B,.NEXT(A)
	MOVE	C,B			;This becomes last destination
	JRST	GRTSET			;Go satisfy request, now guaranteed.
GRANTED:HRRZM	C,ST(USER)		;Update ST, STTOP, release any
	MOVE	TEMP,.STTOP(C)		; spaces made unnecessary by diminished
	MOVEM	TEMP,STTOP(USER)	; active strings
	SKIPN	A,.NEXT(C)		;Get next space past last DEST, if any,
	 JRST	 STSTAT			; then clear any next space pointers.
	SETZM	.NEXT(C)
RELLUP:	MOVEI	B,-.HDRSIZ(A)		;Release any spaces which are
	MOVE	A,.NEXT(A)
	PUSHJ	P,CORREL		; apparently no longer necessary.
	JUMPN	A,RELLUP
STSTAT:				;Check that Full-word alignment produced
	SKIPE	SGLIGN(USER)	;Alignment also implies clearing
	 PUSHJ	 P,RESCLR	;Free space
	MOVEI	B,=15		;Update REMCHR by initial request, plus a
	ADD	B,SGACS+A(USER)	; bit of slop (NOT by STREQD, which specifies
	ADDB	B,REMCHR(USER)	; free space -- slop is unfree, for safety.)
	JUMPGE	B,[ERR <DRYROT -- String GC Surprised at Untoward Occurrence>]
	MOVMS	SGCCNT(USER)	;Now indicate done with GC
	SKIPN	SGCTIME(USER)	;Timing active?
	 JRST	 NOTIME		;No
	MOVEI	TEMP,
NOTENX <
	CALL6	(TEMP,MSTIME)	;Collect GC times
>;NOTENX
TENX <
	EXCH	1,TEMP
	PUSH	P,2
	JSYS	TIME
	POP	P,2
	EXCH	1,TEMP
>;TENX
	ADDB	TEMP,SGCTIME(USER)
	ADDM	TEMP,SGCTOTAL(USER)
NOTIME:
	SKIPN	.SGCINT
	 JRST	 QUITGC
	MOVN	TEMP,REMCHR(USER);SIZE OF GRANT, LESS ORIGINAL REQUEST
	PUSH	P,TEMP
	PUSH	P,SGACS+1(USER)	;ORIGINAL REQUEST
	PUSH	P,SGCCNT(USER)	;AS FAR AS I CAN TELL, JUST USING UP CELLS
	PUSH	P,SGCNUM(USER)	; IN THE CALL STACK
	PUSHJ	P,@.SGCINT
QUITGC:	MOVE	USER,GOGTAB	;PARANOID
	HRLZI	11,SGACS(USER)	;Restore and return
	BLT	11,11
	POPJ	P,
SGSORT:	HLLZ	B,(A)		;don't collect constants
	JUMPE	B,SGRST
	HRRZ	TEMP,1(A)
	MOVEI	B,STLIST-.NEXT(USER)
SGLUP1:	SKIPN	B,.NEXT(B)
	 JRST	 NORANGE	;Range exhausted, bad string
	CAML	TEMP,B		;Address check of string bp
	CAML	TEMP,.STTOP(B)	; against both ends of each 
	 JRST	 SGLUP1		; space determines if string in range
INRANGE:SUB	TEMP,B		;Convert bp to space-relative
	IMULI	TEMP,5		; character count
	HLLZ	C,1(A)
	TLNN	C,777770	;Make sure there are still byte ptr. bits
	 JRST	 [MOVE A,A	;ERR type 7 gets AC # from here
		  ERR <SGSORT-- string encountered twice, descriptor addr = >,7
		  JRST SGRST]	;Don't handle again.
	HRRI	C,[BYTE(7) 0,1,2,3,4,5]
	ILDB	C,C		;Space-relative count fits in
	ADD	C,TEMP		; rh, lh 0 signals
	MOVEM	C,1(A)		; re-encounter (above)
	MOVE	C,.LIST(B)	;Insert descriptor, linked by
	HRLM	C,(A)		; string number field, into
	HRRZM	A,.LIST(B)	; list for this space
	JRST	SGRST
NORANGE:MOVE	A,A		;String not in range, complain, NULL it,
	ERR	<String GC: Descriptor byte ptr. out of bounds, Addr. is >,7
	SETZM	(A)		; and go on.
SGRST:	ADDI	A,2		;Auto-increment descriptor index
	POPJ	P,
HERE(%SPGC)	HRRZ	A,SPDL(USER)	;START AT BASE OF STACK
↑%SPGC1:ADDI	A,1
	JRST	SGTST		;AND WORK UP TO CURRENT POINTER
STRNGSTACKMARKLOOP:
	PUSHJ	P,SGSORT	;SORT IT INTO LIST
SGTST:
	CAIGE	A,(SP)		;DONE?
	 JRST	 STRNGSTACKMARKLOOP ;NO
GPOPJ:	POPJ	P,		;YES, GO ON TO NEXT TYPE
HERE (%STRMRK)
	SKIPN	T,STRLNK(USER)	;GET LINK
	 POPJ	 P,		; NO STRINGS AT ALL
STMKL1:	HRRZ	A,-1(T);<	;=>1ST STRING
	HLRZ	Q2,-1(T)	;# STRINGS THIS PROC
	JRST	SOJLP		;GO LOOP
STMKLP:	PUSHJ	P,SGSORT	;SORT VARIABLES INTO LIST
SOJLP:	SOJGE	Q2,STMKLP	;SORT UNTIL DONE WITH THIS PROC (SGSORT INCRS A)
STRMK4:	HRRZ	T,(T)		;NEXT PROCEDURE
	JUMPN	T,STMKL1	; IF THERE IS ONE
	POPJ	P,		;DONE
INTERNAL %ARRSRT
HERE (%ARRSRT)
↑%ARSR1:
PROCDO:	HLRZ	Q1,1(RF)	;FETCH PDA
	CAIN	Q1,SPRPDA	;IS IT SPROUTER??
	POPJ	P,		;YES
	MOVE	Q1,PD.LLW(Q1)	;WE HAVE TO DO SOMETHING -- PT AT LVI
CHK:	SKIPN	T,(Q1)		;GET ENTRY
	JRST	GODOWN		;0 MEANS OF PROC DESCR
	TLC	T,100000	;TYPE 2? (STRING ARRAY)
	TLNE	T,740000	;
	AOJA	Q1,CHK		;NO
	SKIPN	A,@T		;THERE??
	AOJA	Q1,CHK		;NO
	SUBI	A,1;<		;A=>2D WORD, FIRST ENTRY -- DCS 5-3-72
	SKIPL	Q2,-1(A)	;BETTER BE THERE
	ERR	<DRYROT at Arrsrt>
	PUSHJ	P,ARPUTX	;GO SORT IT
	AOJA	Q1,CHK
GODOWN:	HRRZ	RF,(RF)		;NOTE THAT RESTR WILL PUT RF BACK
	CAIE	RF,-1		;
	JRST	PROCDO 		;-1 WILL SAY END
LARR:	SKIPN	E,ARYLS(USER)	;LEAPING LISTS
	POPJ	P,		;NONE
LAR1:	
	HLRZ	Q2,(E)		;GET ADDRESS
	MOVEI	A,-1(Q2);<	;A=>1ST WORD, FIRST ENTRY
	SKIPL	Q2,-2(Q2)		;BE SURE
	ERR	<DRYROT -- LEAPing error at ARRSRT>
	PUSHJ	P,ARPUTX	;GO SORT IT
LAR2:	HRRZ	E,(E)		;MERRILY WE LINK ALONG
	JUMPN	E,LAR1		;
	POPJ	P,		;HOME AT LAST
ARPUTX:	
	HRRZS	Q2		;YES, GET TOTAL SIZE
	LSH	Q2,-1		;NUMBER OF STRINGS
	JRST	ARSLP
ARS3:	PUSHJ	 P,SGSORT	; BUT COLLECT NON-CONSTANTS 
ARSLP:	SOJGE	Q2,ARS3		;A INCREMENTED IN SGSORT, LOOP UNTIL DONE
	POPJ	P,		;ALL DONE WITH THIS ARRAY.
SRTSPC:	MOVE	A,STBUCK(USER)	;Clear bucket list
	SETZM	(A)
	ADDI	A,1
	HRLI	A,-1(A)
	MOVEI	C,BKSZ-2(A)
	BLT	A,(C)
	SKIPN	A,.LIST(B)
	 JRST	 SORTED
DSCLUP:	AOS	SGCNUM(USER)	;Count strings handled.
	HLRZ	FF,(A)
	MOVE	C,1(A)
	MOVE	E,C		;For later (below)
	IMULI	C,MLT
	IDIV	C,.SIZE(B)	;Compute bucket entry
	ADD	C,STBUCK(USER)	; (partition space among bckts)
	MOVE	Q1,C
	HRRZ	T,(A)
SGSLUP:	MOVE	D,C
	HLRZ	C,(C)
	JUMPE	C,[HRRM A,(Q1)	;** NEW will be end string,
		   JRST INSERT]	;    keep track of it for linkage
	CAMGE	E,1(C)
	 JRST	 INSERT		;NEW begins before NEXT, insert
	CAME	E,1(C)
	 JRST	 SGSLUP		;NEW begins after NEXT, keep looking
	HRRZ	TEMP,(C)
	CAMG	T,TEMP		;Insert by descending length
	 JRST	 SGSLUP
INSERT:	HRLM	A,(D)
	HRLM	C,(A)		;Link is in lh of word 2 of descriptor
	MOVE	A,FF
	JUMPN	A,DSCLUP
SORTED:	MOVE	C,STBUCK(USER)	;Starting at the end of the bucket
	HRLI	C,D		; array, look only at non-zero
	MOVEI	D,BKSZ-1	; entries.  Each iteration, retain
	MOVEI	A,0		; the newest <first> pointer, having
LNKLUP:	SKIPN	E,@C		; placed the previous <first> pointer
	 JRST	 AOCHK		; into the list identified by the
	HRLM	A,(E)		; newest <last> pointer.  The first
	HLRZ	A,E		; <first> pointer is 0
AOCHK:	SOJGE	D,LNKLUP
	MOVEM	A,.LIST(B)
	POPJ	P,
SOURCE:	MOVE	E,.LIST(B)
	JUMPE	E,[SKIPN B,.NEXT(B)
		   POPJ P,		;no-skip, return
		   JRST SOURCE]
	MOVE	Q1,1(E)
	IDIVI	Q1,5
	ADD	Q1,B
	HLL	Q1,[PTBL1: POINT 7,0	;!HOOK! IF PTBL OF SUBSTR AVAIL, 
		    POINT 7,0,6		; declare it external and use it
		    POINT 7,0,13	; here -- tables are the same
		    POINT 7,0,20
		    POINT 7,0,27
		    POINT 7,0,35](Q2)
	PUSH	P,Q1
	HRLS	E
	MOVN	A,1(E)
	HRRZ	D,(E)
	SUB	D,A
	ADDM	A,1(E)		;Adjust 1st descr. location count to nest-rel.
SRCLUP:	HLRZ	Q1,(E)		;Next elt.
	JUMPE	Q1,NONEST	;If end-loc in D does not reach the next
	CAMG	D,1(Q1)		; descriptor's location, nest is done
	 JRST	 NONEST		;(Adjoining, non-overlapping nests must be
	HRRZ	TEMP,(Q1)
	ADD	TEMP,1(Q1)	; moved separately because of full-word reqmt.
	CAMGE	D,TEMP		;Adjust nest-end location, if new string
	 MOVE	 D,TEMP		; extends beyond old nest
	ADDM	A,1(Q1)		;Adjust location count to nest-relative.
	HRR	E,Q1		;Will be last descriptor in nest at NONEST
	JRST	SRCLUP
NONEST:	HRRZM	Q1,.LIST(B)	;Update list, retrieve BP, compute length,
	HRRZS	(E)		;Clear last elt in nest
	HLRZS	E		;Return ptr. to 1st, as advertised
	ADD	D,A		; skip-return as advertised
	POP	P,A
	AOS	(P)
	POPJ	P,
DEST:	MOVE	Q1,D		;SAVE LENGTH
DEST1:	SKIPN	SGLIGN(USER)
	 JRST	 NOLIGN
	PUSHJ	P,INSET			;Inset aligns TOPBYTE to full word,
	PUSH	P,D+1			; but it should already be there really.
	ADDI	D,4			;Move smallest multiple of 5 characters
	IDIVI	D,5			; which hold nest.
	IMULI	D,5
	POP	P,D+1
NOLIGN:	ADDM	D,REMCHR(USER)		;Standard room test
	SKIPGE	REMCHR(USER)
	 JRST	 ISROOM
NOROOM:	PUSHJ	P,WASTE			;Count waste in space being left
	HRRZ	C,.NEXT(C)		;Since we are moving strings "down",
	JUMPE	C,[ERR <DRYROT -- No more room for strings -- very strange>]
	PUSHJ	P,DSTSET		; space is a fatal error.
	JRST	DEST1			;Try again, C, REMCHR, TOPBYTE are adjusted.
ISROOM:	MOVE	FF,TOPBYTE(USER)
	CAME	A,FF			;Avoid moving the nest to its previous
	 JRST	 MVTST			; location (expensive NO-OP).
	 JRST	 MVDON
MVLP:	ILDB	TEMP,A
	IDPB	TEMP,FF
MVTST:	SOJGE	D,MVLP
	MOVE	FF,TOPBYTE(USER)	;FF←BP of first char
MVDON:	MOVSI	A,40			; in destination nest
	MOVE	D,E			;First, adjust TOPBYTE, then
	MOVEI	E,TOPBYTE-1(USER)	; the strings of the nest
	LDB	TEMP,[POINT 3,FF,5]
	TRC	TEMP,4
	JRST	FIXTOP		;Start in middle to get topbyte
FIXLP:	HLRZ	D,(E)
	HLLM	A,(E)		;Update string number
	MOVE	Q1,1(E)		;Compute new BP -- see SUBSTR in STRSER
FIXTOP:	MOVE	T,FF
	ADD	Q1,TEMP
	CAILE	Q1,4
	 JRST	 [CAILE Q1,9
		  JRST	    [IDIVI Q1,5
			     ADD   T,Q1
			     HLL   T,PTBL1(Q2)
			     JRST  PTWY]
		  SUBI  Q1,5
		  AOJA  T,.+1]
	HLL	T,PTBL1(Q1)
PTWY:
	MOVEM	T,1(E)		;Store new BP, to descriptor or topbyte
	MOVE	E,D		;loop
	 JUMPN	 E,FIXLP
	POPJ	P,
DSTSET:	HRLI	C,(<POINT 7,0>)
	MOVEM	C,TOPBYTE(USER)
	MOVN	TEMP,.SIZE(C)
	IMULI	TEMP,5
	MOVEM	TEMP,REMCHR(USER)
	POPJ	P,
WASTE:	PUSH	P,TEMP+1
	MOVN	TEMP,REMCHR(USER)	;Unused characters this space
	IDIVI	TEMP,5			;Just rough estimate.
	POP	P,TEMP+1
	ADDM	TEMP,SGCWASTE(USER)
	POPJ	P,
HEREFK(SGINS,.SGINS)
	PUSH	P,-2(P)		;ADDR OF ROUTINE
	PUSHJ	P,SGREM		;NEVER LET IT BE IN TWICE
	MOVE	USER,GOGTAB
	POP	P,UUO1(USER)
	POP	P,LPSA;<	;=>LINK BLOCK FOR NEW ROUTINE
	POP	P,-1(LPSA)	;PUT ROUTINE ADDRESS AWAY
	HRL	LPSA,SGROUT(USER);GET OLD LINK POINTER
	HLRM	LPSA,(LPSA)	;PUT IN NEW LINK POSITION
	HRRM	LPSA,SGROUT(USER);PUT NEW POINTER IN LINK HEAD
	JRST	@3(P)		;RETURN
HEREFK(SGREM,.SGREM)
	MOVE	USER,GOGTAB
	POP	P,UUO1(USER)
	POP	P,TEMP		;ADDR TO BE REMOVED
	MOVEI	LPSA,SGROUT(USER);HEAD OF LIST
SGRL:	MOVE	USER,LPSA	;PREV←THIS
	SKIPN	LPSA,(USER)	;THIS←(PREV)
	 JRST	 @2(P)		;DIDN'T FIND IT
	CAME	TEMP,-1(LPSA)	;IS THIS THE ROUTINE?
	 JRST	 SGRL		;NO, GET NEXT
	HRRZ	TEMP,(LPSA)	;YES, REMOVE IT FROM LIST
	HRRM	TEMP,(USER)
	JRST	@2(P)
HEREFK(STCLER,.STCLR)			;
	SKIPE	SGLIGN(USER)		;CLEAR REST?
	PUSHJ	P,RESCLR	;CLEAR REST OF STRING SPACE
	SKIPN	T,STRLNK(USER)	;PARALLELS STRNGC'S LOOP
	POPJ	P,		;CLOSELY
	PUSH	P,B		;JUST IN CASE
	HRLZI	B,-1		;FOR TESTING STRING NO.
STC1:	HRRZ	A,-1(T)
	HLRZ	Q2,-1(T)
STCLLP:	SOJL	Q2,STCLD1
	TDNE	B,(A)		;DON'T COLLECT STRING CONSTANTS
	SETZM	(A)
	ADDI	A,2
	JRST	STCLLP
STCLD1:	HRRZ	T,(T)
	JUMPN	T,STC1
	POP	P,B
	POPJ	P,
RESCLR:	SKIPL	A,TOPBYTE(USER)	;CAN ZERO FIRST WORD IF 440700
	ADDI	A,1		;ELSE START AT NEXT
	SETZM	(A)
	HRLS	A
	ADDI	A,1		;BLT WORD
	MOVE	B,STTOP(USER)	;END OF STRING SPACE
	BLT	A,-1(B)		;ZERO!!
	POPJ	P,
INTERNAL BRKMSK
↑BRKMSK:	0
	FOR @& JJ←=17,0,-1 <
	<1 ⊗ (JJ+=18)> + (1 ⊗ JJ)>
>;NOLOW
ENDCOM (SGC)