OBJNAM ED.SBR ; Created 23-Aug-85, last modified 23-Jan-86
; Puts specified Interactive BASIC line into user's input buffer for
; editing using the VUE-like system line input editing facility.
; by Irv Bromberg, Medic/OS Consultants, Toronto, Canada
RADIX 10
VEDIT=19
VMINOR=2
VMAJOR=4
VSUB=0

IF EQ,1

Assembled under AMOS/L 1.3 program 4.2(19) size is 908 bytes, hash code
is 001-606-273-412.

Calling syntax:  XCALL ED,LineSpec

where LineSpec is a floating expression specifying BASIC line# to be
edited or is a string containing an ED.SBR command.  The valid ED.SBR
commands are:

       xcall ed,"F"    edit first line
       xcall ed,"L"    edit last line
       xcall ed,"N"    edit next line  (same as XCALL ED with no parameter)
       xcall ed,"P"    edit previous line
       xcall ed,"I"    insert line

The insert line command brings up the last edited line number followed
by a space provided that that line number does not already exist.  It
therefore inserts AFTER the last edited line.  You may wish to leave gaps
between line numbers for later use, in that case use the insert line
command repeatedly (convenient if a RELIEF function key is defined for
this, see below) until the desired line number appears OR simply
backspace to change the line number to leave the desired gap, being
careful not to use an existing line number.

Because of the way RELIEF handles programmable functions it is
convenient to program the following functions into RELIEF.TBL:

       @f=xcall ed,"f" ; Funct-f =edit first line
       @l=xcall ed,"l" ; Funct-l =edit last line
       @n=xcall ed,"n" ; Funct-n =edit next line
       @p=xcall ed,"p" ; Funct-p =edit previous line
       @i=xcall ed,"i" ; Funct-i =insert line
       @e=xcall ed,$   ; Funct-e =allow line# to be input

This allows the desired function key to be hit without having to clear
the currently edited line since RELIEF automatically clears the current
input line before processing the programmable function keycode.

The specified line is displayed for editing using the VUE-like system
line editor (LINEED, RELIEF, or AMOS/L 1.4 system line editor) Any
Horizontal Tab control characters in the source code of the line will be
converted to single spaces as the line is displayed, so that the line
editor will not get confused.  The cursor will be left at the end of the
line to be edited.  If the line is too long for the user's terminal's
type-ahead input buffer a warning will be issued (excess cannot be
displayed). ED.SBR can only be invoked from Interactive BASIC - when it
is called from a compiled BASIC program it will display an error message
and signal a Control-C Operator Interrupt to BASIC. The default line
number is the line after the last line edited by ED.SBR, or the first
line if ED.SBR is being called for the first time since the BASIC.LIT
command was invoked (the last edited line number is stored in the
filename extension word of the memory module containing the program
being edited - this word is not used by BASIC and therefore use of it
does not interfere with BASIC programming). When a line number greater
than the last existing line number is specified ED displays the number
of the last existing line.  Attempt to go to next line after the last
line causes the last line to be returned (except for Insert command).

SCALE is supported (scaled floating values) for line numbers.

Edit history:
3.1(15) 4-Dec-85    Released to AMUS, for publication in AMUS.LOG.
3.2(16) 11-Dec-85   Added support for SCALE (scaled floating values).
                   Use user stack for floating workspace.
4.0(17) 16-Dec-85   Major rewrite to add ED.SBR string commands
4.1(18) 17-Dec-85   "Next" to stop at end of program instead of errmsg.
       18-Dec-85   Made SCALE support optional by conditional assembly.
       30-Dec-85   Changed back SCALE to required assembly.
4.2(19) 23-Jan-86   Fix to work with RELIEF's new KBD re-display patch,
                   need to TRMICP a ^S before the line and a ^Q after it.
ENDC

SEARCH SYS
SEARCH SYSSYM
SEARCH TRM

JCB     =A0
Impure  =A0
Prog    =A1
LastLn  =A2
ASCBUF  =A2
ArgBas  =A3
String  =A3
Worksp  =A4
TCB     =A5
Float   =A6
Atemp   =A6

Line    =D0
Char    =D1
CrtCmd  =D1
Number  =D1
Length  =D2
Count   =D3
LineNo  =D4
LastNo  =D5
Ptype   =D6
Dtemp   =D6
Total   =D7

HT=9
CR=13
XON=17
XOFF=19
SPACE=32
CTRL=-64
Cmd=^H0FF00

