Google
 

Trailing-Edge - PDP-10 Archives - decuslib20-08 - decus/20-0178/uuocon.mac
There are 20 other files named uuocon.mac in the archive. Click here to see a list.
Subttl	Table of contents for Memory

;	   -- Section --					   -- Page --
;
;  1.   Table of contents............................................. 1
;  2.	 Uuocon module initialization................................. 2
;  3.	 Uuocon routine............................................... 3
;  4.	 Printing module
;	  4.1   %Print routine........................................ 5
;	  4.2   Setjfn routine and dispatch table..................... 6
;	  4.3   Special character subroutines......................... 7
;  5.	 GETJFN routine - Obtain a jfn on a file...................... 10
;  6.	 Crash routine - Handle program fatal errors.................. 12
;  7.	 Memory management
;	  7.1   Memory management symbols............................. 13
;	  7.2   Free and used block formats........................... 14
;	  7.3   %INIMEM - Memory initialization....................... 15
;	  7.4   %GETMEM - get a block of memory....................... 16
;	  7.5   %GIVMEM - return a memory chunk....................... 17
;	  7.6   Addmem - add memory to free list...................... 18
;	  7.7   General subroutines................................... 19
;  8.	 Sorting module
;	  8.1   %Sort (main routine).................................. 20
;	  8.2   Partitioning routine.................................. 21
;  9.	CMD routines
;	  9.1   Cmdini and Prompt..................................... 22
;	  9.2   Rflde, Rfield, Cfield................................. 22
;	  9.3   Noise and Confrm...................................... 23
;	  9.4   Support routines...................................... 23
;  10.	String Routines
;	  10.1   Insert............................................... 24
;	  10.2   Copy and Delete...................................... 25
;
;		     (End of table of contents)
Subttl  Uuocon module initialization

Search	Symbol			; Set up macro definitions
Sall				; Supress expansions

;Version information
Vmajor==2			; Major version
Vminor==2			; Minor version
Vedit==37			; Edit number
Vcust==4			; WCC originated

.TTN.	UUOCON,Local UUO controller

;Author: Douglas Bigelow, Wesleyan Computing Center


;We come here when we have an opcode between 0 and 37, vectored by location
;41 which contains a PUSHJ P,UUOCON.

;Set up the UUO trap

	LOC	41		; Uuo vector location
	CALL	UUOCON		; The instruction
	RELOC			; Relocate to previous context


;External storage
	Extern	UUOTAB
Subttl  Uuocon routine

;Almost all accumulators are respected in these routines, which may use freely
;ACs A-D and T1-T4.  Accumulator A is sometimes used to return arguments,
;so for those routines, A and B are smashed.  Otherwise A and B as well
;as all other ac's must be preserved.

;The following structures refer to the LUUO instruction
DEFSTR	(INSFLD,40,8,9) 	; Instruction field
DEFSTR	(ACFLD,40,12,4) 	; Accumulator field
DEFSTR	(MEMFLD,40,35,18)	; Memory address field


;Beginning of the routine
UUOCON: TRVAR	<<SAVAB,2>,<SAVCD,2>,<SAVT12,2>,<SAVT34,2>>
	DEFINE	RETPC <-1(.fp)> ; Symbol for return address
	DMOVEM	A,SAVAB 	; Save all the acs we use
	DMOVEM	C,SAVCD
	DMOVEM	T1,SAVT12
	DMOVEM	T3,SAVT34
	LOAD	B,MEMFLD	; Get the memory field
	LOAD	C,ACFLD 	; Get the calling ac
	MOVE	A,(C)		; Get the contents of the ac
	CAIN	C,B		; Ac b is special.  is it there?
	 MOVE	A,1+SAVAB	; Yes, get the value
	CAIN	C,A		; Ac a is special too
	 MOVE	A,0+SAVAB	; Get that value
	CAIN	C,C		; Finally, check for ac c
	 MOVE	A,0+SAVCD	; Get value from ac c
	LOAD	C,INSFLD	; Get instruction code
	CAIL	C,T%%MIN##	; If too low
	 CAILE	C,T%%MAX##	; Or too high?
	  JRST	ILLUUO		; Illegal value
	SUBI	C,T%%MIN##	; Convert to dispatch offset
	CALL	@UUOTAB(C)	; Call the routine

UUO.Z:	 DMOVE	A,SAVAB 	; Non-skip return means restore a and b
	DMOVE	C,SAVCD 	; Restore other acs
	DMOVE	T1,SAVT12
	DMOVE	T3,SAVT34
	RET			; And return to caller
;Here if the instruction was illegal

;If the interrupt system is enabled, this routine will generate an interrupt
;on the illegal instruction code panic channel.  Otherwise it will just cause
;an error message to print out and the call will be ignored.

ILLUUO: MOVEI	A,.FHSLF	; Our handle
	MOVX	B,II%ILI	; Illegal instruction code
	IIC%			; Initiate an interrupt
	PRGERR	(Illegal instruction code,CONT)
	JRST	UUO.Z		; Restore ACs and return


;Here for unimplemented instructions

ENTRY	%NOTIN
%NOTIN: PRGERR	(<Illegal use of non-included UUO function>,RETN)
Subttl  Printing module -- %Print routine

