News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Assembler mistake: CALL vs INVOKE

Started by NoCforMe, September 12, 2022, 06:47:54 AM

Previous topic - Next topic

NoCforMe

And by assembler mistake, I mean a mistake on the part of MASM, the macro assembler, not me. I think.

In my editor program I kept losing the "Goodies" dialog; sometimes it just wouldn't come up at all. Traced the problem to my custom mcListbox control in that dialog. It wasn't getting initialized, therefore Windows sent destroy messages to the dialog even before it got created.

Long story short: it wasn't getting initialized because I wasn't calling my initialization function correctly. It's defined as


InitmcLB PROC hInst:HINSTANCE


and yet I was able to do this:


CALL InitmcLB

(because at some earlier point it had no parameter)

Shouldn't this have generated an assembler error? Like INVOKing with too many or too few arguments? It just said "fine, whatever".

BAD assembler. Bad boy.
(The weird thing is, it worked most of the time, even though the instance handle wasn't getting set correctly in RegisterClassEx(). ????)

Assembly language programming should be fun. That's why I do it.

zedd151

Masm only checks arg count for "invoke". That is one of its main purposes, along with type checking for the arguments. If you use "call" instead, no argument count or argument type checking is done - that is left up to the coder in that instance.  call != invoke, unless argument count and type are correct.

jj2007

Swordfish is correct. How should the assembler even know how many parameters you pushed before the call, e.g. in the simple example below?

NoCforMe

Right. Although still, if you think about it: when you do push-push-call, you're generally not calling a procedure name; you're calling either through a DWORD variable or register. (Until I saw your example I didn't even know that you could call a function by name this way.)

If I were the one writing the assembler, I think I'd at least flag it as a warning of someone made the mistake I did here. Because clearly my subroutine takes an argument, which I'm not (explicitly) providing.

Or maybe they should give us an option to flag such warnings or not.

But then, MASM isn't "strongly typed" like C is, so it gives us programmers more, and more interesting, ways to get into trouble.
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on September 12, 2022, 08:13:27 AMwhen you do push-push-call, you're generally not calling a procedure name; you're calling either through a DWORD variable or register.

In my code, 95% of the push-push-calls concern procedure names, only a handful call registers or memory. However, I rarely use push-push-call, mostly to save some bytes as in the example above. The invoke macro is a lot safer, handier and more transparent.

hutch--

Its an interesting case where the traditional "invoke" notation in 32 bit MASM had arg count checking which helped a lot of people catch errors in their code. Traditionally, assemblers never needed such things (real men[tm] etc ....) and while it was introduced about 1990 to make MASM a bit more usable, it only lingered on as it was too much hassle to change it.

Come 64 bit which is mainly new code (apart from the pre-processor) and "bye bye" high level constructions. The old (read ancient) pre-processor in part compensated for this shortfall with enough macros but 64 bit MASM was pointed at people who actually know how to write code of that type.

Now this comes down to using the documentation to get the argument count and type right. Hardware is never gracious, it expects the right data to be pointed at it and will do horrible things if you get it wrong. While the old "Blue Screen Of Death" had a reputation, a real[tm] phukup was the instant "black screen of death", even in protected mode 32 and 64 bit code. At least you knew something was wrong when it happened.

The old rule still applies here, RTFM, understand it and pass the correct data/arguments to the CPU.  :rolleyes:

NoCforMe

Quote from: hutch-- on September 12, 2022, 11:50:59 AM
The old rule still applies here, RTFM, understand it and pass the correct data/arguments to the CPU.  :rolleyes:

Yes, agree 1003% (especially the RTFM part*). Just one quibble, though: we're miles (kilometers) away from the actual CPU here, what with HAL** and all (unless you do something really gross to the FPU and it pukes all over you) ...

* In a previous life I was a tech writer who wrote computer manuals. Good ones, I was told by my customers, unlike the other shit out there.
** "Hardware abstraction layer" (whatever the hell that is)
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: hutch-- on September 12, 2022, 11:50:59 AMthe traditional "invoke" notation in 32 bit MASM had arg count checking which helped a lot of people catch errors in their code. Traditionally, assemblers never needed such things (real men[tm] etc ....)

Real Men[tm] know how to write macros that are understood even by ML64:

mov hEdit, rv(CreateWindowEx, 0, Chr$("RichEdit20A"), NULL, reStyle, 0, 0, 1, 1, hWnd, ID_EDIT, wcx.hInstance) ; one less
mov hEdit, rv(CreateWindowEx, 0, Chr$("RichEdit20A"), NULL, reStyle, 0, 0, 1, 1, hWnd, ID_EDIT, wcx.hInstance, NULL)
mov hEdit, rv(CreateWindowEx, 0, Chr$("RichEdit20A"), NULL, reStyle, 0, 0, 1, 1, hWnd, ID_EDIT, wcx.hInstance, NULL, 123) ; one more


*** Assemble using ml64  ***
Assembling: tmp_file.asm
** 64-bit assembly **
## line 32: not enough arguments for CreateWindowExA ##
## line 34: too many arguments for CreateWindowExA ##

daydreamer

the advantage of call vs invoke when no arguments proc is invoke sometimes need you define proto procname to work,but call doesnt

my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

NoCforMe

The disadvantage of call vs. invoke is that although you don't need to define a prototype, your chances of screwing up the number or order of arguments using push-push-call are much higher (and won't be caught by the assembler but will cause mysterious run-time errors).

But Real Men™...
Assembly language programming should be fun. That's why I do it.

daydreamer

Quote from: daydreamer on September 12, 2022, 06:46:16 PM
the advantage of call vs invoke when no arguments proc is invoke sometimes need you define proto procname to work,but call doesnt
makes me wonder if this will work?

proc number1
LOCAL x,y,z:DWORD
mov x,100
mov y,200
mov z,65
call procthatneeds3argument
ret ;???? maybe need some adjustment to not crash after the above trick?
endp number1

;locals are placed on stack in 32bit assembly,will the called proc use x,y,z instead of 3 pushed arguments?
would be useful if called proc uses local array?
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

hutch--

 :biggrin:

> Real Men[tm] know how to write macros that are understood even by ML64:

Yeah yeah, but at the cost of massive overhead when at last count, there are over 15 thousand API functions. The old rule applies, get the argument count right and pass the correct data.

jj2007

Quote from: hutch-- on September 12, 2022, 08:48:10 PMat the cost of massive overhead when at last count, there are over 15 thousand API functions

Slightly less massive than Windows.inc

hutch--

 :biggrin:

I think you know this response, REAL MEN[tm] don't need someone to hold their hot little hand when if they stuff it up, they generally know why.  :tongue:

jj2007

The jinvoke (the one that teaches ML64 to count and check arguments) is 458 lines long (and that's only a small part of the JBasic 64-/32-bit dual assembly framework). Guess what? Nobody held my hot little hand when I coded it.