Valhalla Legends Forums Archive | C/C++ Programming | Calling API from assembler?

AuthorMessageTime
BreW
So, I'm wondering. How do I call an API from the inline assembler? I've tried just about everything. This is what it's looking like so far:
[code]
inline void MsgBox(HWND hwnd, LPCTSTR text, LPCTSTR title, int otherstuff);
char asdfg[] = "hello";
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
__asm {
push MB_OK
push 0
push offset asdfg
push 0
call MsgBox
add esp, 16
}
return 0;
}

inline void MsgBox(HWND hwnd, LPCTSTR text, LPCTSTR title, int otherstuff) {
MessageBox(hwnd, text, title, otherstuff);
}
[/code]

But I absolutely hate having to make a wrapper function for calling an API. What is the syntax for calling an API?
Like.... call dword ptr [apiname@ordinalname]? I've tried that, but it didn't work. MSDN has no documentation on this. I'm clueless.
October 3, 2007, 1:19 AM
Quarantine
Perhaps there is a calling-convention confusion. Maybe using the naked keyword would help some.

/stabinthedark
October 3, 2007, 1:40 AM
warz
http://en.wikipedia.org/wiki/X86_calling_conventions

That explains everything. It's a useful link. I referenced it a lot back when I was dealing with game hacks.
October 3, 2007, 1:42 AM
BreW
Aha, i've got it! Thanks, guys! It's just call dword ptr [MessageBox].
It also pops my stack for me, I don't have to add 16 to esp manually. Say, msdn said that you're supposed to pop it to ebx 4 times, but is adding 16 to esp a good idea? (will it mess something up in the long run?) Doing that seems like i'm just making a workaround to stop the _chkesp error box and nothing more.
October 3, 2007, 2:11 AM
Camel
[quote author=brew link=topic=17078.msg173477#msg173477 date=1191377468]
Aha, i've got it! Thanks, guys! It's just call dword ptr [MessageBox].
It also pops my stack for me, I don't have to add 16 to esp manually. Say, msdn said that it'd be a good idea to pop it to ebx 4 times, but is adding 16 to esp a good idea? (will it mess something up in the long run?)
[/quote]

The last time I looked at x86 asm, it added to esp after calls. Sometimes, the compiler will let esp float around to save instructions, which is pretty annoying.
October 3, 2007, 2:14 AM
warz
Look into different calling conventions. Some clean the stack for you, and some require the caller to clean the stack.
October 3, 2007, 2:26 AM
BreW
About that link you posted, warz,
[quote]
stdcall
The stdcall[1] calling convention is a variation on the pascal calling convention in which parameters are passed right-to-left. Registers EAX, ECX, and EDX are preserved for use within the function. Return values are stored in the EAX register.

Stdcall is the standard calling convention for the Microsoft WIN32 API.

[/quote]

[code]
push MB_OK
push 0
push offset hello
push 0
call dword ptr [MessageBox]
[/code]

uh..
October 3, 2007, 2:33 AM
warz
I'm not sure what you're trying to point out.
October 3, 2007, 4:11 PM
BreW
I push the parameters in the reverse order. stdcall pushes them in order. That wiki article says that stdcall is the standard calling convention for win32 api. My code is working fine, though.
EDIT** Nevermind, i just failed to read it properly. It's identical to _cdecl. For some reason I always thought stdcall gets the parameters in order.

Also, I'm wondering how come i can't use ret in WinMain? and where is the previous execution address stored? (before the call, where the program returns to after ret)

EDIT**
why does the following code crash when it calls sprintf? :/
[code]
mov eax, 2
cmp eax, 1
jne overmsgbox

push 212121h
push 0
push ebx
mov ebx, esp
add ebx, 8
push ebx
push 0
call dword ptr [MessageBox]
add esp, 4
jmp done
overmsgbox:
push esp
push offset format
push asdf
call dword ptr [sprintf]
push 0
push 0
push asdf
push 0
call dword ptr [MessageBox]
done:
xor eax, eax
[/code]
says it's an access violation.
October 3, 2007, 7:33 PM
devcode
Remove the "offset" from push offset format. Hi brew.
October 4, 2007, 2:05 AM
BreW
[quote author=devcode link=topic=17078.msg173516#msg173516 date=1191463505]
Remove the "offset" from push offset format. Hi brew.
[/quote]
Hi devcode. That doesn't work.
October 4, 2007, 2:47 AM
devcode
[quote author=brew link=topic=17078.msg173519#msg173519 date=1191466061]
[quote author=devcode link=topic=17078.msg173516#msg173516 date=1191463505]
Remove the "offset" from push offset format. Hi brew.
[/quote]
Hi devcode. That doesn't work.
[/quote]

Worked fine for me, but then again you didn't post your entire code, just a snippet of it.
October 4, 2007, 3:01 AM
Camel
Have you looked at the stack in a debugger? Does it match what you expect?
October 4, 2007, 6:29 PM
BreW
It does.
October 4, 2007, 9:46 PM
UserLoser
What is the point of doing this anyways?
October 4, 2007, 10:28 PM
BreW
I intend to eventually make a bot in asm. Now to find out how to access a struct's members...
October 4, 2007, 10:37 PM
Quarantine
whoas man a bot in asm! cool guy.

On a serious note:

What makes you think you can out-optimize the compiler? You'd be wasting your time to be honest.
There is little or no practicality in writing a Client in ASM.
October 5, 2007, 2:42 AM
Hell-Lord
[quote author=Warrior link=topic=17078.msg173556#msg173556 date=1191552156]
whoas man a bot in asm! cool guy.

On a serious note:

What makes you think you can out-optimize the compiler? You'd be wasting your time to be honest.
There is little or no practicality in writing a Client in ASM.
[/quote]

Indeed, why waste your own time writing a bot in ASM when a well written bot in even VB6 can do the job.
October 5, 2007, 2:57 AM
St0rm.iD
There's a HUGE amount you can learn by doing some work in assembler. With that said, it's unlikely you'll be able to write a bot in all ASM without killing yourself first.

And there's no practical reason to, either :)
October 5, 2007, 3:10 AM
Camel
For everything you could do in C that you do in asm, expect the following:
* The development time will be ten times longer than if you used C
* The code will run no more than 10% faster

If you're just trying to write a lightweight efficient bot, you're wasting your time. If you're trying to learn about computers and assembly, then it's a worthy cause.

No matter how complex a bot is (within reason), it always spends almost all of its time yielding to other threads.
October 5, 2007, 2:27 PM
BreW
It may be. I don't intend to have the entire thing in assembler..... (i'll use char string##[] for constant strings) but I will make most of it with asm. Of course it's not going to be practical, I'm just doing it because I would learn THAT much more. And trust me, it's not that hard. 95% of code for battle.net bots is calling win32 api. Even if it does only run 10% faster then with C, wouldn't it be worth it? The only problem would be the development time (and i have a lot of spare time)
@Warrior: Yeah, I can out optimize the compiler quite easily. IIRC in an older topic Yegg and I were discussing about how the MSVC6 compiler actually un-optimizes some code.
October 5, 2007, 7:29 PM
warz
[quote author=brew link=topic=17078.msg173596#msg173596 date=1191612564]@Warrior: Yeah, I can out optimize the compiler quite easily. IIRC in an older topic Yegg and I were discussing about how the MSVC6 compiler actually un-optimizes some code.[/quote]

You still use visual studio 6? Wow, guy, get with the times.
October 5, 2007, 7:54 PM
Quarantine
[quote author=Camel link=topic=17078.msg173589#msg173589 date=1191594477]
* The code will run no more than 10% faster
[/quote]

I think speed might actually be worse if he's not a wiz at ASM. Considering the heavy optimization that compilers do.
I'm pretty sure the whole "ASM is faster" myth is under ideal situations with expert programmers.
October 5, 2007, 8:06 PM
Camel
[quote author=Warrior link=topic=17078.msg173600#msg173600 date=1191614790]
[quote author=Camel link=topic=17078.msg173589#msg173589 date=1191594477]
* The code will run no more than 10% faster
[/quote]

I think speed might actually be worse if he's not a wiz at ASM. Considering the heavy optimization that compilers do.
I'm pretty sure the whole "ASM is faster" myth is under ideal situations with expert programmers.
[/quote]

The compiler doesn't always know what's best for each situation; it knows what it's trying to accomplish according to what each C instruction tells it to do. Heuristic optimizations are not nearly as effective as you might think. Sure, it'll usually pick up silly things like repetitive adds to the same register, but it can't look at a segment of code, figure out what the goal is, and re-write it entirely.

I would say 10% is the least amount of speed increase you would get, as long as the person writing the asm is familiar with every instruction and has at least a basic understanding of the tradeoffs of each one.
October 7, 2007, 8:04 AM
Win32
[quote]
It also pops my stack for me, I don't have to add 16 to esp manually. Say, msdn said that you're supposed to pop it to ebx 4 times,
but is adding 16 to esp a good idea? (will it mess something up in the long run?) Doing that seems like i'm just making a workaround to
stop the _chkesp error box and nothing more.
[/quote]
It depends on the calling convention, all conventions I know of require the callee to pop the parameters off the stack. The only
exception (I'm aware of) is those conventions that accept a dynamic number of arguments.

[quote]
why does the following code crash when it calls sprintf? :/
[/quote]
I'm too tired to bother trying to read that (might want to clean it), but I'd say it's something to do with the fact sprintf is a
cdecl (dynamic param count) call: You need to pop the given parameters, who knows what the function does (then again, you can just
walk through it with a disassembler)


[quote]
I intend to eventually make a bot in asm. Now to find out how to access a struct's members...
[/quote]
There's no reason you cannot use identifiers in asm.


[quote]
Indeed, why waste your own time writing a bot in ASM when a well written bot in even VB6 can do the job.
[/quote]
Naughty naughty.


--
Personally, Brew, I think you may want to spend some time reading into processor architecture. (Intel.com, "IA Manuals") and aquire
a little more knowledge before you attempt a task such as a chat client.

I've been working with assembly for several years now, it's an extremely enlightening learning experience, yet the practical applications
assembly has is minimal. Even so, most of my projects are written in a 40:60 Asm to C ratio, mainly due to I'm a performance nazi and have
enough experience to far outperform any compiler. Still, not even I would bother writing a whole chat client in pure assembly, the
amount of time you'll spend debugging (especially since you're a relative novice) is going to consume project time 10 fold.

At present, you are not going to be outperforming the compiler, not even Borland, so here's a few pointers for when working with Asm:

1. Do not use half-registers, (ie, WORD), there's a good possibility of a stall depending on how many write's are in flight.
2. Structure your code so a branch is only taken on the least-likely outcome.
3. Avoid branches whenever possible, if possible, substitute with conditional instructions (CMOV, SETCC, ect...)
4. Align loops on 16-byte boundaries (use the ALIGN directive)
5. If you plan to use a spin-wait loop (I presume you'll be multithreading), then insert a PAUSE instruction to avoid cache thrashing on exit.
6. Align data on their natural size boundaries (I'm sure you know this)
7. Use the addressing modes supported by the processor, instead of calculating address yourself (such as scaled index). LEA is your friend also.
8. MUL/DIV are two of the most expensive instructions, (~30 clocks (Processor-dependent)), use shifts (SHL/SHR/ect) when possible.
9. Serialize data access, perform write's in chunks instead of patterns like: read/read/write/read/write.

A few other things to keep in mind:

* EAX, ECX, EDX are considered volatile registers in C++, thus you are free to use them in your own assembly routines without corrupting
  the caller's state (important when mixing Asm and C).

* Try using the naked (__declspec(naked)) modifier when writing assembly routines, this enables (and requires) you to write the prologue and epilogue
  allowing you to also use EBP (Personally, I never use EBP for "stack frames", it's worthless) (Note: EBP _is_ volatile)

* If you use registers in your assembly routines other than EAX,EDX,ECX, you must preserve the caller's value of these register (this is the prologue/epilogue)


Anything else, feel free to ask.
October 25, 2007, 1:59 PM
Yegg
I thought I'd throw this in here because I feel it's pretty interesting. If anybody remembers or knows of the NBA Jam game for Sega Genesis from years and years ago, the entire game was written by one man entirely in assembly.
October 25, 2007, 4:07 PM
Quarantine
[quote author=Yegg link=topic=17078.msg174211#msg174211 date=1193328435]
I thought I'd throw this in here because I feel it's pretty interesting. If anybody remembers or knows of the NBA Jam game for Sega Genesis from years and years ago, the entire game was written by one man entirely in assembly.
[/quote]

I'm 100% sure that the Chatter Bots of today are infinitely more complex than anything ever conjured on the Sega Genesis.
October 25, 2007, 8:58 PM
Barabajagal
Hey man, don't dis the genesis. It gave us Zero Wing, and where would our bases be if not for that game? ;D
October 25, 2007, 10:34 PM
Myndfyr
[quote author=Andy link=topic=17078.msg174220#msg174220 date=1193351646]
Hey man, don't dis the genesis. It gave us Zero Wing, and where would our bases be if not for that game? ;D
[/quote]
Also Kid Chameleon and Altered Beast!
October 25, 2007, 10:37 PM
Barabajagal
Wasn't Sonic Adventure on the genesis, too? The one where the first level is "snowboarding" on the street chased by a giant Semi?
October 25, 2007, 10:44 PM
BreW
[quote author=Andy link=topic=17078.msg174220#msg174220 date=1193351646]
and where would our bases be if not for that game? ;D
[/quote]
They'd still be there, they just are not belong to us.
October 25, 2007, 11:54 PM
Quarantine
[quote author=Andy link=topic=17078.msg174220#msg174220 date=1193351646]
Hey man, don't dis the genesis. It gave us Zero Wing, and where would our bases be if not for that game? ;D
[/quote]

You're correct. If not for them, I would not know that the bases were belong to them.
October 26, 2007, 1:12 AM
squiggly
I wish I could troll as well as you guyz
October 26, 2007, 2:48 AM
Barabajagal
And then nobody would know who the Laziest Men on Mars were, so in effect, they owe their career to Genesis. Sega should tap that.
October 26, 2007, 2:58 AM
St0rm.iD
They should reissue Sega Genesis with Zero Wing soldered into the cartridge slot.
October 26, 2007, 1:54 PM
Kp
[quote author=Win32 link=topic=17078.msg174208#msg174208 date=1193320787]
Personally, I never use EBP for "stack frames", it's worthless) (Note: EBP _is_ volatile)
[/quote]

EBP is not volatile.  You must preserve the previous value.  You may get away with treating it as volatile if the caller happens not to need it after the call.  Stack frames are very nice for debugging, since it lets the debugger generate a proper callstack reliably.  If you do not use stack frames, the debugger is forced to guess, and that rarely turns out well.
October 27, 2007, 4:14 AM
Win32
[quote]
EBP is not volatile.  You must preserve the previous value.  You may get away with treating it as volatile if the caller happens not to need it after the call.  Stack frames are very nice for debugging, since it lets the debugger generate a proper callstack reliably.  If you do not use stack frames, the debugger is forced to guess, and that rarely turns out well.
[/quote]
Ofcourse EBP is volatile, unless stack frames are actually disabled (and I would presume in the thread starter's case it is, as it is for most). I think you're confused as to what volatile means; as you say "you must preserve the previous value", hence why EBP is considered volatile across function calls.
October 27, 2007, 5:43 AM
Kp
You are using volatile to mean two completely opposite things.  First, you say:

[quote author=Win32 link=topic=17078.msg174208#msg174208 date=1193320787]
* EAX, ECX, EDX are considered volatile registers in C++, thus you are free to use them in your own assembly routines without corrupting
  the caller's state (important when mixing Asm and C).
[/quote]The standard calling convention for C++ on Win32 x86 says that EAX, ECX, and EDX may not be preserved by the called function.  That is, you must not rely on their value being the same after the function call as it was before the function call.

Then, you say:
[quote author=Win32 link=topic=17078.msg174208#msg174208 date=1193320787](Note: EBP _is_ volatile)[/quote]

The caller normally expects ebp to be preserved across function calls, which is why it can be used as a stack frame pointer.

Finally, you say:[quote author=Win32 link=topic=17078.msg174250#msg174250 date=1193463825]
Ofcourse EBP is volatile, unless stack frames are actually disabled (and I would presume in the thread starter's case it is, as it is for most). I think you're confused as to what volatile means; as you say "you must preserve the previous value", hence why EBP is considered volatile across function calls.
[/quote]

So you are consistent in saying that "EBP is volatile."  You state in your first quote that EAX, ECX, and EDX are "volatile" and that you can change them freely.  However, in your third quote, you go on to agree that EBP must be preserved, which is the opposite of being able to change it freely.  So which is it?  Must EBP be preserved or can it be changed freely?  Is it volatile or not?
October 27, 2007, 9:20 PM
BreW
Just wondering, why wasn't ebx listed with win32's list of volatile registers? must it be preserved or something? I've been modifying ebx just like eax or ecx and i've had no errors.
October 27, 2007, 11:29 PM
devcode
[quote author=brew link=topic=17078.msg174262#msg174262 date=1193527754]
Just wondering, why wasn't ebx listed with win32's list of volatile registers? must it be preserved or something? I've been modifying ebx just like eax or ecx and i've had no errors.
[/quote]

gewgelz ftw

[MSDN]
When using __asm to write assembly language in C/C++ functions, you don't need to preserve the EAX, EBX, ECX, EDX, ESI, or EDI registers. In addition, by using EBX, ESI or EDI in inline assembly code, you force the compiler to save and restore those registers in the function prologue and epilogue.
[/MSDN]
October 27, 2007, 11:32 PM
Win32
[quote]
The standard calling convention for C++ on Win32 x86 says that EAX, ECX, and EDX may not be preserved by the called function.  That is, you must not rely on their value being the same after the function call as it was before the function call.
[/quote]
Yes, my bad on that behalf. Non-volatile*


[quote]
The caller normally expects ebp to be preserved across function calls, which is why it can be used as a stack frame pointer.
[/quote]
Hence why I said volatile, but I understand why you say because of my mistake in the previous :)


[quote]
Just wondering, why wasn't ebx listed with win32's list of volatile registers? must it be preserved or something? I've been modifying ebx just like eax or ecx and i've had no errors.
[/quote]
That would be just luck that no caller's have used EBX.

Volatile:
EBX, ESI, EDI, EBP (If using stack frames)

Non-volatile:
EAX ECX EDX


If you're writing a CPU-intensive routine that requires alot of frequently accessed data, you can also use ESP (Failure to preserve this, will just cause a myriad of problems :))

Too bad you can't use GS, as I found out the other day :(

I believe you also need to preserve some FPU flag register (if using the FPU), not sure exactly since I don't use floating-point math much.
October 28, 2007, 12:23 AM

Search