;Call with A containing parameter to print, if applicable.
;B contains the address of the string to print.

;JFN storage goes into data segment
;STG	(P..JFN)		; Holds current output jfn
Extern	P..JFN			; Defined in host program

ENTRY	%PRINT
%PRINT: TRVAR	<<P..STR,100>>	; Storage for string
	MOVE	T3,A		; Save value for later use
	HRRZ	T1,B		; Get location
	HRLI	T1,(POINT 7,)	; Make a byte pointer
	BPM	T2,P..STR	; Destination byte pointer

;Loop reading and writing bytes
PR.A:	ILDB	B,T1		; Get a byte
	CAIN	B,"%"		; Is it special flag?
	 JRST	PR.B		; Nope
	IDPB	B,T2		; Deposit byte
	JUMPN	B,PR.A		; Loop until null

;Here on the end of the string
	SKIPN	A,P..JFN	; Jfn set up?
	 MOVEI	A,.PRIOU	; Nope, use primary
	HRROI	B,P..STR	; Point to string
	SETZ	C,		; End on a null
	SOUT%			; Send it
	RET			; And return

;Here when we found a percent sign
PR.B:	ILDB	B,T1		; Get following symbol
	CAILE	B,"Z"		; Lower case?
	 SUBI	B,40		; Yes, convert to upper
	SUBI	B,"A"		; Remove ascii offset
	JUMPL	B,PR.A		; Negative not allowed
	CAILE	B,"Z"		; Neither is anything too high
	 JRST	PR.A		; Reject
	CALL	@PRITAB(B)	; Call proper routine
	JRST	PR.A		; Loop
Subttl  Printing module -- Setjfn routine and dispatch table

;Setjfn - sets up the output jfn for the print routines.  Call with
;B containing the jfn to use.

ENTRY	%SETJF
%SETJF: MOVEM	B,P..JFN	; Set the jfn
	RET			; And return

;Dispatch table follows

DEFINE	DISTAB(CH..),<
	IRPC	CH..,<EXP TAB.'CH..>
>

;Sample:	(ABCDEFGHIJKLMNOPQRSTUVWXYZ)
PRITAB: DISTAB	($BCDE$$$$J$$$NOPQ$STUV$$$$)
Subttl  Printing module -- Special character subroutines

;These routines all have labels of the form TAB."X", where "X" is the
;letter which involked the routine.  All unimplemented letters trap to
;the routine TAB.$

;Unimplemented functions
TAB.$:	RET			; Return

;Type a crlf
TAB.C:	MOVEI	A,15		; Cr
	IDPB	A,T2		; Deposit it
	MOVEI	A,12		; Lf
	IDPB	A,T2		; Deposit
	RET			; Return

;Type the time of day
TAB.D:	MOVE	A,T2		; Get pointer
	SETO	B,		; Current time
	SETZ	C,		; Regular format
	ODTIM%			; Type it
	MOVE	T2,A		; Regain new byte pointer
	RET			; And return

;Print out the last error encountered by the process
TAB.E:	MOVE	A,T2		; Get pointer
	HRLOI	B,.FHSLF	; This fork
	SETZ	C,		; No limit
	ERSTR%			; Get last error
	 JFCL
	 JFCL			; Ignore error returns
	MOVE	T2,A		; Restore string pointer
	RET			; And return

;Print JFN information for a file
TAB.J:	MOVE	A,T2		; Destination
	MOVE	B,T3		; The jfn
	SETZ	C,		; Standard format
	JFNS%			; Type it
	MOVE	T2,A		; Get pointer back
	RET			; And return

;; continued on next page
;Type a number in binary [PBG 19-Jan-82]
TAB.B:	MOVEI	C,2		; Binary
	JRST	TAB.O+1 	; Jump to output routine

;Type a number in decimal
TAB.N:	MOVEI	C,12		; Decimal
	CAIA			; Skip

;Type a number in octal
TAB.O:	MOVEI	C,10		; Octal
	MOVE	B,T3		; Get the number
	MOVE	A,T2		; Get pointer
	NOUT%			; Type it
	 RET			; Can't
	MOVE	T2,A		; Restore pointer
	RET			; And return

;Type a percent sign
TAB.P:	MOVEI	A,"%"		; Get it
	IDPB	A,T2		; Deposit it
	RET			; And return

;Initialize for an error message
TAB.Q:	MOVEI	A,.CTTRM	; Controlling terminal
	CFIBF%			; Clear input buffer
	DOBE%			; Wait for output to complete
	MOVE	A,T2		; Get string pointer
	HRROI	B,[BYTE (7) 15,12,"?",40,0]
	SETZ	C,		; No limit
	SOUT%			; Append string
	MOVE	T2,A		; Get revised pointer
	RET			; Done

;Type out a sixbit word
TAB.S:	MOVEI	C,6		; Max 6 characters
	MOVE	B,[POINT 6,T3]	; Sixbit pointer
TAB.S1: ILDB	A,B		; Get a byte
	JUMPE	A,TAB.S2	; Ignore spaces
	ADDI	A,40		; Convert to ascii
	IDPB	A,T2		; Deposit it
