News:

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

Macro for initialising local variables

Started by jj2007, October 08, 2007, 03:02:09 PM

Previous topic - Next topic

zooba

Quote from: jj2007 on November 03, 2007, 06:44:01 PM
Quote from: Rockoon on November 03, 2007, 05:23:53 PM
I would use this sort of strategy to both allocate and initialize the locals:
Fine if you have access to MASM's "LOCAL" routine (you aren't working for Microsoft by accident?  :bg)
You also would have to know the number and size of the local variables.
The ClearLocals macro builds on current MASM behaviour, and works with all types of local variables including structures, by simply using the difference between base page and stack pointer after allocation of locals.

All of this is accessible as part of the prologue macro, which is well documented in Hutch's MASM help file and my additions to the link before. Adding initialisation is relatively simple based on the example there.

My personal preference is to initialise locals manually, since it's rare that you need everything zero initialised.

Cheers,

Zooba :U

hutch--

jj,

Your comparison between 32 bit PB and 16 bit GFA basics is not well informed. 16 bit anything is smaller. I wrote 16 bit GFA basic in the early 90s and it was a good tool in its time but it never made the adaption to 32 bit code and effectively turned into a dead product. PB is a better dialect in that it properly handles LOCAL variable by default, GFA was a pain with its globals unless specified as local, PB has a very good inline assembler and a much bigger runtime library built into it. It is also fully written in assembler and just recently the compiler was ported to 32 bit so its much faster and has a far larger capacity.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

jj2007

Quote from: Rockoon on November 03, 2007, 07:16:14 PM
i'm sorry I thought you wanted "(=tiny and fast)"
stosb is definately not fast .. high minimum latency .. and its only a byte at a time

ClearLocals MACRO
mov edx, edi ; save edi to a free ACD register
mov ecx, ebp ; Base pointer into counter
mov edi, esp ; current stack pointer as destination
sub ecx, edi ; subtract from counter
shr ecx, 2 ; divide by 4, stack is dword aligned (3 bytes extra)
xor eax,eax ; we want zeroed locals
rep stosd ; fill (5 cycles per local variable)
mov edi, edx ; give edi back to Windows
ENDM

shr exc, 2 and stosd increase speed by a factor of 4 and size by 3 bytes - your choice. Nobody would use a procedure call with lots of local variables in an innermost time-critical loop, but anyway, I respect religious feelings about stosb.

Quote from: Rockoon on November 03, 2007, 07:16:14 PM
as far as your other criticisms.. are you lazy or something? :)
I am definitely lazy! That's why I wanted this macro: Just insert "ClearLocals" behind the last LOCAL, without any arguments, and be reassured there is no garbage in your local variables.

Quote from: Rockoon on November 03, 2007, 07:16:14 PM
.. none of them are actualy valid .. you can indeed calculate the size of the locals any way you want to and still rip through with pushes ..

..but now here is a criticism .. why would you want to initialize ALL locals? Seems like usualy you would only want to initialize structures.. individual temporary machine words should almost never need initialized memory because they start out as registers ..

.. for instance, any handles that get returned from api calls (such as hMAPI in the original post) .. no initial value is valid here .. the code would always ensure that its value was a non-erroring result of an api call .. so why initialize it?
Of course, you can calculate the size and do other athletic exercises, but me I want it simple & stupid. And yes you are not forced to initialize your locals. Nobody forces you to type "ClearLocals" after your last LOCAL. Nobody forces you to use horribly un-professional constructs like .IF eax==0 in your code. These things are meant for lazy unprofessional old people like me...

Rockoon

Quote from: jj2007 on November 04, 2007, 10:39:47 AM
I am definitely lazy! That's why I wanted this macro..

..the basic strategies have been expained to you .. if you complain about such trivial details such as how the amount of memory is handed to the macro then I can only assume one thing.. that you are so lazy that you want others to not only define a strategy but also implement it for you...

cut and paste much?

When C++ compilers can be coerced to emit rcl and rcr, I *might* consider using one.

jj2007


Mark Jones

Well... I think it's a nifty idea. Good job. :U
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

jj2007

Quote from: Mark Jones on November 04, 2007, 05:14:43 PM
Well... I think it's a nifty idea. Good job. :U
You made my day, thanxalot  :bg

Larry Hammick

Assuming we're in a "proc" block or a routine that uses either of these:
enter bytes,0
...
push ebp
mov ebp,esp
sub esp,bytes       ;"bytes" certainly should be a multiple of 4

this works, a little more neatly than Basic's method:
@localzeros macro bytes
    mov ecx,bytes/4
    mov esp,ebp
@@: push 0
    loop @B
