Simple Command Shell

Hi. It seems that no one is able to help me with the question I posted. I thought it might be easier if I posted fully-commented (and I mean FULLY) code. This is the beginnings of a command shell for a hobby OS I am writing. Naturally, this program only uses BIOS interrupts (I will use my own kernel's interrupts later, but that's not the case for now). The program consists of a simple loop where the computer waits for a key to be pressed. When it is, the program tests to see if an enter, backspace, extended key, or normal key was pressed (for the latter, the character is simply added to the buffer). The problem arises when backspace is pressed. The subroutine written to handle backspaces first takes one character off the command buffer, then moves the cursor back one space, and finally removes the character from the screen. However, this subroutine doesn't always work. Sometimes the backspace subroutine works perfectly - the character is removed, the cursor moves, and the buffer is one character shorter. BUT when there are intPmptLen characters in the buffer, the program seems to ignore the backspace keypress. I am using NASM 0.98.38 to assemble this prog, and VMWare to test it (although I don't believe that it has anything to do with the problem). Thanks for any help you can give me! - Dale

[code]

[org 0x0000]
[bits 16]

[segment .code]

; prompt begins here

mov BYTE [intCmdLen], 0x00 ; set current command length to zero
mov di, strCmdBufr ; move location of command buffer to di
mov si, strPrompt
call dispstr

prompt:
call getkey

cmp al, 0x00 ; check if key entered is an extended key
je .extended_key ; if yes, go to appropriate function
cmp al, 0xE0 ; check if key entered is a new extended key
je .extended_key ; if yes, go to appropriate function

cmp al, 0x08 ; check if key entered is backspace
je .backspace_key ; if yes, go to appropriate function

cmp al, 0x0D ; check if key entered is enter
je .enter_key ; if yes, go to appropriate function

mov bh, [intCmdMax]
mov bl, [intCmdLen]
cmp bh, bl ; check to see if the maximum length has been reached
je prompt ; if yes, ignore keypress and continue loop

; otherwise, add character to buffer, display it and continue loop
mov [di], al ; add character to buffer
inc di ; increment buffer pointer
inc BYTE [intCmdLen] ; increment command length variable

mov ah, 0x0E ; BIOS display-character function
mov bl, 0x07 ; normal color and background
int 0x10 ; display character (actual character is already in al)

jmp prompt ; continue loop

.extended_key:
jmp prompt ; we don't really want to deal with this for now

.backspace_key: ; compare length of buffer to 0, and proceed accordingly
mov bh, 0x00 ; move value of zero into bh
mov bl, [intCmdLen] ; move length of buffer into bl
cmp bh, bl ; and compare
je prompt ; if the buffer is already at zero, resume loop

mov ah, BYTE [intCmdLen]
cmp ah, BYTE [intPmptLen] ; check to see if cursor is at edge of prompt / buffer len = zero
je prompt ; if it is, resume loop

dec BYTE [intCmdLen] ; otherwise, decrement buffer length
dec di

mov ah, 0x0E ; otherwise, BIOS display-character function will move cursor for us
mov bh, 0x00 ; ?
mov bl, 0x07 ; color and background normal
int 0x10 ; move cursor back one space

mov ah, 0x09 ; BIOS display-character without moving cursor function
mov al, ' ' ; replace whatever character with a blank space (i.e. ' ')
mov bh, 0x00 ; ?
mov bl, 0x07 ; color and background normal
mov cx, 0x01 ; times to display character (i.e. 1)
int 0x10 ; call function

jmp prompt ; continue loop

.enter_key:
mov BYTE [di], 0x00 ; make command buffer a null-terminated string

call .process_cmd

mov si, strCRLF ; display crlf
call dispstr


mov BYTE [intCmdLen], 0x00 ; reset current command length to zero
mov di, strCmdBufr ; move location of command buffer to di
mov si, strPrompt
call dispstr

jmp prompt ; resume loop

.process_cmd:
mov si, strCmdBufr ; strCmdBufr -> si
mov cx, 5 ; size of ['help',0] string
mov di, cmdHelp ; cmdHelp -> di
repe cmpsb ; compare bytes until match or no match after cx bytes
je .cmd_help ; if a match, jump to .cmd_help