TAB.S2: SOJG	C,TAB.S1	; Loop for all six
	RET			; And return

;Type a tab
TAB.T:	MOVEI	A,11		; Get it
	IDPB	A,T2		; Deposit it
	RET			; And return

;Type out a user or directory name
TAB.U:	MOVE	A,T2		; Get pointer
	MOVE	B,T3		; Get number
	DIRST%			; Type it out
	 RET			; If error
	MOVE	T2,A		; Restore pointer
	RET			; And return
;Type out the version number of the calling program
TAB.V:	MOVEI	A,.FHSLF	; Our handle
	GEVEC%			; Get entry vector
	HLRZ	A,B		; Isolate length
	HRRZS	B		; And address
	CAIGE	A,(JRST)	; Jrst means old style
	CAIGE	A,3		; Less than 3 means old style
	 SKIPA	T4,137		; So copy version
	MOVE	T4,2(B) 	; Otherwise copy new style version
	MOVE	A,T2		; Copy pointer
	LDB	B,[POINT 9,T4,11]	; Get major version
	MOVEI	C,10		; Octal
	NOUT%
	 JFCL
	LDB	D,[POINT 6,T4,17]	; Get minor version
	JUMPE	D,TAB.V1	; If no minor, don't print it
	MOVEI	B,"."		; A period
	BOUT%			; Print it
	MOVE	B,D		; Get the number
	NOUT%			; Write it
	 JFCL
TAB.V1: MOVEI	B,"("		; Seperator
	BOUT%
	LDB	B,[POINT 18,T4,35]	; Get edit
	NOUT%
	 JFCL
	MOVEI	B, ")"		; End seperator
	BOUT%
	LDB	D,[POINT 3,T4,2]	; Get cust
	JUMPE	D,TAB.V2	; Return if zero
	MOVEI	B,"-"		; Now the vcust
	BOUT%
	MOVE	B,D		; Get the number
	NOUT%
	 JFCL
TAB.V2: MOVE	T2,A		; Copy back the pointer
	RET			; Return
Subttl  GETJFN routine - Obtain a jfn on a file

;This routine finds a file in either DSK:, your login area, or both in
;either order.

ENTRY	%GETJF
%GETJF:TRVAR	<<TMPBF,30>>	; Get some storage
	LOAD	T1,ACFLD	; Get calling ac
	MOVE	T2,B		; Store the string address
	CAIN	T1,4		; Output file?
	 JRST	GTJ.2		; Yes
	CAIG	T1,1		; Code 0 or 1?
	 JRST	GTJ.1		; Yes
	CALL	GT.LOG		; 2 or 3, try login area first
	 RETSKP 		; Success
	CAIN	T1,3		; Is it log: area only?
	 JRST	GTJ.3		; Yes, quit

;Here to try DSK: first
GTJ.1:	CALL	GT.DSK		; Try disk area
	 RETSKP 		; Success
	JUMPN	T1,GTJ.3	; Return if not code 0
	CALL	GT.LOG		; Try login area
	 RETSKP 		; Success
	JRST	GTJ.3		; Failure

;Here to open an output file
GTJ.2:	CALL	GT.LOG		; Try login area only
	 RETSKP 		; Success
	JRST	GTJ.3		; Failure

;Here for failure return
GTJ.3:	SETZ	A,		; No jfn returned
	RETSKP			; Return
;Routine to test for file in default disk area
GT.DSK: MOVX	A,GJ%OLD!GJ%SHT ; Old file only
	HRRO	B,T2		; Get the string
	GTJFN%			; Get a jfn
	 RETSKP 		; Failure
	RET			; Success, jfn in a

;Routine to test for file in LOGIN area
GT.LOG: HRROI	A,TMPBF 	; Point to destination
	HRROI	B,[ASCIZ .PS:<.]	; Initial string
	SETZ	C,		; End on null
	SOUT%			; Transfer it
	MOVE	T3,A		; Save the pointer
	GJINF%			; Get our job info
	MOVE	B,A		; User number into b
	MOVE	A,T3		; Byte pointer
	DIRST%			; Write out our directory
	 RETSKP 		; Return without success
	MOVEI	B,">"		; Directory terminator
	IDPB	B,A		; Tack it on
	HRRO	B,T2		; Get filename string
	SETZ	C,		; Copy until null
	SOUT%			; Transfer it
	MOVX	A,GJ%SHT!GJ%OLD ; File flags
	CAIN	T1,4		; Output file?
	 MOVX	A,GJ%SHT!GJ%FOU ; Yes, set different flags
	HRROI	B,TMPBF 	; Point to buffer
	GTJFN%			; Get a jfn
	 RETSKP 		; Can't
	RET			; Success
Subttl  Crash routine - Handle program fatal errors

;Uses the Jsys acs, and restarts the program at the starting address
;if continued.

