Reversing a string given by the user in Assembly Language

Viewed 319

I am working on a program in Assembly Language right now where the user can input any word of their preference and then the program will reverse it. So, if the user inputs HELLO and the program will output OLLEH.

The algorithm I decided to implement was by first getting the length of the string and storing it in length. I will then use the length to traverse backward in the given string and store each character in a new memory location which is REVERSE. I tested my program several times but it only outputs one character. I tried several words and noticed that the character is always output is the second character of the string so I am unsure if it is able to reverse the word or not.

Did I miss anything or I implemented a code block incorrectly?

Note: The GET_STRING[end] 5 is used when I run the code in .exe format.

Below is the code I am currently working on:

%include "io.inc"
section .data
string dd 0x00
end dd 0x00
REVERSE dd 0x00
length db 0

section .text
global CMAIN
CMAIN:
    ;write your code here
    
    PRINT_STRING "Please enter a string: "
    GET_STRING [string], 10
    
    lea esi, [string]
    lea edi, [REVERSE]
    lea ecx, [length]
    
L1:
    mov al, [esi]
    cmp al, 0
    JE FINISH
    JNE INCREMENT
    inc esi
    jmp L1

INCREMENT:
    inc byte[length]
    inc esi
    jmp L1
    

    FINISH: 
    L2:
;points to the current index of the string (i.e. if Hello,it will first point at 'o' which is 5
    mov al, [ecx] 
    cmp cl, 0  ;checks if length == 0
    JE DONE
    JNE DECREMENT
    dec ecx
    jmp L2

DECREMENT:
    mov byte[edi], al ;adds a character from [string]
    dec ecx
    jmp L2
    
DONE:
    PRINT_STRING REVERSE
    GET_STRING[end], 5
    xor eax, eax
    ret
1 Answers

If GET_STRING gives you a zero-terminated string then your L1 part will find the length of that string.

This is what remains after removing the redundant code:

    lea   esi, [string]
L1:
    mov   al, [esi]
    cmp   al, 0
    je    FINISH
    inc   byte [length]
    inc   esi
    jmp   L1
FINISH:
lea ecx, [length]

In the L2 part however

  • you are using the address of the length variable as if it were an address into the string itself!
  • you are checking for length == 0 from a register that you did not load with the length

At the start of part L2, the ESI register still points at the terminating zero. So you'll have to decrement first and then fetch and store the character.

Of course you can't copy any characters if the length variable contains 0. So check this condition first.

    lea   edi, [REVERSE]
    mov   cl, [length]
    test  cl, cl
    jz    DONE
L2:
    dec   esi             ; Decrement
    mov   al, [esi]       ; Fetch
    mov   [edi], al       ; Store
    inc   edi
    dec   cl              ; 1 more character done
    jnz   L2
DONE:
    mov   [edi], cl       ; CL=0 at this point, now zero-terminating the result

If you keep your current data definitions, then don't input more than 3 characters. A dword dd allows for just 3 characters and 1 terminating zero.
Alternatively widen your buffers:

string  db 11 dup (0)        ; GET_STRING [string], 10
end     db  6 dup (0)        ; GET_STRING [end], 5
REVERSE db 11 dup (0)
length  db 0

Below is a version that doesn't use a memory-based length variable. The length of the string is stored in ECX.

    lea   edi, [REVERSE]
    lea   esi, [string]
L1:
    mov   al, [esi]
    inc   esi
    cmp   al, 0
    jne   L1
    lea   ecx, [esi - 1 - String]
    jecxz DONE
; ESI points behind the terminating zero, the fetch uses a -2 offset (avoiding address stall)
L2:
    mov   al, [esi - 2]   ; Fetch
    mov   [edi], al       ; Store
    dec   esi
    inc   edi
    dec   ecx             ; 1 more character done
    jnz   L2
DONE:
    mov   [edi], cl       ; CL=0 at this point, now zero-terminating the result
Related