Yes, I abuse LOCAL and EBP, because LOCAL only works with ebp.
Gaze upong my abuse and be horrified and amazed:


OPTION PROLOGUE:NONE ;***Notice- no stack frame
OPTION EPILOGUE:NONE

align 16
Render PROC PUBLIC
LOCAL D3D[0]:_D3D
mov .EBP,ebp ;***just to be safe, save ebp
mov ebp,[D3Dvt] ;***Now set ebp to the address of the vtable

invoke [D3D].Clear,[g_pd3dDevice] ,0,0,D3DCLEAR_TARGET,0FF0000FFh,f1x0,0

invoke [D3D].BeginScene,[g_pd3dDevice]

invoke [D3D].SetStreamSource,[g_pd3dDevice],0,g_pVB,0,sizeof (CUSTOMVERTEX)
invoke [D3D].SetFVF,[g_pd3dDevice],D3DFVF_CUSTOMVERTEX
invoke [D3D].DrawPrimitive,[g_pd3dDevice],D3DPT_TRIANGLELIST,0,1

invoke [D3D].EndScene,[g_pd3dDevice]

invoke [D3D].Present,[g_pd3dDevice],0,0,0,0

mov ebp,.EBP ;***jus to be safe restore EBP
ret
Render endp

OPTION PROLOGUE:PROLOGUEDEF
OPTION EPILOGUE:EPILOGUEDEF
-----------------------------------------------------------------------------------
Disassembly:
00401140 89 2D 44 30 40 00 mov dword ptr [.EBP (403044h)],ebp
mov ebp,[D3Dvt]
00401146 8B 2D 4C 30 40 00 mov ebp,dword ptr [D3Dvt (40304Ch)]

invoke [D3D].Clear,[g_pd3dDevice] ,0,0,D3DCLEAR_TARGET,0FF0000FFh,f1x0,0
0040114C 6A 00 push 0
0040114E 68 00 00 80 3F push 3F800000h
00401153 68 FF 00 00 FF push 0FF0000FFh
00401158 6A 01 push 1
0040115A 6A 00 push 0
0040115C 6A 00 push 0
0040115E FF 35 04 30 40 00 push dword ptr [g_pd3dDevice (403004h)]
00401164 FF 95 AC 00 00 00 call dword ptr [ebp+0ACh]

invoke [D3D].BeginScene,[g_pd3dDevice]
0040116A FF 35 04 30 40 00 push dword ptr [g_pd3dDevice (403004h)]
00401170 FF 95 A4 00 00 00 call dword ptr [ebp+0A4h]

invoke [D3D].SetStreamSource,[g_pd3dDevice],0,g_pVB,0,sizeof (CUSTOMVERTEX)
00401176 6A 14 push 14h
00401178 6A 00 push 0
0040117A FF 35 08 30 40 00 push dword ptr [g_pVB (403008h)]
00401180 6A 00 push 0
00401182 FF 35 04 30 40 00 push dword ptr [g_pd3dDevice (403004h)]
00401188 FF 95 90 01 00 00 call dword ptr [ebp+190h]
invoke [D3D].SetFVF,[g_pd3dDevice],D3DFVF_CUSTOMVERTEX
0040118E 6A 44 push 44h
00401190 FF 35 04 30 40 00 push dword ptr [g_pd3dDevice (403004h)]
00401196 FF 95 64 01 00 00 call dword ptr [ebp+164h]
invoke [D3D].DrawPrimitive,[g_pd3dDevice],D3DPT_TRIANGLELIST,0,1
0040119C 6A 01 push 1
0040119E 6A 00 push 0
004011A0 6A 04 push 4
004011A2 FF 35 04 30 40 00 push dword ptr [g_pd3dDevice (403004h)]
004011A8 FF 95 44 01 00 00 call dword ptr [ebp+144h]

invoke [D3D].EndScene,[g_pd3dDevice]
004011AE FF 35 04 30 40 00 push dword ptr [g_pd3dDevice (403004h)]
004011B4 FF 95 A8 00 00 00 call dword ptr [ebp+0A8h]

invoke [D3D].Present,[g_pd3dDevice],0,0,0,0
004011BA 6A 00 push 0
004011BC 6A 00 push 0
004011BE 6A 00 push 0
004011C0 6A 00 push 0
004011C2 FF 35 04 30 40 00 push dword ptr [g_pd3dDevice (403004h)]
004011C8 FF 55 44 call dword ptr [ebp+44h]

mov ebp,.EBP
004011CB 8B 2D 44 30 40 00 mov ebp,dword ptr [.EBP (403044h)]
ret
004011D1 C3 ret
----------------------------------------------------------------
more complex example:

Cleanup PROC PUBLIC
LOCAL D3D[0]:_D3D
LOCAL ID3D9[0]:_ID3D9
LOCAL D3DVB[0]:_D3DVB
mov .EBP,ebp
mov ebp,[D3Dvt] ;***move EBP to the start of one vtable

cmp dword ptr [g_pd3dDevice],0
je .1

invoke [D3D].Release,[g_pd3dDevice]
.1:
cmp dword ptr [g_pD3D],0
je .2

mov ebp,[D9vt] ;***now move to the start of another vtable

