;*******************************************************************************
;* Module    : READLN.SUB
;* Programmer: Tony Papadimitriou <tonyp@acm.org>
;* Purpose   : Read a text line (with or w/out echo) from getc macro input
;* Language  : Motorola/Freescale/NXP HC08/9S08 Assembly Language (aspisys.com/ASM8)
;* Status    : FREEWARE Copyright (c) 2021 by Tony Papadimitriou <tonyp@acm.org>
;* Original  : http://www.aspisys.com/code/hc08/readln.html
;* Note(s)   : Use: #Include readln.sub
;* History   : 10.12.09 v1.00 Original
;*           : 16.06.17       BugFix: Added missing TSTA instruction to check length
;*           :                Added echo option via the putc macro
;*           :                Added backspace recognition in echo mode only
;*           :                Added Escape recognition in echo mode only
;*           : 16.08.01       Minor optimization in Readln proc [-1 byte]
;*           :                Removed redundant BEQ in Readln proc [-2 bytes]
;*           :                Minor optimization in Readln proc Done@@ [-4 bytes]
;*           : 21.09.09       Minor optimization by Readln proc Cont@@ [-2 bytes]
;*           :                Simplified CCR[C] checks in Readln proc [-3 bytes]
;*******************************************************************************

#ifmain ;-----------------------------------------------------------------------
                    #ListOff
                    #Uses     mcu.inc
                    #ListOn

getc                macro
          #if :index = 1
                    ...       \@~macro~\@ macro is a stub
                    mexit
          #endif
                    !...
                    endm

putc                macro
          #if :index = 1
                    ...       \@~macro~\@ macro is a stub
                    mexit
          #endif
                    !...
                    endm
#endif ;------------------------------------------------------------------------
?_OBJECT_?
;*******************************************************************************
; Purpose: Read a CR terminated line (LFs ignored)
; Input  : HX -> buffer for received string
;        : A = maximum buffer size (including NUL terminator)
;        : CCR[C] = 0: no echo and no editing allowed
;        : CCR[C] = 1: with echo and editing allowed
; Output : A = number of characters read
;        : (HX -> Buffer) is filled with input data
; Note(s): A terminator character (CR) is required to exit the routine
;        : If length is zero, you just wait for a CR without saving
;        : If more characters arrive than buffer can hold, they are truncated

Readln              macro     [Buffer][,MaxLength][,echo]
          #ifb ~@~
                    call      ~0~
                    mexit
          #endif
                    mdef      3,clc               ;;default is no echo
          #ifnoparm ~3~ = clc
                    mset      3,sec
          #endif
          #ifparm ~1~
           #ifb ~2~
                    #push
                    #spauto   :sp
                    pshhx
                    @@lea     ~1~
                    ~3~
                    call      ~0~
                    pulhx
                    #pull
                    mexit
           #endif
          #endif
          #ifparm ~2~
           #ifb ~1~
                    #push
                    #spauto   :sp
                    psha
                    lda       ~2~
                    ~3~
                    call      ~0~
                    pula
                    #pull
                    mexit
           #endif
          #endif
                    #push
                    #spauto   :sp
                    push
                    lda       ~2~
                    @@lea     ~1~
                    ~3~
                    call      ~0~
                    pull
                    #pull
                    endm

;-------------------------------------------------------------------------------

                    #spauto

Readln              proc
                    pshhx     .buf@@

                    #ais
                    psha      len@@

                    tpa
                    and       #CCR_C_             ;we only care for CCR[C] flag
                    psha      ccr@@

                    lda       len@@,sp
                    psha      remain@@            ;chars remaining (max characters to read)
                    beq       Save?@@             ;if zero, wait for terminator
          ;--------------------------------------
                    bra       Cont@@

Loop@@              @getc                         ;read a character
                    cbeqa     #LF,Loop@@          ;LFs are ignored
                    cbeqa     #CR,Done@@          ;on CR terminator, exit
          ;-------------------------------------- ;the remaining checks apply only in echo mode
                    tst       ccr@@,sp            ;is echo enabled?
                    beq       Save?@@             ;no, skip next checks

                    cbeqa     #BS,Backspace@@     ;on BS, if in echo mode, delete last character
                    cbeqa     #ESC,ClearLine@@    ;on ESC, if in echo mode, clear the whole line
          ;--------------------------------------
Save?@@             tst       remain@@,sp         ;a possible BS puts us back in the game
                    beq       Loop@@              ;if zero, only terminator will work

                    cmpa      #' '                ;nothing below space is accepted
                    blo       Loop@@

                    sta       ,x                  ;anything else save in buffer

                    tst       ccr@@,sp            ;is echo enabled?
                    beq       MakeAsciz@@         ;no, skip echo

                    @putc                         ;echo character

MakeAsciz@@         aix       #1                  ;point to next buffer position
Cont@@              clr       ,x                  ;make it ASCIZ string
                    dec       remain@@,sp         ;one less char to receive
                    bra       Loop@@
          ;--------------------------------------
ClearLine@@         lda       len@@,sp            ;delete all characters
                    bra       BS_Loop@@

Backspace@@         lda       #1                  ;delete just one character

BS_Loop@@           psha
                    lda       remain@@,sp         ;get remaining length
                    inca                          ;if incremented, check ...
                    cmpa      len@@,sp            ;are we at the beginning?
                    bhs       BS_Cont@@           ;yes, ignore backspace
          ;-------------------------------------- ;erase previous character
                    lda       #BS
                    @putc
                    lda       #' '
                    @putc
                    lda       #BS
                    @putc
          ;--------------------------------------
                    inc       remain@@,sp         ;un-count deleted character
                    aix       #-1                 ;back up buffer position
                    clr       ,x                  ;make it ASCIZ string
BS_Cont@@           pula
                    dbnza     BS_Loop@@           ;repeat requested number of times
                    bra       Loop@@              ;go get another character
          ;--------------------------------------
Done@@              tsx
                    lda       len@@,spx           ;original string length
                    sub       remain@@,spx        ;subtract remaining length
                    deca                          ;do NOT count end-of-line
          ;-------------------------------------- ;A = number of characters read
                    ais       #:ais               ;de-allocate temporaries
                    pulhx
                    rtc

                    #sp
;*******************************************************************************
                    #Exit
;*******************************************************************************
                    @EndStats

buffer              @var      80

Start               proc
                    @rsp
                    ldhx      #buffer
                    lda       #::buffer
                    sec
                    call      Readln
                    bra       *

                    @vector   Vreset,Start