How to reverse and print a string in assembly lang

2019-06-14 14:46发布

问题:

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

回答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.



回答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