ENTRY	%CRASH
%CRASH: BPL	A,< Error at PC >
	ESOUT			; Type string with accompanying effects
	MOVEI	A,.PRIOU	; Primary output
	HRRZ	B,RETPC 	; Location we came from
	SUBI	B,2		; Reset to point at location in error
	MOVEI	C,10		; In octal
	NOUT%			; Type the pc
	 JFCL			; If we can't, give up
	TYPNCR	< -- >		; Spacer
	MOVEI	A,.PRIOU	; Primary output again
	HRLOI	B,.FHSLF	; This fork,,last error
	SETZ	C,		; No limit
	ERSTR%			; Type last error string
	 JFCL			; Ignore errors
	 JFCL			; Ignore errors
	TYPE	<>		; End with a crlf
	HALTF%			; Halt this fork

;Here if continued
	HRRZ	A,120		; Starting address
	JRST	(A)		; Go to it


;End of printer module

	PRGEND
Subttl  Memory management -- Memory management symbols

Title	Memory - Memory management section
Search	Symbol,Macsym,Monsym
Sall

;Symbol definitions used in storage management routines
Extern MEMLNK,MEMBLK		; Define in host program
LNK==T4 			; Link register
PROSIZ==3			; Size of prototype memory block
MINSIZ==4			; Minimum size of requested block
FSTFRE==121			; First free location
TAGVAL==123456			; Block used tag

;Program storage goes into data segment
;STG	(MEMLNK)		; Link to first block
;STG	(MEMBLK,PROSIZ) 	; Prototype memory block

;Structures offset by value of LNK
DEFSTR	(TAG,(LNK),17,18)	; Tag field
DEFSTR	(SIZE,(LNK),35,18)	; Size of block
DEFSTR	(LAST,1(LNK),17,18)	; Backward link
DEFSTR	(NEXT,1(LNK),35,18)	; Forward link

;Structures where offset must be added
DEFSTR	(XTAG,0,17,18)		; Tag (no index)
DEFSTR	(XSIZE,0,35,18) 	; Size (no index)
DEFSTR	(XLAST,1,17,18) 	; Backward link (no index)
DEFSTR	(XNEXT,1,35,18) 	; Forward link (no index)
Subttl  Memory management -- Free and used block formats

;			   Format of free storage block
;
;
;			  1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3
;     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
;    |-----------------------------------------------------------------------|
;+0  |	       Tag field (565656)	 |	     Size of block	     |
;    |-----------------------------------------------------------------------|
;+1  |		 Backward link		 |	      Forward link	     |
;    |-----------------------------------------------------------------------|
;+2  |				     Free space 			     |
;    |	 -   -	 -   -	 -   -	 -   -	 -   -	 -   -	 -   -	 -   -	 -   |
;    |				 ( M  word block )			     |
;    |-----------------------------------------------------------------------|
;+N  |	       Tag field (565656)	 |	     Size of block	     |
;    |-----------------------------------------------------------------------|
;


;			   Format of used storage block
;
;
;			  1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3
;     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
;    |-----------------------------------------------------------------------|
;+0  |		    (Unused)		 |	     Size of block	     |
;    |-----------------------------------------------------------------------|
;+1  |		    Free space (First location known to caller) 	     |
;    |	 -   -	 -   -	 -   -	 -   -	 -   -	 -   -	 -   -	 -   -	 -   |
;    |				 ( M  word block )			     |
;    |-----------------------------------------------------------------------|
;
Subttl  Memory management -- %INIMEM - Memory initialization

;No arguments - call when all memory manipulation is complete (all desired
;program segments have been called, etc.)

ENTRY	%INIME
%INIME: MOVEI	LNK,MEMBLK	; Prototype memory block
	MOVEM	LNK,MEMLNK	; First link
	MOVEI	A,TAGVAL	; Get free block tag
	STOR	A,TAG		; Store it
	MOVEI	A,PROSIZ	; Size of block
	STOR	A,SIZE		; Store it
	STOR	LNK,LAST	; Set back link to itself
	STOR	LNK,NEXT	; And next link too

;Now we add a chunk of memory to the end of our core image
	CALL	ADDMEM		; Add a k
	 JRST	NOMORE		; Can't, machine size exceeded
	RET			; Return to caller, restoring A and B

;Here if requested space can't be obtained
NOMORE: MOVEI	A,.FHSLF	; Our handle
	MOVEI	B,.ICMSE	; Machine size exceeded
	IIC%			; Initiate an interrupt
	PRGERR	(Machine size exceeded,CONT)
	RET			; Return if we didn't die
Subttl  Memory management -- %GETMEM - get a block of memory

;Call with B containing number of words desired.
;Returns with A containing address of block returned.

ENTRY	%GETME
%GETME: MOVEI	A,1(B)		; Real number of words requested
	MOVE	LNK,MEMLNK	; Get first link
	CAIGE	A,MINSIZ	; Minimum size of block
	 MOVEI	A,MINSIZ	; Set if necessary
	JRST	M.GE.B		; Skip first part

;Here we get the next link and make sure that we haven't circled around
M.GE.A: LOAD	LNK,NEXT	; Get next link
	CAME	LNK,MEMLNK	; Where we started?
	 JRST	M.GE.B		; Nope, keep looking
	CALL	ADDMEM		; Yes, try adding more memory
	 JRST	NOMORE		; No more memory!
	MOVE	LNK,MEMLNK	; Restart the search
