Seems to work OK.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
fcall MACRO procedure,arg1,arg2,arg3,arg4,arg5
push ebx
IFNB <arg5>
.err
%echo 4 argument limit exceeded
ENDIF
IFNB <arg1>
mov eax, arg1
ENDIF
IFNB <arg2>
mov ebx, arg2
ENDIF
IFNB <arg3>
mov ecx, arg3
ENDIF
IFNB <arg4>
mov edx, arg4
ENDIF
call procedure
pop ebx
ENDM
.data
tmsg db "Text Message",0
pmsg dd tmsg
tttl db "Title",0
pttl dd tttl
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
fcall mbox,0,pmsg,pttl,MB_OK
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
mbox proc
invoke MessageBox,eax,ebx,ecx,edx
ret
mbox endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
comment *
00401010 start:
00401010 E807000000 call fn_0040101C
00401015 6A00 push 0
00401017 E8EAFFFFFF call jmp_ExitProcess
0040101C fn_0040101C: ; Xref 00401010
0040101C 53 push ebx
0040101D B800000000 mov eax,0
00401022 8B1D0D304000 mov ebx,[off_0040300D]
00401028 8B0D17304000 mov ecx,[off_00403017]
0040102E BA00000000 mov edx,0
00401033 E802000000 call fn_0040103A
00401038 5B pop ebx
00401039 C3 ret
0040103A fn_0040103A: ; Xref 00401033
0040103A 52 push edx
0040103B 51 push ecx
0040103C 53 push ebx
0040103D 50 push eax
0040103E E8BDFFFFFF call jmp_MessageBoxA
00401043 C3 ret
*
Works fine, but I would suggest to save ebx only when needed:
fcall MACRO procedure,arg1,arg2,arg3,arg4,arg5
IFNB <arg5>
.err
%echo 4 argument limit exceeded
ENDIF
IFNB <arg1>
mov eax, arg1
ENDIF
IFNB <arg2>
push ebx
mov ebx, arg2
ENDIF
IFNB <arg3>
mov ecx, arg3
ENDIF
IFNB <arg4>
mov edx, arg4
ENDIF
call procedure
IFNB <arg2>
pop ebx
endif
ENDM
I have a better version that does not use ebx.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
fastcall32 MACRO procedure:REQ,arg1,arg2,arg3,arg4,arg5,arg6, \
arg7,arg8,arg9,arg10,arg11,arg12, \
arg13,arg14,arg15,arg16,arg17,arg18
;; -----------------------
;; stack arguments 4 to 18
;; -----------------------
IFNB <arg18>
push reparg(arg18)
ENDIF
IFNB <arg17>
push reparg(arg17)
ENDIF
IFNB <arg16>
push reparg(arg16)
ENDIF
IFNB <arg15>
push reparg(arg15)
ENDIF
IFNB <arg14>
push reparg(arg14)
ENDIF
IFNB <arg13>
push reparg(arg13)
ENDIF
IFNB <arg12>
push reparg(arg12)
ENDIF
IFNB <arg11>
push reparg(arg11)
ENDIF
IFNB <arg10>
push reparg(arg10)
ENDIF
IFNB <arg9>
push reparg(arg9)
ENDIF
IFNB <arg8>
push reparg(arg8)
ENDIF
IFNB <arg7>
push reparg(arg7)
ENDIF
IFNB <arg6>
push reparg(arg6)
ENDIF
IFNB <arg5>
push reparg(arg5)
ENDIF
IFNB <arg4>
push reparg(arg4)
ENDIF
;; -------------------------
;; register arguments 1 to 3
;; -------------------------
IFNB <arg3>
mov edx, reparg(arg3)
ENDIF
IFNB <arg2>
mov ecx, reparg(arg2)
ENDIF
IFNB <arg1>
mov eax, reparg(arg1)
ENDIF
call procedure
ENDM
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
fastcall32 mbox,0,"Text Message","Title",MB_OK
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
mbox proc styl:DWORD
invoke MessageBox,eax,ecx,edx,styl
ret
mbox endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
This is the disassembly of the above, reasonably efficient for a stackframe procedure.
0040101C fn_0040101C: ; Xref 00401010
0040101C 6A00 push 0
0040101E BA00304000 mov edx,offset off_00403000 ; 'Title',000h
00401023 B906304000 mov ecx,offset off_00403006 ; 'Text Message',000h
00401028 B800000000 mov eax,0
0040102D E801000000 call fn_00401033
00401032 C3 ret
00401033 fn_00401033: ; Xref 0040102D
00401033 55 push ebp
00401034 8BEC mov ebp,esp
00401036 FF7508 push dword ptr [ebp+8]
00401039 52 push edx
0040103A 51 push ecx
0040103B 50 push eax
0040103C E8BFFFFFFF call jmp_MessageBoxA
00401041 C9 leave
00401042 C20400 ret 4
Here is a variation that emulates shadow space if a nested set of calls is required. It starts to get up to the overhead of a normal STDCALL so there is little advantage in doing so but for low argument counts while calling a leaf procedure, its a more efficient calling technique.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
fastcall32 mbox,0,"Text Message","Title",MB_OK
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
mbox proc styl:DWORD
LOCAL hMbox :DWORD
LOCAL pMsg :DWORD
LOCAL pTtl :DWORD
mov hMbox, eax
mov pMsg, ecx
mov pTtl, edx
push styl
push pTtl
push pMsg
push hMbox
call MessageBox
ret
mbox endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