invoke [ID3D9].Release,[g_pD3D]
.2:
cmp dword ptr [g_pVB],0
je .3

mov ebp,[DVBvt] ;***again, move EBP to the start of another vtable

invoke [D3DVB].Release,[g_pVB]
.3:
mov ebp,.EBP
ret
Cleanup endp

You are not supposed to use LOCAL without a stack, but I am anyway. I'm no C++ expert, but I think I'm using EBP similar the C++ this pointer.

Visual C++ uses ecx as its this pointer. I'm wonder if it is possible to make a macro that duplicates the functionality of LOCAL using a reg besides EBP. I've notice that VC with stack frames removed sometimes uses EDI as a vtable pointer.

I do not know much about macro. I looked at a lot of stuff last night, but could not find what I was looking for. Most examples assume you want your macro to produce code. LOCAL does not produce code. LOCAL does produce locally scoped typed variables with EBP as an index*.

*If you use LOCAL foobar:DWORD, it does not use EBP, just the variable. BUT if you use LOCAL foobar[0]:MYSTRUCT, it will use the form , and the [0] aligns to the top of the struct. You set EBP to the start address of the memory that you want to be treaded as that struct. An easy to use pointer to dynamically allocated structs, etc.

Why would I want to do this? I try to make readable code. The downside is that I must use EBP. What if I want a stackframe, and want to use stack based locals with floating struture pointer? I can't, unless there is a way to get a macro that will do what LOCAL does, but with another register. Has anyone made a macro like this? If anyone knows how to use macros to make types, and not code. If you give me some hints on how it is done, I can try to make such a macro.

Thanks.
Posted on 2003-05-01 07:39:31 by ThoughtCriminal
Heh, cute :)

With the ebp save/restore method your code isn't thread safe, but that hardly matters in a global "render" function. Seems like a cute trick for DirectX stuff.

If you want the usual LOCAL support, you should be able to achieve much the same for any register with an ASSUME directive, I suppose.

Of course it'd be like "invoke .Release," instead of , but that could be taken care of with a local EQU or something.
Posted on 2003-05-01 07:50:41 by f0dder
Thanks for your input f0dder.

Actually the whole program except for WinMain and WndProc use the same method of using EBP to point to data structures and vtables. WinMain and WndProc still have stack frames.

With the ebp save/restore method your code isn't thread safe, but that hardly matters in a global "render" function. Seems like a cute trick for DirectX stuff.

I have not used threading yet. The reason for this post is I'd like to do the same thing, but with a safer reg like ecx.

If you want the usual LOCAL support, you should be able to achieve much the same for any register with an ASSUME directive, I suppose.

Of course it'd be like "invoke .Release," instead of , but that could be taken care of with a local EQU or something.

I have not used ASSUME too much. I'll have to look into what kind of code it generates.

I started thinking about the equate idea too. Is there a difference between TEXTEQU and EQU?

D3D TEXTEQU ecx

Then check the code and see if I get

I found when translating C++ to assem, with COM objects and vtables base+index is easier than just and address to translate. VC will use base + index for those things. For example, if your Release method and the VC release method, when disassembled,are both the same. Chances are your translation of the structure was correct.

Thanks.
Posted on 2003-05-01 23:14:04 by ThoughtCriminal
Write your own PROLOGUE/EPILOGUE! Your are not using the default, so write your own!

http://www.asmcommunity.net/board/index.php?topic=2153&highlight=PROLOGUE
Posted on 2003-05-01 23:35:45 by bitRAKE
Thanks bitRAKE!! Great idea.

Now does anyone know were I can get some newbie tutorials on macros?

Thanks.
Posted on 2003-05-02 01:24:06 by ThoughtCriminal
I have thought about this a week before, I am doing a work about this. I think it will work.
Posted on 2003-05-02 03:03:19 by taowen2002
Guess I was really missing something by not using assume:


_foo STRUC

bar1 FCALL@4 PTR 0
bar2 FCALL@4 PTR 0
bar3 FCALL@4 PTR 0

_foo ENDS

_bar STRUC

foo1 FCALL@4 PTR 0
foo2 FCALL@4 PTR 0
foo3 FCALL@4 PTR 0

_bar ENDS

assume ecx:ptr _foo
call [ecx].bar2
0040101A FF 51 04 call dword ptr [ecx+4]

assume ecx:ptr _bar
call [ecx].foo3
0040101D FF 51 08 call dword ptr [ecx+8]

This is not working code, but I'm getting the instruction form I want.
Then I could do a TEXTEQU

D3D TEXTEQU ecx

And it would probably work.

oops, make that:

D3D TEXTEQU <ecx>

and it works.
Posted on 2003-05-03 05:50:24 by ThoughtCriminal
With the ebp save/restore method your code isn't thread safe, but that hardly matters in a global "render" function. Seems like a cute trick for DirectX stuff.

Why is that?
Thanks.
Posted on 2003-05-06 00:32:48 by GogetaSSJ4
With most stuff, you will only have a single thread running something like a "global render" function. With other COM stuff, especially if it's something you write once and stuff into a library for re-use, the picture can be quite different though.

The problem is the saving of EBP into a global variable - global variables == big re-entrancy problems.
Posted on 2003-05-06 01:59:39 by f0dder