mov si, strCmdBufr ; strCmdBufr -> si
mov cx, 4 ; size of ['echo'] string
mov di, cmdEcho ; cmdEcho -> di
repe cmpsb ; compare bytes until match or no match after cx bytes
je .cmd_echo ; if a match, jump to .cmd_echo

mov si, strCmdBufr ; strCmdBufr -> si
mov cx, 7 ; size of ['reboot', 0] string
mov di, cmdReboot ; cmdReboot -> di
repe cmpsb ; compare bytes until match or no match after cx bytes
je .cmd_reboot ; if a match, jump to .cmd_reboot

mov si, strCmdBufr ; strCmdBufr -> si
mov cx, 4 ; size of ['cls', 0] string
mov di, cmdCls ; cmdCls -> di
repe cmpsb ; compare bytes until match or no match after cx bytes
je .cmd_cls ; if a match, jump to .cmd_cls

ret

.cmd_help

mov si, strHelp
call dispstr

ret

.cmd_echo

mov si, strCRLF
call dispstr
mov si, strCmdBufr + 5
call dispstr

ret

.cmd_reboot

int 0x19

ret

.cmd_cls

push ax

xor ax, ax
mov al, 0x03
int 10h

pop ax

ret

; *********************************************************************

getkey:
mov ah, 0
int 0x16
ret

; *********************************************************************

reboot:
int 0x19
ret

; *********************************************************************

dispstr:

lodsb ; load a byte at ds:si into al
or al, al ; check to see if it is a 0
jz .done ; it is the end of the string if it is a zero/null

mov ah, 0Eh ; 0Eh function of int 10h prints character to screen
mov bx, 0007h ; sets the standard text attributes
int 10h ; calls video bios function - increases si by one byte
jmp dispstr

.done:
ret

; *********************************************************************

[segment .data]

strPrompt db "CMD-", 0
intPmptLen db 0x04
intCmdMax db 0xFC

strSpace db " ", 0
strCRLF db 13, 10, 0
strDot db ".", 0

cmdHelp db "help", 0
cmdEcho db "echo"
cmdReboot db "reboot", 0
cmdCls db "cls, 0"

strHelp db 13, 10
db "You typed 'help'. Following is a brief list of supported commands.", 13, 10
db 13, 10
db "help - this is what you just typed", 13, 10
db "echo ??? - prints a string where ??? is the string", 13, 10
db "cls - clears the screen", 13, 10
db "reboot - performs a cold restart", 13, 10
db 0

; *********************************************************************

[segment .vars]

strCmdBufr resb 0xFF
intCmdLen db 0x00

[/code]

Comments

  • (for the latter, the character is simply added to the buffer). The problem arises when backspace is pressed. The subroutine written to handle backspaces first takes one character off the command buffer, then moves the cursor back one space, and finally removes the character from the screen. However, this subroutine doesn't always work. Sometimes the backspace subroutine works perfectly - the character is removed, the cursor moves, and the buffer is one character shorter. BUT when there are intPmptLen characters in the buffer, the program seems to ignore the backspace keypress.

    your problem code is right here. You pointed it out in your description of the problem.

    [code]
    mov ah, BYTE [intCmdLen]
    cmp ah, BYTE [intPmptLen] ; check to see if cursor is at edge of prompt / buffer len = zero
    je prompt ; if it is, resume
    [/code]

    When the BS key is pressed, you check to see if the buffer length is 0, and skip it if so. Perfect. But then you check to see if the length of the buffer is = to the length of the prompt, which doesn't make much sense. This makes it break, because the prompt length is hardcoded at 4, as soon as you try to backspace beyond a 5 character input stream, it'll ignore it.

    Remove those above 3 lines of code and it works fine.

    -jeff!

  • I guess I got confused between the length of the command buffer and the distance of the cursor from the left edge of the screen. It works just fine now. That's a pretty stupid mistake to make - glad you could point it out. It is much appreciated ~

    - Dale
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories