I'm not really sure who figured this out, me or Bitrake(probly Bitrake), but I now know how to get invoke to construct an indirect call and can post proof. Fodder can be happy now:alright:



P4 TYPEDEF proto :DWORD, :DWORD, :DWORD, :DWORD

PP4 TYPEDEF PTR P4

.data?
myString SSTRING<>
.code
lea eax, dword ptr sstring; Bitrake used
mov myString.handle, eax; jump dd OFFSET jumphere
; this works good for my stuff
invoke PP4 ptr [myString], ADDR myString, 1,4096, 8192
;now for the dissassembly of the invoke:
00401123 FF 15 A4 43 40 00 call dword ptr [myString (4043A4h)]


Yep, thats right, the first hex code is FF. FF is the code for call indirect. It might say myString, but I'm really calling the address at myString. Thanks for the help, I sure learned something. I hope otheres learn for this, esp. the OOP framework makers.
:)

And here I always hated TYPEDEFs.
Posted on 2002-04-18 07:06:54 by ThoughtCriminal
Sucks you can't do it without typedefs, not very elegant imo :/.
Posted on 2002-04-18 08:10:13 by f0dder
f0dder, can you dream up some elegant syntax for me to keep in mind for my assembler? Keep in mind that I'd like to support similar if not greater checking/options than MASM: language (passing method), parameter type/count checking, direct/indirect, etc.
Posted on 2002-04-18 08:24:29 by bitRAKE
Assuming you have a proto for messagebox, something like the
following would be okay imo:



invoke (type ExitProcess) ptr [myvariable], 0 ; indirect
invoke (type ExitProcess) ptr mylabel, 0 ; normal call

I think it's pretty straightforward to read, and I don't think it should
be too *****y to code. A more elegant syntax might be possible,
but this would suffice :).
Posted on 2002-04-18 08:36:55 by f0dder
It may be inelegant, but the results for two TYPEDEFS are worth it.

I was able to get ride of worse stuff like:


sstring proc saddr:DWORD, ftype:DWORD, src:DWORD, dst:DWORD
handle LABEL DWORD


and now I have what I wanted, a simple way to combine data and functions in structures. I was able to get rid of my nested STRUCT and just use one:


SSTRING STRUC
hdl dd <?>
len dd <?>
sta dd <?>
lst dd <?>
rw1 dd <?>
rw2 dd <?>
nul dd 0
SSTRING ENDS
[\CODE]

hdl is the address in this case.

being kicked out of office again
Posted on 2002-04-18 09:22:44 by ThoughtCriminal
Ok, I'm wonderfully lost... :) Not unusual for me...

