Home > OS >  Assembly skips first symbol while printing a message
Assembly skips first symbol while printing a message

Time:09-22

It is my first time writing in Assembly, so the code might not be perfect (or even good), but it is how I worked it out, don't judge too much :) . It is written for Intel x86 using DOSbox and Turbo Assembler/Linker/Debugger if that makes any difference. The task is to get a line from the user and convert all uppercase into lowercase (leaving everything else as is). The problem is that when I print each symbol one by one and add '\n'(new line) after it, it works fine, however, I need my output to be in a single line, so when I try to print without '\n', it skips the very first symbol. Why it does that, how to fix it, and why it works with '\n'?

; Task is to get input from the user and convert any upper case letters into lower case
; and print modified line

.model small
.stack 100h

.data
    start_msg db "Enter a line:", 0Dh, 0Ah, 24h    ; "line\n", $
    out_msg db "Converted line:", 0Dh, 0Ah, 24h
    buffer db 100, ?, 100 dup(0)
.code

start:
    mov dx, @data                  ; moves data into dx
    mov ds, dx                     ; moves dx (data) into data segment

    ; prints start_msg
    mov     ah, 09h
    mov     dx, offset start_msg
    int     21h
    
    ; reads inputed line and puts it into buffer
    
    mov ah, 0Ah
    mov dx, offset buffer
    int 21h
    
    ; prints '\n' 
    mov dl, 0Ah 
    mov ah, 02h
    int 21h
    
    ; prints out_msg
    mov ah,  09h
    mov dx, offset out_msg
    int 21h
    
    ; puts pointer to the first character of inputed line in bx
    mov bx, offset buffer   2
    loopString: 
        
        mov dl,[bx] ; reads a simbol from buffer and puts into dl
    
        cmp dl, 'A' ; if symbol is "more" than 'A', jump to ifUpper
        jae ifUpper
        
        ; prints simbol
        print:
            mov ah, 02h
            int 21h
        
        ; checks, if line ended
        inc bx ; bx  
        cmp dl, 0
        je endLoop ; ends looping
        
        ; temp part of code, puts '\n' after each printed symbol 
        ; if it is commented, skips first character from inputed line
        ; everything works if used, however I need final output to be in one line. 
        ;mov dl, 0Ah ; Uncomment me
        ;mov ah, 02h ; Uncomment me
        ;int 21h ; Uncomment me
        
        jmp loopString ; if line end was not detected, loops again 
        
    ifUpper:
        cmp dl, 'Z' ; checks if symbol is "more" than 'Z'
        ja print ; it is not upper, just prints the symbol
        add dl, 20h ;  32 (converts upper to lower)
        jmp print ; prints converted symbol
        
    endLoop:
    
    mov ah, 4ch             ; back to dos
    mov al, 0               ; error
    int 21h                
end start

Un\comment lines that say "Uncomment me" to see outputs with and without '\n'. Thanks in advance.

CodePudding user response:

cmp dl, 0
je endLoop ; ends looping

This is the problematic part of your code. You should be comparing to the value 13 instead. I suggest you read about how the DOS.BufferedInput function works in this Q/A. DOS always includes the carriage return as a terminating byte.

  • You have chosen to compare with zero because you have started from an all-zeroes input buffer. However, the zero that you are looking for might not even be there anymore by the time that DOS returns control to your code! If the user at the keyboard first types a (very) long text and then starts backspacing the zero is gone.

  • In case the zero is still in the right place, there's a second reason why it fails. You have placed the check for zero after printing it, which doesn't make sense since it's not part of the input, is it? Now an ASCII code 0 prints as a space character and because the byte preceding the zero was inevitably a carriage return (13), the cursor already moved to the beginning of the line and the first character on the line was erased. There's really nothing magic about printing an ASCII code 0.

This is how you could write it. Because you do want to print the terminating carriage return, even an 'empty input' consists of one byte. Therefore a Repeat-Until loop is fine.

    mov  bx, offset buffer   2
Repeat:
    mov  dl, [bx]
    cmp  dl, 'A'
    jb   print
    cmp  dl, 'Z'
    ja   print
    add  dl, 32     ; Make LCase
print:
    mov  ah, 02h
    int  21h
    inc  bx
    cmp  dl, 13
    jne  Repeat     ; Until 13 was printed
    mov  dl, 10 
    int  21h
  • Related