SCALE=128       ; =offset to SCALE value in BASIC impure area

       PHDR    -1,0,PH$REE!PH$REU      ; re-entrant & re-usable

       TST     @Impure                 ; are we in Interactive BASIC?
       BNE     Go
       TYPECR  <?ED.SBR can only be called from Interactive BASIC>
       MOV     JOBCUR,JCB
       ORW     #J.CCC,JOBSTS(JCB)      ; signal error to BASIC program
       RTN

Go:     MOV     @Impure,Prog
       LEA     LastLn,-6(Prog)         ; last edited line stored in EXT word
       CLR     LineNo                  ; pre-clear for word move
       MOVW    (LastLn),LineNo
       CLR     Length                  ; pre-clear for word moves
       CLR     Line                    ; pre-clear last line number
       ADD     #2,Prog                 ; now pointing to first line length
       TSTW    @ArgBas                 ; any parameters passed?
       JEQ     Next                    ; no, go to next line
       MOVB    2(ArgBas),Ptype         ; get parameter type
       CMPB    Ptype,#4                ; line number must be floating
       JEQ     GetFlt
       CMPB    Ptype,#2                ; is it string?
       BNE     Syntax                  ; yes, go parse the string

Parse:  MOV     4(ArgBas),Atemp         ; index the string
       MOVB    @Atemp,Char             ; get the command character
       UCS                             ; make it uppercase
       CMPB    Char,#'F                ; "First"?
       BNE     10$
       CLR     LineNo                  ; set next after zero
       JMP     Next
10$:    CMPB    Char,#'L                ; "Last"?
       JEQ     FndLast
       CMPB    Char,#'N                ; "Next"?
       JEQ     Next                    ; yes, goto next line
       CMPB    Char,#'P                ; "Previous"?
       JEQ     FndPrev
       CMPB    Char,#'I                ; "Insert"?
       JEQ     Insert

Syntax: TTYI
       ASCII   "?Syntax:  XCALL ED,LineSpec"
       BYTE    CR
       ASCII   "LineSpec may be line number (floating expression) or"
       BYTE    CR
       ASCII   "F=First  L=Last  N=Next  P=Previous  I=Insert"
       BYTE    CR,0

Empty:  BYTE    ' ,0                    ; empty line for insert
       EVEN
       RTN

Insert: ; find last edited line number
       INCW    LineNo                  ; make sure next line# doesn't exist
10$:    MOV     Line,LastNo
       ADDW    Length,Prog
       MOVW    @Prog,Length
       MOVW    2(Prog),Line
       CMPW    Line,#-1                ; trap reached end of prog
       BEQ     OK
       CMPW    Line,LineNo
       BLO     10$
       BNE     OK
       TYPECR  <?Cannot insert, line already exists>
       RTN                             ; return to BASIC
OK:     LEA     Prog,Empty              ; point to empty line for insertion
       SUB     #4,Prog
       MOVW    #1,Length               ; only one space there
       JMP     Found

FndPrev:MOV     Line,LastNo             ; find previous line number
       MOV     Prog,Atemp              ; save pointer
       ADDW    Length,Prog
       MOVW    @Prog,Length
       MOVW    2(Prog),Line
       CMPW    Line,#-1                ; trap reached end of prog
       BEQ     10$
       CMPW    Line,LineNo
       BLO     FndPrev
10$:    MOV     Atemp,Prog              ; restore pointer to Prev line
       MOVW    @Prog,Length
       MOVW    2(Prog),Line
       MOVW    Line,LineNo
       JMP     Found

Next:   MOV     Line,LastNo
       MOV     Prog,Atemp              ; in case we pass end of program
       ADDW    Length,Prog
       MOVW    @Prog,Length
       MOVW    2(Prog),Line
       CMPW    Line,#-1                ; trap reached end of prog
       BEQ     SetLast
       CMPW    Line,LineNo
       BLOS    Next
       MOVW    Line,LineNo
       JMP     Found

FndLast:MOV     Line,LastNo             ; search for last line
       MOV     Prog,Atemp              ; save pointer
       ADDW    Length,Prog
       MOVW    @Prog,Length
       MOVW    2(Prog),Line
       CMPW    Line,#-1                ; trap reached end of prog
       BNE     FndLast
SetLast:MOV     Atemp,Prog              ; recall pointer
       MOVW    @Prog,Length
       MOVW    2(Prog),Line            ; recall line#
       MOVW    Line,LineNo
       JMP     Found

