This question have problay already been posted here but i tried search but found nothing!
is there way to change invoke to call i masm? like tasm use, been using masm for while now but like old tasm "call" syntax instead!
There are many people who prefer call over invoke. For me, either is okay, but to answer your question. Push the parameters of an invoke statement, one at a time, in reverse order and then change invoke to call. That is all it takes.
invoke LoadString, hInst, Eax, Offset szWork, LengthOf szWork
becomes;
Push LengthOf szWork ; Size of our buffer
Push Offset szWork ; Buffer to load string into
Push Eax ; Resource #
Push hInst ; Instance
Call LoadString ; Load the tip from STRINGTABLE
Hope this answers your question,
Paul
BTW: Yo further amplify on what I have said, invoke is recommended over call because it is a macro that actually does the same thing but has the further advantage that the assembler can match the invoke with a proto statement to make sure you have all the parameters correct and correctly stated. With the call statement it is too easy to make errors that are almost impossible to debug.
Paul
If you are making a series of calls in the same procedure it can be considered a cheap optimization if the values pushed onto the stack are already in registers. I've used this doing a number of back to back bitblts.
And if you need to pass the address of a procedure parameter or local variable (from within a procedure), you need to use ADDR instead of OFFSET for invoke, and LEA for the call method. For example:
lea eax, parm1
push eax
lea eax, local1
push eax
...
call ---
Of course that type of optimization only is effective if you are making more than one call to the same procedure, otherwise the added overhead of loading the registers will slow down your code. Once the data is in the registers thinks go faster on successive calls, just make sure you make use of the 'uses' option in the proc statement or you will be asking for trouble.
Paul
LAS3R,
Which TASM call syntax are you talking about?
The simple CALL which takes only a destination address and represents exactly one x86 instruction? MASM has the simple call.
Or the extended CALL syntax which allows parameters similarly to INVOKE in MASM? In MASM, the extended call syntax exists only as INVOKE.
Quote from: pbrennick on February 12, 2005, 09:10:54 PM
Of course that type of optimization only is effective if you are making more than one call to the same procedure, otherwise the added overhead of loading the registers will slow down your code. Once the data is in the registers thinks go faster on successive calls, just make sure you make use of the 'uses' option in the proc statement or you will be asking for trouble.
Paul
It seems to work without the
USES directive. Its handy when the same parameters are reused alot to push them onto stack along with those that change. The registers save having to push from memory to memory and the routine cleans the stack with Call ___, Call ___, Call ___, .........
Actually, that is not correct if you are using ebx, esi or edi. They must always be preserved. Either by using the 'uses' option or push and pop instructions at the start and end of the procedure. Anything else is asking for disaster. With such a limitted amount of registters you are bound to need to use on e or more of them.
Paul
I don't play with ebx, esi and edi when the stack is changing or have local variables. The optimization is solely from pushing constants that are in registers onto the stack before they get trashed by Windows' API calls.
That's good, other's follow our lead, that is why I felt I needed to clarify. I am not finding fault with your methods. I just want the newbies to know the rules they need to follow in order to generate safe code.
Paul
I agree. I never assume any registers are preserved once the code flow is handed over to windows.
Alloy,
It is probably worth disassembling what you write to see what TASM did with it. It will always reduce down to opcodes so it is better to undestand what the opcode production looks like than rely on a higher level procedure calling technique.
what i meant was, i want same syntax as invoke but instead of "invoke whatever, push eax, push ebx, push edx" as example , i would be able use "call whatever, push eax, push ebx, push edx" , i know it's not tasm but just replace invoke to call!
so i mean:
call whatever, push eax, push ebx, push edx
and not, already know this one but like invoke beacuse got all on same line, but don't like invoke, want call :)
push edx
push ebx
push eax
call whatever
is it possible to change?
I already answered this question.
Paul
call me dumb or whatever but i don't see answer hehe, all i understand so far is that u explained that invoke, whatever, ebx, eax, ecx is same as
push ecx
push eax
push ebx
call whatever
i like invoke more beacuse i can put all on same line instead of 4 lines as above, wich in end make code very big read and hard understand
but found patch that change masm syntax from invoke, whatever, ebx, eax, ecx to just whatever, ebx, eax, ecx , and that's not really what i want either hehe, just want old nice call "word" back but still same invoke function with addr instead of offset such!
i think i explained bit bad in my first post, hope this one is better!
LAS3R,
The difference betweem addr and offset is addr is using in local declarations (within the procedure) and offset is used for global declarations.
At this point I really don't have any idea WTF you want.
Paul
In TASM you can simply say
Call Message_Box,param1,param2,eax,ebx,offset my_string,[edx],[esi.structure.item]
and so on ... basically just like invoke in MASM but using Call instead (as a keyword)
The call in TASM is better than invoke in MASM because you can use variables and structures defined after the call statement. This is because call statement is internal to TASM and support multiple passes unlike a macro like invoke.
I guess that is what he has saw somewhere in some TASM source code and now he is begging for that.
But mASM is using invoke and that is that... he somehow has a problem understanding this ;)
paul lol , calm down :P
all i want is change word invoke to call in masm, somehow ,either by hex editing masm or changing inc file what ever need to do, so anyone know how i shall do it???
:U
Just use invoke and stop driving us nuts. I for one am done with this thread.
Paul
lol, np using invoke!
just wanted know if was possible :U
LAS3R,
Glad to hear it. Some questions are hard for me to relate to, I have been around longer than most everybody here and sometimes it is hard to view a problem from the eyes of a beginner. I am glad you are being good natured in the face of my gruffness.
Paul
np mate hehe, infact u should have medallion for even trying to understand what i asked, not sure i really understood now that i read it myself :green2
This invoke/call MACRO maybe useful in future,
because this
http://nosub.win32asmcommunity.net/viewtopic.php?t=20505&highlight=ml64
Kestrel,
You are a little behind the times, after Manhattan shared the info around, enough people have passed feedback to Microsoft to get this result.
http://lab.msdn.microsoft.com/ProductFeedback/viewfeedback.aspx?feedbackid=db4193d3-14fa-49dc-84e5-f06089b7e496
That's scary not seeing that Microsoft knows the low level advantages and uses of their own tools.
They were evidently misinformed and this reversal is the result of what I have recently viewed as a willingness to listen to others and follow their suggestions. As a professional with a stubborn pride in my own work, I know how difficult this can be. Even though I have always had a high level of respect for Microsoft and its people, this has only caused it to reach a new plateau and I feel the need to vocalize it. I also would like to thank Manhattan for looking out for us, and of course, Hutch, what can I say. Most if not all of the success of the rebirth of assembly, a language I fell in love with before any other language was available is something Hutch is directly responsible for. <NOT Betov> And all the user base has gone to make a very positive reinforcement for everything Hutch has done. Thanks to you ALL!
Paul
<I probably should move this to the soap box, sorry, I want to make sure it gets seen.>
LAS3R:
You could make a macro. Check the source code in \masm32\examples\EXAMPLE5\SCALL
Or you could make an app that searches for any call's and replaces them with invoke's, before getting ml to assemble it.
LAS3R,
Even though it is difficult to use the word 'call' to replace 'invoke', you can use 'calll'
.386
.model flat, stdcall
option casemap:none
;
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
;
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
;
calll equ invoke
;
.data
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
caption db 'Hello World!', 0
message db 'Hello World!', 0
;
.code
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
start:
calll MessageBox, 0, ADDR message, ADDR caption, MB_OK
calll ExitProcess, 0
end start
Unfortunately, in masm, reserved words are case insensitive or there would be a better solution.
Paul
I don't really understand why anyone would want to do this, but it is possible...
.386
.model flat, stdcall
option casemap:none
;
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
;
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
;
option nokeyword:<call>
call equ invoke
;
.data
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
caption db 'Hello World!', 0
message db 'Hello World!', 0
;
.code
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
start:
call MessageBox, 0, ADDR message, ADDR caption, MB_OK
call ExitProcess, 0
end start
While some of the techniques propsed here are very interesting, at the bottom line, if you want to use TASM CALL syntax, write it in TASM. If you want MASM functionality, write it in MASM using INVOKE. TASM is barely supported these days and its only the old timers who know how to use it so there is little point in wasting time trying to emulate an out of date assembler.
MichaelW,
One reason I could see people doing what you have shown is to make a debug output to console version of call and easily switch it off to do
the non-debug version at another time. Makes it rather easy and nice to do call interception.
Of course, doing the same with invoke is what I could do. :)
If you have several calls it is possible to replace them with a jump. For example you want to post a message to several controls :
The syntax with invoke is :
invoke PostMessage,hWnd1,EM_LIMTTEXT,256,0
invoke PostMessage,hWnd2,EM_LIMTTEXT,256,0
invoke PostMessage,hWnd3,EM_LIMTTEXT,256,0
It could be replaced with
push 0
push 256
push EM_LIMITTEXT
push hWnd3
push OFFSET Label_1
push 0
push 256
push EM_LIMITTEXT
push hWnd2
push OFFSET PostMessage
push 0
push 256
push EM_LIMITTEXT
push hWnd1
push OFFSET PostMessage
jmp PostMessage
Label_1 :
It is more difficult than using INVOKE but you don't use 2 calls !
I think it is quickyer and more compact than INVOKE.
What do you think ?
:eek
Kenavo
Philippe RIO
Intel processors LIKE you to use paired call/return.
Using this method screws up prediction, so although there appears to be less work, I'm not sure of how much of a speed-up it will actually give.
Just a random thought.
Quote from: Tedd on March 01, 2005, 11:40:50 AM
Using this method screws up prediction
What is prediction?
Certain operations use prediction to determine how to fill the cache. For example a Jcc backwards will normally be taken so the branch prediction circuitry will fill the cache following the Jcc with instructions from the point where it jumps to. A forward Jcc is not normally taken so it fills the cache with instructions immediately after the opcode. In the P4 you can affect the prediction algorithm with the branch hint opcode modifiers or using the hint.xxxx directive in GoAsm...
2Eh - hint that the branch will not occur most of the time.
3Eh - hint that the branch will occur most of the time.
GoAsm only...
CMP EDX,EAX
hint.branch JZ >L1
;
L1:
Speed penalties for missed cache hits can be substantial, stalling the processor while the cache is being emptied and refilled.
Quote from: Tedd on March 01, 2005, 11:40:50 AM
Intel processors LIKE you to use paired call/return.
Using this method screws up prediction, so although there appears to be less work, I'm not sure of how much of a speed-up it will actually give.
Just a random thought.
I'm not sure branch prediction would make a difference here.
This, however, is one of those "assembly language horrors" that has made assembly language famous for being difficult to read, maintain, and debug. For the two call operations it saves, you would be hard-pressed to convince me that the time savings are worth the few cycles saved.
Also note: this trick is going to screw up many debuggers that track things like call stacks.
Finally, don't forget that this scheme consumes more stack space than three sequential calls. Usually this isn't important, but sometimes it can be (e.g., in device drivers and ISRs).
It *is* a neat hack, but it definitely falls into the category of "being tricky for tricky's sake" and this is exactly the kind of code that the following programmer who works on the program *doesn't* want to see.
Cheers,
Randy Hyde
I don't think like you. But every one is free...
You can save more than 2 calls. Imagine a call to wsprintf followed by a call to MessageBox.
In the wsprintf call you initialize a local data which is used whith MessageBox. The syntax is :
push MB_OK
push OFFSET szCaption
lea eax,szLocalResult
push eax
push hWnd
push OFFSET Label_1
push dwData
push OFFSET szFmt
push eax
push OFFSET MessageBox
jmp wsprintf
Label_1 :
add esp,12
Ok this method is longer than INVOKE, but it is ASM language not a C like...
If I want to write in C I use Visual Studio and if I want to write in asm I use MASM.
With the "high level" extension (why "high level" ?) it is not easy to optimize our program.
If I use asm it's to have quick and small programs.
It always is longer to develop in ASM than in C or PASCAL.
This long time must also be used for optimization.
An other avantage of this method is for reverse engeenering.
It is more difficult to dissasembly these programs.
Thanks for everyone who take some time to give me an answer, even if what I think is different, very different.
Kenavo
IDCat,
I respect your right to have your own opinion but atsome point you need to listen to the voice of experience. After many years of programming, that which causes problems gets left behind and while us 'oldtimers' were climbing the ladder, believe me, we tried it all. You will see as time goes by. In the meantime, have fun.
Paul
I understand and think you are right.
Thanks
I certainly agree with Paul here, we have all done our share of code designs that the processor barely struggles to make sense of but if you want another human being to be able to maintain it some time down the track, you would risk him coming after you with an axe or as Mirno put it, a desire to kneecap you for having done so.
The design you proposed is clever and it is worth understanding how things like tis are done but putting it into production code is a surefire method for you to remove your name from the code and leave the country before the next guy finds you. :green2
You know I have seen some pretty neat coding schemes, some that saved 1000 clocks !!! But if you think about it well what does that represent. On a 1Ghz processor, it represents 1/1000000 of a second. So if you managed to save that 1000 clocks every second it would be 11.57 days before you would accumulate 1 second of time savings. However, from a standpoint of clarity, come back to your own code after not touching it for 2 years and see how much of your valuable time you waste tracing through the unintelligible code. I will always take the clearer source over the questionable optimizations. And BTW, last I checked the Intel manuals CALL/RET were actual opcodes and therefore are assembly, not C or anything else.
IDCat,
I like your method, but the basics are not new. It is an old trick to PUSH variables onto the stack when they are available, but not immediately needed. I believe that compiliers do that all the time. Does the CPU really give a damn if a RET does not have a matching CALL? And if the "chained" return addresses are within a busy inner loop, the saved time could mean something. As for obfuscation, be sure to document what you are doing. You are not engaging in anything weird that is difficult to understand. Ratch
donkey, the point is that if programs were written well and fully optimized, we could run about ten programs simultaneously without stalling on a 100Mhz.
I like Donkey's words, they remind me of something I used to say to individuals learning how toprogram, 'What difference does it make whether you get the answer in picoseconds or nanoseconds as long as the answer is correct, because for all intents and purposes; we cannot tell the difference.'
Paul
pbrennick,
Quote'What difference does it make whether you get the answer in picoseconds or nanoseconds as long as the answer is correct, because for all intents and purposes; we cannot tell the difference.'
Those pico's and nano's add up when the load is heavy. My Windows boots up correctly, but it sure takes a long time to do so. I sure can tell the difference using MATHCAD between my old slow and new fast machine. Ratch
Running on my P3, this shows only a few cycles of variation from run to run, and the "horror" code is no faster than the sensible code.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.586 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
include timers.asm
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
LOOPCOUNT EQU 5000000
counter_begin LOOPCOUNT, HIGH_PRIORITY_CLASS
invoke PostMessage,0,EM_LIMITTEXT,256,0
invoke PostMessage,0,EM_LIMITTEXT,256,0
invoke PostMessage,0,EM_LIMITTEXT,256,0
counter_end
print ustr$(eax)
print chr$(" cycles",13,10)
counter_begin LOOPCOUNT, HIGH_PRIORITY_CLASS
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET Label_1
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET PostMessage
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET PostMessage
jmp PostMessage
Label_1:
counter_end
print ustr$(eax)
print chr$(" cycles",13,10)
mov eax,input(13,10,"Press enter to exit...")
exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
2151 cycles
2152 cycles
Hi MichaelW,
After three consecutive tests on my PIV, I got these results:
3244 cycles
3301 cycles
3284 cycles
3382 cycles
3225 cycles
3291 cycles
Ok, You all are right.
I thought that I could win more cycles. I knew that this code would be hard to modify... later.
It was a test.
And if the push were replaced by the "mov ESP+XXX,data". Would this code be quick ?
I have to search an other method.
Thanks for your help. Sincerely
Kenavo
In this case, optimizing the instructions that call the API code will make no significant difference because almost all of the execution time (~99.3%) is spent in the API code.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.586 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
include timers.asm
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
LOOPCOUNT EQU 5000000
counter_begin LOOPCOUNT, HIGH_PRIORITY_CLASS
invoke PostMessage,0,EM_LIMITTEXT,256,0
invoke PostMessage,0,EM_LIMITTEXT,256,0
invoke PostMessage,0,EM_LIMITTEXT,256,0
counter_end
print ustr$(eax)
print chr$(" cycles",13,10)
counter_begin LOOPCOUNT, HIGH_PRIORITY_CLASS
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET Label_1
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET PostMessage
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET PostMessage
jmp PostMessage
Label_1:
counter_end
print ustr$(eax)
print chr$(" cycles",13,10)
counter_begin LOOPCOUNT, HIGH_PRIORITY_CLASS
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET Label_1
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET PostMessage
push 0
push 256
push EM_LIMITTEXT
push 0
push OFFSET PostMessage
;jmp PostMessage
REPEAT 15
pop eax
ENDM
counter_end
print ustr$(eax)
print chr$(" cycles",13,10)
mov eax,input(13,10,"Press enter to exit...")
exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
2141 cycles
2144 cycles
30 cycles
But in the case that the majority is my own code, would matter if I use movs instead of pushes???, altought that will require take care of the movement of memory to memory that push can do and mov cant.