'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 |