Can someone explain (if you'd be so kind) the use of indirect vs direct call and possibly a (compilable) example of both...

I know it's alot, but I really want to understand this topic better...

Sliver
Posted on 2002-04-18 12:55:43 by Sliver
I'll try to whip up a little example, but basically an indirect call allows you to change the execution path of the code without using self-modifying code. Whereas a direct call is static - you can't change it without modifying the code. Take my example above, you could do: mov , OFFSET MyNewRoutine -- to change all the indirect invokes using to point to a different algorithm.
Posted on 2002-04-18 13:06:43 by bitRAKE
bitRAKE,

Would an example be:

Call different routines for different processors,
especially different FPU's. In other words direct
the program to a diferent opitmized procedure
depending on the user's CPU?

farrier
Posted on 2002-04-18 15:47:31 by farrier
farrier, that is a good example. Again, I like to state this is done at runtime without the hassles of SMC (self-modifying code). Create a table of routines for each processor, test for the processor type at the start, and copy the appropriate table over the active pointers -- now all the indirect calls point to the routines optimized for the current processor.
Posted on 2002-04-18 19:18:57 by bitRAKE
Sliver, I'll try to explain what I'm doing and why the indirect call makes it better.



First I make my STRUC:
SSTRING STRUC
hdl dd <?>
len dd <?>
sta dd <?>
st dd <?>
rw1 dd <?>
rw2 dd <?>
nul dd 0
SSTRING ENDS

mystring SSTRING<>

Now mystring and hdl ARE BOTH THE SAME ADDRESS
let just say:

address of mystring = 1000 value = [0]
address of hdl = 1000 value = [0]
address of len = 1004 value = [0]
address of sta = 1008 value = [0]
.
etc.

;There is probly a better way to do this, but this is quick to type:

lea eax, dword ptr myfunction; address of myfunction is 2000
mov dword ptr , eax; put adress of myfunction into
the memory location of mystring
now:
address of mystring = 1000 value = [2000]
address of hdl = 1000 value = [0]
address of len = 1004 value = [0]
address of sta = 1008 value = [0]
.
etc.

The first address of the structure mystring contains the address of my function. Now I can call myfunction using mystring as a pointer. The original purpose of this thread was how to avoid having to do this:

push (blah)
push (blah)
push (blah)
push (blah)
call dword ptr

instead:

invoke PP4 ptr ,.......

The typedefs:
P4 TYPEDEF proto :DWORD, :DWORD, :DWORD, :DWORD
PP4 TYPEDEF PTR P4

Are needed to explain to invoke that we want to the address pointed to buy address given. I came here llooking for a way to use invoke, because it is cleaner than push,push,push,call.

Anyway, this is some of what you can do with this stuff:

You can keep you data and functions together in a structure, basically a class.

You can make multiple instances:

mystring SSTRING<>
foo SSTRING<>

each will hold a pointer to myfunction, and each can call the funtion:

invoke PP4 ptr ,.......
invoke PP4 ptr ,.......

could have multiple function address pointers in a structure:

invoke PP4 ptr [4],.......
invoke PP4 ptr ,.......

Lot more stuff you could do. It's kinda complicated, yet horribly simple. I hope I've explained well enough.
Posted on 2002-04-19 00:31:15 by ThoughtCriminal
I do appreciate people taking the time to go over this area with me as it is quite fascinating :)

I still would like a "real world" example to demonstrate it's use...

For as of right now I can't see for the life of me why it's useful to have a structure contains the address of a function (although if you can use this for function overloading -- something I understand a bit better from C++ well then that's something)...

So a simple example would be appreciated... I tend to learn by example :) Trying to not be a "give me source, give me give me give me..." kinda person... So if and when any of you have time.. :)

Sliver
Posted on 2002-04-19 01:16:19 by Sliver
Look at our OOP kit in MASM v7 (\masm32\OOP\)

Its 98% the same stuff being discussed.

We use structures to indicate that certain fields of memory (that the stucture is applied against) is to be interpreted as function offsets with a proto type to be used with it.

Thus when calling a "class method", the name 'method' is acutally an entry in a structure, that was aply named by 'class'.

Ie) String.isEmtpy

would have a structure like:

String STRUCT ; << Class Name
...
...
isEmpty PTR ProtoVal ? ; << Method Name
...
String ENDS

The advantange is, if i want to re-use the string code, and add better features, i can 'inherit' this as a base class:


BetterString STRUCT
String <> ; << insert old struct (inherit)
NewMethods PTR ProtoVal2 ?
..
BetterString ENDS


Now all that was needed is to code for the new methods, all the original stuff is done simply by including it in the BetterString class structure.

Where the converstation of this thread comes in is: What if i wanted the "isEmpty" method in newly defined BetterString (which inherites String) to now have a differenty way of evaluating. As it stands, the inherited method, still points to the function written for just the "String" class in mind.

It would suck to have to write a IsEmpty2 method just to get around the fact that IsEmpty wont work on the inherited class.

However, since all method functions are pointed to by offsets (indicated in struct entries), we can simply change the pointer DATA in memory to another fucntion, writted specifically to evaluate if the BetterString is empty.

This is easy to do:

mov BetterString.IsEmpy, offset NewIsEmtpy


In practice, if you know you have a sting type (String or BetterString), you can count on the fact that they both share a common method "IsEmpty". They appear like 'black boxes' to the coder, as you dont need to know 'how' they are working, just that if you call one or the other, you will get the correct results for the type of class.

