
.BEGIN RUBOUT		;Rubout processor routine for -*-MIDAS-*- programs.

.AUXIL			;Don't mention all my symbols in crefs of programs that use me.

	;PRINT VERSION NUMBER
	.TYO6 .IFNM1
	.TYO 40
	.TYO6 .IFNM2
	PRINTX/ INCLUDED IN THIS ASSEMBLY.
/

;The main entry points of the RUBOUT package are INIT and READ.
;Each time you want to start reading one object, you should call INIT.
;After that, you call READ one or more times until you have enough
;input to act on.  When you wish to start reading a "new" object -- and not
;allow the old one to be rubbed out any longer -- call INIT again.

;There are also subsidiary entry points called character service routines.
;These handle the display updating for one input character.  The
;rubout-processing significance of all characters is determined by
;a routine which you supply, called DISPATCH, which examines the
;character and goes to the appropriate service routines.
;We supply a default definition of DISPATCH for you to use as
;all or part of your dispatch routine, and also as a guide to what
;dispatch routines should do.


;Calling Conventions:

;All subroutines herein are called by PUSHJ P,.  They normally do NOT skip-return.
;Character service routines skip, with a return code in A,
;to tell READ to return with that code.

;Arguments are passed in ACs A,B,C and D.  D must be C+1.
;Subroutines may alter A-D as documented with each routine.


;These switches may be set by the caller to request optional features.
;They may be defined local to the block RUBOUT.

IFNDEF $$MULTI,$$MULTI==0	;Be efficient for CRs, BSs and TABs in the text.
				;$$MULTI==1 makes the code larger but makes
				;rubbing out these characters do less display.
IFNDEF $$FCI,$$FCI==1		;Handle full char set input
				;(distinguish Alpha from C-B).
				;$$FCI==1 works for ASCII input streams too.
IFNDEF $$PROMPT,$$PROMPT==0	;1 => call PROMPT to type a prompt string when needed.
IFNDEF $$HELP,$$HELP==0		;1 => call HELP when the user types Top-H.
IFNDEF $$CTLBRK,$$CTLBRK==0	;1 => the default dispatch routine
				;makes all random control characters break.
IFNDEF $$CTLECH,$$CTLECH==1	;1 => control chars that perform editing functions
				  ;also echo, and must be erased.  Doesn't include Rubout.
IFNDEF $$BRKINS,$$BRKINS==1	;1 => break characters insert themselves first,
				;in the default dispatch routine.
IFNDEF $$FFCLR,$$FFCLR==1	;1 => ^L echoes as clear screen.
				;0 => it echoes as uparrow-L, and we re-use the same lines.

;These exits must be defined by the user.  They may be defined local
;to the block RUBOUT:

;INCHR		reads a character, returning it in A.
;OUTCHR		outputs the character in A.  --MORE-- should, for
;		most applications, be inhibited
;		by having %TJMOR set in the channel used for output,
;		if it is a terminal.  If you want to do --MORE--,
;		you should make sure that when the --MORE-- is proceeded
;		its line is re-used so that the screen is as if
;		the --MORE-- had never been.
;DISPLAY	outputs the character in A in display mode (%TJDIS).
;		It can be any stream that knows the meaning of ^P.
;		If outputting to a terminal, set %TJMOR.
;PROMPT		types a prompt-string.  Called at appropriate times.
;		Needed only if $$PROMPT==1.  Not called when READ is
;		entered;  if you want that, do it yourself.
;HELP		types a help-string when the user typed Top-H.
;		Needed only if $$HELP==1.  On displays,
;		HELP may wish to wait for input to be available
;		and then jump to REDISP without reading it.
;DISPATCH	takes a character in A, and jumps off to the service
;		routine for it, which may be one of those provided
;		by RUBOUT (such as RUBOUT, INSERT, BREAK, etc.).
;		A sample dispatch routine is called RB$DSP.
;		You can use it, if it is right, by doing
;		RUBOUT"DISPATCH==RUBOUT"RB$DSP
;		after the .INSRT SYSENG;RUBOUT, or your dispatch
;		can jump off to it, after handling a few cases specially.

