There is a general thought that a vararg stdcall function can not be created. One reason is "ret" instruction only takes imm16, not a register. The other is, that supposedly (I can't find the actual line in C89 to support this) C doesn't require that all args passed into the function to be va_arg()ed. The callee can ignore excess args according to C. But dont we want to use __stdcall to avoid all the extra "add esp, *" instructions, or the fat "mov eax, dword ptr [esi+*]; mov dword ptr [esp+*], eax" instructions used in non-moving esp style functions instead of push? Well, we can. Here is a simple cdecl vararg function converted into a stdcall vararg function. To use it from C, you will need to cast it every time obviously and write C macros ADD1, ADD2, ADD3, ADD4, etc.
Before
unsigned int adder(unsigned int paramCount, ...) {
va_list va;
unsigned int ret = 0;
unsigned int i;
va_start(va,paramCount);
for(i=0;i<paramCount;i++){
ret += va_arg(va,unsigned int);
}
va_end(va);
return ret;
}
after
_paramCount$ = 8 ; size = 4
_addersc PROC NEAR ; COMDAT
; 273 : unsigned int i;
; 274 : va_start(va,paramCount);
; 275 : for(i=0;i<paramCount;i++){
; call DWORD PTR __imp__DebugBreak@0
mov edx, DWORD PTR _paramCount$[esp-4]
xor eax, eax
test edx, edx
jbe SHORT _end
; 271 : va_list va;
; 272 : unsigned int ret = 0;
lea ecx, DWORD PTR _paramCount$[esp-4]
redo:
; 276 : ret += va_arg(va,unsigned int);
add ecx, 4
add eax, DWORD PTR [ecx]
dec edx
jne SHORT redo
_end:
; 277 : }
; 278 : va_end(va);
; 279 : return ret;
; 280 : }
pop edx ; put ret addr in reg
add ecx, 4 ; wipe arg paramCount's space
mov esp, ecx ; put esp at end of incoming args
push edx ; put ret addr back in stack in new location
ret ; use ret instruction instead of jmp edx to avoid
;the .2 us delay (by my measurement) of a missed CPU return stack buffer
;prediction
_addersc ENDP
C calling convention allows this because the RET instruction does not "remove" args from the stack
instead, the caller is responsible for balancing the stack after the call
Hi bulk,
Welcome to the Forum :icon14:
You might like this thread (http://www.masmforum.com/board/index.php?topic=14679.msg119125#msg119125).
Hi bulk88,
welcome to the forum and have a lot of fun.
Gunther
Not the best solution but the code below is working :
include \masm32\include\masm32rt.inc
include invoke.inc
.data
string db 'Sum = %u',13,10,0
.data?
.code
start:
_invoke CalcSum,1,3,5,7,9
invoke crt_printf,ADDR string,eax
_invoke CalcSum,2,4,6
invoke crt_printf,ADDR string,eax
invoke ExitProcess,0
CalcSum PROC
LOCAL argcnt:DWORD
mov ecx,DWORD PTR [ebp+8] ; get the number of parameters
; obtained by the _invoke macro
mov argcnt,ecx
xor eax,eax
lea edx,[ebp+12] ; get the address of the first
; argument
@@:
add eax,DWORD PTR [edx]
add edx,4
dec ecx
jnz @b
mov ecx,argcnt
inc ecx
shl ecx,2
leave
mov edx,DWORD PTR [esp] ; get the return address
add esp,ecx ; balance the stack
mov DWORD PTR [esp],edx
retn
CalcSum ENDP
END start