I been out of programming for awhile about 3 years and decided to write a game and engine. I been kicking this game idea around for about 8 years and I finally took the plunge. After playing around with C for a couple months, I turned back to assembly. Now doing it in assembly may seem like going in the wrong direction but MASM is just one awesome MACRO assembler. I have had MASM for going on 16 years and it is easier than all the ancient pre 60s compiler that requires you to spend more time formating than writing code. With 28 years behind me in assembly it just feels right.
Anyway, I took out the MASM 6.0 disks and sat down to install. Well kind of a problem to install if you don't have a floppy drive on your machine. I took the disks to a friend to have him copy the disks to my pen drive and he informed me that the disks were ruined.
I said "No problem, I'll go to Microsoft web site and get a copy. After all, I have a licence". I searched and searched the site for MASM and found nothing so I did a web search. This site turned up and I downloaded the package. Although it does not have PWB it is still a great package. Glad to see that the MASM team is keeping it alive. I just wish everyone that programs could learn how easy MASM makes programming in assembly. MASM is one of Microsofts most powerful tools so I am kind of let down that they droped the ball. There are no books on subjects like this so I am making a book on these special things that kind of are unconventional. I would like to point out I have no offical education on programming and no school can teach this stuff. You have to learn it through experience.
I dug into the include files and looked at D3D9.INC. It is so shallow like nothing done with it. Of course it has the stubs to get the DLL loaded and some profiling. I got to thinking that maybe others are either just not interested or continue to write code and use virtual calls to the dlls or don't know how to access the power of c headers. I like structured programming myself and rather go a route that allows for C styled calls. If you understand a little about Jump tables and how they are implemented you can access any dll through the object pointers that are defined in the header file. Getting into the header file for D3D9 you will find the following:
typedef interface IDirect3D9 IDirect3D9;
typedef interface IDirect3DDevice9 IDirect3DDevice9;
typedef interface IDirect3DStateBlock9 IDirect3DStateBlock9;
typedef interface IDirect3DVertexDeclaration9
.........................
..............................
.............................. etc,.
There are a lot of interfaces in the D3D9.dll. These define interfaces within the D3D9 DLL. Now if you look futher down you see the interface, There a tons of these interfaces all over the C headers you know and they allow access to the routines.
#define INTERFACE IDirect3D9
DECLARE_INTERFACE_(IDirect3D9, IUnknown)
{
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IDirect3D9 methods ***/
STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE;
STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE;
STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE;
STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE;
STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE;
STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE;
STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE;
STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE;
STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE;
STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE;
STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE;
STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;
#ifdef D3D_DEBUG_INFO
LPCWSTR Version;
#endif
};
Did you notice that all the calls are defined for you? BUT these are not conventional calls. These are pointers within the structure of the *LPDIRECT3D9 which is accessed when you INVOKE Direct3DCreate9, SDK_VERSION which is defined in the D3D9.LIB
INVOKE Direct3DCreate9,32 ;sdk_version
mov g_pD3D,eax ;save the pointer
The INVOKE Direct3DCreate9 returns a pointer to the INTERFACE of IDirect3D9. That is a pointer to a jump table which jumps to the routine and the routine does the return.
OK so you wonder if you can get the arguments to the routine, will it work? YES!
So how do you get those arguments to the routine?
If you look futher in the header you find...
typedef struct IDirect3D9 *LPDIRECT3D9, *PDIRECT3D9;
#if !defined(__cplusplus) || defined(CINTERFACE)
#define IDirect3D9_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
#define IDirect3D9_AddRef(p) (p)->lpVtbl->AddRef(p)
#define IDirect3D9_Release(p) (p)->lpVtbl->Release(p)
#define IDirect3D9_RegisterSoftwareDevice(p,a) (p)->lpVtbl->RegisterSoftwareDevice(p,a)
#define IDirect3D9_GetAdapterCount(p) (p)->lpVtbl->GetAdapterCount(p)
#define IDirect3D9_GetAdapterIdentifier(p,a,b,c) (p)->lpVtbl->GetAdapterIdentifier(p,a,b,c)
#define IDirect3D9_GetAdapterModeCount(p,a,b) (p)->lpVtbl->GetAdapterModeCount(p,a,b)
#define IDirect3D9_EnumAdapterModes(p,a,b,c,d) (p)->lpVtbl->EnumAdapterModes(p,a,b,c,d)
#define IDirect3D9_GetAdapterDisplayMode(p,a,b) (p)->lpVtbl->GetAdapterDisplayMode(p,a,b)
#define IDirect3D9_CheckDeviceType(p,a,b,c,d,e) (p)->lpVtbl->CheckDeviceType(p,a,b,c,d,e)
#define IDirect3D9_CheckDeviceFormat(p,a,b,c,d,e,f) (p)->lpVtbl->CheckDeviceFormat(p,a,b,c,d,e,f)
#define IDirect3D9_CheckDeviceMultiSampleType(p,a,b,c,d,e,f) (p)->lpVtbl->CheckDeviceMultiSampleType(p,a,b,c,d,e,f)
#define IDirect3D9_CheckDepthStencilMatch(p,a,b,c,d,e) (p)->lpVtbl->CheckDepthStencilMatch(p,a,b,c,d,e)
#define IDirect3D9_CheckDeviceFormatConversion(p,a,b,c,d) (p)->lpVtbl->CheckDeviceFormatConversion(p,a,b,c,d)
#define IDirect3D9_GetDeviceCaps(p,a,b,c) (p)->lpVtbl->GetDeviceCaps(p,a,b,c)
#define IDirect3D9_GetAdapterMonitor(p,a) (p)->lpVtbl->GetAdapterMonitor(p,a)
#define IDirect3D9_CreateDevice(p,a,b,c,d,e,f) (p)->lpVtbl->CreateDevice(p,a,b,c,d,e,f)
This tells you the order to make the calls
lpVtbl is the Long pointer to the Virtual Table
so you know..
You make a stack frame just like the INVOKE EXCEPT you do not make a call because the interface is not defined in a include file as a procedure. You make a call to the POINTER. Well since you make a call to a pointer and you process the arguments, you have to put them in the stack YOURSELF.
OK sound complicated. It really isn't and you can build macros that make it look EXACTLY like C code
Now lets take the biggie one here and break it down
STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;
STDMETHOD is the standard calling convention so if you were to invoke you would pass the stack UINT, D3DDEVTYPE, HWND, DWORD , D3DPRESENT_PARAMETERS*, IDirect3DDevice9**
push uint
push d3ddevtype
mov eax,hwnd
push eax
push DWORD
lea eax,d3dpresent_parameters
push eax
mov eax,offset Idirect3ddevice9
push eax
call CreateDevice
emm.. well you can't call it.
you need to build your own stack frame and call the pointer
Since you are emulating the stack you have to build the stackframe backwards.
mov eax,offset IDirect3DDevice9 ;a pointer and will be filled in by
;the engine and point to another
;interface. Look at the list.
mov dword ptr [esp+24],eax (arg6*4)
lea eax, [d3dpresent_parameters]
mov dword ptr [esp+20],eax (arg5*4)
mov dword ptr [esp+16],DWORD (arg4*4)
mov eax,hwnd
mov dword ptr [esp+12],eax (arg3*4)
mov dword ptr [esp+8],d3ddevtype (arg2*4)
mov dword ptr [esp+4],uint (arg1*4)
Well we are still missing one and guess what that is
the pointer!
g_pD3D
BUT this pointer is special in that it points to the jump table defined in DECLARE interface STDMETHOD if you count them you get 17 but it is zero based or 0 to 16 so we count down and get 16 as the interface number.
16*4 = 64. Four bytes make a dword and the jump table is dwords. SO we do this:
mov eax,g_pD3D
mov dword ptr[esp],eax
mov edx,dword ptr[eax]
call [edx+64]
sub esp,28 ;you have to clean the stack for these kind of calls.
well that was a lot of work just to get the interface and I am sure if you program you don't want to keep typin this every time so we make a macro in line 16
we call it IDirect3D9_CreateDevice MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ , e:REQ , f:REQ
mov eax,&p&
mov dword ptr[esp],eax
mov edx,dword ptr[eax]
mov eax,offset &f&
mov dword ptr [esp+24],eax
lea eax, [&e&]
mov dword ptr [esp+20],eax
mov dword ptr [esp+16],&d&
mov eax,&c&
mov dword ptr [esp+12],eax
mov dword ptr [esp+8],&b&
mov dword ptr [esp+4],&a&
call [edx+64]
sub esp,28
ENDM
Then when we want to get the device we do this
IDirect3D9_CreateDevice g_pD3D, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface
In C it looks like this
g_pD3D-->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
YAH! making it easier arn't we?
Well lets take this a bit futher and unleash some of MASM's true power
I don't want to edit every line with all that code so lets make a couple macros to make it even easier.
sproc macro p:req ;standard procedure
mov eax,&p&
mov dword ptr[esp],eax
mov edx,dword ptr[eax]
ENDM
mcall MACRO c:REQ, s:REQ ;memory access call
if c EQ 0
call dword ptr[edx]
else
call dword ptr[edx+c]
endif
sub esp,s
ENDM
const MACRO a:REQ, c:REQ ;constant
if c EQ 0
mov dword ptr[esp],&a&
else
mov dword ptr[esp+c],&a&
endif
ENDM
adptr MACRO a:REQ, c:REQ ;address pointer
mov eax,&a&
stnct c
ENDM
ppptr MACRO a:REQ, c:REQ ;offset pointer
mov eax,offset &a&
stnct c
ENDM
poter MACRO a:REQ, c:REQ ;address access pointer
lea eax,[&a&]
stnct c
ENDM
stnct MACRO c:req ;makes repeated counts easier
if c EQ 0
mov dword ptr[esp],eax
else
mov dword ptr[esp+c],eax
endif
ENDM
Since every line is a pointer and it is all standardized we can make a few comments in each macro and it will make the entire process even easier
so now we take the macro and edit it like this.
IDirect3D9_CreateDevice MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ , e:REQ , f:REQ
;(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;
sproc &p&
ppptr &f&,24
poter &e&,20
const &d&,16
adptr &c&,12
const &b&,8
const &a&,4
mcall 64,28
ENDM
well since we now have it standarized we can automate the whole process!
YAH!!!
;---------------------------------------------------
; Generated macro file
; Making function calls easier in assembly. - DEMI
;---------------------------------------------------
sproc macro p:req
mov eax,&p&
mov dword ptr[esp],eax
mov edx,dword ptr[eax]
ENDM
mcall MACRO c:REQ, s:REQ
if c EQ 0
call dword ptr[edx]
else
call dword ptr[edx+c]
endif
sub esp,s
ENDM
const MACRO a:REQ, c:REQ
if c EQ 0
mov dword ptr[esp],&a&
else
mov dword ptr[esp+c],&a&
endif
ENDM
adptr MACRO a:REQ, c:REQ
mov eax,&a&
stnct c
ENDM
ppptr MACRO a:REQ, c:REQ
mov eax,offset &a&
stnct c
ENDM
poter MACRO a:REQ, c:REQ
lea eax,[&a&]
stnct c
ENDM
stnct MACRO c:req
if c EQ 0
mov dword ptr[esp],eax
else
mov dword ptr[esp+c],eax
endif
ENDM
IDirect3D9_QueryInterface MACRO p:REQ, a:REQ , b:REQ
;(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;
sproc &p&
ppptr &b&,8
const &a&,4
mcall 0,12
ENDM
IDirect3D9_AddRef MACRO p:REQ
; <-- Returns a value --> (ULONG,AddRef)(THIS) PURE;
sproc &p&
mcall 4,4
ENDM
IDirect3D9_Release MACRO p:REQ
; <-- Returns a value --> (ULONG,Release)(THIS) PURE;
sproc &p&
mcall 8,4
ENDM
IDirect3D9_RegisterSoftwareDevice MACRO p:REQ, a:REQ
;(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE;
sproc &p&
adptr &a&,4
mcall 12,8
ENDM
IDirect3D9_GetAdapterCount MACRO p:REQ
; <-- Returns a value --> (UINT, GetAdapterCount)(THIS) PURE;
sproc &p&
mcall 16,4
ENDM
IDirect3D9_GetAdapterIdentifier MACRO p:REQ, a:REQ , b:REQ , c:REQ
;(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE;
sproc &p&
adptr &c&,12
const &b&,8
const &a&,4
mcall 20,16
ENDM
IDirect3D9_GetAdapterModeCount MACRO p:REQ, a:REQ , b:REQ
; <-- Returns a value --> (UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE;
sproc &p&
const &b&,8
const &a&,4
mcall 24,12
ENDM
IDirect3D9_EnumAdapterModes MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ
;(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE;
sproc &p&
adptr &d&,16
const &c&,12
const &b&,8
const &a&,4
mcall 28,20
ENDM
IDirect3D9_GetAdapterDisplayMode MACRO p:REQ, a:REQ , b:REQ
;(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE;
sproc &p&
adptr &b&,8
const &a&,4
mcall 32,12
ENDM
IDirect3D9_CheckDeviceType MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ , e:REQ
;(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE;
sproc &p&
const &e&,20
const &d&,16
const &c&,12
const &b&,8
const &a&,4
mcall 36,24
ENDM
IDirect3D9_CheckDeviceFormat MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ , e:REQ , f:REQ
;(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE;
sproc &p&
const &f&,24
const &e&,20
const &d&,16
const &c&,12
const &b&,8
const &a&,4
mcall 40,28
ENDM
IDirect3D9_CheckDeviceMultiSampleType MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ , e:REQ , f:REQ
;(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE;
sproc &p&
adptr &f&,24
const &e&,20
const &d&,16
const &c&,12
const &b&,8
const &a&,4
mcall 44,28
ENDM
IDirect3D9_CheckDepthStencilMatch MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ , e:REQ
;(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE;
sproc &p&
const &e&,20
const &d&,16
const &c&,12
const &b&,8
const &a&,4
mcall 48,24
ENDM
IDirect3D9_CheckDeviceFormatConversion MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ
;(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE;
sproc &p&
const &d&,16
const &c&,12
const &b&,8
const &a&,4
mcall 52,20
ENDM
IDirect3D9_GetDeviceCaps MACRO p:REQ, a:REQ , b:REQ , c:REQ
;(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE;
sproc &p&
adptr &c&,12
const &b&,8
const &a&,4
mcall 56,16
ENDM
IDirect3D9_GetAdapterMonitor MACRO p:REQ, a:REQ
; <-- Returns a value --> (HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE;
sproc &p&
const &a&,4
mcall 60,8
ENDM
IDirect3D9_CreateDevice MACRO p:REQ, a:REQ , b:REQ , c:REQ , d:REQ , e:REQ , f:REQ
;(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;
sproc &p&
ppptr &f&,24
poter &e&,20
const &d&,16
adptr &c&,12
const &b&,8
const &a&,4
mcall 64,28
ENDM
If anyone is interested in taking the test bed code and making it mature give me a shout. I have to many things going to maintain another program.
Nice work Demi, and welcome back. Curious, did you see the OpenGL sub-forum?
http://www.masm32.com/board/index.php?board=41.0
Quote from: Mark Jones on November 20, 2008, 06:13:13 AM
Nice work Demi, and welcome back. Curious, did you see the OpenGL sub-forum?
http://www.masm32.com/board/index.php?board=41.0
No I did not see them. Thanks for pointing me in that direction. I been doing the SDK stuff and figuring out how to access the DLL. I folowed the SDK examples and the code looks almost like C.
I removed the code and am putting it in the OpenGL forum.
[attachment deleted by admin]