How to reverse and print a string in assembly language
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
anonuser
Updated on September 12, 2020Comments
-
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 over 8 yearsOkay 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 over 8 yearsThe 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 over 8 yearsYea I changed all the references so that it would mesh.
-
anonuser over 8 yearsall variable names correspond correctly on my end
-
Michael Petch over 8 yearsI 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 over 8 yearsThis 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 over 8 yearsI 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 over 8 yearsDid 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 over 8 yearsIt'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 over 8 yearsActually 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 usespop eax
. -
zetavolt over 8 yearsi didn't even notice it, it does indeed. well, if you count the stack frame as a "CPU stack"
-
Peter Cordes over 8 yearsReversing 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 usedbswap
to reverse those bytes before pushing. (Or,movbe
to load-with-swap on the fly. (Atom, Haswell, and Jaguar)). -
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 byesp
is exactly what "using the stack" means. You can't very well pointesp
at some buffer, unless you know that no signal handler can run and potentially stomp on memory below your buffer. -
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 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 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 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 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 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 linelink /SUBSYSTEM:CONSOLE filename.obj /ENTRY:main
where filename.obj is whatever you called your file (and an OBJ extension instead of ASM)