;Service routines provided in this file include
;INSERT, BREAK, BRKCR, BRKINS, INSCR, QUOTE, QUIT, CANCEL, RUBOUT,
;IGNORE, REDISP, CLRLIN, CTLU.


;Each call to the rubout processor must give the address of an argument
;block, called the rubout processor block, in B.
;This block enables you to carry on several transactions with
;RUBOUT at the same time (or one inside another).  If you wish,
;you can make your DISPATCH, OUTCHR, etc. entries jump through
;extra words at the end of the rubout processor block (words which
;normally are not used), and thus have different routines for
;each transaction.

;These are the words in the rubout processor argument block
;that are used by this package:

RB.==777777		;Bit typeout mode prefix.
RB.POS==0		;Vpos,,Hpos of 1st character in buffer.
RB.WID==1		;Width of line on terminal.
RB.TYP==2		;TTY characteristics.
			;positive => printing terminal.
  RB%FIL==2		; bit 3.2 => not even that, just reading from a file.
			;0 => glass teletype (character erase only, no move up).
			;negative => full display.
  RB%CID==1		; bit 3.1 => can insert/delete as well.
  RB%SAI==2		; BIT 3.2 => SAIL mode on, ctl chars echo as one pos.
RB.BEG==3		;B.P. to ILDB start of buffer.
RB.END==4		;B.P. to ILDB 1st character after end buffer.
			;Actually, you should leave space for two characters
			;to be stored past RB.END.
RB.PTR==5		;B.P. for storing into buffer.
RB.PRS==6		;B.P. to last character parsed at next level up.
RB.STAT==7		;Word of flags indicating temporary state of editing

RB.LEN==10		;Number of words in the rubout block

;The caller should set up RB.BEG and RB.END, then call INIT which will
;initialize RB.TYP, RB.WID, and RB.POS and RB.PTR.

;The use of RB.PRS:
;Complicated break-conditions do not need to be expressed in the DISPATCH routine.
;If you use $$BRKINS==1 (Insert break characters in the buffer), then
;if the higher level parser decides it needs more characters it can just call
;READ again to get more.  It could then re-parse the whole input string.
;To save time, it can put in RB.PRS the pointer to the last character that
;it actually parsed.  On return from READ, if RB.PRS is unchanged, all
;characters up to that point were not changed (ie, not rubbed out)
;and the parser can continue from where it left off.
;Otherwise, RB.PRS will be zero, to indicate that the parser must
;rescan the entire string as returned by READ.
;If you like, you can keep the parser's fetch-pointer in RB.PRS,
;calling READ when it reaches RB.PTR, and noticing when it is zeroed.

DEFINE SYSCAL NAME,ARGS
	.CALL [SETZ ? SIXBIT/NAME/ ? ARGS ((SETZ))]
TERMIN

;Get a character's graphic type.  Takes character in A, returns type in A.

;Type codes are:
 ;0 - 1-POSITION CHARACTER.
 ;1 - ORDINARY CTL CHAR - USUALLY 2-POSITION, BUT 1-POSITION IN SAIL MODE.
 ;2 - BACKSPACE.
 ;3 - CR
 ;4 - LF
 ;5 - TAB.
 ;6 - SPECIAL CTL CHARACTER - 2-POSITION EVEN IN SAIL MODE.
 ;7 - ^G

CHRTYP:
IFN $$FCI,[
	PUSH P,C		;If storing 9-bit bytes in the buffer,
	LDB C,[300600,,RB.PTR(B)]
	CAIN C,7
	 JRST CHRTY1
	CAIGE A,177		;assume that anything
	 JRST [	SETZ A,		;below 177 is either an ASCII graphic or a SAIL graphic.
		JRST POPCJ]
CHRTY1:	PUSHJ P,CHRASC		;Other things were echoed by converting them to ASCII.
	POP P,C
];IFN $$FCI
	CAIN A,177
	 JRST [	MOVEI A,6	;Assume rubout is a 2-position character,
		POPJ P,]	;even though we shouldn't be echoing them.
	PUSH P,C
	PUSH P,D
	MOVE C,A
	IDIVI C,6
	CAIL A,40
	 TDZA A,A
	  LDB A,RRCHBP(D)
	JRST POPDCJ

;TABLES USED BY CHRTYP THE ENTRY FOR EACH
;CHARACTER IS AN INDEX INTO RRFORT OR RRBACT.

RRCHBP:	REPEAT 6,<360600-<6*.RPCNT>_12.>,,RRCHTB(C)

RRCHTB:	.BYTE 6
	6		;^@
	1		;^A
	1		;^B
	1		;^C
	1		;^D
	1		;^E
	1		;^F
	7		;^G
	2		;^H
	5		;^I
	4		;^J
	1		;^K
	1		;^L
	3		;^M
	1		;^N
	1		;^O
	1		;^P
	1		;^Q
	1		;^R
	1		;^S
	1		;^T
	1		;^U
	1		;^V
	1		;^W
	1		;^X
	1		;^Y
	1		;^Z
	0		;ALTMODE, 1 POSITION.
	1		;^^
	1		;[ ;^]
	1		;^\
	1		;^_
	.BYTE

;INIT empties the buffer and also tells RUBOUT about
;the characteristics of the output stream used
;for echoing of output during rubout processing.
;If it is a terminal, the capabilities of it are also remembered for use
;in chosing display strategies.  If it is not a terminal, or if -1 is
;passed as the channel number, we assume that no echoing of input or rubbed
;out characters is desired.

;INIT takes the output channel in A and the rubout processing block address in B.
;It sets RB.TYP, RB.WID and RB.POS.  It also initializes the buffer to be empty.

INIT:	PUSHJ P,INITB

;Learn about a new output source, without marking the buffer empty.
INITO:	PUSH P,C
	PUSH P,D
	SYSCAL CNSGET,[A ? MOVEM D ? MOVEM RB.WID(B)	;Read tty width into RB.WID.
		 REPEAT 3,[ ? MOVEM D]]	;Read TTYOPT of TTY into D.
	 JRST INIT1		;Jump if not really a TTY.
	SETZ C,			;First assume it's a glass TTY.
	TLNN D,%TOMVU
	 TLNE D,%TOOVR		;If it isn't one, try a printing TTY.
	  MOVSI C,200000	;RB.TYP should be positive.
	TLNE D,%TOERS
	 MOVSI C,600000		;For erasable display, it should be negative.
	TLNE D,%TOCID
	 TLO C,RB%CID		;If we can insert/delete chars, remember that.
	MOVEM C,RB.TYP(B)	
	SYSCAL RCPOS,[A ? MOVEM RB.POS(B)]
	 .LOSE %LSFIL
POPDCJ:	POP P,D
POPCJ:	POP P,C
CPOPJ:	POPJ P,

INIT1:	MOVSI C,200000+RB%FIL
	MOVEM C,RB.TYP(B)
	JRST POPDCJ

;Set up to do some rubout processing.  Takes the Rubout processing block addr in B
;and marks the buffer empty.
INITB:	PUSH P,C
	MOVE C,RB.BEG(B)
	MOVEM C,RB.PTR(B)
	JRST POPCJ

IFN $$FCI,[
;Normalize a 9-bit character so we can do comparisons on it.
CHRNRM:	;ANDCMI A,%TXSFT+%TXSFL
	TRNE A,%TXCTL+%TXMTA		;Convert lower case controls/metas to upper case,
	 TRNN A,100
	  JRST CHRNR1
	TRC A,177
	TRCN A,177			;but don't turn Control-Rubout into Control-_.
	 TRZ A,40
CHRNR1:	TRNE A,%TXTOP+%TXCTL+140	;ASCII control chars must be turned into %TXCTL+char.
	 POPJ P,
	CAIE A,33			;except for altmode.
	 ADDI A,%TXCTL+100
	POPJ P,

;Convert a 9-bit character in A to ASCII, if necessary for insertion in the buffer
;(that is, if the buffer is 7-bit).
CHRASI:	PUSH P,C
	LDB C,[300600,,RB.PTR(B)]
	CAIE C,7
	 JRST POPCJ
	POP P,C
;Convert a 9-bit character in A to ASCII.
CHRASC:	ANDI A,%TXCTL+%TXASC
	TRZN A,%TXCTL
	 POPJ P,
	CAIN A,177
	 POPJ P,
	CAIE A,40
	 CAIL A,140
	  SUBI A,40
	ANDCMI A,100
	POPJ P,
];IFN $$FCI

