Hi, people!
I try to write macro function to simplify the call like this (C style):



SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;


The simplest version (actually it's much more complex) of my macro function is:



$DIV MACRO Dividend:REQ, Divisor:REQ

IF (OPATTR (Divisor)) AND 00000100y
;; Divisor is an immediate value
IF Divisor EQ 2
shr Dividend, 1
ENDIF
ENDIF

EXITM <eax>
ENDM



Then i call SetViewportOrgEx to times this way:



push NULL
push $DIV(cyClient, 2)
push $DIV(cxClient, 2)
push hdc
call SetViewportOrgEx

invoke SetViewportOrgEx, hdc, $DIV(cxClient, 2), $DIV(cyClient, 2), NULL


The first call assembles to this, and this is exactly what might be expected:


.text:0040115E push NULL
.text:00401160 mov eax, cyClient
.text:00401165 shr eax, 1
.text:00401167 push eax
.text:00401168 mov eax, cxClient
.text:0040116D shr eax, 1
.text:0040116F push eax
.text:00401170 push [ebp+hdc]
.text:00401173 call SetViewportOrgEx


But the second one assembles to this :-(


.text:00401179 mov eax, cxClient
.text:0040117E shr eax, 1
.text:00401180 mov eax, cyClient
.text:00401185 shr eax, 1
.text:00401187 push NULL
.text:00401189 push eax
.text:0040118A push eax
.text:0040118B push [ebp+hdc]
.text:0040118E call SetViewportOrgEx


My question is:
Posted on 2001-10-10 10:09:06 by MacroFan
Oops, sorry.
The macro is:



$DIV MACRO Dividend:REQ, Divisor:REQ

IF (OPATTR (Divisor)) AND 00000100y
;; Divisor is an immediate value
IF Divisor EQ 2
mov eax, Dividend
shr eax, 1
ENDIF
ENDIF

EXITM <eax>
ENDM
Posted on 2001-10-10 10:13:09 by MacroFan
would be a very nice feature but is impossible with the build in invoke macro. But you can write your own "invoke".

japheth
Posted on 2001-10-10 11:19:52 by japheth
Hmm... May be you are right, but how about this.

My custom macro without INVOKE:


_CALL MACRO arglist:VARARG

local n, arg

n TEXTEQU @ArgCount( <arglist> )
% FOR arg, @ArgRev( <arglist> )
n TEXTEQU %(n - 1)
IF n EQ 0
call arg
ELSE
push arg
ENDIF
ENDM
ENDM



And this call,


_CALL SetViewportOrgEx, hdc, $DIV(g_cxClient, 2), $DIV(g_cyClient, 2), NULL



assembles to this:


.text:00401197 mov eax, cyClient
.text:0040119C shr eax, 1
.text:0040119E mov eax, cxClient
.text:004011A3 shr eax, 1
.text:004011A5 push NULL
.text:004011A7 push eax
.text:004011A8 push eax
.text:004011A9 push [ebp+hdc]
.text:004011AC call SetViewportOrgEx



Exactly the same as with invoke, but there is no invoke at all.
Posted on 2001-10-10 12:19:41 by MacroFan
If you place <ECHO arglist> at the begining of the _CALL,
you'll see that MASM passes arglist to the _CALL this way:



SetViewportOrgEx,hdc,eax,eax,NULL


It means that two $DIV macros are already expanded.
So, the sequence is:


Expand $DIV
Expand $DIV
Expand _CALL


My goal to enforce MASM do it this way:


Expand _CALL
{
Expand $DIV
Expand $DIV
}


My guess that i can achieve my goal, only if i'll pass arglist to _CALL as one text argument.
Something like this:


_CALL "SetViewportOrgEx, hdc, $DIV(cxClient, 2), $DIV(cyClient, 2), NULL"


But if i'll do so, i'll have to handle all parameters manually, character by character.
And i want avoid this. So, may be there is other simple method?
Posted on 2001-10-10 13:00:53 by MacroFan
Another option might be to pass a register to the macro as the destination:
invoke  SetViewportOrgEx, hdc, $DIV(ecx, cxClient, 2), $DIV(edx, cyClient, 2), NULL

This makes the macro flexible enough to work around your problem. Other uses present themselves: :)
mov dxClient, $DIV(ecx, cxClient, 2)

mov dyClient, $DIV(eax, cyClient, 2)
mul ecx
mov cArea, eax
Posted on 2001-10-10 17:23:23 by bitRAKE
MacroFan,

your _CALL macro is not bad, but to work correctly you must prevent MASM from evaluating the arguments before "executing" the macro. So instead of



_CALL SetViewportOrgEx, hdc, $DIV(g_cxClient, 2), $DIV(g_cyClient, 2), NULL


code



_CALL SetViewportOrgEx, hdc, <$DIV(g_cxClient, 2)>,< $DIV(g_cyClient, 2)>, NULL


japheth
Posted on 2001-10-11 03:31:53 by japheth
2 bitRAKE
Another option might be to pass a register to the macro as the destination


Not my goal. I don't want take care about destination registers.
Furthermore, there are too few of them. And what about this?



_CALL StretchBlt, hdcDest,
$ADD(nXOriginDest, 100),
$ADD(nYOriginDest, 50),
$DIV(nWidthDest, 2),
$DIV(nHeightDest, 2),
hdcSrc,
$SUB(nXOriginSrc, 20),
$SUB(nYOriginSrc, 80),
$MUL(nWidthSrc, 4),
$MUL(nHeightSrc, 4),
SRCCOPY


I want use only eax as destination,
and after each ($ADD, $DIV, $SUB, $MUL) macro evaluating push result on stack.




2 japheth
...you must prevent MASM from evaluating the arguments before "executing" the macro

Right, but does not work for me.
And this macro call


_CALL SetViewportOrgEx, hdc, <$DIV(cxClient, 2)>, <$DIV(cyClient, 2)>, NULL

leads to this strange result :-(((


.text:00401145 mov eax, cxClient
.text:0040114A shr eax, 1
.text:0040114C mov eax, cyClient
.text:00401151 shr eax, 1
.text:00401153 mov eax, cxClient
.text:00401158 shr eax, 1
.text:0040115A mov eax, cyClient
.text:0040115F shr eax, 1
.text:00401161 mov eax, cxClient
.text:00401166 shr eax, 1
.text:00401168 mov eax, cyClient
.text:0040116D shr eax, 1
.text:0040116F push NULL
.text:00401171 push eax
.text:00401172 push eax
.text:00401173 push [ebp+hdc]
.text:00401176 call SetViewportOrgEx
Posted on 2001-10-11 04:06:42 by MacroFan
MacroFan,

when using @ArgCount/@ArgRef, you are evaluating the parameters and this evaluation always generates code. When using this simple version of _CALL:



_CALL MACRO arg0:REQ, arg1, arg2, arg3, arg4, arg5

ifnb <arg5>
push arg5
endif
ifnb <arg4>
push arg4
endif
ifnb <arg3>
push arg3
endif
ifnb <arg2>
push arg2
endif
ifnb <arg1>
push arg1
endif

call arg0
ENDM


all works fine.

So just investigate a bit how to use VARARGS without evaluating the parms and you are done. Please post us your working solution!

japheth
Posted on 2001-10-11 05:22:02 by japheth
May be these macros will help You.



@add macro v1: REQ, v2: REQ
local res
.data
res dword ?
.code
push eax
mov eax, v1
add eax, v2
mov res, eax
pop eax
exitm <&res>
endm

@div macro v1: REQ, v2: REQ
local res
.data
res dword ?
.code
push edx
push eax
xor edx, edx
mov eax, v1
push v2
pop res
div res
mov res, eax
pop eax
pop edx
exitm <&res>
endm
Posted on 2001-10-11 06:34:31 by vkim
2 japheth
when using @ArgCount/@ArgRef, you are evaluating the parameters and this evaluation always generates code


These are my @ArgCount and @ArgRev


@ArgCount MACRO arglist:VARARG
LOCAL count, arg, t
count = 0
FOR arg, <arglist>
count = count + 1

;; t e s t
t TEXTEQU %count
% ECHO t
;; t e s t


ENDM
EXITM %count
ENDM


@ArgRev MACRO arglist
LOCAL txt, arg
txt TEXTEQU <>
% FOR arg, <arglist>
txt CATSTR <arg>, <!,>, txt

;; t e s t
% ECHO txt
;; t e s t

ENDM

txt SUBSTR txt, 1, @SizeStr( %txt ) - 1
txt CATSTR <!<>, txt, <!>>
EXITM txt
ENDM


Lines labeled ";; t e s t" temporary added for test.
_CALL SetViewportOrgEx, hdc, <$DIV(cxClient, 2)>, <$DIV(cyClient, 2)>, NULL
echoes this:


ECHO from @ArgCount
1
2
3
4
5

ECHO from @ArgRev
SetViewportOrgEx,
hdc,SetViewportOrgEx,
eax,hdc,SetViewportOrgEx,
eax,eax,hdc,SetViewportOrgEx,
NULL,eax,eax,hdc,SetViewportOrgEx,


I can see that parameters passed to @ArgRev are already evaluated.
May be i'm wrong, but in my oppinion that avaluation occurs not because of @ArgCount or @ArgRev.


When using this simple version of _CALL all works fine.


??? May be i'm beef-witted man, or you did't understand something, or something else.
But i renamed your version of macro to _CALL2 and assembled this three lines.



invoke SetViewportOrgEx, hdc, $DIV(cxClient, 2), $DIV(cyClient, 2), NULL
_CALL SetViewportOrgEx, hdc, $DIV(cxClient, 2), $DIV(cyClient, 2), NULL
_CALL2 SetViewportOrgEx, hdc, $DIV(cxClient, 2), $DIV(cyClient, 2), NULL


Below the result:


.text:00401002 mov eax, cyClient
.text:00401007 shr eax, 1
.text:00401009 mov eax, cxClient
.text:0040100E shr eax, 1
.text:00401010 push NULL
.text:00401012 push eax
.text:00401013 push eax
.text:00401014 push hdc
.text:0040101A call SetViewportOrgEx
.text:0040101F mov eax, cyClient
.text:00401024 shr eax, 1
.text:00401026 mov eax, cxClient
.text:0040102B shr eax, 1
.text:0040102D push NULL
.text:0040102F push eax
.text:00401030 push eax
.text:00401031 push hdc
.text:00401037 call SetViewportOrgEx
.text:0040103C mov eax, cyClient
.text:00401041 shr eax, 1
.text:00401043 mov eax, cxClient
.text:00401048 shr eax, 1
.text:0040104A push NULL
.text:0040104C push eax
.text:0040104D push eax
.text:0040104E push hdc
.text:00401054 call SetViewportOrgEx


Can you see any difference? I can't.
Did you try to assemble it with your _CALL or it's only theoretical.
If it assembles for you as expected (with my $DIV of course), please let me know.

2 vkim
I'll try.
Posted on 2001-10-11 07:28:42 by MacroFan
Spasibo, vkim! But though


_CALL SetViewportOrgEx, hdc, @add(cxClient, 2), @add(cyClient, 4), NULL

assembles as expected


.text:00401002 push eax
.text:00401003 mov eax, cxClient
.text:00401008 add eax, 2
.text:0040100B mov X, eax ; mov X, @add(cxClient, 2)
.text:00401010 pop eax
.text:00401011 push eax
.text:00401012 mov eax, cyClient
.text:00401017 add eax, 4
.text:0040101A mov Y, eax ; mov Y, @add(cyClient, 4)
.text:0040101F pop eax
.text:00401020 push NULL
.text:00401022 push Y
.text:00401028 push X
.text:0040102E push hdc
.text:00401034 call SetViewportOrgEx


your macros didn't help me of course. Because of:

push eax
pop eax
push eax
pop eax
and two DWORD in .data section


Too much for one simple call. Isn't it!?
Posted on 2001-10-11 08:40:21 by MacroFan
MacroFan,

itr wasnt theoretically. Heres an excerpt from my listing file:



_CALL MACRO arg0:REQ, arg1, arg2, arg3, arg4, arg5

ifnb <arg5>
push arg5
endif
ifnb <arg4>
push arg4
endif
ifnb <arg3>
push arg3
endif
ifnb <arg2>
push arg2
endif
ifnb <arg1>
push arg1
endif

call arg0
ENDM

$DIV MACRO Dividend:REQ, Divisor:REQ

IF (OPATTR (Divisor)) AND 00000100y

; Divisor is an immediate value
IF Divisor EQ 2
mov eax,Dividend
shr eax, 1
ENDIF
ENDIF

EXITM <eax>
ENDM

;invoke SetViewportOrgEx, hdc, $DIV(cxClient, 2), $DIV(cyClient, 2), NULL
_CALL SetViewportOrgEx, hdc, <$DIV(cxClient, 2)>, <$DIV(cyClient, 2)>, NULL
00000000 6A 00 1 push NULL
00000002 A1 00000004 R 2 mov eax,cyClient
00000007 D1 E8 2 shr eax, 1
00000009 50 1 push $DIV(cyClient, 2)
0000000A A1 00000000 R 2 mov eax,cxClient
0000000F D1 E8 2 shr eax, 1
00000011 50 1 push $DIV(cxClient, 2)
00000012 FF 35 00000008 R 1 push hdc
00000018 FF 15 00000000 E 1 call SetViewportOrgEx


In my eyes it looks correct. Dont forget the brackets.

japheth
Posted on 2001-10-11 10:02:39 by japheth
japheth, very nice! Will be of use to me. :) Yes, I see that you changed the point of evaluation by not using VARARG - clever!
Posted on 2001-10-11 17:16:07 by bitRAKE
Hi, MacroFan!
I'm pleasently surprised with Your Russian! :)
Yes, my previous macros required too much resources. Ok, look at this:


.386
.model flat, stdcall
option casemap: none

include \masm32\include\windows.inc
include \masm32\include\oleaut32.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\debug.inc

includelib \masm32\lib\debug.lib
includelib \masm32\lib\oleaut32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib

i = 0

TestFunc proto :dword, :dword, :dword, :dword

@add macro v1: REQ, v2: REQ
local idx
i = i + 1
if i eq 1
mov eax, esp
endif
sub esp, 8
idx = i * 4
mov dword ptr [esp+idx], v1
add dword ptr [esp+idx], v2
exitm <dword ptr [eax-&idx]>
endm


$call macro function: REQ, args: VARARG
invoke function, args
if i ne 0
i = i * 8
add esp, i
i = 0
endif
endm

.data

.code
start:
xor edx, edx
$call TestFunc, @add(edx, 1), @add(edx, 2), @add(edx, 3), @add(edx, 4)
mov edx, 10
$call TestFunc, @add(edx, 1), @add(edx, 2), @add(edx, 3), @add(edx, 4)
ret

TestFunc proc a1: dword, a2: dword, a3: dword, a4: dword
PrintDword a1 ;PrintDword is my macro to check value
PrintDword a2
PrintDword a3
PrintDword a4
PrintLine
ret
TestFunc endp

end start

Below is result:


00050 $call TestFunc, @add(edx, 1), @add(edx, 2), @add(edx, 3), @add(edx, 4)
0167:00401012 mov eax,esp
0167:00401014 sub esp,08
0167:00401017 mov [esp+04],edx
0167:0040101b add dword ptr [esp+04],01
0167:00401020 sub esp,08
0167:00401023 mov [esp+08],edx
0167:00401027 add dword ptr [esp+08],02
0167:0040102c sub esp,08
0167:0040102f mov [esp+0c],edx
0167:00401033 add dword ptr [esp+0c],03
0167:00401038 sub esp,08
0167:0040103b mov [esp+10],edx
0167:0040103f add dword ptr [esp+10],04
0167:00401044 push dword ptr [eax-10]
0167:00401047 push dword ptr [eax-0c]
0167:0040104a push dword ptr [eax-08]
0167:0040104d push dword ptr [eax-04]
0167:00401050 call TestFunc
0167:00401055 add esp,20
Posted on 2001-10-11 23:27:30 by vkim
2 japheth?
In my eyes it looks correct. Dont forget the brackets

Opps...My mistake. Hab' vergessen <>. Now it's OK. Thnx.

2 vkim
I'm pleasently surprised with Your Russian!

Nichigo udivitel'nogo. Mama v detstve uchila ;-)
Yes, my previous macros required too much resources. Ok, look at this:

Hmm... Much better. But too many code. (i mean size of code in .text section)
And you gave me good idea - to form stack manually without using of push.
BTW, I don't have close look at your macro yet.
But at first glance the four push are not necessary.
Because you can form stack with mov dword ptr , param,
then point esp to apropriate value and call.
In the case of four parameters it should be something like this:


mov [esp-04], edx ; a4
add dword ptr [esp-04], 1
mov [esp-08], edx ; a3
add dword ptr [esp-08], 2
mov [esp-0Ch], edx ; a2
add dword ptr [esp-0Ch], 3
mov [esp-10h], edx ; a1
add dword ptr [esp-10h], 4
sub esp, 10h
call TestFunc
; add esp,10h ; not necessary because of stdcall


So, people, my main problem is solved now.
Vielen Dank! japheth
Bol'shoe spasibo! vkim
Thank you, very much! bitRAKE
Now i have to do my homework by myself.
Posted on 2001-10-12 07:26:39 by MacroFan