Hello,
The latest versions of Asmc and Poasm can assemble the code below. Ml.exe coming with the Masm32 package reports :
printfProto.asm(51) : error A2114: INVOKE argument type mismatch : argument : 0
.386
.model flat,stdcall
option casemap:none
ExitProcess PROTO :DWORD
__p__iob PROTO C
vfprintf PROTO C :DWORD,:VARARG
printfx PROTO C :DWORD,:VARARG
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib
_iobuf STRUCT
_ptr DWORD ?
_cnt DWORD ?
_base DWORD ?
_flag DWORD ?
_file DWORD ?
_charbuf DWORD ?
_bufsiz DWORD ?
_tmpfname DWORD ?
_iobuf ENDS
FILE TYPEDEF _iobuf
.data
format db 'Pi is nearly equal to %f',0
pi real8 3.141592
.code
start:
invoke printfx,ADDR format,ADDR pi
invoke ExitProcess,0
printfx PROC C _format:DWORD,args:VARARG
invoke __p__iob
; #define stdout (&__iob_func()[1])
add eax,SIZEOF(FILE)
dummy:
invoke vfprintf,eax,_format,args
ret
printfx ENDP
END start
Edit : Replacing the invoke macro here with my own invoke macro is solving the issue :
_invoke vfprintf,eax,_format,args
Would it work by replacing?:
vfprintf PROTO C :DWORD,:VARARG
printfx PROTO C :DWORD,:VARARG
with:
vfprintf PROTO C :VARARG
printfx PROTO C :VARARG
Hm, calling a "vararg"-proc from another "vararg"-proc, and simply hand over the vararg "argument" - that's a pretty adventurous thing to do. I doubt that AsmC or Poasm can do that in a way as it is "expected" by the programmer.
So perhaps Masm is right by simply rejecting this.
Quote from: _japheth on February 01, 2024, 06:57:47 PMSo perhaps Masm is right by simply rejecting this.
Probably is just luck that others assemblers work in this example.
Must be tested with more arguments.
Old ML work with a little modification:
printfx PROC C _format:DWORD,args:VARARG
local arg1:dword
push args
pop arg1
invoke __p__iob
; #define stdout (&__iob_func()[1])
add eax,SIZEOF(FILE)
dummy:
invoke vfprintf,eax,_format,arg1
ret
printfx ENDP
That work because there is only 1 argument in VARARG, with more arguments probably fail, I don't know.
Quote from: Vortex on February 01, 2024, 06:21:59 AMThe latest versions of Asmc and Poasm can assemble the code below.
I've put together a testbed based on your code, see attachment.
I builds and runs fine with UAsm, AsmC and PoAsm.
It fails with all MASM versions I've tested (6.14, 6.15, 14.0).
There is no problem with passing on the varargs, as long as the assembler is able to do that. UAsm, AsmC and PoAsm are able to do it, at point "Dummy" they pass the two arguments on the stack that they received above on entry into the main proc.
MASM can't do that, so it chokes - bad luck. I suggest to dump MASM and work with the improved clones instead. PoAsm is a good option, too, unfortunately not for me because my macros are too complex.
Quote from: jj2007 on February 01, 2024, 11:21:16 PMThere is no problem with passing on the varargs, as long as the assembler is able to do that. UAsm, AsmC and PoAsm are able to do it, at point "Dummy" they pass the two arguments on the stack that they received above on entry into the main proc.
Well, I expressed myself very carefully in my post above (so nobody's feelings are hurt :bgrin: ), but actually it is impossible inside a "vararg" procedure to determine at assembly-time the size of the vararg argument.
Since the size is unknown to the assembler, how is an INVOKE, placed inside the procedure and using the procedure's vararg-argument as argument for a call, supposed to emit the correct number of pushes?
Hi fearless,
Thanks for the suggestion. Wih the modified prototypes, I receive the same error message :
vfprintf PROTO C :VARARG
printfx PROTO C :VARARG
printfx PROC C args:VARARG
invoke __p__iob
; #define stdout (&__iob_func()[1])
add eax,SIZEOF(FILE)
invoke vfprintf,eax,args
ret
printfx ENDP
printfProto.asm(52) : error A2114: INVOKE argument type mismatch : argument : 0
Hi Japhet,
QuoteHm, calling a "vararg"-proc from another "vararg"-proc, and simply hand over the vararg "argument" - that's a pretty adventurous thing to do. I doubt that AsmC or Poasm can do that in a way as it is "expected" by the programmer.
Starting with VC 2019 ( or maybe even before ), MS decided to inline the good old printf function. Here is the case :
.
.
extern __declspec(dllimport) FILE* __cdecl __acrt_iob_func(unsigned);
#define stdin (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))
#pragma comment (lib, "ucrtbase.lib")
__declspec(dllimport) int __cdecl __stdio_common_vfprintf(long long, FILE*, char const* , int, va_list);
int printf(const char * format, ...)
{
int ret;
va_list vl;
va_start(vl, format);
ret = __stdio_common_vfprintf(0, stdout, format, 0, vl);
va_end(vl);
return ret;
}
https://masm32.com/board/index.php?msg=104933
Quotebut actually it is impossible inside a "vararg" procedure to determine at assembly-time the size of the vararg argument.
For 32-bit coding : True but for practical reasons, one can assume that every argument is DWORD sized for most of the time.
QuoteQuotebut actually it is impossible inside a "vararg" procedure to determine at assembly-time the size of the vararg argument.
For 32-bit coding : True but for practical reasons, one can assume that every argument is DWORD sized for most of the time.
That's not the point here. The reason why your code may work ( does it indeed? ) is not because "all arguments are DWORDs", but because you're using vfprintf(). This function does NOT have a variable number of arguments, but exactly 3. Insofar your prototype for vfprintf in the first post is incorrect.
Hi Japheth,
Thanks, correcting the prototype :
int vfprintf ( FILE * stream, const char * format, va_list arg );
vfprintf PROTO C :DWORD,:DWORD,:VARARG
The correct approach :
lea ecx,args
invoke vfprintf,eax,_format,ecx
.data
pi real8 3.141592
format db '%s is nearly equal to %f',0
str1 db 'Pi',0
.code
start:
invoke printfx,ADDR format,ADDR str1,pi
invoke ExitProcess,0
printfx PROC C _format:DWORD,args:VARARG
invoke __p__iob
; #define stdout (&__iob_func()[1])
add eax,SIZEOF(FILE)
lea ecx,args
invoke vfprintf,eax,_format,ecx
ret
printfx ENDP
Hi Jochen,
Thanks for the Poasm samples. Sorry for the confusing label Dummy, it should be removed. The vfprintf based printf function is a compact and small implenentation.
If you'll pardon a naive question, what about wsprintf()? I've used this probably hundreds of times in my code with never a problem. (I use MASM.)
It's in our windows.inc as
wsprintfA PROTO C :VARARG
So what's the big problem here?
Hi NoCforMe,
wsprintfA PROTO C :VARARG
This one is valid too with the condition that the coder is careful to type the correct parameters.
Reading the documentation of Microsoft :
int WINAPIV wsprintfA(
[out] LPSTR unnamedParam1,
[in] LPCSTR unnamedParam2,
...
);
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-wsprintfa
wsprintfA PROTO C :DWORD,:DWORD,:VARARG
This full prototype is enforcing the user to specify the buffer receiving the formatted output and the format-control specification. One could miss the first two parameters with the prototype found in windows.inc
Yes, yes, I know all that: first parm is the output buffer, second is the format string, and the rest are variables to be formatted. I can use that function in my sleep (and often do).
My question is why there's so much angst here about using vararg functions like wsprintf(). As I said, I've never had any problems using this with MASM. i use INVOKE, so the assembler takes care of stack management for me.
Is this a 64-bit problem?
Quote from: NoCforMe on February 02, 2024, 07:52:12 AMSo what's the big problem here?
Perhaps none. Not all threads must handle a
big problem.
Btw., it's also not mandatory to participate in a thread. If you have difficulties understanding what's going on - or just don't want to bother reading a thread carefully - then you might consider
not to post at all.
OK, noted.