SUBTTL Main rubout processing routine

;The main function of the rubout package is READ.
;It takes the pointer to a rubout processor argument block in B.
;It reads characters and processes them until either a break character
;is read, the buffer becomes full, or an attempt is made to rub out
;past the beginning of the buffer.
;On return, A contains -1 if the buffer is full;  -2, in case of
;over-rubout;  otherwise, it contains the break character that caused
;the exit.  No other accumulator is clobbered.

READ:	PUSH P,C
	PUSH P,D
RBLOOP:	PUSHJ P,INCHR		;Read next character into A.
	SKIPLE RB.TYP(B)	;After a CR coming form a file, flush the LF.
	 CAIE A,^M
	  JRST RBLOO2
	PUSHJ P,INCHR
	MOVEI A,^M
RBLOO2:
IFN $$HELP,[
	CAIN A,%TXTOP+"H	;If user supplied a HELP routine, call it for Top-H.
	 JRST [	PUSHJ P,HELP	;Must check for Top-H before the CHRNRM, which makes it H.
		JRST RBLOOP]
];IFN $$HELP
IFN $$FCI,PUSHJ P,CHRNRM
	PUSHJ P,DISPATCH	;Let user decide what to do with the character.
	 JRST RBLOO1
	JRST POPDCJ		;His routine skips => it wants to exit.
				;It should have put the return code in A.

RBLOO1:	MOVE C,RB.PTR(B)	;User didn't ask to return,
	CAME C,RB.END(B)	;but return anyway if the buffer has become full.
	 JRST RBLOOP
	MOVNI A,1
	JRST POPDCJ

IFE $$FCI,[
;Here is the default RB.DSP character dispatch routine which we provide.
RB$DSP:	CAIN A,177
	 JRST RUBOUT
	CAIN A,^U
	 JRST CTLU
	CAIN A,^D
	 JRST CANCEL
	CAIN A,^G
	 JRST QUIT
	CAIN A,^M
IFN $$BRKINS, JRST BRKCR
IFE $$BRKINS, JRST BREAK
	CAIE A,^_
	 CAIN A,^C
IFN $$BRKINS, JRST BRKINS
IFE $$BRKINS, JRST BREAK
	CAIN A,^Q
	 JRST QUOTE
	CAIN A,^L
	 JRST REDISP
IFN $$CTLBRK,[
	CAIGE A,40
IFN $$BRKINS, JRST BRKINS
IFE $$BRKINS, JRST BREAK
]
	JRST INSECH
];IFE $$FCI

IFN $$FCI,[
;Default dispatch for 9-bit characters.
RB$DSP:	CAIN A,177
	 JRST RUBOUT
	CAIN A,%TXCTL+"U
	 JRST CTLU
	CAIN A,%TXCTL+"D
	 JRST CANCEL
	CAIN A,%TXCTL+"G
	 JRST QUIT
	CAIN A,%TXCTL+"M
IFN $$BRKINS, JRST BRKCR
IFE $$BRKINS, JRST BREAK
	CAIE A,%TXCTL+"_
	 CAIN A,%TXCTL+"C
IFN $$BRKINS, JRST BRKINS
IFE $$BRKINS, JRST BREAK
	CAIN A,%TXCTL+"Q
	 JRST QUOTE
	CAIN A,%TXCTL+"L
	 JRST REDISP
IFN $$CTLBRK,[
	TRNE A,%TXCTL+%TXMTA
IFN $$BRKINS, JRST BRKINS
IFE $$BRKINS, JRST BREAK
]
	JRST INSECH
];IFN $$FCI

