How to reverse and print a string in assembly language

25,376

Solution 1

This is against my better judgement since the snippet of code for reversing hasn't even been integrated into the code the original poster created. The variable names differ. A quick and dirty integration of the code is to create a variable nameSize that holds the number of characters read from a call to ReadString. ReadString (part of the Irvine32 library) returns the number of characters read in register EAX.

In the .data section add the variable:

nameSize  DWORD ? 

After the ReadString move contents of EAX register to nameSize. This code:

mov edx, OFFSET buffer
mov Ecx, SIZEOF buffer
call ReadString

Should be:

mov edx, OFFSET buffer
mov Ecx, SIZEOF buffer
call ReadString
mov nameSize, eax       ; EAX contains number of characters read into buffer

In the code snippet for reversing code remove the lines off the bottom for the end of procedure etc. These aren't needed since we will do this in our original code.

Invoke ExitProcess,0
main endp
end main

Everywhere in the string reversal code where we see the variable aName change it to buffer since that is where we placed the user's name. Place that code into our program and use WriteString to print the reversed buffer at the end. The code could look something like:

INCLUDE Irvine32.inc

.data
buffer byte 20 DUP(0)
byteCount DWORD ?
nameSize  DWORD ?

Question  byte  "Please enter your name."  ,0
Greeting byte   "Hello " ,0
Statement byte " Here is your name backwards"

.code
main proc

    mov edx , OFFSET Question
    call WriteString
    call CRLF
    Call CRLF

    mov edx, OFFSET buffer
    mov Ecx, SIZEOF buffer
    call ReadString
    mov nameSize, eax

    push edx
    mov EDX ,OFFSET greeting
    Call WriteString
    pop edx
    call WriteString
    Call CRLF
    Call CRLF

    mov  ecx,nameSize
    mov  esi,0

L1: movzx eax,buffer[esi]    ; get character
    push eax                ; push on stack
    inc  esi
    loop L1

    ; Pop the name from the stack in reverse
    ; and store it in the aName array.

    mov  ecx,nameSize
    mov  esi,0

L2: pop  eax                ; get character
    mov  buffer[esi],al      ; store in string
    inc  esi
    loop L2


    mov EDX ,OFFSET buffer
    call WriteString         ; Write the reversed string that is now in buffer

    exit
main ENDP
END

If you get linking errors, you may not be linking in all the prerequisite libraries. Try adding these lines to the top of your program:

INCLUDE Irvine32.inc
INCLUDELIB Irvine32.lib
INCLUDELIB user32.lib
INCLUDELIB kernel32.lib

I should point out that this is a very inefficient way to reverse a string if you don't mind destroying the original. It can be done without a secondary buffer on the stack by reversing the string in place.

Solution 2

At a high level:

  • Allocate a 'reverse' and 'text' buffer
  • Read the string into 'text'
  • Make a pointer at the end of text, copying each character to the beginning, decrementing and incrementing both respectively.
  • Print new 'reverse' buffer.

Doing this without allocating a new buffer is possible as well, but should be avoided in general because of the cost of invoking a system call (which you would need to do after each character)

section .data

prompt      db  "Please enter your name: ", 10
length      equ $ - prompt
text        times 255 db 0
buffer      times 255 db 0

Enter your text
section .text
global main
main:
    mov  rax, 1
    mov  rdi, 1
    mov  rsi, prompt
    mov  rdx, length
    syscall

    mov  rax, 0
    mov  rdi, 0
    mov  rsi, text
    syscall

    mov  rcx, rax  ; rcx will be the character counter.
    mov  rsi, text ; a pointer to the current character. Start from the beginning.
    add  rsi, rcx  
    dec  rsi       ; Remember the 0-index
    mov  rdi, buffer

;; This subroutine is also SUB-optimal if your teacher demands
;; performance, look into the advantages of `lea` and a simple 
;; rep;scas loop as well.
process_loop:
    mov bl, [rsi]               ; Now copy from back to front
    mov [rdi], bl
    inc rdi
    dec rsi
    dec rax
    jnz process_loop

    mov  rax, 1                 ; And print the string
      mov  rdi, 1
      mov  rsi, buffer
      mov  rdx, rcx
      syscall

exit:
    mov     rax, 60
    mov     rdi, 0
    syscall
Share:
25,376
anonuser
Author by

anonuser

Updated on September 12, 2020

