News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

How to flip flop a string?

Started by bcddd214, October 14, 2011, 12:56:40 AM

Previous topic - Next topic

bcddd214

I am trying to figure out how to flip flop a string 'without using the stack'.

I.E.
bobby -> ybbob

I am using Irvine32 and conceptually I am stumped as to how to take a string that is read in from the macro readstring

   mov      edx,offset      mes1
   call            WriteString
   mov      edx,offset      inName
   call            ReadString

In the above, a message displays 'whatever' and then a string entered and assigned to the variable inName.

I am not the best with string tools in asm and not sure the best route to take.

jj2007

Hi BCDD && StatusQuo,

This thread has solutions to your homework problem.

Joyful reading & good night,
jj

dedndave

SwapIt  PROTO   :DWORD,:DWORD

        .CODE

SwapIt  PROC    lpString:DWORD,dwLength:DWORD

        mov     ecx,dwLength
        mov     edx,lpString
        dec     ecx
        add     ecx,edx
        jmp short SwpIt1

SwpIt0: mov     al,[edx]
        mov     ah,[ecx]
        mov     [ecx],al
        mov     [edx],ah
        dec     ecx
        inc     edx

SwpIt1: cmp     ecx,edx
        ja      SwpIt0

        ret

SwapIt  ENDP


to use it.....

        .DATA

TstStr  db 'abcde',0

        .CODE

;be sure to pass the length of the string, less the null terminator

        INVOKE  SwapIt,offset TstStr,sizeof TstStr-1

bcddd214

I think I am doing it right, but it just hangs???

Title               flipTest
INCLUDE Irvine32.inc
;################################################

.data

mes1      BYTE "Enter a 5 digit string: ",0
inName      DWORD         3 dup(0)
math1      BYTE         2

;################################################

.code


;################################################


getNameProc PROC
    push    ebp
   mov     ebp, esp
   
   push   edx
   mov      edx,offset      mes1
   call               WriteString
   mov      edx,offset      inName
   mov     ecx, sizeof      inName
   call               ReadString
   pop      edx

   pop     ebp
   ret
getNameProc ENDP

SwapIt  PROC    lpString:DWORD,dwLength:DWORD

   push   ebp
   mov      ebp,esp
   push   ecx
   push   edx
   mov      edi,[ebp+8]
    mov     ecx,dwLength
    mov     edx,lpString
    dec     ecx
    add     ecx,edx
    jmp short SwpIt1

SwpIt0: mov     al,[edx]
    mov     ah,[ecx]
    mov     [ecx],al
    mov     [edx],ah
    dec     ecx
    inc     edx

SwpIt1: cmp     ecx,edx
    ja      SwpIt0
   pop      edx
   pop      ecx
   pop      ebp
    ret

SwapIt  ENDP




main PROC
   call   getNameProc
   push    OFFSET inName
   call   SwapIt
   call   writedec
   exit
main    ENDP

INVOKE  SwapIt,offset inName,sizeof inName-1
;INVOKE  ExitProcess,0
;INVOKE  stringLengthProc,offset msg1

END main

bcddd214

PS, the above is coming from this working program I am trying to adjust to the flip flop method in this post.

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

.data

mes1      BYTE "Enter Your Name: ",0
inName      DWORD         128 dup(0)
;stringSize   BYTE         128 dup(0)
math1      BYTE         2

;################################################

.code


;################################################


getNameProc PROC
    push    ebp
   mov     ebp, esp
   
   push   edx
   mov      edx,offset      mes1
   call               WriteString
   mov      edx,offset      inName
   mov     ecx, sizeof      inName
   call               ReadString
   pop      edx

   pop     ebp
   ret
getNameProc ENDP


stringLengthProc PROC

   push   ebp
   mov      ebp,esp
   push   ecx
   push   edx
   mov      edi,[ebp+8]

   mov      ecx, 0
L1:

   mov      bl,[edi + ecx]
   cmp      bl, 0
   JE      L1Exit
   inc      ecx
   JMP      L1

L1Exit:
   
   mov      eax,ecx
   pop      edx
   pop      ecx
   pop      ebp
   ret      4
stringLengthProc ENDP




main PROC
   call   getNameProc
   push    OFFSET inName
   call   stringLengthProc
   call   writedec
   exit
main    ENDP

INVOKE  ExitProcess,0
;INVOKE  stringLengthProc,offset msg1

END main

dedndave

you are close   :U

when you specify parameters on the PROC line, or if you specify local variables, the assembler generates a prologue and epilogue
you can turn this feature off with OPTION directives - we'll skip that for now

