3

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
euriseth
  • 63
  • 6
  • 2
    Thanks for putting the work in to give a detailed problem description. Some comments on your code, indicating what you think your code is doing, might help, too. Without them, I can only see what it does and it's hard to tell if this aligns with your intent. – fuz Nov 27 '20 at 14:48
  • 1
    Thank you for pointing that out! I edited my post and added comments in my code. I hope my comments were able to explain what I am trying to implement right now. – euriseth Nov 27 '20 at 14:59
  • JE FINISH and JNE INCREMENT cover both possibilities, so the `inc esi` / `jmp L1` following them are unreachable. You should be able to simplify your logic by removing that, and simply have the `INCREMENT` case fall through into that block. (In fact, you should be able to arrange your loop to only have one total branch, a conditional at the bottom. [Why are loops always compiled into "do...while" style (tail jump)?](https://stackoverflow.com/q/47783926)) There's also no point in having a `length` in memory; you have enough registers. Also, hopefully an input function returns a length... – Peter Cordes Nov 27 '20 at 14:59
  • 1
    Also, `dd 0` only take 4 bytes of space, so the user entering anything longer than that will overlap into the space you reserved after another label. – Peter Cordes Nov 27 '20 at 15:00
  • @PeterCordes I thought that perhaps the `GET_STRING` function would allocate a buffer of the given length for the string, but I can't say for sure as I am not familiar with the `io.inc` macro package for ... whatever assembler it is OP is programming for. – fuz Nov 27 '20 at 15:07
  • @fuz: me neither, could be anything, especially when we don't know if this code is calling it correctly or not. It's plausible that `GET_STRING [string], 10` stores a pointer into `[string]`, but that seems like a weird design. For the current code to work, it would have to `lea reg, [string]` I think, which is also a weird design for the macro. Anyway, it could still return a length in EAX so you don't need to strlen it. – Peter Cordes Nov 27 '20 at 15:41
  • 1
    The `io.inc` is a package required in SASM. SASM is currently the assembler required for me to use to solve the problem. I am still trying to incorporate what everyone is sharing here right now to my answer because I am still having a difficult time migrating in another language. – euriseth Nov 29 '20 at 15:03
  • Given that *SASM* is an Integrated Development Environment (IDE) that can work with any of 4 different assemblers (*FASM*, *GAS*, *MASM*, and *NASM*), you should mention in future questions (if any of course...) which of these assemblers you use. It will enable us to write answers that use the correct syntax already. – Sep Roland Nov 29 '20 at 18:24

1 Answers1

2

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
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • 1
    Thank you for explaining to me the parts where I went wrong. Your explanation was really helpful in making me understand where the register is pointing. It really made things clearer. – euriseth Nov 29 '20 at 15:18