News:

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

Need info on usage of ESP & EBP in code

Started by Rainstorm, April 25, 2007, 12:21:11 AM

Previous topic - Next topic

Rainstorm

hi,

How do i go about using ESP & EBP in my code ?
What are the things i have to look out for  ?
how can i find out if ESP & EBP are already being used (assuming am not using them manually) ?
they are used for procedures so its a delicate proccess i guess.

would appreciate any guidlines,..tips....tricks..  : )

thank-you
-

raymond

I often write small apps for solving math problems. Since those never require the creation of a window, I also never bother to use full blown procs creating stack frames. If some code needs to be repeated, I generally write it as a subroutine using data from registers or from memory as in the old days. Thus, if I occasionally would like to have an extra register, I would use EBP without any concern about saving or restoring it because it must have been saved by the OS and restored when exiting from my program.

However, toying with the ESP register is a totaly different animal. In theory, you could save it in memory and restore it later. But, while you may be using it for some other task, you can't push anything on the stack without risking a page fault aborting your app. Although possible, using the ESP register for some other purpose may be more troublesome than finding some other alternative.

If your app requires the creation of stack frames because of passed parameters or local variables, you should not risk using the EBP register either for other tasks.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

PBrennick

Rainstorm,
They are always being used. You can use them if you are careful but in general, I would not if I were you. There really is no need and the usage of those two registers are really not best for a beginner.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

hutch--

Rainstorm,

ESP and EBP are used in standard MASM procedures so if you need or want to learn how they work the trick is to start writing procedures that do not use a stack frame where you control the use of the regsters directly. The gains in not using a stack frame is an extra register in EBP to use in code and slightly lower overhead in the call/ret of a procedure but this only matters if the procedure is very short.

If you like to live dangerously you can also use ESP in some instances but you must know what you are doing to try stuff like this.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Rainstorm

Hi.

thanks all,........for the replies.

raymond wrote..
QuoteHowever, toying with the ESP register is a totaly different animal. In theory, you could save it in memory and restore it later.
so if i want to save ESP or EBP, I cannot use push/pop to do it,..I have to save it to a variable, is that right ?  -  Also don't know exactly what you mean by ..'write it as a subroutine'

QuoteESP and EBP are used in standard MASM procedures so if you need or want to learn how they work the trick is to start writing procedures that do not use a stack frame where you control the use of the regsters directly.
Is there a good example of this ? that shows the general method involved & what I have to look out for ? - writing such a procedure..that is.

i guess it would be worth being able to have EBP available  & learn how to at least have EBP free if need be.

have one more Question
when i do something like  movzx ebx, byte ptr [esi+1]    is it faster than  something like 
movzx ebx, byte ptr [variable+1]     (where esi is the address of the 'variable') - though the first one uses a register it stll is accesing the same memory - right ?
So if the 1st instruction is faster, then why so ?

This is an example of some code that i just tried, using EBP & it assembles proper & seems to run as expected. Also would be interested in any comments on the code. basically it just does a case insensitive comparison & it doesn't call any procs.


    .data

  stringd db ": where the Stars shine;..all night",0
  strings db "stars",0
 
  total_matches dd 0

      ch_tbl \
      db   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15
      db  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
      db  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
      db  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63
      db  64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111
      db 112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95
      db  96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111
      db 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
      db 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143
      db 144,145,146,147,148,149,150,151,152,153,154,155,156,156,158,159
      db 160,161,162,163,164,165,166,167,168,169,170,171,172,173,173,175
      db 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191
      db 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207
      db 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223
      db 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239
      db 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255


; -------------------------------------------------------------------------------------------
    .code

