Google
 

Trailing-Edge - PDP-10 Archives - steco_19840320_1er_E35 - 10,5676/teco/newsrc/tecupd.mac
There are 3 other files named tecupd.mac in the archive. Click here to see a list.
	SUBTTL	Introduction

; Copyright (c) 1980, 1981 Stevens Institute of Technology, Hoboken, New Jersey
; 07030.

; This software may be used and copied provided that this copyright notice
;is included, and provided that copies of all modifications are sent to:
;
;	TECO Project
;	Computer Center
;	Stevens Institute of Technology
;	Castle Point Station
;	Hoboken, New Jersey    07030
;
;
; The information in this software is subject to change without notice
; and should not be construed as a commitment by Stevens Institute of
; Technology.

  ; Search needed universals

	SEARCH	TECUNV		; TECO universal file

  ; Generate the prologue


	TECVER==200		; Major version number
	TECMIN==1		; Minor version number
	TECEDT==1166		; Edit level
	TECWHO==0		; Last editor


	PROLOGUE(UPD,<TECO Video update processing>)	; Generate the TITLE and other stuff
	SUBTTL	Table of Contents
	SUBTTL	Revision History
COMMENT	|


1000	Start of this version

1005	By: Nick Bush		On: 21-July-1980
	Add capability for scrolling the screen downwards on terminals with
	the function. This currently works only for VT-52's, but provides
	the necessary support for any terminal with a scrolling region.
	Modules: TECDEC,TECVID

1007	By: Nick Bush		On: 30-July-1980
	1) Make CHKTYI work a little better on slow speed (<=600 baud) 
	   terminals. It will now wait until the output buffer is empty before
	   returning the no input return.
	
	2) Put the cursor in a better position to indicate the location of PT
	   when PT = Z. The only time the cursor will now be over the prompt
	   is when PT = Z and the last line of the buffer is terminated by an
	   end of line character, and is on the last line of the window.
	Modules: TECUNV,TECVID,TECTRM

1013	By: Nick Bush		On: 6-August-1980
	Make UPDLIN use SC$WLN to write out a line if the line on the screen is
	blank, and we didn't have a function block for the replacement.
	
	Make FS and FN searches only move the text in the text buffer once, not
	do a delete and an insert. It will now only delete characters if the
	new string is shorter, and only insert space if the new string is longer.
	Modules: TECVID,TECSRH

1015	By: Nick Bush		On: 11-August-1980
	Don't give the NSM message until after we have parsed the Q-register name.
	Otherwise, we a end up executing the Q-register name.
	Modules: TECVID

1024	By: Robert McQueen		On: 19-August-1980
	Clear the executing flag in V$ASK and turn in back on after the character
	input has been done.
	Modules: TECVID

1026	By: Nick Bush		On: 21-August-1980
	When the command buffer is not the last thing on the screen on a terminal
	without insert/delete line or scrolling, if type in extends past the
	end of the section, various errors can result (ill mem ref's, looping,
	...).
	Modules: TECVID

1047	By: Robert McQueen		On: 24-October-1980
	79 Character lines would cause the 79th charcter to print twice, the
	second time on the wrapped around line if there was no CRLF ending the
	line.
	Modules: TECVID

1061	By: Nick Bush		On: 18-December-1980
	1) Finish and debug Tektronix 4025 support.
	2) Add FW command.
	3) Add capability of logging screen update info in log file.
	Modules: TECUNV,TECTEK,TECSRH,TECTBL,TECECM,TECVID,TECERR

1064	By: Robert McQueen		On: 23-December-1980
	VT100s are strange in that after clearing the screen the cursor is left on
	the bottom of the screen.  Add a new word to the $CRxxx blocks $CRERY to
	be the Y position after the screen is erased.
	Modules: TECUNV,TECDEC,TECVID

