Toby Opferman
http://www.opferman.net
programming@opferman.net
Win32 Assembly Intermediate Tutorial
(TASM v5.0)
"How to make a Win32 Program in Assembly"
This program is for those who know assembly, know Win32 API and know
how to declare and call functions and define variable types & structures for
Win32. A good idea is to read my Intro to Win32 Assembly Tutor before reading
this tutor.
In this tutor, I will tell you how to set up your assembly program and
even help you make a generic template so that it's very simple to create your
Win32 Assembly programs with absurd ease!
First thing you need it the processor. 386, 486 or better is a must.
.486
or
.486p
I go with 486, but you can do what you want. The processor you define only limits you
in what instructions you can use and there aren't that many new instructions on the 486,
or even that many on a pentium. But, you can still define those processors but it
doesn't really matter too much. The 386 instruction set defines the most instructions
for pmode operations, the 486 and 586 just picked up a few tricks (And CPU cycles!).
Next, you want to define your model.
.MODEL FLAT, STDCALL
You want to define FLAT mode! That is just wonderful because FLAT mode is Soooooo easy
to use. Also define Standard Calls, that is just how windows will react with your functions,
there's nothing to think about there, it really isn't nesscary to know why you declare it
with STDCALL. I'm not so sure myself why you don't use PASCAL like in Win16 assembly, I haven't
gone that far in depth but it doesn't really matter, you can get by!
Now, you can include your WIN32.INC file!
INCLUDE WIN32.INC
That's it. That will include all your functions and structures. I will talk a little more
about the defitions later, as since you define at lot but don't use them all, there's a little
note about that later in this tutor.
Then, just define your code and data.
Here is what your file should look like so far:
.486p
.MODEL FLAT, STDCALL
INCLUDE WIN32.INC
.DATA
; Define your variablesHere
.CODE
START: ; This is where your code exeuction begins.
; You may also call this WinMain if you want.
; Put Code Here
END START ; This goes at the very end of the Text File, exactly like a DOS program.
That is a shell. We will fill it in now and we should come up with a template.
For Data, all we need are:
TitleName db 'First Windows Program', 0
ClassName db 'FirstWindow', 0
Msg MSGSTRUCT <?> ; The Message Struct
WC WNDCLASS <?> ; The Windows Class
hwnd dd ? ; Handle to Window
hInstance dd ? ; Handle to Instance
We begin our code with a call to GetModuleHandle. It returns a Module, which is the
same thing as a HINSTANCE.
PUSH L 0
CALL GetModuleHandle
MOV [hInstance], EAX ; Get The Instance
Now, we have our hInstance, and now we can register the class.
CALL RegClass ; Register The Class
We can make this a seperate procedure just to make our WinMain look cleaner.
With this procedure, you can populate the Windows Class To What you want.
RegClass PROC
MOV EAX, [hInstance]
MOV [WC.clsHInstance], EAX ; Populate the Windows Class Structure
MOV [WC.clsStyle], 0
MOV [WC.clsLpfnWndProc], OFFSET WndProc ; Your Windows CallBack Function (Get Into This Later)
MOV [WC.clsCbClsExtra], 0
MOV [WC.clsCbWndExtra], 0
PUSH L IDI_APPLICATION ; Load The Icon
PUSH EAX
CALL LoadIcon
MOV [WC.clsHIcon], EAX
MOV [WC.hIconSm], EAX
PUSH L IDC_ARROW ; Load Cursor
PUSH L 0
CALL LoadCursor
MOV [WC.clsHCursor], EAX
PUSH L BLACK_BRUSH
CALL GetStockObject ; Black Background
MOV [WC.clsHbrBackground], EAX
MOV [WC.clsLpszMenuName], 0
MOV [WC.clsLpszClassName], OFFSET ClassName
PUSH OFFSET WC
CALL RegisterClass ; Register the Window
RET
ENDP
Then, we must Create The Window.
CALL CreateWind ; Create The Window
We can make this also a seperate procedure just to clean up WinMain.
CreateWind PROC
PUSH L 0
PUSH [hInst]
PUSH L 0
PUSH L 0
PUSH L CW_USEDEFAULT
PUSH L CW_USEDEFAULT
PUSH L CW_USEDEFAULT
PUSH L CW_USEDEFAULT
PUSH L WS_OVERLAPPEDWINDOW
PUSH OFFSET TitleName
PUSH OFFSET ClassName
PUSH L 0 ; These are the parameters to CreateWindowEx, Just loaded
; BackWards.
CALL CreateWindowEx ; Create The Window
MOV [hwnd], EAX ; The Create Window Returns the Handle to the window.
RET
ENDP
Now, we call ShowWindow and UpDate Window
PUSH L SW_SHOW
PUSH [hwnd]
CALL ShowWindow
PUSH [hwnd]
CALL UpdateWindow
This will create our window and display it on the screen.
Last thing we need to do in the WinMain is the Windows Message Loop.
This will poll for it's messages, when it finds a message for it's window, it will
translate it into a code, then dispatch it to it's Window Message Handler function.
In Pseudo Code:
WAIT FOR MESSAGE:
IF MESSAGE GET MESSAGE
IF MESSAGE IS QUIT THEN EXIT
TRANSLATE THE MESSAGE
SEND MESSAGE TO WINDOW ROUTINE
GO BACK TO WAIT FOR MESSAGE
EXIT
You have already seen the message loop in the beginner's tut, so I will just paste it here
with some explianations for it.
MessageLoop:
PUSH L 0
PUSH L 0
PUSH L 0
PUSH OFFSET Msg
CALL GetMessage ; Call To GetMessage
TEST EAX, EAX ; Quit Loop?
JZ SHORT EndProgram
PUSH OFFSET Msg
CALL TranslateMessage ; Translate Message
PUSH OFFSET Msg
CALL DispatchMessage ; Dispatch Message
JMP SHORT MessageLoop
EndProgram:
PUSH [Msg.wParam]
CALL ExitProcess ; End Program
The above will do exactly what the Pseudo code says. Gets the windows message, translates it
and sends it out. For most windows programs, the above will not change. There is a variation
to the loop that is more advanced and there are sometimes things you want to do between
translateing message and dispatching or before. But, those are not in general applications.
Many applications are just fine with the above and changing the above is not likely in the
interest of a novice windows programmer. When the time comes that you may need to change
it for a speical case program, you would most likely be in expert level and already
know exactly what to do and how to do it.
So, this is what your program should look like so far:
.486p
.MODEL FLAT, STDCALL
INCLUDE WIN32.INC
.DATA
Msg MSGSTRUCT <?> ; The Message Struct
WC WNDCLASS <?> ; The Windows Class
hwnd dd ? ; Handle to Window
hInstance dd ? ; Handle to Instance
.CODE
START: ; This is where your code exeuction begins.
PUSH L 0
CALL GetModule
MOV [hInstance], EAX ; Get The Instance
CALL RegClass ; Register The Class
CALL CreateWind ; Create The Window
PUSH L SW_SHOWNORMAL
PUSH [hwnd]
CALL ShowWindow ; Show Window
PUSH [hwnd]
CALL UpdateWindow ; Update The Window
MessageLoop:
PUSH L 0
PUSH L 0
PUSH L 0
PUSH OFFSET Msg
CALL GetMessage ; Call To GetMessage
TEST EAX, EAX ; Quit Loop?
JZ SHORT EndProgram
PUSH OFFSET Msg
CALL TranslateMessage ; Translate Message
PUSH OFFSET Msg
CALL DispatchMessage ; Dispatch Message
JMP SHORT MessageLoop
EndProgram:
PUSH [Msg.wParam]
CALL ExitProcess ; End Program
; END OF WinMain.
; Your Proc's Go Here (I left them out for space reasons, so you can view the top part in peace.
END START ; End of File.
ExitProcess will terminate the program and it will not return.
Next, we will add error checking to the WinMain.
CALL RegClass ; Register The Class
CALL CreateWind ; Create The Window
These two calls can return an error if the window does not register. All you have to do is
check EAX. If EAX is 0, then call ExitProcess with a 0 in the parameter.
CALL RegClass ; Register The Class
TEST EAX, EAX ; Check Window Register Error
JNZ SHORT NO_ERROR
PUSH L 0
CALL ExitProcess ; Exit
NO_ERROR:
CALL CreateWind ; Create The Window
TEST EAX, EAX ; Check Window Creation Error
JNZ SHORT WINDOW_CREATED
PUSH L 0
CALL ExitProcess ; Exit
WINDOW_CREATED:
Now, in C you may have seen:
return FALSE; and not return 0;
Well, do this in your WIN32.INC
FALSE equ <0>
TRUE equ <1>
Then, in your programs, just do
PUSH L FALSE
or
PUSH L TRUE
for those parameters.
Yes, defining 0 to mean a bunch of differnt things (i.e. NULL, FALSE, etc) can be
redundent, but it does not add any code to your program. It will only add tokens to your
assembler and will not effect your assembled code. It just just more readable to see NULL's
and FALSE's. Where you can tell a NULL you can replace with a pointer and a FALSE you can
replace with a value or TRUE.
Now, you basically have your template. You can change the RegClass proc and CreateWind
proc to customize the window. The rest of it can basically stay in place.
** As a side note, you may see something like a call to FindWindow in the Tasm examples.
The logic there is just to make sure that if there is another instance of the program, it
will put a space in it's own name to make it unique. You can do that if you want, there is
no _MAJOR_ need to. You may also change the code to instead of putting a space in, to exit
if it is already in memory, to allow only one of your programs to run at a time. Force them
not to be able to have multiple sessions of your program loaded. Depending on the type of
program you have, this may be a good idea. For the most part, you can really ignore that
though. Your general applications probably don't matter, especially for a beginner. The programs
you write probably won't be as complex as to want to force 1 instance of itself for device
sharing reasons, or memory reasons.
Now, we will define our windows call back function. This is the function that windows calls
when it wants to tell our window to do something, or that the user wants to do something to
the window. An event has occured pertaining to that window.
WndProc PROC USES EBX EDI ESI, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam:DWORD
; Code Here
ENDP
Once you define the above, you are ready to insert your code.
You would want to make sure any WM_messages are defined in your WIN32.INC
For intermediate level, I would hope you can figure out the rest. It's just plain old windows
code, if you've programmed windows in C, you should have no problem. Here is a quick look:
MOV EAX, [Msg]
CMP EAX, WM_CREATE ; Check Messages
JE W_CREATE
PUSH [lParam]
PUSH [wParam]
PUSH [Msg]
PUSH [hwnd]
CALL DefWindowProc ; Default
RET
W_CREATE: ; WM_CREATE Message
; Code
XOR EAX, EAX
RET
Now you should be ready to move onto advanced Win32 Assembly Tutor.
You should know how to:
1. Create a WinMain with all your window defines
2. Create the outline of a Call back Function.
3. Have your own template for Win32.
In Advanced Win32 we will go into detail on WndProc. We will also learn how to compile
this program! And we will also cover resources breifly and how to include them
in your programs!