News:

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

Passing arguments to proc

Started by Klod, October 23, 2007, 05:07:58 AM

Previous topic - Next topic

Klod

This is my first working program in asm and I need some guidance and some help.
For the last several days I have been experimenting, meaning I tried every which way to make a simple program work, but with only partial  success.
I built a console application to prove out my logic and code and it works. See the console app.
I then went on to build a dialog prog, but have problems with declaration, I think. I got the pointers working, but no strings are displayed in the list box.

From the Docs
lea eax, MyVar    ;ADDRESS of a variable into the EAX register. 
mov lpMyVar, eax   ;POINTER to the address.

I have two files and I would like to use 1 pointer
stralloc flen      ;allocate flen bytes of space on the heap for tthe file
mov hFile1, eax         ; pointer to allocated memory

stralloc flen      ;allocate flen bytes of space on the heap for tthe file
mov hFile2, eax         ; pointer to allocated memory

in data p2File dd ?
later I would like it like this...
lea eax, hFile1
mov eax,[eax]
mov p2File, eax   would this be a pointer to a pointer??


I also tied this:
LOCAL pbuf  :DWORD
LOCAL buffer[260]:BYTE
mov pbuf, ptr$(buffer)                  ; cast buffer to pointer
This works.


LOCAL sPos  :DWORD
LOCAL p2SPos:DWORD
mov p2SPos, ptr$(sPos)   
This does not work

Could any one enlighten me on these subjects please?

Are there better ways to pass multiple parameters to a proc?

Thanks in advance
Klod

[attachment deleted by admin]

Tedd


in data p2File dd ?
later I would like it like this...
lea eax, hFile1
mov eax,[eax]
mov p2File, eax   would this be a pointer to a pointer??

..no :P That would just get you the original pointer, i.e. p2File = hFile1
"lea eax,hFile1" gets you a pointer to hFile1, in eax - so all you need to do is store that => "mov p2File,eax" (just remove the "mov eax,[eax]")

To pass multiple parameters you can always pass the two parameters individually, but as this is a dialog box, you've got the right idea :wink

Also, just to make your head spin (or possibly bring enlightenment :lol) "lea eax,hFile1" has the same effect as "mov eax,OFFSET hFile1" - in this instance they both do the same thing (get you a pointer to hFile1). 'lea' is only required in some cases, where there's a register+offset combination - something that's usually hidden from you by using symbol names - but it generally only happens when referring to local variables.
No snowflake in an avalanche feels responsible.

Mark Jones

#2
Hi Klod, one thing you could easily do, which does not require a debugger, is simply to display your unknown pointer/value in a MessageBox. Consider this:

.data?
    myVal           DD  ?
    szValH          DD  12 dup (?)
    szValD          DD  12 dup (?)
.code
    mov myVal,12345678h               ; put something in our unknown value
    invoke dw2hex,myVal,addr szValH   ; works backwards, converts dword to hex ascii string
    invoke dwtoa,myVal,addr szValD    ; same for dword to ascii equivalent
    invoke MessageBox,0,addr szValD,addr szValH,MB_OK ; display these


Whatever you "pass" to the functions, gets converted to an ASCII string and displayed. This displays the hex value in the top of the window, and the decimal value in the center.

Still another thing which could work in a pinch, would be to move the value in question into EAX, then issue an INT 3. This will cause the code to "crash" at that point, but the value you are interested in will be in EAX, so (assuming your operating system will display a crash report) you could do that to see what was in EAX.

Another thing, procedures are not always required. Sometimes it is easier, faster, and cleaner to just use jump labels, and pass values manually using either the stack or registers. Consider this following block of code, which uses ESI and EDI to manipulate a string:

.data?
    szDest     dd  ?
    szPath     dd  MAX_PATH dup (?)
    szArgs     dd  MAX_PATH dup (?)
.code
    push esi
    push edi
    call GetCommandLine                 ; returns pointer to string
    mov esi,eax                         ; esi = ["\file.exe"] [driveletter], [programtorun], [arg...]
    cmp byte ptr [esi],22h              ; if filename
    jnz noquote                         ; is not quoted
@@: inc esi                             ; next char
    cmp byte ptr [esi],22h              ; look for quote
    jnz @B                              ; loop until found
    jmp drive
noquote:
@@: inc esi                             ; skip over c:\path\file.exe
    cmp byte ptr [esi-1],00h            ; was char == null?
    jz ready                            ; if so, done
    cmp byte ptr [esi-1],20h            ; was previous char a space?
    jnz @B                              ; loop until filename+space skipped
drive:
    lea edi,szDest                      ; copy dest drive+space
@@: movsb                               ;
    cmp byte ptr [esi-1],00h            ; check for null
    jz ready                            ;
    cmp byte ptr [esi-1],20h            ; check for space
    jnz @B                              ;
   
    lea edi,szProg                      ; copy program to run
@@: movsb                               ;
    cmp byte ptr [esi-1],00h            ;
    jz ready                            ;
    cmp byte ptr [esi-1],20h            ;
    jnz @B                              ;

    lea edi,szArgs                      ; copy any arguments
@@: movsb                               ;
    cmp byte ptr [esi-1],00             ;
    jnz @B                              ;
ready:
    pop edi                             ; restore edi
    pop esi                             ; and esi


This reads the command-line passed to it and splits the values into the file being ran, drive letter specified, filename to run, and any arguments provided for it (this is part of my DWG application elsewhere.) The same functionality could have been made with procedures... however it is far easier just to do this. Hope that gives you some ideas.
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

Ian_B

Quote from: Mark Jones on October 23, 2007, 03:24:03 PMSometimes it is easier, faster, and cleaner to just use jump labels, and pass values manually using either the stack or registers.
It's always faster and cleaner to keep values in registers and pass them to different program segments that way rather than pushing/popping arguments, even with procedures.