GetFlt: MOV     4(ArgBas),Float         ; get address of specified line#
       SUB     #6,SP                   ; get some workspace
       MOV     SP,Worksp               ; point there
       MOVB    (Float)+,(Worksp)+      ; get line#
       MOVB    (Float)+,(Worksp)+
       MOVB    (Float)+,(Worksp)+
       MOVB    (Float)+,(Worksp)+
       MOVB    (Float)+,(Worksp)+
       MOVB    (Float)+,(Worksp)+
       MOV     SP,Worksp               ; restore pointer
       MOV     Scale(Impure),D7        ; scale factor in use?
       BEQ     GetNum
       NEG     D7
       FPWR    @Worksp,D7              ; yes, adjust the scaled value
GetNum: FFTOL   @Worksp,LineNo          ; get specified line number
       ADD     #6,SP                   ; return stack workspace
       TST     LineNo
       BNE     ChkTop
Range:  TYPECR  <?Line number must be in range 1 to 65533>
       RTN
ChkTop: CMP     LineNo,#65533           ; make sure line# within range
       BHI     Range
NxtLin: MOV     Line,LastNo
       ADDW    Length,Prog             ; skip to next line
       MOVW    @Prog,Length            ; get next line length
       MOVW    2(Prog),Line            ; get next line number
       CMPW    Line,#-1                ; -1 means end of program found
       BEQ     Passed
       CMPW    Line,LineNo             ; is this the matching line number?
       BLO     NxtLin                  ; too low, keep searching
       BEQ     Found                   ; yes, found it
NotFnd: TYPECR  <?Line not found>
       RTN

Passed: MOV     LastNo,Number           ; get last line number found
       BEQ     Nil                     ; nothing found
       TYPE    <?Last line number is>  ; passed last existing line number
       DCVT    0,OT$TRM!OT$LSP         ; so show what that line number is
       CRLF
       RTN

Nil:    TYPECR  <?No program found>
       RTN

Found:  MOVW    LineNo,@LastLn          ; save last edited line#
       MOV     JOBCUR,JCB
       MOV     JOBTRM(JCB),TCB
       MOV     T.IBS(TCB),Count
       SUB     #5,Count                ; allow for CR LF NULL, DBF pre-decr
       ; found specified line#, cursor up and clear eos to keep things neat
       MOVW    #Cmd!3,CrtCmd           ; cursor up
       TCRT
       MOVW    #Cmd!10,CrtCmd          ; clear to eos
       TCRT
       LEA     String,4(Prog)          ; now skip ahead to first blank/tab
10$:    MOVB    (String)+,Char
       CMPB    Char,#SPACE
       BEQ     20$
       CMPB    Char,#HT                ; have to output HT as single space
       BEQ     20$                     ; else line editor will get confused
       DEC     Length
       BR      10$
20$:    MOV     LineNo,Number           ; copy for DCVT
       SUB     #8,SP                   ; get some memory workspace
       MOV     SP,ASCBUF
       DCVT    0,OT$MEM!OT$TSP         ; output line# to input buffer
       CLRB    (ASCBUF)                ; terminate with NULL
       MOV     ASCBUF,Total
       SUB     SP,Total                ; calculate length of line number
       ADD     Length,Total            ; add length of line
       CMP     Total,Count             ; check if we have enough room
       BLOS    PutNum                  ; yes, no warning

Warning:TTYI
       ASCII   "%Warning - Line longer than Input Buffer, "
       ASCII   "excess cannot be displayed"
       BYTE    CR,0
       EVEN
WaitOIP:TSTB    T.STS(TCB)              ; Wait for OIP to finish before
       BPL     10$                     ; continuing
       SLEEP   #500                    ; sleep wastes less CPU time than
       BR      WaitOIP                 ; a tight loop would
10$:    ; output has finished

PutNum: CLR     Char
       MOVB    #XOFF,Char              ; suspend terminal output
       TRMICP
       ORW     #J.TIW,JOBSTS(JCB)      ; fake input wait state
       MOV     SP,ASCBUF               ; restore pointer to start of number
NxtDig: MOVB    (ASCBUF)+,Char          ; get next digit
       BEQ     PutLin                  ; terminate on NULL
       TRMICP
       DBF     Count,NxtDig
PutLin: ADD     #8,SP                   ; return used workspace
NxtChr: MOVB    (String)+,Char          ; reached NULL terminator in string?
       BEQ     Done
       CMPB    Char,#HT                ; if it's a tab, convert to a space
       BNE     10$
       MOVB    #SPACE,Char
10$:    TRMICP                          ; force it as input to self
       DBF     Count,NxtChr
Done:   ANDW    #^C<J.TIW>,JOBSTS(JCB)  ; undo fake input wait state
       MOVB    #XON,Char               ; re-enable terminal output
       TRMICP
       RTN     ; (leave cursor positioned at the end of the displayed line)

       END