start:

    mov esi, offset stringd         ; address of data string in esi
    sub esi, 1

  colon_check:
    add esi, 1                      ; move data pointer forward 
    cmp byte ptr [esi], 58          ; check for ':' in data string
    jne colon_check
  space_check:
    add esi, 1
    cmp byte ptr [esi], 32          ; check for space in data string
    jne space_check


  mov edx, offset ch_tbl
 
  reset_:
    mov edi, offset strings         ; (resets search string to start)
                                       
  search_:

    add esi, 1
    cmp byte ptr [esi], 0           ; check for 0 terminator in data string
    je exit_
   
    movzx ebx, byte ptr [edi]       ; move search byte into ebx
    movzx eax, byte ptr [esi]       ; mov data byte into eax
    movzx ecx, byte ptr [edx+ebx]   ; replace by matching index in table (makes lower case)
    cmp cl, byte ptr [edx+eax]      ; check if data & search chars match
    jne search_

    xor ecx, ecx
    sub ecx,1

  match_:
    add ecx, 1
    cmp byte ptr [edi+ecx], 0           ; check for 0 terminator in search string
    je match_found

    movzx ebx, byte ptr [edi+ecx]
    movzx ebp, byte ptr [esi+ecx]
    movzx eax, byte ptr [edx+ebx]
    cmp al, byte ptr [edx+ebp]        ;compare chars from corresponding index in the table
    je match_
    jmp reset_

  match_found:
    add total_matches, 1
    jmp reset_                           ; jmp back, reset the search index

  exit_:

    print "Total Matches --- "
    print ustr$(total_matches),13,10
    inkey
    exit

end start




raymond

Quoteso if i want to save ESP or EBP, I cannot use push/pop to do it,..I have to save it to a variable, is that right ?

That is right for ESP. It would also be right for EBP after you use ESP for something else. However, you could use the stack for saving EBP before you use ESP for something else or don't use ESP at all for something else.

QuoteAlso don't know exactly what you mean by ..'write it as a subroutine'

Here's an example where I would qualify my dw2a code as a subroutine (no proc, no stack frame, no passed parameters, no local variables, no endp):


.data
   textbuf  db  24 dup(?)

.code

dw2a:
   mov   ecx,10
   pushd 0           ;for later use as terminating 0
@@:
   xor   edx,edx
   div   ecx
   add   dl,"0"      ;convert to ascii
   push  edx         ;save each digit on stack
   .if   eax != 0
      jmp   @B    ;continue conversion
   .endif

   push  edi
   lea   edi,textbuf
@@:
   pop   eax         ;retrieve each ascii character from the stack
   stosb
   or    al,al
   jnz   @B          ;continue until terminating 0 is retrieved
   pop   edi
   ret

start:
   mov  eax,4321
   mul  eax
   call  dw2a    ;go convert the content of EAX to ascii for display
   invoke MessageBox,0,ADDR buffer,0,MB_OK
   invoke ExitProcess,0
end start


Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

lingo

You can free register EDX and use it about  "add total_matches"  replacement too... :lol
; mov edx, offset ch_tbl
 
  reset_:
    mov edi, offset strings         ; (resets search string to start)
                                       
  search_:

    add esi, 1
    cmp byte ptr [esi], 0           ; check for 0 terminator in data string
    je exit_
   
    movzx ebx, byte ptr [edi]       ; move search byte into ebx
    movzx eax, byte ptr [esi]       ; mov data byte into eax
   ; movzx ecx, byte ptr [edx+ebx]   ; replace by matching index in table (makes lower case)
   ; cmp cl, byte ptr [edx+eax]      ; check if data & search chars match
    movzx ecx, byte ptr [ch_tbl+ebx]   ; replace by matching index in table (makes lower case)
    cmp cl, byte ptr [ch_tbl+eax]      ; check if data & search chars match
    jne search_

    ;xor ecx, ecx
    ;sub ecx,1
     or ecx,  -1

  match_:
    add ecx, 1
    cmp byte ptr [edi+ecx], 0           ; check for 0 terminator in search string
    je match_found

    movzx ebx, byte ptr [edi+ecx]
    movzx ebp, byte ptr [esi+ecx]
  ;  movzx eax, byte ptr [edx+ebx]
  ;  cmp al, byte ptr [edx+ebp]        ;compare chars from corresponding index in the table
    movzx eax, byte ptr [ch_tbl+ebx]
    cmp al, byte ptr [ch_tbl+ebp]        ;compare chars from corresponding index in the table
 
   je match_
    jmp reset_

  match_found:
    add total_matches, 1


You can see how Hutch used ESP register too:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

align 16