M.GE.B: LOAD	T3,SIZE 	; Get size of block
	CAMGE	T3,A		; Size big enough?
	 JRST	M.GE.A		; Nope, try again
	PUSH	P,A		; Save size for later use
	MOVE	A,LNK		; Get address in a for unlink routine
	LOAD	T1,NEXT 	; Get next link
	MOVEM	T1,MEMLNK	; Reset the memory link
	CALL	UNLINK		; Unlink the block from the chain
	CALL	CLEAR		; Clear the block
	MOVEI	A,1(LNK)	; Caller's start of block is one past real
	POP	P,T2		; Get back size requested
	LOAD	T1,SIZE 	; Get size received
	SUB	T1,T2		; Get difference
	CAIG	T1,MINSIZ	; Enough to bother with?
	 RETSKP 		; Nope, toss it in as an extra
	STOR	T2,SIZE 	; Set new size of requested block
	ADD	LNK,T2		; Point to overage
	STOR	T1,SIZE 	; Store size of new block
	PUSH	P,A		; Save info we're returning
	CALL	M.GIVX		; While we return the extra chunk
	POP	P,A		; Restore info
	RETSKP			; Return to caller with A set up
Subttl  Memory management -- %GIVMEM - return a memory chunk

;Call with B containing the block of memory to be returned.  Note that only
;a block obtained from %GETMEM can be returned via %GIVMEM, and the whole bloc
;must be returned, rather than just a part.

ENTRY	%GIVME
%GIVME: MOVEI	LNK,-1(B)	; Get true block address
M.GIVX: MOVEI	A,1(LNK)	; Also make sure a is set
	MOVE	T1,-1(LNK)	; Get contents of previous word
	HLRZ	T2,T1		; Get tag field
	CAIE	T2,TAGVAL	; Is it a free block tag?
	 JRST	M.GI.A		; Nope, check the following chunk
	SUBI	A,1(T1) 	; Point to previous block
	HLRZ	T2,(A)		; Get tag field
	CAIE	T2,TAGVAL	; Is it another tag?
	 JRST	M.GI.A		; Nope, give up
	CALL	UNLINK		; Unlink the previous block
	EXCH	LNK,A		; Make link point to previous block
	CALL	COLLAP		; Collapse the blocks

;Here we attempt to tack on the following block
M.GI.A: LOAD	A,SIZE		; Get size of block
	ADD	A,LNK		; Get address of following block
	HLRZ	T2,(A)		; Get tag field
	CAIE	T2,TAGVAL	; A tag field?
	 JRST	M.GI.B		; Nope, quit
	LOAD	T1,XSIZE,(A)	; Get size of block
	ADDI	T1,-1(A)	; Add it's address
	HLRZ	T2,(T1) 	; Get tag field
	CAIE	T2,TAGVAL	; Match?
	 JRST	M.GI.B		; Nope, give up
	CALL	UNLINK		; Unlink the block pointed to by a
	CALL	COLLAP		; Yes, collapse the blocks

;Here when we're ready to link in the new block
M.GI.B: MOVE	A,MEMLNK	; Get link word for start of list
	STOR	A,LAST		; Store as new block's last link
	LOAD	T1,XNEXT,(A)	; Get next link
	STOR	T1,NEXT 	; Store as new block's next link
	STOR	LNK,XNEXT,(A)	; Store our link as last block's next link
	STOR	LNK,XLAST,(T1)	; Store our link as next block's last link
	MOVEM	LNK,MEMLNK	; Store us as the first block in chain
	MOVEI	T1,TAGVAL	; Get tag value
	STOR	T1,TAG		; And store it
	LOAD	T1,SIZE 	; Size of block
	ADDI	T1,-1(LNK)	; Point to last word
	MOVE	T2,(LNK)	; Get block header
	MOVEM	T2,(T1) 	; Make it block trailer, too
	RET			; Finally return to caller, restoring A and B
Subttl  Memory management -- Addmem - add memory to free list

;Subroutine to add memory at the end of our core image to the free list.
;Error return is taken if no more memory available.

ADDMEM: HRRZ	A,FSTFRE	; Get first free memory location
	ADDI	A,777		; Bump up to next page (maybe)
	LSH	A,-^D9		; Convert to page number
	HRLI	A,.FHSLF	; Fork handle in left
	RPACS%			; Read page access
	TXNE	B,PA%PEX	; Existing page?
	 RET			; Yes, can't add it
	HRRZ	LNK,A		; Get page number
	LSH	LNK,^D9 	; Make into address again
	ADDI	LNK,777 	; And get to last word in page
	SETZM	(LNK)		; Clear last word
	MOVE	T1,LNK		; Get last word in block + 1
	EXCH	LNK,FSTFRE	; Set new first free word, get start of block
	SUB	T1,LNK		; Get length of block
	STOR	T1,SIZE 	; Store it in block
	CALL	M.GIVX		; Add to the free pool
	RETSKP			; And return
Subttl  Memory management -- General subroutines

;Subroutine to collapse two adjacent blocks.
;Call with LNK containing address of first block, and T1 containing size
;of block being appended.

