; FANLED - FANcy Line EDitor for TurboDOS 1.4x
;
; RELEASE VERSION HISTORY
; 1.00  04/02/85 changed kill buffer logic to retain last kill buffer
;                contents until ^K or ^X (rather than erasing at the
;                beginning of each edit session).  This now allows
;                use of the kill buffer as "storage" for a frequently
;                used command.
; 1.10  04/05/85 Optimized code for size
; 1.11           fixed quote problem
; 1.20  04/09/85 Made edit keys global
; 1.30  05/04/85 Fixed problem in ^Y routine
;
       name    ('FANLED')
;
; Input:
;   HL points to a data structure:
;   <HL>   = CLBLEN (command line buffer length
;   <HL+1> = actual command line length
;   <HL+2> = first byte of null-terminated command line
; Output:
;   HL     = pointer to actual command line length
;   A      = actual command line length
;
z80
;
; Externals used:
;
       extrn   dms,echo,conin,conout
;
; Global entry point:
;
       public  inpln
;
; Global patch point:
;
       public  forspc
;
; command character equates
;
beglin  equ     'A'-40h         ; ^A - beginning of line
backch  equ     'B'-40h         ; ^B - cursor backwards
delfor  equ     'D'-40h         ; ^D - delete char forward
endlin  equ     'E'-40h         ; ^E - end of line
forwch  equ     'F'-40h         ; ^F - cursor forward
delbak  equ     'H'-40h         ; ^H - delete char backward
kilfor  equ     'K'-40h         ; ^K - kill forward
quote   equ     'Q'-40h         ; ^Q - quote next char
recall  equ     'R'-40h         ; ^R - recall previous command line
abort   equ     'U'-40h         ; ^U - abort line
kilbak  equ     'X'-40h         ; ^X - kill backward
yank    equ     'Y'-40h         ; ^Y - yank (unkill)
delete  equ     7fh             ; DEL- same as ^H
;
; other equates
;
cr      equ     0dh             ; carriage return
bell    equ     7               ; bell character
litc    equ     '^'             ; caret (prefix for control char display)
;
; data storage areas
;
       dseg
;
forspc: db      'L'-40h         ; nondestructive forward space character
qflag:  db      0ffh            ; quote next flag
clsave: dw      0               ; command line structure pointer save
clen:   db      0               ; command line length
insy:   db      0               ; insert yank flag
killen: db      0               ; length of kill buffer
kilbuf: ds      162             ; 162 byte kill buffer
oldlin: ds      162             ; old command line
;
; command table
;
cmdtbl:
fanbg:: db      beglin
       dw      cbegli
fanbk:: db      backch
       dw      cbackc
fanel:: db      endlin
       dw      cendli
fanfc:: db      forwch
       dw      cforwc
fandf:: db      delfor
       dw      cdelfo
fandb:: db      delbak
       dw      cdelba
fankf:: db      kilfor
       dw      ckilfo
fanqu:: db      quote
       dw      cquote
fanrc:: db      recall
       dw      crecal
fankb:: db      kilbak
       dw      ckilba
fanya:: db      yank
       dw      cyank
fanab:: db      abort
       dw      cabort
       db      delete
       dw      cdelba
       db      cr
       dw      exit
ncmds   equ     ($-cmdtbl)/3            ; number of commands
;
       cseg
;
; REGISTER USAGE:
; HL normally points to the current character in the command line
;    unless used otherwise
; DE normally points to the kill buffer or the old line buffer
; B  always holds the current byte count for the main command line
; C  always holds the current cursor position in the command line
; A  usually holds a byte received from the keyboard and/or echoed
;
; SUBROUTINES:
;
; GETCH - get character in A, preserve all regs
;
getch:  push    bc
       push    de
       push    hl
       call    conin
       jr      popr
;
; BKSPC - backspace
;
bkspc:  ld      a,delbak
;
; PUTCH - display character in A, preserve all regs
;
putch:  push    bc
       push    de
       push    hl
       ld      c,a
       push    af
       call    conout
       pop     af
popr:   pop     hl
       pop     de
       pop     bc
       ret
;
; ERROR - ring the bell
;
error:  ld      a,bell
       jr      putch
;
; DELLFT - delete left character with pointer movement
;          assume: HL points into command line, B holds
;          current byte count
;
dellft: dec     b               ; decrement count
       dec     c               ; decrement cursor pointer
       dec     hl              ; decrement pointer
       ld      a,(hl)          ; get byte
       cp      ' '             ; if control...
       jr      nc,delchl       ; delete one char if no control
       inc     d               ; increment control char counter
       call    delchl
;
; DELCHL - delete left character (bs,sp,bs)
;
delchl: push    bc
       push    de
       push    hl
       call    dms
       db      8,' ',88h
       jr      popr
;
; GETCMD - A=character, determine if it's an edit command
;
getcmd: push    hl
       push    bc
       cp      cr
       jr      z,iscr
       ld      hl,qflag
       bit     0,(hl)
       set     0,(hl)
       jr      z,getcmx
iscr:   ld      b,ncmds
       ld      hl,cmdtbl       ; point to command table
getcml: cp      (hl)
       jr      z,gotcmd        ; got a command
       inc     hl
       inc     hl
       inc     hl
       djnz    getcml          ; keep searching
       ld      b,a
       xor     a               ; set zero flag
       ld      a,b
       jr      getcmx          ; exit
;
gotcmd: inc     hl
       ld      e,(hl)
       inc     hl
       ld      d,(hl)
       xor     a
       inc     a               ; reset zero flag
getcmx: pop     bc
       pop     hl
       ret
;
;
; MAIN PROGRAM ENTRY POINT
;
;
inpln:  xor     a               ; zero byte count/cursor buffer
       ld      b,a
       ld      c,a
       dec     a
       ld      (qflag),a       ; reset quote flag
       ld      a,(hl)          ; get max byte count
       inc     hl              ; point to current byte count
       ld      (clsave),hl     ; save pointer
       ld      (hl),0          ; be sure command line is empty
       inc     hl              ; point to first char of command line
       ld      (clen),a        ; store max command line length
;
; this is the main loop
;
getnxt: call    getch           ; get a character
       call    getcmd          ; check if command, return zero if not
       jr      z,ascii         ; no command, enter it into cmd line
       push    de              ; jump to de
       ret
;
; enter A into command line, echo character
;
ascii:  push    af
       ld      a,(clen)        ; get max line length
       cp      b               ; reached?
       jr      nz,nolov        ;   nope
       pop     af
       call    error           ;      else beep
       jr      getnxt          ;         and try again
;
nolov:  ld      a,c             ; see if at end of line
       cp      b
       jr      nz,insert       ; inside line, so do insert mode
       pop     af
       ld      (hl),a
       inc     hl              ; increment
       inc     b               ;    pointers
       inc     c
       call    outch
       jr      getnxt          ; go get another
;
; INSERT - insert a character, move all others right
;
insert: ld      a,(clen)        ; get allowed length
       cp      b
       jr      nz,insne
       call    error           ; complain
       pop     af
       jr      getnxt          ;    and exit if too long
;
insne:  ld      a,b             ; get total chars in line
       sub     c               ; subtract cursor position
       push    bc              ; save pointers
       ld      c,a             ; set counter
       ld      b,0             ; bc=number of bytes right of cursor
       add     hl,bc           ; point to end of line
       ld      d,h             ; duplicate in de
       ld      e,l
       inc     de              ; de points past line
       inc     bc
       inc     bc
       lddr                    ; move line up one char
       inc     hl
       pop     bc              ; get counters back
       inc     b               ; increment cursor and count
       inc     c
       pop     af              ; now get character
       inc     hl              ; increment pointer
       ld      (hl),a          ; save it in command line
       push    hl              ; save pointer
       push    bc              ; save counters
       ld      a,b
       sub     c               ; count again
       inc     a
       ld      b,a
       ld      c,a
       ld      a,(hl)          ; compensate if ctl char inserted
       cp      ' '
       jr      nc,insl
       dec     c
insl:   ld      a,(hl)
       cp      ' '
       jr      nc,insnc
       inc     c
insnc:  call    outch           ; disply the char
       inc     hl
       djnz    insl
;
insd:   ld      b,c
       dec     b
insdl:  call    bkspc
       djnz    insdl
;
inse:   pop     bc
       pop     hl
       inc     hl
       jp      getnxt
;
; OUTCH - output a character, display control char if necessary
;
outch:  cp      ' '             ; check if ctl character
       jr      nc,noctl        ; skip if not
       push    af              ; save char
       ld      a,litc          ; prefix char
       call    putch           ; display it
       pop     af              ; get char back
       add     a,40h           ; make it alpha
noctl:  jp      putch           ; display char
;
;
; COMMAND PROCESSOR ROUTINES
;
; CR typed -- exit back to turbodos
;
exit:   ld      a,c             ; see if we're at end of line
       cp      b               ; if so continue
       jr      z,cntxit
       inc     hl              ; else up pointers
       inc     c
       jr      exit
;
cntxit: ld      (hl),0          ; terminate command line
       push    bc
       ld      a,cr
       call    echo            ; echo the RETURN
       pop     bc
       ld      hl,(clsave)     ; get command line pointer
       ld      (hl),b          ; save actual byte count
       push    hl              ; save
       push    bc              ;    registers
       ld      c,b             ; set up count
       ld      b,0
       inc     c
       ld      de,oldlin       ; point to previous line buffer
       ldir                    ; store current line there
       pop     bc              ; get regs back
       pop     hl
       ld      a,b             ; byte count in A
       ret
;
;
; CBEGLI - go to beginning of line
;
cbegli: inc     c               ; set up cursor pointer
cbegl1: dec     c               ; decrement cursor ptr
       jr      z,..gn1         ;    until zero
       call    bkspc
       dec     hl
       ld      a,(hl)
       cp      ' '
       call    c,bkspc
       jr      cbegl1
;
; CENDLI - go to end of line
;
cendli: ld      a,c             ; get cursor
       cp      b               ; if cursor at end of line
       jr      z,..gn1         ;    then get next char
       inc     c               ; point to next char
       ld      a,(hl)
       inc     hl
       cp      ' '
       ld      a,(forspc)
       call    c,putch
       call    putch
       jr      cendli          ; and so on
;
; CBACKC - back 1 char
;
cbackc: ld      a,c             ; get count
       or      a               ; if at left
       jr      z,..gn1         ;    do nothing
       call    bkspc           ; else backspace
       dec     c               ; decrement counter
       dec     hl              ;    pointer
       ld      a,(hl)
       cp      ' '
       call    c,bkspc
       jr      ..gn1
;
; CFORWC - forward 1 char
;
cforwc: ld      a,c             ; get count
       cp      b               ; if at end
       jr      z,..gn1         ;    do nothing
       inc     c               ; increment counter
       ld      a,(hl)
       cp      ' '
       ld      a,(forspc)
       call    c,putch
       call    putch           ; move cursor
       inc     hl              ;    pointer
.gn1:   jp      getnxt
;
; CQUOTE - do next char literally
;
cquote: xor     a
       ld      (qflag),a
       jr      ..gn1
;
; CRECAL - recall previous command line
;
crecal: inc     b               ; test if b=0
       dec     b
       jr      nz,..gn1        ; count must be 0
       ld      a,(oldlin)      ; anything in?
       or      a
       jr      z,..gn1         ;    no, empty
       push    hl
       ld      de,oldlin       ; get old line
       ex      de,hl           ; set up for move
       dec     de
       ld      c,(hl)
       ld      b,0
       push    bc
       inc     c
       ldir
       pop     bc
       ld      b,c             ; get count into b
       pop     hl              ; get pointer
dslp:   ld      a,(hl)          ; get byte
       call    outch           ; display it with ctl char expansion
       inc     hl              ; point to next
       djnz    dslp            ; until all done
       ld      b,c             ; count = cursor
       jr      ..gn1           ; exit
;
; CDELBA - delete previous character, move following chars to left
;
cdelba: ld      a,c             ; see if at start of line
       or      a
       jr      z,..gn2
       ld      de,0
       call    dellft          ; delete char to the left
       ld      a,b
       cp      c
       jr      z,..gn2
       jr      movdel          ; move after delete
;
; CDELFO - delete following character, move remainder left
;
cdelfo: ld      a,c             ; if cursor at right end
       cp      b
       jr      z,..gn2         ;    then do nothing
       ld      de,0
       dec     b
movdel: ld      a,b             ; see if end of line
       push    bc              ; save counters
       push    hl              ; save line ptr
       sub     c               ; a=number of chars
       push    af
       inc     a
       ld      c,a
       ld      b,0
       push    de              ; save control char count
       ld      d,h
       ld      e,l
       inc     hl
       ldir
       pop     de              ; get control char count (0/1)
       pop     af              ; get byte count
       pop     hl              ; get line pointer
       push    hl              ; save it again
       ld      b,a
       or      a
       jr      nz,mvdell
       ld      a,' '
       call    putch
       call    putch
       call    bkspc
       call    bkspc
       jr      mvdelq
;
mvdell: ld      a,(hl)
       cp      ' '
       jr      nc,mvd1
       inc     e
mvd1:   call    outch
       inc     e               ; increment char count
       inc     hl
       djnz    mvdell
       ld      a,' '
       call    putch           ; wipe out last char
       call    putch
       call    bkspc
mvdel1: ld      b,e
       call    bkspc
mvdel2: call    bkspc
       djnz    mvdel2
mvdelq: pop     hl
       pop     bc
.gn2:   jp      getnxt
;
; CKILFO - kill all chars forward
;
ckilfo: ld      a,b
       cp      c
       jr      z,..gn2         ; nothing to kill
       sub     c               ; get byte count
       ld      b,c
       push    bc              ; save count, cursor
       ld      (killen),a      ; store kill buffer length
       ld      c,a
       ld      b,0
       push    bc
       push    hl              ; save line pointer
       ld      de,kilbuf
       ldir                    ; move into kill buffer
       pop     hl
       pop     bc
       push    hl
       ld      b,c
ckilf1: ld      a,(hl)
       inc     hl
       cp      ' '
       ld      a,' '
       jr      nc,ckilf2
       call    putch
       inc     c
ckilf2: call    putch
       djnz    ckilf1
       ld      b,c
ckilf3: call    bkspc
       djnz    ckilf3
       pop     hl
       pop     bc
       ld      (hl),0
       jr      ..gn2
;
; CKILBA - kill all chars backward
;
ckilba: ld      a,c
       or      a
       jr      z,..gn2         ; nothing to kill
       push    bc              ; save counters
       push    hl              ; save pointer
       ld      de,(clsave)     ; get pointer to start
       inc     de
       or      a
       sbc     hl,de           ; hl is now length
       ld      b,h
       ld      c,l
       ld      hl,killen       ; point to kill buffer
       ld      (hl),c
       inc     hl
       ex      de,hl
       ldir                    ; move stuff into kill buffer
       pop     hl              ; get old ptr
       pop     bc
       push    bc
       push    hl
ckilb1: dec     hl
       ld      a,(hl)
       cp      ' '
       ld      a,8
       call    c,putch
       call    putch
       dec     c
       jr      nz,ckilb1
       ld      e,0
ckilb2: ld      a,(hl)
       inc     hl
       cp      ' '
       ld      a,' '
       jr      nc,ckilb3
       call    putch
       inc     e
ckilb3: call    putch
       inc     e
       djnz    ckilb2
       ld      b,e
ckilb4: call    bkspc
       djnz    ckilb4
       pop     hl              ; get line ptr
       pop     bc              ; get counters
       ld      a,b
       sub     c               ; a now has byte count
       ld      c,a
       ld      b,0
       push    bc
       inc     bc
       ld      de,(clsave)
       inc     de
       push    de
       ldir
       pop     hl
       pop     bc
       ld      b,c
       ld      c,0
       ld      a,b
       or      a
       jr      z,..gn3
       push    hl
       push    bc
       ld      e,0
ckilb5: ld      a,(hl)
       cp      ' '
       jr      nc,ckilb6
       inc     e
       push    af
       ld      a,litc
       call    putch
       pop     af
       add     a,40h
ckilb6: call    putch
       inc     e
       inc     hl
       djnz    ckilb5
       ld      b,e
ckilb7: call    bkspc
       djnz    ckilb7
       pop     bc
       pop     hl
.gn3:   jp      getnxt
;
; CYANK - yank kill buffer to current cursor
;
cyank:  ld      a,(killen)      ; get kill buffer length
       or      a
       jr      z,..gn3         ; kill buffer empty
       add     a,b             ; check total size
       jr      c,er1           ; gee, much too much (>256)!
       ld      e,a
       ld      a,(clen)
       cp      e
       jr      nc,yntl         ; go ahead if not too long
er1:    call    error           ; too long
       jr      ..gn3
;
yntl:   xor     a
       ld      (insy),a
       ld      a,b
       cp      c               ; check if cursor at end of line
       ld      a,(killen)      ; get length of kill buffer
       jr      z,nomove        ; skip if at end of line
       push    bc              ; save count
       push    hl              ; save pointer
       ld      e,a
       ld      d,0             ; de = number of bytes to be freed
       ld      a,b
       sub     c               ; a has bytes to be moved
       ld      c,a
       ld      b,0             ; bc has bytes tbm
       add     hl,bc           ; point to end of line
       push    hl
       add     hl,de           ; point to new end of line
       ex      de,hl
       pop     hl
       inc     bc
       inc     bc
       lddr
       pop     hl
       pop     bc
       ld      a,0ffh
       ld      (insy),a
       ld      a,(killen)      ; get length of kill buffer
nomove: push    bc
       push    hl
       ex      de,hl           ; destination to de
       ld      hl,kilbuf
       ld      c,a
       ld      b,0
       push    bc
       ldir
       pop     de
       pop     hl
       pop     bc
ydslp:  ld      a,(hl)
       call    outch
       inc     b
       inc     c
       inc     hl
       dec     e
       jr      nz,ydslp
       ld      a,(insy)
       inc     a
       jr      nz,..gn3
;
; now redisplay remainder of line, then step back
;
       push    hl              ; save line ptr
       push    bc
       ld      e,0             ; character count
ytl1:   ld      a,c
       cp      b
       jr      z,ytl25
       inc     c
       ld      a,(hl)
       cp      ' '
       jr      nc,ytl2
       inc     e
ytl2:   inc     e
       call    outch
       inc     hl
       jr      ytl1
;
ytl25:  pop     bc
ytl3:   call    bkspc
       dec     e
       jr      nz,ytl3
       pop     hl
       jr      ..gn3
;
; abort line
;
cabort: ld      bc,0
       ld      hl,(clsave)
       inc     hl
       jp      exit
;
       END
ointer
       ld      e,a
       ld      d,0             ; de = number of bytes to be freed
       ld      a,b
       sub     c               ; a has byt