finstr proc spos:DWORD,psrc:DWORD,patn:DWORD

  ; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷
  ; simplified faster InString algorithm.
  ; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷
  ; arguments
  ; ---------
  ; 1 spos = 1 based index start offset in source address
  ; 2 psrc = the source address to be searched
  ; 3 patn = the address of the pattern to be searched for
  ; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷
  ; return values
  ; -------------
  ; on match, 1 based index offset from start of source.
  ; no match returns zero.
  ; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷
  ; NOTE: The "spos" can be set past the end of the
  ; source buffer which will generate an unhandled
  ; exception. To address this potential problem with
  ; sequential searches, the return value should be
  ; incremented by ONE BYTE at a time which ensures
  ; that the starting position "spos" never goes past
  ; the end of the source buffer.
  ; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷

    .data?
      align 16
      _ebx dd ?
      _esi dd ?
      _edi dd ?
      _ebp dd ?
      _esp dd ?
    .code

    mov _ebx, ebx
    mov _esi, esi
    mov _edi, edi
    mov _ebp, ebp
    mov _esp, esp

    mov edi, [esp+12]               ; patn
    movzx eax, BYTE PTR [edi]       ; set 1st patn byte in AL
    mov esi, [esp+8]                ; psrc
    ;;; add esi, [esp+4]                ; spos
    sub esi, 4                      ; correct for 1 to 0 based index
                                    ; and dec by 1 for following loop
    mov esp, 80808080h
    mov ebp, 1

; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷

  scanloop:
  REPEAT 16
  ; ----------------------------------------------
  ; test if the next 4 bytes contain an ascii zero
  ; ----------------------------------------------
    add esi, 4
    mov edx, [esi]
    lea ecx, [edx-01010101h]
    not edx
    and ecx, esp        ; modify ECX
    test ecx, edx       ; test with no modify
    jnz no_match
  ; ----------------------------------------
  ; test each byte for a patn 1st char match
  ; ----------------------------------------
    movzx ebx, BYTE PTR [esi]
    sub ebx, eax
    jz si0
    movzx ecx, BYTE PTR [esi+1]
    sub ecx, eax
    jz si1
    movzx edx, BYTE PTR [esi+2]
    sub edx, eax
    jz si2
    movzx ecx, BYTE PTR [esi+3]
    sub ecx, eax
    jz si3
  ENDM
    jmp scanloop

; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷

  si3:
    add esi, ebp
  si2:
    add esi, ebp
  si1:
    add esi, ebp
  si0:                              ; set index
    xor ebx, ebx

; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷

  matchloop:
  REPEAT 8
    add ebx, ebp
    movzx edx, BYTE PTR [edi+ebx]
    test edx, edx                   ; test for patn terminator
    jz match                        ; text match if patn terminator found
    movzx ecx, BYTE PTR [esi+ebx]
    cmp ecx, edx                    ; test for match
    jne scanloop

    add ebx, ebp
    movzx edx, BYTE PTR [edi+ebx]
    test edx, edx                   ; test for patn terminator
    jz match                        ; text match if patn terminator found
    movzx ecx, BYTE PTR [esi+ebx]
    cmp ecx, edx                    ; test for match
    jne scanloop

  ENDM
    jmp matchloop

; ÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷-÷

  match:
    mov esp, _esp
    sub esi, [esp+8]                ; psrc
    ; lea eax, [esi+1]
    mov eax, esi
    jmp cleanup

  no_match:
    mov esp, _esp
    xor eax, eax

  cleanup:
    mov ebx, _ebx
    mov esi, _esi
    mov edi, _edi
    mov ebp, _ebp

    ret 12

finstr endp

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

Funny e.. :lol



Crosscross

never chang ebp,which stores the former address of esp,in a subfunction, if you forget to restore it, your program  will crash immediately,but your can chang the esp as you like to,and you neednt have to restore it ,which is restored automaticly in LEAVE,

on the whole,ebp is used for local variables in subroutines,and esp ,beside its pop and push uses ,it is alse used to get the variable which are pushed into the stack and passed to subfunctions

hutch--

Crosscross,

Most of us know the deal with writing stack frame free procedures and exactly the reason why you do so is to get the extra register EBP but it means you must know what you are doing with the stack pointer ESP as well. Tracking the current location of ESP takes a little practice when you use push or pop but its not that hard to do with practice. With a "leaf" procedure you can in fact write locals to ESP below the current location [esp-8] etc ... but it becomes increasingly more complicated if you try to nest prcedures written in this manner.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Rainstorm

Hi

Raymond, thanks for the info &  the example, they were very helpful.