SUBTTL Various action routines for input characters.

;Dispatch here for a character that quotes the next one (eg, ^Q).
;The quoting character goes in the buffer so that it can quote
;the next character at the next level of parsing.
QUOTE:
IFE $$CTLECH,PUSHJ P,OUTECH
IFN $$FCI,PUSHJ P,CHRASI
	IDPB A,RB.PTR(B)	;Store the ^Q.
	PUSHJ P,INCHR		;Read the quoted character.

;Insert a character, echoing it if it was a non-echoed control-character.
INSECH:   CAIE A,177
IFN $$CTLECH,CAIA
.ELSE [
IFN $$FCI, TRNE A,%TXCTL
.ELSE	   CAIGE A,40
]
	    PUSHJ P,OUTECH
	  JRST INSERT

;Insert char followed by a LF.  For CR, when it isn't a break.
INSCR:	MOVEI A,^M
	IDPB A,RB.PTR(B)
	MOVEI A,^J
	MOVE C,RB.END(B)	;Note that the buffer may become full
	CAMN C,RB.PTR(B)	;after just the CR.
	 JRST [	MOVEI B,1
		AOS (P)
		POPJ P,]

;Dispatch here for an ordinary character, that should just be inserted,
;and has already been echoed.
INSERT:
IFN $$FCI,PUSHJ P,CHRASI	;If char is 9-bit and buffer is 7-bit, convert.
	IDPB A,RB.PTR(B)
	POPJ P,

;Insert char and a LF, then break.
BRKCR:	PUSH P,A
	MOVEI A,^M
	IDPB A,RB.PTR(B)
	MOVEI A,^J
	IDPB A,RB.PTR(B)
	POP P,A
	JRST BREAK

;Dispatch here for a break character.  We store it and a null,
;then signal READ to return the break character.
;If the character is a control char which didn't echo, we echo it.
BRKINS:	PUSH P,A
	PUSHJ P,INSECH		;Insert, maybe echoing or converting 9-bit to 7-bit.
	POP P,A

;Dispatch here for a break character that should not be stored
;in the buffer.
BREAK:	SETZ C,			;Store a 0 to make it ASCIZ,
	MOVE D,RB.PTR(B)	;but don't make RB.PTR point after it.
	IDPB C,D
	AOS (P)
	POPJ P,

;Here to "quit".  Acts like too many rubouts, flushing any amount of input.
QUIT:	MOVE C,RB.BEG(B)	;Mark the buffer empty.
	MOVEM C,RB.PTR(B)
	SETZM RB.PRS(B)		;Tell user his old parsing work is no use.
	MOVNI A,2		;Make READ return -2.
	AOS (P)
	POPJ P,

;Empty the entire buffer.  The same as exactly enough rubouts
;but types out differently.
CANCEL:	SETZM RB.PRS(B)		;Tell user his old parsing is no longer any use.
	SKIPGE D,RB.TYP(B)	;On a display, clear the display window first,
	 JRST CANCE1		;while we can still tell how long it is.
	MOVE C,RB.BEG(B)	;Empty the buffer.
	MOVEM C,RB.PTR(B)
	TLNE D,RB%FIL
	 POPJ P,		;If reading from file, don't type anything.
	MOVEI A,40		;Printing terminal, type "XXX?" and CRLF.
	PUSHJ P,OUTCHR
	MOVEI A,"X
	PUSHJ P,OUTCHR
	PUSHJ P,OUTCHR
	PUSHJ P,OUTCHR
	MOVEI A,"?
	PUSHJ P,OUTCHR
	JRST WCLEAR		;CRLF, and prompt if appropriate.

CANCE1:	PUSHJ P,WCLEAR		;Clear out the display window.
	MOVE C,RB.BEG(B)	;Empty the buffer.
	MOVEM C,RB.PTR(B)
	POPJ P,