COLLAP: LOAD	T1,SIZE 	; Get size of block
	ADDI	T1,(LNK)	; Point to next
	HRRZ	T2,(T1) 	; Get size of next
	ADDM	T2,(LNK)	; Add in size of new block
	RET			; Return


;Subroutine to unlink a block from the queue.
;Call with A containing address of block to unlink.

UNLINK: LOAD	T1,XLAST,(A)	; Get last block
	LOAD	T2,XNEXT,(A)	; Get next block
	STOR	T1,XLAST,(T2)	; Store back link in forward link's back link
	STOR	T2,XNEXT,(T1)	; Store next link in backward link's next link
	CAMN	A,MEMLNK	; Have we unlinked the first block?
	 MOVEM	T2,MEMLNK	; Yes, must provide another
	RET			; Return to caller


;Subroutine to clear a memory block
;Call with LNK pointing to the block

CLEAR:	HRLI	T1,1(LNK)	; First word to clear
	HRRI	T1,2(LNK)	; Second
	SETZM	1(LNK)		; Clear it
	LOAD	T2,SIZE 	; Size of block
	ADDI	T2,-1(LNK)	; Add address to yield last word of block
	BLT	T1,(T2) 	; Clear the block
	RET			; And return


;End of memory management segment

	PRGEND
Subttl  Sorting module -- %Sort (main routine)

Title	Sorter -- Sorting module
Search	Symbol,Macsym,Monsym
Sall

;Call with parameters:
;	T1 is address of start of array
;	T2 is address of end of array
;All acs are respected
;Uses Quicksort algorithm to sort array into descending order.

ENTRY	%SORT
%SORT:	STKVAR	<<KEEP,2>,<CONT,2>>	; Storage
	DMOVEM	A,KEEP		; Store acs
	DMOVE	T1,A		; Also move them
	MOVX	A,.INFIN	; Plus infinity
	EXCH	A,-1(T1)	; Store in array start-1
	MOVX	B,.MINFI	; Minus infinity
	EXCH	B,1(T2) 	; Store in array end+1
	DMOVEM	A,CONT		; Store them
	CALL	QSORT		; Quicksort the array
	DMOVE	T1,KEEP 	; Restore acs
	DMOVE	A,CONT		; Get values
	MOVEM	A,-1(T1)	; Restore first
	MOVEM	B,1(T2) 	; And last
	RET			; Return

;Qsort - a recursive sorting algorithm

QSORT:	STKVAR	<II,JJ,KK>	; Local storage
	MOVEM	T1,II		; Store variables
	MOVEM	T2,JJ		; ...
	CAML	T1,T2		; I<j?
	 RET			; Nope, exit from qsort
	CALL	PART		; Partition the array from i to j
	MOVEM	T1,KK		; Store returned value
	SOS	T2,T1		; End point into t2
	MOVE	T1,II		; Beginning point into t1
	CALL	QSORT		; Quicksort this part of array
	AOS	T1,KK		; Get k+1 value
	MOVE	T2,JJ		; End of array
	CALL	QSORT		; Quicksort this part of array
	RET			; And return
Subttl  Sorting module -- Partitioning routine

;This algorithm partitions the data and returns an index to the array such
;that everything before the partition is greater than the partition value, and
;everything beyond is less.

PART:	MOVEI	A,1(T1) 	; First index
	MOVE	B,T2		; Second
	MOVE	C,(T1)		; Partition value

PART.1: CAMLE	A,B		; A greater than b?
	 JRST	PART.4		; Yes, end

PART.2: CAMG	C,(A)		; Compare value
	 AOJA	A,PART.2	; Loop until partition value is higher
PART.3: CAMLE	C,(B)		; Compare value
	 SOJA	B,PART.3	; Loop until partition value is exceeded

	CAML	A,B		; Low index below high index?
	 JRST	PART.4		; Nope, don't switch
	MOVE	T3,(A)		; Switch array values
	EXCH	T3,(B)		; ...
	MOVEM	T3,(A)		; ...
	AOJ	A,		; Bump low index
	SOJ	B,		; Decrement low index
	JRST	PART.1		; And loop

PART.4: MOVE	T3,(T1) 	; Now switch array values
	EXCH	T3,(B)		; ...
	MOVEM	T3,(T1) 	; ...
	MOVE	T1,B		; This is the parameter returned
	RET			; Finished


;End of sorter module

	PRGEND
Subttl	CMD routines -- Cmdini and Prompt

Title	Comand - Comnd% jsys routines
Search	Symbol,Macsym,Monsym
Sall

Extern	SBK,CMDBUF,ATMBUF,CMDACS,CJFNBK,REPARA,CMBLT1,CMBLT2
Extern	CMDPDL,CMDBLN,CMDPLN,ATMBLN

;Special definitions
;** This must match the trvar at UUOCON:.
	TRVAR	<<SAVAB,2>,<SAVCD,2>,<SAVT12,2>,<SAVT34,2>>
	DEFINE	RETPC <-1(.fp)> ; Symbol for return address