Comments

  • anonuser
    anonuser over 3 years

    So my assignment was to write a program in assembly code that could make a statement, recieve a user inputted string. Print that string then reverse it using the cpu stack and print it again. this is what I have thus far.

    INCLUDE Irvine32.inc
    
    .data
    buffer byte 20 DUP(0)
    byteCount DWORD ?
    
    Question  byte  "Please enter your name."  ,0
    Greeting byte   "Hello " ,0
    Statement byte " Here is your name backwards"
    
    .code
    main proc
    
    
    mov edx , OFFSET Question
    call WriteString
    call CRLF
    Call CRLF
    
    mov edx, OFFSET buffer
    mov Ecx, SIZEOF buffer
    call ReadString
    
    push edx
    mov EDX ,OFFSET greeting
    Call WriteString
    pop edx
    call WriteString
    Call CRLF
    Call CRLF
    

    As you can see this successfully accepts a user entered input and displays it but Im really struggling trying to reverse it.

    I tried these here that I copied from the book from a chapter about reversing strings.

    ; Push the name on the stack.
    
    mov ecx,nameSize
    mov  esi,0
    
    L1: movzx eax,aName[esi]    ; get character
    push eax                ; push on stack
    inc  esi
    loop L1
    
    ; Pop the name from the stack in reverse
    ; and store it in the aName array.
    
    mov  ecx,nameSize
    mov  esi,0
    
    L2: pop  eax                ; get character
    mov  aName[esi],al      ; store in string
    inc  esi
    loop L2
    
    Invoke ExitProcess,0
    main endp
    end main
    

    but I get as output nothing.

    it says "hello, (yourname here)"

    it says "this is your name backwards "

    ive tried just about every different incarnation of this I can think of and no avail. im at the end of my "string" here

    • Michael Petch
      Michael Petch over 8 years
      Okay reversing the string would be the main part of your assignment. Can you show/tell us the things that you have tried that were not successful?
    • Michael Petch
      Michael Petch over 8 years
      The code you added doesn't even use the variable names that are the same. Did you actually try to actually integrate that code into your program?
    • anonuser
      anonuser over 8 years
      Yea I changed all the references so that it would mesh.
    • anonuser
      anonuser over 8 years
      all variable names correspond correctly on my end
    • Michael Petch
      Michael Petch over 8 years
      I created an answer that should get you started with MASM/Irvine32 library on Windows. The variables are not the same. Your reversal code uses aName and the original code you wrote uses buffer . Your original code doesn't save the number of characters read (number of characters in user's name). The variable nameSize doesn't appear in your original code etc.
  • Michael Petch
    Michael Petch over 8 years
    This is 64 bit code for Linux using NASM syntax (and apparently using 32 bit syscalls). The original poster is using 32-bit code on Windows using MASM syntax. I don't think this provides much of an answer within the environment the OP is using.
  • zetavolt
    zetavolt over 8 years
    I agree and noticed that, I also don't think it's very generous to the initial poster to provide them an exact answer they can copy for their homework. Needless to say, porting this logic is trivial and would force the user to be familiar with the concepts (and possibly learn semantics on MASM like byte ptr)
  • Michael Petch
    Michael Petch over 8 years
    Did you happen to see me preface my answer with This is against my better judgement. What I wasn't willing to supply was the more efficient in place method that I would hope a professor would give better marks for.Which is why in my epilogue I made a comment to that effect ;) The user had a functioning reversal function that just needed integration. Not like I wrote much in the way of new code (3 lines)
  • zetavolt
    zetavolt over 8 years
    It's also worth noting that he says in the question that the assignment requires him to reverse it using a "CPU stack", which, while it's very straightforward, neither of us have done. (like push/pop the chars away)
  • Michael Petch
    Michael Petch over 8 years
    Actually the code he provided (and which I reused) does in fact use the stack for the secondary buffer. You'll observe that each character goes through the process of push eax in one loop putting the string on the stack. Another loop uses pop eax.
  • zetavolt
    zetavolt over 8 years
    i didn't even notice it, it does indeed. well, if you count the stack frame as a "CPU stack"
  • Peter Cordes
    Peter Cordes over 8 years
    Reversing a sequence of 16, 32, or 64bit integers by pushing onto the stack is actually kind of clever. Then it's right there on the stack, in reverse order, from [esp] to [esp + count * size]. Popping it off again is ridiculous though. And since there's no way to push a single byte onto the stack (push [m8] or something), you're right that this idea is terrible for strings. It would work if you loaded 2, 4B, or 8B at a time, and used bswap to reverse those bytes before pushing. (Or, movbe to load-with-swap on the fly. (Atom, Haswell, and Jaguar)).
  • Peter Cordes
    Peter Cordes over 8 years
    @zv_: what other kind of stack is built in to the ISA? I'd say push/pop to write/read memory pointed to by esp is exactly what "using the stack" means. You can't very well point esp at some buffer, unless you know that no signal handler can run and potentially stomp on memory below your buffer.
  • Michael Petch
    Michael Petch over 8 years
    @PeterCordes It was the code the OP tried to integrate with (in the updated question) and wanted help just getting the basics done (homework assignment). The question is/was borderline vote to close depending on how you want to define reasonable effort . If I provided my answer I would want to get an honorary degre from their institution ;-)
  • anonuser
    anonuser over 8 years
    + Michael Petch Thanks but I cant get your code to even compile. I get two errors on building it. 1 unresolved externals and unresolved external symbol_mainCRTStartup
  • Michael Petch
    Michael Petch over 8 years
    @JonDeal Those errors are not in compiling, those errors are in the linking phase. And from the looks of it you aren't linking against all the required libraries. I simply used the code you gave me. I have added a small section to the bottom of my answer that may solve the linking issues. What I do know is that the program does compile and run here.
  • anonuser
    anonuser over 8 years
    +Michael Petch. Hmm still isn't linking I tried all those include statements and im still getting those two errors. Im really at a loss here, Why would it not be working? You haven't written any code that I haven't used before.
  • Michael Petch
    Michael Petch over 8 years
    @JonDeal I used your code. The fact it isn't linking is a problem in your development environment. You provided code here and I have done nothing more than reuse it and added a few lines. Are you doing your development in Visual Studio or from the command line? I really think you need to be talking to your teacher assistant or professor about your development environment if you have questions of that nature.
  • Michael Petch
    Michael Petch over 8 years
    @JonDeal The only other recommendation is that if you know how, add /ENTRY:main on your linker command line. Assuming you put all those IncludeLib lines in your file as I suggested, I can successfully link with this command line link /SUBSYSTEM:CONSOLE filename.obj /ENTRY:main where filename.obj is whatever you called your file (and an OBJ extension instead of ASM)