CTLU:	
IFN $$CTLECH,PUSHJ P,IGNORE	; If the ^U echoed, must erase it too.
;Delete one line backwards.  Deletes at least one character.
CLRLIN:	PUSHJ P,RUBOUT		; DELETE AT LEAST ONE CHAR, STOPPING ONLY AT LINE BEG.
	 CAIA
	  JRST POPJ1		; Beginning of buffer => propagate the skip.
	MOVE C,RB.PTR(B)
	CAMN C,RB.BEG(B)	; Else stop rubbing if now at start of line.
	 POPJ P,
	LDB A,C
	CAIE A,^J
	 JRST CLRLIN
	POPJ P,

;Dispatch here to ignore a character.  On a display, erase it if it echoed.
IGNORE:
IFE $$CTLECH,[
IFE $$FCI,CAIGE A,40
IFN $$FCI,TRNE A,%TXCTL
	   POPJ P,
]
	SKIPL RB.TYP(B)
	 POPJ P,
	JRST RUBOU1

SUBTTL Various display operations

;Here to retype the entire contents of the buffer.
REDISP:
IFN $$FFCLR,IFN $$PROMPT, PUSHJ P,PROMPT ;If ^L clears the screen, just re-prompt.
IFE $$FFCLR,PUSHJ P,WCLEAR		;Else, erase all the lines that are in use
					;and put cursor after the previous prompt.
	MOVE C,RB.BEG(B)
REDIS1:	CAMN C,RB.PTR(B)
	 POPJ P,
	ILDB A,C
	PUSHJ P,OUTECH
	JRST REDIS1

;Clear the display window.  On printing terminal, just CRLF and prompt.
WCLEAR:	SKIPL RB.TYP(B)
	 JRST [	PUSHJ P,CRLF
		IFN $$PROMPT, PUSHJ P,PROMPT
		POPJ P,]
;Here for clearing buffer on a display.
IFN $$MULTI,[
	PUSHJ P,RESYNCH		;Compute vpos,,hpos of end of buffer in D.
	PUSH P,D
	PUSHJ P,CRSHOME		;Move cursor to beginning of rubout processor area.
	MOVEI A,"L
	PUSHJ P,DISP2		;Clear rest of that line.
	HLRZS (P)
	POP P,D			;Then CRLF down thru the lines that were occupied.
	HLRZ C,RB.POS(B)
	CAMN C,D
	 POPJ P,
WCLEA1:	CAMN C,D
	 JRST CRSHOM		;Then move cursor to beginning of area again.
	MOVEI A,"D
	PUSHJ P,DISP2		;Move down one line
	MOVEI A,"L
	PUSHJ P,DISP2		;and clear the one we get to.
	AOJA C,WCLEA1
];IFN $$MULTI
.ELSE [
	PUSHJ P,CRSHOME		;Move cursor to beginning of rubout processor area.
	MOVEI A,"L
	JRST DISP2		;Clear rest of that line.
];IFN $$MULTI

;On a display only, move the cursor to the beginning of the window area.
CRSHOM:	MOVEI A,"V
	PUSHJ P,DISP2
	HLRZ A,RB.POS(B)
	ADDI A,10
	PUSHJ P,DISPLAY
	MOVEI A,"H
	PUSHJ P,DISP2
	HRRZ A,RB.POS(B)
	ADDI A,10
	PUSHJ P,DISPLAY
	POPJ P,

;Output a ^P followed by the character in A, using DISPLAY supplied by user.
DISP2:
	PUSH P,A
	MOVEI A,^P
	PUSHJ P,DISPLAY
	POP P,A
	PUSHJ P,DISPLAY
	POPJ P,

;Output the character in A as it should be echoed, preserving A.
OUTECH:	PUSH P,A
	PUSHJ P,OUTEC1
	POP P,A
	POPJ P,

OUTEC1:	CAIGE A,177
	 JRST OUTPUT
IFN $$FCI,PUSHJ P,CHRASC
	CAIN A,177
	 JRST OUTRUB
	CAIN A,^M
	 JRST CRLF
	JRST OUTPUT

