Search code examples
stringassemblyinputx86irvine32

Reversing and changing case of a given string x86 assembly


The objective of this program is to reverse a given string while switching each letter's case. String cannot be longer than 20 characters, if input is longer than that, program requires user to enter string again. Program ends when user inputs 'enter' and the program ends after printing an ending sentence.

Implementing this I have 3 problems:

  1. I tried to get input string by using call ReadString and since this procedure stops when enter key is given, the console freeze-ends when I press the enter key to normally finish the program. How could I correct my code to make it print an ending message and then end the program normally with return value 0?

  2. If input string is longer than 20 characters, it should require user to input string again. So I wrote ja L1. Bur for some reason, mov bytecount, eax; cmp bytecount, 20; cannot seem to filter the case properly. When the line mov bytecount, eax is executed, the value of bytecount is correct, but when the program executes the next line, cmp bytecount, 20, the value of bytecount changes. I don't know what I'm doing wrong.

  3. CaseChange procedure freezes when it is executed, so I guess it's looping infinitely, but I can't find what condition is wrong.

.data
MaxLength = 20
prompt3 BYTE "End of program",0
buffer BYTE MaxLength DUP(0)
bytecount DWORD ?

.code 
main PROC
    call Clrscr
L1: mov edx, OFFSET buffer
    mov ecx, SIZEOF buffer
    call PromptForInput     ; printing input prompt
    call ReadString      
    mov bytecount, eax
    cmp bytecount, 20       ;*** get input again if number of characters in the string is greater than 20
    ja L1                   ;*** 
    call ReverseString     
    call CaseChange         ;***
    mov edx, OFFSET buffer
    call WriteString        ;printing the result
    loop L1
    mov edx, OFFSET prompt3 ;*** after input <ent> how do I print prompt3?
    call WriteString
    exit
main ENDP

CaseChange PROC
    pushad 
    mov eax, OFFSET buffer
L1:
    mov dl, BYTE PTR[eax]
    test dl, dl
    jz L3
    cmp dl, 'A'
    jl L3
    xor dl,32
    cmp dl,'z'
L2:
    inc eax
    jmp L1
L3:
    popad
    ret
CaseChange ENDP

(input prompt): Cats and Dogs.

(output prompt): .SGOd DNA STAc

(input prompt): too long for the given limit

(input prompt):

End of program


Solution

  • The manual tells us for ReadString:

    Reads a string of up to ECX non-null characters from standard input, stopping when the user presses the Enter key.
    A null byte is stored following the characters input, but the trailing carriage return and line feed characters are not placed into the buffer.
    ECX should always be smaller than the buffer size (never equal to the buffer size) because the null byte could be the (ECX+1)th character stored.

    Following this it's clear to see that you need to enlarge the buffer:

    buffer BYTE MaxLength + 1 DUP (0)
    

    If you specified ECX=MaxLength then you can't get an input that is longer than MaxLength characters and thus there's no real need to test for such condition. (*)

    If the user presses the enter key with no preceeding characters, then ReadString will return with EAX=0. Test this and jump to your final message.

    A big error is where you wrote loop L1. The loop instruction works with the ECX register to perform a known number of iterations. Your program needs to just jump back to the top with no conditions attached. Use jmp L1.

    It's best if you keep things logically together. Don't mix call PromptForInput with call ReadString and its parameters. Can you be sure that PromptForInput doesn't change EDX or ECX?

    L1: call    PromptForInput
    
        mov     edx, OFFSET buffer
        mov     ecx, MaxLength
        call    ReadString         ; -> EAX
        test    eax, eax
        jz      L2                 ; Exit
    
        call    ReverseString       ; Is this working fine?
        call    CaseChange
    
        mov     edx, OFFSET buffer
        call    WriteString         ; Printing the result
    
        jmp     L1
    
    L2:
        mov     edx, OFFSET prompt3
        call    WriteString         ; Final message
    
        exit
    

    • cmp dl, 'A' jl L3 The ChangeCase procedure needs to traverse the whole string, yet you leave as soon as you stumble upon a byte less than 65. Please use the unsigned condition when working with ASCII codes [0,255].

    • xor dl,32 You don't actually write any changes in the string memory.

    • cmp dl,'z' You don't act on this compare.

    If all you need to preserve is 1 register then don't use pushad and popad.

    CaseChange PROC
        push    esi
        mov     esi, OFFSET buffer
    L1:
        lodsb
        test    al, al        ; End of string ?
        jz      L2
        or      al, 32        ; If "A".."Z" Then "a".."z"
        cmp     al, 'a'
        jb      L1
        cmp     al, 'z'
        ja      L1
        xor     byte ptr [esi-1], 32  ; Change case IN string memory
        jmp     L1
    L2:
        pop     esi
        ret
    CaseChange ENDP
    

    (*) If you want to impose this "String cannot be longer than 20 characters" error then define a much bigger buffer and allow ReadString to return more than 20 characters so you can jump back in the program:

    buffer BYTE 99 + 1 DUP (0)
    
    ...
    
    L1: call    PromptForInput
    
        mov     edx, OFFSET buffer
        mov     ecx, 99
        call    ReadString         ; -> EAX
        cmp     eax, 20
        ja      L1
        test    eax, eax
        jz      L2                 ; Exit
    

    One final advice is to make sure that ReverseString and CaseChange are OK by testing them independently:

        call    ReverseString
        ;;;call    CaseChange
        mov     edx, OFFSET buffer
        call    WriteString         ; Printing the result
    

    and later

        ;;;call    ReverseString
        call    CaseChange
        mov     edx, OFFSET buffer
        call    WriteString         ; Printing the result
    

    and only then

        call    ReverseString
        call    CaseChange
        mov     edx, OFFSET buffer
        call    WriteString         ; Printing the result