endm
...
myroutine proc var1:DORD
    @localzeros 20h
    ;do whatever with the data
    @localzeros 20h    ;reset the locals
    ;do some more stuff
    ret       ;inside a proc, "ret" is a macro which uses "leave" or "mov esp,ebp"
myroutine endp

This manoeuvre is used in some C routines I have seen.
Although the CPU supports instructions such as "dec esp" and "inc esp", those quickly cause trouble in protected mode. ESP should always be a multiple of four.

jj2007

Not quite sure if I understood what your routines do, but the second one requires that you know the size of your locals.
My version seems almost foolproof, although I modified it recently to preserve edx; while ecx is still NOT preserved.

Usage example:
DlgHook   proc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL rcDesktop:RECT, rcNew:RECT
LOCAL sLen:DWORD,ColWidth:DWORD,ecxR:DWORD
LOCAL colTxt:DWORD,colBg:DWORD
LOCAL hLVID:DWORD
LOCAL lvi:LVITEM,hdi:HDITEM
LOCAL lvc:LVCOLUMN
LOCAL lvFlag:DWORD, lvCt:DWORD, lvPos:DWORD
LOCAL LocBuf[lBuf]:BYTE, pBuf:DWORD
  ClearLocals ; all done!


ClearLocals MACRO ; first instruction after LOCALS - ecx NOT preserved!!
mov eax, edi ; save edi to a free ACD register
mov ecx, ebp ; Base pointer into counter
mov edi, esp ; current stack pointer as destination
sub ecx, edi ; subtract from counter
shr ecx, 2 ; divide by 4, stack is dword aligned (3 bytes extra)
push eax ; contains original edi
xor eax,eax ; we want zeroed locals
rep stosd ; fill (5 cycles per local variable)
pop edi ; give edi back to Windows
ENDM


Larry Hammick

The routine "myroutine" was just a stub to illustrate where the macro could go, first to zero the locals on entry to the routine, and again later in the routine, even if there is new stuff on the stack at that point. Variant:
@resetlocals macro    ;zero everything from esp up to ebp
    mov ecx,ebp
    sub ecx,esp
    mov esp,ebp     ;discard existing locals
    shr ecx,2
@@: push 0
    loop @B
      ;esp is now the same as it was prior to the macro
endm

ToutEnMasm

Hello,
After  reading this posts and make some experiments ,I have found one error.
A correct optimized macro is:
Quote
ZEROLOCALES MACRO dernierelocale:REQ
   mov edx,edi
   mov ecx,ebp
   lea edi,dernierelocale
   sub ecx,edi
   xor eax,eax
   shr ecx,2
   rep stosd
   mov edi,edx   
ENDM         
Loading edi with the adress of the last local is required.In case you used   "uses esi edi ebx" , those three registers are erased if you put esp in edi




jj2007

Since I need this very often, I decided to re-write it as a procedure. Usage:


MyProggieWithFunnyLocals proc
LOCAL tmp:DWORD, tl:DWORD, flShow:DWORD
LOCAL xPosC:DWORD, yPosC:DWORD, HtC:DWORD, WdC:DWORD
LOCAL TxSize:SIZEL
LOCAL rc:RECT
LOCAL CtrlDc:DWORD
LOCAL LocBuf[60]:BYTE
  call ClearLocals
  .. do things ...


As you see, no arguments are needed, so even PROTO and INVOKE are not necessary. But you need one global variable in the data? section.

jj


.data?
EspGlob dd ?
...
ClearLocals proc ; first instruction after LOCALS - eax will be zero on exit
pop EspGlob ; save the return address - now the stack is identical to the calling procedure
xchg eax,ecx ; save ecx
mov ecx,ebp ; base page of calling procedure
sub ecx,esp ; ebp - esp = No. of bytes in locals
mov esp,ebp ; discard existing locals
shr ecx,2 ; divide by four
@@: push 0 ; dwords on stack
loop @B
xchg eax,ecx ; restore ecx
push EspGlob ; restore the return address
ret
ClearLocals endp

ToutEnMasm

Hello,
To jj2007,your proc go to crash if you use "uses esi edi ebx"

see this
Quote
               push    ebp
               mov     ebp,esp
   add     esp,0FFFFFDA8h   ;- size of local
   push    esi
   push    edi
   push    ebx
                                <- esp is here

                 pop     ebx
                 pop     edi
                pop     esi
                 leave
      ret



jj2007

Quote from: ToutEnMasm on February 20, 2008, 09:36:13 PM
Hello,
To jj2007,your proc go to crash if you use "uses esi edi ebx"
Quote

Thanks for this warning, ToutEnMasm. :U