lingo,
QuoteYou can free register EDX and use it about  "add total_matches"  replacement too...
actually had a question related to that in my earlier post
Quotewhen i do something like  movzx ebx, byte ptr [esi+1]    is it faster than  something like
movzx ebx, byte ptr [variable+1]  ? (where esi is the address of the 'variable') - though the first one uses a register it stll is accesing the same memory - right ?
So if the 1st instruction is faster, then why so ?

thank-you,.all.
-

dsouza123

Quote
when i do something like  movzx ebx, byte ptr [esi+1]    is it faster than  something like
movzx ebx, byte ptr [variable+1]  ? (where esi is the address of the 'variable') - though the first one uses a register it stll is accesing the same memory - right ?
So if the 1st instruction is faster, then why so

Tradeoffs:
movzx ebx, byte ptr [esi+1]  is tying up an extra register esi
-versus-
movzx ebx, byte ptr [variable+1] which doesn't affect esi

movzx ebx, byte ptr [esi+1]  can point to any memory
-versus-
movzx ebx, byte ptr [variable+1] is used with memory declared in .data or .data?

Haven't done any tests to determine which is faster.

Things that can be determined:
Both will have to access the byte at the effective address
At some point the esi version had to load esi with the offset of the variable, so an extra instruction
The esi version is shorter (by 3 bytes), which may allow more instructions to fit in a 16 byte area
The variable version already has the effective address encoded in the instruction
The esi version does the addition at run time and then is used to generate an effective address
The variable version already has the addition to the address done at assembly time

Rainstorm

Dsouza,
    that info gave me a much better perspective, appreciate the feedback.
would be interested to know which is quicker though.
Also, it seems that, movzx ebx, byte ptr [variable+1] doesn't need the 'byte ptr' part (when the varaiable name is used unlike a reg. like esi) & can be written as movzx ebx,  [variable+1] probably because the declaration of the size of the variable in the .Data section, hints to the assembler that a byte should be moved into ebx. - correct me if am wrong


MichaelW

Quotewhen i do something like movzx ebx, byte ptr [esi+1] is it faster than something like
movzx ebx, byte ptr [variable+1]
Both forms should generally execute in about the same number of cycles. AFAIK the effective address calculations affected the cycle counts only for the very early processors.
eschew obfuscation

dsouza123

Quote
Also, it seems that, movzx ebx, byte ptr [variable+1] doesn't need the 'byte ptr' part (when the varaiable name is used unlike a reg. like esi) & can be written as movzx ebx,  [variable+1] probably because the declaration of the size of the variable in the .Data section, hints to the assembler that a byte should be moved into ebx.

Not needing byte ptr is only if the data size of variable matches the size of data accessed.
It does no harm including it, and it makes the intended data size wanted perfectly clear.
Bonus, if you switch code to the esi version it will already be setup.

.data
  variableb db 1,2,3,4
  variablew dw 1,2,3,4
  variabled dd 1,2,3,4

.code
start:
  movzx ebx,  byte ptr [variableb + 1]
  movzx ebx,  word ptr [variableb + 1]   ; need word ptr
  nop
  movzx ebx,  byte ptr [variablew + 1]   ; need byte ptr
  movzx ebx,  word ptr [variablew + 1]
  nop
  movzx ebx,  byte ptr [variabled + 1]   ; need byte ptr
  movzx ebx,  word ptr [variabled + 1]   ; need word ptr
  nop

PBrennick

Raymond,
There is a problem with your example. The first pop into eax will be the value that was in edi and not one of the characters you pushed using edx.

Do this instead:

.data
   textbuf  db  24 dup(?)

.code

dw2a:
   push  edi
   mov   ecx,10
   pushd 0           ;for later use as terminating 0
@@:
   xor   edx,edx
   div   ecx
   add   dl,"0"      ;convert to ascii
   push  edx         ;save each digit on stack
   .if   eax != 0
      jmp   @B    ;continue conversion
   .endif

   lea   edi,textbuf
@@:
   pop   eax         ;retrieve each ascii character from the stack
   stosb
   or    al,al
   jnz   @B          ;continue until terminating 0 is retrieved
   pop   edi
   ret

start:
   mov  eax,4321
   mul  eax
   call  dw2a    ;go convert the content of EAX to ascii for display
   invoke MessageBox,0,ADDR buffer,0,MB_OK
   invoke ExitProcess,0
end start



Paul
The GeneSys Project is available from:
The Repository or My crappy website