OUTRUB:	MOVEI A,"^
	PUSHJ P,OUTPUT
	MOVEI A,"?
	JRST OUTPUT

;Type a CRLF via the output 1 character instruction.
CRLF:	MOVEI A,^M
	PUSHJ P,OUTCHR
	MOVEI A,^J
	PUSHJ P,OUTCHR
	POPJ P,

SUBTTL The operation of rubbing out one character

;This is the character action routine for Rubout, which we assume
;was not echoed.
RUBOUT:	PUSHJ P,DBPPTR		;Back up RB.PTR one byte.
	 JRST QUIT		;It was at beginning => return -2 ("overrrubout") from READ.
	MOVE A,C
	ILDB A,A		;What character are we rubbing out?
	LDB D,C
	CAIN A,^J		;If rubbing out a ^J which follows a ^M,
	 CAIE D,^M
	  JRST RUBOU3
	CAMN C,RB.BEG(B)
	 JRST RUBOU3
	SKIPG RB.TYP(B)		;Rub out the ^M too.
	 JRST RUBOU4		;On display, just call rubout again after this call is done.
	PUSHJ P,DBPPTR		;On printing TTY, type a CRLF and flush both characters.
	JRST CRLF

RUBOU4:	PUSH P,[RUBOUT]
RUBOU3:	SKIPLE RB.TYP(B)	;Printing terminal?
OUTPUT:	 JRST OUTCHR		;on printing terminal, just echo the character.

;Here to rub the character in A out on a display screen.
RUBOU1:	PUSHJ P,CHRTYP
	XCT RUBTAB(A)
RUBOU2:	MOVEI A,"X
	JRST DISP2

RUBTAB:	JFCL		;Normal character;  fall through to RUBOU2 and send ^PX.
	JRST RUBCTL	;Control char may be one position or two.
	JRST RUBBS	;Backspace rubs out by moving forward.
	JRST RUBCR	;CR rubs out requiring redisplay.
	JRST RUBLF	;LF is simple, but maybe also rub preceding CR
	JRST RUBHT	;Tab is hard to rub out.
	PUSHJ P,RUBOU2	;Rubout and ^@ take two positions always.
	JRST OUTPUT	;^G takes no positions;  echo it to rub it out.

RUBCTL:	MOVSI D,RB%SAI	;Control chars are one pos or two according to sail mode.
	TDNN D,RB.TYP(B)
	 PUSHJ P,RUBOU2
	JRST RUBOU2

RUBBS:	RUBCR:	RUBHT:
IFN $$MULTI,[
	PUSHJ P,RESYNCH	;Compute current vpos,,hpos in D.
	MOVEI A,"H
	PUSHJ P,DISP2	;Move cursor to that position.
	MOVEI A,10(D)
	PUSHJ P,DISPLAY
	POPJ P,
]
.ELSE 	JRST REDISP

RUBLF:	MOVEI A,"U	;Rubbing out LF:  move up one line.
	JRST DISP2

;Decrement RB.PTR by one byte, updating RB.PRS as necessary.
;Skips unless RB.PTR was pointing at the beginning of the buffer,
;in which case it was not changed.  RB.PTR is left in C, either way.
DBPPTR:	MOVE C,RB.PTR(B)
	CAMN C,RB.BEG(B)
	 POPJ P,
	CAMN C,RB.PRS(B)
	 SETZM RB.PRS(B)
	PUSHJ P,DBPC
	MOVEM C,RB.PTR(B)
POPJ1:	AOS (P)
	POPJ P,

;Decrement the byte pointer in C.  If $$FCI, we handle 7-bit and 9-bit byte pointers.
;Otherwise, we handle only 7-bit byte pointers.
DBPC:
IFN $$FCI,[
	LDB D,[300600,,C]
	CAIE D,7
	 JRST DBP9
]
	ADD C,[070000,,]
	SKIPGE C
	 SUB C,[430000,,1]	;Decrement byte pointer.
	POPJ P,

IFN $$FCI,[
DBP9:	ADD C,[110000,,]
	SKIPGE C
	 SUB C,[440000,,1]
	POPJ P,
]