;Initial state block for cmdini
inisbk: z,,repars		; .cmflg -- no flags,, where to reparse
	.priin,,.priou		; .cmioj -- i/o
	point 7,[z]		; .cmrty -- default is null prompt
	point 7,cmdbuf		; .cmbfp -- where our buffer is
	point 7,cmdbuf		; .cmpnt -- first input field
	cmdbln*5		; .cmcnt -- max number of chars
	z			; .cminc -- no unparsed chars yet
	point 7,atmbuf		; .cmabp -- where atom buffer is
	atmbln*5		; .cmabc -- size of atom buffer
	cjfnbk			; .cmgjb -- location of gtjfn block


;Cmdini -- initialize CMD package
;Argument is IOWD pointer to stack.
;(Must be reinitialized if a new stack is used.)

ENTRY	%CMDIN
%CMDIN: move	a,[inisbk,,sbk] ; Blt pointer
	blt	a,sbk+.cmgjb	; Copy initial state block to state block
	ifn.	b		; Is there an argument?
	 move	a,(b)		; Yes, get argument
	else.			; Nope, make a best guess
	 move	a,.fp		; By assuming that stack bottom
	 sub	a,[2,,2]	; Was the calling address
	endif.
	hlre	b,a		; Get the size field
	movns	b		; Negate it
	caig	b,cmdpln	; Is there enough room?
	ifskp.			; Nope, time to complain
	 movei	a,.fhslf	; Cause an interupt on the push-down
	 movx	b,ii%pov	; Overflow panic channel
	 iic%			; If continued, exlain
	 prgerr (CMDPLN too small to save stack,EXIT)
	endif.
	movsi	b,1(a)		; We're ok, make blt pointer
	hrri	b,cmdpdl	; stak,,cmdpdl
	movem	b,cmblt1	; Our first pointer
	movni	b,(a)		; Now figure how stack to save
	addi	b,cmdpdl	; -<stak-1>+cmdpdl
	hrli	b,p		; 1-stak+cmdpdl(p)
	movem	b,cmblt2	; Which is our second pointer
	ret			; Done

;Prompt -- start new command line
;Possible arguments in memory field
;	address of asciz string to use as prompt, or
;	0 for no prompt at all (null string), or
;	-1 to reuse previous prompt (same as 0 if first call)

ENTRY	%PROMP
%PROMP: skipn	b		; If argument is zero
	 movei	b,[z]		; Then prompt is a null string
	caie	b,-1		; If argument isn't minus one
	 hrrom	b,sbk+.cmrty	; Then it's the new prompt's address
	dmove	a,savab 	; Get saved a and b
	move	c,savcd 	; Get saved c
	dmovem	f,cmdacs+f	; Save two acs
	move	a,[b,,cmdacs+b] ; Blt pointer
	blt	a,cmdacs+16	; Save most of the acs
	movei	a,retpc 	; Get address of top of stack
	movem	a,cmdacs+p	; Save last ac
	move	a,cmblt1	; Save the stack
	blt	a,@cmblt2
	move	a,retpc 	; Get called address
	movem	a,repara	; This is our reparse address
	movei	b,[flddb. .cmini]
	call	field		; Go type the prompt
	 jfcl			; Shouldn't happen
	ret			; Done

Subttl	CMD routines -- Rflde, Rfield, Cfield

;Field parsing routines
;Argument is address of function discriptor block.
;Returns with these results in acs 1 and 2
;ac1/	LH:	flags from cmd state block.
;	RH:	address of function block used to parse command.
;ac2/		data returned by COMND% jsys,
;		or error code if rflde is returning an error.

;rflde -- parse a field, skip iff successful

ENTRY	%RFLDE
%RFLDE: call	field		; Try to parse
	ifskp.	(aos retpc)	; Pass skip down the line
	hrr	a,c		; Copy func block pointer
	retskp			; Return, keep new a and b


;rfield -- parse a field, proccess any errors.
;	Returns only on success.

ENTRY	%RFIEL
%RFIEL: call	field		; Parse the field
	 jrst	cmderr		; Failed, go proccess error
	hrr	a,c		; Copy function block pointer
	retskp			; Return, keep new a and b

;cfield -- same as rfield, but confirm afterwards

ENTRY	%CFIEL
%CFIEL: call	field		; Parse the field
	 jrst	cmderr		; Failed, go process error
	dmovem	a,savab 	; Store returned values
	call	%confr		; Confirm
	ret			; Done
Subttl	CMD routines -- Noise and Confrm

;noise -- parse (generate) a noise word
;Argument is address of string to type

ENTRY	%NOISE
%NOISE: move	t2,b		; Get the string address
	tlo	t2,-1		; Convert to pointer
	movx	t1,fld(.cmnoi,cm%fnc)
	movei	b,t1		; Function block is t1-t2
	call	field		; Go parse (type) the string
	 jfcl			; This shouldn't happen
	ret			; Done

;confrm -- get confirmation
;No argument

ENTRY	%CONFR
%CONFR: movei	b,[flddb. .cmcfm]
	call	field		; Get confirmation
	 jrst	cmderr		; Didn't confirm, go complain
	ret			; Done


subttl	CMD routines -- Support routines

;field -- parsing routine, does actual COMND% jsys
;b/	address of function block
field:	movei	a,sbk		; Get address of state block
	comnd%			; The real thing!
	 erjmp	r		; Single return on errors
	txne	a,cm%nop	; Did it parse?
	 ret			; Nope, single return
	retskp			; Success, skip return