what that means is, the assembler takes care of EBP for you

SwapIt  PROC    lpString:DWORD,dwLength:DWORD

   push   ebp                                 ;remove this line
   mov      ebp,esp                           ;remove this line
   push   ecx
   push   edx
   mov      edi,[ebp+8]                       ;remove this line, too
    mov     ecx,dwLength
    mov     edx,lpString
    dec     ecx
    add     ecx,edx
    jmp short SwpIt1

SwpIt0: mov     al,[edx]
    mov     ah,[ecx]
    mov     [ecx],al
    mov     [edx],ah
    dec     ecx
    inc     edx

SwpIt1: cmp     ecx,edx
    ja      SwpIt0
   pop      edx
   pop      ecx
   pop      ebp                               ;remove this line
    ret

SwapIt  ENDP

bcddd214

She still just hangs??

Title               flipTest
INCLUDE Irvine32.inc
;################################################

.data

mes1      BYTE "Enter a 5 digit string: ",0
inName      DWORD         3 dup(0)
math1      BYTE         2

;################################################

.code


;################################################


getNameProc PROC
    push    ebp
   mov     ebp, esp
   
   push   edx
   mov      edx,offset      mes1
   call               WriteString
   mov      edx,offset      inName
   mov     ecx, sizeof      inName
   call               ReadString
   pop      edx

   pop     ebp
   ret
getNameProc ENDP

SwapIt  PROC    lpString:DWORD,dwLength:DWORD

;   push   ebp
;   mov      ebp,esp
   push   ecx
   push   edx
;   mov      edi,[ebp+8]
    mov     ecx,dwLength
    mov     edx,lpString
    dec     ecx
    add     ecx,edx
    jmp short SwpIt1

SwpIt0: mov     al,[edx]
    mov     ah,[ecx]
    mov     [ecx],al
    mov     [edx],ah
    dec     ecx
    inc     edx

SwpIt1: cmp     ecx,edx
    ja      SwpIt0
   pop      edx
   pop      ecx
;   pop      ebp
    ret

SwapIt  ENDP




main PROC
   call   getNameProc
   push    OFFSET inName
   call   SwapIt
   call   writedec
   exit
main    ENDP

INVOKE  SwapIt,offset inName,sizeof inName-1
;INVOKE  ExitProcess,0
;INVOKE  stringLengthProc,offset msg1

END main

dedndave

try this....

Title               flipTest
INCLUDE Irvine32.inc

;################################################

SwapIt           PROTO :DWORD,:DWORD
stringLengthProc PROTO :DWORD

;################################################

.data

mes1      BYTE "Enter a 5 digit string: ",0
inName      DWORD         3 dup(0)
math1      BYTE         2

;################################################

.code

;************************************************

getNameProc PROC

   push   edx
   mov      edx,offset      mes1
   call               WriteString
   mov      edx,offset      inName
   mov     ecx, sizeof      inName
   call               ReadString
   pop      edx
   ret

getNameProc ENDP

;************************************************

stringLengthProc PROC lpString:DWORD

;returns the string length in EAX

        push    ecx
        push    edi
        mov     al,0
        or      ecx,-1
        mov     edi,lpString
        repnz   scasb
        or      eax,-2
        sub     eax,ecx
        pop     edi
        pop     ecx
        ret

stringLengthProc ENDP

;************************************************

SwapIt  PROC    lpString:DWORD,dwLength:DWORD

   push   ecx
   push   edx
    mov     ecx,dwLength
    mov     edx,lpString
    dec     ecx
    add     ecx,edx
    jmp short SwpIt1

SwpIt0: mov     al,[edx]
    mov     ah,[ecx]
    mov     [ecx],al
    mov     [edx],ah
    dec     ecx
    inc     edx

SwpIt1: cmp     ecx,edx
    ja      SwpIt0
   pop      edx
   pop      ecx
    ret

SwapIt  ENDP

;************************************************

main PROC

        call    getNameProc
        INVOKE  stringLengthProc,offset inName
        INVOKE  SwapIt,offset inName,eax
        mov     edx,offset inName
        call    WriteString
        call    Crlf
        exit

main    ENDP

;################################################

END main

bcddd214

Awesome!

Can you explain to how the information is passed through edi without calling on it again?
I am curious how that worked.

dedndave

well - if you look closely, we do not use EDI
we use EDX, instead
a matter of preference, as in win32, EDI should be preserved, whereas EDX need not be
Kip Irvine did not necessarily use that convention in his library, though   :P

