'backspace error in asm input string routine

I'm writing an OS in asm and I'm trying to implement the MikeOS input string routine. When I run it, it works well the only thing is that I can only backspace one character, then it doesn't backspace anymore. Here is the code:

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

; os_input_string --- Get a string from keyboard input

; IN: AX = output address, BX = maximum bytes of output string
; OUT: nothing

os_input_string:
    
    pusha

    ; If the character count is zero, don't do anything.
    cmp bx, 0
    je .done

    mov di, ax          ; DI = Current position in buffer
    
    dec bx              ; BX = Maximum characters in string
    mov cx, bx          ; CX = Remaining character count

.get_char:

    call _getkey

    cmp al, 8
    je .backspace

    cmp al, 13          ; The ENTER key ends the prompt
    je .end_string

    ; Do not add any characters if the maximum size has been reached.

    jcxz .get_char

    ; Only add printable characters (ASCII Values 32-126)
    cmp al, ' '
    jb .get_char

    cmp al, 126
    ja .get_char

    call .add_char

    dec cx
    jmp .get_char

.end_string:

    mov al, 0
    stosb

.done:

    popa
    ret

.backspace:

    ; Check if there are any characters to backspace
    cmp cx, bx 
    jge .get_char

    inc cx              ; Increase characters remaining

    call .reverse_cursor        ; Move back to the previous character
    mov al, ' '         ; Print a space on the character
    call .add_char
    call .reverse_cursor        ; Now move the cursor back again

    jmp .get_char

.reverse_cursor:

    dec di              ; Move the output pointer backwards
    
    call _get_cursor_pos
    cmp dl, 1           ; Is the cursor at the start of line?
    je .back_line

    dec dl              ; If not, just decrease the column
    call _set_cursor
    ret

.back_line:

    dec dh              ; Otherwise, move the cursor to the end
    mov dl, 79          ; of the previous line.
    call _set_cursor
    ret


.add_char:

    stosb
    mov ah, 0x0E            ; Teletype Function
    push bx
    mov bh, 0           ; Video Page 0
    push bp             ; Some BIOS's may mess up BP
    int 0x10
    pop bp
    pop bx
    ret

here's _get_cursor_pos

_get_cursor_pos:

    push bx
    mov ah, 03h
    mov bh, 0
    int 10h
    pop bx
    ret

here's _set_cursor

    _set_cursor:
    pusha
    mov bh, 00

    mov ah, 2
    int 10h
    popa
    ret


Solution 1:[1]

Backspace messes with your variables

When you retrieve the cursor position, you inadvertently modify the CX register that holds your remaining characters count. See the description of this BIOS function GetCursorPositionAndConfiguration.

You can't use pusha and popa for your _get_cursor_pos (like you did for the _set_cursor code), because you need to return the DX register. Use next code that allows AX to get trashed:

; IN () OUT (dx) MOD (ax)
_get_cursor_pos:
    push bx
    push cx
    mov  bh, 0
    mov  ah, 03h
    int  10h     ; -> CX, DX
    pop  cx
    pop  bx
    ret

; IN (dx) OUT () MOD (ax)
_set_cursor_pos:
    push bx
    mov  bh, 0
    mov  ah, 02h
    int  10h
    pop  bx
    ret

A small additional problem

cmp dl, 1           ; Is the cursor at the start of line?
je .back_line

The cursor column ranges from 0 to 79 on the 80x25 text screen. Testing for being at the start of the line needs to compare with 0.

The below code not only corrects the mistake, it also streamlines the procedure:

.reverse_cursor:
  dec  di              ; Move the output pointer backwards
  call _get_cursor_pos ; -> DL DH
  dec  dl              ; This DEC turns negative if the cursor was at the left
  jns  .SetCursor      ; If that happens, fall through in .back_line
.back_line:
  dec dh               ; Otherwise, move the cursor to the end
  mov dl, 79           ; of the previous line.
.SetCursor:
  jmp _set_cursor

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1