;cmderr -- handles cmd errors
cmderr: hrroi	a,asc(Command error -- )
	esout%
	movei	a,.priou	; To primary output
	hrloi	b,.fhslf	; Most recent error
	setz	c,		; Any length
	erstr%			; Standard error message
	 jfcl
	 jfcl
	sos	repara		; Make reprompt happen
				; (fall into repars)

;repars -- where COMND% jsys jumps to for reparse
repars: move	p,cmdacs+p	; Get old stack pointer
	movs	a,cmblt1	; Restore the stack
	blt	a,(p)
	movsi	16,cmdacs	; Restore rest of the acs
	blt	16,16
	jrstf	@repara 	; Go reparse or reprompt

	PRGEND			; End of comand module
Subttl	String Routines -- Insert

Title	String - String manipulation routines
Search	Symbol,Macsym,Monsym
Sall

Extern	S1,S2			; External string pointer storage

ENTRY	%Inser

; Here we insert either AC B blanks into S1, or B characters from the pointer
; specified in A into S1.  If B is zero, we just insert spaces.
; The string in S1 is assumed to end in a null.

%Inser: move	t1,s1+1 	; Get the pointer to the string
	move	p1,a		; The counter is in p1
	move	p2,@b		; Save the possible pointer
	move	p3,s1		; Save the current pointer

; Now loop, looking for the string end:
	ildb	a,t1		; Get a character
	jumpn	a,.-1		; If not the null, get the next one

; Now we're at the end of the string, insert AC A blanks into the string
	move	a,p1		; Get the character count
	adjbp	a,t1		; Adjust our byte pointer that many...into a
	movem	a,s1		; And save it as our returned pointer
	movei	c,40		; Put a space into c
.inlup.:movem	a,t2		; Now t1 has the old end, t2 has the new end
	camn	t1,p3		; Are we at the word for insertion?
	 jrst	.inin.		; Yes, do the insertion
	ldb	a,t1		; Get a character from the end
	dpb	c,t1		; Write over it with a space
	dpb	a,t2		; Deposit it to the new end
	seto	a,		; Get a negative one
	adjbp	a,t1		; Move the pointer back one
	move	t1,a		; Get the pointer back into t1
	seto	a,		; Negate a again
	adjbp	a,t2		; Move the new end back one
	jrst	.inlup. 	; And loop

; Here when we've inserted the proper number of spaces into the string.
; If we have a pointer, copy the characters, if not, return:
.inin.: skipn	p2		; Do we have a pointer to string to insert?
	 ret			; No, just return then
	ildb	a,p2		; Yes, get a character
	idpb	a,p3		; And move it
	sojg	p1,.-2		; Loop until done
	ret			; When done transfering, just return
Subttl	String Routines -- Copy and Delete

ENTRY	%Copy

; Copy loops, copying from S1 to S2, stopping when it reaches the character in
; B or the number of times in A.

%Copy:	ildb	c,s1		; Get a char from the source string
	idpb	c,s2		; No, copy the character to the destination
	camn	b,c		; Is it the one we're looking for?
	 ret			; Yes, return, restoring all AC's
	jumpl	a,%copy 	; If counter is negative, get the next char
	sojg	a,%copy 	; Decriment counter...if still positive, loop
	ret			; If zero, we've done enough...return


ENTRY	%Delet

; Here to delete the number of characters in ac B starting at the char pointed
; to by S1, or until we come to the character in ac A:

%Delet: move	t3,b		; Get the number of characters to copy
	jumpe	a,.dnum.	; If the character to stop on is null, just
				; copy the specified number

; Here to delete until we reach a specified character.	First we see how many
; characters from the current pointer the specified character is:
	setz	t3,		; Clear our char counter
	move	t4,s1		; Copy the pointer
	ildb	t1,t4		; Get a character
	aos	t3		; Increment our counter
	caml	t3,a		; Are we still below the max given?
	 jrst	.+3		; No, stop looking
	came	t1,b		; Is it the character we're looking for?
	 jrst	.-5		; No, look at the next one

; Here when we've found the character.	Delete the number of chars in t3:
.dnum.: setz	d,		; Put a null in D
	adjbp	t3,s1		; Point to the next character to keep
	move	t4,s1		; Now get the original
.delch.:ldb	a,t3		; Get the next character to keep
	idpb	d,t3		; Write over it with a null
	dpb	a,t4		; Save it in t4
	skipe	a		; A null?
	 ret			; Yes, return, deletion complete
	ibp	t4		; Increment the byte pointer
	jrst	.delch. 	; Loop until we reach a null

ENTRY	%FIND

;  Find The letter C in the string pointed to by Ac. The pointer
;    is set to char found, or if not found, pointer is at end of
;    string.  The pointer is returned in A, and last character is
;    in B:

%Find:	ildb   t1,a		; Get a character
	camn   b,t1		; Is it the one we're looking for?
	 retskp 		; Yes, return to caller
	jumpn  t1,%find 	; If not a null, look at the next character
	retskp			; If it is a null, return


;End of subroutine package
	END			; No start address specified