This is because behind the sceens, the pointers have been adjusted to proper method fucntions. All the while both presenting a common name (.IsEmpty) for the fucntion.


This is why we use such methods discussed in this thread.
However, looking back that might have been alot to grasp. I think the CPU type example is more clear use of the same function pointing.

In this case, in code you could simply write:

CPU Struct
setType PTR proto. ...
doLotsOfMath PTR proto ..
CPU ENDS

.code

; Find CPU type, get this *RUN TIME INFO* into eax

invoke CPU.setTyp, eax

; this points to a function that will recieve eax, and evaluate
; it.
;
; .if( eax has MMX suport) then 'mov CPU.doLotOfMath, offset MMX_MathFucntion'
; .else 'mov CPU.doLotsOfMath, offset Standard_MathFunction'

..
..
Later in the code its time to do "Lots of Math"

invoke CPU.LotsOfMath, eax, ecx


The advantage is simple. The programmer only needs to pass runtime set up info to the type evaluation routine (setType), and then it can blindly know that when its time to do "lots of math" the function pointed to will be the best choice for the platform currently running the program!! No extra thought needed. If there is MMX suport, the invoke will be pointing to the MMX verision of math, if not, the same invoke will point to a standard form.

Anywho, hope this helps you see things better..
:alright:
NaN
Posted on 2002-04-19 02:17:44 by NaN
Another example. Assume you have optimized code for a range of
processors. Instead of doing



.if [cpu] == cpu_pplain
; a bunch of calls here
.elseif [cpu] == cpu_pmmx
; similar bunch of calls here, just to pmmx functions
.elseif [cpu] == cpu_ppro
; you get the idea? :)
.elseif [cpu] == cpu_3dnow
; yadda yadda
.elseif [cpu] == cpu_cpu_sse2
; woop
.endif

every time you have CPU-specific calls to do, you can do this large
if/elseif block at program startup, select a function pointer table by
CPU, and copy over the "current" function pointer table. Runtime,
whenever you have CPU-specific code, you just do a single indirect
call, and the function pointer takes you to the CPU specific code.

DLL imports work the same way. When you "call MessageBox",
you end up at the following:


MessageBox: jmp dword ptr [__imp__MessageBoxA]

the __imp__MessageBoxA pointer is set up for you by the windows
DLL loader.

Indirection is quite useful, huh?
Posted on 2002-04-19 07:36:36 by f0dder
A loader that solves the fixups (even at runtime) would be even better ;)
In my compiler I noticed sometimes really dramatic CPU cycles differences removing this (unnecessary, if one doesn't use standard tools) indirection. Reading from the RAM can be very slow at times.. and is not really necessary.
Posted on 2002-04-19 07:55:44 by Maverick
Maverick, you would not want to use it this that way in small loop reentering a function. Put the address in a register in that case.
Of course you already know that.

Being able to invoke with indirection is cosmetic. Keeps the code cleaner, and more uniform.
Posted on 2002-04-19 09:05:08 by ThoughtCriminal
CALL or JMP directly to the location.. it's faster than in modern CPU's.

Take note (helped from an own loader) of all the locations (relative or absolute) that have to be fixup.. so you can relocate those referiments at runtime, whenver you wish.

These solutions are damn simple, but look complex and advanced only because most assembly coders still use in the year 2002 the development tools of the year 1946.

That's the problem.
Posted on 2002-04-19 18:18:08 by Maverick
Maverick, perhaps you will give us some 2002 development tools?
Posted on 2002-04-19 21:11:03 by f0dder

Maverick, perhaps you will give us some 2002 development tools?
The work of a life.. why should I?

I'm not sure that would make sense.. would you realase to the public domain all the stuff you earn money with?

It costed me some doubts even to release something simple and basic as the profiler.. still now it's not 100% clear to me what's releasable and what should not be.

But if my suggestions and advice sound annoying, I'll stop posting them. :)
Posted on 2002-04-20 04:37:40 by Maverick