Klod

Thank you all for your replies
thanks, Tedd for pointing out the pointer thing. What threw me was that lea eax,offset hFile1 would also compile.
Quote
'lea' is only required in some cases, where there's a register+offset combination - something that's usually hidden from you by using symbol names - but it generally only happens when referring to local variables.
When I see dis assemblies like:
004010AC   8D4510   LEA EAX,DWORD PTR SS:[EBP+10H]      


004012BC   8945F8   MOV DWORD PTR SS:[EBP-08H],EAX      
004012BF   837D0801    CMP DWORD PTR SS:[EBP+08H],01H   

I suspect a "slight of hand" by the assembler.   

Speaking of the stack, if I understand right, the stack is an area of memory set aside for each thread and esp is basically the offset into this memory segment. So, for each argument or local stack variable, there should be an stacksegment+offset notation in terms of esp like SS:[512]. Or an absolute esp value. If I push eax onto the stack, this would mean to a [esp] value equal to SS:+offset? I ask this question because it would enable simple checking of the scope of  local variables like cmp localVar,esp. If esp >, localVar is inscope, else not a valid address?


To Mark Jones

Yes, I do display values in Messageboxes, but had removed them from the sources to reduce redundant code.

Second method, mov eax,whatever and int 3, I will try out. Thanks.

Quoteprocedures are not always required

Yes, I am aware of this, but there is questions about style and code re usability, readability, flexibility etc... vs performance...  I'm still working on a style that suits me... I'm open to suggestions.

Here is my basic Idea:
There are lots of good basic procedures in the Masm package or for down load from various sites, no need to reinvent the wheel. I would like to create hub procedures, which use these basic procs to create procs of  higher hierarchy or as I call them hub procs. The base criteria for base procs is, they have to be generic. The LoadFile proc in the Masm package meets this criteria. With it, you can load almost any file into memory, regardless if its a bmp, text file etc, all you need to do is pass the right parameters to it and handle the return. It should also have the capability to interface with other hubs... etc..

The hub procedure contains the logic and delegates tasks and handles the returned data to obtain a higher level functionality.
Example:
Pseudocode
Hubproc Fill list     
LoadFile                                     ;load a file into memory, handle release of memory
readEdit         ;Get search string from edit box
loop !EOF
ReadLine         ;read one line at a time into temp buffer
InString         ;cmp buffer to testString.
IF yes -->ADDTOLIST
ELSE jmp loop
relese memory
done

Looking at my code, reveals, that my parameter passing sucks. Some people pass kidney stones easier...
It also becomes evident, that a lot of data has to be passed to and fro to procs. Here is where my code breaks.
My second thought is to keep only pointers to data and code as local data in the hub procedure. This is why I asked the question about the stack .
Anyway, what I really am looking for is an interface.

To Ian_B
Yes, I am aware of this to.

TX
Klod




Tedd

Quote from: Klod on October 24, 2007, 04:57:57 AM
004010AC   8D4510   LEA EAX,DWORD PTR SS:[EBP+10H]
  :
004012BC   8945F8   MOV DWORD PTR SS:[EBP-08H],EAX
004012BF   837D0801    CMP DWORD PTR SS:[EBP+08H],01H

I suspect a "slight of hand" by the assembler.   
When you see offsets from ebp like that, it's usually from accessing local variables (which are allocated on the stack) or parameters passed to the procedure. At the start of a procedure you'll often see "push ebp", "mov ebp,esp", "sub esp,????????" (or sometimes it adds a negative amount - same effect, smaller instruction coding). What that does is save ebp (so it's not messed up), store the position of the 'bottom' of the stack in ebp, then allocate space for the local variables. The reason for copying the position of the stack bottom is to have some reference point for the local variables that doesn't move; each time you push/pop, the value of esp changes, so using offsets from esp starts to get complicated because you need to remember how it's changed (that's not to say it's impossible - some compilers often do it). Whereas, with ebp, the offset to a particular variable is always the same (as long as you don't change ebp.)

Quote
Speaking of the stack, if I understand right, the stack is an area of memory set aside for each thread and esp is basically the offset into this memory segment. So, for each argument or local stack variable, there should be an stacksegment+offset notation in terms of esp like SS:[512]. Or an absolute esp value. If I push eax onto the stack, this would mean to a [esp] value equal to SS:+offset? I ask this question because it would enable simple checking of the scope of  local variables like cmp localVar,esp. If esp >, localVar is inscope, else not a valid address?
Yes.. but no :bdg The stack is just another area of memory, but it's the same memory as everything else. Segments aren't particularly used in 32-bit asm; though there is a stack-segment, it's almost always implicit, so there's little need to bother with it. "MOV DWORD PTR SS:[EBP-08H],EAX" means exactly the same as "MOV [EBP-08H],EAX" because SS is the default segment for ebp (and esp) so there's no need to directly specify it - it's just the disassembler being pedantic. But yes, each thread does get its own stack (and registers..) The register+offset notation for each variable is ebp+offset (as explained above), referencing directly into SS isn't advised - the positions of local variables depends very much on the exact level of the stack at entry to the procedure, also the stack increases backwards :P For checking, all local variables must fall between ebp and esp :wink

Quote
Yes, I am aware of this, but there is questions about style and code re usability, readability, flexibility etc... vs performance...  I'm still working on a style that suits me... I'm open to suggestions.
Style, readability, etc - yes. The most important thing is that your code works correctly, first - that means you need to be able to understand it. You can meddle with inlining functions if the need grabs you, later. Do what suits you and feels 'nicest' as you're writing, your own style will emerge as you code various things and make personal decisions about how to arrange your code.
No snowflake in an avalanche feels responsible.