1065	By: Robert McQueen		On: 29-December-1980
	Remove the previous edit since it is easier to just add a home sequence
	after the erase function.  The erase sequence is now: <esc>[2J<esc>[H
	Modules: TECUNV,TECDEC,TECVID

1066	By: Robert McQueen		On: 6-January-1981
	Fix various problems with TECVID processing of VT100s.  FFD stopcodes
	and illegal memory references.
	Modules: TECVID

1102	By: Nick Bush		On: 23-Febuary-1981
	Fix VIDPOS to handle wrapped around lines correctly.  It was using the
	wrong value to check if it had found the correct line.
	Modules: TECVID

1106	By: Nick Bush		On: 13-May-1981
	Improve screen updating for times when the new screen has portions which
	are identical with the old.  This will also fix most cases of wrapped
	around lines on the top of the section of the screen.
	Also fix some random /MODE:DUMP bugs.
	Modules: TECUNV,TECVID,TECCMD,TECECM,TECUUO,TECMEM

1107	By: Nick Bush		On: 15-May-1981
	PTD stopcodes if screen update is aborted during a scroll down operation.
	Modules: TECVID

1110	By: Nick Bush		On: 30-June-1981
	Fix a couple bugs in updating.  Make sure that all lines which
	get destroyed as a result of scroll operations actually get marked
	that way.  Also do not just assume that if the first and last
	lines are valid that all lines in between are.  Check that all
	lines are valid before using the current screen.
	Modules: TECVID

1112	By: Robert McQueen		On: 10-July-1981
	Half the screen would disappear on ADM3As and other types of messed up
	screen updates.  This would only happen if it was cheaper to clear the
	screen and then write the data.  Clear LD$SME in the new LDBs in this case.
	Modules: TECVID

1113	By: Nick Bush		On: 12-July-1981
	Fix a backwards compare in SC$WRS.  The update will now correctly
	fix the bottom line when that should be more efficient.
	Modules: TECVID

1116	By: Nick Bush		On: 10-August-1981
	More work on VT-100's.  Get size of scrolling region right, and also make
	sure the region is properly set when we do a LF in the command echoing
	section.
	Modules: TECVID,TECDEC

1117	By: Robert McQueen		On: 14-August-1981
	- Scroll up didn't work correctly at all times.
	- Correct the cursor position after you scroll the screen up.
	Modules: TECVID

1120	By: Robert McQueen		On: 14-August-1981
	NRC stopcodes from control U processing on VT100s if you have a two line
	COMMAND-BUFFER.
	Modules: TECVID

1121	By: Nick Bush		On: 17-August-1981
	Control-U's in three line command buffers did not work.  More
	checks were needed in DISPLY.
	
	Fix defaulting of file spec paths to work correctly.  A previous edit
	had broken it.
	Modules: TECVID,TECFIL

1122	By: nb		On: 21-August-1981
	Fix DISPLY to not ill mem ref with extremely long lines. (>15000 characters).
	DISP.4 was referencing the previously found BEG (or thought it was) without
	first checking if there were any lines left on the top of the screen.
	Modules: TECVID

Start of Version 200A(1126)

1130	By: Nick Bush@SIT		On: 5-November-1981
	Split TECVID into two files:  TECVID and TECUPD.
	Redo how command editing is do in video mode to make the ^W editing
	character easier to implement.
	Add a new format for the V command when in video mode to allow the
	user more control over the position of text on the screen.
	Modules: TECVID,TECUPD,TECMVM

1145	By: Nick Bush		On: 8-Febuary-1982
	Re-write command input routines to implement FI command. As part of this
	allow user to set prompt by storing the prompt text for normal commands in
	the Q-register 'COMMAND-PROMPT'.
	Modules: TECUNV,TECPRS,TECCIN,TECFCM,TECVID,TECUPD

1146	By: Nick Bush		On: 16-March-1982
	Fix PTD stopcode that occurs with type ahead when a long command has been typed
	and not yet updated away.
	Modules: TECUPD

1152	By: Nick Bush		On: 2-April-1982
	Yet another check to avoid PTD stopcode.  In FDSPLY/DISPLY, make sure that the
	fixed position value is reasonable before we use it.
	Modules: TECUPD

1154	By: Nick Bush		On: 8-April-1982
	Add debugging routine to output the cost matrix for the section update costs.
	Fix VT-100 insert and delete cost routines to count the costs for setting
	the scroll region in the correct places.  This should make VT-100's update
	a bit faster, and make the update seem less strange to the user.
	Modules: TECUPD,TECDEC

1157	By: Nick Bush		On: 28-April-1982
	Fix command input to work correctly with a ^U after an escape on the
	first line of a command.
	
	Put in yet another check in an attempt to avoid PTD stopcodes for the command
	buffer.
	Modules: TECCIN,TECUPD

1161	By: Nick Bush		On: 13-May-1982
	Add new code to support peek-ahead for immediate (FC table) commands and
	for a new form of the control-T command.
	Add the new form of the control-T command to allow macros to peek at
	input, and to make use of timed input.  Also add the Q-register
	TERMINAL-INPUT-BUFFER to hold the text being peeked at, with the side
	effect of allowing macros to store text into the Q-register, and have
	it be treated as input typed on the terminal.
	Also change the space command to just pass through the arguments unless
	EO is set to 4 or less.
	Modules: TECUNV,TECVID,TECUPD,TECMVM,TECPRS,TECTRM,TECCMD,TECTBL

1163	By: Nick Bush		On: 19-May-1982
	Fix ill mem ref when terminal length is set so that command buffer is
	not displayed.
	
	Also add VK100 (GIGI) support from Nick Tamburri at DEC.
	Modules: TECUPD,TECTBL,TECDEC

1166	By: Nick Bush		On: 15-June-1982
	Yet another attempt to fix PTD's.
	Modules: TECUPD
|
	SUBTTL	Macro definitions

; The following macros are for use in checking for type-ahead

	DEFINE $CHTYI(FLAG),<
	IFB <FLAG>,<PUSHJ P,CHKTYI>
	IFIDN <FLAG>,<SEEN>,<SKIPN TYIFLG>
	IFIDN <FLAG>,<NOWAIT>,<PUSHJ P,CHTYI0>
	> ; End of $CHTYI definition
	SUBTTL	Screen editing -- SC$UPD - Update the screen

;+
;.Hl1 SC$UPD
;This routine will update the screen before each new command is input.
;.literal
;
; Usage:
;	T1/ .TRUE if LDB's should be updated with info from VIN
;	T1/ .FALSE if they should not (i.e., just update screen as is)
;	PUSHJ	P,SC$UPD
;	(Return)
;.end literal
;-

	$CODE			; Put into correct PSECT

	NDS.	D$LNCE,	^D80	; Initial estimate of per 1000 iteration
				; line cost time. This is the approximate
				; runtime per 1000 iterations of the line
				; update cost algortim. This is in
				; milliseconds.

SC$UPD:	JMPNS	.POPJ		; If not screen stuff, just return
	JMPF	T1,.+2		; Should we update LDBQRG?
	 PUSHJ	P,SC$SSR	; Yes, set up the regions of the screen
	JMPNS	.POPJ		; If not screen mode anymore, just return
	$CHTYI			; Check for type ahead
	 JRST	.+2		; None, go ahead and update the screen
	  POPJ	P,		; There is some, skip the update
	$SAVE	<P1,P2,P3>	; Save P1
	MOVE	P3,SUPDAD	; Get the update mem address
	MOVN	T1,SCRNLN	; Get the negative number of lines
	HRLI	P1,(T1)		; Into left half
	HRRI	P1,(P3)		; Get the update LDB address
SC$U.L:	MOVEI	T1,(P1)		; Get the LDB address
	PUSHJ	P,FLSLDB	; Flush the LDB
	ADDX	P1,$LDLEN-1	; Bump to next line
	AOBJN	P1,SC$U.L	; And loop

	MOVX	T1,D$LNCE	; Get the estimated cost
	MOVEM	T1,LITRTM	; Save it for later
	MOVN	P1,SCRNLN	; Get the number of lines again
	MOVSI	P1,(P1)		; Build an AOBJx pointer
	HRR	P1,SUPDAD	; Put in the address of the LDBs
	SETZ	P2,		; Clear the last pointer address

SC$UP0:	LOAD.	T1,LDBQRG,(P1)	; Get the address of the last one
	JUMPE	T1,SC$UP1	; If zero, skip it
	MOVE	T2,SUPDAD	; Get the new LDB address in case we call SC$WRS
	CAME	T1,P2		; Are the addresses the same ?
	 PUSHJ	P,SC$WRS	; Update this section of the screen
	LOAD.	P2,LDBQRG,(P1)	; Get the pointer again
SC$UP1:	ADDX	P1,$LDLEN-1	; Compute the address of the next
	$CHTYI			; Check if we should continue
	 AOBJN	P1,SC$UP0	; Loop looking at all the pointers
	SETZM	MESFLG		; Things can not be messed up now

	SETZ	P1,		; Flag nothing seen yet
	LOAD.	P2,TPTADR,+VINADR ; Get the address of the info block
	LOAD.	P3,VINNLN,(P2)	; And get the number of lines

SUPD.A:	LOAD.	T1,VINQRG,(P2)	; Get the Q-register this is
	JUMPE	T1,SUPD.B	; If nothinghere, try the next
	CAMN	T1,P1		; Same as last one?
	 JRST	SUPD.B		; Yes, skip it
	MOVE	P1,T1		; No, get the address
	MOVEI	T1,$QRPDB(P1)	; Get the address of the previous pointer
	SKIPE	(T1)		; Is there one?
	 PUSHJ	P,M$RELB	; Yes, release it
	LOAD.	T1,TPTADR,+$QRTPT(P1) ; Get the text buffer address
	MOVEI	T2,$QRPDB(P1)	; Get the address of the pointer
	PUSHJ	P,M$USEB	; And set it up
	LOAD.	T2,BLKEND,(T1)	; Get the current end pointer
	STOR.	T2,BLKOED,(T1)	; Remember it for the next update
	STORI.	.MINFI,T2,BLKLST,(T1) ; Last modified loc=-1
	STORI.	.INFIN,T2,BLKFST,(T1) ; First modified =inifity

SUPD.B:	AOJ	P2,		; Bump to next line
	SOJG	P3,SUPD.A	; And try again

	MOVE	P3,SUPDAD	; Get the address of the update info
	MOVN	P1,SCRNLN	; Get the number of lines on the screen again
	MOVSI	P2,(P1)		; Set up P2
	MOVSI	P1,(P1)		; And P1
	HRR	P1,SCRNAD	; With pointers to the correct LDB's
	HRR	P2,P3		;  .  .  .

	$CHTYI			; Check for type ahead again
	 JRST	.+2		; None, continue on
	  JRST	SUPD.5		; There is some, just go return the blocks

	DMOVE	T1,P1		; Get the pointers
	PUSHJ	P,SCTCST	; And calculate the best method of updating the screen

; We have the cheapest cost function so far
;Check if clearing the whole screen and then re-writing it is cheaper

	PUSH	P,T1		; Save T1
	MOVE	T2,P2		; Get the pointer to the new lines
	SETZ	T3,		; And clear a place to accumulate the cost

SUPD.6:	LOAD.	T1,LDBNUM,(T2)	; Get the number of chars here
	ADD	T3,T1		; Total them in
	CAML	T3,TOTCST	; See if too much yet
	 JRST	SUPD.7		; Yes, forget this
	ADDX	T2,$LDLEN-1	; Bump to the next line
	AOBJN	T2,SUPD.6	; Loop for all the lines

	PUSHJ	P,SC$ERS	; Clearing the screen is cheaper, clear the screen
	SETZ	T1,		; Clear the function pointer
	EXCH	T1,(P)		;  .  .  .
	JUMPE	T1,.+2		; If no function block, skip this
	 PUSHJ	P,M$RBLK	; And return the core from the block
	PUSH	P,P1		; Save P1
	MOVN	P1,SCRNLN	; Get the number of lines
	HRLI	P1,(P1)		; Make an AOBJx pointer
	HRR	P1,FNCLST	; Get the address of the function list

SUPD.8:	SETZ	T1,		; Clear T1
	EXCH	T1,(P1)		; And get the function skip
	JUMPE	T1,.+2		; Don't try to return nothing
	 PUSHJ	P,M$RBLK	; Return the block
	AOBJN	P1,SUPD.8	; Loop for all the lines

; Now clear the SME bits in the new LDBs.

	MOVE	P1,P2		; Copy the pointer
	MOVX	T1,LD$SME	; Get the bit to clear

SUPD.9:	ANDCAM	T1,$LDFLG(P1)	; Clear it
	ADDX	P1,$LDLEN-1	; Loop
	AOBJN	P1,SUPD.9	;  for all the ldb
	POP	P,P1		; And restore P1

SUPD.7:	POP	P,T1		; Restore the function block

	DMOVE	T2,P1		; Get the pointers again
	PUSHJ	P,UPDSCT	; Update the screen

; Here to update PTPOS

SUPD.5:	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the text buffer address
	LOAD.	T1,BLKPT,(T1)	; And get the pointer
	MOVEI	T2,TXTBUF	; Get the QRG block address
	MOVX	T3,QR$VID	; Get the flag to check
	TDNN	T3,$QRFLG(T2)	; Is TXTBUF displayed?
	 MOVE	T2,CUREDT	; No, use current buffer
	TDNE	T3,$QRFLG(T2)	; Is it displayed?
	 PUSHJ	P,FNDCHP	; Find the position of this character
	  JRST	SC$UP4		; If not on the screen, put over the prompt
	DMOVEM	T1,PTPOS	; Save the position
	DMOVE	X,T1		; Get the postion
	TXNE	F,F.INFI	; Doing an FI command?
	 POPJ	P,		; Yes, don't do this
	$CHTYI			; Check for type ahead
	 PUSHJ	P,SC$POS	; No, position the cursor
	POPJ	P,		; Yes, leave the cursor as it is

; Here if we can't find a reasonable place to put the cursor. Put it over
;where the prompt will go in the command section, and flag that escapes should
;not move the cursor from the echoing position

SC$UP4:	LOAD.	Y,QRGOFS,+CMDBUF ; Get the offset for the first line of the command buffer
	MOVEM	Y,PTPOS+$OFSY	; Store it
	SETZ	X,		; Clear the X position
	SETOM	PTPOS+$OFSX	; Flag to not put cursor here later
	TXNE	F,F.INFI	; Doing an FI command?
	 POPJ	P,		; Yes, leave the cursor where it is
	$CHTYI			; Check for type ahead
	 PJRST	SC$POS		; Go place the cursor
	POPJ	P,		; There is some, just return
	SUBTTL	Screen editing -- SC$UPS - Update a section

;+
;.HL1 SC$UPS
;This routine will update a section.  It will first determine if the pointer
;is currently on the screen.  If the pointer is not on the screen DISPLY
;will be called to build the new pointers, else the begin and end pointer
;for each LDB for this section will be updated.  After the pointers have
;been updated the screen and screen buffer will be updated with the current
;information.
;.literal
;
; Usage:
;	MOVE	T1,QRG.block.address
;	PUSHJ	P,SC$UPS
;	(Return)
;
;.end literal
;-


SC$UPS:	JMPNS	.POPJ		; Return if not screen mode
	MOVX	T2,QR$VID	; Check if displayed
	TDNN	T2,$QRFLG(T1)	; Is it?
	 POPJ	P,		; No, just return
	$SAVE	<P1,P2,P3,P4>	; Save P1
	$SAVE	<A1,A2>		; Save A1 and A2
	$SAVE	<CH>		; Save CH
	FRAME.	<CURPT,CUROFS,CURNLN,CURFLG> ; Make some variables on the stack
	MOVE	P1,T1		; Copy the argument

	MOVE	P3,SUPDAD	; Get the update mem address
	MOVN	T1,SCRNLN	; Get the negative number of lines
	HRLI	P2,(T1)		; Into left half
	HRRI	P2,(P3)		; Get the update LDB address
	MOVE	P4,SCRNAD	; Get the address of the current screen info
SUPS.1:	MOVEI	T1,(P2)		; Get the LDB address
	PUSHJ	P,FLSLDB	; Flush the LDB
	LOAD.	T1,LDBQRG,(P4)	; Copy the fudged QRG info
	STOR.	T1,LDBQRG,(P2)	;  .  .  .
	ADDX	P4,$LDLEN	; Advance to the next LDB
	ADDX	P2,$LDLEN-1	; Bump to next line
	AOBJN	P2,SUPS.1	; And loop

	MOVX	T1,D$LNCE	; Get the estimated cost
	MOVEM	T1,LITRTM	; Save it for later


	LOAD.	T1,TPTADR,+$QRTPT(P1) ; Get the buffer address
	LOAD.	T2,BLKPT,(T1)	; Get the current pointer
	MOVEM	T2,CURPT	; Save it
	LOAD.	T3,QRGNLN,(P1)	; Get the number of lines in the Q-reg
	MOVEM	T3,CURNLN	; Save it
	LOAD.	T2,QRGOFS,(P1)	; Get the first line number
	MOVEM	T2,CUROFS	; Save it
	LOAD.	T3,QRGFLG,(P1)	; Get the flags
	MOVEM	T3,CURFLG	; Save them

; Now determine where this Q-reg is currently displayed.  It may have
;been shifted due to type-in or incomplete screen updating.

	MOVE	T1,T2		; Get a copy of the first line number
	IMULX	T2,$LDLEN	; get the offset to the LDB
	ADD	T2,SCRNAD	; And point to the correct one
	PUSH	P,T2		; Save for counting up
	SETZ	T4,		; And clear the count
	SUB	T1,SCRNLN	; Get the number of lines we can go down
SUPS.2:	CFME.	T3,LDBQRG,(T2),P1 ; Is this the right data?
	 JRST	SUPS.3		; No, skip it
	ADDX	T2,$LDLEN	; Yes, advance to the next line
	AOJ	T4,		; Bump the line count
	AOJL	T1,SUPS.2	; And loop for all lines that we can

SUPS.3:	POP	P,T2		; Get the LDB address back
	LOAD.	T1,LDBLIN,(T2)	; Get the line number back

SUPS.4:	SUBX	T2,$LDLEN	; Back up a line
	SOJL	T1,SUPS.5	;  .  .  .
	CFMN.	T3,LDBQRG,(T2),P1 ; Same QRG?
	 AOJA	T4,SUPS.4	; Try again

SUPS.5:	AOJ	T1,		; Fix up for the last decrement
	STOR.	T1,QRGOFS,(P1)	; Store the offset
	STOR.	T4,QRGNLN,(P1)	; And the number of lines
	ADDX	T2,$LDLEN	; Point back at the first LDB
	LOAD.	T2,LDBBEG,(T2)	; And get the position
	JUMPGE	T2,.+2		; Is it okay?
	 SETZ	T2,		; No, use first position of buffer
	LOAD.	T1,TPTADR,+$QRTPT(P1) ; Get the buffer address
	CFMG.	,BLKFST,(T1),T2	; After first mod?
	 SETZ	T2,		; Yes, work from start
	STOR.	T2,QRGVAL,(P1)	; Store into value word
	ZERO.	T2,QRGFLN,(P1)	; Fix this on the first line of the section

	MOVEI	T1,(P1)		; Get the QRG address
	MOVE	T2,P3		; Get the new address
	PUSHJ	P,SC$WRS	; Write the new text
	MOVEI	T1,$QRPDB(P1)	; Get the address of the previous pointer
	SKIPE	(T1)		; Is there one?
	 PUSHJ	P,M$RELB	; Yes, release it
	LOAD.	T1,TPTADR,+$QRTPT(P1) ; Get the text buffer address
	MOVEI	T2,$QRPDB(P1)	; Get the address of the pointer
	PUSHJ	P,M$USEB	; And set it up
	LOAD.	T2,BLKEND,(T1)	; Get the current end pointer
	STOR.	T2,BLKOED,(T1)	; Remember it for the next update
	STORI.	.MINFI,T2,BLKLST,(T1) ; Last modified loc=-infinity
	STORI.	.INFIN,T2,BLKFST,(T1) ; First modified =inifity

	LOAD.	T1,QRGNLN,(P1)	; Get the number of lines in the section
	LOAD.	T2,QRGOFS,(P1)	; And the offset to the first
	IMULX	T2,$LDLEN	; Get the offset in words
	MOVE	P2,SCRNAD	; Get the address of the current screen
	ADD	P2,T2		; Point to the first line of this section
	ADD	P3,T2		; Also on the new screen
	MOVN	T1,T1		; Get the number of lines
	HRLI	P2,(T1)		; And make the AOBJx pointers
	HRLI	P3,(T1)		;  .  .  .


; Now compute the cost of going from one to the other

	DMOVE	T1,P2		; Get the address of the lists
	PUSHJ	P,SCTCST	; Compute the cost of updating the section

	DMOVE	T2,P2		; Now get the lists to update the sections
	PUSHJ	P,UPDSCT	; And update the sections


; Now determine the position of the pointer for this QRG

	MOVE	T2,P1		; Get the address
	MOVE	T1,CURPT	; And get the pointer
	PUSHJ	P,FNDLDB	; Find what line this is on
	 JRST	.+2		; It isn't there, figure out what to do
	  JRST	SUPS.6		; We found the LDB, go handle it

; Here if we couldn't find the LDB.  This can only be because PT is at Z.
	LOAD.	P2,TPTADR,+$QRTPT(P1) ; Get the address of the text buffer
	MOVE	T1,CURPT	; Get the pointer
	JUMPE	T1,SUPS.9	; If at start of buffer, make sure we put cursor at start of window
	CFME.	,BLKEND,(P2),T1	; Pointer at the end of the buffer?
	 JRST	SUPS.A		; No, not displayed for some reason

	SOJ	T1,		; Decrement to point to the last char
	IDIVX	T1,5		; Convert to word/byte offset
	HLL	T1,BTAB(T2)	; Get the byte pointer
	ADDI	T1,.BKTLN(P2)	; And make the absolute address
	LDB	CH,T1		; Get the final character

	LOAD.	T1,QRGOFS,(P1)	; Get the offset to the first line
	IMULX	T1,$LDLEN	; Convert to LDB offset
	ADD	T1,SCRNAD	; Make the absolute address
	LOAD.	T2,QRGNLN,(P1)	; Get the number of lines
	MOVN	T2,T2		; Make it negative
	HRLI	T1,(T2)		; And make the AOBJx pointer

SUPS.7:	LOAD.	T2,LDBEND,(T1)	; Get the end pointer
	JUMPL	T2,SUPS.8	; Is it valid?
	ADDX	T1,$LDLEN-1	; Yes, bump to the next
	AOBJN	T1,SUPS.7	; And loop for all the LDB's

; If we get here, all of the lines of the window are used. The only
;special case we need to check now is if the last line does not end with an
;end of line character.

	PUSHJ	P,CKTRM		; And check if it is a line terminator
	 JRST	.+2		; Skip
	  JRST	SUPS.A		; Final char is line terminator, just put over prompt
	LOAD.	T2,LDBLIN,-$LDLEN(T1) ; Get the line number
	LOAD.	T1,LDBNUM,-$LDLEN(T1) ; And get the number of chars on the line
	JRST	SUPS.E		; And go set things up

; Here if there are blank lines in the window. Find the correct place to put
;the pointer.

SUPS.8:	PUSHJ	P,CKTRM		; Check if last char was a line terminator
	 SUBX	T1,$LDLEN	; No, back up one line
	LOAD.	T2,LDBLIN,(T1)	; Get the line number
	LOAD.	T1,LDBNUM,(T1)	; Get the position on the line
	JRST	SUPS.E		; And go finish up

; Here if the pointer is at the start of the buffer. The cursor must
;then be put in column 0 of the first line of the window

SUPS.9:	LOAD.	T2,QRGOFS,(P1)	; Get the line number
	SETZ	T1,		; Column 0
	JRST	SUPS.E		; Go finish up

SUPS.A:	LOAD.	T2,QRGOFS,(P1)	; Get the offset to the first line
	LOAD.	T1,QRGNLN,(P1)	; Plus the line number
	ADDI	T2,-1(T1)	; Get the last line number
	SETZ	T1,		; Put at start of line
	JRST	SUPS.E		; And go return it

SUPS.6:	MOVE	T2,CURPT	; Get the pointer address back
	LOAD.	T3,TPTADR,+$QRTPT(P1) ; And get the text buffer address
	PUSHJ	P,FNDPOS	; Get the position

; Now restore the pointer in this text buffer

SUPS.E:	MOVE	T3,CUROFS	; Get the old offset back
	STOR.	T3,QRGOFS,(P1)	; Store it
	MOVE	T3,CURNLN	; And number of lines
	STOR.	T3,QRGNLN,(P1)	; And store it
	MOVE	T3,CURFLG	; Get the flags
	STOR.	T3,QRGFLG,(P1)	; And store them back
	LOAD.	T3,TPTADR,(P1)	; Get the address
	MOVE	T4,CURPT	; Get the pointer back
	STOR.	T4,BLKPT,(T3)	; Store the old pointer back
	POPJ	P,		; And return
	SUBTTL	Screen editing -- SC$WRS - Write new chars into a section

;+
;.hl2 SC$WRS
; This routine will cause the text for a section to be written into the
;new LDB's for later updating of the screen.
;.b.lit
;
; Usage:
;	MOVE	T1,QRG address
;	MOVE	T2,Base address of new LDB's
;	PUSHJ	P,SC$WRS
;	 (return, text written into new LDB's, P1 and P4 intact)
;
;.end literal
;-

SC$WRS:	$SAVE	<P1,P2,P3,P4>	; Save the ac's
	FRAME.	<TOPLIN,BOTLIN,SVEPT,OLDPT> ; Make some variables
	MOVE	P1,T1		; Get the QRG address
	MOVE	P4,T2		; And the screen buffer address
	LOAD.	P2,TPTADR,+$QRTPT(P1) ; Get the text pointer address
	LOAD.	T2,BLKPT,(P2)	; Get the current PT
	MOVEM	T2,OLDPT	; Save here also
	MOVE	T1,$QRFLG(P1)	; Check if fixed display
	TXNN	T1,QR$FIX	; Fixed position display
	 JRST	SWRS.1		; No, skip this
	LOAD.	T2,QRGVAL,(P1)	; Get the value word (position to fix)
	CFMGE.	,BLKEND,(P2),T2	; Within bounds?
	 JRST	SWRS.2		; No, punt it
	CFMLE.	,BLKFST,(P2),T2	; Are we before the first modification?
	 JRST	SWRS.1		; Yes, use this value
	SUB	T2,.BKOED(P2)	; Fix up to be relative to the new end
	ADD	T2,.BKEND(P2)	;  .  .  .
	CFMGE.	,BLKEND,(P2),T2	; Still ok?
	 JRST	SWRS.2		; No, punt
	CFMG.	,BLKLST,(P2),T2	; After last modification?
	 JRST	SWRS.1		; Yes, all is fine
SWRS.2:	BITOFF	T1,QR$FIX!QR$SHF,$QRFLG(P1) ; No, clear out the fixed flag
	MOVE	T2,OLDPT	; Fix position is bad, use current one
SWRS.1:	MOVEM	T2,SVEPT	; Save the current PT
	LOAD.	T1,TPTADR,+$QRPDB(P1) ; Get the buffer we were displaying last time
	MOVX	T2,QR$UPD	; And the flag to set
	SKIPN	MESFLG		; Whole screen need to be done from scratch?
	 CAIE	T1,(P2)		; Still on the same buffer?
	  IORM	T2,$QRFLG(P1)	; No, flag no data is valid
	TDNE	T2,$QRFLG(P1)	; Forced update needed?
	 JRST	SWRS.U		; Yes, do it
	SETOM	TOPLIN		; Clear the current top line
	SETOM	BOTLIN		; And bottom line
	LOAD.	T1,QRGOFS,(P1)	; Get the offset to the first line
	LOAD.	T4,QRGNLN,(P1)	; Get the number of lines also
	IMULX	T1,$LDLEN	; Make this the word offset
	ADD	T1,SCRNAD	; Get the offset to the correct LDB
	MOVEI	T2,-1(T4)	; Get the number of lines
	IMULX	T2,$LDLEN	; Make it the offset from the first
	ADD	T2,T1		; And get the address of the last line
	MOVE	P3,T1		; And copy the LDB addresses
	LOAD	T1,$LDFLG(P3),LD$ICA ; Addresses ok?
	JMPT	T1,SWRS.D	; No, punt it
	CFME.	T1,LDBQRG,(P3),P1 ; Did the display get changed?
	 JRST	SWRS.D		; Yes, can't count on anything
	LOAD.	T1,LDBBEG,(P3)	; Get the start of the first line
	JUMPL	T1,SWRS.D	; If not valid, just refigure from scratch
	CFMG.	,BLKFST,(P2),T1	; Was there some modification before the start of the screen?
	 JRST	SWRS.A		; Yes, go see whether we can fix the bottom
	CAMLE	T1,SVEPT	; Is PT before the start of the screen?
	 JRST	SWRS.D		; Yes, figure out new placement completely

	LOAD.	T3,LDBEND,(T2)	; Get the end address
	JUMPL	T3,SWRS.F	; Not valid, find last valid line
	MOVX	T1,LD$CRLF	; Check if there is a CRLF after the last line
	TDNE	T1,$LDFLG(T2)	;  .  .  .
	 ADDI	T3,2		; Yes, count them
	CFMG.	,BLKFST,(P2),T3	; Before first modification?
	 JRST	SWRS.F		; No, must do whole set of checks
	CAMG	T3,SVEPT	; Is PT still on the screen?
	 JRST	SWRS.D		; No, punt
; Here if there has been no modification before the start of the screen,
;and PT is after the start of the screen.  We must find the first line that
;has been modified, and work from there.

SWRS.F:	MOVE	T2,P3		; Get a copy of the LDB address
	MOVX	T3,LD$CRLF	; Check if the line included a CRLF

SWRS.Z:	CFME.	T1,LDBQRG,$LDLEN(T2),P1 ; Still correct Q-reg?
	 JRST	SWRS.Y		; No, done with the loop
	CFXGE.	,LDBBEG,$LDLEN(T2),0	; Is this line valid?
	 JRST	SWRS.Y		; No, all done with this loop
	LOAD.	T1,LDBEND,(T2)	; Get the end
	JUMPL	T1,SWRS.Y	; If END is no good, assume BEG is also bad
	TDNE	T3,$LDFLG(T2)	; Is there one?
	 ADDI	T1,2		; Yes, count it
	CFMG.	,BLKFST,(P2),T1	; Is the first modification after this point?
	 JRST	SWRS.Y		; No, count the number of lines the same
	SOJLE	T4,SWRS.W	; If this is the last line, the modification is after the screen
	ADDX	T2,$LDLEN	; Advance to next line
	CFME.	,LDBBEG,(T2),T1	; Is the end of the last line the start of this?
	 JRST	SWRS.Y		; No, punt this line
	JRST	SWRS.Z		; Yes, get it's bounds
; Here if the no text was changed on the screen, and PT is still on
;it somewhere.

SWRS.W:	LOAD.	T1,QRGNLN,(P1)	; Get the number of line
	LOAD.	T2,QRGOFS,(P1)	; Get the top line
	MOVEM	T2,TOPLIN	; Save as first line not modified
	ADDI	T2,-1(T1)	; Get the last line
	MOVEM	T2,BOTLIN	; Save that also
	MOVX	T2,QR$FIX	; See if fixed or shifting
	TDNE	T2,$QRFLG(P1)	; Is it?
	 JRST	SWRS.C		; Yes, must really call FDSPLY or DSPLY
	MOVN	T1,T1		; Make it negative
	HRLI	P3,(T1)		; Make the loop counter
	MOVEI	T1,(P3)		; Get the LDB address of the first line
	SUB	T1,SCRNAD	; Get the offset to he first line
	ADD	P4,T1		; Point to the the new lines also

SWRS.G:
	DEFINE XXX(LIST),<IRP LIST,<
	LOAD.	T1,LDB'LIST,(P3)	;; Get the old item
	STOR.	T1,LDB'LIST,(P4)	;; Store it
	>> ; End of DEFINE XXX
	XXX	(<BEG,END,NUM,FLG>) ; Move all the data
	BITON	T1,LD$SME,$LDFLG(P4) ; Flag the new data is same as old
	LOAD.	T1,LDBTXT,(P3)	; Get the address of the old text
	MOVSI	T1,(T1)		; Put in left half
	LOAD.	T2,LDBTXT,(P4)	; Get the destination address
	HRRI	T1,(T2)		; Set up the BLT pointer
	ADD	T2,LINWDS	; Get the final address
	BLT	T1,-1(T2)	; Move the text
	ADDX	P4,$LDLEN	; Advance to next line
	ADDX	P3,$LDLEN-1	;  .  .  .
	AOBJN	P3,SWRS.G	; Loop for all the lines
	JRST	SWRS.9		; And go finish up
; Here with T2 pointing at the first line which does not definitely contain
;valid data.  We will attempt to determine if PT is still on the screen,
;or if not, whether we should fix the bottom or top line of the screen
;and attempt to display it that way.

SWRS.Y:	LOAD.	T3,LDBLIN,(T2)	; Get the line number of the line we found
	MOVEM	T3,TOPLIN	; Remember the top line number
; Here to determine how many (if any) lines on the bottom of the section
;are valid as they are.

SWRS.A:	LOAD.	T1,QRGOFS,(P1)	; Get the offset of the first line
	LOAD.	T4,QRGNLN,(P1)	; And the number of lines
	ADDI	T1,-1(T4)	; Get the offset of the last line
	IMULX	T1,$LDLEN	; Make the address
	ADD	T1,SCRNAD	;  .  .  .
	LOAD.	T2,LDBEND,(T1)	; Get the end address
	JUMPL	T2,SWRS.C	; No, go try fixing the first line
	CFME.	T3,LDBQRG,(T1),P1 ; Same Q-reg previously?
	 JRST	SWRS.C		; No, can't fix this
	MOVE	T3,SVEPT	; Get PT
	SUB	T3,.BKEND(P2)	; Get it relative to the old end
	ADD	T3,.BKOED(P2)	;  .  .  .
	CFMG.	,LDBEND,(T1),T3	; Is PT on the screen?
	 JRST	SWRS.D		; No, just refigure from scratch

SWRS.B:	LOAD.	T2,LDBEND,(T1)	; Get the end address
	JUMPL	T2,SWRS.C	; No, go try fixing the first line
	CFME.	T3,LDBQRG,(T1),P1 ; Still valid?
	 JRST	SWRS.C		;  .  . .
	CFML.	,BLKLST,(P2),T2	; Is this before the last modified thing?
	 JRST	SWRS.C		; No, all done
	SOJLE	T4,SWRS.H	; Loop for all lines in this section

	LOAD.	T3,LDBBEG,(T1)	; Get the start of this line
	JUMPL	T3,SWRS.C	; And give up if it isn't valid
	SUBX	T1,$LDLEN	; Back up a line
	CFME.	T2,LDBQRG,(T1),P1 ; Same Q-register?
	 JRST	SWRS.C		; No,nothing left
	MOVE	T2,$LDFLG(T1)	; Get the flags from the previous line
	TXNE	T2,LD$CRLF	; Previous line end with a CRLF?
	 SUBI	T3,2		; Yes, account for that also
	CFME.	,LDBEND,(T1),T3	; Is the BEG/END pair correct?
	 JRST	SWRS.C		; No, no lines left
	LOAD.	T3,LDBLIN,+$LDLEN(T1) ; Get the line number in case
	TXNN	T2,LD$WAP	; Previous line wrap?
	 MOVEM	T3,BOTLIN	; No, this line is ok
	JRST	SWRS.B		; Loop for next line
; Here if we need to make a choice between fixing the top and bottom
;lines. We will fix the end which has the best chance of preserving
;the larger number of lines on the screen.

SWRS.C:	SKIPGE	T1,TOPLIN	; Get the top line we can keep
	 JRST	SWRS.H		; Can't, try the bottom
	LOAD.	T2,QRGOFS,(P1)	; Get the offset to the first line
	SUB	T1,T2		; Get the number of lines that are the same
	AOJ	T1,		; Plus one
	LOAD.	T3,QRGNLN,(P1)	; Get the total number of lines
	ADDI	T2,-1(T3)	; Get the last line number
	SKIPL	T3,BOTLIN	; Get the bottom line number
	 SUBM	T2,T3		; Get the number the same
	AOJ	T3,		; Plus one
	CAMLE	T3,T1		; More lines on top the same?
	 JRST	SWRS.H		; No, fix the bottom

; Here if we want to try fixing the top line.

	MOVE	T1,SVEPT	; Get the saved PT
	STOR.	T1,BLKPT,(P2)	; Save it
	LOAD.	T1,LDBBEG,(P3)	; Get the starting address
	MOVE	T2,P1		; And the QRG address
	LOAD.	T3,LDBLIN,(P3)	; And the line number
	EXCH	P4,SCRNAD	; Use correct buffer
	PUSHJ	P,FDSPLY	; Write the buffer
	MOVE	T1,SVEPT	; Get PT
	MOVE	T2,P1		; And get the QRG address
	PUSHJ	P,FNDLDB	; Find PT on the screen
	 JRST	SWRS.L		; Couldn't, check if at Z
	EXCH	P4,SCRNAD	; Swap things back
	JRST	SWRS.0		; And go write in the text

; Here if we couldn't find the position.  Check if it is at Z, and if
;it actually is on the screen.

SWRS.L:	EXCH	P4,SCRNAD	; Fix the address back
	MOVE	T1,SVEPT	; Get the pointer back
	CFME.	,BLKEND,(P2),T1	; Is PT=Z?
	 JRST	SWRS.H		; No, try fixing the bottom line
	LOAD.	T1,QRGOFS,(P1)	; Get the offset to the first line
	LOAD.	T2,QRGNLN,(P1)	; And the number of lines
	ADDI	T1,-1(T2)	; Get the last line offset
	IMULX	T1,$LDLEN	; Make the word offset
	ADD	T1,P4		; Get the actual address
	CFXGE.	,LDBEND,(T1),0	; Last line blank?
	 JRST	SWRS.0		; Yes, PT is on the screen
	FALL	SWRS.H		; No, try fixing the bottom line

; Here to try writing the screen by fixing the bottom line.

SWRS.H:	LOAD.	T3,QRGOFS,(P1)	; Get the first line number
	LOAD.	T4,QRGNLN,(P1)	; And the number of lines
	ADDI	T3,-1(T4)	; Get the offset to the last line
	IMULX	T3,$LDLEN	;  .  .  .
	ADD	T3,SCRNAD	; Get the address
	CFME.	T1,LDBQRG,(T3),P1 ; Same QRG?
	 JRST	SWRS.D		; No, can't do this
	LOAD.	T1,LDBBEG,(T3)	; Get the start address
	JUMPL	T1,SWRS.D	; If the bottom is not valid, punt
	CFML.	,BLKFST,(P2),T1	; Before first mod?
	 JRST	SWRS.I		; Yes, do it
	CFML.	,BLKLST,(P2),T1	; After last mod?
	 JRST	SWRS.D		; No, punt
	SUB	T1,.BKOED(P2)	; Yes, offset by the old end
	ADD	T1,.BKEND(P2)	; And make it correct for current items
SWRS.I:	MOVE	T2,SVEPT	; Get the saved PT
	STOR.	T2,BLKPT,(P2)	; Save it
	MOVE	T2,P1		; Get the QRG address
	LOAD.	T3,LDBLIN,(T3)	; Get the line number
	EXCH	P4,SCRNAD	; Use correct buffer
	PUSHJ	P,FDSPLY	; Get the bounds
	MOVE	T1,SVEPT	; Get PT
	MOVE	T2,P1		; And the QRG
	PUSHJ	P,FNDLDB	; Find the line with PT
	 JRST	[EXCH	P4,SCRNAD	; Couldn't, fix things back up
		JRST	SWRS.D]		; And do it from scratch
	EXCH	P4,SCRNAD	; Put things back
	JRST	SWRS.0		; And go write out the text
; Here if we for some reason must redraw the section without
;regard to what is currently there.  This is done on a refresh command,
;or if the text buffer being display in the section changed, or for
;some other reason.  In order to remember this fact until we actually
;get some valid text out on the screen, we will invalidate all of
;the BEG/END entries for the lines in the section.

SWRS.U:	LOAD.	T1,QRGOFS,(P1)	; Get the offset to the first line
	IMULX	T1,$LDLEN	; Make it the word offset
	ADD	T1,SCRNAD	; Point at the LDB
	LOAD.	T2,QRGNLN,(P1)	; Get the number of lines

SWRS.T:	ONES.	,LDBBEG,(T1)	; Clear the BEG
	ONES.	,LDBEND,(T1)	; And the END of the line
	ADDX	T1,$LDLEN	; Point to the next line
	SOJG	T2,SWRS.T	; Loop for all lines

; Here if we have no valid choice other than to re-center to screen.

SWRS.D:	MOVE	T1,SVEPT	; Get the current PT back
	STOR.	T1,BLKPT,(P2)	; Make sure PT is correct
	MOVE	T2,P1		; Get the QRG block address
	EXCH	P4,SCRNAD	; Save the screen address
	PUSHJ	P,DISPLY	; Determine the new LDB offsets (BEG/ENDs)
	EXCH	P4,SCRNAD	; Get the temp LDB list back again
	FALL	SWRS.0		; Update the screen now

; Here to update the screen

SWRS.0:	MOVE	P2,P4		; Copy the address for later
	LOAD.	T1,QRGNLN,(P1)	; Get the number of lines to check
	MOVN	T1,T1		; Build the negative of it
	HRL	P4,T1		; Build an AOBJN pointer to it
	LOAD.	T1,QRGOFS,(P1)	; Get the offset
	IMULX	T1,$LDLEN	; Compute the length
	ADD	P4,T1		; Compute the address of the first LDB

SWRS.3:	SETZ	X,		; Clear the X offset
	LOAD.	T1,TPTADR,+$QRTPT(P1) ; Get the text block address
	LOAD.	T2,LDBBEG,(P4)	; Get the beginning address
	JUMPL	T2,SWRS.5	; Skip this LDB if none
	PUSHJ	P,SETLIN	; Set up to get the characters
	LOAD.	T4,LDBTXT,(P4)	; Get the address of the text buffer
	HRLI	T4,(POINT 7)	; Finish up the byte pointer

SWRS.4:	CFMG.	T3,LDBNUM,(P4),X ; Finished with the loop ?
	 JRST	SWRS.5		; Yes, exit it
	LOAD.	T1,TPTADR,+$QRTPT(P1) ; Get the text block address
	PUSHJ	P,GETINC	; Get the next character
	  JRST	SWRS.5		; Finished with this line ?
	PUSHJ	P,SC$WRT	; Write the character
	JRST	SWRS.4		; Loop back

SWRS.5:	ADDX	P4,$LDLEN-1	; Point to the next
	AOBJN	P4,SWRS.3	; Loop for all characters

; Now determine if any lines should have LD$SME turned on.

	MOVX	T1,QR$UPD	; Get the flag
	TDNN	T1,$QRFLG(P1)	; Is this a forced update?
	 SKIPE	MESFLG		; Or complete refresh?
	  JRST	SWRS.9		; Yes, don't worry about identical lines
	LOAD.	T1,QRGOFS,(P1)	; Get the offset to the first line
	IMULX	T1,$LDLEN	; Make it the address of the LDB
	ADD	P2,T1		;  (the new one)
	MOVE	P3,T1		; Get the offset
	ADD	P3,SCRNAD	; And point to the old line
	LOAD.	T2,QRGNLN,(P1)	; Get the number of lines
	MOVN	T2,T2		; Make it negative
	HRLI	P2,(T2)		; Make the pointer
	MOVX	T3,LD$SME	; Get the flag to turn on
	LOAD.	P4,TPTADR,+$QRTPT(P1) ; Get the buffer address

SWRS.8:	CFME.	T1,LDBQRG,(P3),P1 ; Same QRG?
	 JRST	SWRS.N		; No, skip it
	LOAD.	T1,LDBNUM,(P2)	; Get the number of characters
	CFME.	,LDBNUM,(P3),T1	; Same?
	 JRST	SWRS.7		; No, skip this line
	LOAD.	T1,LDBBEG,(P2)	; Get the start address
	CFMG.	,BLKFST,(P4),T1	; Before first change?
	 JRST	SWRS.6		; No, try after the last
	CFME.	,LDBBEG,(P3),T1	; Save BEG?
	 JRST	SWRS.7		; No, try the next
	LOAD.	T1,LDBEND,(P2)	; Get the END
	CFMG.	,BLKFST,(P4),T1	; Before the first change?
	 JRST	SWRS.N		; No, try the next
	CFME.	,LDBEND,(P3),T1	; Yes, same end?
	 JRST	SWRS.P		; No, try again
	LOAD	T1,$LDFLG(P2),LD$CRLF ; Get the CRLF flag
	LOAD	T2,$LDFLG(P3),LD$CRLF ; And the other
	XOR	T1,T2		; Put them together
	JUMPN	T1,SWRS.P	; Same?
	IORM	T3,$LDFLG(P2)	; Flag line is same
	JRST	SWRS.P		; And try the next

; Here if the line starts after the first change

SWRS.6:	SUB	T1,.BKEND(P4)	; Offset to old end
	ADD	T1,.BKOED(P4)	;  .  .  .
	CFML.	,BLKLST,(P4),T1	; After last change?
	 JRST	SWRS.O		; No, try next
	CFME.	,LDBBEG,(P3),T1	; Yes, same as other BEG?
	 JRST	SWRS.O		; No, try next
	LOAD.	T1,LDBEND,(P2)	; Get the end of the line
	SUB	T1,.BKEND(P4)	; Offset to old end
	ADD	T1,.BKOED(P4)	;  .  .  .
	CFME.	,LDBEND,(P3),T1	; Same end?
	 JRST	SWRS.O		; No, advance to next line
	LOAD	T1,$LDFLG(P2),LD$CRLF ; Make sure CRLF's also match
	LOAD	T2,$LDFLG(P3),LD$CRLF ;   .  .  .
	XOR	T1,T2		; Get the sum
	JUMPN	T1,SWRS.O	; Same?
	IORM	T3,$LDFLG(P2)	; Yes, flag it
	JRST	SWRS.O		; And try the next line

SWRS.7:	LOAD.	T1,LDBBEG,(P3)	; Get the beg for this line
	CFMG.	,BLKFST,(P4),T1	; Is this before the first change
	 JRST	SWRS.O		; No, check for after last change
	LOAD.	T1,LDBEND,(P3)	; Get the end of the line
	CFML.	,BLKFST,(P4),T1	; Is the entire line before the changed section?
	 JRST	SWRS.P		; Yes, all done with this line
	ONES.	,LDBBEG,(P3)	; No, clear out the LDB
	ONES.	,LDBEND,(P3)	;  .  .  .
	JRST	SWRS.P		; And continue on

SWRS.O:	MOVE	T1,.BKEND(P4)	; Get the current end
	SUB	T1,.BKOED(P4)	; Minus the old end
	ADDM	T1,$LDEND(P3)	; Fix up the addresses
	EXCH	T1,$LDBEG(P3)	;  .  .  .
	ADDM	T1,$LDBEG(P3)	;  .  .  .
	CFMGE.	,BLKLST,(P4),T1	; Was the last modification before this?
	 JRST	SWRS.P		; Yes, all is fine
SWRS.N:	ONES.	,LDBBEG,(P3)	; No, clear out the LDB's
	ONES.	,LDBEND,(P3)	;  .  .  .

SWRS.P:	ADDX	P3,$LDLEN	; Advance to next line
	ADDX	P2,$LDLEN-1	;  .  .  .
	AOBJN	P2,SWRS.8	; And loop

SWRS.9:	BITOFF	T1,QR$UPD,$QRFLG(P1) ; Clear the forced update flag
	CFXL.	,BLKLST,(P2),0	; Anything actually modified?
	 TXNN	T1,QR$FIX	; Is this a fixed position pass?
	  JRST	SWRS.X		; No, skip this
	LOAD.	T2,QRGVAL,(P1)	; Get the value word
	CFML.	,BLKFST,(P2),T2	; After first item  modified?
	 JRST	SWRS.X		; No, skip this
	SUB	T2,.BKOED(P2)	; Yes, fix up to be relative to new coordinates
	ADD	T2,.BKEND(P2)	;  .  .  .
	STOR.	T2,QRGVAL,(P1)	; And store the value back
SWRS.X:	MOVX	T2,QR$FIX	; Get the flags to clear
	TXNE	T1,QR$SHF	; Only one time shift of screen
	 ANDCAM	T2,$QRFLG(P1)	; Yes, clear the flags
	MOVX	T2,QR$SHF	; Get the other flag
	TXC	T1,QR$FIX!QR$SHF ; See if both flags were on
	TXCE	T1,QR$FIX!QR$SHF ;  .  .  .
	 ANDCAM	T2,$QRFLG(P1)	; No, flag the the shift data is worthless now
	LOAD.	T1,TPTADR,+$QRTPT(P1) ; Get the address of the buffer
	MOVE	T2,OLDPT	; Get the pointer back
	PUSHJ	P,SETLIN	; Reset the byte pointer
	POPJ	P,		; And return
	SUBTTL	Screen editing -- Update section

;+
;.HL1 UPDSCT
;This routine will update a section using the functions that are given
;to it.  It assumes the cost analysis has been done to produce the
;the functions.
;.literal
;
; Usage:
;	MOVE	T1,[XWD -n,Function.block.address]
;	MOVE	T2,[XWD -n,Old.LDB.address]
;	MOVE	T3,[XWD -n,New.LDB.address]
;	PUSHJ	P,UPDSCT
;
;.end literal
;-

UPDSCT:	$SAVE	<P1,P2,P3,P4>	; Save P1 to P4
	$SAVE	<X,Y>		; Save the X and Y addresses
	PUSH	P,T1		; Save the address of the block
	SETOM	UPCSFL		; Flag we are doing the update now
	DMOVE	P2,T2		; Copy the LDB lists
	TXNE	CRT,CR$SCR	; Terminal do scrolling instead of insert/delete line
	 PUSHJ	P,OPR2SC	; Yes, convert operations to use only scrolling
	MOVE	P1,T1		; Copy the function block address
	SETZ	X,		; Clear X
	LOAD.	Y,LDBLIN,(P2)	; Line to start at
	SETZB	P4,IDLCNT	; Clear the number of lines delete/inserted
	$CHTYI			; Any type ahead?
	 JRST	.+2		; No, continue on
	  JRST	UPDS.2		; Yes, just return the blocks
	JUMPN	P1,UPDS.0	; If we have a list, go do it
	MOVX	P1,<INSVL.($OPRCH,CS$OPR)> ; No list, assume replaces
	MOVEM	P1,FNCTMP	; Store the item
	HLRE	P1,P2		; Get the number of lines
	MOVN	P1,P1		; Make it positive
	STOR.	P1,CSTRPT,+FNCTMP ; Store the repeat count
	MOVX	P1,<XWD -1,FNCTMP> ; Get the pointer

UPDS.0:	LOAD.	T1,CSTRPT,(P1)	; Get the repeat cost
	LOAD.	T2,CSTDEP,(P1)	; Get the dependant info
	LOAD.	T3,CSTOPR,(P1)	; Get the function
	PUSHJ	P,@UPSTBL(T3)	; Call the routine to do the work
	$CHTYI(SEEN)		; Any type ahead?
	 AOBJN	P1,UPDS.0	; Loop for all the functions

; Here when screen is done updating.  We must return any function blocks

UPDS.2:	MOVN	T1,SCRNLN	; Get the number of lines
	MOVSI	P1,(T1)		; Produce the AOBJN pointer
	HRR	P1,FNCLST	; Get the address
UPDS.1:	SKIPE	T1,(P1)		; Get the address of a function
	 PUSHJ	P,M$RBLK	; Return that block
	SETZM	(P1)		; Clear the entry
	AOBJN	P1,UPDS.1	; Loop for all items
	POP	P,T1		; Restore the address of the block
	JUMPE	T1,.POPJ	; Just return if no function block
	PJRST	M$RBLK		; Return the block to the memory

; Table to dispatch to the different routines

TABDEF	UPS,$OP
 TABENT	SDN,<EXP UPSSDN>	; Routine to scroll down
 TABENT	SUP,<EXP UPSSUP>	; Routine to scroll up
 TABENT	DCH,<EXP UPSDCH>	; Routine to delete a line
 TABENT ICH,<EXP UPSICH>	; Routine to insert a line
 TABENT ACH,<EXP UPSACH>	; Routine to accept a line
 TABENT RCH,<EXP UPSRCH>	; Routine to replace a line
 TABENT NOP,<EXP UPSNOP>	; Routine to process a no-op
TABEND
	SUBTTL	Screen editing -- Update section -- Scroll up

;+
;.HL2 UPSSUP
;This routine will cause n lines to be scrolled up on the screen.
;The boundary line will be in the CSTDEP field of the function.
;.literal
;
; Usage:
;	MOVE	T1,Number.of.lines
;	PUSHJ	P,UPSSUP
;	(Return)
;
;.end literal
;-

UPSSUP:	JUMPE	T1,USUP.D	; Is this the end of a scrolled region?
	$SAVE	<P1>		; Save P1
	MOVE	P1,T1		; Get the count
	PUSH	P,T2		; Save the offset to the last line
	SUBM	T2,T1		; Get the number of lines which will remain
	SUB	P4,P1		; Count the number of lines
	MOVN	T3,P1		; Also for total
	ADDM	T3,IDLCNT	; So that replace works correctly
				; the same
	PUSHJ	P,UPSLUP	; Move the LDB's up
	TXNE	CRT,CR$SCR	; Have scrolling functions?
	 JRST	USUP.2		; Yes, go use them
	PUSH	P,Y		; Save the current line number
	MOVE	Y,$CRLIN(CRT)	; Get the final line number (+1)
	SOJ	Y,		; Decrement to get real one
	SETZ	X,		; Clear the X pos
	PUSHJ	P,SC$POS	; Position to that point
	MOVE	T1,P1		; Get the number to do
	MOVX	CH,.CHLFD	; Get a line feed
	PUSHJ	P,SC$IMG	; And cause the screen to scroll
	SOJG	T1,.-1		; Output the correct number to move things up
	POP	P,Y		; Restore the line number
	POP	P,T2		; Restore T2
	POPJ	P,		; And return

; Here if the terminal has scrolling functions

USUP.2:	MOVE	T1,P1		; Get the number to scroll
	MOVE	T2,Y		; Get the top line
	PUSH	P,Y		; Save Y
	MOVE	Y,-1(P)		; Get the offset
	ADD	Y,T2		; And make the correct bottom line number
	SUBI	Y,1		; . . .
	XCT	$CRSUP(CRT)	; Scroll the region
	POP	P,Y		; Restore Y
	POP	P,T2		; Restore T2
	POPJ	P,		; And return

; Here if the function is for the end of a scroll. Fix up the insert/delete
;line counts and write the text.

USUP.D:	PUSH	P,T2		; Save the count
	MOVE	T1,T2		; Copy it
	PUSHJ	P,UPSRCH	; Update the section
	POP	P,T2		; Restore the count
	ADD	P4,T2		; Fix this count
	ADDM	T2,IDLCNT	; Also here.
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Update section -- Scroll down

;+
;.HL2 UPSSDN
;This routine will cause n lines to be scrolled down on the screen.
;The boundary line number will be in the CSTDEP field of the function
;block.
;.literal
;
; Usage:
;	MOVE	T1,Number.of.lines
;	MOVE	T2,Offset to bottom line
;	PUSHJ	P,UPSSDN
;	(Return)
;
;.end literal
;-

UPSSDN:	JUMPE	T1,USDN.D	; Is this the bottom line?
	$SAVE	<P1>		; Save P1
	MOVE	P1,T1		; Get the number of lines to scroll
	PUSH	P,T2		; Save the offset to the last line
	MOVE	T1,T2		; Get it for fixing the LDB's
	PUSHJ	P,UPSLDN	; Scroll things down
IFN FTDEBUG,<
	TXNE	CRT,CR$SCR	; Have real scrolling?
	 JRST	USDN.F		; Yes, go do it
	TXNN	CRT,CR$RLF	; No, have a reverse line feed?
	 STOPCD	NRL,<UPSSDN called for terminal without reverse line feed>
	SKIPE	Y		; First line of screen?
	 STOPCD	NFL,<Not first line for UPSSDN to scroll>
USDN.F:> ; End of IFN FTDEBUG

; First cause the terminal to actually move the lines.

	MOVE	T1,P1		; Get the repeat count
	POP	P,T2		; And get the offset off the stack
	ADDI	T2,(Y)		; Point to the bottom line
	SOJ	T2,		; Minus one
	XCT	$CRSDN(CRT)	; Scroll down
	ADD	P4,P1		; Fix the count of lines inserted
	ADDM	P1,IDLCNT	; Here also

USDN.3:	$CHTYI			; Any type ahead
	 JRST	.+2		; No, continue on
	  POPJ	P,		; Yes, return
	LOAD.	T1,LDBQRG,(P3)	; Update the Q-reg address
	STOR.	T1,LDBQRG,(P2)	;  .  .  .
	LOAD.	T1,LDBNUM,(P3)	; Get the number of chars
	STOR.	T1,LDBNUM,(P2)	; Save it
	LOAD.	T1,LDBBEG,(P3)	; Get the start
	STOR.	T1,LDBBEG,(P2)	; Save it
	LOAD.	T1,LDBEND,(P3)	; Get the end
	STOR.	T1,LDBEND,(P2)	; Save it
	LOAD.	T1,LDBFLG,(P3)	; Get the flags
	STOR.	T1,LDBFLG,(P2)	; Save them
	LOAD.	T1,LDBTXT,(P3)	; Get the text
	LOAD.	T2,LDBTXT,(P2)	; And the old
	STOR.	T1,LDBTXT,(P2)	; Switch them around
	STOR.	T2,LDBTXT,(P3)	; Save the other
	MOVEI	T1,(P2)		; Get the LDB address
	PUSHJ	P,SC$WLN	; Write the line out
	ADDX	P2,<XWD 1,$LDLEN> ; Bump to the next line
	ADDX	P3,<XWD 1,$LDLEN> ; This one also
	AOJ	Y,		; Bump the line number
	SOJLE	P1,.POPJ	; Done yet?
	JRST	USDN.3		; No, try again

; Here at the bottom of a scrolled down section

USDN.D:	SUB	P4,T2		; Fix this count
	MOVN	T2,T2		; Make it negative
	ADDM	T2,IDLCNT	; And fix the other
	POPJ	P,		; All done


	SUBTTL	Screen editing -- Update section -- Accept a line

;+
;.HL2 UPSACH
;This routine will accept n lines.  It is called with a repeat count.
;.literal
;
; Usage:
;	MOVE	T1,Repeat.count
;	PUSHJ	P,UPSACH
;	(Return)
;
;.end literal
;-

UPSACH:	ADD	Y,T1		; Increment the Y address

; Loop to copy the line information (other than the text)

UPSA.0:	LOAD.	T2,LDBQRG,(P3)	; Copy the Q-reg address
	STOR.	T2,LDBQRG,(P2)	;  .  .  .
	LOAD.	T2,LDBBEG,(P3)	; Get the new beg
	STOR.	T2,LDBBEG,(P2)	; Store it
	LOAD.	T2,LDBEND,(P3)	; Get the new end
	STOR.	T2,LDBEND,(P2)	; Store it
	LOAD.	T2,LDBFLG,(P3)	; Get the flags
	STOR.	T2,LDBFLG,(P2)	; Store them

; Note that LDBNUM and LDBTXT don't need to be copied since the LDBNUM
;fields must be the same and the LDBTXT buffers contain the same text in
;order for the line to be accepted

	ADDX	P2,<XWD 1,$LDLEN> ; Bump the old pointer
	ADDX	P3,<XWD 1,$LDLEN> ; And the new
	SOJG	T1,UPSA.0	; Loop for all the lines being accepted
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Update section -- Replace a line

;+
;.HL2 UPSRCH
;This routine will replace n lines.  It is called with a repeat count
;for the number of lines that are to be replaced.
;.literal
;
; Usage:
;	MOVE	T1,Repeat.count
;	PUSHJ	P,UPSRCH
;	(Return)
;
;.end literal
;-

UPSRCH:	$SAVE	<P1>		; Save P1
	MOVE	P1,T1		; Copy the repeat count
UPSR.0:	SETZ	T1,		; Clear this
	SKIPE	IDLCNT		; Inserts/delets balanced?
	 JRST	UPSR.1		; No, Skip this
	MOVX	T2,LD$SME	; Check if lines are identical
	TDNE	T2,$LDFLG(P3)	; Are they?
	 JRST	UPSR.3		; Yes, don't need to even look at them
	LOAD.	T2,LDBLIN,(P2)	; Yes, get the line number
	ADD	T2,FNCLST	; Point to the function block
	EXCH	T1,(T2)		; Get the function block for line N
	JRST	UPSR.2		; Go update the line

; Here if we don't have a function block for these lines

UPSR.1:	LOAD.	T2,LDBNUM,(P2)	; Get the number of characters
	JUMPE	T2,UPSR.2	; Just update the line
	TXNN	CRT,CR$DIC	; Have delete and insert character?
	 JRST	UPSR.2		; No, skip this
	MOVEI	T1,(P2)		; Get the old LDB address
	MOVEI	T2,(P3)		; And the new
	PUSHJ	P,LINCST	; Compute the best method of updating this line
	MOVE	T1,T2		; Get the address of the function block
UPSR.2:	MOVEI	T2,(P2)		; Get the addresses
	MOVEI	T3,(P3)		; . . .
	PUSHJ	P,UPDLIN	; Update the line
UPSR.3:	LOAD.	T1,LDBQRG,(P3)	;  Get the Q-reg address
	STOR.	T1,LDBQRG,(P2)	; Save it in the new LDB
	LOAD.	T1,LDBTXT,(P2)	; Get the old text address
	LOAD.	T2,LDBTXT,(P3)	; And the new
	STOR.	T1,LDBTXT,(P3)	; Reverse the pointers
	STOR.	T2,LDBTXT,(P2)	;  .  .  .
	LOAD.	T1,LDBNUM,(P3)	; Get the new number of chars
	STOR.	T1,LDBNUM,(P2)	; Store it
	LOAD.	T1,LDBBEG,(P3)	; Get the beg
	STOR.	T1,LDBBEG,(P2)	; Store it
	LOAD.	T1,LDBEND,(P3)	; Get the end address
	STOR.	T1,LDBEND,(P2)	; Store it
	LOAD.	T1,LDBFLG,(P3)	; Get the flags
	STOR.	T1,LDBFLG,(P2)	; Store them
	ADDX	P2,<XWD 1,$LDLEN> ; Move to the next LDB
	ADDX	P3,<XWD 1,$LDLEN> ; This one also
	AOJ	Y,		; Bump the line number
	$CHTYI			; Check if any type ahead
	 SOJG	P1,UPSR.0	; Loop for replaces
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Update section -- Delete a line

;+
;.HL2 UPSDCH
;This routine will cause a line(s) to be deleted.  The number of lines
;to be deleted depends on the repeat count.
;.literal
;
; Usage:
;	MOVE	T1,Repeat.count
;	PUSHJ	P,UPSDCH
;	(Return)
;
;.end literal
;-

UPSDCH:	$SAVE	<P1>		; Save P1
	MOVE	P1,T1		; Copy the repeat count
	SETZ	X,		; Clear the X position

; Check if we are deleting to the end of the screen

	LOAD.	T1,LDBLIN,(P2)	; Get the line number of the first line
	ADD	T1,P1		; Plus the number being deleted
IFN FTDEBUG,<
	CAMG	T1,SCRNLN	; Deleting to the end of the screen?
	 JRST	UPSD.3		; No, must do delete lines
	STOPCD	(DMM,<A delete section function cause movement out of the matrix>)
>; End of IFN FTDEBUG

; First loop clearing the text in the LDBs that are being deleted

UPSD.3:	MOVE	T2,SCRNLN	; Get the line number of the first
	SUBM	T2,T1		; Compute the number to move up
	PUSHJ	P,UPSLUP	; Move lines up

; Now do the work of deleting the information from the screen

	SUB	P4,P1		; Decrement by the number of lines
	MOVN	T1,P1		; Get the negative number
	ADDM	T1,IDLCNT	; And fix the total insert/delete count
	PUSHJ	P,SC$POS	; Position to the correct place
	MOVE	T1,P1		; Copy the repeat factor into the right place
	XCT	$CRDLL(CRT)	; Delete the lines
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Update section -- Insert a line(s)

;+
;.HL2 UPSICH
;This routine will cause one or more lines to be inserted.  The number
;of lines that are handled depends on the repeat count given.
;.literal
;
; Usage:
;	MOVE	T1,Repeat.count
;	PUSHJ	P,UPSICH
;	(Return)
;
;.end literal
;-

UPSICH:	ADD	P4,T1		; Add in the repeat count
	ADDM	T1,IDLCNT	; Also fix the total counter
	SKIPLE	P4		; Have to do it the hard way ?
	 PUSHJ	P,UPSISB	; Yes, call the subroutine
	$SAVE	<P1>		; Save P1
	MOVE	P1,T1		; Copy the repeat count to a safer place

; Move the LDBs around first.  This is done by first saving the TXT
; addresses from the bottom of the screen on the stack.  Then moving
; the ones above down.

	MOVE	T2,SCRNLN	; Get the number of lines on the CRT
	SUBI	T2,1		; Decrement by one
	IMULX	T2,$LDLEN	; Compute the address of the last LDB
	ADD	T2,SCRNAD	; Get the actual address
	MOVE	T4,T2		; Get a copy of it

UPSI.3:	LOAD.	T3,LDBTXT,(T2)	; Get the address of the text block
	PUSH	P,T3		; Save it on the stack
	SUBX	T2,$LDLEN	; Backup up one LDB
	SOJG	T1,UPSI.3	; Loop for the number of lines being inserted

; Now move the other information down

	MOVE	T1,SCRNLN	; Get the number of lines again
	SUB	T1,Y		; And get the number of lines we are moving
	SUB	T1,P1		;  .  .  .
UPSI.4:	LOAD.	T3,LDBQRG,(T2)	; Get the address of the Q-reg
	STOR.	T3,LDBQRG,(T4)	; Store it
	LOAD.	T3,LDBTXT,(T2)	; Get the address of a text block
	STOR.	T3,LDBTXT,(T4)	; Store it down below
	LOAD.	T3,LDBNUM,(T2)	; Move the number of characters
	STOR.	T3,LDBNUM,(T4)	; . . 
	LOAD.	T3,LDBFLG,(T2)	; Move the flags down
	STOR.	T3,LDBFLG,(T4)	; . . .
	LOAD.	T3,LDBBEG,(T2)	; Move the beginning down
	STOR.	T3,LDBBEG,(T4)	; . . .
	LOAD.	T3,LDBEND,(T2)	; Move the ending down
	STOR.	T3,LDBEND,(T4)	; . . .
	SUBX	T2,$LDLEN	; Back up another LDB
	SUBX	T4,$LDLEN	; . . .
	SOJG	T1,UPSI.4	; Try again
	MOVE	T1,P1		; Get the number of lines being inserted
	IMULX	T1,$LDLEN	; And make the number of words to back up P2
	HRLI	T1,(P1)		; Also fix the counter


	SETZ	X,		; Clear the X position
	PUSHJ	P,SC$POS	; Position to the place
	MOVE	T1,P1		; Get the number to insert
	XCT	$CRINL(CRT)	; Cause that may lines to appear

UPSI.0:	$CHTYI			; Check if any type ahead
	 JRST	.+2		; No, continue on
	  JRST	UPSI.6		; Yes, go clear out the remaining LDB's
	LOAD.	T1,LDBQRG,(P3)	; Copy the QRG address
	STOR.	T1,LDBQRG,(P2)	;  .  .  .
	LOAD.	T1,LDBBEG,(P3)	; Get the beginning adress
	STOR.	T1,LDBBEG,(P2)	; Store it
	LOAD.	T1,LDBEND,(P3)	; Move the ends
	STOR.	T1,LDBEND,(P2)	; . . .
	LOAD.	T1,LDBFLG,(P3)	; Move the flags
	STOR.	T1,LDBFLG,(P2)	; . . .
	LOAD.	T1,LDBNUM,(P3)	; Move the number of characters
	STOR.	T1,LDBNUM,(P2)	; . . .
	LOAD.	T1,LDBTXT,(P3)	; Get the address of the text
	STOR.	T1,LDBTXT,(P2)	; Store in a safer place
	POP	P,T2		; Get the text area from the stack
	STOR.	T2,LDBTXT,(P3)	; Make it the one that gets returned
	MOVE	T1,P2		; Get the LDB address
	PUSHJ	P,SC$WLN	; And write the line

	ADDX	P2,<1,,$LDLEN>	; Point to the next LDB
	ADDX	P3,<1,,$LDLEN>	; Point to the next LDB
	AOJ	Y,		; Bump the line number
	SETZ	X,		; Clear the X position
	SOJLE	P1,.POPJ	; Return if done
	JRST	UPSI.0		; Loop back

; Here if the user has typed ahead. We will abort the typeout, so we
;have to fix up the rest of the LDB's to reflect the current state of the
;screen.

UPSI.6:	POP	P,T1		; Restore the ldb
	STOR.	T1,LDBTXT,(P2)	; Get the address of teh text
	MOVEI	T1,(P2)		; Get the LDB address
	PUSHJ	P,FLSLDB	; Flush out the LDB
	ADDX	P2,<XWD 1,$LDLEN> ; Bump the pointer
	SOJG	P1,UPSI.6	; Loop for all the lines we still have left
	POPJ	P,		; And return
; Here if we must first delete line before the lines can be inserted
; Look down the function list for a delete function and process that
; before the insert.  After enough deletes have been done then the insert
; should be capable of being done.

UPSISB:	$SAVE	<X,Y>		; Save the position
	$SAVE	<P1,P2,P3>	; Save the pointers to LDBs and functions
	$SAVE	<T1,IDLCNT>	; Save T1 and the insert/delete count
	MOVEI	T2,(T1)		; Get a copy of the repeat count
	IMULX	T2,$LDLEN	; And make it the number of words to skip in the LDB's
	HRLI	T2,(T1)		; Fix the pointers
	ADD	P3,T2		; Skip the right number of new lines

UPSIS0:	AOBJP	P1,[STOPCD ROF,<Ran out of functions at UPSISB>]
	LOAD.	T1,CSTRPT,(P1)	; Get a repeat count
	LOAD.	T2,CSTOPR,(P1)	; And the operation
	CAXE	T2,$OPDCH	; Is this a delete function ?
	 JRST	UPSIS1		; No, Just update the pointers and continue
	MOVX	T2,$OPNOP	; Change the function to a no-operation
	STOR.	T2,CSTOPR,(P1)	; Store the operation
	PUSHJ	P,UPSDCH	; Do the function
	$CHTYI			; Check for type ahead
	 JUMPG	P4,UPSIS0	; If more to do than loop
	POPJ	P,		; Else just return

; Here if the function is not a delete function.  This means that the
; function is just skiped and the line counter updated with the repeat
; count.

UPSIS1:	CAXE	T2,$OPICH	; Is this an insert function
	 CAXN	T2,$OPNOP	; Was it a no-op?
	  JRST	UPSIS0		; Yes, don't bother incrementing either
	ADD	Y,T1		; Increment the count
	MOVEI	T3,(T1)		; Get the repeat count
	IMULX	T3,$LDLEN	; Get the number of words to skip
	HRLI	T3,(T1)		; Make it the number to fix the AOBJx pointer
	ADD	P3,T3		; Increment the new line pointer
	ADD	P2,T3		; No, increment the other pointer also
	JRST	UPSIS0		; Loop back
	SUBTTL	Screen editing -- Update section -- No-op

;+
;.HL2 UPSNOP
;This routine will cause a no-op to be processed.  This occurs if you
;have to insert lines before you have started to delete lines.
;.literal
;
; Usage:
;	MOVE	T1,Repeat.count
;	PUSHJ	P,UPSNOP
;	(Return)
;
;.end literal
;-

UPSNOP:	MOVN	T2,T1		; Get the number of lines
	ADDM	T2,IDLCNT	; And fix the counter (NOOP's were deletes)
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- UPSLUP - Move LDB's up

;+
;.hl1 UPSLUP
; This routine will move the information a given set of LDB's upwards.
;The LDB's from the top of the section will be cleared.
;.b.lit
;
; Usage:
;	MOVE	P1,Number.of.lines
;	MOVE	P2,AOBJx pointer to first line of section
;	MOVE	T1,Number of lines which will remain the same
;	PUSHJ	P,UPSLUP
;	 (return here, LDB's moved)
;
;.end literal
;-

UPSLUP:	JUMPE	T1,UDLN.W	; If none remain the same, just clear the lines
	MOVE	T3,T1		; Get the offset for later
	MOVN	T4,P1		; Get minus the number being repeated
	MOVSI	T4,(T4)		; Build the AOBJN pointer
	HRR	T4,P2		; Get the address of the first

UPSL.0:	LOAD.	T1,LDBTXT,(T4)	; Get the address
	PUSH	P,T1		; And save it on the stack
	PUSHJ	P,CLRL.0	; Clear this LDB
	ADDX	T4,$LDLEN-1	; Point to the next entry
	AOBJN	T4,UPSL.0	; Loop for all the LDBs

; Now play musical LDBs so the SCRNAD always points to what is really being
; displayed on the screen.  This is done by BLTing the information up.

	LOAD.	T1,LDBLIN,(P2)	; Get the line number of this LDB
	ADD	T1,P1		; Add in the number being deleted
				;  T1 now contains the line number of the last
				;  LDB
	MOVE	T2,T3		; Get the number of times to loop
	HRRZ	T3,P2		; Get the address to
	MOVE	T1,P1		; Get the number of lines
	IMULX	T1,$LDLEN	; Make it the number of words for the LDB's
	ADDI	T1,(P2)		; And point to the address to fetch from
	PUSH	P,P1		; Save P1
	PUSH	P,P2		; And P2

UPSL.2:	LOAD.	T4,LDBQRG,(T1)	; Copy the Q-reg address
	STOR.	T4,LDBQRG,(T3)	;  .  .  .
	LOAD.	T4,LDBBEG,(T1)	; Get the from address
	STOR.	T4,LDBBEG,(T3)	; Store it
	LOAD.	T4,LDBEND,(T1)	; Move the ending
	STOR.	T4,LDBEND,(T3)	; . . .
	LOAD.	T4,LDBNUM,(T1)	; Get the number
	STOR.	T4,LDBNUM,(T3)	; . . .
	LOAD.	T4,LDBFLG,(T1)	; Get the flags
	STOR.	T4,LDBFLG,(T3)	; Store the flags
	LOAD.	T4,LDBTXT,(T1)	; Get the text block address
	STOR.	T4,LDBTXT,(T3)	; . . .
	ADDX	T3,$LDLEN	; Point to the next LDB
	ADDX	T1,$LDLEN	; . . .
	SOJG	T2,UPSL.2	; Loop for all the LDBs
	POP	P,P2		; Restore P2
	POP	P,P1		; And P1

	MOVEI	T1,(T3)		; Get the address for storing the old LDB's
	MOVN	T3,P1		; Build an AOBJN loop
	HRL	T1,T3		; . . .

UPSL.1:	POP	P,T2		; Restore a TXT address
	STOR.	T2,LDBTXT,(T1)	; Store it into the correct place
	ZERO.	,LDBNUM,(T1)	; Clear the number of chars on the line
	ZERO.	,LDBFLG,(T1)	; And the flags
	ONES.	,LDBBEG,(T1)	; Clear the BEG
	ONES.	,LDBEND,(T1)	; And the END so we no this data is worthless
	ADDX	T1,$LDLEN-1	; Compute the next address
	AOBJN	T1,UPSL.1	; Loop for all items

	POPJ	P,		; All done
	SUBTTL	Screen editing -- UPSLDN - Move LDB's down


;+
;.HL1 UPSLDN
; This routine is called to move the LDB information down. This is used
;when a section of the screen is being scrolled down by an operation.
;.literal
;
; Usage:
;	MOVE	P1,Number of lines to scroll
;	MOVE	P2,AOBJx pointer to the LDB's
;	MOVE	T1,Size of section
;	PUSHJ	P,UPSLDN
;	 (return here, text moved)
;
;.end literal
;-

UPSLDN:	CAMN	T1,P1		; Moving the whole section?
	 JRST	UDLN.W		; Yes, go handle it
	$SAVE	<P3,P4>		; Save a couple ac's to work with
	MOVE	P3,T1		; Get the size of the section
	MOVEI	T2,-1(T1)	; Get the total number of lines being affected
	IMULX	T2,$LDLEN	; And make it the offset to the last
	ADDI	T2,(P2)		; Point to the last line
	MOVE	T3,P1		; Get the number of lines the section is being shifted
	MOVE	T4,T2		; Get the pointer to the line

UDLN.1:	LOAD.	T1,LDBTXT,(T2)	; Get the text address
	PUSH	P,T1		; Save it for later
	SUBX	T2,$LDLEN	; Back up a line
	SOJG	T3,UDLN.1	; And loop

; Now move the lines which will remain the same down

	MOVE	T3,P3		; Get the number of lines total
	SUB	T3,P1		; Minus the number being shifted out

; Macro to make things easier

DEFINE MVDN(FLD),<
    IRP <FLD>,<
	LOAD.	T1,LDB'FLD,(T2)	;; Get the info
	STOR.	T1,LDB'FLD,(T4)	;; And move it down
    > ;; End of IRP
> ; End of MVDN macro

	PUSH	P,P1		; Save P1
	PUSH	P,P2		; And P2

UDLN.2:	MVDN(<QRG,TXT,NUM,FLG,BEG,END>) ; Move everything down
	SUBX	T2,$LDLEN	; Back up the top pointer
	SUBX	T4,$LDLEN	; And the bottom
	SOJG	T3,UDLN.2	; And loop for all that are necessary
	POP	P,P2		; Restore P2
	POP	P,P1		; And P1

; Now pop the lines we saved on the stack and clear them

	MOVE	T3,P1		; Get the number of times to loop

UDLN.3:	POP	P,T1		; Get the address back
	STOR.	T1,LDBTXT,(T4)	; Store it
	MOVEI	T1,(T4)		; Get the LDB address
	PUSHJ	P,FLSLDB	; Clear the text out
	SUBX	T4,$LDLEN	; Back up a line
	SOJG	T3,UDLN.3	; And loop

	POPJ	P,		; All done

; Here if for some reason we are scrolling the same number of lines as there
;are in the section. (I'm not sure if this will ever occur, but the cost
;algorithms will find some non-obvious ways of doing things at times.)

UDLN.W:	MOVEI	T4,(P2)		; Get the first LDB address
	MOVEI	T3,(P1)		; And the number of times to loop

UDLN.4:	MOVEI	T1,(T4)		; Get the LDB address
	PUSHJ	P,FLSLDB	; And clear it out
	ADDX	T4,$LDLEN	; Bump to the next line
	SOJG	T3,UDLN.4	; And loop

	POPJ	P,		; All done
	SUBTTL	Screen editing -- OPR2SC -- Convert operation to scrolling

;+
;.hl1 OPR2SC
;This routine will cause the insert and delete functions to be converted
;into scrolling operations.  This will only happen on terminals that do
;not have the insert and delete line functions.  A good example of a terminal
;like this is the VT100.
;.literal
;
; Usage:
;	MOVE	T1,Function.list
;	PUSHJ	P,OPR2SC
;	(Return -- Updated list in T1)
;
;.end literal
;-

OPR2SC:	JUMPE	T1,.POPJ	; Just return if no list to search
	$SAVE	<P1,P2,P3,P4>	; Save a few registers
	PUSH	P,T1		; Save T1
	MOVE	P1,T1		; Copy the function list address
	MOVE	P2,FNCBLK	; Get the function block address
	MOVEI	P3,1(P)		; Get the address of the free stack entry
IFN FTKL,ADJSP	P,@SCRNLN	; Allocate the space on the stack
IFE FTKL,<
	MOVE	T1,SCRNLN	; Get the number of lines
	HRLI	T1,(T1)		; Make the XWD
	ADD	P,T1		; And fix the stack pointer
> ; End of IFE FTKL
	MOVEI	P4,(P3)		; Get the base address here also

; First run through the list clearing out CSTDEP in all the entries, so
;that we can use it ourself

	PUSH	P,P1		; Save the pointer
	MOVX	T1,CS$DEP	; Get the mask
	ANDCAM	T1,(P1)		; Clear the field
	AOBJN	P1,.-1		; And loop for all of them
	POP	P,P1		; Restore the pointer

OPR2.0:	CAIN	P3,(P4)		; Any fixups to do?
	 JRST	OPR2.1		; No, skip this
	HLRE	T1,P1		; Get the current loc into the list
	HLRE	T2,(P3)		; And the one we are waiting for
	CAME	T1,T2		; Correct function list entry?
	 JRST	OPR2.1		; No, do this one
	HRRE	T1,(P3)		; Get the repeat count
	MOVX	T2,<INSVL.($OPSDN,CS$OPR)> ; Assume this is the end of a scroll down
	JUMPGE	T1,.+2		; Is it?
	 MOVX	T2,<INSVL.($OPSUP,CS$OPR)> ; No, get the scroll up
	MOVM	T1,T1		; Get the count
	STOR.	T1,CSTDEP,+T2	; Save it
	MOVEM	T2,(P2)		; Store the word
	AOJ	P2,		; Bump the index
	AOJA	P3,OPR2.0	; And check the next fixup

OPR2.1:	MOVE	T1,(P1)		; Get the next function word
	LOAD.	T2,CSTOPR,+T1	; And get the operation
	XCT	O2STBL(T2)	; Do correct thing
	AOBJN	P1,OPR2.0	; And loop for the next
IFN FTKL,<
	MOVN	T2,SCRNLN	; Get the number of lines
	ADJSP	P,(T2)		; Remove the items from the stack
>; End of IFN FTKL
IFE FTKL,<
	MOVE	T2,SCRNLN	; Get the number of lines to remove
	HRL	T2,T2		; Move the number into both halfs
	SUB	P,T2		; Fix the stack
>; End of IFE FTKL
	POP	P,T1		; Get the count back
	HRR	T1,FNCBLK	; And set the pointer correctly
	SUB	P2,FNCBLK	; Get the length
	MOVN	P2,P2		; Negate it
	HRLI	T1,(P2)		; And set up the pointer
	POPJ	P,		; And return

; The following is the dispatch table for the operations on the function
; list.  These routines will determine that is to be done with the function
; list.

TABDEF	O2S,$OP
 TABENT	NOP,<JFCL>		; No-op function
 TABENT	ACH,<PUSHJ P,O2SACH>	; Accept function
 TABENT	RCH,<PUSHJ P,O2SRCH>	; Replace function
 TABENT	SUP,<PUSHJ P,O2SSUP>	; scroll up (?)
 TABENT	SDN,<PUSHJ P,O2SSDN>	; Scroll down (?)
 TABENT	ICH,<PUSHJ P,O2SICH>	; Insert lines
 TABENT DCH,<PUSHJ P,O2SDCH>	; Delete lines
TABEND
	SUBTTL	Screen editing -- OPR2SC -- Accept function
	SUBTTL	Screen editing -- OPR2SC -- Replace function

; Here for the accept and replace functions.

O2SRCH:
O2SACH:	MOVEM	T1,(P2)		; Save the entry
	AOJA	P2,.POPJ	; And return
	SUBTTL	Screen editing -- OPR2SC -- Scroll down fuction

; Here to handle the scroll down function.  This function is not expected
; to occur.

O2SSDN:	STOPCD	FSD,<Found a scroll down function in OPR2SC>




	SUBTTL	Screen editing -- OPR2SC -- Scroll up function

; Here to handle the scroll up function.  This function is not expected
; to occur.

O2SSUP:	STOPCD	FSU,<Found a scrolll up function in OPR2SC>
	SUBTTL	Screen editing -- OPR2SC -- Insert lines

; Here to cause lines to be inserted.  To do this we must first
; find the matching delete(s) for this function.

O2SICH:	SKIPA	T4,[EXP $OPDCH]	; Get the function to look for
O2SDCH:	 MOVX	T4,$OPICH	; Get the insert function we want to find

O2SI.0:	MOVE	T1,T4		; Get the function
	PUSHJ	P,O2SFND	; Find it
	JUMPE	T1,[STOPCD FFD,<Failed to find a delete to match>]
	MOVX	T3,<INSVL.($OPSDN,CS$OPR)> ; Get the scroll down function
	CAXE	T4,$OPDCH	; Is that correct?
	 MOVX	T3,<INSVL.($OPSUP,CS$OPR)> ; No, scroll down
	MOVEM	T3,(P2)		; Save the function
	PUSH	P,T2		; Save the number of lines being skipped
	HLLM	T1,(P4)		; Save the index for the point to do the fixup
	LOAD.	T2,CSTRPT,(T1)	; Get the repeat count for the function we found
	LOAD.	T3,CSTRPT,(P1)	; And the one for the current function
	CAME	T2,T3		; Same counts on both functions?
	 JRST	O2SI.1		; No, check which is larger
	STOR.	T3,CSTRPT,(P2)	; Yes, store the count
	LOAD.	T2,CSTDEP,(T1)	; Get the count of inserts already done (maybe)
	ADD	T2,T3		; Plus the amount we are doing now
	CAXE	T4,$OPDCH	; Was this an insert?
	 STOR.	T2,CSTDEP,(T1)	; Yes, update the count
	POP	P,T2		; Get the number of skipped lines back
	ADD	T2,T3		; Get the total
	STOR.	T2,CSTDEP,(P2)	; Store the offset
	LOAD.	T3,CSTRPT,(P2)	; Get the repeat count back
	CAXE	T4,$OPDCH	; Was this an insert?
	 MOVN	T3,T3		; No, negate the count
	HRRM	T3,(P4)		; And save it
	AOJ	P4,		; Bump past the new entry
	STORI.	$OPNOP,T3,CSTOPR,(T1) ; And convert the other function to a no-op
	AOJA	P2,.POPJ	; Count the entry and return

; Here if the repeat counts don't match

O2SI.1:	CAMG	T2,T3		; Bottom function larger?
	 JRST	O2SI.2		; No, go handle it
	SUB	T2,T3		; Yes, get the number left
	STOR.	T3,CSTRPT,(P2)	; Save the count of scroll functions
	STOR.	T2,CSTRPT,(T1)	; And fix up the bottom function repeat count
	POP	P,T2		; Get the number of skipped lines back
	ADD	T3,T2		; Get the total length of the region
	STOR.	T3,CSTDEP,(P2)	; Store the offset
	LOAD.	T3,CSTRPT,(P2)	; Get the repeat count
	LOAD.	T2,CSTDEP,(T1)	; Get the count of inserts already done (maybe)
	ADD	T2,T3		; Plus the amount we are doing now
	CAXE	T4,$OPDCH	; Was this an insert?
	 STOR.	T2,CSTDEP,(T1)	; Yes, update the count
	CAXE	T4,$OPDCH	; Was this an insert?
	 MOVN	T3,T3		; No, negate the count
	HRRM	T3,(P4)		; And save it
	AOJ	P4,		; Bump past the new entry
	AOJA	P2,.POPJ	; Count the entry and return

; Here if the top function has the larger repeat count

O2SI.2:	STOR.	T2,CSTRPT,(P2)	; Store the number we can do first
	CAXE	T4,$OPDCH	; Insert ?
	 ADDM	T3,(P)		; Get the overall length
	SUB	T3,T2		; Get the number left
	STOR.	T3,CSTRPT,(P1)	; Reset it
	STORI.	$OPNOP,T3,CSTOPR,(T1) ; Change the bottom function to a no-op
	POP	P,T3		; Get the number of skipped lines back
	CAXN	T4,$OPDCH	; Insert ?
	 ADD	T3,T2		; No, must have different count
	STOR.	T3,CSTDEP,(P2)	; Store the offset
	LOAD.	T3,CSTRPT,(P2)	; Get the repeat count
	LOAD.	T2,CSTDEP,(T1)	; Get the count of inserts already done (maybe)
	ADD	T2,T3		; Plus the amount we are doing now
	CAXE	T4,$OPDCH	; Was this an insert?
	 STOR.	T2,CSTDEP,(T1)	; Yes, update the count
	CAXE	T4,$OPDCH	; Was this an insert?
	 MOVN	T3,T3		; No, negate the count
	HRRM	T3,(P4)		; And save it
	AOJ	P4,		; Bump past the new entry
	ANDX	T1,RH.ALF	; Keep only the address
	CAIE	T1,1(P1)	; Is this the next item?
	 AOJA	P2,O2SI.0	; No, just try again
	AOJ	P2,		; Point to the next word
	MOVX	T2,<INSVL.($OPSDN,CS$OPR)> ; Get the operation
	CAXE	T4,$OPDCH	; Insert?
	 MOVX	T2,<INSVL.($OPSUP,CS$OPR)> ; No, get correct operation
	PUSH	P,T2		; Save on the stack
	HRRE	T2,-1(P4)	; Get the offset
	MOVM	T2,T2		; Make positive
	STOR.	T2,CSTDEP,(P)	; Store it
	SOJ	P4,		; Remove the item from the list
	PUSHJ	P,O2SI.0	; Generate the rest of this item
	POP	P,(P2)		; Restore the operation
	AOJA	P2,.POPJ	; And return
	SUBTTL	Screen editing -- OPR2SC -- Find a function

; This routine will look for a function on the function list.  It
; will return the address of the function and the number of lines
; from the start to it.
;
; Usage:
;	MOVE	T1,Function to look for
;	PUSHJ	P,O2SFND	; Find the function
;	(Return - T1 contains the address and T2 the number of lines)

O2SFND:	$SAVE	<P1,P2>		; Save the function list pointer
	SETZ	T2,		; Clear the number of lines
	AOBJP	P1,O2SF.2	; Return if finished

O2SF.0:	LOAD.	T3,CSTRPT,(P1)	; Get the repeat count
	LOAD.	P2,CSTOPR,(P1)	; Get the operation
	CAXE	P2,$OPNOP	; Is this a no-op?
	 CAXN	P2,$OPICH	; No, is this an insert?
	  LOAD.	T3,CSTDEP,(P1)	; Get the count of operations already done here
	CAME	P2,T1		; Same operator?
	 JRST	O2SF.1		; No, keep looking
	CAXN	P2,$OPICH	; Insert?
	 ADD	T2,T3		; Yes, include the count of what was already done
	MOVE	T1,P1		; Copy the pointer
	POPJ	P,		; Return

; Here to increment the count of the number of lines

O2SF.1:	ADD	T2,T3		; Increment the pointer
O2SF.3:	AOBJN	P1,O2SF.0	; Loop for all the functions
O2SF.2:	SETZ	T1,		; Clear it
	POPJ	P,		; And return
	SUBTTL	Screen editing -- SCTCST - Determine best way of updating screen

;+
;.HL1 SCTCST
; This routine will calculate the best method of updating the screen.
;It will do this only for terminals which have insert/delete line
;functions. The algorithm used is the same as that used by LINCST
;for terminals with insert and delete character functions.
;.b.lit
;
; Usage:
;	MOVE	T1,[XWD -n,Old LDB's]
;	MOVE	T2,[XWD -n,New LDB's]
;	PUSHJ	P,SCTCST
;	 (return, T1= -n,,function block, or 0 if just do replacements)
;
;.end literal
;-

SCTCST:	$SAVE	<P1,P2,P3,P4>	; Save some room to work
	$SAVE	<X,Y>		; Save X and Y also
	MOVEM	T1,OLDLDP	; Save the old line pointers
	MOVEM	T2,NEWLDP	; And the new one
	SETZM	TOTCST		; Clear the total cost

; First thing we will do is to find the first pair of old
;and new lines which are different. These lines will be the first lines
;we consider for moving.

	DMOVE	X,T1		; Get the pointers
	LOAD.	P1,LDBLIN,(X)	; Get the line number of the first line
	MOVE	P2,P1		; Get a copy
	ADD	P2,CSTLST	; Get the address of the cost list
	ADD	P1,FNCLST	; Plus the address of the function list
	SETZB	P3,P4		; Clear the first and last LDB addresses
	SETOM	UPCSFL		; Flag we want the function blocks returned

SCTC.B:	LOAD	T2,$LDFLG(Y),LD$SME ; Check if the same line
	JMPT	T2,[SETZB T1,T2		; Clear the cost and function
		JRST	SCTC.Z]		; And go store it
	HRRZ	T1,X		; Get the old LDB address
	HRRZ	T2,Y		; And the new
	PUSHJ	P,LINCST	; Get the cost
SCTC.Z:	MOVEM	T1,(P2)		; Save the cost
	MOVEM	T2,(P1)		; And the function block address
	ADDM	T1,TOTCST	; Get the total cost so far
	JUMPE	T1,SCTC.C	; Line the same?
	MOVE	P3,X		; No, remember the LDB address
	JUMPN	P4,SCTC.C	; Go for the next
	MOVE	P4,X		; Remember first different line

SCTC.C:	ADDX	X,$LDLEN-1	; Point almost to the next
	ADDX	Y,$LDLEN	; Make this one point to the next

	$CHTYI(NOWAIT)		; Check for type ahead
	 JRST	.+2		; None, skip on
	  PJRST	.RET0		; Have some, return now
	AOJ	P1,		; Bump the function pointer
	AOJ	P2,		; And the cost pointer
	AOBJN	X,SCTC.B	; Loop for all the lines

; Here with P4= first old LDB of lines which are different and
;P3= last old LDB of lines which are different.
	TXNN	CRT,CR$DIL!CR$SCR ; Terminal have delete/insert line or scrolling region?
	 JRST	SCTRPL		; No, calculate based on replacement plus line feed scrolling

	HLRE	T1,P4		; Get the first line
	HLRE	T2,P3		; And the last
	HLRE	T3,OLDLDP	; Get the number of old lines
	SUBB	T1,T3		; Get the number of initial lines to accept
	MOVEM	T1,BEGACC	; Save the number of accept functions to start with
	JUMPE	P3,.+2		; Get any ending lines?
	 AOJ	T2,		; Get the number of lines at the end to accept
	MOVNM	T2,ENDACC	; Save it
	IMULX	T1,$LDLEN	; Get the amount to skip the necessary LDB's
	SUB	T3,T2		; Make the number of lines total we are accepting
	HRL	T1,T3		; Make the amount to add to the pointers
	ADDM	T1,OLDLDP	; Add to the old pointer
	ADDM	T1,NEWLDP	; And the new one to point to the middle lines
	SKIPL	OLDLDP		; Have anything left to worry about?
	 PJRST	.RET0		; No, return a zero now

; Here with the pointers to the lines corrected to the ones
;we wish to consider for updating.

	HLRE	T1,OLDLDP	; Get the number of old lines
	HLRE	T2,NEWLDP	; And the number of new ones
	SOJ	T1,		; One more for each
	MOVMM	T1,NUMCOL	; Save the number of columns
	SOJ	T2,		;  .  .  .
IFN FTDEBUG,MOVMM T2,NUMROW	; Save the number of rows we have
	IMUL	T1,T2		; Get the total number needed
	MOVX	T2,.BTGEN	; Get the block type
	PUSHJ	P,M$GBLK	; Get a block for this
	MOVEM	T1,MATRIX	; Save the address for later
	MOVE	P1,T1		; Get a copy
	MOVX	T1,<INSVL.($OPACH,CS$OPR)> ; Get the initial operation
	MOVEM	T1,(P1)		; Save it
	AOJ	P1,		; Point to row 0, column 1
	MOVE	X,OLDLDP	; Get the pointer to the old lines
	MOVE	Y,NEWLDP	; Get the address of the new lines

IFN FTDEBUG,<
	PUSHJ	P,D$LINS	; Dump the lines for debugging
> ; End of IFN FTDEBUG

; Here to build the top row of the table. P1= the address of the element

SCTC.0:	MOVE	T1,-1(P1)	; Get the previous entry
	XCT	$CRCDL(CRT)	; Get the delete cost
	MOVE	T2,-1(P1)	; Get the previous entry back again
	PUSHJ	P,SCTSUB	; Fix up the costs
	MOVEM	T2,(P1)		; Store the entry
	AOJ	P1,		; Bump to the next entry
	ADDX	X,$LDLEN-1	; Point to next
	AOBJN	X,SCTC.0	; Loop for all the entries

; Now we must build column 0 of the table.

	MOVE	P1,MATRIX	; Reset P1
	MOVE	X,OLDLDP	; Get the old pointer
	MOVE	Y,NEWLDP	; Get the pointer to the new lines

SCTC.1:	MOVE	T1,(P1)		; Get the previous entry
	XCT	$CRCIL(CRT)	; Get the insert cost
	MOVE	T2,(P1)		; Get the previous entry
	PUSHJ	P,SCTSUB	; Fix it up
	ADD	P1,NUMCOL	; Move to next row
	MOVEM	T2,(P1)		; Store the entry
	ADDX	Y,$LDLEN-1	; Bump to next line
	AOBJN	Y,SCTC.1	; Loop for all of the lines
	$CHTYI(NOWAIT)		; Check for type ahead
	 JRST	.+2		; None, continue on
	  JRST	SCTC.E		; Go abort

	MOVE	Y,NEWLDP	; Reset Y
	MOVE	P1,MATRIX	; And P1
	SETZM	UPCSFL		; Flag we only want the cost now

; Here to loop for all of the new lines. Reset the pointers
;to the old lines, and set up P2 to point to the new row of the
;matrix. P1 enters pointing to the previous row, column 0.

SCTC.2:	AOS	P2,P1		; Bump to the correct column and copy
	ADD	P2,NUMCOL	; And make it point to the correct row
	MOVE	X,OLDLDP	; Get the line pointer

; Here to loop for all of the old lines, comparing them to
;a given new line.

SCTC.3:	LOAD.	T1,LDBLIN,(X)	; Get the number of the old line
	CFMN.	T2,LDBLIN,(Y),T1 ; Different?
	 JRST	SCTC.4		; No, we already did this cost
	HRRZ	T1,X		; Get the old line
	HRRZ	T2,Y		; And the new one
	PUSHJ	P,LINCST	; Get the cost to convert one to the other
	JRST	SCTC.5		; Go check for accept or replace

SCTC.4:	ADD	T1,CSTLST	; Get the address of the remembered cost
	MOVE	T1,(T1)		; And get the cost

SCTC.5:	MOVX	T2,<INSVL.($OPRCH,CS$OPR)> ; Assume we can do an accept
	STOR.	T1,CSTCST,+T2	; Store the cost
	JUMPN	T1,.+2		; Is it a replace?
	 MOVX	T2,<INSVL.($OPACH,CS$OPR)> ; Replace operation
	MOVE	T1,T2		; Get the operator
	MOVE	T2,-1(P1)	; Get the previous operation
	LOAD.	T3,CSTDEP,+T2	; Get the terminal dependent field
	STOR.	T3,CSTDEP,+T1	; Save in new item
	PUSHJ	P,SCTSUB	; Get the total cost
	DMOVE	P3,T1		; Get the cost and the entry
	MOVE	T1,(P1)		; Get the previous entry
	XCT	$CRCIL(CRT)	; And calculate the insert line cost
	MOVE	T2,(P1)		; Get the old entry back
	PUSHJ	P,SCTSUB	; Get the real cost
	CAMGE	T1,P3		; Is this cheaper than the replace or accept?
	 DMOVE	P3,T1		; Yes, get the values

	MOVE	T1,-1(P2)	; Get the previous to
	XCT	$CRCDL(CRT)	; get the delete cost
	MOVE	T2,-1(P2)	; Get the previous entry back
	PUSHJ	P,SCTSUB	; Get the total cost
	CAMGE	T1,P3		; Cheaper this way?
	 DMOVE	P3,T1		; Yes, get the args
	MOVEM	P4,(P2)		; Store the chosen method of updating

; Here after finishing one entry, step to the new column in this row

	$CHTYI(NOWAIT)		; Check for type ahead
	 JRST	.+2		; None
	  JRST	SCTC.E		; Go abort
	AOJ	P1,		; Bump the previous row pointer
	AOJ	P2,		; And the previous column pointer
	ADDX	X,$LDLEN-1	; Bump almost to the next line
	AOBJN	X,SCTC.3	; Loop unless done with this row

; Here after finishing a row, step to the next

	ADDX	Y,$LDLEN-1	; Increment to the next new line
	AOBJN	Y,SCTC.2	; And loop if any left

IFN FTDEBUG,PUSHJ P,D$OMTX	; Output the matrix

	SOS	T1,P2		; Get the address of the last entry
	LOAD.	T2,CSTCST,(P2)	; Get the cost
	MOVEM	T2,TOTCST	; Save it
	SETO	T2,		; Flag this is a line by line matrix
	PUSHJ	P,BLDFNC	; Build the function block
	MOVE	P1,T2		; Save the function block address
	MOVE	T1,MATRIX	; Get the line matrix address
	PUSHJ	P,M$RBLK	; Return the core
	MOVE	T1,P1		; Get the function pointer back
	POPJ	P,		; And return

; Here if we are aborting due to type ahead.  Return the matrix
;and return a zero for the function block

SCTC.E:	MOVE	T1,MATRIX	; Get the address
	PUSHJ	P,M$RBLK	; Return the block
	SETZB	T1,TOTCST	; Clear the function block address
	POPJ	P,		; And return
	SUBTTL	Screen editing -- SCTCST -- SCTRPL

; Here to calculate best way of updating terminals which can only move
;the screen by using a line feed to scroll the screen upwards.
; This will get the cost for each possibility of lines scrolling.

SCTRPL:	SKIPN	T1,TOTCST	; Get the total cost
	 POPJ	P,		; Screens are the same, just return
	HLRE	T2,OLDLDP	; Get the length of the old screen
	MOVN	T2,T2		; Make it positive
	SETZ	T1,		; Clear this
	CAME	T2,SCRNLN	; Is this for the whole screen?
	 POPJ	P,		; No, can't do any fancy things

	SETZM	BEGACC		; Clear number of scrolls to do

	MOVE	X,OLDLDP	; Get the pointer to the old lines
	ADDX	X,<XWD 1,$LDLEN> ; Bump it to the second line
	MOVE	Y,NEWLDP	; Get the new line pointer
	MOVEI	P4,1		; Get the cost of one scroll (one char)

SCTR.0:	MOVE	P3,P4		; Get the base cost
	DMOVE	P1,X		; Get the pointers

SCTR.1:	MOVEI	T1,(P1)		; Get the old line
	MOVEI	T2,(P2)		; And the new
	PUSHJ	P,LINCST	; Get the difference
	ADD	P3,T1		; Include the cost
	CAML	P3,TOTCST	; Is this more than the cheapest so far?
	 JRST	SCTR.3		; Yes, try the next
	ADDX	P2,<XWD 1,$LDLEN> ; No, advance the pointer
	ADDX	P1,$LDLEN-1	; This one also
	AOBJN	P1,SCTR.1	; And loop

SCTR.2:	LOAD.	T1,LDBNUM,(P2)	; Get the number of chars on this line
	ADD	P3,T1		; Total them in
	CAML	P3,TOTCST	; Too expensive?
	 JRST	SCTR.3		; Yes, try next
	ADDX	P2,$LDLEN-1	; No, advance to the next buffer
	AOBJN	P2,SCTR.2	; And loop

	MOVEM	P3,TOTCST	; If we get here it must be cheaper
	MOVEM	P4,BEGACC	; Store the number
SCTR.3:	AOJ	P4,		; Bump the scroll cost
	ADDX	X,$LDLEN-1	; And advance to the next
	AOBJN	X,SCTR.0	; loop for all the lines

; Now check if we should do the reverse line feed

	TXNN	CRT,CR$RLF	; Terminal have reverse line feed
	 JRST	SCTR.4		; Continue on

; Terminal has a reverse scroll operation. We will now calculate the
;costs of scrolling the screen down with the reverse line feed.
;This is done by calculating the lower diagonal of the cost matrix.

	SETZM	ENDACC		; Clear the count
	MOVE	X,OLDLDP	; Get the old LDB pointer
	MOVE	Y,NEWLDP	; And the new one
	ADDX	Y,<XWD 1,$LDLEN> ; Bump to the next line
	SETZ	P4,		; Clear the initial cost

SCTR.5:	LOAD.	T1,LDBNUM,-$LDLEN(Y) ; Get the cost of writing out the previous line
	ADDX	P4,<XWD 1,0>	; Count the number of operations
	ADDI	P4,(T1)		; Add in the cost of writing the line
	XCT	$CRCIL(CRT)	; Get the cost of inserting one line
	ADDI	P4,(T1)		; Get the total so far
	MOVEI	P3,(P4)		; Get the amount
	CAML	P3,TOTCST	; Past the total already?
	 JRST	SCTR.7		; Yes, punt out now
	DMOVE	P1,X		; Get the pointers

SCTR.6:	MOVEI	T1,(P1)		; Get the old LDB
	MOVEI	T2,(P2)		; And the new one
	PUSHJ	P,LINCST	; Calc the cost
	ADD	P3,T1		; Total the cost
	CAML	P3,TOTCST	; Too much yet?
	 JRST	SCTR.8		; Yes, try again
	ADDX	P1,<XWD 1,$LDLEN> ; Bump to the next line
	ADDX	P2,$LDLEN-1	; Bump this one also
	AOBJN	P2,SCTR.6	; And loop

; If we succeed in falling out of the above loop, we now have a cheaper
;way of updating the screen. We must store the info and the cost.

	MOVEM	P3,TOTCST	; Total the cost
	HLRZM	P4,ENDACC	; And the number of inserts
	SETZM	BEGACC		; No delete stuff

SCTR.8:	ADDX	Y,$LDLEN-1	; Bump the starting line address
	AOBJN	Y,SCTR.5	; And loop

SCTR.7:	SKIPN	P3,ENDACC	; Did we decide to do it this way?
	 JRST	SCTR.4		; No, go check for the other case
	MOVX	P4,<INSVL.($OPSDN,CS$OPR)> ; Get the operation
	JRST	SCTR.9		; And go generate the function block

; Here to check if we decided to scroll the screen up

SCTR.4:	SKIPN	T1,BEGACC	; Any scroll functions?
	 POPJ	P,		; No, return
	MOVX	P4,<INSVL.($OPSUP,CS$OPR)> ; Get the operation
	MOVE	P3,BEGACC	; And the repeat count

; Now to set up a function block. This will be a block as follows:
;	Scroll up BEGACC lines using the whole screen
;	Replace SCRNLN-BEGACC lines
;	End scroll up of BEGACC lines

SCTR.9:	MOVX	T1,3		; No, get a three word block
	MOVX	T2,.BTGEN	; Get the block type
	PUSHJ	P,M$GBLK	; Get the block
	MOVE	T2,SCRNLN	; Get the number of lines
	MOVE	T3,P4		; Get the function
	STOR.	T2,CSTDEP,+T3	; Store the offset from first line to last
	STOR.	P3,CSTRPT,+T3	; Store it
	MOVEM	T3,(T1)		; Store the function word
	MOVX	T3,<INSVL.($OPRCH,CS$OPR)> ; Get the accept function
	MOVE	T2,SCRNLN	; Get the number of replaces to do
	SUB	T2,P3		; Minus the number of scrolls
	STOR.	T2,CSTRPT,+T3	; Store the number
	MOVEM	T3,1(T1)	; Store the word
	STOR.	P3,CSTDEP,+P4	; Store in dependent field for end of scroll
	MOVEM	P4,2(T1)	; Save the word
	HRLI	T1,-3		; Set up the length
	POPJ	P,		; And return
	SUBTTL	Screen editing -- SCTSUB - Fix up a matrix entry

;+
;.HL2 SCTSUB
; This routine is called to fix up a cost matrix entry returned from
;the terminal dependent routines. It will add the previous cost, any
;cost due to a required positioning operation, and set the repeat
;count for the entry correctly.
;.b.lit
;
; Usage:
;	MOVE	T1,New entry from terminal dependent routine
;	MOVE	T2,Previous entry
;	PUSHJ	P,SCTSUB
;	 (Return, T1= cost in characters, T2=updated new entry)
;
;.end literal
;-

SCTSUB:	LOAD.	T3,CSTOPR,+T2	; Get the previous operation
	LOAD.	T4,CSTOPR,+T1	; Get the new operation
	CAME	T3,T4		; Same operation?
	 JRST	SCTS.0		; No, check for positioning cost
	LOAD.	T3,CSTRPT,+T2	; Get the repeat count
	AOSA	T3		; Plus one
SCTS.1:	 MOVEI	T3,1		; Here if starting a new operation, this is first one
	STOR.	T3,CSTRPT,+T1	; Store the new repeat count
	LOAD.	T3,CSTCST,+T2	; Get the old cost
	MOVE	T2,T1		; Copy the new entry
	LOAD.	T1,CSTCST,+T2	; Get the new cost
	ADD	T1,T3		; Make the total cost
	CAXE	T4,$OPICH	; Insert function?
	 TDZA	T3,T3		; No, extra cost is 0
	  LOAD.	T3,LDBNUM,(Y)	; Yes, extra cost is the number of chars on the line
	ADD	T1,T3		; Add the extra cost
	CAXL	T1,<1_<WID(CS$CST)>-1> ; Is it the maximum possible cost?
	 MOVX	T1,.INFIN	; Yes, get infinity
	STOR.	T1,CSTCST,+T2	; Store the total cost
	POPJ	P,		; And return

; Here when we are starting a new operation

SCTS.0:	CAXE	T3,$OPACH	; Was the previous operation an accept?
	 JRST	SCTS.1		; No, no position cost needed
	PUSH	P,T2		; Save the old entry
	PUSH	P,T1		; And the new
	MOVX	T1,<XWD 1,1>	; Set up to calculate the position cost
	LOAD.	T2,CSTRPT,+T2	; Get the number of accepts we are ending
	ADD	T2,T1		; Make the correct position
	XCT	$CRCPP(CRT)	; Get the positioning cost
	POP	P,T2		; Get the new entry back
	LOAD.	T3,CSTCST,+T2	; Get the cost from that
	ADD	T1,T3		; Get the total
	LOAD.	T3,CSTCST,(P)	; Get the old cost
	ADD	T1,T3		; Add that also
	CFXE.	T4,CSTOPR,+T2,$OPICH ; Insert function?
	 TDZA	T3,T3		; No, extra cost is 0
	  LOAD.	T3,LDBNUM,(Y)	; Yes, extra cost is the number of chars on the line
	ADD	T1,T3		; Add the extra cost
	POP	P,(P)		; Fix the stack
	CAXL	T1,<1_<WID(CS$CST)>-1> ; Is it the maximum possible cost?
	 MOVX	T1,.INFIN	; Yes, get infinity
	STOR.	T1,CSTCST,+T2	; Store the total cost
	STORI.	1,T3,CSTRPT,+T2 ; Store the new repeat count
	POPJ	P,		; And return
	SUBTTL	Screen update routines -- LINCST - Calculate best way of line update

;+
;.HL1
; This routine will calculate the cheapest way of changing one line into
;another. If the two lines are the same relative lines on the screen, it
;will calculate the method using insert and delete character operations,
;if the terminal has them. Otherwise, it will just use replace and
;accept operations.
;.b.lit
;
; Usage:
;	MOVE	T1,OLD.LDB.address
;	MOVE	T2,NEW.LDB.address
;	PUSHJ	P,LINCST
;	 (return, T1= Cost in characters, T2=AOBJx pointer to function block or 0)
;
;.end literal
;-

LINCST:	$SAVE	<P1,P2,P3,P4>	; Save some room
	$SAVE	<X,Y,CRT>	; Save X and Y
	MOVEM	T1,OLDLDB	; Save the old LDB address
	MOVEM	T2,NEWLDB	; And the new LDB address
	FALL	LNCRPL		; Fall into the accept/replace cost calcuation
; Here to calculate the cost of changing the line based only
;on accept and replace operations.

LNCRPL:	LOAD.	P3,LDBTXT,(T1)	; Get the old text address
	HRLI	P3,(POINT 7,)	; Make it a byte pointer
	LOAD.	P4,LDBTXT,(T2)	; Get the new text address
	HRLI	P4,(POINT 7,)	; Make it a byte pointer
	MOVEM	P3,OLDPTR	; Save the old byte pointer
	MOVEM	P4,NEWPTR	; And the new byte pointer
	LOAD.	X,LDBNUM,(T1)	; Get the number of old characters
	LOAD.	Y,LDBNUM,(T2)	; and in the new line
	MOVE	T1,X		; Get the length of the old line
	SUB	T1,Y		; Get the difference in the lengths
	JUMPL	T1,LNCR.1	; If old string is shorter, go handle it
	IDIVI	T1,5		; New string shorter, calculate how much to bump old pointer
	MOVE	P4,P3		; Get the old byte pointer
	HLL	P4,BTAB-1(T2)	; Set up the old pointer
	ADDI	P4,(T1)		; And fix the address
	DMOVE	T1,[ILDB T1,P4	; Get the instruction to fetch second character
		MOVE	P2,Y]	; And the instruction to fetch the offset
	JRST	LNCR.2		; Go store common things

LNCR.1:	MOVN	T1,T1		; Get the number of chars to bump the pointer
	IDIVI	T1,5		; Get the number of words and bytes
	HLL	P4,BTAB-1(T2)	; Fix the byte pointer
	ADDI	P4,(T1)		; And bump the address
	DMOVE	T1,[ILDB T2,P4	; Get the instruction to use
		MOVE	P2,X]	; And the instruction to fetch the offset

; Here after P4 is set up with proper byte pointer and T1 with instruction
;to fetch the character into the correct ac

LNCR.2:	DMOVEM	T1,LNCXCT	; Save the instructions
	SETZ	P1,		; Clear number of first different character
	XCT	T2		; And suffix counters
	SETZB	T3,T4		; Clear the cost and number of accepts
	MOVEI	P3,1		;  .  .  .

; Now loop for all the characters

LNCR.3:	SOJL	X,LNCR.X	; Old string run out?
	SOJL	Y,LNCR.Y	; No, how about the new one?
	ILDB	T1,OLDPTR	; Get the old character
	ILDB	T2,NEWPTR	; And the new one
	CAIN	T1,(T2)		; Same characters?
	 AOJA	T3,LNCR.5	; Yes, just count it

; Here for different characters

	JUMPE	T3,LNCR.4	; If no previous accepts, skip the position cost
	PUSH	P,T1		; Save T1
	PUSH	P,T2		; And T2
	PUSH	P,T4		; And T4
	MOVSI	T2,(T3)		; Get the number of accepts
	MOVX	T1,<XWD 1,1>	; Get the funny postition
	ADD	T2,T1		; To avoid special case
	XCT	$CRCPP(CRT)	; Get the position cost
	POP	P,T4		; Restore T4
	ADD	T4,T1		; Add it in
	POP	P,T2		; And T2
	POP	P,T1		; And T1

	SETZ	T3,		; Clear the number of accepts
LNCR.4:	AOJ	T4,		; Bump the cost for the replace

	JUMPG	P1,LNCR.5	; Have a first different character yet?
	MOVE	P1,P3		; No, get the number of the character

; Here to compare the suffixes of the strings

LNCR.5:	XCT	LNCXCT		; Get the other character
	CAIE	T1,(T2)		; Are these the same?
	 XCT	LNCXC2		; Get the position
	AOJA	P3,LNCR.3	; And go try for the next characters

; Here if the old string ran out first

LNCR.X:	ADD	T4,Y		; Make the total cost
	JUMPLE	Y,LNCR.E	; If no new characters left either, go handle it
	JUMPG	P1,LNCR.7	; No, go check for other special cases
	MOVE	T1,Y		; Yes, get the number of additional chars to type
	SETZ	T2,		; And clear the function pointer
	POPJ	P,		; And return

; Here if old string is not a prefix of the new one

LNCR.7:	TXNN	CRT,CR$DIC	; Have insert and delete character ops?
	 JRST	LNCR.E		; No, go return the correct cost
	MOVE	P3,OLDLDB	; Get the old LDB address
	MOVE	P4,NEWLDB	; Get the address of the new lines LDB
	LOAD.	T2,LDBNUM,(P3)	; And get the number of characters
	CAME	P2,T2		; Is old line suffix of new?
	 JRST	LNCR.8		; No, try for insert in middle
	MOVEI	T1,2		; Get the number of words we need
	MOVX	T2,.BTGEN	; And the block type
	SKIPL	UPCSFL		; Caller want the blocks?
	 SKIPA	T1,[EXP FNCTMP]	; No, use the temp
	PUSHJ	P,M$GBLK	; Get a block
	MOVE	P1,T1		; Remember the address
	MOVX	T2,<INSVL.($OPICH,CS$OPR)> ; Get the operation
	STOR.	Y,CSTRPT,+T2	; Store the repeat count
	MOVEM	T2,(P1)		; Save the function word
	MOVX	T2,<INSVL.($OPACH,CS$OPR)> ; Get the accept operation
	STOR.	P2,CSTRPT,+T2	; Store the repeat count
	MOVEM	T2,1(P1)	; Store the word
	MOVN	Y,Y		; Get the number of inserts we need
	HRLI	Y,(Y)		; Put in left half
	SETZ	X,		; Flag we are staring from left margin
	MOVX	P3,<INSVL.($OPACH,CS$OPR)> ; Get the initial function

LNCR.9:	MOVE	T1,P3		; Get the old operation
	XCT	$CRCIC(CRT)	; And get the insert cost
	MOVE	T2,P3		; Get the old word back
	PUSHJ	P,LINSUB	; And do the cost and repeat count rot
	DMOVE	P2,T1		; Save the items
	AOBJN	Y,LNCR.9	; Loop for all the inserts

	MOVE	T1,P2		; Get the total cost
	MOVE	T2,P1		; And the function block
	HRLI	T2,-2		; Make it an AOBJx pointer
	POPJ	P,		; And return



; Here if line is neither suffix or prefix of new line. Check if the
;new line is made by just inserting text into the old line.

LNCR.8:	SOJ	P1,		; Make P1 the number of initial accepts
	MOVE	P3,P2		; Get the number of final accepts
	ADD	P3,P1		; Plus intial accepts
	CAMGE	P3,T2		; Does this account for the entire old line?
	 JRST	LNCR.6		; No, must perform cost algorithm to get it correct
	MOVE	P2,T2		; Get the length of the old line
	SUB	P2,P1		; And make the actual number of final accepts needed
	MOVEI	T1,3		; Get the number of words we need
	MOVX	T2,.BTGEN	; Get the block type
	SKIPL	UPCSFL		; Caller want the blocks?
	 SKIPA	T1,[EXP FNCTMP]	; No, use the temp
	  PUSHJ	P,M$GBLK	; Get a block
	MOVX	T2,<INSVL.($OPACH,CS$OPR)> ; Get the accept operation
	STOR.	P1,CSTRPT,+T2	; Store the repeat count
	MOVEM	T2,(T1)		; Store the function word
	STOR.	P2,CSTRPT,+T2	; Store the repeat count
	MOVEM	T2,2(T1)	; Store the function
	MOVX	T2,<INSVL.($OPICH,CS$OPR)> ; Get the insert function
	STOR.	Y,CSTRPT,+T2	; Store the repeat count
	MOVEM	T2,1(T1)	; Store the function
	MOVN	Y,Y		; Get the number of inserts
	HRLI	Y,(Y)		; Into the left half
	HRRI	Y,(P1)		; Stuff the number of accepts in the right half
	MOVEI	X,(P1)		; Here also
	MOVE	P1,T1		; Remember the address of the block
	MOVE	P3,(P1)		; Get the insert word
	PUSHJ	P,LNCR.9	; Calcualte the cost
	HRLI	T2,-3		; Set the number of operations
	POPJ	P,		; And return

; Here if new line was shorter than the old line.

LNCR.Y:	AOJ	X,		; Correct back to right value
	MOVE	T1,X		; Yes, get the number of old chars left
	XCT	$CRCDE(CRT)	; Get the cost of deleting to the end of line
	ADD	T4,T1		; Get the total cost
	JUMPG	P1,LNCR.B	; No, try other cases
	MOVE	T1,T4		; Get the cost
	SETZ	T2,		; Clear the function block
	POPJ	P,		; And return

; Here if the new line is not a complete prefix of the old
; Check if it is a suffix of the old line

LNCR.B:	TXNN	CRT,CR$DIC	; Any delete and insert character functions?
	 JRST	LNCR.E		; No, can't do anything else
	MOVE	P4,NEWLDB	; Get the new line LDB address
	LOAD.	T2,LDBNUM,(P4)	; And get the number of characters on the line
	CAME	T2,P2		; All of the characters match?
	 JRST	LNCR.C		; No, go check for just deleting some chars
	MOVEI	T1,2		; Yes, need a two word block
	MOVX	T2,.BTGEN	; Get the block type
	SKIPL	UPCSFL		; Caller want the blocks?
	 SKIPA	T1,[EXP FNCTMP]	; No, use the temp
	  PUSHJ	P,M$GBLK	; And get a block of text
	MOVX	T2,<INSVL.($OPDCH,CS$OPR)> ; Get the delete character operation
	STOR.	X,CSTRPT,+T2	; Store the count
	MOVEM	T2,(T1)		; And store the function word
	MOVX	T2,<INSVL.($OPACH,CS$OPR)> ; Get the accept operator
	STOR.	P2,CSTRPT,+T2	; Store the repeat count
	MOVEM	T2,1(T1)	; And store the function word
	MOVN	X,X		; Make the AOBJx pointer
	MOVSI	X,(X)		;  .  .  .
	SETZ	Y,		; Clear the new position
	MOVE	P1,T1		; Get the block address
	MOVE	P3,(T1)		; Get the function word
	MOVE	T1,1(T1)	; Get the accept function for the first

LNCR.D:	XCT	$CRCDC(CRT)	; Get the delete character
	MOVE	T2,P3		; Get the previous word again
	PUSHJ	P,LINSUB	; Get the total cost and repeat count
	DMOVE	P2,T1		; Get the args
	MOVE	T1,P3		; Get the previous function
	AOBJN	X,LNCR.D	; Loop for all the deletes

	MOVE	T1,P2		; Get the cost
	MOVE	T2,P1		; Get the function block address
	HRLI	T2,-2		; set up the counter
	POPJ	P,		; And return

; Here if it might be possible to just delete a string somewhere internal
;to the string

LNCR.C:	SOS	P3,P1		; Get the number of initial accepts
	ADD	P3,P2		; And make the total
	CAMGE	P3,T2		; Can we just delete a portion of the old line to get the new one?
	 JRST	LNCR.6		; No, must only check a small
	MOVE	P3,T2		; Get the number of new chars
	SUB	P3,P1		; Get the number of chars left after initial insert
	MOVEI	T1,3		; Get the number of functions we need
	MOVX	T2,.BTGEN	; Get the block type
	SKIPL	UPCSFL		; Caller want the blocks?
	 SKIPA	T1,[EXP FNCTMP]	; No, use the temp
	  PUSHJ	P,M$GBLK	; Get a block
	MOVX	T2,<INSVL.($OPACH,CS$OPR)> ; Get the function type
	STOR.	P1,CSTRPT,+T2	; Store the repeat count
	MOVEM	T2,(T1)		; And store the word
	STOR.	P3,CSTRPT,+T2	; Store the final repeat count
	MOVEM	T2,2(T1)	; And remember the final accepts
	MOVX	T2,<INSVL.($OPDCH,CS$OPR)> ; Get the middle function (delete)
	STOR.	X,CSTRPT,+T2	; And store the repeat coutn
	MOVEM	T2,1(T1)	; Store the function word
	MOVE	P1,T1		; Get the block address into the correct place
	MOVE	T1,(T1)		; Get the initial function word
	MOVE	P3,T1		; Get the initial cost
	MOVN	X,X		; Make the AOBJx pointer
	MOVSI	X,(X)		;  .  .  .
	SETZ	Y,		; Clear the new position
	PUSHJ	P, LNCR.D	; And calc the cost
	HRLI	T2,-3		; Get the number of functions
	POPJ	P,		; And return


; Here if a portion of the old line is mapped into a portion of the new line.
LNCR.E:	SOJ	P1,		; Decrement to be the number of initial accepts

LNCR.6:	MOVE	P3,OLDLDB	; Get the old LDB address
	MOVE	P4,NEWLDB	; And the new
	MOVE	T1,P1		; Get the number of initial accepts
	IDIVI	T1,5		; Make it the word and byte offsets to skip
	LOAD.	T3,LDBTXT,(P3)	; Get the old text address
	HLL	T3,BTAB-1(T2)	; And set up the correct byte pointer
	ADDI	T3,(T1)		;  .  .  .
	MOVEM	T3,OLDPTR	; Save it
	MOVEM	T3,OLDSPT	; Also save here to reset OLDPTR after each column
	LOAD.	T3,LDBTXT,(P4)	; Get the address of the new text
	HLL	T3,BTAB-1(T2)	; Get the byte pointer set up
	ADDI	T3,(T1)		;  .  .  .
	MOVEM	T3,NEWPTR	; Store the pointer
	LOAD.	X,LDBNUM,(P3)	; Get the number of old chars
	LOAD.	Y,LDBNUM,(P4)	; And of new ones
	SUB	X,P1		; Get the number which we are not accepting
	SUB	X,P2		;  .  .  . 
	SUB	Y,P1		;  .  .  .
	SUB	Y,P2		;  .  .  .
	
; Here to calculate the cost based on insert/delete characters as well
;as accept and replace operations.
; The algorithm used is basically that given by Wagner and Fischer
;in "The String-to-String Correction Problem", JACM, January 1974,
;volume 21, no. 1.
;This implementation of the algorithm also carries along the operation
;which had the minimum cost at each point in the matrix, and the number
;of times any operation is repeated. This allows it to account for
;terminals which allow a single command to delete more than one character,
;require the terminal to be put into insert mode, or to include the
;cost of the positioning that is required after accepting characters.
;
; The algorithm works by creating a matrix of n columns by m rows,
;where n is the number of characters in the old line, and m is
;the number of characters in the new line.
;
;AC usage:
;	P1/	Address of matrix entry (row-1,column)
;	P2/	Address of matrix entry (row,column)
;	P3/	Minimum cost for this entry so far
;	P4/	Corresponding matrix entry
;	X/	Column index
;	Y/	Row index
;	CH/	Character new line for current position
;	CRT/	CRT block address
;
;Low segment locations:
;
;	OLDPTR/	Byte pointer to old line text
;	NEWPTR/	Byte pointer to new line text
;	OLDLDB/	Address of old LDB
;	NEWLDB/	Address of new LDB
;

; First we must set up column 0 and row 0 of the matrix. We do this
; by first calling the terminal dependent routine for get the initial
; entry (0,0). We then loop for the rest of row 0, calling the
; terminal dependent delete cost routine to get each entry based on the
; previous. Finally we loop for the rest of column 0, calling the
; terminal dependent insert cost routine for each entry.

LINC.0:	SETZ	T2,		; Clear the function block address
	MOVE	T1,T4		; And get a copy of the cost
	JUMPE	X,.POPJ		; Return if no string to worry about
	JUMPE	Y,.POPJ		; For either one
	SKIPGE	UPCSFL		; Want full algorithm?
	 TXNN	CRT,CR$DIC	; Have delete and insert character?
	  POPJ	P,		; Either not the same line or no insert/delete char
	MOVE	T3,TRMSPD	; Get the speed of the terminal
	$CHTYI(NOWAIT)		; Check for type ahead
	 CAMG	T1,LNCSPT(T3)	; Is the replacement cost low enough to just use that?
	  POPJ	P,		; Yes, return
	$SAVE	<BEGACC,ENDACC,NUMCOL,MATRIX> ; Save the beginning and ending accept locations
	MOVEM	P1,BEGACC	; And save the correct values
	MOVEM	P2,ENDACC	;  .  .  .
	AOJ	X,		; Get the number of columns total
	MOVEM	X,NUMCOL	; Save it
	MOVN	X,X		; Get the negative
	MOVSI	X,(X)		; And make an AOBJx pointer
	MOVEM	X,COLPTR	; Save it
	AOS	P1,Y		; Make this correct
	MOVN	Y,Y		; Get the negative
	MOVSI	Y,(Y)		; And make the row AOBJx pointer
	MOVEM	Y,ROWPTR	; Save it
	IMUL	P1,NUMCOL	; Multiply the number of row by the number of columns

; Now check if we should do the complete algorithm. P1 contains the
;number of iterations, and we have an estimate of the time per
;iteration. If the estimated time to perform this algorithm exceeds
;the assembly time constant, we will not bother doing it at all, and
;just return the replacement cost.

	NDS.	D$LNCT,	<^D4*^D1000>/^D24 ; Max time is about 4 seconds for whole screen

	MOVE	T3,LITRTM	; Get the time per iteration (in milliseconds)
	MUL	T3,P1		; Get the total estimate
	MOVEM	P1,NUMITR	; Save the number of iterations
	DIVX	T3,^D1000	; Get the real estimate for this time
	CAXL	T3,D$LNCT	; Will it take too long?
	 POPJ	P,		; Yes, just return now
	MOVX	T1,%NSUPT	; Get the current system up time
	GETTAB	T1,		; Get it
	 SETZ	T1,		; Couldn't?
	MULX	T1,^D1000	; Convert to milli-jiffies
	DIV	T1,JIFSEC	; And to milli-seconds
	MOVEM	T1,LBGUPT	; Save the time for later
	MOVE	T1,P1		; No, get the number of entries we need
	MOVX	T2,.BTGEN	; Get the block type
	PUSHJ	P,M$GBLK	; And get the matrix
	MOVEM	T1,MATRIX	; Save the address
	MOVE	P1,T1		; Get the address of the first element
	XCT	$CRCIN(CRT)	; Get the initial cost
	MOVEM	T1,(P1)		; Store it and point to row 0, column 1
	AOJ	P1,		; Point to correct entry
	AOBJP	X,.+1		; Bump the pointer

LINC.1:	MOVE	T1,-1(P1)	; Get the previous entry
	XCT	$CRCDC(CRT)	; Get the delete cost
	MOVE	T2,-1(P1)	; Get the previous entry back
	PUSHJ	P,LINSUB	; Set the correct repeat info
	MOVEM	T2,(P1)		; Store the entry and point to the next
	AOJ	P1,		; Point to correct entry
	AOBJN	X,LINC.1	; And loop for all the entrys

; Now set up column 0

	MOVE	P1,MATRIX	; Get the matrix address back
	MOVE	P2,P1		; Get a copy
	ADD	P2,NUMCOL	; Point to the first row column 0
	MOVE	X,COLPTR	; Reset the column number
	AOBJP	Y,.+1		; And bump to row 1, column 0

LINC.2:	MOVE	T1,(P1)		; Get the previous entry
	XCT	$CRCIC(CRT)	; Get the insert cost
	MOVE	T2,(P1)		; Get the previous entry back
	PUSHJ	P,LINSUB	; Get the repeat count correct
	MOVEM	T2,(P2)		; Store the entry
	ADD	P1,NUMCOL	; Move down a row
	ADD	P2,NUMCOL	;  .  .  .
	AOBJN	Y,LINC.2	; Loop for all the insert entries

; Now to loop through the rest of the matrix to find the cheapest
;path.

	MOVE	Y,ROWPTR	; Get the pointer to the row
	AOBJP	Y,.+1		; Point to the row 1
	MOVE	P1,MATRIX	; Set up P1

; Here to start a new row. Reset the necessary pointers

LINC.3:	MOVE	X,COLPTR	; Get the column pointer
	AOBJP	X,.+1		; Point to column 1
	AOS	P2,P1		; Skip column 0 and get a copy
	ADD	P2,NUMCOL	; And point to the correct row
	MOVE	T1,OLDSPT	; Get the byte pointer
	MOVEM	T1,OLDPTR	; And reset the running copy
	ILDB	CH,NEWPTR	; Get the next character from the new string

; Here to do the next entry in the column

LINC.4:	ILDB	T1,OLDPTR	; Get an old character
	CAIE	T1,(CH)		; Is the character the same?
	 JRST	LINC.A		; No, must try replacing it
	MOVE	T1,-1(P1)	; Yes, get the diagonal entry
	STORI.	$OPACH,T2,CSTOPR,+T1 ; Store the operation
	ZERO.	,CSTCST,+T1	; Clear the cost
	JRST	LINC.B		; And go join common code

; Here to try a replace operation

LINC.A:	MOVE	T1,-1(P1)	; Get the entry up the diagonal
	XCT	$CRCRP(CRT)	; And get the replace cost

; Here for either a replace or accept operation. Get any extra costs, and
;save as cheapest so far.

LINC.B:	MOVE	T2,-1(P1)	; Get the previous entry back
	PUSHJ	P,LINSUB	; Add any positioning cost and update repeat count

	DMOVE	P3,T1		; Remember this as cheapest so far

; Now try an insert operation

	MOVE	T1,(P1)		; Get the entry above the current one
	XCT	$CRCIC(CRT)	; And get the insert cost
	MOVE	T2,(P1)		; Get the previous entry back
	PUSHJ	P,LINSUB	; Do positioning and repeat count stuff
	CAMG	T1,P3		; Is the insert cheaper than the replace or accept?
	 DMOVE	P3,T1		; Yes, remember it instead

; Now to try the delete character operation

	MOVE	T1,-1(P2)	; Get the previous column entry
	XCT	$CRCDC(CRT)	; Get the delete cost
	MOVE	T2,-1(P2)	; Get the previous entry back
	PUSHJ	P,LINSUB	; Set repeat count and correct cost
	CAMG	T1,P3		; Is this better than the cheapest so far?
	 DMOVE	P3,T1		; Yes, get the entry
	MOVEM	P4,(P2)		; Save the cheapest one we found

	AOJ	P1,		; Bump the previous row pointer
	AOJ	P2,		; And the current pointer

LINC.Z:	AOBJN	X,LINC.4	; Loop for all of the columns on this row
	AOBJN	Y,LINC.3	; All done with this row, step to the next

; Here after the matrix is built. Now we must build the function
;block by traversing the matrix backwards.
;We will build the function block in the matrix. The method for
;building the function block is to start at the final entry in the matrix
;and work backwards. The entries which are used from that point on are
;found by working backwards using the operations and the repeat counts.
; We come in with P2= the address of the final entry +1

	SOJ	P2,		; Back up to the final entry
	LOAD.	P4,CSTCST,(P2)	; Remember the cost to return later
	MOVX	T1,%NSUPT	; Get the uptime now
	GETTAB	T1,		;  .  .  .
	 MOVX	T1,<D$LNCE*^D60>/^D1000 ; Use our estimate
	MULX	T1,^D1000	; Muliply by a thousand
	DIV	T1,JIFSEC	; Convert to milliseconds
	SUB	T1,LBGUPT	; Get the elapsed time
	MULX	T1,^D1000	; Times a thousand
	DIV	T1,NUMITR	; And get the time per thousand iterations
	MOVEM	T1,LITRTM	; Save it for next call
	MOVE	T1,P2		; Get the address of the last entry
	PUSHJ	P,BLDFNC	; And build the function block
	MOVE	T1,P4		; Get the total cost
	DMOVE	P1,T1		; Get the return values
	MOVE	T1,MATRIX	; Get the address of the matrix
	PUSHJ	P,M$RBLK	; Return the block
	DMOVE	T1,P1		; Get back the return values
	POPJ	P,		; And return
	SUBTTL	Screen editing -- BLDFNC - Build a function block from a matrix


;+
;.hl2 BLDFNC
; This routine will build a function block from a cost calculation
;matrix.
;.b.lit
;
; Usage:
;	MOVE	T1,Address of last entry in matrix
;	PUSHJ	P,BLDFNC
;	 (return, T1= AOBJx pointer to function block)
;
;.end literal
;-

BLDFNC:	$SAVE	<P1,P2,P3>	; Save some room
	SETZ	P3,		; Initialize the count of functions
	MOVE	P1,T1		; Get a copy of the address
	MOVE	P2,T1		; Here also

BLDF.0:	MOVE	T1,(P2)		; Get the entry
	LOAD.	T2,CSTOPR,+T1	; Get the operation
	LOAD.	T3,CSTRPT,+T1	; And the repeat count
	CAXN	T2,$OPACH	; Is it an accept operation?
	 JUMPE	T3,BLDF.D	; Yes, if the repeat count is zero we are
				; done (back at row 0, column 0)
IFN FTDEBUG,<
	SKIPN	T3		; Have a repeat count?
	 STOPCD	ZRC,<Zero function repeat count found in BLDFNC>
	CAML	P1,MATRIX	; Make sure this is ok
	 CAMG	P2,MATRIX	; Make sure we haven't gone back too far
	  STOPCD BFL,<BLDFNC lost start of cost matrix>
> ; End of IFN FTDEBUG
	MOVEM	T1,(P1)		; Store the operation
	SOJ	P1,		; Back up one entry
	AOJ	P3,		; Count that we used it
	XCT	BLFTBL(T2)	; Move to the next entry
	JRST	BLDF.0		; And go process it

TABDEF	BLF,$OP
 TABENT	ACH,<PUSHJ P,BLFACH>	; Accept character, move up diagonal
 TABENT RCH,<PUSHJ P,BLFACH>	; Replace character, also up diagonal
 TABENT	ICH,<PUSHJ P,BLFICH>	; Insert character, move up n rows
 TABENT	DCH,<SUB P2,T3>		; Delete character, move back n columns
TABEND

; Here for accept or replace operations. We must move up the diagonal

BLFACH:	SUB	P2,T3		; Move over the correct number of columns

; Join in here for insert character operations. This must just move up
;the correct number of rows

BLFICH:	IMUL	T3,NUMCOL	; Calculate the amount for the upward movement
	SUB	P2,T3		; And move up the correct amount
	POPJ	P,		; Return
; Here after the functions have been collected in the matrix. P3
;contains the number of entries, P1 the address of the first.

BLDF.D:	MOVE	T1,P3		; Get the number of words we need
	CAIN	T1,1		; Only need one word?
	 JRST	[SETZB	T2,T1		; Since can only be replace or accept
		POPJ	P,]		; Just return the cost
	SKIPE	BEGACC		; Any starting accepts to add?
	 AOS	T1,P3		; Yes, count them
	SKIPE	ENDACC		; Any ending accepts to add?
	 AOS	T1,P3		; Yes, count it
	MOVX	T2,.BTGEN	; Get the block type
	PUSHJ	P,M$GBLK	; Get the core
	MOVE	T2,T1		; Save a copy
	MOVN	T3,P3		; Get the number of entries
	HRLI	T2,(T3)		; And make the AOBJx pointer
	SKIPN	T3,BEGACC	; Any starting accepts?
	 JRST	BLDF.F		; No, skip this
	LSH	T3,<ALIGN.(CS$RPT)> ; Yes, put into correct place for repeat count
	TXO	T3,<INSVL.($OPACH,CS$OPR)> ; Flag it is an accept
	MOVEM	T3,(T1)		; Store the first accepts and point to the next
	AOJ	T1,		;  .  .  .
	SOJ	P3,		; And one less to BLT
BLDF.F:	MOVE	T3,T1		; Get a copy of the address
	HRLI	T1,1(P1)	; Get where we are coming from
	ADD	T3,P3		; And make the final address +1
	SKIPE	ENDACC		; Any ending accepts?
	 SOJ	T3,		; Yes, BLT one less
	BLT	T1,-1(T3)	; Move the functions
	SKIPN	T1,ENDACC	; Any ending line accepts?
	 POPJ	P,		; No, all done
	LSH	T1,<ALIGN.(CS$RPT)> ; Yes, put into correct place for repeat count
	TXO	T1,<INSVL.($OPACH,CS$OPR)> ; Flag it is an accept
	MOVEM	T1,(T3)		; Store the first accepts
	POPJ	P,		; And return it
	SUBTTL	Screen editing -- LNCSPT -- Table of minimum costs

; This table is used to determine whether the insert/delete character
;cost algorithm should be used. The table is indexed by the terminal
;transmit speed. Anytime the replacement cost will take less than
;D$ICSP milli seconds, the replacement will be done instead of the
;insert/delete algoritm.

NDS.	D$ICSP,	^D100		; Number of milliseconds to update a line

DEFINE ENT(SPEED,CPS,IDX),<

	TABENT	SPEED,<<CPS*D$ICSP>/^D1000>
>
	RADIX	10		; Go to base 10 for this table

TABDEF	LNCSPT,.TS,0
	TSPEED			; Expand the macro
TABEND

	RADIX	8		; Back to octal
	SUBTTL	Screen updating routines -- LINCST -- LINSUB - Fix up a matrix entry

;+
;.HL2 LINSUB
; This routine is called to fix up a cost matrix entry returned from
;the terminal dependent routines. It will add the previous cost, any
;cost due to a required positioning operation, and set the repeat
;count for the entry correctly.
;.b.lit
;
; Usage:
;	MOVE	T1,New entry from terminal dependent routine
;	MOVE	T2,Previous entry
;	PUSHJ	P,LINSUB
;	 (Return, T1= cost in characters, T2=updated new entry)
;
;.end literal
;-

LINSUB:	LOAD.	T3,CSTOPR,+T2	; Get the previous operation
	CFME.	T4,CSTOPR,+T1,T3 ; Was it the same as the new one?
	 JRST	LINS.0		; No, check for positioning cost
	LOAD.	T3,CSTRPT,+T2	; Get the repeat count
	AOSA	T3		; Plus one
LINS.1:	 MOVEI	T3,1		; Here if starting a new operation, this is first one
	STOR.	T3,CSTRPT,+T1	; Store the new repeat count
	LOAD.	T3,CSTCST,+T2	; Get the old cost
	MOVE	T2,T1		; Copy the new entry
	LOAD.	T1,CSTCST,+T2	; Get the new cost
	ADD	T1,T3		; Make the total cost
	CAXL	T1,<1_<WID(CS$CST)>-1> ; Is it the maximum possible cost?
	 MOVX	T1,.INFIN	; Yes, get infinity
	STOR.	T1,CSTCST,+T2	; Store the total cost
	POPJ	P,		; And return

; Here when we are starting a new operation

LINS.0:	CAXE	T3,$OPACH	; Was the previous operation an accept?
	 JRST	LINS.1		; No, no position cost needed
	PUSH	P,T2		; Save the old entry
	PUSH	P,T1		; And the new
	MOVX	T1,<XWD 1,1>	; Set up to calculate the position cost
	LOADS.	T2,CSTRPT,+T2	; Get the number of accepts we are ending
	ADD	T2,T1		; Make the correct position
	XCT	$CRCPP(CRT)	; Get the positioning cost
	POP	P,T2		; Get the new entry back
	LOAD.	T3,CSTCST,+T2	; Get the cost from that
	ADD	T1,T3		; Get the total
	LOAD.	T3,CSTCST,(P)	; Get the old cost
	ADD	T1,T3		; Add that also
	CAXL	T1,<1_<WID(CS$CST)>-1> ; Is it the maximum possible cost?
	 MOVX	T1,.INFIN	; Yes, get infinity
	STOR.	T1,CSTCST,+T2	; Store the total cost
	POP	P,(P)		; Fix the stack
	STORI.	1,T3,CSTRPT,+T2 ; Store the new repeat count
	POPJ	P,		; nd return
	SUBTTL	Screen editing -- Debugging output routines -- D$LINS

;+
;.HL1 Debugging output routines
; The following routines are used when debugging to output the
;screen update information for use in debugging the cost calculation
;routines.
;.hl2 D$LINS
; This routine will output the lines which are being changed.
;.lit
;
;Usage:
;	X/ Pointer to old LDB's
;	Y/ Pointer to new LDB's
;	PUSHJ	P,D$LINS
;	 (return, all ac's intact)
;
;.end lit
;-

IFN FTDEBUG,<

D$LINS:	SKIPN	UPDDEB			; Want debugging output?
	 POPJ	P,			; No, skip this
	$SAVE	<T1,T2,T3,T4,X,Y,CH>	; Save some ac's
DLIN.1:	LOAD.	T4,LDBLIN,(X)		; Get the old line number
	XMOVEI	T1,[$STRING(<^X/LOGC.0/old^D/T4/:^I/9/ ^N>)] ; Get the string to type
	PUSHJ	P,T$TYPE		; Output the data
	LOAD.	T4,LDBTXT,(X)		; Get the address of the text
	LOAD.	T3,LDBNUM,(X)		; And get the character count
	PUSHJ	P,DLIN.S		; Type the text and a CRLF

	LOAD.	T4,LDBLIN,(Y)		; Get the new line number
	XMOVEI	T1,[$STRING(<^X/LOGC.0/new^D/T4/:^I/9/ ^N>)] ; Get what to type
	PUSHJ	P,T$TYPE		; Send the output
	LOAD.	T4,LDBTXT,(Y)		; Get the address of the text
	LOAD.	T3,LDBNUM,(Y)		; Get the number of chars to output
	PUSHJ	P,DLIN.S		; Output the text

	ADDX	X,$LDLEN		; Advance the pointers
	ADDX	Y,$LDLEN-1		;  .  .  .
	AOBJN	Y,DLIN.1		; And loop for all the lines

	XMOVEI	T1,[$STRING(<^X/LOGC.0/------------------>)] ; Delimit the lines
	PUSHJ	P,T$TYPE		; Type it out
	POPJ	P,			; And return


; Subroutine to output the text of the lines

DLIN.S:	JUMPE	T3,D$CRLF		; Any chars?
	TXO	T4,<POINT 7,>		; Yes, set up the byte pointer

DLIN.U:	ILDB	CH,T4			; Get a character
	PUSHJ	P,LOGC.0		; Output it
	SOJG	T3,DLIN.U		; Loop for all the characters

D$CRLF:	MOVX	CH,.CHCRT		; Get a CR
	PUSHJ	P,LOGC.0		; Output the character
	MOVX	CH,.CHLFD		; Get the LF
	PJRST	LOGC.0			; And output it
> ; End of IFN FTDEBUG
	SUBTTL	Screen editing -- Debugging output routines -- D$OMTX

;+
;.HL2 D$OMTX
; This routine will output a cost matrix.
;-

IFN FTDEBUG,<

D$OMTX:	SKIPN	UPDDEB			; Want debugging output?
	 POPJ	P,			; No, all done
	$SAVE	<T1,T2,T3,T4,P1,P2,P3,P4,CH,X,Y> ; Save some ac's

	MOVE	P1,MATRIX		; Get the pointer to the matrix
	MOVN	X,NUMROW		; Get the number of rows
	MOVSI	X,(X)			; Set up the row counter

; Loop for each row

OMTX.1:	MOVN	Y,NUMCOL		; Get the number of columns it has
	MOVSI	Y,(Y)			; Set up as a counter
	SETZ	P2,			; Clear the count per line

OMTX.2:	SOJG	P2,OMTX.3		; Room for more?
	PUSHJ	P,D$CRLF		; No, do a CRLF
	MOVE	P2,UPDDEB		; Get the number of items/line

OMTX.3:	MOVEI	T2,(X)			; Get the current row number
	MOVEI	T3,(Y)			; And the current column
	LOAD.	T4,CSTOPR,(P1)		; Get the operation
	XMOVEI	T4,D$OPRT(T4)		; Get the pointer to the text
	LOAD.	P3,CSTRPT,(P1)		; Get the repeat count
	LOAD.	P4,CSTDEP,(P1)		; And the dependent info
	LOAD.	CH,CSTCST,(P1)		; And the cost
	XMOVEI	T1,[$STRING(<^X/LOGC.0/(^D/T2/,^D/T3/):^T/(T4)/-^D/P3/,D=^O/P4/,C=^D/CH/	^N>)]
	PUSHJ	P,T$TYPE		; Type the row and column

	AOJ	P1,			; Increment the pointer
	AOBJN	Y,OMTX.2		; And loop for everything in this row

	PUSHJ	P,D$CRLF		; Do an extra CRLF
	AOBJN	X,OMTX.1		; And loop for all rows

	XMOVEI	T1,[$STRING(<^X/LOGC.0/=========================>)]
	PJRST	T$TYPE			; Type the separtion and return


	TABDEF	D$OPRT,$OP,<ASCIZ |??|>
	 TABENT	DCH,<ASCII |DL|>
	 TABENT	ICH,<ASCII |IN|>
	 TABENT	RCH,<ASCII |RP|>
	 TABENT	ACH,<ASCII |AC|>
	 TABENT	SUP,<ASCII |SU|>
	 TABENT	SDN,<ASCII |SD|>
	 TABENT	NOP,<ASCII |NO|>
	TABEND
> ; End of IFN FTDEBUG
	SUBTTL	Screen editing -- Update a line

;+
;.HL1 UPDLIN
;This routine will use a function block to update a line.  It will
;call the terminal dependent routines to delete to the end of a line
;and to insert/delete characters and postion the cursor.
;.literal
;
; Usage:
;	MOVEI	T1,Function.block.address
;	MOVEI	T2,Old.LDB.address
;	MOVEI	T3,New.LDB.address
;	PUSHJ	P,UPDLIN
;	(Return)
;
;.end literal
;-

UPDLIN:	$SAVE	<P1,P2,P3,P4>	; Save a few registers
	PUSH	P,T1		; Save the block address
	MOVE	P1,T1		; Get the block address again
	SETZ	X,		; Clear the X offset
	LOAD.	P2,LDBTXT,(T2)	; Get the address of the text block
	HRLI	P2,(POINT 7)	; Build the byte pointer
	LOAD.	P3,LDBTXT,(T3)	; Get the address of the text block
	HRLI	P3,(POINT 7)	; Build the byte pointer
	LOAD.	P4,LDBNUM,(T2)	; Get the number of characters on this line
	LOAD.	T2,LDBNUM,(T3)	; And the other
	JUMPE	T1,UPDL.1	; If no function block dumb terminal


; Here is the main function processing loop

UPDL.0:	LOAD.	T1,CSTRPT,(P1)	; Get the repeat count
	LOAD.	T2,CSTOPR,(P1)	; Get the operation
	PUSHJ	P,@UPLTBL(T2)	; Dispatch to the correct routine
	AOBJN	P1,UPDL.0	; Loop for all functions
	POP	P,T1		; Restore the block address
	PJRST	M$RBLK		; Return the block
; Here if there is no function block to use.  Therefore we must do
; everthing the hard way.  Just check character by character to see
; if we should accept or replace the character.

; Enter with acs as follows:
;	P2/	byte pointer to old line
;	P3/	byte pointer to new line
;	P4/	number of characters on old line
;	T2/	number of characters on new line


UPDL.1:	POP	P,(P)		; Remove the zero
	MOVE	T1,T3		; Get the new LDB address
	PJUMPE	P4,SC$WLN	; If there were no characters on the old line, just write out the new one
	MOVE	T3,T2		; Assume new line is shorter
	CAMGE	P4,T3		; Is it?
	 MOVE	T3,P4		; No, use length of old line
	SUBM	T2,P4		; Make the new length - old length
	SETZB	X,T4		; Starting at the beginning of the line

; First loop through the number of characters that are on both lines

UPDL.2:	MOVE	T2,CH		; Save a copy of the character
	SOJL	T3,UPDL.4	; Have any characters on shortest line?
	ILDB	CH,P3		; Yes, get the new character
	ILDB	T1,P2		; And the old one
	CAIN	CH,(T1)		; Same character?
	 AOJA	T4,UPDL.2	; Yes, loop for next character
	JUMPE	T4,UPDL.3	; Is this the first different character after
				; a run of like ones?
	CAIE	T4,1		; Only one character?
	 JRST	UPDL.7		; No, go handle it
	EXCH	T2,CH		; Switch the two characters
	XCT	$CRTCH(CRT)	; Type the character
	EXCH	T2,CH		; Switch them back
	SETZ	T4,		; Clear the number to increment X
UPDL.7:	ADD	X,T4		; Increment the position
	SETZ	T4,		; And clear the count
UPDL.3:	XCT	$CRTCH(CRT)	; No, type the character
	JRST	UPDL.2		; And go for the next

; Here after beginning of line is done. Now we either have to do a delete
; to end of line or finish writing the text

UPDL.4:	JUMPL	P4,UPDL.6	; If old line was longer, go do a delete to end of line
	JRST	.+2		; Skip in the first time

UPDL.5:	 MOVX	T2," "		; After first character, forward spacing is done with a space
	SOJL	P4,.POPJ	; Loop for all the characters
	ILDB	CH,P3		; Get a character
	CAIN	CH," "		; Is it a space?
	 AOJA	T4,UPDL.5	; Yes, just increment the position
	JUMPE	T4,UPDL.9	; Any spaces first?
	CAIE	T4,1		; Only one?
	 JRST	UPDL.A		; No, skip this
	EXCH	T2,CH		; Save CH
	PUSHJ	P,SC$CHR	; Type the character
	EXCH	T2,CH		; Restore the character
	SETZ	T4,		; Clear the offset
UPDL.A:	ADD	X,T4		; Increment the position
	SETZ	T4,		; And clear the count
UPDL.9:	PUSHJ	P,SC$CHR	; And type the character
	JRST	UPDL.5		; Loop for all the characters

; Here to do the delete to end of line

UPDL.6:	ADD	X,T4		; Correct the position
	PUSHJ	P,SC$POS	; Position the cursor
	MOVM	T1,P4		; Get the number of characters to delete
	XCT	$CRDEL(CRT)	; Delete to end of line
	POPJ	P,		; Return

; The following is the dispatch table for the operations that are
; being preformed on the line


TABDEF	UPL,$OP
 TABENT	SUP,<EXP UPLSUP>	; Scroll up (Section only item)
 TABENT	SDN,<EXP UPLSDN>	; Scroll down (Section only item)
 TABENT	ACH,<EXP UPLACH>	; Accept the character
 TABENT	DCH,<EXP UPLDCH>	; Delete the character
 TABENT	ICH,<EXP UPLICH>	; Insert the character
 TABENT	RCH,<EXP UPLRCH>	; Replace the character
 TABENT	NOP,<EXP UPLNOP>	; No-op operation
TABEND
	SUBTTL	Screen editing -- Update a line -- Scroll up

;+
;.HL2 UPLSUP
;This routine is not expected to be executed.  If it does then an internal
;error has occured.
;-

UPLSUP:	STOPCD	SUL,<Attempt to scroll up a line>





	SUBTTL	Screen editing -- Update a line -- Scroll down

;+
;.HL2 UPLSDN
;This routine is not expected to be executed. If it does then an internal
;error has occured.
;-

UPLSDN:	STOPCD	SDL,<Attempt to scroll down a line>
	SUBTTL	Screen editing -- Update a line -- Accept

;+
;.HL2 UPLACH
; This routine will accept the character given.  It will update the byte
;pointers that are being kept and update the line position.
;.literal
;
; Usage:
;	LOAD.	T1,CSTRPT,(P1)
;	PUSHJ	P,UPLACH
;	(Return)
;
;.end literal
;-

UPLACH:	ADD	X,T1		; Update the X position
	IDIVI	T1,5		; Determine the number of words
	ADD	P2,T1		; Update the address
	ADD	P3,T1		; . . .
UPLA.0:	SOJL	T2,.POPJ	; Return if no more bytes
	IBP	P2		; Increment the byte pointers
	IBP	P3		; . . .
	JRST	UPLA.0		; Loop
	SUBTTL	Screen editing -- Update a line -- Delete

;+
;.HL2 UPLDCH
;This routine will delete characters from the screen.  It will update the
;number of characters that are on the line and then position the cursor
;and delete the characters by calling a terminal dependent routine.
;.literal
;
; Usage:
;	LOAD.	T1,CSTRPT,(P1)
;	PUSHJ	P,UPLDCH
;	(Return)
;
;.end literal
;-

UPLDCH:	SUB	P4,T1		; Decrement the number of characters on the line
	PUSHJ	P,UPLBYT	; Update the byte pointer
	PUSHJ	P,SC$POS	; Update the cursor postion
	TXC	P1,LH.ALF	; Is this the last function ?
	TXCN	P1,LH.ALF	; . . .
	 JRST	UPLD.0		; Yes, Then just do a DEL
	XCT	$CRDCH(CRT)	; Cause the characters to go away
	POPJ	P,		; Return to the caller

UPLD.0:	XCT	$CRDEL(CRT)	; Delete to the end of the line
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Update a line -- Replace

;+
;.HL2 UPLRCH
;This routine will replace characters that are on the screen.  It will
;first update the byte pointer(Old LDB) and then position the cursor
;to the correct place.  After that is done it will read characters
;from the new LDB and print them on the terminal.
;.literal
;
; Usage:
;	LOAD.	T1,CSTRPT,(P1)	; Get the repeat count
;	PUSHJ	P,UPLRCH
;	(Return)
;
;.end literal
;-

UPLRCH:	PUSHJ	P,UPLBYT	; Update the byte pointer
	PUSHJ	P,SC$POS	; Update the position

UPLR.0:	ILDB	CH,P3		; Get the first character to type
	XCT	$CRTCH(CRT)	; Type a character
	SOJG	T1,UPLR.0	; Loop for all the characters
	POPJ	P,		; Return if done
	SUBTTL	Screen editing -- Update a line -- Insert

;+
;.HL2 UPLICH
;This routine will cause characters to be inserted on the line.  It will
;update the number of characters that are on the line first.  If the number
;of characters exceeds the number allowed on a line it will search the function
;block for a delete function.  It will cause the delete to happen and then
;see if there is room for the characters on the line.  After that is
;done the characters will be inserted on the line.
;-

UPLICH:	$SAVE	<P1>		; Save P1
	ADD	P4,T1		; Update the number of characters on the line
	CAMLE	P4,SCRWID	; Is there room on the line ?
	  PUSHJ	P,UPLISB	; No, Call the subroutine to handle this case
	TXC	P1,LH.ALF	; Is this the last function on the line ?
	TXCN	P1,LH.ALF	; . . .
	 JRST	UPLI.1		; Yes, Just type the characters
	MOVE	P1,T1		; Get the count

UPLI.0:	ILDB	CH,P3		; Get the character to insert
	PUSHJ	P,SC$POS	; Position to correct place
	XCT	$CRICH(CRT)	; Cause the character to be inserted
	SOJG	P1,UPLI.0	; Loop back
	POPJ	P,		; Return to the caller

UPLI.1:	ILDB	CH,P3		; Get a character
	PUSHJ	P,SC$CHR	; Type the character on the terminal
	SOJG	T1,UPLI.1	; Loop for all character
	POPJ	P,		; Return to the caller

; Here to handle the case of not enough space on the line to insert the
; characters.  First try and delete the characters so that the insert will
; not cause characters to fall off of the end of the line.  (Into the
; bit bucket!!)

UPLISB:	$SAVE	<X,P1,P2,T1>	; Save a few registers
UPLI.2:	AOBJP	P1,.POPJ	; If no more give up (What else STOPCD ?)
	LOAD.	T2,CSTOPR,(P1)	; Get the operation
	LOAD.	T1,CSTRPT,(P1)	; Get the repeat count
	CAXN	T2,$OPDCH	; Is this a delete ?
	  JRST	UPLI.3		; Yes, Go process it
	CAXE	T2,$OPICH	; Is this an insert ?
	 CAXN	T2,$OPNOP	; Or a converted function ?
	  JRST	UPLI.2		; Yes, ignore these functions
	ADD	X,T1		; Update the X position
	JRST	UPLI.2		; Loop back

UPLI.3:	MOVX	T2,$OPNOP	; Convert the operation to a no-op
	STOR.	T2,CSTOPR,(P1)	; . . .
	PUSHJ	P,UPLDCH	; Cause the delete to happen
	CAMG	P4,SCRWID	; Insert fit yet ?
	  POPJ	P,		; Yes, Return and do it
	JRST	UPLI.2		; No, Try again
	SUBTTL	Screen editing -- Update a line -- No-op

;+
;.HL2 UPLNOP
;This routine will just advance the byte pointer to the next position
;since it could not be done in the insert code.
;.literal
;
; Usage:
;	LOAD.	T1,CSTRPT,(P1)	; Get the repeat count
;	PUSHJ	P,UPLNOP
;	(Return)
;
;.end literal
;-

UPLNOP:	SKIPA			; Skip enter into the routine
	FALL	UPLBYT		; Make sure the routine is next
	SUBTTL	Screen editing -- Update a line -- Subroutines -- UPLBYT

;+
;.HL3 UPLBYT
;This routine will advance the old byte pointer.
;.literal
;
; Usage:
;	LOAD.	T1,CSTRPT,(P1)
;	PUSHJ	P,UPLBYT
;	(Return)
;
;.end literal
;This routine will not smash T1.
;-

UPLBYT:	$SAVE	<T1>		; Save T1
	IDIVI	T1,5		; Compute the number of words
	ADD	P2,T1		; Update the address field
UPLB.0:	SOJL	T2,.POPJ	; Return if done
	IBP	P2		; Increment the byte pointer
	JRST	UPLB.0		; Loop back if not done yet
	SUBTTL	Screen editing -- SC$WRT - Write a character to the screen buffer


;+
;.hl1 SC$WRT
; This routine will write a character into the screen buffer.
;It will do any conversion into printable characters that is needed.
;.b.literal
; Usage:
;	MOVE	T4,Byte.pointer
;	MOVEI	CH,Character
;	MOVE	X,X position
;	PUSHJ	P,SC$WRT
;	(return, T4 and X updated)
;
;.end literal
;-

SC$WRT:	CAXN	CH,.CHESC	; Is this an escapse?
	 MOVX	CH,"$"		; Yes, use a dollar sign
	CAXN	CH,.CHDEL	; Delete?
	 SETO	CH,		; Yes, make it one less than a null
	CAXL	CH," "		; Special processing ?
	 JRST	SWRT.3		; No - Just continue
	CAXE	CH,.CHTAB	; Is this a tab ?
	 JRST	SWRT.2		; No - Just a control character

; Here because TABs need special processing.   We must determine
; how many of the spaces that must be output

	MOVX	CH," "		; Get the character to output
SWRT.1:	PUSHJ	P,SWRT.3	; Output the character and update X
	TXNE	X,7		; . . .
	 JRST	SWRT.1		; Not at the tab stop - loop
	POPJ	P,		; All done, return

; Here if the character to be output is a control character
; save the character output a "^" and then the control character +40

SWRT.2:	PUSH	P,CH		; Save the character
	MOVX	CH,"^"		; Get the character to output
	PUSHJ	P,SWRT.3	; Output the character
	POP	P,CH		; Restore the character
	ADDX	CH,"A"-.CHCNA	; Convert to upper case equivalent
SWRT.3:	IDPB	CH,T4		; Store the character
	AOJA	X,.POPJ		; Return with X updated
	SUBTTL	Screen editing -- Subroutines -- SC$WLN - Write a line

;+
;.hl1 SC$WLN
; This routine will output a line to the screen. It should be used when
;it is known that the line on the screen is already blank.
;.b.literal
;
; Usage:
;	MOVE	T1,LDB.address
;	PUSHJ	P,SC$WLN
;	 (return, line written)
;
;.end lit
;-

SC$WLN:	$SAVE	<P1,P2,P3,P4>	; Save some ac's
	$SAVE	<X,Y>		; Save X and Y also
	$SAVE	<CH>		; And CH
	MOVE	P1,T1		; Get the argument
	LOAD.	Y,LDBLIN,(P1)	; Get the line number
	SETZ	X,		; Clear the column
	LOAD.	P2,LDBTXT,(P1)	; Get the text address
	HRLI	P2,(POINT 7,)	; Set up the byte pointer
	LOAD.	P3,LDBNUM,(P1)	; And get the number of chars
	MOVX	P4,.MINFI	; Get negative infinty
	
SWLN.1:	CAML	X,P3		; Done yet?
	 POPJ	P,		; Yes, return
	ILDB	CH,P2		; No, get a character
	CAIN	CH," "		; Is it a printing character?
	 AOJA	P4,[AOJA X,SWLN.1] ; No, count it and continue
	JUMPLE	P4,SWLN.2	; Any spaces first?
	HRL	T1,CURPOS+$OFSX	; Get the current X position
	HRR	T1,CURPOS+$OFSY	; And the line number
	HRLI	T2,(X)		; Put in correct half
	HRRI	T2,(Y)		;  .  .  .
	XCT	$CRCPP(CRT)	; Get the position cost
	CAMG	T1,P4		; Cheaper to output the spaces?
	 JRST	SWLN.2		; No, go do the position
	SUB	X,P4		; Put in correct place
	PUSH	P,CH		; Save the character
	MOVEI	CH," "		; Get a space
	PUSHJ	P,SC$CHR	; Output it
	SOJG	P4,.-1		; And loop for all that are necessary
	POP	P,CH		; Restore CH

SWLN.2:	SETZ	P4,		; Clear the count of spaces
	PUSHJ	P,SC$CHR	; Output the character
	JRST	SWLN.1		; And go for the next
	SUBTTL	Screen editing -- Subroutines -- DISPLY - Deterine what to display

;+
;.HL1 DISPLY
;This routine will determine what should be displayed on the users screen.
;It will attempt to center the lines around the current PT for that
;text buffer.
;.literal
;
; Usage:
;	MOVE	T2,QRG address
;	MOVE	T1,Character address to center around
;	PUSHJ	P,DISPLY
;	(Return)
;
; Or
;
;	MOVE	T2,QRG block address
;	MOVE	T1,Character address of start of line
;	MOVE	T3,Line number to fix display with
;	PUSHJ	P,FDSPLY
;	(Return)
;
;
; Returns:
;	T1 - Character address of the first line
;	T2 - Character address of the end of the last line
;
;.end literal
;-
	DS$FDS==1B1		; Flag in P2 that this is an FDSPLY call
	DS$BLN==1B2		; Flag in P2 to leave last line blank
	DS$CMD==1B3		; Flag that this is the command buffer
	DS$FIX==1B4		; Flag that the start of this is fixed
				; This will cause blank lines to appear on the
				; bottom of the section at times

FDSPLY:	$SAVE	<P1,P2,P3,P4>	; Save the Pn registers
	$SAVE	<A1,A2>		; Save the argument processing registers
	DMOVE	P1,T1		; Copy the arguments
	TXO	P2,DS$FDS	; Flag this is an FDSPLY call
IFN FTXADR,TXO	P2,IFIW		; Make this a local address
	LOAD.	P3,QRGNLN,(P2)	; Get the number of lines
	LOAD.	T1,QRGOFS,(P2)	; Get the offset
	SUB	T3,T1		; Remove the offset (Make it a relative line)
	MOVE	P4,T3		; Copy for later
	PJRST	DISP.0		; Enter the routine


DISPLY:	$SAVE	(<P1,P2,P3,P4>)	; Save the Px ac's
	$SAVE	(<A1,A2>)	; Save the arg ac's
	DMOVE	P1,T1		; Get the args
IFN FTXADR,TXO	P2,IFIW		; Make this a local address
	TXZ	P2,DS$FDS	; Flag DISPLY entry point
	LOAD.	P3,QRGNLN,(P2)	; Get the number of lines in the buffer
	MOVE	P4,P3		; Get a copy
	ASH	P4,-1		; Get half the size


; Enter here from the alternate entry point

DISP.0:	JUMPE	P3,.RET0	; No cost if no lines
	MOVX	T1,QR$FIX	; Check if we have a specific position
	TDNN	T1,$QRFLG(P2)	;  .  .  .
	 JRST	DISP.K		; Go join common routine
	TXO	P2,DS$FIX	; Yes, flag it
	LOAD.	T2,QRGVAL,(P2)	; Get the fixed starting point
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the address of the buffer
	LOAD.	P4,QRGFLN,(P2)	; Get the line to fix this on
	CFMLE.	,BLKFST,(T1),T2	; If not after last mod
	 JRST	DISP.Q		; Then use value as it is
	SUB	T2,.BKOED(T1)	; After last mod, fix up to new position
	ADD	T2,.BKEND(T1)	;  .  .  .
DISP.Q:	CFME.	,BLKEND,(T1),T2	; Is the fixed position =Z?
	 JRST	DISP.R		; No, all is fine
	SOSGE	T3,T2		; Get the position
	 POPJ	P,		; Nothing there, punt out now
	CFMGE.	,BLKEND,(T1),T2	; Within bounds of buffer?
	 JRST	[TXZ	P2,DS$FIX	; No, flag we aren't fixing this in place
		JRST	DISP.K]		; And continue on
	SOJL	T3,DISP.R	; If only one character, no problem with CRLF
	IDIVI	T3,^D5		; Otherwise, convert to a byte pointer
	TDO	T3,BTAB(T4)	;  .  .  .
	ADD	T3,T1		; Plus the buffer address
	ADDX	T3,.BKTLN	; And the offset
	LDB	CH,T3		; Get the next to last character
	CAXE	CH,.CHCRT	; CR?
	 JRST	DISP.R		; No, can't be CRLF
	ILDB	CH,T3		; Get the last character
	CAXN	CH,.CHLFD	; LF of the CRLF?
	 SOJ	T2,		; Yes, back up one more character
DISP.R:	MOVE	P1,T2		; Get the valid value

DISP.K:	HRRZ	T1,P2		; Get the QRG address
	CAIN	T1,CMDBUF	; Is this the command buffer?
	 TXO	P2,DS$CMD	; Yes, flag it
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the text buffer address
	LOAD.	T2,BLKEND,(T1)	; Get the end address
	JUMPE	T2,.RET0	; If no text in buffer, return a zero
	CAMLE	P1,T2		; Is the position to use valid?
	 JRST	[LOAD.	P1,BLKPT,(T1)	; No, get one that is
		TXZ	P2,DS$FIX	; Flag this is not a fixed update
		JRST	.+1]		; And continue
	LOAD	T3,$QRFLG(P2),QR$DLC ; Get "Display last CRLF" flag
	STOR	T3,P2,DS$BLN	; Store as blank line flag
	CFME.	,BLKPT,(T1),T2	; Current pointer at end of buffer?
	 TXZ	P2,DS$BLN	; No, forget the flag
	SOJ	T2,		; Point at the last character
	IDIVI	T2,5		; Get the position
	HLL	T2,BTAB(T3)	; Get the byte pointer
	ADDI	T2,.BKTLN(T1)	;  .  .  .
	LDB	CH,T2		; Get the last character
	PUSHJ	P,CKTRM		; Check if terminator
	 TXZ	P2,DS$BLN	; Clear the flag
; Build pointers to the stack which we will use for storing the line
;bounds until we have found the top and bottom lines.

; Macro to allocate pointers to the sections on the stack

DEFINE XXX(THING),<
	LSTOF.			;; Turn the listing off
	IRP <THING>,<
	MOVEM	T4,THING'PTR	;; Save for pointing to the THING's
	HRRM	T4,THING'BLT	;; Store for moving the THING's down one
	AOJ	T4,		;; Make the other half of the pointer
	HRLM	T4,THING'BLT	;; Store it
	ADDI	T4,-1(P3)	;; Point to the next block
	HRRM	T4,THING'POP	;; Store the pointer
	AOJ	T4,		;; Point back to the next block
	>;; End of IRP THING
	LSTON.			;; Turn the listing on again
> ; end of DEFINE XXX

	MOVX	T4,<Z (P3)>	; Get the index reg
	HRRI	T4,1(P)		; And the address on the stack
	XXX	<BEG,END,FLG,NUM> ; Generate the pointers
	MOVE	T4,P3		; Get the total number of lines
	TXO	T4,(1B0)	; Turn on the sign bit
	HRLM	T4,BEGPOP	; And store for the POP loop
	HRLM	T4,ENDPOP	; Here also
	HRLM	T4,FLGPOP	; . . .
	HRLM	T4,NUMPOP	; . . .
	MOVEI	T4,1(P3)	; Get the number again
	IMULI	T4,4		; Mult by the number of items to store
				;  on the stack
IFN FTKL,<
	ADJSP	P,(T4)		; Make the space on the stack
> ; End of IFN FTKL
IFE FTKL,<
	HRLI	T4,(T4)		; Make the XWD to add
	ADD	P,T4		; And adjust the stack pointer
> ; End of IFE FTKL
	HRR	T1,BEGPTR	; Get the address of the first
	SETZM	(T1)		; Clear it
	HRLI	T1,1(T1)	; Build a BLT pointer
	MOVSS	T1		; Move it to the correct halfs
	BLT	T1,-1(P)	; Clear the stack
	SUBM	P4,P3		; Make the number of lines on the bottom
	HRLI	P3,(P3)		; And make the AOBJx pointer
	HRRI	P3,(P4)		; And get the center line offset
	TXNN	P2,DS$BLN	; Want a blank line on the bottom?
	  JRST	DISP.J		; No, Skip this
	SOJ	P4,		; Yes, one less line on top
	TXC	P3,LH.ALF	; Make sure this isn't just one line
	TXCE	P3,LH.ALF	; If so, ignore FDSPLY call bit
	 TXNN	P2,DS$FDS	; FDISPLY call?
	  JUMPL	P4,[SETOM @BEGPTR	; Clear the pointer
		SETOM	@ENDPTR		; . . .
		SETZM	@FLGPTR		; . . .
		SETZM	@NUMPTR		; . . .
		JRST	DISP.9]		; All done
	SUBI	P3,1		; Start the loop one higher
	JUMPG	P4,DISP.J	; If any lines left on top, continue on
	TXC	P3,LH.ALF	; Check if only one line on bottom
	TXCN	P3,LH.ALF	;  .  .  .
	 JRST	DISP.J		; Yes, we must have it right already
	AOJ	P4,		; No, bump it up one
	AOBJN	P3,DISP.J	; And reset P3
	STOPCD	FNL,<Funny number of lines in section>
; First we must find the line which contains PT. To do this we find the
;beginning of the line in the buffer which contains the pointer, then
;we start getting screen sized lines until we find the line on the
;screen with the pointer. Any lines before this we remember in the block
;of BEG's and END's, moving them up toward the top of the screen while
;doing so.

DISP.J:	TXNE	P2,DS$FDS	; If FDSPLY call, we have the address to start with
	 JRST	DISP.G		; Got it, go set things up
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	MOVE	T2,P1		; Get the pointer
	CFME.	,BLKEND,(T1),P1	; At the end of the buffer?
	 JRST	DISP.I		; No, go handle it
	SUBI	T2,2		; Yes, back up one more
	IDIVX	T2,5		; And make the byte pointer to the last
	HLL	T2,BTAB(T3)	; character of the buffer
	ADDI	T2,.BKTLN(T1)	; And point to the correct word
	LDB	CH,T2		; Get the character
	CAXE	CH,.CHCRT	; Was this a carriage return?
	 JRST	DISP.H		; No, skip this
	ILDB	CH,T2		; Get the next character
	CAXN	CH,.CHLFD	; Is it a line feed?
	 SOJ	P1,		; Yes, back up by one
	JRST	.+2		; Skip in
DISP.H:	ILDB	CH,T2		; Get the next character (last one in buffer)
	PUSHJ	P,CKTRM		; Check if a line terminator
	 JRST	DISP.F		; Not, skip
	SOJA	P1,DISP.F		; It is, back up over it

DISP.I:	SOJ	T2,		; Back up one character
	IDIVX	T2,5		; Get the offset to the character
	HLL	T2,BTAB(T3)	; Get the byte pointer
	ADDI	T2,.BKTLN(T1)	; And get the address
	LDB	CH,T2		; Get the character
	CAXE	CH,.CHCRT	; Is it a carriage return?
	 JRST	DISP.F		; No, skip this
	ILDB	CH,T2		; Yes, get the character at the pointer
	CAXN	CH,.CHLFD	; Is it a line feed?
	 SUBI	P1,2		; Yes, back up the point we are looking for
DISP.F:	STOR.	P1,BLKPT,(T1)	; Store the pointer for GETVID

	PUSHJ	P,GETVID	; And get the line bounds

	SKIPA	T2,A2		; Get the start of the line
DISP.G:	 MOVE	T2,P1		; Get the start we were given
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	PUSHJ	P,SETLIN	; And set up to return the lines

DISP.1:	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	PUSHJ	P,RTNLIN	; And fine the bounds of the screen line
	 STOPCD	(PTD,<PT disappeared>)
	MOVEM	T1,@BEGPTR	; Store the start
	MOVEM	T2,@ENDPTR	; And the end
	MOVEM	T3,@NUMPTR	; Store the number of positions
	TXNE	P2,DS$CMD	; Is this the command buffer?
	 TXO	T4,LD$CMD	; Yes, flag this is part of the command
	MOVEM	T4,@FLGPTR	; Store the flags
	CAMG	P1,T2		; Is the pointer within this line?
	 JRST	DISP.2		; Yes, go build down from here
	MOVE	T3,BEGBLT	; Get the BLT pointer
	MOVEI	T4,@BEGPTR	; Check where we are at currently
	CAIGE	T4,(T3)		; Doing the BLT make sense?
	 SOJA	P4,DISP.1	; No, just count the line and try again
	BLT	T3,@BEGPTR	; Yes, move the BEG's

DEFINE XXX(THING),<
	LSTOF.			;; Turn the listing off
IRP THING,<
	MOVE	T3,THING'BLT	;; Get the BLT pointer
	BLT	T3,@THING'PTR	;; And move the section
> ;; End of IRP
	LSTON.			;; Turn the listing on again
> ; End of DEFINE XXX
	XXX(<END,FLG,NUM>)	; And move the rest
	SOJA	P4,DISP.1	; Loop for the next line

; Now we must fill the bottom of the screen. At this point P4 has
;the number left to fill on the top of the screen and P3 has the
;AOBJx pointer to the bottom of the screen.

DISP.2:	AOBJP	P3,DISP.3	; Need to do any here?
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	PUSHJ	P,RTNLIN	; Get the line bounds
	 JRST	DISP.6		; Hit the end of the screen first
	MOVEM	T1,@BEGPTR	; Store the start
	MOVEM	T3,@NUMPTR	; Store the length
	MOVEM	T2,@ENDPTR	; And the end of this line
	TXNE	P2,DS$CMD	; Is this the command buffer?
	 TXO	T4,LD$CMD	; Yes, flag this is part of the command
	MOVEM	T4,@FLGPTR	; Store the LDB flags
	JRST	DISP.2		; Try again
; Now we must fill out the top of the screen. We must take the beginning
;of the top line we have and back up from there. We enter with
;P4 = the number of lines we have to fill on the top of the screen
;If P4 is negative or zero we are done and can go get the screen
;bounds out of our table.



DISP.3:	PUSH	P,P1		; Save the current pointer
	EXCH	P3,P4		; And set up so that the xxxPTR words work
	JUMPLE	P3,DISP.A	; If nothing left, don't even try to get the pointer

DISP.4:	MOVE	P1,@BEGPTR	; Get the current pointer
	SOJL	P3,DISP.A	; Go return the bounds
	SOJL	P1,DISP.7	; Minus one to get to the previous line
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	STOR.	P1,BLKPT,(T1)	; Store for GETVID
	PUSHJ	P,GETVID	; Find it
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	MOVE	P1,A1		; Get the end of the line
	MOVE	T2,A1		; Get the end address of the line
	SUBI	T2,1		; Minus two to get before any CRLF
	IDIVI	T2,5		; Convert to word/byte index
	ADDI	T2,.BKTLN(T1)	; Point to the correct word
	HLL	T2,BTAB(T3)	; And get the correct byte pointer
	LDB	T3,T2		; Get the character
	CAXE	T3,.CHCRT	; Is it a carriage return?
	 JRST	DISP.E		; No, skip this
	ILDB	T3,T2		; Get the next character
	CAXN	T3,.CHLFD	; Is it a line feed
	 SUBI	P1,2		; Yes, there will be two non-displayed chars
DISP.E:	MOVE	T2,A2		; And the character address
	PUSHJ	P,SETLIN	; And set up to get the lines

DISP.5:	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	PUSHJ	P,RTNLIN	; Get the line bounds
	 STOPCD	(EBB,<End of buffer while going backwards>)
	MOVEM	T1,@BEGPTR	; Store the start
	MOVEM	T2,@ENDPTR	; And the end
	MOVEM	T3,@NUMPTR	; Store the length
	TXNE	P2,DS$CMD	; Is this the command buffer?
	 TXO	T4,LD$CMD	; Yes, flag this is part of the command
	MOVEM	T4,@FLGPTR	; Store the flags
	CAMGE	P1,T2		; Hit the end of this line yet?
	  JRST	DISP.4		; Yes, go try the next
	MOVE	T3,BEGBLT	; No, get the BLT pointer
	CAIGE	T4,(T3)		; Make sense to BLT?
	 JRST	DISP.5		; No, skip it
	BLT	T3,@BEGPTR	; Yes, move the beg's
	XXX(<END,FLG,NUM>)	; Move the rest
	JRST	DISP.5		; And try again
; Here if we hit the end of the screen while trying to fill the bottom
;of the screen.  We will move all of the BEG/END pairs down to the bottom of
;the screen and try to fill the top.

DISP.6:	TXNE	P2,DS$FIX	; If this is fixed, then leave the white space
	 JRST	DISP.3		; Just do the top of the screen now
	JUMPLE	P4,DISP.9	; Done?
DEFINE XXX(STK),<
	LSTOF.			;; Turn the listing off
 IRP <STK>,<
	MOVE	T4,STK'POP	;; Set up to move the STK's
	HLRE	T1,P3		;; Get the amount we need to move them
IFN FTKL,<
	ADJSP	T4,(T1)		;; Fix it up for the number to move
	MOVN	T1,T1		;;  .  .  .
> ;; End of IFN FTKL
IFE FTKL,<
	MOVN	T1,T1		;; Make positive
	HRLI	T1,(T1)		;; And make the XWD
	SUB	T4,T1		;; Fix the pointer
> ;; End of IFE FTKL

	HRLI	T1,(POP T4,(T4)) ;; Put in the pop instruction
	XCT	T1		;; Do the POP loop
	JUMPL	T4,.-1		;;  .  .  .
>;; End of IRP <STK>
	LSTON.			;; Turn the listing back on
>; End of XXX macro definition
	XXX	(<BEG,END,FLG,NUM>) ; Move the areas
	ADDI	P4,(T1)		; Fix now for the amount we still need to do on the top
	ADDI	P3,(T1)		; Also here
	TXZ	P3,LH.ALF	; And flag we are done
	JRST	DISP.3		; Go try to fill the bottom
; Here if we hit the beginning of the buffer before the top of the
;screen was filled. We move what we already have up to the top and then
;fill the bottom of the screen up.

DISP.7:	POP	P,P1		; Get the pointer back
	EXCH	P3,P4		; And fix these up once more
	MOVE	T3,BEGBLT	; Set up to move to the top of the screen
	MOVSI	T4,(P4)		; Get the amount to add
	ADD	T3,T4		; Fix where to move from
	HRRI	T4,(P4)		; Make it an XWD
	SUB	P3,T4		; And fix the AOBJx pointer
	SUB	P3,[XWD 2,2]	; Back off to correct line
	MOVEI	T4,@BEGPTR	; Check if it really makes sense to do the BLT
	CAIGE	T4,(T3)		; Make sense?
	 JRST	DISP.8		; No, skip it
	BLT	T3,@BEGPTR	; Yes, do the blt
DEFINE XXX(THING),<
	LSTOF.			;; Turn the listing off
IRP THING,<
	MOVE	T3,THING'BLT	;; Get the other BLT pointer
	MOVSI	T4,(P4)		;; Get the amount to move
	ADD	T3,T4		;; Fix the BLT pointer
	BLT	T3,@THING'PTR	;; And move the THING's
> ;; End of IRP THING
	LSTON.			;; Turn the listing on
> ; End of DEFINE
	XXX(<END,FLG,NUM>)	; Move the other sections

DISP.8:	SETZ	P4,		; Flag that we are done with the top
	MOVE	T2,@ENDPTR	; Get the end of the previous line
	MOVE	T1,@FLGPTR	; And the flags
	TXNE	T1,LD$CRLF	; Line end with a CRLF?
	 ADDI	T2,2		; Yes, skip over the CRLF
	LOAD.	T1,TPTADR,+$QRTPT(P2) ; Get the buffer address
	PUSHJ	P,SETLIN	; Set up the line for next call to RTNLIN
	JRST	DISP.2		; And try again
; Here when we are done. Return the limits from the blocks

DISP.A:	POP	P,P1		; Restore the pointer
	EXCH	P3,P4		; And put the ac's back

DISP.9:	LOAD.	T1,QRGOFS,(P2)	; Get the offset to the first line
	IMULX	T1,$LDLEN	; Make it the address
	ADD	T1,SCRNAD	;  .  .  .
	LOAD.	T2,QRGNLN,(P2)	; Get the number of lines
	MOVN	T2,T2		; Make it negative
	HRLI	T1,(T2)		; And make the AOBJx pointer
	MOVNI	P3,(P3)		; Make the pointer to the limits
	MOVSI	P3,(P3)		; And make it an AOBJx pointer

DEFINE XXX(THING),<
	LSTOF.			;; Turn the listing off
IRP THING,<
	MOVE	T2,@THING'PTR	;; Get the THING
	STOR.	T2,LDB'THING,(T1) ;; Store it
> ;; End of IRP
	LSTON.			;; Turn the listing on again
> ; End of DEFINE XXX

DISP.B:	XXX(<BEG,END,FLG,NUM>)	; Move the values
	ADDX	T1,$LDLEN-1	; Bump to the next
	AOBJP	T1,DISP.C	; Run out yet?
	AOBJN	P3,DISP.B	; No, get the next pair

DISP.D:	ONES.	,LDBBEG,(T1)	; Flag nothing on this line
	ONES.	,LDBEND,(T1)	;  .  .  .
	ZERO.	,LDBFLG,(T1)	;  .  .  .
	ZERO.	,LDBNUM,(T1)	;  .  .  .
	ADDX	T1,$LDLEN-1	; Bump to next
	AOBJN	T1,DISP.D	; Loop for all the lines

DISP.C:	LOAD.	T3,QRGNLN,(P2)	; Get the number of lines
	AOJ	T3,		; Plus one
	ASH	T3,2		; Time four
	HRLI	T3,(T3)		; Make the XWD
	SUB	P,T3		; And fix the stack
	POPJ	P,		; And return
	SUBTTL	Screen editing -- Subroutines -- SETLIN and RTNLIN

;+
;.hl1 SETLIN
;This routine will do the initialization for the RTNLIN routine.  This
;routine is called with a character position and the text buffer address
;for the RTNLIN routine.
;.literal
;
; Usage:
;	MOVE	T1,Text buffer address
;	MOVE	T2,Character address
;	PUSHJ	P,RTNLIN
;	(Return)
;.end literal
;.HL1 RTNLIN
;This routine will return the character addresses for the beginning of the
;line and the end of the line.  It will also return the number of character
;positions taked up by the characters on the line.
;.literal
;
; Usage:
;	MOVE	T1,Text buffer address
;	PUSHJ	P,RTNLIN
;	(Return -- Failed)
;	(Return -- T1 = Beginning address, T2 = Ending, T3 = Number of positions
;		T4 = LDB flags)
;.end literal
;-

SETLIN:	STOR.	T2,BLKPT,(T1)	; Store the position
	IDIVI	T2,5		; Break into word/char offset
	HLL	T2,BTAB-1(T3)	; Get the byte pointer
	ADDI	T2,.BKTLN(T1)	; And make it absolute
	STOR.	T2,BLKPTR,(T1)	; Store the byte pointer
	POPJ	P,		; Return

RTNLIN:	$SAVE	<CH>		; Save the character AC
	$SAVE	<P1,P2,P3>	; Save a few other registers
	SETZB	P3,CH		; Clear the number of positions
	LOAD.	P1,BLKPT,(T1)	; Get the beginning address

RTNL.0:	CAML	P3,SCRWID	; Is this the end ?
	 JRST	RTNL.3		; Yes - Just return
	MOVE	T3,P3		; Copy the old character position
	MOVE	T2,CH		; Copy the last character
	PUSHJ	P,GETINC	; Get the next character
	 JRST	RTNL.1		; Failed - This must be the end
	CAXN	CH,.CHTAB	; Is this a tab
	 JRST	[TXO	P3,7		; And round up
		ADDI	P3,1		; And round up
		JRST	RTNL.0]		; Try for the next character
	CAXN	CH,.CHDEL	; Delete?
	 AOJ	P3,		; Yes, counts for two positions (prints as ^?)
	CAXE	CH,.CHESC	; Is this an escape
	 CAXL	CH," "		; No, Is this a control character ?
	  AOJA	P3,RTNL.0	; Either an escape or normal character
	ADDI	P3,2		; Control character
	PUSHJ	P,CKTRM		; Is this a line terminator ?
	 JRST	RTNL.0		; No - Skip this then
	LOAD.	P2,BLKPT,(T1)	; Get the current pointer
	CAXE	CH,.CHCRT	; Was the character string CRLF ?
	  JRST	RTNL.2		; No - Skip this
	PUSHJ	P,GETINC	; Get the next character
	  JRST	RTNL.2		; And return a zero flag word
	CAXE	CH,.CHLFD	; Is this a line feed ?
	  JRST	[PUSHJ	P,RTNL.A	; No - Back up the one
		JRST	RTNL.2]		; And return a zero flag word
	SUBI	P2,1		; Yes - Back up the pointer one
	SUBI	P3,2		; And two positions
RTNL.Z:	SKIPA	T4,[EXP LD$CRLF] ; And return a CRLF flag

; Here to move the arguments and return a zero for the flag

RTNL.2:	SETZ	T4,		; Clear the flag
RTNL.Y:	DMOVE	T1,P1		; Copy the arguments
	MOVE	T3,P3		; To return
	JRST	.POPJ1		; And give a good return

; Here if the getting of the next character failed

RTNL.1:	LOAD.	P2,BLKPT,(T1)	; Get the pointer
	JUMPE	P3,.POPJ	; Return if nothing
	JRST	RTNL.2		; Just get out

; Here if the position is equal to the width of the screen
; Check to see if the next two characters a <CR><LF> if so eat
; them and return, else return that the line wraps around to the next
; line.

RTNL.3:	LOAD.	P2,BLKPT,(T1)	; Get the current pointer
	PUSHJ	P,GETINC	; Get the next character
	  JRST	RTNL.7		; Failed - Back up and fail later
	CAXE	CH,.CHCRT	; Is this a <CR> ?
	 JRST	RTNL.6		; No - Must be a wrap
	PUSHJ	P,GETINC	; Get the next charcter for the <LF>
	  JRST	RTNL.6		; Nothing - Back up one charcter and return
				;  wrap around flag
	CAXN	CH,.CHLFD	; Is this a line feed ?
	  JRST	RTNL.Z		; Yes - Return the CRLF flag

RTNL.4:	PUSHJ	P,RTNL.A	; Decrement the pointer
RTNL.6:	PUSHJ	P,RTNL.A	; And again
RTNL.7:	MOVX	T4,LD$WAP	; Get the wrap around flag
	JRST	RTNL.Y		; And return the information


; Here to decrement the pointers

RTNL.A:	DECR.	T2,BLKPT,(T1)	; Decrement the pointer
	PJRST	SETLIN		; Set up the line getter
	SUBTTL	Screen editing -- Subroutines -- FLSLDB - Flush an LDB

;+
;.HL1 FLSLDB
;This routine will first clear the LDB beginning and end pointers,
;the number of character being displayed and the flags.  It will
;then call CLRLDB to move spaces into the LDB that is being used.
;.literal
;
; Usage:
;	MOVE	T1,LDB.address
;	PUSHJ	P,FLSLDB
;	(Return)
;.end literal
;-

FLSLDB:	ZERO.	,LDBFLG,(T1)	; Clear the flags
	ZERO.	,LDBNUM,(T1)	; . . .
	ONES.	,LDBBEG,(T1)	; Set the beginning and the end
	ONES.	,LDBEND,(T1)	;  to minus one
	FALL	CLRLDB		; Fall into CLRLDB
	SUBTTL	Screen editing -- Subroutines -- CLRLDB - Clear an LDB

;+
;.HL1 CLRLDB
;This routine will set the LDBTXT line to blanks.
;.literal
;
; Usage:
;	MOVE	T1,LDB address
;	PUSHJ	P,CLRLDB
;	(Return)
;
;	   -- or --
;
;	MOVE	T1,Line address
;	PUSHJ	P,CLRL.0
;	(return)
;.end literal
;-

CLRLDB:	LOAD.	T1,LDBTXT,(T1)	; Get the address
CLRL.0:
IFN FTDEBUG,<
	CAIG	T1,IMPEND	; Is this at least in the real data area ?
	 STOPCD	ACR,<Attempt to clear registers in CLRLDB>
>; End of IFN FTDEBUG
	MOVX	T2,<ASCII |     |> ; Get the blanks to store
	MOVEM	T2,(T1)		; Store them into the LDB
	HRL	T1,T1		; Build the BLT pointer
	ADDI	T1,1		; . . .
	MOVE	T2,LINWDS	; Get the number of words per line
	SOJLE	T2,.POPJ	; Don't need the BLT if only one word
	ADDI	T2,(T1)		; Point to the last word
	BLT	T1,-1(T2)	; Set the block to spaces
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Subroutines -- CKTRM - Check for a line terminator

;+
;.HL1 CKTRM
;This routine will work like CKEOL except that the terminator characters
;are defined as the following: Carriage return, Line feed, Form feed, and
;vertical tab.
;.literal
;
; Usage:
;	MOVE	CH,Character to check
;	PUSHJ	P,CKTRM
;	(Return - Not a terminator)
;	(Return - Terminator)
;
;.end literal
;-

CKTRM:	CAXE	CH,.CHCRT	; Is this a carriage return ?
	 CAXN	CH,.CHLFD	; Or a line feed
	  JRST	.POPJ1		; Yes - Return (Most likely case if any)
	CAXE	CH,.CHVTB	; Is this a vertical tab ?
	 CAXN	CH,.CHFFD	; Or a form feed ?
	  AOS	(P)		; Yes - Increment the return address
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Subroutines -- FNDLDB - Find the LDB

;+
;.HL1 FNDLDB
;This routine will find the LDB associated with the character address.
;.literal
;
; Usage:
;	MOVE	T1,Character address
;	MOVE	T2,QRG block address
;	PUSHJ	P,FNDLDB
;	(Failure return)
;	(Good return)
;
; On failure:
;	T1 = 0	if not on screen
;	T1 = -1	if not being displayed at all
;
; On success:
;	T1 = Address of the LDB
;.end literal
;-

FNDLDB:	$SAVE	<P1,P2>		; Save P1 and P2
	DMOVE	P1,T1		; Copy the address
	MOVX	T1,QR$VID	; Is this being displayed ?
	TDNN	T1,$QRFLG(P2)	; . . .
	  JRST	[SETO	T1,		; Return a minus one
		POPJ	P,]		; And return
	LOAD.	T1,QRGOFS,(P2)	; Get the first line offset
	IMULX	T1,$LDLEN	; Compute the address of the first LDB
	ADD	T1,SCRNAD	; Add in the LDB list address
	LOAD.	T2,QRGNLN,(P2)	; Get the number of lines
	MOVN	T2,T2		; Make it negative
	HRL	T1,T2		; Make this an AOBJN pointer
	MOVX	T2,LD$CRLF	; Get the CRLF flag to check

FNDL.0:	CFMLE.	,LDBBEG,(T1),P1	; Is this the LDB ?
	 JRST	FNDL.1		; Out of range - Return
	LOAD.	T3,LDBEND,(T1)	; Get the ending character address
	TDNE	T2,$LDFLG(T1)	; End with a CRLF ?
	 ADDI	T3,2		; Yes - Increment the end
	CAMGE	P1,T3		; On this line ?
	 JRST	.POPJ1		; Give a good return
	ADDX	T1,$LDLEN-1	; Point to the next entry minus one
	AOBJN	T1,FNDL.0	; Loop for all LDBs
	LOAD.	T2,TPTADR,+$QRTPT(P2) ; Get the text address
	CFME.	,BLKEND,(T2),T3	; At end of buffer?
	 JRST	FNDL.1		; No, not displayed
	CAMN	P1,T3		; Yes, exactly right place?
	 AOJA	T1,.POPJ	; Yes, return the LDB address
FNDL.1:	SETZ	T1,		; Clear not on the screen
	POPJ	P,		; Return to the caller
	SUBTTL	Screen editing -- Subroutines -- FNDPOS

;+
;.hl1 FNDPOS
; This routine will find the X position of a given character address on a
;line.
;.b.lit
;
; Usage:
;	MOVEI	T1,LDB address
;	MOVE	T2,Character address
;	MOVE	T3,Text buffer address
;	PUSHJ	P,FNDPOS
;	 (return here, T1/T2 containing X and Y positions)
;
;.end literal
;-

FNDPOS:	$SAVE	<P1,P2,P3,P4>	; Save some room
	DMOVE	P1,T1		; Get the arguments
	MOVE	P3,T3		; Get the text buffer address
	LOAD.	P4,BLKPT,(P3)	; Get the current pointer
	MOVE	T1,P3		; Get the text buffer address
	LOAD.	T2,LDBBEG,(P1)	; And the character address to start at
	PUSHJ	P,SETLIN	; Set up to get the characters

	LOAD.	T1,LDBBEG,(P1)	; Get the character address of the start of the line
	SUB	P2,T1		; Get the number of characters to skip
	SETZ	T4,		; Clear the position

; Loop until we find the right character

FNDP.1:	SOJGE	P2,FNDP.0	; Count the character
FNDP.3:	MOVE	T1,T4		; Get the position within the line
	LOAD.	T2,LDBLIN,(P1)	; And the line number
	STOR.	P4,BLKPT,(P3)	; Store the pointer back
	POPJ	P,		; And return

FNDP.0:	MOVE	T1,P3		; Get the buffer address
	PUSHJ	P,GETINC	; And get a character
	 JRST	FNDP.3		; All done, go return the value
	CAXN	CH,.CHDEL	; Delete ?
	 AOJ	T4,		; Yes, prints as ^?
	CAIE	CH,.CHESC	; Escape?
	 CAIL	CH," "		; Control character?
	  AOJA	T4,FNDP.1	; No, takes one space
	PUSHJ	P,CKTRM		; Yes, is it a line terminator?
	 JRST	FNDP.2		; No, check for a tab
	ADDI	T4,2		; Yes, bump by two
	CAIE	P2,1		; Is this the last character?
	 CAXE	CH,.CHCRT	; Carriage return?
	  JRST	FNDP.1		; No, go count the character
	SETZ	T4,		; Yes, move back to right margin
	JRST	FNDP.1		; And go count it

FNDP.2:	CAXE	CH,.CHTAB	; Is it a tab?
	 AOJA	T4,.+2		; No, bump by one and skip
	  TXO	T4,7		; Yes, move to the tab stop
	AOJA	T4,FNDP.1	; Go check if done
	SUBTTL	FNDCHP - Find the position of a given character

;+
;.HL1 FNDCHP
; This routine will attempt to find the position on the screen of a given
;character.
;.lit
;
; Usage:
;	T1/ Character address
;	T2/ QRG.address
;	PUSHJ	P,FNDCHP
;	 (not on screen return)
;	(On screen, T1/T2 = corrdinates)
;
;.end lit
;-

FNDCHP:	$SAVE	<P1,P2,P3>	; Save some ac's
	DMOVE	P1,T1		; Get a copy of the arguments
	PUSHJ	P,FNDLDB	; Find what line it is on
	 PJRST	FNDC.2		; If not on the screen, use CMDPOS
	LOAD.	T3,TPTADR,+$QRTPT+TXTBUF ; Get the buffer address back
	MOVE	T2,P1		; Get the position we want
	PUSHJ	P,FNDPOS	; Find the position on the line
	PJRST	.POPJ1		; And give the skip return

; Here if it does not seem to be on the screen

FNDC.2:	JUMPL	T1,.POPJ	; If not displayed, punt
	JUMPN	P1,FNDC.3	; Special case of first character?
	LOAD.	T1,QRGOFS,(P2)	; Get the offset to the first line
	IMULX	T1,$LDLEN	;  .  .  .
	ADD	T1,SCRNAD	; Get the LDB address
	CFME.	T2,LDBQRG,(T1),P2 ; Correct item?
	 JRST	FNDC.3		; No, can't put at start of buffer
	CFXLE.	,LDBBEG,(T1),0	; Is the first character in the buffer on the screen?
	 JRST	FNDC.3		; No, ignore this
	LOAD.	T2,LDBLIN,(T1)	; Yes, get the first line number
	SETZ	T1,		; And clear the X position
	PJRST	.POPJ1		; And return

FNDC.3:	LOAD.	P3,TPTADR,+$QRTPT(P2) ; Get the address of the buffer
	CFME.	,BLKEND,(P3),P1	; Pointer at the end of the buffer?
	 POPJ	P,		; No, this address is not displayed

	MOVE	T1,P1		; Get the character address
	SOJ	T1,		; Decrement to point to the last char
	IDIVX	T1,5		; Convert to word/byte offset
	HLL	T1,BTAB(T2)	; Get the byte pointer
	ADDI	T1,.BKTLN(P3)	; And make the absolute address
	LDB	CH,T1		; Get the final character

	LOAD.	T1,QRGOFS,(P2)	; Get the offset to the first line
	IMULX	T1,$LDLEN	; Convert to LDB offset
	ADD	T1,SCRNAD	; Make the absolute address
	LOAD.	T2,QRGNLN,(P2)	; Get the number of lines
	MOVN	T2,T2		; Make it negative
	HRLI	T1,(T2)		; And make the AOBJx pointer

FNDC.7:	LOAD.	T2,LDBEND,(T1)	; Get the end pointer
	JUMPL	T2,FNDC.8	; Is it valid?
	ADDX	T1,$LDLEN-1	; Yes, bump to the next
	AOBJN	T1,FNDC.7	; And loop for all the LDB's

; If we get here, all of the lines of the window are used. The only
;special case we need to check now is if the last line does not end with an
;end of line character.

	PUSHJ	P,CKTRM		; And check if it is a line terminator
	 JRST	.+2		; Skip
	  POPJ	P,		; Give not found return
	LOAD.	T2,LDBLIN,-$LDLEN(T1) ; Get the line number
	LOAD.	T1,LDBNUM,-$LDLEN(T1) ; And get the number of chars on the line
	PJRST	.POPJ1		; Give a good return

; Here if there are blank lines in the window. Find the correct place to put
;the pointer.

FNDC.8:	PUSHJ	P,CKTRM		; Check if last char was a line terminator
	 SUBX	T1,$LDLEN	; No, back up one line
	LOAD.	T2,LDBLIN,(T1)	; Get the line number
	LOAD.	T1,LDBNUM,(T1)	; Get the position on the line
	PJRST	.POPJ1		; Return
	SUBTTL	Screen editing -- Subroutines -- VIDPOS - Return the command position

;+
;.hl1 VIDPOS
;This routine will return the X position of in the current command buffer.
;It is used for the command processing.
;.literal
;
; Usage:
;	PUSHJ	P,VIDPOS
;	(Return - T1 contains the X position,
;		  T2 contains the END character address
;		  T3 contians the BEG character address
;		  T4 contians the flags)
;
;.end literal
;-

VIDPOS:	$SAVE	<A1,A2>		; Save the argument registers
	LOAD.	T1,TPTADR,+CMDBUF+$QRTPT ; Get the text buffer address
	PUSHJ	P,GETVID	; Get the address
	LOAD.	T1,TPTADR,+CMDBUF+$QRTPT ; Get the text buffer again
	LOAD.	A1,BLKPT,(T1)	; Save the current pointer
	MOVE	T2,A2		; Get the pointer address
	PUSHJ	P,SETLIN	; Set up RTLINE calls

VIDP.0:	LOAD.	T1,TPTADR,+CMDBUF+$QRTPT ; Get the text buffer address again
	PUSHJ	P,RTNLIN	; Call RTLINE to return the position
	 JRST	VIDP.1		; End of buffer, return zeros
	TXNE	T4,LD$CRLF	; End of a CRLF ?
	 ADDI	T2,2		; Yes, increment the position
	CAMLE	A1,T2		; Is this the line we wanted?
	 JRST	VIDP.0		; No, try again
	EXCH	T1,T3		; Yes, Get the position
	POPJ	P,		; Return to the caller

; Here if there are no characters on this line

VIDP.1:	SETZB	T1,T2		; Clear the position
	SETZB	T3,T4		; And the others
	POPJ	P,		; And return
	SUBTTL	Screen editing -- Subroutines -- GETVID

;+
;.hl2 GETVID
; This routine is a generallized GETARG or GETCMD routine.  It will
;expect to be given the text buffer that is to be used for looking
;up the lines.
;.literal
;
; Usage:
;	MOVE	T1,TPT.address
;	PUSHJ	P,GETVID
;	(Return -- A1 and A2 set up)
;.end literal
;-

GETVID:	$SAVE	<CH,P1>		; Save CH and P1
	MOVE	P1,T1		; Copy the TPT address
	LOAD.	T1,BLKPT,(P1)	; Get the pointer
	SOSGE	T4,T1		; Minus one character
	 JRST	GETV.5		; If negative we don't have to look any further
	IDIVI	T1,5		; And split into word/character indices
	ADDI	T1,.BKTLN(P1)	; Make the word address
	HLL	T1,BTAB(T2)	; And the byte pointer
	LOAD.	T2,BLKEND,(P1)	; Get the end pointer
	SOJ	T2,		; And check if we are pointing there
	SETO	CH,		; Assume we are at the end
	CAML	T4,T2		; Are we?
	 JRST	GETV.4		; And continue on
	PUSH	P,T1		; Save the byte pointer
	ILDB	CH,T1		; Get the character after this
	POP	P,T1		; Restore the byte pointer

GETV.4:	MOVE	T2,CH		; Copy the old character
	LDB	CH,T1		; Get the character
	PUSHJ	P,CKTRM		; Check if end of line
	 JRST	GETV.7		; Nope, back up the byte pointer and try again
	CAXN	CH,.CHCRT	; Is this a carriage return?
	 CAXE	T2,.CHLFD	; Yes, was it paired with a line feed?
	  JRST	GETV.5		; Yes, all done

GETV.7:	ADDX	T1,<INSVL.(7,BP.PFL)> ; Back up the position
	JUMPG	T1,GETV.8	; Need to go over word boundary?
	SOJ	T1,		; Yes, do it
	TXZ	T1,BP.PFL	; Clear out the position field
	TXO	T1,<INSVL.(1,BP.PFL)> ; And put in a one
GETV.8:	SOJGE	T4,GETV.4	; And loop unless we hit the start of the buffer
GETV.5:	AOS	A2,T4		; Get the start of the string
	LOAD.	A1,BLKPT,(P1)	; And the end
	POPJ	P,		; And return
	SUBTTL	Screen editing -- Subroutines -- CHKTYI - Check if any type ahead

;+
;.hl1 CHKTYI
; This routine is called to check if the user has typed any characters
;yet. This is so that the screen updating can be aborted when the user
;starts typing the next command.
;.b.lit
;
;	PUSHJ	P,CHKTYI
;	 (return, no characters waiting)
;	(return, characters waiting for input)
;
;.end literal
;-


CHTYI0:	SKIPE	NOTYIA		; Type-ahead disabled?
	 POPJ	P,		; Yes, give up
	SKIPN	TYIFLG		; Any type-ahead?
	 PUSHJ	P,T$CTYI	; Check for type-in
	  POPJ	P,		; No type-ahead
	SETOM	TYIFLG		; Remember we have it
	PJRST	.POPJ1		; We have type-ahead

CHKTYI:	SKIPE	NOTYIA		; Type-ahead disabled?
	 POPJ	P,		; Yes, return now
	SKIPE	TYIFLG		; Already have type ahead
	 PJRST	.POPJ1		; Yes, give the type ahead seen return
	PUSHJ	P,T$CTYI	; Check for type-in
	 JRST	CHKT.1		; None yet, check if any output in the buffer
	SETOM	TYIFLG		; Yes, flag it
	JRST	.POPJ1		; Give the waiting return

; Here to check whether the output buffer is empty. If it isn't and
;we are on a slow terminal, we should HIBERnate a little bit before
;continuing. This makes it possible to have typeahead abort the update
;and not have half the screen update still be in the monitors output
;buffers.

	ND	D$SLOW,.TS120	; 600 baud and below are slow terminals
	ND	D$WTIM,^D100	; Wait a tenth of a second before trying again
	ND	D$FTIM,^D50	; Number of mill-seconds to fudge calculated time


CHKT.1:	TXNE	F,F.TYOF	; Any typeout in the buffers?
	 PUSHJ	P,TTYOUT	; Yes, must also account for that
	$SAVE	<P1,P2>		; Save P1
	MOVE	P1,TRMSPD	; Get the terminal speed
	CAXLE	P1,D$SLOW	; Is it a slow terminal?
	 POPJ	P,		; No, forget this
CHKT.2:	MOVE	P1,[XWD 2,TRMTOC] ; Get the pointer
	TRMOP.	P1,		; Get the number of characters  in the output buffer
	 JRST	CHKT.5		; Can't, check if output present at all
	IMULX	P1,^D1000	; Get the number of chars*1000
	MOVE	P2,TRMSPD	; Get the speed
	IDIV	P1,CPSTBL(P2)	; And determine the time it will take to type them
	SUBX	P1,D$FTIM	; Fudge time for chars which typed out now
	JUMPG	P1,CHKT.5	; If we must wait, go do it
	JRST	CHKT.3		; Otherwise go check for input again

; Here if .TOTOC TRMOP. doesn't work

CHKT.5:	MOVE	P1,[XWD 2,TRMSOP] ; Get the TRMOP args
	TRMOP.	P1,		; Any output present?
	 JRST	CHKT.3		; No, go check if there is typeahead now
	MOVX	P1,HB.RTC!D$WTIM ; Yes, get the wait time
CHKT.4:	HIBER	P1,		; Wait a bit
	 JFCL			; Ignore it
	JRST	CHKT.2		; And go check if there is still output

; Here after checking the output buffer to check for typeahead again

CHKT.3:	PUSHJ	P,T$CTYI	; Check for type-in
	 POPJ	P,		; No, just return
	SETOM	TYIFLG		; Yes, flag we have some
	PJRST	.POPJ1		; And give the type ahead return
	SUBTTL	Low segment for TECUPD

	$IMPURE			; Do the impure storage
	LOWVER(UPD,1)		; Low segment version number of this module


; CRT data

FNCLST:	BLOCK	1		; Function list for updating lines
FNCBLK:	BLOCK	1		; Address of function block for overall update
FNCTMP:	BLOCK	3		; Temp function list for UPDSCT
TYIFLG:	BLOCK	1		; Flag whether type ahead has been seen yet
NOTYIA:	BLOCK	1		; Type-ahead abort disabled
IDLCNT:	BLOCK	1		; Insert/delete line counter for UPDSCT


; For DISPLY

BEGPTR:	BLOCK	1		; Pointer into block of line BEG's
ENDPTR:	BLOCK	1		; Pointer into block of line END's
FLGPTR:	BLOCK	1		; Pointer into block of line FLG's
NUMPTR:	BLOCK	1		;   " for line lengths
BEGBLT:	BLOCK	1		; BLT pointer for moving BEG's toward top of screen
ENDBLT:	BLOCK	1		; BLT pointer for END's
FLGBLT:	BLOCK	1		; BLT pointer for FLG's
NUMBLT:	BLOCK	1		;   " for line lengths
BEGPOP:	BLOCK	1		; POP loop pointer for BEG's
ENDPOP:	BLOCK	1		; POP loop pointer for END's
FLGPOP:	BLOCK	1		; POP loop pointer for FLG's
NUMPOP:	BLOCK	1		;   " for line lengths

; For LINCST

OLDSPT:	BLOCK	1		; Saved byte pointer to old text
OLDPTR:	BLOCK	1		; Running byte pointer to old text
NEWPTR:	BLOCK	1		; Running byte pointer to new text

LNCXCT:	BLOCK	2		; Correct ILDB instruction for LNCRPL
 LNCXC2==LNCXCT+1		; Correct instruction to fetch number of chars left in suffix of string
LITRTM:	BLOCK	1		; LINCST time estimate per 1000 iterations
LBGUPT:	BLOCK	1		; Uptime in milli-seconds saved when
				; LINCST loop was started
NUMITR:	BLOCK	1		; Number of iterations being performed by LINCST

OLDLDB:	BLOCK	1		; Address of old lines LDB
NEWLDB:	BLOCK	1		; Address of new lines LDB

COLPTR:	BLOCK	1		; AOBJx pointer to column
ROWPTR:	BLOCK	1		; AOBJx pointer to row
NUMCOL:	BLOCK	1		; Number of columns in table. This is the amount
				; to add to go down one row.

MATRIX:	BLOCK	1		; Matrix used to calculate min cost
IFN FTDEBUG,<
NUMROW:	BLOCK	1		; Number of rows in matrix
UPDDEB:	BLOCK	1		; Update debugging output flag
> ; End of IFN FTDEBUG

; For SCTCST

OLDLDP:	BLOCK	1		; AOBJx pointer to old lines
NEWLDP:	BLOCK	1		; AOBJx pointer to new lines
TOTCST:	BLOCK	1		; Running minimum cost for SCTRPL
ENDACC:	BLOCK	1		; Number of accepts to end with
BEGACC:	BLOCK	1		; Number of accepts to start with
CSTLST:	BLOCK	1		; Address of list of line to line replacement costs
UPCSFL:	BLOCK	1		; Flag -1 indicates doing update,
				; +0 indicates doing cost calc

	SUBTTL	End of TECUPD

	END			; End of TECUPD