IFN $$MULTI,[

;Compute current Vpos,,hpos at RB.PTR and return it in D,
;by assuming that RB.POS is valid for beginning of buffer
;and scanning through the buffer, seeing what the characters do to the cursor.
;Clobbers A and C.
RESYNCH:
	HRRZI C,1(P)		;C points to 3-word block on stack.
	MOVE D,RB.BEG(B)	;(C) holds b.p. to scan with.
	PUSH P,D
	HRRZ D,RB.POS(B)	;1(C) holds HPOS.
	PUSH P,D
	HLRZ D,RB.POS(B)	;2(C) holds VPOS.
	PUSH P,D
RBCRS1:	MOVE D,(C)
	CAMN D,RB.PTR(B)	;Scan till reach end of occupied region.
	 JRST RBCRS2
	PUSHJ P,RBFORW
	JRST RBCRS1

RBCRS2:	HRRZ D,1(C)		;D gets the new Vpos,,Hpos.
	HRL D,2(C)		;Flush the block of temporary variables.
	SUB P,[3,,3]
	POPJ P,

;Cursor position calculating routines.

;Move pointer in (C) one character forward, adjusting VPOS in 2(C)
;and HPOS in 1(C) for the character moved over.
RBFORW:	ILDB A,(C)
	PUSHJ P,CHRTYP		;Get the character's "type code".
	XCT RRFORT(A)		;DISPATCH ON TYPE OF CHAR.
RRFOR1:	 AOS A,1(C)
RRFOR3:	CAMGE A,RB.WID(B)	;HAVE WE MOVED PAST RIGHT MARGIN?
	 POPJ P,
	CAMN A,RB.WID(B)	;CHECK FOR JUST REACHING THE RIGHT MARGIN.
	 JRST RRCNTN
RRCNT1:	SUB A,RB.WID(B)
	MOVEM A,1(C)
RRFORV:	AOS 2(C)
	POPJ P,

RRCNTN:	MOVE D,(C)
	ILDB D,D
	CAIN D,^M
	 POPJ P,
	JRST RRCNT1

RRFORT:	AOSA A,1(C)	;ORDINARY CHAR, MOVE FWD 1 POS.
	JRST RRFORC	;NON-FORMATTING CONTROLS.
	JRST RRFORH	;MOVE FWD OVER ^H - CHECK ^HPRINT FLAG.
	JRST RRFWCR	;^M, SPECIAL.
	JRST RRFORL	;^J, DOWN 1 LINE.
	JRST RRFOTB	;^I
	JRST RRFOR2	;2-POS CTL CHRS NOT AFFECTED BY FS SAIL (Rubout and ^@)
	POPJ P,		;^G takes up no space.

RRFOTB:	MOVE D,1(C)
	MOVEI A,10(D)
	ANDCMI A,7		;A HAS NEXT TAB STOP'S POSITION.
	CAMLE A,RB.WID(B)	;BUT IF THAT'S OFF THE SCREEN, TAB STOP IS RIGHT MARGIN,
	 CAMN D,RB.WID(B)	;UNLESS WE'RE ALREADY AT THE MARGIN, IN WHICH CASE
	  CAIA			;WE CAN TAB 8 SPACES INTO NEXT LINE VIA CONTINUATION.
	   MOVE A,RB.WID(B)
	MOVEM A,1(C)
	JRST RRFOR3

RRFORL:	AOS 2(C)
	POPJ P,

RRFWCR:	SETZM 1(C)
	POPJ P,

RRFORH:	SKIPN 1(C)
	 JRST RRFOR2
	SOS 1(C)
	POPJ P,

;NON-FORMATTING CONTROLS, CHECK FS SAIL FLAG.
RRFORC:	MOVE D,RB.TYP(B)
	TLNN D,RB%SAI		;SAIL mode => it is a one-position character.
RRFOR2:	 AOS 1(C)		;Else treat as 2-pos ctl char.
	JRST RRFOR1

] ;IFN $$MULTI

.END RUBOUT
