News:

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

Procedure call syntax in MASM32

Started by cork, July 11, 2010, 03:58:35 PM

Previous topic - Next topic

cork

In MASM32, is there any difference between this syntax:

sum1 PROC NEAR
   mov AX, CX
   add AX, DX
   ret
sum1 ENDP

As compared to this syntax:

sum2:
   mov AX, CX
   add AX, DX
ret

Obviously the second looks cleaner. Is there any functional difference, under the hood, in how they operate? Or is it purely syntax?

Also, are people concerned with NEAR procedure calls and FAR procedure calls? Or in the 32-bit world is everything a NEAR procedure call?

dedndave

actually, the first one is cleaner - and generally prefered
inside a PROC, all the labels are local
other than that, they are functionally the same

the code you are showing is 16-bit code (i.e. uses 16-bit register sizes)
when you use 16-bit registers in 32-bit programs, there is an added "size override" operand byte added to the instruction
so, you are usually better off to use the full 32-bit register, even if you are only doing 16-bit math
one exception might be 16-bit signed integers, where the sign flag needs to be set by the result of a 16-bit operation
even then, you can extend the sign to 32 bits, then perform the operation

in 32-bit code, we use the "flat" memory model
that means everything fits into a single segment and is NEAR
we rarely have to mess with segment registers or FAR pointers   :bg
afterall, you can directly address 4 Gb with a 32-bit address

cork

Thank you. I'll start using the "PROC NEAR" syntax. I didn't know the bit about labels being local and was frustrated with the other syntax, as the labels weren't local. Thanks for clearing that up for me... the other info also... thanks, I enjoy learning this stuff.

redskull

Quote from: dedndave on July 11, 2010, 05:00:32 PM
other than that, they are functionally the same

Note that PROC will add stack frame code into your program (mov ebp, esp, etc), whereas the label method will not.

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

dedndave

oops - forgot all about that
although, in this case, the PROC has no parameters, so it shouldn't generate a PROLOGUE or EPILOGUE

jj2007

Hi cork,

Here is a complete example with stack frames and "uses". You should see it through Olly's eyes :eek
include \masm32\include\masm32rt.inc

MyTest PROTO: DWORD, :DWORD

.code
AppName db "Masm32 is great!", 0
Hello db "A message:", 0

start:
invoke MyTest, offset AppName, addr Hello
exit

MyTest proc uses esi edi ebx arg1:DWORD, arg2:DWORD
LOCAL lv1, lv2, locbuf[260]:BYTE
  MsgBox 0, arg1, arg2, MB_OK
  ret
MyTest endp

end start

cork

I've just started using LOCAL variables in my procedures today. Today I've been digging into a chapter on the stack and the stack frame and how that's all set up with parameters, call, and local variables, and then how the stack frame is unwound upon ret. Pretty interesting, but it's going to take a bit before it really sinks in and I get fluent in its use. Right now I feel like a chicken pecking at the ground for bits of wheat.

hutch--

Try the simplest form of PROC first.


ItemName PROC arg1:DWORD,arg2:DWORD

    LOCAL lVar1  :DWORD
    LOCAL lVar2  :DWORD

    nop    ; do nothing.
    nop

    ret

ItemName endp


Then use the DumpPE that comes with MASM32 to disassemble it and you will see what a PROC does.

There are rules about what registers you preserve if you use them in the proc, JJ has shown a higher level technique but its worth understanding what happens with the bare instructions.

If you need to use the 3 registers EBX, ESI + EDI in a stack frame based procedure that PROC creates you manually push and pop the registers like this.


Item proc args etc ....

LOCAL var :DWORD etc ....

push ebx
push esi
push edi

  ; your code

pop edi
pop esi
pop ebx

ret



Note that the registers are popped in reverse order, the stack is a last on, first off technique. This ensures that the 3 registers mentioned if used are restored to the same value on exit which is required if you need to use them.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dedndave

#8
that stuff has always been easy for me
that's because (in days of old) we used to write our own stack frames
seeing what the stack frame looks like can help you understand how it all works

INVOKE method:
Funk    PROTO   :DWORD,:DWORD,:DWORD
;
;
;
        INVOKE  Funk,Parameter1,Parameter2,Parameter3


INVOKE generates more or less the same code as the PUSH and CALL method, depending on how the Parms are pushed:
        push    Parameter3
        push    Parameter2
        push    Parameter1
        call    Funk


to write your own stack frame, temporarily disable PROLOGUE and EPILOGUE:
        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

Funk    PROC    Parm1:DWORD,Parm2:DWORD,Parm3:DWORD

        push    ebx               ;for win32, these 4 registers should generally be preserved
        push    esi               ;EAX, ECX, and EDX can be destroyed and are sometimes used to return results
        push    edi
        push    ebp
        mov     ebp,esp
        sub     esp,12            ;4*LocalDwordCount

        mov     ebx,[ebp+20]      ;Parm1
        mov     esi,[ebp+24]      ;Parm2
        mov     edi,[ebp+28]      ;Parm3

        mov     eax,[ebp-4]       ;Local1
        mov     ecx,[ebp-8]       ;Local2
        mov     edx,[ebp-12]      ;Local3