we are using a calling convention named "StdCall" - this is declared in Irvine32.inc
so - the following information applies to that convention

when you use INVOKE...
        INVOKE  SwapIt,offset inName,eax

the assembler generates code that pushes the parameters (right-to-left) onto the stack, then makes the call
        push    eax
        push    offset inName
        call    SwapIt


the assembler also takes care of parameter passing, the stack frame base pointer (EBP), and cleaning up the stack after the call
that includes a LEAVE instruction just before the RET
so, before you ask what LEAVE does, it is equivalent to this
        mov     esp,ebp         ;restore ESP with original contents
        pop     ebp             ;restore EBP with original contents


now for the rest of the PROC
we write this code...
SwapIt  PROC    lpString:DWORD,dwLength:DWORD

        push    ecx
        push    edx
        mov     ecx,dwLength
        mov     edx,lpString
        dec     ecx
        add     ecx,edx
        jmp short SwpIt1

SwpIt0: mov     al,[edx]
        mov     ah,[ecx]
        mov     [ecx],al
        mov     [edx],ah
        dec     ecx
        inc     edx

SwpIt1: cmp     ecx,edx
        ja      SwpIt0

        pop     edx
        pop     ecx
        ret

SwapIt  ENDP


the assembler actually generates something like this...

SwapIt  PROC    lpString:DWORD,dwLength:DWORD

        push    ebp
        mov     ebp,esp
        push    ecx
        push    edx
        mov     ecx,[ebp+12]
        mov     edx,[ebp+8]
        dec     ecx
        add     ecx,edx
        jmp short SwpIt1

SwpIt0: mov     al,[edx]
        mov     ah,[ecx]
        mov     [ecx],al
        mov     [edx],ah
        dec     ecx
        inc     edx

SwpIt1: cmp     ecx,edx
        ja      SwpIt0

        pop     edx
        pop     ecx
        leave
        ret     8

SwapIt  ENDP


so, the assembler pushes EBP and fills it with ESP, setting up the stack frame
it then replaces the named parameters with EBP-relative indexed addresses, like [EBP+8]
and, everyplace we put a RET instruction, it inserts a LEAVE and balances the stack by adjusting the RET to RET 8
if we had 3 dword parameters, it would change it to RET 12, and so on

jj2007

In addition to Dave's perfect examples, here two short algos for string flipping. The upper algo, xx1, weighs in at a whopping 33 bytes, while the lower one does the job with 30 bytes without ever using the stack :bg

You might lookup the otherwise rarely used instructions repne scasb and stosb.

include \masm32\MasmBasic\MasmBasic.inc   ; download
.data
Src   db "TheStringToFlipFlopHas34Characters", 0
Dest   db "Not yet filled with anything, 34cs", "?"
Overflow   db "This should be left intact", 0
   Init
   CodeSize xx1
   CodeSize xx2
xx1_s:
   mov edi, offset Src
   or ecx, -1
   xor eax, eax
   repne scasb
   mov edx, offset Dest   ; edi is at end of source
   xchg edx, edi   ; now edi is start of dest, edx is end of source
   
dec edx
   dec edx
   neg ecx
   dec ecx
   push edi
   .Repeat
      mov al, [edx]   ; get a byte from end of source
      stosb   ; poke to start of dest, increase dest
      dec edx   ; lower source
      dec ecx   ; decrease counter
   .Until Zero?
   pop edi
xx1_endp:
   Print edi, CrLf$

   
Src2 equ Dest   ; we flipped a string above, now we flip it back to
   Dest2 equ Src   ; the original state, so we need to swap src and dest
xx2_s:   ; -------------------------------------------------------------
   mov edi, offset Src2   ; get a pointer to the source (in this case, the old destination)
   or ecx, -1   ; no limit for repne
   mov al, 0   ; to get the len, we search for a nullbyte
   repne scasb   ; edi is now at end of source+1
   mov edx, offset Dest2
   mov ecx, edx   ; we will test against the start of the destination
   xchg edx, edi   ; now edi is start of dest, edx is end of source
   
dec edx
   .Repeat
      dec edx   ; lower source pointer
      mov al, [edx]   ; get a byte from end of source
      stosb   ; poke to start of dest, increase dest
   .Until edx<=ecx
xx2_endp:   ; -------------------------------------------------------------
   Inkey edx, CrLf$   ; show the dest
   
; Inkey offset Overflow   ; optional: check if the buffer behind dest is intact
   Exit
end start