RoutineExit:
        pop     ebp               ;pop ebp may be replaced with leave if stack might be unbalanced at exit
        pop     edi               ;the leave instruction is the same as: mov esp,ebp | pop ebp
        pop     esi
        pop     ebx
        ret     12                ;return and discard 3 dword parameters from stack

Funk    ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

after you are done, return PROLOGUE and EPILOGUE to default modes

the stack frame looks like this
first thing pushed on the stack is at the highest address
1) Parm3                   [ebp+28]
2) Parm2                   [ebp+24]
3) Parm1                   [ebp+20]
4) RETurn address for CALL [ebp+16]
5) saved EBX contents      [ebp+12]
6) saved ESI contents      [ebp+8]
7) saved EDI contents      [ebp+4]
8) saved EBP contents      [ebp]
9) Local1                  [ebp-4]
10) Local2                 [ebp-8]
11) Local3                 [ebp-12]


if we let the assembler handle the prologue and epilogue, we can write the same routine like this
the assembler will generate more or less the same code as above
Funk    PROC uses ebx esi edi Parm1:DWORD,Parm2:DWORD,Parm3:DWORD

        LOCAL   Local1:DWORD
        LOCAL   Local2:DWORD
        LOCAL   Local3:DWORD

        mov     ebx,Parm1
        mov     esi,Parm2
        mov     edi,Parm3

        mov     eax,Local1
        mov     ecx,Local2
        mov     edx,Local3

RoutineExit:
        ret

Funk    ENDP

cork

Thanks, all this is very useful. I'm reading the MASM documentation as well as another book and combined with these comments its got me less-lost. I'm going to really focus on setting up my own stack frames, rather than just use call/invoke. After doing that for a couple of weeks, it should be pounded in my head good enough. I'm the type of person that can see the concept, but to remember it and use it well, I have to drill it into my head. Thanks again.

hutch--

Just a bit of advice from an old timer, stack frame free procedures can be very complicated and often for no real advantage so I would suggest that you use the normal stack frame procedure while you are learning other stuff, you can come back to them later if you need to get stack overhead down. It is in fact of limited value in that if the code in a procedure is short enough to see an advantage from having no stack frame, its even more efficient to simply inline the procs content in the calling procedure and its usually a lot faster again.

The only real gain in most instances from writing a stack frame free proc is to get the extra register EBP but there are other ways around that in larger procedures.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dedndave

i don't suggest you write that way all the time, either   :bg
but, it helps to illustrate how stack frames work
it makes it easier to understand what is going on "under the hood" when you use the modern conventions

cork

I've settled on the following... I'll use invoke 95% of the time, because I am new at this and don't want to get bogged down.

I'll continue to explore the stack frame and how to set it up and tear it down, because I want to understand perfectly how various calling conventions work. For now I can barely write a bubble sort, so I don't want to get too deep, too soon.

The thing I'm curious about is why there aren't 20 or 30 registers that do nothing but store a value, but can quickly be swapped into the regular working registers in 1 cycle? I guess most local variables will probably be in cache and I don't believe cache takes all that long to load into a register... so it probably wouldn't make much of a difference.

The things I find the hardest - not terribly hard, but modestly hard - so far have been:
- addressing modes that involve more than 1 item... register + offset, etc.
- stack frames
- cache (I consider this an advanced subject - how to setup stuff to optimize cache access)
Everything else has been easy enough. The biggest pain is always pushing registers onto the stack and then popping them off because there are so few. Regardless I'm loving assembly language. It is clearing up a lot of mysteries for me.


oex

Quote from: cork on July 15, 2010, 03:24:07 PM
The thing I'm curious about is why there aren't 20 or 30 registers that do nothing but store a value, but can quickly be swapped into the regular working registers in 1 cycle? I guess most local variables will probably be in cache and I don't believe cache takes all that long to load into a register... so it probably wouldn't make much of a difference.

Some processors ie arm have more registers (16 I think).... There are also 14 GPRs on x86 64 bit I think although only 6 on 32 bit
We are all of us insane, just to varying degrees and intelligently balanced through networking

http://www.hereford.tv

clive

Quote from: oex
Some processors ie arm have more registers (16 I think)....

Quote from: cork
The thing I'm curious about is why there aren't 20 or 30 registers that do nothing but store a value, but can quickly be swapped into the regular working registers in 1 cycle?

Indeed, although r15 is the program counter, r14 the link register, r13 the stack, and r11 or r7 the frame pointer. The number that are usable in any given circumstance are less than ideal.

MIPS has 32 registers, r0 is always zero, plus you need ra, sp, fp, gp, and others committed to system specific needs.

The architecture you probably want is Sparc, it has a large rolling bank of hardware registers (defined by specific implementation 40..520, 32 visible [8+24]), which are spilled to memory if you get too deep.

Or perhaps Itanium (128)?

The 6502 dealt with this issue by using "zero page", and the 8051 by making registers, peripherals and memory somewhat amorphous.

The 80x86 is a pretty hideous example of how thing might been done, the fact that the register resources are limited and lack orthogonality is a matter of history/legacy, mitigated somewhat in the 386.

For all practical purposes the values on the stack are going to be quite close to the core.
It could be a random act of randomness. Those happen a lot as well.